Il codice compilatore suggerisce che questo è di progettazione, anche se I don' so qual è il ragionamento ufficiale che sta dietro. Inoltre, non sono sicuro di quanto impegno ci vorrebbe per implementare in modo affidabile questa funzionalità, ma ci sono sicuramente alcune limitazioni nel modo in cui le cose sono attualmente fatte.
Anche se la mia conoscenza del compilatore PHP non è ampia, cercherò di illustrare ciò che credo continui in modo da poter vedere dove c'è un problema. Il tuo esempio di codice rende un buon candidato per questo processo, in modo da useremo che:
class Foo {
public $path = array(
realpath(".")
);
}
Come siete ben consapevoli, questo causa un errore di sintassi. Questo è il risultato della PHP grammar, il che rende la seguente definizione rilevanti:
class_variable_declaration:
//...
| T_VARIABLE '=' static_scalar //...
;
Così, quando si definiscono i valori delle variabili come $path
, il valore atteso deve corrispondere alla definizione di uno scalare statica.Non sorprende, questo è un termine improprio dato che la definizione di uno scalare statica comprende anche tipi array i cui valori sono anche scalari statiche:
static_scalar: /* compile-time evaluated scalars */
//...
| T_ARRAY '(' static_array_pair_list ')' // ...
//...
;
Supponiamo per un secondo che la grammatica era diverso, e la linea indicato in la regola delcaration variabile di classe sembrava qualcosa di più come la seguente che sarebbe abbinare il vostro codice di esempio (nonostante la rottura assegnazioni altrimenti validi):
class_variable_declaration:
//...
| T_VARIABLE '=' T_ARRAY '(' array_pair_list ')' // ...
;
Dopo ricompilare PHP, lo script di esempio non sarebbe più fallire con quella errore di sintassi. Invece, fallirebbe con l'errore di tempo di compilazione "Tipo di binding non valido". Poiché il codice è ora valido in base alla grammatica, ciò indica che c'è effettivamente qualcosa di specifico nella progettazione del compilatore che causa problemi. Per capire di cosa si tratta, torniamo alla grammatica originale per un momento e immaginiamo che l'esempio di codice abbia un compito valido di $path = array(2);
.
Utilizzando la grammatica come guida, è possibile esaminare le azioni richiamate nello compiler code durante l'analisi di questo esempio di codice. Ho lasciato alcune parti meno importanti, ma il processo di simile a questa:
// ...
// 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();
Mentre il compilatore fa un sacco in questi vari metodi, è importante notare alcune cose.
- Una chiamata a
zend_do_begin_class_declaration()
risultati in una chiamata a get_next_op()
. Ciò significa che aggiunge un nuovo codice operativo all'array di codice operativo corrente.
array_init()
e zend_do_add_static_array_element()
non generare nuovi codici. Invece, la matrice viene immediatamente creata e aggiunta alla tabella delle proprietà della classe corrente. Le dichiarazioni di metodo funzionano in modo simile, tramite un caso speciale in zend_do_begin_function_declaration()
.
zend_do_early_binding()
consuma l'ultimo codice operativo sull'array codice operativo corrente, controllo per uno dei seguenti tipi prima di impostarlo a un NOP:
- ZEND_DECLARE_FUNCTION
- ZEND_DECLARE_CLASS
- ZEND_DECLARE_INHERITED_CLASS
- ZEND_VERIFY_ABSTRACT_CLASS
- ZEND_ADD_INTERFACE
Si noti che in quest'ultimo caso, se il tipo di codice operativo non è uno dei tipi previsti, viene generato un errore – Il "non valido tipo di rilegatura" errore . Da ciò, possiamo dire che consentire l'assegnazione dei valori non statici in qualche modo fa sì che l'ultimo codice operativo sia qualcosa di diverso dal previsto. Quindi, cosa succede quando usiamo un array non statico con la grammatica modificata?
Invece di chiamare array_init()
, il compilatore prepara gli argomenti e chiama zend_do_init_array()
. Questo a sua volta chiama get_next_op()
e aggiunge un nuovo INIT_ARRAY opcode, producendo qualcosa di simile al seguente:
DECLARE_CLASS 'Foo'
SEND_VAL '.'
DO_FCALL 'realpath'
INIT_ARRAY
Qui sta la radice del problema.Aggiungendo questi codici opzionali, zend_do_early_binding()
riceve un input imprevisto e genera un'eccezione. Poiché il processo di definizione delle classi e delle funzioni di associazione precoce sembra essere parte integrante del processo di compilazione di PHP, non può essere ignorato (sebbene la produzione/consumo di DECLARE_CLASS sia un po 'disordinato). Allo stesso modo, non è pratico provare e valutare questi opcode aggiuntivi in linea (non si può essere sicuri che una data funzione o classe sia già stata risolta), quindi non c'è modo di evitare di generare gli opcode.
Una possibile soluzione sarebbe quella di creare un nuovo array di codice operativo che fosse stato applicato alla dichiarazione della variabile di classe, analogamente a come vengono gestite le definizioni del metodo. Il problema nel fare ciò è decidere quando valutare una tale sequenza run-once. Si farebbe quando il file contenente la classe viene caricato, quando si accede per la prima volta alla proprietà o quando viene costruito un oggetto di quel tipo?
Come hai sottolineato, altri linguaggi dinamici hanno trovato un modo per gestire questo scenario, quindi non è impossibile prendere quella decisione e farlo funzionare. Da quello che posso dire però, farlo nel caso di PHP non sarebbe una correzione su una sola riga, e i progettisti linguistici sembrano aver deciso che non era qualcosa che valesse la pena includere a questo punto.
@Gordon Si prega di migliorare le risposte. – Schwern
@Schwern Per favore, migliora le domande. – Gordon
Per essere chiari, accetto quando la domanda ha una risposta definitiva. Finora tutte le risposte sono speculazioni. Utile, ma ancora congetture. – Schwern