Skip to content

Instantly share code, notes, and snippets.

@katbailey
Created June 23, 2012 18:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save katbailey/f9c35b0b8eb64155b4ca to your computer and use it in GitHub Desktop.
Save katbailey/f9c35b0b8eb64155b4ca to your computer and use it in GitHub Desktop.
Switch to Kernel + Bundles + Container Aware Event Dispatcher
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