This article is part of the CakeDC Advent Calendar 2024 (December 13th 2024)
In this article we'll explore some the new features of the recently released PHP 8.4 version.
Every year in the second half of autumn a new version of PHP, on which our beloved CakePHP is based, is released. This time it is the major version 8.4, and it adds many exciting features and improvements.
Among the many new features and improvements, In this article, we will cover these two interesting added functionalities.:
- Property hooks
- Asymmetric Visibility
Property hooks
Property hooks also known as property accessors are a way to intercept and override the way properties are read and written. This new functionality significantly reduces the amount of boilerplate code by allowing you to skip separate getter and setter methods.
To use property hooks use get
and set
hooks
class ClassWithPropertyHooks
{
public string $first_name {
get => $this->formatNamePart($this->first_name);
set (string $first_name) => trim($first_name);
}
public string $last_name {
get => $this->formatNamePart($this->last_name);
set (string $last_name) => trim($last_name);
}
private function formatNamePart(string $namePart): string
{
return ucfirst(strtolower($namePart));
}
}
$obj = new ClassWithPropertyHooks();
$obj->first_name = 'ADAM';
echo $obj->first_name; // prints Adam;
$obj->last_name = 'RUSINOWSKI';
echo $obj->last_name; // prints Rusinowski
Hooks are placed in {}
right after the property declaration, inside you can define both hooks,
it is also allowed to define only one, get
or set
.
Each hook can have a body enclosed in {}
or if the hook is a single expression, arrow expression can be used.
Set Hook can optionally define a name and type of the incoming value, this type must be the same or covariant with the type of the property.
All hooks operate in the scope of the object, you can use any public, protected or private method or property inside the body of a hook.
Hooks allow you to create virtual properties. Virtual properties are properties that do not have a backed value and
no hook directly refers to the value of the property. Instead, the value when read may be the result of some processing
or combination of other properties. Virtual properties do not occupy the object's memory and if the set
hook is
undefined, they cause an error.
An example below presents the usage of a virtual property $full_name
and the usage of the object method
formatNamePart
in the body of the hooks:
class ClassWithPropertyHooks
{
public string $first_name {
final get => $this->formatNamePart($this->first_name);
final set (string $first_name) => trim($first_name);
}
public string $last_name {
get => $this->formatNamePart($this->last_name);
set (string $last_name) => trim($last_name);
}
public ?string $full_name {
get {
if ($this->first_name || $this->last_name) {
return trim("$this->first_name $this->last_name");
}
return null;
}
}
private function formatNamePart(string $namePart): string
{
return ucfirst(strtolower($namePart));
}
}
$obj = new ClassWithPropertyHooks();
$obj->first_name = 'ADAM';
$obj->last_name = 'rusinowski';
echo $obj->full_name; // prints Adam Rusinowski;
$obj->full_name = 'Adam Rusinowski'; // this will cause error since the set hook is not defined
Hooks can be made final so they may not be overridden in child class.
class ClassWithPropertyHooks
{
public string $first_name {
get => $this->formatNamePart($this->first_name);
final set (string $first_name) => trim($first_name);
}
private function formatNamePart(string $namePart): string
{
return ucfirst(strtolower($namePart));
}
}
class ChildClassWithPropertyHooks extends ClassWithPropertyHooks
{
public string $first_name {
get => trim($this->first_name);
set (string $first_name) => strtolower($first_name); // this is not allowed
}
}
If you want to have access to the parent hook you can use the syntax
parent::$propertyName::get()
or parent::$property::set()
inside the hook in the
child class.
class ClassWithPropertyHooks
{
public string $first_name {
get => $this->formatNamePart($this->first_name);
set (string $first_name) => trim($first_name);
}
private function formatNamePart(string $namePart): string
{
return ucfirst(strtolower($namePart));
}
}
class ChildClassWithPropertyHooks extends ClassWithPropertyHooks
{
public string $first_name {
get => trim($this->first_name);
set (string $first_name) => parent::$first_name::set(strtolower(%$first_name));
}
}
Properties with property hooks cannot be marked as readonly
, so to limit their modification you can use the
asymmetric visibility feature.
Asymmetric Visibility
With Asymmetric Visibility you can control the scope of writing and reading properties independently. This reduces the amount of boilerplate code when you want to prohibit modification of property values from outside the class.
class ExampleClass
{
public private(set) int $counter = 0;
public function increaseCounter(): void
{
++$this->counter;
}
}
$exampleClass = new ExampleClass();
echo $exampleClass->counter; // prints 0;
$exampleClass->increaseCounter();
echo $exampleClass->counter; // prints 1;
$exampleClass->counter = 5; // this is not allowed
Asymmetric Visibility is subject to certain rules:
- only properties with a defined type can have separate visibility for the
set
hook - the visibility of the
set
hook must be the same as the visibility of theget
hook or more restrictive - getting a reference to a property will use the
set
visibility because the reference can change the value of the property - writing to an array property includes the
get
andset
operations internally so theset
visibility will be used
When using inheritance, remember that the child class can change the visibility of set
and get
but if the
visibility of set
or get
is private
in the parent class, changing it to something else in the child class
will result in a Fatal Error.
Conclusion
The above features greatly extend the capabilities of the PHP language.
Property hooks and asymmetric visibility can be useful in value objects, among other things. They can successfully replace getters and setters, thus reducing the amount of boilerplate code in your applications.
In the next article, we will cover some more great improvements that the developer community has added to the new version of PHP.
This article is part of the CakeDC Advent Calendar 2024 (December 13th 2024)