trabajo tesis temas sobre sistemas sistema sindrome recomendacion proyecto mexico investigacion down doctorales antecedentes php class include require extends

php - temas - tesis sobre el sindrome de down en mexico



¿La clase derivada definida más adelante en el mismo archivo "no existe"? (2)

Esto simplemente con la clase de manejo de PHP en los archivos incluidos include dirname(__FILE__) . "/a.php"; include dirname(__FILE__) . "/a.php";

BB existe porque extiende B que se definió en el mismo archivo.

BA no existe porque PHP no analizó A línea se llama

Ambos trabajarían devuelven el mismo resultado

El uso de la class BA extends B

include dirname(__FILE__) . "/a.php"; echo "BA (before): ", class_exists("BA") ? "exists" : "doesn’t exist", "/n"; echo "BB: ", class_exists("BB") ? "exists" : "doesn’t exist", "/n"; class B { } class BA extends B { } class BB extends B { } echo "BA (after): ", class_exists("BA") ? "exists" : "doesn’t exist", "/n";

O definir la class A y usar la class BA extends A

class A { } echo "<pre>"; echo "BA (before): ", class_exists("BA") ? "exists" : "doesn’t exist", "/n"; echo "BB: ", class_exists("BB") ? "exists" : "doesn’t exist", "/n"; class B { } class BA extends A { } class BB extends B { } echo "BA (after): ", class_exists("BA") ? "exists" : "doesn’t exist", "/n";

Salida

BA (before): exists BB: exists BA (after): exists

Conclusión

FORMULAR PHP DOC

Cuando se incluye un archivo, el código que contiene hereda el alcance variable de la línea en la que ocurre la inclusión. Cualquier variable disponible en esa línea en el archivo de llamada estará disponible dentro del archivo llamado, desde ese punto en adelante. Sin embargo, todas las funciones y clases definidas en el archivo incluido tienen el alcance global.

Creo que las clases ampliadas están cubiertas en lo que dice el documento PHP, esto se puede tratar como ERROR que debe corregirse, pero para el horario principal incluya su clase antes de llamar o usarlas

Supongamos que tenemos dos archivos php, a.php y b.php. Aquí está el contenido del archivo a.php:

<?php // content of a.php class A { }

Y aquí está el contenido del archivo b.php

<?php // content of b.php include dirname(__FILE__) . "/a.php"; echo "A: ", class_exists("A") ? "exists" : "doesn’t exist", "/n"; echo "B: ", class_exists("B") ? "exists" : "doesn’t exist", "/n"; echo "BA (before): ", class_exists("BA") ? "exists" : "doesn’t exist", "/n"; echo "BB: ", class_exists("BB") ? "exists" : "doesn’t exist", "/n"; class B { } class BA extends A { } class BB extends B { } echo "BA (after): ", class_exists("BA") ? "exists" : "doesn’t exist", "/n";

Si ejecuta el script b.php, tiene este resultado:

A: exists B: exists BA (before): doesn’t exist BB: exists BA (after): exists

¿Por qué la clase BA existe solo después de la definición de la clase? ¿Y por qué existen las otras clases incluso antes de su definición? ¿Cuál es la diferencia? Esperaría tener un comportamiento común en ambos casos ... ¿Hay alguna manera de poder usar la clase BA incluso antes de su definición?

Gracias

Michele


Descargo de responsabilidad: no pretendo entender el funcionamiento interno de Zend. La siguiente es mi interpretación de la fuente PHP, alimentada en gran parte por conjeturas educadas. Aunque estoy completamente seguro de la conclusión, la terminología o los detalles podrían estar apagados. Me encantaría saber de alguien con experiencia en Zend Internals sobre el tema.

La investigación

Desde el analizador de PHP podemos ver que cuando se encuentra una declaración de clase, se zend_do_early_binding función zend_do_early_binding . Aquí está el código que maneja la declaración de las clases derivadas:

case ZEND_DECLARE_INHERITED_CLASS: { zend_op *fetch_class_opline = opline-1; zval *parent_name; zend_class_entry **pce; parent_name = &CONSTANT(fetch_class_opline->op2.constant); if ((zend_lookup_class(Z_STRVAL_P(parent_name), Z_STRLEN_P(parent_name), &pce TSRMLS_CC) == FAILURE) || ((CG(compiler_options) & ZEND_COMPILE_IGNORE_INTERNAL_CLASSES) && ((*pce)->type == ZEND_INTERNAL_CLASS))) { if (CG(compiler_options) & ZEND_COMPILE_DELAYED_BINDING) { zend_uint *opline_num = &CG(active_op_array)->early_binding; while (*opline_num != -1) { opline_num = &CG(active_op_array)->opcodes[*opline_num].result.opline_num; } *opline_num = opline - CG(active_op_array)->opcodes; opline->opcode = ZEND_DECLARE_INHERITED_CLASS_DELAYED; opline->result_type = IS_UNUSED; opline->result.opline_num = -1; } return; } if (do_bind_inherited_class(CG(active_op_array), opline, CG(class_table), *pce, 1 TSRMLS_CC) == NULL) { return; } /* clear unnecessary ZEND_FETCH_CLASS opcode */ zend_del_literal(CG(active_op_array), fetch_class_opline->op2.constant); MAKE_NOP(fetch_class_opline); table = CG(class_table); break; }

