Source of file Cache.php

Size: 17,065 Bytes - Last Modified: 2016-05-18T03:08:27+02:00

buildproject/core/module_system/system/Cache.php

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558
<?php
/*"******************************************************************************************************
*   (c) 2004-2006 by MulchProductions, www.mulchprod.de                                                 *
*   (c) 2007-2016 by Kajona, www.kajona.de                                                              *
*       Published under the GNU LGPL v2.1, see /system/licence_lgpl.txt                                 *
********************************************************************************************************/

namespace Kajona\System\System;


/**
 * The cache-class is the central way of caching objects in Kajona.
 * The cache can be used by multiple sources, e.g. pages, navigations and so on.
 * Each entry is identified by various key, to be defined by the using element.
 *
 * Currently, a cache-entry can be identified by the following fields:
 *  - source name
 *  - hash1
 *  - hash2
 *  - language
 *
 * When saving a new value, make sure to have maintained:
 *  - content
 *  - leasetime (otherwise the entry will be invalid as soon as saved to the database)
 *
 * @internal Replaced by CacheManager
 * @package module_system
 * @author sidler@mulchprod.de
 * @since 3.3.1
 */
class Cache
{

    private $strSourceName = "";
    private $strHash1 = "";
    private $strHash2 = "";
    private $strLanguage = "";
    private $strContent = "";

    private $intLeasetime = 0;
    private $intEntryHits = 0;

    private $strCacheId = "";

    /**
     *
     * @var Database
     */
    private $objDB;

    /**
     * Internal counter of cleanups.
     * If set to true, a cleanup was done and won't be fired again.
     *
     * @var bool
     */
    private static $bitCleanupDone = false;

    private static $intHits = 0;
    private static $intRequests = 0;
    private static $intSaves = 0;

    private static $arrInternalCache = array();


    /**
     * Constructor, being private. Please note that
     * there are factory methods in order to get instances.
     *
     * @see Cache::getCachedEntry()
     * @see Cache::createNewInstance()
     *
     * @param $strSourceName
     * @param $strHash1
     * @param $strHash2
     * @param $strLanguage
     * @param $strContent
     * @param $intLeasetime
     * @param $strCacheId
     */
    private function __construct($strSourceName, $strHash1, $strHash2, $strLanguage, $strContent, $intLeasetime, $strCacheId)
    {

        $this->objDB = Carrier::getInstance()->getObjDB();

        $this->strSourceName = $strSourceName;
        $this->strHash1 = $strHash1;
        $this->strHash2 = $strHash2;
        $this->strLanguage = $strLanguage;
        $this->strContent = $strContent;
        $this->intLeasetime = $intLeasetime;
        $this->strCacheId = $strCacheId;
    }


