Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding a cache manager #124

Open
wants to merge 14 commits into
base: 2.x
Choose a base branch
from
3 changes: 3 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ addons:

services:
- mongodb
- memcached

env:
matrix:
Expand All @@ -32,6 +33,8 @@ before_script:
# Set up stuff for the webhooks tests.
- nohup php -S localhost:8888 > /dev/null 2>&1 &

- memcached -p 2020 -d

- export NUNTIUS_BASE_URL=http://localhost:8888

script:
Expand Down
11 changes: 11 additions & 0 deletions capsules/core/system/src/Annotations/Cache.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

namespace Nuntius\System\Annotations;

/**
* @Annotation
* @Target("CLASS")
*/
class Cache extends NuntiusAnnotationBase implements NuntiusAnnotation {

}
82 changes: 82 additions & 0 deletions capsules/core/system/src/CacheBase.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<?php

namespace Nuntius\System;

/**
* Class CacheBase
*
* @package Nuntius\System
*/
abstract class CacheBase {

public $plugin_id;

/**
* Prepare the environment by the need of the plugin.
*
* Examples:
* * Install a record in the DB
* * Set up the connection for a remote DB.
*
* @return mixed
*/
abstract public function install();

/**
* Clear a cache by the cid.
*
* @param string $cid
* The cache ID.
*
* @return mixed
*/
abstract public function clear($cid = NULL);

/**
* Getting the cache from the DB.
*
* @param $cid
* The cache id.
*
* @return mixed
*/
abstract public function get($cid);

/**
* Get all the caches in the current cache bin.
*
* @param array $cids
* List of cache ID.
*
* @return mixed
*/
abstract public function getMultiple($cids);

/**
* Setting the cache.
*
* @param $id
* The cache ID.
* @param $content
* The content of the cache.s
* @param int $expires
* When the cache is not longer valid.
*
* @return mixed
*/
abstract public function set($id, $content, $expires = NULL);

/**
* Determine if the plugin is ready to use.
*
* The function is useful when the plugin depends on external library or a
* specific type of storage.
*
* @return bool
* By default return true.
*/
static public function ready() {
return true;
}

}
76 changes: 76 additions & 0 deletions capsules/core/system/src/CachePluginManager.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<?php

namespace Nuntius\System;

use Nuntius\Nuntius;

class CachePluginManager {

/**
* @var PluginManager
*/
protected $pluginManager;

/**
* EntityPluginManager constructor.
* @param PluginManager $plugin_manager
*/
public function __construct(PluginManager $plugin_manager) {
$this->pluginManager = $plugin_manager;
}

/**
* @return CacheBase[]
*
* @throws \Doctrine\Common\Annotations\AnnotationException
* @throws \Nuntius\Capsule\CapsuleErrorException
* @throws \ReflectionException
*/
public function getCacheList() {
/** @var CacheBase[] $caches */
$caches = $this->pluginManager->getPlugins('Plugin\Cache', new \Nuntius\System\Annotations\Cache());

return array_filter($caches, function($item) {
return call_user_func([$item['namespace'], 'ready']);
});
}

/**
* @param $id
*
* @return mixed
* @throws \Exception
* @throws \Nuntius\Capsule\CapsuleErrorException
*/
public function createInstance($id) {
$list = $this->getCacheList();

if (!in_array($id, array_keys($list))) {
throw new \Exception('The cache plugin ' . $id . ' does not exists or it is not ready to use.');
}

// todo: Move to trait or something.
$plugin_info = $list[$id];

// Check if the hook need the container or not.
if (method_exists($plugin_info['namespace'], 'getContainer')) {
$object = call_user_func(array($plugin_info['namespace'], 'getContainer'), Nuntius::container());
}
else {
$object = new $plugin_info['namespace'];
}

foreach ($plugin_info as $key => $value) {

if ($key == 'id') {
// We don't want to override the record ID.
$key = 'plugin_id';
}

$object->{$key} = $value;
}

return $object;
}

}
32 changes: 31 additions & 1 deletion capsules/core/system/src/EntityBase.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Nuntius\System;

use Nuntius\Db\DbDispatcher;
use Nuntius\Nuntius;
use Symfony\Component\Validator\Validation;

/**
Expand Down Expand Up @@ -57,7 +58,6 @@ public function __construct(DbDispatcher $db, HooksDispatcherInterface $hooks_di
$this->storage = $db->getStorage();
$this->hooksDispatcher = $hooks_dispatcher;
$this->entityPluginManager = $entity_plugin_manager;

$this->validator = Validation::createValidator();
}

Expand Down Expand Up @@ -494,4 +494,34 @@ protected function constraints() {
return [];
}

/**
* Keep only the properties we need to serialize.
*
* @return array
*/
public function __sleep() {
return array_merge($this->properties, [
'plugin_id',
'properties',
'relations',
'hooksDispatcher',
'validator',
'entityPluginManager',
'namespace',
'provided_by',
]);
}

/**
* Bring back the properties we removed while serializing.
*
* @throws \Exception
*/
public function __wakeup() {
$this->dbDispatcher = Nuntius::container()->get('db');
$this->storage = $this->dbDispatcher->getStorage();
// Setting the storage.
$this->storage->table($this->plugin_id);
}

}
133 changes: 133 additions & 0 deletions capsules/core/system/src/Plugin/Cache/DBCache.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
<?php

namespace Nuntius\System\Plugin\Cache;

use Nuntius\Db\DbDispatcher;
use Nuntius\System\CacheBase;
use Nuntius\System\Annotations\Cache as cache;
use Nuntius\System\HookContainerInterface;

/**
* @cache(
* id = "db",
* )
*/
class DBCache extends CacheBase implements HookContainerInterface {

/**
* The DB dispatcher service.
*
* @var DbDispatcher
*/
protected $dbDispatcher;

/**
* Using the container to use dependency injection to hooks.
*
* @param \Symfony\Component\DependencyInjection\ContainerBuilder $container
* @return mixed
*
* @throws \Exception
*/
static function getContainer(\Symfony\Component\DependencyInjection\ContainerBuilder $container) {
return new static($container->get('db'));
}

/**
* Constructor.
*
* @param DbDispatcher $db_dispatcher
* The DB dispatcher service.
*/
function __construct(DbDispatcher $db_dispatcher) {
$this->dbDispatcher = $db_dispatcher;
}

/**
* {@inheritdoc}
*/
public function install() {
if ($this->dbDispatcher->getOperations()->tableExists('cache')) {
return;
}

$this->dbDispatcher->getOperations()->tableCreate('cache');
}

/**
* {@inheritdoc}
*/
public function clear($cid = NULL) {
$storage = $this->dbDispatcher->getStorage()->table('cache');

if ($cid) {
if (is_array($cid)) {
$storage->deleteMultiple($cid);
}
else {
$storage->delete($cid);
}

return;
}

// Delete all.
$storage->deleteMultiple();
}

/**
* {@inheritdoc}
*/
public function get($cid) {
$caches = $this->getMultiple([$cid]);

return reset($caches);
}

/**
* {@inheritdoc}
*/
public function getMultiple($cids) {
$results = $this->dbDispatcher
->getQuery()
->table('cache')
->condition('id', $cids, 'IN')
->condition('expires', time(), '>=')
->execute();

$content = [];

foreach ($results as &$result) {
$content[] = unserialize($result['content']);
}

return $content;
}

/**
* {@inheritdoc}
*/
public function set($id, $content, $expires = NULL) {
if (!$expires) {
$expires = time() + (86400 * 365 * 5);
}

$old_content = $content;

$content = serialize($content);

$data = [
'id' => $id,
'content' => $content,
'expires' => $expires,
];

$this->dbDispatcher
->getStorage()
->table('cache')
->save($data);

return $old_content;
}

}
Loading