Skip to content

Commit

Permalink
Hooks documentation (#4094)
Browse files Browse the repository at this point in the history
* Add basic Hooks syntax.

* Document the __PROPERTY__ magic constant.

* Virtual properties.

* Document inheritance.

* Note incompatibility with readonly.

* Interface properties.

* Abstract properties.

* Discuss property variance.

* Add serialization section.

---------

Co-authored-by: Gina Peter Banyard <girgias@php.net>
  • Loading branch information
Crell and Girgias authored Nov 20, 2024
1 parent 550b9c3 commit f9dabde
Show file tree
Hide file tree
Showing 6 changed files with 730 additions and 9 deletions.
6 changes: 6 additions & 0 deletions language/constants.xml
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,12 @@ echo ANIMALS[1]; // outputs "cat"
The class method name.
</entry>
</row>
<row xml:id="constant.property">
<entry><constant>__PROPERTY__</constant></entry>
<entry>
Only valid inside a <link linkend="language.oop5.property-hooks">property hook</link>. It is equal to the name of the property.
</entry>
</row>
<row xml:id="constant.namespace">
<entry><constant>__NAMESPACE__</constant></entry>
<entry>
Expand Down
1 change: 1 addition & 0 deletions language/oop5.xml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

&language.oop5.basic;
&language.oop5.properties;
&language.oop5.property-hooks;
&language.oop5.constants;
&language.oop5.autoload;
&language.oop5.decon;
Expand Down
84 changes: 78 additions & 6 deletions language/oop5/abstract.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@
<title>Class Abstraction</title>

<para>
PHP has abstract classes and methods.
PHP has abstract classes, methods, and properties.
Classes defined as abstract cannot be instantiated, and any class that
contains at least one abstract method must also be abstract.
Methods defined as abstract simply declare the method's signature;
they cannot define the implementation.
contains at least one abstract method or property must also be abstract.
Methods defined as abstract simply declare the method's signature and whether it is public or protected;
they cannot define the implementation. Properties defined as abstract
may declare a requirement for <literal>get</literal> or <literal>set</literal>
behavior, and may provide an implementation for one, but not both, operations.
</para>

<para>
Expand All @@ -19,8 +21,18 @@
<link linkend="language.oop.lsp">signature compatibility</link> rules.
</para>

<para>
As of PHP 8.4, an abstract class may declare an abstract property, either public or protected.
A protected abstract property may be satisfied by a property that is readable/writeable from either
protected or public scope.
</para>
<para>
An abstract property may be satisfied either by a standard property or by a property
with defined <link linkend="language.oop5.property-hooks">hooks</link>, corresponding to the required operation.
</para>

<example>
<title>Abstract class example</title>
<title>Abstract method example</title>
<programlisting role="php">
<![CDATA[
<?php
Expand Down Expand Up @@ -80,7 +92,7 @@ FOO_ConcreteClass2
</example>

<example>
<title>Abstract class example</title>
<title>Abstract method example</title>
<programlisting role="php">
<![CDATA[
<?php
Expand Down Expand Up @@ -121,6 +133,66 @@ Mrs. Pacwoman
]]>
</screen>
</example>
<example>
<title>Abstract property example</title>
<programlisting role="php">
<![CDATA[
<?php
abstract class A
{
// Extending classes must have a publicly-gettable property.
abstract public string $readable { get; }
// Extending classes must have a protected- or public-writeable property.
abstract protected string $writeable { set; }
// Extending classes must have a protected or public symmetric property.
abstract protected string $both { get; set; }
}
class C extends A
{
// This satisfies the requirement and also makes it settable, which is valid.
public string $readable;
// This would NOT satisfy the requirement, as it is not publicly readable.
protected string $readable;
// This satisfies the requirement exactly, so is sufficient.
// It may only be written to, and only from protected scope.
protected string $writeable {
set => $value;
}
// This expands the visibility from protected to public, which is fine.
public string $both;
}
?>
]]>
</programlisting>
</example>
<para>
An abstract property on an abstract class may provide implementations for any hook,
but must have either <literal>get</literal> or <literal>set</literal> declared but not defined (as in the example above).
</para>
<example>
<title>Abstract property example</title>
<programlisting role="php">
<![CDATA[
<?php
abstract class A
{
// This provides a default (but overridable) set implementation,
// and requires child classes to provide a get implementation.
abstract public string $foo {
get;
set { $this->foo = $value };
}
}
?>
]]>
</programlisting>
</example>
</sect1>
<!-- Keep this comment at the end of the file
Local variables:
Expand Down
81 changes: 79 additions & 2 deletions language/oop5/interfaces.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
<sect1 xml:id="language.oop5.interfaces" xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Object Interfaces</title>
<para>
Object interfaces allow you to create code which specifies which methods a
class must implement, without having to define how these methods are
Object interfaces allow you to create code which specifies which methods and properties a
class must implement, without having to define how these methods or properties are
implemented. Interfaces share a namespace with classes and traits, so they may
not use the same name.
</para>
Expand Down Expand Up @@ -89,6 +89,83 @@
Prior to PHP 8.1.0, they cannot be overridden by a class/interface that inherits them.
</para>
</sect2>
<sect2 xml:id="language.oop5.interfaces.properties">
<title>Properties</title>
<para>
As of PHP 8.4.0, interfaces may also declare properties. If they do, the declaration must specify if the
property is to be readable, writeable, or both. The interface declaration applies only to public read
and write access.
</para>
<para>
An class may satisfy an interface property in multiple ways. It may define a public property. It may
define a public <link linkend="language.oop5.property-hooks.virtual">virtual property</link> that implements
only the corresponding hook. Or a read property may be satisfied by a <literal>readonly</literal> property.
However, an interface property that is settable may not be <literal>readonly</literal>.
</para>
<example>
<title>Interface properties example</title>
<programlisting role="php">
<![CDATA[
<?php
interface I
{
// An implementing class MUST have a publicly-readable property,
// but whether or not it's publicly settable is unrestricted.
public string $readable { get; }
// An implementing class MUST have a publicly-writeable property,
// but whether or not it's publicly readable is unrestricted.
public string $writeable { set; }
// An implementing class MUST have a property that is both publicly
// readable and publicly writeable.
public string $both { get; set; }
}
// This class implements all three properties as traditional, un-hooked
// properties. That's entirely valid.
class C1 implements I
{
public string $readable;
public string $writeable;
public string $both;
}
// This class implements all three properties using just the hooks
// that are requested. This is also entirely valid.
class C2 implements I
{
private string $written = '';
private string $all = '';
// Uses only a get hook to create a virtual property.
// This satisfies the "public get" requirement.
// It is not writeable, but that is not required by the interface.
public string $readable { get => strtoupper($this->writeable); }
// The interface only requires the property be settable,
// but also including get operations is entirely valid.
// This example creates a virtual property, which is fine.
public string $writeable {
get => $this->written;
set => $value;
}
// This property requires both read and write be possible,
// so we need to either implement both, or allow it to have
// the default behavior.
public string $both {
get => $this->all;
set => strtoupper($value);
}
}
?>
]]>
</programlisting>
</example>
</sect2>
<sect2 xml:id="language.oop5.interfaces.examples">
&reftitle.examples;
<example xml:id="language.oop5.interfaces.examples.ex1">
Expand Down
Loading

0 comments on commit f9dabde

Please sign in to comment.