Refactor cache to remove dependency on container

This commit is contained in:
Timothy Warren 2016-08-01 13:02:26 -04:00
parent 893584696b
commit 6b9770698b
17 changed files with 171 additions and 60 deletions

View File

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project default="full-build" name="animeclient" basedir="."> <project default="full-build" name="animeclient" basedir=".">
<autoloader autoloaderpath="${project.basedir}/vendor/autoload.php" />
<!-- By default, we assume all tools to be on the $PATH --> <!-- By default, we assume all tools to be on the $PATH -->
<property name="pdepend" value="pdepend" /> <property name="pdepend" value="pdepend" />
<property name="phpcpd" value="phpcpd" /> <property name="phpcpd" value="phpcpd" />
@ -10,7 +11,7 @@
<property name="sonar" value="sonar-runner" /> <property name="sonar" value="sonar-runner" />
<target name="full-build" <target name="full-build"
depends="prepare,static-analysis,phpunit,phpdox,sonar" depends="prepare,static-analysis,phpunit,phpdox"
description="Performs static analysis, runs the tests, and generates project documentation" description="Performs static analysis, runs the tests, and generates project documentation"
/> />
<target name="quick-build" <target name="quick-build"

View File

@ -19,6 +19,8 @@ use GuzzleHttp\Psr7\ResponseInterface;
use GuzzleHttp\Exception\ClientException; use GuzzleHttp\Exception\ClientException;
use Aviat\Ion\Di\ContainerInterface; use Aviat\Ion\Di\ContainerInterface;
use Aviat\Ion\Cache\CacheInterface;
use Aviat\Ion\Model; use Aviat\Ion\Model;
use Aviat\AnimeClient\AnimeClient; use Aviat\AnimeClient\AnimeClient;

View File

