Typehint Conversion Caveat Explained
2014-Sep-21, Sunday 18:12![[personal profile]](https://www.dreamwidth.org/img/silk/identity/user.png)
I was highly amused by the reactions to my previous post on the programming and php subreddits. So I spent a little time to isolate the typehint conversion optional argument issue.
It’s not me, it’s PHP. And frankly, I don’t understand the point of the backtrace going to the trouble of making things references if doing things with those references isn’t even supported.
That bug description isn’t entirely accurate as to what’s going on, however, as some test cases show.
<?php
function f(int $a, int $b = null, int $c = null) {
error_log(json_encode([$a, $b, $c]));
error_log(json_encode(func_get_args()));
}
function changeArg($code, $str) {
preg_match('/^Argument (\\d+) /', $str, $m);
$i = $m[1]-1;
$bt = debug_backtrace(null, 2);
++$bt[1]['args'][$i];
return true;
}
set_error_handler('changeArg', E_ALL);
f(3, 2, 1);
Outputs:
[4,2,1]
[4,3,2]
Note that the required argument changes, but the optional argument does not. Also note that func_get_args() returns the altered argument, in disagreement with the actual argument.
Where it gets interesting is if we alter that $i to be $i+1. That is, if we change the argument after the one we got an error about.
<?php
function f(int $a, int $b = null, int $c = null) {
error_log(json_encode([$a, $b, $c]));
error_log(json_encode(func_get_args()));
}
function changeArg($code, $str) {
preg_match('/^Argument (\\d+) /', $str, $m);
$i = $m[1]-1;
$bt = debug_backtrace(null, 2);
if (count($bt[1]['args']) > $i+1) {
++$bt[1]['args'][$i+1];
}
return true;
}
set_error_handler('changeArg', E_ALL);
f(3, 2, 1);
Outputs:
[3,3,2]
[3,3,2]
Then the change sticks, and the optional argument is modified.
By switching to $i-1, you’ll notice that even the required argument fails to be modified. Which means in the default case where one tries to modify the argument an error was triggered about, the argument’s reference is being broken slightly earlier for optional arguments than for required arguments. That, to me, strongly suggests a bug, because the behavior is inconsistent.
But my use-case isn’t supported, and presumably it doesn’t affect any other use-cases, so I’ll either have to live with it or write a code pre-processor which fixes up the issue. Ah well.
I'm left confused about why there exists such a thing as continuable errors if fixing the error and continuing on isn't supported, but ... PHP isn't exactly known for it's strong sense of feature coherency.