-
-
Save katbailey/f9c35b0b8eb64155b4ca to your computer and use it in GitHub Desktop.
Switch to Kernel + Bundles + Container Aware Event Dispatcher
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc | |
index fa1335b..c0aa974 100644 | |
--- a/core/includes/bootstrap.inc | |
+++ b/core/includes/bootstrap.inc | |
@@ -2446,7 +2446,7 @@ function drupal_container(ContainerBuilder $reset = NULL) { | |
$container = $reset; | |
} | |
elseif (!isset($container)) { | |
- $container = new ContainerBuilder(); | |
+ // HALP!! | |
} | |
return $container; | |
} | |
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 0000000..291e54b | |
--- /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 aa73ace..87d0fbf 100644 | |
--- a/core/lib/Drupal/Core/DependencyInjection/ContainerBuilder.php | |
+++ b/core/lib/Drupal/Core/DependencyInjection/ContainerBuilder.php | |
@@ -8,24 +8,38 @@ | |
namespace Drupal\Core\DependencyInjection; | |
use Symfony\Component\DependencyInjection\ContainerBuilder as BaseContainerBuilder; | |
+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(); | |
+ } | |
- // 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'); | |
+ $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()); | |
+ } | |
- // Register the default language content. | |
- $this->register(LANGUAGE_TYPE_CONTENT, 'Drupal\\Core\\Language\\Language'); | |
- } | |
} | |
diff --git a/core/lib/Drupal/Core/DrupalBundle.php b/core/lib/Drupal/Core/DrupalBundle.php | |
new file mode 100644 | |
index 0000000..6dcf217 | |
--- /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 | |
index 7361a82..f777e19 100644 | |
--- a/core/lib/Drupal/Core/DrupalKernel.php | |
+++ b/core/lib/Drupal/Core/DrupalKernel.php | |
@@ -7,59 +7,74 @@ | |
namespace Drupal\Core; | |
-use Symfony\Component\HttpKernel\HttpKernel; | |
-use Symfony\Component\EventDispatcher\EventDispatcherInterface; | |
-use Symfony\Component\HttpKernel\Controller\ControllerResolverInterface; | |
-use Symfony\Component\HttpKernel\EventListener\ExceptionListener; | |
-use Drupal\Core\EventSubscriber\ViewSubscriber; | |
-use Drupal\Core\EventSubscriber\AccessSubscriber; | |
-use Drupal\Core\EventSubscriber\FinishResponseSubscriber; | |
-use Drupal\Core\EventSubscriber\PathSubscriber; | |
-use Drupal\Core\EventSubscriber\LegacyRequestSubscriber; | |
-use Drupal\Core\EventSubscriber\LegacyControllerSubscriber; | |
-use Drupal\Core\EventSubscriber\MaintenanceModeSubscriber; | |
-use Drupal\Core\EventSubscriber\RequestCloseSubscriber; | |
-use Drupal\Core\EventSubscriber\RouterListener; | |
+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 HttpKernel { | |
+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; | |
+ } | |
+ | |
/** | |
- * Constructor. | |
- * | |
- * @param Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher | |
- * An EventDispatcherInterface instance. | |
- * @param Symfony\Component\HttpKernel\Controller\ControllerResolverInterface $resolver | |
- * A ControllerResolverInterface instance. | |
+ * Initializes the service container. | |
*/ | |
- public function __construct(EventDispatcherInterface $dispatcher, ControllerResolverInterface $resolver) { | |
- parent::__construct($dispatcher, $resolver); | |
+ protected function initializeContainer() | |
+ { | |
+ $this->container = $this->buildContainer(); | |
+ $this->container->set('kernel', $this); | |
+ drupal_container($this->container); | |
+ } | |
- $this->matcher = new LegacyUrlMatcher(); | |
- $this->dispatcher->addSubscriber(new RouterListener($this->matcher)); | |
+ /** | |
+ * 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; | |
+ } | |
- $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. | |
- $this->dispatcher->addSubscriber(new ViewSubscriber($negotiation)); | |
- $this->dispatcher->addSubscriber(new AccessSubscriber()); | |
- $this->dispatcher->addSubscriber(new MaintenanceModeSubscriber()); | |
- $this->dispatcher->addSubscriber(new PathSubscriber()); | |
- $this->dispatcher->addSubscriber(new LegacyRequestSubscriber()); | |
- $this->dispatcher->addSubscriber(new LegacyControllerSubscriber()); | |
- $this->dispatcher->addSubscriber(new FinishResponseSubscriber()); | |
- $this->dispatcher->addSubscriber(new RequestCloseSubscriber()); | |
+ /** | |
+ * Gets a new ContainerBuilder instance used to build the service container. | |
+ * | |
+ * @return ContainerBuilder | |
+ */ | |
+ protected function getContainerBuilder() | |
+ { | |
+ return new ContainerBuilder(new ParameterBag($this->getKernelParameters())); | |
+ } | |
- // 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. | |
- $this->dispatcher->addSubscriber(new ExceptionListener(array(new ExceptionController($this, $negotiation), 'execute'))); | |
+ 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 b163395..cb068b4 100644 | |
--- a/core/lib/Drupal/Core/ExceptionController.php | |
+++ b/core/lib/Drupal/Core/ExceptionController.php | |
@@ -7,25 +7,17 @@ | |
namespace Drupal\Core; | |
+use Symfony\Component\DependencyInjection\ContainerAware; | |
use Symfony\Component\HttpFoundation\Request; | |
use Symfony\Component\HttpFoundation\Response; | |
use Symfony\Component\HttpFoundation\JsonResponse; | |
-use Symfony\Component\HttpKernel\HttpKernelInterface; | |
+use Symfony\Component\HttpKernel\HttpKernel; | |
use Symfony\Component\HttpKernel\Exception\FlattenException; | |
/** | |
* This controller handles HTTP errors generated by the routing system. | |
*/ | |
-class ExceptionController { | |
- | |
- /** | |
- * The kernel that spawned this controller. | |
- * | |
- * We will use this to fire subrequests as needed. | |
- * | |
- * @var Symfony\Component\HttpKernel\HttpKernelInterface | |
- */ | |
- protected $kernel; | |
+class ExceptionController extends ContainerAware { | |
/** | |
* The content negotiation library. | |
@@ -37,15 +29,11 @@ class ExceptionController { | |
/** | |
* Constructor. | |
* | |
- * @param Symfony\Component\HttpKernel\HttpKernelInterface $kernel | |
- * The kernel that spawned this controller, so that it can be reused | |
- * for subrequests. | |
* @param Drupal\Core\ContentNegotiation $negotiation | |
* The content negotiation library to use to determine the correct response | |
* format. | |
*/ | |
- public function __construct(HttpKernelInterface $kernel, ContentNegotiation $negotiation) { | |
- $this->kernel = $kernel; | |
+ public function __construct(ContentNegotiation $negotiation) { | |
$this->negotiation = $negotiation; | |
} | |
@@ -119,7 +107,7 @@ class ExceptionController { | |
drupal_static_reset('menu_set_active_trail'); | |
menu_reset_static_cache(); | |
- $response = $this->kernel->handle($subrequest, DrupalKernel::SUB_REQUEST); | |
+ $response = $this->container->get('http_kernel')->handle($subrequest, HttpKernel::SUB_REQUEST); | |
$response->setStatusCode(403, 'Access denied'); | |
} | |
else { | |
@@ -184,7 +172,7 @@ class ExceptionController { | |
drupal_static_reset('menu_set_active_trail'); | |
menu_reset_static_cache(); | |
- $response = $this->kernel->handle($subrequest, HttpKernelInterface::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 2f05bb3..10e38af 100644 | |
--- a/index.php | |
+++ b/index.php | |
@@ -13,8 +13,6 @@ | |
use Drupal\Core\DrupalKernel; | |
use Symfony\Component\HttpFoundation\Request; | |
-use Symfony\Component\EventDispatcher\EventDispatcher; | |
-use Symfony\Component\HttpKernel\Controller\ControllerResolver; | |
/** | |
* Root directory of Drupal installation. | |
@@ -23,6 +21,7 @@ define('DRUPAL_ROOT', getcwd()); | |
// 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(); | |
@@ -32,6 +31,9 @@ $request = Request::createFromGlobals(); | |
// 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. | |
@@ -39,9 +41,6 @@ request($request); | |
// @see Drupal\Core\EventSubscriber\LegacyRequestSubscriber; | |
drupal_bootstrap(DRUPAL_BOOTSTRAP_CODE); | |
-$dispatcher = new EventDispatcher(); | |
-$resolver = new ControllerResolver(); | |
- | |
-$kernel = new DrupalKernel($dispatcher, $resolver); | |
$response = $kernel->handle($request)->prepare($request)->send(); | |
+ | |
$kernel->terminate($request, $response); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment