¿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, ... expres una declaración, mientras que print expres una expresión, ya que se evalúa como un valor. Por lo tanto, como otras declaraciones, se echo exprsostiene por sí solo y es incapaz de ser incluido en una expresión:
5 + echo 6; // syntax error
Por el contrario, print exprsolo 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 printque se trata de un operador unario, como !o ~no es un operador. Lo que !, ~ and printtienen en común es que todos están integrados en PHP y cada uno toma solo un argumento. Puede usar printpara 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é printdevuelve un valor y echono lo hace?
Las expresiones evalúan los valores. Por ejemplo 2 + 3evalúa a 5, y abs(-10)se evalúa como 10. Dado que print expres en sí misma una expresión, entonces debe contener un valor y lo hace, un valor consistente de 1indica 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 printuna 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); // 28los paréntesis, ayuda a evaluar la expresión. A diferencia de los nombres de funciones, printes 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 isseto 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. printresulta 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 printfunción en la lista. (Aunque los printfamigos 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 exproecho 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 esiempre tienen los mismos efectos secundarios que print e, y el valor de retorno de print ese ignora cuando se usa como una declaración, podemos entenderlo echo ecomo 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
ey convierte el valor resultante en una cadena s. (Por lo tanto, print ees equivalente a print (string) e).
- Transmite la cadena
sal 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
echocompilaciones individuales en un código de operación:
echo 125;
ECHO 125
echocompilaciones 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 echono 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 1la variable de resultado y delega el trabajo real al ZEND_ECHOcontrolador. ZEND_ECHOhace 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 xvsprint 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,cvsecho 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 echoen otras partes del código también. echotiene 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.