Skip to content

Commit df49b62

Browse files
committed
Merge remote-tracking branch 'driebit/master'
2 parents 7368517 + 718407f commit df49b62

24 files changed

+1353
-0
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
vendor/
2+
composer.lock
3+
phpunit.xml

.travis.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
language: php
2+
3+
php:
4+
- 5.3
5+
- 5.4
6+
7+
before_script:
8+
- composer install --dev
9+
10+
script: phpunit --coverage-text

CacheManager.php

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
<?php
2+
3+
namespace Driebit\HttpCacheBundle;
4+
5+
use Driebit\HttpCacheBundle\HttpCache\HttpCacheInterface;
6+
use Symfony\Component\Routing\RouterInterface;
7+
8+
/**
9+
* Manages HTTP cache invalidations
10+
*
11+
*/
12+
class CacheManager
13+
{
14+
/**
15+
* @var HttpCacheInterface
16+
*/
17+
protected $cache;
18+
19+
/**
20+
* @var RouterInterface
21+
*/
22+
protected $router;
23+
24+
/**
25+
* Invalidation queue
26+
*
27+
* @var array
28+
*/
29+
protected $invalidationQueue = array();
30+
31+
/**
32+
* Constructor
33+
*
34+
* @param HttpCacheInterface $cache HTTP cache
35+
* @param RouterInterface $router Symfony router
36+
*/
37+
public function __construct(HttpCacheInterface $cache, RouterInterface $router)
38+
{
39+
$this->cache = $cache;
40+
$this->router = $router;
41+
}
42+
43+
/**
44+
* Invalidate a path (URL)
45+
*
46+
* @param string $path Path
47+
*
48+
* @return $this
49+
*/
50+
public function invalidatePath($path)
51+
{
52+
$this->invalidationQueue[$path] = $path;
53+
54+
return $this;
55+
}
56+
57+
/**
58+
* Invalidate a route
59+
*
60+
* @param string $name Route name
61+
* @param array $parameters Route parameters (optional)
62+
*
63+
* @return $this
64+
*/
65+
public function invalidateRoute($name, $parameters = array())
66+
{
67+
$this->invalidatePath($this->router->generate($name, $parameters));
68+
69+
return $this;
70+
}
71+
72+
/**
73+
* Flush all paths queued for invalidation
74+
*
75+
* @return array Paths that were flushed from the queue
76+
*/
77+
public function flush()
78+
{
79+
$queue = $this->getInvalidationQueue();
80+
81+
if (0 === count($queue)) {
82+
return $queue;
83+
}
84+
85+
$this->cache->invalidateUrls($queue);
86+
$this->invalidationQueue = array();
87+
88+
return $queue;
89+
}
90+
91+
/**
92+
* Get paths (URLs) that are queued for invalidation
93+
*
94+
* @return array
95+
*/
96+
public function getInvalidationQueue()
97+
{
98+
return \array_values($this->invalidationQueue);
99+
}
100+
}

