参考文献: 比较 PHP 的 print 和 echo

PHP 的 printecho有什么不同?

StackOverflow 有许多关于 PHP 的 printecho关键字使用的问题。

这篇文章的目的是提供一个关于 PHP 的 printecho关键字的规范的 参考文献问题和答案,并比较它们之间的差异和用例。

15589 次浏览

为什么是两种结构?

关于 打印Echo的事实是,虽然它们在用户看来是两个不同的结构,但是如果你深入到基本的问题,比如说查看内部源代码,它们实际上都是 echo 的阴影。源代码包括解析器和操作码处理程序。考虑一个简单的操作,比如显示数字零。无论使用 echo 还是 print,都将调用相同的处理程序“ ZEND _ ECHO _ SPEC _ CONST _ HANDLER”。Print 的处理程序在调用 echo 的处理程序之前做一件事,它确保 print 的返回值为1,如下所示:

ZVAL_LONG(&EX_T(opline->result.var).tmp_var, 1);

(见 以供参考)

如果希望在条件表达式中使用 print,那么返回值是一个方便的选择。为什么是1而不是100?在 PHP 中,1或100的真值是相同的,即 true,而在布尔上下文中,0等同于一个假值。在 PHP 中,所有非零值(正值和负值)都是真值,这源于 PHP 的 Perl 遗产。

但是,如果是这种情况,那么人们可能想知道为什么 echo 采用多个参数,而 print 只能处理一个参数。对于这个答案,我们需要转向解析器,特别是文件 Zend _ language _ parser. y。您将注意到 echo 具有内置的灵活性,因此它可以打印一个或多个表达式(参见 给你)。而 print 只能打印一个表达式(参见 那里)。

语法

在 C 编程语言和受其影响的语言(如 PHP)中,语句和表达式是有区别的。在语法上,echo expr, expr, ... expr是一个语句,而 print expr是一个表达式,因为它的计算结果是一个值。因此,与其他语句一样,echo expr是独立的,不能包含在一个表达式中:

5 + echo 6;   // syntax error

相比之下,print expr可以单独形成一种声明:

print 5; // valid

或者,成为一个表达的一部分:

   $x = (5 + print 5); // 5
var_dump( $x );     // 6

人们可能会把 print想象成一元运算符,就像 !~一样,但它不是运算符。!, ~ and print的共同之处在于它们都内置在 PHP 中,每个参数只有一个参数。您可以使用 print创建以下奇怪但有效的代码:

    <?php
print print print print 7; // 7111

乍一看,最后一个 print 语句打印的操作数是’7’第一,这个结果可能看起来很奇怪。但是,如果你深入研究一下实际的操作码,你就会明白:

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

生成的第一个操作码是对应于“ print7”的操作码。“ ~ 0”是一个临时变量,其值为1。该变量成为下一个打印操作码的操作数,而下一个打印操作码又返回一个临时变量,这个过程重复进行。最后一个临时变量根本没有被使用,所以它被释放了。

为什么 print返回一个值而 echo不返回?

计算值的表达式。例如,2 + 3的计算结果为 5,而 abs(-10)的计算结果为 10。由于 print expr本身就是一个表达式,因此它应该保存一个值,而且它确实保存了,一致的 1值表示一个真实的结果,通过返回一个非零值,表达式对于包含在另一个表达式中变得有用。例如,在此代码片段中,print 的返回值在确定函数序列时非常有用:

<?php


function bar( $baz ) {
// other code
}
function foo() {
return print("In and out ...\n");
}


if ( foo() ) {


bar();
}

在动态调试时,您可能会发现具有特殊价值的打印,如下面的示例所示:

<?php
$haystack = 'abcde';
$needle = 'f';
strpos($haystack,$needle) !== FALSE OR print "$needle not in $haystack";


// output: f not in abcde

作为一个旁注,通常,语句不是表达式; 它们不返回值。当然,例外情况是使用 print 甚至简单表达式作为语句的表达式语句,例如 1;,这是 PHP 从 C 继承的语法。表达式语句可能看起来很奇怪,但它非常有用,使得向函数传递参数成为可能。

print是一个函数吗?

