Initial commit: apps.prometeysev.ru
This commit is contained in:
commit
96ccabcd8b
6561 files changed
+1270349
No files matched your search
@@ -0,0 +1,4 @@
|
||||
phpunit.xml
|
||||
composer.lock
|
||||
composer.phar
|
||||
/vendor/
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
language: php
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- php: 5.6
|
||||
dist: xenial
|
||||
env: 'COMPOSER_FLAGS="--prefer-stable --prefer-lowest" TWIG_VERSION="^1.0"'
|
||||
- php: 5.6
|
||||
dist: xenial
|
||||
env: TWIG_VERSION="^1.0"
|
||||
- php: 7.0
|
||||
dist: xenial
|
||||
env: TWIG_VERSION="^1.0"
|
||||
- php: 7.1
|
||||
dist: bionic
|
||||
env: TWIG_VERSION="^1.0"
|
||||
- php: 7.2
|
||||
dist: bionic
|
||||
env: TWIG_VERSION="^1.0"
|
||||
- php: 7.3
|
||||
dist: bionic
|
||||
env: TWIG_VERSION="^1.0"
|
||||
- php: 7.4
|
||||
dist: bionic
|
||||
env: TWIG_VERSION="^1.0"
|
||||
- php: 7.0
|
||||
dist: xenial
|
||||
env: TWIG_VERSION="^2.0"
|
||||
- php: 7.1
|
||||
dist: bionic
|
||||
env: TWIG_VERSION="^2.0"
|
||||
- php: 7.2
|
||||
dist: bionic
|
||||
env: TWIG_VERSION="^2.0"
|
||||
- php: 7.3
|
||||
dist: bionic
|
||||
env: TWIG_VERSION="^2.0"
|
||||
- php: 7.4
|
||||
dist: bionic
|
||||
env: TWIG_VERSION="^2.0"
|
||||
|
||||
install: composer require twig/twig:${TWIG_VERSION}
|
||||
|
||||
script: composer test
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
Copyright (c) 2013 Alexander <iam.asm89@gmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is furnished
|
||||
to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
+238
@@ -0,0 +1,238 @@
|
||||
Twig cache extension
|
||||
====================
|
||||
|
||||
The missing cache extension for Twig. The extension allows for caching rendered parts of
|
||||
templates using several cache strategies.
|
||||
|
||||
[](http://travis-ci.org/asm89/twig-cache-extension)
|
||||
|
||||
## Installation
|
||||
|
||||
The extension is installable via composer:
|
||||
|
||||
```json
|
||||
{
|
||||
"require": {
|
||||
"asm89/twig-cache-extension": "~1.0"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Quick start
|
||||
|
||||
### Setup
|
||||
|
||||
A minimal setup for adding the extension with the `LifeTimeCacheStrategy` and
|
||||
doctrine array cache is as following:
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
use Doctrine\Common\Cache\ArrayCache;
|
||||
use Asm89\Twig\CacheExtension\CacheProvider\DoctrineCacheAdapter;
|
||||
use Asm89\Twig\CacheExtension\CacheStrategy\LifetimeCacheStrategy;
|
||||
use Asm89\Twig\CacheExtension\Extension as CacheExtension;
|
||||
|
||||
$cacheProvider = new DoctrineCacheAdapter(new ArrayCache());
|
||||
$cacheStrategy = new LifetimeCacheStrategy($cacheProvider);
|
||||
$cacheExtension = new CacheExtension($cacheStrategy);
|
||||
|
||||
$twig->addExtension($cacheExtension);
|
||||
```
|
||||
|
||||
### Want to use a PSR-6 cache pool?
|
||||
|
||||
Instead of using the default `DoctrineCacheAdapter` the extension also has
|
||||
a `PSR-6` compatible adapter. You need to instantiate one of the cache pool
|
||||
implementations as can be found on: http://php-cache.readthedocs.io/en/latest/
|
||||
|
||||
Example: Making use of the `ApcuCachePool` via the `PsrCacheAdapter`:
|
||||
|
||||
```bash
|
||||
composer require cache/apcu-adapter
|
||||
```
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
use Asm89\Twig\CacheExtension\CacheProvider\PsrCacheAdapter;
|
||||
use Asm89\Twig\CacheExtension\CacheStrategy\LifetimeCacheStrategy;
|
||||
use Asm89\Twig\CacheExtension\Extension as CacheExtension;
|
||||
use Cache\Adapter\Apcu\ApcuCachePool;
|
||||
|
||||
$cacheProvider = new PsrCacheAdapter(new ApcuCachePool());
|
||||
$cacheStrategy = new LifetimeCacheStrategy($cacheProvider);
|
||||
$cacheExtension = new CacheExtension($cacheStrategy);
|
||||
|
||||
$twig->addExtension($cacheExtension);
|
||||
```
|
||||
|
||||
### Usage
|
||||
|
||||
To cache a part of a template in Twig surround the code with a `cache` block.
|
||||
The cache block takes two parameters, first an "annotation" part, second the
|
||||
"value" the cache strategy can work with. Example:
|
||||
|
||||
```jinja
|
||||
{% cache 'v1/summary' 900 %}
|
||||
{# heavy lifting template stuff here, include/render other partials etc #}
|
||||
{% endcache %}
|
||||
```
|
||||
|
||||
Cache blocks can be nested:
|
||||
|
||||
```jinja
|
||||
{% cache 'v1' 900 %}
|
||||
{% for item in items %}
|
||||
{% cache 'v1' item %}
|
||||
{# ... #}
|
||||
{% endcache %}
|
||||
{% endfor %}
|
||||
{% endcache %}
|
||||
```
|
||||
|
||||
The annotation can also be an expression:
|
||||
|
||||
```jinja
|
||||
{% set version = 42 %}
|
||||
{% cache 'hello_v' ~ version 900 %}
|
||||
Hello {{ name }}!
|
||||
{% endcache %}
|
||||
```
|
||||
|
||||
## Cache strategies
|
||||
|
||||
The extension ships with a few cache strategies out of the box. Setup and usage
|
||||
of all of them is described below.
|
||||
|
||||
### Lifetime
|
||||
|
||||
See the ["Quick start"](#quick-start) for usage information of the `LifetimeCacheStrategy`.
|
||||
|
||||
### Generational
|
||||
|
||||
Strategy for generational caching.
|
||||
|
||||
In theory the strategy only saves fragments to the cache with infinite
|
||||
lifetime. The key of the strategy lies in the fact that the keys for blocks
|
||||
will change as the value for which the key is generated changes.
|
||||
|
||||
For example: entities containing a last update time, would include a timestamp
|
||||
in the key. For an interesting blog post about this type of caching see:
|
||||
http://37signals.com/svn/posts/3113-how-key-based-cache-expiration-works
|
||||
|
||||
### Blackhole
|
||||
|
||||
Strategy for development mode.
|
||||
|
||||
In development mode it often not very useful to cache fragments. The blackhole
|
||||
strategy provides an easy way to not cache anything it all. It always generates
|
||||
a new key and does not fetch or save any fragments.
|
||||
|
||||
#### Setup
|
||||
|
||||
In order to use the strategy you need to setup a `KeyGenerator` class that is
|
||||
able to generate a cache key for a given value.
|
||||
|
||||
The following naive example always assumes the value is an object with the methods
|
||||
`getId()` and `getUpdatedAt()` method. The key then composed from the class
|
||||
name, the id and the updated time of the object:
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
use Asm89\Twig\CacheExtension\CacheStrategy\KeyGeneratorInterface;
|
||||
|
||||
class MyKeyGenerator implements KeyGeneratorInterface
|
||||
{
|
||||
public function generateKey($value)
|
||||
{
|
||||
return get_class($value) . '_' . $value->getId() . '_' . $value->getUpdatedAt();
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
Next the `GenerationalCacheStrategy` needs to be setup with the keygenerator.
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
use Asm89\Twig\CacheExtension\CacheStrategy\GenerationalCacheStrategy;
|
||||
use Asm89\Twig\CacheExtension\Extension as CacheExtension;
|
||||
|
||||
$keyGenerator = new MyKeyGenerator();
|
||||
$cacheProvider = /* see Quick start */;
|
||||
$cacheStrategy = new GenerationalCacheStrategy($cacheProvider, $keyGenerator, 0 /* = infinite lifetime */);
|
||||
$cacheExtension = new CacheExtension($cacheStrategy);
|
||||
|
||||
$twig->addExtension($cacheExtension);
|
||||
```
|
||||
|
||||
#### Usage
|
||||
|
||||
The strategy expects an object as value for determining the cache key of the
|
||||
block:
|
||||
|
||||
```jinja
|
||||
{% cache 'v1/summary' item %}
|
||||
{# heavy lifting template stuff here, include/render other partials etc #}
|
||||
{% endcache %}
|
||||
```
|
||||
|
||||
### Using multiple strategies
|
||||
|
||||
Different cache strategies are useful for different usecases. It is possible to
|
||||
mix multiple strategies in an application with the
|
||||
`IndexedChainingCacheStrategy`. The strategy takes an array of `'name' =>
|
||||
$strategy` and delegates the caching to the appropriate strategy.
|
||||
|
||||
#### Setup
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
use Asm89\Twig\CacheExtension\CacheStrategy\IndexedChainingCacheStrategy;
|
||||
use Asm89\Twig\CacheExtension\Extension as CacheExtension;
|
||||
|
||||
$cacheStrategy = new IndexedChainingCacheStrategy(array(
|
||||
'time' => $lifetimeCacheStrategy,
|
||||
'gen' => $generationalCacheStrategy,
|
||||
));
|
||||
$cacheExtension = new CacheExtension($cacheStrategy);
|
||||
|
||||
$twig->addExtension($cacheExtension);
|
||||
```
|
||||
|
||||
#### Usage
|
||||
|
||||
The strategy expects an array with as key the name of the strategy which it
|
||||
needs to delegate to and as value the appropriate value for the delegated
|
||||
strategy.
|
||||
|
||||
```jinja
|
||||
{# delegate to lifetime strategy #}
|
||||
{% cache 'v1/summary' {time: 300} %}
|
||||
{# heavy lifting template stuff here, include/render other partials etc #}
|
||||
{% endcache %}
|
||||
|
||||
{# delegate to generational strategy #}
|
||||
{% cache 'v1/summary' {gen: item} %}
|
||||
{# heavy lifting template stuff here, include/render other partials etc #}
|
||||
{% endcache %}
|
||||
```
|
||||
|
||||
## Implementing a cache strategy
|
||||
|
||||
Creating separate caches for different access levels, languages or other
|
||||
usecases can be done by implementing a custom cache strategy. In order to do so
|
||||
implement the `CacheProviderInterface`. It is recommended to use composition
|
||||
and wrap a custom strategy around an existing one.
|
||||
|
||||
## Authors
|
||||
|
||||
Alexander <iam.asm89@gmail.com>
|
||||
|
||||
## License
|
||||
|
||||
twig-cache-extension is licensed under the MIT License - see the LICENSE file for details
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"name": "asm89/twig-cache-extension",
|
||||
"description": "Cache fragments of templates directly within Twig.",
|
||||
"keywords": ["twig", "cache", "extension"],
|
||||
"homepage": "https://github.com/asm89/twig-cache-extension",
|
||||
"type": "library",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Alexander",
|
||||
"email": "iam.asm89@gmail.com"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=5.3.2",
|
||||
"twig/twig": "^1.0|^2.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^5.0 || ^4.8.10",
|
||||
"doctrine/cache": "~1.0"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "phpunit"
|
||||
},
|
||||
"suggest": {
|
||||
"psr/cache-implementation": "To make use of PSR-6 cache implementation via PsrCacheAdapter."
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"": "lib/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"": "test/"
|
||||
}
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.4-dev"
|
||||
}
|
||||
}
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of twig-cache-extension.
|
||||
*
|
||||
* (c) Alexander <iam.asm89@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Asm89\Twig\CacheExtension\CacheProvider;
|
||||
|
||||
use Asm89\Twig\CacheExtension\CacheProviderInterface;
|
||||
use Doctrine\Common\Cache\Cache;
|
||||
|
||||
/**
|
||||
* Adapter class to use the cache classes provider by Doctrine.
|
||||
*
|
||||
* @author Alexander <iam.asm89@gmail.com>
|
||||
*/
|
||||
class DoctrineCacheAdapter implements CacheProviderInterface
|
||||
{
|
||||
private $cache;
|
||||
|
||||
/**
|
||||
* @param Cache $cache
|
||||
*/
|
||||
public function __construct(Cache $cache)
|
||||
{
|
||||
$this->cache = $cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function fetch($key)
|
||||
{
|
||||
return $this->cache->fetch($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function save($key, $value, $lifetime = 0)
|
||||
{
|
||||
return $this->cache->save($key, $value, $lifetime);
|
||||
}
|
||||
}
|
||||
Vendored
+68
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of twig-cache-extension.
|
||||
*
|
||||
* (c) Alexander <iam.asm89@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Asm89\Twig\CacheExtension\CacheProvider;
|
||||
|
||||
use Asm89\Twig\CacheExtension\CacheProviderInterface;
|
||||
use Psr\Cache\CacheItemPoolInterface;
|
||||
|
||||
/**
|
||||
* Adapter class to make extension interoperable with every PSR-6 adapter.
|
||||
*
|
||||
* @see http://php-cache.readthedocs.io/
|
||||
*
|
||||
* @author Rvanlaak <rvanlaak@gmail.com>
|
||||
*/
|
||||
class PsrCacheAdapter implements CacheProviderInterface
|
||||
{
|
||||
/**
|
||||
* @var CacheItemPoolInterface
|
||||
*/
|
||||
private $cache;
|
||||
|
||||
/**
|
||||
* @param CacheItemPoolInterface $cache
|
||||
*/
|
||||
public function __construct(CacheItemPoolInterface $cache)
|
||||
{
|
||||
$this->cache = $cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
* @return mixed|false
|
||||
*/
|
||||
public function fetch($key)
|
||||
{
|
||||
// PSR-6 implementation returns null, CacheProviderInterface expects false
|
||||
$item = $this->cache->getItem($key);
|
||||
if ($item->isHit()) {
|
||||
return $item->get();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
* @param string $value
|
||||
* @param int|\DateInterval $lifetime
|
||||
* @return bool
|
||||
*/
|
||||
public function save($key, $value, $lifetime = 0)
|
||||
{
|
||||
$item = $this->cache->getItem($key);
|
||||
$item->set($value);
|
||||
$item->expiresAfter($lifetime);
|
||||
|
||||
return $this->cache->save($item);
|
||||
}
|
||||
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of twig-cache-extension.
|
||||
*
|
||||
* (c) Alexander <iam.asm89@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Asm89\Twig\CacheExtension;
|
||||
|
||||
/**
|
||||
* Cache provider interface.
|
||||
*
|
||||
* @author Alexander <iam.asm89@gmail.com>
|
||||
*/
|
||||
interface CacheProviderInterface
|
||||
{
|
||||
/**
|
||||
* @param string $key
|
||||
*
|
||||
* @return mixed False, if there was no value to be fetched. Null or a string otherwise.
|
||||
*/
|
||||
public function fetch($key);
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
* @param string $value
|
||||
* @param integer $lifetime
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function save($key, $value, $lifetime = 0);
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of twig-cache-extension.
|
||||
*
|
||||
* (c) Alexander <iam.asm89@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Asm89\Twig\CacheExtension\CacheStrategy;
|
||||
|
||||
use Asm89\Twig\CacheExtension\CacheStrategyInterface;
|
||||
|
||||
/**
|
||||
* CacheStrategy which doesn't cache at all
|
||||
*
|
||||
* This strategy can be used in development mode, e.g. editing twig templates,
|
||||
* to prevent previously cached versions from being rendered.
|
||||
*
|
||||
* @see https://github.com/asm89/twig-cache-extension/pull/29
|
||||
*
|
||||
* @author Hagen Hübel <hhuebel@itinance.com>
|
||||
*
|
||||
* @package Asm89\Twig\CacheExtension\CacheStrategy
|
||||
*/
|
||||
class BlackholeCacheStrategy implements CacheStrategyInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function fetchBlock($key)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function generateKey($annotation, $value)
|
||||
{
|
||||
return microtime(true) . mt_rand();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function saveBlock($key, $block)
|
||||
{
|
||||
// fire and forget
|
||||
}
|
||||
}
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of twig-cache-extension.
|
||||
*
|
||||
* (c) Alexander <iam.asm89@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Asm89\Twig\CacheExtension\CacheStrategy;
|
||||
|
||||
use Asm89\Twig\CacheExtension\CacheProviderInterface;
|
||||
use Asm89\Twig\CacheExtension\CacheStrategyInterface;
|
||||
use Asm89\Twig\CacheExtension\Exception\InvalidCacheKeyException;
|
||||
|
||||
/**
|
||||
* Strategy for generational caching.
|
||||
*
|
||||
* In theory the strategy only saves fragments to the cache with infinite
|
||||
* lifetime. The key of the strategy lies in the fact that the keys for blocks
|
||||
* will change as the value for which the key is generated changes.
|
||||
*
|
||||
* For example: entities containing a last update time, would include a
|
||||
* timestamp in the key.
|
||||
*
|
||||
* @see http://37signals.com/svn/posts/3113-how-key-based-cache-expiration-works
|
||||
*
|
||||
* @author Alexander <iam.asm89@gmail.com>
|
||||
*/
|
||||
class GenerationalCacheStrategy implements CacheStrategyInterface
|
||||
{
|
||||
private $keyGenerator;
|
||||
private $cache;
|
||||
private $lifetime;
|
||||
|
||||
/**
|
||||
* @param CacheProviderInterface $cache
|
||||
* @param KeyGeneratorInterface $keyGenerator
|
||||
* @param integer $lifetime
|
||||
*/
|
||||
public function __construct(CacheProviderInterface $cache, KeyGeneratorInterface $keyGenerator, $lifetime = 0)
|
||||
{
|
||||
$this->keyGenerator = $keyGenerator;
|
||||
$this->cache = $cache;
|
||||
$this->lifetime = $lifetime;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function fetchBlock($key)
|
||||
{
|
||||
return $this->cache->fetch($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function generateKey($annotation, $value)
|
||||
{
|
||||
$key = $this->keyGenerator->generateKey($value);
|
||||
|
||||
if (null === $key) {
|
||||
throw new InvalidCacheKeyException();
|
||||
}
|
||||
|
||||
return $annotation . '__GCS__' . $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function saveBlock($key, $block)
|
||||
{
|
||||
return $this->cache->save($key, $block, $this->lifetime);
|
||||
}
|
||||
}
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of twig-cache-extension.
|
||||
*
|
||||
* (c) Alexander <iam.asm89@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Asm89\Twig\CacheExtension\CacheStrategy;
|
||||
|
||||
use Asm89\Twig\CacheExtension\CacheStrategyInterface;
|
||||
use Asm89\Twig\CacheExtension\Exception\NonExistingStrategyException;
|
||||
use Asm89\Twig\CacheExtension\Exception\NonExistingStrategyKeyException;
|
||||
|
||||
/**
|
||||
* Combines several configured cache strategies.
|
||||
*
|
||||
* Useful for combining for example generational cache strategy with a lifetime
|
||||
* cache strategy, but also useful when combining several generational cache
|
||||
* strategies which differ on cache lifetime (infinite, 1hr, 5m).
|
||||
*
|
||||
* @author Alexander <iam.asm89@gmail.com>
|
||||
*/
|
||||
class IndexedChainingCacheStrategy implements CacheStrategyInterface
|
||||
{
|
||||
/**
|
||||
* @var CacheStrategyInterface[]
|
||||
*/
|
||||
private $strategies;
|
||||
|
||||
/**
|
||||
* @param array $strategies
|
||||
*/
|
||||
public function __construct(array $strategies)
|
||||
{
|
||||
$this->strategies = $strategies;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function fetchBlock($key)
|
||||
{
|
||||
return $this->strategies[$key['strategyKey']]->fetchBlock($key['key']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function generateKey($annotation, $value)
|
||||
{
|
||||
if (!is_array($value) || null === $strategyKey = key($value)) {
|
||||
throw new NonExistingStrategyKeyException();
|
||||
}
|
||||
|
||||
if (!isset($this->strategies[$strategyKey])) {
|
||||
throw new NonExistingStrategyException($strategyKey);
|
||||
}
|
||||
|
||||
return array(
|
||||
'strategyKey' => $strategyKey,
|
||||
'key' => $this->strategies[$strategyKey]->generateKey($annotation, current($value)),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function saveBlock($key, $block)
|
||||
{
|
||||
return $this->strategies[$key['strategyKey']]->saveBlock($key['key'], $block);
|
||||
}
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of twig-cache-extension.
|
||||
*
|
||||
* (c) Alexander <iam.asm89@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Asm89\Twig\CacheExtension\CacheStrategy;
|
||||
|
||||
/**
|
||||
* Generates a key for a given value.
|
||||
*
|
||||
* @author Alexander <iam.asm89@gmail.com>
|
||||
*/
|
||||
interface KeyGeneratorInterface
|
||||
{
|
||||
/**
|
||||
* Generate a cache key for a given value.
|
||||
*
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function generateKey($value);
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of twig-cache-extension.
|
||||
*
|
||||
* (c) Alexander <iam.asm89@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Asm89\Twig\CacheExtension\CacheStrategy;
|
||||
|
||||
use Asm89\Twig\CacheExtension\CacheProviderInterface;
|
||||
use Asm89\Twig\CacheExtension\CacheStrategyInterface;
|
||||
use Asm89\Twig\CacheExtension\Exception\InvalidCacheLifetimeException;
|
||||
|
||||
/**
|
||||
* Strategy for caching with a pre-defined lifetime.
|
||||
*
|
||||
* The value passed to the strategy is the lifetime of the cache block in
|
||||
* seconds.
|
||||
*
|
||||
* @author Alexander <iam.asm89@gmail.com>
|
||||
*/
|
||||
class LifetimeCacheStrategy implements CacheStrategyInterface
|
||||
{
|
||||
private $cache;
|
||||
|
||||
/**
|
||||
* @param CacheProviderInterface $cache
|
||||
*/
|
||||
public function __construct(CacheProviderInterface $cache)
|
||||
{
|
||||
$this->cache = $cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function fetchBlock($key)
|
||||
{
|
||||
return $this->cache->fetch($key['key']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function generateKey($annotation, $value)
|
||||
{
|
||||
if (!is_numeric($value)) {
|
||||
throw new InvalidCacheLifetimeException($value);
|
||||
}
|
||||
|
||||
return array(
|
||||
'lifetime' => $value,
|
||||
'key' => '__LCS__' . $annotation,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function saveBlock($key, $block)
|
||||
{
|
||||
return $this->cache->save($key['key'], $block, $key['lifetime']);
|
||||
}
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of twig-cache-extension.
|
||||
*
|
||||
* (c) Alexander <iam.asm89@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Asm89\Twig\CacheExtension;
|
||||
|
||||
/**
|
||||
* Cache strategy interface.
|
||||
*
|
||||
* @author Alexander <iam.asm89@gmail.com>
|
||||
*/
|
||||
interface CacheStrategyInterface
|
||||
{
|
||||
/**
|
||||
* Fetch the block for a given key.
|
||||
*
|
||||
* @param mixed $key
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function fetchBlock($key);
|
||||
|
||||
/**
|
||||
* Generate a key for the value.
|
||||
*
|
||||
* @param string $annotation
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function generateKey($annotation, $value);
|
||||
|
||||
/**
|
||||
* Save the contents of a rendered block.
|
||||
*
|
||||
* @param mixed $key
|
||||
* @param string $block
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function saveBlock($key, $block);
|
||||
}
|
||||
Vendored
+20
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of twig-cache-extension.
|
||||
*
|
||||
* (c) Alexander <iam.asm89@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Asm89\Twig\CacheExtension\Exception;
|
||||
|
||||
/**
|
||||
* @todo Replace \RuntimeException with \InvalidArgumentException at version 2.0
|
||||
*/
|
||||
class BaseException extends \RuntimeException
|
||||
{
|
||||
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of twig-cache-extension.
|
||||
*
|
||||
* (c) Alexander <iam.asm89@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Asm89\Twig\CacheExtension\Exception;
|
||||
|
||||
class InvalidCacheKeyException extends BaseException
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct($message = 'Key generator did not return a key.', $code = 0, \Exception $previous = null)
|
||||
{
|
||||
parent::__construct($message, $code, $previous);
|
||||
}
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of twig-cache-extension.
|
||||
*
|
||||
* (c) Alexander <iam.asm89@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Asm89\Twig\CacheExtension\Exception;
|
||||
|
||||
class InvalidCacheLifetimeException extends BaseException
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct($lifetime, $code = 0, \Exception $previous = null)
|
||||
{
|
||||
parent::__construct(sprintf('Value "%s" is not a valid lifetime.', gettype($lifetime)), $code, $previous);
|
||||
}
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of twig-cache-extension.
|
||||
*
|
||||
* (c) Alexander <iam.asm89@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Asm89\Twig\CacheExtension\Exception;
|
||||
|
||||
class NonExistingStrategyException extends BaseException
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct($strategyKey, $code = 0, \Exception $previous = null)
|
||||
{
|
||||
parent::__construct(sprintf('No strategy configured with key "%s".', $strategyKey), $code, $previous);
|
||||
}
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of twig-cache-extension.
|
||||
*
|
||||
* (c) Alexander <iam.asm89@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Asm89\Twig\CacheExtension\Exception;
|
||||
|
||||
class NonExistingStrategyKeyException extends BaseException
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct($message = 'No strategy key found in value.', $code = 0, \Exception $previous = null)
|
||||
{
|
||||
parent::__construct($message, $code, $previous);
|
||||
}
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of twig-cache-extension.
|
||||
*
|
||||
* (c) Alexander <iam.asm89@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Asm89\Twig\CacheExtension;
|
||||
|
||||
/**
|
||||
* Extension for caching template blocks with twig.
|
||||
*
|
||||
* @author Alexander <iam.asm89@gmail.com>
|
||||
*/
|
||||
class Extension extends \Twig_Extension
|
||||
{
|
||||
private $cacheStrategy;
|
||||
|
||||
/**
|
||||
* @param CacheStrategyInterface $cacheStrategy
|
||||
*/
|
||||
public function __construct(CacheStrategyInterface $cacheStrategy)
|
||||
{
|
||||
$this->cacheStrategy = $cacheStrategy;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return CacheStrategyInterface
|
||||
*/
|
||||
public function getCacheStrategy()
|
||||
{
|
||||
return $this->cacheStrategy;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
if (version_compare(\Twig_Environment::VERSION, '1.26.0', '>=')) {
|
||||
return __CLASS__;
|
||||
}
|
||||
return 'asm89_cache';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getTokenParsers()
|
||||
{
|
||||
return array(
|
||||
new TokenParser\Cache(),
|
||||
);
|
||||
}
|
||||
}
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of twig-cache-extension.
|
||||
*
|
||||
* (c) Alexander <iam.asm89@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Asm89\Twig\CacheExtension\Node;
|
||||
|
||||
/**
|
||||
* Cache twig node.
|
||||
*
|
||||
* @author Alexander <iam.asm89@gmail.com>
|
||||
*/
|
||||
class CacheNode extends \Twig_Node
|
||||
{
|
||||
private static $cacheCount = 1;
|
||||
|
||||
/**
|
||||
* @param \Twig_Node_Expression $annotation
|
||||
* @param \Twig_Node_Expression $keyInfo
|
||||
* @param \Twig_NodeInterface $body
|
||||
* @param integer $lineno
|
||||
* @param string $tag
|
||||
*/
|
||||
public function __construct(\Twig_Node_Expression $annotation, \Twig_Node_Expression $keyInfo, \Twig_Node $body, $lineno, $tag = null)
|
||||
{
|
||||
parent::__construct(array('key_info' => $keyInfo, 'body' => $body, 'annotation' => $annotation), array(), $lineno, $tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function compile(\Twig_Compiler $compiler)
|
||||
{
|
||||
$i = self::$cacheCount++;
|
||||
|
||||
if (version_compare(\Twig_Environment::VERSION, '1.26.0', '>=')) {
|
||||
$extension = 'Asm89\Twig\CacheExtension\Extension';
|
||||
} else {
|
||||
$extension = 'asm89_cache';
|
||||
}
|
||||
|
||||
$compiler
|
||||
->addDebugInfo($this)
|
||||
->write("\$asm89CacheStrategy".$i." = \$this->env->getExtension('{$extension}')->getCacheStrategy();\n")
|
||||
->write("\$asm89Key".$i." = \$asm89CacheStrategy".$i."->generateKey(")
|
||||
->subcompile($this->getNode('annotation'))
|
||||
->raw(", ")
|
||||
->subcompile($this->getNode('key_info'))
|
||||
->write(");\n")
|
||||
->write("\$asm89CacheBody".$i." = \$asm89CacheStrategy".$i."->fetchBlock(\$asm89Key".$i.");\n")
|
||||
->write("if (\$asm89CacheBody".$i." === false) {\n")
|
||||
->indent()
|
||||
->write("ob_start();\n")
|
||||
->indent()
|
||||
->subcompile($this->getNode('body'))
|
||||
->outdent()
|
||||
->write("\n")
|
||||
->write("\$asm89CacheBody".$i." = ob_get_clean();\n")
|
||||
->write("\$asm89CacheStrategy".$i."->saveBlock(\$asm89Key".$i.", \$asm89CacheBody".$i.");\n")
|
||||
->outdent()
|
||||
->write("}\n")
|
||||
->write("echo \$asm89CacheBody".$i.";\n")
|
||||
;
|
||||
}
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of twig-cache-extension.
|
||||
*
|
||||
* (c) Alexander <iam.asm89@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Asm89\Twig\CacheExtension\TokenParser;
|
||||
|
||||
use Asm89\Twig\CacheExtension\Node\CacheNode;
|
||||
|
||||
/**
|
||||
* Parser for cache/endcache blocks.
|
||||
*
|
||||
* @author Alexander <iam.asm89@gmail.com>
|
||||
*/
|
||||
class Cache extends \Twig_TokenParser
|
||||
{
|
||||
/**
|
||||
* @param \Twig_Token $token
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function decideCacheEnd(\Twig_Token $token)
|
||||
{
|
||||
return $token->test('endcache');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getTag()
|
||||
{
|
||||
return 'cache';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function parse(\Twig_Token $token)
|
||||
{
|
||||
$lineno = $token->getLine();
|
||||
$stream = $this->parser->getStream();
|
||||
|
||||
$annotation = $this->parser->getExpressionParser()->parseExpression();
|
||||
|
||||
$key = $this->parser->getExpressionParser()->parseExpression();
|
||||
|
||||
$stream->expect(\Twig_Token::BLOCK_END_TYPE);
|
||||
$body = $this->parser->subparse(array($this, 'decideCacheEnd'), true);
|
||||
$stream->expect(\Twig_Token::BLOCK_END_TYPE);
|
||||
|
||||
return new CacheNode($annotation, $key, $body, $lineno, $this->getTag());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<phpunit backupGlobals="false"
|
||||
backupStaticAttributes="false"
|
||||
colors="true"
|
||||
convertErrorsToExceptions="true"
|
||||
convertNoticesToExceptions="true"
|
||||
convertWarningsToExceptions="true"
|
||||
processIsolation="false"
|
||||
stopOnFailure="false"
|
||||
syntaxCheck="false"
|
||||
bootstrap="test/bootstrap.php"
|
||||
>
|
||||
<testsuites>
|
||||
<testsuite name="Twig Cache Extension Test Suite">
|
||||
<directory>./test/Asm89/</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
|
||||
<filter>
|
||||
<whitelist>
|
||||
<directory suffix=".php">./lib/</directory>
|
||||
</whitelist>
|
||||
</filter>
|
||||
</phpunit>
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of twig-cache-extension.
|
||||
*
|
||||
* (c) Alexander <iam.asm89@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Asm89\Twig\CacheExtension\Tests\CacheProvider;
|
||||
|
||||
use Asm89\Twig\CacheExtension\CacheProvider\DoctrineCacheAdapter;
|
||||
|
||||
class DoctrineCacheAdapterTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testFetch()
|
||||
{
|
||||
$cacheMock = $this->createCacheMock();
|
||||
$cacheMock->expects($this->any())
|
||||
->method('fetch')
|
||||
->will($this->returnValue('fromcache'));
|
||||
|
||||
$cache = new DoctrineCacheAdapter($cacheMock);
|
||||
|
||||
$this->assertEquals('fromcache', $cache->fetch('test'));
|
||||
}
|
||||
|
||||
public function testSave()
|
||||
{
|
||||
$cacheMock = $this->createCacheMock();
|
||||
$cacheMock->expects($this->once())
|
||||
->method('save')
|
||||
->with('key', 'value', 42);
|
||||
|
||||
$cache = new DoctrineCacheAdapter($cacheMock);
|
||||
|
||||
$cache->save('key', 'value', 42);
|
||||
}
|
||||
|
||||
public function createCacheMock()
|
||||
{
|
||||
return $this->createMock('Doctrine\Common\Cache\Cache');
|
||||
}
|
||||
}
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of twig-cache-extension.
|
||||
*
|
||||
* (c) Alexander <iam.asm89@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Asm89\Twig\CacheExtension\Tests\CacheStrategy;
|
||||
|
||||
use Asm89\Twig\CacheExtension\CacheStrategy\GenerationalCacheStrategy;
|
||||
|
||||
class GenerationalCacheStrategyTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
private $keyGeneratorMock;
|
||||
private $cacheProviderMock;
|
||||
|
||||
public function createCacheStrategy($lifetime = 0)
|
||||
{
|
||||
$this->keyGeneratorMock = $this->createKeyGeneratorMock();
|
||||
$this->cacheProviderMock = $this->createCacheProviderMock();
|
||||
|
||||
return new GenerationalCacheStrategy($this->cacheProviderMock, $this->keyGeneratorMock, $lifetime);
|
||||
}
|
||||
|
||||
public function testGenerateKeyContainsAnnotation()
|
||||
{
|
||||
$strategy = $this->createCacheStrategy();
|
||||
$this->keyGeneratorMock->expects($this->any())
|
||||
->method('generateKey')
|
||||
->will($this->returnValue('foo'));
|
||||
|
||||
$this->assertEquals('v42__GCS__foo', $strategy->generateKey('v42', 'value'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Asm89\Twig\CacheExtension\Exception\InvalidCacheKeyException
|
||||
*/
|
||||
public function testGenerationKeyThrowsExceptionWhenKeyGeneratorReturnsNull()
|
||||
{
|
||||
$strategy = $this->createCacheStrategy();
|
||||
|
||||
$strategy->generateKey('v42', 'value');
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getLifeTimes
|
||||
*/
|
||||
public function testSaveBlockUsesConfiguredLifetime($lifetime)
|
||||
{
|
||||
$strategy = $this->createCacheStrategy($lifetime);
|
||||
$this->cacheProviderMock->expects($this->any())
|
||||
->method('save')
|
||||
->with('key', 'value', $lifetime)
|
||||
->will($this->returnValue('foo'));
|
||||
|
||||
$strategy->saveBlock('key', 'value');
|
||||
}
|
||||
|
||||
public function getLifetimes()
|
||||
{
|
||||
return array(
|
||||
array(0),
|
||||
array(60),
|
||||
);
|
||||
}
|
||||
|
||||
public function createKeyGeneratorMock()
|
||||
{
|
||||
return $this->createMock('Asm89\Twig\CacheExtension\CacheStrategy\KeyGeneratorInterface');
|
||||
}
|
||||
|
||||
public function createCacheProviderMock()
|
||||
{
|
||||
return $this->createMock('Asm89\Twig\CacheExtension\CacheProviderInterface');
|
||||
}
|
||||
}
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of twig-cache-extension.
|
||||
*
|
||||
* (c) Alexander <iam.asm89@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Asm89\Twig\CacheExtension\Tests\CacheStrategy;
|
||||
|
||||
use Asm89\Twig\CacheExtension\CacheStrategy\IndexedChainingCacheStrategy;
|
||||
|
||||
class IndexedChainingCacheStrategyTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
private $cacheStrategyMocks = array();
|
||||
|
||||
public function createCacheStrategy()
|
||||
{
|
||||
foreach($this->getStrategies() as $config) {
|
||||
list($key, $return) = $config;
|
||||
|
||||
$cacheStrategyMock = $this->createCacheStrategyMock();
|
||||
$cacheStrategyMock->expects($this->any())
|
||||
->method('generateKey')
|
||||
->will($this->returnValue($return));
|
||||
|
||||
$this->cacheStrategyMocks[$key] = $cacheStrategyMock;
|
||||
}
|
||||
|
||||
return new IndexedChainingCacheStrategy($this->cacheStrategyMocks);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getStrategies
|
||||
*/
|
||||
public function testGenerateKeyProxiesToAppropriateStrategy($key, $return)
|
||||
{
|
||||
$strategy = $this->createCacheStrategy();
|
||||
|
||||
$generatedKey = $strategy->generateKey('v42', array($key => 'proxied_value'));
|
||||
|
||||
$this->assertEquals($return, $generatedKey['key']);
|
||||
$this->assertEquals($key, $generatedKey['strategyKey']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Asm89\Twig\CacheExtension\Exception\NonExistingStrategyKeyException
|
||||
*/
|
||||
public function testGenerateKeyThrowsExceptionOnMissingKey()
|
||||
{
|
||||
$strategy = $this->createCacheStrategy();
|
||||
$strategy->generateKey('v42', 'proxied_value');
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Asm89\Twig\CacheExtension\Exception\NonExistingStrategyException
|
||||
* @expectedExceptionMessage No strategy configured with key "unknown"
|
||||
*/
|
||||
public function testGenerateKeyThrowsExceptionOnUnknownKey()
|
||||
{
|
||||
$strategy = $this->createCacheStrategy();
|
||||
$strategy->generateKey('v42', array('unknown' => 'proxied_value'));
|
||||
}
|
||||
|
||||
public function getStrategies()
|
||||
{
|
||||
return array(
|
||||
array('foo', 'foo_key'),
|
||||
array('bar', 'bar_key'),
|
||||
);
|
||||
}
|
||||
|
||||
public function createCacheStrategyMock()
|
||||
{
|
||||
return $this->createMock('Asm89\Twig\CacheExtension\CacheStrategyInterface');
|
||||
}
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of twig-cache-extension.
|
||||
*
|
||||
* (c) Alexander <iam.asm89@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Asm89\Twig\CacheExtension\Tests\CacheStrategy;
|
||||
|
||||
use Asm89\Twig\CacheExtension\CacheStrategy\LifetimeCacheStrategy;
|
||||
|
||||
class LifetimeCacheStrategyTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
private $cacheProviderMock;
|
||||
|
||||
public function createCacheStrategy()
|
||||
{
|
||||
$this->cacheProviderMock = $this->createCacheProviderMock();
|
||||
|
||||
return new LifetimeCacheStrategy($this->cacheProviderMock);
|
||||
}
|
||||
|
||||
public function testGenerateKeyUsesGivenLifetime()
|
||||
{
|
||||
$strategy = $this->createCacheStrategy();
|
||||
|
||||
$key = $strategy->generateKey('v42', 42);
|
||||
|
||||
$this->assertEquals(42, $key['lifetime']);
|
||||
}
|
||||
|
||||
public function testGenerateKeyAnnotatesKey()
|
||||
{
|
||||
$strategy = $this->createCacheStrategy();
|
||||
|
||||
$key = $strategy->generateKey('the_annotation', 42);
|
||||
|
||||
$this->assertContains('the_annotation', $key['key']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getInvalidLifetimeValues
|
||||
* @expectedException \Asm89\Twig\CacheExtension\Exception\InvalidCacheLifetimeException
|
||||
*/
|
||||
public function testGenerateKeyThrowsExceptionWhenNoLifetimeProvided($value)
|
||||
{
|
||||
$strategy = $this->createCacheStrategy();
|
||||
|
||||
$strategy->generateKey('v42', $value);
|
||||
}
|
||||
|
||||
public function getInvalidLifetimeValues()
|
||||
{
|
||||
return array(
|
||||
array('foo'),
|
||||
array(array('foo')),
|
||||
);
|
||||
}
|
||||
|
||||
public function createCacheProviderMock()
|
||||
{
|
||||
return $this->createMock('Asm89\Twig\CacheExtension\CacheProviderInterface');
|
||||
}
|
||||
}
|
||||
Vendored
+182
@@ -0,0 +1,182 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of twig-cache-extension.
|
||||
*
|
||||
* (c) Alexander <iam.asm89@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Asm89\Twig\CacheExtension\Tests;
|
||||
|
||||
use Asm89\Twig\CacheExtension\CacheProvider\DoctrineCacheAdapter;
|
||||
use Asm89\Twig\CacheExtension\CacheStrategy\KeyGeneratorInterface;
|
||||
use Asm89\Twig\CacheExtension\CacheStrategy\GenerationalCacheStrategy;
|
||||
use Asm89\Twig\CacheExtension\CacheStrategy\IndexedChainingCacheStrategy;
|
||||
use Asm89\Twig\CacheExtension\CacheStrategy\LifetimeCacheStrategy;
|
||||
use Asm89\Twig\CacheExtension\Extension;
|
||||
use Doctrine\Common\Cache\ArrayCache;
|
||||
use Twig_Loader_Filesystem;
|
||||
use Twig_Environment;
|
||||
|
||||
class FunctionalExtensionTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
private $cache;
|
||||
|
||||
protected function createCacheProvider()
|
||||
{
|
||||
$this->cache = new ArrayCache();
|
||||
|
||||
return new DoctrineCacheAdapter($this->cache);
|
||||
}
|
||||
|
||||
protected function createCacheStrategy($name = null)
|
||||
{
|
||||
$cacheProvider = $this->createCacheProvider();
|
||||
$keyGenerator = $this->createKeyGenerator();
|
||||
$lifetime = 0;
|
||||
|
||||
switch ($name) {
|
||||
case 'time':
|
||||
return new LifetimeCacheStrategy($cacheProvider);
|
||||
case 'indexed':
|
||||
return new IndexedChainingCacheStrategy(array(
|
||||
'gcs' => new GenerationalCacheStrategy($cacheProvider, $keyGenerator, $lifetime),
|
||||
'time' => new LifetimeCacheStrategy($cacheProvider),
|
||||
));
|
||||
default:
|
||||
return new GenerationalCacheStrategy($cacheProvider, $keyGenerator, $lifetime);
|
||||
}
|
||||
}
|
||||
|
||||
protected function createKeyGenerator()
|
||||
{
|
||||
return new KeyGenerator();
|
||||
}
|
||||
|
||||
protected function createTwig($cacheStrategyName = null)
|
||||
{
|
||||
$loader = new Twig_Loader_Filesystem(__DIR__ . '/fixtures/');
|
||||
$twig = new Twig_Environment($loader);
|
||||
|
||||
$cacheExtension = new Extension($this->createCacheStrategy($cacheStrategyName));
|
||||
|
||||
$twig->addExtension($cacheExtension);
|
||||
|
||||
return $twig;
|
||||
}
|
||||
|
||||
protected function getValue($value, $updatedAt)
|
||||
{
|
||||
return new Value($value, $updatedAt);
|
||||
}
|
||||
|
||||
public function testCachesWithSameCacheKey()
|
||||
{
|
||||
$twig = $this->createTwig();
|
||||
|
||||
$rendered = $twig->render('gcs_value.twig', array('value' => $this->getValue('asm89', 1)));
|
||||
$this->assertEquals('Hello asm89!', $rendered);
|
||||
|
||||
$rendered2 = $twig->render('gcs_value.twig', array('value' => $this->getValue('fabpot', 1)));
|
||||
$this->assertEquals('Hello asm89!', $rendered2);
|
||||
}
|
||||
|
||||
public function testDifferentCacheOnDifferentAnnotation()
|
||||
{
|
||||
$twig = $this->createTwig();
|
||||
|
||||
$rendered = $twig->render('gcs_value.twig', array('value' => $this->getValue('asm89', 1)));
|
||||
$this->assertEquals('Hello asm89!', $rendered);
|
||||
|
||||
$rendered2 = $twig->render('gcs_value.twig', array('value' => $this->getValue('fabpot', 1)));
|
||||
$this->assertEquals('Hello asm89!', $rendered2);
|
||||
|
||||
$rendered3 = $twig->render('gcs_value_v2.twig', array('value' => $this->getValue('fabpot', 1)));
|
||||
$this->assertEquals('Hello fabpot!', $rendered3);
|
||||
}
|
||||
|
||||
public function testLifetimeCacheStrategy()
|
||||
{
|
||||
$twig = $this->createTwig('time');
|
||||
|
||||
$rendered = $twig->render('lcs_value.twig', array('value' => $this->getValue('asm89', 1)));
|
||||
$this->assertEquals('Hello asm89!', $rendered);
|
||||
|
||||
$rendered2 = $twig->render('lcs_value.twig', array('value' => $this->getValue('fabpot', 1)));
|
||||
$this->assertEquals('Hello asm89!', $rendered2);
|
||||
|
||||
$this->cache->flushAll();
|
||||
|
||||
$rendered3 = $twig->render('lcs_value.twig', array('value' => $this->getValue('fabpot', 1)));
|
||||
$this->assertEquals('Hello fabpot!', $rendered3);
|
||||
}
|
||||
|
||||
public function testIndexedChainingStrategy()
|
||||
{
|
||||
$twig = $this->createTwig('indexed');
|
||||
|
||||
$rendered = $twig->render('ics_value.twig', array('value' => $this->getValue('asm89', 1)));
|
||||
$this->assertEquals('Hello asm89!', $rendered);
|
||||
|
||||
$rendered2 = $twig->render('ics_value.twig', array('value' => $this->getValue('fabpot', 1)));
|
||||
$this->assertEquals('Hello asm89!', $rendered2);
|
||||
|
||||
$this->cache->flushAll();
|
||||
|
||||
$rendered3 = $twig->render('ics_value.twig', array('value' => $this->getValue('fabpot', 1)));
|
||||
$this->assertEquals('Hello fabpot!', $rendered3);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Twig_Error_Runtime
|
||||
* @expectedExceptionMessage An exception has been thrown during the rendering of a template ("No strategy key found in value.")
|
||||
*/
|
||||
public function testIndexedChainingStrategyNeedsKey()
|
||||
{
|
||||
$twig = $this->createTwig('indexed');
|
||||
|
||||
$twig->render('ics_no_key.twig', array('value' => $this->getValue('asm89', 1)));
|
||||
}
|
||||
|
||||
public function testAnnotationExpression()
|
||||
{
|
||||
$twig = $this->createTwig('indexed');
|
||||
|
||||
$rendered = $twig->render('annotation_expression.twig', array('value' => $this->getValue('asm89', 1), 'version' => 1));
|
||||
$this->assertEquals('Hello asm89!Hello asm89!', $rendered);
|
||||
}
|
||||
}
|
||||
|
||||
class KeyGenerator implements KeyGeneratorInterface
|
||||
{
|
||||
public function generateKey($value)
|
||||
{
|
||||
return get_class($value) . '_' . $value->getUpdatedAt();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Value
|
||||
{
|
||||
private $value;
|
||||
private $updatedAt;
|
||||
|
||||
public function __construct($value, $updatedAt)
|
||||
{
|
||||
$this->value = $value;
|
||||
$this->updatedAt = $updatedAt;
|
||||
}
|
||||
|
||||
public function getUpdatedAt()
|
||||
{
|
||||
return $this->updatedAt;
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
{%- set k = "v'" ~ version -%}
|
||||
{%- cache k {time: 10} -%}Hello {{ value }}!{%- endcache -%}
|
||||
{%- cache 'bob'|capitalize {gcs: value} %}Hello {{ value }}!{%- endcache -%}
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
{% set annotation = 'v1' %}
|
||||
{% cache annotation value %}Hello {{ value }}!{% endcache %}
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
{% cache 'v1' value %}Hello {{ value }}!{% endcache %}
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
{% cache 'v2' value %}Hello {{ value }}!{% endcache %}
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
{% cache 'v1' 10 %}Hello {{ value }}!{% endcache %}
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
{% cache 'v1' {time: 10} %}Hello {{ value }}!{% endcache %}
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
{% cache 'v1' 10 %}Hello {{ value }}!{% endcache %}
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of twig-cache-extension.
|
||||
*
|
||||
* (c) Alexander <iam.asm89@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
if (file_exists($file = __DIR__.'/../vendor/autoload.php')) {
|
||||
$autoload = require_once $file;
|
||||
} else {
|
||||
throw new RuntimeException('Install dependencies to run test suite.');
|
||||
}
|
||||
Reference in new issue
Block a user