with strip_tags remove ejemplo php language-design php-internals

strip_tags - ¿Por qué los atributos de PHP no permiten funciones?



strip_tags wordpress (5)

Soy bastante nuevo en PHP, pero he estado programando en idiomas similares durante años. Me quedé desconcertado por lo siguiente:

class Foo { public $path = array( realpath(".") ); }

Produjo un error de sintaxis: Error de Parse error: syntax error, unexpected ''('', expecting '')'' in test.php on line 5 : Parse error: syntax error, unexpected ''('', expecting '')'' in test.php on line 5 que es la llamada realpath .

Pero esto funciona bien:

$path = array( realpath(".") );

Después de golpear mi cabeza contra esto por un tiempo, me dijeron que no puedes llamar a las funciones en un atributo predeterminado; tienes que hacerlo en __construct . Mi pregunta es: ¿por qué? ¿Es esto una "característica" o implementación descuidada? ¿Cuál es el razonamiento?


Mi pregunta es: ¿por qué? ¿Es esto una "característica" o implementación descuidada?

Yo diría que definitivamente es una característica. Una definición de clase es un modelo de código, y no se supone que ejecute código en el momento de su definición. Rompería la abstracción y la encapsulación del objeto.

Sin embargo, esta es solo mi opinión. No puedo decir con certeza qué idea tenían los desarrolladores al definir esto.


El código del compilador sugiere que esto es por diseño, aunque 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 PHP no es extenso, intentaré e ilustraré lo que creo que ocurre para que pueda ver dónde hay un problema. Su muestra de código es una buena candidata para este proceso, así que la usaremos:

class Foo { public $path = array( realpath(".") ); }

Como bien sabe, esto causa un error de sintaxis. Esto es el resultado de la gramática de PHP , que hace la siguiente definición relevante:

class_variable_declaration: //... | T_VARIABLE ''='' static_scalar //... ;

Por lo tanto, al definir los valores de variables como $path , el valor esperado debe coincidir con la definición de un escalar estático. Como era de esperar, este es un nombre poco apropiado dado que la definición de un escalar estático también incluye tipos de matriz cuyos valores también son escalares estáticos:

static_scalar: /* compile-time evaluated scalars */ //... | T_ARRAY ''('' static_array_pair_list '')'' // ... //... ;

Supongamos por un segundo que la gramática es diferente, y la línea anotada en la regla de eliminación de variables de clase se parecía más a la siguiente, que coincidiría con la muestra de código (a pesar de que se hayan roto las asignaciones válidas):

class_variable_declaration: //... | T_VARIABLE ''='' T_ARRAY ''('' array_pair_list '')'' // ... ;

Después de recompilar PHP, la secuencia de comandos de muestra ya no fallaría con ese 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 del 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 código del compilador al analizar este ejemplo de código. He dejado algunas partes menos importantes, pero el proceso se ve así:

// ... // 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();

Si bien el compilador hace mucho en estos diversos métodos, es importante tener en cuenta algunas cosas.

  1. Una llamada a zend_do_begin_class_declaration() da como resultado una llamada a get_next_op() . Esto significa que agrega un nuevo código de operación al conjunto de códigos de operación actual.
  2. array_init() y zend_do_add_static_array_element() no generan nuevos zend_do_add_static_array_element() . 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() .
  3. zend_do_early_binding() consume el último código de operación en el conjunto de códigos de operación actual, verificando uno de los siguientes tipos antes de establecerlo en 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 produce un error: el error "Tipo de enlace no válido" . 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 a array_init() , el compilador prepara los argumentos y llamadas zend_do_init_array() . Esto a su vez llama a get_next_op() y agrega un nuevo código de operación INIT_ARRAY , produciendo algo como lo siguiente:

DECLARE_CLASS ''Foo'' SEND_VAL ''.'' DO_FCALL ''realpath'' INIT_ARRAY

Aquí radica la raíz del problema. Al agregar estos zend_do_early_binding() , zend_do_early_binding() obtiene 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 se definió en la declaración de variable de clase, similar a cómo se manejan las definiciones de 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 forma de manejar este escenario, por lo que no es imposible tomar esa decisión y hacer que funcione. 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.


Es una implementación de analizador descuidado. No tengo la terminología correcta para describirlo (creo que el término "reducción beta" se ajusta de alguna manera ...), pero el analizador de lenguaje PHP es más complejo y más complicado de lo que necesita ser, y así todo tipo de Se requiere una carcasa especial para diferentes construcciones de lenguaje.


Probablemente puedas lograr algo similar como esto:

class Foo { public $path = __DIR__; }

IIRC __DIR__ necesita php 5.3+, __FILE__ ha estado por más tiempo


Supongo que no podrá tener un seguimiento de pila correcto si el error no ocurre en una línea ejecutable ... Dado que no puede haber ningún error al inicializar valores con constantes, no hay ningún problema con eso, pero la función puede lanzar excepciones / errores y necesita ser llamada dentro de una línea ejecutable, y no declarativa.