DependencyInjection/Configuration.php

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
<?php
2+
3+
namespace Driebit\HttpCacheBundle\DependencyInjection;
4+
5+
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
6+
use Symfony\Component\Config\Definition\ConfigurationInterface;
7+
8+
/**
9+
* This is the class that validates and merges configuration from your app/config files
10+
*
11+
* To learn more see {@link http://symfony.com/doc/current/cookbook/bundles/extension.html#cookbook-bundles-extension-config-class}
12+
*/
13+
class Configuration implements ConfigurationInterface
14+
{
15+
/**
16+
* {@inheritDoc}
17+
*/
18+
public function getConfigTreeBuilder()
19+
{
20+
$treeBuilder = new TreeBuilder();
21+
$rootNode = $treeBuilder->root('driebit_http_cache');
22+
23+
$rootNode
24+
->children()
25+
->arrayNode('http_cache')->isRequired()
26+
->children()
27+
->arrayNode('varnish')
28+
->children()
29+
->scalarNode('host')->isRequired()->end()
30+
->arrayNode('ips')
31+
->isRequired()
32+
->requiresAtLeastOneElement()
33+
->prototype('scalar')->end()
34+
->end()
35+
->end()
36+
->end()
37+
->end()
38+
->end()
39+
->arrayNode('invalidators')
40+
->useAttributeAsKey('name')
41+
->prototype('array')
42+
->children()
43+
->arrayNode('origin_routes')
44+
->isRequired()
45+
->requiresAtLeastOneElement()
46+
->prototype('scalar')->end()
47+
->end()
48+
->arrayNode('invalidate_routes')
49+
->useAttributeAsKey('name')
50+
->prototype('array')
51+
->children()
52+
->scalarNode('parameter_mapper')->end()
53+
->booleanNode('ignore_extra_params')->defaultTrue()->end()
54+
->end()
55+
->end()
56+
->end()
57+
->end()
58+
->end()
59+
->end()
60+
->end();
61+
62+
return $treeBuilder;
63+
}
64+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php
2+
3+
namespace Driebit\HttpCacheBundle\DependencyInjection;
4+
5+
use Symfony\Component\DependencyInjection\ContainerBuilder;
6+
use Symfony\Component\Config\FileLocator;
7+
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
8+
use Symfony\Component\DependencyInjection\Loader;
9+
10+
/**
11+
* This is the class that loads and manages your bundle configuration
12+
*
13+
* To learn more see {@link http://symfony.com/doc/current/cookbook/bundles/extension.html}
14+
*/
15+
class DriebitHttpCacheExtension extends Extension
16+
{
17+
/**
18+
* {@inheritDoc}
19+
*/
20+
public function load(array $configs, ContainerBuilder $container)
21+
{
22+
$configuration = new Configuration();
23+
$config = $this->processConfiguration($configuration, $configs);
24+
25+
$loader = new Loader\XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
26+
$loader->load('services.xml');
27+
$container->setParameter('driebit_http_cache.invalidators', $config['invalidators']);
28+
29+
if (isset($config['http_cache']['varnish'])) {
30+
$loader->load('varnish.xml');
31+
$container->setParameter('driebit_http_cache.http_cache.varnish.ips', $config['http_cache']['varnish']['ips']);
32+
$container->setParameter('driebit_http_cache.http_cache.varnish.host', $config['http_cache']['varnish']['host']);
33+
34+
}
35+
}
36+
}

DriebitHttpCacheBundle.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
3+
namespace Driebit\HttpCacheBundle;
4+
5+
use Symfony\Component\HttpKernel\Bundle\Bundle;
6+
7+
class DriebitHttpCacheBundle extends Bundle
8+
{
9+
}
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
<?php
2+
3+
namespace Driebit\HttpCacheBundle\EventListener;
4+
5+
use Driebit\HttpCacheBundle\CacheManager;
6+
use Driebit\HttpCacheBundle\Invalidator\InvalidatorCollection;
7+
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
8+
use Symfony\Component\HttpKernel\KernelEvents;
9+
use Symfony\Component\HttpKernel\Event\PostResponseEvent;
10+
use Symfony\Component\Routing\RouterInterface;
11+
12+
/**
13+
* On kernel.terminate event, this listener invalidates routes for the current request and flushes the cache manager
14+
*
15+
* @author David de Boer <[email protected]>
16+
*/
17+
class InvalidationListener implements EventSubscriberInterface
18+
{
19+
/**
20+
* Cache manager
21+
*
22+
* @var CacheManager
23+
*/
24+
protected $cacheManager;
25+
26+
/**
27+
* Invalidator collection
28+
*
29+
* @var InvalidatorCollection
30+
*/
31+
protected $invalidators;
32+
33+
/**
34+
* Router
35+
*
36+
* @var RouterInterface
37+
*/
38+
protected $router;
39+
40+
/**
41+
* Constructor
42+
*
43+
* @param CacheManager $cacheManager
44+
* @param InvalidatorCollection $invalidators
45+
* @param RouterInterface $router
46+
*/
47+
public function __construct(
48+
CacheManager $cacheManager,
49+
InvalidatorCollection $invalidators,
50+
RouterInterface $router
51+
) {
52+
$this->cacheManager = $cacheManager;
53+
$this->invalidators = $invalidators;
54+
$this->router = $router;
55+
}
56+
57+
/**
58+
* Apply invalidators and flush cache manager
59+
*
60+
* On kernel.terminate:
61+
* - see if any invalidators apply to the current request and, if so, add
62+
* their routes to the cache manager
63+
* - flush the cache manager in order to send invalidation requests to the
64+
* HTTP cache.
65+
*
66+
* @param PostResponseEvent $event
67+
*
68+
* @return array Paths that were flushed from the invalidation queue
69+
*/
70+
public function onKernelTerminate(PostResponseEvent $event)
71+
{
72+
// Are there any invalidators configured for the current request route?
73+
$request = $event->getRequest();
74+
$requestRoute = $request->attributes->get('_route');
75+
if (!$this->invalidators->hasInvalidatorRoute($requestRoute)) {
76+
return $this->cacheManager->flush();
77+
}
78+
79+
// Don't invalidate any caches if the request was unsuccessful
80+
$response = $event->getResponse();
81+
if (!$response->isSuccessful()) {
82+
return $this->cacheManager->flush();
83+
}
84+
85+
$requestParams = $request->attributes->get('_route_params');
86+
$invalidators = $this->invalidators->getInvalidators($requestRoute);
87+
foreach ($invalidators as $invalidator) {
88+
foreach ($invalidator->getInvalidatedRoutes() as $route => $config) {
89+
$path = $this->router->generate($route, $requestParams);
90+
91+
// If extra route parameters should be ignored, strip the query
92+
// string generated by the Symfony router from the path
93+
if (isset($config['ignore_extra_params'])
94+
&& $config['ignore_extra_params']
95+
&& $pos = strpos($path, '?')
96+
) {
97+
$path = substr($path, 0, $pos);
98+
}
99+
100+
$this->cacheManager->invalidatePath($path);
101+
}
102+
}
103+
104+
return $this->cacheManager->flush();
105+
}
106+
107+
/**
108+
* {@inheritdoc}
109+
*/
110+
public static function getSubscribedEvents()
111+
{
112+
return array(KernelEvents::TERMINATE => 'onKernelTerminate');
113+
}
114+
}

HttpCache/HttpCacheInterface.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
namespace Driebit\HttpCacheBundle\HttpCache;
4+
5+
/**
6+
* A shared HTTP cache
7+
*
8+
* @author David de Boer <[email protected]>
9+
*/
10+
interface HttpCacheInterface
11+
{
12+
/**
13+
* Invalidate one URL
14+
*
15+
* @param string $url
16+
*/
17+
public function invalidateUrl($url);
18+
19+
/**
20+
* Invalidate multiple URLs
21+
*
22+
* @param array $urls
23+
*/
24+
public function invalidateUrls(array $urls);
25+
}

0 commit comments

Comments
 (0)