    /**
     * Tries to load a cached entry.
     * If existing, a valid object is returned.
     * Otherwise, e.g. if not found or invalid,
     * null is returned instead.
     *
     * @param string $strSourceName
     * @param string $strHash1
     * @param string $strHash2
     * @param string $strLanguage
     * @param bool $bitCreateInstanceOnNotFound if no entry was found, a new instance is created and returned. Set the content afterwards!
     *
     * @return Cache or null
     */
    public static function getCachedEntry($strSourceName, $strHash1, $strHash2 = null, $strLanguage = null, $bitCreateInstanceOnNotFound = false)
    {

        self::$intRequests++;

        //first run - search the internal cache
        foreach (self::$arrInternalCache as $arrSingleCacheEntry) {
            if ($arrSingleCacheEntry["cache_source"] == $strSourceName
                && $arrSingleCacheEntry["cache_hash1"] == $strHash1
                && ($strHash2 == null || $arrSingleCacheEntry["cache_hash2"] == $strHash2)
                && ($strLanguage == null || $arrSingleCacheEntry["cache_language"] == $strLanguage)
            ) {

                $objCacheEntry = new Cache(
                    $arrSingleCacheEntry["cache_source"],
                    $arrSingleCacheEntry["cache_hash1"],
                    $arrSingleCacheEntry["cache_hash2"],
                    $arrSingleCacheEntry["cache_language"],
                    $arrSingleCacheEntry["cache_content"],
                    $arrSingleCacheEntry["cache_leasetime"],
                    $arrSingleCacheEntry["cache_id"]
                );
                self::$intHits++;
                if (_cache_ === true) {
                    $objCacheEntry->increaseCacheEntryHits();
                }

                return $objCacheEntry;
            }
        }

        //search in the database to find a matching entry
        $strQuery = "SELECT *
                       FROM "._dbprefix_."cache
                      WHERE cache_source = ?
                        AND cache_hash1 = ?
                        ".($strHash2 != null ? " AND cache_hash2 = ? " : "")."
                        ".($strLanguage != null ? " AND cache_language = ? " : "")."
                        AND cache_leasetime > ? ";

        $arrParams = array($strSourceName, $strHash1);
        if ($strHash2 != null) {
            $arrParams[] = $strHash2;
        }
        if ($strLanguage != null) {
            $arrParams[] = $strLanguage;
        }

        $arrParams[] = time();

        $arrRow = Carrier::getInstance()->getObjDB()->getPRow($strQuery, $arrParams);
        if (isset($arrRow["cache_id"])) {
            $objCacheEntry = new Cache(
                $arrRow["cache_source"],
                $arrRow["cache_hash1"],
                $arrRow["cache_hash2"],
                $arrRow["cache_language"],
                $arrRow["cache_content"],
                $arrRow["cache_leasetime"],
                $arrRow["cache_id"]
            );

            self::$intHits++;
            if (_cache_ === true) {
                $objCacheEntry->increaseCacheEntryHits();
            }

            return $objCacheEntry;
        }
        else {
            //entry not existing, return null or create a new one?
            if ($bitCreateInstanceOnNotFound) {
                $objCache = Cache::createNewInstance($strSourceName);
                $objCache->setStrHash1($strHash1);
                $objCache->setStrHash2($strHash2);
                $objCache->setStrLanguage($strLanguage);

                return $objCache;
            }
            return null;
        }
    }

    /**
     * Creates a new instance of Cache.
     * All values are initialized, the one to
     * specify at construction time is only the source-name.
     * The lease-time is set to the current time, so make sure
     * to increase the time in order to profit from a
     * cached entry.
     * Use the setters to populate the entry with data.
     *
     * @param string $strSourceName
     *
     * @return Cache
     */
    public static function createNewInstance($strSourceName)
    {
        return new Cache($strSourceName, "", "", "", "", time(), null);
    }

    /**
     * Saves the object to the database.
     * Differs between update or insert.
     *
     * @throws Exception
     * @return bool
     */
    public function updateObjectToDb()
    {


        //run a cleanup
        Cache::cleanCache();

        //at least a source and hash1 given?
        if ($this->strSourceName == "" && $this->strHash1 == "") {
            throw new Exception("not all required params given", Exception::$level_ERROR);
        }

        //check if the new entry will be valid at least a second, otherwise quit saving
        if (time() > $this->intLeasetime) {
            return false;
        }

        $strQuery = "";
        $arrParams = array();
        $arrEscape = array();
        if ($this->strCacheId == null) {
            $this->strCacheId = generateSystemid();
            //insert
            $strQuery = "INSERT INTO "._dbprefix_."cache
                       (cache_id, cache_source, cache_hash1, cache_hash2, cache_language, cache_content, cache_leasetime, cache_hits) VALUES
                       (   ?, ?, ?, ?, ?, ?, ?, 1) ";
            $arrParams = array(
                $this->strCacheId,
                $this->getStrSourceName(),
                $this->getStrHash1(),
                $this->getStrHash2(),
                $this->getStrLanguage(),
                $this->getStrContent(),
                $this->getIntLeasetime()
            );
            $arrEscape = array(true, true, true, true, true, false, true);
        }
        else {
            //update
            $strQuery = "UPDATE "._dbprefix_."cache
                            SET cache_source = ?,
                                cache_hash1 = ?,
                                cache_hash2 = ?,
                                cache_language = ?,
                                cache_content = ?,
                                cache_leasetime = ?
                          WHERE cache_id = ?";
            $arrParams = array(
                $this->getStrSourceName(),
                $this->getStrHash1(),
                $this->getStrHash2(),
                $this->getStrLanguage(),
                $this->getStrContent(),
                $this->getIntLeasetime(),
                $this->strCacheId
            );
            $arrEscape = array(true, true, true, true, false, true, true);
        }

        self::$intSaves++;
        return $this->objDB->_pQuery($strQuery, $arrParams, $arrEscape);

    }

