language/oop5/interfaces.xml
0c2a0d736df56dd0f3a1b88bd6ce7c975c38285a
...
...
@@ -1,14 +1,15 @@
1
1
<?xml version="1.0" encoding="utf-8"?>
2
2
<!-- $Revision$ -->
3
-
<sect1 xml:id="language.oop5.interfaces" xmlns="http://docbook.org/ns/docbook">
3
+
<sect1 xml:id="language.oop5.interfaces" xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink">
4
4
<title>Object Interfaces</title>
5
5
<para>
6
-
Object interfaces allow you to create code which specifies which methods a
7
-
class must implement, without having to define how these methods are
8
-
handled.
6
+
Object interfaces allow you to create code which specifies which methods and properties a
7
+
class must implement, without having to define how these methods or properties are
8
+
implemented. Interfaces share a namespace with classes, traits, and enumerations, so they may
9
+
not use the same name.
9
10
</para>
10
11
<para>
11
-
Interfaces are defined in the same was as a class, but with the <literal>interface</literal>
12
+
Interfaces are defined in the same way as a class, but with the <literal>interface</literal>
12
13
keyword replacing the <literal>class</literal> keyword and without any of the methods having
13
14
their contents defined.
14
15
</para>
...
...
@@ -16,6 +17,37 @@
16
17
All methods declared in an interface must be public; this is the nature of an
17
18
interface.
18
19
</para>
20
+
<para>
21
+
In practice, interfaces serve two complementary purposes:
22
+
</para>
23
+
<simplelist>
24
+
<member>
25
+
To allow developers to create objects of different classes that may be used interchangeably
26
+
because they implement the same interface or interfaces. A common example is multiple database access services,
27
+
multiple payment gateways, or different caching strategies. Different implementations may
28
+
be swapped out without requiring any changes to the code that uses them.
29
+
</member>
30
+
<member>
31
+
To allow a function or method to accept and operate on a parameter that conforms to an
32
+
interface, while not caring what else the object may do or how it is implemented. These interfaces
33
+
are often named like <literal>Iterable</literal>, <literal>Cacheable</literal>, <literal>Renderable</literal>,
34
+
or so on to describe the significance of the behavior.
35
+
</member>
36
+
</simplelist>
37
+
<para>
38
+
Interfaces may define
39
+
<link linkend="language.oop5.magic">magic methods</link> to require implementing classes to
40
+
implement those methods.
41
+
</para>
42
+
<note>
43
+
<para>
44
+
Although they are supported, including <link linkend="language.oop5.decon.constructor">constructors</link>
45
+
in interfaces is strongly discouraged. Doing so significantly reduces the flexibility of the object implementing the
46
+
interface. Additionally, constructors are not enforced by inheritance rules, which can cause inconsistent
47
+
and unexpected behavior.
48
+
</para>
49
+
</note>
50
+

