Как стать автором
Поиск
Написать публикацию
Обновить

Кастуем магией PHP

Время на прочтение2 мин
Количество просмотров6.2K
PHP замечательный язык программирования. При всех его недостатках он не перестает удивлять. Недавно столкнулся со следующим — на первый взгляд загадочным — его поведением.

Как известно 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
Теги:
Хабы:
Всего голосов 89: ↑29 и ↓60-31
Комментарии22

Публикации

Ближайшие события