diff --git a/core/core.services.yml b/core/core.services.yml
index da2d413..3a377fc 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -153,7 +153,9 @@ services:
- [addSubscriber, ['@http_client_simpletest_subscriber']]
- [setUserAgent, ['Drupal (+http://drupal.org/)']]
container.namespaces:
- class: ArrayObject
+ class: Krautoload\SearchableNamespaces_Default
+ factory_service: class_loader
+ factory_method: buildSearchableNamespaces
arguments: [ '%container.namespaces%' ]
tags:
- { name: persist }
diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc
index d9f44da..951fd5f 100644
--- a/core/includes/bootstrap.inc
+++ b/core/includes/bootstrap.inc
@@ -2769,9 +2769,8 @@ function drupal_classloader($class_loader = NULL) {
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/ClassLoader.php';
- $loader = new ClassLoader();
+ // Include and boot the Krautoload ClassLoader.
+ require_once DRUPAL_ROOT . '/core/vendor/krautoload/src/Krautoload.php';
// Register the class loader.
// When configured to use APC, the ApcClassLoader is registered instead.
@@ -2782,21 +2781,18 @@ function drupal_classloader($class_loader = NULL) {
$class_loader = settings()->get('class_loader', 'default');
}
if ($class_loader === 'apc') {
- require_once DRUPAL_ROOT . '/core/vendor/symfony/class-loader/Symfony/Component/ClassLoader/ApcClassLoader.php';
- $apc_loader = new ApcClassLoader('drupal.' . drupal_get_hash_salt(), $loader);
- $apc_loader->register();
+ $salt = 'drupal.' . drupal_get_hash_salt();
+ // TODO: Use the salt and boot Krautoload with APC.
+ $loader = Krautoload::start();
}
else {
- $loader->register();
+ $loader = Krautoload::start();
}
// Register namespaces for vendor libraries managed by Composer.
- $prefixes_and_namespaces = require DRUPAL_ROOT . '/core/vendor/composer/autoload_namespaces.php';
- $loader->addPrefixes($prefixes_and_namespaces);
-
- // Register the loader with PHP.
- $loader->register();
+ $loader->composerVendorDir(DRUPAL_ROOT . '/core/vendor');
}
+
return $loader;
}
@@ -2810,7 +2806,8 @@ function drupal_classloader($class_loader = NULL) {
*/
function drupal_classloader_register($name, $path) {
$loader = drupal_classloader();
- $loader->addPrefix('Drupal\\' . $name, DRUPAL_ROOT . '/' . $path . '/lib');
+ $loader->addNamespacePSR0('Drupal\\' . $name, DRUPAL_ROOT . '/' . $path . '/lib');
+ $loader->addNamespacePSRX('Drupal\\' . $name, DRUPAL_ROOT . '/' . $path . '/src');
}
/**
diff --git a/core/lib/Drupal/Component/Plugin/Discovery/AnnotatedClassDiscovery.php b/core/lib/Drupal/Component/Plugin/Discovery/AnnotatedClassDiscovery.php
index 377bf4a..84d4f47 100644
--- a/core/lib/Drupal/Component/Plugin/Discovery/AnnotatedClassDiscovery.php
+++ b/core/lib/Drupal/Component/Plugin/Discovery/AnnotatedClassDiscovery.php
@@ -7,12 +7,9 @@
namespace Drupal\Component\Plugin\Discovery;
-use DirectoryIterator;
use Drupal\Component\Plugin\Discovery\DiscoveryInterface;
-use Drupal\Component\Reflection\MockFileFinder;
-use Doctrine\Common\Annotations\AnnotationReader;
use Doctrine\Common\Annotations\AnnotationRegistry;
-use Doctrine\Common\Reflection\StaticReflectionParser;
+use Krautoload\SearchableNamespaces_Interface as SearchableNamespacesInterface;
/**
* Defines a discovery mechanism to find annotated plugins in PSR-0 namespaces.
@@ -56,7 +53,7 @@ class AnnotatedClassDiscovery implements DiscoveryInterface {
* (optional) The name of the annotation that contains the plugin definition.
* Defaults to 'Drupal\Component\Annotation\Plugin'.
*/
- function __construct($plugin_namespaces = array(), $annotation_namespaces = array(), $plugin_definition_annotation_name = 'Drupal\Component\Annotation\Plugin') {
+ function __construct(SearchableNamespacesInterface $plugin_namespaces, SearchableNamespacesInterface $annotation_namespaces, $plugin_definition_annotation_name = 'Drupal\Component\Annotation\Plugin') {
$this->pluginNamespaces = $plugin_namespaces;
$this->annotationNamespaces = $annotation_namespaces;
$this->pluginDefinitionAnnotationName = $plugin_definition_annotation_name;
@@ -74,43 +71,15 @@ public function getDefinition($plugin_id) {
* Implements Drupal\Component\Plugin\Discovery\DiscoveryInterface::getDefinitions().
*/
public function getDefinitions() {
- $definitions = array();
- $reader = new AnnotationReader();
- // Prevent @endlink from being parsed as an annotation.
- $reader->addGlobalIgnoredName('endlink');
// Register the namespaces of classes that can be used for annotations.
- AnnotationRegistry::registerAutoloadNamespaces($this->getAnnotationNamespaces());
+ AnnotationRegistry::reset();
+ AnnotationRegistry::registerLoader(array($this->getAnnotationNamespaces(), 'classExistsInNamespaces'));
- // 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)) {
- foreach (new DirectoryIterator($dir) as $fileinfo) {
- // @todo Once core requires 5.3.6, use $fileinfo->getExtension().
- if (pathinfo($fileinfo->getFilename(), PATHINFO_EXTENSION) == 'php') {
- $class = $namespace . '\\' . $fileinfo->getBasename('.php');
-
- // The filename is already known, so there is no need to find the
- // file. However, StaticReflectionParser needs a finder, so use a
- // mock version.
- $finder = MockFileFinder::create($fileinfo->getPathName());
- $parser = new StaticReflectionParser($class, $finder);
-
- if ($annotation = $reader->getClassAnnotation($parser->getReflectionClass(), $this->pluginDefinitionAnnotationName)) {
- // AnnotationInterface::get() returns the array definition
- // instead of requiring us to work with the annotation object.
- $definition = $annotation->get();
- $definition['class'] = $class;
- $definitions[$definition['id']] = $definition;
- }
- }
- }
- }
- }
- }
- return $definitions;
+ // Scan namespaces.
+ $discoveryAPI = new ClassFileVisitorAPI($this->pluginDefinitionAnnotationName, $this->getAnnotationNamespaces());
+ $this->getPluginNamespaces()->apiVisitClassFiles($discoveryAPI, FALSE);
+ return $discoveryAPI->getDefinitions();
}
/**
@@ -121,10 +90,9 @@ protected function getPluginNamespaces() {
}
/**
- * Returns an array of PSR-0 namespaces to search for annotation classes.
+ * Returns a searchable namespace collection to search for annotation classes.
*/
protected function getAnnotationNamespaces() {
return $this->annotationNamespaces;
}
-
}
diff --git a/core/lib/Drupal/Component/Plugin/Discovery/ClassFileVisitorAPI.php b/core/lib/Drupal/Component/Plugin/Discovery/ClassFileVisitorAPI.php
new file mode 100644
index 0000000..13dc7f9
--- /dev/null
+++ b/core/lib/Drupal/Component/Plugin/Discovery/ClassFileVisitorAPI.php
@@ -0,0 +1,106 @@
+reader = new AnnotationReader();
+ // Prevent @endlink from being parsed as an annotation.
+ $this->reader->addGlobalIgnoredName('endlink');
+ $this->annotationName = $annotationName;
+ }
+
+ /**
+ * Get the array of plugin definitions, after everything is scanned.
+ *
+ * @return array
+ */
+ function getDefinitions() {
+ return $this->definitions;
+ }
+
+ /**
+ * The directory scan has found a file,
+ * which is expected to define the given class.
+ *
+ * @param string $file
+ * @param array $relativeClassNames
+ * Classes that could be in this file according to PSR-0 mapping.
+ * This array is never empty.
+ * The first class in this array is always the class which has no
+ * underscores after the last namespace separator.
+ */
+ function fileWithClass($file, $relativeClassName) {
+ $this->parseFileWithClass($file, $relativeClassName);
+ }
+
+ /**
+ * The directory scan has found a file,
+ * which may define any or none of the given classes.
+ *
+ * @param string $file
+ * @param array $relativeClassNames
+ * Classes that could be in this file according to PSR-0 mapping.
+ * This array is never empty.
+ * The first class in this array is always the class which has no
+ * underscores after the last namespace separator.
+ */
+ function fileWithClassCandidates($file, $relativeClassNames) {
+ // Only pick the first class, which is the no-underscore version.
+ $this->parseFileWithClass($file, $relativeClassNames[0]);
+ }
+
+ /**
+ * The directory scan has found a file which is expected to define the given
+ * class.
+ *
+ * @param string $file
+ * @param string $relativeClassName
+ */
+ protected function parseFileWithClass($file, $relativeClassName) {
+ $class = $this->getNamespace() . $relativeClassName;
+ if ($annotation = $this->extractClassAnnotation($file, $class)) {
+ // AnnotationInterface::get() returns the array definition
+ // instead of requiring us to work with the annotation object.
+ $definition = $annotation->get();
+ $definition['class'] = $class;
+ $this->definitions[$definition['id']] = $definition;
+ }
+ }
+
+ /**
+ * Extract the annotation from a class.
+ *
+ * @param string $file
+ * @param string $class
+ */
+ protected function extractClassAnnotation($file, $class) {
+ // The filename is already known, so there is no need to find the
+ // file. However, StaticReflectionParser needs a finder, so use a
+ // mock version.
+ $finder = MockFileFinder::create($file);
+ $parser = new StaticReflectionParser($class, $finder);
+ return $this->reader->getClassAnnotation($parser->getReflectionClass(), $this->annotationName);
+ }
+}
diff --git a/core/lib/Drupal/Core/Action/ActionManager.php b/core/lib/Drupal/Core/Action/ActionManager.php
index c987d8c..e522337 100644
--- a/core/lib/Drupal/Core/Action/ActionManager.php
+++ b/core/lib/Drupal/Core/Action/ActionManager.php
@@ -11,6 +11,7 @@
use Drupal\Core\Plugin\Discovery\AlterDecorator;
use Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery;
use Drupal\Core\Plugin\Factory\ContainerFactory;
+use Krautoload\SearchableNamespaces_Interface as SearchableNamespacesInterface;
/**
* Provides an Action plugin manager.
@@ -27,7 +28,7 @@ class ActionManager extends PluginManagerBase {
* An object that implements \Traversable which contains the root paths
* keyed by the corresponding namespace to look for plugin implementations.
*/
- public function __construct(\Traversable $namespaces) {
+ public function __construct(SearchableNamespacesInterface $namespaces) {
$this->discovery = new AnnotatedClassDiscovery('Action', $namespaces, array(), 'Drupal\Core\Annotation\Action');
$this->discovery = new AlterDecorator($this->discovery, 'action_info');
diff --git a/core/lib/Drupal/Core/Archiver/ArchiverManager.php b/core/lib/Drupal/Core/Archiver/ArchiverManager.php
index f3bfc96..7e32b62 100644
--- a/core/lib/Drupal/Core/Archiver/ArchiverManager.php
+++ b/core/lib/Drupal/Core/Archiver/ArchiverManager.php
@@ -11,6 +11,7 @@
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Language\LanguageManager;
use Drupal\Core\Plugin\DefaultPluginManager;
+use Krautoload\SearchableNamespaces_Interface as SearchableNamespacesInterface;
/**
* Archiver plugin manager.
@@ -30,7 +31,7 @@ class ArchiverManager extends DefaultPluginManager {
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* The module handler to invoke the alter hook with.
*/
- public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, LanguageManager $language_manager, ModuleHandlerInterface $module_handler) {
+ public function __construct(SearchableNamespacesInterface $namespaces, CacheBackendInterface $cache_backend, LanguageManager $language_manager, ModuleHandlerInterface $module_handler) {
parent::__construct('Archiver', $namespaces);
$this->alterInfo($module_handler, 'archiver_info');
$this->setCacheBackend($cache_backend, $language_manager, 'archiver_info');
diff --git a/core/lib/Drupal/Core/Condition/ConditionManager.php b/core/lib/Drupal/Core/Condition/ConditionManager.php
index 2897743..c7fdf0a 100644
--- a/core/lib/Drupal/Core/Condition/ConditionManager.php
+++ b/core/lib/Drupal/Core/Condition/ConditionManager.php
@@ -13,6 +13,7 @@
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Language\LanguageManager;
use Drupal\Core\Plugin\DefaultPluginManager;
+use Krautoload\SearchableNamespaces_Interface as SearchableNamespacesInterface;
/**
* A plugin manager for condition plugins.
@@ -32,12 +33,12 @@ class ConditionManager extends DefaultPluginManager implements ExecutableManager
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* The module handler to invoke the alter hook with.
*/
- public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, LanguageManager $language_manager, ModuleHandlerInterface $module_handler) {
+ public function __construct(SearchableNamespacesInterface $namespaces, CacheBackendInterface $cache_backend, LanguageManager $language_manager, ModuleHandlerInterface $module_handler) {
$this->alterInfo($module_handler, 'condition_info');
$this->setCacheBackend($cache_backend, $language_manager, 'condition');
$annotation_namespaces = array(
- 'Drupal\Core\Condition\Annotation' => DRUPAL_ROOT . '/core/lib',
+ 'Drupal\Core\Condition\Annotation' => TRUE,
);
parent::__construct('Condition', $namespaces, $annotation_namespaces, 'Drupal\Core\Condition\Annotation\Condition');
}
diff --git a/core/lib/Drupal/Core/DrupalKernel.php b/core/lib/Drupal/Core/DrupalKernel.php
index af1fe52..0725a7f 100644
--- a/core/lib/Drupal/Core/DrupalKernel.php
+++ b/core/lib/Drupal/Core/DrupalKernel.php
@@ -150,7 +150,7 @@ class DrupalKernel implements DrupalKernelInterface, TerminableInterface {
* 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 \Symfony\Component\ClassLoader\ClassLoader $class_loader
+ * @param \Krautoload\RegistrationHub $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
@@ -159,7 +159,7 @@ class DrupalKernel implements DrupalKernelInterface, TerminableInterface {
* (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->booted = false;
$this->classLoader = $class_loader;
@@ -233,7 +233,8 @@ public function discoverServiceProviders() {
$this->moduleList = isset($module_list['enabled']) ? $module_list['enabled'] : array();
}
$module_filenames = $this->getModuleFileNames();
- $this->registerNamespaces($this->getModuleNamespaces($module_filenames));
+ $this->classLoader->addNamespacesPSR0($this->getModuleNamespacesPSR0($module_filenames));
+ $this->classLoader->addNamespacesPSRX($this->getModuleNamespacesPSRX($module_filenames));
// Load each module's serviceProvider class.
foreach ($this->moduleList as $module => $weight) {
@@ -417,8 +418,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));
+ $this->classLoader->addNamespacesPSR0($this->getModuleNamespacesPSR0($container_modules));
+ $this->classLoader->addNamespacesPSRX($this->getModuleNamespacesPSRX($container_modules));
// If 'container.modules' is wrong, the container must be rebuilt.
if (!isset($this->moduleList)) {
@@ -427,13 +428,20 @@ protected function initializeContainer() {
if (array_keys($this->moduleList) !== array_keys($container_modules)) {
$persist = $this->getServicesToPersist();
unset($this->container);
- // Revert the class loader to its prior state. However,
- // 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_before += array_fill_keys(array_diff(array_keys($namespaces_after), array_keys($namespaces_before)), array());
- $this->registerNamespaces($namespaces_before);
+ // @todo At this point, previous versions did attempt to revert the
+ // class loader to its previous state, with the intention to remove
+ // erroneous registrations.
+ // However, we can assume that this did not work, because
+ // \Symfony\Component\ClassLoader\ClassLoader does not support removal
+ // of registered namespaces, it only allows adding them.
+ // The other question is, whether removal of namespace registrations
+ // is desirable in the first place. Some of the classes might already
+ // be included, and removal of the namespace cannot undo that.
+ // A more interesting case is if a module has moved to a different
+ // directory. This case is not even detected in the if() clause above.
+ //
+ // Conclusion: A revert mechanic can be added, but needs to be
+ // discussed first.
}
}
@@ -441,11 +449,13 @@ protected function initializeContainer() {
$this->container = $this->buildContainer();
$this->persistServices($persist);
- // The namespaces are marked as persistent, so objects like the annotated
- // class discovery still has the right object. We may have updated the
- // list of modules, so set it.
+ // The searchable namespaces collection is marked as persistent, so
+ // objects like the annotated class discovery still have the right object.
if ($this->container->initialized('container.namespaces')) {
- $this->container->get('container.namespaces')->exchangeArray($this->container->getParameter('container.namespaces'));
+ // The list of namespaces may have changed, so set it.
+ $this->container->get('container.namespaces')->setNamespaces($this->container->getParameter('container.namespaces'));
+ // There may be a new class loader, so set it.
+ $this->container->get('container.namespaces')->setFinder($this->classLoader->getFinder());
}
if ($this->allowDumping) {
@@ -457,6 +467,7 @@ protected function initializeContainer() {
// Set the class loader which was registered as a synthetic service.
$this->container->set('class_loader', $this->classLoader);
+
// If we have a request set it back to the new container.
if (isset($request)) {
$this->container->enterScope('request');
@@ -507,7 +518,9 @@ 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());
+ // The only thing that uses the directories is the annotation discovery,
+ // which is still on PSR-0 thanks to static methods in doctrine.
+ $namespaces = $this->getModuleNamespacesPSR0($this->getModuleFileNames());
// Add all components in \Drupal\Core and \Drupal\Component that have a
// Plugin directory.
foreach (array('Core', 'Component') as $parent_directory) {
@@ -518,10 +531,10 @@ protected function buildContainer() {
}
}
}
- $container->setParameter('container.namespaces', $namespaces);
+ $container->setParameter('container.namespaces', array_keys($namespaces));
// Register synthetic services.
- $container->register('class_loader', 'Symfony\Component\ClassLoader\ClassLoader')->setSynthetic(TRUE);
+ $container->register('class_loader', 'Krautoload\RegistrationHub')->setSynthetic(TRUE);
$container->register('kernel', 'Symfony\Component\HttpKernel\KernelInterface')->setSynthetic(TRUE);
$container->register('service_container', 'Symfony\Component\DependencyInjection\ContainerInterface')->setSynthetic(TRUE);
$yaml_loader = new YamlFileLoader($container);
@@ -647,9 +660,9 @@ protected function getModuleFileNames() {
}
/**
- * Gets the namespaces of each enabled module.
+ * Gets the PSR-0 namespace directories of each enabled module.
*/
- protected function getModuleNamespaces($moduleFileNames) {
+ protected function getModuleNamespacesPSR0($moduleFileNames) {
$namespaces = array();
foreach ($moduleFileNames as $module => $filename) {
$namespaces["Drupal\\$module"] = DRUPAL_ROOT . '/' . dirname($filename) . '/lib';
@@ -658,9 +671,13 @@ protected function getModuleNamespaces($moduleFileNames) {
}
/**
- * Registers a list of namespaces.
+ * Gets the PSR-X namespace directories of each enabled module.
*/
- protected function registerNamespaces(array $namespaces = array()) {
- $this->classLoader->addPrefixes($namespaces);
+ protected function getModuleNamespacesPSRX($moduleFileNames) {
+ $namespaces = array();
+ foreach ($moduleFileNames as $module => $filename) {
+ $namespaces["Drupal\\$module"] = DRUPAL_ROOT . '/' . dirname($filename) . '/src';
+ }
+ return $namespaces;
}
}
diff --git a/core/lib/Drupal/Core/Entity/EntityManager.php b/core/lib/Drupal/Core/Entity/EntityManager.php
index 4766a91..3d26337 100644
--- a/core/lib/Drupal/Core/Entity/EntityManager.php
+++ b/core/lib/Drupal/Core/Entity/EntityManager.php
@@ -19,6 +19,7 @@
use Drupal\Core\Plugin\Discovery\InfoHookDecorator;
use Drupal\Core\Cache\CacheBackendInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
+use Krautoload\SearchableNamespaces_Interface as SearchableNamespacesInterface;
/**
* Manages entity type plugin definitions.
@@ -102,10 +103,10 @@ class EntityManager extends PluginManagerBase {
* @param \Drupal\Core\Language\LanguageManager $language_manager
* The language manager.
*/
- public function __construct(\Traversable $namespaces, ContainerInterface $container, ModuleHandlerInterface $module_handler, CacheBackendInterface $cache, LanguageManager $language_manager) {
+ public function __construct(SearchableNamespacesInterface $namespaces, ContainerInterface $container, ModuleHandlerInterface $module_handler, CacheBackendInterface $cache, LanguageManager $language_manager) {
// Allow the plugin definition to be altered by hook_entity_info_alter().
$annotation_namespaces = array(
- 'Drupal\Core\Entity\Annotation' => DRUPAL_ROOT . '/core/lib',
+ 'Drupal\Core\Entity\Annotation' => TRUE,
);
$this->moduleHandler = $module_handler;
diff --git a/core/lib/Drupal/Core/Entity/Field/FieldTypePluginManager.php b/core/lib/Drupal/Core/Entity/Field/FieldTypePluginManager.php
index 9f9ceb8..0c9e081 100644
--- a/core/lib/Drupal/Core/Entity/Field/FieldTypePluginManager.php
+++ b/core/lib/Drupal/Core/Entity/Field/FieldTypePluginManager.php
@@ -13,6 +13,7 @@
use Drupal\Core\Language\LanguageManager;
use Drupal\Core\Plugin\DefaultPluginManager;
use Drupal\field\Plugin\Type\FieldType\LegacyFieldTypeDiscoveryDecorator;
+use Krautoload\SearchableNamespaces_Interface as SearchableNamespacesInterface;
/**
* Plugin manager for 'field type' plugins.
@@ -41,9 +42,9 @@ class FieldTypePluginManager extends DefaultPluginManager {
* @param \Drupal\Core\Extension\ModuleHandlerInterface
* The module handler.
*/
- public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, LanguageManager $language_manager, ModuleHandlerInterface $module_handler) {
+ public function __construct(SearchableNamespacesInterface $namespaces, CacheBackendInterface $cache_backend, LanguageManager $language_manager, ModuleHandlerInterface $module_handler) {
$annotation_namespaces = array(
- 'Drupal\Core\Entity\Annotation' => DRUPAL_ROOT . '/core/lib',
+ 'Drupal\Core\Entity\Annotation' => TRUE,
);
parent::__construct('field/field_type', $namespaces, $annotation_namespaces, 'Drupal\Core\Entity\Annotation\FieldType');
$this->alterInfo($module_handler, 'field_info');
diff --git a/core/lib/Drupal/Core/Plugin/DefaultPluginManager.php b/core/lib/Drupal/Core/Plugin/DefaultPluginManager.php
index 7a1cdac..d2b98d9 100644
--- a/core/lib/Drupal/Core/Plugin/DefaultPluginManager.php
+++ b/core/lib/Drupal/Core/Plugin/DefaultPluginManager.php
@@ -18,6 +18,7 @@
use Drupal\Core\Language\Language;
use Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery;
use Drupal\Core\Plugin\Factory\ContainerFactory;
+use Krautoload\SearchableNamespaces_Interface as SearchableNamespacesInterface;
/**
* Base class for plugin managers.
@@ -95,7 +96,7 @@ class DefaultPluginManager extends PluginManagerBase implements PluginManagerInt
* (optional) The name of the annotation that contains the plugin definition.
* Defaults to 'Drupal\Component\Annotation\Plugin'.
*/
- public function __construct($subdir, \Traversable $namespaces, $annotation_namespaces = array(), $plugin_definition_annotation_name = 'Drupal\Component\Annotation\Plugin') {
+ public function __construct($subdir, SearchableNamespacesInterface $namespaces, $annotation_namespaces = array(), $plugin_definition_annotation_name = 'Drupal\Component\Annotation\Plugin') {
$this->subdir = $subdir;
$this->discovery = new AnnotatedClassDiscovery($subdir, $namespaces, $annotation_namespaces, $plugin_definition_annotation_name);
$this->discovery = new DerivativeDiscoveryDecorator($this->discovery);
diff --git a/core/lib/Drupal/Core/Plugin/Discovery/AnnotatedClassDiscovery.php b/core/lib/Drupal/Core/Plugin/Discovery/AnnotatedClassDiscovery.php
index 6695db7..07cfd93 100644
--- a/core/lib/Drupal/Core/Plugin/Discovery/AnnotatedClassDiscovery.php
+++ b/core/lib/Drupal/Core/Plugin/Discovery/AnnotatedClassDiscovery.php
@@ -8,6 +8,7 @@
namespace Drupal\Core\Plugin\Discovery;
use Drupal\Component\Plugin\Discovery\AnnotatedClassDiscovery as ComponentAnnotatedClassDiscovery;
+use Krautoload\SearchableNamespaces_Interface as SearchableNamespacesInterface;
/**
* Defines a discovery mechanism to find annotated plugins in PSR-0 namespaces.
@@ -33,7 +34,7 @@ class AnnotatedClassDiscovery extends ComponentAnnotatedClassDiscovery {
*
* @var \Traversable
*/
- protected $rootNamespacesIterator;
+ protected $rootNamespaces;
/**
* Constructs an AnnotatedClassDiscovery object.
@@ -51,14 +52,16 @@ class AnnotatedClassDiscovery extends ComponentAnnotatedClassDiscovery {
* (optional) The name of the annotation that contains the plugin definition.
* Defaults to 'Drupal\Component\Annotation\Plugin'.
*/
- function __construct($subdir, \Traversable $root_namespaces, $annotation_namespaces = array(), $plugin_definition_annotation_name = 'Drupal\Component\Annotation\Plugin') {
+ function __construct($subdir, SearchableNamespacesInterface $root_namespaces, $annotation_namespaces = array(), $plugin_definition_annotation_name = 'Drupal\Component\Annotation\Plugin') {
$this->subdir = str_replace('/', '\\', $subdir);
- $this->rootNamespacesIterator = $root_namespaces;
- $annotation_namespaces += array(
- 'Drupal\Component\Annotation' => DRUPAL_ROOT . '/core/lib',
- 'Drupal\Core\Annotation' => DRUPAL_ROOT . '/core/lib',
- );
- $plugin_namespaces = array();
+ $this->rootNamespaces = $root_namespaces;
+ if (!is_object($annotation_namespaces)) {
+ $annotation_namespaces = $root_namespaces->buildSearchableNamespaces(array_keys($annotation_namespaces));
+ }
+ $annotation_namespaces->addNamespace('Drupal\Component\Annotation');
+ $annotation_namespaces->addNamespace('Drupal\Core\Annotation');
+ // For performance reasons, initialize with an empty namespace collection.
+ $plugin_namespaces = $root_namespaces->buildSearchableNamespaces(array());
parent::__construct($plugin_namespaces, $annotation_namespaces, $plugin_definition_annotation_name);
}
@@ -99,12 +102,7 @@ protected function getProviderFromNamespace($namespace) {
* {@inheritdoc}
*/
protected function getPluginNamespaces() {
- $plugin_namespaces = array();
- foreach ($this->rootNamespacesIterator as $namespace => $dir) {
- $plugin_namespaces["$namespace\\Plugin\\{$this->subdir}"] = array($dir);
- }
-
- return $plugin_namespaces;
+ return $this->rootNamespaces->buildFromSuffix("\\Plugin\\{$this->subdir}");
}
}
diff --git a/core/lib/Drupal/Core/TypedData/TypedDataManager.php b/core/lib/Drupal/Core/TypedData/TypedDataManager.php
index 5c48fb0..0ea9c9b 100644
--- a/core/lib/Drupal/Core/TypedData/TypedDataManager.php
+++ b/core/lib/Drupal/Core/TypedData/TypedDataManager.php
@@ -18,6 +18,7 @@
use Drupal\Core\Validation\DrupalTranslator;
use Symfony\Component\Validator\ValidatorInterface;
use Symfony\Component\Validator\Validation;
+use Krautoload\SearchableNamespaces_Interface as SearchableNamespacesInterface;
/**
* Manages data type plugins.
@@ -45,12 +46,12 @@ class TypedDataManager extends DefaultPluginManager {
*/
protected $prototypes = array();
- public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, LanguageManager $language_manager, ModuleHandlerInterface $module_handler) {
+ public function __construct(SearchableNamespacesInterface $namespaces, CacheBackendInterface $cache_backend, LanguageManager $language_manager, ModuleHandlerInterface $module_handler) {
$this->alterInfo($module_handler, 'data_type_info');
$this->setCacheBackend($cache_backend, $language_manager, 'typed_data:types');
$annotation_namespaces = array(
- 'Drupal\Core\TypedData\Annotation' => DRUPAL_ROOT . '/core/lib',
+ 'Drupal\Core\TypedData\Annotation' => TRUE,
);
parent::__construct('DataType', $namespaces, $annotation_namespaces, 'Drupal\Core\TypedData\Annotation\DataType');
}
diff --git a/core/lib/Drupal/Core/Validation/ConstraintManager.php b/core/lib/Drupal/Core/Validation/ConstraintManager.php
index 1284978..8bcd4ed 100644
--- a/core/lib/Drupal/Core/Validation/ConstraintManager.php
+++ b/core/lib/Drupal/Core/Validation/ConstraintManager.php
@@ -12,6 +12,7 @@
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Language\LanguageManager;
use Drupal\Core\Plugin\DefaultPluginManager;
+use Krautoload\SearchableNamespaces_Interface as SearchableNamespacesInterface;
/**
* Constraint plugin manager.
@@ -46,7 +47,7 @@ class ConstraintManager extends DefaultPluginManager {
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* The module handler to invoke the alter hook with.
*/
- public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, LanguageManager $language_manager, ModuleHandlerInterface $module_handler) {
+ public function __construct(SearchableNamespacesInterface $namespaces, CacheBackendInterface $cache_backend, LanguageManager $language_manager, ModuleHandlerInterface $module_handler) {
parent::__construct('Validation/Constraint', $namespaces);
$this->discovery = new StaticDiscoveryDecorator($this->discovery, array($this, 'registerDefinitions'));
$this->alterInfo($module_handler, 'validation_constraint');
diff --git a/core/modules/aggregator/lib/Drupal/aggregator/Plugin/AggregatorPluginManager.php b/core/modules/aggregator/lib/Drupal/aggregator/Plugin/AggregatorPluginManager.php
index 2aa6731..78bc921 100644
--- a/core/modules/aggregator/lib/Drupal/aggregator/Plugin/AggregatorPluginManager.php
+++ b/core/modules/aggregator/lib/Drupal/aggregator/Plugin/AggregatorPluginManager.php
@@ -12,6 +12,7 @@
use Drupal\Core\Language\Language;
use Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery;
use Drupal\Core\Plugin\Discovery\CacheDecorator;
+use Krautoload\SearchableNamespaces_Interface as SearchableNamespacesInterface;
/**
* Manages aggregator plugins.
@@ -27,7 +28,7 @@ class AggregatorPluginManager extends PluginManagerBase {
* An object that implements \Traversable which contains the root paths
* keyed by the corresponding namespace to look for plugin implementations,
*/
- public function __construct($type, \Traversable $namespaces) {
+ public function __construct($type, SearchableNamespacesInterface $namespaces) {
$type_annotations = array(
'fetcher' => 'Drupal\aggregator\Annotation\AggregatorFetcher',
'parser' => 'Drupal\aggregator\Annotation\AggregatorParser',
diff --git a/core/modules/block/lib/Drupal/block/Plugin/Type/BlockManager.php b/core/modules/block/lib/Drupal/block/Plugin/Type/BlockManager.php
index 3963262..03b705a 100644
--- a/core/modules/block/lib/Drupal/block/Plugin/Type/BlockManager.php
+++ b/core/modules/block/lib/Drupal/block/Plugin/Type/BlockManager.php
@@ -10,6 +10,7 @@
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Language\LanguageManager;
use Drupal\Core\Plugin\DefaultPluginManager;
+use Krautoload\SearchableNamespaces_Interface as SearchableNamespacesInterface;
/**
* Manages discovery and instantiation of block plugins.
*
@@ -32,7 +33,7 @@ class BlockManager extends DefaultPluginManager {
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* The module handler to invoke the alter hook with.
*/
- public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, LanguageManager $language_manager, ModuleHandlerInterface $module_handler) {
+ public function __construct(SearchableNamespacesInterface $namespaces, CacheBackendInterface $cache_backend, LanguageManager $language_manager, ModuleHandlerInterface $module_handler) {
parent::__construct('Block', $namespaces);
$this->alterInfo($module_handler, 'block');
$this->setCacheBackend($cache_backend, $language_manager, 'block_plugins');
diff --git a/core/modules/ckeditor/lib/Drupal/ckeditor/CKEditorPluginManager.php b/core/modules/ckeditor/lib/Drupal/ckeditor/CKEditorPluginManager.php
index 3b1ad15..1457f89 100644
--- a/core/modules/ckeditor/lib/Drupal/ckeditor/CKEditorPluginManager.php
+++ b/core/modules/ckeditor/lib/Drupal/ckeditor/CKEditorPluginManager.php
@@ -15,6 +15,7 @@
use Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery;
use Drupal\Core\Plugin\Discovery\CacheDecorator;
use Drupal\editor\Plugin\Core\Entity\Editor;
+use Krautoload\SearchableNamespaces_Interface as SearchableNamespacesInterface;
/**
* CKEditor Plugin manager.
@@ -28,8 +29,8 @@ class CKEditorPluginManager extends PluginManagerBase {
* An object that implements \Traversable which contains the root paths
* keyed by the corresponding namespace to look for plugin implementations,
*/
- public function __construct(\Traversable $namespaces) {
- $annotation_namespaces = array('Drupal\ckeditor\Annotation' => $namespaces['Drupal\ckeditor']);
+ public function __construct(SearchableNamespacesInterface $namespaces) {
+ $annotation_namespaces = $namespaces->buildSearchableNamespaces(array('Drupal\ckeditor\Annotation'));
$this->discovery = new AnnotatedClassDiscovery('CKEditorPlugin', $namespaces, $annotation_namespaces, 'Drupal\ckeditor\Annotation\CKEditorPlugin');
$this->discovery = new AlterDecorator($this->discovery, 'ckeditor_plugin_info');
$this->discovery = new CacheDecorator($this->discovery, 'ckeditor_plugin');
diff --git a/core/modules/edit/lib/Drupal/edit/Plugin/InPlaceEditorManager.php b/core/modules/edit/lib/Drupal/edit/Plugin/InPlaceEditorManager.php
index f4c544b..0d30f4f 100644
--- a/core/modules/edit/lib/Drupal/edit/Plugin/InPlaceEditorManager.php
+++ b/core/modules/edit/lib/Drupal/edit/Plugin/InPlaceEditorManager.php
@@ -13,6 +13,7 @@
use Drupal\Core\Plugin\Discovery\AlterDecorator;
use Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery;
use Drupal\Core\Plugin\Discovery\CacheDecorator;
+use Krautoload\SearchableNamespaces_Interface as SearchableNamespacesInterface;
/**
* Editor manager.
@@ -28,8 +29,8 @@ class InPlaceEditorManager extends PluginManagerBase {
* An object that implements \Traversable which contains the root paths
* keyed by the corresponding namespace to look for plugin implementations,
*/
- public function __construct(\Traversable $namespaces) {
- $annotation_namespaces = array('Drupal\edit\Annotation' => $namespaces['Drupal\edit']);
+ public function __construct(SearchableNamespacesInterface $namespaces) {
+ $annotation_namespaces = array('Drupal\edit\Annotation' => TRUE);
$this->discovery = new AnnotatedClassDiscovery('InPlaceEditor', $namespaces, $annotation_namespaces, 'Drupal\edit\Annotation\InPlaceEditor');
$this->discovery = new AlterDecorator($this->discovery, 'edit_editor');
$this->discovery = new CacheDecorator($this->discovery, 'edit:editor');
diff --git a/core/modules/editor/lib/Drupal/editor/Plugin/EditorManager.php b/core/modules/editor/lib/Drupal/editor/Plugin/EditorManager.php
index 2d05b1a..990bcbb 100644
--- a/core/modules/editor/lib/Drupal/editor/Plugin/EditorManager.php
+++ b/core/modules/editor/lib/Drupal/editor/Plugin/EditorManager.php
@@ -13,6 +13,7 @@
use Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery;
use Drupal\Core\Plugin\Discovery\CacheDecorator;
use Drupal\Core\Plugin\Factory\ContainerFactory;
+use Krautoload\SearchableNamespaces_Interface as SearchableNamespacesInterface;
/**
* Configurable text editor manager.
@@ -26,8 +27,8 @@ class EditorManager extends PluginManagerBase {
* An object that implements \Traversable which contains the root paths
* keyed by the corresponding namespace to look for plugin implementations,
*/
- public function __construct(\Traversable $namespaces) {
- $annotation_namespaces = array('Drupal\editor\Annotation' => $namespaces['Drupal\editor']);
+ public function __construct(SearchableNamespacesInterface $namespaces) {
+ $annotation_namespaces = array('Drupal\editor\Annotation' => TRUE);
$this->discovery = new AnnotatedClassDiscovery('Editor', $namespaces, $annotation_namespaces, 'Drupal\editor\Annotation\Editor');
$this->discovery = new AlterDecorator($this->discovery, 'editor_info');
$this->discovery = new CacheDecorator($this->discovery, 'editor');
diff --git a/core/modules/entity_reference/lib/Drupal/entity_reference/Plugin/Type/SelectionPluginManager.php b/core/modules/entity_reference/lib/Drupal/entity_reference/Plugin/Type/SelectionPluginManager.php
index d51544d..8267e2c 100644
--- a/core/modules/entity_reference/lib/Drupal/entity_reference/Plugin/Type/SelectionPluginManager.php
+++ b/core/modules/entity_reference/lib/Drupal/entity_reference/Plugin/Type/SelectionPluginManager.php
@@ -14,6 +14,7 @@
use Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery;
use Drupal\Core\Plugin\Discovery\CacheDecorator;
use Drupal\entity_reference\Plugin\Type\Selection\SelectionBroken;
+use Krautoload\SearchableNamespaces_Interface as SearchableNamespacesInterface;
/**
* Plugin type manager for the Entity Reference Selection plugin.
@@ -27,7 +28,7 @@ class SelectionPluginManager extends PluginManagerBase {
* An object that implements \Traversable which contains the root paths
* keyed by the corresponding namespace to look for plugin implementations,
*/
- public function __construct(\Traversable $namespaces) {
+ public function __construct(SearchableNamespacesInterface $namespaces) {
$this->baseDiscovery = new AlterDecorator(new AnnotatedClassDiscovery('entity_reference/selection', $namespaces), 'entity_reference_selection');
$this->discovery = new CacheDecorator($this->baseDiscovery, 'entity_reference_selection');
$this->factory = new ReflectionFactory($this);
diff --git a/core/modules/field/lib/Drupal/field/Plugin/Type/Formatter/FormatterPluginManager.php b/core/modules/field/lib/Drupal/field/Plugin/Type/Formatter/FormatterPluginManager.php
index f3cb4cb..60cde44 100644
--- a/core/modules/field/lib/Drupal/field/Plugin/Type/Formatter/FormatterPluginManager.php
+++ b/core/modules/field/lib/Drupal/field/Plugin/Type/Formatter/FormatterPluginManager.php
@@ -17,6 +17,7 @@
use Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery;
use Drupal\Core\Plugin\Discovery\AlterDecorator;
use Drupal\field\Plugin\Core\Entity\FieldInstance;
+use Krautoload\SearchableNamespaces_Interface as SearchableNamespacesInterface;
/**
* Plugin type manager for field formatters.
@@ -33,7 +34,7 @@ class FormatterPluginManager extends DefaultPluginManager {
/**
* Constructs a FormatterPluginManager object.
*
- * @param \Traversable $namespaces
+ * @param SearchableNamespacesInterface $namespaces
* An object that implements \Traversable which contains the root paths
* keyed by the corresponding namespace to look for plugin implementations.
* @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
@@ -43,8 +44,8 @@ class FormatterPluginManager extends DefaultPluginManager {
* @param \Drupal\Core\Language\LanguageManager $language_manager
* The language manager.
*/
- public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler, LanguageManager $language_manager) {
- $annotation_namespaces = array('Drupal\field\Annotation' => $namespaces['Drupal\field']);
+ public function __construct(SearchableNamespacesInterface $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler, LanguageManager $language_manager) {
+ $annotation_namespaces = $namespaces->buildSearchableNamespaces(array('Drupal\field\Annotation'));
parent::__construct('field/formatter', $namespaces, $annotation_namespaces, 'Drupal\field\Annotation\FieldFormatter');
diff --git a/core/modules/field/lib/Drupal/field/Plugin/Type/Widget/WidgetPluginManager.php b/core/modules/field/lib/Drupal/field/Plugin/Type/Widget/WidgetPluginManager.php
index 673f4c1..dddf7b6 100644
--- a/core/modules/field/lib/Drupal/field/Plugin/Type/Widget/WidgetPluginManager.php
+++ b/core/modules/field/lib/Drupal/field/Plugin/Type/Widget/WidgetPluginManager.php
@@ -16,6 +16,7 @@
use Drupal\Core\Plugin\Discovery\CacheDecorator;
use Drupal\Core\Plugin\Discovery\AlterDecorator;
use Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery;
+use Krautoload\SearchableNamespaces_Interface as SearchableNamespacesInterface;
/**
* Plugin type manager for field widgets.
@@ -52,7 +53,7 @@ class WidgetPluginManager extends DefaultPluginManager {
* @param \Drupal\Core\Language\LanguageManager $language_manager
* The language manager.
*/
- public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler, LanguageManager $language_manager) {
+ public function __construct(SearchableNamespacesInterface $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler, LanguageManager $language_manager) {
parent::__construct('field/widget', $namespaces);
$this->setCacheBackend($cache_backend, $language_manager, 'field_widget_types');
diff --git a/core/modules/filter/lib/Drupal/filter/FilterPluginManager.php b/core/modules/filter/lib/Drupal/filter/FilterPluginManager.php
index 881cdf7..8a24c94 100644
--- a/core/modules/filter/lib/Drupal/filter/FilterPluginManager.php
+++ b/core/modules/filter/lib/Drupal/filter/FilterPluginManager.php
@@ -14,6 +14,7 @@
use Drupal\Core\Plugin\Discovery\AlterDecorator;
use Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery;
use Drupal\Core\Plugin\Discovery\CacheDecorator;
+use Krautoload\SearchableNamespaces_Interface as SearchableNamespacesInterface;
/**
* Manages text processing filters.
@@ -29,8 +30,8 @@ class FilterPluginManager extends PluginManagerBase {
* An object that implements \Traversable which contains the root paths
* keyed by the corresponding namespace to look for plugin implementations.
*/
- public function __construct(\Traversable $namespaces) {
- $annotation_namespaces = array('Drupal\filter\Annotation' => $namespaces['Drupal\filter']);
+ public function __construct(SearchableNamespacesInterface $namespaces) {
+ $annotation_namespaces = array('Drupal\filter\Annotation' => TRUE);
$this->discovery = new AnnotatedClassDiscovery('Filter', $namespaces, $annotation_namespaces, 'Drupal\filter\Annotation\Filter');
$this->discovery = new AlterDecorator($this->discovery, 'filter_info');
$cache_key = 'filter_plugins:' . language(Language::TYPE_INTERFACE)->id;
diff --git a/core/modules/layout/lib/Drupal/layout/Plugin/Type/LayoutManager.php b/core/modules/layout/lib/Drupal/layout/Plugin/Type/LayoutManager.php
index e22623f1..115fedb 100644
--- a/core/modules/layout/lib/Drupal/layout/Plugin/Type/LayoutManager.php
+++ b/core/modules/layout/lib/Drupal/layout/Plugin/Type/LayoutManager.php
@@ -12,6 +12,7 @@
use Drupal\Component\Plugin\Discovery\ProcessDecorator;
use Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery;
use Drupal\Component\Plugin\Factory\ReflectionFactory;
+use Krautoload\SearchableNamespaces_Interface as SearchableNamespacesInterface;
/**
* Layout plugin manager.
@@ -29,7 +30,7 @@ class LayoutManager extends PluginManagerBase {
* An object that implements \Traversable which contains the root paths
* keyed by the corresponding namespace to look for plugin implementations,
*/
- public function __construct(\Traversable $namespaces) {
+ public function __construct(SearchableNamespacesInterface $namespaces) {
// Create layout plugin derivatives from declaratively defined layouts.
$this->discovery = new AnnotatedClassDiscovery('Layout', $namespaces);
$this->discovery = new DerivativeDiscoveryDecorator($this->discovery);
diff --git a/core/modules/rest/lib/Drupal/rest/Plugin/Type/ResourcePluginManager.php b/core/modules/rest/lib/Drupal/rest/Plugin/Type/ResourcePluginManager.php
index 6edea17..cc4865e 100644
--- a/core/modules/rest/lib/Drupal/rest/Plugin/Type/ResourcePluginManager.php
+++ b/core/modules/rest/lib/Drupal/rest/Plugin/Type/ResourcePluginManager.php
@@ -13,6 +13,7 @@
use Drupal\Core\Plugin\Discovery\AlterDecorator;
use Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery;
use Drupal\Core\Plugin\Discovery\CacheDecorator;
+use Krautoload\SearchableNamespaces_Interface as SearchableNamespacesInterface;
/**
* Manages discovery and instantiation of resource plugins.
@@ -26,7 +27,7 @@ class ResourcePluginManager extends PluginManagerBase {
* An object that implements \Traversable which contains the root paths
* keyed by the corresponding namespace to look for plugin implementations,
*/
- public function __construct(\Traversable $namespaces) {
+ public function __construct(SearchableNamespacesInterface $namespaces) {
// Create resource plugin derivatives from declaratively defined resources.
$this->discovery = new AnnotatedClassDiscovery('rest/resource', $namespaces);
$this->discovery = new DerivativeDiscoveryDecorator($this->discovery);
diff --git a/core/modules/simpletest/simpletest.module b/core/modules/simpletest/simpletest.module
index 5461cab..1f78c25 100644
--- a/core/modules/simpletest/simpletest.module
+++ b/core/modules/simpletest/simpletest.module
@@ -444,38 +444,24 @@ function simpletest_test_get_all() {
$groups = $cache->data;
}
else {
- // Select all PSR-0 classes in the Tests namespace of all modules.
- $classes = array();
+ // Assemble test namespaces for modules, themes and install profiles.
$module_data = system_rebuild_module_data();
$all_data = $module_data + system_rebuild_theme_data();
$all_data += drupal_system_listing('/\.profile$/', 'profiles', 'name');
+ $namespaces = array();
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';
- // 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);
- }
- }
- }
+ $namespaces[] = "Drupal\\$name\\Tests";
}
+ // Build a searchable namespace collection.
+ $namespaces = drupal_classloader()->buildSearchableNamespaces($namespaces);
+
// Check that each class has a getInfo() method and store the information
// in an array keyed with the group specified in the test information.
$groups = array();
- foreach ($classes as $class) {
+ foreach ($namespaces->discoverExistingClasses(TRUE) as $class) {
// Test classes need to implement getInfo() to be valid.
- if (class_exists($class) && method_exists($class, 'getInfo')) {
+ if (method_exists($class, 'getInfo')) {
$info = call_user_func(array($class, 'getInfo'));
// If this test class requires a non-existing module, skip it.
@@ -520,23 +506,14 @@ 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()->addPrefix('Drupal\\' . $name . '\\Tests', DRUPAL_ROOT . '/' . dirname($file->uri) . '/tests');
+ drupal_classloader()->addNamespacePSR0('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()->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()->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/',
- ));
+ drupal_classloader()->addNamespacePSR0('Drupal\\Tests', DRUPAL_ROOT . '/core/tests');
}
/**
diff --git a/core/modules/system/lib/Drupal/system/Plugin/ImageToolkitManager.php b/core/modules/system/lib/Drupal/system/Plugin/ImageToolkitManager.php
index ddad9c6..a19898f 100644
--- a/core/modules/system/lib/Drupal/system/Plugin/ImageToolkitManager.php
+++ b/core/modules/system/lib/Drupal/system/Plugin/ImageToolkitManager.php
@@ -10,6 +10,7 @@
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Language\LanguageManager;
use Drupal\Core\Plugin\DefaultPluginManager;
+use Krautoload\SearchableNamespaces_Interface as SearchableNamespacesInterface;
/**
* Manages toolkit plugins.
@@ -27,7 +28,7 @@ class ImageToolkitManager extends DefaultPluginManager {
* @param \Drupal\Core\Language\LanguageManager $language_manager
* The language manager.
*/
- public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, LanguageManager $language_manager) {
+ public function __construct(SearchableNamespacesInterface $namespaces, CacheBackendInterface $cache_backend, LanguageManager $language_manager) {
parent::__construct('ImageToolkit', $namespaces);
$this->setCacheBackend($cache_backend, $language_manager, 'image_toolkit');
}
diff --git a/core/modules/system/lib/Drupal/system/Plugin/Type/PluginUIManager.php b/core/modules/system/lib/Drupal/system/Plugin/Type/PluginUIManager.php
index 0514fe3..dd5e8d8 100644
--- a/core/modules/system/lib/Drupal/system/Plugin/Type/PluginUIManager.php
+++ b/core/modules/system/lib/Drupal/system/Plugin/Type/PluginUIManager.php
@@ -13,6 +13,7 @@
use Drupal\Core\Plugin\Discovery\AlterDecorator;
use Drupal\Core\Plugin\Discovery\CacheDecorator;
use Drupal\Component\Plugin\Factory\DefaultFactory;
+use Krautoload\SearchableNamespaces_Interface as SearchableNamespacesInterface;
/**
* Manages discovery and instantiation of Plugin UI plugins.
@@ -28,7 +29,7 @@ class PluginUIManager extends PluginManagerBase {
* An object that implements \Traversable which contains the root paths
* keyed by the corresponding namespace to look for plugin implementations,
*/
- public function __construct(\Traversable $namespaces) {
+ public function __construct(SearchableNamespacesInterface $namespaces) {
$this->discovery = new AnnotatedClassDiscovery('PluginUI', $namespaces);
$this->discovery = new DerivativeDiscoveryDecorator($this->discovery);
$this->discovery = new AlterDecorator($this->discovery, 'plugin_ui');
diff --git a/core/modules/system/lib/Drupal/system/Tests/DrupalKernel/DrupalKernelTest.php b/core/modules/system/lib/Drupal/system/Tests/DrupalKernel/DrupalKernelTest.php
index db60d18..0273bb6 100644
--- a/core/modules/system/lib/Drupal/system/Tests/DrupalKernel/DrupalKernelTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/DrupalKernel/DrupalKernelTest.php
@@ -80,7 +80,7 @@ function testCompileDIC() {
// Test that our synthetic services are there.
$classloader = $container->get('class_loader');
$refClass = new ReflectionClass($classloader);
- $this->assertTrue($refClass->hasMethod('loadClass'), 'Container has a classloader');
+ $this->assertTrue($refClass->hasMethod('composerPrefixes'), 'Container has a classloader registration tool.');
// We make this assertion here purely to show that the new container below
// is functioning correctly, i.e. we get a brand new ContainerBuilder
@@ -108,7 +108,8 @@ function testCompileDIC() {
// Test that our synthetic services are there.
$classloader = $container->get('class_loader');
$refClass = new ReflectionClass($classloader);
- $this->assertTrue($refClass->hasMethod('loadClass'), 'Container has a classloader');
+ // @todo The signature of the registration tool needs to be finalized.
+ $this->assertTrue($refClass->hasMethod('composerPrefixes'), 'Container has a classloader registration tool.');
// Check that the location of the new module is registered.
$modules = $container->getParameter('container.modules');
$this->assertEqual($modules['service_provider_test'], drupal_get_filename('module', 'service_provider_test'));
diff --git a/core/modules/system/lib/Drupal/system/Tests/Module/ClassLoaderTest.php b/core/modules/system/lib/Drupal/system/Tests/Module/ClassLoaderTest.php
index 2fd8eb9..80dcecd 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Module/ClassLoaderTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Module/ClassLoaderTest.php
@@ -44,6 +44,54 @@ function testClassLoading() {
}
/**
+ * Tests various edge cases with PSR-0.
+ *
+ * We do each test in a separate request, to make sure that the classes are
+ * not already loaded due to a previous test.
+ */
+ function testPSR0Specialties() {
+ // Enable the module_test and module_autoload_test modules.
+ module_enable(array('module_test', 'module_autoload_test'), FALSE);
+ $this->resetAll();
+
+ // Test that underscores in PSR-0 classes are dealt with correctly.
+ $this->drupalGet('module-test/class-loading/psr0-underscores');
+ $this->assertText('A class with underscores was found.',
+ 'Underscores in the class name are replaced with directory separators.');
+ $this->assertText('A file with underscores was not included.',
+ 'No file with underscores in the file name is included.');
+
+ // Test that the PSR-0 class loader does not include the same file twice.
+ $this->drupalGet('module-test/class-loading/psr0-multi-include');
+ $this->assertText('The application did not crash.',
+ 'The class loader avoids multiple file inclusion, if two PSR-0 classes are expected to be in the same file.');
+
+ // Test that no classes from the wrong module are included.
+ $this->drupalGet('module-test/class-loading/psr0-prefix-clash');
+ $this->assertText('A file was not included because it is in the wrong folder.',
+ 'A PSR-0 class loader will not look in the lib folder of another module.');
+ }
+
+ /**
+ * Tests PSR-X class loading for modules.
+ * PSR-X classes are in the src/ folder of a module.
+ */
+ function testClassLoadingPSRX() {
+ // Enable the module_test and module_autoload_test modules.
+ module_enable(array('module_test', 'module_autoload_test'), FALSE);
+ $this->resetAll();
+
+ // Check that PSR-X classes are found.
+ $this->drupalGet('module-test/class-loading/psrx');
+ $this->assertText('SomeClassPSRX was found.',
+ 'Classes are found by the PSR-X class loader.');
+ $this->assertText('ClassWith_Underscore_PSRX was found.',
+ 'Classes with underscores are found by the PSR-X class loader.');
+ $this->assertText('Class in sub-namespace was found.',
+ 'Classes in sub-namespaces are found by the PSR-X class loader.');
+ }
+
+ /**
* Tests that module-provided classes can't be loaded from disabled modules.
*
* @see \Drupal\module_autoload_test\SomeClass
diff --git a/core/modules/system/tests/modules/module_autoload_test/lib/Drupal/module_autoload_test/ExistingClass/WithUnderscore.php b/core/modules/system/tests/modules/module_autoload_test/lib/Drupal/module_autoload_test/ExistingClass/WithUnderscore.php
new file mode 100644
index 0000000..dda4756
--- /dev/null
+++ b/core/modules/system/tests/modules/module_autoload_test/lib/Drupal/module_autoload_test/ExistingClass/WithUnderscore.php
@@ -0,0 +1,10 @@
+ 'module_test_class_loading',
'access callback' => TRUE,
);
+ $items['module-test/class-loading/psr0-underscores'] = array(
+ 'title' => 'Test PSR-0 underscore handling',
+ 'page callback' => 'module_test_class_loading_psr0_underscores',
+ 'access callback' => TRUE,
+ );
+ $items['module-test/class-loading/psr0-multi-include'] = array(
+ 'title' => 'Test PSR-0 multi includes',
+ 'page callback' => 'module_test_class_loading_psr0_multi_include',
+ 'access callback' => TRUE,
+ );
+ $items['module-test/class-loading/psr0-prefix-clash'] = array(
+ 'title' => 'Test PSR-0 prefix clash',
+ 'page callback' => 'module_test_class_loading_psr0_prefix_clash',
+ 'access callback' => TRUE,
+ );
+ $items['module-test/class-loading/psrx'] = array(
+ 'title' => 'Test PSR-X class loading',
+ 'page callback' => 'module_test_class_loading_psrx',
+ 'access callback' => TRUE,
+ );
return $items;
}
@@ -150,6 +170,58 @@ function module_test_class_loading() {
}
/**
+ * Page callback for 'module-test/class-loading/psr0-underscores'.
+ */
+function module_test_class_loading_psr0_underscores() {
+ $pieces = array();
+ if (class_exists('Drupal\module_autoload_test\ExistingClass_WithUnderscore')) {
+ $pieces[] = 'A class with underscores was found.';
+ }
+ if (!class_exists('Drupal\module_autoload_test\UnloadableClass_WithUnderscore')) {
+ $pieces[] = 'A file with underscores was not included.';
+ }
+ return implode('
', array_filter($pieces));
+}
+
+/**
+ * Page callback for 'module-test/class-loading/psr0-multi-include'.
+ */
+function module_test_class_loading_psr0_multi_include() {
+ class_exists('Drupal\module_autoload_test\SubNamespace\SomeClass');
+ class_exists('Drupal\module_autoload_test\SubNamespace_SomeClass');
+ // If we get here, the application did not crash.
+ return 'The application did not crash.';
+}
+
+/**
+ * Page callback for 'module-test/class-loading/psr0-prefix-clash'.
+ */
+function module_test_class_loading_psr0_prefix_clash() {
+ // The class is defined in the lib folder of module_autoload_test, but we
+ // expect the class loader to ignore that file.
+ if (!class_exists('Drupal\module_autoload_test_subnamespace\PrefixTestClass')) {
+ return 'A file was not included because it is in the wrong folder.';
+ }
+}
+
+/**
+ * Page callback for 'module-test/class-loading/psrx'.
+ */
+function module_test_class_loading_psrx() {
+ $pieces = array();
+ if (class_exists('Drupal\module_autoload_test\SomeClassPSRX')) {
+ $pieces[] = 'SomeClassPSRX was found.';
+ }
+ if (class_exists('Drupal\module_autoload_test\ClassWith_Underscore_PSRX')) {
+ $pieces[] = 'ClassWith_Underscore_PSRX was found.';
+ }
+ if (class_exists('Drupal\module_autoload_test\SubNamespacePSRX\SomeClass')) {
+ $pieces[] = 'Class in sub-namespace was found.';
+ }
+ return implode('
', array_filter($pieces));
+}
+
+/**
* Implements hook_modules_enabled().
*/
function module_test_modules_enabled($modules) {
diff --git a/core/modules/tour/lib/Drupal/tour/TipPluginManager.php b/core/modules/tour/lib/Drupal/tour/TipPluginManager.php
index e23669b..6b0b118 100644
--- a/core/modules/tour/lib/Drupal/tour/TipPluginManager.php
+++ b/core/modules/tour/lib/Drupal/tour/TipPluginManager.php
@@ -13,6 +13,7 @@
use Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery;
use Drupal\Core\Plugin\Discovery\CacheDecorator;
use Drupal\Core\Plugin\Factory\ContainerFactory;
+use Krautoload\SearchableNamespaces_Interface as SearchableNamespacesInterface;
/**
* Configurable tour manager.
@@ -26,8 +27,8 @@ class TipPluginManager extends PluginManagerBase {
* An object that implements \Traversable which contains the root paths
* keyed by the corresponding namespace to look for plugin implementations,
*/
- public function __construct(\Traversable $namespaces) {
- $annotation_namespaces = array('Drupal\tour\Annotation' => $namespaces['Drupal\tour']);
+ public function __construct(SearchableNamespacesInterface $namespaces) {
+ $annotation_namespaces = array('Drupal\tour\Annotation' => TRUE);
$this->discovery = new AnnotatedClassDiscovery('tour/tip', $namespaces, $annotation_namespaces, 'Drupal\tour\Annotation\Tip');
$this->discovery = new AlterDecorator($this->discovery, 'tour_tips_info');
$this->discovery = new CacheDecorator($this->discovery, 'tour');
diff --git a/core/modules/views/lib/Drupal/views/Plugin/Discovery/ViewsHandlerDiscovery.php b/core/modules/views/lib/Drupal/views/Plugin/Discovery/ViewsHandlerDiscovery.php
index 460ef5a..1ef3e4e 100644
--- a/core/modules/views/lib/Drupal/views/Plugin/Discovery/ViewsHandlerDiscovery.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/Discovery/ViewsHandlerDiscovery.php
@@ -8,6 +8,7 @@
namespace Drupal\views\Plugin\Discovery;
use Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery;
+use Krautoload\SearchableNamespaces_Interface as SearchableNamespacesInterface;
/**
* Defines a discovery mechanism to find Views handlers in PSR-0 namespaces.
@@ -26,31 +27,24 @@ class ViewsHandlerDiscovery extends AnnotatedClassDiscovery {
*
* @var \Traversable
*/
- protected $rootNamespacesIterator;
+ protected $rootNamespaces;
/**
* Constructs a ViewsHandlerDiscovery object.
*
* @param string $type
* The plugin type, for example filter.
- * @param \Traversable $root_namespaces
+ * @param SearchableNamespacesInterface $root_namespaces
* An object that implements \Traversable which contains the root paths
* keyed by the corresponding namespace to look for plugin implementations,
*/
- function __construct($type, \Traversable $root_namespaces) {
+ function __construct($type, SearchableNamespacesInterface $root_namespaces) {
$this->type = $type;
- $this->rootNamespacesIterator = $root_namespaces;
+ $this->rootNamespaces = $root_namespaces;
- $annotation_namespaces = array(
- 'Drupal\Component\Annotation' => DRUPAL_ROOT . '/core/lib',
- );
- $plugin_namespaces = array();
- foreach ($root_namespaces as $namespace => $dir) {
- $plugin_namespaces["$namespace\\Plugin\\views\\{$type}"] = array($dir);
- }
+ $this->pluginNamespaces = $root_namespaces->buildFromSuffix("\\Plugin\\views\\{$type}");
+ $this->annotationNamespaces = $root_namespaces->buildSearchableNamespaces(array('Drupal\Component\Annotation'));
- $this->pluginNamespaces = $plugin_namespaces;
- $this->annotationNamespaces = $annotation_namespaces;
$this->pluginDefinitionAnnotationName = 'Drupal\Component\Annotation\PluginID';
}
@@ -70,12 +64,7 @@ public function getDefinitions() {
* {@inheritdoc}
*/
protected function getPluginNamespaces() {
- $plugin_namespaces = array();
- foreach ($this->rootNamespacesIterator as $namespace => $dir) {
- $plugin_namespaces["$namespace\\Plugin\\views\\{$this->type}"] = array($dir);
- }
-
- return $plugin_namespaces;
+ return $this->rootNamespaces->buildFromSuffix("\\Plugin\\views\\{$this->type}");
}
}
diff --git a/core/modules/views/lib/Drupal/views/Plugin/ViewsHandlerManager.php b/core/modules/views/lib/Drupal/views/Plugin/ViewsHandlerManager.php
index 4ac2fac..228e12d 100644
--- a/core/modules/views/lib/Drupal/views/Plugin/ViewsHandlerManager.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/ViewsHandlerManager.php
@@ -13,6 +13,7 @@
use Drupal\Core\Plugin\Discovery\CacheDecorator;
use Drupal\views\Plugin\Discovery\ViewsHandlerDiscovery;
use Drupal\views\ViewsData;
+use Krautoload\SearchableNamespaces_Interface as SearchableNamespacesInterface;
/**
* Plugin type manager for all views handlers.
@@ -46,7 +47,7 @@ class ViewsHandlerManager extends PluginManagerBase {
* @param \Drupal\views\ViewsData $views_data
* The views data cache.
*/
- public function __construct($handler_type, \Traversable $namespaces, ViewsData $views_data) {
+ public function __construct($handler_type, SearchableNamespacesInterface $namespaces, ViewsData $views_data) {
$this->discovery = new ViewsHandlerDiscovery($handler_type, $namespaces);
$this->discovery = new CacheDecorator($this->discovery, "views:$handler_type", 'views_info');
diff --git a/core/modules/views/lib/Drupal/views/Plugin/ViewsPluginManager.php b/core/modules/views/lib/Drupal/views/Plugin/ViewsPluginManager.php
index 2b8c6a6..b6036b4 100644
--- a/core/modules/views/lib/Drupal/views/Plugin/ViewsPluginManager.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/ViewsPluginManager.php
@@ -15,6 +15,7 @@
use Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery;
use Drupal\Core\Plugin\Discovery\CacheDecorator;
use Drupal\Core\Plugin\Factory\ContainerFactory;
+use Krautoload\SearchableNamespaces_Interface as SearchableNamespacesInterface;
/**
* Plugin type manager for all views plugins.
@@ -30,7 +31,7 @@ class ViewsPluginManager extends PluginManagerBase {
* An object that implements \Traversable which contains the root paths
* keyed by the corresponding namespace to look for plugin implementations,
*/
- public function __construct($type, \Traversable $namespaces) {
+ public function __construct($type, SearchableNamespacesInterface $namespaces) {
$this->discovery = new AnnotatedClassDiscovery("views/$type", $namespaces);
$this->discovery = new DerivativeDiscoveryDecorator($this->discovery);
$this->discovery = new ProcessDecorator($this->discovery, array($this, 'processDefinition'));
diff --git a/core/vendor/krautoload/LICENSE b/core/vendor/krautoload/LICENSE
new file mode 100644
index 0000000..9b3e7af
--- /dev/null
+++ b/core/vendor/krautoload/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2013 Andreas Hennings
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/core/vendor/krautoload/README.md b/core/vendor/krautoload/README.md
new file mode 100644
index 0000000..d8d3534
--- /dev/null
+++ b/core/vendor/krautoload/README.md
@@ -0,0 +1,88 @@
+Krautoload is a pluggable PHP class autoloader library that makes you fantasize of Kartoffelbrei, Kasseler and Sauerkraut.
+It has native support for
+- [PSR-0](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md)
+- Variation of PSR-0 which allows shallow directory structures.
+- PEAR (that is the old-school pattern with underscores instead of namespaces)
+- Variation of PEAR which allows shallow directory structures.
+- The [proposed PSR-X](https://github.com/php-fig/fig-standards/blob/master/proposed/autoloader.md), which is a shallow-dir variation of PSR-0 without the special underscore handling.
+
+Besides that, custom plugins can be mapped to any namespaces and prefixes.
+This way, you can easily support old-school libraries which don't support any standards, without bloating the SPL autoload stack.
+
+Krautoload is designed to perform equally well no matter how many namespaces are registered.
+
+
+## Project status and history
+
+The project is to be considered in "Preview" status.
+It should work ok, but API details may still change based on community feedback.
+Especially, the term "PSR-X" may change in the future, if it gets accepted.
+
+A cache layer (APC) basically exist, but is not accessible yet.
+
+The project is a spin-off of the ["xautoload" module for Drupal](http://drupal.org/project/xautoload), with some changes.
+
+Unlike xautoload, Krautoload is written in anticipation of the hopefully upcoming PSR-X.
+It is optimized for PSR-X, and needs a tiny-tiny extra operation if wired up with PSR-0, for the special underscore handling.
+
+
+## Purpose / Audience
+
+Modern PHP projects typically use class loading solutions shipped with the framework, or provided by Composer.
+Thus, Krautoload is mainly aimed at framework developers, for copying or inspiration.
+
+
+## Usage
+
+Krautoload provides a start-off class with static methods, for those who want to avoid a lengthy bootstrap.
+Alternative bootstrap helpers may be provided based on your feedback.
+
+```php
+require_once "$path_to_krautoload/src/Krautoload.php";
+
+// Create the class loader and register it.
+$krautoload = Krautoload::start();
+
+// Register additional namespaces
+$krautoload->namespacePSR0('FooVendor\FooPackage', "$path_to_foo_package/src");
+
+new FooVendor\FooPackage\Foo\Bar\Baz();
+```
+
+See [Krautoload\RegistrationHub](https://github.com/donquixote/krautoload/blob/master/src/Krautoload/RegistrationHub.php)
+to see all the available registration methods.
+
+
+### Usage with Composer
+
+Krautoload can be used instead of the autoload.php generated by Composer.
+Boot Krautoload like above, and then
+
+```php
+// Let Krautoload look for the files defining the namespace map and class map.
+$krautoload->composerVendorDir($vendor_dir);
+```
+
+
+## Unit tests
+
+Krautoload is designed to be unit-testable, better than other class loaders.
+Its architecture allows to mock out and simulate all hits to the filesystem (file_exists(), require_once, etc).
+
+Unfortunately, I have no experience with testing frameworks outside of Drupal (yet).
+Thus, no tests exist yet.
+(but there are tests in Drupal xautoload, which show that the architecture is ok)
+
+
+## Benchmarks
+
+Benchmarks would be nice to have, but they do not exist yet. Any help
+appreciated.
+
+Like Tests, benchmarks *can* be programmed with a mocked-out filesystem, where
+instead of actually doing file inclusion or file_exists(), we simply count the
+number of times this would happen.
+
+This will not tell us the real duration in a live environment, but it will
+determine the time used for the actual class finding much more accurately than
+with the natural variations of filesystem operations.
diff --git a/core/vendor/krautoload/src/Krautoload.php b/core/vendor/krautoload/src/Krautoload.php
new file mode 100644
index 0000000..f8bc9d1
--- /dev/null
+++ b/core/vendor/krautoload/src/Krautoload.php
@@ -0,0 +1,46 @@
+register();
+
+ // Wire up the class finder so it can find Krautoload classes.
+ // Krautoload uses PSR-0 with only underscores after the package namespace.
+ $plugin = new Krautoload\NamespacePathPlugin_ShallowPSR0_AllUnderscore();
+ $finder->registerNamespacePathPlugin('Krautoload/', $basedir . DIRECTORY_SEPARATOR, $plugin);
+
+ // Create the registration hub.
+ self::$hub = new Krautoload\RegistrationHub($finder);
+ return self::$hub;
+ }
+
+ static function registration() {
+ if (!isset(self::$hub)) {
+ throw new Exception("Krautoload::start() must run before Krautoload::registration()");
+ }
+ return self::$hub;
+ }
+}
diff --git a/core/vendor/krautoload/src/Krautoload/ClassFinder/Interface.php b/core/vendor/krautoload/src/Krautoload/ClassFinder/Interface.php
new file mode 100644
index 0000000..e13007d
--- /dev/null
+++ b/core/vendor/krautoload/src/Krautoload/ClassFinder/Interface.php
@@ -0,0 +1,37 @@
+suggestFile($file) with all suggestions we
+ * can find, until it returns TRUE. Once suggestFile() returns TRUE, we stop
+ * and return TRUE as well. The $file will be in the $api object, so we
+ * don't need to return it.
+ * @param string $class
+ * The name of the class, with all namespaces prepended.
+ * E.g. Some\Namespace\Some\Class
+ *
+ * @return TRUE|NULL
+ * TRUE, if we found the file for the class.
+ * That is, if the $api->suggestFile($file) method returned TRUE one time.
+ * NULL, if we have no more suggestions.
+ */
+ public function apiFindFile($api, $class);
+}
diff --git a/core/vendor/krautoload/src/Krautoload/ClassFinder/Pluggable.php b/core/vendor/krautoload/src/Krautoload/ClassFinder/Pluggable.php
new file mode 100644
index 0000000..60ada28
--- /dev/null
+++ b/core/vendor/krautoload/src/Krautoload/ClassFinder/Pluggable.php
@@ -0,0 +1,153 @@
+suggestFile($file) method, which returns TRUE if the
+ // suggested file exists.
+ // The ->apiFindFile() method is supposed to suggest a number of files
+ // to the $api, until one is successful, and then return TRUE. Or return
+ // FALSE, if nothing was found.
+ if ($this->apiFindFile($api, $class)) {
+ return TRUE;
+ }
+ return FALSE;
+ }
+
+ /**
+ * Finds the path to the file where the class is defined.
+ *
+ * @param InjectedAPI_ClassFinder_Interface $api
+ * API object with a suggestFile() method.
+ * We are supposed to call $api->suggestFile($file) with all suggestions we
+ * can find, until it returns TRUE. Once suggestFile() returns TRUE, we stop
+ * and return TRUE as well. The $file will be in the $api object, so we
+ * don't need to return it.
+ * @param string $class
+ * The name of the class, with all namespaces prepended.
+ * E.g. Some\Namespace\Some\Class
+ *
+ * @return TRUE|NULL
+ * TRUE, if we found the file for the class.
+ * That is, if the $api->suggestFile($file) method returned TRUE one time.
+ * NULL, if we have no more suggestions.
+ */
+ public function apiFindFile($api, $class) {
+
+ // Discard initial namespace separator.
+ if ('\\' === $class[0]) {
+ $class = substr($class, 1);
+ }
+
+ // First check if the literal class name is registered.
+ if (!empty($this->classes[$class])) {
+ foreach ($this->classes[$class] as $file => $skip_class_exists) {
+ if ($skip_class_exists) {
+ if ($api->guessFile($file)) {
+ return TRUE;
+ }
+ }
+ else {
+ if ($api->guessFileCandidate($file)) {
+ return TRUE;
+ }
+ }
+ }
+ }
+
+ // Distinguish namespace vs underscore-only.
+ if (FALSE !== $pos = strrpos($class, '\\')) {
+
+ // Loop through positions of '\\', backwards.
+ $logicalBasePath = str_replace('\\', DIRECTORY_SEPARATOR, substr($class, 0, $pos + 1));
+ $relativePath = substr($class, $pos + 1) . '.php';
+ if ($this->apiMapFindFile($api, $this->namespaceMap, $logicalBasePath, $relativePath)) {
+ return TRUE;
+ }
+ }
+ else {
+
+ // The class is not within a namespace.
+ // Fall back to the prefix-based finder.
+ $logicalBasePath = str_replace('_', DIRECTORY_SEPARATOR, $class) . '.php';
+ if ($this->apiMapFindFile($api, $this->prefixMap, $logicalBasePath, '')) {
+ return TRUE;
+ }
+ }
+ }
+
+ /**
+ * Find the file for a class that in PSR-0 or PEAR would be in
+ * $psr_0_root . '/' . $logicalBasePath . $relativePath
+ *
+ * @param array $map
+ * Either the namespace map or the prefix
+ * @param InjectedAPI_ClassFinder_Interface $api
+ * API object with a suggestFile() method.
+ * We are supposed to call $api->suggestFile($file) with all suggestions we
+ * can find, until it returns TRUE. Once suggestFile() returns TRUE, we stop
+ * and return TRUE as well. The $file will be in the $api object, so we
+ * don't need to return it.
+ * @param string $logicalBasePath
+ * First part of the canonical path, with trailing DIRECTORY_SEPARATOR.
+ * @param string $relativePath
+ * Second part of the canonical path, ending with '.php'.
+ *
+ * @return TRUE|NULL
+ * TRUE, if we found the file for the class.
+ * That is, if the $api->suggestFile($file) method returned TRUE one time.
+ * NULL, if we have no more suggestions.
+ */
+ protected function apiMapFindFile($api, $map, $logicalBasePath, $relativePath) {
+ $logicalPath = $logicalBasePath . $relativePath;
+ while (TRUE) {
+
+ // Check any plugin registered for this fragment.
+ if (!empty($map[$logicalBasePath])) {
+ foreach ($map[$logicalBasePath] as $baseDir => $plugin) {
+ if ($plugin->pluginFindFile($api, $baseDir, $relativePath)) {
+ return TRUE;
+ }
+ }
+ }
+
+ // Continue with parent fragment.
+ if ('' === $logicalBasePath) {
+ break;
+ }
+ elseif (DIRECTORY_SEPARATOR === $logicalBasePath) {
+ // This happens if a class begins with an underscore.
+ $logicalBasePath = '';
+ $relativePath = $logicalPath;
+ }
+ elseif (FALSE !== $pos = strrpos($logicalBasePath, DIRECTORY_SEPARATOR, -2)) {
+ $logicalBasePath = substr($logicalBasePath, 0, $pos + 1);
+ $relativePath = substr($logicalPath, $pos + 1);
+ }
+ else {
+ $logicalBasePath = '';
+ $relativePath = $logicalPath;
+ }
+ }
+ }
+}
diff --git a/core/vendor/krautoload/src/Krautoload/ClassLoader/Abstract.php b/core/vendor/krautoload/src/Krautoload/ClassLoader/Abstract.php
new file mode 100644
index 0000000..1ba5274
--- /dev/null
+++ b/core/vendor/krautoload/src/Krautoload/ClassLoader/Abstract.php
@@ -0,0 +1,38 @@
+= 0) {
+ spl_autoload_register(array($this, 'loadClass'), TRUE, $prepend);
+ }
+ elseif ($prepend) {
+ $loaders = spl_autoload_functions();
+ spl_autoload_register(array($this, 'loadClass'));
+ foreach ($loaders as $loader) {
+ spl_autoload_unregister($loader);
+ spl_autoload_register($loader);
+ }
+ }
+ else {
+ spl_autoload_register(array($this, 'loadClass'));
+ }
+ }
+
+ /**
+ * Unregister from the spl autoload stack.
+ */
+ function unregister() {
+ spl_autoload_unregister(array($this, 'loadClass'));
+ }
+}
diff --git a/core/vendor/krautoload/src/Krautoload/ClassLoader/ApcAggressive.php b/core/vendor/krautoload/src/Krautoload/ClassLoader/ApcAggressive.php
new file mode 100644
index 0000000..0d931f5
--- /dev/null
+++ b/core/vendor/krautoload/src/Krautoload/ClassLoader/ApcAggressive.php
@@ -0,0 +1,24 @@
+prefix . $class)) {
+ apc_store($this->prefix . $class, $file = parent::findFile($class));
+ }
+
+ return $file;
+ }
+}
diff --git a/core/vendor/krautoload/src/Krautoload/ClassLoader/ApcCache.php b/core/vendor/krautoload/src/Krautoload/ClassLoader/ApcCache.php
new file mode 100644
index 0000000..66bd423
--- /dev/null
+++ b/core/vendor/krautoload/src/Krautoload/ClassLoader/ApcCache.php
@@ -0,0 +1,67 @@
+prefix = $prefix;
+ parent::__construct($finder);
+ }
+
+ /**
+ * Set the APC prefix after a flush cache.
+ *
+ * @param string $prefix
+ * A prefix for the storage key in APC.
+ */
+ function setApcPrefix($prefix) {
+ $this->prefix = $prefix;
+ }
+
+ /**
+ * Callback for class loading. This will include ("require") the file found.
+ *
+ * @param string $class
+ * The class to load.
+ */
+ function loadClass($class) {
+
+ if ($file = $this->findFile($class)) {
+ require $file;
+ }
+ }
+
+ /**
+ * For compatibility, it is possible to use the class loader as a finder.
+ *
+ * @param string $class
+ * The class to find.
+ *
+ * @return string
+ * File where the class is assumed to be.
+ */
+ function findFile($class) {
+
+ if (
+ (FALSE === $file = apc_fetch($this->prefix . $class)) ||
+ (!empty($file) && !is_file($file))
+ ) {
+ // Resolve cache miss.
+ apc_store($this->prefix . $class, $file = parent::findFile($class));
+ }
+
+ return $file;
+ }
+}
diff --git a/core/vendor/krautoload/src/Krautoload/ClassLoader/Interface.php b/core/vendor/krautoload/src/Krautoload/ClassLoader/Interface.php
new file mode 100644
index 0000000..b94b2da
--- /dev/null
+++ b/core/vendor/krautoload/src/Krautoload/ClassLoader/Interface.php
@@ -0,0 +1,38 @@
+finder = $finder;
+ }
+
+ /**
+ * Callback for class loading. This will include ("require") the file found.
+ *
+ * @param string $class
+ * The class to load.
+ */
+ function loadClass($class) {
+ $api = new InjectedAPI_ClassFinder_LoadClass($class);
+ // $api has a ->suggestFile($file) method, which returns TRUE if the
+ // suggested file exists.
+ // The $finder->findFile() method is supposed to suggest a number of files
+ // to the $api, until one is successful, and then return TRUE. Or return
+ // FALSE, if nothing was found.
+ if ($this->finder->apiFindFile($api, $class)) {
+ return TRUE;
+ }
+ return FALSE;
+ }
+}
diff --git a/core/vendor/krautoload/src/Krautoload/ClassLoader/Pluggable.php b/core/vendor/krautoload/src/Krautoload/ClassLoader/Pluggable.php
new file mode 100644
index 0000000..01ce6b2
--- /dev/null
+++ b/core/vendor/krautoload/src/Krautoload/ClassLoader/Pluggable.php
@@ -0,0 +1,155 @@
+classes[$class][$file_path] = TRUE;
+ }
+
+ /**
+ * Register a plugin for a namespace.
+ *
+ * @param string $namespace
+ * The namespace, e.g. "My\Library"
+ * @param string $dir
+ * The deep path, e.g. "../lib/My/Namespace"
+ * @param FinderPlugin_Interface $plugin
+ * The plugin.
+ */
+ public function registerNamespacePathPlugin($logicalPath, $dir, $plugin) {
+ $this->namespaceMap[$logicalPath][$dir] = $plugin;
+ }
+
+ /**
+ * Register a plugin for a prefix.
+ *
+ * @param string $prefix
+ * The prefix, e.g. "My_Library"
+ * @param string $dir
+ * The deep filesystem location, e.g. "../lib/My/Prefix".
+ * @param FinderPlugin_Interface $plugin
+ * The plugin. See
+ */
+ public function registerPrefixPathPlugin($prefix_path_fragment, $dir, $plugin) {
+ $this->prefixMap[$prefix_path_fragment][$dir] = $plugin;
+ }
+
+ /**
+ * Callback for class loading. This will include ("require") the file found.
+ *
+ * @param string $class
+ * The class to load.
+ */
+ function loadClass($class) {
+
+ // Discard initial namespace separator.
+ if ('\\' === $class[0]) {
+ $class = substr($class, 1);
+ }
+
+ // First check if the literal class name is registered.
+ if (!empty($this->classes[$class])) {
+ foreach ($this->classes[$class] as $file => $skipClassExists) {
+ if (is_file($file)) {
+ if ($skipClassExists) {
+ // Assume that the file does indeed define the class.
+ include $file;
+ return TRUE;
+ }
+ else {
+ // Assume that the file MAY define the class.
+ include_once $file;
+ if (class_exists($class, FALSE) || interface_exists($class, FALSE)) {
+ return TRUE;
+ }
+ }
+ }
+ }
+ }
+
+ // Distinguish namespace vs underscore-only.
+ // This is an internal implementation choice, and has nothing to do with
+ // whether or not the PSR-0 spec is correctly implemented.
+ if (FALSE !== $pos = strrpos($class, '\\')) {
+
+ // Loop through positions of '\\', backwards.
+ $logicalBasePath = str_replace('\\', DIRECTORY_SEPARATOR, substr($class, 0, $pos + 1));
+ $relativePath = substr($class, $pos + 1) . '.php';
+ if ($this->mapLoadClass($this->namespaceMap, $class, $logicalBasePath, $relativePath)) {
+ return TRUE;
+ }
+ }
+ else {
+
+ // The class is not within a namespace.
+ // Fall back to the prefix-based finder.
+ $logicalBasePath = str_replace('_', DIRECTORY_SEPARATOR, $class) . '.php';
+ if ($this->mapLoadClass($this->prefixMap, $class, $logicalBasePath, '')) {
+ return TRUE;
+ }
+ }
+ }
+
+ /**
+ * Find the file for a class that in PSR-0 or PEAR would be in
+ * $psr_0_root . '/' . $logicalBasePath . $relativePath
+ *
+ * @param array $map
+ * Either the namespace map or the prefix
+ * @param string $logicalBasePath
+ * First part of the canonical path, with trailing DIRECTORY_SEPARATOR.
+ * @param string $relativePath
+ * Second part of the canonical path, ending with '.php'.
+ *
+ * @return TRUE|NULL
+ * TRUE, if we found the file for the class.
+ * That is, if the $api->suggestFile($file) method returned TRUE one time.
+ * NULL, if we have no more suggestions.
+ */
+ protected function mapLoadClass($map, $class, $logicalBasePath, $relativePath) {
+
+ $path = $logicalBasePath . $relativePath;
+ while (TRUE) {
+ // Check any plugin registered for this fragment.
+ if (!empty($map[$logicalBasePath])) {
+ foreach ($map[$logicalBasePath] as $baseDir => $plugin) {
+ if ($plugin->pluginLoadClass($class, $baseDir, $relativePath)) {
+ return TRUE;
+ }
+ }
+ }
+
+ // Continue with parent fragment.
+ if ('' === $logicalBasePath) {
+ break;
+ }
+ elseif (DIRECTORY_SEPARATOR === $logicalBasePath) {
+ // This happens if a class begins with an underscore.
+ $logicalBasePath = '';
+ $relativePath = $path;
+ }
+ elseif (FALSE !== $pos = strrpos($logicalBasePath, DIRECTORY_SEPARATOR, -2)) {
+ $logicalBasePath = substr($logicalBasePath, 0, $pos + 1);
+ $relativePath = substr($path, $pos + 1);
+ }
+ else {
+ $logicalBasePath = '';
+ $relativePath = $path;
+ }
+ }
+ }
+}
diff --git a/core/vendor/krautoload/src/Krautoload/ClassLoader/Pluggable/Interface.php b/core/vendor/krautoload/src/Krautoload/ClassLoader/Pluggable/Interface.php
new file mode 100644
index 0000000..432824b
--- /dev/null
+++ b/core/vendor/krautoload/src/Krautoload/ClassLoader/Pluggable/Interface.php
@@ -0,0 +1,51 @@
+nsp = $namespace;
+ }
+
+ function getNamespace() {
+ return $this->nsp;
+ }
+
+ function getClassName($relativeClassName) {
+ return $this->nsp . $relativeClassName;
+ }
+}
diff --git a/core/vendor/krautoload/src/Krautoload/InjectedAPI/ClassFileVisitor/CollectCandidateClasses.php b/core/vendor/krautoload/src/Krautoload/InjectedAPI/ClassFileVisitor/CollectCandidateClasses.php
new file mode 100644
index 0000000..a794d30
--- /dev/null
+++ b/core/vendor/krautoload/src/Krautoload/InjectedAPI/ClassFileVisitor/CollectCandidateClasses.php
@@ -0,0 +1,22 @@
+classes);
+ }
+
+ function fileWithClass($file, $relativeClassName) {
+ $this->classes[$this->getClassName($relativeClassName)] = TRUE;
+ }
+
+ function fileWithClassCandidates($file, $relativeClassNames) {
+ foreach ($relativeClassNames as $relativeClassName) {
+ $this->classes[$this->getClassName($relativeClassName)] = TRUE;
+ }
+ }
+}
diff --git a/core/vendor/krautoload/src/Krautoload/InjectedAPI/ClassFileVisitor/CollectExistingClasses.php b/core/vendor/krautoload/src/Krautoload/InjectedAPI/ClassFileVisitor/CollectExistingClasses.php
new file mode 100644
index 0000000..2b264b6
--- /dev/null
+++ b/core/vendor/krautoload/src/Krautoload/InjectedAPI/ClassFileVisitor/CollectExistingClasses.php
@@ -0,0 +1,16 @@
+classes;
+ }
+
+ function confirmedFileWithClass($file, $class) {
+ $this->classes[$class] = $class;
+ }
+}
diff --git a/core/vendor/krautoload/src/Krautoload/InjectedAPI/ClassFileVisitor/IncludeEachAbstract.php b/core/vendor/krautoload/src/Krautoload/InjectedAPI/ClassFileVisitor/IncludeEachAbstract.php
new file mode 100644
index 0000000..45c0a3e
--- /dev/null
+++ b/core/vendor/krautoload/src/Krautoload/InjectedAPI/ClassFileVisitor/IncludeEachAbstract.php
@@ -0,0 +1,41 @@
+getClassName($relativeClassName), FALSE)) {
+ $this->confirmedFileWithClass($file, $class);
+ }
+ elseif (interface_exists($class, FALSE)) {
+ $this->confirmedFileWithInterface($file, $class);
+ }
+ }
+
+ function fileWithClassCandidates($file, $relativeClassNames) {
+ include_once $file;
+ foreach ($relativeClassNames as $relativeClassName) {
+ // Only find classes, not interfaces.
+ if (class_exists($class = $this->getClassName($relativeClassName), FALSE)) {
+ $this->confirmedFileWithClass($file, $class);
+ }
+ elseif (interface_exists($class, FALSE)) {
+ $this->confirmedFileWithInterface($file, $class);
+ }
+ }
+ }
+
+ abstract protected function confirmedFileWithClass($file, $class);
+
+ protected function confirmedFileWithInterface($file, $interface) {
+ // Do nothing by default.
+ }
+}
diff --git a/core/vendor/krautoload/src/Krautoload/InjectedAPI/ClassFileVisitor/Interface.php b/core/vendor/krautoload/src/Krautoload/InjectedAPI/ClassFileVisitor/Interface.php
new file mode 100644
index 0000000..39d63ee
--- /dev/null
+++ b/core/vendor/krautoload/src/Krautoload/InjectedAPI/ClassFileVisitor/Interface.php
@@ -0,0 +1,14 @@
+className = $class_name;
+ }
+
+ /**
+ * Get the name of the class we are looking for.
+ *
+ * @return string
+ * The class we are looking for.
+ */
+ function getClass() {
+ return $this->className;
+ }
+
+ /**
+ * Check if a file exists, considering the full include path.
+ *
+ * @param string $file
+ * The filepath
+ * @return boolean
+ * TRUE, if the file exists somewhere in include path.
+ */
+ protected function fileExistsInIncludePath($file) {
+ if (function_exists('stream_resolve_include_path')) {
+ // Use the PHP 5.3.1+ way of doing this.
+ return (FALSE !== stream_resolve_include_path($file));
+ }
+ elseif ($file{0} === DIRECTORY_SEPARATOR) {
+ // That's an absolute path already.
+ return file_exists($file);
+ }
+ else {
+ // Manually loop all candidate paths.
+ foreach (explode(PATH_SEPARATOR, get_include_path()) as $base_dir) {
+ if (file_exists($base_dir . DIRECTORY_SEPARATOR . $file)) {
+ return TRUE;
+ }
+ }
+ return FALSE;
+ }
+ }
+}
diff --git a/core/vendor/krautoload/src/Krautoload/InjectedAPI/ClassFinder/CollectFiles.php b/core/vendor/krautoload/src/Krautoload/InjectedAPI/ClassFinder/CollectFiles.php
new file mode 100644
index 0000000..adf4532
--- /dev/null
+++ b/core/vendor/krautoload/src/Krautoload/InjectedAPI/ClassFinder/CollectFiles.php
@@ -0,0 +1,144 @@
+files;
+ }
+
+ /**
+ * Suggest a file that, if the file exists,
+ * HAS TO declare the class we are looking for.
+ *
+ * @param string $file
+ * The file that is supposed to declare the class.
+ *
+ * @return boolean
+ * TRUE, if the file exists.
+ * FALSE, otherwise.
+ */
+ function guessFile($file) {
+ if (is_file($file)) {
+ $this->files[$file] = TRUE;
+ return $file;
+ }
+ return FALSE;
+ }
+
+ /**
+ * Suggest a file that, if the file exists,
+ * MAY declare the class we are looking for.
+ *
+ * @param string $file
+ * The file that is supposed to declare the class.
+ *
+ * @return boolean
+ * Always FALSE, because we had no chance to check whether the file actually
+ * defines the class.
+ */
+ function guessFileCandidate($file) {
+ if (is_file($file)) {
+ $this->files[$file] = FALSE;
+ }
+ return FALSE;
+ }
+
+ /**
+ * Suggest a file that HAS TO declare the class we are looking for.
+ *
+ * Unlike guessFile(), claimFile() being called means that the caller is sure
+ * that the file does exist. Thus, we can skip the is_file() check, saving a
+ * few nanoseconds.
+ *
+ * This is useful if a plugin already did the is_file() check by itself.
+ *
+ * @param string $file
+ * The file that is supposed to declare the class.
+ *
+ * @return boolean
+ * Always TRUE, because further candidates are not interesting.
+ */
+ function claimFile($file) {
+ $this->files[$file] = TRUE;
+ return TRUE;
+ }
+
+ /**
+ * Suggest a file that MAY declare the class we are looking for.
+ *
+ * Unlike guessFile(), claimFile() being called means that the caller is sure
+ * that the file does exist. Thus, we can skip the is_file() check, saving a
+ * few nanoseconds.
+ *
+ * This is useful if a plugin already did the is_file() check by itself.
+ *
+ * @param string $file
+ * The file that is supposed to declare the class.
+ *
+ * @return boolean
+ * Always FALSE, because we had no chance to check whether the file actually
+ * defines the class.
+ */
+ function claimFileCandidate($file) {
+ $this->files[$file] = FALSE;
+ return FALSE;
+ }
+
+ /**
+ * Suggest a file that, if the file exists,
+ * HAS TO declare the class we are looking for.
+ *
+ * Unlike guessFile(), this one checks the full PHP include path.
+ *
+ * @param string $file
+ * The file that is supposed to declare the class.
+ *
+ * @return boolean
+ * TRUE, if the file exists.
+ * FALSE, otherwise.
+ */
+ function guessFile_checkIncludePath($file) {
+ if ($this->fileExistsInIncludePath($file)) {
+ $this->files[$file] = TRUE;
+ return TRUE;
+ }
+ return FALSE;
+ }
+
+ /**
+ * Suggest a file that, if the file exists,
+ * MAY declare the class we are looking for.
+ *
+ * Unlike guessFile(), this one checks the full PHP include path.
+ *
+ * @param string $file
+ * The file that is supposed to declare the class.
+ *
+ * @return boolean
+ * Always FALSE, because we had no chance to check whether the file actually
+ * defines the class.
+ */
+ function guessFileCandidate_checkIncludePath($file) {
+ if ($this->fileExistsInIncludePath($file)) {
+ $this->files[$file] = FALSE;
+ }
+ return FALSE;
+ }
+}
diff --git a/core/vendor/krautoload/src/Krautoload/InjectedAPI/ClassFinder/FindExistingClass.php b/core/vendor/krautoload/src/Krautoload/InjectedAPI/ClassFinder/FindExistingClass.php
new file mode 100644
index 0000000..2170393
--- /dev/null
+++ b/core/vendor/krautoload/src/Krautoload/InjectedAPI/ClassFinder/FindExistingClass.php
@@ -0,0 +1,115 @@
+fileExistsInIncludePath($file);
+ }
+
+ /**
+ * Suggest a file that, if the file exists,
+ * MAY declare the class we are looking for.
+ * Include that file, if it exists.
+ *
+ * Unlike guessFile(), this one checks the full PHP include path.
+ *
+ * @param string $file
+ * The file that is supposed to declare the class.
+ *
+ * @return boolean
+ * TRUE, if the file exists and the class exists after file inclusion.
+ * FALSE, otherwise.
+ */
+ function guessFileCandidate_checkIncludePath($file) {
+ return $this->fileExistsInIncludePath($file);
+ }
+}
diff --git a/core/vendor/krautoload/src/Krautoload/InjectedAPI/ClassFinder/Interface.php b/core/vendor/krautoload/src/Krautoload/InjectedAPI/ClassFinder/Interface.php
new file mode 100644
index 0000000..df1e90a
--- /dev/null
+++ b/core/vendor/krautoload/src/Krautoload/InjectedAPI/ClassFinder/Interface.php
@@ -0,0 +1,111 @@
+className, FALSE) || interface_exists($this->className, FALSE);
+ }
+ return FALSE;
+ }
+
+ /**
+ * Suggest a file that HAS TO declare the class we are looking for.
+ * Include that file.
+ *
+ * Unlike guessFile(), claimFile() being called means that the caller is sure
+ * that the file does exist. Thus, we can skip the is_file() check, saving a
+ * few nanoseconds.
+ *
+ * This is useful if a plugin already did the is_file() check by itself.
+ *
+ * @param string $file
+ * The file that is supposed to declare the class.
+ *
+ * @return boolean
+ * Always TRUE, because we assume the file does exist and does define the
+ * class.
+ */
+ function claimFile($file) {
+ require $file;
+ return TRUE;
+ }
+
+ /**
+ * Suggest a file that MAY declare the class we are looking for.
+ * Include that file.
+ *
+ * Unlike guessFile(), claimFile() being called means that the caller is sure
+ * that the file does exist. Thus, we can skip the is_file() check, saving a
+ * few nanoseconds.
+ *
+ * This is useful if a plugin already did the is_file() check by itself.
+ *
+ * @param string $file
+ * The file that is supposed to declare the class.
+ *
+ * @return boolean
+ * TRUE, if the class exists after file inclusion.
+ * FALSE, otherwise
+ */
+ function claimFileCandidate($file) {
+ require_once $file;
+ return class_exists($this->className, FALSE) || interface_exists($this->className, FALSE);
+ }
+
+ /**
+ * Suggest a file that, if the file exists,
+ * HAS TO declare the class we are looking for.
+ * Include that file, if it exists.
+ *
+ * Unlike guessFile(), this one checks the full PHP include path.
+ *
+ * @param string $file
+ * The file that is supposed to declare the class.
+ *
+ * @return boolean
+ * TRUE, if the file exists.
+ * FALSE, otherwise.
+ */
+ function guessFile_checkIncludePath($file) {
+ if ($this->fileExistsInIncludePath($file)) {
+ include $file;
+ return TRUE;
+ }
+ return FALSE;
+ }
+
+ /**
+ * Suggest a file that, if the file exists,
+ * MAY declare the class we are looking for.
+ * Include that file, if it exists.
+ *
+ * Unlike guessFile(), this one checks the full PHP include path.
+ *
+ * @param string $file
+ * The file that is supposed to declare the class.
+ *
+ * @return boolean
+ * TRUE, if the file exists and the class exists after file inclusion.
+ * FALSE, otherwise.
+ */
+ function guessFileCandidate_checkIncludePath($file) {
+ if ($this->fileExistsInIncludePath($file)) {
+ include_once $file;
+ return class_exists($this->className, FALSE) || interface_exists($this->className, FALSE);
+ }
+ return FALSE;
+ }
+}
diff --git a/core/vendor/krautoload/src/Krautoload/InjectedAPI/NamespaceVisitor/Interface.php b/core/vendor/krautoload/src/Krautoload/InjectedAPI/NamespaceVisitor/Interface.php
new file mode 100644
index 0000000..0dcbbc4
--- /dev/null
+++ b/core/vendor/krautoload/src/Krautoload/InjectedAPI/NamespaceVisitor/Interface.php
@@ -0,0 +1,10 @@
+api = $api;
+ }
+
+ /**
+ * @param string $namespace
+ */
+ function setNamespace($namespace) {
+ $this->api->setNamespace($namespace);
+ }
+}
diff --git a/core/vendor/krautoload/src/Krautoload/InjectedAPI/NamespaceVisitor/ScanNamespace.php b/core/vendor/krautoload/src/Krautoload/InjectedAPI/NamespaceVisitor/ScanNamespace.php
new file mode 100644
index 0000000..5d6ce9a
--- /dev/null
+++ b/core/vendor/krautoload/src/Krautoload/InjectedAPI/NamespaceVisitor/ScanNamespace.php
@@ -0,0 +1,10 @@
+pluginScanNamespace($this->api, $baseDir, $relativePath);
+ }
+}
diff --git a/core/vendor/krautoload/src/Krautoload/InjectedAPI/NamespaceVisitor/ScanRecursive.php b/core/vendor/krautoload/src/Krautoload/InjectedAPI/NamespaceVisitor/ScanRecursive.php
new file mode 100644
index 0000000..bdb2a44
--- /dev/null
+++ b/core/vendor/krautoload/src/Krautoload/InjectedAPI/NamespaceVisitor/ScanRecursive.php
@@ -0,0 +1,10 @@
+pluginScanRecursive($this->api, $baseDir, $relativePath);
+ }
+}
diff --git a/core/vendor/krautoload/src/Krautoload/NamespacePathPlugin/Interface.php b/core/vendor/krautoload/src/Krautoload/NamespacePathPlugin/Interface.php
new file mode 100644
index 0000000..de9cd58
--- /dev/null
+++ b/core/vendor/krautoload/src/Krautoload/NamespacePathPlugin/Interface.php
@@ -0,0 +1,46 @@
+getClass() will return the FQCN of the class we are looking for.
+ * @param string $phyicalBasePath
+ * Physical base path associated with the logical base path.
+ * @param string $relativePath
+ * Second part of the logical path built from the FQCN.
+ *
+ * @return boolean
+ * TRUE, if the $api did return TRUE for one candidate file.
+ */
+ function pluginFindFile($api, $baseDir, $relativePath);
+
+ /**
+ * Shortcut to directly load the class, with no $api in the way.
+ *
+ * @param string $class
+ * The class that is to be loaded.
+ * @param string $phyicalBasePath
+ * Physical base path associated with the logical base path.
+ * @param string $relativePath
+ * Second part of the logical path built from the FQCN.
+ *
+ * @return boolean
+ * TRUE, if the class was loaded.
+ */
+ function pluginLoadClass($class, $baseDir, $relativePath);
+
+ function pluginScanNamespace($api, $baseDir, $relativePath);
+
+ function pluginScanRecursive($api, $baseDir, $relativePath);
+}
diff --git a/core/vendor/krautoload/src/Krautoload/NamespacePathPlugin/LegacyLiteral.php b/core/vendor/krautoload/src/Krautoload/NamespacePathPlugin/LegacyLiteral.php
new file mode 100644
index 0000000..e69de29
diff --git a/core/vendor/krautoload/src/Krautoload/NamespacePathPlugin/PSRX.php b/core/vendor/krautoload/src/Krautoload/NamespacePathPlugin/PSRX.php
new file mode 100644
index 0000000..ae3abe1
--- /dev/null
+++ b/core/vendor/krautoload/src/Krautoload/NamespacePathPlugin/PSRX.php
@@ -0,0 +1,60 @@
+guessFile($baseDir . $relativePath);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ function pluginLoadClass($class, $baseDir, $relativePath) {
+ if (is_file($file = $baseDir . $relativePath)) {
+ include $file;
+ return TRUE;
+ }
+ }
+
+ /**
+ * @inheritdoc
+ */
+ function pluginScanNamespace($api, $baseDir, $relativePath) {
+ if (is_dir($dir = $baseDir . $relativePath)) {
+ foreach (new \DirectoryIterator($dir) as $fileinfo) {
+ // @todo With PHP 5.3.6, this could be $fileinfo->getExtension().
+ if (pathinfo($fileinfo->getFilename(), PATHINFO_EXTENSION) == 'php') {
+ $api->fileWithClass($fileinfo->getPathname(), '\\' . $fileinfo->getBasename('.php'));
+ }
+ }
+ }
+ }
+
+ /**
+ * @inheritdoc
+ */
+ function pluginScanRecursive($api, $baseDir, $relativePath) {
+ if (is_dir($dir = $baseDir . $relativePath)) {
+ $this->doScanRecursive($api, $dir);
+ }
+ }
+
+ protected function doScanRecursive($api, $dir, $relativeNamespace = '') {
+ foreach (new \DirectoryIterator($dir) as $fileinfo) {
+ // @todo With PHP 5.3.6, this could be $fileinfo->getExtension().
+ if (pathinfo($fileinfo->getFilename(), PATHINFO_EXTENSION) == 'php') {
+ $relativeClassName = $relativeNamespace . '\\' . $fileinfo->getBasename('.php');
+ $api->fileWithClass($fileinfo->getPathname(), $relativeClassName);
+ }
+ elseif (!$fileinfo->isDot() && $fileinfo->isDir()) {
+ $relativeSubNamespace = $relativeNamespace . '\\' . $fileinfo->getFilename();
+ $this->doScanRecursive($api, $fileinfo->getPathname(), $relativeSubNamespace);
+ }
+ }
+ }
+}
diff --git a/core/vendor/krautoload/src/Krautoload/NamespacePathPlugin/ShallowPSR0.php b/core/vendor/krautoload/src/Krautoload/NamespacePathPlugin/ShallowPSR0.php
new file mode 100644
index 0000000..e6a04fb
--- /dev/null
+++ b/core/vendor/krautoload/src/Krautoload/NamespacePathPlugin/ShallowPSR0.php
@@ -0,0 +1,88 @@
+guessFileCandidate($baseDir . $relativePath)) {
+ return TRUE;
+ }
+ }
+
+ function pluginLoadClass($class, $baseDir, $relativePath) {
+
+ // Replace the underscores after the last directory separator.
+ if (FALSE !== $pos = strrpos($relativePath, DIRECTORY_SEPARATOR)) {
+ $relativePath = substr($relativePath, 0, $pos) . str_replace('_', DIRECTORY_SEPARATOR, substr($relativePath, $pos));
+ }
+ else {
+ $relativePath = str_replace('_', DIRECTORY_SEPARATOR, $relativePath);
+ }
+
+ // Check whether the file exists.
+ if (is_file($file = $baseDir . $relativePath)) {
+ // We don't know if the file defines the class,
+ // and whether it was already included.
+ include_once $file;
+ return class_exists($class, FALSE) || interface_exists($class, FALSE);
+ }
+ }
+
+ function pluginScanNamespace($api, $baseDir, $relativePath) {
+ if (is_dir($dir = $baseDir . $relativePath)) {
+ foreach (new \DirectoryIterator($dir) as $fileinfo) {
+ // @todo With PHP 5.3.6, this could be $fileinfo->getExtension().
+ if (pathinfo($fileinfo->getFilename(), PATHINFO_EXTENSION) == 'php') {
+ $api->fileWithClassCandidates($fileinfo->getPathname(), array('\\' . $fileinfo->getBasename('.php')));
+ }
+ }
+ }
+ }
+
+ function pluginScanRecursive($api, $baseDir, $relativePath) {
+ if (is_array($baseDir)) {
+ throw new \Exception("Base dir must not be array.");
+ }
+ if (is_array($relativePath)) {
+ throw new \Exception("Relative path must not be array.");
+ }
+ if (is_dir($dir = $baseDir . $relativePath)) {
+ $this->doScanRecursive($api, $dir);
+ }
+ }
+
+ protected function doScanRecursive($api, $dir, $relativeNamespaces = array('\\')) {
+ foreach (new \DirectoryIterator($dir) as $fileinfo) {
+ // @todo With PHP 5.3.6, this could be $fileinfo->getExtension().
+ if (pathinfo($fileinfo->getFilename(), PATHINFO_EXTENSION) == 'php') {
+ $relativeClassNames = array();
+ foreach ($relativeNamespaces as $relativeNamespace) {
+ $relativeClassNames[] = $relativeNamespace . $fileinfo->getBasename('.php');
+ }
+ if (!empty($relativeClassNames)) {
+ $api->fileWithClassCandidates($fileinfo->getPathname(), $relativeClassNames);
+ }
+ }
+ elseif (!$fileinfo->isDot() && $fileinfo->isDir()) {
+ $relativeSubNamespaces = array($relativeNamespaces[0] . $fileinfo->getFilename() . '\\');
+ foreach ($relativeNamespaces as $relativeNamespace) {
+ $relativeSubNamespaces[] = $relativeNamespace . $fileinfo->getFilename() . '_';
+ }
+ $this->doScanRecursive($api, $fileinfo->getPathname(), $relativeSubNamespaces);
+ }
+ }
+ }
+}
diff --git a/core/vendor/krautoload/src/Krautoload/NamespacePathPlugin/ShallowPSR0/AllUnderscore.php b/core/vendor/krautoload/src/Krautoload/NamespacePathPlugin/ShallowPSR0/AllUnderscore.php
new file mode 100644
index 0000000..8e83497
--- /dev/null
+++ b/core/vendor/krautoload/src/Krautoload/NamespacePathPlugin/ShallowPSR0/AllUnderscore.php
@@ -0,0 +1,98 @@
+guessFile($baseDir . $relativePath)) {
+ return TRUE;
+ }
+ }
+
+ function pluginLoadClass($class, $baseDir, $relativePath) {
+
+ // Check for underscores after the last directory separator.
+ // In other words: Check for the last underscore, and whether that is
+ // followed by a directory separator.
+ if (FALSE !== strrpos($relativePath, DIRECTORY_SEPARATOR)) {
+ // Ignore this class.
+ return;
+ }
+ // We are safe, the class is not in a sub-namespace.
+ // So we can proceed with class loading.
+
+ // Replace all underscores in the suffix part.
+ $relativePath = str_replace('_', DIRECTORY_SEPARATOR, $relativePath);
+
+ // We "guess", because we don't know whether the file exists.
+ if (is_file($file = $baseDir . $relativePath)) {
+ include $file;
+ return TRUE;
+ }
+ }
+
+ function pluginScanNamespace($api, $baseDir, $relativePath) {
+ // Check that $namespace is NOT a sub-namespace of the registered namespace.
+ if ('' === $relativePath) {
+ if (is_dir($baseDir)) {
+ foreach (new \DirectoryIterator($baseDir) as $fileinfo) {
+ // @todo With PHP 5.3.6, this could be $fileinfo->getExtension().
+ if (pathinfo($fileinfo->getFilename(), PATHINFO_EXTENSION) == 'php') {
+ $api->fileWithClass($fileinfo->getPathname(), '\\' . $fileinfo->getBasename('.php'));
+ }
+ }
+ }
+ }
+ }
+
+ function pluginScanRecursive($api, $baseDir, $relativePath) {
+ // Check that $namespace is NOT a sub-namespace of the registered namespace.
+ if ('' === $relativePath) {
+ if (is_dir($baseDir)) {
+ $this->doScanRecursive($api, $baseDir);
+ }
+ }
+ }
+
+ protected function doScanRecursive($api, $dir, $relativeNamespace = '\\') {
+ foreach (new \DirectoryIterator($dir) as $fileinfo) {
+ // @todo With PHP 5.3.6, this could be $fileinfo->getExtension().
+ if (pathinfo($fileinfo->getFilename(), PATHINFO_EXTENSION) == 'php') {
+ $api->fileWithClass($fileinfo->getPathname(), $relativeNamespace . $fileinfo->getBasename('.php'));
+ }
+ elseif (!$fileinfo->isDot() && $fileinfo->isDir()) {
+ $relativeSubNamespace = $relativeNamespace . $fileinfo->getFilename() . '_';
+ $this->doScanRecursive($api, $fileinfo->getPathname(), $relativeSubNamespace);
+ }
+ }
+ }
+}
diff --git a/core/vendor/krautoload/src/Krautoload/NamespacePathPlugin/ShallowPSR0/MapLeak.php b/core/vendor/krautoload/src/Krautoload/NamespacePathPlugin/ShallowPSR0/MapLeak.php
new file mode 100644
index 0000000..3ada5d6
--- /dev/null
+++ b/core/vendor/krautoload/src/Krautoload/NamespacePathPlugin/ShallowPSR0/MapLeak.php
@@ -0,0 +1,35 @@
+relativePrefixes[$relativePrefix] = strlen($relativePrefix);
+ }
+
+ function pluginFindFile($api, $baseDir, $relativePath) {
+ if ($this->checkPrefix($relativePath)) {
+ return parent::pluginFindFile($api, $baseDir, $relativePath);
+ }
+ }
+
+ function pluginLoadClass($class, $baseDir, $relativePath) {
+ if ($this->checkPrefix($relativePath)) {
+ return parent::pluginLoadClass($class, $baseDir, $relativePath);
+ }
+ }
+
+ protected function checkPrefix($relativePath) {
+ foreach ($this->relativePrefixes as $relativePrefix => $length) {
+ if (!strncmp($relativePath, $relativePrefix, $length) {
+ return TRUE;
+ }
+ }
+ }
+}
diff --git a/core/vendor/krautoload/src/Krautoload/NamespacePathPlugin/ShallowPSR0/NoConflict.php b/core/vendor/krautoload/src/Krautoload/NamespacePathPlugin/ShallowPSR0/NoConflict.php
new file mode 100644
index 0000000..a926dbf
--- /dev/null
+++ b/core/vendor/krautoload/src/Krautoload/NamespacePathPlugin/ShallowPSR0/NoConflict.php
@@ -0,0 +1,48 @@
+guessFile($baseDir . $relativePath)) {
+ return TRUE;
+ }
+ }
+
+ function pluginLoadClass($class, $baseDir, $relativePath) {
+ // We need to replace the underscores after the last directory separator.
+ if (FALSE !== $pos = strrpos($relativePath, DIRECTORY_SEPARATOR)) {
+ $relativePath = substr($relativePath, 0, $pos) . str_replace('_', DIRECTORY_SEPARATOR, substr($relativePath, $pos));
+ }
+ else {
+ $relativePath = str_replace('_', DIRECTORY_SEPARATOR, $relativePath);
+ }
+ // We don't know if the file exists.
+ if (is_file($file = $baseDir . $relativePath)) {
+ // We assume that the file defines the class.
+ include $file;
+ return TRUE;
+ }
+ }
+}
diff --git a/core/vendor/krautoload/src/Krautoload/NamespacePathPlugin/ShallowPSR0/NoUnderscore.php b/core/vendor/krautoload/src/Krautoload/NamespacePathPlugin/ShallowPSR0/NoUnderscore.php
new file mode 100644
index 0000000..a3a4b22
--- /dev/null
+++ b/core/vendor/krautoload/src/Krautoload/NamespacePathPlugin/ShallowPSR0/NoUnderscore.php
@@ -0,0 +1,28 @@
+setNamespace($namespace);
+
+ while (TRUE) {
+
+ // Check any plugin registered for this fragment.
+ if (!empty($this->namespaceMap[$logicalBasePath])) {
+ foreach ($this->namespaceMap[$logicalBasePath] as $baseDir => $plugin) {
+ $api->namespaceDirectoryPlugin($baseDir, $relativePath, $plugin);
+ }
+ }
+
+ // Continue with parent fragment.
+ if ('' === $logicalBasePath) {
+ break;
+ }
+ elseif (DIRECTORY_SEPARATOR === $logicalBasePath) {
+ // This happens if a class begins with an underscore.
+ $logicalBasePath = '';
+ $relativePath = $logicalPath;
+ }
+ elseif (FALSE !== $pos = strrpos($logicalBasePath, DIRECTORY_SEPARATOR, -2)) {
+ $logicalBasePath = substr($logicalBasePath, 0, $pos + 1);
+ $relativePath = substr($logicalPath, $pos + 1);
+ }
+ else {
+ $logicalBasePath = '';
+ $relativePath = $logicalPath;
+ }
+ }
+ }
+
+ /**
+ * @param InjectedAPI_NamespaceVisitor_Interface $api
+ * @param array $namespaces
+ */
+ public function apiVisitNamespaces($api, $namespaces) {
+ foreach ($namespaces as $namespace) {
+ $this->apiVisitNamespace($api, $namespace);
+ }
+ }
+
+ public function apiVisitBaseNamespaces($api) {
+ foreach ($this->namespaceMap as $logicalBasePath => $plugins) {
+ $namespace = str_replace(DIRECTORY_SEPARATOR, '\\', substr($logicalBasePath, 0, -1));
+ $api->setNamespace($namespace);
+ foreach ($plugins as $baseDir => $plugin) {
+ $api->namespaceDirectoryPlugin($baseDir, '', $plugin);
+ }
+ }
+ }
+}
diff --git a/core/vendor/krautoload/src/Krautoload/PrefixPathPlugin/Interface.php b/core/vendor/krautoload/src/Krautoload/PrefixPathPlugin/Interface.php
new file mode 100644
index 0000000..fd29b26
--- /dev/null
+++ b/core/vendor/krautoload/src/Krautoload/PrefixPathPlugin/Interface.php
@@ -0,0 +1,41 @@
+getClass() will return the FQCN of the class we are looking for.
+ * @param string $baseDir
+ * Physical base path associated with the logical base path.
+ * @param string $relativePath
+ * Second part of the logical path built from the FQCN.
+ *
+ * @return boolean
+ * TRUE, if the $api did return TRUE for one candidate file.
+ */
+ function pluginFindFile($api, $baseDir, $relativePath);
+
+ /**
+ * Shortcut to directly load the class, with no $api in the way.
+ *
+ * @param string $class
+ * The class that is to be loaded.
+ * @param string $baseDir
+ * Physical base path associated with the logical base path.
+ * @param string $relativePath
+ * Second part of the logical path built from the FQCN.
+ *
+ * @return boolean
+ * TRUE, if the class was loaded.
+ */
+ function pluginLoadClass($class, $baseDir, $relativePath);
+}
diff --git a/core/vendor/krautoload/src/Krautoload/PrefixPathPlugin/ShallowPEAR.php b/core/vendor/krautoload/src/Krautoload/PrefixPathPlugin/ShallowPEAR.php
new file mode 100644
index 0000000..dd1417f
--- /dev/null
+++ b/core/vendor/krautoload/src/Krautoload/PrefixPathPlugin/ShallowPEAR.php
@@ -0,0 +1,17 @@
+guessFile($baseDir . $relativePath);
+ }
+
+ function pluginLoadClass($class, $baseDir, $relativePath) {
+ if (is_file($file = $baseDir . $relativePath)) {
+ include $file;
+ return TRUE;
+ }
+ }
+}
diff --git a/core/vendor/krautoload/src/Krautoload/PrefixPathPlugin/ShallowPEAR/MapLeak.php b/core/vendor/krautoload/src/Krautoload/PrefixPathPlugin/ShallowPEAR/MapLeak.php
new file mode 100644
index 0000000..4d5f557
--- /dev/null
+++ b/core/vendor/krautoload/src/Krautoload/PrefixPathPlugin/ShallowPEAR/MapLeak.php
@@ -0,0 +1,35 @@
+relativePrefixes[$relativePrefix] = strlen($relativePrefix);
+ }
+
+ function pluginFindFile($api, $baseDir, $relativePath) {
+ if ($this->checkPrefix($relativePath)) {
+ return $api->guessFile($baseDir . $relativePath);
+ }
+ }
+
+ function pluginLoadClass($class, $baseDir, $relativePath) {
+ if ($this->checkPrefix($relativePath)) {
+ if (is_file($file = $baseDir . $relativePath)) {
+ include $file;
+ return TRUE;
+ }
+ }
+ }
+
+ protected function checkPrefix($relativePath) {
+ foreach ($this->relativePrefixes as $relativePrefix => $length) {
+ if (!strncmp($relativePath, $relativePrefix, $length) {
+ return TRUE;
+ }
+ }
+ }
+}
diff --git a/core/vendor/krautoload/src/Krautoload/PrefixPathPlugin/ShallowPEAR/Uncertain.php b/core/vendor/krautoload/src/Krautoload/PrefixPathPlugin/ShallowPEAR/Uncertain.php
new file mode 100644
index 0000000..d322792
--- /dev/null
+++ b/core/vendor/krautoload/src/Krautoload/PrefixPathPlugin/ShallowPEAR/Uncertain.php
@@ -0,0 +1,17 @@
+guessFileCandidate($baseDir . $relativePath);
+ }
+
+ function pluginLoadClass($class, $baseDir, $relativePath) {
+ if (is_file($file = $baseDir . $relativePath)) {
+ include_once $file;
+ return class_exists($class, FALSE) || interface_exists($class, FALSE);
+ }
+ }
+}
diff --git a/core/vendor/krautoload/src/Krautoload/RegistrationHub.php b/core/vendor/krautoload/src/Krautoload/RegistrationHub.php
new file mode 100644
index 0000000..2f07caa
--- /dev/null
+++ b/core/vendor/krautoload/src/Krautoload/RegistrationHub.php
@@ -0,0 +1,253 @@
+finder = $finder;
+ $this->plugins['ShallowPEAR'] = new PrefixPathPlugin_ShallowPEAR();
+ $this->plugins['ShallowPSR0'] = new NamespacePathPlugin_ShallowPSR0();
+ $this->plugins['PSRX'] = new NamespacePathPlugin_PSRX();
+ }
+
+ function getFinder() {
+ return $this->finder;
+ }
+
+ /**
+ * @param callback $callback
+ * Registration callback, which takes as an argument the registration hub.
+ */
+ function krautoloadCallback($callback) {
+ call_user_func($callback, $this);
+ }
+
+ /**
+ * @param string $file
+ * Path to a PHP file that, on inclusion, returns a registration callback.
+ */
+ function krautoloadFile($file) {
+ $callback = require $file;
+ call_user_func($callback, $this);
+ }
+
+ /**
+ * @param string $dir
+ * Vendor directory of a project using composer.
+ * This allows to use Krautoload for composer-based PHP projects.
+ */
+ function composerVendorDir($dir) {
+ if (is_file($dir . '/composer/autoload_namespaces.php')) {
+ $namespaces = include $dir . '/composer/autoload_namespaces.php';
+ $this->addPrefixesPSR0($namespaces);
+ }
+ if (is_file($dir . '/composer/autoload_classmap.php')) {
+ $class_map = include $dir . '/composer/autoload_classmap.php';
+ foreach ($class_map as $class => $file) {
+ $this->finder->registerClass($class, $file);
+ }
+ }
+ }
+
+ /**
+ * Adds prefixes.
+ *
+ * @param array $prefixes
+ * Prefixes to add
+ */
+ function addPrefixesPSR0(array $prefixes) {
+ foreach ($prefixes as $prefix => $rootDirs) {
+ $this->addPrefixPSR0($prefix, $rootDirs);
+ }
+ }
+
+ /**
+ * Registers a set of classes
+ *
+ * @param string $prefix
+ * The classes prefix
+ * @param array|string $rootDirs
+ * The location(s) of the classes
+ */
+ function addPrefixPSR0($prefix, $rootDirs) {
+
+ if (!$prefix) {
+ // We consider this as a "fallback".
+ }
+ elseif ('\\' === substr($prefix, -1)) {
+ // We assume that $prefix is meant as a namespace,
+ // and the paths are PSR-0 directories.
+ $namespace = substr($prefix, 0, -1);
+ foreach ((array) $rootDirs as $rootDir) {
+ $this->addNamespacePSR0($namespace, $rootDir);
+ }
+ }
+ elseif (FALSE !== strrpos($prefix, '\\')) {
+ // We assume that $prefix is meant as a namespace,
+ // and the paths are PSR-0 directories.
+ $namespace = $prefix;
+ foreach ((array) $rootDirs as $rootDir) {
+ $this->addNamespacePSR0($namespace, $rootDir);
+ $this->addClassFile($prefix, $rootDir . '.php');
+ }
+ // TODO:
+ // Register special plugins to cover other FQCNs
+ // that happen to begin with with the prefix.
+ }
+ elseif ('_' === substr($prefix, -1)) {
+ // We assume that $prefix is meant as a PEAR prefix,
+ // and the paths are PSR-0 directories.
+ foreach ((array) $rootDirs as $rootDir) {
+ $this->addPrefixPEAR(substr($prefix, 0, -1), $rootDir);
+ }
+ // TODO:
+ // Register special plugins to cover other FQCNs
+ // that happen to begin with with the prefix.
+ }
+ else {
+ // We assume that $prefix is meant as a PEAR prefix OR as namespace,
+ // and the paths are PSR-0 or PEAR directories.
+ foreach ((array) $rootDirs as $rootDir) {
+ $this->addNamespacePSR0($prefix, $rootDir);
+ $this->addPrefixPEAR($prefix, $rootDir);
+ $this->addClassFile($prefix, $rootDir . '.php');
+ }
+ // TODO:
+ // Register special plugins to cover other FQCNs
+ // that happen to begin with with the prefix.
+ }
+ }
+
+ function addNamespacesPSR0($namespaces) {
+ foreach ($namespaces as $namespace => $rootDirs) {
+ $this->addNamespacePSR0($namespace, $rootDirs);
+ }
+ }
+
+ function addNamespacePSR0($namespace, $rootDirs) {
+ if (empty($namespace)) {
+ foreach ((array) $rootDirs as $rootDir) {
+ $rootDir = strlen($rootDir) ? $rootDir . DIRECTORY_SEPARATOR : '';
+ $this->finder->registerNamespacePathPlugin('', $rootDir, $this->plugins['ShallowPSR0']);
+ $this->finder->registerPrefixPathPlugin('', $rootDir, $this->plugins['ShallowPEAR']);
+ }
+ }
+ else {
+ $logicalBasePath = $this->namespaceLogicalPath($namespace);
+ foreach ((array) $rootDirs as $rootDir) {
+ $baseDir = strlen($rootDir) ? $rootDir . DIRECTORY_SEPARATOR : '';
+ $baseDir .= $logicalBasePath;
+ $this->finder->registerNamespacePathPlugin($logicalBasePath, $baseDir, $this->plugins['ShallowPSR0']);
+ }
+ }
+ }
+
+ function addNamespacesShallowPSR0($namespaces) {
+ foreach ($namespaces as $namespace => $baseDirs) {
+ $this->addNamespaceShallowPSR0($namespace, $baseDirs);
+ }
+ }
+
+ function addNamespaceShallowPSR0($namespace, $baseDirs) {
+ $logicalBasePath = $this->namespaceLogicalPath($namespace);
+ foreach ((array) $baseDirs as $baseDir) {
+ $baseDir = strlen($baseDir) ? $baseDir . DIRECTORY_SEPARATOR : '';
+ $this->finder->registerNamespacePathPlugin($logicalBasePath, $baseDir, $this->plugins['ShallowPSR0']);
+ }
+ }
+
+ function addPrefixesPEAR($prefixes) {
+ foreach ($prefixes as $prefix => $rootDirs) {
+ $this->addPrefixPEAR($prefix, $rootDirs);
+ }
+ }
+
+ function addPrefixPEAR($prefix, $rootDirs) {
+ $logicalBasePath = $this->prefixLogicalPath($prefix);
+ foreach ((array) $rootDirs as $rootDir) {
+ $baseDir = strlen($rootDir) ? $rootDir . DIRECTORY_SEPARATOR : '';
+ $baseDir .= $logicalBasePath;
+ $this->finder->registerPrefixPathPlugin($logicalBasePath, $baseDir, $this->plugins['ShallowPEAR']);
+ }
+ }
+
+ function addPrefixesShallowPEAR($prefixes) {
+ foreach ($prefixes as $prefix => $baseDirs) {
+ $this->addPrefixPEAR($prefix, $baseDirs);
+ }
+ }
+
+ function addPrefixShallowPEAR($prefix, $baseDirs) {
+ $logicalBasePath = $this->prefixLogicalPath($prefix);
+ foreach ((array) $baseDirs as $baseDir) {
+ $baseDir = strlen($baseDir) ? $baseDir . DIRECTORY_SEPARATOR : '';
+ $this->finder->registerPrefixPathPlugin($logicalBasePath, $baseDir, $this->plugins['ShallowPEAR']);
+ }
+ }
+
+ function addNamespacesPSRX($namespaces) {
+ foreach ($namespaces as $namespace => $baseDirs) {
+ $this->addNamespacePSRX($namespace, $baseDirs);
+ }
+ }
+
+ function addNamespacePSRX($namespace, $baseDirs) {
+ $logicalBasePath = $this->namespaceLogicalPath($namespace);
+ foreach ((array) $baseDirs as $baseDir) {
+ $baseDir = strlen($baseDir) ? $baseDir . DIRECTORY_SEPARATOR : '';
+ $this->finder->registerNamespacePathPlugin($logicalBasePath, $baseDir, $this->plugins['PSRX']);
+ }
+ }
+
+ function addClassFile($class, $file) {
+ $this->finder->registerClass($class, $file);
+ }
+
+ function buildSearchableNamespaces($namespaces = array()) {
+ $searchable = new SearchableNamespaces_Default($this->finder);
+ $searchable->addNamespaces($namespaces);
+ return $searchable;
+ }
+
+ /**
+ * Replace the namespace separator with directory separator.
+ *
+ * @param string $namespace
+ * Namespace without trailing namespace separator.
+ *
+ * @return string
+ * Path fragment representing the namespace, with trailing DIRECTORY_SEPARATOR.
+ */
+ protected function namespaceLogicalPath($namespace) {
+ return
+ strlen($namespace)
+ ? str_replace('\\', DIRECTORY_SEPARATOR, $namespace . '\\')
+ : ''
+ ;
+ }
+
+ /**
+ * Convert the underscores of a prefix into directory separators.
+ *
+ * @param string $prefix
+ * Prefix, without trailing underscore.
+ *
+ * @return string
+ * Path fragment representing the prefix, with trailing DIRECTORY_SEPARATOR.
+ */
+ protected function prefixLogicalPath($prefix) {
+ return
+ strlen($prefix)
+ ? str_replace('_', DIRECTORY_SEPARATOR, $prefix . '_')
+ : ''
+ ;
+ }
+}
diff --git a/core/vendor/krautoload/src/Krautoload/SearchableNamespaces/Default.php b/core/vendor/krautoload/src/Krautoload/SearchableNamespaces/Default.php
new file mode 100644
index 0000000..a74f9cd
--- /dev/null
+++ b/core/vendor/krautoload/src/Krautoload/SearchableNamespaces/Default.php
@@ -0,0 +1,184 @@
+finder = $finder;
+ }
+
+ /**
+ * @param NamespaceVisitor_Interface $finder
+ * @todo This should be a more universal interface..
+ */
+ function setFinder(NamespaceVisitor_Interface $finder) {
+ $this->finder = $finder;
+ }
+
+ /**
+ * Add a namespace.
+ *
+ * @param string $namespace
+ */
+ function addNamespace($namespace) {
+ $this->namespaces[$namespace] = $namespace;
+ }
+
+ /**
+ * Set namespaces.
+ *
+ * @param array $namespaces
+ */
+ function setNamespaces(array $namespaces) {
+ $this->namespaces = array();
+ $this->addNamespaces($namespaces);
+ }
+
+ /**
+ * Add namespaces.
+ *
+ * @param array $namespaces
+ */
+ function addNamespaces(array $namespaces) {
+ foreach ($namespaces as $namespace) {
+ $this->namespaces[$namespace] = $namespace;
+ }
+ }
+
+ /**
+ * Get namespaces.
+ *
+ * @param array $namespaces
+ */
+ function getNamespaces() {
+ return $this->namespaces;
+ }
+
+ /**
+ * @param array $namespaces
+ * Namespaces for the new family.
+ *
+ * @return SearchableNamespaces_Interface
+ * Newly created namespace family.
+ */
+ function buildSearchableNamespaces(array $namespaces = array()) {
+ $new = new self($this->finder);
+ $new->addNamespaces($namespaces);
+ return $new;
+ }
+
+ /**
+ * @param string $suffix
+ * Namespace suffix to append to each namespace.
+ *
+ * @return SearchableNamespaces_Interface
+ * Newly created namespace family.
+ */
+ function buildFromSuffix($suffix) {
+ if ('\\' !== $suffix[0]) {
+ $suffix = '\\' . $suffix;
+ }
+ $new = $this->buildSearchableNamespaces();
+ foreach ($this->namespaces as $namespace) {
+ $new->addNamespace($namespace . $suffix);
+ }
+ return $new;
+ }
+
+ /**
+ * Scan all registered namespaces for classes.
+ * Tell the $api object about each class file that is found.
+ *
+ * @param InjectedAPI_ClassFileVisitor_Interface $api
+ * @param boolean $recursive
+ */
+ function apiVisitClassFiles(InjectedAPI_ClassFileVisitor_Interface $api, $recursive = FALSE) {
+ $namespaceFinderAPI = $recursive ? new InjectedAPI_NamespaceVisitor_ScanRecursive($api) : new InjectedAPI_NamespaceVisitor_ScanNamespace($api);
+ $this->finder->apiVisitNamespaces($namespaceFinderAPI, array_keys($this->namespaces));
+ }
+
+ /**
+ * Visit all namespaces.
+ *
+ * @param InjectedAPI_ClassFileVisitor_Interface $api
+ * @param boolean $recursive
+ */
+ function apiVisitNamespaces(InjectedAPI_NamespaceVisitor_Interface $api) {
+ $this->finder->apiVisitNamespaces($api, array_keys($this->namespaces));
+ }
+
+ /**
+ * Scan all registered namespaces for class files, include each file, and
+ * return all classes that actually exist (but no interfaces).
+ *
+ * @param InjectedAPI_ClassFileVisitor_Interface $api
+ * @param boolean $recursive
+ *
+ * @return array
+ * Collected class names.
+ */
+ function discoverExistingClasses($recursive = FALSE) {
+ $api = new InjectedAPI_ClassFileVisitor_CollectExistingClasses();
+ $this->apiVisitClassFiles($api, $recursive);
+ return $api->getCollectedClasses();
+ }
+
+ /**
+ * Scan all registered namespaces for class files, and return all names that
+ * may be defined as a class or interface within these namespaces.
+ *
+ * @param InjectedAPI_ClassFileVisitor_Interface $api
+ * @param boolean $recursive
+ *
+ * @return array
+ * Collected class names.
+ */
+ function discoverCandidateClasses($recursive = FALSE) {
+ $api = new InjectedAPI_ClassFileVisitor_CollectCandidateClasses();
+ $this->apiVisitClassFiles($api, $recursive);
+ return $api->getCollectedClasses();
+ }
+
+ /**
+ * Check if the given class is "known", and load it.
+ * This will check the following:
+ * - Is the class within any of the registered namespaces?
+ * - Is there is a file for this class, within the registered directories?
+ * (Include that file, if it exists.)
+ * - Is the class defined after file inclusion?
+ *
+ * The method can return FALSE even if the class is defined
+ */
+ function classExistsInNamespaces($class) {
+ return $this->classIsInNamespaces($class) && $this->classExistsInFinder($class);
+ }
+
+ protected function classIsInNamespaces($class) {
+ $prefix = $class;
+ while (FALSE !== $pos = strrpos($prefix, '\\')) {
+ $prefix = substr($prefix, 0, $pos);
+ if (isset($this->namespaces[$prefix])) {
+ return TRUE;
+ }
+ }
+ return FALSE;
+ }
+
+ protected function classExistsInFinder($class) {
+ if (Util::classIsDefined($class)) {
+ $api = new InjectedAPI_ClassFinder_FindExistingClass($class);
+ }
+ else {
+ $api = new InjectedAPI_ClassFinder_LoadClass($class);
+ }
+ return $this->finder->apiFindFile($api, $class);
+ }
+}
diff --git a/core/vendor/krautoload/src/Krautoload/SearchableNamespaces/Interface.php b/core/vendor/krautoload/src/Krautoload/SearchableNamespaces/Interface.php
new file mode 100644
index 0000000..1f380d0
--- /dev/null
+++ b/core/vendor/krautoload/src/Krautoload/SearchableNamespaces/Interface.php
@@ -0,0 +1,98 @@
+ $item) {
+ if ($item['function'] === 'spl_autoload_call') {
+ switch ($f = $trace[$i + 1]['function']) {
+ case 'class_exists':
+ case 'interface_exists':
+ case 'method_exists':
+ case 'is_callable':
+ // @todo Add more cases.
+ return TRUE;
+ default:
+ return FALSE;
+ }
+ }
+ }
+ }
+
+ static function classIsDefined($class) {
+ return class_exists($class, FALSE) || interface_exists($class, FALSE);
+ }
+}