Since version 8.1, PHP introduced "readonly" properties and since 8.2, PHP introduced "readonly" classes, but what are they, how do they work and why are they extremely useful for PHP programmers? We'll explore all this and more and learn how you can use read-only properties and classes in your next PHP object-oriented programming project.
What are read-only properties?
In PHP, it is now possible for web servers running 8.1 or later to use read-only properties. As the name suggests, in the context of object-oriented programming (OOP) they are properties that once set, cannot be modified again, and become read-only. This means that outside classes cannot change the set values of the properties, ensuring that their values don't and can't get modified directly.
Why are read-only properties and classes useful?
There are many reasons why developers choose to set properties and classes as read-only but the most common is encapsulation.
In the principles of OOP, encapsulation makes up an important and fundamental part that enforces the programming paradigm. By having read-only properties, the class controls access to its values and prevents outside code from changing them. This results in having better control over your objects by guaranteeing values remain constant and avoiding unintended side effects. Whilst encapsulation consists of more benefits such as data hiding, abstraction, and code maintainability, the process of protecting a class' properties helps give your code more modularity by making each class and its functions do one main core job and thus create immutable objects.
Another common area of read-only classes in PHP is API design. If you've ever created an API document or coded your own API server, you'll quickly come across areas with the public code base that you require immutability. This is an object which you wish its contents not to be altered. Within application programming interfaces it is common to make available to your user's setters and getters for your objects. By doing this you make your code easier to understand by others, and those using the API know what they can set and what they can't. Having read-only objects can also help the performance of your PHP application, as you know that for the lifetime of the object, its contents will not change, and therefore you can boost performance by caching various objects, as the value cannot be changed.
PHP developers also like to use read-only properties to help ensure thread safety within PHP. By having objects read-only, we can safely allow different threads access to the properties without the risk of concurrent modifications to it, including timing issues such as data races.
How to use readonly properties in PHP
Let's explore how to use readonly properties in PHP 8.1 and higher with code examples. To set a class property to read-only, you can use the "readonly" keyword in PHP. Since PHP 8.1 "readonly" is now a reserved keyword and can be now used like the below example. Remember this will only work in the context of OOP in PHP (where you are using a class of code).
public readonly string $email_address;
Next, let's explore how this looks inside a full-class object. In the PHP code example below we have a class called "Foo". It has one property of "name" which has been set to readonly. This means, on initialisation, the property will get set and then will be unable to be changed throughout its lifetime. Instead of using a "setter" function, we're setting the property on the class' construct (when you first initialise the class). Because it's of type string, we pass a string value to the class, in this case, "Bar". Next, if we echo out the property it returns "Bar".
class Foo {
public readonly string $name;
public function __construct(string $name) {
$this->name = $name;
}
}
$foo = new Foo("Bar");
# Outputs: 'Bar'
echo $foo->name;
However, if we attempt to set the property after initialisation, PHP will trigger a fatal error "Cannot modify readonly property". This is also true if we attempt to use PHP's unset function.
Fatal error: Uncaught Error: Cannot modify readonly property Foo::$name
class Foo {
public readonly string $name;
public function __construct(string $name) {
$this->name = $name;
}
}
$foo = new Foo("Bar");
# Triggers; Fatal error: Uncaught Error: Cannot modify readonly property Foo::$name
$foo->name = 'FooBar';
The same can happen if we create a setter function in the class and attempt to call it after the property has been instantiated.
public function setName() {
$this->name = 'Bar';
}
# Triggers; Fatal error: Uncaught Error: Cannot modify readonly property Foo::$name
$foo->name = 'FooBar';
Readonly properties are also protected once the class has been created, even if you don't set them. Let's take our original code example but this time remove the construct function. Without this function, we're unable to set the property "name" value. If we then attempt to set it, PHP will trigger a fatal error; "typed property Foo::$name must not be accessed before initialization". That's because we're attempting to access the property even before we've set it.
class Foo {
public readonly string $name;
}
$foo = new Foo();
# Triggers; Fatal error: Typed property Foo::$name must not be accessed before initialization
$foo->name = 'FooBar';
It might be tempting to create a setter method for the property if not created via the construct, however, because PHP works with scoped variables, PHP, in this case, will trigger a fatal error; "Cannot initialize readonly property Foot::$name from global scope".
class Foo {
public readonly string $name;
public function setName($name) {
$this->name = $name;
}
}
$foo = new Foo();
# Triggers; Fatal error: Uncaught Error: Cannot initialize readonly property Foo::$name from global scope
$foo->setName('FooBar');
How to use readonly classes in PHP
Since PHP 8.2 and higher it's possible to set the whole class as readonly. What this means in practice is that all properties within are readonly, not that the class is readonly and cannot be used. Let's explore an example. In the below example, we've added the PHP keyword readonly to the start of the class keyword to specify that this whole class has readonly properties. Because of this, we've dropped the readonly keyword from the individual properties. They work the same but means that any property found here is readonly.
readonly class Foo {
public string $name;
public string $email;
public function __construct(string $name) {
$this->name= $name;
}
}
$foo = new Foo('FooBar');
Conclusion
Using read-only properties and classes in PHP is a great way to protect class objects from external classes and code to ensure they remain and behave in a certain way. It's important to remember that attempting to change a read-only value will result in a fatal PHP error. Having good documentation and class and function PHPDoc will help others ensure they're aware of the class' attributes.