/home/arranoyd/energyclinic.com.hr/wp-content/plugins/docket-cache/includes/cache.php
<?php
/**
 * Docket Cache.
 *
 * @author  Nawawi Jamili
 * @license MIT
 *
 * @see    https://github.com/nawawi/docket-cache
 */
\defined('ABSPATH') || exit;

/*
 * Reference:
 *  wp-includes/class-wp-object-cache.php
 *  wp-includes/cache.php
 */

/**
 * Core class that implements an object cache.
 */
#[AllowDynamicProperties]
class WP_Object_Cache
{
    /**
     * Holds the cached objects.
     *
     * @var array
     */
    private $cache = [];

    /**
     * The amount of times the cache data was already stored in the cache.
     *
     * @var int
     */
    public $cache_hits = 0;

    /**
     * Amount of times the cache did not have the request in cache.
     *
     * @var int
     */
    public $cache_misses = 0;

    /**
     * The amount of times the cached data was fetched from the cache file.
     *
     * @var int
     */
    public $persistent_cache_hits = 0;

    /**
     * List of global cache groups.
     *
     * @var array
     */
    protected $global_groups = [];

    /**
     * List of non persistent groups.
     *
     * @var array
     */
    protected $non_persistent_groups = [];

    /**
     * List of non persistent keys.
     *
     * @var array
     */
    protected $non_persistent_keys = [];

    /**
     * List of non persistent group:key.
     *
     * @var array
     */
    protected $non_persistent_groupkey = [];

    /**
     * List of group:key exclude from pecaching.
     *
     * @var array
     */
    private $bypass_precache = [];

    /**
     * The blog prefix to prepend to keys in non-global groups.
     *
     * @var string
     */
    private $blog_prefix;

    /**
     * Holds the value of is_multisite().
     *
     * @var bool
     */
    private $multisite;

    /**
     * Holds the value of Network Id.
     *
     * @var bool
     */
    private $network_id = 1;

    /**
     * The cache path.
     *
     * @var string
     */
    private $cache_path;

    /**
     * The cache maximum size of cache file.
     *
     * @var int
     */
    private $cache_maxsize = 3145728;

    /**
     * The cache file lifespan.
     *
     * @var int
     */
    private $cache_maxttl = 345600;

    /**
     * List of filtered groups.
     *
     * @var array
     */
    private $filtered_groups = false;

    /**
     * Show signature.
     *
     * @var bool
     */
    private $add_signature;

    /**
     * List of caches to preload.
     *
     * @var array
     */
    public $precache = [];

    /**
     * List of loaded keys.
     *
     * @var array
     */
    public $precache_loaded = [];

    /**
     * Precache status.
     *
     * @var bool
     */
    private $is_precache = false;

    /**
     * Precache key.
     *
     * @var string
     */
    private $precache_hashkey = '';

    /**
     * Precache max keys.
     *
     * @var int
     */
    private $precache_maxkey = 20;

    /**
     * Precache max groups.
     *
     * @var int
     */
    private $precache_maxgroup = 20;

    /**
     * Precache max cache file (url).
     *
     * @var int
     */
    private $precache_maxfile = 100;

    /**
     * Maximum cache file.
     *
     * @var int
     */
    private $maxfile = 50000;

    /**
     * Check cache file limit in real-time.
     *
     * @var bool
     */
    private $maxfile_livecheck = true;

    /**
     * The maximum time in seconds a script is allowed to run.
     *
     * @var int
     */
    private $max_execution_time = 0;

    /**
     * Start of run timestamp.
     *
     * @var int
     */
    private $wp_start_timestamp = 0;

    /**
     * Dev mode.
     *
     * @var bool
     */
    private $is_dev = false;

    /**
     * Ignore stale cache.
     *
     * @var bool
     */
    private $ignore_stalecache = false;

    /**
     * Ignore empty cache.
     *
     * @var bool
     */
    private $ignore_emptycache = false;

    /**
     * Store Transients in DB.
     *
     * @var bool
     */
    private $use_transientdb = false;

    /**
     * List of transient exclude from store in DB.
     *
     * @var array
     */
    private $bypass_transientdb = [];

    /**
     * Sets up object properties.
     */
    public function __construct()
    {
        $this->multisite = \function_exists('is_multisite') && is_multisite();
        $this->blog_prefix = $this->switch_to_blog(get_current_blog_id());
        $this->network_id = (int) nwdcx_network_id();
        $this->dc_init();
    }

    /**
     * Makes private properties readable for backward compatibility.
     *
     * @since 4.0.0
     *
     * @param string $name property to get
     *
     * @return mixed property
     */
    public function __get($name)
    {
        return $this->$name;
    }

    /**
     * Makes private properties settable for backward compatibility.
     *
     * @since 4.0.0
     *
     * @param string $name  property to set
     * @param mixed  $value property value
     *
     * @return mixed newly-set property
     */
    public function __set($name, $value)
    {
        return $this->$name = $value;
    }

    /**
     * Makes private properties checkable for backward compatibility.
     *
     * @since 4.0.0
     *
     * @param string $name property to check if set
     *
     * @return bool whether the property is set
     */
    public function __isset($name)
    {
        return isset($this->$name);
    }

    /**
     * Makes private properties un-settable for backward compatibility.
     *
     * @since 4.0.0
     *
     * @param string $name property to unset
     */
    public function __unset($name)
    {
        unset($this->$name);
    }

    /**
     * Serves as a utility function to determine whether a key exists in the cache.
     *
     * @param int|string $key   cache key to check for existence
     * @param string     $group cache group for the key existence check
     *
     * @return bool whether the key exists in the cache for the given group
     */
    protected function _exists($key, $group, $force = false)
    {
        // check key
        if (!$this->is_valid_key($key)) {
            return false;
        }

        // check group
        if (!\is_string($group)) {
            // unset junk
            unset($this->cache[$group]);
            unset($this->precache[$group]);

            return false;
        }

        $is_exists = !$force && !empty($this->cache) && isset($this->cache[$group]) && (isset($this->cache[$group][$key]) || \array_key_exists($key, $this->cache[$group]));

        if (!$is_exists && !$this->is_non_persistent_groups($group) && !$this->is_non_persistent_keys($key) && !$this->is_non_persistent_groupkey($group, $key) && !$this->is_stalecache_ignored($key, $group)) {
            $data = $this->dc_get($key, $group, false, $codestub_false);
            if (false !== $data) {
                $is_exists = true;
                $this->cache[$group][$key] = $data;

                if ($this->is_precache && !$codestub_false && !$this->fs()->suspend_cache_write() && !$this->is_bypass_precache($group, $key)) {
                    if (empty($this->precache[$group])) {
                        $this->precache[$group][$key] = 1;
                    } elseif (\count($this->precache[$group]) < $this->precache_maxkey && \count($this->precache) < $this->precache_maxgroup) {
                        $this->precache[$group][$key] = 1;
                    }
                }
            }
        }

        return $is_exists;
    }

    /**
     * Adds data to the cache if it doesn't already exist.
     *
     * @uses WP_Object_Cache::_exists() Checks to see if the cache already has data.
     * @uses WP_Object_Cache::set()     Sets the data after the checking the cache
     *                                  contents existence.
     *
     * @param int|string $key    what to call the contents in the cache
     * @param mixed      $data   the contents to store in the cache
     * @param string     $group  Optional. Where to group the cache contents. Default 'default'.
     * @param int        $expire Optional. When to expire the cache contents. Default 0 (no expiration).
     *
     * @return bool true on success, false if cache key and group already exist
     */
    public function add($key, $data, $group = 'default', $expire = 0)
    {
        if (wp_suspend_cache_addition()) {
            return false;
        }

        if (!$this->is_valid_key($key)) {
            return false;
        }

        if (empty($group)) {
            $group = 'default';
        }

        $cache_key = $this->dc_key($key, $group);
        if ($this->_exists($cache_key, $group)) {
            return false;
        }

        return $this->set($key, $data, $group, (int) $expire);
    }