Este código llama inmediatamente a zend_lookup_class para ver si la clase padre existe en la tabla de símbolos ... y luego difiere dependiendo de si el padre se encuentra o no.

Primero veamos qué hace si se encuentra la clase padre:

if (do_bind_inherited_class(CG(active_op_array), opline, CG(class_table), *pce, 1 TSRMLS_CC) == NULL) { return; }

Pasando a do_bind_inherited_class , vemos que el último argumento (que en esta llamada es 1 ) se llama compile_time . Esto suena interesante. ¿Qué hace con este argumento?

if (compile_time) { op1 = &CONSTANT_EX(op_array, opline->op1.constant); op2 = &CONSTANT_EX(op_array, opline->op2.constant); } else { op1 = opline->op1.zv; op2 = opline->op2.zv; } found_ce = zend_hash_quick_find(class_table, Z_STRVAL_P(op1), Z_STRLEN_P(op1), Z_HASH_P(op1), (void **) &pce); if (found_ce == FAILURE) { if (!compile_time) { /* If we''re in compile time, in practice, it''s quite possible * that we''ll never reach this class declaration at runtime, * so we shut up about it. This allows the if (!defined(''FOO'')) { return; } * approach to work. */ zend_error(E_COMPILE_ERROR, "Cannot redeclare class %s", Z_STRVAL_P(op2)); } return NULL; } else { ce = *pce; }

De acuerdo ... por lo que lee los nombres de las clases primarias y derivadas, ya sea desde una perspectiva estática (desde la perspectiva del usuario de PHP) o dinámica, dependiendo del estado compile_time . Luego trata de encontrar la entrada de clase ("ce") en la tabla de clases, y si no se encuentra, entonces ... vuelve sin hacer nada en tiempo de compilación, pero emite un error fatal en el tiempo de ejecución .

Esto suena enormemente importante. Regresemos a zend_do_early_binding . ¿Qué sucede si no se encuentra la clase padre?

if (CG(compiler_options) & ZEND_COMPILE_DELAYED_BINDING) { zend_uint *opline_num = &CG(active_op_array)->early_binding; while (*opline_num != -1) { opline_num = &CG(active_op_array)->opcodes[*opline_num].result.opline_num; } *opline_num = opline - CG(active_op_array)->opcodes; opline->opcode = ZEND_DECLARE_INHERITED_CLASS_DELAYED; opline->result_type = IS_UNUSED; opline->result.opline_num = -1; } return;

Parece que está generando do_bind_inherited_class de do_bind_inherited_class que activarán una llamada a do_bind_inherited_class nuevamente, pero esta vez, el valor de compile_time será 0 (falso).

Finalmente, ¿qué pasa con la implementación de la función PHP class_exists ? Al mirar la fuente, se muestra este fragmento:

found = zend_hash_find(EG(class_table), name, len+1, (void **) &ce);

¡Estupendo! ¡Esta variable class_table es la misma class_table que participa en la llamada do_bind_inherited_class que vimos antes! Entonces, el valor de retorno de class_exists depende de si una entrada para la clase ya ha sido insertada en class_table por do_bind_inherited_class .

Las conclusiones

El compilador Zend no actúa sobre las directivas include en tiempo de compilación (incluso si el nombre del archivo está codificado).

Si lo hiciera, entonces no habría ninguna razón para emitir un error fatal de redeclaración de clase basado en el indicador compile_time no configurado; el error podría emitirse incondicionalmente.

Cuando el compilador encuentra una declaración de clase derivada donde la clase base no ha sido declarada en el mismo archivo de script, empuja el acto de registrar la clase en sus estructuras internas de datos al tiempo de ejecución.

Esto es evidente a partir del último fragmento de código anterior, que configura un código de operación ZEND_DECLARE_INHERITED_CLASS_DELAYED para registrar la clase cuando se ejecuta el script. En ese punto, el indicador compile_time será false y el comportamiento será sutilmente diferente.

El valor de retorno de class_exists depende de si la clase ya se ha registrado.

Como esto ocurre de diferentes maneras en tiempo de compilación y en tiempo de ejecución, el comportamiento de class_exists también es diferente:

  • las clases cuyos antepasados ​​están todas incluidas en el mismo archivo fuente se registran en tiempo de compilación; existen y se pueden crear instancias en cualquier punto de esa secuencia de comandos
  • las clases que tienen un ancestro definido en otro archivo fuente se registran en tiempo de ejecución; antes de que la VM ejecute los class_exists false que corresponden a la definición de la clase en la fuente, estas clases no existen para todos los propósitos prácticos ( class_exists devuelve false , instanciar da un error fatal)