DrupalKernel.php 7.65 KB
Newer Older
1 2 3 4 5 6 7 8 9
<?php

/**
 * @file
 * Definition of Drupal\Core\DrupalKernel.
 */

namespace Drupal\Core;

10
use Drupal\Core\Cache\CacheBackendInterface;
11
use Drupal\Core\CoreBundle;
12
use Drupal\Component\PhpStorage\PhpStorageInterface;
13 14 15 16
use Symfony\Component\HttpKernel\Kernel;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Symfony\Component\Config\Loader\LoaderInterface;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
17
use Symfony\Component\DependencyInjection\Dumper\PhpDumper;
18 19 20

/**
 * The DrupalKernel class is the core of Drupal itself.
21 22 23 24 25 26 27
 *
 * This class is responsible for building the Dependency Injection Container and
 * also deals with the registration of bundles. It allows registered bundles to
 * add their services to the container. Core provides the CoreBundle, which adds
 * the services required for all core subsystems. Each module can then add its
 * own bundle, i.e. a subclass of Symfony\Component\HttpKernel\Bundle, to
 * register services to the container.
28 29 30
 */
class DrupalKernel extends Kernel {

31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
  /**
   * Holds the list of enabled modules.
   *
   * @var array
   */
  protected $moduleList;

  /**
   * Cache object for getting or setting the compiled container's class name.
   *
   * @var \Drupal\Core\Cache\CacheBackendInterface
   */
  protected $compilationIndexCache;

  /**
   * PHP code storage object to use for the compiled container.
   *
   * @var \Drupal\Component\PhpStorage\PhpStorageInterface
   */
  protected $storage;