不,这是一种语言结构。虽然所有的函数调用都是表达式,但是 print (expr)是一个表达式,尽管它看起来像是在使用函数调用语法。实际上,这些括号是括号 -expr 语法,对于表达式计算非常有用。这解释了这样一个事实: 如果表达式是一个简单的表达式,比如 print "Hello, world!",那么有时它们是可选的。对于更复杂的表达式(如 print (5 ** 2 + 6/2); // 28) ,括号有助于计算表达式。与函数名不同,print在语法上是一个关键字和语义上的 “语言结构”

PHP 中的术语“语言结构”通常指的是“伪”函数,如 issetempty。尽管这些“构造”看起来与函数完全一样,但它们实际上是 Fexprs,也就是说,参数传递给它们时没有进行计算,这需要编译器进行特殊处理。print恰好是一个 fexpr,它选择以与函数相同的方式计算参数。

通过打印 get_defined_functions()可以看出差异: 没有列出 print函数。(虽然 printf和朋友是: 不像 print,他们是真正的功能。)

那么为什么打印(foo)有效呢?

echo(foo)的工作原理一样。这些括号与函数调用括号完全不同,因为它们属于表达式。这就是为什么我们可以编码 echo ( 5 + 8 ),并且可以期望显示13的结果(参见 参考文献)。这些括号用于计算表达式而不是调用函数。注意: 在 PHP 中括号还有其他用途,比如 if 条件表达式、赋值列表、函数声明等等。

为什么 print(1,2,3)echo(1,2,3)会导致语法错误?

语法是 print exprecho exprecho expr, expr, ..., expr。当 PHP 遇到 (1,2,3)时,它尝试将其解析为单个表达式,但是失败了,因为与 C 不同,PHP 实际上没有一个二进制逗号运算符; 逗号更多地起到分隔符的作用。(尽管如此,您可以在 PHP 的 for 循环中找到一个二进制逗号,它的语法继承自 C。)

语义学

语句 echo e1, e2, ..., eN;可以理解为 echo e1; echo e2; ...; echo eN;的句法糖。

由于所有的表达式都是语句,而且 echo e总是具有与 print e相同的副作用,并且在将 print e用作语句时忽略返回值,因此我们可以将 echo e理解为 print e的语法糖。

这两个观察结果意味着 echo e1, e2, ..., eN;可以被看作是 print e1; print e2; ... print eN;的句法糖。(然而,请注意下面的非语义运行时差异。)

因此,我们只需要为 print.print e定义语义,在计算时:

  1. 将其单个参数 e模型铸造的结果值计算为字符串 s(因此,print e等效于 print (string) e)
  2. 将字符串 s流到 输出缓冲器输出缓冲器(最终将流到标准输出)。
  3. 计算为整数 1

字节码级别的差异

print涉及填充返回变量(伪代码)的小开销

print 125;


PRINT  125,$temp     ; print 125 and place 1 in $temp
UNSET  $temp         ; remove $temp

单个 echo编译成一个操作码:

echo 125;


ECHO 125

多值 echo编译成多个操作码

echo 123, 456;


ECHO 123
ECHO 456

注意,多值 echo不连接它的参数,而是逐个输出它们。

参考文献: zend_do_printzend_do_echo

运行时间差异

ZEND_PRINT 的实现如下(伪代码)

PRINT  var, result:


result = 1
ECHO var

因此,它基本上将 1放在 result 变量中,并将实际作业委托给 ZEND_ECHO处理程序。ZEND_ECHO执行以下操作

ECHO var:


if var is object
temp = var->toString()
zend_print_variable(temp)
else
zend_print_variable(var)

其中 zend_print_variable()执行实际的“打印”(实际上,它只是重定向到一个专用的 SAPI 函数)。

速度: echo x vs print x

Echo不同,打印分配一个临时变量。然而,花在这个活动上的时间很少,所以这两种语言结构之间的差异是可以忽略不计的。

速度: echo a,b,c vs echo a.b.c

第一个语句汇编成三个独立的语句。第二个程序计算整个表达式 a.b.c.,打印结果并立即处理它。因为连接涉及到内存分配和复制,所以第一个选项会更有效。

那么用哪一个呢?

在 Web 应用程序中,输出主要集中在模板中。因为模板使用 <?=,这是 echo的别名,所以在代码的其他部分也遵循 echo似乎是合乎逻辑的。echo还有一个额外的优势,它可以打印多个表达式而不需要连接它们,而且不需要填充临时返回变量。所以,使用 echo

print可以作为语句存在,而不需要; ,这就是我们可以将它包含在 $b; print "TRUE": print "FALSE";中的原因