    /**
     * Deletes all cached entries, either for a single source or for all sources,
     * more filters like parametrized
     *
     * @param string $strSource
     * @param string $strHash1
     * @param string $strHash2
     *
     * @return bool
     * @static
     */
    public static function flushCache($strSource = "", $strHash1 = "", $strHash2 = "")
    {
        if(SystemModule::getModuleByName("system") == null) {
            return;
        }
        $strQuery = "DELETE FROM "._dbprefix_."cache ";

        $arrWhere = array();
        $arrParams = array();

        if ($strSource != "") {
            $arrWhere[] = " cache_source = ? ";
            $arrParams[] = $strSource;
        }

        if ($strHash1 != "") {
            $arrWhere[] = " cache_hash1 = ? ";
            $arrParams[] = $strHash1;
        }

        if ($strHash2 != "") {
            $arrWhere[] = " cache_hash2 = ? ";
            $arrParams[] = $strHash2;
        }

        if (count($arrWhere) > 0) {
            $strQuery .= "WHERE ".implode(" AND ", $arrWhere);
        }

        return Carrier::getInstance()->getObjDB()->_pQuery($strQuery, $arrParams);
    }

    /**
     * Returns the list of sources currently stored to the cache
     *
     * @return string[]
     */
    public static function getCacheSources()
    {
        $strQuery = "SELECT DISTINCT cache_source FROM  "._dbprefix_."cache";

        $arrReturn = array();
        $arrSourceRows = Carrier::getInstance()->getObjDB()->getPArray($strQuery, array());
        foreach ($arrSourceRows as $arrSingleRow) {
            $arrReturn[] = $arrSingleRow["cache_source"];
        }

        return $arrReturn;
    }

    /**
     * Deletes all invalid entries from the cache.
     * Does this only once per run, so all calls after
     * the first one will be skipped.
     *
     * @return bool
     * @static
     */
    public static function cleanCache()
    {
        if (Cache::$bitCleanupDone === false) {
            $strQuery = "DELETE FROM "._dbprefix_."cache WHERE cache_leasetime < ?";
            Cache::$bitCleanupDone = true;
            return Carrier::getInstance()->getObjDB()->_pQuery($strQuery, array(time()));
        }
        return true;
    }

    /**
     * This static method can be used to pre-load entries into the internal cache.
     * When loading a list of cached-entries differing in only a single param, this method
     * could be way faster:
     * Preload all elements without taking the param being different into account to load the list within
     * a single query. Later on the regular getCachedEntry() can be used to access entries available in
     * the cache; the loading from the internal cache or the database is done by the class internally and
     * transparent to the using objects.
     *
     * @param string $strSourceName
     * @param string $strHash1
     * @param string $strHash2
     * @param string $strLanguage
     */
    public static function fillInternalCache($strSourceName, $strHash1, $strHash2 = null, $strLanguage = null)
    {
        $strQuery = "SELECT *
                       FROM "._dbprefix_."cache
                      WHERE cache_source = ?
                        AND cache_hash1 = ?
                        ".($strHash2 != null ? " AND cache_hash2 = ? " : "")."
                        ".($strLanguage != null ? " AND cache_language = ? " : "")."
                        AND cache_leasetime > ?  ";

        $arrParams = array($strSourceName, $strHash1);
        if ($strHash2 != null) {
            $arrParams[] = $strHash2;
        }
        if ($strLanguage != null) {
            $arrParams[] = $strLanguage;
        }
        $arrParams[] = time();

        $arrRow = Carrier::getInstance()->getObjDB()->getPArray($strQuery, $arrParams);
        foreach ($arrRow as $arrSingleRow) {
            self::$arrInternalCache[$arrSingleRow["cache_id"]] = $arrSingleRow;
        }
    }