  /**
   * Constructs a DrupalKernel object.
   *
   * @param string $environment
   *   String indicating the environment, e.g. 'prod' or 'dev'. Used by
   *   Symfony\Component\HttpKernel\Kernel::__construct(). Drupal does not use
   *   this value currently. Pass 'prod'.
   * @param bool $debug
   *   Boolean indicating whether we are in debug mode. Used by
   *   Symfony\Component\HttpKernel\Kernel::__construct(). Drupal does not use
   *   this value currently. Pass TRUE.
   * @param array $module_list
   *   (optional) The array of enabled modules as returned by module_list().
   * @param Drupal\Core\Cache\CacheBackendInterface $compilation_index_cache
   *   (optional) If wanting to dump a compiled container to disk or use a
   *   previously compiled container, the cache object for the bin that stores
   *   the class name of the compiled container.
   */
  public function __construct($environment, $debug, array $module_list = NULL, CacheBackendInterface $compilation_index_cache = NULL) {
    parent::__construct($environment, $debug);
    $this->compilationIndexCache = $compilation_index_cache;
    $this->storage = drupal_php_storage('service_container');
    if (isset($module_list)) {
      $this->moduleList = $module_list;
    }
    else {
      // @todo This is a temporary measure which will no longer be necessary
      //   once we have an ExtensionHandler for managing this list. See
      //   http://drupal.org/node/1331486.
      $this->moduleList = module_list();
    }
  }

85 86 87 88 89 90 91 92 93 94
  /**
   * Overrides Kernel::init().
   */
  public function init() {
    // Intentionally empty. The sole purpose is to not execute Kernel::init(),
    // since that overrides/breaks Drupal's current error handling.
    // @todo Investigate whether it is possible to migrate Drupal's error
    //   handling to the one of Kernel without losing functionality.
  }

95 96 97
  /**
   * Returns an array of available bundles.
   */
98
  public function registerBundles() {
99
    $bundles = array(
100
      new CoreBundle(),
101
    );
102

103
    foreach ($this->moduleList as $module) {
katbailey's avatar
katbailey committed
104
      $camelized = ContainerBuilder::camelize($module);
105
      $class = "Drupal\\{$module}\\{$camelized}Bundle";
106 107 108
      if (class_exists($class)) {
        $bundles[] = new $class();
      }
109
    }
110 111
    return $bundles;
  }
112 113


114 115 116
  /**
   * Initializes the service container.
   */
117
  protected function initializeContainer() {
118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
    $this->container = NULL;
    if ($this->compilationIndexCache) {
      // The name of the compiled container class is generated from the hash of
      // its contents and cached. This enables multiple compiled containers
      // (for example, for different states of which modules are enabled) to
      // exist simultaneously on disk and in memory.
      if ($cache = $this->compilationIndexCache->get(implode(':', array('service_container', $this->environment, $this->debug)))) {
        $class = $cache->data;
        $cache_file = $class . '.php';

        // First, try to load.
        if (!class_exists($class, FALSE)) {
          $this->storage->load($cache_file);
        }
        // If the load succeeded or the class already existed, use it.
        if (class_exists($class, FALSE)) {
          $fully_qualified_class_name = '\\' . $class;
          $this->container = new $fully_qualified_class_name;
        }
      }
    }
    if (!isset($this->container)) {
      $this->container = $this->buildContainer();
      if ($this->compilationIndexCache && !$this->dumpDrupalContainer($this->container, $this->getContainerBaseClass())) {
        // We want to log this as an error but we cannot call watchdog() until
        // the container has been fully built and set in drupal_container().
        $error = 'Container cannot be written to disk';
      }
    }

148
    $this->container->set('kernel', $this);
149

150
    drupal_container($this->container);
151 152 153 154

    if (isset($error)) {
      watchdog('DrupalKernel', $error);
    }
155
  }
156

157 158 159 160 161
  /**
   * Builds the service container.
   *
   * @return ContainerBuilder The compiled service container
   */
162
  protected function buildContainer() {
163
    $container = $this->getContainerBuilder();
katbailey's avatar
katbailey committed
164

165
    // Merge in the minimal bootstrap container.
katbailey's avatar
katbailey committed
166 167 168
    if ($bootstrap_container = drupal_container()) {
      $container->merge($bootstrap_container);
    }
169 170
    foreach ($this->bundles as $bundle) {
      $bundle->build($container);
171
    }
172
    $container->compile();
173 174
    return $container;
  }
175

176 177 178 179 180
  /**
   * Gets a new ContainerBuilder instance used to build the service container.
   *
   * @return ContainerBuilder
   */
181
  protected function getContainerBuilder() {
182 183 184
    return new ContainerBuilder(new ParameterBag($this->getKernelParameters()));
  }

185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212
  /**
   * Dumps the service container to PHP code in the config directory.
   *
   * This method is based on the dumpContainer method in the parent class, but
   * that method is reliant on the Config component which we do not use here.
   *
   * @param ContainerBuilder $container
   *   The service container.
   * @param string $baseClass
   *   The name of the container's base class
   *
   * @return bool
   *   TRUE if the container was successfully dumped to disk.
   */
  protected function dumpDrupalContainer(ContainerBuilder $container, $baseClass) {
    if (!$this->storage->writeable()) {
      return FALSE;
    }
    // Cache the container.
    $dumper = new PhpDumper($container);
    $content = $dumper->dump(array('class' => 'DrupalServiceContainerStub', 'base_class' => $baseClass));
    $class = 'c' . hash('sha256', $content);
    $content = str_replace('DrupalServiceContainerStub', $class, $content);
    $this->compilationIndexCache->set(implode(':', array('service_container', $this->environment, $this->debug)), $class);

    return $this->storage->save($class . '.php', $content);
  }

katbailey's avatar
katbailey committed
213
  /**
Crell's avatar
Crell committed
214 215
   * Overrides and eliminates this method from the parent class. Do not use.
   *
katbailey's avatar
katbailey committed
216 217 218 219 220 221 222
   * This method is part of the KernelInterface interface, but takes an object
   * implementing LoaderInterface as its only parameter. This is part of the
   * Config compoment from Symfony, which is not provided by Drupal core.
   *
   * Modules wishing to provide an extension to this class which uses this
   * method are responsible for ensuring the Config component exists.
   */
223
  public function registerContainerConfiguration(LoaderInterface $loader) {
224
  }
225
}