Ответ 1
Почему две конструкции?
Истина о печати и эхо заключается в том, что, хотя они кажутся пользователям как две разные конструкции, они оба являются действительно оттенками эха, если вы переходите к основам, то есть смотрите внутренний код. Этот исходный код включает в себя синтаксический анализатор, а также обработчики кода операций. Рассмотрим простое действие, такое как отображение нулевого числа. Будь вы используете эхо или печать, вызывается тот же обработчик "ZEND_ECHO_SPEC_CONST_HANDLER". Обработчик для печати делает одно, прежде чем он вызывает обработчик для эха, он гарантирует, что возвращаемое значение для печати равно 1:
ZVAL_LONG(&EX_T(opline->result.var).tmp_var, 1);
(см. здесь для справки)
Возвращаемое значение - это удобство, если вы хотите использовать печать в условном выражении. Почему 1, а не 100? Ну, в PHP правдоподобие 1 или 100 одинаково, т.е. True, тогда как 0 в булевом контексте приравнивается как ложное значение. В PHP все ненулевые значения (положительные и отрицательные) являются истинными значениями, и это происходит из наследия PHP Perl.
Но если это так, то можно задаться вопросом, почему эхо принимает несколько аргументов, тогда как печать может обрабатывать только один. Для этого ответа нам нужно обратиться к парсеру, в частности к файлу zend_language_parser.y. Вы заметите, что эхо имеет гибкость, встроенную для печати одного или нескольких выражений (см. здесь). в то время как печать ограничена печатью только одного выражения (см. там).
Синтаксис
В языке программирования 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
Самый первый код операции, который генерируется, соответствует тому, что соответствует "print 7". "~ 0" - временная переменная, значение которой равно 1. Эта переменная становится и операндом для следующего кода операции печати, который, в свою очередь, возвращает временную переменную, и процесс повторяется. Последняя временная переменная не используется вообще, поэтому она освобождается.
Почему print
возвращает значение, а echo
- нет?
Выражения оценивают значения. Например, 2 + 3
имеет значение 5
, а abs(-10)
- 10
. Так как print expr
само является выражением, то оно должно содержать значение, и оно делает, согласованное значение 1
указывает на правдивый результат и, возвращая ненулевое значение, выражение становится полезным для включения в другое выражение. Например, в этом фрагменте возвращаемое значение печати полезно при определении последовательности функций:
<?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 обычно относится к "псевдо" функциям типа isset
или empty
. Хотя эти "конструкции" выглядят точно так же, как функции, они фактически 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 expr
, echo expr
или echo expr, expr, ..., expr
. Когда PHP встречается с (1,2,3)
, он пытается разобрать его как одно выражение и терпит неудачу, потому что в отличие от C PHP не имеет бинарного оператора запятой; запятая больше служит разделителем. (Вы можете найти двоичную запятую, тем не менее, в PHP for-loops, синтаксис, который он унаследовал от 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
, когда оценивается:
- оценивает свой единственный аргумент
e
и type-casts полученное значение для строкиs
. (Таким образом,print e
эквивалентноprint (string) e
.) - Поток строки
s
в выходной буфер (который в конечном итоге будет передан на стандартный вывод). - Вычисляет целое число
1
.
Различия на уровне байт-кода
print
включает небольшие накладные расходы при заполнении возвращаемой переменной (псевдокод)
print 125;
PRINT 125,$temp ; print 125 and place 1 in $temp
UNSET $temp ; remove $temp
single echo
компилируется в один код операции:
echo 125;
ECHO 125
multi-value echo
компилируется в несколько кодов операций
echo 123, 456;
ECHO 123
ECHO 456
Обратите внимание, что многозначное значение echo
не объединяет его аргументы, а выводит их по очереди.
Ссылка: zend_do_print
, zend_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 x
vs print x
В отличие от эха, print выделяет временную переменную. Однако количество времени, затрачиваемого на эту деятельность, незначительно, поэтому разница между этими двумя языковыми конструкциями ничтожно мала.
Скорость: echo a,b,c
vs echo a.b.c
Первый объединяет до трех отдельных операторов. Второе оценивает все выражение a.b.c.
, печатает результат и немедленно его удаляет. Поскольку конкатенация включает в себя распределение памяти и копирование, первый вариант будет более эффективным.
Итак, какой из них использовать?
В веб-приложениях вывод главным образом сосредоточен в шаблонах. Поскольку в шаблонах используется <?=
, который является псевдонимом echo
, логично придерживаться echo
в других частях кода. echo
имеет дополнительное преимущество, заключающееся в возможности печати нескольких выражений без их конкатенации и не требует накладных расходов на заполнение временной возвращаемой переменной. Итак, используйте echo
.