Вход Регистрация
Файл: concrete5.7.5.6/concrete/vendor/tedivm/stash/src/Stash/Item.php
Строк: 475
<?php

/*
 * This file is part of the Stash package.
 *
 * (c) Robert Hafner <tedivm@tedivm.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Stash;

use 
StashExceptionException;
use 
StashInterfacesDriverInterface;
use 
StashInterfacesItemInterface;
use 
StashInterfacesPoolInterface;

/**
 * Stash caches data that has a high generation cost, such as template preprocessing or code that requires a database
 * connection. This class can store any native php datatype, as long as it can be serialized (so when creating classes
 * that you wish to store instances of, remember the __sleep and __wake magic functions).
 *
 * @package Stash
 * @author  Robert Hafner <tedivm@tedivm.com>
 */
class Item implements ItemInterface
{
    
/**
     * @deprecated
     */
    
const SP_NONE         0;

    
/**
     * @deprecated
     */
    
const SP_OLD          1;

    
/**
     * @deprecated
     */
    
const SP_VALUE        2;

    
/**
     * @deprecated
     */
    
const SP_SLEEP        3;

    
/**
     * @deprecated
     */
    
const SP_PRECOMPUTE   4;

    
/**
     * This is the default time, in seconds, that objects are cached for.
     *
     * @var int seconds
     */
    
public static $cacheTime 432000// five days

    /**
     * Disables the cache system wide. It is used internally when the storage engine fails or if the cache is being
     * cleared. This differs from the cacheEnabled property in that it affects all instances of the cache, not just one.
     *
     * @var bool
     */
    
public static $runtimeDisable false;

    
/**
     * Used internally to mark the class as disabled. Unlike the static runtimeDisable flag this is effective only for
     * the current instance.
     *
     * @var bool
     */
    
protected $cacheEnabled true;

    
/**
     * Contains a list of default arguments for when users do not supply them.
     *
     * @var array
     */
    
protected $defaults = array('precompute_time' => 40// time, in seconds, before expiration
                                
'sleep_time' => 500// time, in microseconds, to sleep
                                
'sleep_attempts' => 1// number of times to sleep, wake up, and recheck cache
                                
'stampede_ttl' => 30// How long a stampede flag will be acknowledged
    
);

    
/**
     * The identifier for the item being cached. It is set through the setupKey function.
     *
     * @var array One dimensional array representing the location of a cached object.
     */
    
protected $key;

    
/**
     * A serialized version of the key, used primarily used as the index in various arrays.
     *
     * @var string
     */
    
protected $keyString;

    
/**
     * Marks whether or not stampede protection is enabled for this instance of Stash.
     *
     * @var bool
     */
    
protected $stampedeRunning false;

    
/**
     * The Pool that spawned this instance of the Item..
     *
     * @var StashInterfacesPoolInterface
     */
    
protected $pool;

    
/**
     * The cacheDriver being used by the system. While this class handles all of the higher functions, it's the cache
     * driver here that handles all of the storage/retrieval functionality. This value is set by the constructor.
     *
     * @var StashInterfacesDriverInterface
     */
    
protected $driver;

    
/**
     * If set various then errors and exceptions will get passed to the PSR Compliant logging library. This
     * can be set using the setLogger() function in this class.
     *
     * @var PsrLogLoggerInterface
     */
    
protected $logger;

    
/**
     * Defines the namespace the item lives in.
     *
     * @var string|null
     */
    
protected $namespace null;

    
/**
     * This is a flag to see if a valid response is returned. It is set by the getData function and is used by the
     * isMiss function.
     *
     * @var bool
     */
    
private $isHit null;


    
/**
     * {@inheritdoc}
     */
    
public function setPool(PoolInterface $pool)
    {
        
$this->pool $pool;
        
$this->setDriver($pool->getDriver());
    }

    
/**
     * {@inheritdoc}
     */
    
public function setKey($key$namespace null)
    {
        
$this->namespace $namespace;
        
$this->setupKey($key);
    }

    
/**
     * {@inheritdoc}
     */
    
public function disable()
    {
        
$this->cacheEnabled false;

        return 
true;
    }

    
/**
     * {@inheritdoc}
     */
    
public function getKey()
    {
        return isset(
$this->keyString) ? $this->keyString false;
    }

    
/**
     * {@inheritdoc}
     */
    
public function clear()
    {
        try {
            return 
$this->executeClear();
        } catch (
Exception $e) {
            
$this->logException('Clearing cache caused exception.'$e);
            
$this->disable();

            return 
false;
        }
    }

