yf_tpl class

Stadard Framework template engine.

Fileplugins/tpl/classes/yf_tpl.class.php
Use_class('tpl')

Public properties

A site sets them in its config with $PROJECT_CONF['tpl']['NAME'] = ...;

PropertyDefaultDescription
$ADD_QUICK_PAGE_INFO
bool
false Will add cur date, generation time, memory and db queries into any common page before body
$ALLOWED_EXTS
string
['tpl', 'stpl', 'html'] @conf_skip Ability to use files with these extensions as templates
$ALLOW_CUSTOM_FILTER
bool
false Allow custom filter for all parsed stpls
$ALLOW_EVAL_PHP_CODE
bool
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_STPLS
bool
false Allow language-based special stpls
$ALLOW_PHP_TEMPLATES
bool
false Allow pure php templates
$ALLOW_SKIN_INHERITANCE
bool
true Allow skin inheritance (only one level used)
$AUTO_ESCAPE
bool
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_RAW
array
['center_area', 'content', 'body', 'html', 'header', 'footer', ... (15 items)]
array (
  0 => 'center_area',
  1 => 'content',
  2 => 'body',
  3 => 'html',
  4 => 'header',
  5 => 'footer',
  6 => 'form',
  7 => 'table',
  8 => 'filter',
  9 => 'js',
  10 => 'css',
  11 => 'menu',
  12 => 'pager',
  13 => 'left_area',
  14 => 'right_area',
)
Replace var names that carry HTML by design and are never auto-escaped (layout slots filled by the framework)
$AUTO_LOAD_PACKED_STPLS
bool
false If available - use packed STPLs without checking if some exists in project
$COMPILED_DIR
bool
'stpls_compiled/' Compile templates folder
$COMPILE_CHECK_STPL_CHANGED
bool
false TTL for compiled stpls
$COMPILE_TEMPLATES
bool
false Allow to compile templates
$COMPILE_TTL
bool
3600 TTL for compiled stpls
$COMPRESS_OUTPUT
bool
false Compressing output by cutting '\t','\r','\n',' ',' '
$CSS_FRAMEWORK_VARIANTS
bool
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_INFO
bool
false Custom meta information (customizable for every page) : page titles, meta keywords, description
$DEBUG_STPL_VARS
bool
false
$DRIVER_NAME
string
'yf' Current tempalte engine dirver to use
$EXIT_AFTER_ECHO
bool
false Exit after sending main content
$EXPOSE_SUPERGLOBALS
bool
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_ALL
bool
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_DB
bool
false Use database to store templates
$INHERIT_SKIN null
$INHERIT_SKIN2 null
$IS_FRONT false
$LOG_EXEC_INFO []
$MEDIA_PATH ''
$NO_TAGS_IN_INSERTED
bool
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_CONTENT
bool
true Catch any output before gzipped content (works only with GZIP)
$REMOVED_DRIVERS
array
['blitz' => 'removed in yf 3.9, see https://github.com/yfix/yf/blob/ma'..., 'smarty' => 'removed, see https://github.com/yfix/yf/blob/master/docs/'..., 'fenom' => 'removed, see https://github.com/yfix/yf/blob/master/docs/'...]
array (
  'blitz' => 'removed in yf 3.9, see https://github.com/yfix/yf/blob/master/docs/UPGRADING.md',
  'smarty' => 'removed, see https://github.com/yfix/yf/blob/master/docs/UPGRADING.md',
  'fenom' => 'removed, see https://github.com/yfix/yf/blob/master/docs/UPGRADING.md',
)
Deleted template drivers: name => reason; a requested one warns and the yf driver renders instead
$REWRITE_MODE
bool
false Using SEO - friendly URLs (All links need to be absolute)
$TIDY_OUTPUT
bool
false Use or not Tidy to cleanup output
$TPL_NAME
string
'main' default template name
$TPL_PATH
string
'' @conf_skip Path to the templates (including current theme path)
$USE_SOURCE_BACKTRACE
bool
true Use backtrace to get STPLs source (where called from) FOR DEBUG_MODE ONLY !
$_CENTER_RESULT null
$_IMAGES_PATH
string
'images/' @conf_skip
$_INHERITED_SKIN null
$_INHERITED_SKIN2 null
$_OUTPUT_FILTERS []
$_STPL_EXT
string
'.stpl' @conf_skip
$_THEMES_PATH
string
'templates/' @conf_skip
$_TIDY_CONFIG ''
$_TMP_FROM_DB null
$_UPLOADS_PATH
string
'uploads/' @conf_skip
$_avail_arrays
array
['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_tags
array
[] Global scope tags (included in any parsed template)
$_in_css_variant
bool
false @conf_skip set while a .bs5 variant is being resolved
$_lang_theme_path null
$_output_body_length null
$_raw_names
array
[] @conf_skip Replace names filled with rendered HTML during this request ({catch} results), never auto-escaped
$_user_error_msg null
$driver null

Public methods 47

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);
    }

exists

public function exists($stpl_name = '', $force_storage = '')

Alias.

    public function exists($stpl_name = '', $force_storage = '')
    {
        return (bool) $this->_stpl_exists($stpl_name, $force_storage);
    }

get

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);
    }

init_graphics

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();
        }
    }

parse

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) : '';
    }

parse_raw

public function parse_raw($name, $replace = [], $params = [])
    public function parse_raw($name, $replace = [], $params = [])
    {
        $params['raw'] = true;
        return $this->parse($name, $replace, $params);
    }

parse_string

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);
    }

raw

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;
    }

Methods starting with _ hooks and helpers; not routable as page actions

__call

public 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);
    }

_esc

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;
    }

_i18n_wrapper

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;
    }

_init

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_global_tags()
    {
        $data = [
            'main_user_id' => (int) main()->USER_ID,
            'is_logged_in' => (int) ((bool) main()->USER_ID),
            'site_id' => (int) conf('SITE_ID'),
            'lang' => conf('language'),
            'tpl_path' => MEDIA_PATH . $this->TPL_PATH,
        ];
        foreach ($data as $k => $v) {
            $this->_global_tags[$k] = $v;
        }
    }
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);
    }

_out

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 _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;
    }

_stpl_exists

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);
    }

_url_wrapper

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;
    }