diff --git a/.travis.yml b/.travis.yml index 809359f..be7ab8e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,6 +10,7 @@ addons: services: - mongodb + - memcached env: matrix: @@ -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: diff --git a/capsules/core/system/src/Annotations/Cache.php b/capsules/core/system/src/Annotations/Cache.php new file mode 100644 index 0000000..44f2645 --- /dev/null +++ b/capsules/core/system/src/Annotations/Cache.php @@ -0,0 +1,11 @@ +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; + } + +} diff --git a/capsules/core/system/src/EntityBase.php b/capsules/core/system/src/EntityBase.php index bf29f2d..c0cf987 100644 --- a/capsules/core/system/src/EntityBase.php +++ b/capsules/core/system/src/EntityBase.php @@ -3,6 +3,7 @@ namespace Nuntius\System; use Nuntius\Db\DbDispatcher; +use Nuntius\Nuntius; use Symfony\Component\Validator\Validation; /** @@ -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(); } @@ -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); + } + } diff --git a/capsules/core/system/src/Plugin/Cache/DBCache.php b/capsules/core/system/src/Plugin/Cache/DBCache.php new file mode 100644 index 0000000..b1c28d4 --- /dev/null +++ b/capsules/core/system/src/Plugin/Cache/DBCache.php @@ -0,0 +1,133 @@ +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; + } + +} diff --git a/capsules/core/system/src/Plugin/Cache/Memcache.php b/capsules/core/system/src/Plugin/Cache/Memcache.php new file mode 100644 index 0000000..a268f94 --- /dev/null +++ b/capsules/core/system/src/Plugin/Cache/Memcache.php @@ -0,0 +1,142 @@ +get('config')); + } + + /** + * Memcache constructor. + * + * @param NuntiusConfig $config + */ + public function __construct(NuntiusConfig $config) { + $this->config = $config; + + $connection = $config->getSetting('memcache'); + + $this->memcached = new \Memcached(); + $this->memcached->addServer($connection['host'], $connection['port']); + } + + /** + * {@inheritdoc} + */ + public function install() { + // Nothing to do here. + } + + /** + * {@inheritdoc} + */ + public function clear($cid = NULL) { + $this->memcached->delete($cid); + + if ($cid) { + if (is_array($cid)) { + $this->memcached->deleteMulti($cid); + } + else { + $this->memcached->delete($cid); + } + + return; + } + + $this->memcached->delete($cid); + } + + /** + * Getting the cache from the DB. + * + * @param $cid + * The cache id. + * + * @return mixed + */ + public function get($cid) { + $content = $this->getMultiple([$cid]); + + return reset($content); + } + + /** + * {@inheritdoc} + */ + public function getMultiple($cids) { + $result = $this->memcached->getMulti($cids); + + return array_map(function($item) { + return unserialize($item); + }, $result); + } + + /** + * {@inheritdoc} + */ + public function set($id, $content, $expires = NULL) { + + if (!$expires) { + $expires = time() + (86400 * 365 * 5); + } + + $old_content = $content; + $content = serialize($content); + + $this->memcached->set($id, $content, $expires); + + return $old_content; + } + + /** + * Checking that the memcache is ready to use. + * + * @return bool + * + * @throws \Exception + */ + public static function ready() { + if (!class_exists('Memcached')) { + return false; + } + + /** @var NuntiusConfig $config */ + $config = Nuntius::container()->get('config'); + + if (!@$connection = $config->getSetting('memcache')) { + return false; + } + + $m = new \Memcached(); + $m->addServer($connection['host'], $connection['port']); + + return $m->getStats(); + } +} diff --git a/capsules/core/system/src/System.php b/capsules/core/system/src/System.php index f56203d..bea2c3d 100644 --- a/capsules/core/system/src/System.php +++ b/capsules/core/system/src/System.php @@ -41,4 +41,13 @@ public static function getEntityManager() { return Nuntius::container()->get('entity.plugin_manager'); } + /** + * @return CachePluginManager + * + * @throws \Exception + */ + public static function getCacheManager() { + return Nuntius::container()->get('cache.plugin_manager'); + } + } diff --git a/capsules/core/system/system.services.yml b/capsules/core/system/system.services.yml index ec5c3c1..0dbbe12 100644 --- a/capsules/core/system/system.services.yml +++ b/capsules/core/system/system.services.yml @@ -18,8 +18,9 @@ services: entity.plugin_manager: class: \Nuntius\System\EntityPluginManager arguments: ['@plugin_manager'] - constraint: - class: \Nuntius\System\Constraints\NuntiusConstraint + cache.plugin_manager: + class: \Nuntius\System\CachePluginManager + arguments: ['@plugin_manager'] hooks: capsule_install: class: \Nuntius\System\Hooks\CapsuleInstall diff --git a/settings/credentials.yml b/settings/credentials.yml index 7ba8dbc..998829e 100644 --- a/settings/credentials.yml +++ b/settings/credentials.yml @@ -15,3 +15,8 @@ access_token: '' # Choose your DB drive. db_driver: '' + +# Set the memcache connections. +memcache: + host: localhost + port: 2020 diff --git a/src/NuntiusRethinkdb.php b/src/NuntiusRethinkdb.php index 4a9a1e0..6ccf9d4 100644 --- a/src/NuntiusRethinkdb.php +++ b/src/NuntiusRethinkdb.php @@ -35,13 +35,16 @@ class NuntiusRethinkdb { * The config service. */ function __construct(NuntiusConfig $config) { - $info = $config->getSetting('rethinkdb'); - $this->db = $info['db']; - try { - $this->connection = \r\connect($info['host'], $info['port'], $info['db'], $info['api_key'], $info['timeout']); - } catch (\Exception $e) { - $this->error = $e->getMessage(); - } + $this->confg = $config; + + $info = $config->getSetting('rethinkdb'); + $this->db = $info['db']; + + try { + $this->connection = \r\connect($info['host'], $info['port'], $info['db'], $info['api_key'], $info['timeout']); + } catch (\Exception $e) { + $this->error = $e->getMessage(); + } } /** diff --git a/tests/CachingTest.php b/tests/CachingTest.php new file mode 100644 index 0000000..36fbbbe --- /dev/null +++ b/tests/CachingTest.php @@ -0,0 +1,92 @@ + 'entity.plugin_manager', + 'capsuleService' => 'capsule_manager', + 'cacheManager' => 'cache.plugin_manager', + ]; + + protected $capsules = ['system']; + + /** + * @var EntityPluginManager + */ + protected $entityManager; + + /** + * @var CapsuleServiceInterface + */ + protected $capsuleService; + + /** + * @var CachePluginManager + */ + protected $cacheManager; + + /** + * @return System + * + * @throws \Nuntius\Capsule\CapsuleErrorException + */ + protected function createSystem() { + /** @var System $entity */ + $entity = $this->entityManager->createInstance('system'); + + $entity->name = 'Testing'; + $entity->machine_name = 'testing'; + $entity->description = 'testing entity'; + $entity->path = '.'; + $entity->status = 1; + + return $entity->save(); + } + + /** + * Testing the caching mechanisms. + */ + public function testCache() { +// $this->cacheTestHelper($this->cacheManager->createInstance('db')); + $this->cacheTestHelper($this->cacheManager->createInstance('memcache')); + } + + /** + * A method for testing how various caches handle the caching flow. + * + * @param CacheBase $cache + * The cache object. + * + * @throws \Nuntius\Capsule\CapsuleErrorException + */ + protected function cacheTestHelper(CacheBase $cache) { + $cid = 'testing_' . time(); + + $this->assertFalse($cache->get($cid)); + $cache->set($cid, 'foo', time() + 5); + + $this->assertEquals($cache->get($cid), 'foo'); + sleep(10); + $this->assertFalse($cache->get($cid), 'foo'); + + $system = $this->createSystem(); + + $system_cid = 'system_cache_' . time(); + $cache->set($system_cid, $system); + + $this->assertEquals($cache->get($system_cid), $system); + } + +} diff --git a/text.php b/text.php index e8f3aff..7983dd2 100644 --- a/text.php +++ b/text.php @@ -2,21 +2,15 @@ require_once 'autoloader.php'; -use Symfony\Component\Validator\Validation; -use Symfony\Component\Validator\Constraints\Length; -use Symfony\Component\Validator\Constraints\NotBlank; +/** @var \Nuntius\System\Plugin\Cache\Memcache $cache */ +//$cache = \Nuntius\System\System::getCacheManager()->createInstance('memcache'); +// +//d($cache->get('a')); -\Nuntius\Nuntius::getCapsuleManager()->enableCapsule('capsule_test_secondary'); +$foo = \Nuntius\System\System::getEntityManager()->createInstance('system')->load('0161dd3c-b8af-4d84-b1cf-114c5162363e'); -/** @var \Nuntius\System\Plugin\Entity\System $entity */ -$entity = \Nuntius\System\System::getEntityManager()->createInstance('system'); +$ser = serialize($foo); +$unser = unserialize($ser); -$entity->name = 'Testing'; -$entity->machine_name = 'testing'; -$entity->description = 'testing entity'; -$entity->path = '.'; -$entity->status = 1; - -$entity = $entity->save(); -$entity->delete($entity->id); +d($unser); \ No newline at end of file