(Questo è più lungo di quanto volessi; per favore abbi pazienza.)
La maggior parte delle lingue è costituita da qualcosa chiamato "sintassi": la lingua è composta da diverse parole chiave ben definite e l'intera gamma di espressioni che è possibile costruire in quella lingua è costituita da quella sintassi.
Ad esempio, supponiamo che tu abbia un semplice "linguaggio" aritmetico a quattro funzioni che accetta solo interi a una cifra come input e ignora completamente l'ordine delle operazioni (ti ho detto che era un linguaggio semplice). Quella lingua potrebbe essere definita dalla sintassi:
// The | means "or" and the := represents definition
$expression := $number | $expression $operator $expression
$number := 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
$operator := + | - | * | /
Da queste tre regole è possibile creare un numero qualsiasi di espressioni aritmetiche di input a una cifra. È quindi possibile scrivere un parser per questa sintassi che rompe qualsiasi input validi nei suoi tipi di componenti ( $expression
, $number
, o $operator
) e si occupa del risultato. Ad esempio, l'espressione 3 + 4 * 5
può essere suddivisa come segue:
// Parentheses used for ease of explanation; they have no true syntactical meaning
$expression = 3 + 4 * 5
= $expression $operator (4 * 5) // Expand into $exp $op $exp
= $number $operator $expression // Rewrite: $exp -> $num
= $number $operator $expression $operator $expression // Expand again
= $number $operator $number $operator $number // Rewrite again
Ora abbiamo una sintassi completamente analizzata, nel nostro linguaggio definito, per l'espressione originale. Una volta ottenuto questo, possiamo esaminare e scrivere un parser per trovare i risultati di tutte le combinazioni di $number $operator $number
, e sputare un risultato quando ne abbiamo solo uno $number
rimasto.
Tieni presente che non sono $expression
rimasti costrutti nella versione analizzata finale della nostra espressione originale. Questo perché $expression
può sempre essere ridotto a una combinazione di altre cose nella nostra lingua.
PHP è più o meno lo stesso: i costrutti del linguaggio sono riconosciuti come l'equivalente del nostro $number
o $operator
. Non possono essere ridotti ad altri costrutti linguistici ; invece, sono le unità di base da cui è costruito il linguaggio. La differenza fondamentale tra funzioni e costrutti del linguaggio è questa: il parser si occupa direttamente dei costrutti del linguaggio. Semplifica le funzioni in costrutti linguistici.
Il motivo per cui i costrutti del linguaggio possono o meno richiedere le parentesi e il motivo per cui alcuni hanno valori di ritorno mentre altri non dipendono interamente dai dettagli tecnici specifici dell'implementazione del parser PHP. Non sono molto esperto di come funziona il parser, quindi non posso rispondere a queste domande in modo specifico, ma immagina per un secondo un linguaggio che inizi con questo:
$expression := ($expression) | ...
In effetti, questa lingua è libera di prendere qualsiasi espressione trova e di eliminare le parentesi circostanti. PHP (e qui sto impiegando pura supposizione) potrebbe impiegare qualcosa di simile per i suoi costrutti del linguaggio: print("Hello")
potrebbe essere ridotto a print "Hello"
prima di essere analizzato, o viceversa (le definizioni del linguaggio possono aggiungere parentesi e sbarazzarsene).
Questa è la radice del motivo per cui non è possibile ridefinire i costrutti del linguaggio come echo
o print
: sono effettivamente codificati nel parser, mentre le funzioni sono mappate a un insieme di costrutti del linguaggio e il parser consente di modificare tale mappatura in fase di compilazione o runtime in sostituire il proprio insieme di costrutti o espressioni linguistiche.
Alla fine della giornata, la differenza interna tra costrutti ed espressioni è questa: i costrutti del linguaggio sono compresi e trattati dal parser. Le funzioni incorporate, sebbene fornite dal linguaggio, vengono mappate e semplificate in un insieme di costrutti del linguaggio prima dell'analisi.
Ulteriori informazioni:
- Modulo Backus-Naur , la sintassi utilizzata per definire i linguaggi formali (yacc usa questo modulo)
Modifica: leggendo alcune delle altre risposte, le persone fanno buoni punti. Tra loro:
- Un linguaggio incorporato è più veloce da chiamare rispetto a una funzione. Questo è vero, anche se solo marginalmente, perché l'interprete PHP non ha bisogno di mappare quella funzione ai suoi equivalenti incorporati nel linguaggio prima dell'analisi. Su una macchina moderna, tuttavia, la differenza è abbastanza trascurabile.
- Un linguaggio incorporato ignora il controllo degli errori. Questo può o non può essere vero, a seconda dell'implementazione interna di PHP per ogni builtin. È certamente vero che il più delle volte, le funzioni avranno un controllo degli errori più avanzato e altre funzionalità che i builtin non hanno.
- I costrutti del linguaggio non possono essere utilizzati come callback di funzioni. Questo è vero, perché un costrutto non è una funzione . Sono entità separate. Quando si codifica un builtin, non si codifica una funzione che accetta argomenti: la sintassi del builtin viene gestita direttamente dal parser ed è riconosciuta come builtin, piuttosto che come funzione. (Questo può essere più facile da capire se consideri i linguaggi con funzioni di prima classe: effettivamente, puoi passare le funzioni come oggetti. Non puoi farlo con i builtin.)