From 6b9770698bc33849345533b215b1b60c6fe8dc10 Mon Sep 17 00:00:00 2001 From: "Timothy J. Warren" Date: Mon, 1 Aug 2016 13:02:26 -0400 Subject: [PATCH] Refactor cache to remove dependency on container --- build.xml | 3 +- src/Aviat/AnimeClient/Model/API.php | 2 + src/Aviat/AnimeClient/Model/Collection.php | 4 +- src/Aviat/AnimeClient/Model/Manga.php | 2 +- src/Aviat/Ion/Cache/CacheInterface.php | 4 +- src/Aviat/Ion/Cache/CacheManager.php | 3 +- .../DriverInterface.php} | 10 +-- src/Aviat/Ion/Cache/Driver/DriverTrait.php | 62 +++++++++++++++++++ src/Aviat/Ion/Cache/Driver/NullDriver.php | 22 ++----- src/Aviat/Ion/Cache/Driver/RedisDriver.php | 43 +++++++++---- src/Aviat/Ion/Cache/Driver/SQLDriver.php | 25 +++++--- src/Aviat/Ion/Json.php | 10 ++- .../Model/AnimeCollectionModelTest.php | 2 +- tests/AnimeClient_TestCase.php | 31 ++++++++++ .../Model/BaseDBModelTest.php | 4 +- tests/bootstrap.php | 3 - tests/mocks.php | 1 - 17 files changed, 171 insertions(+), 60 deletions(-) rename src/Aviat/Ion/Cache/{CacheDriverInterface.php => Driver/DriverInterface.php} (80%) create mode 100644 src/Aviat/Ion/Cache/Driver/DriverTrait.php rename tests/{AnimeClient => Ion}/Model/BaseDBModelTest.php (59%) diff --git a/build.xml b/build.xml index b8554ad5..35dbaf72 100644 --- a/build.xml +++ b/build.xml @@ -1,5 +1,6 @@ + @@ -10,7 +11,7 @@ get('config')); try { diff --git a/src/Aviat/AnimeClient/Model/Manga.php b/src/Aviat/AnimeClient/Model/Manga.php index 999578af..8cb51f26 100644 --- a/src/Aviat/AnimeClient/Model/Manga.php +++ b/src/Aviat/AnimeClient/Model/Manga.php @@ -162,7 +162,7 @@ class Manga extends API { $logger->warning("Non 200 response for search api call"); $logger->warning($response->getBody()); - throw new RuntimeException($response->getEffectiveUrl()); + throw new \RuntimeException($response->getEffectiveUrl()); } return Json::decode($response->getBody(), TRUE); diff --git a/src/Aviat/Ion/Cache/CacheInterface.php b/src/Aviat/Ion/Cache/CacheInterface.php index 94957384..4a4413bf 100644 --- a/src/Aviat/Ion/Cache/CacheInterface.php +++ b/src/Aviat/Ion/Cache/CacheInterface.php @@ -18,7 +18,7 @@ namespace Aviat\Ion\Cache; interface CacheInterface { /** - * Retreive a cached value if it exists, otherwise, get the value + * Retrieve a cached value if it exists, otherwise, get the value * from the passed arguments * * @param object $object - object to retrieve fresh value from @@ -29,7 +29,7 @@ interface CacheInterface { public function get($object, $method, array $args=[]); /** - * Retreive a fresh value, and update the cache + * Retrieve a fresh value, and update the cache * * @param object $object - object to retrieve fresh value from * @param string $method - method name to call diff --git a/src/Aviat/Ion/Cache/CacheManager.php b/src/Aviat/Ion/Cache/CacheManager.php index 34fc4887..d03947a2 100644 --- a/src/Aviat/Ion/Cache/CacheManager.php +++ b/src/Aviat/Ion/Cache/CacheManager.php @@ -13,6 +13,7 @@ namespace Aviat\Ion\Cache; use Aviat\Ion\ConfigInterface; +use Aviat\Ion\Cache\Driver\DriverInterface; /** * Class proxying cached and fresh values from the selected cache driver @@ -20,7 +21,7 @@ use Aviat\Ion\ConfigInterface; class CacheManager implements CacheInterface { /** - * @var CacheDriverInterface + * @var DriverInterface */ protected $driver; diff --git a/src/Aviat/Ion/Cache/CacheDriverInterface.php b/src/Aviat/Ion/Cache/Driver/DriverInterface.php similarity index 80% rename from src/Aviat/Ion/Cache/CacheDriverInterface.php rename to src/Aviat/Ion/Cache/Driver/DriverInterface.php index 9813f9bc..2f826c21 100644 --- a/src/Aviat/Ion/Cache/CacheDriverInterface.php +++ b/src/Aviat/Ion/Cache/Driver/DriverInterface.php @@ -10,12 +10,12 @@ * @license MIT */ -namespace Aviat\Ion\Cache; +namespace Aviat\Ion\Cache\Driver; /** * Interface for cache drivers */ -interface CacheDriverInterface { +interface DriverInterface { /** * Retreive a value from the cache backend * @@ -29,7 +29,7 @@ interface CacheDriverInterface { * * @param string $key * @param mixed $value - * @return CacheDriverInterface + * @return DriverInterface */ public function set($key, $value); @@ -37,7 +37,7 @@ interface CacheDriverInterface { * Invalidate a cached value * * @param string $key - * @return CacheDriverInterface + * @return DriverInterface */ public function invalidate($key); @@ -48,4 +48,4 @@ interface CacheDriverInterface { */ public function invalidateAll(); } -// End of CacheDriverInterface.php \ No newline at end of file +// End of DriverInterface.php \ No newline at end of file diff --git a/src/Aviat/Ion/Cache/Driver/DriverTrait.php b/src/Aviat/Ion/Cache/Driver/DriverTrait.php new file mode 100644 index 00000000..57b6b750 --- /dev/null +++ b/src/Aviat/Ion/Cache/Driver/DriverTrait.php @@ -0,0 +1,62 @@ +data = []; - } - - /** - * Retreive a value from the cache backend + * Retrieve a value from the cache backend * * @param string $key * @return mixed @@ -53,7 +41,7 @@ class NullDriver implements CacheDriverInterface { * * @param string $key * @param mixed $value - * @return CacheDriverInterface + * @return DriverInterface */ public function set($key, $value) { @@ -65,7 +53,7 @@ class NullDriver implements CacheDriverInterface { * Invalidate a cached value * * @param string $key - * @return CacheDriverInterface + * @return DriverInterface */ public function invalidate($key) { diff --git a/src/Aviat/Ion/Cache/Driver/RedisDriver.php b/src/Aviat/Ion/Cache/Driver/RedisDriver.php index 85e140d5..dc385aa3 100644 --- a/src/Aviat/Ion/Cache/Driver/RedisDriver.php +++ b/src/Aviat/Ion/Cache/Driver/RedisDriver.php @@ -13,11 +13,15 @@ namespace Aviat\Ion\Cache\Driver; use Aviat\Ion\ConfigInterface; -use Aviat\Ion\Cache\CacheDriverInterface; use Predis\Client; -class RedisDriver implements CacheDriverInterface { +/** + * Cache Driver for a Redis backend + */ +class RedisDriver implements DriverInterface { + + use DriverTrait; /** * THe Predis library instance @@ -35,6 +39,10 @@ class RedisDriver implements CacheDriverInterface { { $redisConfig = $config->get('redis'); + // If you don't have a redis password set, and you attempt to send an + // empty string, Redis will think you want to authenticate with a password + // that is an empty string. To work around this, empty string passwords + // are considered to be a lack of a password if (array_key_exists('password', $redisConfig) && $redisConfig['password'] === '') { unset($redisConfig['password']); @@ -54,36 +62,45 @@ class RedisDriver implements CacheDriverInterface { /** * Retrieve a value from the cache backend * - * @param string $key + * @param string $rawKey * @return mixed */ - public function get($key) + public function get($rawKey) { - return json_decode($this->redis->get($key)); + $key = $this->prefix($rawKey); + $serializedData = $this->redis->get($key); + + return $this->unserialize($serializedData); } /** * Set a cached value * - * @param string $key + * @param string $rawKey * @param mixed $value - * @return CacheDriverInterface + * @return DriverInterface */ - public function set($key, $value) + public function set($rawKey, $value) { - $this->redis->set($key, json_encode($value)); + $key = $this->prefix($rawKey); + $serializedData = $this->serialize($value); + + $this->redis->set($key, $serializedData); + return $this; } /** * Invalidate a cached value * - * @param string $key - * @return CacheDriverInterface + * @param string $rawKey + * @return DriverInterface */ - public function invalidate($key) + public function invalidate($rawKey) { + $key = $this->prefix($rawKey); $this->redis->del($key); + return $this; } @@ -94,7 +111,7 @@ class RedisDriver implements CacheDriverInterface { */ public function invalidateAll() { - $this->redis->flushDB(); + $this->redis->flushdb(); } } // End of RedisDriver.php \ No newline at end of file diff --git a/src/Aviat/Ion/Cache/Driver/SQLDriver.php b/src/Aviat/Ion/Cache/Driver/SQLDriver.php index f3bbc64d..703128ec 100644 --- a/src/Aviat/Ion/Cache/Driver/SQLDriver.php +++ b/src/Aviat/Ion/Cache/Driver/SQLDriver.php @@ -13,14 +13,15 @@ namespace Aviat\Ion\Cache\Driver; use Aviat\Ion\ConfigInterface; -use Aviat\Ion\Cache\CacheDriverInterface; use Aviat\Ion\Exception\ConfigException; use Aviat\Ion\Model\DB; /** * Driver for caching via a traditional SQL database */ -class SQLDriver extends DB implements CacheDriverInterface { +class SQLDriver extends DB implements DriverInterface { + + use DriverTrait; /** * The query builder object @@ -60,12 +61,14 @@ class SQLDriver extends DB implements CacheDriverInterface { ->get(); $row = $query->fetch(\PDO::FETCH_ASSOC); - if ( ! empty($row)) + + if (empty($row)) { - return json_decode($row['value']); + return NULL; } - - return NULL; + + $serializedData = $row['value']; + return $this->unserialize($serializedData); } /** @@ -73,15 +76,17 @@ class SQLDriver extends DB implements CacheDriverInterface { * * @param string $key * @param mixed $value - * @return CacheDriverInterface + * @return DriverInterface */ public function set($key, $value) { + $serializedData = $this->serialize($value); + $this->db->set([ 'key' => $key, - 'value' => json_encode($value), + 'value' => $serializedData, ]); - + $this->db->insert('cache'); return $this; @@ -91,7 +96,7 @@ class SQLDriver extends DB implements CacheDriverInterface { * Invalidate a cached value * * @param string $key - * @return CacheDriverInterface + * @return DriverInterface */ public function invalidate($key) { diff --git a/src/Aviat/Ion/Json.php b/src/Aviat/Ion/Json.php index 42370b19..b1581ecc 100644 --- a/src/Aviat/Ion/Json.php +++ b/src/Aviat/Ion/Json.php @@ -60,7 +60,15 @@ class Json { */ public static function decode($json, $assoc = TRUE, $depth = 512, $options = 0) { - $data = json_decode($json, $assoc, $depth, $options); + // Don't try to decode null + if (empty($json)) + { + return NULL; + } + + // cast json to string so that streams from guzzle are correctly decoded + $data = json_decode((string) $json, $assoc, $depth, $options); + self::check_json_error(); return $data; } diff --git a/tests/AnimeClient/Model/AnimeCollectionModelTest.php b/tests/AnimeClient/Model/AnimeCollectionModelTest.php index 1004b75d..5c931c15 100644 --- a/tests/AnimeClient/Model/AnimeCollectionModelTest.php +++ b/tests/AnimeClient/Model/AnimeCollectionModelTest.php @@ -31,7 +31,7 @@ class AnimeCollectionModelTest extends AnimeClient_TestCase { public function testSanity() { $friend = new Friend($this->collectionModel); - $this->assertInstanceOf('Aviat\AnimeClient\Model\DB', $this->collectionModel); + $this->assertInstanceOf('Aviat\Ion\Model\DB', $this->collectionModel); $this->assertInstanceOf('Aviat\AnimeClient\Model\Anime', $friend->anime_model); } diff --git a/tests/AnimeClient_TestCase.php b/tests/AnimeClient_TestCase.php index d778f14b..6b32e3ba 100644 --- a/tests/AnimeClient_TestCase.php +++ b/tests/AnimeClient_TestCase.php @@ -8,6 +8,7 @@ use GuzzleHttp\Psr7\Response; use Zend\Diactoros\ServerRequestFactory; use Zend\Diactoros\Response as HttpResponse; +use Aviat\Ion\Json; use Aviat\AnimeClient\AnimeClient; define('ROOT_DIR', __DIR__ . '/../'); @@ -119,6 +120,36 @@ class AnimeClient_TestCase extends PHPUnit_Framework_TestCase { $this->container->set('response', new HttpResponse()); } + /** + * Simplify getting test data + * + * Takes multiple path arguments + * + * @return string - contents of the data file + */ + public function getMockFile() + { + $args = func_get_args(); + array_unshift($args, TEST_DATA_DIR); + $filePath = implode(DIRECTORY_SEPARATOR, $args); + + return file_get_contents($filePath); + } + + /** + * Simplify getting mocked test data + * + * Takes multiple path arguments + * + * @return mixed - the decoded data + */ + public function getMockFileData() + { + $rawData = call_user_func_array([$this, 'getMockFile'], func_get_args()); + + return Json::decode($rawData); + } + /** * Create a mock guzzle client for testing * api call methods diff --git a/tests/AnimeClient/Model/BaseDBModelTest.php b/tests/Ion/Model/BaseDBModelTest.php similarity index 59% rename from tests/AnimeClient/Model/BaseDBModelTest.php rename to tests/Ion/Model/BaseDBModelTest.php index dbf9a40c..bdaf1954 100644 --- a/tests/AnimeClient/Model/BaseDBModelTest.php +++ b/tests/Ion/Model/BaseDBModelTest.php @@ -1,12 +1,12 @@ container); + $baseDBModel = new BaseDBModel($this->container->get('config')); $this->assertTrue(is_object($baseDBModel)); } } \ No newline at end of file diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 9df960d1..27f1c52e 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -3,9 +3,6 @@ * Global setup for unit tests */ -use Aviat\Ion\Json; -use Aviat\AnimeClient\AnimeClient; - // Work around the silly timezone error $timezone = ini_get('date.timezone'); if ($timezone === '' || $timezone === FALSE) diff --git a/tests/mocks.php b/tests/mocks.php index d4884bbe..fca39551 100644 --- a/tests/mocks.php +++ b/tests/mocks.php @@ -6,7 +6,6 @@ use Aviat\Ion\Enum; use Aviat\Ion\Friend; use Aviat\Ion\Json; -use Aviat\Ion\Di\ContainerInterface; use Aviat\Ion\Transformer\AbstractTransformer; use Aviat\Ion\View; use Aviat\Ion\View\HtmlView;