language/oop5/magic.xml
5e15a6c3e4d5819102361ae78e73c90a06238c8a
...
...
@@ -3,7 +3,19 @@
3
3
<sect1 xml:id="language.oop5.magic" xmlns="http://docbook.org/ns/docbook">
4
4
<title>Magic Methods</title>
5
5
<para>
6
-
The function names
6
+
Magic methods are special methods which override PHP's default's action
7
+
when certain actions are performed on an object.
8
+
</para>
9
+
<caution>
10
+
<simpara>
11
+
All methods names starting with <literal>__</literal> are reserved by PHP.
12
+
Therefore, it is not recommended to use such method names unless overriding
13
+
PHP's behavior.
14
+
</simpara>
15
+
</caution>
16
+
<para>
17
+
The following method names are considered magical:
18
+
<!-- Should be an itemized list ? -->
7
19
<link linkend="object.construct">__construct()</link>,
8
20
<link linkend="object.destruct">__destruct()</link>,
9
21
<link linkend="object.call">__call()</link>,
...
...
@@ -19,27 +31,38 @@
19
31
<link linkend="object.tostring">__toString()</link>,
20
32
<link linkend="object.invoke">__invoke()</link>,
21
33
<link linkend="object.set-state">__set_state()</link>,
22
-
<link linkend="object.clone">__clone()</link> and
23
-
<link linkend="object.debuginfo">__debugInfo()</link>
24
-
are magical in PHP classes. You
25
-
cannot have functions with these names in any of your
26
-
classes unless you want the magic functionality associated
27
-
with them.
34
+
<link linkend="object.clone">__clone()</link>, and
35
+
<link linkend="object.debuginfo">__debugInfo()</link>.
28
36
</para>
29
37

30
-
<note>
31
-
<simpara>
32
-
All magic methods <emphasis>MUST</emphasis> be declared as <literal>public</literal>
33
-
</simpara>
34
-
</note>
35
-