    /**
     * Adds multiple values to the cache in one call.
     *
     * @param array  $data   array of keys and values to be added
     * @param string $group  Optional. Where the cache contents are grouped. Default empty.
     * @param int    $expire Optional. When to expire the cache contents, in seconds.
     *                       Default 0 (no expiration).
     *
     * @return bool[] Array of return values, grouped by key. Each value is either
     *                true on success, or false if cache key and group already exist.
     */
    public function add_multiple(array $data, $group = '', $expire = 0)
    {
        $values = [];

        foreach ($data as $key => $value) {
            $values[$key] = $this->add($key, $value, $group, $expire);
        }

        return $values;
    }

    /**
     * Replaces the contents in the cache, if contents already exist.
     *
     * @see WP_Object_Cache::set()
     *
     * @param int|string $key    what to call the contents in the cache
     * @param mixed      $data   the contents to store in the cache
     * @param string     $group  Optional. Where to group the cache contents. Default 'default'.
     * @param int        $expire Optional. When to expire the cache contents. Default 0 (no expiration).
     *
     * @return bool false if not exists, true if contents were replaced
     */
    public function replace($key, $data, $group = 'default', $expire = 0)
    {
        if (!$this->is_valid_key($key)) {
            return false;
        }

        if (empty($group)) {
            $group = 'default';
        }

        $cache_key = $this->dc_key($key, $group);

        if (!$this->_exists($cache_key, $group)) {
            return false;
        }

        return $this->set($key, $data, $group, (int) $expire);
    }

    /**
     * Sets the data contents into the cache.
     *
     * The cache contents are grouped by the $group parameter followed by the
     * $key. This allows for duplicate ids in unique groups. Therefore, naming of
     * the group should be used with care and should follow normal function
     * naming guidelines outside of core WordPress usage.
     *
     * @param int|string $key    what to call the contents in the cache
     * @param mixed      $data   the contents to store in the cache
     * @param string     $group  Optional. Where to group the cache contents. Default 'default'.
     * @param int        $expire the expiration time, defaults to 0
     *
     * @return true always returns true
     */
    public function set($key, $data, $group = 'default', $expire = 0)
    {
        if (!$this->is_valid_key($key)) {
            return false;
        }

        if (empty($group)) {
            $group = 'default';
        }

        $cache_key = $this->dc_key($key, $group);

        if (\is_object($data)) {
            $data = clone $data;
        }

        $this->cache[$group][$cache_key] = $data;

        // suspend new cache
        if ($this->fs()->suspend_cache_write() && !is_file($this->get_file_path($cache_key, $group))) {
            return true;
        }

        if ((!$this->is_non_persistent_groups($group) && !$this->is_non_persistent_keys($key) && !$this->is_non_persistent_groupkey($group, $key) && !$this->is_stalecache_ignored($key, $group)) || $this->is_filtered_groups($group, $key)) {
            $expire = $this->maybe_expire($group, $expire, $key);
            $this->dc_save($cache_key, $this->cache[$group][$cache_key], $group, $expire, $key);
        }

        return true;
    }

    /**
     * Sets multiple values to the cache in one call.
     *
     * @param array  $data   array of key and value to be set
     * @param string $group  Optional. Where the cache contents are grouped. Default empty.
     * @param int    $expire Optional. When to expire the cache contents, in seconds.
     *                       Default 0 (no expiration).
     *
     * @return bool[] Array of return values, grouped by key. Each value is always true.
     */
    public function set_multiple(array $data, $group = '', $expire = 0)
    {
        $values = [];

        foreach ($data as $key => $value) {
            $values[$key] = $this->set($key, $value, $group, $expire);
        }

        return $values;
    }

    /**
     * Retrieves the cache contents, if it exists.
     *
     * The contents will be first attempted to be retrieved by searching by the
     * key in the cache group. If the cache is hit (success) then the contents
     * are returned.
     *
     * On failure, the number of cache misses will be incremented.
     *
     * @param int|string $key   what the contents in the cache are called
     * @param string     $group Optional. Where the cache contents are grouped. Default 'default'.
     * @param bool       $force Optional. Unused. Whether to force a refetch rather than relying on the local
     *                          cache. Default false.
     * @param bool       $found Optional. Whether the key was found in the cache (passed by reference).
     *                          Disambiguates a return of false, a storable value. Default null.
     *
     * @return mixed|false the cache contents on success, false on failure to retrieve contents
     */
    public function get($key, $group = 'default', $force = false, &$found = null, $doing_precache = false)
    {
        if (!$this->is_valid_key($key)) {
            return false;
        }

        if (empty($group)) {
            $group = 'default';
        }

        $cache_key = $this->dc_key($key, $group);

        if ($this->_exists($cache_key, $group, $force)) {
            if (!$doing_precache) {
                ++$this->cache_hits;
            }
            $found = true;
            if (\is_object($this->cache[$group][$cache_key])) {
                return clone $this->cache[$group][$cache_key];
            }

            return $this->cache[$group][$cache_key];
        }

        $found = false;
        if (!$doing_precache) {
            ++$this->cache_misses;
        }

        return false;
    }

    /**
     * Retrieves multiple values from the cache in one call.
     *
     * @param array  $keys  array of keys under which the cache contents are stored
     * @param string $group Optional. Where the cache contents are grouped. Default 'default'.
     * @param bool   $force Optional. Whether to force an update of the local cache
     *                      from the persistent cache. Default false.
     *
     * @return array array of values organized into groups
     */
    public function get_multiple($keys, $group = 'default', $force = false)
    {
        $values = [];
        if (!empty($keys) && \is_array($keys)) {
            foreach ($keys as $key) {
                $values[$key] = $this->get($key, $group, $force);
            }
        }

        return $values;
    }

    /**
     * Removes the contents of the cache key in the group.
     *
     * If the cache key does not exist in the group, then nothing will happen.
     *
     * @param int|string $key        what the contents in the cache are called
     * @param string     $group      Optional. Where the cache contents are grouped. Default 'default'.
     * @param bool       $deprecated Optional. Unused. Default false.
     *
     * @return bool false if the contents weren't deleted and true on success
     */
    public function delete($key, $group = 'default', $deprecated = false)
    {
        if (!$this->is_valid_key($key)) {
            return false;
        }

        if (empty($group)) {
            $group = 'default';
        }

        $key = $this->dc_key($key, $group);

        unset($this->cache[$group][$key]);
        unset($this->precache[$group][$key]);

        return $this->dc_remove($key, $group);
    }

    /**
     * Deletes multiple values from the cache in one call.
     *
     * @param array  $keys  array of keys to be deleted
     * @param string $group Optional. Where the cache contents are grouped. Default empty.
     *
     * @return bool[] Array of return values, grouped by key. Each value is either
     *                true on success, or false if the contents were not deleted.
     */
    public function delete_multiple(array $keys, $group = '')
    {
        $values = [];

        foreach ($keys as $key) {
            $values[$key] = $this->delete($key, $group);
        }

        return $values;
    }

    /**
     * Increments numeric cache item's value.
     *
     * @param int|string $key    The cache key to increment
     * @param int        $offset Optional. The amount by which to increment the item's value. Default 1.
     * @param string     $group  Optional. The group the key is in. Default 'default'.
     *
     * @return int|false the item's new value on success, false on failure
     */
    public function incr($key, $offset = 1, $group = 'default')
    {
        if (!$this->is_valid_key($key)) {
            return false;
        }

        if (empty($group)) {
            $group = 'default';
        }

        $cache_key = $this->dc_key($key, $group);

        if (!$this->_exists($cache_key, $group)) {
            return false;
        }

        if (!is_numeric($this->cache[$group][$cache_key])) {
            $this->cache[$group][$cache_key] = 0;
        }

        $offset = (int) $offset;

        $this->cache[$group][$cache_key] += $offset;

        if ($this->cache[$group][$cache_key] < 0) {
            $this->cache[$group][$cache_key] = 0;
        }

        $this->dc_update($cache_key, $this->cache[$group][$cache_key], $group);

        return $this->cache[$group][$cache_key];
    }

