¿Por qué dos construcciones?
La verdad acerca de la impresión y el eco es que, si bien los usuarios los ven como dos construcciones distintas, ambos son realmente sombras de eco si se llega a lo básico, es decir, mire el código fuente interno. Ese código fuente involucra tanto el analizador como los manejadores de código de operación. Considere una acción simple como mostrar el número cero. Ya sea que use echo o print, se invocará el mismo controlador "ZEND_ECHO_SPEC_CONST_HANDLER". El controlador para imprimir hace una cosa antes de invocar al controlador para echo, se asegura de que el valor de retorno para print sea 1, de la siguiente manera:
ZVAL_LONG(&EX_T(opline->result.var).tmp_var, 1);
(ver aquí para referencia )
El valor de retorno es conveniente si se desea utilizar print en una expresión condicional. ¿Por qué 1 y no 100? Bueno, en PHP, la veracidad de 1 o 100 es la misma, es decir, verdadera, mientras que 0 en un contexto booleano equivale a un valor falso. En PHP, todos los valores distintos de cero (positivo y negativo) son valores verdaderos y esto se deriva del legado de Perl de PHP.
Pero, si este es el caso, entonces uno puede preguntarse por qué echo toma múltiples argumentos mientras que print solo puede manejar uno. Para esta respuesta, debemos recurrir al analizador, específicamente al archivo zend_language_parser.y . Notarás que echo tiene la flexibilidad incorporada para que pueda imprimir una o varias expresiones (ver aquí ). mientras que print se limita a imprimir solo una expresión (ver allí ).
Sintaxis
En el lenguaje de programación C y los lenguajes influenciados por él, como PHP, hay una distinción entre declaraciones y expresiones. Sintácticamente, echo expr, expr, ... expr
es una declaración, mientras que print expr
es una expresión, ya que se evalúa como un valor. Por lo tanto, como otras declaraciones, se echo expr
sostiene por sí solo y es incapaz de ser incluido en una expresión:
5 + echo 6; // syntax error
Por el contrario, print expr
solo puede formar una declaración:
print 5; // valid
O ser parte de una expresión:
$x = (5 + print 5); // 5
var_dump( $x ); // 6
Uno podría sentirse tentado a pensar print
que se trata de un operador unario, como !
o ~
no es un operador. Lo que !, ~ and print
tienen en común es que todos están integrados en PHP y cada uno toma solo un argumento. Puede usar print
para crear el siguiente código extraño pero válido:
<?php
print print print print 7; // 7111
A primera vista, el resultado puede parecer extraño que la última declaración de impresión imprima primero su operando de '7' . Pero, si profundiza y mira los códigos de operación reales, tiene sentido:
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
El primer código de operación que se genera es el correspondiente a la 'impresión 7'. El '~ 0' es una variable temporal cuyo valor es 1. Esa variable se convierte y operando para el siguiente código de operación de impresión que a su vez devuelve una variable temporal y el proceso se repite. La última variable temporal no se usa en absoluto, por lo que se libera.
¿Por qué print
devuelve un valor y echo
no lo hace?
Las expresiones evalúan los valores. Por ejemplo 2 + 3
evalúa a 5
, y abs(-10)
se evalúa como 10
. Dado que print expr
es en sí misma una expresión, entonces debe contener un valor y lo hace, un valor consistente de 1
indica un resultado verdadero y al devolver un valor distinto de cero la expresión se vuelve útil para su inclusión en otra expresión. Por ejemplo, en este fragmento, el valor de retorno de print es útil para determinar una secuencia de funciones:
<?php
function bar( $baz ) {
// other code
}
function foo() {
return print("In and out ...\n");
}
if ( foo() ) {
bar();
}
Puede encontrar impresiones de un valor particular cuando se trata de depurar sobre la marcha, como lo ilustra el siguiente ejemplo:
<?php
$haystack = 'abcde';
$needle = 'f';
strpos($haystack,$needle) !== FALSE OR print "$needle not in $haystack";
// output: f not in abcde
Como nota al margen, en general, las declaraciones no son expresiones; No devuelven un valor. La excepción, por supuesto, son las declaraciones de expresión que usan expresiones impresas e incluso simples usadas como una declaración, como 1;
una sintaxis que PHP hereda de C. La declaración de expresión puede parecer extraña pero es muy útil, ya que permite pasar argumentos a funciones
Es print
una función?
No, es una construcción de lenguaje. Si bien todas las llamadas a funciones son expresiones, print (expr)
es una expresión, a pesar de lo visual que aparece como si estuviera usando la sintaxis de llamadas a funciones. En verdad, estos paréntesis son sintaxis paréntesis-expr, útiles para la evaluación de expresiones. Eso explica el hecho de que a veces son opcionales si la expresión es simple, como print "Hello, world!"
. Con una expresión más compleja, como print (5 ** 2 + 6/2); // 28
los paréntesis, ayuda a evaluar la expresión. A diferencia de los nombres de funciones, print
es sintácticamente una palabra clave y semánticamente una "construcción de lenguaje" .
El término "construcción de lenguaje" en PHP generalmente se refiere a funciones "pseudo" como isset
o empty
. Aunque estas "construcciones" se ven exactamente como funciones, en realidad son fexprs , es decir, los argumentos se les pasan sin ser evaluados, lo que requiere un tratamiento especial del compilador. print
resulta ser un fexpr que elige evaluar su argumento de la misma manera que una función.
La diferencia se puede ver imprimiendo get_defined_functions()
: no hay ninguna print
función en la lista. (Aunque los printf
amigos son: a diferencia print
, son funciones verdaderas).
¿Por qué funciona print (foo) entonces?
Por la misma razón que echo(foo)
funciona. Estos paréntesis son bastante diferentes de los paréntesis de llamadas a funciones porque pertenecen a expresiones en su lugar. Es por eso que uno puede codificar echo ( 5 + 8 )
y esperar que se muestre un resultado de 13 (ver referencia ). Estos paréntesis están involucrados en la evaluación de una expresión en lugar de invocar una función. Nota: hay otros usos para paréntesis en PHP, como expresiones if-conditional, listas de asignación, declaraciones de funciones, etc.
¿Por qué hacer print(1,2,3)
y echo(1,2,3)
dar lugar a errores de sintaxis?
La sintaxis es print expr
, echo expr
oecho expr, expr, ..., expr
. Cuando PHP se encuentra (1,2,3)
, intenta analizarlo como una sola expresión y falla, porque a diferencia de C, PHP realmente no tiene un operador de coma binario; la coma sirve más como separador. (No obstante, puede encontrar una coma binaria en los bucles for de PHP, sintaxis que heredó de C.)
Semántica
La declaración echo e1, e2, ..., eN;
puede entenderse como azúcar sintáctica paraecho e1; echo e2; ...; echo eN;
.
Como todas las expresiones son declaraciones y echo e
siempre tienen los mismos efectos secundarios que print e
, y el valor de retorno de print e
se ignora cuando se usa como una declaración, podemos entenderlo echo e
como azúcar sintáctica paraprint e
.
Estas dos observaciones significan que echo e1, e2, ..., eN;
puede verse como azúcar sintáctica paraprint e1; print e2; ... print eN;
. (Sin embargo, tenga en cuenta las diferencias de tiempo de ejecución no semánticas a continuación).
Por lo tanto, solo tenemos que definir la semántica para print
. print e
, cuando se evalúa:
- evalúa su argumento único
e
y convierte el valor resultante en una cadena s
. (Por lo tanto, print e
es equivalente a print (string) e
).
- Transmite la cadena
s
al búfer de salida (que eventualmente se transmitirá a la salida estándar).
- Evalúa al entero
1
.
Diferencias a nivel de bytecode
print
implica una pequeña sobrecarga de poblar la variable de retorno (pseudocódigo)
print 125;
PRINT 125,$temp ; print 125 and place 1 in $temp
UNSET $temp ; remove $temp
echo
compilaciones individuales en un código de operación:
echo 125;
ECHO 125
echo
compilaciones de múltiples valores para múltiples códigos de operación
echo 123, 456;
ECHO 123
ECHO 456
Tenga en cuenta que el valor múltiple echo
no concatena sus argumentos, sino que los genera uno por uno.
Referencia: zend_do_print
, zend_do_echo
.
Diferencias de tiempo de ejecución
ZEND_PRINT
se implementa de la siguiente manera (pseudocódigo)
PRINT var, result:
result = 1
ECHO var
Por lo tanto, básicamente coloca 1
la variable de resultado y delega el trabajo real al ZEND_ECHO
controlador. ZEND_ECHO
hace lo siguiente
ECHO var:
if var is object
temp = var->toString()
zend_print_variable(temp)
else
zend_print_variable(var)
donde zend_print_variable()
realiza la "impresión" real (de hecho, simplemente redirige a una función SAPI dedicada).
Velocidad: echo x
vsprint x
A diferencia de echo , print asigna una variable temporal. Sin embargo, la cantidad de tiempo dedicado a esta actividad es minúscula, por lo que la diferencia entre estas dos construcciones de lenguaje es insignificante.
Velocidad: echo a,b,c
vsecho a.b.c
El primero se resume en tres declaraciones separadas. El segundo evalúa la expresión completa.a.b.c.
, imprime el resultado y lo elimina de inmediato. Dado que la concatenación implica asignaciones de memoria y copia, la primera opción será más eficiente.
Entonces, ¿cuál usar?
En aplicaciones web, la salida se concentra principalmente en plantillas. Dado que las plantillas usan <?=
, que es el alias de echo
, parece lógico seguir echo
en otras partes del código también. echo
tiene una ventaja adicional de poder imprimir múltiples expresiones sin concatenarlas y no implica una sobrecarga de llenar una variable de retorno temporal. Entonces, use echo
.