Hi Claude, Le mer. 28 janv. 2026 à 19:53, Claude Pache <[email protected]> a écrit :
> > > 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.) > > > Thank you for the thoughtful feedback. You raise valid points about type precision and PHPDoc annotations being harder to express with CPP. I've added a "Design Considerations" section to the RFC acknowledging these tradeoffs and clarifying when traditional declaration remains preferable (type narrowing, detailed annotations, complex initialization) vs. when CPP + reassignment fits well (simple transformations like trim/lowercase, validation with fallback). The key point is: this RFC adds an option, it doesn't mandate any style. If "final at declaration" clarity matters for a specific property, traditional declaration remains available. Regarding the "final assignment" concern: an earlier iteration considered restricting reassignment to only the constructor body (no other methods could reassign), but this was ruled out, at least for consistency with __clone(). Cheers, Nicolas