36
-
<caution>
38
+
<warning>
39
+
<!-- See for a code example of this behaviour: https://3v4l.org/Bov34 -->
37
40
<simpara>
38
-
PHP reserves all function names starting with __ as magical.
39
-
It is recommended that you do not use function names with
40
-
__ in PHP unless you want some documented magic functionality.
41
+
All magic methods, with the exception of
42
+
<link linkend="object.construct">__construct()</link>,
43
+
<link linkend="object.destruct">__destruct()</link>, and
44
+
<link linkend="object.clone">__clone()</link>,
45
+
<emphasis>must</emphasis> be declared as <literal>public</literal>,
46
+
otherwise an <constant>E_WARNING</constant> is emitted.
47
+
Prior to PHP 8.0.0, no diagnostic was emitted for the magic methods
48
+
<link linkend="object.sleep">__sleep()</link>,
49
+
<link linkend="object.wakeup">__wakeup()</link>,
50
+
<link linkend="object.serialize">__serialize()</link>,
51
+
<link linkend="object.unserialize">__unserialize()</link>, and
52
+
<link linkend="object.set-state">__set_state()</link>.
41
53
</simpara>
42
-
</caution>
54
+
</warning>
55
+
<warning>
56
+
<para>
57
+
If type declarations are used in the definition of a magic method, they
58
+
must be identical to the signature described in this document.
59
+
Otherwise, a fatal error is emitted.
60
+
Prior to PHP 8.0.0, no diagnostic was emitted.
61
+
However, <link linkend="object.construct">__construct()</link> and
62
+
<link linkend="object.destruct">__destruct()</link> must not declare a return type;
63
+
otherwise a fatal error is emitted.
64
+
</para>
65
+
</warning>
43
66
44
67
<sect2 xml:id="language.oop5.magic.sleep">
45
68
<title>
...
...
@@ -69,15 +92,19 @@
69
92
<para>
70
93
It is not possible for <link linkend="object.sleep">__sleep()</link> to return names of
71
94
private properties in parent classes. Doing this will result in an
72
-
<constant>E_NOTICE</constant> level error. Instead you may use the
73
-
<classname>Serializable</classname> interface.
95
+
<constant>E_NOTICE</constant> level error.
96
+
Use <link linkend="object.serialize">__serialize()</link> instead.
97
+
</para>
98
+
</note>
99
+
<note>
100
+
<para>
101
+
As of PHP 8.0.0, returning a value which is not an array from <link linkend="object.sleep">__sleep()</link> generates a warning. Previously, it generated a notice.
74
102
</para>
75
103
</note>
76
104
<para>
77
105
The intended use of <link linkend="object.sleep">__sleep()</link> is to commit pending
78
106
data or perform similar cleanup tasks. Also, the function is
79
-
useful if you have very large objects which do not need to be
80
-
saved completely.
107
+
useful if a very large object doesn't need to be saved completely.
81
108
</para>
82
109
<para>
83
110
Conversely, <function>unserialize</function> checks for the
...
...
@@ -181,7 +208,7 @@ class Connection
181
208
</note>
182
209
<note>
183
210
<para>
184
-
This feature is available since PHP 7.4.0.
211
+
This feature is available as of PHP 7.4.0.
185
212
</para>
186
213
</note>
187
214
<example>
...
...
@@ -239,15 +266,43 @@ class Connection
239
266
<para>
240
267
The <link linkend="object.tostring">__toString()</link> method allows a class to decide
241
268
how it will react when it is treated like a string. For example,
242
-
what <literal>echo $obj;</literal> will print. This method must
243
-
return a string, as otherwise a fatal <constant>E_RECOVERABLE_ERROR</constant>
244
-
level error is emitted.
269
+
what <literal>echo $obj;</literal> will print.
245
270
</para>
246
271
<warning>
272
+
<para>
273
+
As of PHP 8.0.0, the return value follows standard PHP type semantics,
274
+
meaning it will be coerced into a <type>string</type> if possible and if
275
+
<link linkend="language.types.declarations.strict">strict typing</link>
276
+
is disabled.
277
+
</para>
278
+
<para>
279
+
A <interfacename>Stringable</interfacename> object will
280
+
<emphasis>not</emphasis> be accepted by a <type>string</type> type declaration if
281
+
<link linkend="language.types.declarations.strict">strict typing</link>
282
+
is enabled. If such behaviour is wanted the type declaration must accept
283
+
<interfacename>Stringable</interfacename> and <type>string</type> via a union type.
284
+
</para>
285
+
<para>
286
+
As of PHP 8.0.0, any class that contains a <link linkend="object.tostring">__toString()</link>
287
+
method will also implicitly implement the <interfacename>Stringable</interfacename> interface, and will
288
+
thus pass type checks for that interface. Explicitly implementing the interface anyway is
289
+
recommended.
290
+
</para>
291
+
<para>
292
+
In PHP 7.4, the returned value <emphasis>must</emphasis> be a
293
+
<type>string</type>, otherwise an <classname>Error</classname> is thrown.
294
+
</para>
295
+
<para>
296
+
Prior to PHP 7.4.0, the returned value <emphasis>must</emphasis> be a
297
+
<type>string</type>, otherwise a fatal <constant>E_RECOVERABLE_ERROR</constant>
298
+
is emitted.
299
+
</para>
300
+
</warning>
301
+
<warning>
247
302
<simpara>
248
303
It was not possible to throw an exception from within a
249
-
<link linkend="object.tostring">__toString()</link> method before PHP 7.4.0. Doing so will
250
-
result in a fatal error.
304
+
<link linkend="object.tostring">__toString()</link>
305
+
method prior to PHP 7.4.0. Doing so will result in a fatal error.
251
306
</simpara>
252
307
</warning>
253
308
<example>
...
...
@@ -321,6 +376,96 @@ bool(true)
321
376
]]>
322
377
</screen>
323
378
</example>
379
+
<example>
380
+
<title>Using <link linkend="object.invoke">__invoke()</link></title>
381
+
<programlisting role="php">
382
+
<![CDATA[
383
+
<?php
384
+
class Sort
385
+
{
386
+
private $key;
387
+

388
+
public function __construct(string $key)
389
+
{
390
+
$this->key = $key;
391
+
}
392
+

393
+
public function __invoke(array $a, array $b): int
394
+
{
395
+
return $a[$this->key] <=> $b[$this->key];
396
+
}
397
+
}
398
+

399
+
$customers = [
400
+
['id' => 1, 'first_name' => 'John', 'last_name' => 'Do'],
401
+
['id' => 3, 'first_name' => 'Alice', 'last_name' => 'Gustav'],
402
+
['id' => 2, 'first_name' => 'Bob', 'last_name' => 'Filipe']
403
+
];
404
+

405
+
// sort customers by first name
406
+
usort($customers, new Sort('first_name'));
407
+
print_r($customers);
408
+

409
+
// sort customers by last name
410
+
usort($customers, new Sort('last_name'));
411
+
print_r($customers);
412
+
?>
413
+
]]>
414
+
</programlisting>
415
+
&example.outputs;
416
+
<screen>
417
+
<![CDATA[
418
+
Array
419
+
(
420
+
[0] => Array
421
+
(
422
+
[id] => 3
423
+
[first_name] => Alice
424
+
[last_name] => Gustav
425
+
)
426
+

427
+
[1] => Array
428
+
(
429
+
[id] => 2
430
+
[first_name] => Bob
431
+
[last_name] => Filipe
432
+
)
433
+

434
+
[2] => Array
435
+
(
436
+
[id] => 1
437
+
[first_name] => John
438
+
[last_name] => Do
439
+
)
440
+

441
+
)
442
+
Array
443
+
(
444
+
[0] => Array
445
+
(
446
+
[id] => 1
447
+
[first_name] => John
448
+
[last_name] => Do
449
+
)
450
+

451
+
[1] => Array
452
+
(
453
+
[id] => 2
454
+
[first_name] => Bob
455
+
[last_name] => Filipe
456
+
)
457
+

458
+
[2] => Array
459
+
(
460
+
[id] => 3
461
+
[first_name] => Alice
462
+
[last_name] => Gustav
463
+
)
464
+

465
+
)
466
+
]]>
467
+
</screen>
468
+
</example>
324
469
</sect2>
325
470