    /**
     * Decrements numeric cache item's value.
     *
     * @param int|string $key    the cache key to decrement
     * @param int        $offset Optional. The amount by which to decrement the item's value. Default 1.
     * @param string     $group  Optional. The group the key is in. Default 'default'.
     *
     * @return int|false the item's new value on success, false on failure
     */
    public function decr($key, $offset = 1, $group = 'default')
    {
        if (!$this->is_valid_key($key)) {
            return false;
        }

        if (empty($group)) {
            $group = 'default';
        }

        $cache_key = $this->dc_key($key, $group);

        if (!$this->_exists($cache_key, $group)) {
            return false;
        }

        if (!is_numeric($this->cache[$group][$cache_key])) {
            $this->cache[$group][$cache_key] = 0;
        }

        $offset = (int) $offset;

        $this->cache[$group][$cache_key] -= $offset;

        if ($this->cache[$group][$cache_key] < 0) {
            $this->cache[$group][$cache_key] = 0;
        }

        $this->dc_update($cache_key, $this->cache[$group][$cache_key], $group);

        return $this->cache[$group][$cache_key];
    }

    /**
     * Clears the object cache of all data.
     *
     * @param bool $is_runtime Optional. Only removes cache items from the in-memory runtime cache.
     *
     * @return bool true on success, false on failure
     */
    public function flush($is_runtime = false)
    {
        $this->cache = [];
        $this->precache = [];
        $this->precache_loaded = [];

        return $is_runtime ? true : $this->dc_flush();
    }

    /**
     * Sets the list of global cache groups.
     *
     * @param array $groups list of groups that are global
     */
    public function add_global_groups($groups)
    {
        $groups = (array) $groups;

        $groups = array_fill_keys($groups, true);
        $this->global_groups = array_merge($this->global_groups, $groups);
    }

    /**
     * Switches the internal blog ID.
     *
     * This changes the blog ID used to create keys in blog specific groups.
     *
     * @param int $blog_id blog ID
     */
    public function switch_to_blog($blog_id)
    {
        $blog_id = (int) $blog_id;
        $this->blog_prefix = $this->multisite ? $blog_id.':' : '';
    }

    /**
     * Echoes the stats of the caching.
     *
     * Gives the cache hits, and cache misses. Also prints every cached group,
     * key and the data.
     */
    public function stats()
    {
        $ret = '';
        $ret .= '<p>';
        $ret .= "<strong>Cache Hits:</strong> {$this->cache_hits}<br />";
        $ret .= "<strong>Persistent Cache Hits:</strong> {$this->persistent_cache_hits}<br />";
        $ret .= "<strong>Cache Misses:</strong> {$this->cache_misses}<br />";
        $ret .= '</p>';
        $ret .= '<ul>';
        $total = 0;

        foreach ($this->cache as $group => $cache) {
            $ret .= '<li><strong>Group:</strong> '.esc_html($group).' - ( '.number_format(\strlen(serialize($cache)) / KB_IN_BYTES, 2).'K )</li>';
            $total += \strlen(serialize($cache));
        }

        $ret .= '</ul>';
        $ret .= '<p>total: '.number_format($total / KB_IN_BYTES).'</p>';
        echo $ret;
    }

    /**
     * Sets the list of non persistent groups.
     *
     * @param array $groups list of groups that are to be ignored
     */
    public function add_non_persistent_groups($groups)
    {
        $groups = (array) $groups;

        $groups = array_fill_keys($groups, true);
        $this->non_persistent_groups = array_merge($this->non_persistent_groups, $groups);
    }

    /**
     * Check if group in non persistent groups.
     *
     * @param bool $group cache group
     */
    protected function is_non_persistent_groups($group)
    {
        return !empty($this->non_persistent_groups) && \array_key_exists($group, $this->non_persistent_groups);
    }

    /**
     * Sets the list of non persistent keys.
     *
     * @param array $keys list of keys that are to be ignored
     */
    public function add_non_persistent_keys($keys)
    {
        $keys = (array) $keys;

        $keys = array_fill_keys($keys, true);
        $this->non_persistent_keys = array_merge($this->non_persistent_keys, $keys);
    }

    /**
     * Check if key in non persistent keys.
     *
     * @param bool $key cache key
     */
    protected function is_non_persistent_keys($key)
    {
        return !empty($this->non_persistent_keys) && \array_key_exists($key, $this->non_persistent_keys);
    }

    /**
     * Check if key in non persistent index.
     *
     * @param bool $group cache group
     * @param bool $key   cache key
     */
    protected function is_non_persistent_groupkey($group, $key)
    {
        if (!empty($this->non_persistent_groupkey) && !empty($this->non_persistent_groupkey[$group])) {
            $bypass_data = $this->non_persistent_groupkey[$group];

            if (\is_array($bypass_data)) {
                return false !== array_search($key, $bypass_data);
            }

            if ($key === $bypass_data) {
                return true;
            }
        }

        return false;
    }

    /**
     * Bypass preache.
     *
     * @param bool $group cache group
     * @param bool $key   cache key
     */
    private function is_bypass_precache($group, $key)
    {
        if ($this->fs()->is_docketcachegroup($group, $key) || $this->fs()->is_transient($group) || $this->has_stalecache($key, $group)) {
            return true;
        }

        if ('gbmedia-cpttables' === $group || 'doing_cron' === $key || preg_match('@^(\d+|[0-9a-f]{32})$@', $key)) {
            return true;
        }

        if (!empty($this->bypass_precache) && !empty($this->bypass_precache[$group])) {
            $bypass_data = $this->bypass_precache[$group];

            if (\is_array($bypass_data)) {
                return false !== array_search($key, $bypass_data);
            }

            if ($key === $bypass_data) {
                return true;
            }
        }

        return false;
    }

    /**
     * is_valid_key.
     */
    private function is_valid_key($key)
    {
        if (\is_int($key)) {
            return true;
        }

        if (\is_string($key) && '' !== trim($key)) {
            return true;
        }

        if (!\function_exists('__') && \function_exists('wp_load_translations_early')) {
            wp_load_translations_early();
        }

        return false;
    }

    /**
     * is_user_logged_in.
     */
    private function is_user_logged_in()
    {
        return \function_exists('is_user_logged_in') && is_user_logged_in();
    }

    /**
     * is_filtered_groups.
     */
    protected function is_filtered_groups($group, $key)
    {
        if (!\is_array($this->filtered_groups) || !isset($this->filtered_groups[$group])) {
            return false;
        }

        if (false === $this->filtered_groups[$group]) {
            $this->filtered_groups[$group][] = $key;
            $this->filtered_groups[$group] = array_unique($this->filtered_groups[$group]);

            return true;
        }

        if (\in_array($key, $this->filtered_groups[$group])) {
            return true;
        }

        return false;
    }

    /**
     * flush_filtered_groups.
     */
    private function flush_filtered_groups($hook, $args)
    {
        if (!\is_array($this->filtered_groups)) {
            return false;
        }

        foreach ($this->filtered_groups as $group => $keys) {
            if (empty($keys) || !\is_array($keys)) {
                continue;
            }

            $keys = array_unique($keys);
            foreach ($keys as $key) {
                $this->delete($key, $group);
                $this->dc_log('flush', '000000000000-'.$this->item_hash(__FUNCTION__), $group.':'.$key);
            }
        }

        return true;
    }

