Nawiasy zmieniające semantykę wyniku wywołania funkcji

Zauważono w innym pytaniu że owijanie wyniku wywołania funkcji PHP w nawiasach może w jakiś sposób przekształcić wynik w pełnowartościowe wyrażenie, takie że działa:

<?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();

Staram się znaleźć cokolwiek w dokumentacji wprost i jednoznacznie wyjaśnić, co się tu dzieje. W przeciwieństwie do C++, Nie wiem wystarczająco dużo o gramatyce PHP i jej traktowaniu wyrażeń/wyrażeń, aby czerpać ją osobiście.

Jest coś ukrytego w dokumentacji dotyczącej tego zachowania? Jeśli nie, czy ktoś inny może to wyjaśnić bez uciekania się do przypuszczeń?


Update

Po raz pierwszy znalazłem ten EBNF rzekomo reprezentujący gramatykę PHP, i próbowałem dekodować moje Skrypty sam, ale w końcu się poddałem.

Następnie używając phc aby wygenerować plik .dot dwóch wariantów foo(), i wyprodukował obrazy AST dla obu skryptów, używając następujących polecenia:

$ 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

W obu przypadkach wynik był dokładnie taki sam:

Parse tree of snippets 1 and 2

Author: Community, 2011-07-18

2 answers

To zachowanie można zaklasyfikować jako błąd , więc zdecydowanie nie powinieneś na nim polegać.

(uproszczone) warunki dla wiadomości Nie, która ma być rzucona podczas wywołania funkcji są następujące (patrz definicja kodu opcode ZEND_SEND_VAR_NO_REF):

    Nie jest to wywołanie funkcji (lub jeśli jest, to zwraca przez odwołanie), a
  • argument jest albo referencją, albo ma liczbę referencji 1 (jeśli ma liczbę referencji 1, jest zamieniany na Referencja).
Przeanalizujmy je bardziej szczegółowo. Pierwszy punkt to prawda (nie wywołanie funkcji)

Ze względu na dodatkowe nawiasy, PHP nie wykrywa już, że argument jest wywołaniem funkcji.

Podczas parsowania niepustej listy argumentów funkcji istnieją trzy możliwości dla PHP:

  • An expr_without_variable
  • A variable
  • (a &, po którym następuje variable, dla usuniętego przejścia czasu wywołania przez odniesienie funkcja)

Podczas pisania tylko get_array() PHP widzi to jako variable.

(get_array()) z drugiej strony nie kwalifikuje się jako variable. Jest to expr_without_variable.

Ostatecznie wpływa to na sposób kompilacji kodu, mianowicie Rozszerzona wartość kodu opcode {[11] } nie będzie już zawierać znacznika ZEND_ARG_SEND_FUNCTION, czyli sposobu, w jaki wywołanie funkcji jest wykrywane w implementacji kodu opcode.

Drugi punkt jest prawdziwy (liczba referencji wynosi 1)

W kilku punktach, Silnik Zend zezwala na brak odniesień z liczbą odniesień 1, gdzie oczekuje się odniesień. Dane te nie powinny być ujawniane użytkownikowi, ale niestety są tutaj.

W twoim przykładzie zwracasz tablicę, do której nie odwołujesz się nigdzie indziej. Gdyby tak było, nadal otrzymałbyś wiadomość, tzn. ten drugi punkt nie byłby prawdziwy.

Tak więc poniższy bardzo podobny przykład nie działa :

<?php

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

return reset((get_array()));
 32
Author: NikiC,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-05-18 13:14:28

A) aby zrozumieć, co się tutaj dzieje, należy zrozumieć obsługę PHP wartości/zmiennych i odniesień (PDF, 1.2 MB). Jak stwierdzono w dokumentacji: "referencje nie są wskaźnikami" ; i można zwracać zmienne tylko przez odniesienie z funkcji - nic więcej.

Moim zdaniem oznacza to, że każda funkcja w PHP zwróci referencję. Ale niektóre funkcje (wbudowane w PHP) wymagają wartości / zmienne jako argumenty. Teraz, jeśli zagnieżdżasz wywołania funkcji, wewnętrzne zwraca referencję, podczas gdy zewnętrzne oczekuje wartości. Prowadzi to do' słynnego ' błędu E_STRICT "tylko zmienne powinny być przekazywane przez odniesienie".

$fileName = 'example.txt';
$fileExtension = array_pop(explode('.', $fileName));
// will result in Error 2048: Only variables should be passed by reference in…

B) znalazłem w Opis PHP-składnia linkowany w pytaniu .

expr_without_variable = "(" expr ")"

W połączeniu z tym zdaniem z dokumentacji : "w PHP prawie wszystko co piszesz jest ekspresja. Najprostszym i najdokładniejszym sposobem definiowania wyrażenia jest "wszystko, co ma wartość".", to prowadzi mnie do wniosku, że even (5) jest wyrażeniem w PHP, które ocenia do liczby całkowitej o wartości 5.

(Ponieważ $a = 5 jest nie tylko przypisaniem, ale także wyrażeniem, które ewaluuje do 5.)

Wniosek

Jeśli przekażesz odwołanie do wyrażenia (...), to wyrażenie zwróci wartość, która następnie może być przekazana jako argument do funkcja zewnętrzna. Jeśli to (moja linia myślenia) jest prawdziwa, następujące dwie linie powinny działać równoważnie: {]}

// what I've used over years: (spaces only added for readability)
$fileExtension = array_pop( ( explode('.', $fileName) ) );
// vs
$fileExtension = array_pop( $tmp = explode('.', $fileName) );

Zobacz także PHP 5.0.5: Błąd krytyczny: tylko zmienne mogą być przekazywane przez odniesienie; 13.09.2005

 1
Author: feeela,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:18:33