Qual è la differenza tra un costrutto di linguaggio e una funzione "incorporata" in PHP?


92

So che include, isset, require, print, echo, e alcuni altri non sono funzioni ma costrutti del linguaggio.

Alcuni di questi costrutti linguistici necessitano di parentesi, altri no.

require 'file.php';
isset($x);

Alcuni hanno un valore di ritorno, altri no.

print 'foo'; //1
echo  'foo'; //no return value

Allora qual è la differenza interna tra un costrutto di linguaggio e una funzione incorporata?

Risposte:


131

(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 * 5può 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 $numberrimasto.

Tieni presente che non sono $expressionrimasti costrutti nella versione analizzata finale della nostra espressione originale. Questo perché $expressionpuò 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 $numbero $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 echoo 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.)

2
Ottima risposta abbastanza aperta da poter essere applicata a molte lingue, non solo a PHP. Grazie!
Levi Botelho

15

I costrutti del linguaggio sono forniti dal linguaggio stesso (come istruzioni come "if", "while", ...); da qui il loro nome.

Una conseguenza di ciò è che sono più veloci da invocare rispetto alle funzioni predefinite o definite dall'utente (o almeno così ho sentito / letto più volte)

Non ho idea di come sia fatto, ma una cosa che possono fare (essendo integrati direttamente nel langage) è "bypassare" una sorta di meccanismo di gestione degli errori. Ad esempio, isset () può essere utilizzato con variabili inesistenti senza causare alcun avviso, avviso o errore.

function test($param) {}
if (test($a)) {
    // Notice: Undefined variable: a
}

if (isset($b)) {
    // No notice
}

* Nota che non è il caso dei costrutti di tutte le lingue.

Un'altra differenza tra funzioni e costrutti del linguaggio è che alcuni di questi possono essere chiamati senza parentesi, come una parola chiave.

Per esempio :

echo 'test'; // language construct => OK

function my_function($param) {}
my_function 'test'; // function => Parse error: syntax error, unexpected T_CONSTANT_ENCAPSED_STRING

Anche qui non è il caso di tutti i costrutti linguistici.

Suppongo che non ci sia assolutamente alcun modo per "disabilitare" un costrutto di linguaggio perché fa parte del linguaggio stesso. D'altra parte, molte funzioni PHP "integrate" non sono realmente integrate perché sono fornite da estensioni in modo che siano sempre attive (ma non tutte)

Un'altra differenza è che i costrutti del linguaggio non possono essere usati come "puntatori a funzione" (intendo, callback, per esempio):

$a = array(10, 20);

function test($param) {echo $param . '<br />';}
array_map('test', $a);  // OK (function)

array_map('echo', $a);  // Warning: array_map() expects parameter 1 to be a valid callback, function 'echo' not found or invalid function name

Non ho altra idea che mi venga in mente in questo momento ... e non so molto degli interni di PHP ... Quindi sarà proprio ora ^^

Se non ottieni molte risposte qui, forse potresti chiederlo agli interni della mailing list (vedi http://www.php.net/mailing-lists.php ), dove ci sono molti sviluppatori PHP core; sono quelli che probabilmente saprebbero di quella roba ^^

(E sono davvero interessato alle altre risposte, btw ^^)

Come riferimento: elenco di parole chiave e costrutti linguistici in PHP


È possibile avere una funzione che accetta una variabile non impostata senza generare un avviso prendendo la variabile per riferimento. Questo non è limitato ai costrutti del linguaggio come isset ().
Tom Haigh

Oh, non ci ho pensato :-( Grazie!
Pascal MARTIN

4

Dopo aver attraversato il codice, ho scoperto che php analizza alcune istruzioni in un file yacc. Quindi sono casi speciali.

(vedi Zend / zend_language_parser.y)

A parte questo, non credo che ci siano altre differenze.


1

È possibile sovrascrivere le funzioni integrate . Le parole chiave sono per sempre.


Non è una funzione incorporata. È definito nell'estensione APD (Advanced PHP Debugger).
Ionuț G. Stan

riguardo alle funzioni di override, potresti avere un bottino all'estensione runkit (non è neanche core, è un'estensione, quindi non risponde all'OP, ma solo a questa risposta); è davvero potente e più recente dell'APD (e credo di aver sentito qualche tempo fa che alcune persone ci stavano ancora lavorando, anche se non è mostrato su pecl.php.net)
Pascal MARTIN
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.