El compilador de código sugiere que esto es por diseño, aunque yo no' Sé cuál es el razonamiento oficial detrás de eso. Tampoco estoy seguro de cuánto esfuerzo se necesitaría para implementar de manera confiable esta funcionalidad, pero definitivamente existen algunas limitaciones en la forma en que se hacen las cosas actualmente.
Aunque mi conocimiento del compilador de PHP no es extenso, intentaré e ilustraré lo que creo que ocurre para que pueda ver dónde hay un problema. Su ejemplo de código hace un buen candidato para este proceso, por lo que vamos a utilizar lo siguiente:
class Foo {
public $path = array(
realpath(".")
);
}
Como bien sabe, esto provoca un error de sintaxis. Este es el resultado de la PHP grammar, lo que hace la siguiente definición pertinente:
class_variable_declaration:
//...
| T_VARIABLE '=' static_scalar //...
;
Así, al definir los valores de variables tales como $path
, el valor esperado debe coincidir con la definición de un escalar estática.Como era de esperar, esto es algo de un nombre inapropiado ya que la definición de un escalar estática también incluye tipos de matriz cuyos valores son también escalares estáticas:
static_scalar: /* compile-time evaluated scalars */
//...
| T_ARRAY '(' static_array_pair_list ')' // ...
//...
;
Supongamos por un segundo que la gramática era diferente, y la línea se indica en la regla delcaration variable de clase parecía algo más parecido a lo siguiente, que se correspondería con el ejemplo de código (a pesar de dividir las tareas de otro modo válidos):
class_variable_declaration:
//...
| T_VARIABLE '=' T_ARRAY '(' array_pair_list ')' // ...
;
Después de recompilar PHP, el script de ejemplo ya no fallar con el error de sintaxis. En cambio, fallaría con el error de tiempo de compilación "Tipo de enlace no válido". Como el código ahora es válido en función de la gramática, esto indica que realmente hay algo específico en el diseño del compilador que está causando problemas. Para descubrir qué es eso, volvamos a la gramática original por un momento e imaginemos que el ejemplo de código tenía una asignación válida de $path = array(2);
.
Usando la gramática como guía, es posible recorrer las acciones invocadas en el compiler code al analizar este ejemplo de código. He dejado algunas partes menos importantes, pero el proceso es como la siguiente:
// ...
// Begins the class declaration
zend_do_begin_class_declaration(znode, "Foo", znode);
// Set some modifiers on the current znode...
// ...
// Create the array
array_init(znode);
// Add the value we specified
zend_do_add_static_array_element(znode, NULL, 2);
// Declare the property as a member of the class
zend_do_declare_property('$path', znode);
// End the class declaration
zend_do_end_class_declaration(znode, "Foo");
// ...
zend_do_early_binding();
// ...
zend_do_end_compilation();
Mientras que el compilador hace mucho en estos diversos métodos, es importante tener en cuenta algunas cosas.
- Una llamada al
zend_do_begin_class_declaration()
da como resultado una llamada al get_next_op()
. Esto significa que agrega un nuevo código de operación al conjunto de códigos de operación actual.
array_init()
y zend_do_add_static_array_element()
no genera nuevos códigos de operación. En cambio, la matriz se crea inmediatamente y se agrega a la tabla de propiedades de la clase actual. Las declaraciones de métodos funcionan de manera similar, a través de un caso especial en zend_do_begin_function_declaration()
.
zend_do_early_binding()
consume el último código de operación de la matriz de código de operación actual, la comprobación de uno de los siguientes tipos antes de dejarla a un NOP:
- ZEND_DECLARE_FUNCTION
- ZEND_DECLARE_CLASS
- ZEND_DECLARE_INHERITED_CLASS
- ZEND_VERIFY_ABSTRACT_CLASS
- ZEND_ADD_INTERFACE
Tenga en cuenta que en el último caso, si el tipo de código de operación no es uno de los tipos esperados, se emite un error – El "tipo de enlace no válido" error. A partir de esto, podemos decir que permitir que los valores no estáticos se asignen de alguna manera hace que el último código de operación sea diferente de lo esperado. Entonces, ¿qué sucede cuando utilizamos una matriz no estática con la gramática modificada?
En lugar de llamar al array_init()
, el compilador prepara los argumentos y llama al zend_do_init_array()
. Esto a su vez llama get_next_op()
y añade una nueva INIT_ARRAY opcode, produciendo algo como lo siguiente:
DECLARE_CLASS 'Foo'
SEND_VAL '.'
DO_FCALL 'realpath'
INIT_ARRAY
Aquí reside la raíz del problema.Al agregar estos códigos de operación, zend_do_early_binding()
recibe una entrada inesperada y arroja una excepción. Como el proceso de las definiciones de clases y funciones vinculantes tempranas parece bastante integral para el proceso de compilación de PHP, no puede simplemente ignorarse (aunque la producción/consumo de DECLARE_CLASS es un tanto desordenada). Del mismo modo, no es práctico intentar evaluar estos códigos de operación adicionales en línea (no se puede estar seguro de que una determinada función o clase se haya resuelto todavía), por lo que no hay forma de evitar generar los códigos de operación.
Una posible solución sería construir una nueva matriz de código de operación que tuviera como ámbito la declaración de la variable de clase, similar a cómo se manejan las definiciones de los métodos. El problema al hacerlo es decidir cuándo evaluar una secuencia de ejecución única. ¿Se realizará cuando se cargue el archivo que contiene la clase, cuando se acceda a la propiedad por primera vez o cuando se construya un objeto de ese tipo?
Como ha señalado, otros lenguajes dinámicos han encontrado una manera de manejar este escenario, por lo que no es imposible tomar esa decisión y ponerla en práctica. Sin embargo, por lo que puedo decir, hacerlo en el caso de PHP no sería una corrección de una línea, y los diseñadores del lenguaje parecen haber decidido que no era algo valioso incluir en este punto.
@Gordon Por favor, mejore las respuestas. – Schwern
@Schwern Por favor, mejore las preguntas. – Gordon
Para ser claro, acepto cuando la pregunta es definitivamente respondida. Hasta ahora, todas las respuestas son especulaciones. Útil, pero aún especulación. – Schwern