    private function 
executeClear()
    {
        if (
$this->isDisabled()) {
            return 
false;
        }

        return 
$this->driver->clear(isset($this->key) ? $this->key null);
    }

    
/**
     * {@inheritdoc}
     */
    
public function get($invalidation 0$arg null$arg2 null)
    {
        try {
            return 
$this->executeGet($invalidation$arg$arg2);
        } catch (
Exception $e) {
            
$this->logException('Retrieving from cache caused exception.'$e);
            
$this->disable();

            return 
null;
        }
    }

    private function 
executeGet($invalidation$arg$arg2)
    {
        
$this->isHit false;

        if (
$this->isDisabled()) {
            return 
null;
        }

        if (!isset(
$this->key)) {
            return 
null;
        }

        if (!
is_array($invalidation)) {
            
$vArray = array();

            if (isset(
$invalidation)) {
                
$vArray[] = $invalidation;
            }

            if (isset(
$arg)) {
                
$vArray[] = $arg;
            }

            if (isset(
$arg2)) {
                
$vArray[] = $arg2;
            }

            
$invalidation $vArray;
        }

        
$record $this->getRecord();

        
$this->validateRecord($invalidation$record);

        return isset(
$record['data']['return']) ? $record['data']['return'] : null;
    }

    
/**
     * {@inheritdoc}
     */
    
public function isMiss()
    {
        if (!isset(
$this->isHit))
            
$this->get();

        if (
$this->isDisabled()) {
            return 
true;
        }

        return !
$this->isHit;
    }

    
/**
     * {@inheritdoc}
     */
    
public function lock($ttl null)
    {
       if (
$this->isDisabled()) {
            return 
true;
        }

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

        
$this->stampedeRunning true;

        
$expiration = isset($ttl) && is_numeric($ttl) ? (int) $ttl $this->defaults['stampede_ttl'];


        
$spkey $this->key;
        
$spkey[0] = 'sp';

        return 
$this->driver->storeData($spkeytruetime() + $expiration);
    }

    
/**
     * {@inheritdoc}
     */
    
public function set($data$ttl null)
    {
        try {
            return 
$this->executeSet($data$ttl);
        } catch (
Exception $e) {
            
$this->logException('Setting value in cache caused exception.'$e);
            
$this->disable();

            return 
false;
        }
    }