    /**
     * maybe_expire.
     */
    private function maybe_expire($group, $expire = 0, $key = '')
    {
        if (empty($expire)) {
            $expire = 0;
        }

        $expire = $this->fs()->sanitize_timestamp($expire);
        $maxttl = $this->cache_maxttl;

        if (0 === $expire && $maxttl < 2419200) {
            if ($this->fs()->is_transient($group)) {
                if ('site-transient' === $group && \in_array($key, ['update_plugins', 'update_themes', 'update_core', '_woocommerce_helper_updates'])) {
                    $expire = $maxttl < 2419200 ? 2419200 : $maxttl; // 28d
                } else {
                    // $expire = $maxttl < 604800 ? 604800 : $maxttl; // 7d
                    $expire = $maxttl < 86400 ? $maxttl : 86400; // 1d
                }
            } elseif ($this->fs()->is_wp_options($group)) {
                $expire = $maxttl < 1209600 ? 1209600 : $maxttl; // 14d
            } elseif (\in_array($group, ['terms', 'posts', 'post_meta', 'comments', 'comment_feed', 'sites', 'networks'])) {
                $expire = $maxttl < 1209600 ? 1209600 : $maxttl; // 14d
            }

            // wp stale cache
            // group-queries: wp >= 6.3
            elseif ($this->fs()->is_wp_cache_group_queries($group)) {
                $expire = $maxttl < 86400 ? $maxttl : 86400; // 1d
            }

            // wp stale cache
            // prefix:md5hash:microtime
            // wp_query|get_terms|get_comments|comment_feed|get_sites|get_network_ids|get_page_by_path|other?
            elseif (false !== strpos($key, ':') && @preg_match('@^([a-zA-Z0-9\._-]+):([0-9a-f]{32}):([0-9\. ]+)$@', $key)) {
                $expire = $maxttl < 86400 ? $maxttl : 86400; // 1d
            }

            // wp stale cache
            // cache timestamp
            elseif ('last_changed' === $key) {
                $expire = $maxttl < 2419200 ? 2419200 : $maxttl; // 28d
            }

            // advcpost
            // docketcache-post-(found|media|timestamp)
            elseif (false !== strpos($group, 'docketcache-post-')) {
                $expire = $maxttl < 86400 ? $maxttl : 86400; // 1d
            }

            // advcpost
            // docketcache-post-media
            elseif ('docketcache-post-media' === $group) {
                $expire = $maxttl < 2419200 ? 2419200 : $maxttl; // 28d
            }

            // advcpost
            // cache timestamp
            elseif ('docketcache-post' === $group && 'cache_incr' === $key) {
                $expire = $maxttl < 2419200 ? 2419200 : $maxttl; // 28d
            }

            // woocommerce stale cache
            // cache prefix
            elseif (false !== strpos($key, '_cache_prefix') && @preg_match('@^wc_(.*?)_cache_prefix$@', $key)) {
                $expire = $maxttl < 2419200 ? 2419200 : $maxttl; // 28d
            }

            // woocommerce stale cache
            // wc_cache_0.72953700 1651592702
            elseif (false !== strpos($key, 'wc_cache_') && @preg_match('@^wc_cache_([0-9\. ]+)_@', $key)) {
                $expire = $maxttl < 86400 ? $maxttl : 86400; // 1d
            } elseif (false !== strpos($group, 'wc_cache_') && @preg_match('@^wc_cache_([0-9\. ]+)_@', $group)) {
                $expire = $maxttl < 86400 ? $maxttl : 86400; // 1d
            }

            // common cache
            elseif (preg_match('@[0-9a-f]{32}@', $key)) {
                $expire = $maxttl < 86400 ? $maxttl : 86400; // 1d
            }

            // else
            else {
                $expire = $maxttl;
            }
        }

        return $expire;
    }

    /**
     * get_item_hash.
     */
    private function get_item_hash($file)
    {
        return basename($file, '.php');
    }

    /**
     * item_hash.
     */
    private function item_hash($str, $length = 12)
    {
        if (!$this->is_valid_key($str)) {
            $str = serialize($str);
        }

        if (empty($length)) {
            return md5($str);
        }

        return substr(md5($str), 0, $length);
    }

    /**
     * get_file_path.
     */
    private function get_file_path($key, $group)
    {
        $hash_group = $this->item_hash($group);
        $hash_key = $this->item_hash($key);

        $index = $hash_group.'-'.$hash_key;

        if ($this->cf()->is_dcfalse('CHUNKCACHEDIR', true)) {
            return $this->cache_path.$index.'.php';
        }

        $chunk_path = $this->fs()->get_chunk_path($hash_group, $hash_key);

        return $this->cache_path.$chunk_path.$index.'.php';
    }

    /**
     * skip_stats.
     */
    private function skip_stats($group, $key = '')
    {
        if ($this->is_non_persistent_groups($group) || (!empty($keys) && $this->is_non_persistent_keys($key))) {
            return true;
        }

        return $this->cf()->is_dcfalse('LOG_ALL') && $this->fs()->is_docketcachegroup($group);
    }

    /**
     * has_stalecache.
     */
    private function has_stalecache($key, $group = '')
    {
        if ($this->fs()->is_wp_cache_group_queries($group)) {
            return true;
        }

        if ('wc_' === substr($key, 0, 3) && '_cache_prefix' === substr($key, -13)) {
            return true;
        }

        if ('wc_cache_' === substr($key, 0, 9) || 'wc_cache_' === substr($group, 0, 9)) {
            return true;
        }

        if (false !== strpos($key, ':') && @preg_match('@^([a-zA-Z0-9\._-]+):([0-9a-f]{32}):([0-9\. ]+)$@', $key)) {
            return true;
        }

        if (false !== strpos($group, 'docketcache-post-') && preg_match('@^docketcache-post-\d+$@', $group)) {
            return true;
        }

        return false;
    }

    /**
     * is_stalecache_ignored.
     */
    private function is_stalecache_ignored($key, $group = '')
    {
        if ($this->ignore_stalecache) {
            return $this->has_stalecache($key, $group);
        }

        return false;
    }

    /**
     * fs.
     */
    private function fs()
    {
        static $inst;
        if (!\is_object($inst)) {
            $inst = new Nawawi\DocketCache\Filesystem();
        }

        return $inst;
    }

    /**
     * cf.
     */
    private function cf()
    {
        static $inst;
        if (!\is_object($inst)) {
            $inst = new Nawawi\DocketCache\Constans();
        }

        return $inst;
    }

    /**
     * transientdb.
     */
    private function transient_db()
    {
        static $inst;
        if (!\is_object($inst)) {
            $inst = new Nawawi\DocketCache\TransientDb();
        }

        return $inst;
    }

    /**
     * dc_key.
     */
    private function dc_key($key, $group)
    {
        if ($this->multisite && !\array_key_exists($group, $this->global_groups)) {
            $key = $this->blog_prefix.$key;
        }

        return $key;
    }

    /**
     * dc_log.
     */
    private function dc_log($tag, $id, $data)
    {
        if ($this->cf()->is_dcfalse('LOG')) {
            return false;
        }

        if ($this->skip_stats($data)) {
            return false;
        }

        if ($this->cf()->is_dcfalse('LOG_ALL')) {
            if (!\in_array($tag, ['hit', 'miss'])) {
                return false;
            }

            if (false !== strpos($data, 'user') && @preg_match('@^user(s|email|logins|_meta)\:.*@', $data)) {
                return false;
            }
        }

        $caller = '';
        if (!empty($_SERVER['REQUEST_URI'])) {
            $caller = $_SERVER['REQUEST_URI'];
        } elseif ($this->cf()->is_dctrue('WPCLI')) {
            $caller = 'wp-cli';
        }

        if (false !== strpos($caller, '?page=docket-cache')) {
            return false;
        }

        static $duplicate = [];

        $buff = $this->item_hash($tag.$id.$data.$caller);
        if (isset($duplicate[$buff])) {
            return false;
        }

        $duplicate[$buff] = 1;

        return $this->fs()->log($tag, $id, $data, $caller);
    }

    /**
     * dc_flush.
     */
    private function dc_flush()
    {
        $dir = $this->cache_path;
        $is_timeout = false;
        $cnt = $this->fs()->cachedir_flush($dir, false, $is_timeout);
        $logkey = '000000000000-'.$this->item_hash(__FUNCTION__);

        if ($is_timeout) {
            $this->dc_log('err', $logkey, 'Process aborted. Reached maximum execution time. Total cache flushed: '.$cnt);

            return false;
        }

        if (false === $cnt) {
            $this->dc_log('err', $logkey, 'Cache could not be flushed');

            return false;
        }

        if ($cnt > 0) {
            $this->dc_log('flush', $logkey, 'Total cache flushed: '.$cnt);
        }

        return true;
    }

    /**
     * dc_remove.
     */
    private function dc_remove($key, $group)
    {
        $result = true;
        if ($this->use_transientdb && $this->fs()->is_transient($group)) {
            $result = $this->transient_db()->delete($key, $group);
        }

        $file = $this->get_file_path($key, $group);
        $this->fs()->unlink($file, false);
        $this->dc_log('del', $this->get_item_hash($file), $group.':'.$key);

        return $result;
    }