@ -14,6 +14,7 @@
namespace Aviat\AnimeClient\Model; namespace Aviat\AnimeClient\Model;
use Aviat\Ion\Di\ContainerInterface; use Aviat\Ion\Di\ContainerInterface;
use Aviat\Ion\Model\DB;
/** /**
* Base model for anime and manga collections * Base model for anime and manga collections
@ -36,11 +37,10 @@ class Collection extends DB {
* Create a new collection object * Create a new collection object
* *
* @param ContainerInterface $container * @param ContainerInterface $container
* @return void
*/ */
public function __construct(ContainerInterface $container) public function __construct(ContainerInterface $container)
{ {
parent::__construct($container); parent::__construct($container->get('config'));
try try
{ {

View File

@ -162,7 +162,7 @@ class Manga extends API {
$logger->warning("Non 200 response for search api call"); $logger->warning("Non 200 response for search api call");
$logger->warning($response->getBody()); $logger->warning($response->getBody());
throw new RuntimeException($response->getEffectiveUrl()); throw new \RuntimeException($response->getEffectiveUrl());
} }
return Json::decode($response->getBody(), TRUE); return Json::decode($response->getBody(), TRUE);

View File

@ -18,7 +18,7 @@ namespace Aviat\Ion\Cache;
interface CacheInterface { 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 * from the passed arguments
* *
* @param object $object - object to retrieve fresh value from * @param object $object - object to retrieve fresh value from
@ -29,7 +29,7 @@ interface CacheInterface {
public function get($object, $method, array $args=[]); 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 object $object - object to retrieve fresh value from
* @param string $method - method name to call * @param string $method - method name to call

View File

@ -13,6 +13,7 @@
namespace Aviat\Ion\Cache; namespace Aviat\Ion\Cache;
use Aviat\Ion\ConfigInterface; use Aviat\Ion\ConfigInterface;
use Aviat\Ion\Cache\Driver\DriverInterface;
/** /**
* Class proxying cached and fresh values from the selected cache driver * Class proxying cached and fresh values from the selected cache driver
@ -20,7 +21,7 @@ use Aviat\Ion\ConfigInterface;
class CacheManager implements CacheInterface { class CacheManager implements CacheInterface {
/** /**
* @var CacheDriverInterface * @var DriverInterface
*/ */
protected $driver; protected $driver;

View File

@ -10,12 +10,12 @@
* @license MIT * @license MIT
*/ */
namespace Aviat\Ion\Cache; namespace Aviat\Ion\Cache\Driver;
/** /**
* Interface for cache drivers * Interface for cache drivers
*/ */
interface CacheDriverInterface { interface DriverInterface {
/** /**
* Retreive a value from the cache backend * Retreive a value from the cache backend
* *
@ -29,7 +29,7 @@ interface CacheDriverInterface {
* *
* @param string $key * @param string $key
* @param mixed $value * @param mixed $value
* @return CacheDriverInterface * @return DriverInterface
*/ */
public function set($key, $value); public function set($key, $value);
@ -37,7 +37,7 @@ interface CacheDriverInterface {
* Invalidate a cached value * Invalidate a cached value
* *
* @param string $key * @param string $key
* @return CacheDriverInterface * @return DriverInterface
*/ */
public function invalidate($key); public function invalidate($key);
@ -48,4 +48,4 @@ interface CacheDriverInterface {
*/ */
public function invalidateAll(); public function invalidateAll();
} }
// End of CacheDriverInterface.php // End of DriverInterface.php

View File

@ -0,0 +1,62 @@
<?php
/**
* Ion
*
* Building blocks for web development
*
* @package Ion
* @author Timothy J. Warren
* @copyright Copyright (c) 2015 - 2016
* @license MIT
*/
namespace Aviat\Ion\Cache\Driver;
use Aviat\Ion\Json;
use Aviat\Ion\JsonException;
/**
* Abstract base for Cache drivers to share common functionality
*/
trait DriverTrait {
/**
* Key prefix for key / value cache stores
*/
protected static $CACHE_KEY_PREFIX = "hbac:cache:";
/**
* Set key prefix for cache drivers that have global keys
*
* @param string $key - the raw key name
* @return string - the prefixed key name
*/
protected function prefix($key)
{
return static::$CACHE_KEY_PREFIX . $key;
}
/**
* Converts data to cache to a string representation for storage in a cache
*
* @param mixed $data - data to store in the cache backend
* @return string
*/
protected function serialize($data)
{
return Json::encode($data);
}
/**
* Convert serialized data from cache backend to native types
*
* @param string $data - data from cache backend
* @return mixed
* @throws JsonException
*/
protected function unserialize($data)
{
return Json::decode($data);
}
}
// End of DriverTrait.php

View File

@ -11,32 +11,20 @@
*/ */
namespace Aviat\Ion\Cache\Driver; namespace Aviat\Ion\Cache\Driver;
use Aviat\Ion\ConfigInterface; use Aviat\Ion\ConfigInterface;
use Aviat\Ion\Cache\CacheDriverInterface;
/** /**
* The Driver for no real cache * The Driver for no real cache
*/ */
class NullDriver implements CacheDriverInterface { class NullDriver implements DriverInterface {
/** /**
* 'Cache' for Null data store * 'Cache' for Null data store
*/ */
protected $data; protected $data = [];
/** /**
* Create the Null cache driver * Retrieve a value from the cache backend
*
* @param ConfigInterface $config The configuration management class
*/
public function __construct(ConfigInterface $config)
{
$this->data = [];
}
/**
* Retreive a value from the cache backend
* *
* @param string $key * @param string $key
* @return mixed * @return mixed
@ -53,7 +41,7 @@ class NullDriver implements CacheDriverInterface {
* *
* @param string $key * @param string $key
* @param mixed $value * @param mixed $value
* @return CacheDriverInterface * @return DriverInterface
*/ */
public function set($key, $value) public function set($key, $value)
{ {
@ -65,7 +53,7 @@ class NullDriver implements CacheDriverInterface {
* Invalidate a cached value * Invalidate a cached value
* *
* @param string $key * @param string $key
* @return CacheDriverInterface * @return DriverInterface
*/ */
public function invalidate($key) public function invalidate($key)
{ {

View File

@ -13,11 +13,15 @@
namespace Aviat\Ion\Cache\Driver; namespace Aviat\Ion\Cache\Driver;
use Aviat\Ion\ConfigInterface; use Aviat\Ion\ConfigInterface;
use Aviat\Ion\Cache\CacheDriverInterface;
use Predis\Client; use Predis\Client;
class RedisDriver implements CacheDriverInterface { /**
* Cache Driver for a Redis backend
*/
class RedisDriver implements DriverInterface {
use DriverTrait;
/** /**
* THe Predis library instance * THe Predis library instance
@ -35,6 +39,10 @@ class RedisDriver implements CacheDriverInterface {
{ {
$redisConfig = $config->get('redis'); $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'] === '') if (array_key_exists('password', $redisConfig) && $redisConfig['password'] === '')
{ {
unset($redisConfig['password']); unset($redisConfig['password']);
@ -54,36 +62,45 @@ class RedisDriver implements CacheDriverInterface {
/** /**
* Retrieve a value from the cache backend * Retrieve a value from the cache backend
* *
* @param string $key * @param string $rawKey
* @return mixed * @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 * Set a cached value
* *
* @param string $key * @param string $rawKey
* @param mixed $value * @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; return $this;
} }
/** /**
* Invalidate a cached value * Invalidate a cached value
* *
* @param string $key * @param string $rawKey
* @return CacheDriverInterface * @return DriverInterface
*/ */
public function invalidate($key) public function invalidate($rawKey)
{ {
$key = $this->prefix($rawKey);
$this->redis->del($key); $this->redis->del($key);
return $this; return $this;
} }
@ -94,7 +111,7 @@ class RedisDriver implements CacheDriverInterface {
*/ */
public function invalidateAll() public function invalidateAll()
{ {
$this->redis->flushDB(); $this->redis->flushdb();
} }
} }
// End of RedisDriver.php // End of RedisDriver.php

View File

@ -13,14 +13,15 @@
namespace Aviat\Ion\Cache\Driver; namespace Aviat\Ion\Cache\Driver;
use Aviat\Ion\ConfigInterface; use Aviat\Ion\ConfigInterface;
use Aviat\Ion\Cache\CacheDriverInterface;
use Aviat\Ion\Exception\ConfigException; use Aviat\Ion\Exception\ConfigException;
use Aviat\Ion\Model\DB; use Aviat\Ion\Model\DB;
/** /**
* Driver for caching via a traditional SQL database * 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 * The query builder object
@ -60,12 +61,14 @@ class SQLDriver extends DB implements CacheDriverInterface {
->get(); ->get();
$row = $query->fetch(\PDO::FETCH_ASSOC); $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,13 +76,15 @@ class SQLDriver extends DB implements CacheDriverInterface {
* *
* @param string $key * @param string $key
* @param mixed $value * @param mixed $value
* @return CacheDriverInterface * @return DriverInterface
*/ */
public function set($key, $value) public function set($key, $value)
{ {
$serializedData = $this->serialize($value);
$this->db->set([ $this->db->set([
'key' => $key, 'key' => $key,
'value' => json_encode($value), 'value' => $serializedData,
]); ]);
$this->db->insert('cache'); $this->db->insert('cache');
@ -91,7 +96,7 @@ class SQLDriver extends DB implements CacheDriverInterface {
* Invalidate a cached value * Invalidate a cached value
* *
* @param string $key * @param string $key
* @return CacheDriverInterface * @return DriverInterface
*/ */
public function invalidate($key) public function invalidate($key)
{ {

View File

@ -60,7 +60,15 @@ class Json {
*/ */
public static function decode($json, $assoc = TRUE, $depth = 512, $options = 0) 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(); self::check_json_error();
return $data; return $data;
} }

View File

@ -31,7 +31,7 @@ class AnimeCollectionModelTest extends AnimeClient_TestCase {
public function testSanity() public function testSanity()
{ {
$friend = new Friend($this->collectionModel); $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); $this->assertInstanceOf('Aviat\AnimeClient\Model\Anime', $friend->anime_model);
} }

View File

@ -8,6 +8,7 @@ use GuzzleHttp\Psr7\Response;
use Zend\Diactoros\ServerRequestFactory; use Zend\Diactoros\ServerRequestFactory;
use Zend\Diactoros\Response as HttpResponse; use Zend\Diactoros\Response as HttpResponse;
use Aviat\Ion\Json;
use Aviat\AnimeClient\AnimeClient; use Aviat\AnimeClient\AnimeClient;
define('ROOT_DIR', __DIR__ . '/../'); define('ROOT_DIR', __DIR__ . '/../');
@ -119,6 +120,36 @@ class AnimeClient_TestCase extends PHPUnit_Framework_TestCase {
$this->container->set('response', new HttpResponse()); $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 * Create a mock guzzle client for testing
* api call methods * api call methods

View File

@ -1,12 +1,12 @@
<?php <?php
use Aviat\AnimeClient\Model\DB as BaseDBModel; use Aviat\Ion\Model\DB as BaseDBModel;
class BaseDBModelTest extends AnimeClient_TestCase { class BaseDBModelTest extends AnimeClient_TestCase {
public function testBaseDBModelSanity() public function testBaseDBModelSanity()
{ {
$baseDBModel = new BaseDBModel($this->container); $baseDBModel = new BaseDBModel($this->container->get('config'));
$this->assertTrue(is_object($baseDBModel)); $this->assertTrue(is_object($baseDBModel));
} }
} }

View File

@ -3,9 +3,6 @@
* Global setup for unit tests * Global setup for unit tests
*/ */
use Aviat\Ion\Json;
use Aviat\AnimeClient\AnimeClient;
// Work around the silly timezone error // Work around the silly timezone error
$timezone = ini_get('date.timezone'); $timezone = ini_get('date.timezone');
if ($timezone === '' || $timezone === FALSE) if ($timezone === '' || $timezone === FALSE)

View File

@ -6,7 +6,6 @@
use Aviat\Ion\Enum; use Aviat\Ion\Enum;
use Aviat\Ion\Friend; use Aviat\Ion\Friend;
use Aviat\Ion\Json; use Aviat\Ion\Json;
use Aviat\Ion\Di\ContainerInterface;
use Aviat\Ion\Transformer\AbstractTransformer; use Aviat\Ion\Transformer\AbstractTransformer;
use Aviat\Ion\View; use Aviat\Ion\View;
use Aviat\Ion\View\HtmlView; use Aviat\Ion\View\HtmlView;