[php-src] Issue #11488: null default-value promoted class parameter before required parameter is silently ignored
| From: | syranide | Date: | Tue, 20 Jun 2023 08:14:16 +0000 |
| Subject: | [php-src] Issue #11488: null default-value promoted class parameter before required parameter is silently ignored | ||
| Groups: | php.bugs | ||
| Request: | Send a blank email to php-bugs+get-244753@lists.php.net to get a copy of this message | ||
Issue: https://github.com/php/php-src/issues/11488
Author: syranide
### Description
The following code:
```php
<?php
class Foobar {
public function __construct(
public string|null $a = null,
public $b,
) {}
}
new Foobar(b: 'x');
```
Resulted in this output:
```
PHP Fatal error: Uncaught ArgumentCountError: Foobar::__construct(): Argument #1 ($a) not passed
```
But I expected this output instead:
```
Optional parameter $a declared before required parameter $b is implicitly treated as a required
parameter
```
To explain; $a does not end up being optional, and if you perform reflection on the parameter and/or
property, neither parameter/property has a default-value. So PHP is silently ignoring the
user-specified behavior.
### Further information
Normal functions and methods support "poor mans nullable types" for now, which makes the
above case a weird quirk of the backwards compatibility, as noted by @KapitanOczywisty in this post
https://github.com/php/php-src/issues/11485#issuecomment-1597779887.
However, constructors with parameter/property promotion behave in a stricter manner.
```php
class C1 {
public function __construct(
public string|null $a = null,
public $b,
) {}
}
class C2 {
public function __construct(
public string|null $a = "a",
public $b,
) {}
}
class C3 {
public function __construct(
public string $a = null,
public $b,
) {}
}
class C4 {
public function __construct(
public string $a = "a",
public $b,
) {}
}
class C5 {
public function __construct(
public $a = null,
public $b,
) {}
}
```
* C1 emits no error, but silently makes the parameter/property mandatory.
* C2 emits "Deprecated: Optional parameter $a declared before required parameter $b is
implicitly treated as a required parameter".
* C3 emits "Fatal error: Cannot use null as default value for parameter $a of type
string".
* C4 emits "Deprecated: Optional parameter $a declared before required parameter $b is
implicitly treated as a required parameter".
* C5 emits "Deprecated: Optional parameter $a declared before required parameter $b is
implicitly treated as a required parameter".
C3 proves that poor mans nullable types aren't intended to apply to constructors with typed
promoted parameters/properties like they do for normal parameters, and C5 proves that it's
deprecated any way for them. And C2 and C4 proves that optional parameters aren't allowed
before required parameters and emits a deprecation.
However, C1 emits no warning at all, even though parameter/property doesn't end up having a
null default-value, nor is it optional. So C1 behaves identically to not specifying a default-value
at all, whereas the only purpose of specifying the null default-value is to make it optional, which
it isn't.
So it definitely seems like C1 should emit a deprecation as well.
### PHP Version
PHP 8.2.7
### Operating System
_No response_