    /**
     * dc_remove_group.
     */
    public function dc_remove_group($group)
    {
        $total = 0;
        if (!$this->fs()->is_docketcachedir($this->cache_path)) {
            return $total;
        }

        $pattern = '@^'.$this->item_hash($group).'\-([a-z0-9]{12})\.php$@';

        if (\is_array($group) && !empty($group)) {
            $groups = array_map(function ($name) {
                return $this->item_hash($name);
            }, $group);

            $pattern = '@^('.implode('|', $groups).")\-([a-z0-9]{12})\.php$@";
            $group = implode(',', $group);
        }

        $this->fs()->suspend_cache_write(true);
        $max_execution_time = $this->fs()->get_max_execution_time(180);

        $slowdown = 0;
        foreach ($this->fs()->scanfiles($this->cache_path, null, $pattern) as $object) {
            if ($object->isFile()) {
                $fx = $object->getPathName();
                $fn = $object->getFileName();
                $this->fs()->unlink($fx, true);
                ++$total;

                array_map(function ($grp) use ($fx) {
                    unset($this->cache[$grp]);
                    $this->dc_log('flush', $this->get_item_hash($fx), $grp.':*');
                }, explode(',', $group));
            }

            if ($slowdown > 10) {
                $slowdown = 0;
                usleep(5000);
            }

            ++$slowdown;

            if ($max_execution_time > 0 && (microtime(true) - $this->wp_start_timestamp) > $max_execution_time) {
                break;
            }
        }

        $this->fs()->suspend_cache_write(false);

        if ($this->use_transientdb && $this->fs()->is_transient(explode(',', $group)) && \function_exists('nwdcx_cleanuptransient')) {
            $total += nwdcx_cleanuptransient();
        }

        return $total;
    }

    /**
     * dc_remove_group_match.
     */
    public function dc_remove_group_match($group)
    {
        $total = 0;
        if (!$this->fs()->is_docketcachedir($this->cache_path)) {
            return $total;
        }

        $this->fs()->suspend_cache_write(true);
        $max_execution_time = $this->fs()->get_max_execution_time(180);

        $slowdown = 0;
        $pattern = '@^([a-z0-9]{12})\-([a-z0-9]{12})\.php$@';
        foreach ($this->fs()->scanfiles($this->cache_path, null, $pattern) as $object) {
            if ($object->isFile()) {
                $fx = $object->getPathName();
                $data = $this->fs()->cache_get($fx);
                if (!empty($data) && !empty($data['group'])) {
                    $match = $data['group'];

                    if (\is_array($group) && !empty($group)) {
                        foreach ($group as $grp) {
                            if ($grp === substr($match, 0, \strlen($grp))) {
                                $this->fs()->unlink($fx, true);
                                $this->dc_log('flush', $this->get_item_hash($fx), $match.':*');
                                unset($this->cache[$match]);

                                ++$total;
                            }
                        }
                    } else {
                        if ($group === substr($match, 0, \strlen($group))) {
                            $this->fs()->unlink($fx, true);
                            $this->dc_log('flush', $this->get_item_hash($fx), $match.':*');
                            unset($this->cache[$match]);

                            ++$total;
                        }
                    }
                }
                unset($data);
            }

            if ($slowdown > 10) {
                $slowdown = 0;
                usleep(5000);
            }

            ++$slowdown;

            if ($max_execution_time > 0 && (microtime(true) - $this->wp_start_timestamp) > $max_execution_time) {
                break;
            }
        }

        $this->fs()->suspend_cache_write(false);

        return $total;
    }

    /**
     * dc_get.
     */
    private function dc_get($key, $group, $is_raw = false, &$codestub_false = false)
    {
        $file = $this->get_file_path($key, $group);
        $logkey = $this->get_item_hash($file);

        if ($this->use_transientdb && !\in_array($key, $this->bypass_transientdb)) {
            if ($this->fs()->is_transient($group)) {
                return $this->transient_db()->get($key, $group);
            }

            if ($this->fs()->is_wp_options($group) && $this->transient_db()->match_key($key)) {
                return false;
            }
        }

        $data = $this->fs()->cache_get($file);
        if (false === $data) {
            if (!$this->skip_stats($group) && !$this->fs()->is_transient($group)) {
                $this->dc_log('miss', $logkey, $group.':'.$key);
            }

            /*if (!$this->skip_stats($group) && !$this->fs()->is_transient($group)) {
                ++$this->cache_misses;

                $this->dc_log('miss', $logkey, $group.':'.$key);
            }*/

            return false;
        }

        $is_timeout = false;
        if (!empty($data['timeout']) && $this->fs()->valid_timestamp($data['timeout']) && time() >= $data['timeout']) {
            $this->dc_log('exp', $logkey, $group.':'.$key);
            $this->fs()->unlink($file, false);
            $is_timeout = true;
        }

        // incase gc not run
        if (!$is_timeout && !empty($data['timestamp']) && $this->fs()->valid_timestamp($data['timestamp'])) {
            $maxttl = time() - $this->cache_maxttl;
            if ($maxttl > $data['timestamp']) {
                $this->dc_log('exp', $logkey, $group.':'.$key);
                $this->fs()->unlink($file, true); // true = delete it instead of truncate
            }
        }

        if (!$this->skip_stats($group)) {
            ++$this->persistent_cache_hits;
            $this->dc_log('hit', $logkey, $group.':'.$key);
        }

        // If the transient does not exist, does not have a value, or has expired, then the return value will be false.
        if (!empty($data['group']) && $this->fs()->is_transient($data['group']) && ('' === $data['data'] || $is_timeout)) {
            $data['data'] = false;
        }

        // nwdcx_unserialize failed to convert serialize object.
        // we unserialize it here to get the object.
        if (!empty($data['data'])) {
            // *_serialize set at dc_save, to load it faster
            if (false !== strpos($data['type'], '_serialize')) {
                $data['data'] = unserialize($data['data']);
            } elseif ('string' === $data['type'] && \function_exists('maybe_unserialize')) {
                // old cache data
                $data['data'] = maybe_unserialize($data['data']);
            }
        }
        clearstatcache();

        // skip precache.
        if (isset($GLOBALS['DOCKET_CACHE_CODESTUB_FALSE']) && isset($GLOBALS['DOCKET_CACHE_CODESTUB_FALSE'][$file])) {
            $codestub_false = true;
        }

        return $is_raw ? $data : $data['data'];
    }

    /**
     * dc_code.
     */
    private function dc_code($file, $arr)
    {
        $logkey = $this->get_item_hash($file);
        $logpref = __FUNCTION__.'():';

        $data = $this->fs()->export_var($arr, $error);

        if (false === $data) {
            $this->dc_log('err', $logkey, $logpref.' Failed to export var -> '.$error);

            return false;
        }

        $code = $this->fs()->code_stub($data);
        $stat = $this->fs()->dump($file, $code, false); // 3rd param = validate

        if (false === $stat) {
            return false;
        }

        if (-1 === $stat) {
            $this->dc_log('err', $logkey, $logpref.' Failed to write');

            return false;
        }

        // remove lock
        $this->fs()->validate_fatal_error_file($file);

        return $stat;
    }

