Riferimento: confronto tra stampa ed eco di PHP


182

Qual è la differenza tra PHP printe echo?

Stack Overflow ha molte domande che pongono sull'utilizzo di PHP printe echoparole chiave.

Lo scopo di questo post è di fornire una domanda di riferimento canonica e una risposta su PHP printe echoparole chiave e confrontare le loro differenze e casi d'uso.


3
Penso che qui non sia abbastanza il valore di ritorno della stampa. Stampa è utile per un output di debug veloce o qualcosa del genere: strpos ($ x, $ y)! == FALSE O stampa "qualcosa". Veloce da scrivere e buono da leggere. E "Is print a function" è stato scomodo da leggere per qualche motivo (il tuo argomento sembra ... strano e non ovvio) - è il costrutto del linguaggio, c'è una cosa molto peggiore che non puoi farci: funzioni variabili.
XzKto

1
Per tenerlo aperto ciò che deve essere fatto qui è: 1. diviso in una domanda e risposta. 2. Riferimento / collegamento a contenuti esistenti sull'argomento in Stack Overflow (un po 'come qui: stackoverflow.com/questions/3737139/… ) ma nella risposta. 3. Deve essere in CW.
Kev,

La "Colonna correlata" va bene, ma non è molto focalizzata. Per aumentare il suo valore come una domanda di riferimento canonica e una risposta, allora dovrebbe anche essere ben studiato e collegamenti ad altre buone risposte specifiche aggiungerebbero valore.
Kev,

La domanda deve davvero essere una vera domanda . Posso aggiungere un banner ad esso riguardante la parte canonica una volta che hai finito.
Tim Post

Risposte:


185

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:

  1. valuta il suo singolo argomento ee lancia il tipo risultante in una stringa s. (Pertanto, print eequivale a print (string) e.)
  2. Trasmette la stringa sal buffer di output (che alla fine verrà trasmesso allo standard output).
  3. 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.


5
Ho ampiamente modificato e chiarito questo, e ho aggiunto una sezione sulla semantica. Sono abbastanza fiducioso, ma qualcuno potrebbe ricontrollarlo.
jameshfisher,

1
Questo significa, quindi, che è meglio usare echo $a,$b,$cper la concatenazione di variabili stringa? Onestamente non l'ho mai visto in uso.
geoff,

1
Che ne dici di una sezione TL; DR che descriva le differenze funzionali? Come: printconsente solo un argomento, echopuò avere più; echonon può far parte di un'espressione mentre printcan e restituisce ...; e potrebbe essere un riassunto delle prestazioni. E tutte quelle parti "perché" e "sotto il cofano" dopo
YakovL

0

So di essere in ritardo, ma una cosa che vorrei aggiungere è quella nel mio codice

 $stmt = mysqli_stmt_init($connection) or echo "error at init"; 

dà un errore "errore di sintassi, 'echo' (T_ECHO) imprevisto"

mentre

 $stmt = mysqli_stmt_init($connection) or print "error at init";

funziona bene.

I documenti su echo dicono che "l'eco (a differenza di altri costrutti del linguaggio) non si comporta come una funzione" qui ma su documenti di stampa dice anche "la stampa non è in realtà una funzione reale (è un costrutto di linguaggio)" qui . Quindi non sono sicuro del perché. E anche sui documenti echo e print dice "Le principali differenze con l'eco sono che print accetta solo un singolo argomento e restituisce sempre 1". Qui

Sarei felice se qualcuno potesse far luce su questo comportamento.

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.