From 47c9feb9e70acfa553571f66ea0e09bde6009cd8 Mon Sep 17 00:00:00 2001 From: Katherine Bailey <katherine@katbailey.net> Date: Mon, 25 Jun 2012 21:04:49 -0700 Subject: [PATCH] Issue #1599108: first pass at adding bundles Conflicts: core/lib/Drupal/Core/DependencyInjection/ContainerBuilder.php --- core/includes/bootstrap.inc | 2 +- core/includes/common.inc | 4 +- .../Compiler/RegisterKernelListenersPass.php | 36 +++++ .../DependencyInjection/ContainerBuilder.php | 127 ++++-------------- core/lib/Drupal/Core/DrupalBundle.php | 124 +++++++++++++++++ core/lib/Drupal/Core/DrupalKernel.php | 80 +++++++++++ core/lib/Drupal/Core/ExceptionController.php | 4 +- index.php | 7 +- 8 files changed, 277 insertions(+), 107 deletions(-) create mode 100644 core/lib/Drupal/Core/DependencyInjection/Compiler/RegisterKernelListenersPass.php create mode 100644 core/lib/Drupal/Core/DrupalBundle.php create mode 100644 core/lib/Drupal/Core/DrupalKernel.php diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc index 18c66951b8ac..d9bf144e22d3 100644 --- a/core/includes/bootstrap.inc +++ b/core/includes/bootstrap.inc @@ -2458,7 +2458,7 @@ function drupal_container(ContainerBuilder $reset = NULL) { $container = $reset; } elseif (!isset($container)) { - $container = new ContainerBuilder(); + // HALP!! } return $container; } diff --git a/core/includes/common.inc b/core/includes/common.inc index e4e00d646ff5..6bbad11be88f 100644 --- a/core/includes/common.inc +++ b/core/includes/common.inc @@ -4913,9 +4913,9 @@ function _drupal_bootstrap_code() { } /** - * Temporary BC function for scripts not using HttpKernel. + * Temporary BC function for scripts not using DrupalKernel. * - * HttpKernel skips this and replicates it via event listeners. + * DrupalKernel skips this and replicates it via event listeners. * * @see Drupal\Core\EventSubscriber\PathSubscriber; * @see Drupal\Core\EventSubscriber\LegacyRequestSubscriber; diff --git a/core/lib/Drupal/Core/DependencyInjection/Compiler/RegisterKernelListenersPass.php b/core/lib/Drupal/Core/DependencyInjection/Compiler/RegisterKernelListenersPass.php new file mode 100644 index 000000000000..291e54bf873b --- /dev/null +++ b/core/lib/Drupal/Core/DependencyInjection/Compiler/RegisterKernelListenersPass.php @@ -0,0 +1,36 @@ +<?php + +/** + * @file + * Definition of Drupal\Core\DependencyInjection\Compiler\RegisterKernelListenersPass. + */ + +namespace Drupal\Core\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; + +class RegisterKernelListenersPass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container) + { + if (!$container->hasDefinition('dispatcher')) { + return; + } + + $definition = $container->getDefinition('dispatcher'); + + foreach ($container->findTaggedServiceIds('kernel.event_subscriber') as $id => $attributes) { + + // We must assume that the class value has been correcly filled, even if the service is created by a factory + $class = $container->getDefinition($id)->getClass(); + + $refClass = new \ReflectionClass($class); + $interface = 'Symfony\Component\EventDispatcher\EventSubscriberInterface'; + if (!$refClass->implementsInterface($interface)) { + throw new \InvalidArgumentException(sprintf('Service "%s" must implement interface "%s".', $id, $interface)); + } + $definition->addMethodCall('addSubscriberService', array($id, $class)); + } + } +} diff --git a/core/lib/Drupal/Core/DependencyInjection/ContainerBuilder.php b/core/lib/Drupal/Core/DependencyInjection/ContainerBuilder.php index a82f5b6117bd..786a3a377ecf 100644 --- a/core/lib/Drupal/Core/DependencyInjection/ContainerBuilder.php +++ b/core/lib/Drupal/Core/DependencyInjection/ContainerBuilder.php @@ -7,113 +7,38 @@ namespace Drupal\Core\DependencyInjection; -use Drupal\Core\ContentNegotiation; -use Drupal\Core\EventSubscriber\AccessSubscriber; -use Drupal\Core\EventSubscriber\FinishResponseSubscriber; -use Drupal\Core\EventSubscriber\LegacyControllerSubscriber; -use Drupal\Core\EventSubscriber\LegacyRequestSubscriber; -use Drupal\Core\EventSubscriber\MaintenanceModeSubscriber; -use Drupal\Core\EventSubscriber\PathSubscriber; -use Drupal\Core\EventSubscriber\RequestCloseSubscriber; -use Drupal\Core\EventSubscriber\RouterListener; -use Drupal\Core\EventSubscriber\ViewSubscriber; -use Drupal\Core\ExceptionController; -use Drupal\Core\LegacyUrlMatcher; use Symfony\Component\DependencyInjection\ContainerBuilder as BaseContainerBuilder; -use Symfony\Component\DependencyInjection\Reference; -use Symfony\Component\EventDispatcher\EventDispatcher; -use Symfony\Component\HttpKernel\EventListener\ExceptionListener; +use Symfony\Component\DependencyInjection\Compiler\Compiler; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\Compiler\PassConfig; +use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag; /** * Drupal's dependency injection container. */ class ContainerBuilder extends BaseContainerBuilder { - /** - * Registers the base Drupal services for the dependency injection container. - */ - public function __construct() { - parent::__construct(); + public function addCompilerPass(CompilerPassInterface $pass, $type = PassConfig::TYPE_BEFORE_OPTIMIZATION) + { + if (!isset($this->compiler) || null === $this->compiler) { + $this->compiler = new Compiler(); + } + + $this->compiler->addPass($pass, $type); + } + + public function compile() + { + if (null === $this->compiler) { + $this->compiler = new Compiler(); + } + + $this->compiler->compile($this); + $this->parameterBag->resolve(); + // TODO: The line below is commented out because there is code that calls + // the set() method on the container after it has been built - that method + // throws an exception if the container's parameters have been frozen. + //$this->parameterBag = new FrozenParameterBag($this->parameterBag->all()); + } - // An interface language always needs to be available for t() and other - // functions. This default is overridden by drupal_language_initialize() - // during language negotiation. - $this->register(LANGUAGE_TYPE_INTERFACE, 'Drupal\\Core\\Language\\Language'); - - // Register the default language content. - $this->register(LANGUAGE_TYPE_CONTENT, 'Drupal\\Core\\Language\\Language'); - - // Register configuration storage dispatcher. - $this->setParameter('config.storage.info', array( - 'Drupal\Core\Config\DatabaseStorage' => array( - 'connection' => 'default', - 'target' => 'default', - 'read' => TRUE, - 'write' => TRUE, - ), - 'Drupal\Core\Config\FileStorage' => array( - 'directory' => config_get_config_directory(), - 'read' => TRUE, - 'write' => FALSE, - ), - )); - $this->register('config.storage.dispatcher', 'Drupal\Core\Config\StorageDispatcher') - ->addArgument('%config.storage.info%'); - - // Register configuration object factory. - $this->register('config.factory', 'Drupal\Core\Config\ConfigFactory') - ->addArgument(new Reference('config.storage.dispatcher')); - - // Register the HTTP kernel services. - $this->register('dispatcher', 'Symfony\Component\EventDispatcher\EventDispatcher') - ->addArgument(new Reference('service_container')) - ->setFactoryClass('Drupal\Core\DependencyInjection\ContainerBuilder') - ->setFactoryMethod('getKernelEventDispatcher'); - $this->register('resolver', 'Symfony\Component\HttpKernel\Controller\ControllerResolver'); - $this->register('httpkernel', 'Symfony\Component\HttpKernel\HttpKernel') - ->addArgument(new Reference('dispatcher')) - ->addArgument(new Reference('resolver')); - } - - /** - * Creates an EventDispatcher for the HttpKernel. Factory method. - * - * @param Drupal\Core\DependencyInjection\ContainerBuilder $container - * The dependency injection container that contains the HTTP kernel. - * - * @return Symfony\Component\EventDispatcher\EventDispatcher - * An EventDispatcher with the default listeners attached to it. - */ - public static function getKernelEventDispatcher($container) { - $dispatcher = new EventDispatcher(); - - $matcher = new LegacyUrlMatcher(); - $dispatcher->addSubscriber(new RouterListener($matcher)); - - $negotiation = new ContentNegotiation(); - - // @todo Make this extensible rather than just hard coding some. - // @todo Add a subscriber to handle other things, too, like our Ajax - // replacement system. - $dispatcher->addSubscriber(new ViewSubscriber($negotiation)); - $dispatcher->addSubscriber(new AccessSubscriber()); - $dispatcher->addSubscriber(new MaintenanceModeSubscriber()); - $dispatcher->addSubscriber(new PathSubscriber()); - $dispatcher->addSubscriber(new LegacyRequestSubscriber()); - $dispatcher->addSubscriber(new LegacyControllerSubscriber()); - $dispatcher->addSubscriber(new FinishResponseSubscriber()); - $dispatcher->addSubscriber(new RequestCloseSubscriber()); - - // Some other form of error occured that wasn't handled by another kernel - // listener. That could mean that it's a method/mime-type/error combination - // that is not accounted for, or some other type of error. Either way, treat - // it as a server-level error and return an HTTP 500. By default, this will - // be an HTML-type response because that's a decent best guess if we don't - // know otherwise. - $exceptionController = new ExceptionController($negotiation); - $exceptionController->setContainer($container); - $dispatcher->addSubscriber(new ExceptionListener(array($exceptionController, 'execute'))); - - return $dispatcher; - } } diff --git a/core/lib/Drupal/Core/DrupalBundle.php b/core/lib/Drupal/Core/DrupalBundle.php new file mode 100644 index 000000000000..6dcf21753853 --- /dev/null +++ b/core/lib/Drupal/Core/DrupalBundle.php @@ -0,0 +1,124 @@ +<?php + +namespace Drupal\Core; + +use Drupal\Core\DependencyInjection\Compiler\RegisterKernelListenersPass; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\HttpKernel\Bundle\Bundle; +use Symfony\Component\DependencyInjection\Compiler\PassConfig; + +class DrupalBundle extends Bundle +{ + public function build(ContainerBuilder $container) + { + parent::build($container); + // An interface language always needs to be available for t() and other + // functions. This default is overridden by drupal_language_initialize() + // during language negotiation. + $container->register(LANGUAGE_TYPE_INTERFACE, 'Drupal\\Core\\Language\\Language'); + + // Register the default language content. + $container->register(LANGUAGE_TYPE_CONTENT, 'Drupal\\Core\\Language\\Language'); + + $definitions = array( + 'dispatcher' => array( + 'class' => 'Symfony\Component\EventDispatcher\ContainerAwareEventDispatcher', + 'references' => array( + 'service_container', + ), + ), + 'resolver' => array( + 'class' => 'Symfony\Component\HttpKernel\Controller\ControllerResolver', + ), + 'http_kernel' => array( + 'class' => 'Symfony\Component\HttpKernel\HttpKernel', + 'references' => array( + 'dispatcher', 'resolver', + ) + ), + 'matcher' => array( + 'class' => 'Drupal\Core\LegacyUrlMatcher', + ), + 'router_listener' => array( + 'class' => 'Drupal\Core\EventSubscriber\RouterListener', + 'references' => array('matcher'), + 'tags' => array('kernel.event_subscriber') + ), + 'content_negotiation' => array( + 'class' => 'Drupal\Core\ContentNegotiation', + ), + 'view_subscriber' => array( + 'class' => 'Drupal\Core\EventSubscriber\ViewSubscriber', + 'references' => array('content_negotiation'), + 'tags' => array('kernel.event_subscriber') + ), + 'access_subscriber' => array( + 'class' => 'Drupal\Core\EventSubscriber\AccessSubscriber', + 'tags' => array('kernel.event_subscriber') + ), + 'maintenance_mode_subscriber' => array( + 'class' => 'Drupal\Core\EventSubscriber\MaintenanceModeSubscriber', + 'tags' => array('kernel.event_subscriber') + ), + 'path_subscriber' => array( + 'class' => 'Drupal\Core\EventSubscriber\PathSubscriber', + 'tags' => array('kernel.event_subscriber') + ), + 'legacy_request_subscriber' => array( + 'class' => 'Drupal\Core\EventSubscriber\LegacyRequestSubscriber', + 'tags' => array('kernel.event_subscriber') + ), + 'legacy_controller_subscriber' => array( + 'class' => 'Drupal\Core\EventSubscriber\LegacyControllerSubscriber', + 'tags' => array('kernel.event_subscriber') + ), + 'finish_response_subscriber' => array( + 'class' => 'Drupal\Core\EventSubscriber\FinishResponseSubscriber', + 'tags' => array('kernel.event_subscriber') + ), + 'request_close_subscriber' => array( + 'class' => 'Drupal\Core\EventSubscriber\RequestCloseSubscriber', + 'tags' => array('kernel.event_subscriber') + ), + 'exception_controller' => array( + 'class' => 'Drupal\Core\ExceptionController', + 'references' => array('content_negotiation'), + 'methods' => array('setContainer' => array('service_container')) + ), + 'exception_listener' => array( + 'class' => 'Symfony\Component\HttpKernel\EventListener\ExceptionListener', + 'references' => array('exception_controller'), + 'tags' => array('kernel.event_subscriber') + ), + ); + + foreach ($definitions as $id => $info) { + $info += array( + 'tags' => array(), + 'references' => array(), + 'methods' => array(), + ); + + $references = array(); + foreach ($info['references'] as $ref_id) { + $references[] = new Reference($ref_id); + } + + $definition = new Definition($info['class'], $references); + + foreach($info['tags'] as $tag) { + $definition->addTag($tag); + } + + foreach ($info['methods'] as $method => $args) { + $definition->addMethodCall($method, $args); + } + + $container->setDefinition($id, $definition); + } + + $container->addCompilerPass(new RegisterKernelListenersPass(), PassConfig::TYPE_AFTER_REMOVING); + } +} \ No newline at end of file diff --git a/core/lib/Drupal/Core/DrupalKernel.php b/core/lib/Drupal/Core/DrupalKernel.php new file mode 100644 index 000000000000..f777e19f61bf --- /dev/null +++ b/core/lib/Drupal/Core/DrupalKernel.php @@ -0,0 +1,80 @@ +<?php + +/** + * @file + * Definition of Drupal\Core\DrupalKernel. + */ + +namespace Drupal\Core; + +use Drupal\Core\DrupalBundle; +use Symfony\Component\HttpKernel\Kernel; +use Drupal\Core\DependencyInjection\ContainerBuilder; +use Symfony\Component\Config\Loader\LoaderInterface; +use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; + +/** + * The DrupalKernel class is the core of Drupal itself. + */ +class DrupalKernel extends Kernel { + + public function registerBundles() + { + $bundles = array( + new DrupalBundle(), + ); + $modules = array_keys(system_list('module_enabled')); + foreach ($modules as $module) { + $class = "\Drupal\{$module}\{$module}Bundle"; + if (class_exists($class)) { + $bundles[] = new $class(); + } + } + return $bundles; + } + + + /** + * Initializes the service container. + */ + protected function initializeContainer() + { + $this->container = $this->buildContainer(); + $this->container->set('kernel', $this); + drupal_container($this->container); + } + + /** + * Builds the service container. + * + * @return ContainerBuilder The compiled service container + */ + protected function buildContainer() + { + $container = $this->getContainerBuilder(); + foreach ($this->bundles as $bundle) { + $bundle->build($container); + } + $container->compile(); + return $container; + } + + + /** + * Gets a new ContainerBuilder instance used to build the service container. + * + * @return ContainerBuilder + */ + protected function getContainerBuilder() + { + return new ContainerBuilder(new ParameterBag($this->getKernelParameters())); + } + + public function registerContainerConfiguration(LoaderInterface $loader) + { + // We have to define this method because it's not defined in the base class, + // but the LoaderInterface class is part of the config component, which we + // are not using, so this is badness :-/ The alternative is to not extend + // the base Kernel class and just implement the KernelInterface. + } +} diff --git a/core/lib/Drupal/Core/ExceptionController.php b/core/lib/Drupal/Core/ExceptionController.php index 7f71e28286b0..836ab56e4026 100644 --- a/core/lib/Drupal/Core/ExceptionController.php +++ b/core/lib/Drupal/Core/ExceptionController.php @@ -107,7 +107,7 @@ public function on403Html(FlattenException $exception, Request $request) { drupal_static_reset('menu_set_active_trail'); menu_reset_static_cache(); - $response = $this->container->get('httpkernel')->handle($subrequest, HttpKernel::SUB_REQUEST); + $response = $this->container->get('http_kernel')->handle($subrequest, HttpKernel::SUB_REQUEST); $response->setStatusCode(403, 'Access denied'); } else { @@ -172,7 +172,7 @@ public function on404Html(FlattenException $exception, Request $request) { drupal_static_reset('menu_set_active_trail'); menu_reset_static_cache(); - $response = $this->container->get('httpkernel')->handle($subrequest, HttpKernel::SUB_REQUEST); + $response = $this->container->get('http_kernel')->handle($subrequest, HttpKernel::SUB_REQUEST); $response->setStatusCode(404, 'Not Found'); } else { diff --git a/index.php b/index.php index 7b99d1047b12..10e38af70954 100644 --- a/index.php +++ b/index.php @@ -11,6 +11,7 @@ * See COPYRIGHT.txt and LICENSE.txt files in the "core" directory. */ +use Drupal\Core\DrupalKernel; use Symfony\Component\HttpFoundation\Request; /** @@ -20,6 +21,7 @@ // Bootstrap the lowest level of what we need. require_once DRUPAL_ROOT . '/core/includes/bootstrap.inc'; drupal_bootstrap(DRUPAL_BOOTSTRAP_CONFIGURATION); +drupal_bootstrap(DRUPAL_BOOTSTRAP_VARIABLES); // Create a request object from the HTTPFoundation. $request = Request::createFromGlobals(); @@ -29,6 +31,9 @@ // container at some point. request($request); +$kernel = new DrupalKernel('prod', FALSE); +$kernel->boot(); + // Bootstrap all of Drupal's subsystems, but do not initialize anything that // depends on the fully resolved Drupal path, because path resolution happens // during the REQUEST event of the kernel. @@ -36,6 +41,6 @@ // @see Drupal\Core\EventSubscriber\LegacyRequestSubscriber; drupal_bootstrap(DRUPAL_BOOTSTRAP_CODE); -$kernel = drupal_container()->get('httpkernel'); $response = $kernel->handle($request)->prepare($request)->send(); + $kernel->terminate($request, $response); -- GitLab