Warum zwei Konstrukte?
Die Wahrheit über Druck und Echo ist, dass sie den Benutzern zwar als zwei unterschiedliche Konstrukte erscheinen, aber beide wirklich Echo-Schattierungen sind, wenn Sie sich auf die Grundlagen konzentrieren, dh den internen Quellcode betrachten. Dieser Quellcode umfasst sowohl den Parser als auch die Opcode-Handler. Stellen Sie sich eine einfache Aktion vor, z. B. die Anzeige der Zahl Null. Unabhängig davon, ob Sie Echo oder Print verwenden, wird derselbe Handler "ZEND_ECHO_SPEC_CONST_HANDLER" aufgerufen. Der Handler für den Druck führt eine Aktion aus, bevor er den Handler für das Echo aufruft. Er stellt sicher, dass der Rückgabewert für den Druck wie folgt 1 ist:
ZVAL_LONG(&EX_T(opline->result.var).tmp_var, 1);
(siehe hier als Referenz )
Der Rückgabewert ist eine Annehmlichkeit, wenn Sie print in einem bedingten Ausdruck verwenden möchten. Warum 1 und nicht 100? Nun, in PHP ist die Wahrhaftigkeit von 1 oder 100 dieselbe, dh wahr, während 0 in einem booleschen Kontext einem falschen Wert entspricht. In PHP sind alle Nicht-Null-Werte (positiv und negativ) wahrheitsgemäße Werte, und dies ergibt sich aus dem Perl-Erbe von PHP.
Wenn dies jedoch der Fall ist, kann man sich fragen, warum Echo mehrere Argumente akzeptiert, während print nur eines verarbeiten kann. Für diese Antwort müssen wir uns an den Parser wenden, insbesondere an die Datei zend_language_parser.y . Sie werden feststellen, dass in Echo die Flexibilität eingebaut ist, dass ein oder mehrere Ausdrücke gedruckt werden können (siehe hier ). Während print nur einen Ausdruck drucken darf (siehe dort ).
Syntax
In der Programmiersprache C und von ihr beeinflussten Sprachen wie PHP wird zwischen Anweisungen und Ausdrücken unterschieden. Syntaktisch echo expr, expr, ... expr
ist eine Anweisung while print expr
ein Ausdruck, da sie einen Wert ergibt. Daher steht es wie andere Aussagen echo expr
für sich und kann nicht in einen Ausdruck aufgenommen werden:
5 + echo 6; // syntax error
Im Gegensatz dazu print expr
kann allein eine Aussage bilden:
print 5; // valid
Oder sei Teil eines Ausdrucks:
$x = (5 + print 5); // 5
var_dump( $x ); // 6
Man könnte versucht sein, sich vorzustellen, print
als wäre es ein unärer Operator, wie !
oder ~
aber es ist kein Operator. Was !, ~ and print
gemeinsam haben , ist , dass sie alle in PHP gebaut sind und jeweils nur ein Argument. Sie können print
den folgenden seltsamen, aber gültigen Code erstellen:
<?php
print print print print 7; // 7111
Auf den ersten Blick kann das Ergebnis seltsam erscheinen , dass der letzte print - Anweisung druckt seine Operanden von ‚7‘ zuerst . Wenn Sie jedoch tiefer graben und sich die tatsächlichen Opcodes ansehen, ist dies sinnvoll:
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
Der allererste Opcode, der generiert wird, entspricht dem 'print 7'. Die '~ 0' ist eine temporäre Variable mit dem Wert 1. Diese Variable wird zum Operanden für den nächsten Druck-Opcode, der wiederum eine temporäre Variable zurückgibt und den Vorgang wiederholt. Die letzte temporäre Variable wird überhaupt nicht verwendet, sondern freigegeben.
Warum wird print
ein Wert zurückgegeben und echo
nicht?
Ausdrücke werden zu Werten ausgewertet. Zum Beispiel 2 + 3
bewertet zu 5
und abs(-10)
bewertet zu 10
. Da print expr
es sich selbst um einen Ausdruck handelt, sollte er einen Wert enthalten, und ein konsistenter Wert von 1
zeigt ein wahrheitsgemäßes Ergebnis an. Durch die Rückgabe eines Werts ungleich Null wird der Ausdruck für die Aufnahme in einen anderen Ausdruck nützlich. In diesem Snippet ist beispielsweise der Rückgabewert von print hilfreich, um eine Funktionssequenz zu bestimmen:
<?php
function bar( $baz ) {
// other code
}
function foo() {
return print("In and out ...\n");
}
if ( foo() ) {
bar();
}
Möglicherweise finden Sie beim schnellen Debuggen einen Ausdruck von besonderem Wert, wie das nächste Beispiel zeigt:
<?php
$haystack = 'abcde';
$needle = 'f';
strpos($haystack,$needle) !== FALSE OR print "$needle not in $haystack";
// output: f not in abcde
Als Randnotiz sind Aussagen im Allgemeinen keine Ausdrücke; Sie geben keinen Wert zurück. Die Ausnahme bilden natürlich Ausdrucksanweisungen, die print verwenden, und sogar einfache Ausdrücke, die als Anweisung verwendet werden, z. B. 1;
eine Syntax, die PHP von C erbt. Die Ausdrucksanweisung mag seltsam aussehen, ist jedoch sehr hilfreich und ermöglicht die Übergabe von Argumenten an Funktionen.
Ist print
eine Funktion?
Nein, es ist ein Sprachkonstrukt. Während alle Funktionsaufrufe Ausdrücke sind, print (expr)
handelt es sich trotz der visuellen Darstellung um einen Ausdruck, der so aussieht, als würde die Funktionsaufrufsyntax verwendet. In Wahrheit sind diese Klammern eine Klammern-Ausdruckssyntax, die für die Auswertung von Ausdrücken nützlich ist. Dies erklärt die Tatsache, dass sie manchmal optional sind, wenn der Ausdruck einfach ist, wie z print "Hello, world!"
. Bei einem komplexeren Ausdruck wie print (5 ** 2 + 6/2); // 28
den Klammern hilft die Bewertung des Ausdrucks. Im Gegensatz zu Funktionsnamen print
ist es syntaktisch ein Schlüsselwort und semantisch ein "Sprachkonstrukt" .
Der Begriff "Sprachkonstrukt" in PHP bezieht sich normalerweise auf "Pseudo" -Funktionen wie isset
oder empty
. Obwohl diese "Konstrukte" genau wie Funktionen aussehen, handelt es sich tatsächlich um Fexprs , dh die Argumente werden an sie übergeben, ohne ausgewertet zu werden, was eine besondere Behandlung durch den Compiler erfordert. print
zufällig ein fexpr, der sein Argument auf die gleiche Weise wie eine Funktion bewertet.
Der Unterschied ist beim Drucken erkennbar get_defined_functions()
: Es ist keine print
Funktion aufgeführt. (Obwohl printf
und Freunde sind: im Gegensatz zu print
, sind sie wahre Funktionen.)
Warum funktioniert print (foo) dann?
Aus dem gleichen Grund echo(foo)
funktioniert das. Diese Klammern unterscheiden sich erheblich von Klammern für Funktionsaufrufe, da sie sich stattdessen auf Ausdrücke beziehen. Aus diesem Grund kann man codieren echo ( 5 + 8 )
und erwarten, dass ein Ergebnis von 13 angezeigt wird (siehe Referenz ). Diese Klammern dienen dazu, einen Ausdruck auszuwerten, anstatt eine Funktion aufzurufen. Hinweis: Es gibt andere Verwendungszwecke für Klammern in PHP, z. B. wenn if-bedingte Ausdrücke, Zuweisungslisten, Funktionsdeklarationen usw.
Warum print(1,2,3)
und echo(1,2,3)
führen zu Syntaxfehlern?
Die Syntax ist print expr
, echo expr
oder echo expr, expr, ..., expr
. Wenn PHP auf etwas trifft (1,2,3)
, versucht es, es als einzelnen Ausdruck zu analysieren, und schlägt fehl, da PHP im Gegensatz zu C nicht wirklich über einen binären Kommaoperator verfügt. Das Komma dient eher als Trennzeichen. (Möglicherweise finden Sie dennoch ein binäres Komma in den for-Schleifen von PHP, dessen Syntax von C geerbt wurde.)
Semantik
Die Aussage echo e1, e2, ..., eN;
kann als syntaktischer Zucker für verstanden werden echo e1; echo e2; ...; echo eN;
.
Da alle Ausdrücke Anweisungen sind und echo e
immer die gleichen Nebenwirkungen haben wie print e
und der Rückgabewert von print e
ignoriert wird, wenn er als Anweisung verwendet wird, können wir echo e
als syntaktischen Zucker für verstehen print e
.
Diese beiden Beobachtungen bedeuten, dass echo e1, e2, ..., eN;
als syntaktischer Zucker für angesehen werden kann print e1; print e2; ... print eN;
. (Beachten Sie jedoch die folgenden nicht-semantischen Laufzeitunterschiede.)
Wir müssen daher nur die Semantik für definieren print
. print e
, wenn bewertet:
- wertet sein einzelnes Argument aus
e
und wandelt den resultierenden Wert in eine Zeichenfolge um s
. (Also print e
ist äquivalent zu print (string) e
.)
- Streamen Sie die Zeichenfolge
s
in den Ausgabepuffer (der schließlich in die Standardausgabe gestreamt wird).
- Wertet die ganze Zahl aus
1
.
Unterschiede auf Bytecode-Ebene
print
Das Auffüllen der Rückgabevariablen (Pseudocode) ist mit einem geringen Aufwand verbunden.
print 125;
PRINT 125,$temp ; print 125 and place 1 in $temp
UNSET $temp ; remove $temp
Single echo
kompiliert zu einem Opcode:
echo 125;
ECHO 125
Multi-Value- echo
Kompilierungen zu mehreren Opcodes
echo 123, 456;
ECHO 123
ECHO 456
Beachten Sie, dass Multi-Value echo
seine Argumente nicht verkettet, sondern einzeln ausgibt.
Referenz: zend_do_print
, zend_do_echo
.
Laufzeitunterschiede
ZEND_PRINT
wird wie folgt implementiert (Pseudocode)
PRINT var, result:
result = 1
ECHO var
Es fügt also 1
im Grunde die Ergebnisvariable ein und delegiert den eigentlichen Job an den ZEND_ECHO
Handler. ZEND_ECHO
macht folgendes
ECHO var:
if var is object
temp = var->toString()
zend_print_variable(temp)
else
zend_print_variable(var)
Dabei wird zend_print_variable()
der eigentliche "Druck" ausgeführt (tatsächlich wird lediglich zu einer dedizierten SAPI-Funktion umgeleitet).
Geschwindigkeit: echo x
vs.print x
Im Gegensatz zu Echo weist print eine temporäre Variable zu. Die für diese Aktivität aufgewendete Zeit ist jedoch winzig, so dass der Unterschied zwischen diesen beiden Sprachkonstrukten vernachlässigbar ist.
Geschwindigkeit: echo a,b,c
vs.echo a.b.c
Die erste besteht aus drei separaten Anweisungen. Der zweite wertet den gesamten Ausdruck aus a.b.c.
, druckt das Ergebnis und entsorgt es sofort. Da die Verkettung Speicherzuweisungen und das Kopieren umfasst, ist die erste Option effizienter.
Also welches?
In Webanwendungen konzentriert sich die Ausgabe hauptsächlich auf Vorlagen. Da Vorlagen verwendet werden <?=
, deren Alias dies ist echo
, erscheint es logisch, sich auch echo
in anderen Teilen des Codes zu halten. echo
hat den zusätzlichen Vorteil, dass mehrere Ausdrücke gedruckt werden können, ohne sie zu verketten, und es ist kein Aufwand erforderlich, eine temporäre Rückgabevariable zu füllen. Also, benutze echo
.