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

от автора

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. Удобно? На вкус и цвет товарищей нет)

ссылка на оригинал статьи http://habrahabr.ru/post/171097/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *