Commit 8c21dcb6 authored by catch's avatar catch

Issue #1658720 by RobLoach, dawehner, sun, chx: Use ClassLoader instead of UniversalClassLoader.

parent 7aca9955
......@@ -21,6 +21,13 @@
"easyrdf/easyrdf": "0.8.0-beta.1",
"phpunit/phpunit": "3.7.15"
},
"autoload": {
"psr-0": {
"Drupal\\Core": "core/lib/",
"Drupal\\Component": "core/lib/",
"Drupal\\Driver": "drivers/lib/"
}
},
"minimum-stability": "dev",
"config": {
"vendor-dir": "core/vendor"
......
......@@ -6,8 +6,8 @@
use Drupal\Core\DrupalKernel;
use Drupal\Core\Database\Database;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Symfony\Component\ClassLoader\UniversalClassLoader;
use Symfony\Component\ClassLoader\ApcUniversalClassLoader;
use Symfony\Component\ClassLoader\ClassLoader;
use Symfony\Component\ClassLoader\ApcClassLoader;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Container;
use Symfony\Component\DependencyInjection\Reference;
......@@ -3076,78 +3076,41 @@ function ip_address() {
* loader class when calling drupal_classloader() from settings.php. It is
* ignored otherwise.
*
* @return Symfony\Component\ClassLoader\UniversalClassLoader
* A UniversalClassLoader class instance (or extension thereof).
* @return \Symfony\Component\ClassLoader\ClassLoader
* A ClassLoader class instance (or extension thereof).
*/
function drupal_classloader($class_loader = NULL) {
// By default, use the UniversalClassLoader 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.
// 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)) {
// Include the Symfony ClassLoader for loading PSR-0-compatible classes.
require_once DRUPAL_ROOT . '/core/vendor/symfony/class-loader/Symfony/Component/ClassLoader/UniversalClassLoader.php';
if (!isset($class_loader) && class_exists('Drupal\Component\Utility\Settings', FALSE)) {
$class_loader = settings()->get('class_loader');
require_once DRUPAL_ROOT . '/core/vendor/symfony/class-loader/Symfony/Component/ClassLoader/ClassLoader.php';
$loader = new ClassLoader();
// 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');
}
switch ($class_loader) {
case 'apc':
if (function_exists('apc_store')) {
require_once DRUPAL_ROOT . '/core/vendor/symfony/class-loader/Symfony/Component/ClassLoader/ApcUniversalClassLoader.php';
$loader = new ApcUniversalClassLoader('drupal.' . $GLOBALS['drupal_hash_salt']);
break;
}
// Fall through to the default loader if APC was not loaded, so that the
// site does not fail completely.
case 'dev':
case 'default':
default:
$loader = new UniversalClassLoader();
break;
if ($class_loader === 'apc') {
require_once DRUPAL_ROOT . '/core/vendor/symfony/class-loader/Symfony/Component/ClassLoader/ApcClassLoader.php';
$apc_loader = new ApcClassLoader('drupal.' . $GLOBALS['drupal_hash_salt'], $loader);
$apc_loader->register();
}
else {
$loader->register();
}
// Register explicit namespaces for Drupal core.
// The majority of namespaces that need to be resolved are from Drupal core,
// so registering/setting them before vendor libraries saves a few
// additional cycles per class lookup.
$loader->registerNamespaces(array(
'Drupal\Core' => DRUPAL_ROOT . '/core/lib',
'Drupal\Component' => DRUPAL_ROOT . '/core/lib',
'Drupal\Driver' => DRUPAL_ROOT . '/drivers/lib',
));
// Register namespaces and PEAR-like prefixes for vendor libraries managed
// by Composer. Composer combines libraries that use PHP 5.3 namespaces and
// ones that use PEAR-like class prefixes in a single array, but the Symfony
// class loader requires them to be registered separately.
// Register namespaces for vendor libraries managed by Composer.
$prefixes_and_namespaces = require DRUPAL_ROOT . '/core/vendor/composer/autoload_namespaces.php';
$prefixes = array();
$namespaces = array();
foreach ($prefixes_and_namespaces as $key => $path) {
// If the key:
// - Contains a namespace separator, we know it's a namespace.
// - Doesn't contain a namespace separator and ends in an "_" (e.g.,
// "Twig_"), it's likely intended as a PEAR-like prefix rather than a
// namespace.
// - Doesn't contain a namespace separator or end in an "_" (e.g.,
// "Assetic"), then it could be either a namespace or an incomplete
// PEAR-like prefix, but we assume the former, since the only example of
// that currently in Drupal is Assetic.
// @todo Switch to a class loader that doesn't require this guessing:
// http://drupal.org/node/1658720.
$is_namespace = (strpos($key, '\\') !== FALSE) && (substr($key, -1) !== '_');
if ($is_namespace) {
$namespaces[rtrim($key, '\\')] = $path;
}
else {
$prefixes[$key] = $path;
}
}
$loader->registerPrefixes($prefixes);
$loader->registerNamespaces($namespaces);
$loader->addPrefixes($prefixes_and_namespaces);
// Register the loader with PHP.
$loader->register();
......@@ -3165,7 +3128,7 @@ function drupal_classloader($class_loader = NULL) {
*/
function drupal_classloader_register($name, $path) {
$loader = drupal_classloader();
$loader->registerNamespace('Drupal\\' . $name, DRUPAL_ROOT . '/' . $path . '/lib');
$loader->addPrefix('Drupal\\' . $name, DRUPAL_ROOT . '/' . $path . '/lib');
}
/**
......
......@@ -11,7 +11,7 @@
use Drupal\Core\Config\BootstrapConfigStorageFactory;
use Drupal\Core\CoreBundle;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Symfony\Component\ClassLoader\UniversalClassLoader;
use Symfony\Component\ClassLoader\ClassLoader;
use Symfony\Component\Config\Loader\LoaderInterface;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
use Symfony\Component\DependencyInjection\Dumper\PhpDumper;
......@@ -67,7 +67,7 @@ class DrupalKernel extends Kernel implements DrupalKernelInterface {
/**
* The classloader object.
*
* @var \Symfony\Component\ClassLoader\UniversalClassLoader
* @var \Symfony\Component\ClassLoader\ClassLoader
*/
protected $classLoader;
......@@ -110,7 +110,7 @@ class DrupalKernel extends Kernel implements DrupalKernelInterface {
* 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 \Symfony\Component\ClassLoader\UniversalClassLoader $class_loader
* @param \Symfony\Component\ClassLoader\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
......@@ -119,7 +119,7 @@ class DrupalKernel extends Kernel implements DrupalKernelInterface {
* (optional) FALSE to stop the container from being written to or read
* from disk. Defaults to TRUE.
*/
public function __construct($environment, $debug, UniversalClassLoader $class_loader, $allow_dumping = TRUE) {
public function __construct($environment, $debug, ClassLoader $class_loader, $allow_dumping = TRUE) {
parent::__construct($environment, $debug);
$this->classLoader = $class_loader;
$this->allowDumping = $allow_dumping;
......@@ -294,7 +294,7 @@ protected function initializeContainer() {
// All namespaces must be registered before we attempt to use any service
// from the container.
$container_modules = $this->container->getParameter('container.modules');
$namespaces_before = $this->classLoader->getNamespaces();
$namespaces_before = $this->classLoader->getPrefixes();
$this->registerNamespaces($this->getModuleNamespaces($container_modules));
// If 'container.modules' is wrong, the container must be rebuilt.
......@@ -308,9 +308,9 @@ protected function initializeContainer() {
// registerNamespaces() performs a merge rather than replace, so to
// effectively remove erroneous registrations, we must replace them with
// empty arrays.
$namespaces_after = $this->classLoader->getNamespaces();
$namespaces_after = $this->classLoader->getPrefixes();
$namespaces_before += array_fill_keys(array_diff(array_keys($namespaces_after), array_keys($namespaces_before)), array());
$this->classLoader->registerNamespaces($namespaces_before);
$this->registerNamespaces($namespaces_before);
}
}
......@@ -376,7 +376,7 @@ protected function buildContainer() {
$container->setParameter('container.namespaces', $namespaces);
// Register synthetic services.
$container->register('class_loader', 'Symfony\Component\ClassLoader\UniversalClassLoader')->setSynthetic(TRUE);
$container->register('class_loader', 'Symfony\Component\ClassLoader\ClassLoader')->setSynthetic(TRUE);
$container->register('kernel', 'Symfony\Component\HttpKernel\KernelInterface')->setSynthetic(TRUE);
$container->register('service_container', 'Symfony\Component\DependencyInjection\ContainerInterface')->setSynthetic(TRUE);
foreach ($this->bundles as $bundle) {
......@@ -474,8 +474,6 @@ protected function getModuleNamespaces($moduleFileNames) {
* Registers a list of namespaces.
*/
protected function registerNamespaces(array $namespaces = array()) {
foreach ($namespaces as $namespace => $dir) {
$this->classLoader->registerNamespace($namespace, $dir);
}
$this->classLoader->addPrefixes($namespaces);
}
}
......@@ -526,19 +526,19 @@ function simpletest_classloader_register() {
$matches = drupal_system_listing('/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.' . $info['extension'] . '$/', $info['dir']);
foreach ($matches as $name => $file) {
drupal_classloader_register($name, dirname($file->uri));
drupal_classloader()->registerNamespace('Drupal\\' . $name . '\\Tests', DRUPAL_ROOT . '/' . dirname($file->uri) . '/tests');
drupal_classloader()->addPrefix('Drupal\\' . $name . '\\Tests', DRUPAL_ROOT . '/' . dirname($file->uri) . '/tests');
// While being there, prime drupal_get_filename().
drupal_get_filename($type, $name, $file->uri);
}
}
// Register the core test directory so we can find Drupal\UnitTestCase.
drupal_classloader()->registerNamespace('Drupal\\Tests', DRUPAL_ROOT . '/core/tests');
drupal_classloader()->addPrefix('Drupal\\Tests', DRUPAL_ROOT . '/core/tests');
// Manually register phpunit prefixes because they use a classmap instead of a
// prefix. This can be safely removed if we move to using composer's
// autoloader with a classmap.
drupal_classloader()->registerPrefixes(array(
drupal_classloader()->addPrefixes(array(
'PHPUnit' => DRUPAL_ROOT . '/core/vendor/phpunit/phpunit',
'File_Iterator' => DRUPAL_ROOT . '/core/vendor/phpunit/php-file-iterator/',
'PHP_Timer' => DRUPAL_ROOT . '/core/vendor/phpunit/php-timer/',
......
......@@ -13,6 +13,12 @@
* Tests class loading.
*/
class ClassLoaderTest extends WebTestBase {
/**
* The expected result from calling the module-provided class' method.
*/
protected $expected = 'Drupal\\module_autoload_test\\SomeClass::testMethod() was invoked.';
public static function getInfo() {
return array(
'name' => 'Module class loader',
......@@ -23,24 +29,33 @@ public static function getInfo() {
/**
* Tests that module-provided classes can be loaded when a module is enabled.
*
* @see \Drupal\module_autoload_test\SomeClass
*/
function testClassLoading() {
$expected = 'Drupal\\module_autoload_test\\SomeClass::testMethod() was invoked.';
// Enable the module_test and module_autoload_test modules.
module_enable(array('module_test', 'module_autoload_test'), FALSE);
$this->resetAll();
// Check twice to test an unprimed and primed system_list() cache.
for ($i=0; $i<2; $i++) {
$this->drupalGet('module-test/class-loading');
$this->assertText($expected, 'Autoloader loads classes from an enabled module.');
$this->assertText($this->expected, 'Autoloader loads classes from an enabled module.');
}
}
/**
* Tests that module-provided classes can't be loaded from disabled modules.
*
* @see \Drupal\module_autoload_test\SomeClass
*/
function testClassLoadingDisabledModules() {
// Ensure that module_autoload_test is disabled.
module_disable(array('module_autoload_test'), FALSE);
$this->resetAll();
// Check twice to test an unprimed and primed system_list() cache.
for ($i=0; $i<2; $i++) {
$this->drupalGet('module-test/class-loading');
$this->assertNoText($expected, 'Autoloader does not load classes from a disabled module.');
$this->assertNoText($this->expected, 'Autoloader does not load classes from a disabled module.');
}
}
}
......@@ -6,6 +6,9 @@
$baseDir = dirname($vendorDir);
return array(
'Drupal\\Driver' => $baseDir . '/../drivers/lib/',
'Drupal\\Core' => $baseDir . '/lib/',
'Drupal\\Component' => $baseDir . '/lib/',
'Twig_' => $vendorDir . '/twig/twig/lib/',
'Symfony\\Component\\Yaml\\' => $vendorDir . '/symfony/yaml/',
'Symfony\\Component\\Validator\\' => $vendorDir . '/symfony/validator/',
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment