yf_cache class

Caching layer.

Fileplugins/cache/classes/yf_cache.class.php
Use_class('cache')

Public properties

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

PropertyDefaultDescription
$AUTO_CACHE_NS
bool
false Force cache class to generate unique namespace, based on project_path. Usually needed to separate projects within same cache storage (memcached as example)
$CACHE_NS
string
'' Namespace for drivers other than 'file'
$DRIVER
string
'memcache' Cache driver to use
$FILES_TTL null
$FORCE_REBUILD_CACHE
bool
false Forcing to delete elements
$NO_CACHE
bool
false Allows to turn off cache at any moment. Useful for unit tests and complex situations.
$RANDOM_TTL_ADD
bool
true Add random value for each entry TTL (to avoid one-time cache invalidation problems)
$REMOVED_DRIVERS
array
['xcache' => 'removed in yf 3.9, see https://github.com/yfix/yf/blob/ma'..., 'couchbase' => 'removed in yf 3.9, see https://github.com/yfix/yf/blob/ma'..., 'mongodb' => 'removed in yf 3.9, see https://github.com/yfix/yf/blob/ma'...]
array (
  'xcache' => 'removed in yf 3.9, see https://github.com/yfix/yf/blob/master/docs/UPGRADING.md',
  'couchbase' => 'removed in yf 3.9, see https://github.com/yfix/yf/blob/master/docs/UPGRADING.md',
  'mongodb' => 'removed in yf 3.9, see https://github.com/yfix/yf/blob/master/docs/UPGRADING.md',
)
Drivers deleted in yf 3.9: name => reason, a requested one warns before the usual "no driver" fallback
$TTL
int
3600 Cache entries time-to-live (in seconds)
$_NO_CACHE_WHY false
$_driver null
$_driver_ok false
$_extend []
$_init_complete false
$_no_cache []
$_tried_to_connect null

Public methods 26

clean

public function clean($name = '')

Clean selected cache entry (alias).

    public function clean($name = '')
    {
        return $this->del($name);
    }

clean_all

public function clean_all()

Clean all cache entries (alias).

    public function clean_all()
    {
        return $this->flush();
    }

clear

public function clear($name = '')

Clean selected cache entry (alias).

    public function clear($name = '')
    {
        return $this->del($name);
    }

clear_all

public function clear_all()

Clean all cache entries (alias).

    public function clear_all()
    {
        return $this->flush();
    }

del

public function del($name)

Delete selected cache entry.

    public function del($name)
    {
        if ($name === false || $name === null) {
            return null;
        }
        $do_real_work = true;
        if ( ! $this->_driver_ok) {
            $do_real_work = false;
        }
        if (is_array($name)) {
            return $this->multi_del($name);
        }
        $time_start = null;
        if (DEBUG_MODE) {
            $time_start = microtime(true);
        }
        $key_name_ns = $this->CACHE_NS . $name;

        $result = null;
        if ($do_real_work) {
            $result = $this->_driver->del($key_name_ns);
        }

        DEBUG_MODE && debug('cache_' . __FUNCTION__ . '[]', [
            'name' => $name,
            'name_real' => $key_name_ns,
            'driver' => $this->DRIVER,
            'time' => round(microtime(true) - $time_start, 5),
            'did_real_work' => (int) $do_real_work,
        ]);
        return $result;
    }

del_by_prefix

public function del_by_prefix($prefix = '')
    public function del_by_prefix($prefix = '')
    {
        $do_real_work = true;
        if ( ! $this->_driver_ok) {
            $do_real_work = false;
        }
        $time_start = null;
        if (DEBUG_MODE) {
            $time_start = microtime(true);
        }
        $result = null;
        if ($do_real_work) {
            if ( ! strlen($prefix) || ! is_string($prefix)) {
                $result = $this->flush();
            } else {
                $result = false;
                $prefix_len = strlen($prefix);
                $keys = $this->list_keys();
                if ($keys === null) {
                    $result = $this->flush();
                } elseif ($keys) {
                    foreach ($keys as $k => $v) {
                        if (substr($v, 0, $prefix_len) !== $prefix) {
                            unset($keys[$k]);
                        }
                    }
                    if ($keys) {
                        $result = $this->multi_del($keys);
                    } else {
                        $result = true;
                    }
                }
            }
        }
        DEBUG_MODE && debug('cache_' . __FUNCTION__ . '[]', [
            'prefix' => $prefix,
            'data' => $result,
            'driver' => $this->DRIVER,
            'time' => microtime(true) - $time_start,
            'did_real_work' => (int) $do_real_work,
        ]);
        return $result;
    }

flush

public function flush()

Clean all cache entries.

    public function flush()
    {
        $do_real_work = true;
        if ( ! $this->_driver_ok) {
            $do_real_work = false;
        }
        $time_start = null;
        if (DEBUG_MODE) {
            $time_start = microtime(true);
        }

        $result = null;
        if ($do_real_work) {
            $result = $this->_driver->flush();
        }

        DEBUG_MODE && debug('cache_' . __FUNCTION__ . '[]', [
            'data' => $result,
            'driver' => $this->DRIVER,
            'time' => microtime(true) - $time_start,
            'did_real_work' => (int) $do_real_work,
        ]);
        return $result;
    }

get

public function get($name, $force_ttl = 0, array $params = [])

Get data from cache.

  • $params array
    public function get($name, $force_ttl = 0, array $params = [])
    {
        if ($name === false || $name === null || ! is_string($name)) {
            return null;
        }
        $do_real_work = true;
        if ( ! $this->_driver_ok || empty($name) || $this->NO_CACHE) {
            $do_real_work = false;
        }
        if ($this->FORCE_REBUILD_CACHE) {
            $this->del($name);
            $do_real_work = false;
        }
        $time_start = null;
        if (DEBUG_MODE) {
            $time_start = microtime(true);
        }
        $key_name_ns = $this->CACHE_NS . $name;

        $result = null;
        if ($do_real_work) {
            $result = $this->_driver->get($key_name_ns, $force_ttl, $params);
        }

        DEBUG_MODE && debug('cache_' . __FUNCTION__ . '[]', [
            'name' => $name,
            'name_real' => $key_name_ns,
            'data' => $result,
            'driver' => $this->DRIVER,
            'params' => $params,
            'force_ttl' => $force_ttl,
            'time' => round(microtime(true) - $time_start, 5),
            'trace' => main()->trace_string(),
            'did_real_work' => (int) $do_real_work,
        ]);
        if (($_GET['refresh_cache'] ?? false) && $this->_url_action_allowed('refresh_cache')) {
            return null;
        }
        return $result;
    }

getset

public function getset($name, callable $func, $ttl = 0, array $params = [])

Get data from cache, if data is empty - then execute callback and store result in cache.

  • $func callable
  • $params array
    public function getset($name, callable $func, $ttl = 0, array $params = [])
    {
        if ($name === false || $name === null || ! is_string($name)) {
            return null;
        }
        $result = null;
        if ( ! @$params['force']) {
            $result = $this->get($name, $ttl, $params);
        }
        if (($result ?? null) === null) {
            $result = $func($name, $ttl, $params, $this);
            if ($result !== null) {
                $this->set($name, $result, $ttl);
            }
        }
        return $result;
    }

list_keys

public function list_keys()
    public function list_keys()
    {
        $do_real_work = true;
        if ( ! $this->_driver_ok) {
            $do_real_work = false;
        }
        $time_start = null;
        if (DEBUG_MODE) {
            $time_start = microtime(true);
        }
        if ( ! ($this->_driver->implemented['list_keys'] ?? null)) {
            $do_real_work = false;
        }
        $result = null;
        if ($do_real_work) {
            $result = $this->_driver->list_keys();
            if ($this->CACHE_NS && is_array($result) && count((array) $result)) {
                $ns_len = strlen($this->CACHE_NS);
                foreach ($result as $k => $v) {
                    if (substr($v, 0, $ns_len) !== $this->CACHE_NS) {
                        unset($result[$k]);
                    } else {
                        $result[$k] = substr($v, $ns_len);
                    }
                }
            }
            if (is_array($result) && count((array) $result)) {
                asort($result);
                $result = array_values($result);
            } else {
                $result = null;
            }
        }
        DEBUG_MODE && debug('cache_' . __FUNCTION__ . '[]', [
            'data' => $result,
            'driver' => $this->DRIVER,
            'time' => microtime(true) - $time_start,
            'did_real_work' => (int) $do_real_work,
        ]);
        return $result;
    }

multi_del

public function multi_del($names = [])

