diff --git a/composer.json b/composer.json
index 7d460ed..9b41c2d 100644
--- a/composer.json
+++ b/composer.json
@@ -42,5 +42,8 @@
"config": {
"vendor-dir": "core/vendor",
"preferred-install": "dist"
+ },
+ "scripts": {
+ "post-autoload-dump": "Drupal\\Core\\Autoload\\GeneratorScript::postAutoloadDump"
}
}
diff --git a/core/autoload.drupal.php b/core/autoload.drupal.php
new file mode 100644
index 0000000..e665445
--- /dev/null
+++ b/core/autoload.drupal.php
@@ -0,0 +1,23 @@
+add('Drupal\\' . $name, DRUPAL_ROOT . '/' . $path . '/lib');
+ $loader->addPsr4('Drupal\\' . $name . '\\', array(
+ DRUPAL_ROOT . '/' . $path . '/lib/Drupal/' . $name,
+ DRUPAL_ROOT . '/' . $path . '/lib',
+ ));
}
/**
diff --git a/core/lib/Drupal/Component/Annotation/Plugin/Discovery/AnnotatedClassDiscovery.php b/core/lib/Drupal/Component/Annotation/Plugin/Discovery/AnnotatedClassDiscovery.php
index b9236be..559163d 100644
--- a/core/lib/Drupal/Component/Annotation/Plugin/Discovery/AnnotatedClassDiscovery.php
+++ b/core/lib/Drupal/Component/Annotation/Plugin/Discovery/AnnotatedClassDiscovery.php
@@ -98,7 +98,6 @@ public function getDefinitions() {
// Search for classes within all PSR-0 namespace locations.
foreach ($this->getPluginNamespaces() as $namespace => $dirs) {
foreach ($dirs as $dir) {
- $dir .= DIRECTORY_SEPARATOR . str_replace('\\', DIRECTORY_SEPARATOR, $namespace);
if (file_exists($dir)) {
$iterator = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($dir, \RecursiveDirectoryIterator::SKIP_DOTS)
diff --git a/core/lib/Drupal/Core/Autoload/ClassLoader.php b/core/lib/Drupal/Core/Autoload/ClassLoader.php
new file mode 100644
index 0000000..8b5f03e
--- /dev/null
+++ b/core/lib/Drupal/Core/Autoload/ClassLoader.php
@@ -0,0 +1,526 @@
+ array('Drupal\Core\\' => 12),
+ * 'Dm' => array(
+ * 'Drupal\Component\\' => 17,
+ * 'Drupal\number\\' => 14,
+ * ),
+ * 'S' => array('Symfony\\' => 8),
+ * )
+ */
+ private $prefixLengthsPsr4 = array();
+
+ /**
+ * @var array
+ * Namespaces mapped to PSR-4 directories.
+ *
+ * Namespaces are represented with trailing namespace separator, but without
+ * a preceding namespace separator.
+ *
+ * Directories for each namespace are represented as a numeric array.
+ * The loader is designed to work whether or not the directories have a
+ * trailing directory separator.
+ *
+ * E.g. a possible value of this variable could be:
+ *
+ * array(
+ * 'Drupal\Core\\' => array(DRUPAL_ROOT . '/core/lib/Drupal/Core'),
+ * 'Drupal\Component\\' => array(DRUPAL_ROOT . '/core/lib/Drupal/Component'),
+ * )
+ */
+ private $prefixDirsPsr4 = array();
+
+ /**
+ * @var array
+ * PSR-4 directories to use if no matching namespace is found.
+ */
+ private $fallbackDirsPsr4 = array();
+
+ /**
+ * @var array
+ * Prefixes mapped to PSR-0 directories.
+ *
+ * The array has a nested structure, where prefixes are grouped by their
+ * first character.
+ *
+ * E.g. a possible value of this variable could be:
+ *
+ * array(
+ * 'D' => array(
+ * 'Drupal\Core\\' => array(DRUPAL_ROOT . '/core/lib'),
+ * 'Drupal\Component\\' => array(DRUPAL_ROOT . '/core/lib'),
+ * 'Drupal\system\\' => array(DRUPAL_ROOT . '/core/modules/system/lib'),
+ * ),
+ * 'S' => array(
+ * 'Symfony\Component\Routing\\' => array(..),
+ * 'Symfony\Component\Process\\' => array(..),
+ * ),
+ * ),
+ */
+ private $prefixesPsr0 = array();
+
+ /**
+ * @var array
+ * PSR-0 directories to use if no matching prefix is found.
+ */
+ private $fallbackDirsPsr0 = array();
+
+ /**
+ * @var bool
+ * TRUE, if the autoloader uses the include path to check for classes.
+ */
+ private $useIncludePath = FALSE;
+
+ /**
+ * @var array
+ * Specific classes mapped to specific PHP files.
+ */
+ private $classMap = array();
+
+ /**
+ * @const
+ * Position in a fully-qualified class name where a character should be
+ * picked to build the index for $prefixLengthsPsr4.
+ * The position [9] has been chosen because it provides a good distribution
+ * of the typical namespaces in a Drupal project.
+ */
+ const PREDICTOR_INDEX = 9;
+
+ /**
+ * Gets the registered prefixes for PSR-0 directories.
+ *
+ * @return array
+ * Registered prefixes mapped to PSR-0 directories.
+ */
+ public function getPrefixes() {
+ return call_user_func_array('array_merge', $this->prefixesPsr0);
+ }
+
+ /**
+ * Gets the registered namespaces for PSR-4 directories.
+ *
+ * @return array
+ * Namespaces mapped to PSR-4 directories.
+ */
+ public function getPrefixesPsr4() {
+ return $this->prefixDirsPsr4;
+ }
+
+ /**
+ * Gets the PSR-0 fallback directories.
+ *
+ * @return array
+ * PSR-0 directories to use if no matching prefix is found.
+ */
+ public function getFallbackDirs() {
+ return $this->fallbackDirsPsr0;
+ }
+
+ /**
+ * Gets the PSR-4 fallback directories.
+ *
+ * @return array
+ * PSR-0 directories to use if no matching prefix is found.
+ */
+ public function getFallbackDirsPsr4() {
+ return $this->fallbackDirsPsr4;
+ }
+
+ /**
+ * Gets the class map.
+ *
+ * @return array
+ * Specific classes mapped to specific PHP files.
+ */
+ public function getClassMap() {
+ return $this->classMap;
+ }
+
+ /**
+ * Adds a class map.
+ *
+ * @param array $classMap
+ * Specific classes mapped to specific PHP files.
+ */
+ public function addClassMap(array $classMap) {
+ if ($this->classMap) {
+ $this->classMap = array_merge($this->classMap, $classMap);
+ }
+ else {
+ $this->classMap = $classMap;
+ }
+ }
+
+ /**
+ * Adds a set of PSR-0 directories for a given prefix.
+ *
+ * The directories will be appended or prepended to the ones previously set
+ * for this prefix, depending on the $prepend parameter.
+ *
+ * @param string $prefix
+ * The prefix.
+ * @param array|string $paths
+ * The PSR-0 root directories.
+ * @param bool $prepend
+ * (optional) Whether to prepend the directories.
+ */
+ public function add($prefix, $paths, $prepend = FALSE) {
+ if (!$prefix) {
+ if ($prepend) {
+ $this->fallbackDirsPsr0 = array_merge(
+ (array) $paths,
+ $this->fallbackDirsPsr0
+ );
+ }
+ else {
+ $this->fallbackDirsPsr0 = array_merge(
+ $this->fallbackDirsPsr0,
+ (array) $paths
+ );
+ }
+
+ return;
+ }
+
+ $first = $prefix[0];
+ if (!isset($this->prefixesPsr0[$first][$prefix])) {
+ $this->prefixesPsr0[$first][$prefix] = (array) $paths;
+
+ return;
+ }
+ if ($prepend) {
+ $this->prefixesPsr0[$first][$prefix] = array_merge(
+ (array) $paths,
+ $this->prefixesPsr0[$first][$prefix]
+ );
+ }
+ else {
+ $this->prefixesPsr0[$first][$prefix] = array_merge(
+ $this->prefixesPsr0[$first][$prefix],
+ (array) $paths
+ );
+ }
+ }
+
+ /**
+ * Adds a set of PSR-4 directories for a given namespace.
+ *
+ * The directories will be appended or prepended to the ones previously set
+ * for this prefix, depending on the $prepend parameter.
+ *
+ * @param string $prefix
+ * The prefix/namespace, with trailing '\\'.
+ * @param array|string $paths
+ * The PSR-0 base directories.
+ * @param bool $prepend
+ * (optional) Whether to prepend the directories.
+ *
+ * @throws \Exception
+ * Throws an exception if the prefix does not end with a trailing namespace
+ * separator.
+ */
+ public function addPsr4($prefix, $paths, $prepend = FALSE) {
+ if (!$prefix) {
+ // Register directories for the root namespace.
+ if ($prepend) {
+ $this->fallbackDirsPsr4 = array_merge(
+ (array) $paths,
+ $this->fallbackDirsPsr4
+ );
+ }
+ else {
+ $this->fallbackDirsPsr4 = array_merge(
+ $this->fallbackDirsPsr4,
+ (array) $paths
+ );
+ }
+ }
+ elseif (!isset($this->prefixDirsPsr4[$prefix])) {
+ // Register directories for a new namespace.
+ $length = strlen($prefix);
+ if ('\\' !== $prefix[$length - 1]) {
+ throw new \Exception("A non-empty PSR-4 prefix must end with a namespace separator.");
+ }
+ if ($length > self::PREDICTOR_INDEX) {
+ // The namespace is long enough to have a character at position [9].
+ $predictor = $prefix[0] . $prefix[self::PREDICTOR_INDEX];
+ $this->prefixLengthsPsr4[$predictor][$prefix] = $length;
+ }
+ else {
+ // The namespace is too short to have a character at position [9].
+ $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
+ }
+ $this->prefixDirsPsr4[$prefix] = (array) $paths;
+ }
+ elseif ($prepend) {
+ // Prepend directories for an already registered namespace.
+ $this->prefixDirsPsr4[$prefix] = array_merge(
+ (array) $paths,
+ $this->prefixDirsPsr4[$prefix]
+ );
+ }
+ else {
+ // Append directories for an already registered namespace.
+ $this->prefixDirsPsr4[$prefix] = array_merge(
+ $this->prefixDirsPsr4[$prefix],
+ (array) $paths
+ );
+ }
+ }
+
+ /**
+ * Sets/Overwrites the PSR-0 directories for a given prefix.
+ *
+ * This will replace any directories that were previously set for this prefix.
+ *
+ * @param string $prefix
+ * The prefix.
+ * @param array|string $paths
+ * The PSR-0 base directories.
+ */
+ public function set($prefix, $paths) {
+ if (!$prefix) {
+ $this->fallbackDirsPsr0 = (array) $paths;
+ }
+ else {
+ $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
+ }
+ }
+
+ /**
+ * Sets/Overwrites the PSR-4 directories for a given prefix.
+ *
+ * This will replace any directories that were previously set for this prefix.
+ *
+ * @param string $prefix
+ * The prefix/namespace, with trailing '\\'
+ * @param array|string $paths
+ * The PSR-4 base directories
+ *
+ * @throws \Exception
+ * Throws an exception if the prefix does not end with a trailing namespace
+ * separator.
+ */
+ public function setPsr4($prefix, $paths) {
+ if (!$prefix) {
+ $this->fallbackDirsPsr4 = (array) $paths;
+ }
+ else {
+ $length = strlen($prefix);
+ if ('\\' !== $prefix[$length - 1]) {
+ throw new \Exception("A non-empty PSR-4 prefix must end with a namespace separator.");
+ }
+ if ($length > self::PREDICTOR_INDEX) {
+ $predictor = $prefix[0] . $prefix[self::PREDICTOR_INDEX];
+ $this->prefixLengthsPsr4[$predictor][$prefix] = $length;
+ }
+ else {
+ $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
+ }
+ $this->prefixDirsPsr4[$prefix] = (array) $paths;
+ }
+ }
+
+ /**
+ * Turns on searching the include path for class files.
+ *
+ * @param bool $useIncludePath
+ */
+ public function setUseIncludePath($useIncludePath) {
+ $this->useIncludePath = $useIncludePath;
+ }
+
+ /**
+ * Can be used to check if the autoloader uses the include path to check
+ * for classes.
+ *
+ * @return bool
+ */
+ public function getUseIncludePath() {
+ return $this->useIncludePath;
+ }
+
+ /**
+ * Registers this instance as an autoloader.
+ *
+ * @param bool $prepend
+ * (optional) Whether to prepend the autoloader or not
+ */
+ public function register($prepend = FALSE) {
+ spl_autoload_register(array($this, 'loadClass'), TRUE, $prepend);
+ }
+
+ /**
+ * Unregisters this instance as an autoloader.
+ */
+ public function unregister() {
+ spl_autoload_unregister(array($this, 'loadClass'));
+ }
+
+ /**
+ * Loads the given class or interface.
+ *
+ * @param string $class
+ * The name of the class.
+ *
+ * @return bool|NULL
+ * TRUE if loaded, NULL otherwise.
+ */
+ public function loadClass($class) {
+ if ($file = $this->findFile($class)) {
+ include $file;
+
+ return TRUE;
+ }
+ }
+
+ /**
+ * Finds the path to the file where the class is defined.
+ *
+ * @param string $class
+ * The name of the class.
+ *
+ * @return string|FALSE
+ * The path if found, FALSE otherwise.
+ */
+ public function findFile($class) {
+ // work around for PHP 5.3.0 - 5.3.2 https://bugs.php.net/50731.
+ if ('\\' == $class[0]) {
+ $class = substr($class, 1);
+ }
+
+ // class map lookup.
+ if (isset($this->classMap[$class])) {
+ return $this->classMap[$class];
+ }
+
+ // PSR-4 lookup.
+ $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . '.php';
+
+ // Check if the class is in any of the namespaces registered for PSR-4,
+ // and that are long enough to have a character at the predictor index.
+ $first = $class[0];
+ if (isset($class[self::PREDICTOR_INDEX])) {
+ $predictor = $first . $class[self::PREDICTOR_INDEX];
+ if (isset($this->prefixLengthsPsr4[$predictor])) {
+ foreach ($this->prefixLengthsPsr4[$predictor] as $prefix => $length) {
+ if (0 === strpos($class, $prefix)) {
+ foreach ($this->prefixDirsPsr4[$prefix] as $dir) {
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) {
+ return $file;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Check if the class is in any of the namespaces registered for PSR-4,
+ // that are too short to have a character at the predictor index.
+ if (isset($this->prefixLengthsPsr4[$first])) {
+ foreach ($this->prefixLengthsPsr4[$first] as $prefix => $length) {
+ if (0 === strpos($class, $prefix)) {
+ foreach ($this->prefixDirsPsr4[$prefix] as $dir) {
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) {
+ return $file;
+ }
+ }
+ }
+ }
+ }
+
+ // PSR-4 fallback dirs.
+ foreach ($this->fallbackDirsPsr4 as $dir) {
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
+ return $file;
+ }
+ }
+
+ // PSR-0 lookup.
+ if (FALSE !== $pos = strrpos($class, '\\')) {
+ // namespaced class name.
+ $logicalPathPsr0
+ = substr($logicalPathPsr4, 0, $pos + 1)
+ . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR)
+ ;
+ }
+ else {
+ // PEAR-like class name.
+ $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . '.php';
+ }
+
+ // Check if the class matches any of the prefixes registered for PSR-0.
+ if (isset($this->prefixesPsr0[$first])) {
+ foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
+ if (0 === strpos($class, $prefix)) {
+ foreach ($dirs as $dir) {
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
+ return $file;
+ }
+ }
+ }
+ }
+ }
+
+ // PSR-0 fallback dirs.
+ foreach ($this->fallbackDirsPsr0 as $dir) {
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
+ return $file;
+ }
+ }
+
+ // PSR-0 include paths.
+ if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
+ return $file;
+ }
+
+ // Remember that this class does not exist.
+ return $this->classMap[$class] = FALSE;
+ }
+
+}
diff --git a/core/lib/Drupal/Core/Autoload/DrupalAutoloaderInit.php b/core/lib/Drupal/Core/Autoload/DrupalAutoloaderInit.php
new file mode 100644
index 0000000..3f63a17
--- /dev/null
+++ b/core/lib/Drupal/Core/Autoload/DrupalAutoloaderInit.php
@@ -0,0 +1,116 @@
+setPsr4('Drupal\Core\\', $baseDir . '/core/lib/Drupal/Core');
+ $loader->setPsr4('Drupal\Component\\', $baseDir . '/core/lib/Drupal/Component');
+ $loader->setPsr4('Drupal\Driver\\', $baseDir . '/drivers/lib/Drupal/Driver');
+
+ // Register the remaining PSR-0 namespaces - mostly for vendor libraries.
+ foreach ($map as $namespace => $path) {
+ $loader->set($namespace, $path);
+ }
+
+ // Register the class map in the class loader.
+ $classMap = require $composerDir . '/autoload_classmap.php';
+ if ($classMap) {
+ $loader->addClassMap($classMap);
+ }
+
+ // Register the class loader on the SPL autoload stack.
+ $loader->register(true);
+
+ // Include specific php files, for packages that use the
+ // 'autoload' > 'files' directive in composer.json.
+ $includeFiles = require $composerDir . '/autoload_files.php';
+ foreach ($includeFiles as $file) {
+ require $file;
+ }
+
+ return $loader;
+ }
+
+}
diff --git a/core/lib/Drupal/Core/Autoload/GeneratorScript.php b/core/lib/Drupal/Core/Autoload/GeneratorScript.php
new file mode 100644
index 0000000..f0457ff
--- /dev/null
+++ b/core/lib/Drupal/Core/Autoload/GeneratorScript.php
@@ -0,0 +1,36 @@
+moduleList = isset($module_list['enabled']) ? $module_list['enabled'] : array();
}
$module_filenames = $this->getModuleFileNames();
- $this->registerNamespaces($this->getModuleNamespaces($module_filenames));
+ $this->registerNamespacesPsr4($this->getModuleNamespacesPsr4($module_filenames));
// Load each module's serviceProvider class.
foreach ($this->moduleList as $module => $weight) {
@@ -250,7 +250,6 @@ public function discoverServiceProviders() {
return $serviceProviders;
}
-
/**
* {@inheritdoc}
*/
@@ -411,8 +410,8 @@ 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->getPrefixes();
- $this->registerNamespaces($this->getModuleNamespaces($container_modules));
+ $namespaces_before = $this->classLoader->getPrefixesPsr4();
+ $this->registerNamespacesPsr4($this->getModuleNamespacesPsr4($container_modules));
// If 'container.modules' is wrong, the container must be rebuilt.
if (!isset($this->moduleList)) {
@@ -425,9 +424,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->getPrefixes();
+ $namespaces_after = $this->classLoader->getPrefixesPsr4();
$namespaces_before += array_fill_keys(array_diff(array_keys($namespaces_after), array_keys($namespaces_before)), array());
- $this->registerNamespaces($namespaces_before);
+ $this->registerNamespacesPsr4($namespaces_before);
}
}
@@ -503,14 +502,15 @@ protected function buildContainer() {
$container->setParameter('container.modules', $this->getModuleFileNames());
// Get a list of namespaces and put it onto the container.
- $namespaces = $this->getModuleNamespaces($this->getModuleFileNames());
+ $namespaces = $this->getModuleNamespacesPsr4($this->getModuleFileNames());
// Add all components in \Drupal\Core and \Drupal\Component that have a
// Plugin directory.
foreach (array('Core', 'Component') as $parent_directory) {
$path = DRUPAL_ROOT . '/core/lib/Drupal/' . $parent_directory;
+ $parent_namespace = 'Drupal\\' . $parent_directory;
foreach (new \DirectoryIterator($path) as $component) {
if (!$component->isDot() && is_dir($component->getPathname() . '/Plugin')) {
- $namespaces['Drupal\\' . $parent_directory . '\\' . $component->getFilename()] = DRUPAL_ROOT . '/core/lib';
+ $namespaces[$parent_namespace . '\\' . $component->getFilename()] = $path . '/' . $component->getFilename();
}
}
}
@@ -632,7 +632,11 @@ protected function storage() {
}
/**
- * Returns the file name for each enabled module.
+ * Gets the file name for each enabled module.
+ *
+ * @return array
+ * Array where each key is a module name, and each value is a path to the
+ * respective *.module or *.profile file.
*/
protected function getModuleFileNames() {
$filenames = array();
@@ -645,18 +649,67 @@ protected function getModuleFileNames() {
}
/**
- * Gets the namespaces of each enabled module.
+ * Gets the PSR-4 base directories for module namespaces.
+ *
+ * @param array $module_file_names
+ * Array where each key is a module name, and each value is a path to the
+ * respective *.module or *.profile file.
+ *
+ * @return array
+ * Array where each key is a module namespace like 'Drupal\system', and each
+ * value is an array of PSR-4 base directories associated with the module
+ * namespace.
+ */
+ protected function getModuleNamespacesPsr4($module_file_names) {
+ $namespaces = array();
+ foreach ($module_file_names as $module => $filename) {
+ // @todo Remove lib/Drupal/$module, once the switch to PSR-4 is complete.
+ $namespaces["Drupal\\$module"][] = DRUPAL_ROOT . '/' . dirname($filename) . '/lib/Drupal/' . $module;
+ $namespaces["Drupal\\$module"][] = DRUPAL_ROOT . '/' . dirname($filename) . '/lib';
+ }
+ return $namespaces;
+ }
+
+ /**
+ * Gets the PSR-0 base directories for module namespaces.
+ *
+ * @param array $module_file_names
+ * Array where each key is a module name, and each value is a path to the
+ * respective *.module or *.profile file.
+ *
+ * @return array
+ * Array where each key is a module namespace like 'Drupal\system', and each
+ * value is a PSR-0 base directory associated with the module namespace.
*/
- protected function getModuleNamespaces($moduleFileNames) {
+ protected function getModuleNamespaces($module_file_names) {
$namespaces = array();
- foreach ($moduleFileNames as $module => $filename) {
+ foreach ($module_file_names as $module => $filename) {
$namespaces["Drupal\\$module"] = DRUPAL_ROOT . '/' . dirname($filename) . '/lib';
}
return $namespaces;
}
/**
- * Registers a list of namespaces.
+ * Registers a list of namespaces with PSR-4 directories for class loading.
+ *
+ * @param array $namespaces
+ * Array where each key is a namespace like 'Drupal\system', and each value
+ * is either a PSR-4 base directory, or an array of PSR-4 base directories
+ * associated with this namespace.
+ */
+ protected function registerNamespacesPsr4(array $namespaces = array()) {
+ foreach ($namespaces as $prefix => $paths) {
+ $this->classLoader->addPsr4($prefix . '\\', $paths);
+ }
+ }
+
+ /**
+ * Registers a list of namespaces with PSR-0 directories for class loading.
+ *
+ * @param array $namespaces
+ * Array where each key is a namespace like 'Drupal\system', and each value
+ * is either a PSR-0 base directory, or an array of PSR-0 base directories
+ * associated with this namespace.
*/
protected function registerNamespaces(array $namespaces = array()) {
foreach ($namespaces as $prefix => $path) {
diff --git a/core/lib/Drupal/Core/Plugin/Discovery/AnnotatedClassDiscovery.php b/core/lib/Drupal/Core/Plugin/Discovery/AnnotatedClassDiscovery.php
index ffafdf6..0192ec3 100644
--- a/core/lib/Drupal/Core/Plugin/Discovery/AnnotatedClassDiscovery.php
+++ b/core/lib/Drupal/Core/Plugin/Discovery/AnnotatedClassDiscovery.php
@@ -15,17 +15,23 @@
class AnnotatedClassDiscovery extends ComponentAnnotatedClassDiscovery {
/**
- * The subdirectory within a namespace to look for plugins.
+ * A suffix to append to each PSR-4 directory associated with a base
+ * namespace, to form the directories where plugins are found.
*
- * If the plugins are in the top level of the namespace and not within a
- * subdirectory, set this to an empty string.
+ * @var string
+ */
+ protected $directorySuffix = '';
+
+ /**
+ * A suffix to append to each base namespace, to obtain the namespaces where
+ * plugins are found.
*
* @var string
*/
- protected $subdir = '';
+ protected $namespaceSuffix = '';
/**
- * An object containing the namespaces to look for plugin implementations.
+ * A list of base namespaces with their PSR-4 directories.
*
* @var \Traversable
*/
@@ -47,7 +53,13 @@ class AnnotatedClassDiscovery extends ComponentAnnotatedClassDiscovery {
*/
function __construct($subdir, \Traversable $root_namespaces, $plugin_definition_annotation_name = 'Drupal\Component\Annotation\Plugin') {
if ($subdir) {
- $this->subdir = str_replace('/', '\\', $subdir);
+ // Prepend a directory separator to $subdir,
+ // if it does not already have one.
+ if ('/' !== $subdir[0]) {
+ $subdir = '/' . $subdir;
+ }
+ $this->directorySuffix = $subdir;
+ $this->namespaceSuffix = str_replace('/', '\\', $subdir);
}
$this->rootNamespacesIterator = $root_namespaces;
$plugin_namespaces = array();
@@ -106,11 +118,28 @@ protected function getProviderFromNamespace($namespace) {
*/
protected function getPluginNamespaces() {
$plugin_namespaces = array();
- foreach ($this->rootNamespacesIterator as $namespace => $dir) {
- if ($this->subdir) {
- $namespace .= "\\{$this->subdir}";
+ if ($this->namespaceSuffix) {
+ foreach ($this->rootNamespacesIterator as $namespace => $dirs) {
+ // Append the namespace suffix to the base namespace, to obtain the
+ // plugin namespace. E.g. 'Drupal\Views' may become
+ // 'Drupal\Views\Plugin\Block'.
+ $namespace .= $this->namespaceSuffix;
+ foreach ((array) $dirs as $dir) {
+ // Append the directory suffix to the PSR-4 base directory, to obtain
+ // the directory where plugins are found.
+ // E.g. DRUPAL_CORE . 'core/modules/views/lib' may become
+ // DRUPAL_CORE . 'core/modules/views/lib/Plugin/Block'.
+ $plugin_namespaces[$namespace][] = $dir . $this->directorySuffix;
+ }
+ }
+ }
+ else {
+ // Both the namespace suffix and the directory suffix are empty,
+ // so the plugin namespaces and directories are the same as the base
+ // directories.
+ foreach ($this->rootNamespacesIterator as $namespace => $dirs) {
+ $plugin_namespaces[$namespace] = (array) $dirs;
}
- $plugin_namespaces[$namespace] = array($dir);
}
return $plugin_namespaces;
diff --git a/core/modules/simpletest/simpletest.module b/core/modules/simpletest/simpletest.module
index bd50676..1b8c282 100644
--- a/core/modules/simpletest/simpletest.module
+++ b/core/modules/simpletest/simpletest.module
@@ -456,21 +456,26 @@ function simpletest_test_get_all($module = NULL) {
);
}
foreach ($all_data as $name => $data) {
- // Build directory in which the test files would reside.
- $tests_dir = DRUPAL_ROOT . '/' . dirname($data->uri) . '/lib/Drupal/' . $name . '/Tests';
+ $extension_dir = DRUPAL_ROOT . '/' . dirname($data->uri);
+
+ // Build directories in which the test files would reside.
+ $tests_dirs = array(
+ $extension_dir . '/lib/Drupal/' . $name . '/Tests',
+ $extension_dir . '/lib/Tests',
+ );
+
+ $namespace = 'Drupal\\' . $name . '\Tests\\';
+
// Scan it for test files if it exists.
- if (is_dir($tests_dir)) {
- $files = file_scan_directory($tests_dir, '/.*\.php/');
- if (!empty($files)) {
- $basedir = DRUPAL_ROOT . '/' . dirname($data->uri) . '/lib/';
- foreach ($files as $file) {
- // Convert the file name into the namespaced class name.
- $replacements = array(
- '/' => '\\',
- $basedir => '',
- '.php' => '',
- );
- $classes[] = strtr($file->uri, $replacements);
+ foreach ($tests_dirs as $tests_dir) {
+ if (is_dir($tests_dir)) {
+ $files = file_scan_directory($tests_dir, '/.*\.php/');
+ if (!empty($files)) {
+ $strlen = strlen($tests_dir) + 1;
+ // Convert the file names into the namespaced class names.
+ foreach ($files as $file) {
+ $classes[] = $namespace . str_replace('/', '\\', substr($file->uri, $strlen, -4));
+ }
}
}
}
@@ -544,7 +549,11 @@ 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));
- $classloader->add('Drupal\\' . $name . '\\Tests', DRUPAL_ROOT . '/' . dirname($file->uri) . '/tests');
+ $extension_dir = dirname($file->uri);
+ $classloader->addPsr4('Drupal\\' . $name . '\\Tests\\', array(
+ $extension_dir . '/tests/Drupal/' . $name . '/Tests',
+ $extension_dir . '/tests/lib',
+ ));
// While being there, prime drupal_get_filename().
drupal_get_filename($type, $name, $file->uri);
}
diff --git a/core/modules/simpletest/tests/Drupal/simpletest/Tests/PhpUnitErrorTest.php b/core/modules/simpletest/tests/Drupal/simpletest/Tests/PhpUnitErrorTest.php
index 49fcb02..8e42afe 100644
--- a/core/modules/simpletest/tests/Drupal/simpletest/Tests/PhpUnitErrorTest.php
+++ b/core/modules/simpletest/tests/Drupal/simpletest/Tests/PhpUnitErrorTest.php
@@ -22,7 +22,14 @@ public static function getInfo() {
* Test errors reported.
*/
public function testPhpUnitXmlParsing() {
- require_once __DIR__ . '/../../../../simpletest.module';
+ // This test class could be either in tests/Drupal/simpletest/Tests/, or in
+ // tests/lib/, after the PSR-4 transition.
+ if (file_exists(__DIR__ . '/../../simpletest.module')) {
+ require_once __DIR__ . '/../../simpletest.module';
+ }
+ else {
+ require_once __DIR__ . '/../../../../simpletest.module';
+ }
$phpunit_error_xml = __DIR__ . '/phpunit_error.xml';
$res = simpletest_phpunit_xml_to_rows(1, $phpunit_error_xml);
$this->assertEquals(count($res), 4, 'All testcases got extracted');
diff --git a/core/modules/system/lib/Drupal/system/Tests/Plugin/Discovery/AnnotatedClassDiscoveryTest.php b/core/modules/system/lib/Drupal/system/Tests/Plugin/Discovery/AnnotatedClassDiscoveryTest.php
index d2fba9b..52bd303 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Plugin/Discovery/AnnotatedClassDiscoveryTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Plugin/Discovery/AnnotatedClassDiscoveryTest.php
@@ -57,7 +57,13 @@ public function setUp() {
'provider' => 'plugin_test',
),
);
- $namespaces = new \ArrayObject(array('Drupal\plugin_test' => DRUPAL_ROOT . '/core/modules/system/tests/modules/plugin_test/lib'));
+ $namespaces = new \ArrayObject(array(
+ 'Drupal\plugin_test' => array(
+ // @todo Remove lib/Drupal/$module, once the switch to PSR-4 is complete.
+ DRUPAL_ROOT . '/core/modules/system/tests/modules/plugin_test/lib/Drupal/plugin_test',
+ DRUPAL_ROOT . '/core/modules/system/tests/modules/plugin_test/lib',
+ ),
+ ));
$this->discovery = new AnnotatedClassDiscovery('Plugin/plugin_test/fruit', $namespaces);
$this->emptyDiscovery = new AnnotatedClassDiscovery('Plugin/non_existing_module/non_existing_plugin_type', $namespaces);
}
diff --git a/core/modules/system/lib/Drupal/system/Tests/Plugin/Discovery/CustomAnnotationClassDiscoveryTest.php b/core/modules/system/lib/Drupal/system/Tests/Plugin/Discovery/CustomAnnotationClassDiscoveryTest.php
index d61c013..17bbfa5 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Plugin/Discovery/CustomAnnotationClassDiscoveryTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Plugin/Discovery/CustomAnnotationClassDiscoveryTest.php
@@ -41,7 +41,13 @@ protected function setUp() {
'provider' => 'plugin_test',
),
);
- $root_namespaces = new \ArrayObject(array('Drupal\plugin_test' => DRUPAL_ROOT . '/core/modules/system/tests/modules/plugin_test/lib'));
+ $root_namespaces = new \ArrayObject(array(
+ 'Drupal\plugin_test' => array(
+ // @todo Remove lib/Drupal/$module, once the switch to PSR-4 is complete.
+ DRUPAL_ROOT . '/core/modules/system/tests/modules/plugin_test/lib/Drupal/plugin_test',
+ DRUPAL_ROOT . '/core/modules/system/tests/modules/plugin_test/lib',
+ ),
+ ));
$this->discovery = new AnnotatedClassDiscovery('Plugin/plugin_test/custom_annotation', $root_namespaces, 'Drupal\plugin_test\Plugin\Annotation\PluginExample');
$this->emptyDiscovery = new AnnotatedClassDiscovery('Plugin/non_existing_module/non_existing_plugin_type', $root_namespaces, 'Drupal\plugin_test\Plugin\Annotation\PluginExample');
diff --git a/core/modules/system/lib/Drupal/system/Tests/Plugin/Discovery/CustomDirectoryAnnotatedClassDiscoveryTest.php b/core/modules/system/lib/Drupal/system/Tests/Plugin/Discovery/CustomDirectoryAnnotatedClassDiscoveryTest.php
index 52d4fea..73bc258 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Plugin/Discovery/CustomDirectoryAnnotatedClassDiscoveryTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Plugin/Discovery/CustomDirectoryAnnotatedClassDiscoveryTest.php
@@ -70,7 +70,21 @@ protected function setUp() {
'provider' => 'plugin_test',
),
);
- $namespaces = new \ArrayObject(array('Drupal\plugin_test' => DRUPAL_ROOT . '/core/modules/system/tests/modules/plugin_test/lib'));
+ // Due to the transition from PSR-0 to PSR-4, plugin classes can be in
+ // either one of
+ // - core/modules/system/tests/modules/plugin_test/lib/Drupal/plugin_test/
+ // - core/modules/system/tests/modules/plugin_test/lib/
+ // To avoid false positives with "Drupal\plugin_test\Drupal\plugin_test\..",
+ // only one of them can be registered.
+ // Note: This precaution is only needed if the plugin namespace is identical
+ // with the module namespace. Usually this is not the case, because every
+ // plugin namespace is like "Drupal\$module\Plugin\..".
+ // @todo Clean this up, once the transition to PSR-4 is complete.
+ $lib_directory = DRUPAL_ROOT . '/core/modules/system/tests/modules/plugin_test/lib';
+ $base_directory = is_dir($lib_directory . '/Drupal/plugin_test')
+ ? $lib_directory . '/Drupal/plugin_test'
+ : $lib_directory;
+ $namespaces = new \ArrayObject(array('Drupal\plugin_test' => $base_directory));
$this->discovery = new AnnotatedClassDiscovery('', $namespaces);
$empty_namespaces = new \ArrayObject();
$this->emptyDiscovery = new AnnotatedClassDiscovery('', $empty_namespaces);
diff --git a/core/phpunit.xml.dist b/core/phpunit.xml.dist
index 35ff254..5d4aec5 100644
--- a/core/phpunit.xml.dist
+++ b/core/phpunit.xml.dist
@@ -12,7 +12,7 @@
./drush/tests
- ./modules/config/tests/config_test/lib/Drupal/config_test
+ ./modules/config/tests/config_test/lib
diff --git a/core/scripts/switch-psr4.sh b/core/scripts/switch-psr4.sh
new file mode 100644
index 0000000..dd69700
--- /dev/null
+++ b/core/scripts/switch-psr4.sh
@@ -0,0 +1,227 @@
+#!/bin/php
+isDot()) {
+ // do nothing
+ }
+ elseif ($fileinfo->isDir()) {
+ process_candidate_dir($fileinfo->getPathname());
+ }
+ }
+}
+
+/**
+ * Recursively scans a directory for Drupal extensions, and runs
+ * process_extension() for each one that it finds.
+ *
+ * @param string $dir
+ * A directory that could be a Drupal extension directory.
+ */
+function process_candidate_dir($dir) {
+ /**
+ * @var \DirectoryIterator $fileinfo
+ */
+ foreach (new \DirectoryIterator($dir) as $fileinfo) {
+ if ($fileinfo->isDot()) {
+ // Ignore "." and "..".
+ }
+ elseif ($fileinfo->isDir()) {
+ // It's a directory.
+ switch ($fileinfo->getFilename()) {
+ case 'lib':
+ // Ignore these directory names.
+ continue;
+ default:
+ // Look for more extensions in subdirectories.
+ process_candidate_dir($fileinfo->getPathname());
+ }
+ }
+ else {
+ // It's a file.
+ if (preg_match('/^(.+).info.yml$/', $fileinfo->getFilename(), $m)) {
+ // It's a *.info.yml file, so we found an extension directory.
+ $extension_name = $m[1];
+ }
+ }
+ }
+ if (isset($extension_name)) {
+ process_extension($extension_name, $dir);
+ process_extension_phpunit($extension_name, $dir);
+ }
+}
+
+/**
+ * Process a Drupal extension (module, theme) in a directory.
+ *
+ * This will move all class files in this extension two levels up, from
+ * lib/Drupal/$name/ to lib/.
+ *
+ * @param string $name
+ * Name of the extension.
+ * @param string $dir
+ * Directory of the extension.
+ */
+function process_extension($name, $dir) {
+
+ if (!is_dir($src = "$dir/lib/Drupal/$name")) {
+ // Nothing to do in this module.
+ return;
+ }
+
+ // Move class files two levels up.
+ move_directory_contents($src, "$dir/lib");
+
+ // Clean up.
+ require_dir_empty("$dir/lib/Drupal");
+ rmdir("$dir/lib/Drupal");
+}
+
+/**
+ * Process a Drupal extension (module, theme) in a directory.
+ *
+ * This will move all PHPUnit class files in this extension from
+ * tests/Drupal/$name/Tests/ to tests/lib/.
+ *
+ * @param string $name
+ * Name of the extension.
+ * @param string $dir
+ * Directory of the extension.
+ */
+function process_extension_phpunit($name, $dir) {
+
+ if (!is_dir($src = "$dir/tests/Drupal/$name/Tests")) {
+ // Nothing to do in this module.
+ return;
+ }
+
+ if (!is_dir($dest = "$dir/tests/lib")) {
+ mkdir($dest);
+ }
+
+ // Move class files two levels up.
+ move_directory_contents($src, $dest);
+
+ // Clean up.
+ require_dir_empty("$dir/tests/Drupal/$name");
+ rmdir("$dir/tests/Drupal/$name");
+ require_dir_empty("$dir/tests/Drupal");
+ rmdir("$dir/tests/Drupal");
+}
+
+/**
+ * Move directory contents from an existing source directory to an existing
+ * destination directory.
+ *
+ * @param string $source
+ * An existing source directory.
+ * @param string $destination
+ * An existing destination directory.
+ *
+ * @throws \Exception
+ */
+function move_directory_contents($source, $destination) {
+
+ if (!is_dir($source)) {
+ throw new \Exception("The source '$source' is not a directory.");
+ }
+
+ if (!is_dir($destination)) {
+ throw new \Exception("The destination '$destination' is not a directory.");
+ }
+
+ /**
+ * @var \DirectoryIterator $fileinfo
+ */
+ foreach (new \DirectoryIterator($source) as $fileinfo) {
+ if ($fileinfo->isDot()) {
+ continue;
+ }
+ $dest_path = $destination . '/' . $fileinfo->getFilename();
+ if (!file_exists($dest_path)) {
+ rename($fileinfo->getPathname(), $dest_path);
+ }
+ elseif ($fileinfo->isFile()) {
+ throw new \Exception("Destination '$dest_path' already exists, cannot overwrite.");
+ }
+ elseif ($fileinfo->isDir()) {
+ if (!is_dir($dest_path)) {
+ throw new \Exception("Destination '$dest_path' is not a directory.");
+ }
+ move_directory_contents($fileinfo->getPathname(), $dest_path);
+ }
+ }
+
+ require_dir_empty($source);
+
+ rmdir($source);
+}
+
+/**
+ * Throws an exception if a directory is not empty.
+ *
+ * @param string $dir
+ * Directory to check.
+ *
+ * @throws \Exception
+ */
+function require_dir_empty($dir) {
+ if (is_file($dir)) {
+ throw new \Exception("The path '$dir' is a file, when it should be a directory.");
+ }
+ if (!is_dir($dir)) {
+ throw new \Exception("The directory '$dir' does not exist.");
+ }
+ if (!is_readable($dir)) {
+ throw new \Exception("The directory '$dir' is not readable.");
+ }
+ /**
+ * @var \DirectoryIterator $fileinfo
+ */
+ foreach (new \DirectoryIterator($dir) as $fileinfo) {
+ if ($fileinfo->isDot()) {
+ continue;
+ }
+ $path = $fileinfo->getPathname();
+ if ($fileinfo->isFile()) {
+ throw new \Exception("File '$path' found in a directory that should be empty.");
+ }
+ elseif ($fileinfo->isDir()) {
+ throw new \Exception("Subdirectory '$path' found in a directory that should be empty.");
+ }
+ }
+}
diff --git a/core/tests/bootstrap.php b/core/tests/bootstrap.php
index c69a52f..532e677 100644
--- a/core/tests/bootstrap.php
+++ b/core/tests/bootstrap.php
@@ -49,20 +49,22 @@ function drupal_phpunit_contrib_extension_directory_roots() {
/**
* Registers the namespace for each extension directory with the autoloader.
*
- * @param Composer\Autoload\ClassLoader $loader
+ * @param Drupal\Core\Autoload\ClassLoader $loader
* The supplied autoloader.
* @param array $dirs
* An associative array of extension directories, keyed by extension name.
*/
-function drupal_phpunit_register_extension_dirs(Composer\Autoload\ClassLoader $loader, $dirs) {
+function drupal_phpunit_register_extension_dirs(Drupal\Core\Autoload\ClassLoader $loader, $dirs) {
foreach ($dirs as $extension => $dir) {
$lib_path = $dir . '/lib';
if (is_dir($lib_path)) {
$loader->add('Drupal\\' . $extension, $lib_path);
+ $loader->addPsr4('Drupal\\' . $extension . '\\', $lib_path);
}
$tests_path = $dir . '/tests';
if (is_dir($tests_path)) {
$loader->add('Drupal\\' . $extension, $tests_path);
+ $loader->addPsr4('Drupal\\' . $extension . '\Tests\\', $tests_path . '/lib');
}
}
}
diff --git a/core/vendor/autoload.php b/core/vendor/autoload.php
index aea9b17..e665445 100644
--- a/core/vendor/autoload.php
+++ b/core/vendor/autoload.php
@@ -1,7 +1,23 @@