yf_tpl classStadard Framework template engine.
| File | plugins/tpl/classes/yf_tpl.class.php |
|---|---|
| Use | _class('tpl') |
A site sets them in its config with $PROJECT_CONF['tpl']['NAME'] = ...;
| Property | Default | Description |
|---|---|---|
$ADD_QUICK_PAGE_INFObool |
false |
Will add cur date, generation time, memory and db queries into any common page before body |
$ALLOWED_EXTSstring |
['tpl', 'stpl', 'html'] |
@conf_skip Ability to use files with these extensions as templates |
$ALLOW_CUSTOM_FILTERbool |
false |
Allow custom filter for all parsed stpls |
$ALLOW_EVAL_PHP_CODEbool |
true |
SECURITY: allow the {eval_code(...)} template tag in both template drivers. While on, templates are code: whoever can edit a template (files, DB templates, inline editor) can run PHP. Set false once no site template uses the tag (the tag is then left as literal text); 4.0 flips the default. |
$ALLOW_LANG_BASED_STPLSbool |
false |
Allow language-based special stpls |
$ALLOW_PHP_TEMPLATESbool |
false |
Allow pure php templates |
$ALLOW_SKIN_INHERITANCEbool |
true |
Allow skin inheritance (only one level used) |
$AUTO_ESCAPEbool |
output printed as is;
* 4.0 flips the default.
*/
Property [ public $AUTO_ESCAPE = false |
SECURITY: escape {var}, {arr.key}, {#.key} and filtered vars on output (htmlspecialchars, no double encoding, like Twig; braces as { / }, which the yf driver would parse again). Opt out per tag with {var|raw}, per value with tpl()->raw($html), per name with AUTO_ESCAPE_RAW. Default false = output printed as is; 4.0 flips the default. |
$AUTO_ESCAPE_RAWarray |
|
Replace var names that carry HTML by design and are never auto-escaped (layout slots filled by the framework) |
$AUTO_LOAD_PACKED_STPLSbool |
false |
If available - use packed STPLs without checking if some exists in project |
$COMPILED_DIRbool |
'stpls_compiled/' |
Compile templates folder |
$COMPILE_CHECK_STPL_CHANGEDbool |
false |
TTL for compiled stpls |
$COMPILE_TEMPLATESbool |
false |
Allow to compile templates |
$COMPILE_TTLbool |
3600 |
TTL for compiled stpls |
$COMPRESS_OUTPUTbool |
false |
Compressing output by cutting '\t','\r','\n',' ',' ' |
$CSS_FRAMEWORK_VARIANTSbool |
bs5 a template `<name>.bs5.stpl` replaces `<name>.stpl` when it comes from the same or a more
* specific location (site, app, project, framework); off: always `<name>.stpl` */
Property [ publi... |
under conf('css_framework') = bs5 a template `<name>.bs5.stpl` replaces `<name>.stpl` when it comes from the same or a more specific location (site, app, project, framework); off: always `<name>.stpl` |
$CUSTOM_META_INFObool |
false |
Custom meta information (customizable for every page) : page titles, meta keywords, description |
$DEBUG_STPL_VARSbool |
false |
|
$DRIVER_NAMEstring |
'yf' |
Current tempalte engine dirver to use |
$EXIT_AFTER_ECHObool |
false |
Exit after sending main content |
$EXPOSE_SUPERGLOBALSbool |
true |
SECURITY: expose the GET, POST, SERVER and ENV superglobals to templates as {get.key} etc (_avail_arrays); false removes the tags |
$FROM_DB_GET_ALLbool |
false |
Get all templates from db or not (1 query or multiple) (NOTE: If true - Slow PHP processing but just 1 db query) |
$GET_STPLS_FROM_DBbool |
false |
Use database to store templates |
$INHERIT_SKIN |
null |
|
$INHERIT_SKIN2 |
null |
|
$IS_FRONT |
false |
|
$LOG_EXEC_INFO |
[] |
|
$MEDIA_PATH |
'' |
|
$NO_TAGS_IN_INSERTEDbool |
false |
SECURITY: the yf driver runs the later passes (tags, {execute()}, {include()}, {exec_last()}) over the whole string again, text it inserted itself included: output of {execute()} / {block()} (the centre area), {include()}, {var} / {#.key} values and value tags like {t()} or {var|filter}. A module or a var that prints request or DB data holding {eval_code(...)} or {const(DB_PSWD)} ran that tag. true keeps inserted text out of those passes (held as a placeholder until the parse ends); tags written in the template itself work as before, and a {var} inside a tag argument ({t({name})}, {url(id={id})}) still reaches the tag as its value. Content that holds tags on purpose parses them itself: static pages always did, news does while this is on. The compiled driver never re-parsed. Default false keeps the old second pass; 4.0 flips. |
$OB_CATCH_CONTENTbool |
true |
Catch any output before gzipped content (works only with GZIP) |
$REMOVED_DRIVERSarray |
|
Deleted template drivers: name => reason; a requested one warns and the yf driver renders instead |
$REWRITE_MODEbool |
false |
Using SEO - friendly URLs (All links need to be absolute) |
$TIDY_OUTPUTbool |
false |
Use or not Tidy to cleanup output |
$TPL_NAMEstring |
'main' |
default template name |
$TPL_PATHstring |
'' |
@conf_skip Path to the templates (including current theme path) |
$USE_SOURCE_BACKTRACEbool |
true |
Use backtrace to get STPLs source (where called from) FOR DEBUG_MODE ONLY ! |
$_CENTER_RESULT |
null |
|
$_IMAGES_PATHstring |
'images/' |
@conf_skip |
$_INHERITED_SKIN |
null |
|
$_INHERITED_SKIN2 |
null |
|
$_OUTPUT_FILTERS |
[] |
|
$_STPL_EXTstring |
'.stpl' |
@conf_skip |
$_THEMES_PATHstring |
'templates/' |
@conf_skip |
$_TIDY_CONFIG |
'' |
|
$_TMP_FROM_DB |
null |
|
$_UPLOADS_PATHstring |
'uploads/' |
@conf_skip |
$_avail_arraysarray |
['get' => '_GET', 'post' => '_POST', 'server' => '_SERVER', 'env' => '_ENV'] |
@conf_skip For '_process_conditions', Will be availiable in conditions with such form: {if('get.object' eq 'login_form')} Hello from login form {/if} |
$_custom_patterns_funcs |
[] |
|
$_custom_patterns_index |
[] |
|
$_def_user_theme |
null |
|
$_get_cached_paths_memo |
[] |
Memo of _get_cached_paths(), one entry per MAIN_TYPE. |
$_global_tagsarray |
[] |
Global scope tags (included in any parsed template) |
$_in_css_variantbool |
false |
@conf_skip set while a .bs5 variant is being resolved |
$_lang_theme_path |
null |
|
$_output_body_length |
null |
|
$_raw_namesarray |
[] |
@conf_skip Replace names filled with rendered HTML during this request ({catch} results), never auto-escaped |
$_user_error_msg |
null |
|
$driver |
null |
public function add_function_callback($name, $func, $params = [])
public function add_function_callback($name, $func, $params = [])
{
$pattern = '/\{(?P<name>' . preg_quote($name) . ')\(\s*["\']{0,1}(?P<args>[a-z0-9_:\/\.]+?)["\']{0,1}\s*\)\}/ims';
if ($params['only_pattern'] ?? null) {
return $pattern;
}
$this->add_pattern_callback($pattern, $func, $name);
}
public function add_pattern_callback($pattern, $func, $pattern_name = '')
public function add_pattern_callback($pattern, $func, $pattern_name = '')
{
$this->_custom_patterns_funcs[$pattern] = $func;
$this->_custom_patterns_index[crc32($pattern)] = $pattern;
$pattern_name && $this->_custom_patterns_index[$pattern_name] = $pattern;
}
public function add_section_callback($name, $func, $params = [])
public function add_section_callback($name, $func, $params = [])
{
$pattern = '/\{(?P<name>' . preg_quote($name) . ')\(\s*["\']{0,1}(?P<args>[^"\'\)\}]*?)["\']{0,1}\s*\)\}\s*(?P<body>.+?)\s*{\/(\1)\}/ims';
if ($params['only_pattern'] ?? null) {
return $pattern;
}
$this->add_pattern_callback($pattern, $func, $name);
}
public function exists($stpl_name = '', $force_storage = '')
Alias.
public function exists($stpl_name = '', $force_storage = '')
{
return (bool) $this->_stpl_exists($stpl_name, $force_storage);
}
public function get($file_name = '', $force_storage = '', $JUST_CHECK_IF_EXISTS = false, $RETURN_TEMPLATE_PATH = false)
Alias.
public function get($file_name = '', $force_storage = '', $JUST_CHECK_IF_EXISTS = false, $RETURN_TEMPLATE_PATH = false)
{
return $this->_get_template_file($file_name, $force_storage, $JUST_CHECK_IF_EXISTS, $RETURN_TEMPLATE_PATH);
}
public function init_graphics()
Initialization of the main content Throws one 'echo' at the end.
public function init_graphics()
{
$init_type = MAIN_TYPE;
// Do not remove this!
$this->_init_global_tags();
// Default user group
if ($init_type == 'user' && empty($_SESSION['user_group'])) {
$_SESSION['user_group'] = 1;
}
if (main()->OUTPUT_CACHING && $init_type == 'user' && ($_SERVER['REQUEST_METHOD'] ?? null) == 'GET') {
_class('output_cache')->_process_output_cache();
}
if ( ! main()->no_graphics()) {
if ($this->OB_CATCH_CONTENT) {
ob_start();
}
// Trying to get default task
$go = null;
if ($init_type == 'user' && ! empty($_SESSION['user_id']) && ! empty($_SESSION['user_group'])) {
$go = conf('default_page_user');
} elseif ($init_type == 'admin') {
$go = conf('default_page_admin');
}
// If setting exists - assign it to the location
if ( ! empty($go) && empty($_GET['object'])) {
$go = str_replace(['./?', './'], '', $go);
$tmp_array = [];
parse_str($go, $tmp_array);
foreach ((array) $tmp_array as $k => $v) {
$_GET[$k] = $v;
}
}
$skip_prefetch = false;
// Determine what template need to be loaded in the center area
$tpl_name = $this->TPL_NAME;
if ($init_type == 'admin' && (empty($_SESSION['admin_id']) || empty($_SESSION['admin_group']))) {
$tpl_name = 'login';
if (main()->is_ajax()) {
no_graphics(true);
main()->IS_403 = true;
header((($_SERVER['SERVER_PROTOCOL'] ?? null) ? ($_SERVER['SERVER_PROTOCOL'] ?? null) : 'HTTP/1.1') . ' 403 Forbidden');
$skip_prefetch = true;
}
if ( ! main()->is_console()) {
$skip_prefetch = true;
}
}
if ($this->GET_STPLS_FROM_DB && $this->FROM_DB_GET_ALL) {
$tmp = from('templates')->where('theme_name', conf('theme'))->where('active', '1')->get_2d('name,text');
foreach ((array) $tmp as $k => $v) {
$tmp[$k] = stripslashes($v);
}
$this->_TMP_FROM_DB = $tmp;
unset($tmp);
}
if ( ! $skip_prefetch) {
if (main()->is_console()) {
// Skip security checks for console mode
_class('core_blocks')->tasks(false);
} else {
_class('core_blocks')->prefetch_center();
}
}
}
if ( ! main()->no_graphics()) {
$body['content'] = $this->_init_main_stpl($tpl_name);
$this->_CENTER_RESULT = '';
if ($this->CUSTOM_META_INFO && $init_type == 'user') {
$this->register_output_filter([$this, '_custom_replace_callback'], 'custom_replace');
}
if ($init_type == 'user' && _class('graphics')->IFRAME_CENTER && (false === strpos(($_SERVER['QUERY_STRING'] ?? ''), 'center_area=1'))) {
$this->register_output_filter([$this, '_replace_for_iframe_callback'], 'replace_for_iframe');
}
}
if ( ! main()->no_graphics()) {
// Replace images paths with their absolute ones
if ($this->REWRITE_MODE && $init_type != 'admin') {
$this->register_output_filter([$this, '_rewrite_links_callback'], 'rewrite_links');
}
if ($this->TIDY_OUTPUT && $init_type != 'admin') {
$this->register_output_filter([$this, '_tidy_cleanup_callback'], 'tidy_cleanup');
}
$body['content'] = $this->_apply_output_filters($body['content']);
if (main()->OUTPUT_CACHING && $init_type == 'user' && ($_SERVER['REQUEST_METHOD'] ?? null) == 'GET') {
_class('output_cache')->_put_page_to_output_cache($body);
}
if ( ! main()->is_console() && ! main()->is_ajax()) {
if (DEBUG_MODE) {
$body['debug_info'] = common()->show_debug_info();
}
$_last_pos = strpos($body['content'], '</body>');
if ($_last_pos) {
$body['content'] = substr($body['content'], 0, $_last_pos) . $body['debug_info'] . '</body></html>';
$body['debug_info'] = '';
}
if ($this->ADD_QUICK_PAGE_INFO) {
$body['exec_time'] = $this->_get_quick_page_info();
}
}
$output = implode('', $body);
$this->_output_body_length = strlen($output);
main()->is_ajax() && main()->_debug_header();
main()->_send_main_headers($this->_output_body_length);
// Throw generated output to user
echo $output;
}
if (DEBUG_MODE && main()->no_graphics() && ! main()->is_console() && ! main()->is_ajax()) {
echo common()->show_debug_info();
}
// an ajax answer without layout; skipped when its output already went out
DEBUG_MODE && main()->no_graphics() && main()->_debug_header();
// Output cache for 'no graphics' content
if (main()->no_graphics() && main()->OUTPUT_CACHING && $init_type == 'user' && ($_SERVER['REQUEST_METHOD'] ?? null) == 'GET') {
_class('output_cache')->_put_page_to_output_cache(ob_get_clean());
}
if (main()->LOG_EXEC || $this->LOG_EXEC_INFO) {
_class('logs')->log_exec();
}
// End sending main output
ob_end_flush();
if ($this->EXIT_AFTER_ECHO) {
exit();
}
}
public function parse($name, $replace = [], $params = [])
Simple template parser (*.stpl).
public function parse($name, $replace = [], $params = [])
{
$name = strtolower(trim($name));
// Support for the driver name in prefix, example: "twig:user/account", "yf:user/account"
if (strpos($name, ':') !== false) {
list($driver, $name) = explode(':', $name);
if ($driver) {
$params['driver'] = $driver;
}
}
// Support for the framework calls
$yf_prefix = 'yf_';
$yfp_len = strlen($yf_prefix);
if (substr($name, 0, $yfp_len) == $yf_prefix) {
$name = substr($name, $yfp_len);
}
if (false !== strpos($name, '@')) {
$r = [
'@object' => ($_GET['object'] ?? null),
'@action' => ($_GET['action'] ?? null),
'@id' => ($_GET['id'] ?? null),
];
$name = str_replace(array_keys($r), array_values($r), $name);
}
if ( ! is_array($params)) {
$params = [];
}
$string = ($params['string'] ?? null) ?: false;
$params['replace_images'] = ($params['replace_images'] ?? null) ?: true;
$params['no_cache'] = ($params['no_cache'] ?? null) ?: false;
$params['force_storage'] = ($params['force_storage'] ?? null) ?: '';
$params['no_include'] = ($params['no_include'] ?? null) ?: false;
$stpl_time_start = null;
if (DEBUG_MODE) {
$stpl_time_start = microtime(true);
}
$replace = (array) $replace + (array) $this->_global_tags;
$replace['error'] = $this->_parse_get_user_errors($name, $replace['error'] ?? null);
if (isset($replace[''])) {
unset($replace['']);
}
if ($this->ALLOW_CUSTOM_FILTER) {
$this->_custom_filter($name, $replace);
}
// Allowing to override driver
if (($params['driver'] ?? null) && ($params['driver'] ?? null) != $this->DRIVER_NAME) {
$driver = _class('tpl_driver_' . ($this->_warn_removed_driver($params['driver']) ? 'yf' : $params['driver']), 'classes/tpl/');
} else {
$driver = $this->driver;
}
$string = $driver->parse($name, $replace, $params);
if ($params['replace_images']) {
$string = $this->_replace_images_paths($string);
}
// wrapper raw
$is_raw = ($params['raw'] ?? false) && ($this->driver->is_raw ?? false);
$raw = $is_raw ? $this->driver->_raw_s : '';
$string = $raw . $string . $raw;
if (DEBUG_MODE) {
$this->_parse_set_debug_info($name, $replace, $params, $string, $stpl_time_start);
}
return $string;
}
public function parse_if_exists($name, $replace = [], $params = [])
Wrapper on parse(), silently failing if template not exists.
public function parse_if_exists($name, $replace = [], $params = [])
{
return $this->exists($name) ? $this->parse($name, $replace, $params) : '';
}
public function parse_raw($name, $replace = [], $params = [])
public function parse_raw($name, $replace = [], $params = [])
{
$params['raw'] = true;
return $this->parse($name, $replace, $params);
}
public function parse_string($string = '', $replace = [], $name = '', $params = [])
Wrapper to parse given template string.
public function parse_string($string = '', $replace = [], $name = '', $params = [])
{
if ( ! strlen($string ?? '')) {
$string = ' ';
}
if ( ! $name) {
$name = 'auto__' . abs(crc32($string));
}
$params['string'] = $string;
return $this->parse($name, $replace, $params);
}
public function parse_string_raw($string = '', $replace = [], $name = '', $params = [])
Wrapper to raw.
public function parse_string_raw($string = '', $replace = [], $name = '', $params = [])
{
$params['raw'] = true;
return $this->parse_string($string, $replace, $name, $params);
}
public function raw($html)
Mark a value as HTML that AUTO_ESCAPE must leave alone: tpl()->parse('x', ['table' => tpl()->raw($html)]).
public function raw($html)
{
class_exists('yf_tpl_raw', false) || require_once __DIR__ . '/tpl/yf_tpl_raw.class.php';
return new yf_tpl_raw($html);
}
public function register_output_filter($callback_impl, $filter_name = '')
Registers custom function to be used in templates.
public function register_output_filter($callback_impl, $filter_name = '')
{
if (empty($filter_name)) {
$filter_name = substr(abs(crc32(microtime(true))), 0, 8);
}
$this->_OUTPUT_FILTERS[$filter_name] = $callback_impl;
}
_ hooks and helpers; not routable as page actionspublic function __call($name, $args)
Catch missing method call.
public function __call($name, $args)
{
return main()->extend_call($this, $name, $args);
}
public function _apply_output_filters($text = '')
Process output filters for the given text.
public function _apply_output_filters($text = '')
{
foreach ((array) $this->_OUTPUT_FILTERS as $cur_filter) {
if (is_callable($cur_filter)) {
$text = call_user_func($cur_filter, $text);
}
}
return $text;
}
public function _compiled_suffix()
Compiled templates differ with these switches, so compiled file names and cache keys carry them.
public function _compiled_suffix()
{
return ($this->ALLOW_EVAL_PHP_CODE ? '' : '_noeval') . ($this->AUTO_ESCAPE ? '_esc' : '') . ($this->EXPOSE_SUPERGLOBALS ? '' : '_nosg')
// the compiled file of `main` holds main.bs5.stpl under bs5: a site switching frameworks must not reuse the other one
. ($this->CSS_FRAMEWORK_VARIANTS && conf('css_framework') === 'bs5' ? '_bs5' : '');
}
public function _css_framework_variant($stpl_name, $all_tpls_paths)
`<name>.bs5` instead of `<name>` under css_framework bs5 when that variant exists in a location at least as specific as the best location of `<name>`: a site's own `main.stpl` keeps winning over a framework `main.bs5.stpl`.
$stpl_name string$all_tpls_paths array public function _css_framework_variant($stpl_name, $all_tpls_paths)
{
if ( ! $this->CSS_FRAMEWORK_VARIANTS || conf('css_framework') !== 'bs5') {
return $stpl_name;
}
$variant = $stpl_name . '.bs5';
if (empty($all_tpls_paths[$variant])) {
return $stpl_name;
}
$rank = static function ($paths) {
foreach (['site', 'app', 'project', 'framework'] as $i => $location) {
if ( ! empty($paths[$location])) {
return $i;
}
}
return PHP_INT_MAX;
};
return $rank($all_tpls_paths[$variant]) <= $rank($all_tpls_paths[$stpl_name] ?? []) ? $variant : $stpl_name;
}
public function _custom_filter($stpl_name, &$replace)
Custom filter (Inherit this method and customize anything you want).
public function _custom_filter($stpl_name, &$replace)
{
if (($stpl_name ?? false) && $stpl_name == 'home_page/main') {
// example only:
// print_r($replace);
// $replace['recent_ads'] = '';
}
}
public function _custom_replace_callback($text = '')
Custom text replacing method.
public function _custom_replace_callback($text = '')
{
return _class('custom_meta_info')->_process($text);
}
public function _debug_mode_callback($text = '')
public function _debug_mode_callback($text = '')
{
if ( ! DEBUG_MODE) {
return $text;
}
return preg_replace_callback('~(<title>)(.*?)(</title>)~ims', function ($m) {
return $m[1] . strip_tags($m[2]) . $m[3];
}, $text);
}
public function _esc($value)
public function _esc($value)
{
if ( ! is_string($value)) {
return $value;
}
return _escape_tpl_braces(htmlspecialchars($value, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8', false));
}
public function _get_cached_paths()
public function _get_cached_paths()
{
if (isset($this->_get_cached_paths_memo[MAIN_TYPE])) {
return $this->_get_cached_paths_memo[MAIN_TYPE];
}
$this->_get_cached_paths_memo[MAIN_TYPE] = getset('tpl_get_cached_paths', function () {
$allowed_exts = (array) $this->ALLOWED_EXTS;
$templates_dir = trim($this->_THEMES_PATH, '/');
$patterns = [
'framework' => [
YF_PATH . $templates_dir . '/*/*.*',
YF_PATH . $templates_dir . '/*/*/*.*',
YF_PATH . $templates_dir . '/*/*/*/*.*',
YF_PATH . 'plugins/*/' . $templates_dir . '/*/*.*',
YF_PATH . 'plugins/*/' . $templates_dir . '/*/*/*.*',
YF_PATH . 'plugins/*/' . $templates_dir . '/*/*/*/*.*',
],
'project' => [
PROJECT_PATH . $templates_dir . '/*/*.*',
PROJECT_PATH . $templates_dir . '/*/*/*.*',
PROJECT_PATH . $templates_dir . '/*/*/*/*.*',
PROJECT_PATH . 'plugins/*/' . $templates_dir . '/*/*.*',
PROJECT_PATH . 'plugins/*/' . $templates_dir . '/*/*/*.*',
PROJECT_PATH . 'plugins/*/' . $templates_dir . '/*/*/*/*.*',
],
'app' => [
APP_PATH . $templates_dir . '/*/*.*',
APP_PATH . $templates_dir . '/*/*/*.*',
APP_PATH . $templates_dir . '/*/*/*/*.*',
APP_PATH . 'plugins/*/' . $templates_dir . '/*/*.*',
APP_PATH . 'plugins/*/' . $templates_dir . '/*/*/*.*',
APP_PATH . 'plugins/*/' . $templates_dir . '/*/*/*/*.*',
],
];
$site_path = (MAIN_TYPE_USER ? SITE_PATH : ADMIN_SITE_PATH);
if (is_site_path()) {
$patterns['site'] = [
$site_path . $templates_dir . '/*/*.*',
$site_path . $templates_dir . '/*/*/*.*',
$site_path . $templates_dir . '/*/*/*/*.*',
$site_path . 'plugins/*/' . $templates_dir . '/*/*.*',
$site_path . 'plugins/*/' . $templates_dir . '/*/*/*.*',
$site_path . 'plugins/*/' . $templates_dir . '/*/*/*/*.*',
];
}
$plens = [
'framework' => strlen(YF_PATH),
'project' => strlen(PROJECT_PATH),
'app' => strlen(APP_PATH),
'site' => isset($patterns['site']) ? strlen($site_path) : null,
];
$names = [];
foreach ($patterns as $gname => $glob_patterns) {
foreach ($glob_patterns as $glob) {
foreach (glob($glob, GLOB_NOSORT) as $path) {
$name = substr($path, $plens[$gname]);
$ext = pathinfo($name, PATHINFO_EXTENSION);
if ( ! $ext || ! in_array($ext, $allowed_exts)) {
continue;
}
$p = explode('/', $name);
if ($p[0] == 'plugins') {
$p = array_slice($p, 2);
}
$theme = '';
if ($p[0] == $templates_dir) {
$theme = $p[1];
$p = array_slice($p, 2);
}
$name = implode('/', $p);
$name = substr($name, 0, -strlen('.' . $ext));
$names[$name][$gname][$theme] = $path;
}
}
}
return $names;
});
return $this->_get_cached_paths_memo[MAIN_TYPE];
}
public function _get_def_user_theme()
Get default user theme (for admin section).
public function _get_def_user_theme()
{
if ( ! empty($this->_def_user_theme)) {
return $this->_def_user_theme;
}
if (empty($this->_def_user_theme)) {
$this->_def_user_theme = 'user';
}
return $this->_def_user_theme;
}
public function _get_quick_page_info()
public function _get_quick_page_info()
{
if ( ! $this->ADD_QUICK_PAGE_INFO) {
return false;
}
return PHP_EOL . '<!-- date: ' . gmdate('Y-m-d H:i:s') . ' UTC, time: ' . round(microtime(true) - main()->_time_start, 3) . ', memory: ' . memory_get_peak_usage() . ', db: ' . (int) db()->NUM_QUERIES . ' -->' . PHP_EOL;
}
public function _get_template_file($file_name = '', $force_storage = '', $JUST_CHECK_IF_EXISTS = false, $RETURN_TEMPLATE_PATH = false)
Read template file contents (or get it from DB).
public function _get_template_file($file_name = '', $force_storage = '', $JUST_CHECK_IF_EXISTS = false, $RETURN_TEMPLATE_PATH = false)
{
$string = false;
$NOT_FOUND = false;
$storage = 'inline';
$paths = null;
$file_path = null;
$file_name = trim(trim(trim($file_name), '/'));
$l = strlen(YF_PREFIX);
if (substr($file_name, 0, $l) == YF_PREFIX) {
$file_name = substr($file_name, $l);
}
$stpl_ext = $this->_STPL_EXT;
$path_ext = pathinfo($file_name, PATHINFO_EXTENSION);
$path_ext && $path_ext = '.' . $path_ext;
// Allowed extension overrides
if ( ! $path_ext || ! in_array($path_ext, (array) $this->ALLOWED_EXTS)) {
$file_name .= $stpl_ext;
}
// Fix double extesion
$file_name = str_replace($stpl_ext . $stpl_ext, $stpl_ext, $file_name);
$stpl_name = str_replace([$stpl_ext, $path_ext], '', $file_name);
if ($this->GET_STPLS_FROM_DB || $force_storage == 'db') {
if ($this->FROM_DB_GET_ALL) {
if ( ! empty($this->_TMP_FROM_DB[$stpl_name])) {
$string = $this->_TMP_FROM_DB[$stpl_name];
unset($this->_TMP_FROM_DB[$stpl_name]);
} else {
$NOT_FOUND = true;
}
} else {
$text = from('templates')->where('theme_name', conf('theme'))->where('name', $stpl_name)->where('active', '1')->one('text');
if (isset($text)) {
$string = stripslashes($text);
} else {
$NOT_FOUND = true;
}
}
$storage = 'db';
} else {
$def_theme = $this->_get_def_user_theme();
$all_tpls_paths = $this->_get_cached_paths();
$variant = $this->_in_css_variant ? $stpl_name : $this->_css_framework_variant($stpl_name, $all_tpls_paths);
if ($variant !== $stpl_name) {
// the variant only when it resolves for this theme / main type; otherwise the base template as before
$this->_in_css_variant = true;
try {
if ($this->_get_template_file($variant . $stpl_ext, $force_storage, true)) {
return $this->_get_template_file($variant . $stpl_ext, $force_storage, $JUST_CHECK_IF_EXISTS, $RETURN_TEMPLATE_PATH);
}
} finally {
$this->_in_css_variant = false;
}
}
$paths = $all_tpls_paths[$stpl_name] ?? null;
// Storages are defined in specially crafted `order`, so do not change it unless you have strong reason
$storages = [];
$site_path = (MAIN_TYPE_USER ? SITE_PATH : ADMIN_SITE_PATH);
$theme = (string) conf('theme');
$storages = [
'dev',
'site_lang',
'site',
'site_inherit',
'site_inherit2',
'app_lang',
'app',
'app_inherit',
'app_inherit2',
'app_user',
'project',
'project_user',
'framework',
'framework_user',
];
$storages = array_filter($storages);
foreach ((array) $storages as $_storage) {
if ($force_storage && $force_storage != $_storage) {
continue;
}
$file_path = '';
$_theme = '';
if (in_array($_storage, ['app', 'project', 'framework'])) {
$_theme = $_storage == 'framework' ? MAIN_TYPE : $theme;
if (isset($paths[$_storage][$_theme])) {
$file_path = $paths[$_storage][$_theme];
}
} elseif (in_array($_storage, ['app_user', 'project_user', 'framework_user']) && MAIN_TYPE_ADMIN && ! in_array($stpl_name, ['main', 'main.bs5'])) {
$s = substr($_storage, 0, -strlen('_user'));
if (isset($paths[$s]['user'])) {
$file_path = $paths[$s]['user'];
}
} elseif ($_storage == 'site') {
$_theme = $_storage == 'framework' ? MAIN_TYPE : $theme;
if (isset($paths[$_storage][$_theme])) {
$file_path = $paths[$_storage][$_theme];
}
} elseif (in_array($_storage, ['app_lang', 'site_lang']) && $this->ALLOW_LANG_BASED_STPLS) {
$lang = conf('language');
$_theme = $theme . '.' . $lang;
$s = substr($_storage, 0, -strlen('_lang'));
if (isset($paths[$s][$_theme])) {
$file_path = $paths[$s][$_theme];
}
} elseif (in_array($_storage, ['app_inherit', 'site_inherit']) && $this->_INHERITED_SKIN) {
$_theme = $this->_INHERITED_SKIN;
$s = substr($_storage, 0, -strlen('_inherit'));
if (isset($paths[$s][$_theme])) {
$file_path = $paths[$s][$_theme];
}
} elseif (in_array($_storage, ['app_inherit2', 'site_inherit2']) && $this->_INHERITED_SKIN2) {
$_theme = $this->_INHERITED_SKIN2;
$s = substr($_storage, 0, -strlen('_inherit2'));
if (isset($paths[$s][$_theme])) {
$file_path = $paths[$s][$_theme];
}
} elseif (in_array($_storage, ['dev'])) {
}
if ( ! $file_path || ! $this->_stpl_path_exists($file_path)) {
continue;
}
$string = $this->_stpl_path_get($file_path);
if ($string !== false) {
$storage = $_storage;
break;
}
}
// Last try from cache (preloaded templates)
if ($string === false) {
$compiled_stpl = conf('_compiled_stpls::' . $stpl_name);
if ($compiled_stpl) {
$string = $compiled_stpl;
$storage = 'compiled_cache';
}
}
if ($string === false) {
$NOT_FOUND = true;
}
}
if (DEBUG_MODE) {
$this->driver->debug[$stpl_name]['storage'] = $storage;
$this->driver->debug[$stpl_name]['storages'] = $paths;
}
if ($RETURN_TEMPLATE_PATH) {
return $file_path;
}
// If we just checking template existance - then stop here
if ($JUST_CHECK_IF_EXISTS) {
return ! $NOT_FOUND;
}
// Log error message if template file was not found
if ($NOT_FOUND) {
trigger_error('STPL: template "' . $file_name . '" in theme "' . conf('theme') . '" not found.', E_USER_WARNING);
}
return $string;
}
public function _i18n_wrapper($input = '', $replace = [])
Wrapper function for t/translate/i18n calls inside templates.
public function _i18n_wrapper($input = '', $replace = [])
{
if ( ! strlen($input)) {
return '';
}
$input = stripslashes(trim($input, '"\''));
$args = [];
// Complex case with substitutions
if (preg_match('/(?P<text>.+?)["\']{1},[\s\t]*%(?P<args>[a-z]+.+)$/ims', $input, $m)) {
foreach (explode(';%', $m['args']) as $arg) {
$attr_name = $attr_val = '';
if (false !== strpos($arg, '=')) {
list($attr_name, $attr_val) = explode('=', trim($arg));
}
$attr_name = trim(str_replace(["'", '"'], '', $attr_name));
$attr_val = trim(str_replace(["'", '"'], '', $attr_val));
$args['%' . $attr_name] = $attr_val;
}
$text_to_translate = $m['text'];
} else {
$text_to_translate = $input;
}
$output = t($text_to_translate, $args);
// Do replacement of the template vars on the last stage
// example: @replace1 will be got from $replace['replace1'] array item
if (false !== strpos($output, '@') && ! empty($replace)) {
$r = [];
foreach ((array) $replace as $k => $v) {
$r['@' . $k] = $this->_out($v, $k);
}
$output = str_replace(array_keys($r), array_values($r), $output);
}
return $output;
}
public function _init()
Framework constructor.
public function _init()
{
if (DEBUG_MODE) {
$this->ADD_QUICK_PAGE_INFO = true;
}
// Needed to ensure backtracking still works on big templates (extended from 1 000 000 on 26kb stpl js() parsing)
ini_set('pcre.backtrack_limit', '10000000');
if (defined('IS_FRONT')) {
conf('IS_FRONT', (bool) constant('IS_FRONT'));
}
$this->IS_FRONT = (bool) conf('IS_FRONT');
// Set custom skin
if ( ! empty($_SESSION['user_skin']) && MAIN_TYPE_USER) {
conf('theme', ($_SESSION['user_skin'] ?? null));
} elseif (defined('DEFAULT_SKIN')) {
conf('theme', constant('DEFAULT_SKIN'));
}
if ( ! conf('theme')) {
conf('theme', MAIN_TYPE);
}
// Directory where themes are stored
conf('THEMES_PATH', $this->_THEMES_PATH);
// Template files extensions
conf('_STPL_EXT', $this->_STPL_EXT);
// Set path to the templates including selected skin
$this->TPL_PATH = $this->_THEMES_PATH . conf('theme') . '/';
if ($this->COMPRESS_OUTPUT) {
$this->register_output_filter([$this, '_simple_cleanup_callback'], 'simple_cleanup');
}
if ($this->ALLOW_LANG_BASED_STPLS) {
$this->_lang_theme_path = PROJECT_PATH . $this->_THEMES_PATH . conf('theme') . '.' . conf('language') . '/';
if ( ! file_exists($this->_lang_theme_path)) {
$this->ALLOW_LANG_BASED_STPLS = false;
$this->_lang_theme_path = '';
}
}
if ($this->ALLOW_SKIN_INHERITANCE) {
if (defined('INHERIT_SKIN')) {
conf('INHERIT_SKIN', constant('INHERIT_SKIN'));
}
if (conf('INHERIT_SKIN') != conf('theme')) {
$this->_INHERITED_SKIN = conf('INHERIT_SKIN');
}
if (defined('INHERIT_SKIN2')) {
conf('INHERIT_SKIN2', constant('INHERIT_SKIN2'));
}
if (conf('INHERIT_SKIN2') != conf('theme')) {
$this->_INHERITED_SKIN2 = conf('INHERIT_SKIN2');
}
}
// Turn off CPU expensive features on overloading
if (conf('HIGH_CPU_LOAD') == 1) {
$this->COMPRESS_OUTPUT = false;
$this->TIDY_OUTPUT = false;
$this->FROM_DB_GET_ALL = false;
}
$this->_init_global_tags();
if (DEBUG_MODE) {
$this->register_output_filter([$this, '_debug_mode_callback'], 'debug_mode');
}
if (main()->is_console()) {
$this->OB_CATCH_CONTENT = false;
}
$this->_set_default_driver($this->DRIVER_NAME);
}
public function _init_global_tags()
Global scope tags.
public function _init_main_stpl($tpl_name = '')
Initialization of the main template in the theme (could be overwritten to match design) Return contents of the main template.
public function _init_main_stpl($tpl_name = '')
{
return $this->parse($tpl_name);
}
public function _out($value, $name = '')
HTML-escape a value when AUTO_ESCAPE is on; arrays, objects and raw() values pass as they are.
$name mixed Replace var name, checked against AUTO_ESCAPE_RAW public function _out($value, $name = '')
{
if ( ! $this->AUTO_ESCAPE || ! is_string($value)) {
return $value;
}
if ($name !== '') {
// foreach vars arrive as "row.key": the key alone is what AUTO_ESCAPE_RAW lists
$short = (string) substr(strrchr('.' . $name, '.'), 1);
if (isset($this->_raw_names[$name]) || in_array($name, (array) $this->AUTO_ESCAPE_RAW, true) || in_array($short, (array) $this->AUTO_ESCAPE_RAW, true)) {
return $value;
}
}
return $this->_esc($value);
}
public function _parse_get_user_errors($name, $err)
public function _parse_get_user_errors($name, $err)
{
if (isset($err)) {
return $err;
}
$err = '';
if ($name != 'main' && common()->_error_exists()) {
if ( ! isset($this->_user_error_msg)) {
$this->_user_error_msg = common()->_show_error_message('', false);
}
$err = $this->_user_error_msg;
}
return $err;
}
public function _parse_set_debug_info($name = '', $replace = [], $params = [], $string = '', $stpl_time_start = 0)
public function _parse_set_debug_info($name = '', $replace = [], $params = [], $string = '', $stpl_time_start = 0)
{
if ( ! DEBUG_MODE) {
return false;
}
if ( ! isset($this->driver->CACHE[$name]['exec_time'])) {
$this->driver->CACHE[$name]['exec_time'] = 0;
}
$this->driver->CACHE[$name]['exec_time'] += (microtime(true) - $stpl_time_start);
// For debug store information about variables used while processing template
if ($this->DEBUG_STPL_VARS) {
debug('STPL_REPLACE_VARS::' . $name . '[]', $replace);
}
if ($this->USE_SOURCE_BACKTRACE) {
debug('STPL_TRACES::' . $name, main()->trace_string());
}
return true;
}
public function _process_clear_unused($string, $replace = [], $name = '')
If content need to be cleaned from unused tags - do that.
public function _process_clear_unused($string, $replace = [], $name = '')
{
return preg_replace('/\{[\w_]+\}/i', '', $string);
}
public function _process_eval_string($string, $replace = [], $name = '')
public function _process_eval_string($string, $replace = [], $name = '')
{
return eval('return "' . str_replace('"', '\"', $string) . '";');
}
public function _process_var_filters($text = '', $filters = '', $name = '')
public function _process_var_filters($text = '', $filters = '', $name = '')
{
if (is_string($filters) && strpos($filters, '|') !== false) {
$filters = explode('|', $filters);
}
if ( ! is_array($filters)) {
$filters = [$filters];
}
$raw = false;
foreach ($filters as $fname) {
if ($fname === 'raw' || $fname === '_prepare_html' || $fname === 'htmlspecialchars') {
$raw = true;
}
// a site-defined global raw() keeps being called, as before
if ($fname === 'raw' && ! function_exists('raw')) {
continue;
}
if (is_callable($fname)) {
$text = $fname($text);
} elseif (is_string($fname) && function_exists($fname)) {
$text = $fname($text);
}
}
return $raw ? $text : $this->_out($text, $name);
}
public function _replace_for_iframe_callback($text = '')
Replace method for 'IFRAME in center' mode.
public function _replace_for_iframe_callback($text = '')
{
return _class('rewrite')->_replace_links_for_iframe($text);
}
public function _replace_images_paths($string = '')
Replace paths to images.
public function _replace_images_paths($string = '')
{
$images_path = (MAIN_TYPE_USER ? $this->MEDIA_PATH : ADMIN_WEB_PATH) . $this->TPL_PATH . $this->_IMAGES_PATH;
$uploads_path = $this->MEDIA_PATH . $this->_UPLOADS_PATH;
$r = [
'"images/' => '"' . $images_path,
'\'images/' => '\'' . $images_path,
'src="uploads/' => 'src="' . $uploads_path,
'"uploads/' => '"' . $uploads_path,
'\'uploads/' => '\'' . $uploads_path,
];
return str_replace(array_keys($r), array_values($r), $string);
}
public function _rewrite_links_callback($text = '')
Rewrite links callback method.
public function _rewrite_links_callback($text = '')
{
return _class('rewrite')->_rewrite_replace_links($text);
}
public function _set_default_driver($name = '')
public function _set_default_driver($name = '')
{
if ( ! $name) {
$name = $this->DRIVER_NAME;
}
if ( ! $name) {
$name = 'yf';
}
if ($this->_warn_removed_driver($name)) {
$name = 'yf';
}
$this->DRIVER_NAME = $name;
$this->driver = _class('tpl_driver_' . $name, 'classes/tpl/');
}
public function _simple_cleanup_callback($text = '')
Simple cleanup (compress) output.
public function _simple_cleanup_callback($text = '')
{
if (DEBUG_MODE) {
debug('compress_output::size_original', strlen($text));
}
$text = str_replace(["\r", "\n", "\t"], '', $text);
$text = preg_replace('#[\s]{2,}#ms', ' ', $text);
// Remove html comments
$text = preg_replace('#<\!--[\w\s\-\/]*?-->#ms', '', $text);
if (DEBUG_MODE) {
debug('compress_output::size_compressed', strlen($text));
}
return $text;
}
public function _stpl_exists($stpl_name = '', $force_storage = '')
Check if template exists (simple wrapper for the '_get_template_file').
public function _stpl_exists($stpl_name = '', $force_storage = '')
{
// Exists in cache
if ( ! $force_storage && isset($this->driver->CACHE[$stpl_name])) {
return true;
}
return (bool) $this->_get_template_file($stpl_name, $force_storage, 1);
}
public function _stpl_path_exists($file_name)
Check if given template exists.
public function _stpl_path_exists($file_name)
{
return file_exists($file_name);
}
public function _stpl_path_get($file_name)
public function _stpl_path_get($file_name)
{
return file_get_contents($file_name);
}
public function _tidy_cleanup_callback($text = '')
Clenup HTML output with Tidy.
public function _tidy_cleanup_callback($text = '')
{
if ( ! class_exists('tidy') || ! extension_loaded('tidy')) {
return $text;
}
$tidy_default_config = [
'alt-text' => '',
'output-xhtml' => true,
];
$tidy = new tidy();
$tidy->parseString($text, $this->_TIDY_CONFIG ?: $tidy_default_config, conf('charset'));
$tidy->cleanRepair();
return $tidy;
}
public function _translate_for_stpl($string = '', $args_from_tpl = '', $lang = '')
Wrapper for translation method (for call from templates or other).
public function _translate_for_stpl($string = '', $args_from_tpl = '', $lang = '')
{
$args = [];
if (is_string($args_from_tpl) && strlen($args_from_tpl)) {
$args = _attrs_string2array($args_from_tpl);
}
return t($string, $args, $lang);
}
public function _url_wrapper($params = [])
Wrapper around 'url()' function, called like this inside templates: {url(object=home_page;action=test)}.
public function _url_wrapper($params = [])
{
// Try to process method params (string like attrib1=value1;attrib2=value2)
if (is_string($params) && strlen($params)) {
// Url like this: /object/action/id
if ($params[0] == '/') {
// Do nothing, just directly pass this to url() as string
} elseif (false !== strpos($params, '=')) {
$params = _attrs_string2array($params);
} else {
list($object, $action, $id, $page) = explode(';', str_replace(',', ';', $params)) + [null, null, null, null];
$params = [
'object' => $object,
'action' => $action,
'id' => $id,
'page' => $page,
];
}
}
return url($params);
}
public function _warn_removed_driver($name)
public function _warn_removed_driver($name)
{
if (isset($this->REMOVED_DRIVERS[$name])) {
trigger_error('TPL: driver "' . $name . '" ' . $this->REMOVED_DRIVERS[$name] . ', falling back to "yf"', E_USER_WARNING);
return true;
}
return false;
}