language/oop5/interfaces.xml
3d522c890d98c563bb283cf89ec5da5f535cfb8f
...
...
@@ -5,7 +5,8 @@
5
5
<para>
6
6
Object interfaces allow you to create code which specifies which methods a
7
7
class must implement, without having to define how these methods are
8
-
implemented.
8
+
implemented. Interfaces share a namespace with classes and traits, so they may
9
+
not use the same name.
9
10
</para>
10
11
<para>
11
12
Interfaces are defined in the same way as a class, but with the <literal>interface</literal>
...
...
@@ -17,10 +18,36 @@
17
18
interface.
18
19
</para>
19
20
<para>
20
-
Note that it is possible to declare a <link
21
-
linkend="language.oop5.decon.constructor">constructor</link> in an interface,
22
-
which can be useful in some contexts, e.g. for use by factories.
21
+
In practice, interfaces serve two complementary purposes:
23
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
+

24
51
<sect2 xml:id="language.oop5.interfaces.implements">
25
52
<title><literal>implements</literal></title>
26
53
<para>
...
...
@@ -31,8 +58,10 @@
31
58
</para>
32
59
<warning>
33
60
<para>
34
-
A class can implement two interfaces which define a method with the
35
-
same name, only if the method declaration in both interfaces is identical.
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.
36
65
</para>
37
66
</warning>
38
67
<note>
...
...
@@ -43,18 +72,21 @@
43
72
</note>
44
73
<note>
45
74
<para>
46
-
The class implementing the interface must declare a method which has a
47
-
<link linkend="language.oop.lsp">compatible signature</link>.
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.
48
80
</para>
49
81
</note>
50
82
</sect2>
51
83
<!-- Move this to OOP constants page? -->
52
84
<sect2 xml:id="language.oop5.interfaces.constants">
53
-
<title><literal>Constants</literal></title>
85
+
<title>Constants</title>
54
86
<para>
55
87
It's possible for interfaces to have constants. Interface constants work exactly
56
-
like <link linkend="language.oop5.constants">class constants</link> except
57
-
they cannot be overridden by a class/interface that inherits them.
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.
58
90
</para>
59
91
</sect2>
60
92
<sect2 xml:id="language.oop5.interfaces.examples">
...
...
@@ -65,8 +97,8 @@
65
97
<![CDATA[
66
98
<?php
67
99

68
-
// Declare the interface 'iTemplate'
69
-
interface iTemplate
100
+
// Declare the interface 'Template'
101
+
interface Template
70
102
{
71
103
public function setVariable($name, $var);
72
104
public function getHtml($template);
...
...
@@ -74,9 +106,9 @@ interface iTemplate
74
106

75
107
// Implement the interface
76
108
// This will work
77
-
class Template implements iTemplate
109
+
class WorkingTemplate implements Template
78
110
{
79
-
private $vars = array();
111
+
private $vars = [];
80
112
81
113
public function setVariable($name, $var)
82
114
{
...
...
@@ -95,10 +127,10 @@ class Template implements iTemplate
95
127

96
128
// This will not work
97
129
// Fatal error: Class BadTemplate contains 1 abstract methods
98
-
// and must therefore be declared abstract (iTemplate::getHtml)
99
-
class BadTemplate implements iTemplate
130
+
// and must therefore be declared abstract (Template::getHtml)
131
+
class BadTemplate implements Template
100
132
{
101
-
private $vars = array();
133
+
private $vars = [];
102
134
103
135
public function setVariable($name, $var)
104
136
{
...
...
@@ -114,18 +146,18 @@ class BadTemplate implements iTemplate
114
146
<programlisting role="php">
115
147
<![CDATA[
116
148
<?php
117
-
interface a
149
+
interface A
118
150
{
119
151
public function foo();
120
152
}
121
153

122
-
interface b extends a
154
+
interface B extends A
123
155
{
124
156
public function baz(Baz $baz);
125
157
}
126
158

127
159
// This will work
128
-
class c implements b
160
+
class C implements B
129
161
{
130
162
public function foo()
131
163
{
...
...
@@ -137,7 +169,7 @@ class c implements b
137
169
}
138
170

139
171
// This will not work and result in a fatal error
140
-
class d implements b
172
+
class D implements B
141
173
{
142
174
public function foo()
143
175
{
...
...
@@ -151,27 +183,54 @@ class d implements b
151
183
]]>
152
184
</programlisting>
153
185
</example>
186
+
<example xml:id="language.oop5.interfaces.examples.variance.multiple.interfaces">
187
+
<title>Variance compatibility with multiple interfaces</title>
188
+
<programlisting role="php">
189
+
<![CDATA[
190
+
<?php
191
+
class Foo {}
192
+
class Bar extends Foo {}
193
+

194
+
interface A {
195
+
public function myfunc(Foo $arg): Foo;
196
+
}
197
+

198
+
interface B {
199
+
public function myfunc(Bar $arg): Bar;
200
+
}
201
+

202
+
class MyClass implements A, B
203
+
{
204
+
public function myfunc(Foo $arg): Bar
205
+
{
206
+
return new Bar();
207
+
}
208
+
}
209
+
?>
210
+
]]>
211
+
</programlisting>
212
+
</example>
154
213
<example xml:id="language.oop5.interfaces.examples.ex3">
155
214
<title>Multiple interface inheritance</title>
156
215
<programlisting role="php">
157
216
<![CDATA[
158
217
<?php
159
-
interface a
218
+
interface A
160
219
{
161
220
public function foo();
162
221
}
163
222

164
-
interface b
223
+
interface B
165
224
{
166
225
public function bar();
167
226
}
168
227

169
-
interface c extends a, b
228
+
interface C extends A, B
170
229
{
171
230
public function baz();
172
231
}
173
232

174
-
class d implements c
233
+
class D implements C
175
234
{
176
235
public function foo()
177
236
{
...
...
@@ -194,20 +253,86 @@ class d implements c
194
253
<programlisting role="php">
195
254
<![CDATA[
196
255
<?php
197
-
interface a
256
+
interface A
198
257
{
199
-
const b = 'Interface constant';
258
+
const B = 'Interface constant';
200
259
}
201
260

202
261
// Prints: Interface constant
203
-
echo a::b;
262
+
echo A::B;
263
+

204
264

265
+
class B implements A
266
+
{
267
+
const B = 'Class constant';
268
+
}
269
+

270
+
// Prints: Class constant
271
+
// Prior to PHP 8.1.0, this will however not work because it was not
272
+
// allowed to override constants.
273
+
echo B::B;
274
+
?>
275
+
]]>
276
+
</programlisting>
277
+
</example>
278
+
<example xml:id="language.oop5.interfaces.examples.ex5">
279
+
<title>Interfaces with abstract classes</title>
280
+
<programlisting role="php">
281
+
<![CDATA[
282
+
<?php
283
+
interface A
284
+
{
285
+
public function foo(string $s): string;
286
+

287
+
public function bar(int $i): int;
288
+
}
289
+

290
+
// An abstract class may implement only a portion of an interface.
291
+
// Classes that extend the abstract class must implement the rest.
292
+
abstract class B implements A
293
+
{
294
+
public function foo(string $s): string
295
+
{
296
+
return $s . PHP_EOL;
297
+
}
298
+
}
299
+

300
+
class C extends B
301
+
{
302
+
public function bar(int $i): int
303
+
{
304
+
return $i * 2;
305
+
}
306
+
}
307
+
?>
308
+
]]>
309
+
</programlisting>
310
+
</example>
311
+
<example xml:id="language.oop5.interfaces.examples.ex6">
312
+
<title>Extending and implementing simultaneously</title>
313
+
<programlisting role="php">
314
+
<![CDATA[
315
+
<?php
316
+

317
+
class One
318
+
{
319
+
/* ... */
320
+
}
321
+

322
+
interface Usable
323
+
{
324
+
/* ... */
325
+
}
326
+

327
+
interface Updatable
328
+
{
329
+
/* ... */
330
+
}
205
331

206
-
// This will however not work because it's not allowed to
207
-
// override constants.
208
-
class b implements a
332
+
// The keyword order here is important. 'extends' must come first.
333
+
class Two extends One implements Usable, Updatable
209
334
{
210
-
const b = 'Class constant';
335
+
/* ... */
211
336
}
212
337
?>
213
338
]]>
214
339