<?php

/*
+---------------------------------------------------------------------------+
| Revive Adserver                                                           |
| http://www.revive-adserver.com                                            |
|                                                                           |
| Copyright: See the COPYRIGHT.txt file.                                    |
| License: GPLv2 or later, see the LICENSE.txt file.                        |
+---------------------------------------------------------------------------+
*/

require_once 'Cache/Lite.php';

/**
 * A class to read and save permanent cache data, stored in /etc
 *
 * It features a predictable cache file name and automatic (un)serialising
 * and zlib (de)compression
 */
class OA_PermanentCache
{
    /**
     * @var Cache_Lite
     */
    public $oCache;

    /**
     * @var string
     */
    public $cachePath;

    /**
     * Class constructor
     *
     * @param string $cachePath The cache path
     *
     * @return OA_PermanentCache
     */
    public function __construct($cachePath = null)
    {
        $this->cachePath = is_null($cachePath) ? MAX_PATH . '/etc/permanentcache/' : $cachePath;
        if (!str_ends_with($cachePath, '/')) {
            $this->cachePath .= '/';
        }
        $this->oCache = new Cache_Lite([
            'cacheDir' => $this->cachePath,
            'fileNameProtection' => false,
            'lifeTime' => null,
            'readControlType' => 'md5',
            'dontCacheWhenTheResultIsFalse' => true,
        ]);
    }

    /**
     * A method to get the permanent cache content
     *
     * @param string $cacheName The name of the original file we are retrieving
     * @return mixed The cache content or FALSE in case of cache miss
     */
    public function get($cacheName)
    {
        if (extension_loaded('zlib')) {
            $id = $this->_getId($cacheName);
            $group = $this->_getGroup($cacheName);

            if ($result = $this->oCache->get($id, $group, true)) {
                return unserialize(gzuncompress($result));
            }
        }

        return false;
    }

    /**
     * A method to save the permanent cache content. The content will be serialized and
     * compressed to save space
     *
     * @param mixed  $data     The content to save
     * @param string $cacheName The name of the original file we are storing
     * @return bool True if the cache was correctly saved
     */
    public function save($data, $cacheName)
    {
        if (is_writable($this->cachePath) && extension_loaded('zlib')) {
            $id = $this->_getId($cacheName);
            $group = $this->_getGroup($cacheName);
            return $this->oCache->save(gzcompress(serialize($data), 9), $id, $group);
        }

        return false;
    }

    /**
     * A method to remove a cache file
     *
     * @param string $cacheName The name of the original file
     * @return bool True if the cache was deleted
     */
    public function remove($cacheName)
    {
        $id = $this->_getId($cacheName);
        $group = $this->_getGroup($cacheName);
        return $this->oCache->remove($id, $group);
    }

    /**
     * Private method to generate the Cache_Lite cache ID from a file name
     *
     * @param string $cacheName The name of the original file
     * @return string The cache ID (the base file name without extension)
     */
    public function _getId($cacheName)
    {
        // Deal with class::method style cache names
        $cacheName = str_replace('::', '/', $cacheName);

        // Strip extension
        $cacheName = preg_replace('/\.[^.]+?$/', '', $cacheName);

        $IdName = strtolower(basename($cacheName));
        return preg_replace('/[^a-z0-9]/i', '-', $IdName) . '.bin';
    }

    /**
     * Private method to generate the Cache_Lite cache group from a file name
     *
     * @param string $cacheName The name of the original file
     * @return string The cache group (generated using the file path, or 'default')
     */
    public function _getGroup($cacheName)
    {
        // Deal with class::method style cache names
        $cacheName = str_replace('::', '/', $cacheName);

        // Strip MAX_PATH
        if (str_starts_with($cacheName, MAX_PATH)) {
            $cacheName = substr($cacheName, strlen(MAX_PATH) + 1);
        }

        $groupName = strtolower(dirname($cacheName));
        if (!empty($groupName)) {
            return preg_replace('/[^a-z0-9]/i', '-', $groupName);
        }

        return 'default';
    }
}
