language/oop5/decon.xml
9f351a14a749721a74df1dc6ac9533cf796a652e
...
...
@@ -7,11 +7,10 @@
7
7
<title>Constructor</title>
8
8
<methodsynopsis xml:id="object.construct">
9
9
<type>void</type><methodname>__construct</methodname>
10
-
<methodparam choice="opt"><type>mixed</type><parameter>args</parameter><initializer>""</initializer></methodparam>
11
-
<methodparam choice="opt"><parameter>...</parameter></methodparam>
10
+
<methodparam rep="repeat"><type>mixed</type><parameter>values</parameter><initializer>""</initializer></methodparam>
12
11
</methodsynopsis>
13
12
<para>
14
-
PHP 5 allows developers to declare constructor methods for classes.
13
+
PHP allows developers to declare constructor methods for classes.
15
14
Classes which have a constructor method call this method on each
16
15
newly-created object, so it is suitable for any initialization that the
17
16
object may need before it is used.
...
...
@@ -27,21 +26,21 @@
27
26
</simpara>
28
27
</note>
29
28
<example>
30
-
<title>using new unified constructors</title>
29
+
<title>Constructors in inheritance</title>
31
30
<programlisting role="php">
32
31
<![CDATA[
33
32
<?php
34
33
class BaseClass {
35
-
function __construct() {
36
-
print "In BaseClass constructor\n";
37
-
}
34
+
function __construct() {
35
+
print "In BaseClass constructor\n";
36
+
}
38
37
}
39
38

40
39
class SubClass extends BaseClass {
41
-
function __construct() {
42
-
parent::__construct();
43
-
print "In SubClass constructor\n";
44
-
}
40
+
function __construct() {
41
+
parent::__construct();
42
+
print "In SubClass constructor\n";
43
+
}
45
44
}
46
45

47
46
class OtherSubClass extends BaseClass {
...
...
@@ -62,43 +61,242 @@ $obj = new OtherSubClass();
62
61
</programlisting>
63
62
</example>
64
63
<para>
65
-
For backwards compatibility, if PHP 5 cannot find a
66
-
<link linkend="object.construct">__construct()</link> function for a given class, and the
67
-
class did not inherit one from a parent class, it will
68
-
search for the old-style constructor function, by the name of the class.
69
-
Effectively, it means that the only case that would have compatibility
70
-
issues is if the class had a method named
71
-
<link linkend="object.construct">__construct()</link> which was used for different semantics.
64
+
Unlike other methods, <link linkend="object.construct">__construct()</link>
65
+
is exempt from the usual
66
+
<link linkend="language.oop.lsp">signature compatibility rules</link>
67
+
when being extended.
72
68
</para>
73
69
<para>
74
-
Unlike with other methods, PHP will not generate an
75
-
<constant>E_STRICT</constant> level error message when
76
-
<link linkend="object.construct">__construct()</link> is overridden with different parameters
77
-
than the parent <link linkend="object.construct">__construct()</link> method has.
78
-
</para>
79
-
<para>
80
-
As of PHP 5.3.3, methods with the same name as the last element of a
81
-
namespaced class name will no longer be treated as constructor. This
82
-
change doesn't affect non-namespaced classes.
70
+
Constructors are ordinary methods which are called during the instantiation of their
71
+
corresponding object. As such, they may define an arbitrary number of arguments, which
72
+
may be required, may have a type, and may have a default value. Constructor arguments
73
+
are called by placing the arguments in parentheses after the class name.
83
74
</para>
84
75
<example>
85
-
<title>Constructors in namespaced classes</title>
76
+
<title>Using constructor arguments</title>
86
77
<programlisting role="php">
87
78
<![CDATA[
88
79
<?php
89
-
namespace Foo;
90
-
class Bar {
91
-
public function Bar() {
92
-
// treated as constructor in PHP 5.3.0-5.3.2
93
-
// treated as regular method as of PHP 5.3.3
80
+
class Point {
81
+
protected int $x;
82
+
protected int $y;
83
+

84
+
public function __construct(int $x, int $y = 0) {
85
+
$this->x = $x;
86
+
$this->y = $y;
94
87
}
95
88
}
89
+

90
+
// Pass both parameters.
91
+
$p1 = new Point(4, 5);
92
+
// Pass only the required parameter. $y will take its default value of 0.
93
+
$p2 = new Point(4);
94
+
// With named parameters (as of PHP 8.0):
95
+
$p3 = new Point(y: 5, x: 4);
96
96
?>
97
97
]]>
98
98
</programlisting>
99
99
</example>
100
-
</sect2>
100
+
<para>
101
+
If a class has no constructor, or the constructor has no required arguments, the parentheses
102
+
may be omitted.
103
+
</para>
104
+
<sect3>
105
+
<title>Old-style constructors</title>
106
+
<para>
107
+
Prior to PHP 8.0.0, classes in the global namespace will interpret a method named
108
+
the same as the class as an old-style constructor. That syntax is deprecated,
109
+
and will result in an <constant>E_DEPRECATED</constant> error but still call that function as a constructor.
110
+
If both <link linkend="object.construct">__construct()</link> and a same-name method are
111
+
defined, <link linkend="object.construct">__construct()</link> will be called.
112
+
</para>
113
+
<para>
114
+
In namespaced classes, or any class as of PHP 8.0.0, a method named
115
+
the same as the class never has any special meaning.
116
+
</para>
117
+
<para>Always use <link linkend="object.construct">__construct()</link> in new code.
118
+
</para>
119
+
</sect3>
120
+
<sect3 xml:id="language.oop5.decon.constructor.promotion">
121
+
<title>Constructor Promotion</title>
122
+
<para>
123
+
As of PHP 8.0.0, constructor parameters may also be promoted to correspond to an
124
+
object property. It is very common for constructor parameters to be assigned to
125
+
a property in the constructor but otherwise not operated upon. Constructor promotion
126
+
provides a short-hand for that use case. The example above could be rewritten as the following.
127
+
</para>
128
+
<example>
129
+
<title>Using constructor property promotion</title>
130
+
<programlisting role="php">
131
+
<![CDATA[
132
+
<?php
133
+
class Point {
134
+
public function __construct(protected int $x, protected int $y = 0) {
135
+
}
136
+
}
137
+
]]>
138
+
</programlisting>
139
+
</example>
140
+
<para>
141
+
When a constructor argument includes a modifier, PHP will interpret it as
142
+
both an object property and a constructor argument, and assign the argument value to
143
+
the property. The constructor body may then be empty or may contain other statements.
144
+
Any additional statements will be executed after the argument values have been assigned
145
+
to the corresponding properties.
146
+
</para>
147
+
<para>
148
+
Not all arguments need to be promoted. It is possible to mix and match promoted and not-promoted
149
+
arguments, in any order. Promoted arguments have no impact on code calling the constructor.
150
+
</para>
151
+
<note>
152
+
<para>
153
+
Using a <link linkend="language.oop5.visibility">visibility modifier</link> (<literal>public</literal>,
154
+
<literal>protected</literal> or <literal>private</literal>) is the most likely way to apply property
155
+
promotion, but any other single modifier (such as <literal>readonly</literal>) will have the same effect.
156
+
</para>
157
+
</note>
158
+
<note>
159
+
<para>
160
+
Object properties may not be typed <type>callable</type> due to engine ambiguity that would
161
+
introduce. Promoted arguments, therefore, may not be typed <type>callable</type> either. Any
162
+
other <link linkend="language.types.declarations">type declaration</link> is permitted, however.
163
+
</para>
164
+
</note>
165
+
<note>
166
+
<para>
167
+
As promoted properties are desugared to both a property as well as a function parameter, any
168
+
and all naming restrictions for both properties as well as parameters apply.
169
+
</para>
170
+
</note>
171
+
<note>
172
+
<para>
173
+
<link linkend="language.attributes">Attributes</link> placed on a
174
+
promoted constructor argument will be replicated to both the property
175
+
and argument. Default values on a promoted constructor argument will be replicated only to the argument and not the property.
176
+
</para>
177
+
</note>
178
+
</sect3>
179
+

180
+
<sect3 xml:id="language.oop5.decon.constructor.new">
181
+
<title>New in initializers</title>
182
+
<para>
183
+
As of PHP 8.1.0, objects can be used as default parameter values,
184
+
static variables, and global constants, as well as in attribute arguments.
185
+
Objects can also be passed to <function>define</function> now.
186
+
</para>
187
+
<note>
188
+
<para>
189
+
The use of a dynamic or non-string class name or an anonymous class is not allowed.
190
+
The use of argument unpacking is not allowed.
191
+
The use of unsupported expressions as arguments is not allowed.
192
+
</para>
193
+
</note>
194
+
<example>
195
+
<title>Using new in initializers</title>
196
+
<programlisting role="php">
197
+
<![CDATA[
198
+
<?php
199
+

200
+
// All allowed:
201
+
static $x = new Foo;
202
+

203
+
const C = new Foo;
204
+
205
+
function test($param = new Foo) {}
206
+
207
+
#[AnAttribute(new Foo)]
208
+
class Test {
209
+
public function __construct(
210
+
public $prop = new Foo,
211
+
) {}
212
+
}
213
+

214
+
// All not allowed (compile-time error):
215
+
function test(
216
+
$a = new (CLASS_NAME_CONSTANT)(), // dynamic class name
217
+
$b = new class {}, // anonymous class
218
+
$c = new A(...[]), // argument unpacking
219
+
$d = new B($abc), // unsupported constant expression
220
+
) {}
221
+
?>
222
+
]]>
223
+
</programlisting>
224
+
</example>
225
+
</sect3>
226
+
227
+
<sect3 xml:id="language.oop5.decon.constructor.static">
228
+
<title>Static creation methods</title>
229
+
<para>
230
+
PHP only supports a single constructor per class. In some cases, however, it may be
231
+
desirable to allow an object to be constructed in different ways with different inputs.
232
+
The recommended way to do so is by using static methods as constructor wrappers.
233
+
</para>
234
+
<example>
235
+
<title>Using static creation methods</title>
236
+
<programlisting role="php">
237
+
<![CDATA[
238
+
<?php
239
+
class Product {
240
+

241
+
private ?int $id;
242
+
private ?string $name;
243
+

244
+
private function __construct(?int $id = null, ?string $name = null) {
245
+
$this->id = $id;
246
+
$this->name = $name;
247
+
}
248
+

249
+
public static function fromBasicData(int $id, string $name): static {
250
+
$new = new static($id, $name);
251
+
return $new;
252
+
}
253
+

254
+
public static function fromJson(string $json): static {
255
+
$data = json_decode($json, true);
256
+
return new static($data['id'], $data['name']);
257
+
}
258
+

259
+
public static function fromXml(string $xml): static {
260
+
// Custom logic here.
261
+
$data = convert_xml_to_array($xml);
262
+
$new = new static();
263
+
$new->id = $data['id'];
264
+
$new->name = $data['name'];
265
+
return $new;
266
+
}
267
+
}
101
268

269
+
$p1 = Product::fromBasicData(5, 'Widget');
270
+
$p2 = Product::fromJson($some_json_string);
271
+
$p3 = Product::fromXml($some_xml_string);
272
+
]]>
273
+
</programlisting>
274
+
</example>
275
+
<para>
276
+
The constructor may be made private or protected to prevent it from being called externally.
277
+
If so, only a static method will be able to instantiate the class. Because they are in the
278
+
same class definition they have access to private methods, even if not of the same object
279
+
instance. The private constructor is optional and may or may not make sense depending on
280
+
the use case.
281
+
</para>
282
+
<para>
283
+
The three public static methods then demonstrate different ways of instantiating the object.
284
+
</para>
285
+
<simplelist>
286
+
<member><code>fromBasicData()</code> takes the exact parameters that are needed, then creates the
287
+
object by calling the constructor and returning the result.</member>
288
+
<member><code>fromJson()</code> accepts a JSON string and does some pre-processing on it itself
289
+
to convert it into the format desired by the constructor. It then returns the new object.</member>
290
+
<member><code>fromXml()</code> accepts an XML string, preprocesses it, and then creates a bare
291
+
object. The constructor is still called, but as all of the parameters are optional the method
292
+
skips them. It then assigns values to the object properties directly before returning the result.</member>
293
+
</simplelist>
294
+
<para>
295
+
In all three cases, the <code>static</code> keyword is translated into the name of the class the code is in.
296
+
In this case, <code>Product</code>.
297
+
</para>
298
+
</sect3>
299
+
</sect2>
102
300
<sect2 xml:id="language.oop5.decon.destructor">
103
301
<title>Destructor</title>
104
302
<methodsynopsis xml:id="object.destruct">
...
...
@@ -106,7 +304,7 @@ class Bar {
106
304
<void />
107
305
</methodsynopsis>
108
306
<para>
109
-
PHP 5 introduces a destructor concept similar to that of other
307
+
PHP possesses a destructor concept similar to that of other
110
308
object-oriented languages, such as C++. The destructor method will be
111
309
called as soon as there are no other references to a particular object,
112
310
or in any order during the shutdown sequence.
...
...
@@ -116,19 +314,20 @@ class Bar {
116
314
<programlisting role="php">
117
315
<![CDATA[
118
316
<?php
119
-
class MyDestructableClass {
120
-
function __construct() {
121
-
print "In constructor\n";
122
-
$this->name = "MyDestructableClass";
123
-
}
124
-

125
-
function __destruct() {
126
-
print "Destroying " . $this->name . "\n";
127
-
}
317
+

318
+
class MyDestructableClass
319
+
{
320
+
function __construct() {
321
+
print "In constructor\n";
322
+
}
323
+

324
+
function __destruct() {
325
+
print "Destroying " . __CLASS__ . "\n";
326
+
}
128
327
}
129
328

130
329
$obj = new MyDestructableClass();
131
-
?>
330
+
132
331
]]>
133
332
</programlisting>
134
333
</example>
...
...
@@ -160,7 +359,6 @@ $obj = new MyDestructableClass();
160
359
</sect2>
161
360

162
361
</sect1>
163
-

164
362
<!-- Keep this comment at the end of the file
165
363
Local variables:
166
364
mode: sgml
167
365