├── .gitignore ├── .travis.yml ├── README.md ├── composer.json ├── phpunit.xml.dist ├── src ├── AequasiCacheBundle.php ├── Cache │ ├── LoggingCachePool.php │ └── Memcached.php ├── Command │ └── CacheFlushCommand.php ├── DataCollector │ └── CacheDataCollector.php ├── DependencyInjection │ ├── AequasiCacheExtension.php │ ├── Builder │ │ ├── BaseBuilder.php │ │ └── ServiceBuilder.php │ ├── Compiler │ │ ├── BaseCompilerPass.php │ │ ├── DataCollectorCompilerPass.php │ │ ├── DoctrineSupportCompilerPass.php │ │ ├── RouterCompilerPass.php │ │ └── SessionSupportCompilerPass.php │ └── Configuration.php ├── Resources │ ├── config │ │ ├── collector.yml │ │ └── services.yml │ └── views │ │ └── Collector │ │ └── cache.html.twig ├── Routing │ ├── Matcher │ │ └── CacheUrlMatcher.php │ └── Router.php └── Session │ └── SessionHandler.php └── tests ├── ContainerTest.php ├── DependencyInjection └── AequasiCacheExtensionTest.php ├── Fixtures ├── router.yml └── service.yml ├── TestCase.php └── bootstrap.php /.gitignore: -------------------------------------------------------------------------------- 1 | composer.lock 2 | vendor 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | php: 3 | - "7.0" 4 | - "5.6" 5 | - "5.5" 6 | 7 | env: 8 | - SYMFONY_VERSION=2.7.* 9 | - SYMFONY_VERSION=2.8.* 10 | - SYMFONY_VERSION=3.0.* 11 | 12 | matrix: 13 | fast_finish: true 14 | 15 | sudo: false 16 | 17 | before_script: 18 | - composer self-update 19 | - composer require symfony/symfony:${SYMFONY_VERSION} --no-update 20 | - composer install --dev --prefer-source --no-interaction 21 | 22 | script: 23 | - php vendor/bin/phpunit 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # This Bundle is Deprecated 2 | 3 | This bundle has been replaced by [PHP-Cache](http://www.php-cache.com). Check it out! 4 | 5 | Aequasi cache-bundle [![Build Status](https://travis-ci.org/aequasi/cache-bundle.png?branch=master)](https://travis-ci.org/aequasi/cache-bundle) 6 | ==================== 7 | 8 | #### Cache Bundle for Symfony 2 9 | 10 | Creates services in Symfony 2, for cache, that can also be used with doctrines three cache types (metadata, result, and query). It also provides functionality for session handler support, and Router support. 11 | 12 | The respective cache extensions will be required for your project. 13 | 14 | Redis uses the php redis extension. 15 | 16 | #### Requirements 17 | 18 | - PHP >= 5.6, < 7.1 19 | - Symfony >= 2.7, < 4.0 20 | - [Composer](http://getcomposer.org) 21 | 22 | #### To Install 23 | 24 | Run the following in your project root, assuming you have composer set up for your project 25 | ```sh 26 | composer.phar require aequasi/cache-bundle 27 | ``` 28 | 29 | Add the bundle to app/AppKernel.php 30 | 31 | ```php 32 | $bundles( 33 | ... 34 | new Aequasi\Bundle\CacheBundle\AequasiCacheBundle(), 35 | ... 36 | ); 37 | ``` 38 | 39 | Then add parameters (probably in config.yml) for your servers, and options 40 | 41 | ```yml 42 | aequasi_cache: 43 | instances: 44 | default: 45 | persistent: true # Boolean or persistent_id 46 | namespace: mc 47 | type: memcached 48 | hosts: 49 | - { host: localhost, port: 11211 } 50 | ``` 51 | 52 | To see all the config options, run `php app/console config:dump-reference aequasi_cache` to view the config settings 53 | 54 | 55 | #### Doctrine 56 | 57 | This bundle allows you to use its services for Doctrine's caching methods of metadata, result, and query. 58 | 59 | If you want doctrine to use this as the result and query cache, add this 60 | 61 | ```yml 62 | aequasi_cache: 63 | doctrine: 64 | enabled: true 65 | metadata: 66 | instance: default 67 | entity_managers: [ default ] # the name of your entity_manager connection 68 | document_managers: [ default ] # the name of your document_manager connection 69 | result: 70 | instance: default 71 | entity_managers: [ default, read ] # you may specify multiple entity_managers 72 | query: 73 | instance: default 74 | entity_managers: [ default ] 75 | ``` 76 | 77 | #### Session 78 | 79 | This bundle even allows you to store your session data in one of your cache clusters. To enable: 80 | 81 | ```yml 82 | aequasi_cache: 83 | session: 84 | enabled: true 85 | instance: default 86 | prefix: "session_" 87 | ttl: 7200 88 | ``` 89 | 90 | #### Router 91 | 92 | This bundle also provides router caching, to help speed that section up. To enable: 93 | 94 | ```yml 95 | aequasi_cache: 96 | router: 97 | enabled: true 98 | instance: default 99 | ``` 100 | 101 | If you change any of your routes, you will need to clear all of the route_* keys in your cache. 102 | 103 | 104 | #### To Use 105 | 106 | To use this with doctrine's entity manager, just make sure you have `useResultCache` and/or `useQueryCache` set to true. If you want to use the user cache, just grab the service out of the container like so: 107 | 108 | ```php 109 | // Change default to the name of your instance 110 | $cache = $container->get( 'aequasi_cache.instance.default' ); 111 | // Or 112 | $cache = $container->get( 'aequasi_cache.default' ); 113 | ``` 114 | 115 | Here is an example usage of the service: 116 | 117 | ```php 118 | $cache = $this->get( 'aequasi_cache.instance.default' ); 119 | $item = $cache->getItem('test'); 120 | if ($item->isHit()) { 121 | var_dump($item->get()); 122 | 123 | return; 124 | } 125 | 126 | $cache->saveItem('test', $em->find('AcmeDemoBundle:User', 1), 3600); 127 | ``` 128 | 129 | ### Need Help? 130 | 131 | Create an issue if you've found a bug, or ping me on twitter: @aequasi 132 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aequasi/cache-bundle", 3 | "type": "library", 4 | "description": "Creates services in Symfony 2, for cache, that can also be used with doctrines three cache types. It also provides functionality for session handler support, and Router support.", 5 | "keywords": [ 6 | "php", 7 | "cache", 8 | "redis", 9 | "memcached" 10 | ], 11 | "homepage": "https://github.com/aequasi/cache-bundle", 12 | "license": "Apache v2", 13 | "authors": [ 14 | { 15 | "name": "Aaron Scherer", 16 | "email": "aequasi@gmail.com", 17 | "homepage": "https://github.com/aequasi" 18 | } 19 | ], 20 | "support": { 21 | "email": "aequasi@gmail.com" 22 | }, 23 | "require": { 24 | "php": "^5.5|^7", 25 | "symfony/framework-bundle": "^2.7|^3.0", 26 | "doctrine/cache": "^1.3", 27 | "aequasi/psr-6-doctrine-bridge": "^1.2", 28 | "psr/cache-implementation": "~1.0" 29 | }, 30 | "require-dev": { 31 | "phpunit/phpunit": "^5.1|^4.0" 32 | }, 33 | "repositories": [ 34 | { 35 | "type": "git", 36 | "url": "https://github.com/php-fig/cache.git" 37 | } 38 | ], 39 | "suggest": { 40 | "ext-memcached": "Allows for caching with Memcached", 41 | "ext-redis": "Allows for caching with Redis" 42 | }, 43 | "autoload": { 44 | "psr-4": { 45 | "Aequasi\\Bundle\\CacheBundle\\": "src/" 46 | } 47 | }, 48 | "autoload-dev": { 49 | "psr-4": { 50 | "Aequasi\\Bundle\\CacheBundle\\Tests\\": "tests/" 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 14 | 15 | 16 | ./tests/ 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | benchmark 27 | 28 | 29 | 30 | 31 | 32 | src/ 33 | 34 | Resources 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /src/AequasiCacheBundle.php: -------------------------------------------------------------------------------- 1 | 5 | * @date 2013 6 | * @license http://www.apache.org/licenses/LICENSE-2.0.html Apache License, Version 2.0 7 | */ 8 | 9 | namespace Aequasi\Bundle\CacheBundle; 10 | 11 | use Symfony\Component\HttpKernel\Bundle\Bundle; 12 | use Symfony\Component\DependencyInjection\ContainerBuilder; 13 | use Aequasi\Bundle\CacheBundle\DependencyInjection\Compiler; 14 | 15 | /** 16 | * Class AequasiCacheBundle 17 | * 18 | * @author Aaron Scherer 19 | */ 20 | class AequasiCacheBundle extends Bundle 21 | { 22 | /** 23 | * {@inheritDoc} 24 | */ 25 | public function build(ContainerBuilder $container) 26 | { 27 | parent::build($container); 28 | 29 | $container->addCompilerPass(new Compiler\SessionSupportCompilerPass()); 30 | $container->addCompilerPass(new Compiler\DoctrineSupportCompilerPass()); 31 | $container->addCompilerPass(new Compiler\RouterCompilerPass()); 32 | 33 | if ($container->getParameter('kernel.debug')) { 34 | $container->addCompilerPass(new Compiler\DataCollectorCompilerPass()); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Cache/LoggingCachePool.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the license that is bundled 9 | * with this source code in the file LICENSE 10 | */ 11 | 12 | namespace Aequasi\Bundle\CacheBundle\Cache; 13 | 14 | use Aequasi\Cache\CacheItem; 15 | use Aequasi\Cache\CachePool; 16 | use Psr\Cache\CacheItemInterface; 17 | 18 | /** 19 | * @author Aaron Scherer 20 | */ 21 | class LoggingCachePool extends CachePool 22 | { 23 | /** 24 | * @var array $calls 25 | */ 26 | private $calls = []; 27 | 28 | public function getItem($key) 29 | { 30 | $call = $this->timeCall(__FUNCTION__, [$key]); 31 | $result = $call->result; 32 | $call->result = sprintf('', gettype($result)); 33 | 34 | $this->calls[] = $call; 35 | 36 | return $result; 37 | } 38 | 39 | public function hasItem($key) 40 | { 41 | $call = $this->timeCall(__FUNCTION__, [$key]); 42 | $this->calls[] = $call; 43 | 44 | return $call->result; 45 | } 46 | 47 | public function deleteItem($key) 48 | { 49 | $call = $this->timeCall(__FUNCTION__, [$key]); 50 | $this->calls[] = $call; 51 | 52 | return $call->result; 53 | } 54 | 55 | public function save(CacheItemInterface $item) 56 | { 57 | $itemClone = clone $item; 58 | $itemClone->set(sprintf('get()))); 59 | 60 | $call = $this->timeCall(__FUNCTION__, [$item]); 61 | $call->arguments = ['', $itemClone]; 62 | $this->calls[] = $call; 63 | 64 | return $call->result; 65 | } 66 | 67 | /** 68 | * @param string $name 69 | * @param $arguments 70 | * 71 | * @return object 72 | */ 73 | private function timeCall($name, $arguments) 74 | { 75 | $start = microtime(true); 76 | $result = call_user_func_array(['parent', $name], $arguments); 77 | $time = microtime(true) - $start; 78 | 79 | $object = (object) compact('name', 'arguments', 'start', 'time', 'result'); 80 | 81 | return $object; 82 | } 83 | 84 | /** 85 | * @return array 86 | */ 87 | public function getCalls() 88 | { 89 | return $this->calls; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/Cache/Memcached.php: -------------------------------------------------------------------------------- 1 | 15 | */ 16 | class Memcached extends \Memcached 17 | { 18 | /** 19 | * {@inheritDoc} 20 | */ 21 | public function addServer($host, $port, $weight = 0) 22 | { 23 | $serverList = $this->getServerList(); 24 | foreach ($serverList as $server) { 25 | if ($server['host'] === $host && $server['port'] === $port) { 26 | return false; 27 | } 28 | } 29 | 30 | return parent::addServer($host, $port, $weight); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Command/CacheFlushCommand.php: -------------------------------------------------------------------------------- 1 | 21 | */ 22 | class CacheFlushCommand extends ContainerAwareCommand 23 | { 24 | 25 | /** 26 | * {@inheritdoc} 27 | */ 28 | protected function configure() 29 | { 30 | $this->setName('cache:flush'); 31 | $this->setDescription('Flushes the given cache'); 32 | $this->addArgument('instance', InputArgument::REQUIRED, 'Which instance do you want to clean?'); 33 | } 34 | 35 | /** 36 | * {@inheritdoc} 37 | */ 38 | protected function execute(InputInterface $input, OutputInterface $output) 39 | { 40 | $serviceName = sprintf('aequasi_cache.instance.%s', $input->getArgument('instance')); 41 | 42 | /** @var CacheItemPoolInterface $service */ 43 | $service = $this->getContainer()->get($serviceName); 44 | $service->clear(); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/DataCollector/CacheDataCollector.php: -------------------------------------------------------------------------------- 1 | 21 | */ 22 | class CacheDataCollector extends DataCollector 23 | { 24 | /** 25 | * @var LoggingCachePool[] 26 | */ 27 | private $instances = []; 28 | 29 | /** 30 | * @param $name 31 | * @param LoggingCachePool $instance 32 | */ 33 | public function addInstance($name, LoggingCachePool $instance) 34 | { 35 | $this->instances[$name] = $instance; 36 | } 37 | 38 | /** 39 | * Collects data for the given Request and Response. 40 | * 41 | * @param Request $request A Request instance 42 | * @param Response $response A Response instance 43 | * @param \Exception $exception An Exception instance 44 | * 45 | * @api 46 | */ 47 | public function collect(Request $request, Response $response, \Exception $exception = null) 48 | { 49 | $empty = ['calls' => [], 'config' => [], 'options' => [], 'statistics' => []]; 50 | $this->data = ['instances' => $empty, 'total' => $empty]; 51 | foreach ($this->instances as $name => $instance) { 52 | $calls = $instance->getCalls(); 53 | $this->data['instances']['calls'][$name] = $calls; 54 | } 55 | $this->data['instances']['statistics'] = $this->calculateStatistics(); 56 | $this->data['total']['statistics'] = $this->calculateTotalStatistics(); 57 | } 58 | 59 | /** 60 | * Returns the name of the collector. 61 | * 62 | * @return string The collector name 63 | * 64 | * @api 65 | */ 66 | public function getName() 67 | { 68 | return 'cache'; 69 | } 70 | 71 | /** 72 | * Method returns amount of logged Cache reads: "get" calls 73 | * 74 | * @return array 75 | */ 76 | public function getStatistics() 77 | { 78 | return $this->data['instances']['statistics']; 79 | } 80 | 81 | /** 82 | * Method returns the statistic totals 83 | * 84 | * @return array 85 | */ 86 | public function getTotals() 87 | { 88 | return $this->data['total']['statistics']; 89 | } 90 | 91 | /** 92 | * Method returns all logged Cache call objects 93 | * 94 | * @return mixed 95 | */ 96 | public function getCalls() 97 | { 98 | return $this->data['instances']['calls']; 99 | } 100 | 101 | /** 102 | * @return array 103 | */ 104 | private function calculateStatistics() 105 | { 106 | $statistics = []; 107 | foreach ($this->data['instances']['calls'] as $name => $calls) { 108 | $statistics[$name] = [ 109 | 'calls' => 0, 110 | 'time' => 0, 111 | 'reads' => 0, 112 | 'hits' => 0, 113 | 'misses' => 0, 114 | 'writes' => 0, 115 | 'deletes' => 0 116 | ]; 117 | foreach ($calls as $call) { 118 | $statistics[$name]['calls'] += 1; 119 | $statistics[$name]['time'] += $call->time; 120 | if ($call->name == 'fetch') { 121 | $statistics[$name]['reads'] += 1; 122 | if ($call->result !== false) { 123 | $statistics[$name]['hits'] += 1; 124 | } else { 125 | $statistics[$name]['misses'] += 1; 126 | } 127 | } elseif ($call->name == 'contains' && $call->result === false) { 128 | $statistics[$name]['reads'] += 1; 129 | $statistics[$name]['misses'] += 1; 130 | } elseif ($call->name == 'save') { 131 | $statistics[$name]['writes'] += 1; 132 | } elseif ($call->name == 'delete') { 133 | $statistics[$name]['deletes'] += 1; 134 | } 135 | } 136 | if ($statistics[$name]['reads']) { 137 | $statistics[$name]['ratio'] = 100 * $statistics[$name]['hits'] / $statistics[$name]['reads'].'%'; 138 | } else { 139 | $statistics[$name]['ratio'] = 'N/A'; 140 | } 141 | } 142 | 143 | return $statistics; 144 | } 145 | 146 | /** 147 | * @return array 148 | */ 149 | private function calculateTotalStatistics() 150 | { 151 | $statistics = $this->getStatistics(); 152 | $totals = ['calls' => 0, 'time' => 0, 'reads' => 0, 'hits' => 0, 'misses' => 0, 'writes' => 0]; 153 | foreach ($statistics as $name => $values) { 154 | foreach ($totals as $key => $value) { 155 | $totals[$key] += $statistics[$name][$key]; 156 | } 157 | } 158 | if ($totals['reads']) { 159 | $totals['ratio'] = 100 * $totals['hits'] / $totals['reads'].'%'; 160 | } else { 161 | $totals['ratio'] = 'N/A'; 162 | } 163 | 164 | return $totals; 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /src/DependencyInjection/AequasiCacheExtension.php: -------------------------------------------------------------------------------- 1 | 5 | * @date 2013 6 | * @license http://www.apache.org/licenses/LICENSE-2.0.html Apache License, Version 2.0 7 | */ 8 | 9 | namespace Aequasi\Bundle\CacheBundle\DependencyInjection; 10 | 11 | use Symfony\Component\Config\FileLocator; 12 | use Symfony\Component\DependencyInjection\ContainerBuilder; 13 | use Symfony\Component\DependencyInjection\Loader; 14 | use Symfony\Component\HttpKernel\DependencyInjection\Extension; 15 | 16 | /** 17 | * Class AequasiCacheExtension 18 | * 19 | * @author Aaron Scherer 20 | */ 21 | class AequasiCacheExtension extends Extension 22 | { 23 | /** 24 | * Loads the configs for Cache and puts data into the container 25 | * 26 | * @param array $configs Array of configs 27 | * @param ContainerBuilder $container Container Object 28 | */ 29 | public function load(array $configs, ContainerBuilder $container) 30 | { 31 | $configuration = new Configuration($container->getParameter('kernel.debug')); 32 | $config = $this->processConfiguration($configuration, $configs); 33 | 34 | $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config')); 35 | $loader->load('services.yml'); 36 | 37 | if ($container->getParameter('kernel.debug')) { 38 | $loader->load('collector.yml'); 39 | } 40 | 41 | $container->setParameter($this->getAlias() . '.instance', $config['instances']); 42 | new Builder\ServiceBuilder($container); 43 | 44 | if ($config['router']['enabled']) { 45 | $container->setParameter($this->getAlias() . '.router', $config['router']); 46 | } 47 | 48 | if ($config['session']['enabled']) { 49 | $container->setParameter($this->getAlias() . '.session', $config['session']); 50 | } 51 | 52 | if ($config['doctrine']['enabled']) { 53 | $container->setParameter($this->getAlias() . '.doctrine', $config['doctrine']); 54 | } 55 | } 56 | 57 | /** 58 | * {@inheritDoc} 59 | */ 60 | public function getConfiguration(array $config, ContainerBuilder $container) 61 | { 62 | return new Configuration($container->getParameter('kernel.debug')); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/DependencyInjection/Builder/BaseBuilder.php: -------------------------------------------------------------------------------- 1 | 17 | */ 18 | abstract class BaseBuilder 19 | { 20 | /** 21 | * @var ContainerBuilder 22 | */ 23 | protected $container; 24 | 25 | /** 26 | * {@inheritDoc} 27 | */ 28 | public function __construct(ContainerBuilder $container) 29 | { 30 | $this->container = $container; 31 | 32 | $this->prepare(); 33 | } 34 | 35 | /** 36 | * @return string 37 | */ 38 | protected function getAlias() 39 | { 40 | return 'aequasi_cache'; 41 | } 42 | 43 | /** 44 | * @return mixed 45 | */ 46 | abstract protected function prepare(); 47 | } 48 | -------------------------------------------------------------------------------- /src/DependencyInjection/Builder/ServiceBuilder.php: -------------------------------------------------------------------------------- 1 | 23 | */ 24 | class ServiceBuilder extends BaseBuilder 25 | { 26 | /** 27 | * Array of types, and their options 28 | * 29 | * @var array $types 30 | */ 31 | protected static $types = [ 32 | 'memcache' => [ 33 | 'class' => 'Memcache', 34 | 'connect' => 'addServer' 35 | ], 36 | 'memcached' => [ 37 | 'class' => 'Aequasi\Bundle\CacheBundle\Cache\Memcached', 38 | 'connect' => 'addServer' 39 | ], 40 | 'redis' => [ 41 | 'class' => 'Redis', 42 | 'connect' => 'connect' 43 | ] 44 | ]; 45 | 46 | /** 47 | * {@inheritDoc} 48 | */ 49 | protected function prepare() 50 | { 51 | $instances = $this->container->getParameter($this->getAlias().'.instance'); 52 | 53 | foreach ($instances as $name => $instance) { 54 | $this->buildInstance($name, $instance); 55 | } 56 | } 57 | 58 | /** 59 | * @param string $name 60 | * @param array $instance 61 | * 62 | * @throws InvalidConfigurationException 63 | */ 64 | private function buildInstance($name, array $instance) 65 | { 66 | $typeId = $this->getAlias().'.abstract.'.$instance['type']; 67 | if (!$this->container->hasDefinition($typeId)) { 68 | throw new InvalidConfigurationException( 69 | sprintf( 70 | "`%s` is not a valid cache type. If you are using a custom type, make sure to add your service. ", 71 | $instance['type'] 72 | ) 73 | ); 74 | } 75 | 76 | $service = $this->buildService($typeId, $name, $instance); 77 | 78 | $this->prepareCacheClass($service, $name, $instance); 79 | } 80 | 81 | /** 82 | * @param string $typeId 83 | * @param string $name 84 | * @param array $instance 85 | * 86 | * @return Definition 87 | */ 88 | private function buildService($typeId, $name, array $instance) 89 | { 90 | $namespace = is_null($instance['namespace']) ? $name : $instance['namespace']; 91 | $serviceId = $this->getAlias().'.instance.'.$name; 92 | 93 | // Create the core doctrine cache class 94 | $coreName = $serviceId.'.core'; 95 | $doctrine = $this->container->setDefinition( 96 | $coreName, 97 | new Definition($this->container->getParameter($typeId.'.class')) 98 | ); 99 | $doctrine->addMethodCall('setNamespace', [$namespace]) 100 | ->setPublic(false); 101 | 102 | // Create the CacheItemPoolInterface object, Logging or not 103 | $this->container->setDefinition( 104 | $serviceId, 105 | new Definition($this->getCachePoolClassName(), [new Reference($coreName)]) 106 | ); 107 | 108 | // Set up the simple alias (aequasi_cache.instance.xxx => aequasi_cache.xxx) 109 | $this->container->setAlias($this->getAlias().'.'.$name, $serviceId); 110 | 111 | // Create the Doctrine/PSR-6 Bridge, for the doctrine cache piece 112 | $bridge = $this->container->setDefinition( 113 | $serviceId.'.bridge', 114 | new Definition(DoctrineCacheBridge::class, [new Reference($serviceId)]) 115 | ); 116 | $bridge->setPublic(false); 117 | 118 | return $doctrine; 119 | } 120 | 121 | /** 122 | * @param Definition $service 123 | * @param string $name 124 | * @param array $instance 125 | * 126 | * @return Boolean 127 | */ 128 | private function prepareCacheClass(Definition $service, $name, array $instance) 129 | { 130 | $type = $instance['type']; 131 | $id = sprintf("%s.instance.%s.cache_instance", $this->getAlias(), $name); 132 | switch ($type) { 133 | case 'memcache': 134 | case 'memcached': 135 | case 'redis': 136 | return $this->createCacheInstance($service, $type, $id, $instance); 137 | case 'file_system': 138 | case 'php_file': 139 | $directory = '%kernel.cache_dir%/doctrine/cache'; 140 | if (null !== $instance['directory']) { 141 | $directory = $instance['directory']; 142 | } 143 | $extension = is_null($instance['extension']) ? null : $instance['extension']; 144 | 145 | $service->setArguments([$directory, $extension]); 146 | 147 | return true; 148 | case 'mongo': 149 | case 'sqlite3': 150 | case 'sqlite': 151 | case 'riak': 152 | case 'chain': 153 | return false; 154 | default: 155 | return true; 156 | } 157 | } 158 | 159 | /** 160 | * Creates a cache instance 161 | * 162 | * @param Definition $service 163 | * @param string $type 164 | * @param string $id 165 | * @param array $instance 166 | * 167 | * @return Boolean 168 | */ 169 | public function createCacheInstance(Definition $service, $type, $id, array $instance) 170 | { 171 | if (empty($instance['id'])) { 172 | $cache = new Definition(self::$types[$type]['class']); 173 | 174 | // set memcached options first as they need to be set before the servers are added. 175 | if ($type === 'memcached') { 176 | if (!empty($instance['options']['memcached'])) { 177 | foreach ($instance['options']['memcached'] as $option => $value) { 178 | switch ($option) { 179 | case 'serializer': 180 | case 'hash': 181 | case 'distribution': 182 | $value = constant( 183 | sprintf('\Memcached::%s_%s', strtoupper($option), strtoupper($value)) 184 | ); 185 | break; 186 | } 187 | $cache->addMethodCall( 188 | 'setOption', 189 | [constant(sprintf('\Memcached::OPT_%s', strtoupper($option))), $value] 190 | ); 191 | } 192 | } 193 | } 194 | 195 | if (isset($instance['persistent']) && $instance['persistent'] !== false) { 196 | if ($instance['persistent'] !== true) { 197 | $persistentId = $instance['persistent']; 198 | } else { 199 | $persistentId = substr(md5(serialize($instance['hosts'])), 0, 5); 200 | } 201 | if ($type === 'memcached') { 202 | $cache->setArguments([$persistentId]); 203 | } 204 | if ($type === 'redis') { 205 | self::$types[$type]['connect'] = 'pconnect'; 206 | } 207 | } 208 | 209 | foreach ($instance['hosts'] as $config) { 210 | $arguments = [ 211 | 'host' => empty($config['host']) ? 'localhost' : $config['host'], 212 | 'port' => empty($config['port']) ? 11211 : $config['port'] 213 | ]; 214 | if ($type === 'memcached') { 215 | $arguments[] = is_null($config['weight']) ? 0 : $config['weight']; 216 | } else { 217 | $arguments[] = is_null($config['timeout']) ? 0 : $config['timeout']; 218 | if (isset($persistentId)) { 219 | $arguments[] = $persistentId; 220 | } 221 | } 222 | 223 | $cache->addMethodCall(self::$types[$type]['connect'], $arguments); 224 | } 225 | unset($config); 226 | 227 | if ($type === 'redis') { 228 | if (isset($instance['auth_password']) && null !== $instance['auth_password']) { 229 | $cache->addMethodCall('auth', [$instance['auth_password']]); 230 | } 231 | if (isset($instance['database'])) { 232 | $cache->addMethodCall('select', [$instance['database']]); 233 | } 234 | } 235 | 236 | $this->container->setDefinition($id, $cache); 237 | } else { 238 | $id = $instance['id']; 239 | } 240 | $service->addMethodCall(sprintf('set%s', ucwords($type)), [new Reference($id)]); 241 | 242 | return true; 243 | } 244 | 245 | private function getCachePoolClassName() 246 | { 247 | return $this->container->getParameter('kernel.debug') ? LoggingCachePool::class : CachePool::class; 248 | } 249 | } 250 | -------------------------------------------------------------------------------- /src/DependencyInjection/Compiler/BaseCompilerPass.php: -------------------------------------------------------------------------------- 1 | 18 | */ 19 | abstract class BaseCompilerPass implements CompilerPassInterface 20 | { 21 | /** 22 | * @var ContainerBuilder 23 | */ 24 | protected $container; 25 | 26 | /** 27 | * {@inheritDoc} 28 | */ 29 | public function process(ContainerBuilder $container) 30 | { 31 | $this->container = $container; 32 | 33 | $this->prepare(); 34 | } 35 | 36 | /** 37 | * @return string 38 | */ 39 | protected function getAlias() 40 | { 41 | return 'aequasi_cache'; 42 | } 43 | 44 | /** 45 | * @return mixed 46 | */ 47 | abstract protected function prepare(); 48 | } 49 | -------------------------------------------------------------------------------- /src/DependencyInjection/Compiler/DataCollectorCompilerPass.php: -------------------------------------------------------------------------------- 1 | 17 | */ 18 | class DataCollectorCompilerPass extends BaseCompilerPass 19 | { 20 | /** 21 | * {@inheritDoc} 22 | */ 23 | protected function prepare() 24 | { 25 | $instances = $this->container->getParameter($this->getAlias() . '.instance'); 26 | 27 | $definition = $this->container->getDefinition('data_collector.cache'); 28 | 29 | foreach (array_keys($instances) as $name) { 30 | $instance = new Reference(sprintf("%s.instance.%s", $this->getAlias(), $name)); 31 | $definition->addMethodCall('addInstance', array($name, $instance)); 32 | } 33 | 34 | $this->container->setDefinition('data_collector.cache', $definition); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/DependencyInjection/Compiler/DoctrineSupportCompilerPass.php: -------------------------------------------------------------------------------- 1 | 5 | * @date 2013 6 | * @license http://www.apache.org/licenses/LICENSE-2.0.html Apache License, Version 2.0 7 | */ 8 | 9 | namespace Aequasi\Bundle\CacheBundle\DependencyInjection\Compiler; 10 | 11 | use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; 12 | 13 | /** 14 | * Class DoctrineSupportCompilerPass 15 | * 16 | * @author Aaron Scherer 17 | */ 18 | class DoctrineSupportCompilerPass extends BaseCompilerPass 19 | { 20 | /** 21 | * @return mixed|void 22 | */ 23 | protected function prepare() 24 | { 25 | // If there is no active session support, return 26 | if (!$this->container->hasAlias('doctrine.orm.entity_manager')) { 27 | return; 28 | } 29 | 30 | // If the aequasi.cache.session_handler service is loaded set the alias 31 | if ($this->container->hasParameter($this->getAlias() . '.doctrine')) { 32 | $this->enableDoctrineSupport($this->container->getParameter($this->getAlias() . '.doctrine')); 33 | } 34 | } 35 | 36 | /** 37 | * Loads the Doctrine configuration. 38 | * 39 | * @param array $config A configuration array 40 | * 41 | * @throws InvalidConfigurationException 42 | */ 43 | protected function enableDoctrineSupport(array $config) 44 | { 45 | $types = array('entity_managers', 'document_managers'); 46 | foreach ($config as $cacheType => $cacheData) { 47 | foreach ($types as $type) { 48 | if (!isset($cacheData[$type])) { 49 | continue; 50 | } 51 | 52 | if (!isset($cacheData['instance'])) { 53 | throw new InvalidConfigurationException(sprintf( 54 | "There was no instance passed. Please specify a instance under the %s type", 55 | $cacheType 56 | )); 57 | } 58 | $cacheDefinitionName = sprintf('%s.instance.%s.bridge', $this->getAlias(), $cacheData['instance']); 59 | 60 | foreach ($cacheData[$type] as $manager) { 61 | $doctrineDefinitionName = 62 | sprintf( 63 | "doctrine.%s.%s_%s_cache", 64 | ($type == 'entity_managers' ? 'orm' : 'odm'), 65 | $manager, 66 | $cacheType 67 | ); 68 | 69 | // Replace the doctrine entity manager cache with our bridge 70 | $this->container->setAlias($doctrineDefinitionName, $cacheDefinitionName); 71 | } 72 | } 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/DependencyInjection/Compiler/RouterCompilerPass.php: -------------------------------------------------------------------------------- 1 | 5 | * @date 2013 6 | * @license http://www.apache.org/licenses/LICENSE-2.0.html Apache License, Version 2.0 7 | */ 8 | 9 | namespace Aequasi\Bundle\CacheBundle\DependencyInjection\Compiler; 10 | 11 | use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; 12 | use Symfony\Component\DependencyInjection\Reference; 13 | 14 | /** 15 | * 16 | * 17 | * @author Tobias Nyholm 18 | */ 19 | class RouterCompilerPass extends BaseCompilerPass 20 | { 21 | /** 22 | * @return void 23 | */ 24 | protected function prepare() 25 | { 26 | $router = $this->container->getParameter($this->getAlias() . '.router'); 27 | 28 | if (!$router['enabled']) { 29 | return; 30 | } 31 | $instance = $router['instance']; 32 | 33 | $def = $this->container->findDefinition('router'); 34 | $def->setClass('Aequasi\Bundle\CacheBundle\Routing\Router'); 35 | $def->addMethodCall('setCache', [new Reference(sprintf('aequasi_cache.instance.%s', $instance))]); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/DependencyInjection/Compiler/SessionSupportCompilerPass.php: -------------------------------------------------------------------------------- 1 | 5 | * @date 2013 6 | * @license http://www.apache.org/licenses/LICENSE-2.0.html Apache License, Version 2.0 7 | */ 8 | 9 | namespace Aequasi\Bundle\CacheBundle\DependencyInjection\Compiler; 10 | 11 | use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; 12 | use Symfony\Component\DependencyInjection\Definition; 13 | use Symfony\Component\DependencyInjection\Reference; 14 | 15 | /** 16 | * Class SessionSupportCompilerPass 17 | * 18 | * @author Aaron Scherer 19 | */ 20 | class SessionSupportCompilerPass extends BaseCompilerPass 21 | { 22 | /** 23 | * 24 | */ 25 | protected function prepare() 26 | { 27 | // If there is no active session support, return 28 | if (!$this->container->hasAlias('session.storage')) { 29 | return; 30 | } 31 | 32 | // If the aequasi.cache.session_handler service is loaded set the alias 33 | if ($this->container->hasParameter($this->getAlias() . '.session')) { 34 | $this->enableSessionSupport($this->container->getParameter($this->getAlias() . '.session')); 35 | } 36 | } 37 | 38 | /** 39 | * Enables session support for memcached 40 | * 41 | * @param array $config Configuration for bundle 42 | * 43 | * @throws InvalidConfigurationException 44 | */ 45 | private function enableSessionSupport(array $config) 46 | { 47 | if (empty($config['instance'])) { 48 | throw new InvalidConfigurationException("Instance must be passed under the `session` config."); 49 | } 50 | 51 | $instance = $config['instance']; 52 | $instances = $this->container->getParameter($this->getAlias() . '.instance'); 53 | 54 | if (null === $instance) { 55 | return; 56 | } 57 | if (!isset($instances[$instance])) { 58 | throw new InvalidConfigurationException(sprintf( 59 | "Failed to hook into the session. The instance \"%s\" doesn't exist!", 60 | $instance 61 | )); 62 | } 63 | 64 | // calculate options 65 | $sessionOptions = $this->container->getParameter('session.storage.options'); 66 | if (isset($sessionOptions['cookie_lifetime']) && !isset($config['cookie_lifetime'])) { 67 | $config['cookie_lifetime'] = $sessionOptions['cookie_lifetime']; 68 | } 69 | // load the session handler 70 | $definition = 71 | new Definition($this->container->getParameter(sprintf('%s.session.handler.class', $this->getAlias()))); 72 | $definition->addArgument(new Reference(sprintf('%s.instance.%s.bridge', $this->getAlias(), $instance))) 73 | ->addArgument($config); 74 | 75 | $this->container->setDefinition(sprintf('%s.session_handler', $this->getAlias()), $definition); 76 | $this->container->setAlias('cache.session_handler', sprintf('%s.session_handler', $this->getAlias())); 77 | 78 | $this->container->setAlias('session.handler', 'cache.session_handler'); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/DependencyInjection/Configuration.php: -------------------------------------------------------------------------------- 1 | 5 | * @date 2013 6 | * @license http://www.apache.org/licenses/LICENSE-2.0.html Apache License, Version 2.0 7 | */ 8 | 9 | namespace Aequasi\Bundle\CacheBundle\DependencyInjection; 10 | 11 | use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; 12 | use Symfony\Component\Config\Definition\Builder\TreeBuilder; 13 | use Symfony\Component\Config\Definition\ConfigurationInterface; 14 | 15 | /** 16 | * Class Configuration 17 | * 18 | * @author Aaron Scherer 19 | */ 20 | class Configuration implements ConfigurationInterface 21 | { 22 | 23 | /** 24 | * @var bool 25 | */ 26 | private $debug; 27 | 28 | /** 29 | * Constructor 30 | * 31 | * @param Boolean $debug Whether to use the debug mode 32 | */ 33 | public function __construct($debug) 34 | { 35 | $this->debug = (Boolean)$debug; 36 | } 37 | 38 | /** 39 | * Generates the configuration tree builder. 40 | * 41 | * @return TreeBuilder The tree builder 42 | */ 43 | public function getConfigTreeBuilder() 44 | { 45 | $treeBuilder = new TreeBuilder(); 46 | $rootNode = $treeBuilder->root('cache'); 47 | 48 | $rootNode->children() 49 | ->append($this->getClustersNode()) 50 | ->append($this->addSessionSupportSection()) 51 | ->append($this->addDoctrineSection()) 52 | ->append($this->addRouterSection()) 53 | ->end(); 54 | 55 | return $treeBuilder; 56 | } 57 | 58 | /** 59 | * @return ArrayNodeDefinition 60 | */ 61 | private function getClustersNode() 62 | { 63 | $treeBuilder = new TreeBuilder(); 64 | $node = $treeBuilder->root('instances'); 65 | 66 | $node 67 | ->requiresAtLeastOneElement() 68 | ->addDefaultChildrenIfNoneSet('default') 69 | ->useAttributeAsKey('name') 70 | ->prototype('array') 71 | ->children() 72 | ->enumNode('type') 73 | ->values(array('redis', 'php_file', 'file_system', 'array', 'memcached', 'apc')) 74 | ->end() 75 | ->scalarNode('id') 76 | ->defaultNull() 77 | ->end() 78 | ->scalarNode('namespace') 79 | ->defaultNull() 80 | ->info("Namespace for doctrine keys.") 81 | ->end() 82 | ->integerNode('database') 83 | ->defaultNull() 84 | ->info("For Redis: Specify what database you want.") 85 | ->end() 86 | ->scalarNode('persistent') 87 | ->defaultNull() 88 | ->beforeNormalization() 89 | ->ifTrue( 90 | function ($v) { 91 | return $v === 'true' || $v === 'false'; 92 | } 93 | ) 94 | ->then( 95 | function ($v) { 96 | return (bool) $v; 97 | } 98 | ) 99 | ->end() 100 | ->info("For Redis and Memcached: Specify the persistent id if you want persistent connections.") 101 | ->end() 102 | ->scalarNode('auth_password') 103 | ->info("For Redis: Authorization info.") 104 | ->end() 105 | ->scalarNode('directory') 106 | ->info("For File System and PHP File: Directory to store cache.") 107 | ->defaultNull() 108 | ->end() 109 | ->scalarNode('extension') 110 | ->info("For File System and PHP File: Extension to use.") 111 | ->defaultNull() 112 | ->end() 113 | ->arrayNode('options') 114 | ->info("Options for Redis and Memcached.") 115 | ->children() 116 | ->append($this->getMemcachedOptions()) 117 | ->end() 118 | ->end() 119 | ->arrayNode('hosts') 120 | ->prototype('array') 121 | ->children() 122 | ->scalarNode('host') 123 | ->defaultNull() 124 | ->end() 125 | ->scalarNode('port') 126 | ->defaultNull() 127 | ->validate() 128 | ->ifTrue( 129 | function ($v) { 130 | return !is_null($v) && !is_numeric($v); 131 | } 132 | ) 133 | ->thenInvalid("Host port must be numeric") 134 | ->end() 135 | ->end() 136 | ->scalarNode('weight') 137 | ->info("For Memcached: Weight for given host.") 138 | ->defaultNull() 139 | ->validate() 140 | ->ifTrue( 141 | function ($v) { 142 | return !is_null($v) && !is_numeric($v); 143 | } 144 | ) 145 | ->thenInvalid('host weight must be numeric') 146 | ->end() 147 | ->end() 148 | ->scalarNode('timeout') 149 | ->info("For Redis and Memcache: Timeout for the given host.") 150 | ->defaultNull() 151 | ->validate() 152 | ->ifTrue( 153 | function ($v) { 154 | return !is_null($v) && !is_numeric($v); 155 | } 156 | ) 157 | ->thenInvalid('host timeout must be numeric') 158 | ->end() 159 | ->end() 160 | ->end() 161 | ->end() 162 | ->end() 163 | ->end() 164 | ->end() 165 | ; 166 | 167 | return $node; 168 | } 169 | 170 | /** 171 | * @return ArrayNodeDefinition 172 | */ 173 | private function getMemcachedOptions() 174 | { 175 | $treeBuilder = new TreeBuilder(); 176 | $node = $treeBuilder->root('memcached'); 177 | 178 | if (class_exists('\Memcached')) { 179 | $node 180 | ->children() 181 | ->enumNode('serializer') 182 | ->values(array('php', 'igbinary', 'json')) 183 | ->end() 184 | ->enumNode('hash') 185 | ->values(array('default', 'md5', 'crc', 'fnv1_64', 'fnv1a_64', 'fnv1_32', 'fnv1a_32', 'hsieh', 'murmur')) 186 | ->end() 187 | ->enumNode('distribution') 188 | ->values(array('modula', 'consistent')) 189 | ->end() 190 | ->booleanNode('compression')->end() 191 | ->scalarNode('prefix_key')->end() 192 | ->booleanNode('libketama_compatible')->end() 193 | ->booleanNode('uffer_writes')->end() 194 | ->booleanNode('binary_protocol')->end() 195 | ->booleanNode('no_block')->end() 196 | ->booleanNode('tcp_nodelay')->end() 197 | ->integerNode('socket_send_size')->end() 198 | ->integerNode('socket_recv_size')->end() 199 | ->integerNode('connect_timeout')->end() 200 | ->integerNode('retry_timeout')->end() 201 | ->integerNode('send_timeout')->end() 202 | ->integerNode('recv_timeout')->end() 203 | ->integerNode('poll_timeout')->end() 204 | ->booleanNode('cache_lookups')->end() 205 | ->integerNode('server_failure_limit')->end() 206 | ->end(); 207 | } 208 | 209 | return $node; 210 | } 211 | 212 | /** 213 | * Configure the "aequasi_cache.session" section 214 | * 215 | * @return ArrayNodeDefinition 216 | */ 217 | private function addSessionSupportSection() 218 | { 219 | $tree = new TreeBuilder(); 220 | $node = $tree->root('session'); 221 | 222 | $node 223 | ->addDefaultsIfNotSet() 224 | ->children() 225 | ->booleanNode('enabled') 226 | ->defaultFalse() 227 | ->end() 228 | ->scalarNode('instance')->end() 229 | ->scalarNode('prefix') 230 | ->defaultValue("session_") 231 | ->end() 232 | ->scalarNode('ttl')->end() 233 | ->end() 234 | ; 235 | 236 | return $node; 237 | } 238 | 239 | /** 240 | * Configure the "aequasi_cache.doctrine" section 241 | * 242 | * @return ArrayNodeDefinition 243 | */ 244 | private function addDoctrineSection() 245 | { 246 | $tree = new TreeBuilder(); 247 | $node = $tree->root('doctrine'); 248 | 249 | $node 250 | ->addDefaultsIfNotSet() 251 | ->children() 252 | ->booleanNode('enabled') 253 | ->defaultFalse() 254 | ->isRequired() 255 | ->end() 256 | ->end() 257 | ; 258 | 259 | $types = array('metadata', 'result', 'query'); 260 | foreach ($types as $type) { 261 | $node->children() 262 | ->arrayNode($type) 263 | ->canBeUnset() 264 | ->children() 265 | ->scalarNode('instance') 266 | ->end() 267 | ->arrayNode('entity_managers') 268 | ->defaultValue(array()) 269 | ->beforeNormalization() 270 | ->ifString() 271 | ->then( 272 | function ($v) { 273 | return (array) $v; 274 | } 275 | ) 276 | ->end() 277 | ->prototype('scalar') 278 | ->end() 279 | ->end() 280 | ->arrayNode('document_managers') 281 | ->defaultValue(array()) 282 | ->beforeNormalization() 283 | ->ifString() 284 | ->then( 285 | function ($v) { 286 | return (array) $v; 287 | } 288 | ) 289 | ->end() 290 | ->prototype('scalar') 291 | ->end() 292 | ->end() 293 | ->end() 294 | ->end(); 295 | } 296 | 297 | return $node; 298 | } 299 | 300 | /** 301 | * Configure the "aequasi_cache.router" section 302 | * 303 | * @return ArrayNodeDefinition 304 | */ 305 | private function addRouterSection() 306 | { 307 | $tree = new TreeBuilder(); 308 | $node = $tree->root('router'); 309 | 310 | $node->addDefaultsIfNotSet() 311 | ->children() 312 | ->booleanNode('enabled') 313 | ->defaultFalse() 314 | ->end() 315 | ->scalarNode('instance') 316 | ->defaultNull() 317 | ->end() 318 | ->end(); 319 | 320 | return $node; 321 | } 322 | } 323 | -------------------------------------------------------------------------------- /src/Resources/config/collector.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | aequasi_cache.data_collector.class: Aequasi\Bundle\CacheBundle\DataCollector\CacheDataCollector 3 | aequasi_cache.data_collector.template: AequasiCacheBundle:Collector:cache.html.twig 4 | 5 | services: 6 | data_collector.cache: 7 | class: %aequasi_cache.data_collector.class% 8 | tags: 9 | - { name: data_collector, template: %aequasi_cache.data_collector.template%, id: 'cache' } 10 | 11 | -------------------------------------------------------------------------------- /src/Resources/config/services.yml: -------------------------------------------------------------------------------- 1 | 2 | services: 3 | aequasi_cache.abstract.apc: 4 | class: Doctrine\Common\Cache\ApcCache 5 | abstract: true 6 | 7 | aequasi_cache.abstract.array: 8 | class: Doctrine\Common\Cache\ArrayCache 9 | abstract: true 10 | 11 | aequasi_cache.abstract.file_system: 12 | class: Doctrine\Common\Cache\FilesystemCache 13 | abstract: true 14 | 15 | aequasi_cache.abstract.memcache: 16 | class: Doctrine\Common\Cache\MemcacheCache 17 | abstract: true 18 | 19 | aequasi_cache.abstract.memcached: 20 | class: Doctrine\Common\Cache\MemcachedCache 21 | abstract: true 22 | 23 | aequasi_cache.abstract.redis: 24 | class: Doctrine\Common\Cache\RedisCache 25 | abstract: true 26 | 27 | aequasi_cache.abstract.php_file: 28 | class: Doctrine\Common\Cache\PhpFileCache 29 | abstract: true 30 | 31 | aequasi_cache.abstract.win_cache: 32 | class: Doctrine\Common\Cache\WinCacheCache 33 | abstract: true 34 | 35 | aequasi_cache.abstract.xcache: 36 | class: Doctrine\Common\Cache\XcacheCache 37 | abstract: true 38 | 39 | aequasi_cache.zend_data: 40 | class: Doctrine\Common\Cache\ZendDataCache 41 | abstract: true 42 | -------------------------------------------------------------------------------- /src/Resources/views/Collector/cache.html.twig: -------------------------------------------------------------------------------- 1 | {% extends 'WebProfilerBundle:Profiler:layout.html.twig' %} 2 | 3 | {% block toolbar %} 4 | {% set icon %} 5 | AequasiCache 7 | {{ collector.totals.calls }} 8 | {% if collector.totals.calls > 0 %} 9 | in {{ '%0.2f'|format(collector.totals.time * 1000) }} 10 | ms 11 | {% endif %} 12 | {% endset %} 13 | {% set text %} 14 |
15 | Cache Calls 16 | {{ collector.totals.calls }} 17 |
18 |
19 | Total time 20 | {{ '%0.2f'|format(collector.totals.time * 1000) }} ms 21 |
22 |
23 | Cache hits 24 | {{ collector.totals.hits }}/{{ collector.totals.reads }} ({{ collector.totals.ratio }}) 25 |
26 |
27 | Cache writes 28 | {{ collector.totals.writes }} 29 |
30 | {% endset %} 31 | {% include 'WebProfilerBundle:Profiler:toolbar_item.html.twig' with { 'link': profiler_url } %} 32 | {% endblock %} 33 | 34 | {% block menu %} 35 | 36 | 37 | AequasiCache 39 | 40 | Cache 41 | 42 | {{ collector.totals.calls }} 43 | {{ '%0.0f'|format(collector.totals.time * 1000) }} ms 44 | 45 | 46 | {% endblock %} 47 | 48 | {% block panel %} 49 |

