> Le 22 janv. 2026 à 16:33, Nicolas Grekas <[email protected]> a 
> écrit :
> 
> Dear all,
> 
> Here is a new RFC for you to consider:
> https://wiki.php.net/rfc/promoted_readonly_constructor_reassign
> 
> As a quick intro, my motivation for that RFC is that I find it quite annoying 
> that readonly properties play badly with CPP (constructor property promotion).
> 
> Doing simple processing of any argument before assigning it to a readonly 
> property forces opting out of CPP.
> 
> This RFC would allow setting once a readonly property in the body of a 
> constructor after the property was previously (and implicitly) set using CPP.
> 
> This allows keeping property declarations in their compact form while still 
> enabling validation, normalization, or conditional initialization.
> 
> Cheers,
> Nicolas


Hi,

I am reserved about the proposal, because this style of using CPP and 
processing the value after the fact tends to favour brevity at the expense of 
precision and clarity. Let’s illustrate that with two examples from the RFC. 
First:

```php
class Config {
    public function __construct(
        public readonly ?string $cacheDir = null,
    ) {
        $this->cacheDir ??= sys_get_temp_dir() . '/app_cache';
    }
}
```

As of today you can write:

```php
class Config {
    public readonly string $cacheDir;

    public function __construct(
        ?string $cacheDir = null,
    ) {
        $this->cacheDir = $cacheDir ??= sys_get_temp_dir() . '/app_cache';
    }
}
```

Note that the property is marked as non-nullable, a precision that may be 
useful for both programmers and static analysers. With your proposal, there is 
no way to keep this information.


The second example is similar:

```php
class User {
    public function __construct(
        public readonly string $email,
    ) {
        if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
            throw new InvalidArgumentException('Invalid email');
        }
        $this->email = strtolower($email);  // Normalize
    }
}
```

As of today, it can be written as:

```php
class User {

    /** @var non-empty-string & lowercase-string */
    public readonly string $email;

    public function __construct(
        string $email,
    ) {
        if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
            throw new InvalidArgumentException('Invalid email');
        }
        $this->email = strtolower($email);  // Normalize
    }
}
```

With your proposal, there is no obvious way to keep the additional information 
provided in the phpdoc. Maybe we could imagine something like that:

```php
class User {
    /** 
     * @param string $email the e-mail address as provided
     */
    public function __construct(
        /** @var non-empty-string & lowercase-string the normalised e-mail 
address */
        string $email,
    ) {
        if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
            throw new InvalidArgumentException('Invalid email');
        }
        $this->email = strtolower($email);  // Normalize
    }
}
```

but it is obviously clearer (at least to my eyes) to keep the property and the 
constructor parameter separate.

One additional thought: Readonly properties carry a constraint that is annoying 
at first, but that is finally beneficial for the clarity of code that is 
written. When initialising such a property with something more complex than 
what can be comfortably written in a single expression, I am forced to write 
the intermediate results in a temporary variable and to assign the final value 
to the property at the end of the process, instead of transforming gradually 
the value of the property. The resulting code is a few lines longer, but it is 
no less clear, even it is often clearer, because it is obvious that this 
specific assignment supplies the final value of the property, and there is no 
need to look further down to see whether the value will undergo some additional 
transformations. As of today, this “final assignment” may be part of the 
constructor signature; with this RFC implemented, one can no longer know at a 
glance whether this assignment is “final”.

(Also I sympathise with Larry: rigid coding styles and static analysers’ 
promoted “good practices” add problematic limitations that are not part of the 
semantics of language. I prefer disabling checks in PHPStan rather than 
downgrading to non-safe mutable properties and/or writing getters around them.)

—Claude

Reply via email to