Del several cache entries at once.

    public function multi_del($names = [])
    {
        $do_real_work = true;
        if ( ! $this->_driver_ok) {
            $do_real_work = false;
        }
        $time_start = null;
        if (DEBUG_MODE) {
            $time_start = microtime(true);
        }
        $result = null;
        if ($do_real_work) {
            $old_names = $names;
            $failed = false;
            $implemented = $this->_driver->implemented['multi_del'] ?? null;
            if ($implemented) {
                // Fix names prefix
                foreach ((array) $names as $k => $name) {
                    $names[$k] = $this->CACHE_NS . $name;
                }
                $result = $this->_driver->multi_del($names);
                if ( ! $result) {
                    $failed = true;
                }
            }
            if ( ! $implemented || $result === null) {
                $names = $old_names;
                foreach ((array) $names as $name) {
                    if ( ! $this->del($name)) {
                        $failed = true;
                    }
                }
            }
            $result = $failed ? null : true;
        }
        DEBUG_MODE && debug('cache_' . __FUNCTION__ . '[]', [
            'names' => $names,
            'data' => $result,
            'driver' => $this->DRIVER,
            'time' => microtime(true) - $time_start,
            'did_real_work' => (int) $do_real_work,
        ]);
        return $result;
    }

multi_get

public function multi_get($names = [], $force_ttl = 0, $params = [])

Get several cache entries at once.

    public function multi_get($names = [], $force_ttl = 0, $params = [])
    {
        $do_real_work = true;
        if ( ! $this->_driver_ok || $this->NO_CACHE) {
            $do_real_work = false;
        }
        $time_start = null;
        if (DEBUG_MODE) {
            $time_start = microtime(true);
        }
        if ( ! empty($this->_no_cache)) {
            foreach ((array) $names as $k => $name) {
                if (isset($this->_no_cache[$name])) {
                    unset($names[$k]);
                }
            }
        }
        $result = null;
        if ($do_real_work) {
            if ($this->_driver->implemented['multi_get'] ?? null) {
                // Fix names prefix
                $p_len = strlen($this->CACHE_NS);
                foreach ((array) $names as $k => $name) {
                    $names[$k] = $this->CACHE_NS . $name;
                }
                $result = $this->_driver->multi_get($names, $force_ttl, $params);
                // Fix names prefix
                foreach ((array) $result as $name => $val) {
                    $result[substr($name, $p_len)] = $val;
                    unset($result[$name]);
                }
            } else {
                $result = [];
                foreach ((array) $names as $name) {
                    $res = $this->get($name, $force_ttl, $params);
                    if (isset($res)) {
                        $result[$name] = $res;
                    }
                }
            }
            if ( ! is_array($result) || ! count((array) $result)) {
                $result = null;
            }
        }
        DEBUG_MODE && debug('cache_' . __FUNCTION__ . '[]', [
            'names' => $names,
            'data' => $result,
            'driver' => $this->DRIVER,
            'time' => microtime(true) - $time_start,
            'did_real_work' => (int) $do_real_work,
        ]);
        return $result;
    }

multi_set

public function multi_set($data = [], $ttl = 0)

Set several cache entries at once.

    public function multi_set($data = [], $ttl = 0)
    {
        $do_real_work = true;
        if ( ! $this->_driver_ok || $this->NO_CACHE) {
            $do_real_work = false;
        }
        $time_start = null;
        if (DEBUG_MODE) {
            $time_start = microtime(true);
        }
        if ( ! empty($this->_no_cache)) {
            foreach ((array) $this->_no_cache as $name => $tmp) {
                if (isset($data[$name])) {
                    unset($data[$name]);
                }
            }
        }
        $result = null;
        if ($do_real_work) {
            if ($this->_driver->implemented['multi_set'] ?? null) {
                // Fix names prefix
                foreach ((array) $data as $name => $_data) {
                    $data[$this->CACHE_NS . $name] = $_data;
                    unset($data[$name]);
                }
                $result = $this->_driver->multi_set($data, $ttl);
            } else {
                $failed = false;
                foreach ((array) $data as $name => $_data) {
                    if ( ! $this->set($name, $_data, $ttl)) {
                        $failed = true;
                    }
                }
                $result = ! $failed;
            }
            if ( ! $result) {
                $result = null;
            }
        }
        DEBUG_MODE && debug('cache_' . __FUNCTION__ . '[]', [
            'data' => $data,
            'driver' => $this->DRIVER,
            'time' => microtime(true) - $time_start,
            'did_real_work' => (int) $do_real_work,
        ]);
        return $result;
    }

