diff --git a/composer.json b/composer.json index 593c5546..de11e658 100644 --- a/composer.json +++ b/composer.json @@ -9,9 +9,8 @@ "email": "dev-team@ez.no" } ], - "minimum-stability": "stable", "require": { - "ezsystems/ezpublish-kernel": "^6.7@dev || ^7.0@dev", + "ezsystems/ezpublish-kernel": "~6.7.7@dev || ^6.12.1@dev || ^7.0@dev", "friendsofsymfony/http-cache-bundle": "~1.2|^1.3.8", "symfony/symfony": "^2.7 | ^3.1" }, @@ -33,7 +32,7 @@ }, "extra": { "branch-alias": { - "dev-master": "0.1.x-dev" + "dev-master": "0.3.x-dev" } } } diff --git a/docs/drivers.md b/docs/drivers.md new file mode 100644 index 00000000..a0ba24ca --- /dev/null +++ b/docs/drivers.md @@ -0,0 +1,66 @@ +# Driver support + +You may add integration with other http caches using the extension points provided by this bundle. + +The following extension points are available + - PurgeClient + - TagHandler + - FOS TagHandler + +If you write a new PurgeClient driver, you **must** also create a corresponding TagHandler and vice +versa. Creating a FOS TagHandler is optional. + + +## PurgeClient + +The PurgeClient is responsible for sending purge requests to the http cache when content is about to be invalidated. +The PurgeClient must implement EzSystems\PlatformHttpCacheBundle\PurgeClient\PurgeClientInterface and can be registered +with the following code in services.yml: + +``` +services: + ezplatform.http_cache_myhttpcachebundle.purge_client.myhttpcache: + class: EzSystems\PlatformMyHttpCacheBundle\PurgeClient\MyHttpCachePurgeClient + arguments: ['@ezplatform.http_cache.cache_manager'] + tags: + - {name: ezplatform.http_cache.purge_client, purge_type: myhttpcache} +``` + +Any service which implements the PurgeClientInterface must be tagged with `ezplatform.http_cache.purge_client` in order +to be registered as such. +`purge_type` specifies what the value of the purge_type setting in `app/config/ezplatform.yml` should be in order to +enable this driver. + + +## TagHandler + +The TagHandler is responsible for tagging responses with headers which the http cache recognizes. +The TagHandler must implement EzSystems\PlatformHttpCacheBundle\Handler\TagHandlerInterface and can be registered with +the following code in services.yml: + +``` + ezplatform.http_cache_myhttpcachebundle.tag_handler.myhttpcache: + class: EzSystems\PlatformMyHttpCacheBundle\Handler\MyHttpCacheTagHandler + tags: + - {name: ezplatform.http_cache.tag_handler, purge_type: myhttpcache} + +``` + +Any service which implements the TagHandlerInterface must be tagged with `ezplatform.http_cache.tag_handler` in order +to be registered as such. + +## FOS TagHandler + +The FOS Http cache bundle also has a TagHandler which is not used by eZ Platform except for one thing, the +`fos:httpcache:invalidate:tag` command. With this command you may explicitly invalidate cache by tag. + +Normally, you would not need to implement your own FOS TagHandler as the ezplatform-http-cache bundle ships with a +default one which uses the PurgeClient to invalidate the given tags. +If you need to write your own FOS TagHandler anyway, you may register it with the following code in services.yml: + +``` + ezplatform.http_cache_myhttpcachebundle.fos_tag_handler.myhttpcache: + class: EzSystems\PlatformMyHttpCacheBundle\Handler\MyHttpCacheFosTagHandler + tags: + - {name: ezplatform.http_cache.fos_tag_handler, purge_type: myhttpcache} +``` diff --git a/spec/DependencyInjection/Compiler/KernelPassSpec.php b/spec/DependencyInjection/Compiler/KernelPassSpec.php index 5b6fcdcb..0a37296e 100644 --- a/spec/DependencyInjection/Compiler/KernelPassSpec.php +++ b/spec/DependencyInjection/Compiler/KernelPassSpec.php @@ -52,8 +52,6 @@ function it_disables_the_kernels_httpcache_services(ContainerBuilder $container, ] ])->shouldBeCalled(); - $container->getParameter('purge_type')->shouldBeCalled(); - $this->process($container); } } diff --git a/src/DependencyInjection/Compiler/DriverPass.php b/src/DependencyInjection/Compiler/DriverPass.php new file mode 100644 index 00000000..b9310f7c --- /dev/null +++ b/src/DependencyInjection/Compiler/DriverPass.php @@ -0,0 +1,72 @@ +removeAlias('ezpublish.http_cache.purge_client'); + + $purgeType = $container->getParameter('ezpublish.http_cache.purge_type'); + $configuredPurgeClientServiceId = static::getTaggedService($container, 'ezplatform.http_cache.purge_client'); + if ($configuredPurgeClientServiceId === null) { + throw new \InvalidArgumentException("No driver found being able to handle purge_type '$purgeType'."); + } + $container->setAlias('ezplatform.http_cache.purge_client', $configuredPurgeClientServiceId); + + // TagHandler is responsible for setting correct tags (recognized by the http cache) on responses + $configuredTagHandlerServiceId = static::getTaggedService($container, 'ezplatform.http_cache.tag_handler'); + if ($configuredTagHandlerServiceId !== null) { + $container->setAlias('ezplatform.http_cache.tag_handler', $configuredTagHandlerServiceId); + } + + // FOS TagHandler is making sure running "php app/console fos:httpcache:invalidate:tag " works + $configuredFosTagHandlerServiceId = static::getTaggedService($container, 'ezplatform.http_cache.fos_tag_handler'); + if ($configuredFosTagHandlerServiceId === null) { + // We default to xkey handler. This one should anyway work for most drivers as it just passes a purge request + // on to the purge client + $configuredFosTagHandlerServiceId = 'ezplatform.http_cache.tag_handler.xkey'; + } + $fosTagHandlerDefinition = $container->getDefinition($configuredFosTagHandlerServiceId); + $definition = $container->getDefinition('fos_http_cache.handler.tag_handler'); + $definition->setClass($fosTagHandlerDefinition->getClass()); + $definition->addArgument(new Reference('ezplatform.http_cache.purge_client')); + } + + public static function getTaggedService(ContainerBuilder $container, $tag) + { + $purgeType = $container->getParameter('ezpublish.http_cache.purge_type'); + $configuredTagHandlerServiceId = null; + + $tagHandlerServiceIds = $container->findTaggedServiceIds($tag); + foreach ($tagHandlerServiceIds as $tagHandlerServiceId => $attributes) { + $currentPurgeTypeId = null; + $currentTagHandlerServiceId = null; + foreach ($attributes as $attribute) { + if (array_key_exists('purge_type', $attribute)) { + $currentPurgeTypeId = $attribute['purge_type']; + } + if ($currentPurgeTypeId !== null) { + if ($purgeType === $attribute['purge_type']) { + $configuredTagHandlerServiceId = $tagHandlerServiceId; + break 2; + } + } + } + if ($currentPurgeTypeId === null) { + throw new \InvalidArgumentException("Missing attribute 'purge_type' in tagged service '$tagHandlerServiceId'."); + } + } + + return $configuredTagHandlerServiceId; + } +} diff --git a/src/DependencyInjection/Compiler/KernelPass.php b/src/DependencyInjection/Compiler/KernelPass.php index 099590d6..f4e26cf4 100644 --- a/src/DependencyInjection/Compiler/KernelPass.php +++ b/src/DependencyInjection/Compiler/KernelPass.php @@ -7,8 +7,6 @@ use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Reference; -use EzSystems\PlatformHttpCacheBundle\Handler\TagHandler; /** * Disables some of the http-cache services declared by the kernel so that @@ -43,18 +41,6 @@ public function process(ContainerBuilder $container) return true; })); $container->getDefinition('cache_clearer')->setArguments($arguments); - - if ($container->getAlias('ezpublish.http_cache.purge_client') == 'ezpublish.http_cache.purge_client.fos') { - $container->setAlias('ezplatform.http_cache.purge_client', 'ezplatform.http_cache.purge_client.fos'); - } - - $purgeType = $container->getParameter('purge_type'); - if ($purgeType === 'http') { - // Injecting our own Tag handler - $definition = $container->getDefinition('fos_http_cache.handler.tag_handler'); - $definition->setClass(TagHandler::class); - $definition->addArgument(new Reference('ezplatform.http_cache.purge_client.fos')); - } } /** diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php index dbb42403..f2e0fd9c 100644 --- a/src/DependencyInjection/Configuration.php +++ b/src/DependencyInjection/Configuration.php @@ -18,7 +18,7 @@ class Configuration implements ConfigurationInterface public function getConfigTreeBuilder() { $treeBuilder = new TreeBuilder(); - $rootNode = $treeBuilder->root('ez_systems_platform_http_cache'); + $rootNode = $treeBuilder->root('ez_platform_http_cache'); // Here you should define the parameters that are allowed to // configure your bundle. See the documentation linked above for diff --git a/src/DependencyInjection/EzSystemsPlatformHttpCacheExtension.php b/src/DependencyInjection/EzPlatformHttpCacheExtension.php similarity index 93% rename from src/DependencyInjection/EzSystemsPlatformHttpCacheExtension.php rename to src/DependencyInjection/EzPlatformHttpCacheExtension.php index 74567baa..bf6e0769 100644 --- a/src/DependencyInjection/EzSystemsPlatformHttpCacheExtension.php +++ b/src/DependencyInjection/EzPlatformHttpCacheExtension.php @@ -10,7 +10,7 @@ use Symfony\Component\DependencyInjection\Loader; use Symfony\Component\Yaml\Yaml; -class EzSystemsPlatformHttpCacheExtension extends Extension implements PrependExtensionInterface +class EzPlatformHttpCacheExtension extends Extension implements PrependExtensionInterface { public function load(array $configs, ContainerBuilder $container) { diff --git a/src/EzSystemsPlatformHttpCacheBundle.php b/src/EzSystemsPlatformHttpCacheBundle.php index 33e5c947..cf95cd23 100644 --- a/src/EzSystemsPlatformHttpCacheBundle.php +++ b/src/EzSystemsPlatformHttpCacheBundle.php @@ -4,8 +4,10 @@ use EzSystems\PlatformHttpCacheBundle\DependencyInjection\Compiler\ResponseTaggersPass; use EzSystems\PlatformHttpCacheBundle\DependencyInjection\Compiler\KernelPass; +use EzSystems\PlatformHttpCacheBundle\DependencyInjection\Compiler\DriverPass; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\HttpKernel\Bundle\Bundle; +use EzSystems\PlatformHttpCacheBundle\DependencyInjection\EzPlatformHttpCacheExtension; class EzSystemsPlatformHttpCacheBundle extends Bundle { @@ -15,5 +17,11 @@ public function build(ContainerBuilder $container) $container->addCompilerPass(new ResponseTaggersPass()); $container->addCompilerPass(new KernelPass()); + $container->addCompilerPass(new DriverPass()); + } + + public function getContainerExtension() + { + return new EzPlatformHttpCacheExtension(); } } diff --git a/src/PurgeClient/PurgeClientInterface.php b/src/PurgeClient/PurgeClientInterface.php index 3cd860ae..20605951 100644 --- a/src/PurgeClient/PurgeClientInterface.php +++ b/src/PurgeClient/PurgeClientInterface.php @@ -16,11 +16,16 @@ interface PurgeClientInterface extends KernelPurgeClientInterface { /** - * Triggers the cache purge $locationIds. + * Triggers the cache purge of $tags. * - * It's up to the implementor to decide whether to purge $locationIds right away or to delegate to a separate process. + * It's up to the implementor to decide whether to purge tags right away or to delegate to a separate process. * - * @param array $locationIds Cache resource(s) to purge (e.g. array of URI to purge in a reverse proxy) + * @param array $tags Array of tags to purge */ - public function purge($locationIds); + public function purge($tags); + + /** + * Purge the whole http cache. + */ + public function purgeAll(); } diff --git a/src/PurgeClient/FOSPurgeClient.php b/src/PurgeClient/VarnishPurgeClient.php similarity index 88% rename from src/PurgeClient/FOSPurgeClient.php rename to src/PurgeClient/VarnishPurgeClient.php index ce7beee6..672d69c4 100644 --- a/src/PurgeClient/FOSPurgeClient.php +++ b/src/PurgeClient/VarnishPurgeClient.php @@ -10,11 +10,8 @@ /** * Purge client based on FOSHttpCacheBundle. - * - * Only support BAN requests on purpose, to be able to invalidate cache for a - * collection of Location/Content objects. */ -class FOSPurgeClient implements PurgeClientInterface +class VarnishPurgeClient implements PurgeClientInterface { /** * @var \FOS\HttpCacheBundle\CacheManager diff --git a/src/Resources/config/services.yml b/src/Resources/config/services.yml index 2c73ad9d..f9130bbb 100644 --- a/src/Resources/config/services.yml +++ b/src/Resources/config/services.yml @@ -5,27 +5,35 @@ services: ezplatform.http_cache.proxy_client.varnish.factory: class: EzSystems\PlatformHttpCacheBundle\PurgeClient\VarnishProxyClientFactory - arguments: ["@ezpublish.config.resolver", "@ezpublish.config.dynamic_setting.parser", "%fos_http_cache.proxy_client.varnish.class%"] + arguments: ['@ezpublish.config.resolver', '@ezpublish.config.dynamic_setting.parser', '%fos_http_cache.proxy_client.varnish.class%'] ezplatform.http_cache.purge_client: alias: ezplatform.http_cache.purge_client.local - ezplatform.http_cache.purge_client.fos: - class: EzSystems\PlatformHttpCacheBundle\PurgeClient\FOSPurgeClient - arguments: ["@ezplatform.http_cache.cache_manager"] + ezplatform.http_cache.purge_client.varnish: + class: EzSystems\PlatformHttpCacheBundle\PurgeClient\VarnishPurgeClient + arguments: ['@ezplatform.http_cache.cache_manager'] + tags: + - {name: ezplatform.http_cache.purge_client, purge_type: http} + - {name: ezplatform.http_cache.purge_client, purge_type: varnish} ezplatform.http_cache.purge_client.local: class: EzSystems\PlatformHttpCacheBundle\PurgeClient\LocalPurgeClient - arguments: ["@ezplatform.http_cache.store"] + arguments: ['@ezplatform.http_cache.store'] + tags: + - {name: ezplatform.http_cache.purge_client, purge_type: local} ezplatform.http_cache.store: alias: ezplatform.http_cache.tag_aware_store ezplatform.http_cache.tag_aware_store: class: EzSystems\PlatformHttpCacheBundle\Proxy\TagAwareStore - arguments: ["%ezplatform.http_cache.store.root%"] + arguments: ['%ezplatform.http_cache.store.root%'] ezplatform.http_cache.tag_handler: + alias: ezplatform.http_cache.tag_handler.xkey + + ezplatform.http_cache.tag_handler.xkey: class: EzSystems\PlatformHttpCacheBundle\Handler\TagHandler arguments: - '@ezplatform.http_cache.cache_manager' diff --git a/tests/PurgeClient/FOSPurgeClientTest.php b/tests/PurgeClient/VarnishPurgeClientTest.php similarity index 91% rename from tests/PurgeClient/FOSPurgeClientTest.php rename to tests/PurgeClient/VarnishPurgeClientTest.php index 812eb4a4..5dd9f22a 100644 --- a/tests/PurgeClient/FOSPurgeClientTest.php +++ b/tests/PurgeClient/VarnishPurgeClientTest.php @@ -8,13 +8,13 @@ */ namespace EzSystems\PlatformHttpCacheBundle\Tests\PurgeClient; -use EzSystems\PlatformHttpCacheBundle\PurgeClient\FOSPurgeClient; +use EzSystems\PlatformHttpCacheBundle\PurgeClient\VarnishPurgeClient; use FOS\HttpCache\ProxyClient\ProxyClientInterface; use FOS\HttpCacheBundle\CacheManager; use PHPUnit\Framework\TestCase; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; -class FOSPurgeClientTest extends TestCase +class VarnishPurgeClientTest extends TestCase { /** * @var \PHPUnit_Framework_MockObject_MockObject @@ -22,7 +22,7 @@ class FOSPurgeClientTest extends TestCase private $cacheManager; /** - * @var FOSPurgeClient + * @var VarnishPurgeClient */ private $purgeClient; @@ -39,7 +39,7 @@ protected function setUp() ) ) ->getMock(); - $this->purgeClient = new FOSPurgeClient($this->cacheManager); + $this->purgeClient = new VarnishPurgeClient($this->cacheManager); } public function testPurgeNoLocationIds()