yf_tpl_driver_yf_compile classFramework template engine compile extension code.
| File | plugins/tpl/classes/tpl/yf_tpl_driver_yf_compile.class.php |
|---|---|
| Use | _class('tpl_driver_yf_compile', 'classes/tpl/') |
A site sets them in its config with $PROJECT_CONF['tpl_driver_yf_compile']['NAME'] = ...;
| Property | Default | Description |
|---|---|---|
$_cond_operators |
null |
|
$_math_operators |
null |
|
$_stpl_compile_dir_check |
null |
None without a leading underscore.
_ hooks and helpers; not routable as page actionspublic function _compile($name, $replace = [], $string = '')
Compile given template into pure PHP code.
public function _compile($name, $replace = [], $string = '')
{
$_time_start = microtime(true);
// For later check for templates changes
if (_class('tpl')->COMPILE_CHECK_STPL_CHANGED) {
$_md5_string = md5($string);
}
$compiled_dir = STORAGE_PATH . _class('tpl')->COMPILED_DIR;
// Do not check dir existence twice
if ( ! isset($this->_stpl_compile_dir_check)) {
_mkdir_m($compiled_dir);
$this->_stpl_compile_dir_check = true;
}
$file_name = $compiled_dir . 'c_' . MAIN_TYPE . '_' . urlencode($name) . tpl()->_compiled_suffix() . '.php';
$start = '<' . '?p' . 'hp ';
$end = ' ?' . '>';
$string = $this->_process_patterns($name, $replace, $string);
// Images and uploads paths compile
$web_path = MAIN_TYPE_USER ? 'MEDIA_PATH' : 'ADMIN_WEB_PATH';
$images_path = $web_path . '._class(\'tpl\')->TPL_PATH. _class(\'tpl\')->_IMAGES_PATH';
$to_replace = [
'"images/' => '"' . $start . 'echo ' . $images_path . ';' . $end,
'\'images/' => '\'' . $start . 'echo ' . $images_path . ';' . $end,
'"uploads/' => '"' . $start . 'echo MEDIA_PATH. _class(\'tpl\')->_UPLOADS_PATH;' . $end,
'\'uploads/' => '\'' . $start . 'echo MEDIA_PATH. _class(\'tpl\')->_UPLOADS_PATH;' . $end,
'src="uploads/' => 'src="' . $start . 'echo ' . $web_path . '._class(\'tpl\')->_UPLOADS_PATH;' . $end,
];
$string = str_replace(array_keys($to_replace), $to_replace, $string);
$string = '<' . '?p' . 'hp if(!defined(\'YF_PATH\')) exit(); /* ' .
'date: ' . gmdate('Y-m-d H:i:s') . ' GMT; ' .
'compile_time: ' . common()->_format_time_value(microtime(true) - $_time_start) . '; ' .
'name: ' . $name . '; ' .
' */ ' .
'?' . '>' . PHP_EOL . $string;
file_put_contents($file_name, $string);
return $string;
}
public function _compile_foreach($m)
public function _compile_foreach($m)
{
$start = '<' . '?p' . 'hp ';
$end = ' ?' . '>';
$func = trim($m['func']);
$orig_arr_name = trim($m['key']);
$foreach_arr_name = $orig_arr_name;
$orig_arr_name = str_replace([',', ';', ' ', '=', '\'', '"'], '__', $orig_arr_name);
$foreach_body = $m['body'];
// Example of elseforeach: {foreach(items)} {_key} = {_val} {elseforeach} No records {/foreach}
$no_rows_text = '';
$else_tag = '{elseforeach}';
if (false !== strpos($foreach_body, $else_tag)) {
list($else_before, $no_rows_text) = explode($else_tag, $foreach_body);
$foreach_body = str_replace($else_tag . $no_rows_text, '', $foreach_body);
}
// vars inside foreach
$esc = tpl()->AUTO_ESCAPE;
$foreach_body = preg_replace_callback('/\{\#\.([a-z0-9_-]+)\}/i', function ($m) use ($start, $end, $esc) {
return $start . 'echo ' . ($esc ? '_class(\'tpl\')->_out($_v[\'' . $m[1] . '\'], \'' . $m[1] . '\')' : '$_v[\'' . $m[1] . '\']') . ';' . $end;
}, $foreach_body);
$special_vars = [
'{_key}' => $start . 'echo ' . ($esc ? '_class(\'tpl\')->_out($_k)' : '$_k') . ';' . $end,
'{_val}' => $start . 'echo ' . ($esc ? '_class(\'tpl\')->_out(is_array($_v) ? implode(",", $_v) : $_v)' : '(is_array($_v) ? implode(",", $_v) : $_v)') . ';' . $end,
'{_num}' => $start . 'echo $__f_counter;' . $end,
'{_total}' => $start . 'echo $__f_total;' . $end,
];
$foreach_body = str_replace(array_keys($special_vars), $special_vars, $foreach_body);
$foreach_arr_tag = '';
$foreach_data_tag = '';
// Ability to directly execute some class method and do foreach over it like {foreach_exec(test,give_me_array)} {_key}={_val} {/foreach}
if ($func === 'foreach_exec') {
$foreach_data_tag = 'array()';
if (preg_match('/(?P<object>[\w@\-]+)\s*[,;]\s*(?P<action>[\w@\-]+)\s*[,;]{0,1}\s*(?P<args>.*?)$/ims', $foreach_arr_name, $m_exec)) {
$foreach_data_tag = 'main()->_execute(\'' . $m_exec['object'] . '\', \'' . $m_exec['action'] . '\', \'' . $m_exec['args'] . '\', $name. $this->_STPL_EXT, 0, $use_cache = false)';
}
// Support for deep arrays as main array
} elseif (false !== strpos($foreach_arr_name, '.')) {
list($v1, $v2) = explode('.', $foreach_arr_name);
$global_arrays = tpl()->EXPOSE_SUPERGLOBALS ? tpl()->_avail_arrays : [];
$is_global = is_array($global_arrays) && array_key_exists($v1, $global_arrays);
if ($is_global) {
$foreach_arr_tag = '$this->_escape_tpl_braces_keys($' . $global_arrays[$v1] . '[\'' . $v2 . '\'] ?? null)';
} else {
$foreach_arr_name = trim(str_replace('.', '\'][\'', $foreach_arr_name));
$foreach_arr_tag = '($replace[\'' . $foreach_arr_name . '\'] ?? null)';
}
} else {
$foreach_arr_tag = '($replace[\'' . $foreach_arr_name . '\'] ?? null)';
}
$foreach_arr_tag = '(' . $foreach_arr_tag . ' ?? null)';
if ( ! $foreach_data_tag) {
$foreach_data_tag = 'is_array(' . $foreach_arr_tag . ') ? ' . $foreach_arr_tag . ' : $this->_range_foreach(' . (is_numeric($foreach_arr_name) ? (int) $foreach_arr_name : $foreach_arr_tag) . ')';
}
return '$__foreach_data = ' . $foreach_data_tag . '; ' . PHP_EOL
. '$__f_total = count((array)$__foreach_data); $__f_counter = 0;' . PHP_EOL
. 'if ($__foreach_data) {' . PHP_EOL . 'foreach ($__foreach_data as $_k => $_v) { $__f_counter++; ' . PHP_EOL
. $end . $foreach_body . $start . PHP_EOL
. '}' . PHP_EOL . '} else {' . PHP_EOL . $end . $no_rows_text . $start . '}';
}
public function _compile_if_funcs(array $m)
$m array public function _compile_if_funcs(array $m)
{
$cond = trim($m['cond']);
$multiple_cond = 'AND';
if (in_array($cond, ['if_or', 'elseif_or'])) {
$multiple_cond = 'OR';
}
if (in_array($cond, ['if', 'if_or', 'if_and'])) {
$cond = 'if';
} elseif (in_array($cond, ['elseif', 'elseif_or', 'elseif_and'])) {
$cond = 'elseif';
}
$is_multiple = (strpos($m['left'], ',') !== false);
if ($is_multiple) {
$part_left = [];
foreach (explode(',', trim($m['left'])) as $v) {
$part_left[] = $this->_compile_prepare_cond($v);
}
} else {
$part_left = $this->_compile_prepare_cond($m['left']);
}
$func = trim($m['func']);
// We need these wrappers to make code compatible with PHP 5.3, As this direct code fails: php -r 'var_dump(empty(""));', php -r 'var_dump(isset(""));',
$funcs_map = [
'empty' => '_empty',
'not_ok' => '_empty',
'false' => '_empty',
'not_true' => '_empty',
'isset' => '_isset',
'not_isset' => 'not__isset',
'not_empty' => 'not__empty',
'ok' => 'not__empty',
'true' => 'not__empty',
'not_false' => 'not__empty',
];
if (isset($funcs_map[$func])) {
$func = $funcs_map[$func];
}
$negate = false;
if (substr($func, 0, 4) === 'not_') {
$func = substr($func, 4);
$negate = true;
}
// Example of supported class: {if_validate:is_natural_no_zero(data)} good {/if}
if (false !== strpos($func, ':')) {
list($class_name, $_func) = explode(':', $func);
if (in_array($class_name, ['validate'])) {
$func = '_class_safe("' . $class_name . '")->' . $_func;
} else {
return '';
}
// Example of supported functions: {if_empty(data)} good {/if} {if_not_isset(data.sub1)} good {/if}
} elseif ( ! function_exists($func) && ! in_array($func, ['empty', 'isset'])) {
return '';
}
if ($is_multiple) {
$center_tmp = [];
foreach ($part_left as $v) {
$v = trim($v);
if (strlen($v)) {
$center_tmp[] = ($negate ? '!' : '') . $func . '(' . $v . ')';
}
}
if ( ! count((array) $center_tmp)) {
$center_cond = ($negate ? '!' : '') . $func . '($replace["___not_existing_key__"] ?? null)';
} else {
$center_cond = '(' . implode(') ' . $multiple_cond . ' (', $center_tmp) . ')';
}
} else {
$center_cond = ($negate ? '!' : '') . $func . '(' . (strlen($part_left) ? $part_left : '($replace["___not_existing_key__"] ?? null)') . ')';
}
return ($cond === 'elseif' ? '} ' . $cond : $cond) . '(' . $center_cond . ') {';
}
public function _compile_prepare_cond($cond = '', $for_right = false)
Prepare left part of the condition.
public function _compile_prepare_cond($cond = '', $for_right = false)
{
$cond = trim($cond);
$_array_magick = [
'_key' => '$_k',
'_val' => '$_v',
'_num' => '$__f_counter',
'_total' => '$__f_total',
'_first' => '($__f_counter == 1)',
'_last' => '($__f_counter == $__f_total)',
'_even' => '($__f_counter % 2)',
'_odd' => '(!($__f_counter % 2))',
];
$tmp_len = strlen($cond);
$tmp_first = substr($cond, 0, 1);
// Variable hint, starting from # or @
if (($tmp_first === '@' || $tmp_first == '#') && substr($cond, 0, 2) !== '#.' && $tmp_len > 1) {
$cond = substr($cond, 1);
$tmp_len--;
}
// Number, also support for decimals and floats
if (is_numeric($cond)) {
if ( ! ctype_digit($cond)) {
$cond = '\'' . $cond . '\'';
}
// Simple number or string, started with '%'
} elseif ($tmp_first === '%' && $tmp_len > 1) {
$cond = '\'' . str_replace("'", "\\'", substr($cond, 1)) . '\'';
} elseif (substr($cond, 0, 2) === '#.') {
$cond = '($_v[\'' . substr($cond, 2) . '\'] ?? null)';
// Arithmetic operators (currently we allow only '+' and '-')
} elseif (isset($this->_math_operators[$cond])) {
$cond = $this->_math_operators[$cond];
// Array special magic keyword
} elseif (isset($_array_magick[$cond])) {
$cond = $_array_magick[$cond];
// Module config item
} elseif (strpos($cond, 'module_conf.') === 0) {
list($mod_name, $mod_conf) = explode('.', substr($cond, strlen('module_conf.')));
$cond = 'module_conf(\'' . $mod_name . '\',\'' . $mod_conf . '\')';
// Configuration item
} elseif (strpos($cond, 'conf.') === 0) {
$cond = 'conf(\'' . substr($cond, strlen('conf.')) . '\')';
// Constant
} elseif (strpos($cond, 'const.') === 0) {
$cond = str_replace("'", "\\'", substr($cond, strlen('const.')));
$cond = '(defined(\'' . $cond . '\') ? constant(\'' . $cond . '\') : null)';
// Global array element or sub array
} elseif ( ! $for_right && false !== strpos($cond, '.')) {
$cond = $this->_cond_sub_array($cond);
} elseif ($tmp_len) {
$cond = str_replace("'", "\\'", $cond);
if ($for_right) {
$cond = '(isset($replace[\'' . $cond . '\']) ? $replace[\'' . $cond . '\'] : \'' . $cond . '\')';
} else {
$cond = '($replace[\'' . $cond . '\'] ?? null)';
}
}
return strlen($cond) ? $cond : 'null';
}
public function _compile_prepare_ifs(array $m)
Prepare condition for the compilation.
$m array public function _compile_prepare_ifs(array $m)
{
$cond = $m['cond'] === 'elseif' ? '} ' . $m['cond'] : $m['cond'];
$part_left = $this->_compile_prepare_cond($m['left']);
$part_right = $this->_compile_prepare_cond($m['right'], $for_right = true);
$op = $this->_cond_operators[strtolower(trim($m['op']))];
// Special case for "mod". Examples: {if("id" mod 4)} content {/if}
if ($op === '%') {
$part_left = '!(' . $part_left;
$part_right = $part_right . ')';
}
$add_cond = trim($m['multi_conds']);
if ($add_cond) {
$_this = $this;
$pattern = '/[\s\t]*(?P<cond>and|xor|or)[\s\t]+["\']{0,1}(?P<left>[\w\s\.\-\+\%]+?)["\']{0,1}[\s\t]+(?P<op>eq|ne|gt|lt|ge|le|mod)[\s\t]+["\']{0,1}(?P<right>[\w\s\.\-\#]*)["\']{0,1}/ims';
$add_cond = preg_replace_callback($pattern, function ($m) use ($_this) {
$a_cond = trim($m['cond']);
$a_left = $_this->_compile_prepare_cond($m['left']);
$a_op = $_this->_cond_operators[strtolower(trim($m['op']))];
$a_right = $_this->_compile_prepare_cond($m['right'], $for_right = true);
// Special case for "mod". Examples: {if("id" mod 4)} content {/if}
if ($a_op === '%') {
$a_left = '!(' . $a_left;
$a_right = $a_right . ')';
}
return $a_cond . ' (' . $a_left . ' ' . $a_op . ' ' . $a_right . ') ';
}, $add_cond);
}
return $cond . '(' . trim($part_left . ' ' . $op . ' ' . $part_right . ' ' . $add_cond) . ') {';
}
public function _cond_sub_array($cond)
public function _cond_sub_array($cond)
{
$pos = strpos($cond, '.');
if ($pos === false) {
return '($replace[\'' . str_replace("'", "\\'", $cond) . '\'] ?? null)';
}
$try_elm = substr($cond, 0, $pos);
$try_elm2 = '[\'' . str_replace('.', '\'][\'', substr($cond, $pos + 1)) . '\']';
// Global array
$avail_arrays = _class('tpl')->EXPOSE_SUPERGLOBALS ? (array) _class('tpl')->_avail_arrays : [];
if (isset($avail_arrays[$try_elm])) {
$cond = '($' . $avail_arrays[$try_elm] . $try_elm2 . ' ?? null)';
// Sub array
} else {
$cond = '($replace["' . $try_elm . '"]' . $try_elm2 . ' ?? null)';
}
// ?? keeps missing keys from raising warnings in the generated code
return '(' . $cond . ' ?? null)';
}
public function _init()
public function _init()
{
$tpl_driver_yf = _class('tpl_driver_yf', 'classes/tpl/');
$this->_cond_operators = $tpl_driver_yf->_cond_operators;
$this->_math_operators = $tpl_driver_yf->_math_operators;
}
public function _process_patterns($name, array $replace, $string)
$replace array public function _process_patterns($name, array $replace, $string)
{
$_this = $this;
$start = '<' . '?p' . 'hp ';
$end = ' ?' . '>';
$allow_eval = tpl()->ALLOW_EVAL_PHP_CODE;
// With AUTO_ESCAPE on, printed values go through tpl()->_out(); off (the default) the compiled code is unchanged
$out = tpl()->AUTO_ESCAPE ? function ($expr, $name = '') {
return '_class(\'tpl\')->_out(' . $expr . ($name !== '' ? ', \'' . $name . '\'' : '') . ')';
} : function ($expr, $name = '') {
return $expr;
};
$expose_globals = tpl()->EXPOSE_SUPERGLOBALS;
$patterns = [
// YF tpl comments
'/(\{\{--.*?--\}\})/ims' => function ($m) {
return '';
},
// !! Keep this pattern on top
'/\{(else)\}/i' => function ($m) use ($start, $end) {
return $start . '} else {' . $end;
},
'/\{catch\([\s\t]*["\']{0,1}([\w_-]+?)["\']{0,1}[\s\t]*\)\}(.*?)\{\/catch\}/ims' => function ($m) use ($start, $end) {
return $start . 'ob_start();' . $end . $m[2] . $start . '$replace[\'' . $m[1] . '\'] = trim(ob_get_clean());' . (tpl()->AUTO_ESCAPE ? ' _class(\'tpl\')->_raw_names[\'' . $m[1] . '\'] = true;' : '') . $end;
},
'/\{(t|translate|i18n)\(\s*["\']{0,1}(.*?)["\']{0,1}\s*\)\}/ims' => function ($m) use ($start, $end, $out) {
$str = preg_replace('/\{([a-z0-9_-]+)\}/i', '\'.' . $out('($replace[\'\1\'] ?? null)', '\1') . '.\'', addslashes($m[2]));
return $start . 'echo _class(\'tpl\')->_i18n_wrapper(stripslashes(\'' . $str . '\'), $replace);' . $end;
},
'/\{const\(\s*["\']{0,1}([a-z_][a-z0-9_]+?)["\']{0,1}\s*\)\}/i' => function ($m) use ($start, $end) {
$c = trim($m[1]);
return $start . 'echo (defined(\'' . $c . '\') ? constant(\'' . $c . '\') : null);' . $end;
},
'/\{conf\(\s*["\']{0,1}([a-z_][a-z0-9_:]+?)["\']{0,1}\s*\)\}/i' => function ($m) use ($start, $end) {
return $start . 'echo conf(\'' . $m[1] . '\');' . $end;
},
'/\{module_conf\(\s*["\']{0,1}([a-z_][a-z0-9_:]+?)["\']{0,1}\s*,\s*["\']{0,1}([a-z_][a-z0-9_:]+?)["\']{0,1}\s*\)\}/i' => function ($m) use ($start, $end) {
return $start . 'echo module_conf(\'' . $m[1] . '\',\'' . $m[2] . '\');' . $end;
},
// ifs compiling. NOTE: pattern differs from original adding \#\. symbols, etc
'/\{(?P<cond>if|elseif)\(\s*["\']{0,1}(?P<left>[\w\s\.+%#-]+?)["\']{0,1}[\s\t]+(?P<op>eq|ne|gt|lt|ge|le|mod)[\s\t]+["\']{0,1}(?P<right>[\w\.\-\#]*)["\']{0,1}(?P<multi_conds>[^\(\)\{\}\n]*)\s*\)\}/ims' => function ($m) use ($start, $end, $_this) {
return $start . $_this->_compile_prepare_ifs($m) . $end;
},
// if_funcs compiling
'/\{(?P<cond>if_or|if_and|elseif_or|elseif_and|if|elseif)_(?P<func>[a-z0-9_:]+)\(\s*["\']{0,1}(?P<left>[\w\s\.,+%-]+?)["\']{0,1}[\s\t]*\)\}/ims' => function ($m) use ($start, $end, $_this) {
return $start . $_this->_compile_if_funcs($m) . $end;
},
// foreach pattern compilation
'/\{(?P<func>foreach|foreach_exec)\(\s*["\']{0,1}(?P<key>[a-z0-9_\s\.,;=@-]+)["\']{0,1}\s*\)\}(?P<body>(?![^\{]*?\{\1\(\s*["\']{0,1}?).*?)\{\/\1\}/ims' => function ($m) use ($start, $end, $_this) {
return $start . $_this->_compile_foreach($m) . $end;
},
// if ending tag
'/\{\/if\}/i' => function ($m) use ($start, $end) {
return $start . '}' . $end;
},
// Common replace vars
'/\{([a-z0-9_-]+)\}/i' => function ($m) use ($start, $end, $out) {
return $start . 'echo ' . $out('($replace[\'' . $m[1] . '\'] ?? null)', $m[1]) . ';' . $end;
},
// Second level vars
'/\{([a-z0-9_-]+)\.([a-z0-9_-]+)\}/i' => function ($m) use ($start, $end, $out, $expose_globals) {
$global_arrays = tpl()->_avail_arrays;
$is_global = is_array($global_arrays) && array_key_exists($m[1], $global_arrays);
if ($is_global && ! $expose_globals) {
return '';
}
$expr = $out('(' . ($is_global ? '$' . $global_arrays[$m[1]] . '[\'' . $m[2] . '\']' : '$replace[\'' . $m[1] . '\'][\'' . $m[2] . '\']') . ' ?? null)', $m[1]);
// same output as the yf driver, which escapes the braces because it parses its output again
return $start . 'echo ' . ($is_global ? '_escape_tpl_braces(' . $expr . ')' : $expr) . ';' . $end;
},
// Variable filtering like in Twig. Examples: {var1|trim} {var1|urlencode|trim} {var1|_prepare_html} {var1|my_func} {sub1.var1|trim}
'/\{([a-z0-9_-]+)\|([a-z0-9_\|-]+)\}/i' => function ($m) use ($start, $end) {
return $start . 'echo _class(\'tpl\')->_process_var_filters($replace[\'' . $m[1] . '\'] ?? null,\'' . $m[2] . '\',\'' . $m[1] . '\');' . $end;
},
// Second level variables with filters
'/\{([a-z0-9_-]+)\.([a-z0-9_-]+)\|([a-z0-9_\|-]+)\}/i' => function ($m) use ($start, $end) {
return $start . 'echo _class(\'tpl\')->_process_var_filters($replace[\'' . $m[1] . '\'][\'' . $m[2] . '\'] ?? null, \'' . $m[3] . '\',\'' . $m[1] . '\');' . $end;
},
// Vars inside foreach with filters
'/\{\#\.([a-z0-9_-]+)\|([a-z0-9_\|-]+)\}/i' => function ($m) use ($start, $end) {
return $start . 'echo _class(\'tpl\')->_process_var_filters($_v[\'' . $m[1] . '\'],\'' . $m[2] . '\',\'' . $m[1] . '\');' . $end;
},
'/\{(execute|exec_cached)\(\s*["\']{0,1}\s*([\w@\-]+)\s*[,;]\s*([\w@\-]+)\s*[,;]{0,1}\s*([^"\'\)\}]*)["\']{0,1}\s*\)\}/i' => function ($m) use ($start, $end, $name) {
$is_cached = (false !== strpos($m[1], '_cached'));
return $start . 'echo main()->_execute(\'' . $m[2] . '\',\'' . $m[3] . '\',\'' . $m[4] . '\',\'' . $name . '\',0,' . ($is_cached ? 'true' : 'false') . ');' . $end;
},
'/\{(exec_last|execute_shutdown)\(\s*["\']{0,1}\s*([\w@\-]+)\s*[,;]\s*([\w@\-]+)\s*[,;]{0,1}\s*([^"\'\)\}]*)["\']{0,1}\s*\)\}/i' => function ($m) use ($start, $end) {
return $start . '/*exec_last_start*/echo main()->_execute(\'' . $m[2] . '\',\'' . $m[3] . '\',\'' . $m[4] . '\',\'\',0,false);/*exec_last_end*/' . $end;
},
'/\{block\(\s*([\w\-]+)\s*[,;]{0,1}\s*([^"\'\)\}]*)["\']{0,1}\s*\)\}/i' => function ($m) use ($start, $end, $name) {
return $start . 'echo main()->_execute(\'graphics\',\'_show_block\',\'name=' . $m[1] . ';' . $m[2] . '\',\'' . $name . '\',0,false);' . $end;
},
// Display help tooltip. Examples: {tip('register.login')} or {tip('Some inline help text')} or {tip('Some inline help text';'fa-eye')}
'/\{(tip|itip)\(\s*["\']{0,1}(?P<raw>[^"\'\)\}]*)["\']{0,1}\s*\)\}/ims' => function ($m) use ($start, $end) {
return $start . 'echo _class_safe("graphics")->tip(array("raw"=>\'' . addslashes($m['raw']) . '\', "replace" => $replace, "tpl_name" => $name));' . $end;
},
'/\{(e|user_error)\(\s*["\']{0,1}([\w\.-]+)["\']{0,1}\s*\)\}/ims' => function ($m) use ($start, $end) {
return $start . 'echo common()->_show_error_inline(\'' . $m[2] . '\');' . $end;
},
'/(\{(include|include_if_exists)\(\s*["\']{0,1})\s*([@:\w\\/\.]+)\s*["\']{0,1}?\s*[,;]{0,1}\s*([^"\'\)\}]*)\s*(["\']{0,1}\s*\)\})/i' => function ($m) use ($start, $end) {
$if_exists = (false !== strpos($m[1], '_if_exists'));
return $start . 'echo $this->_include_stpl(\'' . $m[3] . '\',\'' . $m[4] . '\',$replace,' . ($if_exists ? 'true' : 'false') . ');' . $end;
},
'/(\{eval_code\()([^\}]+?)(\)\})/i' => function ($m) use ($start, $end, $allow_eval) {
if ( ! $allow_eval) {
// Base64 keeps the literal tag away from the generic patterns below
return $start . 'echo base64_decode(\'' . base64_encode($m[0]) . '\');' . $end;
}
return $start . 'echo ' . $m[2] . ';' . $end;
},
'/\{cleanup\(\s*\)\}(.*?)\{\/cleanup\}/ims' => function ($m) use ($start, $end) {
return $start . 'ob_start();' . $end . $m[1] . $start . '$__content_to_clean = ob_get_clean(); echo trim(str_replace(array("\r","\n","\t"),"",stripslashes($__content_to_clean))); ' . $end;
},
'/\{ad\(\s*["\']{0,1}([^"\'\)\}]*)["\']{0,1}\s*\)\}/ims' => function ($m) use ($start, $end) {
return $start . 'echo module_safe("advertising")->_show(array("ad"=>\'' . $m[1] . '\'));' . $end;
},
'/\{url\(\s*["\']{0,1}([^"\'\)\}]*)["\']{0,1}\s*\)\}/ims' => function ($m) use ($start, $end) {
return $start . 'echo _class(\'tpl\')->_url_wrapper(\'' . $m[1] . '\');' . $end;
},
'/\{form_row\(\s*["\']{0,1}[\s\t]*([a-z0-9_-]+)[\s\t]*["\']{0,1}([\s\t]*,[\s\t]*["\']{1}([^"\']*)["\']{1})?([\s\t]*,[\s\t]*["\']{1}([^"\']*)["\']{1})?([\s\t]*,[\s\t]*["\']{1}([^"\']*)["\']{1})?\s*\)\}/ims' => function ($m) use ($start, $end) {
return $start . 'echo _class("form2")->tpl_row(\'' . $m[1] . '\',$replace,\'' . $m[3] . '\',\'' . $m[5] . '\',\'' . $m[7] . '\');' . $end;
},
'/\{(?P<func>css|require_css|js|require_js|asset|jquery|angularjs|reactjs|sass|less|jade|coffee)\(\s*["\']{0,1}(?P<args>[^"\'\)\}]*?)["\']{0,1}\s*\)\}\s*(?P<content>.+?)\s*{\/(\1)\}/ims' => function ($m) use ($start, $end) {
if (in_array($m['func'], ['sass', 'less', 'coffee'])) {
return '';
}
return $start . 'echo ' . $m['func'] . '(\'' . str_replace("'", "\\'", $m['content']) . '\', _attrs_string2array(\'' . str_replace("'", "\\'", $m['args']) . '\'));' . $end;
},
// Custom function (compatible with non-compile for extending tpl engine)
'/\{(?P<name>[a-z0-9_:-]+)\(\s*["\']{0,1}(?P<args>[a-z0-9_:\.]+?)["\']{0,1}\s*\)\}/ims' => function ($m) use ($start, $end) {
return $start . 'echo $this->call_custom_pattern(\'' . $m['name'] . '\', \'' . str_replace("'", "\\'", $m['args']) . '\', null, $replace, $name);' . $end;
},
// Custom section (compatible with non-compile for extending tpl engine)
'/\{(?P<name>[a-z0-9_:-]+)\(\s*["\']{0,1}(?P<args>[^"\'\)\}]*?)["\']{0,1}\s*\)\}\s*(?P<body>.+?)\s*{\/(\1)\}/ims' => function ($m) use ($start, $end) {
return $start . 'echo $this->call_custom_pattern(\'' . $m['name'] . '\', \'' . str_replace("'", "\\'", $m['args']) . '\', \'' . str_replace("'", "\\'", $m['body']) . '\', $replace, $name);' . $end;
},
// DEBUG_MODE patterns
'/(\{_debug_get_replace\(\)\})/i' => function ($m) use ($start, $end) {
return $start . 'echo (DEBUG_MODE && is_array($replace) ? "<pre>".print_r(array_keys($replace),1)."</pre>" : "");' . $end;
},
'/(\{_debug_get_vars\(\)\})/i' => function ($m) use ($start, $end) {
return $start . 'echo $this->_debug_get_vars($string);' . $end;
},
];
foreach ((array) $patterns as $pattern => $callback) {
$string = preg_replace_callback($pattern, $callback, $string);
}
// Run exec_last after everything else, but keep its output where the tag stands (same as the non-compiled driver)
if (false !== strpos($string, '/*exec_last_start*/')) {
$exec_lasts = [];
$string = preg_replace_callback('~/\*exec_last_start\*/(.+?)/\*exec_last_end\*/~', function ($m) use (&$exec_lasts) {
$marker = '<!--yf_exec_last_' . count($exec_lasts) . '-->';
$exec_lasts[$marker] = $m[1];
return 'echo \'' . $marker . '\';';
}, $string);
if ($exec_lasts) {
$code = '$__yf_exec_last = array();';
foreach ($exec_lasts as $marker => $call) {
$code .= ' ob_start(); ' . $call . ' $__yf_exec_last[\'' . $marker . '\'] = ob_get_clean();';
}
$string = $start . 'ob_start();' . $end . $string . $start . $code . ' echo strtr(ob_get_clean(), $__yf_exec_last);' . $end;
}
}
return $string;
}