Cache

50 | {% for name, calls in collector.calls %} 51 |

Statistics for '{{ name }}'

52 | 53 | 54 | 55 | {% for key, value in collector.statistics[name] %} 56 | 57 | {% endfor %} 58 | 59 | 60 | 61 | 62 | {% for key, value in collector.statistics[name] %} 63 | {% if key == 'time' %} 64 | 65 | {% else %} 66 | 67 | {% endif %} 68 | {% endfor %} 69 | 70 | 71 |
{{ key|capitalize }}
{{ '%0.2f'|format(value * 1000) }} ms{{ value }}
72 | 73 |

Calls for '{{ name }}'

74 | 75 | {% if not collector.totals.calls %} 76 |

77 | No calls. 78 |

79 | {% else %} 80 |
    81 | {% for i, call in calls %} 82 |
  • 83 |
    84 | Name: {{ call.name }}
    85 | Arguments: {{ call.arguments|json_encode() }}
    86 | Results: {{ call.result|json_encode() }}
    87 |
    88 | 89 | Time: {{ '%0.2f'|format(call.time * 1000) }} ms 90 | 91 |
  • 92 | {% endfor %} 93 |
94 | {% endif %} 95 | {% endfor %} 96 | 97 | {% endblock %} 98 | -------------------------------------------------------------------------------- /src/Routing/Matcher/CacheUrlMatcher.php: -------------------------------------------------------------------------------- 1 | 19 | */ 20 | class CacheUrlMatcher extends UrlMatcher 21 | { 22 | const CACHE_LIFETIME = 604800; // a week 23 | 24 | /** 25 | * @var CacheItemPoolInterface 26 | */ 27 | protected $cache; 28 | 29 | /** 30 | * @param string $pathInfo 31 | * 32 | * @return array 33 | */ 34 | public function match($pathInfo) 35 | { 36 | $host = strtr($this->context->getHost(), '.', '_'); 37 | $method = strtolower($this->context->getMethod()); 38 | $key = 'route_' . $method . '_' . $host . '_' . $pathInfo; 39 | 40 | if ($this->cache->hasItem($key)) { 41 | return $this->cache->getItem($key)->get(); 42 | } 43 | 44 | $match = parent::match($pathInfo); 45 | $item = $this->cache->getItem($key); 46 | $item->set($match) 47 | ->expiresAfter(self::CACHE_LIFETIME); 48 | 49 | return $match; 50 | } 51 | 52 | /** 53 | * @param CacheItemPoolInterface $cache 54 | */ 55 | public function setCache(CacheItemPoolInterface $cache) 56 | { 57 | $this->cache = $cache; 58 | } 59 | 60 | /** 61 | * @return CacheItemPoolInterface 62 | */ 63 | public function getCache() 64 | { 65 | return $this->cache; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/Routing/Router.php: -------------------------------------------------------------------------------- 1 | 22 | */ 23 | class Router extends BaseRouter 24 | { 25 | const CACHE_LIFETIME = 604800; // a week 26 | 27 | /** 28 | * @var ContainerInterface 29 | */ 30 | protected $container; 31 | 32 | /** 33 | * @var CacheItemPoolInterface 34 | */ 35 | protected $cache; 36 | 37 | /** 38 | * @return CacheUrlMatcher|null|\Symfony\Component\Routing\Matcher\UrlMatcherInterface 39 | */ 40 | public function getMatcher() 41 | { 42 | if (null !== $this->matcher) { 43 | return $this->matcher; 44 | } 45 | 46 | $matcher = new CacheUrlMatcher($this->getRouteCollection(), $this->context); 47 | $matcher->setCache($this->cache); 48 | 49 | return $this->matcher = $matcher; 50 | } 51 | 52 | /** 53 | * {@inheritdoc} 54 | */ 55 | public function getRouteCollection() 56 | { 57 | $key = 'route_collection'; 58 | 59 | if (null === $this->collection) { 60 | if ($this->cache->hasItem($key)) { 61 | $collection = $this->cache->getItem($key)->get(); 62 | if ($collection !== null) { 63 | $this->collection = $collection; 64 | 65 | return $this->collection; 66 | } 67 | } 68 | 69 | $this->collection = parent::getRouteCollection(); 70 | $item = $this->cache->getItem($key); 71 | $item->set($this->collection) 72 | ->expiresAfter(self::CACHE_LIFETIME); 73 | } 74 | 75 | return $this->collection; 76 | } 77 | 78 | /** 79 | * @param CacheItemPoolInterface $cache 80 | * 81 | * @return Router 82 | */ 83 | public function setCache(CacheItemPoolInterface $cache) 84 | { 85 | $this->cache = $cache; 86 | 87 | return $this; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/Session/SessionHandler.php: -------------------------------------------------------------------------------- 1 | 5 | * @date 2013 6 | * @license http://www.apache.org/licenses/LICENSE-2.0.html Apache License, Version 2.0 7 | */ 8 | 9 | namespace Aequasi\Bundle\CacheBundle\Session; 10 | 11 | use Doctrine\Common\Cache\Cache; 12 | use Psr\Cache\CacheItemPoolInterface; 13 | 14 | /** 15 | * Class SessionHandler 16 | * 17 | * @author Aaron Scherer 18 | */ 19 | class SessionHandler implements \SessionHandlerInterface 20 | { 21 | /** 22 | * @var CacheItemPoolInterface Cache driver. 23 | */ 24 | private $cache; 25 | 26 | /** 27 | * @var integer Time to live in seconds 28 | */ 29 | private $ttl; 30 | 31 | /** 32 | * @var string Key prefix for shared environments. 33 | */ 34 | private $prefix; 35 | 36 | /** 37 | * Constructor. 38 | * 39 | * List of available options: 40 | * * prefix: The prefix to use for the cache keys in order to avoid collision 41 | * * expiretime: The time to live in seconds 42 | * 43 | * @param CacheItemPoolInterface $cache A Cache instance 44 | * @param array $options An associative array of cache options 45 | * 46 | * @throws \InvalidArgumentException When unsupported options are passed 47 | */ 48 | public function __construct(CacheItemPoolInterface $cache, array $options = array()) 49 | { 50 | $this->cache = $cache; 51 | 52 | $this->ttl = isset($options['cookie_lifetime']) ? (int) $options['cookie_lifetime'] : 86400; 53 | $this->prefix = isset($options['prefix']) ? $options['prefix'] : 'sf2ses_'; 54 | } 55 | 56 | /** 57 | * {@inheritDoc} 58 | */ 59 | public function open($savePath, $sessionName) 60 | { 61 | return true; 62 | } 63 | 64 | /** 65 | * {@inheritDoc} 66 | */ 67 | public function close() 68 | { 69 | return true; 70 | } 71 | 72 | /** 73 | * {@inheritDoc} 74 | */ 75 | public function read($sessionId) 76 | { 77 | $item = $this->cache->getItem($this->prefix.$sessionId); 78 | if ($item->isHit()) { 79 | return $item->get(); 80 | } 81 | 82 | return ''; 83 | } 84 | 85 | /** 86 | * {@inheritDoc} 87 | */ 88 | public function write($sessionId, $data) 89 | { 90 | $item = $this->cache->getItem($this->prefix . $sessionId); 91 | $item->set($data) 92 | ->expiresAfter($this->ttl); 93 | 94 | return $this->cache->save($item); 95 | } 96 | 97 | /** 98 | * {@inheritDoc} 99 | */ 100 | public function destroy($sessionId) 101 | { 102 | $item = $this->cache->getItem($this->prefix . $sessionId); 103 | 104 | return $this->cache->deleteItem($item); 105 | } 106 | 107 | /** 108 | * {@inheritDoc} 109 | */ 110 | public function gc($lifetime) 111 | { 112 | // not required here because cache will auto expire the records anyhow. 113 | return true; 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /tests/ContainerTest.php: -------------------------------------------------------------------------------- 1 | 5 | * @date 2013 6 | * @license http://www.apache.org/licenses/LICENSE-2.0.html Apache License, Version 2.0 7 | */ 8 | 9 | namespace Aequasi\Bundle\CacheBundle\Tests; 10 | 11 | /** 12 | * Class ContainerTest 13 | * 14 | * @author Aaron Scherer 15 | */ 16 | class ContainerTest extends TestCase 17 | { 18 | /** 19 | * 20 | */ 21 | public function testContainer() 22 | { 23 | //$container = $this->createContainer(); 24 | 25 | // @TODO Create Tests..... 26 | $this->assertTrue(true); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /tests/DependencyInjection/AequasiCacheExtensionTest.php: -------------------------------------------------------------------------------- 1 | 19 | */ 20 | class AequasiCacheExtensionTest extends TestCase 21 | { 22 | 23 | /** 24 | * 25 | */ 26 | public function testServiceBuilder() 27 | { 28 | $container = $this->createContainerFromFile('service'); 29 | 30 | $this->assertTrue($container->hasDefinition($this->getAlias().'.instance.default')); 31 | $this->assertTrue($container->hasAlias($this->getAlias().'.default')); 32 | 33 | $this->assertInstanceOf( 34 | CacheItemPoolInterface::class, 35 | $container->get($this->getAlias().'.instance.default') 36 | ); 37 | 38 | $this->assertInstanceOf( 39 | DoctrineCacheBridge::class, 40 | $container->get($this->getAlias().'.instance.default.bridge') 41 | ); 42 | } 43 | 44 | /** 45 | * 46 | */ 47 | public function testRouterBuilder() 48 | { 49 | $container = $this->createContainerFromFile('router'); 50 | 51 | $config = $container->getParameter($this->getAlias().'.router'); 52 | 53 | $this->assertTrue(isset($config['enabled'])); 54 | $this->assertTrue(isset($config['instance'])); 55 | 56 | $this->assertTrue($config['enabled']); 57 | $this->assertEquals($config['instance'], 'default'); 58 | 59 | $this->assertEquals('Aequasi\Bundle\CacheBundle\Routing\Router', $container->getParameter('router.class')); 60 | } 61 | 62 | /** 63 | * @return string 64 | */ 65 | private function getAlias() 66 | { 67 | return 'aequasi_cache'; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /tests/Fixtures/router.yml: -------------------------------------------------------------------------------- 1 | aequasi_cache: 2 | instances: 3 | default: 4 | type: array 5 | router: 6 | enabled: true 7 | instance: default -------------------------------------------------------------------------------- /tests/Fixtures/service.yml: -------------------------------------------------------------------------------- 1 | aequasi_cache: 2 | instances: 3 | default: 4 | type: array -------------------------------------------------------------------------------- /tests/TestCase.php: -------------------------------------------------------------------------------- 1 | 5 | * @date 2013 6 | * @license http://www.apache.org/licenses/LICENSE-2.0.html Apache License, Version 2.0 7 | */ 8 | 9 | namespace Aequasi\Bundle\CacheBundle\Tests; 10 | 11 | use Aequasi\Bundle\CacheBundle\DependencyInjection\AequasiCacheExtension; 12 | use Symfony\Component\DependencyInjection\ContainerBuilder; 13 | use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; 14 | use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; 15 | use Symfony\Component\Config\FileLocator; 16 | 17 | /** 18 | * Class TestCase 19 | * 20 | * @author Aaron Scherer 21 | */ 22 | class TestCase extends \PHPUnit_Framework_TestCase 23 | { 24 | 25 | /** 26 | * @param ContainerBuilder $container 27 | * @param string $file 28 | */ 29 | protected function loadFromFile(ContainerBuilder $container, $file) 30 | { 31 | $loader = new YamlFileLoader($container, new FileLocator(__DIR__ . '/Fixtures')); 32 | $loader->load($file . '.yml'); 33 | } 34 | 35 | /** 36 | * @param array $data 37 | * 38 | * @return ContainerBuilder 39 | */ 40 | protected function createContainer(array $data = array()) 41 | { 42 | return new ContainerBuilder(new ParameterBag(array_merge( 43 | array( 44 | 'kernel.bundles' => array('FrameworkBundle' => 'Symfony\\Bundle\\FrameworkBundle\\FrameworkBundle'), 45 | 'kernel.cache_dir' => __DIR__, 46 | 'kernel.debug' => false, 47 | 'kernel.environment' => 'test', 48 | 'kernel.name' => 'kernel', 49 | 'kernel.root_dir' => __DIR__, 50 | ), 51 | $data 52 | ))); 53 | } 54 | 55 | /** 56 | * @param string $file 57 | * @param array $data 58 | * 59 | * @return ContainerBuilder 60 | */ 61 | protected function createContainerFromFile($file, $data = array()) 62 | { 63 | $container = $this->createContainer($data); 64 | $container->registerExtension(new AequasiCacheExtension()); 65 | $this->loadFromFile($container, $file); 66 | 67 | $container->getCompilerPassConfig() 68 | ->setOptimizationPasses(array()); 69 | $container->getCompilerPassConfig() 70 | ->setRemovingPasses(array()); 71 | $container->compile(); 72 | 73 | return $container; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /tests/bootstrap.php: -------------------------------------------------------------------------------- 1 |