8000 [WIP] Kernel refactor by fabpot · Pull Request #6459 · symfony/symfony · GitHub
[go: up one dir, main page]

Skip to content

[WIP] Kernel refactor #6459

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
Jan 11, 2013
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
moved the logic from HttpKernel in FrameworkBundle to the HttpKernel …
…component
  • Loading branch information
fabpot committed Jan 10, 2013
commit 9aaceb19eebef484ba954bd67965276babf0a9e6
61 changes: 16 additions & 45 deletions src/Symfony/Bridge/Twig/Extension/HttpKernelExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,83 +11,54 @@

namespace Symfony\Bridge\Twig\Extension;

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\HttpContentRenderer;
use Symfony\Component\HttpKernel\Controller\ControllerReference;

/**
* Provides integration with the HttpKernel component.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class HttpKernelExtension extends \Twig_Extension implements EventSubscriberInterface
class HttpKernelExtension extends \Twig_Extension
{
private $kernel;
private $request;
private $renderer;

/**
* Constructor.
*
* @param HttpKernelInterface $kernel A HttpKernelInterface install
* @param HttpContentRenderer $kernel A HttpContentRenderer instance
*/
public function __construct(HttpKernelInterface $kernel)
public function __construct(HttpContentRenderer $renderer)
{
$this->kernel = $kernel;
$this->renderer = $renderer;
}

public function getFunctions()
{
return array(
'render' => new \Twig_Function_Method($this, 'render', array('needs_environment' => true, 'is_safe' => array('html'))),
'render' => new \Twig_Function_Method($this, 'render', array('is_safe' => array('html'))),
'controller' => new \Twig_Function_Method($this, 'controller'),
);
}

/**
* Renders a URI.
*
* @param \Twig_Environment $twig A \Twig_Environment instance
* @param string $uri The URI to render
* @param string $uri A URI
* @param array $options An array of options
*
* @return string The Response content
*
* @throws \RuntimeException
* @see Symfony\Component\HttpKernel\HttpContentRenderer::render()
*/
public function render(\Twig_Environment $twig, $uri)
public function render($uri, $options = array())
{
if (null !== $this->request) {
$cookies = $this->request->cookies->all();
$server = $this->request->server->all();
} else {
$cookies = array();
$server = array();
}

$subRequest = Request::create($uri, 'get', array(), $cookies, array(), $server);
if (null !== $this->request && $this->request->getSession()) {
$subRequest->setSession($this->request->getSession());
}

$response = $this->kernel->handle($subRequest, HttpKernelInterface::SUB_REQUEST, false);

if (!$response->isSuccessful()) {
throw new \RuntimeException(sprintf('Error when rendering "%s" (Status code is %s).', $subRequest->getUri(), $response->getStatusCode()));
}

return $response->getContent();
return $this->renderer->render($uri, $options);
}

public function onKernelRequest(GetResponseEvent $event)
public function controller($controller, $attributes = array(), $query = array())
{
$this->request = $event->getRequest();
}

public static function getSubscribedEvents()
{
return array(
KernelEvents::REQUEST => array('onKernelRequest'),
);
return new ControllerReference($controller, $attributes, $query);
}

public function getName()
Expand Down
23 changes: 10 additions & 13 deletions src/Symfony/Bridge/Twig/Tests/Extension/HttpKernelExtensionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
use Symfony\Bridge\Twig\Extension\HttpKernelExtension;
use Symfony\Bridge\Twig\Tests\TestCase;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\HttpContentRenderer;

