Le compilateur de code suggère que c'est par la conception, mais je ne sais pas ce que le fonctionnaire raisonnement derrière cela est. Je suis également pas sûr de combien d'efforts il faudrait fiable pour mettre en œuvre cette fonctionnalité, mais il y a certainement des limites dans la façon dont les choses sont actuellement fait.
Bien que mes connaissances en PHP compilateur n'est pas vaste, je vais essayer d'illustrer ce que je crois va de sorte que vous pouvez voir où il y a un problème. Votre exemple de code qui fait un bon candidat pour ce processus, de sorte que nous allons utiliser que:
class Foo {
public $path = array(
realpath(".")
);
}
Comme vous le savez, cela provoque une erreur de syntaxe. C'est un résultat de l' PHP grammaire, ce qui rend la suite de définition pertinente:
class_variable_declaration:
//...
| T_VARIABLE '=' static_scalar //...
;
Ainsi, lors de la définition des valeurs de variables telles que l' $path
, la valeur doit correspondre à la définition d'une statique scalaire. Sans surprise, c'est un peu un abus de langage donné que la définition de la statique scalaire comprend également les types de tableau dont les valeurs sont aussi statique scalaires:
static_scalar: /* compile-time evaluated scalars */
//...
| T_ARRAY '(' static_array_pair_list ')' // ...
//...
;
Imaginons une seconde que la grammaire est différente, et la ligne de la variable de classe delcaration de la règle cherché quelque chose de plus, comme les suivants qui correspondent à vos exemple de code (en dépit de la rupture valide affectations):
class_variable_declaration:
//...
| T_VARIABLE '=' T_ARRAY '(' array_pair_list ')' // ...
;
Après recompilation de PHP, le script d'exemple, ne serait plus échouer avec une erreur de syntaxe. Au lieu de cela, il échouera avec l'erreur de compilation "Invalid type de liaison". Depuis que le code est maintenant valide en fonction de la grammaire, cela indique qu'il y a réellement quelque chose de spécifique dans la conception du compilateur qui est la cause du problème. Pour comprendre ce que c'est, nous allons revenir à l'origine de la grammaire pour un instant et imaginez que le code de l'échantillon avaient un motif valable de cession de l' $path = array( 2 );
.
En utilisant la grammaire comme un guide, il est possible de marcher à travers les actions invoquée dans le compilateur de code lors de l'analyse de cet exemple de code. J'ai laissé des moins importantes parties, mais le processus ressemble à quelque chose comme ceci:
// ...
// 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();
Alors que le compilateur ne beaucoup de ces différentes méthodes, il est important de noter quelques petites choses.
- Un appel à l'
zend_do_begin_class_declaration()
résultats dans un appel à l' get_next_op()
. Cela signifie qu'il ajoute une nouvelle opcode pour le courant de l'opcode tableau.
-
array_init()
et zend_do_add_static_array_element()
ne génèrent pas de nouveaux opérateurs. Au lieu de cela, le tableau est immédiatement créé et ajouté à la catégorie " tableau de propriétés. Des déclarations de méthode de travail de la même façon, par l'intermédiaire d'un cas spécial en zend_do_begin_function_declaration()
.
-
zend_do_early_binding()
consomme de la dernière opcode sur le courant de l'opcode tableau, la vérification de l'un des types suivants avant de le mettre à un NOP:
- ZEND_DECLARE_FUNCTION
- ZEND_DECLARE_CLASS
- ZEND_DECLARE_INHERITED_CLASS
- ZEND_VERIFY_ABSTRACT_CLASS
- ZEND_ADD_INTERFACE
Notez que dans ce dernier cas, si l'opcode type n'est pas un de la des types, une erreur est levée – Le "Invalid type de liaison" erreur. À partir de cela, nous pouvons dire que le fait de permettre à des non-valeurs statiques pour être affecté en quelque sorte causes de la dernière opcode pour être autre chose que prévu. Donc, ce qui se passe lorsque nous utilisons un non-statique tableau avec la modification de la grammaire?
Au lieu d'appeler array_init()
, le compilateur prépare les arguments et les appels zend_do_init_array()
. Cela suppose get_next_op()
et ajoute une nouvelle INIT_ARRAY opcode, la production de quelque chose comme ce qui suit:
DECLARE_CLASS 'Foo'
SEND_VAL '.'
DO_FCALL 'realpath'
INIT_ARRAY
C'est ici que réside la racine du problème. En ajoutant ces opcodes, zend_do_early_binding()
obtient un inattendu d'entrée et lève une exception. Comme le processus du début de la liaison de la classe et des définitions de fonction semble assez partie intégrante de la compilation de PHP processus, il ne peut pas être ignoré (bien que le DECLARE_CLASS de production/consommation est une sorte de brouillon). De même, il n'est pas pratique de les essayer et d'évaluer ces opcodes en ligne (vous ne pouvez pas être sûr que la fonction ou de la classe a été résolu), donc il n'y a aucun moyen d'éviter de générer de la opcodes.
Une solution possible serait de construire un nouveau opcode tableau qui a été étendue à la classe la déclaration de la variable, semblable à la façon dont les définitions de méthode sont traitées. Le problème avec le faire est de décider d'évaluer une telle exécuter une fois la séquence. Serait-il faire lorsque le fichier contenant la classe est chargée, lorsque la propriété est d'abord accessible, ou quand un objet de ce type est-elle construite?
Comme vous l'avez souligné, d'autres langages dynamiques ont trouvé un moyen de gérer ce scénario, il n'est donc pas impossible de prendre cette décision et de le faire fonctionner. À partir de ce que je peux dire si, dans le cas de PHP ne serait pas une ligne fixe, et de la langue les concepteurs semblent avoir décidé que ce n'était pas quelque chose d'intéressant, y compris sur ce point.