PHP is a great programming language. For all his faults, he never ceases to amaze. Recently I encountered the following - at first glance mysterious - his behavior.
As you know, PHP has a built-in template engine. All the text that the interpreter encounters between the tags designating the end and the beginning of the PHP code, it sends to the output buffer. You can verify this by the following example:
<?php echo "Hello, "; ?> World. <?php echo "All is fine\n";
The output of the program will be “Hello, World. All is fine, which is to be expected. But what really happens? Let's look at another example:
<?php $three = function() { ?> Three <?php }; $one = function() { ?> One <?php }; $two = function() { ?> Two <?php }; $one(); $two(); $three();
If you run the source code, the output of the program will be “One Two Three”, which is a bit strange. After all, the text in the code was met in a completely different sequence and the “Three One Two” was supposed to get into the output buffer.
')
In fact, PHP does not send text to the output buffer as soon as it encounters it. In the language interpreter there is a special opcode ZEND_ECHO (echo is translated into this opcode) and a piece of text between the PHP code will be translated into the argument of this opcode. That is why we have the text in the second example displayed in the sequence in which we call the created anonymous functions (the text output has become part of the anonymous functions thanks to the opcode ZEND_ECHO.
In the confirmation of my words, a piece of the contents of the file zend_language_parser.y
| T_INLINE_HTML { zend_do_echo(&$1 TSRMLS_CC); }
And the implementation of the function zend_do_echo from 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); }
Well, what is the use of this?
Sense is actually there. After all, we can bind arbitrary output to an anonymous function, which means that theoretically it can be used in the implementation of the template engine. A little thought, I sketched the following version of the contents of the file of such a theoretical template engine:
$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(); ?>
Where in the $ c-> header variable is an object of class CElement. Conveniently? Every man to his own taste)
PS
From the official documentation, the behavior of the interpreter is not entirely obvious. Moreover, it doesn’t mention at all that text is converted to an opcode argument:
when it reaches
the PHP interpreter hits it?