Почему функции обработки функций, такие как call_user_func()
поддерживают передачу параметров по ссылке?
В документах написаны такие простые вещи, как «Обратите внимание, что параметры для call_user_func () не передаются по ссылке». Я предполагаю, что у разработчиков PHP была какая-то причина для отключения этой возможности в этом случае.
Были ли они связаны с техническим ограничением? Это был выбор языка? Как это произошло?
РЕДАКТИРОВАТЬ:
Чтобы прояснить это, вот пример.
<?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. Зачем?
Ответ глубоко уложен в том, как ссылки работают в модели PHP – не обязательно реализация , потому что это может сильно различаться, особенно в версиях 5.x. Я уверен, что вы слышали строки, они не похожи на C-указатели или ссылки на C ++ и т. Д. И т. Д. В принципе, когда переменная назначается или привязывается, это может происходить двумя способами: либо по значению (в котором если новая переменная привязана к новому «ящику», содержащему копию старого значения) или по ссылке (в этом случае новая переменная привязана к тому же значению, что и старое значение). Это верно, говорим ли мы о переменных, аргументах функций или ячейках в массивах.
Когда вы начинаете передавать ссылки на функции, все начинает становиться немного волосатым, очевидно, что целью является изменение исходных переменных. Некоторое время назад call-by-pass-by-reference (способность передавать ссылку в функцию, которая не ожидала ее) устарела, потому что функция, которая не знала, что имеет дело со ссылкой, может «случайно» 'изменить вход. Если перейти на другой уровень, если эта функция вызывает вторую функцию, то сама не ожидала ссылки … тогда все заканчивается тем, что отключается. Это может сработать, но это не гарантируется и может сломаться в некоторой версии PHP.
Здесь вызывается call_user_func()
. Предположим, что вы передаете ссылку на него (и получите связанное предупреждение о прохождении вызова по времени). Затем ваша ссылка привязана к новой переменной – параметрам самой call_user_func()
. Затем, когда вы вызываете свою целевую функцию, ее параметры не связаны там, где вы ожидаете. Они не связаны с исходными параметрами вообще. Они привязаны к локальным переменным, которые находятся в объявлении call_user_func()
. call_user_func_array()
требует осторожности. Помещение ссылки в ячейку массива может быть проблемой – поскольку PHP передает этот массив с семантикой «copy-on-write», вы не можете быть уверены, что массив не будет изменен под вами, и копия не получит снят с оригинальной ссылки.
Наиболее проницательное объяснение, которое я видел (которое помогло мне разобраться в ссылках), было в комментариях к руководству PHP «Передача по ссылке»:
http://ca.php.net/manual/en/language.references.pass.php#99549
В принципе логика идет так. Как бы вы написали свою собственную версию call_user_func()
? – а затем объясните, как это ломается со ссылками, и как это происходит, когда вы избегаете переадресации времени звонка. Другими словами, правильный способ вызова функций (указать значение и позволить PHP решить из объявления функции, передавать ли значение или ссылку) не будет работать, когда вы используете call_user_func()
– вы вызываете две функции в глубину , первое по значению, второе – по отношению к значениям в первом.
Подумайте об этом, и вы будете гораздо глубже понимать ссылки на PHP (и гораздо более сильную мотивацию, чтобы убежать, если можете).
Видеть это:
http://hakre.wordpress.com/2011/03/09/call_user_func_array-php-5-3-and-passing-by-reference/
Можно ли передавать параметры по ссылке с помощью call_user_func_array ()?
http://bugs.php.net/bug.php?id=17309&edit=1
Передача ссылок в массиве работает правильно.
Обновленный ответ:
Вы можете использовать:
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
Другой возможный путь – синтаксис по ссылке остается «правильным»:
$data = 'some data'; $func = 'more'; $func($more); function more(&$data) { // Do something with $data here... }