    /**
     * dc_save.
     */
    private function dc_save($cache_key, $data, $group = 'default', $expire = 0, $key = '')
    {
        if (wp_suspend_cache_addition()) {
            return false;
        }

        $logkey = $this->item_hash($group).'-'.$this->item_hash($cache_key);
        $logpref = __FUNCTION__.'():';

        if ($this->use_transientdb && !\in_array($cache_key, $this->bypass_transientdb)) {
            if ($this->fs()->is_transient($group)) {
                if (!$expire) {
                    $expire = 86400;
                }

                return $this->transient_db()->set($cache_key, $data, $group, time() + $expire);
            }

            if ($this->fs()->is_wp_options($group) && $this->transient_db()->match_key($cache_key)) {
                return false;
            }

            if (\in_array($cache_key, ['notoptions', 'alloptions']) && \is_array($data) && !empty($data)) {
                foreach ($data as $m => $n) {
                    if ($this->transient_db()->match_key($m)) {
                        unset($data[$m]);
                    }
                }
            }
        }

        if (!$this->fs()->mkdir_p($this->cache_path)) {
            return false;
        }

        @$this->fs()->placeholder($this->cache_path);

        $file = $this->get_file_path($cache_key, $group);

        // Skip save to disk, return true.
        if (('' === $data || (\is_array($data) && empty($data))) && ($this->fs()->is_transient($group) || $this->ignore_emptycache)) {
            nwdcx_debuglog(__FUNCTION__.': '.$logkey.': Process aborted. No data availale.');
            $this->fs()->unlink($file, false);
            unset($this->precache[$group][$cache_key]);

            return true;
        }

        // Chunk dir.
        if ($this->cf()->is_dctrue('CHUNKCACHEDIR', true) && !$this->fs()->mkdir_p(\dirname($file))) {
            return false;
        }

        // If $expire is larger than 0, convert it to timestamp.
        $timeout = ($expire > 0 ? time() + $expire : 0);

        $type = \gettype($data);
        if ('unknown type' === $type) {
            return false;
        }

        if ('NULL' === $type && null === $data) {
            $data = '';
        }

        if (!empty($data)) {
            if (!\in_array($type, ['boolean', 'integer', 'double', 'NULL'])) {
                // Abort if object too large.
                $len = 0;
                $nwdcx_suppresserrors = nwdcx_suppresserrors(true);
                if (\function_exists('maybe_serialize')) {
                    $len = \strlen(@maybe_serialize($data));
                } else {
                    $len = \strlen(@serialize($data));
                }
                nwdcx_suppresserrors($nwdcx_suppresserrors);

                if ($len >= $this->cache_maxsize) {
                    $this->dc_log('err', $logkey, $group.':'.$cache_key.' '.$logpref.' The size of object has breached '.$len.' of '.$this->cache_maxsize.' bytes.');

                    nwdcx_debuglog(__FUNCTION__.': '.$logkey.': Process aborted. The size of object has breached '.$len.' of '.$this->cache_maxsize.' bytes.');
                    $this->fs()->unlink($file, false);
                    unset($this->precache[$group][$cache_key]);

                    return false;
                }
            }

            // Unserialize content first.
            if ('string' === $type) {
                $data = nwdcx_unserialize($data);
            } elseif ('array' === $type) {
                $data_r = nwdcx_arraymap('nwdcx_unserialize', $data);

                if (!empty($data_r)) {
                    $data = $data_r;
                }
                unset($data_r);
            }
        }

        $meta = [];
        $meta['timestamp'] = time();

        if ($this->multisite) {
            $meta['network_id'] = $this->network_id;
        }

        $final_type = \gettype($data);
        if ('string' === $final_type && nwdcx_serialized($data)) {
            $final_type = 'string_serialize';
        } elseif ('array' === $final_type) {
            // The Data needs to be serialized.
            // The cache always returns false if the object has a class instance
            // other than stdClass since the class has not been loaded yet.
            $nwdcx_suppresserrors = nwdcx_suppresserrors(true);
            $export_data = @var_export($data, 1);
            if (!empty($export_data)) {
                // 1st priority. If has the "Request" instance.
                if (false !== strpos($export_data, 'Requests_Utility_CaseInsensitiveDictionary::__set_state')) {
                    $data = @serialize($data);
                    if (nwdcx_serialized($data)) {
                        $final_type = 'array_serialize';
                    }
                }

                // 2nd priority. If Transients and has class instance.
                if ('array' === $final_type && $this->fs()->is_transient($group) && false !== strpos($export_data, '::__set_state')) {
                    $data = @serialize($data);
                    if (nwdcx_serialized($data)) {
                        $final_type = 'array_serialize';
                    }
                }
            }
            unset($export_data);
            nwdcx_suppresserrors($nwdcx_suppresserrors);
            // Pass to code_stub.
        }

        $meta['site_id'] = get_current_blog_id();
        $meta['group'] = $group;
        $meta['key'] = $cache_key;
        $meta['type'] = $final_type;

        // If 0 let gc handle it by comparing file mtime
        // and maxttl constants.
        $meta['timeout'] = $timeout;

        // Before code_stub.
        $meta['data'] = $data;

        // Only count new file.
        clearstatcache(true, $file);
        $has_cache_file = is_file($file);
        if (true === $this->dc_code($file, $meta)) {
            nwdcx_debuglog(__FUNCTION__.': '.$this->get_item_hash($file).': Storing to disk.');

            if (!$has_cache_file && $this->maxfile_livecheck) {
                $count_file = (int) $this->get('count_file', 'docketcache-gc');
                if ($this->maxfile > $count_file) {
                    ++$count_file;
                    $this->set('count_file', $count_file, 'docketcache-gc', 86400); // 1d
                }
            }

            return true;
        }

        return false;
    }

    /**
     * dc_update.
     */
    private function dc_update($cache_key, $data, $group)
    {
        $meta = $this->dc_get($cache_key, $group, true);
        if (false === $meta || !\is_array($meta) || !isset($meta['data'])) {
            return false;
        }

        $file = $this->get_file_path($cache_key, $group);
        $meta['data'] = $data;

        if (true === $this->dc_code($file, $meta)) {
            return true;
        }

        return false;
    }

    /**
     * dc_precache_load.
     */
    private function dc_precache_load($hash)
    {
        static $is_done = false;

        if ($is_done) {
            return;
        }

        $cached = [];
        $group = 'docketcache-precache';
        $keys = $this->get($hash, $group);

        if (empty($keys) || !\is_array($keys)) {
            return;
        }

        nwdcx_debuglog(__FUNCTION__.': Process started.');

        $this->precache_loaded[$hash] = $keys;

        $slowdown = 0;
        $cnt_max = 0;

        foreach ($keys as $cache_group => $arr) {
            foreach ($arr as $cache_key) {
                if ($cnt_max >= $this->precache_maxkey) {
                    break 2;
                }

                $force = false;
                $found = false;
                $doing_precache = true;
                if (!isset($cached[$cache_key.$cache_group]) && false !== $this->get($cache_key, $cache_group, $force, $found, $doing_precache)) {
                    $cached[$cache_key.$cache_group] = 1;
                }

                ++$cnt_max;

                if ($slowdown > 10) {
                    $slowdown = 0;
                    usleep(1000);
                }

                ++$slowdown;

                if ($this->max_execution_time > 0 && (microtime(true) - $this->wp_start_timestamp) > $this->max_execution_time) {
                    break 2;
                }
            }
        }

        nwdcx_debuglog(__FUNCTION__.': Process ended. Loaded '.\count($cached));

        unset($keys, $cached);
        $is_done = true;
    }

    /**
     * dc_precache_set.
     */
    private function dc_precache_set($hash)
    {
        $group = 'docketcache-precache';
        $file = $this->get_file_path($hash, $group);

        if (empty($this->precache) || !\is_array($this->precache)) {
            $this->fs()->unlink($file, true);

            return;
        }

        $file_hash = $this->get_item_hash($file);
        $data = [];
        $slowdown = 0;
        $cnt_max = 0;

        nwdcx_debuglog(__FUNCTION__.': '.$file_hash.': Process started.');

        // docketcache-precache-gc
        $count_file = (int) $this->get('count_file', $group.'-gc');
        if ($count_file >= $this->precache_maxfile) {
            nwdcx_debuglog(__FUNCTION__.': '.$file_hash.': Process aborted. Reached maximum file limit ('.$count_file.'/'.$this->precache_maxfile.')');

            return;
        }

        foreach ($this->precache as $cache_group => $cache_keys) {
            if ($cnt_max >= $this->precache_maxkey) {
                break;
            }

            if ($cache_group !== $group) {
                $data[$cache_group] = array_keys($cache_keys);
            }

            ++$cnt_max;

            if ($slowdown > 10) {
                $slowdown = 0;
                usleep(100);
            }

            ++$slowdown;

            if ($this->max_execution_time > 0 && (microtime(true) - $this->wp_start_timestamp) > $this->max_execution_time) {
                nwdcx_debuglog(__FUNCTION__.': '.$file_hash.': Process aborted. Reached maximum execution time.');
                break;
            }
        }

        if (!empty($data)) {
            nwdcx_debuglog(__FUNCTION__.': '.$file_hash.': Total items = '.\count($data, 1));

            if (!empty($this->precache_loaded) && \function_exists('nwdcx_arraysimilar') && nwdcx_arraysimilar($this->precache_loaded[$hash], $data)) {
                nwdcx_debuglog(__FUNCTION__.': '.$file_hash.': Process ended. No data changes.');

                return;
            }

            // docketcache-precache-gc
            if ($this->precache_maxfile > $count_file) {
                clearstatcache(true, $file);

                // only count new file.
                $has_precache_file = is_file($file);

                if ($this->set($hash, $data, $group, 86400)) { // 1d
                    nwdcx_debuglog(__FUNCTION__.': '.$file_hash.': Process ended. Storing cache to disk.');

                    if (!$has_precache_file) {
                        ++$count_file;
                        $this->set('count_file', $count_file, $group.'-gc', 86400); // 1d
                    }

                    return;
                }
            }
            nwdcx_debuglog(__FUNCTION__.': '.$file_hash.': Process aborted. Reached maximum file limit ('.$count_file.'/'.$this->precache_maxfile.')');

            return;
        }

        nwdcx_debuglog(__FUNCTION__.': '.$file_hash.': Process ended. No data available.');
        unset($data, $hash);
    }

