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 exprda 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 printcome se fosse un operatore unario, come !o ~comunque non è un operatore. Ciò che !, ~ and printhanno in comune è che sono tutti integrati in PHP e ognuno accetta solo un argomento. È possibile utilizzare printper 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é printrestituisce un valore e echonon lo fa?
Le espressioni valutano valori. Ad esempio 2 + 3restituisce 5e abs(-10)restituisce 10. Poiché print exprè esso stesso un'espressione, allora dovrebbe contenere un valore e lo fa, un valore coerente di 1indica 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.
È printuna 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); // 28le 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 isseto 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. printsembra 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 è printelencata alcuna funzione. (Anche se gli printfamici 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 expro 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 esempre gli stessi effetti collaterali di print ee il valore restituito di print eviene ignorato quando utilizzato come affermazione, possiamo capire echo ecome 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
ee lancia il tipo risultante in una stringa s. (Pertanto, print eequivale a print (string) e.)
- Trasmette la stringa
sal 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
echocompilazioni singole in un codice operativo:
echo 125;
ECHO 125
il valore echomultiplo viene compilato in più codici operativi
echo 123, 456;
ECHO 123
ECHO 456
Si noti che il valore multiplo echonon 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 1la variabile risultato e delega il vero lavoro al ZEND_ECHOgestore. ZEND_ECHOfa 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 xvsprint 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,cvsecho 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 echoaltre parti del codice. echoha 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.