Подтвердить что ты не робот

Скобки, изменяющие семантику результата вызова функции

Было отмечено в другом вопросе, что перенос результата вызова функции 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();

Я пытаюсь найти что-либо в документации явно и недвусмысленно объяснять, что здесь происходит. В отличие от С++, я не знаю достаточно о грамматике PHP и ее обработке выражений/выражений, чтобы получить ее сам.

Есть ли что-нибудь скрытое в документации относительно этого поведения? Если нет, может ли кто-нибудь объяснить это, не прибегая к предположению?


Update

Я впервые нашел этот 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

В обоих случаях результат был точно таким же:

Дерево разбора фрагментов 1 и 2

4b9b3361

Ответ 1

Это поведение может быть классифицировано как ошибка, поэтому вам определенно не следует полагаться на него.

(упрощенные) условия для сообщения не, которые должны быть брошены на вызов функции, следующие (см. определение opcode ZEND_SEND_VAR_NO_REF):

  • аргумент не является вызовом функции (или если он есть, он возвращается по ссылке) и
  • аргумент является либо ссылкой, либо имеет счетчик ссылок 1 (если у него есть счетчик ссылок 1, он превращается в ссылку).

Проанализируйте их более подробно.

Первая точка верна (не вызов функции)

Из-за дополнительных круглых скобок PHP больше не обнаруживает, что аргумент является вызовом функции.

При анализе списка непустых аргументов функции для PHP существует три возможности:

  • An expr_without_variable
  • A variable
  • (A &, за которым следует variable, для удалённого прохода по времени вызова по ссылке)

При написании только get_array() PHP видит это как variable.

(get_array()), с другой стороны, не квалифицируется как variable. Это expr_without_variable.

Это в конечном счете влияет на способ компиляции кода, а именно расширенное значение кода операции SEND_VAR_NO_REF больше не будет включать флаг ZEND_ARG_SEND_FUNCTION, который является способом обнаружения функции в реализации кода операции.

Вторая точка истинна (количество ссылок равно 1)

В нескольких точках Zend Engine допускает не ссылки с номером ссылки 1, где ожидаются ссылки. Эти данные не должны быть доступны пользователю, но, к сожалению, они здесь.

В вашем примере вы возвращаете массив, который не упоминается нигде. Если бы это было так, вы все равно получили бы сообщение, т.е. Этот второй пункт не был бы правдой.

Итак, следующий очень похожий пример не работает:

<?php

$a = array();
function get_array() {
   return $GLOBALS['a'];
}

return reset((get_array()));

Ответ 2

A) Чтобы понять, что происходит здесь, нужно понять PHP-обработку значений/переменных и ссылок (PDF, 1,2 МБ). Поскольку указано в документации: "ссылки не указатели" ; и вы можете возвращать переменные только по ссылке из функции - ничего больше.

По-моему, это означает, что любая функция в 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.

(As $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