2013-01-25 18:36:16 +04:00
< ? php
2013-02-21 22:51:24 +04:00
/*
2013-06-28 11:53:53 +04:00
* This file is part of Fenom .
2013-02-21 22:51:24 +04:00
*
* ( c ) 2013 Ivan Shalganov
*
2013-04-28 18:08:57 +04:00
* For the full copyright and license information , please view the license . md
2013-02-21 22:51:24 +04:00
* file that was distributed with this source code .
*/
2013-06-28 11:53:53 +04:00
namespace Fenom ;
2014-02-27 16:30:44 +04:00
2013-08-01 01:05:19 +04:00
use Fenom ;
2013-07-29 16:15:52 +04:00
use Fenom\Error\CompileException ;
use Fenom\Error\InvalidUsageException ;
use Fenom\Error\SecurityException ;
use Fenom\Error\TokenizeException ;
2016-08-22 17:08:47 +03:00
use Fenom\Error\UnexpectedTokenException ;
2013-01-25 18:36:16 +04:00
/**
2013-02-21 22:51:24 +04:00
* Template compiler
*
2013-06-28 11:53:53 +04:00
* @ package Fenom
2013-07-04 01:28:10 +04:00
* @ author Ivan Shalganov < a . cobest @ gmail . com >
2013-01-25 18:36:16 +04:00
*/
2013-07-29 14:58:14 +04:00
class Template extends Render
{
2014-01-28 21:24:47 +04:00
const VAR_NAME = '$var' ;
const TPL_NAME = '$tpl' ;
2014-05-06 00:45:37 +04:00
2016-08-22 17:08:47 +03:00
const COMPILE_STAGE_LOADED = 1 ;
const COMPILE_STAGE_PRE_FILTERED = 2 ;
const COMPILE_STAGE_PARSED = 3 ;
const COMPILE_STAGE_PROCESSED = 4 ;
const COMPILE_STAGE_POST_FILTERED = 5 ;
2013-07-03 12:10:50 +04:00
/**
* Disable array parser .
*/
const DENY_ARRAY = 1 ;
/**
* Disable modifier parser .
*/
const DENY_MODS = 2 ;
2015-02-12 12:13:35 +03:00
/**
* Allow parse modifiers with term
*/
const TERM_MODS = 1 ;
/**
* Allow parse range with term
*/
const TERM_RANGE = 1 ;
2013-01-25 18:36:16 +04:00
/**
* @ var int shared counter
*/
public $i = 1 ;
2013-02-23 02:03:05 +04:00
/**
* @ var array of macros
*/
public $macros = array ();
2013-02-23 13:29:20 +04:00
/**
* @ var array of blocks
*/
public $blocks = array ();
2013-06-08 00:08:00 +04:00
2014-02-22 20:34:53 +04:00
/**
* @ var string | null
*/
public $extends ;
/**
* @ var string | null
*/
public $extended ;
2014-02-26 23:57:29 +04:00
/**
* Stack of extended templates
* @ var array
*/
public $ext_stack = array ();
public $extend_body = false ;
2013-06-08 00:08:00 +04:00
2016-10-09 23:40:37 +03:00
/**
* Parent template
* @ var Template
*/
public $parent ;
2013-07-29 14:53:21 +04:00
/**
* Template PHP code
* @ var string
*/
private $_body ;
2016-08-22 17:08:47 +03:00
private $_compile_stage = 0 ;
2013-07-29 14:53:21 +04:00
2013-01-25 18:36:16 +04:00
/**
* Call stack
2014-04-17 23:22:50 +04:00
* @ var Tag []
2013-01-25 18:36:16 +04:00
*/
private $_stack = array ();
/**
* Template source
* @ var string
*/
private $_src ;
/**
* @ var int
*/
private $_line = 1 ;
private $_post = array ();
/**
2014-05-12 00:36:50 +04:00
* @ var bool | string
2013-01-25 18:36:16 +04:00
*/
2013-02-07 17:37:16 +04:00
private $_ignore = false ;
2013-01-25 18:36:16 +04:00
2016-06-09 14:24:35 +03:00
private $_before = array ();
2013-07-26 10:40:07 +04:00
2013-08-01 01:05:19 +04:00
private $_filters = array ();
2013-05-17 22:20:29 +04:00
2013-09-14 11:24:23 +04:00
/**
* @ var int crc32 of the template name
*/
private $_crc = 0 ;
2013-01-25 18:36:16 +04:00
/**
2013-06-28 11:53:53 +04:00
* @ param Fenom $fenom Template storage
2013-04-28 18:08:57 +04:00
* @ param int $options
2016-10-09 23:40:37 +03:00
* @ param Template $parent
2013-02-13 20:51:27 +04:00
*/
2016-10-09 23:40:37 +03:00
public function __construct ( Fenom $fenom , $options , Template $parent = null )
2013-07-29 14:58:14 +04:00
{
2016-10-09 23:40:37 +03:00
$this -> parent = $parent ;
2014-05-06 14:22:58 +04:00
$this -> _fenom = $fenom ;
$this -> _options = $options ;
$this -> _filters = $this -> _fenom -> getFilters ();
2013-10-08 17:48:23 +04:00
$this -> _tag_filters = $this -> _fenom -> getTagFilters ();
2013-02-13 20:51:27 +04:00
}
2013-07-02 11:07:33 +04:00
/**
* Get tag stack size
* @ return int
*/
2013-07-29 14:58:14 +04:00
public function getStackSize ()
{
2013-07-02 11:07:33 +04:00
return count ( $this -> _stack );
}
2014-02-14 15:55:36 +04:00
/**
* @ param string $tag
2014-06-28 21:08:20 +04:00
* @ return bool | \Fenom\Tag
2014-02-14 15:55:36 +04:00
*/
public function getParentScope ( $tag )
{
2014-02-27 16:30:44 +04:00
for ( $i = count ( $this -> _stack ) - 1 ; $i >= 0 ; $i -- ) {
if ( $this -> _stack [ $i ] -> name == $tag ) {
2014-02-14 15:55:36 +04:00
return $this -> _stack [ $i ];
}
}
return false ;
}
2013-02-13 20:51:27 +04:00
/**
* Load source from provider
* @ param string $name
* @ param bool $compile
2013-07-29 16:15:52 +04:00
* @ return self
2013-02-13 20:51:27 +04:00
*/
2013-07-29 14:58:14 +04:00
public function load ( $name , $compile = true )
{
2013-02-13 20:51:27 +04:00
$this -> _name = $name ;
2014-05-06 14:22:58 +04:00
$this -> _crc = crc32 ( $this -> _name );
2013-09-14 11:24:23 +04:00
if ( $provider = strstr ( $name , ':' , true )) {
2014-05-06 14:22:58 +04:00
$this -> _scm = $provider ;
2013-05-30 19:00:00 +04:00
$this -> _base_name = substr ( $name , strlen ( $provider ) + 1 );
2013-02-13 20:51:27 +04:00
} else {
2013-07-03 12:10:50 +04:00
$this -> _base_name = $name ;
2013-02-13 20:51:27 +04:00
}
2013-07-03 12:10:50 +04:00
$this -> _provider = $this -> _fenom -> getProvider ( $provider );
2014-05-06 14:22:58 +04:00
$this -> _src = $this -> _provider -> getSource ( $this -> _base_name , $this -> _time );
2016-08-22 17:08:47 +03:00
$this -> _compile_stage = self :: COMPILE_STAGE_LOADED ;
2013-07-29 14:58:14 +04:00
if ( $compile ) {
2013-02-13 20:51:27 +04:00
$this -> compile ();
}
return $this ;
}
/**
* Load custom source
2013-01-25 18:36:16 +04:00
* @ param string $name template name
2013-02-13 20:51:27 +04:00
* @ param string $src template source
* @ param bool $compile
2013-06-28 11:53:53 +04:00
* @ return \Fenom\Template
2013-01-25 18:36:16 +04:00
*/
2013-07-29 14:58:14 +04:00
public function source ( $name , $src , $compile = true )
{
2013-01-28 16:34:34 +04:00
$this -> _name = $name ;
2014-05-06 14:22:58 +04:00
$this -> _src = $src ;
2013-07-29 14:58:14 +04:00
if ( $compile ) {
2013-02-07 17:37:16 +04:00
$this -> compile ();
}
2013-02-13 20:51:27 +04:00
return $this ;
2013-02-07 17:37:16 +04:00
}
2013-02-13 20:51:27 +04:00
/**
* Convert template to PHP code
*
* @ throws CompileException
*/
2013-07-29 14:58:14 +04:00
public function compile ()
{
2016-08-22 17:08:47 +03:00
$end = $pos = 0 ;
2013-08-02 21:50:04 +04:00
foreach ( $this -> _fenom -> getPreFilters () as $filter ) {
2014-02-22 20:34:53 +04:00
$this -> _src = call_user_func ( $filter , $this , $this -> _src );
2013-08-01 01:05:19 +04:00
}
2016-08-22 17:08:47 +03:00
$this -> _compile_stage = self :: COMPILE_STAGE_PRE_FILTERED ;
2013-07-29 14:58:14 +04:00
while (( $start = strpos ( $this -> _src , '{' , $pos )) !== false ) { // search open-symbol of tags
2014-11-06 00:55:32 +03:00
switch ( substr ( $this -> _src , $start + 1 , 1 )) { // check next character
2013-07-29 14:58:14 +04:00
case " \n " :
case " \r " :
case " \t " :
case " " :
case " } " : // ignore the tag
2013-07-08 07:43:20 +04:00
$this -> _appendText ( substr ( $this -> _src , $pos , $start - $pos + 2 ));
$end = $start + 1 ;
break ;
2013-07-22 18:03:43 +04:00
case " * " : // comment block
2013-07-08 07:43:20 +04:00
$end = strpos ( $this -> _src , '*}' , $start ); // find end of the comment block
2013-07-29 14:58:14 +04:00
if ( $end === false ) {
2013-07-03 12:10:50 +04:00
throw new CompileException ( " Unclosed comment block in line { $this -> _line } " , 0 , 1 , $this -> _name , $this -> _line );
}
2013-07-08 07:43:20 +04:00
$end ++ ;
2013-07-03 12:10:50 +04:00
$this -> _appendText ( substr ( $this -> _src , $pos , $start - $pos ));
2013-05-30 22:41:58 +04:00
$comment = substr ( $this -> _src , $start , $end - $start ); // read the comment block for processing
2013-06-01 02:00:43 +04:00
$this -> _line += substr_count ( $comment , " \n " ); // count lines in comments
2013-07-03 12:10:50 +04:00
unset ( $comment ); // cleanup
2013-07-08 07:43:20 +04:00
break ;
default :
$this -> _appendText ( substr ( $this -> _src , $pos , $start - $pos ));
$end = $start + 1 ;
do {
2013-07-25 02:05:44 +04:00
$need_more = false ;
2014-05-06 14:22:58 +04:00
$end = strpos ( $this -> _src , '}' , $end + 1 ); // search close-symbol of the tag
2013-07-29 14:58:14 +04:00
if ( $end === false ) { // if unexpected end of template
2013-07-08 07:43:20 +04:00
throw new CompileException ( " Unclosed tag in line { $this -> _line } " , 0 , 1 , $this -> _name , $this -> _line );
}
2014-05-06 14:22:58 +04:00
$tag = substr (
$this -> _src ,
2014-07-30 00:23:26 +04:00
$start + 1 , // skip '{'
$end - $start - 1 // skip '}'
);
2013-07-08 07:43:20 +04:00
2013-07-29 14:58:14 +04:00
if ( $this -> _ignore ) { // check ignore
2014-07-30 00:23:26 +04:00
if ( $tag === '/' . $this -> _ignore ) { // turn off ignore
2013-07-08 07:43:20 +04:00
$this -> _ignore = false ;
} else { // still ignore
2014-07-30 00:23:26 +04:00
$this -> _appendText ( '{' . $tag . '}' );
2014-05-12 00:36:50 +04:00
continue ;
2013-07-08 07:43:20 +04:00
}
2014-05-12 00:36:50 +04:00
}
if ( $this -> _tag_filters ) {
foreach ( $this -> _tag_filters as $filter ) {
2014-07-30 00:23:26 +04:00
$tag = call_user_func ( $filter , $tag , $this );
2013-10-08 17:48:23 +04:00
}
2014-05-12 00:36:50 +04:00
}
2014-07-30 00:23:26 +04:00
$tokens = new Tokenizer ( $tag ); // tokenize the tag
2014-05-12 00:36:50 +04:00
if ( $tokens -> isIncomplete ()) { // all strings finished?
$need_more = true ;
} else {
2014-07-30 00:23:26 +04:00
$this -> _appendCode ( $this -> parseTag ( $tokens ), '{' . $tag . '}' ); // start the tag lexer
2014-05-12 00:36:50 +04:00
if ( $tokens -> key ()) { // if tokenizer have tokens - throws exceptions
throw new CompileException ( " Unexpected token ' " . $tokens -> current () . " ' in { $this } line { $this -> _line } , near ' { " . $tokens -> getSnippetAsString ( 0 , 0 ) . " ' <- there " , 0 , E_ERROR , $this -> _name , $this -> _line );
2013-07-08 07:43:20 +04:00
}
}
2013-07-25 02:05:44 +04:00
} while ( $need_more );
2014-07-30 00:23:26 +04:00
unset ( $tag ); // cleanup
2013-07-08 07:43:20 +04:00
break ;
2013-07-03 12:10:50 +04:00
}
2013-07-22 18:03:43 +04:00
$pos = $end + 1 ; // move search-pointer to end of the tag
2013-01-25 18:36:16 +04:00
}
2016-08-22 17:08:47 +03:00
$this -> _compile_stage = self :: COMPILE_STAGE_PARSED ;
2013-07-08 07:43:20 +04:00
2013-05-30 19:00:00 +04:00
gc_collect_cycles ();
2013-07-22 18:03:43 +04:00
$this -> _appendText ( substr ( $this -> _src , $end ? $end + 1 : 0 )); // append tail of the template
2013-07-29 14:58:14 +04:00
if ( $this -> _stack ) {
2013-01-25 18:36:16 +04:00
$_names = array ();
2013-07-29 14:58:14 +04:00
foreach ( $this -> _stack as $scope ) {
$_names [] = '{' . $scope -> name . '} opened on line ' . $scope -> line ;
2013-01-25 18:36:16 +04:00
}
2015-01-08 15:52:33 +03:00
/* @var Tag $scope */
$message = " Unclosed tag " . ( count ( $_names ) > 1 ? " s " : " " ) . " : " . implode ( " , " , $_names );
throw new CompileException ( $message , 0 , 1 , $this -> _name , $scope -> line );
2013-01-25 18:36:16 +04:00
}
2013-06-01 02:00:43 +04:00
$this -> _src = " " ; // cleanup
2013-07-29 14:58:14 +04:00
if ( $this -> _post ) {
foreach ( $this -> _post as $cb ) {
2014-02-22 20:34:53 +04:00
call_user_func_array ( $cb , array ( $this , & $this -> _body ));
2013-01-25 18:36:16 +04:00
}
}
2016-08-22 17:08:47 +03:00
$this -> _compile_stage = self :: COMPILE_STAGE_PROCESSED ;
2013-07-22 18:03:43 +04:00
$this -> addDepend ( $this ); // for 'verify' performance
2013-08-02 21:50:04 +04:00
foreach ( $this -> _fenom -> getPostFilters () as $filter ) {
2014-02-22 20:34:53 +04:00
$this -> _body = call_user_func ( $filter , $this , $this -> _body );
2013-08-01 01:05:19 +04:00
}
2016-08-22 17:08:47 +03:00
$this -> _compile_stage = self :: COMPILE_STAGE_POST_FILTERED ;
}
public function isStageDone ( $stage_no ) {
return $this -> _compile_stage >= $stage_no ;
2013-01-28 16:34:34 +04:00
}
2013-01-25 18:36:16 +04:00
2014-05-06 00:45:37 +04:00
/**
* Set or unset the option
* @ param int $option
* @ param bool $value
*/
2014-05-06 14:22:58 +04:00
public function setOption ( $option , $value )
{
if ( $value ) {
2014-05-06 00:45:37 +04:00
$this -> _options |= $option ;
} else {
$this -> _options &= ~ $option ;
}
}
2013-07-26 10:40:07 +04:00
/**
2013-08-01 01:05:19 +04:00
* Execute some code at loading cache
2013-07-26 10:40:07 +04:00
* @ param $code
2013-08-01 01:05:19 +04:00
* @ return void
2013-07-26 10:40:07 +04:00
*/
2013-07-29 14:58:14 +04:00
public function before ( $code )
{
2016-06-09 14:24:35 +03:00
$this -> _before [] = $code ;
2013-07-26 10:40:07 +04:00
}
2013-03-17 14:37:23 +04:00
/**
2016-05-06 23:04:08 +03:00
* Generate name of temporary internal template variable ( may be random )
2013-03-17 14:37:23 +04:00
* @ return string
*/
2013-07-29 14:58:14 +04:00
public function tmpVar ()
{
2016-05-06 23:04:08 +03:00
return sprintf ( '$t%x_%x' , $this -> _crc ? $this -> _crc : mt_rand ( 0 , 0x7FFFFFFF ), $this -> i ++ );
2013-03-15 00:57:28 +04:00
}
2013-02-26 23:56:06 +04:00
/**
* Append plain text to template body
*
* @ param string $text
*/
2013-07-29 14:58:14 +04:00
private function _appendText ( $text )
{
2013-07-03 12:10:50 +04:00
$this -> _line += substr_count ( $text , " \n " );
2016-02-23 13:16:48 +03:00
$strip = $this -> _options & Fenom :: AUTO_STRIP ;
2013-08-01 01:05:19 +04:00
if ( $this -> _filters ) {
2013-07-29 14:58:14 +04:00
if ( strpos ( $text , " <? " ) === false ) {
2013-08-01 01:05:19 +04:00
foreach ( $this -> _filters as $filter ) {
2014-02-22 20:34:53 +04:00
$text = call_user_func ( $filter , $this , $text );
2013-08-01 01:05:19 +04:00
}
2013-05-17 22:20:29 +04:00
} else {
$fragments = explode ( " <? " , $text );
2013-07-29 14:58:14 +04:00
foreach ( $fragments as & $fragment ) {
if ( $fragment ) {
2013-08-01 01:05:19 +04:00
foreach ( $this -> _filters as $filter ) {
2014-02-22 20:34:53 +04:00
$fragment = call_user_func ( $filter , $this , $fragment );
2013-05-17 22:20:29 +04:00
}
}
}
2016-02-23 13:16:48 +03:00
$text = implode ( '<?php echo "<?"; ?>' . ( $strip ? '' : PHP_EOL ), $fragments );
2013-04-28 18:08:57 +04:00
}
} else {
2016-02-23 13:16:48 +03:00
$text = str_replace ( " <? " , '<?php echo "<?"; ?>' . ( $strip ? '' : PHP_EOL ), $text );
2013-04-28 18:08:57 +04:00
}
2016-08-22 17:08:47 +03:00
if ( $strip ) {
2014-10-30 23:56:16 +03:00
$text = preg_replace ( '/\s+/uS' , ' ' , str_replace ( array ( " \r " , " \n " ), " " , $text ));
2016-02-23 12:40:58 +03:00
$text = str_replace ( " > < " , " >< " , $text );
2014-05-08 12:56:37 +04:00
}
$this -> _body .= $text ;
2013-02-26 23:56:06 +04:00
}
/**
* Append PHP code to template body
*
* @ param string $code
2013-03-14 21:45:00 +04:00
* @ param $source
2013-02-26 23:56:06 +04:00
*/
2013-07-29 14:58:14 +04:00
private function _appendCode ( $code , $source )
{
if ( ! $code ) {
2013-02-27 20:55:08 +04:00
return ;
} else {
2013-07-03 12:10:50 +04:00
$this -> _line += substr_count ( $source , " \n " );
2013-08-29 11:29:34 +04:00
$this -> _body .= " <?php \n /* { $this -> _name } : { $this -> _line } : { $source } */ \n $code ?> " ;
2013-02-27 20:55:08 +04:00
}
2013-02-26 23:56:06 +04:00
}
2014-06-09 23:40:31 +04:00
/**
* @ param $tag_name
*/
2016-08-22 17:08:47 +03:00
public function ignore ( $tag_name )
{
2014-05-12 00:36:50 +04:00
$this -> _ignore = $tag_name ;
}
2013-02-26 23:56:06 +04:00
/**
* @ param callable [] $cb
*/
2013-07-29 14:58:14 +04:00
public function addPostCompile ( $cb )
{
2013-01-25 18:36:16 +04:00
$this -> _post [] = $cb ;
}
/**
* Return PHP code of template
2013-02-27 20:55:08 +04:00
*
2013-01-25 18:36:16 +04:00
* @ return string
*/
2013-07-29 14:58:14 +04:00
public function getBody ()
{
2013-01-28 16:34:34 +04:00
return $this -> _body ;
}
2013-01-25 18:36:16 +04:00
/**
2013-02-07 17:37:16 +04:00
* Return PHP code for saving to file
2013-02-27 20:55:08 +04:00
*
2013-01-25 18:36:16 +04:00
* @ return string
*/
2013-07-29 14:58:14 +04:00
public function getTemplateCode ()
{
2016-06-09 14:24:35 +03:00
$before = $this -> _before ? implode ( " \n " , $this -> _before ) . " \n " : " " ;
2013-07-29 14:58:14 +04:00
return " <?php \n " .
" /** Fenom template ' " . $this -> _name . " ' compiled at " . date ( 'Y-m-d H:i:s' ) . " */ \n " .
$before . // some code 'before' template
2013-08-11 19:55:30 +04:00
" return new Fenom \\ Render( \$ fenom, " . $this -> _getClosureSource () . " , array( \n " .
" \t 'options' => { $this -> _options } , \n " .
" \t 'provider' => " . var_export ( $this -> _scm , true ) . " , \n " .
" \t 'name' => " . var_export ( $this -> _name , true ) . " , \n " .
" \t 'base_name' => " . var_export ( $this -> _base_name , true ) . " , \n " .
" \t 'time' => { $this -> _time } , \n " .
2013-09-02 17:40:58 +04:00
" \t 'depends' => " . var_export ( $this -> _depends , true ) . " , \n " .
2013-08-22 00:03:20 +04:00
" \t 'macros' => " . $this -> _getMacrosArray () . " , \n
2013-08-05 13:07:16 +04:00
)); \n " ;
2013-01-25 18:36:16 +04:00
}
2013-08-22 00:03:20 +04:00
/**
* Make array with macros code
* @ return string
*/
private function _getMacrosArray ()
{
if ( $this -> macros ) {
$macros = array ();
2015-06-03 00:13:09 +03:00
foreach ( $this -> macros as $name => $m ) {
2013-08-22 00:03:20 +04:00
if ( $m [ " recursive " ]) {
2015-06-03 00:13:09 +03:00
$macros [] = " \t \t ' " . $name . " ' => function ( \$ var, \$ tpl) { \n ?> " . $m [ " body " ] . " <?php \n } " ;
2013-08-22 00:03:20 +04:00
}
}
2013-09-02 17:40:58 +04:00
return " array( \n " . implode ( " , \n " , $macros ) . " ) " ;
2013-08-22 00:03:20 +04:00
} else {
return 'array()' ;
}
}
2013-01-25 18:36:16 +04:00
/**
* Return closure code
* @ return string
*/
2013-07-29 14:58:14 +04:00
private function _getClosureSource ()
{
2014-01-27 23:58:14 +04:00
return " function ( \$ var, \$ tpl) { \n ?> { $this -> _body } <?php \n } " ;
2013-01-25 18:36:16 +04:00
}
/**
* Runtime execute template .
*
* @ param array $values input values
* @ throws CompileException
* @ return Render
*/
2013-07-29 14:58:14 +04:00
public function display ( array $values )
{
if ( ! $this -> _code ) {
2013-01-25 18:36:16 +04:00
// evaluate template's code
2013-09-02 17:40:58 +04:00
eval ( " \$ this->_code = " . $this -> _getClosureSource () . " ; \n \$ this->_macros = " . $this -> _getMacrosArray () . ';' );
2013-07-29 14:58:14 +04:00
if ( ! $this -> _code ) {
2013-01-25 18:36:16 +04:00
throw new CompileException ( " Fatal error while creating the template " );
}
}
return parent :: display ( $values );
}
2013-02-23 16:35:11 +04:00
/**
2014-04-17 23:22:50 +04:00
* Add depends
2013-02-23 16:35:11 +04:00
* @ param Render $tpl
*/
2013-07-29 14:58:14 +04:00
public function addDepend ( Render $tpl )
{
2015-01-08 15:52:33 +03:00
$this -> _depends [ $tpl -> getScm ()][ $tpl -> getBaseName ()] = $tpl -> getTime ();
2013-02-23 16:35:11 +04:00
}
2013-02-07 17:37:16 +04:00
2013-07-06 14:03:05 +04:00
/**
* Output the value
*
2014-05-06 00:45:37 +04:00
* @ param string $data
* @ param null | bool $escape
2013-07-06 14:03:05 +04:00
* @ return string
*/
2014-05-06 00:45:37 +04:00
public function out ( $data , $escape = null )
2013-07-29 14:58:14 +04:00
{
2014-05-06 14:22:58 +04:00
if ( $escape === null ) {
2014-05-06 00:45:37 +04:00
$escape = $this -> _options & Fenom :: AUTO_ESCAPE ;
}
if ( $escape ) {
2016-08-22 17:08:47 +03:00
return " echo htmlspecialchars( $data , ENT_COMPAT, " . var_export ( Fenom :: $charset , true ) . " ); " ;
2013-07-04 01:28:10 +04:00
} else {
2013-07-07 01:29:33 +04:00
return " echo $data ; " ;
2013-07-04 01:28:10 +04:00
}
}
2013-07-29 14:58:14 +04:00
2014-02-14 15:55:36 +04:00
/**
* Import block from another template
* @ param string $tpl
*/
2014-02-26 23:57:29 +04:00
public function importBlocks ( $tpl )
{
2014-02-14 15:55:36 +04:00
$donor = $this -> _fenom -> compile ( $tpl , false );
2014-02-27 16:30:44 +04:00
foreach ( $donor -> blocks as $name => $block ) {
if ( ! isset ( $this -> blocks [ $name ])) {
2014-05-06 14:22:58 +04:00
$block [ 'import' ] = $this -> getName ();
2014-02-27 16:30:44 +04:00
$this -> blocks [ $name ] = $block ;
2014-02-14 15:55:36 +04:00
}
}
$this -> addDepend ( $donor );
}
/**
* Extends the template
* @ param string $tpl
2014-02-22 20:34:53 +04:00
* @ return \Fenom\Template parent
2014-02-14 15:55:36 +04:00
*/
2014-02-26 23:57:29 +04:00
public function extend ( $tpl )
{
2016-08-22 17:08:47 +03:00
if ( ! $this -> isStageDone ( self :: COMPILE_STAGE_PARSED )) {
2014-02-22 20:34:53 +04:00
$this -> compile ();
}
2014-05-06 14:22:58 +04:00
$parent = $this -> _fenom -> getRawTemplate () -> load ( $tpl , false );
2016-08-22 17:08:47 +03:00
$parent -> blocks = & $this -> blocks ;
$parent -> macros = & $this -> macros ;
$parent -> _before = & $this -> _before ;
2014-02-22 20:34:53 +04:00
$parent -> extended = $this -> getName ();
2014-02-27 16:30:44 +04:00
if ( ! $this -> ext_stack ) {
2014-02-26 23:57:29 +04:00
$this -> ext_stack [] = $this -> getName ();
}
$this -> ext_stack [] = $parent -> getName ();
2014-05-06 14:22:58 +04:00
$parent -> _options = $this -> _options ;
2014-02-26 23:57:29 +04:00
$parent -> ext_stack = $this -> ext_stack ;
2014-02-14 15:55:36 +04:00
$parent -> compile ();
$this -> _body = $parent -> _body ;
2014-05-06 14:22:58 +04:00
$this -> _src = $parent -> _src ;
2014-02-14 15:55:36 +04:00
$this -> addDepend ( $parent );
2014-02-22 20:34:53 +04:00
return $parent ;
2014-02-14 15:55:36 +04:00
}
2013-07-03 12:10:50 +04:00
/**
2013-07-22 18:03:43 +04:00
* Tag router
2013-07-03 12:10:50 +04:00
* @ param Tokenizer $tokens
*
* @ throws SecurityException
* @ throws CompileException
* @ return string executable PHP code
*/
2013-07-29 14:58:14 +04:00
public function parseTag ( Tokenizer $tokens )
{
2013-01-28 16:34:34 +04:00
try {
2013-07-29 14:58:14 +04:00
if ( $tokens -> is ( Tokenizer :: MACRO_STRING )) {
2014-05-12 00:36:50 +04:00
return $this -> parseAct ( $tokens );
2013-03-14 21:45:00 +04:00
} elseif ( $tokens -> is ( '/' )) {
2013-07-22 18:03:43 +04:00
return $this -> parseEndTag ( $tokens );
2013-02-23 16:35:11 +04:00
} else {
2014-04-17 23:22:50 +04:00
return $this -> out ( $this -> parseExpr ( $tokens ));
2013-01-25 18:36:16 +04:00
}
2013-07-02 11:07:33 +04:00
} catch ( InvalidUsageException $e ) {
2014-02-19 01:53:02 +04:00
throw new CompileException ( $e -> getMessage () . " in { $this -> _name } line { $this -> _line } " , 0 , E_ERROR , $this -> _name , $this -> _line , $e );
2013-01-25 18:36:16 +04:00
} catch ( \LogicException $e ) {
2014-05-12 00:36:50 +04:00
throw new SecurityException ( $e -> getMessage () . " in { $this -> _name } line { $this -> _line } , near ' { " . $tokens -> getSnippetAsString ( 0 , 0 ) . " ' <- there " , 0 , E_ERROR , $this -> _name , $this -> _line , $e );
2013-01-28 16:34:34 +04:00
} catch ( \Exception $e ) {
2014-05-12 00:36:50 +04:00
throw new CompileException ( $e -> getMessage () . " in { $this -> _name } line { $this -> _line } , near ' { " . $tokens -> getSnippetAsString ( 0 , 0 ) . " ' <- there " , 0 , E_ERROR , $this -> _name , $this -> _line , $e );
2016-02-23 13:16:48 +03:00
} catch ( \Throwable $e ) {
throw new CompileException ( $e -> getMessage () . " in { $this -> _name } line { $this -> _line } , near ' { " . $tokens -> getSnippetAsString ( 0 , 0 ) . " ' <- there " , 0 , E_ERROR , $this -> _name , $this -> _line , $e );
2013-01-28 16:34:34 +04:00
}
}
2013-01-25 18:36:16 +04:00
/**
* Close tag handler
2013-02-21 22:51:24 +04:00
*
2013-01-25 18:36:16 +04:00
* @ param Tokenizer $tokens
2013-07-07 01:29:33 +04:00
* @ return string
2013-01-25 18:36:16 +04:00
* @ throws TokenizeException
*/
2013-07-29 14:58:14 +04:00
public function parseEndTag ( Tokenizer $tokens )
{
2013-01-28 16:34:34 +04:00
$name = $tokens -> getNext ( Tokenizer :: MACRO_STRING );
$tokens -> next ();
2013-07-29 14:58:14 +04:00
if ( ! $this -> _stack ) {
2013-01-28 16:34:34 +04:00
throw new TokenizeException ( " Unexpected closing of the tag ' $name ', the tag hasn't been opened " );
}
2014-04-17 23:22:50 +04:00
/** @var Tag $tag */
$tag = array_pop ( $this -> _stack );
if ( $tag -> name !== $name ) {
throw new TokenizeException ( " Unexpected closing of the tag ' $name ' (expecting closing of the tag { $tag -> name } , opened in line { $tag -> line } ) " );
2013-07-07 01:29:33 +04:00
}
2014-04-17 23:22:50 +04:00
return $tag -> end ( $tokens );
2013-07-07 01:29:33 +04:00
}
2013-01-25 18:36:16 +04:00
/**
* Parse action { action ... } or { action ( ... ) ... }
*
* @ static
* @ param Tokenizer $tokens
2013-02-20 18:03:53 +04:00
* @ throws \LogicException
2014-01-28 21:24:47 +04:00
* @ throws \RuntimeException
* @ throws Error\TokenizeException
2013-01-25 18:36:16 +04:00
* @ return string
*/
2013-07-29 14:58:14 +04:00
public function parseAct ( Tokenizer $tokens )
{
2014-04-17 23:22:50 +04:00
$action = $tokens -> get ( Tokenizer :: MACRO_STRING );
$tokens -> next ();
2014-05-06 14:22:58 +04:00
if ( $tokens -> is ( " ( " , T_DOUBLE_COLON , T_NS_SEPARATOR ) && ! $tokens -> isWhiteSpaced ()
) { // just invoke function or static method
2013-02-23 02:03:05 +04:00
$tokens -> back ();
2013-08-11 19:55:30 +04:00
return $this -> out ( $this -> parseExpr ( $tokens ));
2014-04-17 23:22:50 +04:00
} elseif ( $tokens -> is ( '.' )) {
2013-02-23 02:03:05 +04:00
$name = $tokens -> skip () -> get ( Tokenizer :: MACRO_STRING );
2013-07-29 14:58:14 +04:00
if ( $action !== " macro " ) {
$name = $action . " . " . $name ;
2013-02-23 02:03:05 +04:00
}
2013-07-29 14:53:21 +04:00
return $this -> parseMacroCall ( $tokens , $name );
2013-01-25 18:36:16 +04:00
}
2014-04-17 23:22:50 +04:00
if ( $info = $this -> _fenom -> getTag ( $action , $this )) {
$tag = new Tag ( $action , $this , $info , $this -> _body );
2014-04-17 23:27:59 +04:00
if ( $tokens -> is ( ':' )) { // parse tag options
2014-04-17 23:22:50 +04:00
do {
2014-05-06 00:45:37 +04:00
$tag -> tagOption ( $tokens -> next () -> need ( T_STRING ) -> getAndNext ());
2014-04-17 23:27:59 +04:00
} while ( $tokens -> is ( ':' ));
2014-04-09 18:03:49 +04:00
}
2014-04-17 23:22:50 +04:00
$code = $tag -> start ( $tokens );
2014-05-06 00:45:37 +04:00
if ( $tag -> isClosed ()) {
$tag -> restoreAll ();
} else {
2014-04-17 23:22:50 +04:00
array_push ( $this -> _stack , $tag );
2013-01-25 18:36:16 +04:00
}
2014-04-17 23:22:50 +04:00
return $code ;
2013-01-28 16:34:34 +04:00
}
2013-01-25 18:36:16 +04:00
2013-07-29 14:58:14 +04:00
for ( $j = $i = count ( $this -> _stack ) - 1 ; $i >= 0 ; $i -- ) { // call function's internal tag
if ( $this -> _stack [ $i ] -> hasTag ( $action , $j - $i )) {
2013-01-28 16:34:34 +04:00
return $this -> _stack [ $i ] -> tag ( $action , $tokens );
}
}
2013-07-29 14:58:14 +04:00
if ( $tags = $this -> _fenom -> getTagOwners ( $action )) { // unknown template tag
2016-08-22 17:08:47 +03:00
throw new TokenizeException (
" Unexpected tag ' $action ' (this tag can be used with ' " . implode (
2014-05-06 14:22:58 +04:00
" ', ' " ,
$tags
2016-08-22 17:08:47 +03:00
) . " ') "
);
2013-01-25 18:36:16 +04:00
} else {
2014-02-27 16:30:44 +04:00
throw new TokenizeException ( " Unexpected tag ' $action ' " );
2013-01-25 18:36:16 +04:00
}
2013-01-28 16:34:34 +04:00
}
2013-01-25 18:36:16 +04:00
2014-04-17 23:22:50 +04:00
/**
* Get current template line
* @ return int
*/
2014-04-17 23:27:59 +04:00
public function getLine ()
{
2014-04-17 23:22:50 +04:00
return $this -> _line ;
}
2013-01-25 18:36:16 +04:00
/**
2013-08-11 19:55:30 +04:00
* Parse expressions . The mix of operators and terms .
2013-01-25 18:36:16 +04:00
*
* @ param Tokenizer $tokens
2015-02-22 23:07:26 +03:00
* @ param bool $is_var
* @ throws \Exception
2013-10-08 17:48:23 +04:00
* @ return string
2013-01-25 18:36:16 +04:00
*/
2015-02-22 23:07:26 +03:00
public function parseExpr ( Tokenizer $tokens , & $is_var = false )
2013-08-02 21:50:04 +04:00
{
2014-05-06 14:22:58 +04:00
$exp = array ();
$var = false ; // last term was: true - variable, false - mixed
$op = false ; // last exp was operator
2013-08-03 18:56:17 +04:00
$cond = false ; // was comparison operator
2013-08-02 21:50:04 +04:00
while ( $tokens -> valid ()) {
2013-08-02 20:29:18 +04:00
// parse term
2015-02-12 12:13:35 +03:00
$term = $this -> parseTerm ( $tokens , $var , - 1 ); // term of the expression
2013-08-02 21:50:04 +04:00
if ( $term !== false ) {
2013-09-02 17:40:58 +04:00
if ( $tokens -> is ( '?' , '!' )) {
2016-08-22 17:08:47 +03:00
if ( $cond ) {
2015-02-02 12:03:15 +03:00
$term = array_pop ( $exp ) . ' ' . $term ;
2016-08-22 17:08:47 +03:00
$term = '(' . array_pop ( $exp ) . ' ' . $term . ')' ;
2015-02-19 17:11:44 +03:00
$var = false ;
2015-02-02 12:03:15 +03:00
}
2013-09-14 11:24:23 +04:00
$term = $this -> parseTernary ( $tokens , $term , $var );
2014-05-06 14:22:58 +04:00
$var = false ;
2013-09-02 17:40:58 +04:00
}
2013-08-02 20:29:18 +04:00
$exp [] = $term ;
2014-05-06 14:22:58 +04:00
$op = false ;
2013-08-02 20:29:18 +04:00
} else {
break ;
}
2013-08-02 21:50:04 +04:00
if ( ! $tokens -> valid ()) {
2013-08-02 20:29:18 +04:00
break ;
}
// parse operator
2013-08-03 18:56:17 +04:00
if ( $tokens -> is ( Tokenizer :: MACRO_BINARY )) { // binary operator: $a + $b, $a <= $b, ...
if ( $tokens -> is ( Tokenizer :: MACRO_COND )) { // comparison operator
2013-08-02 20:29:18 +04:00
if ( $cond ) {
2013-07-22 18:03:43 +04:00
break ;
}
2013-08-02 20:29:18 +04:00
$cond = true ;
2013-08-06 12:04:23 +04:00
} elseif ( $tokens -> is ( Tokenizer :: MACRO_BOOLEAN )) {
$cond = false ;
2013-08-02 20:29:18 +04:00
}
$op = $tokens -> getAndNext ();
2014-08-06 01:17:21 +04:00
} elseif ( $tokens -> is ( Tokenizer :: MACRO_EQUALS , '[' )) { // assignment operator: $a = 4, $a += 3, ...
2013-08-02 21:50:04 +04:00
if ( ! $var ) {
2013-08-02 20:29:18 +04:00
break ;
}
$op = $tokens -> getAndNext ();
2016-08-22 17:08:47 +03:00
if ( $op == '[' ) {
2014-08-06 01:17:21 +04:00
$tokens -> need ( ']' ) -> next () -> need ( '=' ) -> next ();
$op = '[]=' ;
}
2013-08-03 18:56:17 +04:00
} elseif ( $tokens -> is ( T_STRING )) { // test or containment operator: $a in $b, $a is set, ...
2013-08-02 20:29:18 +04:00
if ( ! $exp ) {
break ;
}
$operator = $tokens -> current ();
if ( $operator == " is " ) {
2014-05-06 14:22:58 +04:00
$item = array_pop ( $exp );
2013-08-02 20:29:18 +04:00
$exp [] = $this -> parseIs ( $tokens , $item , $var );
} elseif ( $operator == " in " || ( $operator == " not " && $tokens -> isNextToken ( " in " ))) {
2014-05-06 14:22:58 +04:00
$item = array_pop ( $exp );
2016-02-23 13:16:48 +03:00
$exp [] = $this -> parseIn ( $tokens , $item );
2013-01-25 18:36:16 +04:00
} else {
2013-08-02 20:29:18 +04:00
break ;
2013-01-25 18:36:16 +04:00
}
2013-08-03 18:56:17 +04:00
} elseif ( $tokens -> is ( '~' )) { // string concatenation operator: 'asd' ~ $var
2015-02-12 12:13:35 +03:00
if ( $tokens -> isNext ( '=' )) { // ~=
2014-06-08 23:50:43 +04:00
$exp [] = " .= " ;
$tokens -> next () -> next ();
} else {
$concat = array ( array_pop ( $exp ));
2014-06-09 23:40:31 +04:00
2014-06-08 23:50:43 +04:00
while ( $tokens -> is ( '~' )) {
$tokens -> next ();
if ( $tokens -> is ( T_LNUMBER , T_DNUMBER )) {
$concat [] = " strval( " . $this -> parseTerm ( $tokens ) . " ) " ;
} else {
2015-05-23 23:09:16 +03:00
2015-02-12 12:13:35 +03:00
if ( $tokens -> is ( '~' )) {
2014-10-05 20:37:30 +04:00
$tokens -> next ();
2015-05-23 23:09:16 +03:00
$concat [] = " ' ' " ;
2014-10-05 20:37:30 +04:00
}
2016-08-22 17:08:47 +03:00
if ( ! $term2 = " strval( " . $this -> parseTerm ( $tokens ) . " ) " ) {
2014-06-09 23:40:31 +04:00
throw new UnexpectedTokenException ( $tokens );
}
2015-05-23 23:09:16 +03:00
$concat [] = $term2 ;
2014-06-08 23:50:43 +04:00
}
2013-08-03 18:56:17 +04:00
}
2014-06-08 23:50:43 +04:00
$exp [] = " ( " . implode ( " . " , $concat ) . " ) " ;
2013-08-03 18:56:17 +04:00
}
2013-08-02 20:29:18 +04:00
} else {
break ;
}
2013-08-02 21:50:04 +04:00
if ( $op ) {
2013-08-02 20:29:18 +04:00
$exp [] = $op ;
}
}
2013-08-11 19:55:30 +04:00
if ( $op || ! $exp ) {
2013-08-02 20:29:18 +04:00
throw new UnexpectedTokenException ( $tokens );
}
2015-02-22 23:07:26 +03:00
2016-08-22 17:08:47 +03:00
if ( count ( $exp ) == 1 && $var ) {
2015-02-22 23:07:26 +03:00
$is_var = true ;
}
2013-08-02 20:29:18 +04:00
return implode ( ' ' , $exp );
}
/**
2013-09-02 17:40:58 +04:00
* Parse any term of expression : - 2 , ++ $var , 'adf'
2013-08-02 20:29:18 +04:00
*
* @ param Tokenizer $tokens
2013-09-02 17:40:58 +04:00
* @ param bool $is_var is parsed term - plain variable
2015-02-12 12:13:35 +03:00
* @ param int $allows
2013-08-02 20:29:18 +04:00
* @ throws \Exception
2013-09-02 17:40:58 +04:00
* @ return bool | string
2013-08-02 20:29:18 +04:00
*/
2015-02-12 12:13:35 +03:00
public function parseTerm ( Tokenizer $tokens , & $is_var = false , $allows = - 1 )
2013-08-02 21:50:04 +04:00
{
2013-08-02 20:29:18 +04:00
$is_var = false ;
2013-08-11 19:55:30 +04:00
if ( $tokens -> is ( Tokenizer :: MACRO_UNARY )) {
$unary = $tokens -> getAndNext ();
} else {
$unary = " " ;
}
2016-08-22 17:08:47 +03:00
switch ( $tokens -> key ()) {
2015-06-01 23:36:30 +03:00
case T_LNUMBER :
case T_DNUMBER :
2016-02-23 13:16:48 +03:00
$code = $unary . $this -> parseScalar ( $tokens );
2015-06-01 23:36:30 +03:00
break ;
case T_CONSTANT_ENCAPSED_STRING :
case '"' :
case T_ENCAPSED_AND_WHITESPACE :
if ( $unary ) {
throw new UnexpectedTokenException ( $tokens -> back ());
2013-09-02 17:40:58 +04:00
}
2016-02-23 13:16:48 +03:00
$code = $this -> parseScalar ( $tokens );
2015-06-01 23:36:30 +03:00
break ;
2016-06-09 14:24:35 +03:00
/** @noinspection PhpMissingBreakStatementInspection */
2015-06-01 23:42:47 +03:00
case '$' :
$code = $this -> parseAccessor ( $tokens , $is_var );
2016-08-22 17:08:47 +03:00
if ( ! $is_var ) {
2017-04-22 15:59:20 +03:00
if ( $tokens -> is ( T_OBJECT_OPERATOR )) {
if ( $this -> _options & Fenom :: DENY_METHODS ) {
throw new \LogicException ( " Forbidden to call methods " );
}
$code = $unary . $this -> parseChain ( $tokens , $code );
} else {
$code = $unary . $code ;
}
2015-06-01 23:42:47 +03:00
break ;
}
2016-08-22 17:08:47 +03:00
/* no break */
2015-06-01 23:36:30 +03:00
case T_VARIABLE :
2016-08-22 17:08:47 +03:00
if ( ! isset ( $code )) {
2015-06-01 23:42:47 +03:00
$code = $this -> parseVariable ( $tokens );
}
2015-06-01 23:36:30 +03:00
if ( $tokens -> is ( " ( " ) && $tokens -> hasBackList ( T_STRING , T_OBJECT_OPERATOR )) {
if ( $this -> _options & Fenom :: DENY_METHODS ) {
throw new \LogicException ( " Forbidden to call methods " );
}
$code = $unary . $this -> parseChain ( $tokens , $code );
} elseif ( $tokens -> is ( Tokenizer :: MACRO_INCDEC )) {
2016-08-22 17:08:47 +03:00
if ( $this -> _options & Fenom :: FORCE_VERIFY ) {
2015-06-01 23:36:30 +03:00
$code = $unary . '(isset(' . $code . ') ? ' . $code . $tokens -> getAndNext () . ' : null)' ;
} else {
$code = $unary . $code . $tokens -> getAndNext ();
}
2014-11-05 16:07:50 +03:00
} else {
2016-08-22 17:08:47 +03:00
if ( $this -> _options & Fenom :: FORCE_VERIFY ) {
2015-06-01 23:36:30 +03:00
$code = $unary . '(isset(' . $code . ') ? ' . $code . ' : null)' ;
} else {
$is_var = true ;
2016-08-22 17:08:47 +03:00
$code = $unary . $code ;
2015-06-01 23:36:30 +03:00
}
2014-11-05 16:07:50 +03:00
}
2015-06-01 23:36:30 +03:00
break ;
case T_DEC :
case T_INC :
2016-08-22 17:08:47 +03:00
if ( $this -> _options & Fenom :: FORCE_VERIFY ) {
$var = $this -> parseVariable ( $tokens );
$code = $unary . '(isset(' . $var . ') ? ' . $tokens -> getAndNext () . $this -> parseVariable ( $tokens ) . ' : null)' ;
2014-11-05 16:07:50 +03:00
} else {
2015-06-01 23:36:30 +03:00
$code = $unary . $tokens -> getAndNext () . $this -> parseVariable ( $tokens );
2014-11-05 16:07:50 +03:00
}
2015-06-01 23:36:30 +03:00
break ;
case '(' :
$tokens -> next ();
$code = $unary . " ( " . $this -> parseExpr ( $tokens ) . " ) " ;
$tokens -> need ( " ) " ) -> next ();
break ;
case T_STRING :
if ( $tokens -> isSpecialVal ()) {
$code = $unary . $tokens -> getAndNext ();
} elseif ( $tokens -> isNext ( " ( " ) && ! $tokens -> getWhitespace ()) {
$func = $this -> _fenom -> getModifier ( $modifier = $tokens -> current (), $this );
if ( ! $func ) {
throw new \Exception ( " Function " . $tokens -> getAndNext () . " not found " );
}
if ( ! is_string ( $func )) { // dynamic modifier
2016-08-22 17:08:47 +03:00
$call = 'call_user_func_array($tpl->getStorage()->getModifier("' . $modifier . '"), array' . $this -> parseArgs ( $tokens -> next ()) . ')' ; // @todo optimize
2015-06-01 23:36:30 +03:00
} else {
$call = $func . $this -> parseArgs ( $tokens -> next ());
}
$code = $unary . $this -> parseChain ( $tokens , $call );
} elseif ( $tokens -> isNext ( T_NS_SEPARATOR , T_DOUBLE_COLON )) {
$method = $this -> parseStatic ( $tokens );
$args = $this -> parseArgs ( $tokens );
$code = $unary . $this -> parseChain ( $tokens , $method . $args );
} else {
return false ;
2013-08-02 20:29:18 +04:00
}
2015-06-01 23:36:30 +03:00
break ;
case T_ISSET :
case T_EMPTY :
$func = $tokens -> getAndNext ();
if ( $tokens -> is ( " ( " ) && $tokens -> isNext ( T_VARIABLE )) {
$code = $unary . $func . " ( " . $this -> parseVariable ( $tokens -> next ()) . " ) " ;
$tokens -> need ( ')' ) -> next ();
2015-02-19 17:04:15 +03:00
} else {
2015-06-01 23:36:30 +03:00
throw new TokenizeException ( " Unexpected token " . $tokens -> getNext () . " , isset() and empty() accept only variables " );
}
break ;
case '[' :
if ( $unary ) {
throw new UnexpectedTokenException ( $tokens -> back ());
}
$code = $this -> parseArray ( $tokens );
break ;
default :
if ( $unary ) {
throw new UnexpectedTokenException ( $tokens -> back ());
} else {
return false ;
2015-02-19 17:04:15 +03:00
}
2013-01-25 18:36:16 +04:00
}
2015-02-12 12:13:35 +03:00
if (( $allows & self :: TERM_MODS ) && $tokens -> is ( '|' )) {
2016-08-22 17:08:47 +03:00
$code = $this -> parseModifier ( $tokens , $code );
$is_var = false ;
2015-02-12 12:13:35 +03:00
}
2016-08-22 17:08:47 +03:00
if (( $allows & self :: TERM_RANGE ) && $tokens -> is ( '.' ) && $tokens -> isNext ( '.' )) {
2015-02-12 12:13:35 +03:00
$tokens -> next () -> next ();
2016-08-22 17:08:47 +03:00
$code = '(new \Fenom\RangeIterator(' . $code . ', ' . $this -> parseTerm ( $tokens , $var , self :: TERM_MODS ) . '))' ;
$is_var = false ;
2015-02-12 12:13:35 +03:00
}
return $code ;
2013-01-28 16:34:34 +04:00
}
2013-01-25 18:36:16 +04:00
2014-08-23 11:41:21 +04:00
/**
* Parse call - chunks : $var -> func () -> func () -> prop -> func () ->...
* @ param Tokenizer $tokens
* @ param string $code start point ( it is $var )
* @ return string
*/
2016-08-22 17:08:47 +03:00
public function parseChain ( Tokenizer $tokens , $code )
{
2014-08-23 11:41:21 +04:00
do {
if ( $tokens -> is ( '(' )) {
$code .= $this -> parseArgs ( $tokens );
}
if ( $tokens -> is ( T_OBJECT_OPERATOR ) && $tokens -> isNext ( T_STRING )) {
$code .= '->' . $tokens -> next () -> getAndNext ();
}
} while ( $tokens -> is ( '(' , T_OBJECT_OPERATOR ));
return $code ;
}
2013-07-03 12:22:35 +04:00
/**
2016-05-06 23:04:08 +03:00
* Parse variable name : $a , $a . b , $a . b [ 'c' ], $a : index
2013-08-11 19:55:30 +04:00
* @ param Tokenizer $tokens
* @ param $var
* @ return string
* @ throws Error\UnexpectedTokenException
*/
2013-09-02 17:40:58 +04:00
public function parseVariable ( Tokenizer $tokens , $var = null )
2013-08-11 19:55:30 +04:00
{
2014-02-27 16:30:44 +04:00
if ( ! $var ) {
2016-08-22 17:08:47 +03:00
if ( $tokens -> isNext ( '@' )) {
2016-05-06 23:04:08 +03:00
// $v = $tokens->get(T_VARIABLE);
$prop = $tokens -> next () -> next () -> get ( T_STRING );
2016-08-22 17:08:47 +03:00
if ( $tag = $this -> getParentScope ( " foreach " )) {
2016-05-06 23:04:08 +03:00
$tokens -> next ();
return Compiler :: foreachProp ( $tag , $prop );
} else {
throw new UnexpectedTokenException ( $tokens );
}
} else {
$var = '$var["' . substr ( $tokens -> get ( T_VARIABLE ), 1 ) . '"]' ;
$tokens -> next ();
}
2013-09-02 17:40:58 +04:00
}
2013-07-29 14:58:14 +04:00
while ( $t = $tokens -> key ()) {
2013-08-11 19:55:30 +04:00
if ( $t === " . " ) {
$tokens -> next ();
2013-07-29 14:58:14 +04:00
if ( $tokens -> is ( T_VARIABLE )) {
2014-01-27 23:58:14 +04:00
$key = '[ $var["' . substr ( $tokens -> getAndNext (), 1 ) . '"] ]' ;
2013-07-29 14:58:14 +04:00
} elseif ( $tokens -> is ( Tokenizer :: MACRO_STRING )) {
2013-08-11 19:55:30 +04:00
$key = '["' . $tokens -> getAndNext () . '"]' ;
} elseif ( $tokens -> is ( Tokenizer :: MACRO_SCALAR )) {
$key = " [ " . $tokens -> getAndNext () . " ] " ;
} elseif ( $tokens -> is ( '"' )) {
$key = " [ " . $this -> parseQuote ( $tokens ) . " ] " ;
2016-08-22 17:08:47 +03:00
} elseif ( $tokens -> is ( '.' )) {
2015-02-12 12:13:35 +03:00
$tokens -> back ();
break ;
2013-01-25 18:36:16 +04:00
} else {
2013-08-11 19:55:30 +04:00
throw new UnexpectedTokenException ( $tokens );
2013-01-25 18:36:16 +04:00
}
2013-08-11 19:55:30 +04:00
$var .= $key ;
} elseif ( $t === " [ " ) {
2016-08-22 17:08:47 +03:00
if ( $tokens -> isNext ( ']' )) {
2014-08-06 01:17:21 +04:00
break ;
}
2013-01-25 18:36:16 +04:00
$tokens -> next ();
2013-07-29 14:58:14 +04:00
if ( $tokens -> is ( Tokenizer :: MACRO_STRING )) {
if ( $tokens -> isNext ( " ( " )) {
2013-08-11 19:55:30 +04:00
$key = " [ " . $this -> parseExpr ( $tokens ) . " ] " ;
2013-01-25 18:36:16 +04:00
} else {
2013-07-29 14:58:14 +04:00
$key = '["' . $tokens -> current () . '"]' ;
2013-01-25 18:36:16 +04:00
$tokens -> next ();
}
} else {
2013-08-11 19:55:30 +04:00
$key = " [ " . $this -> parseExpr ( $tokens ) . " ] " ;
2013-01-25 18:36:16 +04:00
}
$tokens -> get ( " ] " );
$tokens -> next ();
2013-08-11 19:55:30 +04:00
$var .= $key ;
2013-07-29 14:58:14 +04:00
} elseif ( $t === T_DNUMBER ) {
2013-08-11 19:55:30 +04:00
$var .= '[' . substr ( $tokens -> getAndNext (), 1 ) . ']' ;
2013-07-29 14:58:14 +04:00
} elseif ( $t === T_OBJECT_OPERATOR ) {
2013-08-11 19:55:30 +04:00
$var .= " -> " . $tokens -> getNext ( T_STRING );
2013-07-22 18:03:43 +04:00
$tokens -> next ();
2013-03-17 14:37:23 +04:00
} else {
break ;
}
}
2013-08-11 19:55:30 +04:00
return $var ;
2013-03-17 14:37:23 +04:00
}
2013-08-11 19:55:30 +04:00
/**
* Parse accessor
2014-10-05 20:37:30 +04:00
* @ param Tokenizer $tokens
2015-06-01 23:20:02 +03:00
* @ param bool $is_var
2014-10-05 20:37:30 +04:00
* @ return string
2013-08-11 19:55:30 +04:00
*/
2015-06-01 23:20:02 +03:00
public function parseAccessor ( Tokenizer $tokens , & $is_var = false )
2013-08-11 19:55:30 +04:00
{
2014-10-05 20:37:30 +04:00
$accessor = $tokens -> need ( '$' ) -> next () -> need ( '.' ) -> next () -> current ();
2016-08-22 17:08:47 +03:00
$parser = $this -> getStorage () -> getAccessor ( $accessor );
$is_var = false ;
if ( $parser ) {
if ( is_array ( $parser )) {
if ( isset ( $parser [ 'callback' ])) {
2016-06-09 13:44:43 +03:00
$tokens -> next ();
2016-08-22 17:08:47 +03:00
return 'call_user_func($tpl->getStorage()->getAccessor(' . var_export ( $accessor , true ) .
', "callback"), ' . var_export ( $accessor , true ) . ', $tpl, $var)' ;
2016-06-09 13:44:43 +03:00
} else {
2016-08-22 17:08:47 +03:00
return call_user_func_array (
$parser [ 'parser' ], array (
$parser [ 'accessor' ],
$tokens -> next (),
$this ,
& $is_var
)
);
2016-06-09 13:44:43 +03:00
}
2015-08-07 17:20:50 +03:00
} else {
return call_user_func_array ( $parser , array ( $tokens -> next (), $this , & $is_var ));
2015-06-01 23:20:02 +03:00
}
2014-10-05 20:37:30 +04:00
} else {
2016-06-09 13:44:43 +03:00
throw new \RuntimeException ( " Unknown accessor ' \$ . $accessor ' " );
2013-08-11 19:55:30 +04:00
}
}
2013-07-03 12:22:35 +04:00
/**
* Parse ternary operator
*
* @ param Tokenizer $tokens
* @ param $var
2013-09-14 11:24:23 +04:00
* @ param $is_var
2013-07-03 12:22:35 +04:00
* @ return string
* @ throws UnexpectedTokenException
*/
2013-09-14 11:24:23 +04:00
public function parseTernary ( Tokenizer $tokens , $var , $is_var )
2013-07-29 14:58:14 +04:00
{
2013-09-14 11:24:23 +04:00
$empty = $tokens -> is ( '?' );
2013-03-17 14:37:23 +04:00
$tokens -> next ();
2016-06-09 13:44:43 +03:00
if ( $tokens -> is ( " : " , " ? " )) {
2013-03-17 14:37:23 +04:00
$tokens -> next ();
2013-07-29 14:58:14 +04:00
if ( $empty ) {
2014-02-27 16:30:44 +04:00
if ( $is_var ) {
2013-09-14 11:24:23 +04:00
return '(empty(' . $var . ') ? (' . $this -> parseExpr ( $tokens ) . ') : ' . $var . ')' ;
} else {
2014-02-27 16:30:44 +04:00
return '(' . $var . ' ?: (' . $this -> parseExpr ( $tokens ) . '))' ;
2013-09-14 11:24:23 +04:00
}
2013-03-17 14:37:23 +04:00
} else {
2014-02-27 16:30:44 +04:00
if ( $is_var ) {
2013-09-14 11:24:23 +04:00
return '(isset(' . $var . ') ? ' . $var . ' : (' . $this -> parseExpr ( $tokens ) . '))' ;
} else {
return '((' . $var . ' !== null) ? ' . $var . ' : (' . $this -> parseExpr ( $tokens ) . '))' ;
}
2013-03-17 14:37:23 +04:00
}
2014-05-06 14:22:58 +04:00
} elseif ( $tokens -> is (
Tokenizer :: MACRO_BINARY ,
Tokenizer :: MACRO_BOOLEAN ,
Tokenizer :: MACRO_MATH
) || ! $tokens -> valid ()
) {
2013-07-29 14:58:14 +04:00
if ( $empty ) {
2014-02-27 16:30:44 +04:00
if ( $is_var ) {
2013-09-14 11:24:23 +04:00
return '!empty(' . $var . ')' ;
} else {
return '(' . $var . ')' ;
}
2013-03-17 14:37:23 +04:00
} else {
2014-02-27 16:30:44 +04:00
if ( $is_var ) {
2013-09-14 11:24:23 +04:00
return 'isset(' . $var . ')' ;
} else {
return '(' . $var . ' !== null)' ;
}
2013-03-17 14:37:23 +04:00
}
} else {
2013-08-11 19:55:30 +04:00
$expr1 = $this -> parseExpr ( $tokens );
2013-08-02 23:01:06 +04:00
$tokens -> need ( ':' ) -> skip ();
2013-08-11 19:55:30 +04:00
$expr2 = $this -> parseExpr ( $tokens );
2013-07-29 14:58:14 +04:00
if ( $empty ) {
2014-02-27 16:30:44 +04:00
if ( $is_var ) {
2013-09-14 11:24:23 +04:00
return '(empty(' . $var . ') ? ' . $expr2 . ' : ' . $expr1 . ')' ;
} else {
return '(' . $var . ' ? ' . $expr1 . ' : ' . $expr2 . ')' ;
}
2013-03-17 14:37:23 +04:00
} else {
2014-02-27 16:30:44 +04:00
if ( $is_var ) {
2013-09-14 11:24:23 +04:00
return '(isset(' . $var . ') ? ' . $expr1 . ' : ' . $expr2 . ')' ;
} else {
return '((' . $var . ' !== null) ? ' . $expr1 . ' : ' . $expr2 . ')' ;
}
2013-03-17 14:37:23 +04:00
}
}
}
2013-07-22 18:03:43 +04:00
/**
2013-07-29 14:53:21 +04:00
* Parse 'is' and 'is not' operators
2013-08-23 00:55:53 +04:00
* @ see _tests
2013-07-22 18:03:43 +04:00
* @ param Tokenizer $tokens
* @ param string $value
* @ param bool $variable
* @ throws InvalidUsageException
* @ return string
*/
2013-07-29 14:58:14 +04:00
public function parseIs ( Tokenizer $tokens , $value , $variable = false )
{
2013-07-22 18:03:43 +04:00
$tokens -> next ();
2013-07-29 14:58:14 +04:00
if ( $tokens -> current () == 'not' ) {
2013-07-22 18:03:43 +04:00
$invert = '!' ;
2014-05-06 14:22:58 +04:00
$equal = '!=' ;
2013-07-22 18:03:43 +04:00
$tokens -> next ();
} else {
$invert = '' ;
2014-05-06 14:22:58 +04:00
$equal = '==' ;
2013-07-22 18:03:43 +04:00
}
2013-07-29 14:58:14 +04:00
if ( $tokens -> is ( Tokenizer :: MACRO_STRING )) {
2013-07-22 18:03:43 +04:00
$action = $tokens -> current ();
2013-07-29 14:58:14 +04:00
if ( ! $variable && ( $action == " set " || $action == " empty " )) {
2013-07-22 18:03:43 +04:00
$action = " _ $action " ;
$tokens -> next ();
2014-04-12 01:00:58 +04:00
return $invert . sprintf ( $this -> _fenom -> getTest ( $action ), $value );
} elseif ( $test = $this -> _fenom -> getTest ( $action )) {
2013-07-22 18:03:43 +04:00
$tokens -> next ();
2014-04-12 01:00:58 +04:00
return $invert . sprintf ( $test , $value );
2013-07-29 14:58:14 +04:00
} elseif ( $tokens -> isSpecialVal ()) {
2013-07-22 18:03:43 +04:00
$tokens -> next ();
2013-07-29 14:58:14 +04:00
return '(' . $value . ' ' . $equal . '= ' . $action . ')' ;
2013-07-22 18:03:43 +04:00
}
2013-07-29 14:58:14 +04:00
return $invert . '(' . $value . ' instanceof \\' . $this -> parseName ( $tokens ) . ')' ;
2013-09-02 17:40:58 +04:00
} elseif ( $tokens -> is ( T_VARIABLE , '[' , Tokenizer :: MACRO_SCALAR , '"' )) {
return '(' . $value . ' ' . $equal . '= ' . $this -> parseTerm ( $tokens ) . ')' ;
2013-07-29 14:58:14 +04:00
} elseif ( $tokens -> is ( T_NS_SEPARATOR )) { //
return $invert . '(' . $value . ' instanceof \\' . $this -> parseName ( $tokens ) . ')' ;
2013-07-22 18:03:43 +04:00
} else {
throw new InvalidUsageException ( " Unknown argument " );
}
}
/**
* Parse 'in' and 'not in' operators
* @ param Tokenizer $tokens
* @ param string $value
* @ throws InvalidUsageException
* @ throws UnexpectedTokenException
* @ return string
*/
2013-07-29 14:58:14 +04:00
public function parseIn ( Tokenizer $tokens , $value )
{
2013-07-22 18:03:43 +04:00
$checkers = array (
" string " => 'is_int(strpos(%2$s, %1$s))' ,
2014-05-06 14:22:58 +04:00
" list " => " in_array(%s, %s) " ,
" keys " => " array_key_exists(%s, %s) " ,
" auto " => '\Fenom\Modifier::in(%s, %s)'
2013-07-22 18:03:43 +04:00
);
2014-05-06 14:22:58 +04:00
$checker = null ;
$invert = '' ;
2013-07-29 14:58:14 +04:00
if ( $tokens -> current () == 'not' ) {
2013-07-22 18:03:43 +04:00
$invert = '!' ;
$tokens -> next ();
}
2013-07-29 14:58:14 +04:00
if ( $tokens -> current () !== " in " ) {
2013-07-22 18:03:43 +04:00
throw new UnexpectedTokenException ( $tokens );
}
$tokens -> next ();
2013-07-29 14:58:14 +04:00
if ( $tokens -> is ( Tokenizer :: MACRO_STRING )) {
2013-07-22 18:03:43 +04:00
$checker = $tokens -> current ();
2013-07-29 14:58:14 +04:00
if ( ! isset ( $checkers [ $checker ])) {
2013-07-22 18:03:43 +04:00
throw new UnexpectedTokenException ( $tokens );
}
$tokens -> next ();
}
2013-07-29 14:58:14 +04:00
if ( $tokens -> is ( '[' )) {
if ( $checker == " string " ) {
2013-07-22 18:03:43 +04:00
throw new InvalidUsageException ( " Can not use string operation for array " );
2013-07-29 14:58:14 +04:00
} elseif ( ! $checker ) {
2013-07-22 18:03:43 +04:00
$checker = " list " ;
}
2013-07-29 14:58:14 +04:00
return $invert . sprintf ( $checkers [ $checker ], $value , $this -> parseArray ( $tokens ));
} elseif ( $tokens -> is ( '"' , T_ENCAPSED_AND_WHITESPACE , T_CONSTANT_ENCAPSED_STRING )) {
if ( ! $checker ) {
2013-07-22 18:03:43 +04:00
$checker = " string " ;
2013-07-29 14:58:14 +04:00
} elseif ( $checker != " string " ) {
2013-07-22 18:03:43 +04:00
throw new InvalidUsageException ( " Can not use array operation for string " );
}
2013-07-29 14:58:14 +04:00
return $invert . sprintf ( $checkers [ $checker ], " strval( $value ) " , $this -> parseScalar ( $tokens ));
} elseif ( $tokens -> is ( T_VARIABLE , Tokenizer :: MACRO_INCDEC )) {
if ( ! $checker ) {
2013-07-22 18:03:43 +04:00
$checker = " auto " ;
}
2013-09-02 17:40:58 +04:00
return $invert . sprintf ( $checkers [ $checker ], $value , $this -> parseTerm ( $tokens ));
2013-07-22 18:03:43 +04:00
} else {
throw new UnexpectedTokenException ( $tokens );
}
}
/**
* Parse method , class or constant name
*
* @ param Tokenizer $tokens
* @ return string
*/
2013-07-29 14:58:14 +04:00
public function parseName ( Tokenizer $tokens )
{
2013-07-22 18:03:43 +04:00
$tokens -> skipIf ( T_NS_SEPARATOR );
$name = " " ;
2013-07-29 14:58:14 +04:00
if ( $tokens -> is ( T_STRING )) {
2013-07-22 18:03:43 +04:00
$name .= $tokens -> getAndNext ();
2013-07-29 14:58:14 +04:00
while ( $tokens -> is ( T_NS_SEPARATOR )) {
$name .= '\\' . $tokens -> next () -> get ( T_STRING );
2013-07-22 18:03:43 +04:00
$tokens -> next ();
}
}
return $name ;
}
2013-01-25 18:36:16 +04:00
/**
* Parse scalar values
*
* @ param Tokenizer $tokens
2013-09-02 17:40:58 +04:00
* @ throws Error\UnexpectedTokenException
2013-01-25 18:36:16 +04:00
* @ return string
*/
2013-09-02 17:40:58 +04:00
public function parseScalar ( Tokenizer $tokens )
2013-07-29 14:58:14 +04:00
{
2013-09-02 17:40:58 +04:00
$token = $tokens -> key ();
switch ( $token ) {
case T_CONSTANT_ENCAPSED_STRING :
case T_LNUMBER :
case T_DNUMBER :
2013-09-14 11:24:23 +04:00
return $tokens -> getAndNext ();
2013-09-02 17:40:58 +04:00
case T_ENCAPSED_AND_WHITESPACE :
case '"' :
2013-09-14 11:24:23 +04:00
return $this -> parseQuote ( $tokens );
2013-09-02 17:40:58 +04:00
default :
throw new UnexpectedTokenException ( $tokens );
2013-01-25 18:36:16 +04:00
}
}
2015-02-12 12:13:35 +03:00
2013-07-03 12:10:50 +04:00
/**
* Parse string with or without variable
*
* @ param Tokenizer $tokens
* @ throws UnexpectedTokenException
* @ return string
*/
2013-08-02 20:29:18 +04:00
public function parseQuote ( Tokenizer $tokens )
2013-07-29 14:58:14 +04:00
{
2013-09-02 17:40:58 +04:00
if ( $tokens -> is ( '"' )) {
2013-05-30 19:00:00 +04:00
$stop = $tokens -> current ();
$_str = '"' ;
$tokens -> next ();
2013-07-29 14:58:14 +04:00
while ( $t = $tokens -> key ()) {
if ( $t === T_ENCAPSED_AND_WHITESPACE ) {
2013-05-30 19:00:00 +04:00
$_str .= $tokens -> current ();
$tokens -> next ();
2013-07-29 14:58:14 +04:00
} elseif ( $t === T_VARIABLE ) {
if ( strlen ( $_str ) > 1 ) {
2013-05-30 19:00:00 +04:00
$_str .= '".' ;
} else {
$_str = " " ;
}
2014-01-27 23:58:14 +04:00
$_str .= '$var["' . substr ( $tokens -> current (), 1 ) . '"]' ;
2013-05-30 19:00:00 +04:00
$tokens -> next ();
2013-07-29 14:58:14 +04:00
if ( $tokens -> is ( $stop )) {
2013-05-30 19:00:00 +04:00
$tokens -> skip ();
return $_str ;
} else {
$_str .= '."' ;
}
2013-07-29 14:58:14 +04:00
} elseif ( $t === T_CURLY_OPEN ) {
if ( strlen ( $_str ) > 1 ) {
2013-05-30 19:00:00 +04:00
$_str .= '".' ;
} else {
$_str = " " ;
}
$tokens -> getNext ( T_VARIABLE );
2013-08-11 19:55:30 +04:00
$_str .= '(' . $this -> parseExpr ( $tokens ) . ')' ;
2013-07-29 14:58:14 +04:00
if ( $tokens -> is ( $stop )) {
2013-01-25 18:36:16 +04:00
$tokens -> next ();
2013-05-30 19:00:00 +04:00
return $_str ;
2013-01-25 18:36:16 +04:00
} else {
2013-05-30 19:00:00 +04:00
$_str .= '."' ;
2013-01-25 18:36:16 +04:00
}
2013-07-29 14:58:14 +04:00
} elseif ( $t === " } " ) {
2013-05-30 19:00:00 +04:00
$tokens -> next ();
2013-07-29 14:58:14 +04:00
} elseif ( $t === $stop ) {
2013-05-30 19:00:00 +04:00
$tokens -> next ();
2013-07-29 14:58:14 +04:00
return $_str . '"' ;
2013-05-30 19:00:00 +04:00
} else {
break ;
2013-01-25 18:36:16 +04:00
}
}
2013-05-30 19:00:00 +04:00
throw new UnexpectedTokenException ( $tokens );
2013-07-29 14:58:14 +04:00
} elseif ( $tokens -> is ( T_CONSTANT_ENCAPSED_STRING )) {
2013-05-30 19:00:00 +04:00
return $tokens -> getAndNext ();
2013-07-29 14:58:14 +04:00
} elseif ( $tokens -> is ( T_ENCAPSED_AND_WHITESPACE )) {
2013-05-30 19:00:00 +04:00
throw new UnexpectedTokenException ( $tokens );
} else {
return " " ;
2013-01-25 18:36:16 +04:00
}
}
/**
* Parse modifiers
* | modifier : 1 : 2.3 : 'string' : false : $var : ( 4 + 5 * $var3 ) | modifier2 : " str { $var + 3 } ing " : $arr . item
*
* @ param Tokenizer $tokens
* @ param $value
* @ throws \LogicException
* @ throws \Exception
* @ return string
*/
2013-07-29 14:58:14 +04:00
public function parseModifier ( Tokenizer $tokens , $value )
{
while ( $tokens -> is ( " | " )) {
2014-01-28 21:24:47 +04:00
$modifier = $tokens -> getNext ( Tokenizer :: MACRO_STRING );
2014-02-27 16:30:44 +04:00
if ( $tokens -> isNext ( T_DOUBLE_COLON , T_NS_SEPARATOR )) {
2014-01-28 21:24:47 +04:00
$mods = $this -> parseStatic ( $tokens );
} else {
$mods = $this -> _fenom -> getModifier ( $modifier , $this );
if ( ! $mods ) {
throw new \Exception ( " Modifier " . $tokens -> current () . " not found " );
}
$tokens -> next ();
2013-07-24 19:37:07 +04:00
}
2013-01-25 18:36:16 +04:00
2014-01-28 21:24:47 +04:00
$args = array ();
2013-07-29 14:58:14 +04:00
while ( $tokens -> is ( " : " )) {
2015-02-12 12:13:35 +03:00
if (( $args [] = $this -> parseTerm ( $tokens -> next (), $is_var , 0 )) === false ) {
2013-09-02 17:40:58 +04:00
throw new UnexpectedTokenException ( $tokens );
2013-01-25 18:36:16 +04:00
}
}
2013-07-29 14:58:14 +04:00
if ( ! is_string ( $mods )) { // dynamic modifier
2014-02-19 01:48:56 +04:00
$mods = 'call_user_func($tpl->getStorage()->getModifier("' . $modifier . '"), ' ;
2013-06-20 10:36:35 +04:00
} else {
$mods .= " ( " ;
}
2013-07-29 14:58:14 +04:00
if ( $args ) {
$value = $mods . $value . ', ' . implode ( " , " , $args ) . ')' ;
2013-01-25 18:36:16 +04:00
} else {
2013-07-29 14:58:14 +04:00
$value = $mods . $value . ')' ;
2013-01-25 18:36:16 +04:00
}
}
return $value ;
}
/**
* Parse array
* [ 1 , 2.3 , 5 + 7 / $var , 'string' , " str { $var + 3 } ing " , $var2 , []]
*
* @ param Tokenizer $tokens
2014-06-28 16:38:50 +04:00
* @ param int $count amount of elements
* @ throws Error\UnexpectedTokenException
2013-01-25 18:36:16 +04:00
* @ return string
*/
2014-06-28 16:38:50 +04:00
public function parseArray ( Tokenizer $tokens , & $count = 0 )
2013-07-29 14:58:14 +04:00
{
if ( $tokens -> is ( " [ " )) {
2014-06-28 21:08:20 +04:00
$arr = array ();
2013-01-25 18:36:16 +04:00
$tokens -> next ();
2013-07-29 14:58:14 +04:00
while ( $tokens -> valid ()) {
2014-06-28 21:08:20 +04:00
if ( $tokens -> is ( ']' )) {
2013-01-25 18:36:16 +04:00
$tokens -> next ();
2014-06-28 21:08:20 +04:00
return 'array(' . implode ( ', ' , $arr ) . ')' ;
}
if ( $tokens -> is ( '[' )) {
$arr [] = $this -> parseArray ( $tokens );
$count ++ ;
2013-01-25 18:36:16 +04:00
} else {
2014-06-28 21:08:20 +04:00
$expr = $this -> parseExpr ( $tokens );
2016-08-22 17:08:47 +03:00
if ( $tokens -> is ( T_DOUBLE_ARROW )) {
2014-06-28 21:08:20 +04:00
$tokens -> next ();
2016-08-22 17:08:47 +03:00
$arr [] = $expr . ' => ' . $this -> parseExpr ( $tokens );
2014-06-28 21:08:20 +04:00
} else {
$arr [] = $expr ;
}
$count ++ ;
}
2016-08-22 17:08:47 +03:00
if ( $tokens -> is ( ',' )) {
2014-06-28 21:08:20 +04:00
$tokens -> next ();
2013-01-25 18:36:16 +04:00
}
}
}
2013-03-15 00:12:02 +04:00
throw new UnexpectedTokenException ( $tokens );
2013-01-25 18:36:16 +04:00
}
2013-02-23 02:03:05 +04:00
/**
* @ param Tokenizer $tokens
* @ param $name
* @ return string
2013-07-02 11:07:33 +04:00
* @ throws InvalidUsageException
2013-02-23 02:03:05 +04:00
*/
2013-07-29 14:58:14 +04:00
public function parseMacroCall ( Tokenizer $tokens , $name )
{
2013-07-29 14:53:21 +04:00
$recursive = false ;
2014-05-06 14:22:58 +04:00
$macro = false ;
2015-06-03 00:13:09 +03:00
2013-07-29 14:58:14 +04:00
if ( isset ( $this -> macros [ $name ])) {
2014-05-06 14:22:58 +04:00
$macro = $this -> macros [ $name ];
2014-04-09 20:01:20 +04:00
$recursive = $macro [ 'recursive' ];
2013-07-29 14:53:21 +04:00
} else {
2013-07-29 14:58:14 +04:00
foreach ( $this -> _stack as $scope ) {
if ( $scope -> name == 'macro' && $scope [ 'name' ] == $name ) { // invoke recursive
2013-07-29 14:53:21 +04:00
$recursive = $scope ;
2014-05-06 14:22:58 +04:00
$macro = $scope [ 'macro' ];
2013-07-29 14:53:21 +04:00
break ;
2013-02-23 02:03:05 +04:00
}
}
2013-07-29 14:58:14 +04:00
if ( ! $macro ) {
2013-07-29 14:53:21 +04:00
throw new InvalidUsageException ( " Undefined macro ' $name ' " );
}
}
$tokens -> next ();
2014-05-06 14:22:58 +04:00
$p = $this -> parseParams ( $tokens );
2013-07-29 14:53:21 +04:00
$args = array ();
2013-07-29 14:58:14 +04:00
foreach ( $macro [ 'args' ] as $arg ) {
if ( isset ( $p [ $arg ])) {
$args [ $arg ] = $p [ $arg ];
} elseif ( isset ( $macro [ 'defaults' ][ $arg ])) {
$args [ $arg ] = $macro [ 'defaults' ][ $arg ];
2013-07-29 14:53:21 +04:00
} else {
throw new InvalidUsageException ( " Macro ' $name ' require ' $arg ' argument " );
}
}
2013-07-29 14:58:14 +04:00
if ( $recursive ) {
2014-04-17 23:27:59 +04:00
if ( $recursive instanceof Tag ) {
2014-04-09 20:01:20 +04:00
$recursive [ 'recursive' ] = true ;
}
2014-04-17 23:27:59 +04:00
return '$tpl->getMacro("' . $name . '")->__invoke(' . Compiler :: toArray ( $args ) . ', $tpl);' ;
2013-02-23 02:03:05 +04:00
} else {
2014-01-27 23:58:14 +04:00
$vars = $this -> tmpVar ();
2014-02-27 16:30:44 +04:00
return $vars . ' = $var; $var = ' . Compiler :: toArray ( $args ) . ';' . PHP_EOL . '?>' .
$macro [ " body " ] . '<?php' . PHP_EOL . '$var = ' . $vars . '; unset(' . $vars . ');' ;
2013-01-25 18:36:16 +04:00
}
}
2014-01-28 21:24:47 +04:00
/**
* @ param Tokenizer $tokens
* @ throws \LogicException
* @ throws \RuntimeException
* @ return string
*/
public function parseStatic ( Tokenizer $tokens )
{
2014-02-27 16:30:44 +04:00
if ( $this -> _options & Fenom :: DENY_STATICS ) {
2014-01-28 21:24:47 +04:00
throw new \LogicException ( " Static methods are disabled " );
}
$tokens -> skipIf ( T_NS_SEPARATOR );
$name = " " ;
if ( $tokens -> is ( T_STRING )) {
$name .= $tokens -> getAndNext ();
while ( $tokens -> is ( T_NS_SEPARATOR )) {
$name .= '\\' . $tokens -> next () -> get ( T_STRING );
$tokens -> next ();
}
}
$tokens -> need ( T_DOUBLE_COLON ) -> next () -> need ( T_STRING );
$static = $name . " :: " . $tokens -> getAndNext ();
2014-02-27 16:30:44 +04:00
if ( ! is_callable ( $static )) {
2014-01-28 21:24:47 +04:00
throw new \RuntimeException ( " Method $static doesn't exist " );
}
return $static ;
}
2013-01-25 18:36:16 +04:00
/**
* Parse argument list
* ( 1 + 2.3 , 'string' , $var , [ 2 , 4 ])
*
* @ param Tokenizer $tokens
* @ return string
*/
2013-07-29 14:58:14 +04:00
public function parseArgs ( Tokenizer $tokens )
{
2013-01-28 16:34:34 +04:00
$_args = " ( " ;
$tokens -> next ();
$arg = $colon = false ;
2013-07-29 14:58:14 +04:00
while ( $tokens -> valid ()) {
2014-05-06 14:22:58 +04:00
if ( ! $arg && $tokens -> is (
T_VARIABLE ,
T_STRING ,
2016-04-19 12:06:49 +03:00
" $ " ,
2014-05-06 14:22:58 +04:00
" ( " ,
Tokenizer :: MACRO_SCALAR ,
'"' ,
Tokenizer :: MACRO_UNARY ,
Tokenizer :: MACRO_INCDEC
)
) {
2013-08-11 19:55:30 +04:00
$_args .= $this -> parseExpr ( $tokens );
2014-05-06 14:22:58 +04:00
$arg = true ;
2013-01-28 16:34:34 +04:00
$colon = false ;
2013-07-29 14:58:14 +04:00
} elseif ( ! $arg && $tokens -> is ( '[' )) {
2013-01-28 16:34:34 +04:00
$_args .= $this -> parseArray ( $tokens );
2014-05-06 14:22:58 +04:00
$arg = true ;
2013-01-28 16:34:34 +04:00
$colon = false ;
2013-07-29 14:58:14 +04:00
} elseif ( $arg && $tokens -> is ( ',' )) {
$_args .= $tokens -> getAndNext () . ' ' ;
2014-05-06 14:22:58 +04:00
$arg = false ;
2013-01-28 16:34:34 +04:00
$colon = true ;
2013-07-29 14:58:14 +04:00
} elseif ( ! $colon && $tokens -> is ( ')' )) {
2013-01-28 16:34:34 +04:00
$tokens -> next ();
2013-07-29 14:58:14 +04:00
return $_args . ')' ;
2013-01-28 16:34:34 +04:00
} else {
break ;
}
}
2013-07-29 14:58:14 +04:00
throw new TokenizeException ( " Unexpected token ' " . $tokens -> current () . " ' in argument list " );
2013-01-28 16:34:34 +04:00
}
2013-01-25 18:36:16 +04:00
2013-02-20 18:03:53 +04:00
/**
* Parse first unnamed argument
*
* @ param Tokenizer $tokens
* @ param string $static
* @ return mixed | string
*/
2013-07-29 14:58:14 +04:00
public function parsePlainArg ( Tokenizer $tokens , & $static )
{
if ( $tokens -> is ( T_CONSTANT_ENCAPSED_STRING )) {
if ( $tokens -> isNext ( '|' )) {
2013-08-11 19:55:30 +04:00
return $this -> parseExpr ( $tokens );
2013-02-20 18:03:53 +04:00
} else {
2014-05-06 14:22:58 +04:00
$str = $tokens -> getAndNext ();
2013-02-20 18:03:53 +04:00
$static = stripslashes ( substr ( $str , 1 , - 1 ));
return $str ;
}
2013-02-13 20:51:27 +04:00
} else {
2013-08-11 19:55:30 +04:00
return $this -> parseExpr ( $tokens );
2013-02-13 20:51:27 +04:00
}
}
2013-07-29 14:53:21 +04:00
2013-01-25 18:36:16 +04:00
/**
* Parse parameters as $key = $value
* param1 = $var param2 = 3 ...
*
* @ static
* @ param Tokenizer $tokens
2013-07-29 14:58:14 +04:00
* @ param array $defaults
2013-01-25 18:36:16 +04:00
* @ throws \Exception
* @ return array
*/
2013-07-29 14:58:14 +04:00
public function parseParams ( Tokenizer $tokens , array $defaults = null )
{
2013-01-28 16:34:34 +04:00
$params = array ();
2013-07-29 14:58:14 +04:00
while ( $tokens -> valid ()) {
if ( $tokens -> is ( Tokenizer :: MACRO_STRING )) {
2013-01-25 18:36:16 +04:00
$key = $tokens -> getAndNext ();
2013-07-29 14:58:14 +04:00
if ( $defaults && ! isset ( $defaults [ $key ])) {
2015-01-07 17:00:54 +03:00
throw new InvalidUsageException ( " Unknown parameter ' $key ' " );
2013-01-25 18:36:16 +04:00
}
2013-07-29 14:58:14 +04:00
if ( $tokens -> is ( " = " )) {
2013-01-25 18:36:16 +04:00
$tokens -> next ();
2013-08-11 19:55:30 +04:00
$params [ $key ] = $this -> parseExpr ( $tokens );
2015-01-07 17:00:54 +03:00
} else {
2015-01-07 17:19:17 +03:00
throw new InvalidUsageException ( " Invalid value for parameter ' $key ' " );
2013-01-25 18:36:16 +04:00
}
2013-09-02 17:40:58 +04:00
} elseif ( $tokens -> is ( Tokenizer :: MACRO_SCALAR , '"' , T_VARIABLE , " [ " , '(' )) {
2013-08-11 19:55:30 +04:00
$params [] = $this -> parseExpr ( $tokens );
2013-01-25 18:36:16 +04:00
} else {
break ;
}
}
2013-07-29 14:58:14 +04:00
if ( $defaults ) {
2013-01-25 18:36:16 +04:00
$params += $defaults ;
}
return $params ;
2013-01-28 16:34:34 +04:00
}
2016-04-19 12:06:49 +03:00
}