put

public function put($name = '', $data = null, $ttl = 0)

Put data into cache (alias for 'set').

    public function put($name = '', $data = null, $ttl = 0)
    {
        return $this->set($name, $data, $ttl);
    }

refresh

public function refresh($name = '')

Delete selected cache entry (alias).

    public function refresh($name = '')
    {
        return $this->del($name);
    }

refresh_all

public function refresh_all()

Clean all cache entries (alias).

    public function refresh_all()
    {
        return $this->flush();
    }

set

public function set($name, $data, $ttl = 0)

Set data into cache.

    public function set($name, $data, $ttl = 0)
    {
        if ($name === false || $name === null) {
            return null;
        }
        $do_real_work = true;
        if ( ! $this->_driver_ok || $this->NO_CACHE || ( ! is_array($name) && ($this->_no_cache[$name] ?? null))) {
            $do_real_work = false;
        }
        if (is_array($name)) {
            return $this->multi_set($name, $data);
        }
        $time_start = null;
        if (DEBUG_MODE) {
            $time_start = microtime(true);
        }
        $ttl = (int) ($ttl ?: $this->TTL);
        if ($this->RANDOM_TTL_ADD) {
            $ttl += mt_rand(1, 15);
        }
        $key_name_ns = $this->CACHE_NS . $name;

        $result = null;
        if ($do_real_work) {
            $result = $this->_driver->set($key_name_ns, $data, $ttl);
        }

        DEBUG_MODE && debug('cache_' . __FUNCTION__ . '[]', [
            'name' => $name,
            'name_real' => $key_name_ns,
            'data' => $data,
            'driver' => $this->DRIVER,
            'ttl' => $ttl,
            'time' => round(microtime(true) - $time_start, 5),
            'trace' => main()->trace_string(),
            'did_real_work' => (int) $do_real_work,
        ]);
        return $result;
    }

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)
    {
        $self = main()->get_class_name($this);
        $func = null;
        if (isset($this->_extend[$name])) {
            $func = $this->_extend[$name];
        } elseif (isset(main()->_extend[$self][$name])) {
            $func = main()->_extend[$self][$name];
        }
        if ($func) {
            return $func($args[0], $args[1], $args[2], $args[3], $this);
        }
        // Support for driver-specific methods
        if (is_object($this->_driver)) {
            return call_user_func_array([$this->_driver, $name], $args);
        }
        trigger_error(__CLASS__ . ': No method ' . $name, E_USER_WARNING);
        return false;
    }

__clone

public function __clone()
    public function __clone()
    {
        // Reset internal state to class defaults (unset() would turn them into dynamic properties)
        $defaults = get_class_vars(get_class($this));
        foreach ((array) get_object_vars($this) as $k => $v) {
            if ($k[0] == '_') {
                $this->$k = null; // not unset: the clone reads _init_complete etc before _init() runs
            }
        }
    }
public function _clear_cache_files()

Clears all cache entries (alias).

    public function _clear_cache_files()
    {
        return $this->flush();
    }

_connect

public function _connect($params = [])
    public function _connect($params = [])
    {
        if ( ! $this->DRIVER) {
            return null;
        }
        if (isset($this->_tried_to_connect)) {
            return $this->_driver;
        }
        $this->_driver = null;
        $this->_driver_ok = false;
        $driver = $this->_set_current_driver($params);
        if ($driver) {
            $this->_driver = _class('cache_driver_' . $driver, 'classes/cache/');
            $this->_driver_ok = $this->_driver->is_ready();
            $implemented = [];
            foreach (get_class_methods($this->_driver) as $method) {
                if ($method[0] != '_') {
                    $implemented[$method] = $method;
                }
            }
            $this->_driver->implemented = $implemented;
            $this->_driver->_parent = $this;
        } else {
            trigger_error('CACHE: empty driver name, will not use cache', E_USER_WARNING);
        }
        $this->_tried_to_connect = true;
        return $this->_driver;
    }
