DrupalKernel.php 8.18 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
class DrupalKernel extends Kernel implements DrupalKernelInterface {
30

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
117
118
119
120
121
122
123
124
125
126
  /**
   * Implements Drupal\Core\DrupalKernelInterface::updateModules().
   */
  public function updateModules($module_list) {
    $this->moduleList = $module_list;
    // If we haven't yet booted, we don't need to do anything: the new module
    // list will take effect when boot() is called. If we have already booted,
    // then reboot in order to refresh the bundle list and container.
    if ($this->booted) {
      drupal_container(NULL, TRUE);
      $this->booted = FALSE;
      $this->boot();
    }
  }
127

128
129
130
  /**
   * Initializes the service container.
   */
131
  protected function initializeContainer() {
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
    $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';
      }
    }

162
    $this->container->set('kernel', $this);
163

164
    drupal_container($this->container);
165
166
167
168

    if (isset($error)) {
      watchdog('DrupalKernel', $error);
    }
169
  }
170

171
172
173
174
175
  /**
   * Builds the service container.
   *
   * @return ContainerBuilder The compiled service container
   */
176
  protected function buildContainer() {
177
    $container = $this->getContainerBuilder();
katbailey's avatar
katbailey committed
178

179
    // Merge in the minimal bootstrap container.
katbailey's avatar
katbailey committed
180
181
182
    if ($bootstrap_container = drupal_container()) {
      $container->merge($bootstrap_container);
    }
183
184
    foreach ($this->bundles as $bundle) {
      $bundle->build($container);
185
    }
186
    $container->compile();
187
188
    return $container;
  }
189

190
191
192
193
194
  /**
   * Gets a new ContainerBuilder instance used to build the service container.
   *
   * @return ContainerBuilder
   */
195
  protected function getContainerBuilder() {
196
197
198
    return new ContainerBuilder(new ParameterBag($this->getKernelParameters()));
  }

199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
  /**
   * 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
227
  /**
Crell's avatar
Crell committed
228
229
   * Overrides and eliminates this method from the parent class. Do not use.
   *
katbailey's avatar
katbailey committed
230
231
232
233
234
235
236
   * 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.
   */
237
  public function registerContainerConfiguration(LoaderInterface $loader) {
238
  }
239

240
}