class HttpKernelExtensionTest extends TestCase
{
Expand All @@ -31,7 +31,7 @@ protected function setUp()

public function testRenderWithoutMasterRequest()
{
$kernel = $this->getKernel($this->returnValue(new Response('foo')));
$kernel = $this->getHttpContentRenderer($this->returnValue('foo'));

$this->assertEquals('foo', $this->renderTemplate($kernel));
}
Expand All @@ -41,7 +41,7 @@ public function testRenderWithoutMasterRequest()
*/
public function testRenderWithError()
{
$kernel = $this->getKernel($this->throwException(new \Exception('foo')));
$kernel = $this->getHttpContentRenderer($this->throwException(new \Exception('foo')));

$loader = new \Twig_Loader_Array(array('index' => '{{ render("foo") }}'));
$twig = new \Twig_Environment($loader, array('debug' => true, 'cache' => false));
Expand All @@ -50,23 +50,20 @@ public function testRenderWithError()
$this->renderTemplate($kernel);
}

protected function getKernel($return)
protected function getHttpContentRenderer($return)
{
$kernel = $this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface');
$kernel
->expects($this->once())
->method('handle')
->will($return)
;
$strategy = $this->getMock('Symfony\\Component\\HttpKernel\\RenderingStrategy\\RenderingStrategyInterface');
$strategy->expects($this->once())->method('getName')->will($this->returnValue('default'));
$strategy->expects($this->once())->method('render')->will($return);

return $kernel;
return new HttpContentRenderer(array($strategy));
}

protected function renderTemplate(HttpKernelInterface $kernel, $template = '{{ render("foo") }}')
protected function renderTemplate(HttpContentRenderer $renderer, $template = '{{ render("foo") }}')
{
$loader = new \Twig_Loader_Array(array('index' => $template));
$twig = new \Twig_Environment($loader, array('debug' => true, 'cache' => false));
$twig->addExtension(new HttpKernelExtension($kernel));
$twig->addExtension(new HttpKernelExtension($renderer));

return $twig->render('index');
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?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\Bundle\FrameworkBundle\DependencyInjection\Compiler;

use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\Exception\LogicException;

/**
* Adds services tagged kernel.content_renderer_strategy as HTTP content rendering strategies.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class HttpRenderingStrategyPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
if (false === $container->hasDefinition('http_content_renderer')) {
return;
}

$definition = $container->getDefinition('http_content_renderer');
foreach (array_keys($container->findTaggedServiceIds('kernel.content_renderer_strategy')) as $id) {
$definition->addMethodCall('addStrategy', array(new Reference($id)));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ public function load(array $configs, ContainerBuilder $container)

$loader->load('web.xml');
$loader->load('services.xml');
$loader->load('content_generator.xml');

// A translator must always be registered (as support is included by
// default in the Form component). If disabled, an identity translator
Expand Down
2 changes: 2 additions & 0 deletions src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\CompilerDebugDumpPass;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\TranslationExtractorPass;
use Symfony\Bundle\Fr 4D24 ameworkBundle\DependencyInjection\Compiler\TranslationDumperPass;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\HttpRenderingStrategyPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\PassConfig;
use Symfony\Component\DependencyInjection\Scope;
Expand Down Expand Up @@ -65,6 +66,7 @@ public function build(ContainerBuilder $container)
$container->addCompilerPass(new AddCacheClearerPass());
$container->addCompilerPass(new TranslationExtractorPass());
$container->addCompilerPass(new TranslationDumperPass());
$container->addCompilerPass(new HttpRenderingStrategyPass());

if ($container->getParameter('kernel.debug')) {
$container->addCompilerPass(new ContainerBuilderDebugDumpPass(), PassConfig::TYPE_AFTER_REMOVING);
Expand Down
94 changes: 4 additions & 90 deletions src/Symfony/Bundle/FrameworkBundle/HttpKernel.php BEA4
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,6 @@ class HttpKernel extends BaseHttpKernel
{
protected $container;

private $esiSupport;

public function __construct(EventDispatcherInterface $dispatcher, ContainerInterface $container, ControllerResolverInterface $controllerResolver)
{
parent::__construct($dispatcher, $controllerResolver);
Expand Down Expand Up @@ -96,97 +94,13 @@ public function forward($controller, array $attributes = array(), array $query =
*
* @throws \RuntimeException
* @throws \Exception
*/
public function render($uri, array $options = array())
{
$request = $this->container->get('request');

$options = array_merge(array(
'ignore_errors' => !$this->container->getParameter('kernel.debug'),
'alt' => null,
'standalone' => false,
'comment' => '',
'default' => null,
), $options);

if (null === $this->esiSupport) {
$this->esiSupport = $this->container->has('esi') && $this->container->get('esi')->hasSurrogateEsiCapability($request);
}

if ($this->esiSupport && (true === $options['standalone'] || 'esi' === $options['standalone'])) {
return $this->container->get('esi')->renderIncludeTag($uri, $options['alt'], $options['ignore_errors'], $options['comment']);
}

if ('js' === $options['standalone']) {
$defaultContent = null;

$templating = $this->container->get('templating');

if ($options['default']) {
if ($templating->exists($options['default'])) {
$defaultContent = $templating->render($options['default']);
} else {
$defaultContent = $options['default'];
}
} elseif ($template = $this->container->getParameter('templating.hinclude.default_template')) {
$defaultContent = $templating->render($template);
}

return $this->renderHIncludeTag($uri, $defaultContent);
}

$subRequest = Request::create($uri, 'get', array(), $request->cookies->all(), array(), $request->server->all());
if ($session = $request->getSession()) {
$subRequest->setSession($session);
}

$level = ob_get_level();
try {
$response = $this->handle($subRequest, HttpKernelInterface::SUB_REQUEST, false);

if (!$response->isSuccessful()) {
throw new \RuntimeException(sprintf('Error when rendering "%s" (Status code is %s).', $request->getUri(), $response->getStatusCode()));
}

if (!$response instanceof StreamedResponse) {
return $response->getContent();
}

$response->sendContent();
} catch (\Exception $e) {
if ($options['alt']) {
$alt = $options['alt'];
unset($options['alt']);

return $this->render($alt, $options);
}

if (!$options['ignore_errors']) {
throw $e;
}

// let's clean up the output buffers that were created by the sub-request
while (ob_get_level() > $level) {
ob_get_clean();
}
}
}

/**
* Renders an HInclude tag.
*
* @param string $uri A URI
* @param string $defaultContent Default content
*
* @return string
* @deprecated in 2.2, will be removed in 2.3 (use Symfony\Component\HttpKernel\HttpContentRenderer::render() instead)
*/
public function renderHIncludeTag($uri, $defaultContent = null)
public function render($uri, array $options = array())
{
return sprintf('<hx:include src="%s">%s</hx:include>', $uri, $defaultContent);
}
trigger_error('render() is deprecated since version 2.2 and will be removed in 2.3. Use Symfony\Component\HttpKernel\HttpContentRenderer::render() instead.', E_USER_DEPRECATED);

public function hasEsiSupport()
{
return $this->esiSupport;
$this->container->get('http_content_renderer')->render($uri, $options);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?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="http_content_renderer.class">Symfony\Component\HttpKernel\HttpContentRenderer</parameter>
<parameter key="http_content_renderer.strategy.default.class">Symfony\Component\HttpKernel\RenderingStrategy\DefaultRenderingStrategy</parameter>
<parameter key="http_content_renderer.strategy.esi.class">Symfony\Component\HttpKernel\RenderingStrategy\EsiRenderingStrategy</parameter>
<parameter key="http_content_renderer.strategy.hinclude.class">Symfony\Component\HttpKernel\RenderingStrategy\HIncludeRenderingStrategy</parameter>
<parameter key="http_content_renderer.strategy.hinclude.global_template"></parameter>
<parameter key="http_content_renderer.listener.router_proxy.class">Symfony\Component\HttpKernel\EventListener\RouterProxyListener</parameter>
</parameters>

<services>
<service id="http_content_renderer" class="%http_content_renderer.class%">
<tag name="kernel.event_subscriber" />
<argument type="collection" />
<argument>%kernel.debug%</argument>
</service>

<service id="http_content_renderer.strategy.default" class="%http_content_renderer.strategy.default.class%">
<tag name="kernel.content_renderer_strategy" />
<argument type="service" id="http_kernel" />
<call method="setUrlGenerator"><argument type="service" id="router" /></call>
</service>

<service id="http_content_renderer.strategy.esi" class="%http_content_renderer.strategy.esi.class%">
<tag name="kernel.content_renderer_strategy" />
<argument type="service" id="esi" />
<argument type="service" id="http_content_renderer.strategy.default" />
<call method="setUrlGenerator"><argument type="service" id="router" /></call>
</service>

<service id="http_content_renderer.strategy.hinclude" class="%http_content_renderer.strategy.hinclude.class%">
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

strategies could be marked as private, allowing to inline them in the container

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought about that, but because of a bug in the PHPDumper, that's not possible for hinclude. I'm working on fixing the bug so that we can use private services later on.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see #6565 for the bug fix

<tag name="kernel.content_renderer_strategy" />
<argument type="service" id="templating" />
<argument>%http_content_renderer.strategy.hinclude.global_template%</argument>
</service>

<!-- FIXME: make the listener registration optional via a configuration setting? -->
<service id="http_content_renderer.listener.router_proxy" class="%http_content_renderer.listener.router_proxy.class%">
<tag name="kernel.event_subscriber" />
</service>

</services>
</container>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8" ?>

<routes xmlns="http://symfony.com/schema/routing"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing/routing-1.0.xsd">

<route id="_proxy" pattern="/_proxy/{_controller}.{_format}" />
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the _controller required to be of the form SomeBundle:Controller:partial? If so it should probably be specified as requirement for the placeholder.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NB: controllers could be services

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The controller name is really freeform. As mentioned by @Koc, it can be a service controller (but that's just an example as Class::method also works).

</routes>
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@

<service id="templating.helper.actions" class="%templating.helper.actions.class%">
<tag name="templating.helper" alias="actions" />
<argument type="service" id="http_kernel" />
<argument type="service" id="http_content_renderer" />
</service>

<service id="templating.helper.code" class="%templating.helper.code.class%">
Expand Down
Loading
0