Довідка: Порівняння друку та відлуння PHP


182

Яка різниця між PHP printі echo?

Переповнення стека має багато запитань щодо використання PHP printта echoвикористання ключових слів.

Метою цієї публікації є надання канонічного довідкового питання та відповіді про PHP printта echoключові слова та порівняння їх відмінностей та випадків використання.


3
Я думаю, що тут недостатньо щодо повернення значення друку. Друк корисний для швидкого виводу налагодження або чогось подібного: strpos ($ x, $ y)! == ФАЛЬШЕ АБО надрукувати "щось". Швидкий на друк і добре читається. І "Is Print a function" було незручно читати чомусь (ваша аргументація здається ... дивною і не очевидною) - це мовна конструкція, є набагато гірше, що ви не можете зробити з нею: змінні функції.
XzKto

1
Щоб зробити це відкритим, тут потрібно зробити: 1. розділити на запитання та відповіді. 2. Посилання / посилання на існуючий вміст теми про переповнення стека (щось подібне тут: stackoverflow.com/questions/3737139/… ), але у відповіді. 3. Потрібно бути CW.
Кев

"Суміжний стовпчик" чудово, але він не дуже зосереджений. Щоб збільшити його значення як канонічне опорне запитання та відповідь, тоді воно також повинно бути добре вивчене, а посилання на інші конкретні хороші відповіді додадуть значення.
Кев

Питання справді має бути актуальним питанням . Я можу додати банер щодо канонічної частини, коли ви закінчите.
Тім Пост

Відповіді:


185

Чому два конструкти?

Правда про 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, при оцінці:

  1. оцінює свій єдиний аргумент eі тип-кидає отримане значення в рядок s. (Таким чином, print eеквівалентно print (string) e.)
  2. Потоки рядок sв вихідний буфер (який в кінцевому рахунку буде передаватися на стандартний висновок).
  3. Оцінює до цілого числа 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 xvsprint x

На відміну від луни , друк виділяє тимчасову змінну. Однак кількість часу, витраченого на цю діяльність, є незначною, тому різниця між цими двома мовними конструкціями незначна.

Швидкість: echo a,b,cvsecho a.b.c

Перший складається з трьох окремих тверджень. Другий оцінює весь вираз a.b.c., друкує результат і негайно розпоряджається ним. Оскільки конкатенація передбачає розподіл пам'яті та копіювання, перший варіант буде більш ефективним.

То який із них використовувати?

У веб-додатках вихід здебільшого зосереджений у шаблонах. Оскільки шаблони використовують <?=, що є псевдонімом echo, то, здається, логічно дотримуватися і echoв інших частинах коду. echoмає додаткову перевагу - можливість друкувати декілька виразів, не з'єднуючи їх, і не передбачає накладних витрат на заповнення тимчасової змінної повернення. Отже, використовуйте echo.


5
Я детально відредагував і уточнив це питання і додав розділ про семантику. Я досить впевнений у цьому, але хтось міг би двічі перевірити це.
jameshfisher

1
Чи означає це тоді, що краще використовувати echo $a,$b,$cдля об'єднання рядків рядків? Я, чесно кажучи, ніколи цього не бачив у використанні.
geoff

1
Як щодо розділу TL; DR, що описує функціональні відмінності? Як: printдозволяє лише один аргумент, echoможе мати декілька; echoне може бути частиною виразу, хоча printможе і повертається ...; і може бути деяким підсумком ефективності. І всі ті "чому" і "під капотом" згодом
YakovL

0

Я знаю, що я спізнююсь, але одне, що я хотів би додати, це те, що в моєму коді

 $stmt = mysqli_stmt_init($connection) or echo "error at init"; 

видає помилку "синтаксична помилка, несподіване" відлуння "(T_ECHO)"

поки

 $stmt = mysqli_stmt_init($connection) or print "error at init";

працює чудово.

Документи про луні кажуть «відлуння ( в відміну від деяких інших мовних конструкцій) не поводиться як функція» тут , але про пресу документах також говорить , «печатка не на самому ділі реальна функція (це конструкція мови)» тут . Тож я не впевнений, чому. А також про документи ехо та друку говорить: "Основні відмінності відлуння полягають у тому, що друк приймає лише один аргумент і завжди повертає 1." тут

Я був би радий, якщо хтось може пролити трохи світла щодо цієї поведінки.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.