芝麻web文件管理V1.00
编辑当前文件:/home/mgatv524/public_html/fmd/vendor/tedivm/stash/src/Stash/Item.php
* * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Stash; use Stash\Exception\Exception; use Stash\Exception\InvalidArgumentException; use Stash\Interfaces\DriverInterface; use Stash\Interfaces\ItemInterface; use Stash\Interfaces\PoolInterface; /** * 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
*/ class Item implements ItemInterface { /** * 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 ); protected $data; protected $expiration; protected $invalidationMethod = Invalidation::PRECOMPUTE; protected $invalidationArg1 = null; protected $invalidationArg2 = null; /** * 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 \Stash\Interfaces\PoolInterface */ 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 \Stash\Interfaces\DriverInterface */ 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 \Psr\Log\LoggerInterface */ 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->driver = $pool->getDriver(); } /** * {@inheritdoc} */ public function setKey(array $key, $namespace = null) { $this->namespace = $namespace; $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); } /** * {@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() { unset($this->data); unset($this->expiration); if ($this->isDisabled()) { return false; } return $this->driver->clear(isset($this->key) ? $this->key : null); } /** * {@inheritdoc} */ public function get() { try { if (!isset($this->data)) { $this->data = $this->executeGet( $this->invalidationMethod, $this->invalidationArg1, $this->invalidationArg2); } if (false === $this->isHit) { return null; } return $this->data; } catch (Exception $e) { $this->logException('Retrieving from cache caused exception.', $e); $this->disable(); return null; } } /** * {@inheritdoc} */ public function setInvalidationMethod($invalidation = Invalidation::PRECOMPUTE, $arg = null, $arg2 = null) { $this->invalidationMethod = $invalidation; $this->invalidationArg1 = $arg; $this->invalidationArg2 = $arg2; } private function executeGet($invalidation = Invalidation::PRECOMPUTE, $arg = null, $arg2 = null) { $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 isHit() { return !$this->isMiss(); } /** * {@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($spkey, true, time() + $expiration); } /** * {@inheritdoc} */ public function set($value) { if (!isset($this->key)) { return false; } if ($this->isDisabled()) { return $this; } $this->data = $value; return $this; } /** * {@inheritdoc} */ public function setTTL($ttl = null) { if (is_numeric($ttl) || ($ttl instanceof \DateInterval)) { return $this->expiresAfter($ttl); } elseif (($ttl instanceof \DateTimeInterface) || ($ttl instanceof \DateTime)) { return $this->expiresAt($ttl); } else { $this->expiration = null; } return $this; } /** * {@inheritdoc} */ public function expiresAt($expiration = null) { if (!is_null($expiration) && !($expiration instanceof \DateTimeInterface)) { # For compatbility with PHP 5.4 we also allow inheriting from the DateTime object. if (!($expiration instanceof \DateTime)) { throw new InvalidArgumentException('expiresAt requires \DateTimeInterface or null'); } } $this->expiration = $expiration; return $this; } /** * {@inheritdoc} */ public function expiresAfter($time) { $date = new \DateTime(); if (is_numeric($time)) { $dateInterval = \DateInterval::createFromDateString(abs($time) . ' seconds'); if ($time > 0) { $date->add($dateInterval); } else { $date->sub($dateInterval); } $this->expiration = $date; } elseif ($time instanceof \DateInterval) { $date->add($time); $this->expiration = $date; } else { } return $this; } /** * {@inheritdoc} */ public function save() { try { return $this->executeSet($this->data, $this->expiration); } catch (Exception $e) { $this->logException('Setting value in cache caused exception.', $e); $this->disable(); return false; } } private function executeSet($data, $time) { if ($this->isDisabled() || !isset($this->key)) { return false; } $store = array(); $store['return'] = $data; $store['createdOn'] = time(); if (isset($time) && (($time instanceof \DateTime) || ($time instanceof \DateTimeInterface))) { $expiration = $time->getTimestamp(); $cacheTime = $expiration - $store['createdOn']; } else { $cacheTime = self::$cacheTime; } $expiration = $store['createdOn'] + $cacheTime; if ($cacheTime > 0) { $expirationDiff = rand(0, floor($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; } /** * 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) { $invalidation = Invalidation::PRECOMPUTE; if (is_array($validation)) { $argArray = $validation; $invalidation = isset($argArray[0]) ? $argArray[0] : Invalidation::PRECOMPUTE; if (isset($argArray[1])) { $arg = $argArray[1]; } if (isset($argArray[2])) { $arg2 = $argArray[2]; } } $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->executeGet(Invalidation::SLEEP, $time, $attempts - 1); break; case Invalidation::OLD: $this->isHit = isset($record['data']) && $record['data']['return'] !== null; break; default: $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() { if (!isset($this->expiration)) { $record = $this->getRecord(); $dateTime = new \DateTime(); if (!isset($record['expiration'])) { return $dateTime; } $this->expiration = $dateTime->setTimestamp($record['expiration']); } return $this->expiration; } /** * 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); } } }