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; + } +}