diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php
index 885752b89da6b..f68b633dbd894 100644
--- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php
+++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php
@@ -73,6 +73,7 @@ public function getConfigTreeBuilder()
$this->addFormSection($rootNode);
$this->addEsiSection($rootNode);
+ $this->addSsiSection($rootNode);
$this->addRouterProxySection($rootNode);
$this->addProfilerSection($rootNode);
$this->addRouterSection($rootNode);
@@ -130,6 +131,16 @@ private function addRouterProxySection(ArrayNodeDefinition $rootNode)
;
}
+ private function addSsiSection (ArrayNodeDefinition $rootNode) {
+ $rootNode
+ ->children()
+ ->arrayNode('ssi')
+ ->info('ssi configuration')
+ ->canBeDisabled()
+ ->end()
+ ->end();
+ }
+
private function addProfilerSection(ArrayNodeDefinition $rootNode)
{
$rootNode
diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
index 958c2ce35065c..8000ca19c51a3 100644
--- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
+++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
@@ -94,6 +94,10 @@ public function load(array $configs, ContainerBuilder $container)
$this->registerEsiConfiguration($config['esi'], $loader);
}
+ if (isset($config['ssi'])) {
+ $this->registerSsiConfiguration($config['ssi'], $loader);
+ }
+
if (isset($config['router_proxy'])) {
$this->registerRouterProxyConfiguration($config['router_proxy'], $container, $loader);
}
@@ -188,6 +192,18 @@ private function registerEsiConfiguration(array $config, XmlFileLoader $loader)
}
}
+ /**
+ * Loads the SSI configuration.
+ *
+ * @param array $config An SSI configuration array
+ * @param XmlFileLoader $loader An XmlFileLoader instance
+ */
+ private function registerSsiConfiguration (array $config, XmlFileLoader $loader) {
+ if (!empty($config['enabled'])) {
+ $loader->load('ssi.xml');
+ }
+ }
+
/**
* Loads the router proxy configuration.
*
diff --git a/src/Symfony/Bundle/FrameworkBundle/HttpCache/HttpCache.php b/src/Symfony/Bundle/FrameworkBundle/HttpCache/HttpCache.php
index f07c994a5d713..051638362a698 100644
--- a/src/Symfony/Bundle/FrameworkBundle/HttpCache/HttpCache.php
+++ b/src/Symfony/Bundle/FrameworkBundle/HttpCache/HttpCache.php
@@ -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(), array_merge(array('debug' => $kernel->isDebug()), $this->getOptions()));
}
/**
@@ -70,11 +70,6 @@ protected function getOptions()
return array();
}
- protected function createEsi()
- {
- return new Esi();
- }
-
protected function createStore()
{
return new Store($this->cacheDir ?: $this->kernel->getCacheDir().'/http_cache');
diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/ssi.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/ssi.xml
new file mode 100644
index 0000000000000..710377adc2a55
--- /dev/null
+++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/ssi.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+ Symfony\Component\HttpKernel\EventListener\SsiListener
+ Symfony\Component\HttpKernel\RenderingStrategy\SsiRenderingStrategy
+
+
+
+
+
+
+
+
+
+
+
+ %http_content_renderer.proxy_path%
+
+
+
diff --git a/src/Symfony/Component/HttpKernel/EventListener/SsiListener.php b/src/Symfony/Component/HttpKernel/EventListener/SsiListener.php
new file mode 100644
index 0000000000000..56c43a53279f0
--- /dev/null
+++ b/src/Symfony/Component/HttpKernel/EventListener/SsiListener.php
@@ -0,0 +1,53 @@
+
+ *
+ * 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\HttpFoundation\Response;
+use Symfony\Component\HttpKernel\HttpKernelInterface;
+use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
+use Symfony\Component\HttpKernel\KernelEvents;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+
+/**
+ * SsiListener adds a Surrogate-Control HTTP header when the Response needs to be parsed for SSI.
+ *
+ * @author Sebastian Krebs
+ */
+class SsiListener implements EventSubscriberInterface
+{
+ /**
+ * Filters the Response.
+ *
+ * @param FilterResponseEvent $event A FilterResponseEvent instance
+ */
+ public function onKernelResponse(FilterResponseEvent $event)
+ {
+ if (HttpKernelInterface::MASTER_REQUEST === $event->getRequestType()) {
+ $this->updateResponseHeader($event->getResponse());
+ }
+ }
+
+ public static function getSubscribedEvents()
+ {
+ return array(
+ KernelEvents::RESPONSE => 'onKernelResponse',
+ );
+ }
+
+ private function updateResponseHeader (Response $response)
+ {
+ if (false !== strpos($response->getContent(), '#', new IncludeHandler($kernel, $request, $response, $extractor), $response->getContent());
+ }
+}
diff --git a/src/Symfony/Component/HttpKernel/RenderingStrategy/SsiRenderingStrategy.php b/src/Symfony/Component/HttpKernel/RenderingStrategy/SsiRenderingStrategy.php
new file mode 100644
index 0000000000000..90bc45036c4b8
--- /dev/null
+++ b/src/Symfony/Component/HttpKernel/RenderingStrategy/SsiRenderingStrategy.php
@@ -0,0 +1,88 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpKernel\RenderingStrategy;
+
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\HttpKernel\Controller\ControllerReference;
+
+/**
+ * Implements the SSI rendering strategy.
+ *
+ * @author Sebastian Krebs
+ */
+class SsiRenderingStrategy extends ProxyAwareRenderingStrategy
+{
+ private $defaultStrategy;
+
+ /**
+ * Constructor.
+ *
+ * The "fallback" strategy when ESI is not available should always be an
+ * instance of DefaultRenderingStrategy (or a class you are using for the
+ * default strategy).
+ *
+ * @param RenderingStrategyInterface $defaultStrategy The default strategy to use when ESI is not supported
+ */
+ public function __construct(RenderingStrategyInterface $defaultStrategy)
+ {
+ $this->defaultStrategy = $defaultStrategy;
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * Note that if the current Request has no ESI capability, this method
+ * falls back to use the default rendering strategy.
+ *
+ * Additional available options:
+ *
+ * * comment: a comment to add when returning an esi:include tag
+ */
+ public function render($uri, Request $request, array $options = array())
+ {
+ $value = $request->headers->get('Surrogate-Capability');
+
+ if (!$value || strpos($value, 'SSI/1.0') === false) {
+ return $this->defaultStrategy->render($uri, $request, $options);
+ }
+
+ if ($uri instanceof ControllerReference) {
+ $uri = $this->generateProxyUri($uri, $request);
+ }
+
+ $tag = $this->renderIncludeTag($uri, isset($options['ignore_errors']) ? $options['ignore_errors'] : false, isset($options['comment']) ? $options['comment'] : '');
+
+ return new Response($tag);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getName()
+ {
+ return 'ssi';
+ }
+
+ private function renderIncludeTag ($uri, $ignoreErrors = true, $comment = '') {
+ $html = sprintf('',
+ $uri,
+ $ignoreErrors ? ' fmt="?"' : ''
+ );
+
+ if (!empty($comment)) {
+ return sprintf("\n%s", $comment, $html);
+ }
+
+ return $html;
+ }
+}