From 6aeb7e9a3287ccd8bf01596e8825ed880835d014 Mon Sep 17 00:00:00 2001
From: Nathaniel Catchpole <catch@35733.no-reply.drupal.org>
Date: Wed, 3 Sep 2014 14:02:51 +0100
Subject: [PATCH] Issue #2325197 by tstoeckler, sun: Remove
 drupal_classloader().

---
 core/includes/bootstrap.inc                   | 49 ++-----------------
 core/includes/install.core.inc                |  5 +-
 core/lib/Drupal/Core/DrupalKernel.php         | 42 +++++++++-------
 core/lib/Drupal/Core/Site/Settings.php        |  8 ++-
 .../simpletest/src/InstallerTestBase.php      |  5 +-
 .../modules/simpletest/src/KernelTestBase.php |  3 +-
 core/modules/simpletest/src/WebTestBase.php   |  7 +--
 .../Tests/DrupalKernel/DrupalKernelTest.php   | 14 ++----
 .../System/IgnoreReplicaSubscriberTest.php    |  3 +-
 core/rebuild.php                              |  2 +-
 sites/default/default.settings.php            | 22 ++++++---
 11 files changed, 68 insertions(+), 92 deletions(-)

diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc
index 27494e744b09..8c9603106527 100644
--- a/core/includes/bootstrap.inc
+++ b/core/includes/bootstrap.inc
@@ -1024,7 +1024,8 @@ function drupal_bootstrap($phase = NULL) {
 
       switch ($current_phase) {
         case DRUPAL_BOOTSTRAP_CONFIGURATION:
-          $kernel = DrupalKernel::createFromRequest($request, drupal_classloader(), 'prod');
+          $classloader = require __DIR__ . '/../vendor/autoload.php';
+          $kernel = DrupalKernel::createFromRequest($request, $classloader, 'prod');
           break;
 
         case DRUPAL_BOOTSTRAP_KERNEL:
@@ -1398,50 +1399,6 @@ function _current_path($path = NULL) {
   return $current_path;
 }
 
-/**
- * Initializes and returns the class loader.
- *
- * The class loader is responsible for lazy-loading all PSR-0 compatible
- * classes, interfaces, and traits (PHP 5.4 and later). It's only dependency
- * is DRUPAL_ROOT. Otherwise it may be called as early as possible.
- *
- * @param $class_loader
- *   The name of class loader to use. This can be used to change the class
- *   loader class when calling drupal_classloader() from settings.php. It is
- *   ignored otherwise.
- *
- * @return \Composer\Autoload\ClassLoader
- *   A ClassLoader class instance (or extension thereof).
- */
-function drupal_classloader($class_loader = NULL) {
-  // By default, use the ClassLoader which is best for development, as it does
-  // not break when code is moved on the file system. However, as it is slow,
-  // allow to use the APC class loader in production.
-  static $loader;
-
-  if (!isset($loader)) {
-
-    // Retrieve the Composer ClassLoader for loading classes.
-    $loader = include __DIR__ . '/../vendor/autoload.php';
-
-    // Register the class loader.
-    // When configured to use APC, the ApcClassLoader is registered instead.
-    // Note that ApcClassLoader decorates ClassLoader and only provides the
-    // findFile() method, but none of the others. The actual registry is still
-    // in ClassLoader.
-    if (!isset($class_loader)) {
-      $class_loader = Settings::get('class_loader', 'default');
-    }
-    if ($class_loader === 'apc') {
-      require_once __DIR__ . '/../vendor/symfony/class-loader/Symfony/Component/ClassLoader/ApcClassLoader.php';
-      $apc_loader = new ApcClassLoader('drupal.' . Settings::getHashSalt(), $loader);
-      $loader->unregister();
-      $apc_loader->register();
-    }
-  }
-  return $loader;
-}
-
 /**
  * Registers an additional namespace.
  *
@@ -1451,7 +1408,7 @@ function drupal_classloader($class_loader = NULL) {
  *   The relative path to the Drupal component in the filesystem.
  */
 function drupal_classloader_register($name, $path) {
-  $loader = drupal_classloader();
+  $loader = \Drupal::service('class_loader');
   $loader->addPsr4('Drupal\\' . $name . '\\', DRUPAL_ROOT . '/' . $path . '/src');
 }
 
diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc
index 8797e74f14e0..f49b5218fe07 100644
--- a/core/includes/install.core.inc
+++ b/core/includes/install.core.inc
@@ -286,7 +286,8 @@ function install_begin_request(&$install_state) {
   }
 
   $site_path = DrupalKernel::findSitePath($request, FALSE);
-  Settings::initialize($site_path);
+  $class_loader = require __DIR__ . '/../vendor/autoload.php';
+  Settings::initialize($site_path, $class_loader);
 
   // Ensure that procedural dependencies are loaded as early as possible,
   // since the error/exception handlers depend on them.
@@ -358,7 +359,7 @@ function install_begin_request(&$install_state) {
   }
 
   // Only allow dumping the container once the hash salt has been created.
-  $kernel = InstallerKernel::createFromRequest($request, drupal_classloader(), $environment, (bool) Settings::get('hash_salt', FALSE));
+  $kernel = InstallerKernel::createFromRequest($request, $class_loader, $environment, (bool) Settings::get('hash_salt', FALSE));
   $kernel->setSitePath($site_path);
   $kernel->boot();
   $container = $kernel->getContainer();
diff --git a/core/lib/Drupal/Core/DrupalKernel.php b/core/lib/Drupal/Core/DrupalKernel.php
index da657c5f708d..7a303a20a5b7 100644
--- a/core/lib/Drupal/Core/DrupalKernel.php
+++ b/core/lib/Drupal/Core/DrupalKernel.php
@@ -182,19 +182,20 @@ class DrupalKernel implements DrupalKernelInterface, TerminableInterface {
    * Create a DrupalKernel object from a request.
    *
    * @param \Symfony\Component\HttpFoundation\Request $request
-   * @param \Composer\Autoload\ClassLoader $class_loader
-   *   (optional) The classloader is only used if $storage is not given or
-   *   the load from storage fails and a container rebuild is required. In
-   *   this case, the loaded modules will be registered with this loader in
-   *   order to be able to find the module serviceProviders.
+   *   The request.
+   * @param $class_loader
+   *   The class loader. Normally Composer's ClassLoader, as included by the
+   *   front controller, but may also be decorated; e.g.,
+   *   \Symfony\Component\ClassLoader\ApcClassLoader.
    * @param string $environment
    *   String indicating the environment, e.g. 'prod' or 'dev'.
    * @param bool $allow_dumping
    *   (optional) FALSE to stop the container from being written to or read
    *   from disk. Defaults to TRUE.
+   *
    * @return static
    */
-  public static function createFromRequest(Request $request, ClassLoader $class_loader, $environment, $allow_dumping = TRUE) {
+  public static function createFromRequest(Request $request, $class_loader, $environment, $allow_dumping = TRUE) {
     // Include our bootstrap file.
     require_once dirname(dirname(dirname(__DIR__))) . '/includes/bootstrap.inc';
 
@@ -204,9 +205,7 @@ public static function createFromRequest(Request $request, ClassLoader $class_lo
     static::bootEnvironment();
 
     // Get our most basic settings setup.
-    $site_path = static::findSitePath($request);
-    $kernel->setSitePath($site_path);
-    Settings::initialize($site_path);
+    $kernel->initializeSettings($request);
 
     // Redirect the user to the installation script if Drupal has not been
     // installed yet (i.e., if no $databases array has been defined in the
@@ -219,21 +218,32 @@ public static function createFromRequest(Request $request, ClassLoader $class_lo
     return $kernel;
   }
 
+  /**
+   * Initializes the kernel's site path and the Settings singleton.
+   *
+   * @param \Symfony\Component\HttpFoundation\Request $request
+   *   The request that will be used to determine the site path.
+   */
+  protected function initializeSettings(Request $request) {
+    $site_path = static::findSitePath($request);
+    $this->setSitePath($site_path);
+    Settings::initialize($site_path, $this->classLoader);
+  }
+
   /**
    * Constructs a DrupalKernel object.
    *
    * @param string $environment
    *   String indicating the environment, e.g. 'prod' or 'dev'.
-   * @param \Composer\Autoload\ClassLoader $class_loader
-   *   (optional) The class loader is only used if $storage is not given or
-   *   the load from storage fails and a container rebuild is required. In
-   *   this case, the loaded modules will be registered with this loader in
-   *   order to be able to find the module serviceProviders.
+   * @param $class_loader
+   *   The class loader. Normally \Composer\Autoload\ClassLoader, as included by
+   *   the front controller, but may also be decorated; e.g.,
+   *   \Symfony\Component\ClassLoader\ApcClassLoader.
    * @param bool $allow_dumping
    *   (optional) FALSE to stop the container from being written to or read
    *   from disk. Defaults to TRUE.
    */
-  public function __construct($environment, ClassLoader $class_loader, $allow_dumping = TRUE) {
+  public function __construct($environment, $class_loader, $allow_dumping = TRUE) {
     $this->environment = $environment;
     $this->classLoader = $class_loader;
     $this->allowDumping = $allow_dumping;
@@ -349,8 +359,6 @@ public function boot() {
     // Start a page timer:
     Timer::start('page');
 
-    drupal_classloader();
-
     // Load legacy and other functional code.
     require_once DRUPAL_ROOT . '/core/includes/common.inc';
     require_once DRUPAL_ROOT . '/core/includes/database.inc';
diff --git a/core/lib/Drupal/Core/Site/Settings.php b/core/lib/Drupal/Core/Site/Settings.php
index e2d57d2b6e97..def307fd0cbe 100644
--- a/core/lib/Drupal/Core/Site/Settings.php
+++ b/core/lib/Drupal/Core/Site/Settings.php
@@ -87,8 +87,14 @@ public static function getAll() {
    *
    * @param string $site_path
    *   The current site path.
+   * @param \Composer\Autoload\ClassLoader $class_loader
+   *   The class loader that is used for this request. Passed by reference and
+   *   exposed to the local scope of settings.php, so as to allow it to be
+   *   decorated with Symfony's ApcClassLoader, for example.
+   *
+   * @see default.settings.php
    */
-  public static function initialize($site_path) {
+  public static function initialize($site_path, &$class_loader) {
     // Export these settings.php variables to the global namespace.
     global $base_url, $cookie_domain, $config_directories, $config;
     $settings = array();
diff --git a/core/modules/simpletest/src/InstallerTestBase.php b/core/modules/simpletest/src/InstallerTestBase.php
index 34354a5aa959..90f318bbdd3a 100644
--- a/core/modules/simpletest/src/InstallerTestBase.php
+++ b/core/modules/simpletest/src/InstallerTestBase.php
@@ -138,7 +138,8 @@ protected function setUp() {
 
     // Import new settings.php written by the installer.
     $request = Request::createFromGlobals();
-    Settings::initialize(DrupalKernel::findSitePath($request));
+    $class_loader = require DRUPAL_ROOT . '/core/vendor/autoload.php';
+    Settings::initialize(DrupalKernel::findSitePath($request), $class_loader);
     foreach ($GLOBALS['config_directories'] as $type => $path) {
       $this->configDirectories[$type] = $path;
     }
@@ -150,7 +151,7 @@ protected function setUp() {
     // WebTestBase::tearDown() will delete the entire test site directory.
     // Not using File API; a potential error must trigger a PHP warning.
     chmod(DRUPAL_ROOT . '/' . $this->siteDirectory, 0777);
-    $this->kernel = DrupalKernel::createFromRequest($request, drupal_classloader(), 'prod', FALSE);
+    $this->kernel = DrupalKernel::createFromRequest($request, $class_loader, 'prod', FALSE);
     $this->kernel->prepareLegacyRequest($request);
     $this->container = $this->kernel->getContainer();
     $config = $this->container->get('config.factory');
diff --git a/core/modules/simpletest/src/KernelTestBase.php b/core/modules/simpletest/src/KernelTestBase.php
index ea15a3201cf2..4ef5e1097745 100644
--- a/core/modules/simpletest/src/KernelTestBase.php
+++ b/core/modules/simpletest/src/KernelTestBase.php
@@ -155,7 +155,8 @@ protected function setUp() {
     // Back up settings from TestBase::prepareEnvironment().
     $settings = Settings::getAll();
     // Bootstrap a new kernel. Don't use createFromRequest so we don't mess with settings.
-    $this->kernel = new DrupalKernel('testing', drupal_classloader(), FALSE);
+    $class_loader = require DRUPAL_ROOT . '/core/vendor/autoload.php';
+    $this->kernel = new DrupalKernel('testing', $class_loader, FALSE);
     $request = Request::create('/');
     $this->kernel->setSitePath(DrupalKernel::findSitePath($request));
     $this->kernel->boot();
diff --git a/core/modules/simpletest/src/WebTestBase.php b/core/modules/simpletest/src/WebTestBase.php
index c1fe6f70a4f8..e7c1875eb09f 100644
--- a/core/modules/simpletest/src/WebTestBase.php
+++ b/core/modules/simpletest/src/WebTestBase.php
@@ -861,14 +861,15 @@ protected function setUp() {
     // Since Drupal is bootstrapped already, install_begin_request() will not
     // bootstrap into DRUPAL_BOOTSTRAP_CONFIGURATION (again). Hence, we have to
     // reload the newly written custom settings.php manually.
-    Settings::initialize($directory);
+    $class_loader = require DRUPAL_ROOT . '/core/vendor/autoload.php';
+    Settings::initialize($directory, $class_loader);
 
     // Execute the non-interactive installer.
     require_once DRUPAL_ROOT . '/core/includes/install.core.inc';
     install_drupal($parameters);
 
     // Import new settings.php written by the installer.
-    Settings::initialize($directory);
+    Settings::initialize($directory, $class_loader);
     foreach ($GLOBALS['config_directories'] as $type => $path) {
       $this->configDirectories[$type] = $path;
     }
@@ -882,7 +883,7 @@ protected function setUp() {
     chmod($directory, 0777);
 
     $request = \Drupal::request();
-    $this->kernel = DrupalKernel::createFromRequest($request, drupal_classloader(), 'prod', TRUE);
+    $this->kernel = DrupalKernel::createFromRequest($request, $class_loader, 'prod', TRUE);
     $this->kernel->prepareLegacyRequest($request);
     // Force the container to be built from scratch instead of loaded from the
     // disk. This forces us to not accidently load the parent site.
diff --git a/core/modules/system/src/Tests/DrupalKernel/DrupalKernelTest.php b/core/modules/system/src/Tests/DrupalKernel/DrupalKernelTest.php
index a36adaebbc3f..a7ac85a8abe2 100644
--- a/core/modules/system/src/Tests/DrupalKernel/DrupalKernelTest.php
+++ b/core/modules/system/src/Tests/DrupalKernel/DrupalKernelTest.php
@@ -9,7 +9,7 @@
 
 use Drupal\Core\DrupalKernel;
 use Drupal\Core\Site\Settings;
-use Drupal\simpletest\DrupalUnitTestBase;
+use Drupal\simpletest\KernelTestBase;
 use Symfony\Component\HttpFoundation\Request;
 
 /**
@@ -17,12 +17,7 @@
  *
  * @group DrupalKernel
  */
-class DrupalKernelTest extends DrupalUnitTestBase {
-
-  /**
-   * @var \Composer\Autoload\ClassLoader
-   */
-  protected $classloader;
+class DrupalKernelTest extends KernelTestBase {
 
   protected function setUp() {
     // DrupalKernel relies on global $config_directories and requires those
@@ -38,8 +33,6 @@ protected function setUp() {
       'directory' => DRUPAL_ROOT . '/' . $this->public_files_directory . '/php',
       'secret' => Settings::getHashSalt(),
     )));
-
-    $this->classloader = drupal_classloader();
   }
 
   /**
@@ -59,7 +52,8 @@ protected function setUp() {
    */
   protected function getTestKernel(Request $request, array $modules_enabled = NULL, $read_only = FALSE) {
     // Manually create kernel to avoid replacing settings.
-    $kernel = DrupalKernel::createFromRequest($request, drupal_classloader(), 'testing');
+    $class_loader = require DRUPAL_ROOT . '/core/vendor/autoload.php';
+    $kernel = DrupalKernel::createFromRequest($request, $class_loader, 'testing');
     $this->settingsSet('hash_salt', $this->databasePrefix);
     if (isset($modules_enabled)) {
       $kernel->updateModules($modules_enabled);
diff --git a/core/modules/system/src/Tests/System/IgnoreReplicaSubscriberTest.php b/core/modules/system/src/Tests/System/IgnoreReplicaSubscriberTest.php
index 49d11ecf81ae..404b8e4b46b4 100644
--- a/core/modules/system/src/Tests/System/IgnoreReplicaSubscriberTest.php
+++ b/core/modules/system/src/Tests/System/IgnoreReplicaSubscriberTest.php
@@ -32,7 +32,8 @@ function testSystemInitIgnoresSecondaries() {
     Database::addConnectionInfo('default', 'replica', $connection_info['default']);
 
     db_ignore_replica();
-    $kernel = new DrupalKernel('testing', drupal_classloader(), FALSE);
+    $class_loader = require DRUPAL_ROOT . '/core/vendor/autoload.php';
+    $kernel = new DrupalKernel('testing', $class_loader, FALSE);
     $event = new GetResponseEvent($kernel, Request::create('http://example.com'), HttpKernelInterface::MASTER_REQUEST);
     $subscriber = new ReplicaDatabaseIgnoreSubscriber();
     $subscriber->checkReplicaServer($event);
diff --git a/core/rebuild.php b/core/rebuild.php
index f7acbcea8a7c..c915aff7459e 100644
--- a/core/rebuild.php
+++ b/core/rebuild.php
@@ -25,7 +25,7 @@
 // Manually resemble early bootstrap of DrupalKernel::boot().
 require_once __DIR__ . '/includes/bootstrap.inc';
 DrupalKernel::bootEnvironment();
-Settings::initialize(DrupalKernel::findSitePath($request));
+Settings::initialize(DrupalKernel::findSitePath($request), $autoloader);
 
 if (Settings::get('rebuild_access', FALSE) ||
   ($request->get('token') && $request->get('timestamp') &&
diff --git a/sites/default/default.settings.php b/sites/default/default.settings.php
index 017fe8ab82c0..3936dcb4630e 100644
--- a/sites/default/default.settings.php
+++ b/sites/default/default.settings.php
@@ -371,17 +371,23 @@
 /**
  * Class Loader.
  *
- * By default, Drupal uses Composer's ClassLoader, which is best for
- * development, as it does not break when code is moved on the file
- * system. It is possible, however, to wrap the class loader with a
- * cached class loader solution for better performance, which is
+ * By default, Composer's ClassLoader is used, which is best for development, as
+ * it does not break when code is moved in the file system. You can decorate the
+ * class loader with a cached solution for better performance, which is
  * recommended for production sites.
  *
- * Examples:
- *   $settings['class_loader'] = 'apc';
- *   $settings['class_loader'] = 'default';
+ * To do so, you may decorate and replace the local $class_loader variable.
+ *
+ * For example, to use Symfony's APC class loader, uncomment the code below.
  */
-# $settings['class_loader'] = 'apc';
+/*
+if ($settings['hash_salt']) {
+  $apc_loader = new \Symfony\Component\ClassLoader\ApcClassLoader('drupal.' . $settings['hash_salt'], $class_loader);
+  $class_loader->unregister();
+  $apc_loader->register();
+  $class_loader = $apc_loader;
+}
+*/
 
 /**
  * Authorized file system operations:
-- 
GitLab