Чому два конструкти?
Правда про print та echo полягає в тому, що, хоча вони виглядають перед користувачами як дві окремі конструкції, вони є справді відтінком ехо, якщо перейти до основ, тобто подивіться на внутрішній вихідний код. Цей вихідний код включає аналізатор, а також обробники коду. Розглянемо просту дію, наприклад відображення числа нуль. Незалежно від того, використовуєте ви ехо або друк, буде запущено той же обробник "ZEND_ECHO_SPEC_CONST_HANDLER". Обробник для друку виконує одне, перш ніж викликати обробник для відлуння, він переконує, що повернене значення для друку дорівнює 1, наступним чином:
ZVAL_LONG(&EX_T(opline->result.var).tmp_var, 1);
(див. тут довідку )
Повернене значення - це зручність, якщо слід використовувати друк у умовному виразі. Чому 1, а не 100? Ну а в PHP правдивість 1 або 100 однакова, тобто істинна, тоді як 0 в булевому контексті дорівнює як хибне значення. У PHP всі ненульові значення (позитивні та негативні) є трибунними значеннями, і це походить від спадщини Perl PHP.
Але якщо це так, то можна задатися питанням, чому ехо приймає кілька аргументів, тоді як друк може обробляти лише один. Для цієї відповіді нам потрібно звернутися до аналізатора, зокрема до файлу 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
Найперший згенерований опкод - це той, що відповідає "друку 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
Як побічна примітка, твердження, як правило, не є виразами; вони не повертають значення. Виняток, звичайно, є висловлюваннями виразів, які використовують друковані та навіть прості вирази, які використовуються як вираз, наприклад 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's-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
і тип-кидає отримане значення в рядок 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
один echo
компілятор в один опкод:
echo 125;
ECHO 125
багатозначні 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
vsprint x
На відміну від луни , друк виділяє тимчасову змінну. Однак кількість часу, витраченого на цю діяльність, є незначною, тому різниця між цими двома мовними конструкціями незначна.
Швидкість: echo a,b,c
vsecho a.b.c
Перший складається з трьох окремих тверджень. Другий оцінює весь вираз a.b.c.
, друкує результат і негайно розпоряджається ним. Оскільки конкатенація передбачає розподіл пам'яті та копіювання, перший варіант буде більш ефективним.
То який із них використовувати?
У веб-додатках вихід здебільшого зосереджений у шаблонах. Оскільки шаблони використовують <?=
, що є псевдонімом echo
, то, здається, логічно дотримуватися і echo
в інших частинах коду. echo
має додаткову перевагу - можливість друкувати декілька виразів, не з'єднуючи їх, і не передбачає накладних витрат на заповнення тимчасової змінної повернення. Отже, використовуйте echo
.