Home > Backend Development > PHP8 > body text

A big explanation of the new features of PHP8.1: readonly properties read-only properties

藏色散人
Release: 2023-02-17 12:12:01
Original
3566 people have browsed it

This article is translated, original address: https://stitcher.io/blog/php-81-readonly-properties

##PHP 8.1: Read-only properties

Over the years, writing data transfer objects and value objects has become very easy in PHP. Take the DTO in PHP 5.6 as an example:

class BlogData
{
    /** @var string */
    private $title;
    
    /** @var Status */
    private $status;
    
    /** @var \DateTimeImmutable|null */
    private $publishedAt;
   
   /**
    * @param string $title 
    * @param Status $status 
    * @param \DateTimeImmutable|null $publishedAt 
    */
    public function __construct(
        $title,
        $status,
        $publishedAt = null
    ) {
        $this->title = $title;
        $this->status = $status;
        $this->publishedAt = $publishedAt;
    }
    
    /**
     * @return string 
     */
    public function getTitle()
    {
        return $this->title;    
    }
    
    /**
     * @return Status 
     */
    public function getStatus() 
    {
        return $this->status;    
    }
    
    /**
     * @return \DateTimeImmutable|null 
     */
    public function getPublishedAt() 
    {
        return $this->publishedAt;    
    }
}
Copy after login

And compare it to the equivalent in PHP 8.0:

class BlogData
{
    public function __construct(
        private string $title,
        private Status $status,
        private ?DateTimeImmutable $publishedAt = null,
    ) {}
    
    public function getTitle(): string
    {
        return $this->title;    
    }
    
    public function getStatus(): Status 
    {
        return $this->status;    
    }
    
    public function getPublishedAt(): ?DateTimeImmutable
    {
        return $this->publishedAt;    
    }
}
Copy after login

It's quite different, although I think there's still a big problem: All these getters. Personally, I don't use them anymore since PHP 8.0 and its improved properties. I just prefer using public properties instead of adding getters:

class BlogData
{
    public function __construct(
        public string $title,
        public Status $status,
        public ?DateTimeImmutable $publishedAt = null,
    ) {}
}
Copy after login

Object-oriented purists don't like this approach: the internal state of an object should never be directly exposed, and definitely cannot be changed from the outside.

In our project at Spatie we have an internal style guide rule that DTOs and VOs with public properties should not be changed from the outside; a practice that seems to work well and we have been doing it for a long time It's been a while and I haven't had any problems.

However, yes; I agree it would be better if the language ensured that public properties were not overridden at all. Well, PHP 8.1 solved all these problems by introducing the readonly keyword:

class BlogData
{
    public function __construct(
        public readonly string $title,
        public readonly Status $status,
        public readonly ?DateTimeImmutable $publishedAt = null,
    ) {}
}
Copy after login

This keyword basically does what its name suggests: once a property is set, it can no longer be overridden. :

$blog = new BlogData(
    title: 'PHP 8.1: readonly properties', 
    status: Status::PUBLISHED, 
    publishedAt: now()
);
$blog->title = 'Another title';
Error: Cannot modify readonly property BlogData::$title
Copy after login

Knowing that when an object is constructed, it can never change again, provides a level of certainty and peace of mind when writing code: a series of unforeseen data changes simply won't happen again.

Of course, you still want to be able to copy the data to the new object, and possibly change some properties in the process. We'll discuss how to do this using read-only properties later in this article. First, let’s take a closer look at them.

Do you want to know more about PHP 8.1? There is a path to PHP 8.1. For the next 10 days, you will receive a daily email about a new and existing feature of PHP 8.1; you will then be automatically unsubscribed so you won't receive spam or follow-up emails. Subscribe now!

#Input properties only

Read-only properties can only be used in conjunction with typed properties:

class BlogData
{
    public readonly string $title;
    
    public readonly $mixed;
}
Copy after login

However, you can use it mixed as Type hint:

class BlogData
{
    public readonly string $title;
    
    public readonly mixed $mixed;
}
Copy after login

The reason for this restriction is that by omitting the property type, PHP will automatically set the property's value to null if no explicit value is provided in the constructor. This behavior, combined with readonly, can lead to unnecessary confusion.

#Normal and promoted properties

You have seen examples of both: readonly can be added on both normal and promoted properties:

class BlogData
{
    public readonly string $title;
    
    public function __construct(
        public readonly Status $status, 
    ) {}
}
Copy after login

#No default value

Read-only properties cannot have default values:

class BlogData
{
    public readonly string $title = 'Readonly properties';
}
Copy after login

That is, unless they are promoted properties:

class BlogData
{
    public function __construct(
        public readonly string $title = 'Readonly properties', 
    ) {}
}
Copy after login

It is allowed to promote properties because the default value of a promoted property is not used as the default value of the class property, but only applies to the parameters of the constructor. Behind the scenes, the above code will translate to:

class BlogData
{
    public readonly string $title;
    
    public function __construct(
        string $title = 'Readonly properties', 
    ) {
        $this->title = $title;
    }
}
Copy after login

You can see how the actual property is not assigned a default value. By the way, the reason why default values ​​for read-only properties are not allowed is that they are not any different from constants of that form.

#Legacy

Readonly flags are not allowed to be changed during inheritance:

class Foo
{
    public readonly int $prop;
}
class Bar extends Foo
{
    public int $prop;
}
Copy after login

This rule goes both ways: readonly is not allowed to be added during inheritance Or remove the flag.

#Unsetting not allowed

Once a read-only property is set, you cannot change it, or even unset it:

$foo = new Foo('value');
unset($foo->prop);
Copy after login

#Reflection

There is a new method, as well as a flag. ReflectionProperty::isReadOnly()ReflectionProperty::IS_READONLY

#Clone

So if you can't change read-only properties, and can't unset them, then how do you create Make a copy of a DTO or VO and change some of its data? You can't clone them because you won't be able to overwrite their values. There was actually an idea to clone with a construct that would allow this behavior in the future, but that wouldn't solve our problem now.

Well, if you rely on a little reflection magic, you can copy an object with a changed read-only property. By creating an object without calling its constructor (this can be done using reflection), and then by manually copying each property - sometimes overwriting its value - you can actually "clone" an object and change its read-only properties.

I made a little package to do this and it goes like this:

class BlogData
{
    use Cloneable;
    public function __construct(
        public readonly string $title,
    ) {}
}
$dataA = new BlogData('Title');
$dataB = $dataA->with(title: 'Another title');
Copy after login

I actually wrote a dedicated blog post explaining the mechanics behind all of this to you It can be read here.

So, that’s all about read-only properties. I think they're a great feature if you're working on a project that deals with a lot of DTOs and VOs and requires you to carefully manage the flow of data throughout your code. Immutable objects with read-only properties help a lot in this regard.

The above is the detailed content of A big explanation of the new features of PHP8.1: readonly properties read-only properties. For more information, please follow other related articles on the PHP Chinese website!

Related labels:
source:php.cn
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template