    /**
     * dc_precache.
     */
    private function dc_precache_init()
    {
        if (!empty($_POST) || empty($_SERVER['REQUEST_URI']) || $this->cf()->is_dctrue('WPCLI')) {
            return;
        }

        $req_uri = $_SERVER['REQUEST_URI'];
        $dostrip = !empty($_SERVER['QUERY_STRING']);

        $intersect_key = [
            'docketcache_ping' => 1,
            'doing_wp_cron' => 1,
            'wc-ajax' => 1,
            '_fs_blog_admin' => 1,
            'action' => 1,
            'message' => 1,
        ];

        if ($dostrip && !empty($_GET) && array_intersect_key($intersect_key, $_GET)) {
            return;
        }

        if (false !== strpos($req_uri, '/wp-json/') || false !== strpos($req_uri, '/wp-admin/admin-ajax.php') || false !== strpos($req_uri, '/xmlrpc.php') || false !== strpos($req_uri, '/wp-cron.php') || false !== strpos($req_uri, '/robots.txt') || false !== strpos($req_uri, '/favicon.ico')) {
            return;
        }

        $req_host = !empty($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : 'localhost';
        if ('localhost' !== $req_host) {
            $req_host = nwdcx_fixhost($req_host);
        }

        if ($dostrip && $this->is_user_logged_in() && false !== strpos($req_uri, '.php?') && false !== strpos($req_uri, '/wp-admin/') && @preg_match('@/wp-admin/(network/)?.*?\.php\?.*?@', $req_uri)) {
            $dostrip = false;
        }

        // without pretty permalink
        if ($dostrip && !empty($_GET) && empty($_GET['s']) && empty($_GET['q']) && (false !== strpos($req_uri, '/?p=') || false !== strpos($req_uri, '/?cat=') || false !== strpos($req_uri, '/?m=') || false !== strpos($req_uri, '/?page_id=') || false !== strpos($req_uri, '/index.php/')) && !@nwdcx_optget('permalink_structure')) {
            $dostrip = false;
        }

        if ($dostrip) {
            $req_uri = strtok($req_uri, '?#');
        }

        if (empty($req_host) || empty($req_uri)) {
            return;
        }

        // $this->precache_hashkey = $this->item_hash($req_host.$req_uri);
        $this->precache_hashkey = md5($req_host.$req_uri);

        $this->dc_precache_load($this->precache_hashkey);
    }

    /**
     * dc_close.
     * reference:
     *  wp_cache_close()
     *  wp-includes/load.php -> shutdown_action_hook().
     */
    public function dc_close()
    {
        static $is_done = false;

        if (!$is_done) {
            if ($this->is_precache && !empty($this->precache_hashkey) && !$this->fs()->suspend_cache_write()) {
                $this->dc_precache_set($this->precache_hashkey);
            }

            $is_done = true;
        }
    }

    /**
     * dc_init.
     */
    private function dc_init()
    {
        $this->wp_start_timestamp = \defined('WP_START_TIMESTAMP') ? WP_START_TIMESTAMP : microtime(true);
        $this->max_execution_time = $this->fs()->get_max_execution_time();
        $this->is_dev = $this->cf()->is_dctrue('DEV');

        if ($this->cf()->is_dcint('MAXSIZE', $dcvalue)) {
            if (!empty($dcvalue)) {
                $this->cache_maxsize = $this->fs()->sanitize_maxsize($dcvalue);
            }
        }

        if ($this->cf()->is_dcint('MAXTTL', $dcvalue)) {
            if (!empty($dcvalue)) {
                $this->cache_maxttl = $this->fs()->sanitize_maxttl($dcvalue);
            }
        }

        if ($this->cf()->is_dcarray('GLOBAL_GROUPS', $dcvalue)) {
            $this->add_global_groups($dcvalue);
        }

        if ($this->cf()->is_dcarray('IGNORED_GROUPS', $dcvalue)) {
            $this->add_non_persistent_groups($dcvalue);
        }

        if ($this->cf()->is_dcarray('IGNORED_KEYS', $dcvalue)) {
            $this->add_non_persistent_keys($dcvalue);
        }

        if ($this->cf()->is_dcarray('FILTERED_GROUPS', $dcvalue)) {
            $this->filtered_groups = $dcvalue;
        }

        if ($this->cf()->is_dcarray('IGNORED_GROUPKEY', $dcvalue)) {
            $this->non_persistent_groupkey = $dcvalue;
        }

        if ($this->cf()->is_dcarray('IGNORED_PRECACHE', $dcvalue)) {
            $this->bypass_precache = $dcvalue;
        }

        if (class_exists('Nawawi\\DocketCache\\TransientDb')) {
            $this->use_transientdb = $this->cf()->is_dctrue('TRANSIENTDB');
            if ($this->cf()->is_dcarray('IGNORED_TRANSIENTDB', $dcvalue)) {
                $this->bypass_transientdb = $dcvalue;
            }
        }

        $this->cache_path = $this->fs()->define_cache_path($this->cf()->dcvalue('PATH'));
        if ($this->multisite) {
            $this->cache_path = nwdcx_network_dirpath($this->cache_path);
        }

        if ($this->cf()->is_dctrue('WPOPTALOAD')) {
            $this->fs()->optimize_alloptions();
        }

        add_filter(
            'pre_cache_alloptions',
            function ($alloptions) {
                if (isset($alloptions['cron'])) {
                    unset($alloptions['cron']);
                }

                if (isset($alloptions['litespeed_messages'])) {
                    unset($alloptions['litespeed_messages']);
                }

                if (isset($alloptions['litespeed.admin_display.messages'])) {
                    unset($alloptions['litespeed.admin_display.messages']);
                }

                return $alloptions;
            },
            \PHP_INT_MAX
        );

        // litespeed admin notice
        add_action(
            'litespeed_purged_all',
            function () {
                $this->delete('alloptions', 'options');
                $this->delete('litespeed_messages', 'options');
                $this->delete('litespeed.admin_display.messages', 'options');
            },
            \PHP_INT_MAX
        );

        add_action(
            'all_admin_notices',
            function () {
                if (\function_exists('run_litespeed_cache')) {
                    $this->delete('litespeed_messages', 'options');
                    $this->delete('litespeed.admin_display.messages', 'options');
                }
            },
            \PHP_INT_MAX
        );

        foreach (['added', 'updated', 'deleted'] as $prefix) {
            add_action(
                $prefix.'_option',
                function ($option) {
                    if (!wp_installing()) {
                        $alloptions = wp_load_alloptions();
                        if (isset($alloptions[$option])) {
                            add_action(
                                'shutdown',
                                function () {
                                    $this->delete('alloptions', 'options');
                                },
                                \PHP_INT_MAX - 1
                            );
                        }
                        unset($alloptions);
                    }
                },
                \PHP_INT_MAX
            );
        }

        foreach (['activate', 'deactivate'] as $prefix) {
            add_action(
                $prefix.'_plugin',
                function ($plugin, $network) {
                    add_action(
                        'shutdown',
                        function () {
                            if ($this->multisite) {
                                $this->delete($this->network_id.':active_sitewide_plugins', 'site-options');
                                $this->delete($this->network_id.':auto_update_plugins', 'site-options');
                            }

                            $this->delete('uninstall_plugins', 'options');
                            $this->delete('auto_update_plugins', 'options');
                        },
                        \PHP_INT_MAX - 1
                    );
                },
                \PHP_INT_MAX,
                2
            );
        }

        // filtered groups hooks
        if (\is_array($this->filtered_groups)) {
            add_action(
                'save_post',
                function ($post_id, $post, $update) {
                    $this->flush_filtered_groups('save_post', [$post_id, $post, $update]);
                },
                \PHP_INT_MIN,
                3
            );

            add_action(
                'edit_post',
                function ($post_id, $post) {
                    $this->flush_filtered_groups('edit_post', [$post_id, $post]);
                },
                \PHP_INT_MIN,
                2
            );

            add_action(
                'delete_post',
                function ($post_id) {
                    $this->flush_filtered_groups('delete_post', [$post_id]);
                },
                \PHP_INT_MIN
            );
        }

        // html comment
        $this->add_signature = false;
        if ($this->cf()->is_dctrue('SIGNATURE')) {
            add_action(
                'wp_head',
                function () {
                    if (!$this->is_user_logged_in()) {
                        $this->add_signature = true;
                    }
                },
                \PHP_INT_MIN
            );

            add_action(
                'shutdown',
                function () {
                    if ($this->add_signature && !$this->is_user_logged_in()) {
                        echo apply_filters('docketcache/filter/signature/htmlfooter', "\n<!-- Performance optimized by Docket Cache: https://wordpress.org/plugins/docket-cache -->\n");
                    }
                },
                \PHP_INT_MAX
            );
        }

        // bypass stalecache
        $this->ignore_stalecache = $this->cf()->is_dctrue('STALECACHE_IGNORE', true);

        // bypass emptyache
        $this->ignore_emptycache = $this->cf()->is_dctrue('EMPTYCACHE_IGNORE', true);

        // maxfile check
        // true = count file at dc_save, false = will handle by GC.
        $this->maxfile_livecheck = $this->cf()->is_dctrue('MAXFILE_LIVECHECK', true);

        // maxfile
        $this->maxfile = (int) $this->fs()->sanitize_maxfile($this->cf()->dcvalue('MAXFILE', true));
        $count_file = (int) $this->get('count_file', 'docketcache-gc');
        if ($count_file >= $this->maxfile) {
            $this->fs()->suspend_cache_write(true);
        }

        // load precache
        $this->is_precache = $this->cf()->is_dctrue('PRECACHE', true);
        if ($this->is_precache) {
            if ($this->cf()->is_dcint('PRECACHE_MAXGROUP', $dcvalue)) {
                if (!empty($dcvalue)) {
                    $this->precache_maxgroup = $dcvalue;
                }
            }

            if ($this->cf()->is_dcint('PRECACHE_MAXKEY', $dcvalue)) {
                if (!empty($dcvalue)) {
                    $this->precache_maxkey = $dcvalue;
                }
            }

            if ($this->cf()->is_dcint('PRECACHE_MAXFILE', $dcvalue)) {
                if (!empty($dcvalue)) {
                    $this->precache_maxfile = $this->fs()->sanitize_precache_maxfile($dcvalue);
                }
            }

            $this->dc_precache_init();
        }
    }
}

/**
 * Sets up Object Cache Global and assigns it.
 *
 * @global WP_Object_Cache $wp_object_cache
 */
function wp_cache_init()
{
    global $wp_object_cache;
    if (!($wp_object_cache instanceof WP_Object_Cache)) {
        $wp_object_cache = new WP_Object_Cache();
    }
}

/**
 * @see WP_Object_Cache::add()
 */
function wp_cache_add($key, $data, $group = '', $expire = 0)
{
    global $wp_object_cache;

    return $wp_object_cache->add($key, $data, $group, (int) $expire);
}

/**
 * @see WP_Object_Cache::add_multiple()
 */
function wp_cache_add_multiple(array $data, $group = '', $expire = 0)
{
    global $wp_object_cache;

    return $wp_object_cache->add_multiple($data, $group, $expire);
}

/**
 * @see WP_Object_Cache::replace()
 */
function wp_cache_replace($key, $data, $group = '', $expire = 0)
{
    global $wp_object_cache;

    return $wp_object_cache->replace($key, $data, $group, (int) $expire);
}

/**
 * @see WP_Object_Cache::set()
 */
function wp_cache_set($key, $data, $group = '', $expire = 0)
{
    global $wp_object_cache;

    return $wp_object_cache->set($key, $data, $group, (int) $expire);
}

/**
 * @see WP_Object_Cache::set_multiple()
 */
function wp_cache_set_multiple(array $data, $group = '', $expire = 0)
{
    global $wp_object_cache;

    return $wp_object_cache->set_multiple($data, $group, $expire);
}

/**
 * @see WP_Object_Cache::get()
 */
function wp_cache_get($key, $group = '', $force = false, &$found = null)
{
    global $wp_object_cache;

    return $wp_object_cache->get($key, $group, $force, $found);
}

/**
 * @see WP_Object_Cache::get_multiple()
 */
function wp_cache_get_multiple(array $keys, $group = '', $force = false)
{
    global $wp_object_cache;

    return $wp_object_cache->get_multiple($keys, $group, $force);
}

/**
 * @see WP_Object_Cache::delete()
 */
function wp_cache_delete($key, $group = '')
{
    global $wp_object_cache;

    return $wp_object_cache->delete($key, $group);
}

/**
 * @see WP_Object_Cache::delete_multiple()
 */
function wp_cache_delete_multiple(array $keys, $group = '')
{
    global $wp_object_cache;

    return $wp_object_cache->delete_multiple($keys, $group);
}

/**
 * @see WP_Object_Cache::incr()
 */
function wp_cache_incr($key, $offset = 1, $group = '')
{
    global $wp_object_cache;

    return $wp_object_cache->incr($key, $offset, $group);
}

/**
 * @see WP_Object_Cache::decr()
 */
function wp_cache_decr($key, $offset = 1, $group = '')
{
    global $wp_object_cache;

    return $wp_object_cache->decr($key, $offset, $group);
}

/**
 * @see WP_Object_Cache::flush()
 */
function wp_cache_flush()
{
    global $wp_object_cache;

    return $wp_object_cache->flush();
}

/**
 * @see WP_Object_Cache::flush()
 */
function wp_cache_flush_runtime()
{
    global $wp_object_cache;

    return $wp_object_cache->flush(true);
}

/**
 * Determines whether the object cache implementation supports a particular feature.
 *
 * @since 6.1.0
 *
 * @param string $feature Name of the feature to check for. Possible values include:
 *                        'add_multiple', 'set_multiple', 'get_multiple', 'delete_multiple',
 *                        'flush_runtime', 'flush_group'.
 *
 * @return bool true if the feature is supported, false otherwise
 */
function wp_cache_supports($feature)
{
    switch ($feature) {
        case 'add_multiple':
        case 'set_multiple':
        case 'get_multiple':
        case 'delete_multiple':
        case 'flush_runtime':
        case 'flush_group':
            return true;

        default:
            return false;
    }
}

/**
 * @see WP_Object_Cache::dc_close()
 */
function wp_cache_close()
{
    global $wp_object_cache;

    $wp_object_cache->dc_close();

    return true;
}

/**
 * @see WP_Object_Cache::add_non_persistent_groups()
 */
function wp_cache_add_non_persistent_groups($groups)
{
    global $wp_object_cache;
    $wp_object_cache->add_non_persistent_groups($groups);
}

/**
 * @see WP_Object_Cache::add_non_persistent_keys()
 */
function wp_cache_add_non_persistent_keys($keys)
{
    global $wp_object_cache;
    $wp_object_cache->add_non_persistent_keys($keys);
}

/**
 * @see WP_Object_Cache::switch_to_blog()
 */
function wp_cache_switch_to_blog($blog_id)
{
    global $wp_object_cache;

    $wp_object_cache->switch_to_blog($blog_id);
}

/**
 * @see WP_Object_Cache::add_global_groups()
 */
function wp_cache_add_global_groups($groups)
{
    global $wp_object_cache;

    $wp_object_cache->add_global_groups($groups);
}

/**
 * @see WP_Object_Cache::stats()
 */
function wp_cache_stats()
{
    global $wp_object_cache;
    $wp_object_cache->stats();
}

/**
 * @see WP_Object_Cache::dc_remove_group()
 */
function wp_cache_flush_group($group = 'default')
{
    global $wp_object_cache;

    return $wp_object_cache->dc_remove_group($group);
}

/**
 * @see WP_Object_Cache::dc_remove_group_match()
 */
function wp_cache_flush_group_match($group = 'default')
{
    global $wp_object_cache;

    return $wp_object_cache->dc_remove_group_match($group);
}