Skip to content

Commit 16f66c0

Browse files
Crellcmb69
andauthored
Asymmetric Visibility and Final properties (#3828)
* Document asymmetric property visibility. * Document the change in implicit visibility for readonly. * Document final properties. * Note that isPublic/Protected/Private is only in reference to the main visibility. * Use more note tags. * Remove unnecessary para tags. Co-authored-by: Christoph M. Becker <cmbecker69@gmx.de>
1 parent 04d9ade commit 16f66c0

File tree

6 files changed

+183
-31
lines changed

6 files changed

+183
-31
lines changed

language/oop5/final.xml

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<sect1 xml:id="language.oop5.final" xmlns="http://docbook.org/ns/docbook">
44
<title>Final Keyword</title>
55
<para>
6-
The final keyword prevents child classes from overriding a method or constant by
6+
The final keyword prevents child classes from overriding a method, property, or constant by
77
prefixing the definition with <literal>final</literal>. If the class
88
itself is being defined final then it cannot be extended.
99
</para>
@@ -59,10 +59,26 @@ class ChildClass extends BaseClass {
5959
</programlisting>
6060
</example>
6161
</para>
62-
<para>
63-
<example xml:id="language.oop5.final.example.php81">
64-
<title>Final constants example as of PHP 8.1.0</title>
65-
<programlisting role="php">
62+
<example>
63+
<title>Final property example as of PHP 8.4.0</title>
64+
<programlisting role="php">
65+
<![CDATA[
66+
<?php
67+
class BaseClass {
68+
final protected string $test;
69+
}
70+
71+
class ChildClass extends BaseClass {
72+
public string $test;
73+
}
74+
// Results in Fatal error: Cannot override final property BaseClass::$test
75+
?>
76+
]]>
77+
</programlisting>
78+
</example>
79+
<example xml:id="language.oop5.final.example.php81">
80+
<title>Final constants example as of PHP 8.1.0</title>
81+
<programlisting role="php">
6682
<![CDATA[
6783
<?php
6884
class Foo
@@ -78,16 +94,17 @@ class Bar extends Foo
7894
// Fatal error: Bar::X cannot override final constant Foo::X
7995
?>
8096
]]>
81-
</programlisting>
82-
</example>
83-
</para>
97+
</programlisting>
98+
</example>
8499

85100
<note>
86101
<simpara>
87-
Properties cannot be declared final: only classes, methods, and constants (as of PHP 8.1.0) may be declared as final.
102+
As of PHP 8.0.0, private methods may not be declared final except for the <link linkend="language.oop5.decon.constructor">constructor</link>.
88103
</simpara>
104+
</note>
105+
<note>
89106
<simpara>
90-
As of PHP 8.0.0, private methods may not be declared final except for the constructor.
107+
A property that is declared <link linkend="language.oop5.visibility-members-aviz"><literal>private(set)</literal></link> is implicitly <literal>final</literal>.
91108
</simpara>
92109
</note>
93110
</sect1>

language/oop5/properties.xml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,13 @@ Fatal error: Uncaught Error: Typed property Shape::$numberOfSides must not be ac
192192
<sect2 xml:id="language.oop5.properties.readonly-properties">
193193
<title>Readonly properties</title>
194194
<para>
195-
As of PHP 8.1.0, a property can be declared with the <code>readonly</code> modifier, which prevents modification of the property after initialization.
195+
As of PHP 8.1.0, a property can be declared with the <literal>readonly</literal> modifier,
196+
which prevents modification of the property after initialization. Prior to PHP 8.4.0
197+
a <literal>readonly</literal> property is implicitly private-set, and may only be written to
198+
from the same class. As of PHP 8.4.0, <literal>readonly</literal> properties are implicitly
199+
<link linkend="language.oop5.visibility-members-aviz"><literal>protected(set)</literal></link>,
200+
so may be set from child classes. That may be overridden
201+
explicitly if desired.
196202
<example>
197203
<title>Example of readonly properties</title>
198204
<programlisting role="php">

language/oop5/visibility.xml

Lines changed: 133 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,9 @@
2020
protected. Properties declared without any explicit visibility
2121
keyword are defined as public.
2222
</para>
23-
<para>
24-
<example>
25-
<title>Property declaration</title>
26-
<programlisting role="php">
23+
<example>
24+
<title>Property declaration</title>
25+
<programlisting role="php">
2726
<![CDATA[
2827
<?php
2928
/**
@@ -74,10 +73,129 @@ echo $obj2->private; // Undefined
7473
$obj2->printHello(); // Shows Public2, Protected2, Undefined
7574
7675
?>
76+
]]>
77+
</programlisting>
78+
</example>
79+
<sect3 xml:id="language.oop5.visibility-members-aviz">
80+
<title>Asymmetric Property Visibility</title>
81+
<para>
82+
As of PHP 8.4, properties may also have their
83+
visibility set asymmetrically, with a different scope for
84+
reading (<literal>get</literal>) and writing (<literal>set</literal>).
85+
Specifically, the <literal>set</literal> visibility may be
86+
specified separately, provided it is not more permissive than the
87+
default visibility.
88+
</para>
89+
<example>
90+
<title>Asymmetric Property visibility</title>
91+
<programlisting role="php">
92+
<![CDATA[
93+
<?php
94+
class Book
95+
{
96+
public function __construct(
97+
public private(set) string $title,
98+
public protected(set) string $author,
99+
protected private(set) int $pubYear,
100+
) {}
101+
}
102+
103+
class SpecialBook extends Book
104+
{
105+
public function update(string $author, int $year): void
106+
{
107+
$this->author = $author; // OK
108+
$this->pubYear = $year; // Fatal Error
109+
}
110+
}
111+
112+
$b = new Book('How to PHP', 'Peter H. Peterson', 2024);
113+
114+
echo $b->title; // Works
115+
echo $b->author; // Works
116+
echo $b->pubYear; // Fatal Error
117+
118+
$b->title = 'How not to PHP'; // Fatal Error
119+
$b->author = 'Pedro H. Peterson'; // Fatal Error
120+
$b->pubYear = 2023; // Fatal Error
121+
?>
77122
]]>
78123
</programlisting>
79124
</example>
80-
</para>
125+
<para>There are a few caveats regarding asymmetric visibility:</para>
126+
<itemizedlist>
127+
<listitem>
128+
<simpara>
129+
Only typed properties may have a separate <literal>set</literal> visibility.
130+
</simpara>
131+
</listitem>
132+
<listitem>
133+
<simpara>
134+
The <literal>set</literal> visibility must be the same
135+
as <literal>get</literal> or more restrictive. That is,
136+
<code>public protected(set)</code> and <code>protected protected(set)</code>
137+
are allowed, but <code>protected public(set)</code> will cause a syntax error.
138+
</simpara>
139+
</listitem>
140+
<listitem>
141+
<simpara>
142+
If a property is <literal>public</literal>, then the main visibility may be
143+
omitted. That is, <code>public private(set)</code> and <code>private(set)</code>
144+
will have the same result.
145+
</simpara>
146+
</listitem>
147+
<listitem>
148+
<simpara>
149+
A property with <literal>private(set)</literal> visibility
150+
is automatically <literal>final</literal>, and may not be redeclared in a child class.
151+
</simpara>
152+
</listitem>
153+
<listitem>
154+
<simpara>
155+
Obtaining a reference to a property follows <literal>set</literal> visibility, not <literal>get</literal>.
156+
That is because a reference may be used to modify the property value.
157+
</simpara>
158+
</listitem>
159+
<listitem>
160+
<simpara>
161+
Similarly, trying to write to an array property involves both a <literal>get</literal> and
162+
<literal>set</literal> operation internally, and therefore will follow the <literal>set</literal>
163+
visibility, as that is always the more restrictive.
164+
</simpara>
165+
</listitem>
166+
</itemizedlist>
167+
<para>
168+
When a class extends another, the child class may redefine
169+
any property that is not <literal>final</literal>. When doing so,
170+
it may widen either the main visibility or the <literal>set</literal>
171+
visibility, provided that the new visibility is the same or wider
172+
than the parent class. However, be aware that if a <literal>private</literal>
173+
property is overridden, it does not actually change the parent's property
174+
but creates a new property with a different internal name.
175+
</para>
176+
<example>
177+
<title>Asymmetric Property inheritance</title>
178+
<programlisting role="php">
179+
<![CDATA[
180+
<?php
181+
class Book
182+
{
183+
protected string $title;
184+
public protected(set) string $author;
185+
protected private(set) int $pubYear;
186+
}
187+
188+
class SpecialBook extends Book
189+
{
190+
public protected(set) $title; // OK, as reading is wider and writing the same.
191+
public string $author; // OK, as reading is the same and writing is wider.
192+
public protected(set) int $pubYear; // Fatal Error. private(set) properties are final.
193+
}
194+
?>
195+
]]>
196+
</programlisting>
197+
</example>
198+
</sect3>
81199
</sect2>
82200

83201
<sect2 xml:id="language.oop5.visiblity-methods">
@@ -87,10 +205,9 @@ $obj2->printHello(); // Shows Public2, Protected2, Undefined
87205
protected. Methods declared without any explicit visibility
88206
keyword are defined as public.
89207
</para>
90-
<para>
91-
<example>
92-
<title>Method Declaration</title>
93-
<programlisting role="php">
208+
<example>
209+
<title>Method Declaration</title>
210+
<programlisting role="php">
94211
<![CDATA[
95212
<?php
96213
/**
@@ -176,9 +293,8 @@ $myFoo->test(); // Bar::testPrivate
176293
// Foo::testPublic
177294
?>
178295
]]>
179-
</programlisting>
180-
</example>
181-
</para>
296+
</programlisting>
297+
</example>
182298
</sect2>
183299

184300
<sect2 xml:id="language.oop5.visiblity-constants">
@@ -188,10 +304,9 @@ $myFoo->test(); // Bar::testPrivate
188304
protected. Constants declared without any explicit visibility
189305
keyword are defined as public.
190306
</para>
191-
<para>
192-
<example>
193-
<title>Constant Declaration as of PHP 7.1.0</title>
194-
<programlisting role="php">
307+
<example>
308+
<title>Constant Declaration as of PHP 7.1.0</title>
309+
<programlisting role="php">
195310
<![CDATA[
196311
<?php
197312
/**
@@ -242,9 +357,8 @@ echo MyClass2::MY_PUBLIC; // Works
242357
$myclass2->foo2(); // Public and Protected work, not Private
243358
?>
244359
]]>
245-
</programlisting>
246-
</example>
247-
</para>
360+
</programlisting>
361+
</example>
248362
</sect2>
249363

250364
<sect2 xml:id="language.oop5.visibility-other-objects">

reference/reflection/reflectionproperty/isprivate.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@
2727
<para>
2828
&true; if the property is private, &false; otherwise.
2929
</para>
30+
<note>
31+
<simpara>
32+
Note this refers only to the main visibility, and not to a <link linkend="language.oop5.visibility-members-aviz">set-visibility</link>, if specified.
33+
</simpara>
34+
</note>
3035
</refsect1>
3136

3237
<refsect1 role="seealso">

reference/reflection/reflectionproperty/isprotected.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@
2727
<para>
2828
&true; if the property is protected, &false; otherwise.
2929
</para>
30+
<note>
31+
<simpara>
32+
Note this refers only to the main visibility, and not to a <link linkend="language.oop5.visibility-members-aviz">set-visibility</link>, if specified.
33+
</simpara>
34+
</note>
3035
</refsect1>
3136

3237
<refsect1 role="seealso">

reference/reflection/reflectionproperty/ispublic.xml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,13 @@
2525
<refsect1 role="returnvalues">
2626
&reftitle.returnvalues;
2727
<para>
28-
&true; if the property is public, &false; otherwise.
28+
&true; if the property is marked public, &false; otherwise.
2929
</para>
30+
<note>
31+
<simpara>
32+
Note this refers only to the main visibility, and not to a <link linkend="language.oop5.visibility-members-aviz">set-visibility</link>, if specified.
33+
</simpara>
34+
</note>
3035
</refsect1>
3136

3237
<refsect1 role="seealso">

0 commit comments

Comments
 (0)