Permalink
Browse files

feature #10702 [HttpKernel][FrameworkBundle] SSI support (KingCrunch)

This PR was merged into the 2.6-dev branch.

Discussion
----------

[HttpKernel][FrameworkBundle] SSI support

| Q             | A
| ------------- | ---
| Bug fix?      | No
| New feature?  | Yes
| BC breaks?    | No
| Deprecations? | No
| Tests pass?   | Yes
| Fixed tickets | #9419 (, #10684)
| License       | MIT

It does not support comments, or alternative URIs, or "continue" in case of errors. Maybe I can workaround that, but I've decided to left it out for this PR. Especially as far as I can see a "alternative URIs"-hack would _always_ lead to two requests, even if it's not needed.

Commits
-------

06cea08 SSI support
  • Loading branch information...
2 parents fb9dc6a + 06cea08 commit bf140a8487a19d767b9b57102e74c842e8f0a208 @fabpot fabpot committed Jul 25, 2014
Showing with 1,019 additions and 207 deletions.
  1. +12 −0 src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php
  2. +17 −0 src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
  3. +14 −2 src/Symfony/Bundle/FrameworkBundle/HttpCache/HttpCache.php
  4. +13 −0 src/Symfony/Bundle/FrameworkBundle/Resources/config/fragment_renderer.xml
  5. +20 −0 src/Symfony/Bundle/FrameworkBundle/Resources/config/ssi.xml
  6. +1 −0 src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php
  7. +3 −38 src/Symfony/Component/HttpKernel/EventListener/EsiListener.php
  8. +58 −0 src/Symfony/Component/HttpKernel/EventListener/SurrogateListener.php
  9. +79 −0 src/Symfony/Component/HttpKernel/Fragment/AbstractSurrogateFragmentRenderer.php
  10. +1 −57 src/Symfony/Component/HttpKernel/Fragment/EsiFragmentRenderer.php
  11. +59 −0 src/Symfony/Component/HttpKernel/Fragment/SsiFragmentRenderer.php
  12. +48 −3 src/Symfony/Component/HttpKernel/HttpCache/Esi.php
  13. +3 −57 src/Symfony/Component/HttpKernel/HttpCache/EsiResponseCacheStrategy.php
  14. +5 −18 src/Symfony/Component/HttpKernel/HttpCache/EsiResponseCacheStrategyInterface.php
  15. +37 −18 src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php
  16. +85 −0 src/Symfony/Component/HttpKernel/HttpCache/ResponseCacheStrategy.php
  17. +41 −0 src/Symfony/Component/HttpKernel/HttpCache/ResponseCacheStrategyInterface.php
  18. +197 −0 src/Symfony/Component/HttpKernel/HttpCache/Ssi.php
  19. +103 −0 src/Symfony/Component/HttpKernel/HttpCache/SurrogateInterface.php
  20. +4 −4 src/Symfony/Component/HttpKernel/Tests/EventListener/EsiListenerTest.php
  21. +10 −10 src/Symfony/Component/HttpKernel/Tests/HttpCache/EsiTest.php
  22. +209 −0 src/Symfony/Component/HttpKernel/Tests/HttpCache/SsiTest.php
