tag name php php-internals

php - name - Paréntesis que altera la semántica del resultado de la llamada de función



get tag name wordpress (2)

Se señaló en otra pregunta que al ajustar el resultado de una llamada de función PHP entre paréntesis, de alguna manera se puede convertir el resultado en una expresión completamente desarrollada, de modo que lo siguiente funciona:

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

Estoy tratando de encontrar algo en la documentación para explicar explícita e inequívocamente lo que está sucediendo aquí. A diferencia de C ++, no sé lo suficiente sobre la gramática de PHP y su tratamiento de las declaraciones / expresiones para derivarlo yo mismo.

¿Hay algo oculto en la documentación con respecto a este comportamiento? Si no, ¿alguien más puede explicarlo sin recurrir a la suposición?

Actualizar

Primero encontré este EBNF pretendiendo representar la gramática de PHP, e intenté decodificar mis guiones, pero finalmente me di por vencido.

Luego, usando phc para generar un archivo .dot de las dos variantes de foo() , produje imágenes AST para ambos scripts usando los siguientes comandos:

$ 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

En ambos casos, el resultado fue exactamente el mismo:


Este comportamiento podría clasificarse como error , por lo que definitivamente no deberías confiar en él.

Las condiciones (simplificadas) para que el mensaje no se ejecute en una llamada de función son las siguientes (consulte la definición del código de operación ZEND_SEND_VAR_NO_REF ):

  • el argumento no es una llamada a función (o si lo es, regresa por referencia), y
  • el argumento es una referencia o tiene un recuento de referencia 1 (si tiene el recuento de referencia 1, se convierte en una referencia).

Analicemos esto con más detalle.

El primer punto es verdadero (no es una llamada a función)

Debido a los paréntesis adicionales, PHP ya no detecta que el argumento es una llamada a función.

Al analizar una lista de argumentos de función no vacía, hay tres posibilidades para PHP:

  • Una expr_without_variable
  • Una variable
  • (A & seguido de una variable , para el pase de tiempo de llamada eliminado por función de referencia)

Al escribir solo get_array() PHP ve esto como una variable .

(get_array()) por otro lado no califica como una variable . Es un expr_without_variable .

En última instancia, esto afecta la forma en que se compila el código, es decir, el valor extendido del código de operación SEND_VAR_NO_REF ya no incluirá el indicador ZEND_ARG_SEND_FUNCTION , que es la forma en que se detecta la llamada de función en la implementación del código de operación.

El segundo punto es verdadero (el recuento de referencia es 1)

En varios puntos, Zend Engine permite no referencias con el recuento de referencias 1 donde se esperan referencias. Estos detalles no deben exponerse al usuario, pero desafortunadamente están aquí.

En su ejemplo, está devolviendo una matriz a la que no se hace referencia desde ningún otro lugar. Si lo fuera, aún recibirías el mensaje, es decir, este segundo punto no sería cierto.

Entonces el siguiente ejemplo muy similar no funciona :

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


A) Para entender lo que está sucediendo aquí, uno necesita comprender el manejo de PHP de los valores / variables y referencias (PDF, 1.2MB). Como se indica en toda la documentación : "las referencias no son punteros" ; y solo puede devolver variables por referencia desde una función , nada más.

En mi opinión, eso significa que cualquier función en PHP devolverá una referencia . Pero algunas funciones (integradas en PHP) requieren valores / variables como argumentos. Ahora, si está anidando funciones-llamadas, la interna devuelve una referencia, mientras que la externa espera un valor. Esto lleva al "famoso" error E_STRICT "Solo las variables deben pasarse por referencia" .

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

B) Encontré una línea en la descripción de la sintaxis PHP vinculada en la pregunta .

expr_without_variable = "(" expr ")"

En combinación con esta frase de la documentation : "En PHP, casi todo lo que escribes es una expresión. La manera más simple pero más precisa de definir una expresión es ''cualquier cosa que tenga un valor''.", Esto me lleva a la conclusión de que incluso (5) es una expresión en PHP, que se evalúa como un entero con el valor 5.

(Como $a = 5 no es solo una asignación sino también una expresión, que se evalúa como 5.)

Conclusión

Si pasa una referencia a la expresión (...) , esta expresión devolverá un valor, que luego se puede pasar como argumento a la función externa. Si eso (mi línea de pensamiento) es cierto, las siguientes dos líneas deberían funcionar de manera equivalente:

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

Ver también PHP 5.0.5: Error fatal: solo se pueden pasar variables por referencia; 13.09.2005