В другом вопросе было отмечено, что перенос результата вызова функции PHP в круглых скобках может каким-то образом преобразовать результат в полноценное выражение, так что следующее работает:
<?php error_reporting(E_ALL | E_STRICT); function get_array() { return array(); } function foo() { // return reset(get_array()); // ^ error: "Only variables should be passed by reference" return reset((get_array())); // ^ OK } foo();
Я пытаюсь найти что-либо в документации, чтобы явно и недвусмысленно объяснить, что здесь происходит. В отличие от C ++, я не знаю достаточно о грамматике PHP и ее обработке высказываний / выражений, чтобы получить ее сам.
Есть ли что-нибудь скрытое в документации относительно этого поведения? Если нет, может ли кто-нибудь объяснить это, не прибегая к предположению?
Сначала я нашел этот EBNF , чтобы представить грамматику PHP, и попытался сам расшифровать мои скрипты, но в итоге отказался.
Затем, используя phc
для генерации .dot
файла двух вариантов foo()
, я произвел изображения AST для обоих скриптов, используя следующие команды:
$ yum install phc graphviz $ phc --dump-ast-dot test1.php > test1.dot $ dot -Tpng test1.dot > test1.png $ phc --dump-ast-dot test2.php > test2.dot $ dot -Tpng test2.dot > test2.png
В обоих случаях результат был точно таким же:
Такое поведение можно классифицировать как ошибку , поэтому вам определенно не следует полагаться на него.
Условия (упрощенные) для сообщения, которое не нужно бросать на вызов функции, следующие (см. Определение кода операции ZEND_SEND_VAR_NO_REF
):
Давайте проанализируем их более подробно.
Из-за дополнительных круглых скобок PHP больше не обнаруживает, что аргумент является вызовом функции.
При разборе списка непустых аргументов функции есть три возможности для PHP:
expr_without_variable
variable
&
за которым следует variable
, для удаленной передачи времени по ссылке) При написании только get_array()
PHP видит это как variable
.
(get_array())
с другой стороны, не квалифицируется как variable
. Это expr_without_variable
.
Это в конечном счете влияет на способ компиляции кода, а именно расширенное значение кода операции SEND_VAR_NO_REF
больше не будет включать флаг ZEND_ARG_SEND_FUNCTION
, который является способом обнаружения функции в реализации кода операции.
В нескольких точках Zend Engine позволяет ссылаться на ссылочный счет 1, где ожидаются ссылки. Эти детали не должны подвергаться воздействию пользователя, но, к сожалению, они здесь.
В вашем примере вы возвращаете массив, на который не ссылаются нигде. Если это так, вы все равно получите сообщение, т. Е. Эта вторая точка не будет правдой.
Таким образом, следующий очень похожий пример не работает :
<?php $a = array(); function get_array() { return $GLOBALS['a']; } return reset((get_array()));
A) Чтобы понять, что здесь происходит, нужно понимать обработку PHP значений / переменных и ссылок (PDF, 1.2MB). Как указано в документации : «ссылки не являются указателями» ; и вы можете возвращать переменные только по ссылке из функции – ничего больше.
На мой взгляд, это означает, что любая функция в PHP вернет ссылку . Но некоторые функции (встроенные в PHP) требуют значений / переменных в качестве аргументов. Теперь, если вы вставляете функциональные вызовы, внутренний возвращает ссылку, а внешний ожидает значение. Это приводит к «знаменитой» ошибке E_STRICT «Только переменные должны передаваться по ссылке» .
$fileName = 'example.txt'; $fileExtension = array_pop(explode('.', $fileName)); // will result in Error 2048: Only variables should be passed by reference in…
B) Я нашел строку в описании синтаксиса PHP, связанную с вопросом .
expr_without_variable = "(" expr ")"
В сочетании с этим предложением из документации : «В PHP почти все, что вы пишете, является выражением. Самый простой, но наиболее точный способ определения выражения -« все, что имеет значение ». Это приводит меня к выводу, что даже (5)
является выражением в PHP, которое вычисляет целое число со значением 5.
(Поскольку $a = 5
является не только присваиванием, но также выражением, которое вычисляется до 5.)
Вывод
Если вы передадите ссылку на выражение (...)
, это выражение вернет значение, которое затем может быть передано как аргумент внешней функции. Если это (моя линия мысли) истинно, следующие две строки должны работать одинаково:
// what I've used over years: (spaces only added for readability) $fileExtension = array_pop( ( explode('.', $fileName) ) ); // vs $fileExtension = array_pop( $tmp = explode('.', $fileName) );
См. Также PHP 5.0.5: Неустранимая ошибка: только переменные могут передаваться по ссылке; 13.09.2005