Perché due costrutti?
La verità su print ed echo è che mentre appaiono agli utenti come due costrutti distinti, entrambi sono davvero sfumature di eco se si arriva alle basi, cioè si guarda al codice sorgente interno. Quel codice sorgente coinvolge il parser e i gestori opcode. Considera un'azione semplice come la visualizzazione del numero zero. Sia che utilizzi echo o print, verrà invocato lo stesso gestore "ZEND_ECHO_SPEC_CONST_HANDLER". Il gestore per la stampa fa una cosa prima di invocare il gestore per l'eco, si assicura che il valore di ritorno per la stampa sia 1, come segue:
ZVAL_LONG(&EX_T(opline->result.var).tmp_var, 1);
(vedi qui per riferimento )
Il valore restituito è utile se si desidera utilizzare la stampa in un'espressione condizionale. Perché 1 e non 100? Bene in PHP la verità di 1 o 100 è la stessa, cioè vera, mentre 0 in un contesto booleano equivale a un valore falso. In PHP tutti i valori diversi da zero (positivi e negativi) sono valori di verità e questo deriva dall'eredità Perl di PHP.
Ma, se questo è il caso, allora ci si potrebbe chiedere perché l'eco abbia più argomenti mentre print può gestirne solo uno. Per questa risposta dobbiamo passare al parser, in particolare il file zend_language_parser.y . Noterai che l'eco ha la flessibilità integrata in modo che possa stampare una o più espressioni (vedi qui ). mentre la stampa è vincolata alla stampa di una sola espressione (vedi qui ).
Sintassi
Nel linguaggio di programmazione C e nei linguaggi influenzati da esso come PHP, esiste una distinzione tra espressioni ed espressioni. Sintatticamente, echo expr, expr, ... expr
è un'istruzione while print expr
è un'espressione poiché valuta un valore. Pertanto, come altre affermazioni, echo expr
è autonomo ed è incapace di essere incluso in un'espressione:
5 + echo 6; // syntax error
Al contrario, print expr
da solo può formare una dichiarazione:
print 5; // valid
Oppure, fai parte di un'espressione:
$x = (5 + print 5); // 5
var_dump( $x ); // 6
Si potrebbe essere tentati di pensare print
come se fosse un operatore unario, come !
o ~
comunque non è un operatore. Ciò che !, ~ and print
hanno in comune è che sono tutti integrati in PHP e ognuno accetta solo un argomento. È possibile utilizzare print
per creare il seguente codice strano ma valido:
<?php
print print print print 7; // 7111
A prima vista il risultato può sembrare strano che l'ultima istruzione print stampa il suo operando di '7' prima . Ma se scavi più a fondo e osservi i codici operativi effettivi ha senso:
line # * op fetch ext return operands
---------------------------------------------------------------------------------
3 0 > PRINT ~0 7
1 PRINT ~1 ~0
2 PRINT ~2 ~1
3 PRINT ~3 ~2
4 FREE ~3
5 > RETURN 1
Il primo opcode che viene generato è quello corrispondente al 'print 7'. '~ 0' è una variabile temporanea il cui valore è 1. Tale variabile diventa e operando per il successivo opcode di stampa che a sua volta restituisce una variabile temporanea e il processo si ripete. L'ultima variabile temporanea non viene affatto utilizzata, quindi viene liberata.
Perché print
restituisce un valore e echo
non lo fa?
Le espressioni valutano valori. Ad esempio 2 + 3
restituisce 5
e abs(-10)
restituisce 10
. Poiché print expr
è esso stesso un'espressione, allora dovrebbe contenere un valore e lo fa, un valore coerente di 1
indica un risultato veritiero e restituendo un valore diverso da zero l'espressione diventa utile per l'inclusione in un'altra espressione. Ad esempio in questo frammento, il valore restituito di stampa è utile per determinare una sequenza di funzioni:
<?php
function bar( $baz ) {
// other code
}
function foo() {
return print("In and out ...\n");
}
if ( foo() ) {
bar();
}
Potresti trovare stampe di particolare valore quando si tratta di debug al volo, come illustrato nell'esempio seguente:
<?php
$haystack = 'abcde';
$needle = 'f';
strpos($haystack,$needle) !== FALSE OR print "$needle not in $haystack";
// output: f not in abcde
Come nota a margine, generalmente, le dichiarazioni non sono espressioni; non restituiscono un valore. L'eccezione, ovviamente, sono le dichiarazioni di espressione che usano print e persino espressioni semplici usate come un'istruzione, come 1;
una sintassi che PHP eredita da C. L'istruzione di espressione può sembrare strana ma è molto utile, rendendo possibile passare argomenti a funzioni.
È print
una funzione?
No, è un costrutto linguistico. Mentre tutte le chiamate di funzione sono espressioni, print (expr)
è un'espressione, nonostante l'aspetto che appare come se stesse usando la sintassi della chiamata di funzione. In verità queste parentesi sono sintassi parentesi-espressione, utile per la valutazione dell'espressione. Ciò spiega il fatto che a volte sono facoltativi se l'espressione è semplice, come ad esempio print "Hello, world!"
. Con un'espressione più complessa come print (5 ** 2 + 6/2); // 28
le parentesi aiuta la valutazione dell'espressione. A differenza dei nomi delle funzioni, print
è sintatticamente una parola chiave e semanticamente un "costrutto linguistico" .
Il termine "costrutto del linguaggio" in PHP di solito si riferisce a funzioni "pseudo" come isset
o empty
. Sebbene questi "costrutti" appaiano esattamente come funzioni, in realtà sono fexprs , ovvero gli argomenti vengono passati a loro senza essere valutati, il che richiede un trattamento speciale da parte del compilatore. print
sembra essere un fexpr che sceglie di valutare il suo argomento allo stesso modo di una funzione.
La differenza può essere vista stampando get_defined_functions()
: non è print
elencata alcuna funzione. (Anche se gli printf
amici sono: a differenza print
, sono vere funzioni.)
Perché allora la stampa (foo) funziona?
Per lo stesso motivo che echo(foo)
funziona. Queste parentesi sono piuttosto diverse dalle parentesi di chiamate di funzione perché riguardano invece espressioni. Questo è il motivo per cui uno può codificare echo ( 5 + 8 )
e può aspettarsi un risultato di 13 da visualizzare (vedi riferimento ). Queste parentesi sono coinvolte nella valutazione di un'espressione piuttosto che nel richiamare una funzione. Nota: ci sono altri usi per le parentesi in PHP, come espressioni if-condizionali, elenchi di assegnazioni, dichiarazioni di funzioni, ecc.
Perché print(1,2,3)
e echo(1,2,3)
causare errori di sintassi?
La sintassi è print expr
, echo expr
o echo expr, expr, ..., expr
. Quando PHP incontra (1,2,3)
, cerca di analizzarlo come una singola espressione e fallisce, perché a differenza di C, PHP non ha realmente un operatore di virgola binaria; la virgola serve più da separatore. (Puoi trovare una virgola binaria nei for-loop di PHP, sintassi ereditata da C.)
Semantica
L'affermazione echo e1, e2, ..., eN;
può essere intesa come zucchero sintattico per echo e1; echo e2; ...; echo eN;
.
Poiché tutte le espressioni sono affermazioni e hanno echo e
sempre gli stessi effetti collaterali di print e
e il valore restituito di print e
viene ignorato quando utilizzato come affermazione, possiamo capire echo e
come zucchero sintattico per print e
.
Queste due osservazioni significano che echo e1, e2, ..., eN;
può essere visto come zucchero sintattico per print e1; print e2; ... print eN;
. (Tuttavia, notare le differenze di runtime non semantiche di seguito).
Pertanto, dobbiamo solo definire la semantica per print
. print e
, quando valutato:
- valuta il suo singolo argomento
e
e lancia il tipo risultante in una stringa s
. (Pertanto, print e
equivale a print (string) e
.)
- Trasmette la stringa
s
al buffer di output (che alla fine verrà trasmesso allo standard output).
- Valuta l'intero
1
.
Differenze a livello di bytecode
print
comporta un piccolo sovraccarico di popolamento della variabile return (pseudocodice)
print 125;
PRINT 125,$temp ; print 125 and place 1 in $temp
UNSET $temp ; remove $temp
echo
compilazioni singole in un codice operativo:
echo 125;
ECHO 125
il valore echo
multiplo viene compilato in più codici operativi
echo 123, 456;
ECHO 123
ECHO 456
Si noti che il valore multiplo echo
non concatena i suoi argomenti, ma li genera uno per uno.
Riferimento: zend_do_print
, zend_do_echo
.
Differenze di runtime
ZEND_PRINT
è implementato come segue (pseudocodice)
PRINT var, result:
result = 1
ECHO var
Quindi sostanzialmente inserisce 1
la variabile risultato e delega il vero lavoro al ZEND_ECHO
gestore. ZEND_ECHO
fa quanto segue
ECHO var:
if var is object
temp = var->toString()
zend_print_variable(temp)
else
zend_print_variable(var)
dove zend_print_variable()
esegue la "stampa" effettiva (infatti, reindirizza semplicemente a una funzione SAPI dedicata).
Velocità: echo x
vsprint x
A differenza dell'eco , print alloca una variabile temporanea. Tuttavia, il tempo trascorso in questa attività è minimo, quindi la differenza tra questi due costrutti linguistici è trascurabile.
Velocità: echo a,b,c
vsecho a.b.c
Il primo compila fino a tre dichiarazioni separate. Il secondo valuta l'intera espressione a.b.c.
, stampa il risultato e lo dispone immediatamente. Poiché la concatenazione comporta allocazioni di memoria e copia, la prima opzione sarà più efficiente.
Quindi quale usare?
Nelle applicazioni Web, l'output è principalmente concentrato in modelli. Poiché i modelli utilizzano <?=
, che è l'alias di echo
, sembra logico attenersi anche ad echo
altre parti del codice. echo
ha un ulteriore vantaggio di poter stampare espressioni multiple senza concatenarle e non comporta un sovraccarico di popolamento di una variabile di ritorno temporanea. Quindi usa echo
.