public function _get_avail_drivers_list()
    public function _get_avail_drivers_list()
    {
        $prefix = 'cache_driver_';
        $suffix = '.class.php';
        $plen = strlen($prefix);
        $slen = strlen($suffix);
        $patterns = [
            'app' => [
                APP_PATH . 'classes/cache/*' . $prefix . '*' . $suffix,
                APP_PATH . 'plugins/*/classes/cache/*' . $prefix . '*' . $suffix,
            ],
            'framework' => [
                YF_PATH . 'classes/cache/*' . $prefix . '*' . $suffix,
                YF_PATH . 'plugins/*/classes/cache/*' . $prefix . '*' . $suffix,
            ],
        ];

        $drivers = [];
        foreach ($patterns as $paths) {
            foreach ($paths as $path) {
                foreach (glob($path) as $f) {
                    $f = basename($f);
                    $name = substr($f, strpos($f, $prefix) + $plen, -$slen);
                    if ($name) {
                        $drivers[$name] = $name;
                    }
                }
            }
        }
        return $drivers;
    }

_init

public function _init($params = [])

Framework constructor.

    public function _init($params = [])
    {
        if ($this->_init_complete) {
            return true;
        }
        $this->_init_settings();
        $this->_connect($params);
        $this->_init_complete = true;
    }
public function _init_settings($params = [])
    public function _init_settings($params = [])
    {
        // backwards compatibility
        if ($this->FILES_TTL) {
            $this->TTL = $this->FILES_TTL;
        }
        $conf_cache_ns = conf('CACHE_NS');
        // Cache namespace need to be unique, especially when using memcached shared between several projects
        if ( ! $conf_cache_ns && ! $this->CACHE_NS && $this->AUTO_CACHE_NS) {
            $this->CACHE_NS = substr(md5(PROJECT_PATH), 0, 8) . '_';
        }
        if ($conf_cache_ns) {
            $this->CACHE_NS = $conf_cache_ns;
        }
        $conf_no_cache = conf('NO_CACHE');
        // backwards compatibility
        if (defined('USE_CACHE')) {
            if ( ! USE_CACHE) {
                $this->NO_CACHE = true;
                $this->_NO_CACHE_WHY = 'const NO_CACHE defined and false';
            }
        } elseif ($conf_no_cache !== null && $conf_no_cache) {
            $this->NO_CACHE = true;
            $this->_NO_CACHE_WHY = 'conf(NO_CACHE) is true';
        } elseif ( ! main()->USE_SYSTEM_CACHE) {
            $this->NO_CACHE = true;
            $this->_NO_CACHE_WHY = 'main()->USE_SYSTEM_CACHE == false';
        }
        if ((($_GET['no_core_cache'] ?? false) || ($_GET['no_cache'] ?? false)) && $this->_url_action_allowed('no_cache')) {
            $this->NO_CACHE = true;
            $this->_NO_CACHE_WHY = '$_GET param no_cache';
        }
        if ($this->NO_CACHE && ! $this->_NO_CACHE_WHY) {
            $this->_NO_CACHE_WHY = 'cache()->NO_CACHE == true';
        }
        if ((($_GET['refresh_cache'] ?? false) || ($_GET['rebuild_core_cache'] ?? false)) && $this->_url_action_allowed('refresh_cache')) {
            $this->FORCE_REBUILD_CACHE = true;
        }
        $this->FORCE_REBUILD_CACHE = false;
    }
public function _set_current_driver($params = [])
    public function _set_current_driver($params = [])
    {
        $avail_drivers = $this->_get_avail_drivers_list();
        $driver = '';
        $want = isset($params['driver']) ? $params['driver'] : $this->DRIVER;
        if ( ! $want || $want == 'auto') {
            $want = 'memcache';
        }
        if (isset($this->REMOVED_DRIVERS[$want])) {
            trigger_error('CACHE: driver "' . $want . '" ' . $this->REMOVED_DRIVERS[$want], E_USER_WARNING);
        }
        if (isset($avail_drivers[$want])) {
            $driver = $want;
        }
        $this->DRIVER = $driver;
        return $driver;
    }
public function _url_action_allowed($action = '')

Callback that can be overriden to ensure security when allowing url params like no_cache, refresh_cache We can add DEBUG_MODE checking here to not allow refresh_cache attacks, maybe add check for: conf('cache_refresh_token', 'something_random'), main()->CACHE_CONTROL_FROM_URL.

    public function _url_action_allowed($action = '')
    {
        $actions = ['no_cache', 'refresh_cache'];
        $callback = conf('cache_url_action_allowed');
        if (is_callable($callback)) {
            return (bool) $callback($action);
        }
        return true;
    }