├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── composer.json ├── lib └── Asm89 │ └── Twig │ └── CacheExtension │ ├── CacheProvider │ ├── DoctrineCacheAdapter.php │ └── PsrCacheAdapter.php │ ├── CacheProviderInterface.php │ ├── CacheStrategy │ ├── BlackholeCacheStrategy.php │ ├── GenerationalCacheStrategy.php │ ├── IndexedChainingCacheStrategy.php │ ├── KeyGeneratorInterface.php │ └── LifetimeCacheStrategy.php │ ├── CacheStrategyInterface.php │ ├── Exception │ ├── BaseException.php │ ├── InvalidCacheKeyException.php │ ├── InvalidCacheLifetimeException.php │ ├── NonExistingStrategyException.php │ └── NonExistingStrategyKeyException.php │ ├── Extension.php │ ├── Node │ └── CacheNode.php │ └── TokenParser │ └── Cache.php ├── phpunit.xml.dist └── test ├── Asm89 └── Twig │ └── CacheExtension │ └── Tests │ ├── CacheProvider │ └── DoctrineCacheAdapterTest.php │ ├── CacheStrategy │ ├── GenerationCacheStrategyTest.php │ ├── IndexedChainingCacheStrategyTest.php │ └── LifetimeCacheStrategyTest.php │ ├── FunctionalExtensionTest.php │ └── fixtures │ ├── annotation_expression.twig │ ├── annotation_not_string.twig │ ├── gcs_value.twig │ ├── gcs_value_v2.twig │ ├── ics_no_key.twig │ ├── ics_value.twig │ └── lcs_value.twig └── bootstrap.php /.gitignore: -------------------------------------------------------------------------------- 1 | phpunit.xml 2 | composer.lock 3 | composer.phar 4 | /vendor/ 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | matrix: 4 | include: 5 | - php: 5.6 6 | dist: xenial 7 | env: 'COMPOSER_FLAGS="--prefer-stable --prefer-lowest" TWIG_VERSION="^1.0"' 8 | - php: 5.6 9 | dist: xenial 10 | env: TWIG_VERSION="^1.0" 11 | - php: 7.0 12 | dist: xenial 13 | env: TWIG_VERSION="^1.0" 14 | - php: 7.1 15 | dist: bionic 16 | env: TWIG_VERSION="^1.0" 17 | - php: 7.2 18 | dist: bionic 19 | env: TWIG_VERSION="^1.0" 20 | - php: 7.3 21 | dist: bionic 22 | env: TWIG_VERSION="^1.0" 23 | - php: 7.4 24 | dist: bionic 25 | env: TWIG_VERSION="^1.0" 26 | - php: 7.0 27 | dist: xenial 28 | env: TWIG_VERSION="^2.0" 29 | - php: 7.1 30 | dist: bionic 31 | env: TWIG_VERSION="^2.0" 32 | - php: 7.2 33 | dist: bionic 34 | env: TWIG_VERSION="^2.0" 35 | - php: 7.3 36 | dist: bionic 37 | env: TWIG_VERSION="^2.0" 38 | - php: 7.4 39 | dist: bionic 40 | env: TWIG_VERSION="^2.0" 41 | 42 | install: composer require twig/twig:${TWIG_VERSION} 43 | 44 | script: composer test 45 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Alexander 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is furnished 8 | to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Twig cache extension 2 | ==================== 3 | 4 | **This extension moved to the Twig organization. Check out the repository over 5 | there instead: https://github.com/twigphp/twig-cache-extension.** 6 | 7 | The missing cache extension for Twig. The extension allows for caching rendered parts of 8 | templates using several cache strategies. 9 | 10 | [![Build Status](https://secure.travis-ci.org/asm89/twig-cache-extension.png?branch=master)](http://travis-ci.org/asm89/twig-cache-extension) 11 | 12 | ## Installation 13 | 14 | The extension is installable via composer: 15 | 16 | ```json 17 | { 18 | "require": { 19 | "asm89/twig-cache-extension": "~1.0" 20 | } 21 | } 22 | ``` 23 | 24 | ## Quick start 25 | 26 | ### Setup 27 | 28 | A minimal setup for adding the extension with the `LifeTimeCacheStrategy` and 29 | doctrine array cache is as following: 30 | 31 | ```php 32 | addExtension($cacheExtension); 44 | ``` 45 | 46 | ### Want to use a PSR-6 cache pool? 47 | 48 | Instead of using the default `DoctrineCacheAdapter` the extension also has 49 | a `PSR-6` compatible adapter. You need to instantiate one of the cache pool 50 | implementations as can be found on: http://php-cache.readthedocs.io/en/latest/ 51 | 52 | Example: Making use of the `ApcuCachePool` via the `PsrCacheAdapter`: 53 | 54 | ```bash 55 | composer require cache/apcu-adapter 56 | ``` 57 | 58 | ```php 59 | addExtension($cacheExtension); 71 | ``` 72 | 73 | ### Usage 74 | 75 | To cache a part of a template in Twig surround the code with a `cache` block. 76 | The cache block takes two parameters, first an "annotation" part, second the 77 | "value" the cache strategy can work with. Example: 78 | 79 | ```jinja 80 | {% cache 'v1/summary' 900 %} 81 | {# heavy lifting template stuff here, include/render other partials etc #} 82 | {% endcache %} 83 | ``` 84 | 85 | Cache blocks can be nested: 86 | 87 | ```jinja 88 | {% cache 'v1' 900 %} 89 | {% for item in items %} 90 | {% cache 'v1' item %} 91 | {# ... #} 92 | {% endcache %} 93 | {% endfor %} 94 | {% endcache %} 95 | ``` 96 | 97 | The annotation can also be an expression: 98 | 99 | ```jinja 100 | {% set version = 42 %} 101 | {% cache 'hello_v' ~ version 900 %} 102 | Hello {{ name }}! 103 | {% endcache %} 104 | ``` 105 | 106 | ## Cache strategies 107 | 108 | The extension ships with a few cache strategies out of the box. Setup and usage 109 | of all of them is described below. 110 | 111 | ### Lifetime 112 | 113 | See the ["Quick start"](#quick-start) for usage information of the `LifetimeCacheStrategy`. 114 | 115 | ### Generational 116 | 117 | Strategy for generational caching. 118 | 119 | In theory the strategy only saves fragments to the cache with infinite 120 | lifetime. The key of the strategy lies in the fact that the keys for blocks 121 | will change as the value for which the key is generated changes. 122 | 123 | For example: entities containing a last update time, would include a timestamp 124 | in the key. For an interesting blog post about this type of caching see: 125 | http://37signals.com/svn/posts/3113-how-key-based-cache-expiration-works 126 | 127 | ### Blackhole 128 | 129 | Strategy for development mode. 130 | 131 | In development mode it often not very useful to cache fragments. The blackhole 132 | strategy provides an easy way to not cache anything it all. It always generates 133 | a new key and does not fetch or save any fragments. 134 | 135 | #### Setup 136 | 137 | In order to use the strategy you need to setup a `KeyGenerator` class that is 138 | able to generate a cache key for a given value. 139 | 140 | The following naive example always assumes the value is an object with the methods 141 | `getId()` and `getUpdatedAt()` method. The key then composed from the class 142 | name, the id and the updated time of the object: 143 | 144 | ```php 145 | getId() . '_' . $value->getUpdatedAt(); 154 | } 155 | 156 | } 157 | ``` 158 | 159 | Next the `GenerationalCacheStrategy` needs to be setup with the keygenerator. 160 | 161 | ```php 162 | addExtension($cacheExtension); 173 | ``` 174 | 175 | #### Usage 176 | 177 | The strategy expects an object as value for determining the cache key of the 178 | block: 179 | 180 | ```jinja 181 | {% cache 'v1/summary' item %} 182 | {# heavy lifting template stuff here, include/render other partials etc #} 183 | {% endcache %} 184 | ``` 185 | 186 | ### Using multiple strategies 187 | 188 | Different cache strategies are useful for different usecases. It is possible to 189 | mix multiple strategies in an application with the 190 | `IndexedChainingCacheStrategy`. The strategy takes an array of `'name' => 191 | $strategy` and delegates the caching to the appropriate strategy. 192 | 193 | #### Setup 194 | 195 | ```php 196 | $lifetimeCacheStrategy, 203 | 'gen' => $generationalCacheStrategy, 204 | )); 205 | $cacheExtension = new CacheExtension($cacheStrategy); 206 | 207 | $twig->addExtension($cacheExtension); 208 | ``` 209 | 210 | #### Usage 211 | 212 | The strategy expects an array with as key the name of the strategy which it 213 | needs to delegate to and as value the appropriate value for the delegated 214 | strategy. 215 | 216 | ```jinja 217 | {# delegate to lifetime strategy #} 218 | {% cache 'v1/summary' {time: 300} %} 219 | {# heavy lifting template stuff here, include/render other partials etc #} 220 | {% endcache %} 221 | 222 | {# delegate to generational strategy #} 223 | {% cache 'v1/summary' {gen: item} %} 224 | {# heavy lifting template stuff here, include/render other partials etc #} 225 | {% endcache %} 226 | ``` 227 | 228 | ## Implementing a cache strategy 229 | 230 | Creating separate caches for different access levels, languages or other 231 | usecases can be done by implementing a custom cache strategy. In order to do so 232 | implement the `CacheProviderInterface`. It is recommended to use composition 233 | and wrap a custom strategy around an existing one. 234 | 235 | ## Authors 236 | 237 | Alexander 238 | 239 | ## License 240 | 241 | twig-cache-extension is licensed under the MIT License - see the LICENSE file for details 242 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "asm89/twig-cache-extension", 3 | "description": "Cache fragments of templates directly within Twig.", 4 | "keywords": ["twig", "cache", "extension"], 5 | "homepage": "https://github.com/asm89/twig-cache-extension", 6 | "type": "library", 7 | "license": "MIT", 8 | "authors": [ 9 | { 10 | "name": "Alexander", 11 | "email": "iam.asm89@gmail.com" 12 | } 13 | ], 14 | "require": { 15 | "php": ">=5.3.2", 16 | "twig/twig": "^1.0|^2.0" 17 | }, 18 | "require-dev": { 19 | "phpunit/phpunit": "^5.0 || ^4.8.10", 20 | "doctrine/cache": "~1.0" 21 | }, 22 | "scripts": { 23 | "test": "phpunit" 24 | }, 25 | "suggest": { 26 | "psr/cache-implementation": "To make use of PSR-6 cache implementation via PsrCacheAdapter." 27 | }, 28 | "autoload": { 29 | "psr-4": { 30 | "": "lib/" 31 | } 32 | }, 33 | "autoload-dev": { 34 | "psr-4": { 35 | "": "test/" 36 | } 37 | }, 38 | "extra": { 39 | "branch-alias": { 40 | "dev-master": "1.4-dev" 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lib/Asm89/Twig/CacheExtension/CacheProvider/DoctrineCacheAdapter.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Asm89\Twig\CacheExtension\CacheProvider; 13 | 14 | use Asm89\Twig\CacheExtension\CacheProviderInterface; 15 | use Doctrine\Common\Cache\Cache; 16 | 17 | /** 18 | * Adapter class to use the cache classes provider by Doctrine. 19 | * 20 | * @author Alexander 21 | */ 22 | class DoctrineCacheAdapter implements CacheProviderInterface 23 | { 24 | private $cache; 25 | 26 | /** 27 | * @param Cache $cache 28 | */ 29 | public function __construct(Cache $cache) 30 | { 31 | $this->cache = $cache; 32 | } 33 | 34 | /** 35 | * {@inheritDoc} 36 | */ 37 | public function fetch($key) 38 | { 39 | return $this->cache->fetch($key); 40 | } 41 | 42 | /** 43 | * {@inheritDoc} 44 | */ 45 | public function save($key, $value, $lifetime = 0) 46 | { 47 | return $this->cache->save($key, $value, $lifetime); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /lib/Asm89/Twig/CacheExtension/CacheProvider/PsrCacheAdapter.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Asm89\Twig\CacheExtension\CacheProvider; 13 | 14 | use Asm89\Twig\CacheExtension\CacheProviderInterface; 15 | use Psr\Cache\CacheItemPoolInterface; 16 | 17 | /** 18 | * Adapter class to make extension interoperable with every PSR-6 adapter. 19 | * 20 | * @see http://php-cache.readthedocs.io/ 21 | * 22 | * @author Rvanlaak 23 | */ 24 | class PsrCacheAdapter implements CacheProviderInterface 25 | { 26 | /** 27 | * @var CacheItemPoolInterface 28 | */ 29 | private $cache; 30 | 31 | /** 32 | * @param CacheItemPoolInterface $cache 33 | */ 34 | public function __construct(CacheItemPoolInterface $cache) 35 | { 36 | $this->cache = $cache; 37 | } 38 | 39 | /** 40 | * @param string $key 41 | * @return mixed|false 42 | */ 43 | public function fetch($key) 44 | { 45 | // PSR-6 implementation returns null, CacheProviderInterface expects false 46 | $item = $this->cache->getItem($key); 47 | if ($item->isHit()) { 48 | return $item->get(); 49 | } 50 | return false; 51 | } 52 | 53 | /** 54 | * @param string $key 55 | * @param string $value 56 | * @param int|\DateInterval $lifetime 57 | * @return bool 58 | */ 59 | public function save($key, $value, $lifetime = 0) 60 | { 61 | $item = $this->cache->getItem($key); 62 | $item->set($value); 63 | $item->expiresAfter($lifetime); 64 | 65 | return $this->cache->save($item); 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /lib/Asm89/Twig/CacheExtension/CacheProviderInterface.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Asm89\Twig\CacheExtension; 13 | 14 | /** 15 | * Cache provider interface. 16 | * 17 | * @author Alexander 18 | */ 19 | interface CacheProviderInterface 20 | { 21 | /** 22 | * @param string $key 23 | * 24 | * @return mixed False, if there was no value to be fetched. Null or a string otherwise. 25 | */ 26 | public function fetch($key); 27 | 28 | /** 29 | * @param string $key 30 | * @param string $value 31 | * @param integer $lifetime 32 | * 33 | * @return boolean 34 | */ 35 | public function save($key, $value, $lifetime = 0); 36 | } 37 | -------------------------------------------------------------------------------- /lib/Asm89/Twig/CacheExtension/CacheStrategy/BlackholeCacheStrategy.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Asm89\Twig\CacheExtension\CacheStrategy; 13 | 14 | use Asm89\Twig\CacheExtension\CacheStrategyInterface; 15 | 16 | /** 17 | * CacheStrategy which doesn't cache at all 18 | * 19 | * This strategy can be used in development mode, e.g. editing twig templates, 20 | * to prevent previously cached versions from being rendered. 21 | * 22 | * @see https://github.com/asm89/twig-cache-extension/pull/29 23 | * 24 | * @author Hagen Hübel 25 | * 26 | * @package Asm89\Twig\CacheExtension\CacheStrategy 27 | */ 28 | class BlackholeCacheStrategy implements CacheStrategyInterface 29 | { 30 | /** 31 | * {@inheritDoc} 32 | */ 33 | public function fetchBlock($key) 34 | { 35 | return false; 36 | } 37 | 38 | /** 39 | * {@inheritDoc} 40 | */ 41 | public function generateKey($annotation, $value) 42 | { 43 | return microtime(true) . mt_rand(); 44 | } 45 | 46 | /** 47 | * {@inheritDoc} 48 | */ 49 | public function saveBlock($key, $block) 50 | { 51 | // fire and forget 52 | } 53 | } -------------------------------------------------------------------------------- /lib/Asm89/Twig/CacheExtension/CacheStrategy/GenerationalCacheStrategy.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Asm89\Twig\CacheExtension\CacheStrategy; 13 | 14 | use Asm89\Twig\CacheExtension\CacheProviderInterface; 15 | use Asm89\Twig\CacheExtension\CacheStrategyInterface; 16 | use Asm89\Twig\CacheExtension\Exception\InvalidCacheKeyException; 17 | 18 | /** 19 | * Strategy for generational caching. 20 | * 21 | * In theory the strategy only saves fragments to the cache with infinite 22 | * lifetime. The key of the strategy lies in the fact that the keys for blocks 23 | * will change as the value for which the key is generated changes. 24 | * 25 | * For example: entities containing a last update time, would include a 26 | * timestamp in the key. 27 | * 28 | * @see http://37signals.com/svn/posts/3113-how-key-based-cache-expiration-works 29 | * 30 | * @author Alexander 31 | */ 32 | class GenerationalCacheStrategy implements CacheStrategyInterface 33 | { 34 | private $keyGenerator; 35 | private $cache; 36 | private $lifetime; 37 | 38 | /** 39 | * @param CacheProviderInterface $cache 40 | * @param KeyGeneratorInterface $keyGenerator 41 | * @param integer $lifetime 42 | */ 43 | public function __construct(CacheProviderInterface $cache, KeyGeneratorInterface $keyGenerator, $lifetime = 0) 44 | { 45 | $this->keyGenerator = $keyGenerator; 46 | $this->cache = $cache; 47 | $this->lifetime = $lifetime; 48 | } 49 | 50 | /** 51 | * {@inheritDoc} 52 | */ 53 | public function fetchBlock($key) 54 | { 55 | return $this->cache->fetch($key); 56 | } 57 | 58 | /** 59 | * {@inheritDoc} 60 | */ 61 | public function generateKey($annotation, $value) 62 | { 63 | $key = $this->keyGenerator->generateKey($value); 64 | 65 | if (null === $key) { 66 | throw new InvalidCacheKeyException(); 67 | } 68 | 69 | return $annotation . '__GCS__' . $key; 70 | } 71 | 72 | /** 73 | * {@inheritDoc} 74 | */ 75 | public function saveBlock($key, $block) 76 | { 77 | return $this->cache->save($key, $block, $this->lifetime); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /lib/Asm89/Twig/CacheExtension/CacheStrategy/IndexedChainingCacheStrategy.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Asm89\Twig\CacheExtension\CacheStrategy; 13 | 14 | use Asm89\Twig\CacheExtension\CacheStrategyInterface; 15 | use Asm89\Twig\CacheExtension\Exception\NonExistingStrategyException; 16 | use Asm89\Twig\CacheExtension\Exception\NonExistingStrategyKeyException; 17 | 18 | /** 19 | * Combines several configured cache strategies. 20 | * 21 | * Useful for combining for example generational cache strategy with a lifetime 22 | * cache strategy, but also useful when combining several generational cache 23 | * strategies which differ on cache lifetime (infinite, 1hr, 5m). 24 | * 25 | * @author Alexander 26 | */ 27 | class IndexedChainingCacheStrategy implements CacheStrategyInterface 28 | { 29 | /** 30 | * @var CacheStrategyInterface[] 31 | */ 32 | private $strategies; 33 | 34 | /** 35 | * @param array $strategies 36 | */ 37 | public function __construct(array $strategies) 38 | { 39 | $this->strategies = $strategies; 40 | } 41 | 42 | /** 43 | * {@inheritDoc} 44 | */ 45 | public function fetchBlock($key) 46 | { 47 | return $this->strategies[$key['strategyKey']]->fetchBlock($key['key']); 48 | } 49 | 50 | /** 51 | * {@inheritDoc} 52 | */ 53 | public function generateKey($annotation, $value) 54 | { 55 | if (!is_array($value) || null === $strategyKey = key($value)) { 56 | throw new NonExistingStrategyKeyException(); 57 | } 58 | 59 | if (!isset($this->strategies[$strategyKey])) { 60 | throw new NonExistingStrategyException($strategyKey); 61 | } 62 | 63 | return array( 64 | 'strategyKey' => $strategyKey, 65 | 'key' => $this->strategies[$strategyKey]->generateKey($annotation, current($value)), 66 | ); 67 | } 68 | 69 | /** 70 | * {@inheritDoc} 71 | */ 72 | public function saveBlock($key, $block) 73 | { 74 | return $this->strategies[$key['strategyKey']]->saveBlock($key['key'], $block); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /lib/Asm89/Twig/CacheExtension/CacheStrategy/KeyGeneratorInterface.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Asm89\Twig\CacheExtension\CacheStrategy; 13 | 14 | /** 15 | * Generates a key for a given value. 16 | * 17 | * @author Alexander 18 | */ 19 | interface KeyGeneratorInterface 20 | { 21 | /** 22 | * Generate a cache key for a given value. 23 | * 24 | * @param mixed $value 25 | * 26 | * @return string 27 | */ 28 | public function generateKey($value); 29 | } 30 | -------------------------------------------------------------------------------- /lib/Asm89/Twig/CacheExtension/CacheStrategy/LifetimeCacheStrategy.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Asm89\Twig\CacheExtension\CacheStrategy; 13 | 14 | use Asm89\Twig\CacheExtension\CacheProviderInterface; 15 | use Asm89\Twig\CacheExtension\CacheStrategyInterface; 16 | use Asm89\Twig\CacheExtension\Exception\InvalidCacheLifetimeException; 17 | 18 | /** 19 | * Strategy for caching with a pre-defined lifetime. 20 | * 21 | * The value passed to the strategy is the lifetime of the cache block in 22 | * seconds. 23 | * 24 | * @author Alexander 25 | */ 26 | class LifetimeCacheStrategy implements CacheStrategyInterface 27 | { 28 | private $cache; 29 | 30 | /** 31 | * @param CacheProviderInterface $cache 32 | */ 33 | public function __construct(CacheProviderInterface $cache) 34 | { 35 | $this->cache = $cache; 36 | } 37 | 38 | /** 39 | * {@inheritDoc} 40 | */ 41 | public function fetchBlock($key) 42 | { 43 | return $this->cache->fetch($key['key']); 44 | } 45 | 46 | /** 47 | * {@inheritDoc} 48 | */ 49 | public function generateKey($annotation, $value) 50 | { 51 | if (!is_numeric($value)) { 52 | throw new InvalidCacheLifetimeException($value); 53 | } 54 | 55 | return array( 56 | 'lifetime' => $value, 57 | 'key' => '__LCS__' . $annotation, 58 | ); 59 | } 60 | 61 | /** 62 | * {@inheritDoc} 63 | */ 64 | public function saveBlock($key, $block) 65 | { 66 | return $this->cache->save($key['key'], $block, $key['lifetime']); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /lib/Asm89/Twig/CacheExtension/CacheStrategyInterface.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Asm89\Twig\CacheExtension; 13 | 14 | /** 15 | * Cache strategy interface. 16 | * 17 | * @author Alexander 18 | */ 19 | interface CacheStrategyInterface 20 | { 21 | /** 22 | * Fetch the block for a given key. 23 | * 24 | * @param mixed $key 25 | * 26 | * @return mixed 27 | */ 28 | public function fetchBlock($key); 29 | 30 | /** 31 | * Generate a key for the value. 32 | * 33 | * @param string $annotation 34 | * @param mixed $value 35 | * 36 | * @return mixed 37 | */ 38 | public function generateKey($annotation, $value); 39 | 40 | /** 41 | * Save the contents of a rendered block. 42 | * 43 | * @param mixed $key 44 | * @param string $block 45 | * 46 | * @return mixed 47 | */ 48 | public function saveBlock($key, $block); 49 | } 50 | -------------------------------------------------------------------------------- /lib/Asm89/Twig/CacheExtension/Exception/BaseException.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Asm89\Twig\CacheExtension\Exception; 13 | 14 | /** 15 | * @todo Replace \RuntimeException with \InvalidArgumentException at version 2.0 16 | */ 17 | class BaseException extends \RuntimeException 18 | { 19 | 20 | } 21 | -------------------------------------------------------------------------------- /lib/Asm89/Twig/CacheExtension/Exception/InvalidCacheKeyException.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Asm89\Twig\CacheExtension\Exception; 13 | 14 | class InvalidCacheKeyException extends BaseException 15 | { 16 | /** 17 | * {@inheritdoc} 18 | */ 19 | public function __construct($message = 'Key generator did not return a key.', $code = 0, \Exception $previous = null) 20 | { 21 | parent::__construct($message, $code, $previous); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lib/Asm89/Twig/CacheExtension/Exception/InvalidCacheLifetimeException.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Asm89\Twig\CacheExtension\Exception; 13 | 14 | class InvalidCacheLifetimeException extends BaseException 15 | { 16 | /** 17 | * {@inheritdoc} 18 | */ 19 | public function __construct($lifetime, $code = 0, \Exception $previous = null) 20 | { 21 | parent::__construct(sprintf('Value "%s" is not a valid lifetime.', gettype($lifetime)), $code, $previous); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lib/Asm89/Twig/CacheExtension/Exception/NonExistingStrategyException.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Asm89\Twig\CacheExtension\Exception; 13 | 14 | class NonExistingStrategyException extends BaseException 15 | { 16 | /** 17 | * {@inheritdoc} 18 | */ 19 | public function __construct($strategyKey, $code = 0, \Exception $previous = null) 20 | { 21 | parent::__construct(sprintf('No strategy configured with key "%s".', $strategyKey), $code, $previous); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lib/Asm89/Twig/CacheExtension/Exception/NonExistingStrategyKeyException.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Asm89\Twig\CacheExtension\Exception; 13 | 14 | class NonExistingStrategyKeyException extends BaseException 15 | { 16 | /** 17 | * {@inheritdoc} 18 | */ 19 | public function __construct($message = 'No strategy key found in value.', $code = 0, \Exception $previous = null) 20 | { 21 | parent::__construct($message, $code, $previous); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lib/Asm89/Twig/CacheExtension/Extension.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Asm89\Twig\CacheExtension; 13 | 14 | /** 15 | * Extension for caching template blocks with twig. 16 | * 17 | * @author Alexander 18 | */ 19 | class Extension extends \Twig_Extension 20 | { 21 | private $cacheStrategy; 22 | 23 | /** 24 | * @param CacheStrategyInterface $cacheStrategy 25 | */ 26 | public function __construct(CacheStrategyInterface $cacheStrategy) 27 | { 28 | $this->cacheStrategy = $cacheStrategy; 29 | } 30 | 31 | /** 32 | * @return CacheStrategyInterface 33 | */ 34 | public function getCacheStrategy() 35 | { 36 | return $this->cacheStrategy; 37 | } 38 | 39 | /** 40 | * {@inheritDoc} 41 | */ 42 | public function getName() 43 | { 44 | if (version_compare(\Twig_Environment::VERSION, '1.26.0', '>=')) { 45 | return __CLASS__; 46 | } 47 | return 'asm89_cache'; 48 | } 49 | 50 | /** 51 | * {@inheritDoc} 52 | */ 53 | public function getTokenParsers() 54 | { 55 | return array( 56 | new TokenParser\Cache(), 57 | ); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /lib/Asm89/Twig/CacheExtension/Node/CacheNode.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Asm89\Twig\CacheExtension\Node; 13 | 14 | /** 15 | * Cache twig node. 16 | * 17 | * @author Alexander 18 | */ 19 | class CacheNode extends \Twig_Node 20 | { 21 | private static $cacheCount = 1; 22 | 23 | /** 24 | * @param \Twig_Node_Expression $annotation 25 | * @param \Twig_Node_Expression $keyInfo 26 | * @param \Twig_NodeInterface $body 27 | * @param integer $lineno 28 | * @param string $tag 29 | */ 30 | public function __construct(\Twig_Node_Expression $annotation, \Twig_Node_Expression $keyInfo, \Twig_Node $body, $lineno, $tag = null) 31 | { 32 | parent::__construct(array('key_info' => $keyInfo, 'body' => $body, 'annotation' => $annotation), array(), $lineno, $tag); 33 | } 34 | 35 | /** 36 | * {@inheritDoc} 37 | */ 38 | public function compile(\Twig_Compiler $compiler) 39 | { 40 | $i = self::$cacheCount++; 41 | 42 | if (version_compare(\Twig_Environment::VERSION, '1.26.0', '>=')) { 43 | $extension = 'Asm89\Twig\CacheExtension\Extension'; 44 | } else { 45 | $extension = 'asm89_cache'; 46 | } 47 | 48 | $compiler 49 | ->addDebugInfo($this) 50 | ->write("\$asm89CacheStrategy".$i." = \$this->env->getExtension('{$extension}')->getCacheStrategy();\n") 51 | ->write("\$asm89Key".$i." = \$asm89CacheStrategy".$i."->generateKey(") 52 | ->subcompile($this->getNode('annotation')) 53 | ->raw(", ") 54 | ->subcompile($this->getNode('key_info')) 55 | ->write(");\n") 56 | ->write("\$asm89CacheBody".$i." = \$asm89CacheStrategy".$i."->fetchBlock(\$asm89Key".$i.");\n") 57 | ->write("if (\$asm89CacheBody".$i." === false) {\n") 58 | ->indent() 59 | ->write("ob_start();\n") 60 | ->indent() 61 | ->subcompile($this->getNode('body')) 62 | ->outdent() 63 | ->write("\n") 64 | ->write("\$asm89CacheBody".$i." = ob_get_clean();\n") 65 | ->write("\$asm89CacheStrategy".$i."->saveBlock(\$asm89Key".$i.", \$asm89CacheBody".$i.");\n") 66 | ->outdent() 67 | ->write("}\n") 68 | ->write("echo \$asm89CacheBody".$i.";\n") 69 | ; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /lib/Asm89/Twig/CacheExtension/TokenParser/Cache.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Asm89\Twig\CacheExtension\TokenParser; 13 | 14 | use Asm89\Twig\CacheExtension\Node\CacheNode; 15 | 16 | /** 17 | * Parser for cache/endcache blocks. 18 | * 19 | * @author Alexander 20 | */ 21 | class Cache extends \Twig_TokenParser 22 | { 23 | /** 24 | * @param \Twig_Token $token 25 | * 26 | * @return boolean 27 | */ 28 | public function decideCacheEnd(\Twig_Token $token) 29 | { 30 | return $token->test('endcache'); 31 | } 32 | 33 | /** 34 | * {@inheritDoc} 35 | */ 36 | public function getTag() 37 | { 38 | return 'cache'; 39 | } 40 | 41 | /** 42 | * {@inheritDoc} 43 | */ 44 | public function parse(\Twig_Token $token) 45 | { 46 | $lineno = $token->getLine(); 47 | $stream = $this->parser->getStream(); 48 | 49 | $annotation = $this->parser->getExpressionParser()->parseExpression(); 50 | 51 | $key = $this->parser->getExpressionParser()->parseExpression(); 52 | 53 | $stream->expect(\Twig_Token::BLOCK_END_TYPE); 54 | $body = $this->parser->subparse(array($this, 'decideCacheEnd'), true); 55 | $stream->expect(\Twig_Token::BLOCK_END_TYPE); 56 | 57 | return new CacheNode($annotation, $key, $body, $lineno, $this->getTag()); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 14 | 15 | 16 | ./test/Asm89/ 17 | 18 | 19 | 20 | 21 | 22 | ./lib/ 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /test/Asm89/Twig/CacheExtension/Tests/CacheProvider/DoctrineCacheAdapterTest.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Asm89\Twig\CacheExtension\Tests\CacheProvider; 13 | 14 | use Asm89\Twig\CacheExtension\CacheProvider\DoctrineCacheAdapter; 15 | 16 | class DoctrineCacheAdapterTest extends \PHPUnit_Framework_TestCase 17 | { 18 | public function testFetch() 19 | { 20 | $cacheMock = $this->createCacheMock(); 21 | $cacheMock->expects($this->any()) 22 | ->method('fetch') 23 | ->will($this->returnValue('fromcache')); 24 | 25 | $cache = new DoctrineCacheAdapter($cacheMock); 26 | 27 | $this->assertEquals('fromcache', $cache->fetch('test')); 28 | } 29 | 30 | public function testSave() 31 | { 32 | $cacheMock = $this->createCacheMock(); 33 | $cacheMock->expects($this->once()) 34 | ->method('save') 35 | ->with('key', 'value', 42); 36 | 37 | $cache = new DoctrineCacheAdapter($cacheMock); 38 | 39 | $cache->save('key', 'value', 42); 40 | } 41 | 42 | public function createCacheMock() 43 | { 44 | return $this->createMock('Doctrine\Common\Cache\Cache'); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /test/Asm89/Twig/CacheExtension/Tests/CacheStrategy/GenerationCacheStrategyTest.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Asm89\Twig\CacheExtension\Tests\CacheStrategy; 13 | 14 | use Asm89\Twig\CacheExtension\CacheStrategy\GenerationalCacheStrategy; 15 | 16 | class GenerationalCacheStrategyTest extends \PHPUnit_Framework_TestCase 17 | { 18 | private $keyGeneratorMock; 19 | private $cacheProviderMock; 20 | 21 | public function createCacheStrategy($lifetime = 0) 22 | { 23 | $this->keyGeneratorMock = $this->createKeyGeneratorMock(); 24 | $this->cacheProviderMock = $this->createCacheProviderMock(); 25 | 26 | return new GenerationalCacheStrategy($this->cacheProviderMock, $this->keyGeneratorMock, $lifetime); 27 | } 28 | 29 | public function testGenerateKeyContainsAnnotation() 30 | { 31 | $strategy = $this->createCacheStrategy(); 32 | $this->keyGeneratorMock->expects($this->any()) 33 | ->method('generateKey') 34 | ->will($this->returnValue('foo')); 35 | 36 | $this->assertEquals('v42__GCS__foo', $strategy->generateKey('v42', 'value')); 37 | } 38 | 39 | /** 40 | * @expectedException \Asm89\Twig\CacheExtension\Exception\InvalidCacheKeyException 41 | */ 42 | public function testGenerationKeyThrowsExceptionWhenKeyGeneratorReturnsNull() 43 | { 44 | $strategy = $this->createCacheStrategy(); 45 | 46 | $strategy->generateKey('v42', 'value'); 47 | } 48 | 49 | /** 50 | * @dataProvider getLifeTimes 51 | */ 52 | public function testSaveBlockUsesConfiguredLifetime($lifetime) 53 | { 54 | $strategy = $this->createCacheStrategy($lifetime); 55 | $this->cacheProviderMock->expects($this->any()) 56 | ->method('save') 57 | ->with('key', 'value', $lifetime) 58 | ->will($this->returnValue('foo')); 59 | 60 | $strategy->saveBlock('key', 'value'); 61 | } 62 | 63 | public function getLifetimes() 64 | { 65 | return array( 66 | array(0), 67 | array(60), 68 | ); 69 | } 70 | 71 | public function createKeyGeneratorMock() 72 | { 73 | return $this->createMock('Asm89\Twig\CacheExtension\CacheStrategy\KeyGeneratorInterface'); 74 | } 75 | 76 | public function createCacheProviderMock() 77 | { 78 | return $this->createMock('Asm89\Twig\CacheExtension\CacheProviderInterface'); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /test/Asm89/Twig/CacheExtension/Tests/CacheStrategy/IndexedChainingCacheStrategyTest.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Asm89\Twig\CacheExtension\Tests\CacheStrategy; 13 | 14 | use Asm89\Twig\CacheExtension\CacheStrategy\IndexedChainingCacheStrategy; 15 | 16 | class IndexedChainingCacheStrategyTest extends \PHPUnit_Framework_TestCase 17 | { 18 | private $cacheStrategyMocks = array(); 19 | 20 | public function createCacheStrategy() 21 | { 22 | foreach($this->getStrategies() as $config) { 23 | list($key, $return) = $config; 24 | 25 | $cacheStrategyMock = $this->createCacheStrategyMock(); 26 | $cacheStrategyMock->expects($this->any()) 27 | ->method('generateKey') 28 | ->will($this->returnValue($return)); 29 | 30 | $this->cacheStrategyMocks[$key] = $cacheStrategyMock; 31 | } 32 | 33 | return new IndexedChainingCacheStrategy($this->cacheStrategyMocks); 34 | } 35 | 36 | /** 37 | * @dataProvider getStrategies 38 | */ 39 | public function testGenerateKeyProxiesToAppropriateStrategy($key, $return) 40 | { 41 | $strategy = $this->createCacheStrategy(); 42 | 43 | $generatedKey = $strategy->generateKey('v42', array($key => 'proxied_value')); 44 | 45 | $this->assertEquals($return, $generatedKey['key']); 46 | $this->assertEquals($key, $generatedKey['strategyKey']); 47 | } 48 | 49 | /** 50 | * @expectedException \Asm89\Twig\CacheExtension\Exception\NonExistingStrategyKeyException 51 | */ 52 | public function testGenerateKeyThrowsExceptionOnMissingKey() 53 | { 54 | $strategy = $this->createCacheStrategy(); 55 | $strategy->generateKey('v42', 'proxied_value'); 56 | } 57 | 58 | /** 59 | * @expectedException \Asm89\Twig\CacheExtension\Exception\NonExistingStrategyException 60 | * @expectedExceptionMessage No strategy configured with key "unknown" 61 | */ 62 | public function testGenerateKeyThrowsExceptionOnUnknownKey() 63 | { 64 | $strategy = $this->createCacheStrategy(); 65 | $strategy->generateKey('v42', array('unknown' => 'proxied_value')); 66 | } 67 | 68 | public function getStrategies() 69 | { 70 | return array( 71 | array('foo', 'foo_key'), 72 | array('bar', 'bar_key'), 73 | ); 74 | } 75 | 76 | public function createCacheStrategyMock() 77 | { 78 | return $this->createMock('Asm89\Twig\CacheExtension\CacheStrategyInterface'); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /test/Asm89/Twig/CacheExtension/Tests/CacheStrategy/LifetimeCacheStrategyTest.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Asm89\Twig\CacheExtension\Tests\CacheStrategy; 13 | 14 | use Asm89\Twig\CacheExtension\CacheStrategy\LifetimeCacheStrategy; 15 | 16 | class LifetimeCacheStrategyTest extends \PHPUnit_Framework_TestCase 17 | { 18 | private $cacheProviderMock; 19 | 20 | public function createCacheStrategy() 21 | { 22 | $this->cacheProviderMock = $this->createCacheProviderMock(); 23 | 24 | return new LifetimeCacheStrategy($this->cacheProviderMock); 25 | } 26 | 27 | public function testGenerateKeyUsesGivenLifetime() 28 | { 29 | $strategy = $this->createCacheStrategy(); 30 | 31 | $key = $strategy->generateKey('v42', 42); 32 | 33 | $this->assertEquals(42, $key['lifetime']); 34 | } 35 | 36 | public function testGenerateKeyAnnotatesKey() 37 | { 38 | $strategy = $this->createCacheStrategy(); 39 | 40 | $key = $strategy->generateKey('the_annotation', 42); 41 | 42 | $this->assertContains('the_annotation', $key['key']); 43 | } 44 | 45 | /** 46 | * @dataProvider getInvalidLifetimeValues 47 | * @expectedException \Asm89\Twig\CacheExtension\Exception\InvalidCacheLifetimeException 48 | */ 49 | public function testGenerateKeyThrowsExceptionWhenNoLifetimeProvided($value) 50 | { 51 | $strategy = $this->createCacheStrategy(); 52 | 53 | $strategy->generateKey('v42', $value); 54 | } 55 | 56 | public function getInvalidLifetimeValues() 57 | { 58 | return array( 59 | array('foo'), 60 | array(array('foo')), 61 | ); 62 | } 63 | 64 | public function createCacheProviderMock() 65 | { 66 | return $this->createMock('Asm89\Twig\CacheExtension\CacheProviderInterface'); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /test/Asm89/Twig/CacheExtension/Tests/FunctionalExtensionTest.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Asm89\Twig\CacheExtension\Tests; 13 | 14 | use Asm89\Twig\CacheExtension\CacheProvider\DoctrineCacheAdapter; 15 | use Asm89\Twig\CacheExtension\CacheStrategy\KeyGeneratorInterface; 16 | use Asm89\Twig\CacheExtension\CacheStrategy\GenerationalCacheStrategy; 17 | use Asm89\Twig\CacheExtension\CacheStrategy\IndexedChainingCacheStrategy; 18 | use Asm89\Twig\CacheExtension\CacheStrategy\LifetimeCacheStrategy; 19 | use Asm89\Twig\CacheExtension\Extension; 20 | use Doctrine\Common\Cache\ArrayCache; 21 | use Twig_Loader_Filesystem; 22 | use Twig_Environment; 23 | 24 | class FunctionalExtensionTest extends \PHPUnit_Framework_TestCase 25 | { 26 | private $cache; 27 | 28 | protected function createCacheProvider() 29 | { 30 | $this->cache = new ArrayCache(); 31 | 32 | return new DoctrineCacheAdapter($this->cache); 33 | } 34 | 35 | protected function createCacheStrategy($name = null) 36 | { 37 | $cacheProvider = $this->createCacheProvider(); 38 | $keyGenerator = $this->createKeyGenerator(); 39 | $lifetime = 0; 40 | 41 | switch ($name) { 42 | case 'time': 43 | return new LifetimeCacheStrategy($cacheProvider); 44 | case 'indexed': 45 | return new IndexedChainingCacheStrategy(array( 46 | 'gcs' => new GenerationalCacheStrategy($cacheProvider, $keyGenerator, $lifetime), 47 | 'time' => new LifetimeCacheStrategy($cacheProvider), 48 | )); 49 | default: 50 | return new GenerationalCacheStrategy($cacheProvider, $keyGenerator, $lifetime); 51 | } 52 | } 53 | 54 | protected function createKeyGenerator() 55 | { 56 | return new KeyGenerator(); 57 | } 58 | 59 | protected function createTwig($cacheStrategyName = null) 60 | { 61 | $loader = new Twig_Loader_Filesystem(__DIR__ . '/fixtures/'); 62 | $twig = new Twig_Environment($loader); 63 | 64 | $cacheExtension = new Extension($this->createCacheStrategy($cacheStrategyName)); 65 | 66 | $twig->addExtension($cacheExtension); 67 | 68 | return $twig; 69 | } 70 | 71 | protected function getValue($value, $updatedAt) 72 | { 73 | return new Value($value, $updatedAt); 74 | } 75 | 76 | public function testCachesWithSameCacheKey() 77 | { 78 | $twig = $this->createTwig(); 79 | 80 | $rendered = $twig->render('gcs_value.twig', array('value' => $this->getValue('asm89', 1))); 81 | $this->assertEquals('Hello asm89!', $rendered); 82 | 83 | $rendered2 = $twig->render('gcs_value.twig', array('value' => $this->getValue('fabpot', 1))); 84 | $this->assertEquals('Hello asm89!', $rendered2); 85 | } 86 | 87 | public function testDifferentCacheOnDifferentAnnotation() 88 | { 89 | $twig = $this->createTwig(); 90 | 91 | $rendered = $twig->render('gcs_value.twig', array('value' => $this->getValue('asm89', 1))); 92 | $this->assertEquals('Hello asm89!', $rendered); 93 | 94 | $rendered2 = $twig->render('gcs_value.twig', array('value' => $this->getValue('fabpot', 1))); 95 | $this->assertEquals('Hello asm89!', $rendered2); 96 | 97 | $rendered3 = $twig->render('gcs_value_v2.twig', array('value' => $this->getValue('fabpot', 1))); 98 | $this->assertEquals('Hello fabpot!', $rendered3); 99 | } 100 | 101 | public function testLifetimeCacheStrategy() 102 | { 103 | $twig = $this->createTwig('time'); 104 | 105 | $rendered = $twig->render('lcs_value.twig', array('value' => $this->getValue('asm89', 1))); 106 | $this->assertEquals('Hello asm89!', $rendered); 107 | 108 | $rendered2 = $twig->render('lcs_value.twig', array('value' => $this->getValue('fabpot', 1))); 109 | $this->assertEquals('Hello asm89!', $rendered2); 110 | 111 | $this->cache->flushAll(); 112 | 113 | $rendered3 = $twig->render('lcs_value.twig', array('value' => $this->getValue('fabpot', 1))); 114 | $this->assertEquals('Hello fabpot!', $rendered3); 115 | } 116 | 117 | public function testIndexedChainingStrategy() 118 | { 119 | $twig = $this->createTwig('indexed'); 120 | 121 | $rendered = $twig->render('ics_value.twig', array('value' => $this->getValue('asm89', 1))); 122 | $this->assertEquals('Hello asm89!', $rendered); 123 | 124 | $rendered2 = $twig->render('ics_value.twig', array('value' => $this->getValue('fabpot', 1))); 125 | $this->assertEquals('Hello asm89!', $rendered2); 126 | 127 | $this->cache->flushAll(); 128 | 129 | $rendered3 = $twig->render('ics_value.twig', array('value' => $this->getValue('fabpot', 1))); 130 | $this->assertEquals('Hello fabpot!', $rendered3); 131 | } 132 | 133 | /** 134 | * @expectedException Twig_Error_Runtime 135 | * @expectedExceptionMessage An exception has been thrown during the rendering of a template ("No strategy key found in value.") 136 | */ 137 | public function testIndexedChainingStrategyNeedsKey() 138 | { 139 | $twig = $this->createTwig('indexed'); 140 | 141 | $twig->render('ics_no_key.twig', array('value' => $this->getValue('asm89', 1))); 142 | } 143 | 144 | public function testAnnotationExpression() 145 | { 146 | $twig = $this->createTwig('indexed'); 147 | 148 | $rendered = $twig->render('annotation_expression.twig', array('value' => $this->getValue('asm89', 1), 'version' => 1)); 149 | $this->assertEquals('Hello asm89!Hello asm89!', $rendered); 150 | } 151 | } 152 | 153 | class KeyGenerator implements KeyGeneratorInterface 154 | { 155 | public function generateKey($value) 156 | { 157 | return get_class($value) . '_' . $value->getUpdatedAt(); 158 | } 159 | 160 | } 161 | 162 | class Value 163 | { 164 | private $value; 165 | private $updatedAt; 166 | 167 | public function __construct($value, $updatedAt) 168 | { 169 | $this->value = $value; 170 | $this->updatedAt = $updatedAt; 171 | } 172 | 173 | public function getUpdatedAt() 174 | { 175 | return $this->updatedAt; 176 | } 177 | 178 | public function __toString() 179 | { 180 | return $this->value; 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /test/Asm89/Twig/CacheExtension/Tests/fixtures/annotation_expression.twig: -------------------------------------------------------------------------------- 1 | {%- set k = "v'" ~ version -%} 2 | {%- cache k {time: 10} -%}Hello {{ value }}!{%- endcache -%} 3 | {%- cache 'bob'|capitalize {gcs: value} %}Hello {{ value }}!{%- endcache -%} 4 | -------------------------------------------------------------------------------- /test/Asm89/Twig/CacheExtension/Tests/fixtures/annotation_not_string.twig: -------------------------------------------------------------------------------- 1 | {% set annotation = 'v1' %} 2 | {% cache annotation value %}Hello {{ value }}!{% endcache %} 3 | -------------------------------------------------------------------------------- /test/Asm89/Twig/CacheExtension/Tests/fixtures/gcs_value.twig: -------------------------------------------------------------------------------- 1 | {% cache 'v1' value %}Hello {{ value }}!{% endcache %} 2 | -------------------------------------------------------------------------------- /test/Asm89/Twig/CacheExtension/Tests/fixtures/gcs_value_v2.twig: -------------------------------------------------------------------------------- 1 | {% cache 'v2' value %}Hello {{ value }}!{% endcache %} 2 | -------------------------------------------------------------------------------- /test/Asm89/Twig/CacheExtension/Tests/fixtures/ics_no_key.twig: -------------------------------------------------------------------------------- 1 | {% cache 'v1' 10 %}Hello {{ value }}!{% endcache %} 2 | -------------------------------------------------------------------------------- /test/Asm89/Twig/CacheExtension/Tests/fixtures/ics_value.twig: -------------------------------------------------------------------------------- 1 | {% cache 'v1' {time: 10} %}Hello {{ value }}!{% endcache %} 2 | -------------------------------------------------------------------------------- /test/Asm89/Twig/CacheExtension/Tests/fixtures/lcs_value.twig: -------------------------------------------------------------------------------- 1 | {% cache 'v1' 10 %}Hello {{ value }}!{% endcache %} 2 | -------------------------------------------------------------------------------- /test/bootstrap.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | if (file_exists($file = __DIR__.'/../vendor/autoload.php')) { 13 | $autoload = require_once $file; 14 | } else { 15 | throw new RuntimeException('Install dependencies to run test suite.'); 16 | } 17 | --------------------------------------------------------------------------------