diff --git a/CHANGELOG.md b/CHANGELOG.md index 7832df8d..d766a320 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,46 @@ +## Stash v1.0.0 Changelog + +### v1.0.0 + +* Implemented PSR-6 interfaces. + +* Removed `Driver::setOptions($options)` in favor of `Driver::constructor($options)` + +* Removed deprecated DriverList::getDrivers function. + +* Removed deprecated invalidation constants in the Item class. + +* Removed SQLite Extension support (SQLite3 is still available). + +* The `set` function no longer persists data. + +* Removed expiration time for `set` function + +* Added `expiresAt` and `expiresAfter` functions to the Item class. + +* `getExpiration` to return current datetime when no record exists. + +* Added `save` function to PoolInterface. + +* Changed `getItemIterator` to `getItems` + +* RuntimeException now extends from \RuntimeException + +* Added `isHit` function to ItemInterface. + +* Added the `hasItem` function to the Pool, which should mostly be avoided. + +* Renamed `Pool::purge` to `Pool::clear`. + +* Added `Pool::deleteItem` and `Pool::deleteItems`. + +* Removed legacy methods for defining keys- keys must be defined as strings. + +* Added support for "APCU" functions. + +* Removed sqlite2 support (sqlite3 is still supported). + + ## Stash v0.13 Changelog diff --git a/composer.json b/composer.json index ebe8656d..86ebd7a5 100644 --- a/composer.json +++ b/composer.json @@ -7,7 +7,9 @@ "sessions", "memcached", "redis", - "apc" + "apc", + "psr-6", + "psr6" ], "homepage": "http://github.com/tedious/Stash", "type": "library", @@ -23,16 +25,20 @@ } ], "require": { - "php": "^5.4|^7.0" + "php": "^5.4|^7.0", + "psr/cache": "~1.0" }, "require-dev": { "fabpot/php-cs-fixer": "^1.9", - "phpunit/phpunit": "4.7.*", + "phpunit/phpunit": "4.8.*", "satooshi/php-coveralls": "1.0.*" }, "autoload": { "psr-4": { "Stash\\": "src/Stash/" } + }, + "provide": { + "psr/cache-implementation": "1.0.0" } } diff --git a/src/Stash/Driver/AbstractDriver.php b/src/Stash/Driver/AbstractDriver.php index cb20cbb7..68b7150f 100644 --- a/src/Stash/Driver/AbstractDriver.php +++ b/src/Stash/Driver/AbstractDriver.php @@ -50,7 +50,7 @@ public function getDefaultOptions() /** * {@inheritdoc} */ - public function setOptions(array $options = array()) + protected function setOptions(array $options = array()) { // empty } diff --git a/src/Stash/Driver/Apc.php b/src/Stash/Driver/Apc.php index 5be0ab1d..208da4c4 100644 --- a/src/Stash/Driver/Apc.php +++ b/src/Stash/Driver/Apc.php @@ -36,6 +36,14 @@ class Apc extends AbstractDriver */ protected $apcNamespace; + /** + * Whether to use the APCu functions or the original APC ones. + * + * @var string + */ + protected $apcu = false; + + /** * The number of records \ApcIterator will grab at once. * @@ -51,6 +59,10 @@ public function getDefaultOptions() return array( 'ttl' => 300, 'namespace' => md5(__FILE__), + + // Test using the APCUIterator, as some versions of APCU have the + // custom functions but not the iterator class. + 'apcu' => class_exists('\APCUIterator') ); } @@ -62,12 +74,13 @@ public function getDefaultOptions() * * @param array $options */ - public function setOptions(array $options = array()) + protected function setOptions(array $options = array()) { $options += $this->getDefaultOptions(); $this->ttl = (int) $options['ttl']; $this->apcNamespace = $options['namespace']; + $this->apcu = $options['apcu']; } /** @@ -77,7 +90,7 @@ public function getData($key) { $keyString = self::makeKey($key); $success = null; - $data = apc_fetch($keyString, $success); + $data = $this->apcu ? apcu_fetch($keyString, $success) : apc_fetch($keyString, $success); return $success ? $data : false; } @@ -88,8 +101,10 @@ public function getData($key) public function storeData($key, $data, $expiration) { $life = $this->getCacheTime($expiration); + $apckey = $this->makeKey($key); + $store = array('data' => $data, 'expiration' => $expiration); - return apc_store($this->makeKey($key), array('data' => $data, 'expiration' => $expiration), $life); + return $this->apcu ? apcu_store($apckey, $store, $life) : apc_store($apckey, $store, $life); } /** @@ -98,17 +113,18 @@ public function storeData($key, $data, $expiration) public function clear($key = null) { if (!isset($key)) { - return apc_clear_cache('user'); + return $this->apcu ? apcu_clear_cache() : apc_clear_cache('user'); } else { $keyRegex = '[' . $this->makeKey($key) . '*]'; $chunkSize = isset($this->chunkSize) && is_numeric($this->chunkSize) ? $this->chunkSize : 100; do { $emptyIterator = true; - $it = new \APCIterator('user', $keyRegex, \APC_ITER_KEY, $chunkSize); + $it = $this->apcu ? new \APCUIterator($keyRegex, \APC_ITER_KEY, $chunkSize) : new \APCIterator('user', $keyRegex, \APC_ITER_KEY, $chunkSize); + foreach ($it as $item) { $emptyIterator = false; - apc_delete($item['key']); + $this->apcu ? apcu_delete($item['key']) : apc_delete($item['key']); } } while (!$emptyIterator); } @@ -124,14 +140,13 @@ public function purge() $now = time(); $keyRegex = '[' . $this->makeKey(array()) . '*]'; $chunkSize = isset($this->chunkSize) && is_numeric($this->chunkSize) ? $this->chunkSize : 100; - - $it = new \APCIterator('user', $keyRegex, \APC_ITER_KEY, $chunkSize); + $it = $this->apcu ? new \APCUIterator($keyRegex, \APC_ITER_KEY, $chunkSize) : new \APCIterator('user', $keyRegex, \APC_ITER_KEY, $chunkSize); foreach ($it as $item) { $success = null; - $data = apc_fetch($item['key'], $success); + $data = $this->apcu ? apcu_fetch($item['key'], $success): apc_fetch($item['key'], $success); if ($success && is_array($data) && $data['expiration'] <= $now) { - apc_delete($item['key']); + $this->apcu ? apcu_delete($item['key']) : apc_delete($item['key']); } } @@ -145,13 +160,12 @@ public function purge() */ public static function isAvailable() { - // HHVM has some of the APC extension, but not all of it. - if (!class_exists('\APCIterator')) { + // Some versions of HHVM are missing the APCIterator + if (!class_exists('\APCIterator') && !class_exists('\APCUIterator')) { return false; } - return (extension_loaded('apc') && ini_get('apc.enabled')) - && ((php_sapi_name() !== 'cli') || ini_get('apc.enable_cli')); + return function_exists('apcu_fetch') || function_exists('apc_fetch'); } /** @@ -187,4 +201,13 @@ protected function getCacheTime($expiration) return $this->ttl < $life ? $this->ttl : $life; } + + + /** + * {@inheritdoc} + */ + public function isPersistent() + { + return true; + } } diff --git a/src/Stash/Driver/Composite.php b/src/Stash/Driver/Composite.php index bc1bdf74..3875522e 100644 --- a/src/Stash/Driver/Composite.php +++ b/src/Stash/Driver/Composite.php @@ -13,6 +13,7 @@ use Stash; use Stash\Exception\RuntimeException; +use Stash\Exception\InvalidArgumentException; use Stash\Interfaces\DriverInterface; /** @@ -39,26 +40,28 @@ class Composite extends AbstractDriver * * @throws \Stash\Exception\RuntimeException */ - public function setOptions(array $options = array()) + protected function setOptions(array $options = array()) { $options += $this->getDefaultOptions(); - if (isset($options['drivers'])) { - if (count($options['drivers']) < 1) { - throw new RuntimeException('One or more secondary drivers are required.'); - } - $this->drivers = array(); + if (!isset($options['drivers']) || (count($options['drivers']) < 1)) { + throw new RuntimeException('One or more secondary drivers are required.'); + } - foreach ($options['drivers'] as $driver) { - if (!(is_object($driver) && $driver instanceof DriverInterface)) { - continue; - } - $this->drivers[] = $driver; - } + if (!is_array($options['drivers'])) { + throw new InvalidArgumentException('Drivers option requires an array.'); + } - if (count($this->drivers) < 1) { - throw new RuntimeException('None of the secondary drivers can be enabled.'); + $this->drivers = array(); + foreach ($options['drivers'] as $driver) { + if (!(is_object($driver) && $driver instanceof DriverInterface)) { + continue; } + $this->drivers[] = $driver; + } + + if (count($this->drivers) < 1) { + throw new RuntimeException('None of the secondary drivers can be enabled.'); } } diff --git a/src/Stash/Driver/FileSystem.php b/src/Stash/Driver/FileSystem.php index 56bd78bc..442b7907 100644 --- a/src/Stash/Driver/FileSystem.php +++ b/src/Stash/Driver/FileSystem.php @@ -129,7 +129,7 @@ public function getDefaultOptions() * * @throws \Stash\Exception\RuntimeException */ - public function setOptions(array $options = array()) + protected function setOptions(array $options = array()) { $options += $this->getDefaultOptions(); if (!isset($options['path'])) { @@ -156,10 +156,11 @@ public function setOptions(array $options = array()) } $this->encoder = new $encoder; } else { + $encoderInterface = 'Stash\Driver\FileSystem\EncoderInterface'; $encoderClass = 'Stash\Driver\FileSystem\\' . $encoder . 'Encoder'; - if (class_exists($encoder)) { + if (class_exists($encoder) && in_array($encoderInterface, class_implements($encoder))) { $this->encoder = new $encoder(); - } elseif (class_exists($encoderClass)) { + } elseif (class_exists($encoderClass) && in_array($encoderInterface, class_implements($encoderClass))) { $this->encoder = new $encoderClass(); } else { throw new RuntimeException('Invalid Encoder: ' . $encoder); diff --git a/src/Stash/Driver/FileSystem/NativeEncoder.php b/src/Stash/Driver/FileSystem/NativeEncoder.php index 0cb379a5..aee5c3df 100644 --- a/src/Stash/Driver/FileSystem/NativeEncoder.php +++ b/src/Stash/Driver/FileSystem/NativeEncoder.php @@ -21,16 +21,13 @@ public function deserialize($path) return false; } + $expiration = null; include($path); if (!isset($loaded)) { return false; } - if (!isset($expiration)) { - $expiration = null; - } - if (!isset($data)) { $data = null; } @@ -92,22 +89,18 @@ protected function encode($data) $dataString = (bool) $data ? 'true' : 'false'; break; - case 'serialize': - $dataString = 'unserialize(base64_decode(\'' . base64_encode(serialize($data)) . '\'))'; - break; - case 'string': $dataString = sprintf('"%s"', addcslashes($data, "\t\"\$\\")); break; - case 'none': - default : - if (is_numeric($data)) { - $dataString = (string) $data; - } else { - $dataString = 'base64_decode(\'' . base64_encode($data) . '\')'; - } + case 'numeric': + $dataString = (string) $data; break; + + default : + case 'serialize': + $dataString = 'unserialize(base64_decode(\'' . base64_encode(serialize($data)) . '\'))'; + break; } return $dataString; diff --git a/src/Stash/Driver/Memcache.php b/src/Stash/Driver/Memcache.php index 7ad61f80..bd407ea1 100644 --- a/src/Stash/Driver/Memcache.php +++ b/src/Stash/Driver/Memcache.php @@ -81,7 +81,7 @@ public function getDefaultOptions() * * @throws RuntimeException */ - public function setOptions(array $options = array()) + protected function setOptions(array $options = array()) { $options += $this->getDefaultOptions(); diff --git a/src/Stash/Driver/Redis.php b/src/Stash/Driver/Redis.php index 063fc7dd..214a55e7 100644 --- a/src/Stash/Driver/Redis.php +++ b/src/Stash/Driver/Redis.php @@ -61,7 +61,7 @@ class Redis extends AbstractDriver * * @param array $options */ - public function setOptions(array $options = array()) + protected function setOptions(array $options = array()) { $options += $this->getDefaultOptions(); diff --git a/src/Stash/Driver/Sqlite.php b/src/Stash/Driver/Sqlite.php index 9904035c..5e6f3086 100644 --- a/src/Stash/Driver/Sqlite.php +++ b/src/Stash/Driver/Sqlite.php @@ -55,7 +55,7 @@ public function getDefaultOptions() * * @throws \Stash\Exception\RuntimeException */ - public function setOptions(array $options = array()) + protected function setOptions(array $options = array()) { $options += $this->getDefaultOptions(); @@ -67,34 +67,16 @@ public function setOptions(array $options = array()) Utilities::checkFileSystemPermissions($this->cachePath, $this->dirPermissions); - $extension = isset($options['extension']) ? strtolower($options['extension']) : 'any'; - $version = isset($options['version']) ? $options['version'] : 'any'; - - $subdrivers = array(); - if (Sub\SqlitePdo::isAvailable()) { - $subdrivers['pdo'] = '\Stash\Driver\Sub\SqlitePdo'; - } - if (Sub\Sqlite::isAvailable()) { - $subdrivers['sqlite'] = '\Stash\Driver\Sub\Sqlite'; - } - if (Sub\SqlitePdo2::isAvailable()) { - $subdrivers['pdo2'] = '\Stash\Driver\Sub\SqlitePdo2'; - } - - if ($extension == 'pdo' && $version != '2' && isset($subdrivers['pdo'])) { - $driver = $subdrivers['pdo']; - } elseif ($extension == 'sqlite' && isset($subdrivers['sqlite'])) { - $driver = $subdrivers['sqlite']; - } elseif ($extension == 'pdo' && $version != '3' && isset($subdrivers['pdo2'])) { - $driver = $subdrivers['pdo2']; - } elseif (count($subdrivers) > 0 && $extension == 'any') { - $driver = reset($subdrivers); + if (static::isAvailable() && Sub\SqlitePdo::isAvailable()) { + $this->driverClass = '\Stash\Driver\Sub\SqlitePdo'; } else { throw new RuntimeException('No sqlite extension available.'); } - $this->driverClass = $driver; - $this->checkStatus(); + $driver = $this->getSqliteDriver(array('_none')); + if (!$driver) { + throw new RuntimeException('No Sqlite driver could be loaded.'); + } } /** @@ -255,33 +237,12 @@ protected function getCacheList() return count($caches) > 0 ? $caches : false; } - /** - * Checks availability of the specified subdriver. - * - * @throws \Stash\Exception\RuntimeException - * @return bool - */ - protected function checkStatus() - { - if (!static::isAvailable()) { - throw new RuntimeException('No Sqlite extension is available.'); - } - - $driver = $this->getSqliteDriver(array('_none')); - - if (!$driver) { - throw new RuntimeException('No Sqlite driver could be loaded.'); - } - - $driver->checkFileSystemPermissions(); - } - /** * {@inheritdoc} */ public static function isAvailable() { - return (Sub\SqlitePdo::isAvailable()) || (Sub\Sqlite::isAvailable()) || (Sub\SqlitePdo2::isAvailable()); + return Sub\SqlitePdo::isAvailable(); } /** diff --git a/src/Stash/Driver/Sub/Sqlite.php b/src/Stash/Driver/Sub/Sqlite.php deleted file mode 100644 index 09b891cc..00000000 --- a/src/Stash/Driver/Sub/Sqlite.php +++ /dev/null @@ -1,302 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Stash\Driver\Sub; - -use Stash\Exception\RuntimeException; -use Stash\Exception\InvalidArgumentException; - -/** - * Class Sqlite - * - * This SQLite subdriver is used by the main Sqlite driver for interacting with the SQLite extension. - * - * @internal - * @package Stash - * @author Robert Hafner - */ -class Sqlite -{ - /** - * Directory where the SQLite databases are stored. - * - * @var string - */ - protected $path; - - /** - * Output of buildDriver, used to interact with the relevant SQLite extension. - * - * @var \SQLiteDatabase - */ - protected $driver; - - /** - * The SQLite query used to generate the database. - * - * @var string - */ - protected $creationSql = 'CREATE TABLE cacheStore ( - key TEXT UNIQUE ON CONFLICT REPLACE, - expiration INTEGER, - encoding TEXT, - data BLOB - ); - CREATE INDEX keyIndex ON cacheStore (key);'; - - /** - * File permissions of new SQLite databases. - * - * @var string - */ - protected $filePermissions; - - /** - * File permissions of new directories leading to SQLite databases. - * - * @var string - */ - protected $dirPermissions; - - /** - * Amounts of time to wait for the SQLite engine before timing out. - * - * @var int milliseconds - */ - protected $busyTimeout; - - /** - * The appropriate response code to use when retrieving data. - * - * @var int - */ - protected $responseCode; - - /** - * @param string $path - * @param string $directoryPermission - * @param string $filePermission - * @param int $busyTimeout - */ - public function __construct($path, $directoryPermission, $filePermission, $busyTimeout) - { - $this->path = $path; - $this->filePermissions = $filePermission; - $this->dirPermissions = $directoryPermission; - $this->busyTimeout = $busyTimeout; - $this->responseCode = 1; // SQLITE_ASSOC - } - - /** - * Clear out driver, closing file sockets. - */ - public function __destruct() - { - $this->driver = null; - } - - /** - * Retrieves data from cache store. - * - * @param string $key - * @return bool|mixed - */ - public function get($key) - { - if (!($driver = $this->getDriver())) { - return false; - } - - $query = $driver->query("SELECT * FROM cacheStore WHERE key LIKE '{$key}'"); - if ($query === false) { - return false; - } - - if ($resultArray = $query->fetch($this->responseCode)) { - return unserialize(base64_decode($resultArray['data'])); - } - - return false; - } - - /** - * Stores data in sqlite database. - * - * @param string $key - * @param mixed $value - * @param int $expiration - * @return bool - */ - public function set($key, $value, $expiration) - { - if (!($driver = $this->getDriver())) { - return false; - } - - $data = base64_encode(serialize($value)); - - $contentLength = strlen($data); - if ($contentLength > 100000) { - $this->setTimeout($this->busyTimeout * (ceil($contentLength / 100000))); // .5s per 100k - } - - $driver->query("INSERT INTO cacheStore (key, expiration, data) - VALUES ('{$key}', '{$expiration}', '{$data}')"); - - return true; - } - - /** - * Clears data from database. If a key is defined only it and it's children are removed. If everything is set to be - * cleared then the database itself is deleted off disk. - * - * @param null|string $key - * @return bool - */ - public function clear($key = null) - { - // return true if the cache is already empty - if (!($driver = $this->getDriver())) { - return true; - } - - if (!isset($key)) { - unset($driver); - $this->driver = null; - $this->driver = false; - \Stash\Utilities::deleteRecursive($this->path); - } else { - $driver->query("DELETE FROM cacheStore WHERE key LIKE '{$key}%'"); - } - - return true; - } - - /** - * Old data is removed and the "vacuum" operation is run. - * - * @return bool - */ - public function purge() - { - if (!($driver = $this->getDriver())) { - return false; - } - - $driver->query('DELETE FROM cacheStore WHERE expiration < ' . time()); - $driver->query('VACUUM'); - - return true; - } - - /** - * Filesystem integrity and permissions are checked and exceptions thrown for relevant issues. - * - * @throws \Stash\Exception\InvalidArgumentException - * @throws \Stash\Exception\RuntimeException - */ - public function checkFileSystemPermissions() - { - if (!isset($this->path)) { - throw new RuntimeException('No cache path is set.'); - } - - if (!is_writable($this->path) && !is_writable(dirname($this->path))) { - throw new InvalidArgumentException('The cache sqlite file is not writable.'); - } - } - - /** - * Returns true if the SQLiteDatabase class exists. - * - * @return bool - */ - public static function isAvailable() - { - return class_exists('SQLiteDatabase', false); - } - - /** - * Tells the SQLite driver how long to wait for data to be written. - * - * @param int $milliseconds - * @return bool - */ - protected function setTimeout($milliseconds) - { - if (!($driver = $this->getDriver())) { - return false; - } - $driver->busyTimeout($milliseconds); - } - - /** - * Retrieves the relevant SQLite driver, creating the database file if necessary. - * - * @return \SQLiteDatabase - * @throws \Stash\Exception\RuntimeException - */ - protected function getDriver() - { - if (isset($this->driver) && $this->driver !== false) { - return $this->driver; - } - - if (!file_exists($this->path)) { - $dir = $this->path; - - // Since PHP will understand paths with mixed slashes- both the windows \ and unix / variants- we have - // to test for both and see which one is the last in the string. - $pos1 = strrpos($this->path, '/'); - $pos2 = strrpos($this->path, '\\'); - - if ($pos1 || $pos2) { - $pos = $pos1 >= $pos2 ? $pos1 : $pos2; - $dir = substr($this->path, 0, $pos); - } - - if (!is_dir($dir)) { - mkdir($dir, $this->dirPermissions, true); - } - $runInstall = true; - } else { - $runInstall = false; - } - - $db = $this->buildDriver(); - - if ($runInstall && !$db->query($this->creationSql)) { - unlink($this->path); - throw new RuntimeException('Unable to set SQLite: structure'); - } - $this->driver = $db; - - // prevent the cache from getting hungup waiting on a return - $this->setTimeout($this->busyTimeout); - - return $db; - } - - /** - * Creates the actual database driver itself. - * - * @return \SQLiteDatabase - * @throws \Stash\Exception\RuntimeException - */ - protected function buildDriver() - { - if (!$db = new \SQLiteDatabase($this->path, $this->filePermissions, $errorMessage)) { - throw new RuntimeException('Unable to open SQLite Database: ' . $errorMessage); - } - - return $db; - } -} diff --git a/src/Stash/Driver/Sub/SqlitePdo.php b/src/Stash/Driver/Sub/SqlitePdo.php index 60bee68e..36df7874 100644 --- a/src/Stash/Driver/Sub/SqlitePdo.php +++ b/src/Stash/Driver/Sub/SqlitePdo.php @@ -20,8 +20,15 @@ * @package Stash * @author Robert Hafner */ -class SqlitePdo extends Sqlite +class SqlitePdo { + /** + * Directory where the SQLite databases are stored. + * + * @var string + */ + protected $path; + /** * Output of buildDriver, used to interact with the relevant SQLite extension. * @@ -36,20 +43,158 @@ class SqlitePdo extends Sqlite */ protected static $pdoDriver = 'sqlite'; + + /** + * The SQLite query used to generate the database. + * + * @var string + */ + protected $creationSql = 'CREATE TABLE cacheStore ( + key TEXT UNIQUE ON CONFLICT REPLACE, + expiration INTEGER, + encoding TEXT, + data BLOB + ); + CREATE INDEX keyIndex ON cacheStore (key);'; + + /** + * File permissions of new SQLite databases. + * + * @var string + */ + protected $filePermissions; + + /** + * File permissions of new directories leading to SQLite databases. + * + * @var string + */ + protected $dirPermissions; + + /** + * Amounts of time to wait for the SQLite engine before timing out. + * + * @var int milliseconds + */ + protected $busyTimeout; + /** - * {@inheritdoc} + * The appropriate response code to use when retrieving data. + * + * @var int */ - protected $responseCode = \PDO::FETCH_ASSOC; + protected $responseCode; /** - * {@inheritdoc} + * @param string $path + * @param string $directoryPermission + * @param string $filePermission + * @param int $busyTimeout */ public function __construct($path, $directoryPermission, $filePermission, $busyTimeout) { - parent::__construct($path, $directoryPermission, $filePermission, $busyTimeout); + $this->path = $path; + $this->filePermissions = $filePermission; + $this->dirPermissions = $directoryPermission; + $this->busyTimeout = $busyTimeout; $this->responseCode = \PDO::FETCH_ASSOC; } + /** + * Clear out driver, closing file sockets. + */ + public function __destruct() + { + $this->driver = null; + } + + /** + * Retrieves data from cache store. + * + * @param string $key + * @return bool|mixed + */ + public function get($key) + { + $driver = $this->getDriver(); + $query = $driver->query("SELECT * FROM cacheStore WHERE key LIKE '{$key}'"); + if ($query === false) { + return false; + } + + if ($resultArray = $query->fetch($this->responseCode)) { + return unserialize(base64_decode($resultArray['data'])); + } + + return false; + } + + /** + * Stores data in sqlite database. + * + * @param string $key + * @param mixed $value + * @param int $expiration + * @return bool + */ + public function set($key, $value, $expiration) + { + $driver = $this->getDriver(); + $data = base64_encode(serialize($value)); + + $contentLength = strlen($data); + if ($contentLength > 100000) { + $this->setTimeout($this->busyTimeout * (ceil($contentLength / 100000))); // .5s per 100k + } + + $driver->query("INSERT INTO cacheStore (key, expiration, data) + VALUES ('{$key}', '{$expiration}', '{$data}')"); + + return true; + } + + /** + * Clears data from database. If a key is defined only it and it's children are removed. If everything is set to be + * cleared then the database itself is deleted off disk. + * + * @param null|string $key + * @return bool + */ + public function clear($key = null) + { + // return true if the cache is already empty + try { + $driver = $this->getDriver(); + } catch (RuntimeException $e) { + return true; + } + + if (!isset($key)) { + unset($driver); + $this->driver = null; + $this->driver = false; + \Stash\Utilities::deleteRecursive($this->path); + } else { + $driver->query("DELETE FROM cacheStore WHERE key LIKE '{$key}%'"); + } + + return true; + } + + /** + * Old data is removed and the "vacuum" operation is run. + * + * @return bool + */ + public function purge() + { + $driver = $this->getDriver(); + $driver->query('DELETE FROM cacheStore WHERE expiration < ' . time()); + $driver->query('VACUUM'); + + return true; + } + /** * Checks that PDO extension is present and has the appropriate SQLite driver. * @@ -66,20 +211,75 @@ public static function isAvailable() } /** - * {@inheritdoc} + * Tells the SQLite driver how long to wait for data to be written. + * + * @param int $milliseconds + * @return bool */ protected function setTimeout($milliseconds) { - if (!($driver = $this->getDriver())) { - return false; - } - + $driver = $this->getDriver(); $timeout = ceil($milliseconds / 1000); $driver->setAttribute(\PDO::ATTR_TIMEOUT, $timeout); } /** - * {@inheritdoc} + * Retrieves the relevant SQLite driver, creating the database file if necessary. + * + * @return \SQLiteDatabase + * @throws \Stash\Exception\RuntimeException + */ + protected function getDriver() + { + if (isset($this->driver) && $this->driver !== false) { + return $this->driver; + } + + if (!file_exists($this->path)) { + $dir = $this->path; + + // Since PHP will understand paths with mixed slashes- both the windows \ and unix / variants- we have + // to test for both and see which one is the last in the string. + $pos1 = strrpos($this->path, '/'); + $pos2 = strrpos($this->path, '\\'); + + if ($pos1 || $pos2) { + $pos = $pos1 >= $pos2 ? $pos1 : $pos2; + $dir = substr($this->path, 0, $pos); + } + + if (!is_dir($dir)) { + mkdir($dir, $this->dirPermissions, true); + } + $runInstall = true; + } else { + $runInstall = false; + } + + $db = $this->buildDriver(); + + if ($runInstall && !$db->query($this->creationSql)) { + unlink($this->path); + throw new RuntimeException('Unable to set SQLite: structure'); + } + + if (!$db) { + throw new RuntimeException('SQLite driver failed to load'); + } + + $this->driver = $db; + + // prevent the cache from getting hungup waiting on a return + $this->setTimeout($this->busyTimeout); + + return $db; + } + + /** + * Creates the actual database driver itself. + * + * @return \SQLiteDatabase + * @throws \Stash\Exception\RuntimeException */ protected function buildDriver() { diff --git a/src/Stash/DriverList.php b/src/Stash/DriverList.php index 5bd53abc..60689b82 100644 --- a/src/Stash/DriverList.php +++ b/src/Stash/DriverList.php @@ -99,15 +99,4 @@ public static function getDriverClass($name) return self::$drivers[$name]; } - - /** - * Returns a list of cache drivers that are also supported by this system. - * - * @deprecated Deprecated in favor of getAvailableDrivers. - * @return array - */ - public static function getDrivers() - { - return self::getAvailableDrivers(); - } } diff --git a/src/Stash/Exception/Exception.php b/src/Stash/Exception/Exception.php index cea596a6..9148daeb 100644 --- a/src/Stash/Exception/Exception.php +++ b/src/Stash/Exception/Exception.php @@ -11,12 +11,14 @@ namespace Stash\Exception; +use \Psr\Cache\CacheException; + /** * Interface for the Stash exceptions. * * @package Stash * @author Robert Hafner */ -interface Exception +interface Exception extends CacheException { } diff --git a/src/Stash/Exception/InvalidArgumentException.php b/src/Stash/Exception/InvalidArgumentException.php index 1e2dc4f2..3f06ad2b 100644 --- a/src/Stash/Exception/InvalidArgumentException.php +++ b/src/Stash/Exception/InvalidArgumentException.php @@ -11,12 +11,15 @@ namespace Stash\Exception; +//use \Psr\Cache\InvalidArgumentException; + + /** * Exception thrown if an argument does not match with the expected value. * * @package Stash * @author Robert Hafner */ -class InvalidArgumentException extends \InvalidArgumentException implements Exception +class InvalidArgumentException extends \InvalidArgumentException implements Exception, \Psr\Cache\InvalidArgumentException { } diff --git a/src/Stash/Exception/WindowsPathMaxLengthException.php b/src/Stash/Exception/WindowsPathMaxLengthException.php index 2c5d481c..f57fdba6 100644 --- a/src/Stash/Exception/WindowsPathMaxLengthException.php +++ b/src/Stash/Exception/WindowsPathMaxLengthException.php @@ -28,7 +28,7 @@ * @package Stash\Exception * @author Jonathan Chan */ -class WindowsPathMaxLengthException extends \Exception implements Exception +class WindowsPathMaxLengthException extends RuntimeException implements Exception { public function __construct($message="", $code=0, $previous=null) { diff --git a/src/Stash/Interfaces/DriverInterface.php b/src/Stash/Interfaces/DriverInterface.php index 8c1e8955..1ee44824 100644 --- a/src/Stash/Interfaces/DriverInterface.php +++ b/src/Stash/Interfaces/DriverInterface.php @@ -42,15 +42,6 @@ interface DriverInterface { - /** - * Takes an array which is used to pass option values to the driver. As this is the only required function that is - * used specifically by the developer is where any engine specific options should go. An engine that requires - * authentication information, as an example, should get them here. - * - * @param array $options - */ - public function setOptions(array $options = array()); - /** * Returns the previously stored data as well as its expiration date in an associative array. This array contains * two keys - a 'data' key and an 'expiration' key. The 'data' key should be exactly the same as the value passed to diff --git a/src/Stash/Interfaces/ItemInterface.php b/src/Stash/Interfaces/ItemInterface.php index 4a3f8c49..612a1143 100644 --- a/src/Stash/Interfaces/ItemInterface.php +++ b/src/Stash/Interfaces/ItemInterface.php @@ -11,7 +11,9 @@ namespace Stash\Interfaces; -interface ItemInterface +use \Psr\Cache\CacheItemInterface; + +interface ItemInterface extends CacheItemInterface { /** * Sets the Parent Pool for the Item class to use. @@ -44,7 +46,7 @@ public function disable(); * Returns the key as a string. This is particularly useful when the Item is * returned as a group of Items in an Iterator. * - * @return string|bool Returns false if no key is set. + * @return string */ public function getKey(); @@ -63,12 +65,17 @@ public function clear(); * function after call this one. If no value is stored at all then this * function will return null. * - * @param int $invalidation - * @param null $arg - * @param null $arg2 - * @return mixed|null + * @return mixed + */ + public function get(); + + /** + * Returns true if the cached item is valid and usable. + * + * @return bool */ - public function get($invalidation = 0, $arg = null, $arg2 = null); + public function isHit(); + /** * Returns true if the cached item needs to be refreshed. @@ -91,11 +98,10 @@ public function lock($ttl = null); * including arrays and object, except resources and objects which are * unable to be serialized. * - * @param mixed $data bool - * @param int|\DateTime|null $ttl Int is time (seconds), DateTime a future expiration date - * @return bool Returns whether the object was successfully stored or not. + * @param mixed $value bool + * @return static The invoked object */ - public function set($data, $ttl = null); + public function set($value); /** * Extends the expiration on the current cached item. For some engines this @@ -134,4 +140,30 @@ public function getCreation(); * @return \DateTime */ public function getExpiration(); + + + /** + * Sets the expiration based off of an integer or DateInterval + * + * @param int|\DateInterval $time + * @return static The invoked object. + */ + public function expiresAfter($time); + + + /** + * Sets the expiration to a specific time. + * + * @param \DateTimeInterface $expiration + * @return static The invoked object. + */ + public function expiresAt($expiration); + + + /** + * Persists the Item's value to the backend storage. + * + * @return bool + */ + public function save(); } diff --git a/src/Stash/Interfaces/PoolInterface.php b/src/Stash/Interfaces/PoolInterface.php index 16a5ab3b..ca2a23a9 100644 --- a/src/Stash/Interfaces/PoolInterface.php +++ b/src/Stash/Interfaces/PoolInterface.php @@ -11,13 +11,16 @@ namespace Stash\Interfaces; +use \Psr\Cache\CacheItemPoolInterface; +use \Psr\Cache\CacheItemInterface; + /** * * * @package Stash * @author Robert Hafner */ -interface PoolInterface +interface PoolInterface extends CacheItemPoolInterface { /** * Changes the specific Item class generated by the Pool object. @@ -51,11 +54,11 @@ public function setItemClass($class); * @example $item = $pool->getItem(array('permissions', 'user', '4', '2')); * @example $item = $pool->getItem('permissions', 'user', '4', '2'); * - * @param array|string $key + * @param string $key * @return ItemInterface * @throws \InvalidArgumentException */ - public function getItem(); + public function getItem($key); /** * Returns a group of cache objects in an \Iterator @@ -64,9 +67,9 @@ public function getItem(); * each key passed, but is not required to maintain an order. * * @param array $keys - * @return \Iterator + * @return array|\Traversable */ - public function getItemIterator($keys); + public function getItems(array $keys = array()); /** * Empties the entire cache pool of all Items. @@ -75,7 +78,7 @@ public function getItemIterator($keys); * * @return bool True on success */ - public function flush(); + public function clear(); /** * The Purge function allows drivers to perform basic maintenance tasks, such as removing stale or expired items @@ -127,4 +130,52 @@ public function getNamespace(); * @return bool */ public function setLogger($logger); + + /** + * Forces any save-deferred objects to get flushed to the backend drivers. + * + * @return bool + */ + public function commit(); + + /** + * Sets an Item to be saved at some point. This allows buffering for + * multi-save events. + * + * @param CacheItemInterface $item + * @return static The invoked object. + */ + public function saveDeferred(CacheItemInterface $item); + + /** + * Sets an Item to be saved immediately. + * + * @param CacheItemInterface $item + * @return bool + */ + public function save(CacheItemInterface $item); + + /** + * Removes multiple items from the pool by their key. + * + * @param array $keys + * @return bool + */ + public function deleteItems(array $keys); + + /** + * Removes multiple items from the pool by their key. + * + * @param array $keys + * @return bool + */ + public function deleteItem($key); + + /** + * Checks for the existance of an item in the cache. + * + * @param string $key + * @return boolean True if item exists in the cache, false otherwise. + */ + public function hasItem($key); } diff --git a/src/Stash/Item.php b/src/Stash/Item.php index 075cdc0f..171b521c 100644 --- a/src/Stash/Item.php +++ b/src/Stash/Item.php @@ -12,6 +12,7 @@ namespace Stash; use Stash\Exception\Exception; +use Stash\Exception\InvalidArgumentException; use Stash\Interfaces\DriverInterface; use Stash\Interfaces\ItemInterface; use Stash\Interfaces\PoolInterface; @@ -26,31 +27,6 @@ */ 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. * @@ -85,6 +61,15 @@ class Item implements ItemInterface '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. * @@ -209,6 +194,9 @@ public function clear() private function executeClear() { + unset($this->data); + unset($this->expiration); + if ($this->isDisabled()) { return false; } @@ -219,10 +207,17 @@ private function executeClear() /** * {@inheritdoc} */ - public function get($invalidation = Invalidation::PRECOMPUTE, $arg = null, $arg2 = null) + public function get() { try { - return $this->executeGet($invalidation, $arg, $arg2); + if (!isset($this->data)) { + $this->data = $this->executeGet( + $this->invalidationMethod, + $this->invalidationArg1, + $this->invalidationArg2); + } + + return $this->data; } catch (Exception $e) { $this->logException('Retrieving from cache caused exception.', $e); $this->disable(); @@ -231,7 +226,14 @@ public function get($invalidation = Invalidation::PRECOMPUTE, $arg = null, $arg2 } } - private function executeGet($invalidation, $arg, $arg2) + 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; @@ -268,6 +270,15 @@ private function executeGet($invalidation, $arg, $arg2) return isset($record['data']['return']) ? $record['data']['return'] : null; } + + /** + * {@inheritdoc} + */ + public function isHit() + { + return !$this->isMiss(); + } + /** * {@inheritdoc} */ @@ -311,10 +322,69 @@ public function lock($ttl = null) /** * {@inheritdoc} */ - public function set($data, $ttl = null) + public function set($value) + { + if (!isset($this->key)) { + return false; + } + + if ($this->isDisabled()) { + return $this; + } + + $this->data = $value; + return $this; + } + + 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; + } + + 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; + } + + 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; + } + + public function save() { try { - return $this->executeSet($data, $ttl); + return $this->executeSet($this->data, $this->expiration); } catch (Exception $e) { $this->logException('Setting value in cache caused exception.', $e); $this->disable(); @@ -325,11 +395,7 @@ public function set($data, $ttl = null) private function executeSet($data, $time) { - if ($this->isDisabled()) { - return false; - } - - if (!isset($this->key)) { + if ($this->isDisabled() || !isset($this->key)) { return false; } @@ -337,13 +403,9 @@ private function executeSet($data, $time) $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; - } + if (isset($time) && ($time instanceof \DateTime)) { + $expiration = $time->getTimestamp(); + $cacheTime = $expiration - $store['createdOn']; } else { $cacheTime = self::$cacheTime; } @@ -395,16 +457,6 @@ 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. * @@ -475,6 +527,7 @@ protected function getRecord() */ protected function validateRecord($validation, &$record) { + $invalidation = Invalidation::PRECOMPUTE; if (is_array($validation)) { $argArray = $validation; $invalidation = isset($argArray[0]) ? $argArray[0] : Invalidation::PRECOMPUTE; @@ -486,8 +539,6 @@ protected function validateRecord($validation, &$record) if (isset($argArray[2])) { $arg2 = $argArray[2]; } - } else { - $invalidation = Invalidation::PRECOMPUTE; } $curTime = microtime(true); @@ -535,7 +586,6 @@ protected function validateRecord($validation, &$record) 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) { @@ -545,7 +595,7 @@ protected function validateRecord($validation, &$record) } usleep($ptime); - $record['data']['return'] = $this->get(Invalidation::SLEEP, $time, $attempts - 1); + $record['data']['return'] = $this->executeGet(Invalidation::SLEEP, $time, $attempts - 1); break; case Invalidation::OLD: @@ -579,15 +629,18 @@ public function getCreation() */ public function getExpiration() { - $record = $this->getRecord(); - if (!isset($record['expiration'])) { - return false; - } + if (!isset($this->expiration)) { + $record = $this->getRecord(); + $dateTime = new \DateTime(); - $dateTime = new \DateTime(); - $dateTime->setTimestamp($record['expiration']); + if (!isset($record['expiration'])) { + return $dateTime; + } - return $dateTime; + $this->expiration = $dateTime->setTimestamp($record['expiration']); + } + + return $this->expiration; } /** diff --git a/src/Stash/Pool.php b/src/Stash/Pool.php index 9c2fd5a5..07cfa102 100644 --- a/src/Stash/Pool.php +++ b/src/Stash/Pool.php @@ -11,6 +11,8 @@ namespace Stash; +use PSR\Cache\CacheItemInterface; +use Stash\Exception\InvalidArgumentException; use Stash\Driver\Ephemeral; use Stash\Interfaces\DriverInterface; use Stash\Interfaces\ItemInterface; @@ -83,7 +85,7 @@ public function __construct(DriverInterface $driver = null) public function setItemClass($class) { if (!class_exists($class)) { - throw new \InvalidArgumentException('Item class ' . $class . ' does not exist'); + throw new InvalidArgumentException('Item class ' . $class . ' does not exist'); } $interfaces = class_implements($class, true); @@ -100,34 +102,17 @@ public function setItemClass($class) /** * {@inheritdoc} */ - public function getItem() + public function getItem($key) { - $args = func_get_args(); - - if (!isset($args[0])) { - throw new \InvalidArgumentException('Item constructor requires a key.'); - } - - // check to see if a single array was used instead of multiple arguments - if (!isset($args[1]) && is_array($args[0])) { - $args = $args[0]; - } - - // if only one item treat as key string - if (!isset($args[1])) { - $keyString = trim($args[0], '/'); - $key = explode('/', $keyString); - } else { - $key = $args; - } - + $keyString = trim($key, '/'); + $key = explode('/', $keyString); $namespace = empty($this->namespace) ? 'stash_default' : $this->namespace; array_unshift($key, $namespace); foreach ($key as $node) { if (!isset($node[1]) && strlen($node) < 1) { - throw new \InvalidArgumentException('Invalid or Empty Node passed to getItem constructor.'); + throw new InvalidArgumentException('Invalid or Empty Node passed to getItem constructor.'); } } @@ -150,7 +135,7 @@ public function getItem() /** * {@inheritdoc} */ - public function getItemIterator($keys) + public function getItems(array $keys = array()) { // temporarily cheating here by wrapping around single calls. @@ -166,7 +151,65 @@ public function getItemIterator($keys) /** * {@inheritdoc} */ - public function flush() + public function hasItem($key) + { + return $this->getItem($key)->isHit(); + } + + /** + * {@inheritdoc} + */ + public function save(CacheItemInterface $item) + { + return $item->save(); + } + + /** + * {@inheritdoc} + */ + public function saveDeferred(CacheItemInterface $item) + { + return $this->save($item); + } + + /** + * {@inheritdoc} + */ + public function commit() + { + return true; + } + + + /** + * {@inheritdoc} + */ + public function deleteItems(array $keys) + { + // temporarily cheating here by wrapping around single calls. + $items = array(); + $results = true; + foreach ($keys as $key) { + $results = $this->deleteItem($key) && $results; + } + + return $results; + } + + + /** + * {@inheritdoc} + */ + public function deleteItem($key) + { + return $this->getItem($key)->clear(); + } + + + /** + * {@inheritdoc} + */ + public function clear() { if ($this->isDisabled) { return false; @@ -225,10 +268,6 @@ public function setDriver(DriverInterface $driver) */ public function getDriver() { - if (!isset($this->driver)) { - $this->driver = new Ephemeral(); - } - return $this->driver; } diff --git a/src/Stash/Session.php b/src/Stash/Session.php index 206d5014..a2a0cce5 100644 --- a/src/Stash/Session.php +++ b/src/Stash/Session.php @@ -184,7 +184,7 @@ public function write($session_id, $session_data) { $cache = $this->getCache($session_id); - return $cache->set($session_data, $this->options['ttl']); + return $cache->set($session_data)->expiresAfter($this->options['ttl'])->save(); } /** diff --git a/src/Stash/Utilities.php b/src/Stash/Utilities.php index 637121cd..7eaadfa2 100644 --- a/src/Stash/Utilities.php +++ b/src/Stash/Utilities.php @@ -34,8 +34,12 @@ public static function encoding($data) return 'bool'; } - if (is_numeric($data) && ($data >= 2147483648 || $data < -2147483648)) { - return 'serialize'; + if (is_numeric($data)) { + if (is_numeric($data) && ($data >= 2147483648 || $data < -2147483648)) { + return 'serialize'; + } else { + return 'numeric'; + } } if (is_string($data)) { @@ -158,13 +162,7 @@ public static function deleteRecursive($file, $cleanParent = false) } if (file_exists($filename)) { - if ($file->isDir()) { - if (file_exists($filename)) { - @rmdir($filename); - } - } else { - unlink($filename); - } + unlink($filename); } } diff --git a/tests/Stash/Test/AbstractItemTest.php b/tests/Stash/Test/AbstractItemTest.php index 6f83af3e..2de7db4b 100644 --- a/tests/Stash/Test/AbstractItemTest.php +++ b/tests/Stash/Test/AbstractItemTest.php @@ -12,6 +12,7 @@ namespace Stash\Test; use Stash\Item; +use Stash\Invalidation; use Stash\Utilities; use Stash\Driver\Ephemeral; use Stash\Test\Stubs\PoolGetDriverStub; @@ -112,7 +113,7 @@ public function testSet() $this->assertAttributeInternalType('string', 'keyString', $stash, 'Argument based keys setup keystring'); $this->assertAttributeInternalType('array', 'key', $stash, 'Argument based keys setup key'); - $this->assertTrue($stash->set($value), 'Driver class able to store data type ' . $type); + $this->assertTrue($stash->set($value)->save(), 'Driver class able to store data type ' . $type); } $item = $this->getItem(); @@ -130,7 +131,7 @@ public function testGet() foreach ($this->data as $type => $value) { $key = array('base', $type); $stash = $this->testConstruct($key); - $stash->set($value); + $stash->set($value)->save(); // new object, but same backend $stash = $this->testConstruct($key); @@ -184,12 +185,12 @@ public function testInvalidation() $newValue = 'newValue'; $runningStash = $this->testConstruct($key); - $runningStash->set($oldValue, -300); + $runningStash->set($oldValue)->expiresAfter(-300)->save(); // Test without stampede $controlStash = $this->testConstruct($key); - - $return = $controlStash->get(Item::SP_VALUE, $newValue); + $controlStash->setInvalidationMethod(Invalidation::VALUE, $newValue); + $return = $controlStash->get(); $this->assertEquals($oldValue, $return, 'Old value is returned'); $this->assertTrue($controlStash->isMiss()); unset($controlStash); @@ -200,25 +201,25 @@ public function testInvalidation() // Old $oldStash = $this->testConstruct($key); - - $return = $oldStash->get(Item::SP_OLD); + $oldStash->setInvalidationMethod(Invalidation::OLD); + $return = $oldStash->get(Invalidation::OLD); $this->assertEquals($oldValue, $return, 'Old value is returned'); $this->assertFalse($oldStash->isMiss()); unset($oldStash); // Value $valueStash = $this->testConstruct($key); - - $return = $valueStash->get(Item::SP_VALUE, $newValue); + $valueStash->setInvalidationMethod(Invalidation::VALUE, $newValue); + $return = $valueStash->get(Invalidation::VALUE, $newValue); $this->assertEquals($newValue, $return, 'New value is returned'); $this->assertFalse($valueStash->isMiss()); unset($valueStash); // Sleep $sleepStash = $this->testConstruct($key); - + $sleepStash->setInvalidationMethod(Invalidation::SLEEP, 250, 2); $start = microtime(true); - $return = $sleepStash->get(array(Item::SP_SLEEP, 250, 2)); + $return = $sleepStash->get(); $end = microtime(true); $this->assertTrue($sleepStash->isMiss()); @@ -238,46 +239,125 @@ public function testInvalidation() unset($unknownStash); // Test that storing the cache turns off stampede mode. - $runningStash->set($newValue, 30); + $runningStash->set($newValue)->expiresAfter(30)->save(); $this->assertAttributeEquals(false, 'stampedeRunning', $runningStash, 'Stampede flag is off.'); unset($runningStash); // Precompute - test outside limit $precomputeStash = $this->testConstruct($key); - - $return = $precomputeStash->get(Item::SP_PRECOMPUTE, 10); + $precomputeStash->setInvalidationMethod(Invalidation::PRECOMPUTE, 10); + $return = $precomputeStash->get(Invalidation::PRECOMPUTE, 10); $this->assertFalse($precomputeStash->isMiss(), 'Cache is marked as hit'); unset($precomputeStash); // Precompute - test inside limit $precomputeStash = $this->testConstruct($key); - - $return = $precomputeStash->get(Item::SP_PRECOMPUTE, 35); + $precomputeStash->setInvalidationMethod(Invalidation::PRECOMPUTE, 35); + $return = $precomputeStash->get(); $this->assertTrue($precomputeStash->isMiss(), 'Cache is marked as miss'); unset($precomputeStash); // Test Stampede Flag Expiration $key = array('stampede', 'expire'); $Item_SPtest = $this->testConstruct($key); - $Item_SPtest->set($oldValue, -300); + $Item_SPtest->setInvalidationMethod(Invalidation::VALUE, $newValue); + $Item_SPtest->set($oldValue)->expiresAfter(-300)->save(); $Item_SPtest->lock(-5); - $this->assertEquals($oldValue, $Item_SPtest->get(Item::SP_VALUE, $newValue), 'Expired lock is ignored'); + $this->assertEquals($oldValue, $Item_SPtest->get(), 'Expired lock is ignored'); } - public function testSetWithDateTime() + public function testSetTTLDatetime() { $expiration = new \DateTime('now'); $expiration->add(new \DateInterval('P1D')); + $key = array('ttl', 'expiration', 'test'); + $stash = $this->testConstruct($key); + + $stash->set(array(1, 2, 3, 'apples')) + ->setTTL($expiration) + ->save(); + $this->assertLessThanOrEqual($expiration->getTimestamp(), $stash->getExpiration()->getTimestamp()); + + $stash = $this->testConstruct($key); + $data = $stash->get(); + $this->assertEquals(array(1, 2, 3, 'apples'), $data, 'getData returns data stores using a datetime expiration'); + $this->assertLessThanOrEqual($expiration->getTimestamp(), $stash->getExpiration()->getTimestamp()); + } + + public function testSetTTLDateInterval() + { + $interval = new \DateInterval('P1D'); + $expiration = new \DateTime('now'); + $expiration->add($interval); + + $key = array('ttl', 'expiration', 'test'); + $stash = $this->testConstruct($key); + $stash->set(array(1, 2, 3, 'apples')) + ->setTTL($interval) + ->save(); + + $stash = $this->testConstruct($key); + $data = $stash->get(); + $this->assertEquals(array(1, 2, 3, 'apples'), $data, 'getData returns data stores using a datetime expiration'); + $this->assertLessThanOrEqual($expiration->getTimestamp(), $stash->getExpiration()->getTimestamp()); + } + + public function testSetTTLNulll() + { + $key = array('ttl', 'expiration', 'test'); + $stash = $this->testConstruct($key); + $stash->set(array(1, 2, 3, 'apples')) + ->setTTL(null) + ->save(); + + $this->assertAttributeEquals(null, 'expiration', $stash); + } + + + public function testExpiresAt() + { + $expiration = new \DateTime('now'); + $expiration->add(new \DateInterval('P1D')); + + $key = array('base', 'expiration', 'test'); + $stash = $this->testConstruct($key); + + $stash->set(array(1, 2, 3, 'apples')) + ->expiresAt($expiration) + ->save(); + + $stash = $this->testConstruct($key); + $data = $stash->get(); + $this->assertEquals(array(1, 2, 3, 'apples'), $data, 'getData returns data stores using a datetime expiration'); + $this->assertLessThanOrEqual($expiration->getTimestamp(), $stash->getExpiration()->getTimestamp()); + } + + /** + * @expectedException Stash\Exception\InvalidArgumentException + * @expectedExceptionMessage expiresAt requires \DateTimeInterface or null + */ + public function testExpiresAtException() + { + $stash = $this->testConstruct(array('base', 'expiration', 'test')); + $stash->expiresAt(false); + } + + public function testExpiresAfterWithDateTimeInterval() + { $key = array('base', 'expiration', 'test'); $stash = $this->testConstruct($key); - $stash->set(array(1, 2, 3, 'apples'), $expiration); + + $stash->set(array(1, 2, 3, 'apples')) + ->expiresAfter(new \DateInterval('P1D')) + ->save(); $stash = $this->testConstruct($key); $data = $stash->get(); $this->assertEquals(array(1, 2, 3, 'apples'), $data, 'getData returns data stores using a datetime expiration'); } + public function testGetCreation() { $creation = new \DateTime('now'); @@ -289,7 +369,7 @@ public function testGetCreation() $this->assertFalse($stash->getCreation(), 'no record exists yet, return null'); - $stash->set(array('stuff'), $creation); + $stash->set(array('stuff'), $creation)->save(); $stash = $this->testConstruct($key); $createdOn = $stash->getCreation(); @@ -307,9 +387,16 @@ public function testGetExpiration() $key = array('getExpiration', 'test'); $stash = $this->testConstruct($key); - $this->assertFalse($stash->getExpiration(), 'no record exists yet, return null'); - $stash->set(array('stuff'), $expiration); + $currentDate = new \DateTime(); + $returnedDate = $stash->getExpiration(); + + $this->assertLessThanOrEqual(2, $currentDate->getTimestamp() - $returnedDate->getTimestamp(), 'No record set, return as expired.'); + $this->assertLessThanOrEqual(2, $returnedDate->getTimestamp() - $currentDate->getTimestamp(), 'No record set, return as expired.'); + + #$this->assertFalse($stash->getExpiration(), 'no record exists yet, return null'); + + $stash->set(array('stuff'))->expiresAt($expiration)->save(); $stash = $this->testConstruct($key); $itemExpiration = $stash->getExpiration(); @@ -328,23 +415,39 @@ public function testIsMiss() $key = array('isMiss', 'test'); $stash = $this->testConstruct($key); - $stash->set('testString'); + $stash->set('testString')->save(); $stash = $this->testConstruct($key); $this->assertTrue(!$stash->isMiss(), 'isMiss returns false for valid data'); } + public function testIsHit() + { + $stash = $this->testConstruct(array('This', 'Should', 'Fail')); + $this->assertFalse($stash->isHit(), 'isHit returns false for missing data'); + $data = $stash->get(); + $this->assertNull($data, 'getData returns null for missing data'); + + $key = array('isHit', 'test'); + + $stash = $this->testConstruct($key); + $stash->set('testString')->save(); + + $stash = $this->testConstruct($key); + $this->assertTrue($stash->isHit(), 'isHit returns true for valid data'); + } + public function testClear() { // repopulate foreach ($this->data as $type => $value) { $key = array('base', $type); $stash = $this->testConstruct($key); - $stash->set($value); + $stash->set($value)->save(); $this->assertAttributeInternalType('string', 'keyString', $stash, 'Argument based keys setup keystring'); $this->assertAttributeInternalType('array', 'key', $stash, 'Argument based keys setup key'); - $this->assertTrue($stash->set($value), 'Driver class able to store data type ' . $type); + $this->assertTrue($stash->set($value)->save(), 'Driver class able to store data type ' . $type); } foreach ($this->data as $type => $value) { @@ -373,7 +476,7 @@ public function testClear() foreach ($this->data as $type => $value) { $key = array('base', $type); $stash = $this->testConstruct($key); - $stash->set($value); + $stash->set($value)->save(); } // clear @@ -403,10 +506,11 @@ public function testExtend() $stash = $this->testConstruct($key); - $stash->set($value, -600); + $stash->set($value, -600)->save(); $stash = $this->testConstruct($key); - $this->assertTrue($stash->extend(), 'extend returns true'); + $this->assertEquals($stash->extend(), $stash, 'extend returns item object'); + $stash->save(); $stash = $this->testConstruct($key); $data = $stash->get(); @@ -460,7 +564,7 @@ private function getMockedDriver() private function assertDisabledStash(\Stash\Interfaces\ItemInterface $item) { - $this->assertFalse($item->set('true'), 'storeData returns false for disabled cache'); + $this->assertEquals($item, $item->set('true'), 'storeData returns self for disabled cache'); $this->assertNull($item->get(), 'getData returns null for disabled cache'); $this->assertFalse($item->clear(), 'clear returns false for disabled cache'); $this->assertTrue($item->isMiss(), 'isMiss returns true for disabled cache'); diff --git a/tests/Stash/Test/AbstractPoolTest.php b/tests/Stash/Test/AbstractPoolTest.php index 0bdd856b..da36e52d 100644 --- a/tests/Stash/Test/AbstractPoolTest.php +++ b/tests/Stash/Test/AbstractPoolTest.php @@ -33,8 +33,14 @@ class AbstractPoolTest extends \PHPUnit_Framework_TestCase public function testSetDriver() { - $pool = $this->getTestPool(); + $driver = new Ephemeral(); + $pool = new $this->poolClass($driver); + $this->assertAttributeEquals($driver, 'driver', $pool); + } + public function testSetItemDriver() + { + $pool = $this->getTestPool(); $stash = $pool->getItem('test'); $this->assertAttributeInstanceOf('Stash\Driver\Ephemeral', 'driver', $stash, 'set driver is pushed to new stash objects'); } @@ -49,14 +55,36 @@ public function testSetItemClass() $this->assertAttributeEquals($mockClassName, 'itemClass', $pool); } + public function testSetItemClassFakeClassException() + { + try { + $pool = $this->getTestPool(); + $pool->setItemClass('FakeClassName'); + } catch (\Exception $expected) { + return; + } + $this->fail('An expected exception has not been raised.'); + } + + public function testSetItemClassImproperClassException() + { + try { + $pool = $this->getTestPool(); + $pool->setItemClass('\stdClass'); + } catch (\Exception $expected) { + return; + } + $this->fail('An expected exception has not been raised.'); + } + public function testGetItem() { $pool = $this->getTestPool(); - $stash = $pool->getItem('base', 'one'); + $stash = $pool->getItem('base/one'); $this->assertInstanceOf('Stash\Item', $stash, 'getItem returns a Stash\Item object'); - $stash->set($this->data); + $stash->set($this->data)->save(); $storedData = $stash->get(); $this->assertEquals($this->data, $storedData, 'getItem returns working Stash\Item object'); @@ -64,21 +92,85 @@ public function testGetItem() $this->assertEquals('base/one', $key, 'Pool sets proper Item key.'); $pool->setNamespace('TestNamespace'); - $item = $pool->getItem(array('test', 'item')); + $item = $pool->getItem('test/item'); $this->assertAttributeEquals('TestNamespace', 'namespace', $item, 'Pool sets Item namespace.'); } - /** - * @expectedException InvalidArgumentException - * @expectedExceptionMessage Item constructor requires a key. - */ - public function testGetItemInvalidKey() + public function testSaveItem() { $pool = $this->getTestPool(); - $item = $pool->getItem(); + + $this->assertFalse($pool->hasItem('base/one'), 'Pool->hasItem() returns false for item without stored data.'); + $item = $pool->getItem('base/one'); + $this->assertInstanceOf('Stash\Item', $item, 'getItem returns a Stash\Item object'); + + $key = $item->getKey(); + $this->assertEquals('base/one', $key, 'Pool sets proper Item key.'); + + $item->set($this->data); + $this->assertTrue($pool->save($item), 'Pool->save() returns true.'); + $storedData = $item->get(); + $this->assertEquals($this->data, $storedData, 'Pool->save() returns proper data on passed Item.'); + + $item = $pool->getItem('base/one'); + $storedData = $item->get(); + $this->assertEquals($this->data, $storedData, 'Pool->save() returns proper data on new Item instance.'); + + $this->assertTrue($pool->hasItem('base/one'), 'Pool->hasItem() returns true for item with stored data.'); + + $pool->setNamespace('TestNamespace'); + $item = $pool->getItem('test/item'); + + $this->assertAttributeEquals('TestNamespace', 'namespace', $item, 'Pool sets Item namespace.'); } + + public function testSaveDeferredItem() + { + $pool = $this->getTestPool(); + + $this->assertFalse($pool->hasItem('base/one'), 'Pool->hasItem() returns false for item without stored data.'); + $item = $pool->getItem('base/one'); + $this->assertInstanceOf('Stash\Item', $item, 'getItem returns a Stash\Item object'); + + $key = $item->getKey(); + $this->assertEquals('base/one', $key, 'Pool sets proper Item key.'); + + $item->set($this->data); + $this->assertTrue($pool->saveDeferred($item), 'Pool->save() returns true.'); + $storedData = $item->get(); + $this->assertEquals($this->data, $storedData, 'Pool->save() returns proper data on passed Item.'); + + $item = $pool->getItem('base/one'); + $storedData = $item->get(); + $this->assertEquals($this->data, $storedData, 'Pool->save() returns proper data on new Item instance.'); + + $this->assertTrue($pool->hasItem('base/one'), 'Pool->hasItem() returns true for item with stored data.'); + + $pool->setNamespace('TestNamespace'); + $item = $pool->getItem('test/item'); + + $this->assertAttributeEquals('TestNamespace', 'namespace', $item, 'Pool sets Item namespace.'); + } + + public function testHasItem() + { + $pool = $this->getTestPool(); + $this->assertFalse($pool->hasItem('base/one'), 'Pool->hasItem() returns false for item without stored data.'); + $item = $pool->getItem('base/one'); + $item->set($this->data); + $pool->save($item); + $this->assertTrue($pool->hasItem('base/one'), 'Pool->hasItem() returns true for item with stored data.'); + } + + public function testCommit() + { + $pool = $this->getTestPool(); + $this->assertTrue($pool->commit()); + } + + /** * @expectedException InvalidArgumentException * @expectedExceptionMessage Invalid or Empty Node passed to getItem constructor. @@ -89,22 +181,22 @@ public function testGetItemInvalidKeyMissingNode() $item = $pool->getItem('This/Test//Fail'); } - public function testGetItemIterator() + public function testGetItems() { $pool = $this->getTestPool(); $keys = array_keys($this->multiData); - $cacheIterator = $pool->getItemIterator($keys); + $cacheIterator = $pool->getItems($keys); $keyData = $this->multiData; foreach ($cacheIterator as $key => $stash) { $this->assertTrue($stash->isMiss(), 'new Cache in iterator is empty'); - $stash->set($keyData[$key]); + $stash->set($keyData[$key])->save(); unset($keyData[$key]); } $this->assertCount(0, $keyData, 'all keys are accounted for the in cache iterator'); - $cacheIterator = $pool->getItemIterator($keys); + $cacheIterator = $pool->getItems($keys); foreach ($cacheIterator as $key => $stash) { $this->assertEquals($key, $stash->getKey(), 'Item key is not equals key in iterator'); $data = $stash->get($key); @@ -112,28 +204,60 @@ public function testGetItemIterator() } } - public function testFlushCache() + public function testDeleteItems() + { + $pool = $this->getTestPool(); + + $keys = array_keys($this->multiData); + + $cacheIterator = $pool->getItems($keys); + $keyData = $this->multiData; + foreach ($cacheIterator as $stash) { + $key = $stash->getKey(); + $this->assertTrue($stash->isMiss(), 'new Cache in iterator is empty'); + $stash->set($keyData[$key])->save(); + unset($keyData[$key]); + } + $this->assertCount(0, $keyData, 'all keys are accounted for the in cache iterator'); + + $cacheIterator = $pool->getItems($keys); + foreach ($cacheIterator as $item) { + $key = $item->getKey(); + $data = $item->get($key); + $this->assertEquals($this->multiData[$key], $data, 'data put into the pool comes back the same through iterators.'); + } + + $this->assertTrue($pool->deleteItems($keys), 'deleteItems returns true.'); + $cacheIterator = $pool->getItems($keys); + foreach ($cacheIterator as $item) { + $this->assertTrue($item->isMiss(), 'data cleared using deleteItems is removed from the cache.'); + } + } + + + + public function testClearCache() { $pool = $this->getTestPool(); - $stash = $pool->getItem('base', 'one'); - $stash->set($this->data); - $this->assertTrue($pool->flush(), 'flush returns true'); + $stash = $pool->getItem('base/one'); + $stash->set($this->data)->save(); + $this->assertTrue($pool->clear(), 'clear returns true'); - $stash = $pool->getItem('base', 'one'); + $stash = $pool->getItem('base/one'); $this->assertNull($stash->get(), 'clear removes item'); - $this->assertTrue($stash->isMiss(), 'flush causes cache miss'); + $this->assertTrue($stash->isMiss(), 'clear causes cache miss'); } public function testPurgeCache() { $pool = $this->getTestPool(); - $stash = $pool->getItem('base', 'one'); - $stash->set($this->data, -600); + $stash = $pool->getItem('base/one'); + $stash->set($this->data)->expiresAfter(-600)->save(); $this->assertTrue($pool->purge(), 'purge returns true'); - $stash = $pool->getItem('base', 'one'); + $stash = $pool->getItem('base/one'); $this->assertNull($stash->get(), 'purge removes item'); $this->assertTrue($stash->isMiss(), 'purge causes cache miss'); } @@ -162,13 +286,6 @@ public function testInvalidNamespace() $pool->setNamespace('!@#$%^&*('); } - public function testgetItemArrayConversion() - { - $pool = $this->getTestPool(); - - $cache = $pool->getItem(array('base', 'one')); - $this->assertEquals($cache->getKey(), 'base/one'); - } public function testSetLogger() { @@ -180,10 +297,13 @@ public function testSetLogger() $logger = new LoggerStub(); $pool->setLogger($logger); - $this->assertAttributeInstanceOf('Stash\Test\Stubs\LoggerStub', 'logger', $pool, 'setLogger injects logger into Item.'); + $this->assertAttributeInstanceOf('Stash\Test\Stubs\LoggerStub', 'logger', $pool, 'setLogger injects logger into Pool.'); + + $item = $pool->getItem('testItem'); + $this->assertAttributeInstanceOf('Stash\Test\Stubs\LoggerStub', 'logger', $item, 'setLogger injects logger into Pool.'); } - public function testLoggerFlush() + public function testLoggerClear() { $pool = $this->getTestPool(); @@ -194,7 +314,7 @@ public function testLoggerFlush() $pool->setLogger($logger); // triggerlogging - $pool->flush(); + $pool->clear(); $this->assertInstanceOf('Stash\Test\Exception\TestException', $logger->lastContext['exception'], 'Logger was passed exception in event context.'); diff --git a/tests/Stash/Test/CacheExceptionTest.php b/tests/Stash/Test/CacheExceptionTest.php index a8e6cbbd..e770e9c9 100644 --- a/tests/Stash/Test/CacheExceptionTest.php +++ b/tests/Stash/Test/CacheExceptionTest.php @@ -31,7 +31,7 @@ public function testSet() $item->setKey(array('path', 'to', 'store')); $this->assertFalse($item->isDisabled()); - $this->assertFalse($item->set(array(1, 2, 3), 3600)); + $this->assertFalse($item->set(array(1, 2, 3), 3600)->save()); $this->assertTrue($item->isDisabled(), 'Is disabled after exception is thrown in driver'); } @@ -75,17 +75,17 @@ public function testPurge() $this->assertFalse($pool->purge()); } - public function testFlush() + public function testPoolClear() { $pool = new Pool(); $pool->setDriver(new DriverExceptionStub()); $item = $pool->getItem('test'); $this->assertFalse($item->isDisabled()); - $this->assertFalse($pool->flush()); + $this->assertFalse($pool->clear()); $item = $pool->getItem('test'); $this->assertTrue($item->isDisabled(), 'Is disabled after exception is thrown in driver'); - $this->assertFalse($pool->flush()); + $this->assertFalse($pool->clear()); } } diff --git a/tests/Stash/Test/Driver/AbstractDriverTest.php b/tests/Stash/Test/Driver/AbstractDriverTest.php index 9ac57701..10fee4e6 100644 --- a/tests/Stash/Test/Driver/AbstractDriverTest.php +++ b/tests/Stash/Test/Driver/AbstractDriverTest.php @@ -44,6 +44,7 @@ abstract class AbstractDriverTest extends \PHPUnit_Framework_TestCase protected $driverClass; protected $startTime; protected $setup = false; + protected $persistence = false; public static function tearDownAfterClass() { @@ -86,8 +87,7 @@ public function testSetOptions() { $driverType = $this->driverClass; $options = $this->getOptions(); - $driver = new $driverType(); - $driver->setOptions($options); + $driver = new $driverType($options); $this->assertTrue(is_a($driver, $driverType), 'Driver is an instance of ' . $driverType); $this->assertTrue(is_a($driver, '\Stash\Interfaces\DriverInterface'), 'Driver implments the Stash\Driver\DriverInterface interface'); @@ -227,4 +227,15 @@ public function testDestructor($driver) { $driver=null; } + + /** + * @depends testDestructor + */ + public function testIsPersistant() + { + if (!$driver = $this->getFreshDriver()) { + $this->markTestSkipped('Driver class unsuited for current environment'); + } + $this->assertEquals($this->persistence, $driver->isPersistent()); + } } diff --git a/tests/Stash/Test/Driver/ApcTest.php b/tests/Stash/Test/Driver/ApcTest.php index 857b047a..c3437752 100644 --- a/tests/Stash/Test/Driver/ApcTest.php +++ b/tests/Stash/Test/Driver/ApcTest.php @@ -18,6 +18,7 @@ class ApcTest extends AbstractDriverTest { protected $driverClass = 'Stash\Driver\Apc'; + protected $persistence = true; public function testSetOptions() { @@ -25,8 +26,7 @@ public function testSetOptions() $options = $this->getOptions(); $options['namespace'] = 'namespace_test'; $options['ttl'] = 15; - $driver = new $driverType(); - $driver->setOptions($options); + $driver = new $driverType($options); $this->assertAttributeEquals('namespace_test', 'apcNamespace', $driver, 'APC is setting supplied namespace.'); $this->assertAttributeEquals(15, 'ttl', $driver, 'APC is setting supplied ttl.'); diff --git a/tests/Stash/Test/Driver/CompositeTest.php b/tests/Stash/Test/Driver/CompositeTest.php index f2cdeff0..c1bcbf2b 100644 --- a/tests/Stash/Test/Driver/CompositeTest.php +++ b/tests/Stash/Test/Driver/CompositeTest.php @@ -112,4 +112,29 @@ public function testIsPersistent() $driver = new Composite(array('drivers' => $drivers)); $this->assertFalse($driver->isPersistent()); } + + + /** + * @expectedException Stash\Exception\RuntimeException + */ + public function testWithoutDriversException() + { + $driver = new Composite(array('drivers' => null)); + } + + /** + * @expectedException Stash\Exception\RuntimeException + */ + public function testWithFakeDriversException() + { + $driver = new Composite(array('drivers' => array('fakedriver'))); + } + + /** + * @expectedException Stash\Exception\InvalidArgumentException + */ + public function testWithBadDriverArgumentException() + { + $driver = new Composite(array('drivers' => 'fakedriver')); + } } diff --git a/tests/Stash/Test/Driver/EphemeralTest.php b/tests/Stash/Test/Driver/EphemeralTest.php index cca9a7ed..ad47b920 100644 --- a/tests/Stash/Test/Driver/EphemeralTest.php +++ b/tests/Stash/Test/Driver/EphemeralTest.php @@ -31,12 +31,12 @@ public function testKeyCollisions1() $item1 = new Item(); $item1->setPool($poolStub); $item1->setKey(array('##', '#')); - $item1->set('X'); + $item1->set('X')->save(); $item2 = new Item(); $item2->setPool($poolStub); $item2->setKey(array('#', '##')); - $item2->set('Y'); + $item2->set('Y')->save(); $this->assertEquals('X', $item1->get()); } diff --git a/tests/Stash/Test/Driver/FileSystemTest.php b/tests/Stash/Test/Driver/FileSystemTest.php index bde38b37..7db79bed 100644 --- a/tests/Stash/Test/Driver/FileSystemTest.php +++ b/tests/Stash/Test/Driver/FileSystemTest.php @@ -28,6 +28,7 @@ class FileSystemTest extends AbstractDriverTest { protected $driverClass = 'Stash\Driver\FileSystem'; protected $extension = '.php'; + protected $persistence = true; protected function getOptions($options = array()) { @@ -39,14 +40,43 @@ protected function getOptions($options = array()) */ public function testOptionKeyHashFunctionException() { - $driver = new FileSystem(); - $driver->setOptions($this->getOptions(array('keyHashFunction' => 'foobar_'.mt_rand()))); + $driver = new FileSystem($this->getOptions(array('keyHashFunction' => 'foobar_'.mt_rand()))); } + /** + * @expectedException Stash\Exception\RuntimeException + */ + public function testOptionEncoderObjectException() + { + $encoder = new \stdClass(); + $driver = new FileSystem($this->getOptions(array('encoder' => $encoder))); + } + + /** + * @expectedException Stash\Exception\RuntimeException + */ + public function testOptionEncoderStringException() + { + $encoder = 'stdClass'; + $driver = new FileSystem($this->getOptions(array('encoder' => $encoder))); + } + + public function testOptionEncoderAsObject() + { + $encoder = new \Stash\Driver\FileSystem\NativeEncoder(); + $driver = new FileSystem($this->getOptions(array('encoder' => $encoder))); + } + + public function testOptionEncoderAsString() + { + $encoder = '\Stash\Driver\FileSystem\NativeEncoder'; + $driver = new FileSystem($this->getOptions(array('encoder' => $encoder))); + } + + public function testOptionKeyHashFunction() { $driver = new FileSystem(array('keyHashFunction' => 'md5')); - $driver->setOptions($this->getOptions(array('keyHashFunction' => 'md5'))); } /** @@ -60,8 +90,7 @@ public function testOptionKeyHashFunctionDirs() $paths = array('one', 'two', 'three', 'four'); foreach ($hashfunctions as $hashfunction) { - $driver = new FileSystem(); - $driver->setOptions($this->getOptions(array( + $driver = new FileSystem($this->getOptions(array( 'keyHashFunction' => $hashfunction, 'path' => sys_get_temp_dir().DIRECTORY_SEPARATOR.'stash', 'dirSplit' => 1 @@ -75,7 +104,7 @@ public function testOptionKeyHashFunctionDirs() $poolStub->setDriver($driver); $item->setPool($poolStub); $item->setKey($paths); - $item->set($rand); + $item->set($rand)->save(); $allpaths = array_merge(array('cache'), $paths); $predicted = sys_get_temp_dir(). @@ -106,8 +135,7 @@ public function testLongPathFolderCreation() $cachePath = sys_get_temp_dir().DIRECTORY_SEPARATOR.'stash'; - $driver = new FileSystem(); - $driver->setOptions($this->getOptions(array( + $driver = new FileSystem($this->getOptions(array( 'keyHashFunction' => 'Stash\Test\Driver\strdup', 'path' => $cachePath, 'dirSplit' => 1 @@ -143,8 +171,7 @@ public function testLongPathFileCreation() $cachePath = sys_get_temp_dir().DIRECTORY_SEPARATOR.'stash'; - $driver = new FileSystem(); - $driver->setOptions($this->getOptions(array( + $driver = new FileSystem($this->getOptions(array( 'keyHashFunction' => 'Stash\Test\Driver\strdup', 'path' => $cachePath, 'dirSplit' => 1 diff --git a/tests/Stash/Test/Driver/MemcacheAnyTest.php b/tests/Stash/Test/Driver/MemcacheAnyTest.php index 65079cfc..02258404 100644 --- a/tests/Stash/Test/Driver/MemcacheAnyTest.php +++ b/tests/Stash/Test/Driver/MemcacheAnyTest.php @@ -50,8 +50,7 @@ public function testConstruction() $options = array(); $options['servers'][] = array('127.0.0.1', '11211', '50'); - $driver = new Memcache(); - $driver->setOptions($options); + $driver = new Memcache($options); $item = new Item(); $poolStub = new PoolGetDriverStub(); @@ -59,6 +58,6 @@ public function testConstruction() $item->setPool($poolStub); $item->setKey($key); - $this->assertTrue($item->set($key), 'Able to load and store with unconfigured extension.'); + $this->assertTrue($item->set($key)->save(), 'Able to load and store with unconfigured extension.'); } } diff --git a/tests/Stash/Test/Driver/MemcacheTest.php b/tests/Stash/Test/Driver/MemcacheTest.php index 89a1aa49..414e43c0 100644 --- a/tests/Stash/Test/Driver/MemcacheTest.php +++ b/tests/Stash/Test/Driver/MemcacheTest.php @@ -25,6 +25,7 @@ class MemcacheTest extends AbstractDriverTest protected $extension = 'memcache'; protected $servers = array('127.0.0.1', '11211'); + protected $persistence = true; protected function getOptions() { @@ -69,8 +70,7 @@ public function testConstructionOptions() $options['servers'][] = array('127.0.0.1', '11211', '50'); $options['servers'][] = array('127.0.0.1', '11211'); $options['extension'] = $this->extension; - $driver = new Memcache(); - $driver->setOptions($options); + $driver = new Memcache($options); $item = new Item(); $poolStub = new PoolGetDriverStub(); @@ -78,17 +78,16 @@ public function testConstructionOptions() $item->setPool($poolStub); $item->setKey($key); - $this->assertTrue($item->set($key), 'Able to load and store memcache driver using multiple servers'); + $this->assertTrue($item->set($key)->save(), 'Able to load and store memcache driver using multiple servers'); $options = array(); $options['extension'] = $this->extension; - $driver = new Memcache(); - $driver->setOptions($options); + $driver = new Memcache($options); $item = new Item(); $poolStub = new PoolGetDriverStub(); $poolStub->setDriver($driver); $item->setPool($poolStub); $item->setKey($key); - $this->assertTrue($item->set($key), 'Able to load and store memcache driver using default server'); + $this->assertTrue($item->set($key)->save(), 'Able to load and store memcache driver using default server'); } } diff --git a/tests/Stash/Test/Driver/MemcachedTest.php b/tests/Stash/Test/Driver/MemcachedTest.php index 014c296b..11f4ae9e 100644 --- a/tests/Stash/Test/Driver/MemcachedTest.php +++ b/tests/Stash/Test/Driver/MemcachedTest.php @@ -11,6 +11,8 @@ namespace Stash\Test\Driver; +use \Stash\Driver\Memcache; + /** * @package Stash * @author Robert Hafner @@ -32,4 +34,64 @@ protected function getOptions() return array_merge($options, $memcachedOptions); } + + /** + * @expectedException Stash\Exception\RuntimeException + */ + public function testSetHashException() + { + $options = array(); + $options['servers'][] = array('127.0.0.1', '11211', '50'); + $options['servers'][] = array('127.0.0.1', '11211'); + $options['hash'] = 'InvalidOption'; + $driver = new Memcache($options); + } + + /** + * @expectedException Stash\Exception\RuntimeException + */ + public function testSetDistributionException() + { + $options = array(); + $options['servers'][] = array('127.0.0.1', '11211', '50'); + $options['servers'][] = array('127.0.0.1', '11211'); + $options['distribution'] = 'InvalidOption'; + $driver = new Memcache($options); + } + + /** + * @expectedException Stash\Exception\RuntimeException + */ + public function testSetSerializerException() + { + $options = array(); + $options['servers'][] = array('127.0.0.1', '11211', '50'); + $options['servers'][] = array('127.0.0.1', '11211'); + $options['serializer'] = 'InvalidOption'; + $driver = new Memcache($options); + } + + /** + * @expectedException Stash\Exception\RuntimeException + */ + public function testSetNumberedValueException() + { + $options = array(); + $options['servers'][] = array('127.0.0.1', '11211', '50'); + $options['servers'][] = array('127.0.0.1', '11211'); + $options['connect_timeout'] = 'InvalidOption'; + $driver = new Memcache($options); + } + + /** + * @expectedException Stash\Exception\RuntimeException + */ + public function testSetBooleanValueException() + { + $options = array(); + $options['servers'][] = array('127.0.0.1', '11211', '50'); + $options['servers'][] = array('127.0.0.1', '11211'); + $options['cache_lookups'] = 'InvalidOption'; + $driver = new Memcache($options); + } } diff --git a/tests/Stash/Test/Driver/RedisArrayTest.php b/tests/Stash/Test/Driver/RedisArrayTest.php index acc9ab76..858b9b4e 100644 --- a/tests/Stash/Test/Driver/RedisArrayTest.php +++ b/tests/Stash/Test/Driver/RedisArrayTest.php @@ -21,6 +21,7 @@ class RedisArrayTest extends RedisTest protected $redisSecondServer = '127.0.0.1'; protected $redisSecondPort = '6380'; + protected $persistence = true; protected function setUp() { diff --git a/src/Stash/Driver/Sub/SqlitePdo2.php b/tests/Stash/Test/Driver/RedisSocketTest.php similarity index 54% rename from src/Stash/Driver/Sub/SqlitePdo2.php rename to tests/Stash/Test/Driver/RedisSocketTest.php index 8197de00..528160ff 100644 --- a/src/Stash/Driver/Sub/SqlitePdo2.php +++ b/tests/Stash/Test/Driver/RedisSocketTest.php @@ -9,21 +9,20 @@ * file that was distributed with this source code. */ -namespace Stash\Driver\Sub; +namespace Stash\Test\Driver; /** - * Class SqlitePDO2 - * - * This SQLite subdriver uses PDO and SQLite2. - * - * @internal * @package Stash * @author Robert Hafner */ -class SqlitePdo2 extends SqlitePdo +class RedisSocketTest extends RedisTest { - /** - * {@inheritdoc} - */ - protected static $pdoDriver = 'sqlite2'; + protected function getOptions() + { + $socket = '/tmp/redis.sock'; + + return array('servers' => array( + array('socket' => $socket, 'ttl' => 0.1) + )); + } } diff --git a/tests/Stash/Test/Driver/RedisTest.php b/tests/Stash/Test/Driver/RedisTest.php index ca4f1d6c..4d728c7b 100644 --- a/tests/Stash/Test/Driver/RedisTest.php +++ b/tests/Stash/Test/Driver/RedisTest.php @@ -23,6 +23,7 @@ class RedisTest extends AbstractDriverTest protected $redisNoServer = '127.0.0.1'; protected $redisNoPort = '6381'; + protected $persistence = true; protected function setUp() { diff --git a/tests/Stash/Test/Driver/SqliteAnyTest.php b/tests/Stash/Test/Driver/SqliteAnyTest.php index 7e661da8..cbf7c2a8 100644 --- a/tests/Stash/Test/Driver/SqliteAnyTest.php +++ b/tests/Stash/Test/Driver/SqliteAnyTest.php @@ -14,6 +14,7 @@ use Stash\Test\Stubs\PoolGetDriverStub; use Stash\Driver\Sqlite; use Stash\Item; +use Stash\Pool; use Stash\Utilities; /** @@ -39,15 +40,12 @@ public function testConstruction() { $key = array('apple', 'sauce'); - $options = array(); - $driver = new Sqlite(); - $driver->setOptions($options); - $item = new Item(); - $poolSub = new PoolGetDriverStub(); - $poolSub->setDriver($driver); - $item->setPool($poolSub); - $item->setKey($key); - $this->assertTrue($item->set($key), 'Able to load and store with unconfigured extension.'); + $driver = new Sqlite(array()); + $pool = new Pool(); + $pool->setDriver($driver); + $item = $pool->getItem('testKey'); + $item->set($key); + $this->assertTrue($pool->save($item), 'Able to load and store with unconfigured extension.'); } public static function tearDownAfterClass() diff --git a/tests/Stash/Test/Driver/SqlitePdoSqlite2Test.php b/tests/Stash/Test/Driver/SqlitePdoSqlite2Test.php deleted file mode 100644 index 123d0e5c..00000000 --- a/tests/Stash/Test/Driver/SqlitePdoSqlite2Test.php +++ /dev/null @@ -1,46 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Stash\Test\Driver; - -/** - * @package Stash - * @author Robert Hafner - */ -class SqlitePdoSqlite2Test extends AbstractDriverTest -{ - protected $driverClass = 'Stash\Driver\Sqlite'; - protected $subDriverClass = 'Stash\Driver\Sub\SqlitePdo2'; - - protected function setUp() - { - $driver = '\\' . $this->driverClass; - $subDriver = '\\' . $this->subDriverClass; - - if (!$driver::isAvailable() || !$subDriver::isAvailable()) { - $this->markTestSkipped('Driver class unsuited for current environment'); - - return; - } - - parent::setUp(); - } - - public function getOptions() - { - $options = parent::getOptions(); - $options['extension'] = 'pdo'; - $options['nesting'] = 2; - $options['version'] = 2; - - return $options; - } -} diff --git a/tests/Stash/Test/Driver/SqlitePdoSqlite3Test.php b/tests/Stash/Test/Driver/SqlitePdoSqlite3Test.php index cd89b4aa..65144b2d 100644 --- a/tests/Stash/Test/Driver/SqlitePdoSqlite3Test.php +++ b/tests/Stash/Test/Driver/SqlitePdoSqlite3Test.php @@ -19,6 +19,7 @@ class SqlitePdoSqlite3Test extends AbstractDriverTest { protected $driverClass = 'Stash\Driver\Sqlite'; protected $subDriverClass = 'Stash\Driver\Sub\SqlitePdo'; + protected $persistence = true; protected function setUp() { diff --git a/tests/Stash/Test/Driver/SqliteSqlite2Test.php b/tests/Stash/Test/Driver/SqliteSqlite2Test.php deleted file mode 100644 index adad35df..00000000 --- a/tests/Stash/Test/Driver/SqliteSqlite2Test.php +++ /dev/null @@ -1,45 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Stash\Test\Driver; - -/** - * @package Stash - * @author Robert Hafner - */ -class SqliteSqlite2Test extends AbstractDriverTest -{ - protected $driverClass = 'Stash\Driver\Sqlite'; - protected $subDriverClass = 'Stash\Driver\Sub\Sqlite'; - - protected function setUp() - { - $driver = '\\' . $this->driverClass; - $subDriver = '\\' . $this->subDriverClass; - - if (!$driver::isAvailable() || !$subDriver::isAvailable()) { - $this->markTestSkipped('Driver class unsuited for current environment'); - - return; - } - - parent::setUp(); - } - - public function getOptions() - { - $options = parent::getOptions(); - $options['extension'] = 'sqlite'; - $options['nesting'] = 2; - - return $options; - } -} diff --git a/tests/Stash/Test/Driver/UnavailableDriverTest.php b/tests/Stash/Test/Driver/UnavailableDriverTest.php new file mode 100644 index 00000000..6988392c --- /dev/null +++ b/tests/Stash/Test/Driver/UnavailableDriverTest.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Stash\Test\Driver; + +use Stash\Test\Stubs\DriverUnavailableStub; + +/** + * @package Stash + * @author Robert Hafner + */ +class UnavailableDriverTest extends \PHPUnit_Framework_TestCase +{ + /** + * @expectedException Stash\Exception\RuntimeException + */ + public function testUnavailableDriver() + { + new DriverUnavailableStub(); + } +} diff --git a/tests/Stash/Test/DriverListTest.php b/tests/Stash/Test/DriverListTest.php index c1789e70..bf11f054 100644 --- a/tests/Stash/Test/DriverListTest.php +++ b/tests/Stash/Test/DriverListTest.php @@ -24,20 +24,28 @@ public function testGetAvailableDrivers() $drivers = DriverList::getAvailableDrivers(); $this->assertArrayHasKey('FileSystem', $drivers, 'getDrivers returns FileSystem driver'); $this->assertArrayNotHasKey('Array', $drivers, 'getDrivers doesn\'t return Array driver'); + + DriverList::registerDriver('TestUnavailable_getAvailable', '\Stash\Test\Stubs\DriverUnavailableStub'); + $drivers = DriverList::getAvailableDrivers(); + $this->assertArrayNotHasKey('TestUnavailable_getAvailable', $drivers, 'getAllDrivers doesn\'t return TestBroken driver'); } - public function testGetDrivers() + public function testGetAllDrivers() { - $availableDrivers = DriverList::getAvailableDrivers(); - $getDrivers = DriverList::getDrivers(); - $this->assertEquals($availableDrivers, $getDrivers, 'getDrivers is an alias for getAvailableDrivers'); + DriverList::registerDriver('TestBroken_getAll', 'stdClass'); + $drivers = DriverList::getAllDrivers(); + $this->assertArrayNotHasKey('TestBroken_getAll', $drivers, 'getAllDrivers doesn\'t return TestBroken driver'); + + DriverList::registerDriver('TestUnavailable_getAll', '\Stash\Test\Stubs\DriverUnavailableStub'); + $drivers = DriverList::getAllDrivers(); + $this->assertArrayHasKey('TestUnavailable_getAll', $drivers, 'getAllDrivers doesn\'t return TestBroken driver'); } public function testRegisterDriver() { DriverList::registerDriver('Array', 'Stash\Driver\Ephemeral'); - $drivers = DriverList::getDrivers(); + $drivers = DriverList::getAvailableDrivers(); $this->assertArrayHasKey('Array', $drivers, 'getDrivers returns Array driver'); } diff --git a/tests/Stash/Test/ItemLoggerTest.php b/tests/Stash/Test/ItemLoggerTest.php index cfc20b81..1aab6168 100644 --- a/tests/Stash/Test/ItemLoggerTest.php +++ b/tests/Stash/Test/ItemLoggerTest.php @@ -75,7 +75,7 @@ public function testSet() $item->setLogger($logger); // triggerlogging - $item->set('test_key'); + $item->set('test_key')->save(); $this->assertInstanceOf('Stash\Test\Exception\TestException', $logger->lastContext['exception'], 'Logger was passed exception in event context.'); diff --git a/tests/Stash/Test/PoolNamespaceTest.php b/tests/Stash/Test/PoolNamespaceTest.php index 3f4247a3..e8fb5866 100644 --- a/tests/Stash/Test/PoolNamespaceTest.php +++ b/tests/Stash/Test/PoolNamespaceTest.php @@ -30,41 +30,41 @@ protected function getTestPool($skipNametest = false) return $pool; } - public function testFlushNamespacedCache() + public function testClearNamespacedCache() { $pool = $this->getTestPool(true); // No Namespace - $item = $pool->getItem(array('base', 'one')); - $item->set($this->data); + $item = $pool->getItem('base/one'); + $item->set($this->data)->save(); // TestNamespace $pool->setNamespace('TestNamespace'); - $item = $pool->getItem(array('test', 'one')); - $item->set($this->data); + $item = $pool->getItem('test/one'); + $item->set($this->data)->save(); // TestNamespace2 $pool->setNamespace('TestNamespace2'); - $item = $pool->getItem(array('test', 'one')); - $item->set($this->data); + $item = $pool->getItem('test/one'); + $item->set($this->data)->save(); - // Flush TestNamespace + // Clear TestNamespace $pool->setNamespace('TestNamespace'); - $this->assertTrue($pool->flush(), 'Flush succeeds with namespace selected.'); + $this->assertTrue($pool->clear(), 'Clear succeeds with namespace selected.'); // Return to No Namespace $pool->setNamespace(); - $item = $pool->getItem(array('base', 'one')); - $this->assertFalse($item->isMiss(), 'Base item exists after other namespace was flushed.'); - $this->assertEquals($this->data, $item->get(), 'Base item returns data after other namespace was flushed.'); + $item = $pool->getItem('base/one'); + $this->assertFalse($item->isMiss(), 'Base item exists after other namespace was cleared.'); + $this->assertEquals($this->data, $item->get(), 'Base item returns data after other namespace was cleared.'); - // Flush All - $this->assertTrue($pool->flush(), 'Flush succeeds with no namespace.'); + // Clear All + $this->assertTrue($pool->clear(), 'Clear succeeds with no namespace.'); // Return to TestNamespace2 $pool->setNamespace('TestNamespace2'); - $item = $pool->getItem(array('base', 'one')); - $this->assertTrue($item->isMiss(), 'Namespaced item disappears after complete flush.'); + $item = $pool->getItem('base/one'); + $this->assertTrue($item->isMiss(), 'Namespaced item disappears after complete clear.'); } public function testNamespacing() diff --git a/tests/Stash/Test/Stubs/PoolGetDriverStub.php b/tests/Stash/Test/Stubs/PoolGetDriverStub.php index 367acbb2..d4398c82 100644 --- a/tests/Stash/Test/Stubs/PoolGetDriverStub.php +++ b/tests/Stash/Test/Stubs/PoolGetDriverStub.php @@ -11,6 +11,7 @@ namespace Stash\Test\Stubs; +use Psr\Cache\CacheItemInterface; use Stash\Interfaces\PoolInterface; /** @@ -38,17 +39,17 @@ public function setItemClass($class) return true; } - public function getItem() + public function getItem($key) { return false; } - public function getItemIterator($keys) + public function getItems(array $keys = array()) { return false; } - public function flush() + public function clear() { return false; } @@ -72,4 +73,35 @@ public function setLogger($logger) { return false; } + + public function hasItem($key) + { + return false; + } + + public function commit() + { + return false; + } + + public function saveDeferred(CacheItemInterface $item) + { + return false; + } + + public function save(CacheItemInterface $item) + { + return false; + } + + public function deleteItems(array $keys) + { + return false; + } + + + public function deleteItem($key) + { + return false; + } } diff --git a/tests/Stash/Test/UtilitiesTest.php b/tests/Stash/Test/UtilitiesTest.php index 1186a44b..54a238a6 100644 --- a/tests/Stash/Test/UtilitiesTest.php +++ b/tests/Stash/Test/UtilitiesTest.php @@ -27,11 +27,11 @@ public function testEncoding() $this->assertEquals(Utilities::encoding('String of doom!'), 'string', 'encoding recognized string scalar'); - $this->assertEquals(Utilities::encoding(234), 'none', 'encoding recognized integer scalar'); - $this->assertEquals(Utilities::encoding(1.432), 'none', 'encoding recognized float scalar'); + $this->assertEquals(Utilities::encoding(234), 'numeric', 'encoding recognized integer scalar'); + $this->assertEquals(Utilities::encoding(1.432), 'numeric', 'encoding recognized float scalar'); $this->assertEquals(Utilities::encoding(pow(2, 31)), 'serialize', 'encoding recognized large number'); - $this->assertEquals(Utilities::encoding(pow(2, 31) - 1), 'none', 'encoding recognized small number'); + $this->assertEquals(Utilities::encoding(pow(2, 31) - 1), 'numeric', 'encoding recognized small number'); $std = new \stdClass(); $this->assertEquals(Utilities::encoding($std), 'serialize', 'encoding recognized object'); @@ -120,6 +120,24 @@ public function testDeleteRecursive() $this->assertFileNotExists($tmp, 'deleteRecursive cleared out the empty parent directory'); } + + /** + * @expectedException Stash\Exception\RuntimeException + */ + public function testDeleteRecursiveRelativeException() + { + Utilities::deleteRecursive('../tests/fakename'); + } + + /** + * @expectedException Stash\Exception\RuntimeException + */ + public function testDeleteRecursiveRootException() + { + Utilities::deleteRecursive('/'); + } + + public function testCheckEmptyDirectory() { $tmp = sys_get_temp_dir() . '/stash/'; @@ -130,4 +148,41 @@ public function testCheckEmptyDirectory() $this->assertFalse(Utilities::checkForEmptyDirectory($tmp), 'Returns false for non-empty directories'); Utilities::deleteRecursive($tmp); } + + /** + * @expectedException Stash\Exception\RuntimeException + */ + public function testCheckFileSystemPermissionsNullException() + { + Utilities::checkFileSystemPermissions(null, '0644'); + } + + /** + * @expectedException Stash\Exception\InvalidArgumentException + */ + public function testCheckFileSystemPermissionsFileException() + { + $tmp = sys_get_temp_dir() . '/stash/'; + $dir2 = $tmp . 'emptytest/'; + @mkdir($dir2, 0770, true); + touch($dir2 . 'testfile'); + + Utilities::checkFileSystemPermissions($dir2 . 'testfile', '0644'); + } + + /** + * @expectedException Stash\Exception\InvalidArgumentException + */ + public function testCheckFileSystemPermissionsUnaccessibleException() + { + Utilities::checkFileSystemPermissions('/fakedir/cache', '0644'); + } + + /** + * @expectedException Stash\Exception\InvalidArgumentException + */ + public function testCheckFileSystemPermissionsUnwrittableException() + { + Utilities::checkFileSystemPermissions('/home', '0644'); + } } diff --git a/tests/travis/files/redis/redis-server2.conf b/tests/travis/files/redis/redis-server2.conf index 1c41fc50..131feed4 100644 --- a/tests/travis/files/redis/redis-server2.conf +++ b/tests/travis/files/redis/redis-server2.conf @@ -33,8 +33,8 @@ port 6380 # incoming connections. There is no default, so Redis will not listen # on a unix socket when not specified. # -# unixsocket /tmp/redis.sock -# unixsocketperm 755 +unixsocket /tmp/redis.sock +unixsocketperm 777 # Close the connection after a client is idle for N seconds (0 to disable) timeout 0 @@ -146,9 +146,9 @@ dbfilename dump.rdb # # The DB will be written inside this directory, with the filename specified # above using the 'dbfilename' configuration directive. -# +# # The Append Only File will also be created inside this directory. -# +# # Note that you must specify a directory here, not a file name. dir /var/lib/redis2 @@ -250,7 +250,7 @@ slave-priority 100 # # This should stay commented out for backward compatibility and because most # people do not need auth (e.g. they run their own servers). -# +# # Warning: since Redis is pretty fast an outside user can try up to # 150k passwords per second against a good box. This means that you should # use a very strong password otherwise it will be very easy to break. @@ -316,14 +316,14 @@ slave-priority 100 # MAXMEMORY POLICY: how Redis will select what to remove when maxmemory # is reached. You can select among five behaviors: -# +# # volatile-lru -> remove the key with an expire set using an LRU algorithm # allkeys-lru -> remove any key accordingly to the LRU algorithm # volatile-random -> remove a random key with an expire set # allkeys-random -> remove a random key, any key # volatile-ttl -> remove the key with the nearest expire time (minor TTL) # noeviction -> don't expire at all, just return an error on write operations -# +# # Note: with any of the above policies, Redis will return an error on write # operations, when there are not suitable keys for eviction. # @@ -371,7 +371,7 @@ appendonly no # appendfilename appendonly.aof # The fsync() call tells the Operating System to actually write data on disk -# instead to wait for more data in the output buffer. Some OS will really flush +# instead to wait for more data in the output buffer. Some OS will really flush # data on disk, some other OS will just try to do it ASAP. # # Redis supports three different modes: @@ -412,7 +412,7 @@ appendfsync everysec # the same as "appendfsync none". In practical terms, this means that it is # possible to lose up to 30 seconds of log in the worst scenario (with the # default Linux settings). -# +# # If you have latency problems turn this to "yes". Otherwise leave it as # "no" that is the safest pick from the point of view of durability. no-appendfsync-on-rewrite no @@ -420,7 +420,7 @@ no-appendfsync-on-rewrite no # Automatic rewrite of the append only file. # Redis is able to automatically rewrite the log file implicitly calling # BGREWRITEAOF when the AOF log size grows by the specified percentage. -# +# # This is how it works: Redis remembers the size of the AOF file after the # latest rewrite (if no rewrite has happened since the restart, the size of # the AOF at startup is used). @@ -463,7 +463,7 @@ lua-time-limit 5000 # but just the time needed to actually execute the command (this is the only # stage of command execution where the thread is blocked and can not serve # other requests in the meantime). -# +# # You can configure the slow log with two parameters: one tells Redis # what is the execution time, in microseconds, to exceed in order for the # command to get logged, and the other parameter is the length of the @@ -513,7 +513,7 @@ zset-max-ziplist-value 64 # that is rehashing, the more rehashing "steps" are performed, so if the # server is idle the rehashing is never complete and some more memory is used # by the hash table. -# +# # The default is to use this millisecond 10 times every second in order to # active rehashing the main dictionaries, freeing memory when possible. # diff --git a/tests/travis/php_extensions.ini b/tests/travis/php_extensions_5.4.ini similarity index 85% rename from tests/travis/php_extensions.ini rename to tests/travis/php_extensions_5.4.ini index 7fa8c39f..4ad3ced4 100644 --- a/tests/travis/php_extensions.ini +++ b/tests/travis/php_extensions_5.4.ini @@ -4,4 +4,4 @@ extension="memcached.so" extension="redis.so" apc.enabled=1 -apc.enable_cli=1 +apc.enable_cli=1 diff --git a/tests/travis/php_extensions_5.5.ini b/tests/travis/php_extensions_5.5.ini new file mode 100644 index 00000000..62d061f6 --- /dev/null +++ b/tests/travis/php_extensions_5.5.ini @@ -0,0 +1,6 @@ +extension="memcache.so" +extension="memcached.so" +extension="redis.so" + +apc.enabled=1 +apc.enable_cli=1 diff --git a/tests/travis/php_extensions_5.6.ini b/tests/travis/php_extensions_5.6.ini new file mode 100644 index 00000000..5a33e8a1 --- /dev/null +++ b/tests/travis/php_extensions_5.6.ini @@ -0,0 +1,7 @@ +extension="apcu.so" +extension="memcache.so" +extension="memcached.so" +extension="redis.so" + +apc.enabled=1 +apc.enable_cli=1 diff --git a/tests/travis/php_extensions_7.0.ini b/tests/travis/php_extensions_7.0.ini new file mode 100644 index 00000000..91f18930 --- /dev/null +++ b/tests/travis/php_extensions_7.0.ini @@ -0,0 +1,6 @@ +extension="apcu.so" +extension="memcache.so" +extension="memcached.so" + +apc.enabled=1 +apc.enable_cli=1 diff --git a/tests/travis/php_setup.sh b/tests/travis/php_setup.sh index a44e55af..547813b8 100755 --- a/tests/travis/php_setup.sh +++ b/tests/travis/php_setup.sh @@ -9,9 +9,20 @@ echo "**************************" echo "" echo "PHP Version: $TRAVIS_PHP_VERSION" -if [ "$TRAVIS_PHP_VERSION" = "hhvm" ] || [ "$TRAVIS_PHP_VERSION" = "hhvm-nightly" ] || [ "$TRAVIS_PHP_VERSION" = "7.0" ]; then +if [ "$TRAVIS_PHP_VERSION" = "hhvm" ] || [ "$TRAVIS_PHP_VERSION" = "hhvm-nightly" ]; then echo "Unable to install php extensions on current system" +elif [ "$TRAVIS_PHP_VERSION" = "7.0" ]; then + + echo "" + echo "******************************" + echo "Installing apcu extension" + echo "******************************" + set +e + printf "yes\n" | pecl install apcu + set -e + echo "Finished installing apcu-beta extension." + else echo "" @@ -42,12 +53,28 @@ else set -e echo "Finished installing uopz extension." - echo "" - echo "*********************" - echo "Updating php.ini file" - echo "*********************" - echo "" - echo "" - phpenv config-add tests/travis/php_extensions.ini -fi \ No newline at end of file + if [ "$TRAVIS_PHP_VERSION" != "5.4" ] + then + echo "" + echo "******************************" + echo "Installing apcu extension" + echo "******************************" + set +e + printf "yes\n" | pecl install apcu-4.0.8 + set -e + echo "Finished installing apcu-beta extension." + fi + +fi + +if [ -f "tests/travis/php_extensions_${TRAVIS_PHP_VERSION}.ini" ] +then + echo "" + echo "*********************" + echo "Updating php.ini file" + echo "*********************" + echo "" + echo "" + phpenv config-add "tests/travis/php_extensions_${TRAVIS_PHP_VERSION}.ini" +fi