PHP: Неполадка несоответствия между вызовом метода отражения с использованием построенного массива или func_get_args() в версии 5.4
Это очень краткий случай в PHP 5.4 относительно передачи объектов по ссылке, где вы получите эту ошибку:
PHP Warning: Parameter 1 to A::foo() expected to be a reference, value given
Но только как составной эффект:
- Использование отражения для установки унаследованного метода как "доступного",
- И этот метод принимает явно ссылочный аргумент (& аргумент sig)
- И затем вызывать его с помощью func_get_args(), а не конструировать массив аргументов вручную.
Не знаю, почему все это вызывает такое поведение или если они должны.
Важно отметить, что этого эффекта нет в PHP 5.5.
Это код, который вызовет указанную выше ошибку, но если вы прокомментируете строку с помощью COMMENT THIS LINE
, код будет работать нормально (например, объект правильно передается функции foo):
class A {
private function foo(&$arg1) {
var_dump('arg1: ', $arg1);
}
}
class B extends A {
public function bar() {
$x = new stdClass();
$x->baz = 'just a value';
$this->callPrivate($x);
}
private function callPrivate($x)
{
$method = new \ReflectionMethod(
'A',
'foo'
);
//* for some reason, the private function needs to have been changed to be 'accessible' for this to work in 5.4
$method->setAccessible(true);
//working 5.4 (* see above) but not in 5.5
$arguments = func_get_args();
//not working in either
$arguments = array($x); // <---- COMMENT THIS LINE TO SEE IT WORK IN PHP 5.4
return $method->invokeArgs($this, $arguments);
}
}
$y = new B();
$y->bar();
Я не понимаю, почему бы не было разницы между двумя массивами $arguments, поскольку var_dumping их показывает один и тот же вывод. Поэтому я полагаю, что это связано с чем-то более низким уровнем, например, объектные "указатели" различаются (из моей глубины здесь)?
Другой вопрос: если это ошибка в PHP 5.4, 5.5 или оба?
Ответы
Ответ 1
До PHP 5.5.6 func_get_args()
взяли аргументы из стека VM, скопировали их и вернули их в массив. В PHP 5.5.6 была введена оптимизация, которая позволяет избежать этих дорогих копий в общем случае. Вместо того, чтобы копировать zvals, только refcount увеличивается (по-прежнему, несмотря на ошибки).
Обычно такое изменение будет иметь нулевой эффект на код пользователя. Но в двигателе есть несколько мест, где наблюдаемое поведение отличается от пересчета zval. Одним из таких мест является передача по ссылке:
В случае вызова динамической функции zval может передаваться по ссылке либо, если это ссылка , либо если она имеет refcount == 1.
До PHP 5.5.6 в zval в массиве, возвращаемом func_get_args()
, всегда был refcount == 1, поэтому они прошли через этот второй случай. Начиная с PHP 5.5.6 это уже не так, поскольку значения zval всегда будут иметь refcount > 1 и вызывать ошибку, если вы попытаетесь передать их по ссылке.
Примечание. Код действительно не работал до PHP 5.5.6 (по-ref игнорировался). Это было просто неудачное совпадение, что вы не получили сообщение об ошибке;)
Обновление. Мы решили отменить изменение на ветке 5.5 из-за перерыва BC. Вы вернете прежнее поведение в PHP 5.5.8, а новое поведение будет только в PHP 5.6.