PHP замечательный язык программирования. При всех его недостатках он не перестает удивлять. Недавно столкнулся со следующим — на первый взгляд загадочным — его поведением.
Как известно PHP имеет встроенный шаблонизатор. Весь текст, который интерпретатор встречает между тегами обозначающими конец и начало PHP кода, он отправляет в буфер вывода. Убедиться в этом можно на следующем примере:
Выводом программы будет «Hello, World. All is fine», что и следовало ожидать. Но что происходит на самом деле? Посмотрим на другой пример:
Если выполнить исходный код, то выводом программы будет «One Two Three», что немного странно. Ведь текст в коде встречался совсем в другой последовательности и в буфер вывода должно было попасть «Three One Two».
На самом деле PHP не отправляет текст в буфер вывода как только он его встречает. В интерпретаторе языка есть специальный опкод ZEND_ECHO (в этот опкод транслируется echo) и кусок текста между PHP кодом будут транслироваться в аргумент этого опкода. Именно поэтому у нас текст во втором примере выводиться в той последовательности в которой мы вызываем созданные анонимные функции (вывод текста стал частью анонимных функций благодаря опкоду ZEND_ECHO.
В подтверждении моих слов кусочек содержимого файла zend_language_parser.y
И реализация самой функции zend_do_echo из zend_compile.c:
Толк на самом деле есть. Ведь мы можем привязать к анонимной функции произвольный вывод, а значит теоретически это можно использовать в реализации шаблонизатора. Немного подумав я набросал следующий вариант содержимого файла такого теоретического шаблонизатора:
Где в переменой $c->header объект класса CElement. Удобно? На вкус и цвет товарищей нет)
P.S.
Из официальной документации поведение интерпретатора не совсем очевидно. Более того в ней совсем не упоминается о преобразовании текста в аргумент опкода: when the PHP interpreter hits the ?> closing tags, it simply starts outputting whatever it finds (except for an immediately following newline — see instruction separation) until it hits another opening tag
Как известно PHP имеет встроенный шаблонизатор. Весь текст, который интерпретатор встречает между тегами обозначающими конец и начало PHP кода, он отправляет в буфер вывода. Убедиться в этом можно на следующем примере:
<?php
echo "Hello, ";
?>
World.
<?php
echo "All is fine\n";
Выводом программы будет «Hello, World. All is fine», что и следовало ожидать. Но что происходит на самом деле? Посмотрим на другой пример:
<?php
$three = function() {
?>
Three
<?php
};
$one = function() {
?>
One
<?php
};
$two = function() {
?>
Two
<?php
};
$one();
$two();
$three();
Если выполнить исходный код, то выводом программы будет «One Two Three», что немного странно. Ведь текст в коде встречался совсем в другой последовательности и в буфер вывода должно было попасть «Three One Two».
На самом деле PHP не отправляет текст в буфер вывода как только он его встречает. В интерпретаторе языка есть специальный опкод ZEND_ECHO (в этот опкод транслируется echo) и кусок текста между PHP кодом будут транслироваться в аргумент этого опкода. Именно поэтому у нас текст во втором примере выводиться в той последовательности в которой мы вызываем созданные анонимные функции (вывод текста стал частью анонимных функций благодаря опкоду ZEND_ECHO.
В подтверждении моих слов кусочек содержимого файла zend_language_parser.y
| T_INLINE_HTML { zend_do_echo(&$1 TSRMLS_CC); }
И реализация самой функции zend_do_echo из zend_compile.c:
void zend_do_echo(const znode *arg TSRMLS_DC) /* {{{ */
{
zend_op *opline = get_next_op(CG(active_op_array) TSRMLS_CC);
opline->opcode = ZEND_ECHO;
SET_NODE(opline->op1, arg);
SET_UNUSED(opline->op2);
}
/* }}} */
Ну и какой от этого толк?
Толк на самом деле есть. Ведь мы можем привязать к анонимной функции произвольный вывод, а значит теоретически это можно использовать в реализации шаблонизатора. Немного подумав я набросал следующий вариант содержимого файла такого теоретического шаблонизатора:
$c->header->addClass('header')->setContent(function() {
?>
<ul>
<?= $c->li->addClassIf(1, 'active')->setContent('Main') ;?>
<?= $c->li->addClassIf(0, 'active')->setContent('Account') ;?>
<?= $c->li->addClassIf(0, 'active')->setContent('FAQ') ;?>
<?= $c->li->addClassIf(0, 'active')->setContent('Logout') ;?>
</ul>
<?php
})->_print();
?>
Где в переменой $c->header объект класса CElement. Удобно? На вкус и цвет товарищей нет)
P.S.
Из официальной документации поведение интерпретатора не совсем очевидно. Более того в ней совсем не упоминается о преобразовании текста в аргумент опкода: when the PHP interpreter hits the ?> closing tags, it simply starts outputting whatever it finds (except for an immediately following newline — see instruction separation) until it hits another opening tag