19
51
<sect2 xml:id="language.oop5.interfaces.implements">
20
52
<title><literal>implements</literal></title>
21
53
<para>
...
...
@@ -24,14 +56,14 @@
24
56
so will result in a fatal error. Classes may implement more than one interface
25
57
if desired by separating each interface with a comma.
26
58
</para>
27
-
<note>
59
+
<warning>
28
60
<para>
29
-
Prior to PHP 5.3.9, a class could not implement two interfaces that
30
-
specified a method with the same name, since it would cause ambiguity.
31
-
More recent versions of PHP allow this as long as the duplicate methods
32
-
have the same signature.
61
+
A class that implements an interface may use a different name for its parameters than
62
+
the interface. However, as of PHP 8.0 the language supports <link linkend="functions.named-arguments">named arguments</link>, which means
63
+
callers may rely on the parameter name in the interface. For that reason, it is strongly
64
+
recommended that developers use the same parameter names as the interface being implemented.
33
65
</para>
34
-
</note>
66
+
</warning>
35
67
<note>
36
68
<para>
37
69
Interfaces can be extended like classes using the <link linkend="language.oop5.inheritance">extends</link>
...
...
@@ -40,20 +72,108 @@
40
72
</note>
41
73
<note>
42
74
<para>
43
-
The class implementing the interface must use the exact same method
44
-
signatures as are defined in the interface. Not doing so will result in a
45
-
fatal error.
46
-
</para>
47
-
</note>
75
+
The class implementing the interface must declare all methods in the interface
76
+
with a <link linkend="language.oop.lsp">compatible signature</link>. A class can implement multiple interfaces
77
+
which declare a method with the same name. In this case, the implementation must follow the
78
+
<link linkend="language.oop.lsp">signature compatibility rules</link> for all the interfaces. So
79
+
<link linkend="language.oop5.variance">covariance and contravariance</link> can be applied.
80
+
</para>
81
+
</note>
48
82
</sect2>
83
+
<!-- Move this to OOP constants page? -->
49
84
<sect2 xml:id="language.oop5.interfaces.constants">
50
-
<title><literal>Constants</literal></title>
85
+
<title>Constants</title>
51
86
<para>
52
-
It's possible for interfaces to have constants. Interface constants works exactly
53
-
like <link linkend="language.oop5.constants">class constants</link> except
54
-
they cannot be overridden by a class/interface that inherits them.
87
+
It's possible for interfaces to have constants. Interface constants work exactly
88
+
like <link linkend="language.oop5.constants">class constants</link>.
89
+
Prior to PHP 8.1.0, they cannot be overridden by a class/interface that inherits them.
55
90
</para>
56
91
</sect2>
92
+
<sect2 xml:id="language.oop5.interfaces.properties">
93
+
<title>Properties</title>
94
+
<simpara>
95
+
As of PHP 8.4.0, interfaces may also declare properties.
96
+
If they do, the declaration must specify if the property is to be readable,
97
+
writeable, or both.
98
+
The interface declaration applies only to public read and write access.
99
+
</simpara>
100
+
<simpara>
101
+
An class may satisfy an interface property in multiple ways.
102
+
It may define a public property.
103
+
It may define a public
104
+
<link linkend="language.oop5.property-hooks.virtual">virtual property</link>
105
+
that implements only the corresponding hook.
106
+
Or a read property may be satisfied by a <literal>readonly</literal> property.
107
+
However, an interface property that is settable may not be <literal>readonly</literal>.
108
+
</simpara>
109
+
<example>
110
+
<title>Interface properties example</title>
111
+
<programlisting role="php">
112
+
<![CDATA[
113
+
<?php
114
+
interface I
115
+
{
116
+
// An implementing class MUST have a publicly-readable property,
117
+
// but whether or not it's publicly settable is unrestricted.
118
+
public string $readable { get; }
119
+

120
+
// An implementing class MUST have a publicly-writeable property,
121
+
// but whether or not it's publicly readable is unrestricted.
122
+
public string $writeable { set; }
123
+

124
+
// An implementing class MUST have a property that is both publicly
125
+
// readable and publicly writeable.
126
+
public string $both { get; set; }
127
+
}
128
+

129
+
// This class implements all three properties as traditional, un-hooked
130
+
// properties. That's entirely valid.
131
+
class C1 implements I
132
+
{
133
+
public string $readable;
134
+

135
+
public string $writeable;
136
+

137
+
public string $both;
138
+
}
139
+

140
+
// This class implements all three properties using just the hooks
141
+
// that are requested. This is also entirely valid.
142
+
class C2 implements I
143
+
{
144
+
private string $written = '';
145
+
private string $all = '';
146
+

147
+
// Uses only a get hook to create a virtual property.
148
+
// This satisfies the "public get" requirement.
149
+
// It is not writeable, but that is not required by the interface.
150
+
public string $readable { get => strtoupper($this->writeable); }
151
+

152
+
// The interface only requires the property be settable,
153
+
// but also including get operations is entirely valid.
154
+
// This example creates a virtual property, which is fine.
155
+
public string $writeable {
156
+
get => $this->written;
157
+
set {
158
+
$this->written = $value;
159
+
}
160
+
}
161
+

162
+
// This property requires both read and write be possible,
163
+
// so we need to either implement both, or allow it to have
164
+
// the default behavior.
165
+
public string $both {
166
+
get => $this->all;
167
+
set {
168
+
$this->all = strtoupper($value);
169
+
}
170
+
}
171
+
}
172
+
?>
173
+
]]>
174
+
</programlisting>
175
+
</example>
176
+
</sect2>
57
177
<sect2 xml:id="language.oop5.interfaces.examples">
58
178
&reftitle.examples;
59
179
<example xml:id="language.oop5.interfaces.examples.ex1">
...
...
@@ -62,8 +182,8 @@
62
182
<![CDATA[
63
183
<?php
64
184

65
-
// Declare the interface 'iTemplate'
66
-
interface iTemplate
185
+
// Declare the interface 'Template'
186
+
interface Template
67
187
{
68
188
public function setVariable($name, $var);
69
189
public function getHtml($template);
...
...
@@ -71,9 +191,9 @@ interface iTemplate
71
191

72
192
// Implement the interface
73
193
// This will work
74
-
class Template implements iTemplate
194
+
class WorkingTemplate implements Template
75
195
{
76
-
private $vars = array();
196
+
private $vars = [];
77
197
78
198
public function setVariable($name, $var)
79
199
{
...
...
@@ -92,10 +212,10 @@ class Template implements iTemplate
92
212

93
213
// This will not work
94
214
// Fatal error: Class BadTemplate contains 1 abstract methods
95
-
// and must therefore be declared abstract (iTemplate::getHtml)
96
-
class BadTemplate implements iTemplate
215
+
// and must therefore be declared abstract (Template::getHtml)
216
+
class BadTemplate implements Template
97
217
{
98
-
private $vars = array();
218
+
private $vars = [];
99
219
100
220
public function setVariable($name, $var)
101
221
{
...
...
@@ -111,18 +231,18 @@ class BadTemplate implements iTemplate
111
231
<programlisting role="php">
112
232
<![CDATA[
113
233
<?php
114
-
interface a
234
+
interface A
115
235
{
116
236
public function foo();
117
237
}
118
238

119
-
interface b extends a
239
+
interface B extends A
120
240
{
121
241
public function baz(Baz $baz);
122
242
}
123
243

124
244
// This will work
125
-
class c implements b
245
+
class C implements B
126
246
{
127
247
public function foo()
128
248
{
...
...
@@ -134,7 +254,7 @@ class c implements b
134
254
}
135
255

136
256
// This will not work and result in a fatal error
137
-
class d implements b
257
+
class D implements B
138
258
{
139
259
public function foo()
140
260
{
...
...
@@ -145,6 +265,33 @@ class d implements b
145
265
}
146
266
}
147
267
?>
268
+
]]>
269
+
</programlisting>
270
+
</example>
271
+
<example xml:id="language.oop5.interfaces.examples.variance.multiple.interfaces">
272
+
<title>Variance compatibility with multiple interfaces</title>
273
+
<programlisting role="php">
274
+
<![CDATA[
275
+
<?php
276
+
class Foo {}
277
+
class Bar extends Foo {}
278
+

279
+
interface A {
280
+
public function myfunc(Foo $arg): Foo;
281
+
}
282
+

283
+
interface B {
284
+
public function myfunc(Bar $arg): Bar;
285
+
}
286
+

287
+
class MyClass implements A, B
288
+
{
289
+
public function myfunc(Foo $arg): Bar
290
+
{
291
+
return new Bar();
292
+
}
293
+
}
294
+
?>
148
295
]]>
149
296
</programlisting>
150
297
</example>
...
...
@@ -153,22 +300,22 @@ class d implements b
153
300
<programlisting role="php">
154
301
<![CDATA[
155
302
<?php
156
-
interface a
303
+
interface A
157
304
{
158
305
public function foo();
159
306
}
160
307

161
-
interface b
308
+
interface B
162
309
{
163
310
public function bar();
164
311
}
165
312

166
-
interface c extends a, b
313
+
interface C extends A, B
167
314
{
168
315
public function baz();
169
316
}
170
317

171
-
class d implements c
318
+
class D implements C
172
319
{
173
320
public function foo()
174
321
{
...
...
@@ -191,30 +338,96 @@ class d implements c
191
338
<programlisting role="php">
192
339
<![CDATA[
193
340
<?php
194
-
interface a
341
+
interface A
195
342
{
196
-
const b = 'Interface constant';
343
+
const B = 'Interface constant';
197
344
}
198
345

199
346
// Prints: Interface constant
200
-
echo a::b;
347
+
echo A::B;
348
+

349
+

350
+
class B implements A
351
+
{
352
+
const B = 'Class constant';
353
+
}
354
+

355
+
// Prints: Class constant
356
+
// Prior to PHP 8.1.0, this will however not work because it was not
357
+
// allowed to override constants.
358
+
echo B::B;
359
+
?>
360
+
]]>
361
+
</programlisting>
362
+
</example>
363
+
<example xml:id="language.oop5.interfaces.examples.ex5">
364
+
<title>Interfaces with abstract classes</title>
365
+
<programlisting role="php">
366
+
<![CDATA[
367
+
<?php
368
+
interface A
369
+
{
370
+
public function foo(string $s): string;
371
+

372
+
public function bar(int $i): int;
373
+
}
374
+

375
+
// An abstract class may implement only a portion of an interface.
376
+
// Classes that extend the abstract class must implement the rest.
377
+
abstract class B implements A
378
+
{
379
+
public function foo(string $s): string
380
+
{
381
+
return $s . PHP_EOL;
382
+
}
383
+
}
201
384

385
+
class C extends B
386
+
{
387
+
public function bar(int $i): int
388
+
{
389
+
return $i * 2;
390
+
}
391
+
}
392
+
?>
393
+
]]>
394
+
</programlisting>
395
+
</example>
396
+
<example xml:id="language.oop5.interfaces.examples.ex6">
397
+
<title>Extending and implementing simultaneously</title>
398
+
<programlisting role="php">
399
+
<![CDATA[
400
+
<?php
401
+

402
+
class One
403
+
{
404
+
/* ... */
405
+
}
406
+

407
+
interface Usable
408
+
{
409
+
/* ... */
410
+
}
411
+

412
+
interface Updatable
413
+
{
414
+
/* ... */
415
+
}
202
416

203
-
// This will however not work because it's not allowed to
204
-
// override constants.
205
-
class b implements a
417
+
// The keyword order here is important. 'extends' must come first.
418
+
class Two extends One implements Usable, Updatable
206
419
{
207
-
const b = 'Class constant';
420
+
/* ... */
208
421
}
209
422
?>
210
423
]]>
211
424
</programlisting>
212
425
</example>
213
426
<para>
214
-
An interface, together with type-hinting, provides a good way to make sure
427
+
An interface, together with type declarations, provides a good way to make sure
215
428
that a particular object contains particular methods. See
216
429
<link linkend="language.operators.type">instanceof</link> operator and
217
-
<link linkend="language.oop5.typehinting">type hinting</link>.
430
+
<link linkend="language.types.declarations">type declarations</link>.
218
431
</para>
219
432
</sect2>
220
433

221
434