参考:比较PHP的打印和回显


182

PHP print和的有echo什么区别?

堆栈溢出有许多关于PHP printecho关键字用法的问题。

这篇文章的目的是提供 有关PHP 和关键字的规范参考问答,并比较它们的区别和用例。printecho


3
我认为这还不足以提供打印返回值。打印对于快速调试输出或类似的东西很有用:strpos($ x,$ y)!== FALSE或打印“东西”。快速打字,易于阅读。由于某种原因,“正在打印一个函数”很难看懂(您的论点似乎...奇怪而又不明显)-它是语言构造,您无法使用它做的更糟的事情:可变函数。
XzKto 2011年

1
为了保持这种开放性,这里需要做的是:1.分为问答。2.参考/链接到有关Stack Overflow的主题的现有内容(有点像这里:stackoverflow.com/questions/3737139/…),但在答案中。3.需要成为CW。
凯夫(Kev)

“相关列”很好,但不是很集中。为了增加其作为规范参考问题和答案的价值,还应该对其进行深入研究,并与其他特定的良好答案进行链接可以增加价值。
凯夫

这个问题确实需要是一个实际的问题。完成后,我可以向其添加有关规范部分的横幅。
蒂姆·波斯特

Answers:


185

为什么要构造两个?

关于打印回显的事实是,尽管它们以两种截然不同的方式呈现给用户,但如果您了解基础知识(即查看内部源代码),它们实际上都是回显的阴影。该源代码涉及解析器以及操作码处理程序。考虑一个简单的动作,例如显示数字零。无论使用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具有内置的灵活性,因此它可以打印一个或多个表达式(请参阅此处)。而打印被限制为仅打印一个表达(参见那里)。

句法

在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

乍一看,最后打印语句打印出它的“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

生成的第一个操作码是与“打印7”相对应的操作码。“〜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)但尽管看起来很像使用函数调用语法,但它还是一个表达式。实际上,这些括号是括号表达式表达式,可用于表达式求值。这说明了以下事实:如果表达式是简单表达式(例如),它们有时是可选的print "Hello, world!"。对于更复杂的表达式,例如print (5 ** 2 + 6/2); // 28括号,可以帮助评估表达式。与函数名称不同,print语法上是关键字,语义上是“语言构造”

PHP中的“语言构造”一词通常是指“伪”函数,例如issetempty。尽管这些“构造”看起来与函数完全相似,但它们实际上是fexprs,即,参数未经评估就传递给它们,这需要编译器进行特殊处理。print碰巧是一个fexpr,它选择以与函数相同的方式求值。

通过打印可以看出区别get_defined_functions():没有print列出功能。(尽管printf和朋友是:不像print,它们是真正的功能。)

那么为什么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;。(但是,请注意下面的非语义运行时差异。)

因此,我们只需定义的语义printprint e,评估时:

  1. 计算其单个参数e,并将结果值类型转换为string 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了结果变量,并将实际作业委托给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 xvsprint x

echo不同,print分配一个临时变量。但是,在此活动上花费的时间微不足道,因此这两种语言结构之间的差异可以忽略不计。

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

第一个编译为三个独立的语句。第二个计算整个表达式a.b.c.,打印结果并立即处理。由于级联涉及内存分配和复制,因此第一种选择将更加有效。

那么使用哪一个呢?

在Web应用程序中,输出大部分集中在模板中。由于模板使用<?=,这是的别名echo,因此echo在代码的其他部分也遵循逻辑是合乎逻辑的。echo另一个优点是能够打印多个表达式而无需将它们连接在一起,并且不涉及填充临时返回变量的开销。因此,请使用echo


5
我对此进行了广泛的编辑和澄清,并添加了关于语义的部分。我对此很有信心,但有人可以仔细检查。
jameshfisher 2013年

1
那么,这是否意味着最好将其echo $a,$b,$c用于字符串var的串联?老实说,我从未见过这种用法。
geoff 2013年

1
描述功能差异的TL; DR部分如何?像:print只允许一个参数,echo可以有多个;echo可以print并返回... 时不能成为表达式的一部分;可能是一些性能摘要。以及所有这些“为什么”和“
幕后

0

我知道我来晚了,但是我想补充的一件事是在我的代码中

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

给出错误“语法错误,意外的'echo'(T_ECHO)”

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

工作良好。

关于回声文档说,“回声(不像其他一些语言结构)不会表现得像一个函数” 在这里 ,但有关打印文档还表示,“印刷实际上不是一个真正的功能(这是一个语言结构)” 在这里。所以我不确定为什么。关于echo和print的文档也说:“ echo的主要区别是print只接受一个参数,并且总是返回1。” 这里

如果有人可以阐明这种行为,我将很高兴。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.