diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc
index a818c78..f90714b 100644
--- a/core/includes/bootstrap.inc
+++ b/core/includes/bootstrap.inc
@@ -2770,9 +2770,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.
@@ -2783,21 +2782,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;
}
@@ -2811,7 +2807,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->namespacePSR0('Drupal\\' . $name, DRUPAL_ROOT . '/' . $path . '/lib');
+ $loader->namespacePSRX('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..fadc9c0 100644
--- a/core/lib/Drupal/Component/Plugin/Discovery/AnnotatedClassDiscovery.php
+++ b/core/lib/Drupal/Component/Plugin/Discovery/AnnotatedClassDiscovery.php
@@ -7,12 +7,8 @@
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;
/**
* Defines a discovery mechanism to find annotated plugins in PSR-0 namespaces.
@@ -75,42 +71,19 @@ public function getDefinition($plugin_id) {
*/
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());
- // 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;
+ // The discovery engine knows about namespace-directory mappings that are
+ // relevant for plugin discovery.
+ // It does not know the exact plugin directories.
+ $discovery = $this->buildDiscoveryEngine();
+
+ // Scan namespaces.
+ $discoveryAPI = new KrautoloadDiscoveryAPI($this->pluginDefinitionAnnotationName);
+ $discovery->apiScanNamespaces($discoveryAPI, array_keys($this->getPluginNamespaces()), FALSE);
+ return $discoveryAPI->getDefinitions();
}
/**
@@ -127,4 +100,21 @@ protected function getAnnotationNamespaces() {
return $this->annotationNamespaces;
}
+ /**
+ * Build the discovery engine, and build relevant namespaces.
+ * @todo Have this stuff properly injected.
+ */
+ protected function buildDiscoveryEngine() {
+ $discovery = new \Krautoload\ApiClassDiscovery_Pluggable();
+ $registration = new \Krautoload\RegistrationHub($discovery);
+ $modules = \Drupal::getContainer()->get('module_handler')->getModuleList();
+ foreach ($modules as $module => $module_file) {
+ $module_dir = dirname($module_file);
+ $registration->namespacePSRX('Drupal\\' . $module, $module_dir . '/lib/Drupal/' . $module);
+ $registration->namespacePSRX('Drupal\\' . $module, $module_dir . '/src');
+ }
+ $registration->namespacePSRX('Drupal\\Core', DRUPAL_ROOT . '/core/lib/Drupal/Core');
+ $registration->namespacePSRX('Drupal\\Core', DRUPAL_ROOT . '/core/src');
+ return $discovery;
+ }
}
diff --git a/core/lib/Drupal/Component/Plugin/Discovery/KrautoloadDiscoveryAPI.php b/core/lib/Drupal/Component/Plugin/Discovery/KrautoloadDiscoveryAPI.php
new file mode 100644
index 0000000..0b88f7e
--- /dev/null
+++ b/core/lib/Drupal/Component/Plugin/Discovery/KrautoloadDiscoveryAPI.php
@@ -0,0 +1,107 @@
+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 string $class
+ */
+ function fileWithClass($file, $class) {
+ $this->confirmedFileWithClass($file, $class);
+ }
+
+ /**
+ * The directory scan has found a file which may define any or none of the
+ * given classes.
+ *
+ * @param string $file
+ * @param array $classes
+ * 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, $classes) {
+
+ // Include the file to find out if the class is defined.
+ // Note: This test is not 100% certain, because the class may already be
+ // defined somewhere else.
+ include_once $file;
+
+ // Only pick the first class, which is the no-underscore version.
+ if (class_exists($classes[0], FALSE)) {
+ $this->confirmedFileWithClass($file, $classes[0]);
+ }
+ }
+
+ /**
+ * The directory scan has found a file which is expected to define the given
+ * class.
+ *
+ * @param string $file
+ * @param string $class
+ */
+ protected function confirmedFileWithClass($file, $class) {
+ if ($annotation = $this->getClassAnnotation($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 getClassAnnotation($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/DrupalKernel.php b/core/lib/Drupal/Core/DrupalKernel.php
index af1fe52..1bc2a9f 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->namespacesPSR0($this->getModuleNamespacesPSR0($module_filenames));
+ $this->classLoader->namespacesPSRX($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->namespacesPSR0($this->getModuleNamespacesPSR0($container_modules));
+ $this->classLoader->namespacesPSRX($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.
}
}
@@ -507,7 +515,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) {
@@ -647,9 +657,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 +668,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/Plugin/Discovery/AnnotatedClassDiscovery.php b/core/lib/Drupal/Core/Plugin/Discovery/AnnotatedClassDiscovery.php
index 6695db7..f19f8e0 100644
--- a/core/lib/Drupal/Core/Plugin/Discovery/AnnotatedClassDiscovery.php
+++ b/core/lib/Drupal/Core/Plugin/Discovery/AnnotatedClassDiscovery.php
@@ -100,8 +100,8 @@ protected function getProviderFromNamespace($namespace) {
*/
protected function getPluginNamespaces() {
$plugin_namespaces = array();
- foreach ($this->rootNamespacesIterator as $namespace => $dir) {
- $plugin_namespaces["$namespace\\Plugin\\{$this->subdir}"] = array($dir);
+ foreach ($this->rootNamespacesIterator as $namespace => $dirs) {
+ $plugin_namespaces["$namespace\\Plugin\\{$this->subdir}"] = (array) $dirs;
}
return $plugin_namespaces;
diff --git a/core/modules/simpletest/simpletest.module b/core/modules/simpletest/simpletest.module
index 5461cab..843ba8c 100644
--- a/core/modules/simpletest/simpletest.module
+++ b/core/modules/simpletest/simpletest.module
@@ -520,23 +520,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()->namespacePSR0('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()->namespacePSR0('Drupal\\Tests', DRUPAL_ROOT . '/core/tests');
}
/**
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/views/lib/Drupal/views/Plugin/Discovery/ViewsHandlerDiscovery.php b/core/modules/views/lib/Drupal/views/Plugin/Discovery/ViewsHandlerDiscovery.php
index 51664cb..1aa1479 100644
--- a/core/modules/views/lib/Drupal/views/Plugin/Discovery/ViewsHandlerDiscovery.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/Discovery/ViewsHandlerDiscovery.php
@@ -45,8 +45,8 @@ function __construct($type, \Traversable $root_namespaces) {
'Drupal\Component\Annotation' => DRUPAL_ROOT . '/core/lib',
);
$plugin_namespaces = array();
- foreach ($root_namespaces as $namespace => $dir) {
- $plugin_namespaces["$namespace\\Plugin\\views\\{$type}"] = array($dir);
+ foreach ($root_namespaces as $namespace => $dirs) {
+ $plugin_namespaces["$namespace\\Plugin\\views\\{$type}"] = (array) $dirs;
}
parent::__construct($plugin_namespaces, $annotation_namespaces, 'Drupal\Component\Annotation\PluginID');
}
@@ -68,8 +68,8 @@ public function getDefinitions() {
*/
protected function getPluginNamespaces() {
$plugin_namespaces = array();
- foreach ($this->rootNamespacesIterator as $namespace => $dir) {
- $plugin_namespaces["$namespace\\Plugin\\views\\{$this->type}"] = array($dir);
+ foreach ($this->rootNamespacesIterator as $namespace => $dirs) {
+ $plugin_namespaces["$namespace\\Plugin\\views\\{$this->type}"] = (array) $dirs;
}
return $plugin_namespaces;
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..6728243
--- /dev/null
+++ b/core/vendor/krautoload/src/Krautoload.php
@@ -0,0 +1,45 @@
+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\FinderPlugin_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/ApiClassDiscovery/Interface.php b/core/vendor/krautoload/src/Krautoload/ApiClassDiscovery/Interface.php
new file mode 100644
index 0000000..6a436a3
--- /dev/null
+++ b/core/vendor/krautoload/src/Krautoload/ApiClassDiscovery/Interface.php
@@ -0,0 +1,18 @@
+apiFindNamespace($namespaceFinderAPI, $namespace);
+ }
+
+ /**
+ * @param DiscoveryAPI_Interface $api
+ * @param array $namespaces
+ */
+ public function apiScanNamespaces($api, $namespaces, $recursive = FALSE) {
+ $namespaceFinderAPI = $recursive ? new NamespaceFinderAPI_ScanRecursive($api) : new NamespaceFinderAPI_ScanDirectory($api);
+ $this->apiFindNamespaces($namespaceFinderAPI, $namespaces);
+ }
+}
diff --git a/core/vendor/krautoload/src/Krautoload/ApiClassFinder.php b/core/vendor/krautoload/src/Krautoload/ApiClassFinder.php
new file mode 100644
index 0000000..00db0f3
--- /dev/null
+++ b/core/vendor/krautoload/src/Krautoload/ApiClassFinder.php
@@ -0,0 +1,16 @@
+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/ApiClassFinder/Pluggable.php b/core/vendor/krautoload/src/Krautoload/ApiClassFinder/Pluggable.php
new file mode 100644
index 0000000..cf81338
--- /dev/null
+++ b/core/vendor/krautoload/src/Krautoload/ApiClassFinder/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 ClassFinderAPI_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.
+ $namespace_path_fragment = str_replace('\\', DIRECTORY_SEPARATOR, substr($class, 0, $pos + 1));
+ $path_suffix = substr($class, $pos + 1) . '.php';
+ if ($this->apiMapFindFile($api, $this->namespaceMap, $namespace_path_fragment, $path_suffix)) {
+ return TRUE;
+ }
+ }
+ else {
+
+ // The class is not within a namespace.
+ // Fall back to the prefix-based finder.
+ $prefix_path_fragment = str_replace('_', DIRECTORY_SEPARATOR, $class) . '.php';
+ if ($this->apiMapFindFile($api, $this->prefixMap, $prefix_path_fragment, '')) {
+ return TRUE;
+ }
+ }
+ }
+
+ /**
+ * Find the file for a class that in PSR-0 or PEAR would be in
+ * $psr_0_root . '/' . $path_fragment . $path_suffix
+ *
+ * @param array $map
+ * Either the namespace map or the prefix
+ * @param ClassFinderAPI_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 $path_fragment
+ * First part of the canonical path, with trailing DIRECTORY_SEPARATOR.
+ * @param string $path_suffix
+ * 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, $path_fragment, $path_suffix) {
+ $path = $path_fragment . $path_suffix;
+ while (TRUE) {
+
+ // Check any plugin registered for this fragment.
+ if (!empty($map[$path_fragment])) {
+ foreach ($map[$path_fragment] as $dir => $plugin) {
+ if ($plugin->pluginFindFile($api, $path_fragment, $dir, $path_suffix)) {
+ return TRUE;
+ }
+ }
+ }
+
+ // Continue with parent fragment.
+ if ('' === $path_fragment) {
+ break;
+ }
+ elseif (DIRECTORY_SEPARATOR === $path_fragment) {
+ // This happens if a class begins with an underscore.
+ $path_fragment = '';
+ $path_suffix = $path;
+ }
+ elseif (FALSE !== $pos = strrpos($path_fragment, DIRECTORY_SEPARATOR, -2)) {
+ $path_fragment = substr($path_fragment, 0, $pos + 1);
+ $path_suffix = substr($path, $pos + 1);
+ }
+ else {
+ $path_fragment = '';
+ $path_suffix = $path;
+ }
+ }
+ }
+}
diff --git a/core/vendor/krautoload/src/Krautoload/ApiNamespaceFinder/Interface.php b/core/vendor/krautoload/src/Krautoload/ApiNamespaceFinder/Interface.php
new file mode 100644
index 0000000..6a9ea7b
--- /dev/null
+++ b/core/vendor/krautoload/src/Krautoload/ApiNamespaceFinder/Interface.php
@@ -0,0 +1,18 @@
+namespaceMap[$logicalBasePath])) {
+ foreach ($this->namespaceMap[$logicalBasePath] as $dir => $plugin) {
+ if (is_dir($dir . $pathSuffix)) {
+ $api->namespaceDirectoryPlugin($namespace, $dir . $pathSuffix, $plugin);
+ }
+ }
+ }
+
+ // Continue with parent fragment.
+ if ('' === $logicalBasePath) {
+ break;
+ }
+ elseif (DIRECTORY_SEPARATOR === $logicalBasePath) {
+ // This happens if a class begins with an underscore.
+ $logicalBasePath = '';
+ $pathSuffix = $logicalPath;
+ }
+ elseif (FALSE !== $pos = strrpos($logicalBasePath, DIRECTORY_SEPARATOR, -2)) {
+ $logicalBasePath = substr($logicalBasePath, 0, $pos + 1);
+ $pathSuffix = substr($logicalPath, $pos + 1);
+ }
+ else {
+ $logicalBasePath = '';
+ $pathSuffix = $logicalPath;
+ }
+ }
+ }
+
+ /**
+ * @param NamespaceFinderAPI_Interface $api
+ * @param array $namespaces
+ */
+ public function apiFindNamespaces($api, $namespaces) {
+ foreach ($namespaces as $namespace) {
+ $this->apiFindNamespace($api, $namespace);
+ }
+ }
+}
diff --git a/core/vendor/krautoload/src/Krautoload/ClassFinderAPI/Abstract.php b/core/vendor/krautoload/src/Krautoload/ClassFinderAPI/Abstract.php
new file mode 100644
index 0000000..ec9666e
--- /dev/null
+++ b/core/vendor/krautoload/src/Krautoload/ClassFinderAPI/Abstract.php
@@ -0,0 +1,59 @@
+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/ClassFinderAPI/CollectFiles.php b/core/vendor/krautoload/src/Krautoload/ClassFinderAPI/CollectFiles.php
new file mode 100644
index 0000000..ecaf252
--- /dev/null
+++ b/core/vendor/krautoload/src/Krautoload/ClassFinderAPI/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/ClassFinderAPI/Interface.php b/core/vendor/krautoload/src/Krautoload/ClassFinderAPI/Interface.php
new file mode 100644
index 0000000..e0b8e36
--- /dev/null
+++ b/core/vendor/krautoload/src/Krautoload/ClassFinderAPI/Interface.php
@@ -0,0 +1,111 @@
+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);
+ }
+
+ /**
+ * 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);
+ }
+ return FALSE;
+ }
+}
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 ClassFinderAPI_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..94b4508
--- /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($namespace_path_fragment, $dir, $plugin) {
+ $this->namespaceMap[$namespace_path_fragment][$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 => $skip_class_exists) {
+ if (is_file($file)) {
+ if ($skip_class_exists) {
+ // 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)) {
+ 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.
+ $namespace_path_fragment = str_replace('\\', DIRECTORY_SEPARATOR, substr($class, 0, $pos + 1));
+ $path_suffix = substr($class, $pos + 1) . '.php';
+ if ($this->mapLoadClass($this->namespaceMap, $class, $namespace_path_fragment, $path_suffix)) {
+ return TRUE;
+ }
+ }
+ else {
+
+ // The class is not within a namespace.
+ // Fall back to the prefix-based finder.
+ $prefix_path_fragment = str_replace('_', DIRECTORY_SEPARATOR, $class) . '.php';
+ if ($this->mapLoadClass($this->prefixMap, $class, $prefix_path_fragment, '')) {
+ return TRUE;
+ }
+ }
+ }
+
+ /**
+ * Find the file for a class that in PSR-0 or PEAR would be in
+ * $psr_0_root . '/' . $path_fragment . $path_suffix
+ *
+ * @param array $map
+ * Either the namespace map or the prefix
+ * @param string $path_fragment
+ * First part of the canonical path, with trailing DIRECTORY_SEPARATOR.
+ * @param string $path_suffix
+ * 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, $path_fragment, $path_suffix) {
+ $path = $path_fragment . $path_suffix;
+ while (TRUE) {
+
+ // Check any plugin registered for this fragment.
+ if (!empty($map[$path_fragment])) {
+ foreach ($map[$path_fragment] as $dir => $plugin) {
+ if ($plugin->pluginLoadClass($class, $path_fragment, $dir, $path_suffix)) {
+ return TRUE;
+ }
+ }
+ }
+
+ // Continue with parent fragment.
+ if ('' === $path_fragment) {
+ break;
+ }
+ elseif (DIRECTORY_SEPARATOR === $path_fragment) {
+ // This happens if a class begins with an underscore.
+ $path_fragment = '';
+ $path_suffix = $path;
+ }
+ elseif (FALSE !== $pos = strrpos($path_fragment, DIRECTORY_SEPARATOR, -2)) {
+ $path_fragment = substr($path_fragment, 0, $pos + 1);
+ $path_suffix = substr($path, $pos + 1);
+ }
+ else {
+ $path_fragment = '';
+ $path_suffix = $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 @@
+classes;
+ }
+
+ function fileWithClass($file, $class) {
+ $this->classes[$class] = TRUE;
+ }
+
+ function fileWithClassCandidates($file, $classes) {
+ include_once $file;
+ foreach ($classes as $class) {
+ if (class_exists($class, FALSE)) {
+ $this->classes[$class] = TRUE;
+ }
+ }
+ }
+}
diff --git a/core/vendor/krautoload/src/Krautoload/DiscoveryAPI/Interface.php b/core/vendor/krautoload/src/Krautoload/DiscoveryAPI/Interface.php
new file mode 100644
index 0000000..09ced32
--- /dev/null
+++ b/core/vendor/krautoload/src/Krautoload/DiscoveryAPI/Interface.php
@@ -0,0 +1,10 @@
+guessFile($dir . $suffix)) {
+ return TRUE;
+ }
+ }
+
+ function pluginLoadClass($class, $prefix, $dir, $suffix) {
+ if (is_file($file = $dir . $suffix)) {
+ include $file;
+ return TRUE;
+ }
+ }
+
+ function pluginScanDirectory($api, $namespace, $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');
+ $api->fileWithClass($fileinfo->getPathname(), $class);
+ }
+ }
+ }
+
+ function pluginScanRecursive($api, $namespace, $dir, $namespaceSuffix = '') {
+ foreach (new \DirectoryIterator($dir) as $fileinfo) {
+ // @todo Once core requires 5.3.6, use $fileinfo->getExtension().
+ if (pathinfo($fileinfo->getFilename(), PATHINFO_EXTENSION) == 'php') {
+ $suffix = $namespaceSuffix . '\\' . $fileinfo->getBasename('.php');
+ $class = $namespace . $suffix;
+ $api->fileWithClass($fileinfo->getPathname(), $class, $namespace, $suffix);
+ }
+ elseif (!$fileinfo->isDot() && $fileinfo->isDir()) {
+ $childNamespaceSuffix = $namespaceSuffix . '\\' . $fileinfo->getFilename();
+ $this->pluginScanRecursive($api, $namespace, $fileinfo->getPathname(), $childNamespaceSuffix);
+ }
+ }
+ }
+}
diff --git a/core/vendor/krautoload/src/Krautoload/FinderPlugin/ShallowPEAR.php b/core/vendor/krautoload/src/Krautoload/FinderPlugin/ShallowPEAR.php
new file mode 100644
index 0000000..dfdb967
--- /dev/null
+++ b/core/vendor/krautoload/src/Krautoload/FinderPlugin/ShallowPEAR.php
@@ -0,0 +1,20 @@
+guessFile($dir . $suffix)) {
+ return TRUE;
+ }
+ }
+
+ function pluginLoadClass($class, $prefix, $dir, $suffix) {
+ if (is_file($file = $dir . $suffix)) {
+ include $file;
+ return TRUE;
+ }
+ }
+}
diff --git a/core/vendor/krautoload/src/Krautoload/FinderPlugin/ShallowPSR0.php b/core/vendor/krautoload/src/Krautoload/FinderPlugin/ShallowPSR0.php
new file mode 100644
index 0000000..81500d6
--- /dev/null
+++ b/core/vendor/krautoload/src/Krautoload/FinderPlugin/ShallowPSR0.php
@@ -0,0 +1,78 @@
+guessFileCandidate($dir . $suffix)) {
+ return TRUE;
+ }
+ }
+
+ function pluginLoadClass($class, $prefix, $dir, $suffix) {
+
+ // Replace the underscores after the last directory separator.
+ if (FALSE !== $pos = strrpos($suffix, DIRECTORY_SEPARATOR)) {
+ $suffix = substr($suffix, 0, $pos) . str_replace('_', DIRECTORY_SEPARATOR, substr($suffix, $pos));
+ }
+ else {
+ $suffix = str_replace('_', DIRECTORY_SEPARATOR, $suffix);
+ }
+
+ // Check whether the file exists.
+ if (is_file($file = $dir . $suffix)) {
+ // We don't know if the file defines the class,
+ // and whether it was already included.
+ include_once $file;
+ return class_exists($class);
+ }
+ }
+
+ function pluginScanDirectory($api, $namespace, $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');
+ $api->fileWithClassCandidates($fileinfo->getPathname(), array($class));
+ }
+ }
+ }
+
+ function pluginScanRecursive($api, $namespace, $dir, $namespaceSuffixes = array('\\')) {
+ foreach (new \DirectoryIterator($dir) as $fileinfo) {
+ // @todo Once core requires 5.3.6, use $fileinfo->getExtension().
+ if (pathinfo($fileinfo->getFilename(), PATHINFO_EXTENSION) == 'php') {
+ $classes = array();
+ $suffixes = array();
+ foreach ($namespaceSuffixes as $suffix) {
+ $classes[] = $namespace . $suffix . $fileinfo->getBasename('.php');
+ $suffixes[] = $suffix . $fileinfo->getBasename('.php');
+ }
+ if (!empty($classes)) {
+ $api->fileWithClassCandidates($fileinfo->getPathname(), $classes, $namespace, $suffixes);
+ }
+ }
+ elseif (!$fileinfo->isDot() && $fileinfo->isDir()) {
+ $childSuffixes = array();
+ $childSuffixes[] = $namespaceSuffixes[0] . $fileinfo->getFilename() . '\\';
+ foreach ($namespaceSuffixes as $suffix) {
+ $childSuffixes[] = $suffix . $fileinfo->getFilename() . '_';
+ }
+ $this->pluginScanRecursive($api, $namespace, $fileinfo->getPathname(), $childSuffixes);
+ }
+ }
+ }
+}
diff --git a/core/vendor/krautoload/src/Krautoload/FinderPlugin/ShallowPSR0/AllUnderscore.php b/core/vendor/krautoload/src/Krautoload/FinderPlugin/ShallowPSR0/AllUnderscore.php
new file mode 100644
index 0000000..5d034a5
--- /dev/null
+++ b/core/vendor/krautoload/src/Krautoload/FinderPlugin/ShallowPSR0/AllUnderscore.php
@@ -0,0 +1,49 @@
+guessFile($dir . $suffix)) {
+ return TRUE;
+ }
+ }
+
+ function pluginLoadClass($class, $prefix, $dir, $suffix) {
+
+ // Replace all underscores in the suffix part.
+ $suffix = str_replace('_', DIRECTORY_SEPARATOR, $suffix);
+
+ // Check if the file exists.
+ if (is_file($file = $dir . $suffix)) {
+ require $file;
+ // The class is expected to exist after file inclusion.
+ return TRUE;
+ }
+ }
+}
diff --git a/core/vendor/krautoload/src/Krautoload/FinderPlugin/ShallowPSR0/AllUnderscoreInDir.php b/core/vendor/krautoload/src/Krautoload/FinderPlugin/ShallowPSR0/AllUnderscoreInDir.php
new file mode 100644
index 0000000..a97ccf9
--- /dev/null
+++ b/core/vendor/krautoload/src/Krautoload/FinderPlugin/ShallowPSR0/AllUnderscoreInDir.php
@@ -0,0 +1,57 @@
+guessFile($dir . $suffix)) {
+ return TRUE;
+ }
+ }
+
+ function pluginLoadClass($class, $prefix, $dir, $suffix) {
+
+ // 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($suffix, 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.
+ $suffix = str_replace('_', DIRECTORY_SEPARATOR, $suffix);
+
+ // We "guess", because we don't know whether the file exists.
+ if (is_file($file = $dir . $suffix)) {
+ include $file;
+ return TRUE;
+ }
+ }
+}
diff --git a/core/vendor/krautoload/src/Krautoload/FinderPlugin/ShallowPSR0/NoConflict.php b/core/vendor/krautoload/src/Krautoload/FinderPlugin/ShallowPSR0/NoConflict.php
new file mode 100644
index 0000000..03fdbd8
--- /dev/null
+++ b/core/vendor/krautoload/src/Krautoload/FinderPlugin/ShallowPSR0/NoConflict.php
@@ -0,0 +1,82 @@
+guessFile($dir . $suffix)) {
+ return TRUE;
+ }
+ }
+
+ function pluginLoadClass($class, $prefix, $dir, $suffix) {
+ // We need to replace the underscores after the last directory separator.
+ if (FALSE !== $pos = strrpos($suffix, DIRECTORY_SEPARATOR)) {
+ $suffix = substr($suffix, 0, $pos) . str_replace('_', DIRECTORY_SEPARATOR, substr($suffix, $pos));
+ }
+ else {
+ $suffix = str_replace('_', DIRECTORY_SEPARATOR, $suffix);
+ }
+ // We don't know if the file exists.
+ if (is_file($file = $dir . $suffix)) {
+ // We assume that the file defines the class.
+ include $file;
+ return TRUE;
+ }
+ }
+}
diff --git a/core/vendor/krautoload/src/Krautoload/FinderPlugin/ShallowPSR0/NoUnderscore.php b/core/vendor/krautoload/src/Krautoload/FinderPlugin/ShallowPSR0/NoUnderscore.php
new file mode 100644
index 0000000..07dbfd8
--- /dev/null
+++ b/core/vendor/krautoload/src/Krautoload/FinderPlugin/ShallowPSR0/NoUnderscore.php
@@ -0,0 +1,29 @@
+guessFile($dir . $suffix)) {
+ return TRUE;
+ }
+ }
+
+ function pluginLoadClass($class, $prefix, $dir, $suffix) {
+ // 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 !== $pos = strrpos($suffix, '_')) {
+ if (FALSE === strrpos($suffix, DIRECTORY_SEPARATOR, $pos)) {
+ return;
+ }
+ }
+ // We are safe, no underscore was found after the last directory separator.
+ // So we can proceed with class loading.
+
+ // We "guess", because we don't know whether the file exists.
+ if (is_file($file = $dir . $suffix)) {
+ include $file;
+ return TRUE;
+ }
+ }
+}
diff --git a/core/vendor/krautoload/src/Krautoload/NamespaceFinderAPI/Interface.php b/core/vendor/krautoload/src/Krautoload/NamespaceFinderAPI/Interface.php
new file mode 100644
index 0000000..37321f6
--- /dev/null
+++ b/core/vendor/krautoload/src/Krautoload/NamespaceFinderAPI/Interface.php
@@ -0,0 +1,8 @@
+api = $api;
+ }
+
+ public function namespaceDirectoryPlugin($namespace, $dir, $plugin) {
+ $plugin->pluginScanDirectory($this->api, $namespace, $dir);
+ }
+}
diff --git a/core/vendor/krautoload/src/Krautoload/NamespaceFinderAPI/ScanRecursive.php b/core/vendor/krautoload/src/Krautoload/NamespaceFinderAPI/ScanRecursive.php
new file mode 100644
index 0000000..d50f867
--- /dev/null
+++ b/core/vendor/krautoload/src/Krautoload/NamespaceFinderAPI/ScanRecursive.php
@@ -0,0 +1,16 @@
+api = $api;
+ }
+
+ public function namespaceDirectoryPlugin($namespace, $dir, $plugin) {
+ $plugin->pluginScanRecursive($this->api, $namespace, $dir);
+ }
+}
diff --git a/core/vendor/krautoload/src/Krautoload/RegistrationHub.php b/core/vendor/krautoload/src/Krautoload/RegistrationHub.php
new file mode 100644
index 0000000..6919f4f
--- /dev/null
+++ b/core/vendor/krautoload/src/Krautoload/RegistrationHub.php
@@ -0,0 +1,232 @@
+finder = $finder;
+ $this->plugins['ShallowPEAR'] = new FinderPlugin_ShallowPEAR();
+ $this->plugins['ShallowPSR0'] = new FinderPlugin_ShallowPSR0();
+ $this->plugins['PSRX'] = new FinderPlugin_PSRX();
+ }
+
+ /**
+ * @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->composerPrefixes($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 composerPrefixes(array $prefixes) {
+ foreach ($prefixes as $prefix => $path) {
+ $this->composerPrefix($prefix, $path);
+ }
+ }
+
+ /**
+ * Registers a set of classes
+ *
+ * @param string $prefix
+ * The classes prefix
+ * @param array|string $paths
+ * The location(s) of the classes
+ */
+ function composerPrefix($prefix, $paths) {
+
+ 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) $paths as $path) {
+ $this->namespacePSR0($namespace, $path);
+ }
+ }
+ elseif (FALSE !== strrpos($prefix, '\\')) {
+ // We assume that $prefix is meant as a namespace,
+ // and the paths are PSR-0 directories.
+ $namespace = $prefix;
+ foreach ((array) $paths as $path) {
+ $this->namespacePSR0($namespace, $path);
+ $this->classFile($prefix, $path . '.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) $paths as $path) {
+ $this->prefixPEAR(substr($prefix, 0, -1), $path);
+ }
+ // 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) $paths as $path) {
+ $this->namespacePSR0($prefix, $path);
+ $this->prefixPEAR($prefix, $path);
+ $this->classFile($prefix, $path . '.php');
+ }
+ // TODO:
+ // Register special plugins to cover other FQCNs
+ // that happen to begin with with the prefix.
+ }
+ }
+
+ function namespacesPSR0($namespaces, $plugin = NULL) {
+ foreach ($namespaces as $namespace => $paths) {
+ foreach ((array) $paths as $path) {
+ $this->namespacePSR0($namespace, $path);
+ }
+ }
+ }
+
+ function namespacesPluginPSR0($namespaces, $plugin = NULL) {
+ if (!isset($plugin)) {
+ $plugin = $this->plugins['ShallowPSR0'];
+ }
+ elseif (is_string($plugin)) {
+ $class = "Krautoload\\FinderPlugin_ShallowPSR0_$plugin";
+ $plugin = new $class();
+ }
+ foreach ($namespaces as $namespace => $paths) {
+ foreach ((array) $paths as $path) {
+ $this->namespacePluginPSR0($namespace, $path, $plugin);
+ }
+ }
+ }
+
+ function namespacePSR0($namespace, $root_path) {
+ $namespace_path_fragment = $this->namespacePathFragment($namespace);
+ $deep_path = strlen($root_path) ? $root_path . DIRECTORY_SEPARATOR : '';
+ $deep_path .= $namespace_path_fragment;
+ $this->finder->registerNamespacePathPlugin($namespace_path_fragment, $deep_path, $this->plugins['ShallowPSR0']);
+ }
+
+ function namespacePluginPSR0($namespace, $root_path, $plugin) {
+ $namespace_path_fragment = $this->namespacePathFragment($namespace);
+ $deep_path = strlen($root_path) ? $root_path . DIRECTORY_SEPARATOR : '';
+ $deep_path .= $namespace_path_fragment;
+ $this->finder->registerNamespacePathPlugin($namespace_path_fragment, $deep_path, $plugin);
+ }
+
+ function namespaceShallowPSR0($namespace, $deep_path) {
+ $namespace_path_fragment = $this->namespacePathFragment($namespace);
+ $deep_path = strlen($deep_path) ? $deep_path . DIRECTORY_SEPARATOR : '';
+ $this->finder->registerNamespacePathPlugin($namespace_path_fragment, $deep_path, $this->plugins['ShallowPSR0']);
+ }
+
+ function prefixPEAR($prefix, $root_path) {
+ $prefix_path_fragment = $this->prefixPathFragment($prefix);
+ $deep_path = strlen($root_path) ? $root_path . DIRECTORY_SEPARATOR : '';
+ $deep_path .= $prefix_path_fragment;
+ $this->finder->registerPrefixPathPlugin($prefix_path_fragment, $deep_path, $this->plugins['ShallowPEAR']);
+ }
+
+ function prefixShallowPEAR($prefix, $deep_path) {
+ $prefix_path_fragment = $this->prefixPathFragment($prefix);
+ $deep_path = strlen($deep_path) ? $deep_path . DIRECTORY_SEPARATOR : '';
+ $this->finder->registerPrefixPathPlugin($prefix_path_fragment, $deep_path, $this->plugins['ShallowPEAR']);
+ }
+
+ function namespacesPSRX($namespaces) {
+ foreach ($namespaces as $namespace => $paths) {
+ foreach ((array) $paths as $path) {
+ $this->namespacePSRX($namespace, $path);
+ }
+ }
+ }
+
+ function namespacePSRX($namespace, $deep_path) {
+ $namespace_path_fragment = $this->namespacePathFragment($namespace);
+ $deep_path = strlen($deep_path) ? $deep_path . DIRECTORY_SEPARATOR : '';
+ $this->finder->registerNamespacePathPlugin($namespace_path_fragment, $deep_path, $this->plugins['PSRX']);
+ }
+
+ function classFile($class, $file) {
+ $this->finder->registerClass($class, $file);
+ }
+
+ /**
+ * 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 namespacePathFragment($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 prefixPathFragment($prefix) {
+ return
+ strlen($prefix)
+ ? str_replace('_', DIRECTORY_SEPARATOR, $prefix . '_')
+ : ''
+ ;
+ }
+}