    private function 
executeSet($data$time)
    {
        if (
$this->isDisabled()) {
            return 
false;
        }

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

        
$store = array();
        
$store['return'] = $data;
        
$store['createdOn'] = time();

        if (isset(
$time)) {
            if (
$time instanceof DateTime) {
                
$expiration $time->getTimestamp();
                
$cacheTime $expiration $store['createdOn'];
            } else {
                
$cacheTime = isset($time) && is_numeric($time) ? $time self::$cacheTime;
            }
        } else {
            
$cacheTime self::$cacheTime;
        }

        
$expiration $store['createdOn'] + $cacheTime;

        if (
$cacheTime 0) {
            
$expirationDiff rand(0floor($cacheTime .15));
            
$expiration -= $expirationDiff;
        }

        if (
$this->stampedeRunning == true) {
            
$spkey $this->key;
            
$spkey[0] = 'sp'// change "cache" data namespace to stampede namespace
            
$this->driver->clear($spkey);
            
$this->stampedeRunning false;
        }

        return 
$this->driver->storeData($this->key$store$expiration);
    }

    
/**
     * {@inheritdoc}
     */
    
public function extend($ttl null)
    {
        if (
$this->isDisabled()) {
            return 
false;
        }

        return 
$this->set($this->get(), $ttl);
    }

    
/**
     * {@inheritdoc}
     */
    
public function isDisabled()
    {
        return 
self::$runtimeDisable
                
|| !$this->cacheEnabled
                
|| (defined('STASH_DISABLE_CACHE') && STASH_DISABLE_CACHE);
    }

    
/**
     * {@inheritdoc}
     */
    
public function setLogger($logger)
    {
        
$this->logger $logger;
    }

    
/**
     * Sets the driver this object uses to interact with the caching system.
     *
     * @param DriverInterface $driver
     */
    
protected function setDriver(DriverInterface $driver)
    {
        
$this->driver $driver;
    }

    
/**
     * Logs an exception with the Logger class, if it exists.
     *
     * @param  string     $message
     * @param  Exception $exception
     * @return bool
     */
    
protected function logException($message$exception)
    {
        if(!isset(
$this->logger))

            return 
false;

        
$this->logger->critical($message,
                                array(
'exception' => $exception,
                                      
'key' => $this->keyString));

        return 
true;
    }

    
/**
     * Returns true if another Item is currently recalculating the cache.
     *
     * @param  array $key
     * @return bool
     */
    
protected function getStampedeFlag($key)
    {
        
$key[0] = 'sp'// change "cache" data namespace to stampede namespace
        
$spReturn $this->driver->getData($key);
        
$sp = isset($spReturn['data']) ? $spReturn['data'] : false;


        if (isset(
$spReturn['expiration'])) {
            if (
$spReturn['expiration'] < time()) {
                
$sp false;
            }
        }

        return 
$sp;
    }

    
/**
     * Returns the record for the current key. If there is no record than an empty array is returned.
     *
     * @return array
     */
    
protected function getRecord()
    {
        
$record $this->driver->getData($this->key);

        if (!
is_array($record)) {
            return array();
        }

        return 
$record;
    }

    
/**
     * Decides whether the current data is fresh according to the supplied validation technique. As some techniques
     * actively change the record this function takes that in as a reference.
     *
     * This function has the ability to change the isHit property as well as the record passed.
     *
     * @internal
     * @param array $validation
     * @param array &$record
     */
    
protected function validateRecord($validation, &$record)
    {
        if (
is_array($validation)) {
            
$argArray $validation;
            
$invalidation = isset($argArray[0]) ? $argArray[0] : 0;

            if (isset(
$argArray[1])) {
                
$arg $argArray[1];
            }

            if (isset(
$argArray[2])) {
                
$arg2 $argArray[2];
            }
        } else {
            
$invalidation Invalidation::NONE;
        }

        
$curTime microtime(true);

        if (isset(
$record['expiration']) && ($ttl $record['expiration'] - $curTime) > 0) {
            
$this->isHit true;

            if (
$invalidation == Invalidation::PRECOMPUTE) {
                
$time = isset($arg) && is_numeric($arg) ? $arg $this->defaults['precompute_time'];

                
// If stampede control is on it means another cache is already processing, so we return
                // true for the hit.
                
if ($ttl $time) {
                    
$this->isHit = (bool) $this->getStampedeFlag($this->key);
                }
            }

            return;
        }

        if (!isset(
$invalidation) || $invalidation == Invalidation::NONE) {
            
$this->isHit false;

            return;
        }

        if (!
$this->getStampedeFlag($this->key)) {
            
$this->isHit false;

            return;
        }

        switch (
$invalidation) {
            case 
Invalidation::VALUE:
                if (!isset(
$arg)) {
                    
$this->isHit false;

                    return;
                } else {
                    
$record['data']['return'] = $arg;
                    
$this->isHit true;
                }
                break;

            case 
Invalidation::SLEEP:
                
$time = isset($arg) && is_numeric($arg) ? $arg $this->defaults['sleep_time'];
                
$attempts = isset($arg2) && is_numeric($arg2) ? $arg2 $this->defaults['sleep_attempts'];

                
$ptime $time 1000;

                if (
$attempts <= 0) {
                    
$this->isHit false;
                    
$record['data']['return'] = null;
                    break;
                }

                
usleep($ptime);
                
$record['data']['return'] = $this->get(Invalidation::SLEEP$time$attempts 1);
                break;

            case 
Invalidation::OLD:
                
$this->isHit true;
                break;

            default:
            case 
Invalidation::NONE:
                
$this->isHit false;
                break;
        } 
// switch($invalidate)
    
}

    
/**
     * {@inheritdoc}
     */
    
public function getCreation()
    {
        
$record $this->getRecord();
        if (!isset(
$record['data']['createdOn'])) {
            return 
false;
        }

        
$dateTime = new DateTime();
        
$dateTime->setTimestamp($record['data']['createdOn']);

        return 
$dateTime;
    }

    
/**
     * {@inheritdoc}
     */
    
public function getExpiration()
    {
        
$record $this->getRecord();
        if (!isset(
$record['expiration'])) {
            return 
false;
        }

        
$dateTime = new DateTime();
        
$dateTime->setTimestamp($record['expiration']);

        return 
$dateTime;
    }

    
/**
     * This clears out any locks that are present if this Item is prematurely destructed.
     */
    
public function __destruct()
    {
        if (isset(
$this->stampedeRunning) && $this->stampedeRunning == true) {
            
$spkey $this->key;
            
$spkey[0] = 'sp';
            
$this->driver->clear($spkey);
        }
    }

    
/**
     * This function is used by the Pool object while creating this object. It
     * is an internal function an should not be called directly.
     *
     * @param  array                     $key
     * @throws InvalidArgumentException
     */
    
protected function setupKey($key)
    {
        if (!
is_array($key)) {
            throw new 
InvalidArgumentException('Item requires keys as arrays.');
        }

        
$keyStringTmp $key;
        if (isset(
$this->namespace)) {
            
array_shift($keyStringTmp);
        }

        
$this->keyString implode('/'$keyStringTmp);

        
// We implant the namespace "cache" to the front of every stash object's key. This allows us to segment
        // off the user data, and use other 'namespaces' for internal purposes.
        
array_unshift($key'cache');
        
$this->key array_map('strtolower'$key);
    }

}
Онлайн: 1
Реклама