Why two constructs?
The truth about print and echo is that while they appear to users as two distinct constructs, they are both really shades of echo if you get down to basics, i.e. look at the internal source code. That source code involves the parser as well as opcode handlers. Consider a simple action such as displaying the number zero. Whether you use echo or print, the same handler " ZEND_ECHO_SPEC_CONST_HANDLER" will be invoked. The handler for print does one thing before it invokes the handler for echo, it makes sure that the return value for print is 1, as follows:
ZVAL_LONG(&EX_T(opline->result.var).tmp_var, 1);
(see here for reference)
The return value is a convenience should one wish to use print in a conditional expression. Why 1 and not 100? Well in PHP the truthiness of 1 or 100 is the same, i.e. true, whereas 0 in a boolean context equates as a false value. In PHP all non-zero values (positive and negative) are truthy values and this derives from PHP's Perl legacy.
But, if this is the case, then one may wonder why echo take multiple arguments whereas print can only handle one. For this answer we need to turn to the parser, specifically the file zend_language_parser.y. You will note that echo has the flexibility built in so that it may print one or multiple expressions (see here). whereas print is constrained to printing only one expression (see there).
Syntax
In the C programming language and languages influenced by it such as PHP, there is a distinction between statements and expressions. Syntactically, echo expr, expr, ... expr
is a statement while print expr
is an expression since it evaluates to a value. Therefore, like other statements, echo expr
stands on its own and is incapable of inclusion in an expression:
5 + echo 6; // syntax error
In contrast, print expr
, can alone form a statement:
print 5; // valid
Or, be part of an expression:
$x = (5 + print 5); // 5
var_dump( $x ); // 6
One might be tempted to think of print
as if it were a unary operator, like !
or ~
however it is not an operator. What !, ~ and print
have in common is that they are all built into PHP and each takes only one argument. You can use print
to create the following weird but valid code:
<?php
print print print print 7; // 7111
At first glance the result may seem odd that the last print statement prints its operand of '7' first. But, if you dig deeper and look at the actual opcodes it makes sense:
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
The very first opcode that gets generated is that corresponding to the 'print 7'. The '~0' is a temporary variable whose value is 1. That variable becomes and operand for the next print opcode which in turn returns a temporary variable and the process repeats. The last temporary variable doesn't get used at all so, it gets freed.
Why does print
return a value and echo
doesn't?
Expressions evaluate to values. For example 2 + 3
evaluates to 5
, and abs(-10)
evaluates to 10
. Since print expr
is itself an expression, then it should hold a value and it does, a consistent value of 1
indicates a truthy result and by returning a non-zero value the expression becomes useful for inclusion in another expression. For example in this snippet, the return value of print is useful in determining a function sequence:
<?php
function bar( $baz ) {
// other code
}
function foo() {
return print("In and out ...
");
}
if ( foo() ) {
bar();
}
You might find print of particular value when it comes to debugging on the fly, as the next example illustrates:
<?php
$haystack = 'abcde';
$needle = 'f';
strpos($haystack,$needle) !== FALSE OR print "$needle not in $haystack";
// output: f not in abcde
As a side-note, generally, statements are not expressions; they don't return a value. The exception, of course are expression statements which use print and even simple expressions used as a statement, such as1;
, a syntax which PHP inherits from C. The expression statement may look odd but it is very helpful, making it possible to pass arguments to functions.
Is print
a function?
No, it is a language construct. While all function calls are expressions, print (expr)
is an expression, despite the visual which appears as if it were using function call syntax. In truth these parentheses are parentheses-expr syntax, useful for expression evaluation. That accounts for the fact that at times they are optional if the expression is a simple one, such as print "Hello, world!"
. With a more complex expression such as print (5 ** 2 + 6/2); // 28
the parentheses aid the evaluation of the expression. Unlike function names, print
is syntactically a keyword, and semantically a "language construct".
The term "language construct" in PHP usually refers to "pseudo" functions like isset
or empty
. Although these "constructs" look exactly like functions, they are actually fexprs, that is, the arguments are passed to them without being evaluated, which requires special treatment from the compiler. print
happens to be an fexpr that chooses to evaluate its argument in the same way as a function.
The difference can be seen by printing get_defined_functions()
: there is no print
function listed. (Though printf
and friends are: unlike print
, they are true functions.)
Why does print(foo) work then?
For the same reason thatecho(foo)
works. These parentheses are quite different from function call parentheses because they pertain to expressions instead. That is why one may code echo ( 5 + 8 )
and can expect a result of 13 to display (see reference). These parenthesis are involved in evaluating an expression rather than invoking a function. Note: there are other uses for parentheses in PHP, such as if if-conditional expressions, assignment lists, function declarations, etc.
Why do print(1,2,3)
and echo(1,2,3)
result in syntax errors?
The syntax is print expr
, echo expr
or echo expr, expr, ..., expr
. When PHP encounters (1,2,3)
, it tries to parse it as a single expression and fails, because unlike C, PHP does not really have a binary comma operator; the comma serves more as a separator. ( You may find a binary comma nonetheless in PHP's for-loops, syntax it inherited from C.)
Semantics
The statement echo e1, e2, ..., eN;
can be understood as syntactic sugar for echo e1; echo e2; ...; echo eN;
.
Since all expressions are statements, and echo e
always has the same side-effects as print e
, and the return value of print e
is ignored when used as a statement, we can understand echo e
as syntactic sugar for print e
.
These two observations mean that echo e1, e2, ..., eN;
can be seen as syntactic sugar for print e1; print e2; ... print eN;
. (However, note the non-semantic runtime differences below.)
We therefore only have to define the semantics for print
. print e
, when evaluated:
- evaluates its single argument
e
and type-casts the resulting value to a string s
. (Thus, print e
is equivalent to print (string) e
.)
- Streams the string
s
to the output buffer (which eventually will be streamed to the standard output).
- Evaluates to the integer
1
.
Differences at the bytecode level
print
involves a small overhead of populating the return variable (pseudocode)
print 125;
PRINT 125,$temp ; print 125 and place 1 in $temp
UNSET $temp ; remove $temp
single echo
compiles to one opcode:
echo 125;
ECHO 125
multi-value echo
compiles to multiple opcodes
echo 123, 456;
ECHO 123
ECHO 456
Note that multi-value echo
doesn't concatenate its arguments, but outputs them one-by-one.
Reference: zend_do_print
, zend_do_echo
.
Runtime differences
ZEND_PRINT
is implemented as follows (pseudocode)
PRINT var, result:
result = 1
ECHO var
So it basically puts 1
in the result variable and delegates the real job to the ZEND_ECHO
handler. ZEND_ECHO
does the following
ECHO var:
if var is object
temp = var->toString()
zend_print_variable(temp)
else
zend_print_variable(var)
where zend_print_variable()
performs the actual "printing" (in fact, it merely redirects to a dedicated SAPI function).
Speed: echo x
vs print x
Unlike echo, print allocates a temporary variable. However, the amount of time spent on this activity is minuscule, so the difference between these two language constructs is negligible.
Speed: echo a,b,c
vs echo a.b.c
The first one compiles down to t