diff --git a/core/core.services.yml b/core/core.services.yml index d915e10..0b225b4 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -174,6 +174,7 @@ services: factory_class: Symfony\Component\HttpFoundation\Request factory_method: createFromGlobals #synthetic: true + synchronized: true event_dispatcher: class: Symfony\Component\EventDispatcher\ContainerAwareEventDispatcher arguments: ['@service_container'] @@ -182,7 +183,7 @@ services: arguments: ['@service_container'] http_kernel: class: Drupal\Core\HttpKernel - arguments: ['@event_dispatcher', '@service_container', '@controller_resolver'] + arguments: ['@event_dispatcher', '@controller_resolver', '@request'] language_manager: class: Drupal\Core\Language\LanguageManager string_translator.custom_strings: @@ -233,6 +234,23 @@ services: router.dynamic: class: Symfony\Cmf\Component\Routing\DynamicRouter arguments: ['@router.request_context', '@router.matcher', '@url_generator'] + fragment_handler: + class: Symfony\Component\HttpKernel\Fragment\FragmentHandler + calls: + - [setRequest, ['@?request=']] + fragment_listener: + class: Symfony\Component\HttpKernel\EventListener\FragmentListener + arguments: ['@uri_signer'] + tags: + - { name: event_subscriber } + uri_signer: + class: Symfony\Component\HttpKernel\UriSigner + arguments: ['test'] + renderer_inline: + class: Symfony\Component\HttpKernel\Fragment\InlineFragmentRenderer + arguments: ['@http_kernel'] + tags: + - { name: render_strategy } legacy_generator: class: Drupal\Core\Routing\NullGenerator legacy_url_matcher: @@ -324,7 +342,7 @@ services: - { name: route_enhancer, priority: 10 } controller.page: class: Drupal\Core\Controller\HtmlPageController - arguments: ['@http_kernel'] + arguments: ['@http_kernel', '@fragment_handler'] controller.dialog: class: Drupal\Core\Controller\DialogController arguments: ['@http_kernel'] @@ -388,13 +406,11 @@ services: tags: - { name: event_subscriber } arguments: ['@language_manager'] - scope: request redirect_response_subscriber: class: Drupal\Core\EventSubscriber\RedirectResponseSubscriber arguments: ['@url_generator'] tags: - { name: event_subscriber } - scope: request request_close_subscriber: class: Drupal\Core\EventSubscriber\RequestCloseSubscriber tags: diff --git a/core/includes/session.inc b/core/includes/session.inc index 40e495f..6f46f71 100644 --- a/core/includes/session.inc +++ b/core/includes/session.inc @@ -181,7 +181,7 @@ function _drupal_session_write($sid, $value) { // Either ssid or sid or both will be added from $key below. $fields = array( 'uid' => $user->uid, - 'hostname' => Drupal::request()->getClientIP(), + 'hostname' => Drupal::request()->getClientIP() ?: '', 'session' => $value, 'timestamp' => REQUEST_TIME, ); diff --git a/core/lib/Drupal/Core/Controller/HtmlPageController.php b/core/lib/Drupal/Core/Controller/HtmlPageController.php index fd89081..b67f8c8 100644 --- a/core/lib/Drupal/Core/Controller/HtmlPageController.php +++ b/core/lib/Drupal/Core/Controller/HtmlPageController.php @@ -9,6 +9,8 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Controller\ControllerReference; +use Symfony\Component\HttpKernel\Fragment\FragmentHandler; use Symfony\Component\HttpKernel\HttpKernelInterface; /** @@ -24,12 +26,23 @@ class HtmlPageController { protected $httpKernel; /** + * The html fragment handler. + * + * @var \Symfony\Component\HttpKernel\Fragment\FragmentHandler + */ + protected $fragmentHandler; + + /** * Constructs a new HtmlPageController. * * @param \Symfony\Component\HttpKernel\HttpKernelInterface $kernel + * The central http kernel. + * @param \Symfony\Component\HttpKernel\Fragment\FragmentHandler $fragment_handler + * The html fragment handler. */ - public function __construct(HttpKernelInterface $kernel) { + public function __construct(HttpKernelInterface $kernel, FragmentHandler $fragment_handler) { $this->httpKernel = $kernel; + $this->fragmentHandler = $fragment_handler; } /** @@ -58,13 +71,12 @@ public function content(Request $request, $_content) { $attributes->remove('system_path'); $attributes->remove('_content'); - $response = $this->httpKernel->forward($controller, $attributes->all(), $request->query->all()); + $attributes_array = $attributes->all() ?: array(); + $query = $request->query->all() ?: array(); + $controller_reference = new ControllerReference($controller, $attributes_array, $query); + $page_content = $this->fragmentHandler->render($controller_reference); - // For successful (HTTP status 200) responses, decorate with blocks. - if ($response->isOk()) { - $page_content = $response->getContent(); - $response = new Response(drupal_render_page($page_content)); - } + $response = new Response(drupal_render_page($page_content)); return $response; } diff --git a/core/lib/Drupal/Core/CoreServiceProvider.php b/core/lib/Drupal/Core/CoreServiceProvider.php index c3b6994..71eb3a3 100644 --- a/core/lib/Drupal/Core/CoreServiceProvider.php +++ b/core/lib/Drupal/Core/CoreServiceProvider.php @@ -8,6 +8,7 @@ namespace Drupal\Core; use Drupal\Core\Cache\ListCacheBinsPass; +use Drupal\Core\DependencyInjection\Compiler\RegisterFragmentRenderersPass; use Drupal\Core\DependencyInjection\ServiceProviderInterface; use Drupal\Core\DependencyInjection\ContainerBuilder; use Drupal\Core\DependencyInjection\Compiler\ModifyServiceDefinitionsPass; @@ -64,6 +65,8 @@ public function register(ContainerBuilder $container) { $container->addCompilerPass(new RegisterServicesForDestructionPass()); // Add the compiler pass that will process the tagged services. $container->addCompilerPass(new RegisterPathProcessorsPass()); + // Add a compiler pass for registering fragment renderers. + $container->addCompilerPass(new RegisterFragmentRenderersPass()); $container->addCompilerPass(new ListCacheBinsPass()); // Add the compiler pass for appending string translators. $container->addCompilerPass(new RegisterStringTranslatorsPass()); diff --git a/core/lib/Drupal/Core/HttpKernel.php b/core/lib/Drupal/Core/HttpKernel.php index 07c79c7..ae528ee 100644 --- a/core/lib/Drupal/Core/HttpKernel.php +++ b/core/lib/Drupal/Core/HttpKernel.php @@ -2,12 +2,7 @@ /** * @file - * Definition of Drupal\Core\HttpKernel. - * - * @todo This file is copied verbatim, with the exception of the namespace - * change and this commment block, from Symfony full stack's FrameworkBundle. - * Once the FrameworkBundle is available as a Composer package we should switch - * to pulling it via Composer. + * Contains \Drupal\Core\HttpKernel. */ namespace Drupal\Core; @@ -23,226 +18,65 @@ use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** - * This HttpKernel is used to manage scope changes of the DI container. - * - * @author Fabien Potencier - * @author Johannes M. Schmitt + * HttpKernel extension to allow easy in-process subrequests. */ -class HttpKernel extends BaseHttpKernel -{ - protected $container; +class HttpKernel extends BaseHttpKernel { - private $esiSupport; - - public function __construct(EventDispatcherInterface $dispatcher, ContainerInterface $container, ControllerResolverInterface $controllerResolver) - { - parent::__construct($dispatcher, $controllerResolver); - - $this->container = $container; - } - - public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true) - { - $request->headers->set('X-Php-Ob-Level', ob_get_level()); - - $this->container->enterScope('request'); - $this->container->set('request', $request, 'request'); - - try { - $response = parent::handle($request, $type, $catch); - } catch (\Exception $e) { - $this->container->leaveScope('request'); - - throw $e; - } - - $this->container->leaveScope('request'); - - return $response; - } - - /** - * Forwards the request to another controller. - * - * @param string|NULL $controller - * The controller name (a string like BlogBundle:Post:index). - * @param array $attributes - * An array of request attributes. - * @param array $query - * An array of request query parameters. - * - * @return Response - * A Response instance - */ - public function forward($controller, array $attributes = array(), array $query = array()) - { - $subrequest = $this->setupSubrequest($controller, $attributes, $query); - - return $this->handle($subrequest, HttpKernelInterface::SUB_REQUEST); - } - - /** - * Renders a Controller and returns the Response content. - * - * Note that this method generates an esi:include tag only when both the standalone - * option is set to true and the request has ESI capability (@see Symfony\Component\HttpKernel\HttpCache\ESI). - * - * Available options: - * - * * attributes: An array of request attributes (only when the first argument is a controller) - * * query: An array of request query parameters (only when the first argument is a controller) - * * ignore_errors: true to return an empty string in case of an error - * * alt: an alternative controller to execute in case of an error (can be a controller, a URI, or an array with the controller, the attributes, and the query arguments) - * * standalone: whether to generate an esi:include tag or not when ESI is supported - * * comment: a comment to add when returning an esi:include tag - * - * @param string $controller A controller name to execute (a string like BlogBundle:Post:index), or a relative URI - * @param array $options An array of options - * - * @return string The Response content - */ - public function render($controller, array $options = array()) - { - $options = array_merge(array( - 'attributes' => array(), - 'query' => array(), - 'ignore_errors' => !$this->container->getParameter('kernel.debug'), - 'alt' => array(), - 'standalone' => false, - 'comment' => '', - ), $options); - - if (!is_array($options['alt'])) { - $options['alt'] = array($options['alt']); - } - - if (null === $this->esiSupport) { - $this->esiSupport = $this->container->has('esi') && $this->container->get('esi')->hasSurrogateEsiCapability($this->container->get('request')); - } - - if ($this->esiSupport && (true === $options['standalone'] || 'esi' === $options['standalone'])) { - $uri = $this->generateInternalUri($controller, $options['attributes'], $options['query']); - - $alt = ''; - if ($options['alt']) { - $alt = $this->generateInternalUri($options['alt'][0], isset($options['alt'][1]) ? $options['alt'][1] : array(), isset($options['alt'][2]) ? $options['alt'][2] : array()); - } - - return $this->container->get('esi')->renderIncludeTag($uri, $alt, $options['ignore_errors'], $options['comment']); - } - - if ('js' === $options['standalone']) { - $uri = $this->generateInternalUri($controller, $options['attributes'], $options['query'], false); - $defaultContent = null; - - if ($template = $this->container->getParameter('templating.hinclude.default_template')) { - $defaultContent = $this->container->get('templating')->render($template); - } - - return $this->renderHIncludeTag($uri, $defaultContent); - } - - $request = $this->container->get('request'); - - // controller or URI? - if (0 === strpos($controller, '/')) { - $subRequest = Request::create($request->getUriForPath($controller), 'get', array(), $request->cookies->all(), array(), $request->server->all()); - if ($session = $request->getSession()) { - $subRequest->setSession($session); - } - } else { - $options['attributes']['_controller'] = $controller; - - if (!isset($options['attributes']['_format'])) { - $options['attributes']['_format'] = $request->getRequestFormat(); - } - - $options['attributes'][RouteObjectInterface::ROUTE_OBJECT] = '_internal'; - $subRequest = $request->duplicate($options['query'], null, $options['attributes']); - $subRequest->setMethod('GET'); - } - - $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']); - $options['attributes'] = isset($alt[1]) ? $alt[1] : array(); - $options['query'] = isset($alt[2]) ? $alt[2] : array(); - - return $this->render($alt[0], $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(); - } - } - } - - /** - * Generates an internal URI for a given controller. - * - * This method uses the "_internal" route, which should be available. - * - * @param string $controller A controller name to execute (a string like BlogBundle:Post:index), or a relative URI - * @param array $attributes An array of request attributes - * @param array $query An array of request query parameters - * @param boolean $secure - * - * @return string An internal URI - */ - public function generateInternalUri($controller, array $attributes = array(), array $query = array(), $secure = true) - { - if (0 === strpos($controller, '/')) { - return $controller; - } + /** + * The synchronized request object. + * + * @var \Symfony\Component\HttpFoundation\Request + */ + protected $request; - $path = http_build_query($attributes, '', '&'); - $uri = $this->container->get('router')->generate($secure ? '_internal' : '_internal_public', array( - 'controller' => $controller, - 'path' => $path ?: 'none', - '_format' => $this->container->get('request')->getRequestFormat(), - )); + /** + * Constructs a \Drupal\Core\HttpKernel object. + * + * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher + * The event dispatcher. + * @param \Symfony\Component\HttpKernel\Controller\ControllerResolverInterface $resolver + * The controller resolver. + * @param \Symfony\Component\HttpFoundation\Request $request + * The synchronized request object. + */ + public function __construct(EventDispatcherInterface $dispatcher, ControllerResolverInterface $resolver, Request $request) { + parent::__construct($dispatcher, $resolver); - if ($queryString = http_build_query($query, '', '&')) { - $uri .= '?'.$queryString; - } + $this->setRequest($request); + } - return $uri; - } + /** + * Set the request object. + * + * @param \Symfony\Component\HttpFoundation\Request $request + * The synchronized request object. + */ + public function setRequest(Request $request = NULL) { + $this->request = $request; + } - /** - * Renders an HInclude tag. - * - * @param string $uri A URI - * @param string $defaultContent Default content - */ - public function renderHIncludeTag($uri, $defaultContent = null) - { - return sprintf('%s', $uri, $defaultContent); - } + /** + * Forwards the request to another controller. + * + * If what you want back is just a string, do not use this method. Use + * the FragmentHandler. Only use this method if you want back a Response + * object. + * + * @param string $controller + * The controller name (a string like BlogBundle:Post:index) + * @param array $attributes + * An array of request attributes + * @param array $query + * An array of request query parameters + * + * @return Response + * A Response instance + */ + public function forward($controller, array $attributes = array(), array $query = array()) { + $sub_request = $this->setupSubrequest($controller, $attributes, $query); - public function hasEsiSupport() - { - return $this->esiSupport; - } + return $this->handle($sub_request, HttpKernelInterface::SUB_REQUEST); + } /** * Creates a request object for a subrequest. @@ -265,7 +99,7 @@ public function setupSubrequest($controller, array $attributes, array $query) { else { unset($attributes['_controller']); } - return $this->container->get('request')->duplicate($query, NULL, $attributes); + return $this->request->duplicate($query, NULL, $attributes); } } diff --git a/core/tests/Drupal/Tests/Core/HttpKernelTest.php b/core/tests/Drupal/Tests/Core/HttpKernelTest.php index b51e108..6c877eb 100644 --- a/core/tests/Drupal/Tests/Core/HttpKernelTest.php +++ b/core/tests/Drupal/Tests/Core/HttpKernelTest.php @@ -48,7 +48,7 @@ public function testSetupSubrequest() { $dispatcher = new EventDispatcher(); $controller_resolver = new ControllerResolver($container); - $http_kernel = new HttpKernel($dispatcher, $container, $controller_resolver); + $http_kernel = new HttpKernel($dispatcher, $controller_resolver, $request); $test_controller = '\Drupal\Tests\Core\Controller\TestController'; $random_attribute = $this->randomName();