Почему функция PHP call_user_func () не поддерживает передачу по ссылке? - PullRequest
8 голосов
/ 07 июня 2011

Почему функции обработки функций, такие как call_user_func(), не поддерживают передачу параметров по ссылке?

В документах есть краткие слова типа "Обратите внимание, что параметры для call_user_func () не передаются по ссылке". Я предполагаю, что разработчики PHP имели какую-то причину для отключения этой возможности в этом случае.

Были ли у них технические ограничения? Это был выбор дизайна языка? Как это произошло?

EDIT:

Чтобы прояснить это, вот пример.

<?php

function more(&$var){ $var++; }

$count = 0;
print "The count is $count.\n";

more($count);
print "The count is $count.\n";

call_user_func('more', $count);
print "The count is $count.\n";

// Output:
// The count is 0.
// The count is 1.
// The count is 1.

Это нормально работает; call_user_func не передает $ count по ссылке, даже если more () объявил его как указанную переменную. В документации call_user_func четко сказано, что именно так она и должна работать.

Мне хорошо известно, что я могу получить нужный эффект, используя call_user_func_array('more', array(&$count)).

Вопрос в том, почему call_user_func предназначен для такой работы? , передаваемый по справочной документации , говорит, что «одних определений функций достаточно для правильной передачи аргумента по ссылке». Поведение call_user_func является исключением из этого. Почему?

Ответы [ 4 ]

10 голосов
/ 20 июня 2012

Ответ заложен глубоко в том, как работают ссылки в PHP модели - не обязательно в реализации , потому что это может сильно варьироваться, особенно в версиях 5.x. Я уверен, что вы слышали строки, они не похожи на указатели C, или ссылки C ++, и т. Д. И т. Д. В основном, когда переменная назначается или связывается, это может происходить двумя способами - либо по значению (в котором В этом случае новая переменная привязывается к новому «ящику», содержащему копию старого значения), или по ссылке (в этом случае новая переменная привязывается к тому же полю значения, что и старое значение). Это верно, говорим ли мы о переменных, или аргументах функций, или ячейках в массивах.

Когда вы начинаете передавать ссылки в функции, все становится немного странно - очевидно, цель состоит в том, чтобы иметь возможность изменять исходные переменные. Некоторое время назад, передача по ссылке во время вызова (способность передавать ссылку в функцию, которая не ожидала ее появления) устарела, потому что функция, которая не знала, что имеет дело со ссылкой, могла «случайно» изменить вход. Если перейти на другой уровень, если эта функция вызывает вторую функцию, которая сама не ожидала ссылки ... тогда все заканчивается отключением. может работать, но это не гарантировано и может сломаться в некоторых версиях PHP.

Это - то, где call_user_func() входит. Предположим, вы передаете ссылку (и получаете соответствующее предупреждение о передаче по ссылке во время вызова). Тогда ваша ссылка будет связана с новой переменной - параметрами самого call_user_func(). Затем, когда вызывается ваша целевая функция, ее параметры не связаны, где вы ожидаете. Они вообще не связаны с исходными параметрами. Они связаны с локальными переменными, которые указаны в объявлении call_user_func(). call_user_func_array() тоже требует осторожности. Поместить ссылку в ячейку массива может быть проблемой - поскольку PHP передает этот массив с семантикой «копирование при записи», вы не можете быть уверены, что массив не будет изменен под вами, и копия не получит отделено от оригинальной ссылки.

Самое проницательное объяснение, которое я видел (которое помогло мне сориентироваться в ссылках), было в комментарии к руководству PHP «Передача по ссылке»:

http://ca.php.net/manual/en/language.references.pass.php#99549

В основном логика выглядит следующим образом. Как бы вы написали свою собственную версию call_user_func()? - а затем объясните, как это ломается со ссылками, и как оно терпит неудачу, когда вы избегаете передачи по ссылке во время вызова Другими словами, правильный способ вызова функций (укажите значение и позвольте PHP решить из объявления функции, передавать ли значение или ссылку) не будет работать при использовании call_user_func() - вы Вы вызываете две глубокие функции: первую по значению, а вторую по отношению к значениям в первой.

Подумайте об этом, и у вас будет гораздо более глубокое понимание ссылок на PHP (и гораздо более сильная мотивация, если вы сможете избежать ошибок).

3 голосов
/ 07 июня 2011

Обновленный ответ:

Вы можете использовать:

call_user_func('more', &$count)

для достижения того же эффекта, что и:

call_user_func_array('more', array(&$count))

По этой причинеЯ считаю (необоснованно), что call_user_func - это просто сокращение времени компиляции.(т. е. он заменяется на более поздний во время компиляции)

Чтобы дать вам мой актуальный вопрос "Почему call_user_func был разработан для такой работы?":

Возможно, он подпадает подте же строки, что и «Почему некоторые методы strstr и другие str_replace?», почему функции массива haystack, needle и строковые функции needle, haystack?

Это потому, что PHP был разработан многими разными людьми,в течение длительного периода времени и без строгих стандартов в то время.

Оригинальный ответ:

Вы должны убедиться, что вы установили переменную внутри массивак ссылке.

Попробуйте это и обратите внимание на array(&$t) часть:

function test(&$t) {
    $t++;
    echo '$t is '.$t.' inside function'.PHP_EOL;
}

$t = 0;
echo '$t is '.$t.' in global scope'.PHP_EOL;

test($t);

$t++;
echo '$t is '.$t.' in global scope'.PHP_EOL;

call_user_func_array('test', array(&$t));

$t++;
echo '$t is '.$t.' in global scope'.PHP_EOL;

Должен вывести:

$t is 0 in global scope
$t is 1 inside function
$t is 2 in global scope
$t is 3 inside function
$t is 4 in global scope
3 голосов
/ 07 июня 2011
2 голосов
/ 23 февраля 2013

Другой возможный способ - синтаксис по ссылке остается верным:

$data = 'some data';
$func = 'more';
$func($more);

function more(&$data) {
  // Do something with $data here...
}
...