326
471
<sect2 xml:id="language.oop5.magic.set-state">
...
...
@@ -335,7 +480,7 @@ bool(true)
335
480
</para>
336
481
<para>
337
482
The only parameter of this method is an array containing exported
338
-
properties in the form <literal>array('property' => value, ...)</literal>.
483
+
properties in the form <literal>['property' => value, ...]</literal>.
339
484
</para>
340
485
<example>
341
486
<title>Using <link linkend="object.set-state">__set_state()</link></title>
...
...
@@ -361,18 +506,20 @@ $a = new A;
361
506
$a->var1 = 5;
362
507
$a->var2 = 'foo';
363
508

364
-
eval('$b = ' . var_export($a, true) . ';'); // $b = A::__set_state(array(
365
-
// 'var1' => 5,
366
-
// 'var2' => 'foo',
367
-
// ));
509
+
$b = var_export($a, true);
368
510
var_dump($b);
369
-

511
+
eval('$c = ' . $b . ';');
512
+
var_dump($c);
370
513
?>
371
514
]]>
372
515
</programlisting>
373
516
&example.outputs;
374
517
<screen>
375
518
<![CDATA[
519
+
string(60) "A::__set_state(array(
520
+
'var1' => 5,
521
+
'var2' => 'foo',
522
+
))"
376
523
object(A)#2 (2) {
377
524
["var1"]=>
378
525
int(5)
...
...
@@ -386,7 +533,7 @@ object(A)#2 (2) {
386
533
<simpara>
387
534
When exporting an object, <function>var_export</function> does not check
388
535
whether <link linkend="object.set-state">__set_state()</link> is
389
-
implemented by the object's class, so re-importing such objects will fail,
536
+
implemented by the object's class, so re-importing objects will result in an <classname>Error</classname> exception,
390
537
if __set_state() is not implemented. Particularly, this affects some
391
538
internal classes.
392
539
</simpara>
393
540