@@ -81,6 +81,7 @@ public function getConfigTreeBuilder()
$this->addCsrfSection($rootNode);
$this->addFormSection($rootNode);
$this->addEsiSection($rootNode);
+ $this->addSsiSection($rootNode);
$this->addFragmentsSection($rootNode);
$this->addProfilerSection($rootNode);
$this->addRouterSection($rootNode);
@@ -148,6 +149,17 @@ private function addEsiSection(ArrayNodeDefinition $rootNode)
;
}
+ private function addSsiSection(ArrayNodeDefinition $rootNode)
+ {
+ $rootNode
+ ->children()
+ ->arrayNode('ssi')
+ ->info('ssi configuration')
+ ->canBeEnabled()
+ ->end()
+ ->end();
+ }
+
private function addFragmentsSection(ArrayNodeDefinition $rootNode)
{
$rootNode
@@ -122,6 +122,7 @@ public function load(array $configs, ContainerBuilder $container)
$this->registerValidationConfiguration($config['validation'], $container, $loader);
$this->registerEsiConfiguration($config['esi'], $container, $loader);
+ $this->registerSsiConfiguration($config['ssi'], $container, $loader);
$this->registerFragmentsConfiguration($config['fragments'], $container, $loader);
$this->registerProfilerConfiguration($config['profiler'], $container, $loader);
$this->registerTranslatorConfiguration($config['translator'], $container);
@@ -209,6 +210,22 @@ private function registerEsiConfiguration(array $config, ContainerBuilder $conta
}
/**
+ * Loads the SSI configuration.
+ *
+ * @param array $config An SSI configuration array
+ * @param ContainerBuilder $container A ContainerBuilder instance
+ * @param XmlFileLoader $loader An XmlFileLoader instance
+ */
+ private function registerSsiConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader)
+ {
+ if (!$this->isConfigEnabled($container, $config)) {
+ return;
+ }
+
+ $loader->load('ssi.xml');
+ }
+
+ /**
* Loads the fragments configuration.
*
* @param array $config A fragments configuration array
@@ -39,7 +39,7 @@ public function __construct(HttpKernelInterface $kernel, $cacheDir = null)
$this->kernel = $kernel;
$this->cacheDir = $cacheDir;
- parent::__construct($kernel, $this->createStore(), $this->createEsi(), array_merge(array('debug' => $kernel->isDebug()), $this->getOptions()));
+ parent::__construct($kernel, $this->createStore(), $this->createSurrogate(), array_merge(array('debug' => $kernel->isDebug()), $this->getOptions()));
}
/**
@@ -55,7 +55,7 @@ protected function forward(Request $request, $raw = false, Response $entry = nul
{
$this->getKernel()->boot();
$this->getKernel()->getContainer()->set('cache', $this);
- $this->getKernel()->getContainer()->set('esi', $this->getEsi());
+ $this->getKernel()->getContainer()->set($this->getSurrogate()->getName(), $this->getSurrogate());
return parent::forward($request, $raw, $entry);
}
@@ -70,6 +70,18 @@ protected function getOptions()
return array();
}
+ protected function createSurrogate()
+ {
+ return $this->createEsi();
+ }
+
+ /**
+ * Creates new ESI instance
+ *
+ * @return Esi
+ *
+ * @deprecated Deprecated since version 2.6, to be removed in 3.0. Use createSurrogate() instead
+ */
protected function createEsi()
{
return new Esi();
@@ -10,6 +10,7 @@
<parameter key="fragment.renderer.hinclude.class">Symfony\Bundle\FrameworkBundle\Fragment\ContainerAwareHIncludeFragmentRenderer</parameter>
<parameter key="fragment.renderer.hinclude.global_template"></parameter>
<parameter key="fragment.renderer.esi.class">Symfony\Component\HttpKernel\Fragment\EsiFragmentRenderer</parameter>
+ <parameter key="fragment.renderer.ssi.class">Symfony\Component\HttpKernel\Fragment\SsiFragmentRenderer</parameter>
<parameter key="fragment.path">/_fragment</parameter>
</parameters>
@@ -41,5 +42,17 @@
<argument type="service" id="fragment.renderer.inline" />
<call method="setFragmentPath"><argument>%fragment.path%</argument></call>
</service>
+
+ <service id="fragment.renderer.ssi" class="%fragment.renderer.ssi.class%">
+ <tag name="kernel.fragment_renderer" />
+ <argument type="service" id="ssi" on-invalid="null" />
+ <argument type="service" id="fragment.renderer.inline" />
+ <call method="setFragmentPath">
+ <argument>%fragment.path%</argument>
+ </call>
+ <call method="setUriSigner">
+ <argument type="service" id="uri_signer" />
+ </call>
+ </service>
</services>
</container>
@@ -0,0 +1,20 @@
+<?xml version="1.0" ?>
+
+<container xmlns="http://symfony.com/schema/dic/services"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
+
+ <parameters>
+ <parameter key="ssi.class">Symfony\Component\HttpKernel\HttpCache\Ssi</parameter>
+ <parameter key="ssi_listener.class">Symfony\Component\HttpKernel\EventListener\SurrogateListener</parameter>
+ </parameters>
+
+ <services>
+ <service id="ssi" class="%ssi.class%" />
+
+ <service id="ssi_listener" class="%ssi_listener.class%">
+ <tag name="kernel.event_subscriber" />
+ <argument type="service" id="ssi" on-invalid="ignore" />
+ </service>
+ </services>
+</container>
@@ -105,6 +105,7 @@ protected static function getBundleDefaultConfig()
'field_name' => '_token',
),
'esi' => array('enabled' => false),
+ 'ssi' => array('enabled' => false),
'fragments' => array(
'enabled' => false,
'path' => '/_fragment',
@@ -11,48 +11,13 @@
namespace Symfony\Component\HttpKernel\EventListener;
-use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
-use Symfony\Component\HttpKernel\KernelEvents;
-use Symfony\Component\HttpKernel\HttpCache\Esi;
-use Symfony\Component\EventDispatcher\EventSubscriberInterface;
-
/**
* EsiListener adds a Surrogate-Control HTTP header when the Response needs to be parsed for ESI.
*
* @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @deprecated Deprecated since version 2.6, to be removed in 3.0. Use SurrogateListener instead
*/
-class EsiListener implements EventSubscriberInterface
+class EsiListener extends SurrogateListener
{
- private $esi;
-
- /**
- * Constructor.
- *
- * @param Esi $esi An ESI instance
- */
- public function __construct(Esi $esi = null)
- {
- $this->esi = $esi;
- }
-
- /**
- * Filters the Response.
- *
- * @param FilterResponseEvent $event A FilterResponseEvent instance
- */
- public function onKernelResponse(FilterResponseEvent $event)
- {
- if (!$event->isMasterRequest() || null === $this->esi) {
- return;
- }
-
- $this->esi->addSurrogateControl($event->getResponse());
- }
-
- public static function getSubscribedEvents()
- {
- return array(
- KernelEvents::RESPONSE => 'onKernelResponse',
- );
- }
}
@@ -0,0 +1,58 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpKernel\EventListener;
+
+use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
+use Symfony\Component\HttpKernel\HttpCache\SurrogateInterface;
+use Symfony\Component\HttpKernel\KernelEvents;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+
+/**
+ * SurrogateListener adds a Surrogate-Control HTTP header when the Response needs to be parsed for Surrogates
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class SurrogateListener implements EventSubscriberInterface
+{
+ private $surrogate;
+
+ /**
+ * Constructor.
+ *
+ * @param SurrogateInterface $surrogate An SurrogateInterface instance
+ */
+ public function __construct(SurrogateInterface $surrogate = null)
+ {
+ $this->surrogate = $surrogate;
+ }
+
+ /**
+ * Filters the Response.
+ *
+ * @param FilterResponseEvent $event A FilterResponseEvent instance
+ */
+ public function onKernelResponse(FilterResponseEvent $event)
+ {
+ if (!$event->isMasterRequest() || null === $this->surrogate) {
+ return;
+ }
+
+ $this->surrogate->addSurrogateControl($event->getResponse());
+ }
+
+ public static function getSubscribedEvents()
+ {
+ return array(
+ KernelEvents::RESPONSE => 'onKernelResponse',
+ );
+ }
+}
@@ -0,0 +1,79 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpKernel\Fragment;
+
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\HttpKernel\Controller\ControllerReference;
+use Symfony\Component\HttpKernel\HttpCache\SurrogateInterface;
+
+/**
+ * Implements Surrogate rendering strategy.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+abstract class AbstractSurrogateFragmentRenderer extends RoutableFragmentRenderer
+{
+ private $surrogate;
+ private $inlineStrategy;
+
+ /**
+ * Constructor.
+ *
+ * The "fallback" strategy when surrogate is not available should always be an
+ * instance of InlineFragmentRenderer.
+ *
+ * @param SurrogateInterface $surrogate An Surrogate instance
+ * @param FragmentRendererInterface $inlineStrategy The inline strategy to use when the surrogate is not supported
+ */
+ public function __construct(SurrogateInterface $surrogate = null, FragmentRendererInterface $inlineStrategy)
+ {
+ $this->surrogate = $surrogate;
+ $this->inlineStrategy = $inlineStrategy;
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * Note that if the current Request has no surrogate capability, this method
+ * falls back to use the inline rendering strategy.
+ *
+ * Additional available options:
+ *
+ * * alt: an alternative URI to render in case of an error
+ * * comment: a comment to add when returning the surrogate tag
+ *
+ * Note, that not all surrogate strategies support all options. For now
+ * 'alt' and 'comment' are only supported by ESI.
+ *
+ * @see Symfony\Component\HttpKernel\HttpCache\SurrogateInterface
+ */
+ public function render($uri, Request $request, array $options = array())
+ {
+ if (!$this->surrogate || !$this->surrogate->hasSurrogateCapability($request)) {
+ return $this->inlineStrategy->render($uri, $request, $options);
+ }
+
+ if ($uri instanceof ControllerReference) {
+ $uri = $this->generateFragmentUri($uri, $request);
+ }
+
+ $alt = isset($options['alt']) ? $options['alt'] : null;
+ if ($alt instanceof ControllerReference) {
+ $alt = $this->generateFragmentUri($alt, $request);
+ }
+
+ $tag = $this->surrogate->renderIncludeTag($uri, $alt, isset($options['ignore_errors']) ? $options['ignore_errors'] : false, isset($options['comment']) ? $options['comment'] : '');
+
+ return new Response($tag);
+ }
+}
Oops, something went wrong.

0 comments on commit bf140a8

Please sign in to comment.