-
Notifications
You must be signed in to change notification settings - Fork 7.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Cannot set exception code for custom exception #14673
Comments
php-src/Zend/zend_exceptions.c Lines 323 to 326 in a2cecd2
The class MyException extends Exception {
public function __construct() {
$this->code = 5;
parent::__construct();
}
}
$myException = new MyException();
var_dump($myException->getCode()); Instead of redeclaring the class Example extends \Exception {
public function __construct(string $message = "", int $code = 5, ?Throwable $previous = null) {
parent::__construct($message, $code, $previous);
}
}
echo ' -> ', (new Example(''))->getCode(), \PHP_EOL;
echo '0 -> ', (new Example('', 0))->getCode(), \PHP_EOL;
echo '1 -> ', (new Example('', 1))->getCode(), \PHP_EOL; |
And that does not align with how all other classes in PHP work . Property values passed through constructor must always override default property values. |
That's not really how it works. PHP is implementation-defined. This behavior is intentional and has been the same for 21 years. So, I consider this a wontfix, but let's see what others think. |
I believe this behavior is not intentional. The documentation says that in this case the default arguments are ignored, but in practice it also ignores explicitly set argument when it's value is equal to zero, which is very confusing. // This should return zero for any exception classes.
(new Example(code: 0))->getCode(); |
For context: https://www.php.net/manual/en/exception.construct.php
Named arguments are relatively new. The default arguments refers to omitting the arguments, or providing the same value as the default explicitly. But again, PHP is implementation defined. When behavior and documentation diverge, behavior wins, unless it is obviously wrong. Even when it is obviously wrong, it's sometimes better to be cautious and not risk breaking old code, especially in patches. This could be discussed for master, but IMO is out of the question for stable versions. |
It has nothing to do with named arguments.
Well, that's not typical interpretation of "default arguments". In my opinion default arguments are those defined on the function signature, like
That's what I am thinking about it. It obviously wrong. |
To be very clear here, the behavior is totally intentional: if the $code parameter is non-zero then the constructor will set the property to that value. The internal code does something that translates to: function __construct(string $message = "", int $code = 0, ?Throwable $previous = null) {
if (func_num_args() >= 1) {
$this->message = $message;
}
if ($code) {
$this->code = $code;
}
if ($previous) {
$this->previous = $previous;
}
} There's a little magic for the $message, unfortunately, but the behavior for $code is simple and obviously deliberate. The implementation is the source of truth, so if the documentation doesn't accurately describe this behavior then it's the documentation that should be fixed. |
The current behavior for $message is what I expected for $code. It's intuitively clear and aligns with how all other PHP objects work. Why $message and $code have different behavior? <?php
class Example extends \Exception {
public $code = 5;
public $message = 'default';
}
$exception = new Example('', 0);
var_dump($exception->getMessage());
var_dump($exception->getCode());
I don't think that age of a bug should turn it into a feature. ) |
The code (which I suggest you look at, if you haven't yet) is based on whether $message is null - something only the internals can get away with since the signature is supposed to be But that's not really the point. The point is that the constructor follows an unusual pattern, and that pattern has been in place for a long time, so it's highly likely (especially since it's somewhat documented) that some developers have been making use of it. Changes to that shouldn't be made on a whim.
Just because you don't like the behavior doesn't mean it's a bug. Don't mistake me, I don't care for what it's doing either, and I also believe the constructor should work normally and anyone wishing to customize the message or code should do so by overriding the constructor. But me not liking the behavior either still doesn't mean it's a bug. |
Perhaps this should be added to the 8.4 deprecations/9.0 removal list? |
If you referer to that note on php.net that basically means that one of these parameters does not comply with this documentation. Which one depends on how you interpret the term "default arguments".
It's true. However, it's very confusing and it took me quite a lot time to figure out that this behavior comes from PHP internals not from my custom code. |
I understand your desire to "fix" this, but do you have any issues with the work-around? Formatted for short lines: class Example extends \Exception
{
public function __construct(
string $message = "",
int $code = 5,
?Throwable $previous = null
) {
parent::__construct($message, $code, $previous)
}
} |
Description
The following code:
Resulted in this output:
But I expected this output instead:
There is a note about default parameters in exception constructor on php.net. However, case 2 in the above example explicitly passes the code parameter to the constructor.
PHP Version
PHP 8.2
Operating System
Debian 11
The text was updated successfully, but these errors were encountered: