Merge pull request #92 from diolektor/change-cache

Refactoring Cache
This commit is contained in:
Yuriy Pikhtarev 2016-05-11 00:41:57 +03:00
commit b3f321ed8d
11 changed files with 598 additions and 29 deletions

View file

@ -46,6 +46,10 @@ $di->register(new \TorrentPier\ServiceProviders\LogServiceProvider(), [
'config.log.handlers' => $di->config->log->handlers
]);
$di->register(new \TorrentPier\ServiceProviders\CacheServiceProvider(), [
'config.services.cache' => $di->config->services->cache->toArray()
]);
$di->register(new \TorrentPier\ServiceProviders\DbServiceProvider, [
'config.db' => $di->config->db->toArray()
]);
@ -520,4 +524,4 @@ else if (defined('IN_TRACKER'))
dummy_exit(mt_rand(60, 2400));
}
}
}
}

View file

@ -42,7 +42,8 @@
"zendframework/zend-loader": "^2.5",
"zendframework/zend-mail": "^2.5",
"zendframework/zend-session": "^2.5",
"zendframework/zend-version": "^2.5"
"zendframework/zend-version": "^2.5",
"doctrine/cache": "^1.6"
},
"require-dev": {
"phpunit/phpunit": "^4.8"

View file

@ -19,6 +19,15 @@ $config = [
'tp_release_date' => '**/**/2016',
'tp_release_state' => 'ALPHA',
'services' => [
'cache' => [
'adapter' => \TorrentPier\Cache\FileAdapter::class,
'options' => [
'directory' => __DIR__ . '/../internal_data/cache',
],
],
],
// Database
'db' => [
'debug' => '{self.debug}',
@ -533,4 +542,4 @@ else
}
}
return $config;
return $config;

198
src/Cache/Adapter.php Normal file
View file

@ -0,0 +1,198 @@
<?php
namespace TorrentPier\Cache;
use Doctrine\Common\Cache\CacheProvider;
/**
* Class Adapter
* @package TorrentPier\Cache
*/
abstract class Adapter
{
const DEFAULT_NAMESPACE = 'default';
/**
* @var CacheProvider
*/
protected $provider;
/**
* Set options for setting cache provider.
*
* @param array $options
*/
public function setOptions(array $options = [])
{
foreach ($options as $key => $value) {
$method = 'set' . ucfirst($key);
if (!method_exists($this, $method)) {
throw new \InvalidArgumentException('Method ' . $method . ' is undefined.');
}
$this->{$method}($value);
}
}
/**
* Get object provider cache.
*
* @return CacheProvider
*/
abstract public function getProvider();
/**
* Get type cache.
*
* @return string
*/
abstract protected function getType();
/**
* Prepare key.
*
* @param string $key
* @return string
*/
protected function prepareKey($key)
{
if (empty($key) || substr($key, -2) === '::') {
throw new \InvalidArgumentException('The key is not defined.');
}
$result = explode('::', $key, 2);
if (empty($result[1])) {
$namespace = self::DEFAULT_NAMESPACE;
$id = $result[0];
} else {
list($namespace, $id) = $result;
}
$this->getProvider()->setNamespace($namespace);
return $id;
}
/**
* Tests if an entry exists in the cache.
*
* @param string $key
* @return bool
*/
public function has($key)
{
$id = $this->prepareKey($key);
return $this->getProvider()->contains($id);
}
/**
* Fetches an entry from the cache.
*
* @param string $key
* @param mixed|null $defaultValue
* @return mixed|null
*/
public function get($key, $defaultValue = null)
{
$id = $this->prepareKey($key);
$result = $this->getProvider()->fetch($id);
if ($result === false) {
return $defaultValue;
}
return $result;
}
/**
* Returns an associative array of values for keys is found in cache.
*
* @param array $keys
* @return array|\string[]
*/
public function gets(array $keys)
{
$keys = array_map([$this, 'prepareKey'], $keys);
return $this->getProvider()->fetchMultiple([$keys]);
}
/**
* Puts data into the cache.
*
* @param string $key
* @param mixed $data
* @param int $timeLeft
* @return bool
*/
public function set($key, $data, $timeLeft = 0)
{
$id = $this->prepareKey($key);
return $this->getProvider()->save($id, $data, $timeLeft);
}
/**
* Returns a boolean value indicating if the operation succeeded.
*
* @param array $keysAndValues
* @param int $timeLeft
* @return bool
*/
public function sets(array $keysAndValues, $timeLeft)
{
foreach ($keysAndValues as $key => $value) {
$id = $this->prepareKey($key);
$keysAndValues[$id] = $value;
unset($keysAndValues[$key]);
}
return $this->getProvider()->saveMultiple($keysAndValues, $timeLeft);
}
/**
* Deletes a cache entry.
*
* @param string $id
* @return bool
*/
public function delete($id)
{
$id = $this->prepareKey($id);
return $this->getProvider()->delete($id);
}
/**
* Deletes all cache entries in the current cache namespace.
*
* @param string $namespace
* @return bool
*/
public function deleteAll($namespace = '')
{
$this->getProvider()->setNamespace($namespace);
return $this->getProvider()->deleteAll();
}
/**
* Flushes all cache entries, globally.
*
* @return bool
*/
public function flush()
{
return $this->getProvider()->flushAll();
}
/**
* Retrieves cached information from the data store.
*
* @return array
*/
public function stats()
{
$stats = $this->getProvider()->getStats();
if (!$stats) {
$stats = [];
}
$stats['type'] = $this->getType();
return $stats;
}
}

77
src/Cache/FileAdapter.php Normal file
View file

@ -0,0 +1,77 @@
<?php
namespace TorrentPier\Cache;
use Doctrine\Common\Cache\PhpFileCache;
/**
* Class FileAdapter
* @package TorrentPier\Cache
*/
class FileAdapter extends Adapter
{
/**
* @var string The cache directory.
*/
protected $directory;
/**
* @var string The cache file extension.
*/
protected $extension = PhpFileCache::EXTENSION;
/**
* @var int The cache file umask.
*/
protected $umask = 0002;
/**
* {@inheritdoc}
*/
public function getProvider()
{
if (!$this->provider) {
$this->provider = new PhpFileCache($this->directory, $this->extension, $this->umask);
}
return $this->provider;
}
/**
* {@inheritdoc}
*/
protected function getType()
{
return 'Cache in php file';
}
/**
* Set directory path for cache.
*
* @param $directory
*/
protected function setDirectory($directory)
{
$this->directory = $directory;
}
/**
* Set extension file.
*
* @param $extension
*/
protected function setExtension($extension)
{
$this->extension = $extension;
}
/**
* Set umask file.
*
* @param $umask
*/
protected function setUmask($umask)
{
$this->umask = $umask;
}
}

103
src/Cache/MemoryAdapter.php Normal file
View file

@ -0,0 +1,103 @@
<?php
namespace TorrentPier\Cache;
use Doctrine\Common\Cache\MemcacheCache;
use Doctrine\Common\Cache\MemcachedCache;
/**
* Class MemoryAdapter
* @package TorrentPier\Cache
*/
class MemoryAdapter extends Adapter
{
/**
* @var bool
*/
private $isMemcached = false;
/**
* @var array Setting servers.
*/
private $servers = [];
/**
* MemoryAdapter constructor.
*/
public function __construct()
{
$this->isMemcached = extension_loaded('memcached');
}
/**
* {@inheritdoc}
*/
public function getProvider()
{
if (!$this->provider) {
if ($this->isMemcached) {
$this->provider = new MemcachedCache();
$this->provider->setMemcached($this->getMemcached());
} else {
$this->provider = new MemcacheCache();
$this->provider->setMemcache($this->getMemcache());
}
}
return $this->provider;
}
/**
* @param array $servers
*/
protected function setServers(array $servers = [])
{
$this->servers = $servers;
}
/**
* Get memcached api.
*
* @return \Memcached
*/
private function getMemcached()
{
$mem = new \Memcached();
foreach ($this->servers as $server) {
$mem->addServer(
isset($server['host']) ? $server['host'] : '127.0.0.1',
isset($server['port']) ? $server['port'] : 11211,
isset($server['weight']) ? $server['weight'] : 100
);
}
return $mem;
}
/**
* Get memcache api.
*
* @return \Memcache
*/
private function getMemcache()
{
$mem = new \Memcache();
foreach ($this->servers as $server) {
$mem->addserver(
isset($server['host']) ? $server['host'] : '127.0.0.1',
isset($server['port']) ? $server['port'] : 11211,
isset($server['persistent']) ? $server['persistent'] : true,
isset($server['weight']) ? $server['weight'] : 100,
isset($server['timeout']) ? $server['timeout'] : 1
);
}
return $mem;
}
/**
* {@inheritdoc}
*/
protected function getType()
{
return $this->isMemcached ? 'Cache in memcached' : 'Cache in memcache';
}
}

View file

@ -0,0 +1,28 @@
<?php
namespace TorrentPier\ServiceProviders;
use Pimple\Container;
use Pimple\ServiceProviderInterface;
use TorrentPier\Cache\Adapter;
/**
* Class CacheServiceProvider
* @package TorrentPier\ServiceProviders
*/
class CacheServiceProvider implements ServiceProviderInterface
{
/**
* {@inheritdoc}
*/
public function register(Container $container)
{
$container['cache'] = function (Container $container) {
$setting = $container['config.services.cache'];
/** @var Adapter $adapter */
$adapter = new $setting['adapter']();
$adapter->setOptions($setting['options']);
return $adapter;
};
}
}

View file

@ -6,13 +6,19 @@ use Pimple\Container;
use Pimple\ServiceProviderInterface;
use ReCaptcha\ReCaptcha;
/**
* Class CaptchaServiceProvider
* @package TorrentPier\ServiceProviders
*/
class CaptchaServiceProvider implements ServiceProviderInterface
{
/**
* {@inheritdoc}
*/
public function register(Container $container)
{
$container['captcha'] = function (Container $container) {
$captcha = new ReCaptcha($container['config.captcha.secret_key']);
return $captcha;
};
}

View file

@ -7,8 +7,20 @@ require_once __DIR__ . '/common.php';
/** @var \TorrentPier\Di $di */
$di = \TorrentPier\Di::getInstance();
/** @var \TorrentPier\Db\Adapter $db */
$db = $di->db;
/** @var \TorrentPier\Cache\Adapter $cache */
$cache = $di->cache;
//var_dump($cache);
//
$resultHas = $cache->has('key');
$resultGet = $cache->get('key');
$resultSet = $cache->set('key', [[1], [2]], 5);
$resultStats = $cache->stats();
var_dump($resultHas, $resultGet, $resultSet, $resultStats);
///** @var \TorrentPier\Db\Adapter $db */
//$db = $di->db;
///** @var \Symfony\Component\HttpFoundation\Request $request */
//$request = $di->request;
@ -39,26 +51,26 @@ $db = $di->db;
//$log = $di->log;
//$log->debug('test debug information');
\TorrentPier\Log::info('You will see the style guide');
$query = function(\Zend\Db\Sql\Select $select) {
$select->columns(['id' => 'user_id', 'name' => 'username']);
$select->join(['t' => BB_TOPICS], 't.topic_poster = u.user_id', ['title' => 'topic_title']);
// $select->where(['user_id > ?' => 0]);
};
$users = $db->select(['u' => BB_USERS], $query)->all();
$usersCount = $db->count(['u' => BB_USERS], $query);
$content = $di->view->make('styleguide', [
'name' => $di->request->get('name', 'Admin'),
'users' => $users,
'users_count' => $usersCount
]);
/** @var \Symfony\Component\HttpFoundation\Response $response */
$response = \Symfony\Component\HttpFoundation\Response::create();
$response->setContent($content);
$response->prepare($di->request);
$response->send();
//\TorrentPier\Log::info('You will see the style guide');
//
//$query = function(\Zend\Db\Sql\Select $select) {
// $select->columns(['id' => 'user_id', 'name' => 'username']);
// $select->join(['t' => BB_TOPICS], 't.topic_poster = u.user_id', ['title' => 'topic_title']);
//// $select->where(['user_id > ?' => 0]);
//};
//
//$users = $db->select(['u' => BB_USERS], $query)->all();
//$usersCount = $db->count(['u' => BB_USERS], $query);
//
//$content = $di->view->make('styleguide', [
// 'name' => $di->request->get('name', 'Admin'),
// 'users' => $users,
// 'users_count' => $usersCount
//]);
//
///** @var \Symfony\Component\HttpFoundation\Response $response */
//$response = \Symfony\Component\HttpFoundation\Response::create();
//$response->setContent($content);
//
//$response->prepare($di->request);
//$response->send();

View file

@ -0,0 +1,55 @@
<?php
namespace TorrentPier\Cache;
use Doctrine\Common\Cache\PhpFileCache;
/**
* Class FileAdapterTest
* @package TorrentPier\Cache
*/
class FileAdapterTest extends \PHPUnit_Framework_TestCase
{
/** @var Adapter */
private $adapter;
/**
* {@inheritdoc}
*/
public function setUp()
{
$this->adapter = new FileAdapter();
$this->adapter->setOptions([
'directory' => __DIR__ . '/../../../internal_data/cache',
'extension' => '.torrentpier.php',
'umask' => 0644,
]);
}
/**
* Check provider.
*/
public function testAdapterCache()
{
self::assertInstanceOf(PhpFileCache::class, $this->adapter->getProvider());
}
/**
* Test type cache adapter.
*/
public function testStatsType()
{
self::assertEquals('Cache in php file', $this->adapter->stats()['type']);
}
/**
* Check handler error for incorrect error.
*
* @expectedException \InvalidArgumentException
* @expectedExceptionMessage Method setIncorrectKey is undefined.
*/
public function testIncorrectSetting()
{
$this->adapter->setOptions(['incorrectKey' => 'value']);
}
}

View file

@ -0,0 +1,76 @@
<?php
namespace TorrentPier\Cache;
use Doctrine\Common\Cache\MemcacheCache;
use Doctrine\Common\Cache\MemcachedCache;
/**
* Class FileAdapterTest
* @package TorrentPier\Cache
*/
class MemoryAdapterTest extends \PHPUnit_Framework_TestCase
{
/** @var Adapter */
private $adapterMemcache;
/** @var Adapter */
private $adapterMemcached;
/**
* {@inheritdoc}
*/
public function setUp()
{
// memcache
$this->adapterMemcache = $this->getMockBuilder(MemoryAdapter::class)
->setMethods(['getMemcache'])
->disableOriginalConstructor()
->getMock();
$propMemcache = new \ReflectionProperty(MemoryAdapter::class, 'isMemcached');
$propMemcache->setAccessible(true);
$propMemcache->setValue($this->adapterMemcache, false);
$memcacheMock = $this->getMock('\Memcache', ['getStats']);
$memcacheMock->method('getStats')->willReturn([]);
$this->adapterMemcache
->method('getMemcache')
->willReturn($memcacheMock);
// memcached
$this->adapterMemcached = $this->getMockBuilder(MemoryAdapter::class)
->setMethods(['getMemcached'])
->disableOriginalConstructor()
->getMock();
$propMemcached = new \ReflectionProperty(MemoryAdapter::class, 'isMemcached');
$propMemcached->setAccessible(true);
$propMemcached->setValue($this->adapterMemcached, true);
$memcachedMock = $this->getMock('\Memcached', ['getStats', 'getServerList']);
$memcachedMock->method('getStats')->willReturn([]);
$memcachedMock->method('getServerList')->willReturn([['host' => '127.0.0.1', 'port' => 11211]]);
$this->adapterMemcached
->method('getMemcache')
->willReturn($memcachedMock);
}
/**
* Check provider for memcache.
*/
public function testAdapterCacheMemcache()
{
self::assertInstanceOf(MemcacheCache::class, $this->adapterMemcache->getProvider());
}
/**
* Check provider for memcached.
*/
public function testAdapterCacheMemcached()
{
self::assertInstanceOf(MemcachedCache::class, $this->adapterMemcached->getProvider());
}
}