diff --git a/system/autoload/PEAR2/Cache/SHM/Adaptor/APC.php b/system/autoload/PEAR2/Cache/SHM/Adaptor/APC.php new file mode 100644 index 0000000..455d8db --- /dev/null +++ b/system/autoload/PEAR2/Cache/SHM/Adaptor/APC.php @@ -0,0 +1,417 @@ + + * @copyright 2011 Vasil Rangelov + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version 0.2.0 + * @link http://pear2.php.net/PEAR2_Cache_SHM + */ +/** + * The namespace declaration. + */ +namespace PEAR2\Cache\SHM\Adapter; + +/** + * Throws exceptions from this namespace, and extends from this class. + */ +use PEAR2\Cache\SHM; + +/** + * {@link APC::getIterator()} returns this object. + */ +use ArrayObject; + +/** + * Shared memory adapter for the APC extension. + * + * @category Caching + * @package PEAR2_Cache_SHM + * @author Vasil Rangelov + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @link http://pear2.php.net/PEAR2_Cache_SHM + */ +class APC extends SHM +{ + /** + * ID of the current storage. + * + * @var string + */ + protected $persistentId; + + /** + * List of persistent IDs. + * + * A list of persistent IDs within the current request (as keys) with an int + * (as a value) specifying the number of instances in the current request. + * Used as an attempt to ensure implicit lock releases even on errors in the + * critical sections, since APC doesn't have an actual locking function. + * + * @var array + */ + protected static $requestInstances = array(); + + /** + * Array of lock names for each persistent ID. + * + * Array of lock names (as values) for each persistent ID (as key) obtained + * during the current request. + * + * @var array + */ + protected static $locksBackup = array(); + + /** + * Creates a new shared memory storage. + * + * Establishes a separate persistent storage. + * + * @param string $persistentId The ID for the storage. The storage will be + * reused if it exists, or created if it doesn't exist. Data and locks + * are namespaced by this ID. + */ + public function __construct($persistentId) + { + $this->persistentId = __CLASS__ . ' ' . $persistentId; + if (isset(static::$requestInstances[$this->persistentId])) { + static::$requestInstances[$this->persistentId]++; + } else { + static::$requestInstances[$this->persistentId] = 1; + static::$locksBackup[$this->persistentId] = array(); + } + register_shutdown_function( + get_called_class() . '::releaseLocks', + $this->persistentId, + true + ); + } + + /** + * Checks if the adapter meets its requirements. + * + * @return bool TRUE on success, FALSE on failure. + */ + public static function isMeetingRequirements() + { + return extension_loaded('apc') + && version_compare(phpversion('apc'), '3.1.1', '>=') + && ini_get('apc.enabled') + && ('cli' !== PHP_SAPI || ini_get('apc.enable_cli')); + } + + /** + * Releases all locks in a storage. + * + * This function is not meant to be used directly. It is implicitly called + * by the the destructor and as a shutdown function when the request ends. + * One of these calls ends up releasing any unreleased locks obtained + * during the request. A lock is also implicitly released as soon as there + * are no objects left in the current request using the same persistent ID. + * + * @param string $internalPersistentId The internal persistent ID, the locks + * of which are being released. + * @param bool $isAtShutdown Whether the function was executed at + * shutdown. + * + * @return void + * + * @internal + */ + public static function releaseLocks($internalPersistentId, $isAtShutdown) + { + $hasInstances = 0 !== static::$requestInstances[$internalPersistentId]; + if ($isAtShutdown === $hasInstances) { + foreach (static::$locksBackup[$internalPersistentId] as $key) { + apc_delete($internalPersistentId . 'l ' . $key); + } + } + } + + /** + * Releases any locks obtained by this instance as soon as there are no more + * references to the object's persistent ID. + */ + public function __destruct() + { + static::$requestInstances[$this->persistentId]--; + static::releaseLocks($this->persistentId, false); + } + + + /** + * Obtains a named lock. + * + * @param string $key Name of the key to obtain. Note that $key may + * repeat for each distinct $persistentId. + * @param double $timeout If the lock can't be immediately obtained, the + * script will block for at most the specified amount of seconds. + * Setting this to 0 makes lock obtaining non blocking, and setting it + * to NULL makes it block without a time limit. + * + * @return bool TRUE on success, FALSE on failure. + */ + public function lock($key, $timeout = null) + { + $lock = $this->persistentId . 'l ' . $key; + $hasTimeout = $timeout !== null; + $start = microtime(true); + while (!apc_add($lock, 1)) { + if ($hasTimeout && (microtime(true) - $start) > $timeout) { + return false; + } + } + static::$locksBackup[$this->persistentId] = $key; + return true; + } + + /** + * Releases a named lock. + * + * @param string $key Name of the key to release. Note that $key may + * repeat for each distinct $persistentId. + * + * @return bool TRUE on success, FALSE on failure. + */ + public function unlock($key) + { + $lock = $this->persistentId . 'l ' . $key; + $success = apc_delete($lock); + if ($success) { + unset( + static::$locksBackup[$this->persistentId][array_search( + $key, + static::$locksBackup[$this->persistentId], + true + )] + ); + return true; + } + return false; + } + + /** + * Checks if a specified key is in the storage. + * + * @param string $key Name of key to check. + * + * @return bool TRUE if the key is in the storage, FALSE otherwise. + */ + public function exists($key) + { + return apc_exists($this->persistentId . 'd ' . $key); + } + + /** + * Adds a value to the shared memory storage. + * + * Adds a value to the storage if it doesn't exist, or fails if it does. + * + * @param string $key Name of key to associate the value with. + * @param mixed $value Value for the specified key. + * @param int $ttl Seconds to store the value. If set to 0 indicates no + * time limit. + * + * @return bool TRUE on success, FALSE on failure. + */ + public function add($key, $value, $ttl = 0) + { + return apc_add($this->persistentId . 'd ' . $key, $value, $ttl); + } + + /** + * Sets a value in the shared memory storage. + * + * Adds a value to the storage if it doesn't exist, overwrites it otherwise. + * + * @param string $key Name of key to associate the value with. + * @param mixed $value Value for the specified key. + * @param int $ttl Seconds to store the value. If set to 0 indicates no + * time limit. + * + * @return bool TRUE on success, FALSE on failure. + */ + public function set($key, $value, $ttl = 0) + { + return apc_store($this->persistentId . 'd ' . $key, $value, $ttl); + } + + /** + * Gets a value from the shared memory storage. + * + * Gets the current value, or throws an exception if it's not stored. + * + * @param string $key Name of key to get the value of. + * + * @return mixed The current value of the specified key. + */ + public function get($key) + { + $fullKey = $this->persistentId . 'd ' . $key; + if (apc_exists($fullKey)) { + $value = apc_fetch($fullKey, $success); + if (!$success) { + throw new SHM\InvalidArgumentException( + 'Unable to fetch key. ' . + 'Key has either just now expired or (if no TTL was set) ' . + 'is possibly in a race condition with another request.', + 100 + ); + } + return $value; + } + throw new SHM\InvalidArgumentException('No such key in cache', 101); + } + + /** + * Deletes a value from the shared memory storage. + * + * @param string $key Name of key to delete. + * + * @return bool TRUE on success, FALSE on failure. + */ + public function delete($key) + { + return apc_delete($this->persistentId . 'd ' . $key); + } + + /** + * Increases a value from the shared memory storage. + * + * Increases a value from the shared memory storage. Unlike a plain + * set($key, get($key)+$step) combination, this function also implicitly + * performs locking. + * + * @param string $key Name of key to increase. + * @param int $step Value to increase the key by. + * + * @return int The new value. + */ + public function inc($key, $step = 1) + { + $newValue = apc_inc( + $this->persistentId . 'd ' . $key, + (int) $step, + $success + ); + if (!$success) { + throw new SHM\InvalidArgumentException( + 'Unable to increase the value. Are you sure the value is int?', + 102 + ); + } + return $newValue; + } + + /** + * Decreases a value from the shared memory storage. + * + * Decreases a value from the shared memory storage. Unlike a plain + * set($key, get($key)-$step) combination, this function also implicitly + * performs locking. + * + * @param string $key Name of key to decrease. + * @param int $step Value to decrease the key by. + * + * @return int The new value. + */ + public function dec($key, $step = 1) + { + $newValue = apc_dec( + $this->persistentId . 'd ' . $key, + (int) $step, + $success + ); + if (!$success) { + throw new SHM\InvalidArgumentException( + 'Unable to decrease the value. Are you sure the value is int?', + 103 + ); + } + return $newValue; + } + + /** + * Sets a new value if a key has a certain value. + * + * Sets a new value if a key has a certain value. This function only works + * when $old and $new are longs. + * + * @param string $key Key of the value to compare and set. + * @param int $old The value to compare the key against. + * @param int $new The value to set the key to. + * + * @return bool TRUE on success, FALSE on failure. + */ + public function cas($key, $old, $new) + { + return apc_cas($this->persistentId . 'd ' . $key, $old, $new); + } + + /** + * Clears the persistent storage. + * + * Clears the persistent storage, i.e. removes all keys. Locks are left + * intact. + * + * @return void + */ + public function clear() + { + foreach (new APCIterator( + 'user', + '/^' . preg_quote($this->persistentId, '/') . 'd /', + APC_ITER_KEY, + 100, + APC_LIST_ACTIVE + ) as $key) { + apc_delete($key); + } + } + + /** + * Retrieve an external iterator + * + * Returns an external iterator. + * + * @param string|null $filter A PCRE regular expression. + * Only matching keys will be iterated over. + * Setting this to NULL matches all keys of this instance. + * @param bool $keysOnly Whether to return only the keys, + * or return both the keys and values. + * + * @return ArrayObject An array with all matching keys as array keys, + * and values as array values. If $keysOnly is TRUE, the array keys are + * numeric, and the array values are key names. + */ + public function getIterator($filter = null, $keysOnly = false) + { + $result = array(); + foreach (new APCIterator( + 'user', + '/^' . preg_quote($this->persistentId, '/') . 'd /', + APC_ITER_KEY, + 100, + APC_LIST_ACTIVE + ) as $key) { + $localKey = strstr($key, $this->persistentId . 'd '); + if (null === $filter || preg_match($filter, $localKey)) { + if ($keysOnly) { + $result[] = $localKey; + } else { + $result[$localKey] = apc_fetch($key); + } + } + } + return new ArrayObject($result); + } +} diff --git a/system/autoload/PEAR2/Cache/SHM/Adaptor/APCu.php b/system/autoload/PEAR2/Cache/SHM/Adaptor/APCu.php new file mode 100644 index 0000000..534b59e --- /dev/null +++ b/system/autoload/PEAR2/Cache/SHM/Adaptor/APCu.php @@ -0,0 +1,416 @@ + + * @copyright 2011 Vasil Rangelov + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version 0.2.0 + * @link http://pear2.php.net/PEAR2_Cache_SHM + */ +/** + * The namespace declaration. + */ +namespace PEAR2\Cache\SHM\Adapter; + +/** + * Throws exceptions from this namespace, and extends from this class. + */ +use PEAR2\Cache\SHM; + +/** + * {@link APC::getIterator()} returns this object. + */ +use ArrayObject; + +/** + * Shared memory adapter for the APC extension. + * + * @category Caching + * @package PEAR2_Cache_SHM + * @author Vasil Rangelov + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @link http://pear2.php.net/PEAR2_Cache_SHM + */ +class APCu extends SHM +{ + /** + * ID of the current storage. + * + * @var string + */ + protected $persistentId; + + /** + * List of persistent IDs. + * + * A list of persistent IDs within the current request (as keys) with an int + * (as a value) specifying the number of instances in the current request. + * Used as an attempt to ensure implicit lock releases even on errors in the + * critical sections, since APC doesn't have an actual locking function. + * + * @var array + */ + protected static $requestInstances = array(); + + /** + * Array of lock names for each persistent ID. + * + * Array of lock names (as values) for each persistent ID (as key) obtained + * during the current request. + * + * @var array + */ + protected static $locksBackup = array(); + + /** + * Creates a new shared memory storage. + * + * Establishes a separate persistent storage. + * + * @param string $persistentId The ID for the storage. The storage will be + * reused if it exists, or created if it doesn't exist. Data and locks + * are namespaced by this ID. + */ + public function __construct($persistentId) + { + $this->persistentId = __CLASS__ . ' ' . $persistentId; + if (isset(static::$requestInstances[$this->persistentId])) { + static::$requestInstances[$this->persistentId]++; + } else { + static::$requestInstances[$this->persistentId] = 1; + static::$locksBackup[$this->persistentId] = array(); + } + register_shutdown_function( + get_called_class() . '::releaseLocks', + $this->persistentId, + true + ); + } + + /** + * Checks if the adapter meets its requirements. + * + * @return bool TRUE on success, FALSE on failure. + */ + public static function isMeetingRequirements() + { + return extension_loaded('apcu') + && version_compare(phpversion('apcu'), '5.0.0', '>=') + && ini_get('apc.enabled') + && ('cli' !== PHP_SAPI || ini_get('apc.enable_cli')); + } + + /** + * Releases all locks in a storage. + * + * This function is not meant to be used directly. It is implicitly called + * by the the destructor and as a shutdown function when the request ends. + * One of these calls ends up releasing any unreleased locks obtained + * during the request. A lock is also implicitly released as soon as there + * are no objects left in the current request using the same persistent ID. + * + * @param string $internalPersistentId The internal persistent ID, the locks + * of which are being released. + * @param bool $isAtShutdown Whether the function was executed at + * shutdown. + * + * @return void + * + * @internal + */ + public static function releaseLocks($internalPersistentId, $isAtShutdown) + { + $hasInstances = 0 !== static::$requestInstances[$internalPersistentId]; + if ($isAtShutdown === $hasInstances) { + foreach (static::$locksBackup[$internalPersistentId] as $key) { + apcu_delete($internalPersistentId . 'l ' . $key); + } + } + } + + /** + * Releases any locks obtained by this instance as soon as there are no more + * references to the object's persistent ID. + */ + public function __destruct() + { + static::$requestInstances[$this->persistentId]--; + static::releaseLocks($this->persistentId, false); + } + + + /** + * Obtains a named lock. + * + * @param string $key Name of the key to obtain. Note that $key may + * repeat for each distinct $persistentId. + * @param double $timeout If the lock can't be immediately obtained, the + * script will block for at most the specified amount of seconds. + * Setting this to 0 makes lock obtaining non blocking, and setting it + * to NULL makes it block without a time limit. + * + * @return bool TRUE on success, FALSE on failure. + */ + public function lock($key, $timeout = null) + { + $lock = $this->persistentId . 'l ' . $key; + $hasTimeout = $timeout !== null; + $start = microtime(true); + while (!apcu_add($lock, 1)) { + if ($hasTimeout && (microtime(true) - $start) > $timeout) { + return false; + } + } + static::$locksBackup[$this->persistentId] = $key; + return true; + } + + /** + * Releases a named lock. + * + * @param string $key Name of the key to release. Note that $key may + * repeat for each distinct $persistentId. + * + * @return bool TRUE on success, FALSE on failure. + */ + public function unlock($key) + { + $lock = $this->persistentId . 'l ' . $key; + $success = apcu_delete($lock); + if ($success) { + unset( + static::$locksBackup[$this->persistentId][array_search( + $key, + static::$locksBackup[$this->persistentId], + true + )] + ); + return true; + } + return false; + } + + /** + * Checks if a specified key is in the storage. + * + * @param string $key Name of key to check. + * + * @return bool TRUE if the key is in the storage, FALSE otherwise. + */ + public function exists($key) + { + return apcu_exists($this->persistentId . 'd ' . $key); + } + + /** + * Adds a value to the shared memory storage. + * + * Adds a value to the storage if it doesn't exist, or fails if it does. + * + * @param string $key Name of key to associate the value with. + * @param mixed $value Value for the specified key. + * @param int $ttl Seconds to store the value. If set to 0 indicates no + * time limit. + * + * @return bool TRUE on success, FALSE on failure. + */ + public function add($key, $value, $ttl = 0) + { + return apcu_add($this->persistentId . 'd ' . $key, $value, $ttl); + } + + /** + * Sets a value in the shared memory storage. + * + * Adds a value to the storage if it doesn't exist, overwrites it otherwise. + * + * @param string $key Name of key to associate the value with. + * @param mixed $value Value for the specified key. + * @param int $ttl Seconds to store the value. If set to 0 indicates no + * time limit. + * + * @return bool TRUE on success, FALSE on failure. + */ + public function set($key, $value, $ttl = 0) + { + return apcu_store($this->persistentId . 'd ' . $key, $value, $ttl); + } + + /** + * Gets a value from the shared memory storage. + * + * Gets the current value, or throws an exception if it's not stored. + * + * @param string $key Name of key to get the value of. + * + * @return mixed The current value of the specified key. + */ + public function get($key) + { + $fullKey = $this->persistentId . 'd ' . $key; + if (apcu_exists($fullKey)) { + $value = apcu_fetch($fullKey, $success); + if (!$success) { + throw new SHM\InvalidArgumentException( + 'Unable to fetch key. ' . + 'Key has either just now expired or (if no TTL was set) ' . + 'is possibly in a race condition with another request.', + 100 + ); + } + return $value; + } + throw new SHM\InvalidArgumentException('No such key in cache', 101); + } + + /** + * Deletes a value from the shared memory storage. + * + * @param string $key Name of key to delete. + * + * @return bool TRUE on success, FALSE on failure. + */ + public function delete($key) + { + return apcu_delete($this->persistentId . 'd ' . $key); + } + + /** + * Increases a value from the shared memory storage. + * + * Increases a value from the shared memory storage. Unlike a plain + * set($key, get($key)+$step) combination, this function also implicitly + * performs locking. + * + * @param string $key Name of key to increase. + * @param int $step Value to increase the key by. + * + * @return int The new value. + */ + public function inc($key, $step = 1) + { + $newValue = apcu_inc( + $this->persistentId . 'd ' . $key, + (int) $step, + $success + ); + if (!$success) { + throw new SHM\InvalidArgumentException( + 'Unable to increase the value. Are you sure the value is int?', + 102 + ); + } + return $newValue; + } + + /** + * Decreases a value from the shared memory storage. + * + * Decreases a value from the shared memory storage. Unlike a plain + * set($key, get($key)-$step) combination, this function also implicitly + * performs locking. + * + * @param string $key Name of key to decrease. + * @param int $step Value to decrease the key by. + * + * @return int The new value. + */ + public function dec($key, $step = 1) + { + $newValue = apcu_dec( + $this->persistentId . 'd ' . $key, + (int) $step, + $success + ); + if (!$success) { + throw new SHM\InvalidArgumentException( + 'Unable to decrease the value. Are you sure the value is int?', + 103 + ); + } + return $newValue; + } + + /** + * Sets a new value if a key has a certain value. + * + * Sets a new value if a key has a certain value. This function only works + * when $old and $new are longs. + * + * @param string $key Key of the value to compare and set. + * @param int $old The value to compare the key against. + * @param int $new The value to set the key to. + * + * @return bool TRUE on success, FALSE on failure. + */ + public function cas($key, $old, $new) + { + return apcu_cas($this->persistentId . 'd ' . $key, $old, $new); + } + + /** + * Clears the persistent storage. + * + * Clears the persistent storage, i.e. removes all keys. Locks are left + * intact. + * + * @return void + */ + public function clear() + { + foreach (new APCIterator( + 'user', + '/^' . preg_quote($this->persistentId, '/') . 'd /', + APC_ITER_KEY, + 100, + APC_LIST_ACTIVE + ) as $key) { + apcu_delete($key); + } + } + + /** + * Retrieve an external iterator + * + * Returns an external iterator. + * + * @param string|null $filter A PCRE regular expression. + * Only matching keys will be iterated over. + * Setting this to NULL matches all keys of this instance. + * @param bool $keysOnly Whether to return only the keys, + * or return both the keys and values. + * + * @return ArrayObject An array with all matching keys as array keys, + * and values as array values. If $keysOnly is TRUE, the array keys are + * numeric, and the array values are key names. + */ + public function getIterator($filter = null, $keysOnly = false) + { + $result = array(); + foreach (new APCUIterator( + '/^' . preg_quote($this->persistentId, '/') . 'd /', + APC_ITER_KEY, + 100, + APC_LIST_ACTIVE + ) as $key) { + $localKey = strstr($key, $this->persistentId . 'd '); + if (null === $filter || preg_match($filter, $localKey)) { + if ($keysOnly) { + $result[] = $localKey; + } else { + $result[$localKey] = apcu_fetch($key); + } + } + } + return new ArrayObject($result); + } +} diff --git a/system/autoload/PEAR2/Cache/SHM/Adaptor/Placebo.php b/system/autoload/PEAR2/Cache/SHM/Adaptor/Placebo.php new file mode 100644 index 0000000..56c35f3 --- /dev/null +++ b/system/autoload/PEAR2/Cache/SHM/Adaptor/Placebo.php @@ -0,0 +1,369 @@ + + * @copyright 2011 Vasil Rangelov + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version 0.2.0 + * @link http://pear2.php.net/PEAR2_Cache_SHM + */ +/** + * The namespace declaration. + */ +namespace PEAR2\Cache\SHM\Adapter; + +/** + * Throws exceptions from this namespace, and extends from this class. + */ +use PEAR2\Cache\SHM; + +/** + * {@link Placebo::getIterator()} returns this object. + */ +use ArrayObject; + +/** + * This adapter is not truly persistent. It is intended to emulate persistence + * in non persistent environments, so that upper level applications can use a + * single code path for persistent and non persistent code. + * + * @category Caching + * @package PEAR2_Cache_SHM + * @author Vasil Rangelov + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @link http://pear2.php.net/PEAR2_Cache_SHM + */ +class Placebo extends SHM +{ + /** + * ID of the current storage. + * + * @var string + */ + protected $persistentId; + + /** + * List of persistent IDs. + * + * A list of persistent IDs within the current request (as keys) with an int + * (as a value) specifying the number of instances in the current request. + * Used as an attempt to ensure implicit lock releases on destruction. + * + * @var array + */ + protected static $requestInstances = array(); + + /** + * Array of lock names for each persistent ID. + * + * Array of lock names (as values) for each persistent ID (as + * key) obtained during the current request. + * + * @var array + */ + protected static $locksBackup = array(); + + /** + * The data storage. + * + * Each persistent ID is a key, and the value is an array. + * Each such array has data keys as its keys, and an array as a value. + * Each such array has as its elements the value, the timeout and the time + * the data was set. + * + * @var array + */ + protected static $data = array(); + + /** + * Creates a new shared memory storage. + * + * Establishes a separate persistent storage. + * + * @param string $persistentId The ID for the storage. The storage will be + * reused if it exists, or created if it doesn't exist. Data and locks + * are namespaced by this ID. + */ + public function __construct($persistentId) + { + if (isset(static::$requestInstances[$persistentId])) { + ++static::$requestInstances[$persistentId]; + } else { + static::$requestInstances[$persistentId] = 1; + static::$locksBackup[$persistentId] = array(); + static::$data[$persistentId] = array(); + } + $this->persistentId = $persistentId; + } + + /** + * Releases any unreleased locks. + */ + public function __destruct() + { + if (0 === --static::$requestInstances[$this->persistentId]) { + static::$locksBackup[$this->persistentId] = array(); + } + } + + /** + * Checks if the adapter meets its requirements. + * + * @return bool TRUE on success, FALSE on failure. + */ + public static function isMeetingRequirements() + { + return 'cli' === PHP_SAPI; + } + + /** + * Pretends to obtain a lock. + * + * @param string $key Ignored. + * @param double $timeout Ignored. + * + * @return bool TRUE on success, FALSE on failure. + */ + public function lock($key, $timeout = null) + { + $key = (string) $key; + if (in_array($key, static::$locksBackup[$this->persistentId], true)) { + return false; + } + static::$locksBackup[$this->persistentId][] = $key; + return true; + } + + /** + * Pretends to release a lock. + * + * @param string $key Ignored + * + * @return bool TRUE on success, FALSE on failure. + */ + public function unlock($key) + { + $key = (string) $key; + if (!in_array($key, static::$locksBackup[$this->persistentId], true)) { + return false; + } + unset( + static::$locksBackup[$this->persistentId][array_search( + $key, + static::$locksBackup[$this->persistentId], + true + )] + ); + return true; + } + + /** + * Checks if a specified key is in the storage. + * + * @param string $key Name of key to check. + * + * @return bool TRUE if the key is in the storage, FALSE otherwise. + */ + public function exists($key) + { + return array_key_exists($key, static::$data[$this->persistentId]); + } + + /** + * Adds a value to the shared memory storage. + * + * Adds a value to the storage if it doesn't exist, or fails if it does. + * + * @param string $key Name of key to associate the value with. + * @param mixed $value Value for the specified key. + * @param int $ttl Because "true" adapters purge the cache at the next + * request, this setting is ignored. + * + * @return bool TRUE on success, FALSE on failure. + */ + public function add($key, $value, $ttl = 0) + { + if ($this->exists($key)) { + return false; + } + return $this->set($key, $value, $ttl); + } + + /** + * Sets a value in the shared memory storage. + * + * Adds a value to the storage if it doesn't exist, overwrites it otherwise. + * + * @param string $key Name of key to associate the value with. + * @param mixed $value Value for the specified key. + * @param int $ttl Because "true" adapters purge the cache at the next + * request, this setting is ignored. + * + * @return bool TRUE on success, FALSE on failure. + */ + public function set($key, $value, $ttl = 0) + { + static::$data[$this->persistentId][$key] = $value; + return true; + } + + /** + * Gets a value from the shared memory storage. + * + * Gets the current value, or throws an exception if it's not stored. + * + * @param string $key Name of key to get the value of. + * + * @return mixed The current value of the specified key. + */ + public function get($key) + { + if ($this->exists($key)) { + return static::$data[$this->persistentId][$key]; + } + throw new SHM\InvalidArgumentException( + 'Unable to fetch key. No such key.', + 200 + ); + } + + /** + * Deletes a value from the shared memory storage. + * + * @param string $key Name of key to delete. + * + * @return bool TRUE on success, FALSE on failure. + */ + public function delete($key) + { + if ($this->exists($key)) { + unset(static::$data[$this->persistentId][$key]); + return true; + } + return false; + } + + /** + * Increases a value from the shared memory storage. + * + * Increases a value from the shared memory storage. Unlike a plain + * set($key, get($key)+$step) combination, this function also implicitly + * performs locking. + * + * @param string $key Name of key to increase. + * @param int $step Value to increase the key by. + * + * @return int The new value. + */ + public function inc($key, $step = 1) + { + if (!$this->exists($key) || !is_int($value = $this->get($key)) + || !$this->set($key, $value + (int) $step) + ) { + throw new SHM\InvalidArgumentException( + 'Unable to increase the value. Are you sure the value is int?', + 201 + ); + } + return $this->get($key); + } + + /** + * Decreases a value from the shared memory storage. + * + * Decreases a value from the shared memory storage. Unlike a plain + * set($key, get($key)-$step) combination, this function also implicitly + * performs locking. + * + * @param string $key Name of key to decrease. + * @param int $step Value to decrease the key by. + * + * @return int The new value. + */ + public function dec($key, $step = 1) + { + if (!$this->exists($key) || !is_int($value = $this->get($key)) + || !$this->set($key, $value - (int) $step) + ) { + throw new SHM\InvalidArgumentException( + 'Unable to increase the value. Are you sure the value is int?', + 202 + ); + } + return $this->get($key); + } + + /** + * Sets a new value if a key has a certain value. + * + * Sets a new value if a key has a certain value. This function only works + * when $old and $new are longs. + * + * @param string $key Key of the value to compare and set. + * @param int $old The value to compare the key against. + * @param int $new The value to set the key to. + * + * @return bool TRUE on success, FALSE on failure. + */ + public function cas($key, $old, $new) + { + return $this->exists($key) && ($this->get($key) === $old) + && is_int($new) && $this->set($key, $new); + } + + /** + * Clears the persistent storage. + * + * Clears the persistent storage, i.e. removes all keys. Locks are left + * intact. + * + * @return void + */ + public function clear() + { + static::$data[$this->persistentId] = array(); + } + + /** + * Retrieve an external iterator + * + * Returns an external iterator. + * + * @param string|null $filter A PCRE regular expression. + * Only matching keys will be iterated over. + * Setting this to NULL matches all keys of this instance. + * @param bool $keysOnly Whether to return only the keys, + * or return both the keys and values. + * + * @return ArrayObject An array with all matching keys as array keys, + * and values as array values. If $keysOnly is TRUE, the array keys are + * numeric, and the array values are key names. + */ + public function getIterator($filter = null, $keysOnly = false) + { + if (null === $filter) { + return new ArrayObject( + $keysOnly + ? array_keys(static::$data[$this->persistentId]) + : static::$data[$this->persistentId] + ); + } + + $result = array(); + foreach (static::$data[$this->persistentId] as $key => $value) { + if (preg_match($filter, $key)) { + $result[$key] = $value; + } + } + return new ArrayObject($keysOnly ? array_keys($result) : $result); + } +} diff --git a/system/autoload/PEAR2/Cache/SHM/Adaptor/Wincache.php b/system/autoload/PEAR2/Cache/SHM/Adaptor/Wincache.php new file mode 100644 index 0000000..5633f45 --- /dev/null +++ b/system/autoload/PEAR2/Cache/SHM/Adaptor/Wincache.php @@ -0,0 +1,392 @@ + + * @copyright 2011 Vasil Rangelov + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version 0.2.0 + * @link http://pear2.php.net/PEAR2_Cache_SHM + */ +/** + * The namespace declaration. + */ +namespace PEAR2\Cache\SHM\Adapter; + +/** + * Throws exceptions from this namespace, and extends from this class. + */ +use PEAR2\Cache\SHM; + +/** + * {@link Wincache::getIterator()} returns this object. + */ +use ArrayObject; + +/** + * Shared memory adapter for the WinCache extension. + * + * @category Caching + * @package PEAR2_Cache_SHM + * @author Vasil Rangelov + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @link http://pear2.php.net/PEAR2_Cache_SHM + */ +class Wincache extends SHM +{ + /** + * ID of the current storage. + * + * @var string + */ + protected $persistentId; + + /** + * List of persistent IDs. + * + * A list of persistent IDs within the current request (as keys) with an int + * (as a value) specifying the number of instances in the current request. + * Used as an attempt to ensure implicit lock releases on destruction. + * + * @var array + */ + protected static $requestInstances = array(); + + /** + * Array of lock names obtained during the current request. + * + * @var array + */ + protected static $locksBackup = array(); + + /** + * Creates a new shared memory storage. + * + * Establishes a separate persistent storage. + * + * @param string $persistentId The ID for the storage. The storage will be + * reused if it exists, or created if it doesn't exist. Data and locks + * are namespaced by this ID. + */ + public function __construct($persistentId) + { + $this->persistentId + = static::encodeLockName(__CLASS__ . ' ' . $persistentId) . ' '; + if (isset(static::$requestInstances[$this->persistentId])) { + static::$requestInstances[$this->persistentId]++; + } else { + static::$requestInstances[$this->persistentId] = 1; + static::$locksBackup[$this->persistentId] = array(); + } + } + + /** + * Encodes a lock name + * + * Encodes a lock name, so that it can be properly obtained. The scheme used + * is a subset of URL encoding, with only the "%" and "\" characters being + * escaped. The encoding itself is necessary, since lock names can't contain + * the "\" character. + * + * @param string $name The lock name to encode. + * + * @return string The encoded name. + * + * @link http://msdn.microsoft.com/en-us/library/ms682411(VS.85).aspx + */ + protected static function encodeLockName($name) + { + return str_replace(array('%', '\\'), array('%25', '%5C'), $name); + } + + /** + * Checks if the adapter meets its requirements. + * + * @return bool TRUE on success, FALSE on failure. + */ + public static function isMeetingRequirements() + { + return extension_loaded('wincache') + && version_compare(phpversion('wincache'), '1.1.0', '>=') + && ini_get('wincache.ucenabled') + && ('cli' !== PHP_SAPI || ini_get('wincache.enablecli')); + } + + /** + * Releases any locks obtained by this instance as soon as there are no more + * references to the object's persistent ID. + */ + public function __destruct() + { + if (0 === --static::$requestInstances[$this->persistentId]) { + foreach (static::$locksBackup[$this->persistentId] as $key) { + wincache_unlock( + $this->persistentId . static::encodeLockName($key) + ); + } + } + } + + + /** + * Obtains a named lock. + * + * @param string $key Name of the key to obtain. Note that $key may + * repeat for each distinct $persistentId. + * @param double $timeout Ignored with WinCache. Script will always block if + * the lock can't be immediately obtained. + * + * @return bool TRUE on success, FALSE on failure. + */ + public function lock($key, $timeout = null) + { + $result = wincache_lock( + $this->persistentId . static::encodeLockName($key) + ); + if ($result) { + static::$locksBackup[$this->persistentId] = $key; + } + return $result; + } + + /** + * Releases a named lock. + * + * @param string $key Name of the key to release. Note that $key may + * repeat for each distinct $persistentId. + * + * @return bool TRUE on success, FALSE on failure. + */ + public function unlock($key) + { + $result = wincache_unlock( + $this->persistentId . static::encodeLockName($key) + ); + if ($result) { + unset( + static::$locksBackup[$this->persistentId][array_search( + $key, + static::$locksBackup[$this->persistentId], + true + )] + ); + } + return $result; + } + + /** + * Checks if a specified key is in the storage. + * + * @param string $key Name of key to check. + * + * @return bool TRUE if the key is in the storage, FALSE otherwise. + */ + public function exists($key) + { + return wincache_ucache_exists($this->persistentId . $key); + } + + /** + * Adds a value to the shared memory storage. + * + * Sets a value to the storage if it doesn't exist, or fails if it does. + * + * @param string $key Name of key to associate the value with. + * @param mixed $value Value for the specified key. + * @param int $ttl Seconds to store the value. If set to 0 indicates no + * time limit. + * + * @return bool TRUE on success, FALSE on failure. + */ + public function add($key, $value, $ttl = 0) + { + return wincache_ucache_add($this->persistentId . $key, $value, $ttl); + } + + /** + * Sets a value in the shared memory storage. + * + * Adds a value to the storage if it doesn't exist, overwrites it otherwise. + * + * @param string $key Name of key to associate the value with. + * @param mixed $value Value for the specified key. + * @param int $ttl Seconds to store the value. If set to 0 indicates no + * time limit. + * + * @return bool TRUE on success, FALSE on failure. + */ + public function set($key, $value, $ttl = 0) + { + return wincache_ucache_set($this->persistentId . $key, $value, $ttl); + } + + /** + * Gets a value from the shared memory storage. + * + * Gets the current value, or throws an exception if it's not stored. + * + * @param string $key Name of key to get the value of. + * + * @return mixed The current value of the specified key. + */ + public function get($key) + { + $value = wincache_ucache_get($this->persistentId . $key, $success); + if (!$success) { + throw new SHM\InvalidArgumentException( + 'Unable to fetch key. No such key, or key has expired.', + 300 + ); + } + return $value; + } + + /** + * Deletes a value from the shared memory storage. + * + * @param string $key Name of key to delete. + * + * @return bool TRUE on success, FALSE on failure. + */ + public function delete($key) + { + return wincache_ucache_delete($this->persistentId . $key); + } + + /** + * Increases a value from the shared memory storage. + * + * Increases a value from the shared memory storage. Unlike a plain + * set($key, get($key)+$step) combination, this function also implicitly + * performs locking. + * + * @param string $key Name of key to increase. + * @param int $step Value to increase the key by. + * + * @return int The new value. + */ + public function inc($key, $step = 1) + { + $newValue = wincache_ucache_inc( + $this->persistentId . $key, + (int) $step, + $success + ); + if (!$success) { + throw new SHM\InvalidArgumentException( + 'Unable to increase the value. Are you sure the value is int?', + 301 + ); + } + return $newValue; + } + + /** + * Decreases a value from the shared memory storage. + * + * Decreases a value from the shared memory storage. Unlike a plain + * set($key, get($key)-$step) combination, this function also implicitly + * performs locking. + * + * @param string $key Name of key to decrease. + * @param int $step Value to decrease the key by. + * + * @return int The new value. + */ + public function dec($key, $step = 1) + { + $newValue = wincache_ucache_dec( + $this->persistentId . $key, + (int) $step, + $success + ); + if (!$success) { + throw new SHM\InvalidArgumentException( + 'Unable to decrease the value. Are you sure the value is int?', + 302 + ); + } + return $newValue; + } + + /** + * Sets a new value if a key has a certain value. + * + * Sets a new value if a key has a certain value. This function only works + * when $old and $new are longs. + * + * @param string $key Key of the value to compare and set. + * @param int $old The value to compare the key against. + * @param int $new The value to set the key to. + * + * @return bool TRUE on success, FALSE on failure. + */ + public function cas($key, $old, $new) + { + return wincache_ucache_cas($this->persistentId . $key, $old, $new); + } + + /** + * Clears the persistent storage. + * + * Clears the persistent storage, i.e. removes all keys. Locks are left + * intact. + * + * @return void + */ + public function clear() + { + $info = wincache_ucache_info(); + foreach ($info['ucache_entries'] as $entry) { + if (!$entry['is_session'] + && 0 === strpos($entry['key_name'], $this->persistentId) + ) { + wincache_ucache_delete($entry['key_name']); + } + } + } + + /** + * Retrieve an external iterator + * + * Returns an external iterator. + * + * @param string|null $filter A PCRE regular expression. + * Only matching keys will be iterated over. + * Setting this to NULL matches all keys of this instance. + * @param bool $keysOnly Whether to return only the keys, + * or return both the keys and values. + * + * @return ArrayObject An array with all matching keys as array keys, + * and values as array values. If $keysOnly is TRUE, the array keys are + * numeric, and the array values are key names. + */ + public function getIterator($filter = null, $keysOnly = false) + { + $info = wincache_ucache_info(); + $result = array(); + foreach ($info['ucache_entries'] as $entry) { + if (!$entry['is_session'] + && 0 === strpos($entry['key_name'], $this->persistentId) + ) { + $localKey = strstr($entry['key_name'], $this->persistentId); + if (null === $filter || preg_match($filter, $localKey)) { + if ($keysOnly) { + $result[] = $localKey; + } else { + $result[$localKey] = apc_fetch($localKey); + } + } + } + } + return new ArrayObject($result); + } +}