Skip to content

Commit

Permalink
cache fix, add docblocks, add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
marcogiovinazzi committed Apr 5, 2019
1 parent b5ceee4 commit f69b090
Show file tree
Hide file tree
Showing 5 changed files with 251 additions and 18 deletions.
6 changes: 6 additions & 0 deletions src/Comodojo/Dispatcher/Cache/AbstractCache.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
use \Comodojo\Dispatcher\Traits\CacheTrait;

/**
* Abstract internal cache handler
*
* @package Comodojo Dispatcher
* @author Marco Giovinazzi <marco.giovinazzi@comodojo.org>
* @author Marco Castiello <marco.castiello@gmail.com>
Expand All @@ -24,6 +26,10 @@ abstract class AbstractCache {

use CacheTrait;

/**
* @const int DEFAULTTTL
* Default cache TTL, in seconds (24h)
*/
const DEFAULTTTL = 86400;

public function __construct(CacheManager $cache) {
Expand Down
31 changes: 29 additions & 2 deletions src/Comodojo/Dispatcher/Cache/RouterCache.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
<?php namespace Comodojo\Dispatcher\Cache;

/**
* Router cache handler
*
* The dispatcher router uses the cache layer to store a "compiled" version of
* the routing table, to speedup the parsing of configuration files at startup.
*
* @package Comodojo Dispatcher
* @author Marco Giovinazzi <marco.giovinazzi@comodojo.org>
* @author Marco Castiello <marco.castiello@gmail.com>
Expand All @@ -19,17 +24,39 @@

class RouterCache extends AbstractCache {

/**
* @const string CACHE_NAMESPACE
* Namespace for routing table cache
*/
const CACHE_NAMESPACE = "DISPATCHERINTERNALS";

/**
* @const string CACHE_NAME
* Name for routing table cache
*/
const CACHE_NAME = "dispatcher-routes";

/**
* Read the routing table from cache (if any)
*
* @return array
*/
public function read() {

return $this->getCache()->setNamespace(self::CACHE_NAMESPACE)->get(self::CACHE_NAME);
return $this->getCache()
->setNamespace(self::CACHE_NAMESPACE)
->get(self::CACHE_NAME);

}

public function dump($data, $ttl = null) {
/**
* Store the routing table in cache
*
* @return array $data
* @return int $ttl
* @return bool
*/
public function dump($data, $ttl=null) {

return $this->getCache()
->setNamespace(self::CACHE_NAMESPACE)
Expand Down
92 changes: 76 additions & 16 deletions src/Comodojo/Dispatcher/Cache/ServerCache.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@
use \Comodojo\Dispatcher\Response\Model as Response;

/**
* Server-cache handler
*
* This class handles the process to read/write response content in the cache
*
* @NOTE: Server cache will not consider cacheable POST or PUT requests
* because of dispatcher internal structure: if post request is cached
* subsequent requests will never reach the service.
*
* @package Comodojo Dispatcher
* @author Marco Giovinazzi <marco.giovinazzi@comodojo.org>
* @author Marco Castiello <marco.castiello@gmail.com>
Expand All @@ -23,75 +31,127 @@

class ServerCache extends AbstractCache {

// @NOTE: Server cache will not consider cacheable POST or PUT requests
// because of dispatcher internal structure: if post request is cached
// subsequent requests will never reach the service.
/**
* @var array $cachable_methods
* Methods that allow cache usage
*/
protected static $cachable_methods = ['GET', 'HEAD'];

/**
* @var array $cachable_statuses
* List of HTTP return codes that allow cache usage
*/
protected static $cachable_statuses = [200, 203, 300, 301, 302, 404, 410];

/**
* @var string $cache_namespace
* Server-cache namespace
*/
protected static $cache_namespace = "DISPATCHERSERVICES";

/**
* @var bool $bypass
* In case true, cache will be ignored (cache bypass)
*/
protected $bypass = false;

/**
* Read the cached response starting from the request definition (if any)
*
* @param Request $request
* The request model
* @param Response $response
* The response object that will be hydrated in case of success
* @return bool
*/
public function read(
Request $request,
Response $response
) {

if ( $this->bypass === true ) return false;
if ($this->bypass === true) {
return false;
}

$name = self::getCacheName($request);
$cache_object = $this->getCache()
->setNamespace(self::$cache_namespace)
->get($name);

$cache_object = $this->getCache()->setNamespace(self::$cache_namespace)->get($name);

if (is_null($cache_object)) return false;
if (is_null($cache_object)) {
return false;
}

$response->import($cache_object);

return true;

}

/**
* Dump the full request object in the cache
*
* @param Request $request
* The request model (to extract cache definition)
* @param Response $response
* The response object that will be cached
* @param Route $route
* The route model (to extract cache parameters)
* @return bool
*/
public function dump(
Request $request,
Response $response,
Route $route
) {

if ( $this->bypass === true ) return;
if ( $this->bypass === true ) {
return false;
}

$cache = strtoupper($route->getParameter('cache'));

$ttl = $route->getParameter('ttl');

$name = self::getCacheName($request);

$method = (string)$request->getMethod();

$status = $response->getStatus()->get();

if (
($cache == 'SERVER' || $cache == 'BOTH') &&
in_array($method, self::$cachable_methods) &&
in_array($status, self::$cachable_statuses)
) {

$this->getCache()
->setNamespace(self::$cache_namespace)
->set($name, $response->export(), $ttl === null ? self::DEFAULTTTL : intval($ttl));

->set(
$name,
$response->export(),
$ttl === null ? self::DEFAULTTTL : intval($ttl)
);
return true;
}

return false;

}

/**
* Setup the cache bypass mode
*
* @return AbstractCache
*/
public function bypassCache() {

$this->bypass = true;
return $this;

}

/**
* Extract and compute the cache object name
*
* @param Request $request
* The request model
* @return string
*/
private static function getCacheName(Request $request) {

return md5((string)$request->getMethod().(string)$request->getUri());
Expand Down
37 changes: 37 additions & 0 deletions tests/Dispatcher/Cache/RouterCacheTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php namespace Comodojo\Dispatcher\Tests\Cache;

use \Comodojo\SimpleCache\Manager as CacheManager;
use \Comodojo\Foundation\Logging\Manager as LogManager;
use \Comodojo\SimpleCache\Providers\Memory;
use \Comodojo\Dispatcher\Cache\RouterCache;

class RouterCacheTest extends \PHPUnit_Framework_TestCase {

protected $routes = [
"^\/helloworld(?:\/(?P<to>[a-zA-Z0-9_\s!]+)?)?[\/]?$" => []
];

protected static $cache;

public static function setUpBeforeClass() {

$logger = LogManager::create('dispatcher', false)->getLogger();
$manager = new CacheManager(CacheManager::PICK_FIRST, $logger, true, 5);
$manager->addProvider(new Memory([], $logger));
self::$cache = new RouterCache($manager);

}

public function testCacheDump() {

$this->assertTrue(self::$cache->dump($this->routes, 10));

}

public function testCacheRead() {

$this->assertEquals($this->routes, self::$cache->read());

}

}
103 changes: 103 additions & 0 deletions tests/Dispatcher/Cache/ServerCacheTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
<?php namespace Comodojo\Dispatcher\Tests\Cache;

use \Comodojo\Foundation\Base\Configuration;
use \Comodojo\Dispatcher\Components\DefaultConfiguration;
use \Comodojo\SimpleCache\Manager as CacheManager;
use \Comodojo\Foundation\Logging\Manager as LogManager;
use \Comodojo\SimpleCache\Providers\Memory;
use \Comodojo\Dispatcher\Cache\ServerCache;
use \Comodojo\Dispatcher\Request\Model as Request;
use \Comodojo\Dispatcher\Router\Route;
use \Comodojo\Dispatcher\Response\Model as Response;

class ServerCacheTest extends \PHPUnit_Framework_TestCase {

protected $configuration;

protected $logger;

protected $cache;

protected $request;

protected $response;

protected $route;

public function setUp() {

$this->configuration = new Configuration( DefaultConfiguration::get() );
$this->logger = LogManager::create('dispatcher', false)->getLogger();
$manager = new CacheManager(CacheManager::PICK_FIRST, $this->logger, true, 5);
$manager->addProvider(new Memory([], $this->logger));
$this->cache = new ServerCache($manager);
$this->request = new Request($this->configuration, $this->logger);
$this->response = new Response($this->configuration, $this->logger);
$this->route = new Route;

$this->route->setParameters([
"cache" => "SERVER",
"ttl" => 100
]);

}

public function testValidCache() {

$this->request->getMethod()->set('GET');
$this->response->getContent()->set('this is a sample content');
$this->assertTrue(
$this->cache->dump(
$this->request,
$this->response,
$this->route
)
);

$response = new Response($this->configuration, $this->logger);
$cache = $this->cache->read(
$this->request,
$response
);
$this->assertEquals(
$this->response->export(),
$response->export()
);

}

public function testInvalidCache() {

$this->response->getContent()->set('this is a sample content');

$this->request->getMethod()->set('POST');
$this->assertFalse(
$this->cache->dump(
$this->request,
$this->response,
$this->route
)
);

$this->request->getMethod()->set('GET');
$route = new Route;
$this->assertFalse(
$this->cache->dump(
$this->request,
$this->response,
$route
)
);

$this->response->getStatus()->set(307);
$this->assertFalse(
$this->cache->dump(
$this->request,
$this->response,
$this->route
)
);

}

}

0 comments on commit f69b090

Please sign in to comment.