    /**
     * Returns all cached entries.
     *
     * @param int $intStart
     * @param int $intEnd
     *
     * @return array
     */
    public static function getAllCacheEntries($intStart = null, $intEnd = null)
    {
        //search in the database to find a matching entry
        $strQuery = "SELECT *
                       FROM "._dbprefix_."cache
                       ORDER BY cache_leasetime DESC";

        $arrCaches = Carrier::getInstance()->getObjDB()->getPArray($strQuery, array(), $intStart, $intEnd);

        $arrReturn = array();
        foreach ($arrCaches as $arrRow) {
            if (isset($arrRow["cache_id"])) {
                $objCacheEntry = new Cache(
                    $arrRow["cache_source"],
                    $arrRow["cache_hash1"],
                    $arrRow["cache_hash2"],
                    $arrRow["cache_language"],
                    $arrRow["cache_content"],
                    $arrRow["cache_leasetime"],
                    $arrRow["cache_id"]
                );

                $objCacheEntry->setIntEntryHits($arrRow["cache_hits"]);

                $arrReturn[] = $objCacheEntry;
            }
        }

        return $arrReturn;
    }

    /**
     * Returns the number of entries currently in the cache.
     *
     * @return int
     */
    public static function getAllCacheEntriesCount()
    {
        //search in the database to find a matching entry
        $strQuery = "SELECT COUNT(*) FROM "._dbprefix_."cache";

        $arrCaches = Carrier::getInstance()->getObjDB()->getPRow($strQuery, array());
        return $arrCaches["COUNT(*)"];
    }

    public function increaseCacheEntryHits()
    {
        $strQuery = "UPDATE "._dbprefix_."cache SET cache_hits = cache_hits+1 WHERE cache_id=? ";
        return Carrier::getInstance()->getObjDB()->_pQuery($strQuery, array($this->strCacheId));
    }


    public function getStrSourceName()
    {
        return $this->strSourceName;
    }

    public function setStrSourceName($strSourceName)
    {
        $this->strSourceName = $strSourceName;
    }

    public function getStrHash1()
    {
        return $this->strHash1;
    }

    public function setStrHash1($strHash1)
    {
        if ($strHash1 != null) {
            $this->strHash1 = $strHash1;
        }
    }

    public function getStrHash2()
    {
        return $this->strHash2;
    }

    public function setStrHash2($strHash2)
    {
        if ($strHash2 != null) {
            $this->strHash2 = $strHash2;
        }
    }

    public function getStrLanguage()
    {
        return $this->strLanguage;
    }

    public function setStrLanguage($strLanguage)
    {
        if ($strLanguage != null) {
            $this->strLanguage = $strLanguage;
        }
    }

    public function getStrContent()
    {
        return $this->strContent;
    }

    public function setStrContent($strContent)
    {
        $this->strContent = $strContent;
    }

    public function getIntLeasetime()
    {
        return $this->intLeasetime;
    }

    public function setIntLeasetime($intLeasetime)
    {
        $this->intLeasetime = $intLeasetime;
    }

    public static function getIntHits()
    {
        return self::$intHits;
    }

    public static function getIntRequests()
    {
        return self::$intRequests;
    }

    public static function getIntSaves()
    {
        return self::$intSaves;
    }

    public static function getIntCachesize()
    {
        return count(self::$arrInternalCache);
    }

    public function getIntEntryHits()
    {
        return $this->intEntryHits;
    }

    public function setIntEntryHits($intEntryHits)
    {
        $this->intEntryHits = $intEntryHits;
    }

}