diff --git a/composer.json b/composer.json index 97ef123..940e9d0 100644 --- a/composer.json +++ b/composer.json @@ -3,6 +3,12 @@ "description": "Drupal is an open source content management platform powering millions of websites and applications.", "type": "drupal-core", "license": "GPL-2.0+", + "repositories": [ + { + "type": "vcs", + "url": "https://github.com/donquixote/krautoload" + } + ], "require": { "symfony/class-loader": "2.3.*", "symfony/dependency-injection": "2.3.*", @@ -20,7 +26,8 @@ "symfony-cmf/routing": "1.1.*@alpha", "easyrdf/easyrdf": "0.8.*@beta", "phpunit/phpunit": "3.7.*", - "zendframework/zend-feed": "2.2.*" + "zendframework/zend-feed": "2.2.*", + "donquixote/krautoload": "0.0.*@alpha" }, "autoload": { "psr-0": { diff --git a/composer.lock b/composer.lock index 75394ba..3b84c49 100644 --- a/composer.lock +++ b/composer.lock @@ -3,7 +3,7 @@ "This file locks the dependencies of your project to a known state", "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file" ], - "hash": "9cad5a32fc0b4c0fac16fbda1b8ead16", + "hash": "7498cf0dce7a852d3dbeb5681515ed0a", "packages": [ { "name": "doctrine/common", @@ -75,6 +75,58 @@ "time": "2012-09-19 22:55:18" }, { + "name": "donquixote/krautoload", + "version": "0.0.1-alpha7", + "source": { + "type": "git", + "url": "https://github.com/donquixote/krautoload.git", + "reference": "0.0.1-alpha7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/donquixote/krautoload/zipball/0.0.1-alpha7", + "reference": "0.0.1-alpha7", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/php-invoker": ">=1.1.2", + "phpunit/phpunit": ">=3.7" + }, + "type": "library", + "autoload": { + "psr-0": { + "Krautoload\\": "src/" + } + }, + "license": [ + "GPL" + ], + "authors": [ + { + "name": "Andreas Hennings", + "homepage": "http://github.com/donquixote" + } + ], + "description": "Krautoload is a pluggable class loader and class discovery tool.", + "homepage": "http://github.com/donquixote/krautoload", + "keywords": [ + "PEAR", + "PSR-0", + "PSR-4", + "PSR-X", + "autoload", + "classloader" + ], + "support": { + "issues": "http://github.com/donquixote/krautoload/issues", + "source": "https://github.com/donquixote/krautoload/tree/0.0.1-alpha7" + }, + "time": "2013-07-18 21:43:11" + }, + { "name": "easyrdf/easyrdf", "version": "0.8.0-beta.1", "source": { @@ -1685,7 +1737,8 @@ "stability-flags": { "kriswallsmith/assetic": 15, "symfony-cmf/routing": 15, - "easyrdf/easyrdf": 10 + "easyrdf/easyrdf": 10, + "donquixote/krautoload": 15 }, "platform": [ diff --git a/core/core.services.yml b/core/core.services.yml index 640440b..7a01843 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: Drupal\Core\ClassLoader\SearchableNamespacesInterface + 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 9daa71a..b35779e 100644 --- a/core/includes/bootstrap.inc +++ b/core/includes/bootstrap.inc @@ -22,6 +22,8 @@ use Drupal\Core\Lock\DatabaseLockBackend; use Drupal\Core\Lock\LockBackendInterface; use Drupal\Core\Session\UserSession; +use Drupal\Core\ClassLoader\NamespaceInspectorAdapter; +use Drupal\Core\ClassLoader\NamespaceInspectorAdapterInterface; /** * @file @@ -2742,61 +2744,78 @@ function arg($index = NULL, $path = NULL) { } /** - * Initializes and returns the class loader. + * Initializes the class loader, and return an object to controll the class + * loader. * - * The class loader is responsible for lazy-loading all PSR-0 compatible - * classes, interfaces, and traits (PHP 5.4 and later). It's only dependency - * is DRUPAL_ROOT. Otherwise it may be called as early as possible. + * The class loader is responsible for lazy-loading all classes, interfaces, and + * traits (PHP 5.4 and later) that cannot be loaded in other ways. + * It may be called as soon as the DRUPAL_ROOT constant is available. + * (defined in + * Its only dependency is DRUPAL_ROOT. Otherwise it may be called as early as possible. * - * @param $class_loader + * @param $loader_name * The name of class loader to use. This can be used to change the class * loader class when calling drupal_classloader() from settings.php. It is * ignored otherwise. * - * @return \Symfony\Component\ClassLoader\ClassLoader - * A ClassLoader class instance (or extension thereof). + * @return NamespaceInspectorAdapter + * A wrapper around the class loader, with methods to register classes, + * namespaces and prefixes. */ -function drupal_classloader($class_loader = NULL) { - // By default, use the ClassLoader which is best for development, as it does - // not break when code is moved on the file system. However, as it is slow, - // allow to use the APC class loader in production. - static $loader; +function drupal_classloader($loader_name = NULL) { + /** + * @var NamespaceInspectorAdapter $adapter + */ + static $adapter; - if (!isset($loader)) { + // Initialize the class loader, if it is not already initialized. + if (!isset($adapter)) { - // 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 the bootstrap include file for the Krautoload ClassLoader. + // Drupal 8 uses Krautoload instead of the loader shipped with Symfony or + // Composer, to have PSR-4 support and class discovery. + require_once DRUPAL_ROOT . '/core/vendor/donquixote/krautoload/src/Krautoload.php'; - // Register the class loader. - // When configured to use APC, the ApcClassLoader is registered instead. - // Note that ApcClassLoader decorates ClassLoader and only provides the - // findFile() method, but none of the others. The actual registry is still - // in ClassLoader. - if (!isset($class_loader)) { - $class_loader = settings()->get('class_loader', 'default'); + // Check class loader setting, to determine which variation of the class + // loader to use. + if (!isset($loader_name)) { + $loader_name = 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(); - } - else { - $loader->register(); + + // Prepare an options array to initialize Krautoload. + $krautoload_options = array(); + // Use a discovery-enabled version of the class loader. + $krautoload_options['introspection'] = TRUE; + // Activate APC cache, if available. + if ($loader_name === 'apc' && extension_enabled('apc') && function_exists('apc_store')) { + $krautoload_options['cache'] = 'ApcCache'; + $krautoload_options['cache_prefix'] = 'drupal.' . drupal_get_hash_salt(); } - // 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); + // Bootstrap Krautoload with these options. + // The return value is a "RegistrationHub" object, which has various methods + // to register namespaces and other things, but is not the main class loader + // object. Within Drupal, this is being referred to as "$loader_controller". + $adapter = Krautoload::start($krautoload_options); - // Register the loader with PHP. - $loader->register(); + // Register class loading for vendor libraries managed by Composer. + // This will process + // - core/vendor/composer/autoload_namespaces.php + // - core/vendor/composer/autoload_classmap.php + // This will also work with a generated classmap from + // "composer install --optimize-autoloader". + $adapter->composerVendorDir(DRUPAL_ROOT . '/core/vendor'); + + // Replace the adapter with a Drupal-native one. + $adapter = new NamespaceInspectorAdapter($adapter->getInspector()); } - return $loader; + + return $adapter; } /** - * Registers an additional namespace. + * Registers class loading for a Drupal extension. + * (module, theme or install profile) * * @param string $name * The namespace component to register; e.g., 'node'. @@ -2804,8 +2823,7 @@ function drupal_classloader($class_loader = NULL) { * The relative path to the Drupal component in the filesystem. */ function drupal_classloader_register($name, $path) { - $loader = drupal_classloader(); - $loader->addPrefix('Drupal\\' . $name, DRUPAL_ROOT . '/' . $path . '/lib'); + drupal_classloader()->addDrupalExtension($name, $path); } /** diff --git a/core/lib/Drupal/Component/Plugin/Discovery/AnnotatedClassDiscovery.php b/core/lib/Drupal/Component/Plugin/Discovery/AnnotatedClassDiscovery.php index 13f853d..6ebff5a 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 Drupal\Core\ClassLoader\SearchableNamespacesInterface; /** * Defines a discovery mechanism to find annotated plugins in PSR-0 namespaces. @@ -20,16 +17,25 @@ class AnnotatedClassDiscovery implements DiscoveryInterface { /** - * The namespaces within which to find plugin classes. + * An object containing the base namespaces from which the plugin namespaces + * are built by appending the namespace suffix. * - * @var array + * @var SearchableNamespacesInterface */ - protected $pluginNamespaces; + protected $rootNamespaces; + + /** + * Suffix to be appended to each base namespace, + * to obtain the plugin namespaces. + * + * @var string + */ + protected $namespaceSuffix; /** * The namespaces of classes that can be used as annotations. * - * @var array + * @var SearchableNamespacesInterface */ protected $annotationNamespaces; @@ -46,23 +52,34 @@ class AnnotatedClassDiscovery implements DiscoveryInterface { /** * Constructs an AnnotatedClassDiscovery object. * - * @param array $plugin_namespaces - * (optional) An array of namespace that may contain plugin implementations. - * Defaults to an empty array. - * @param array $annotation_namespaces - * (optional) The namespaces of classes that can be used as annotations. - * Defaults to an empty array. + * @param SearchableNamespacesInterface $root_namespaces + * Searchable base namespaces from which the plugin namespaces are built. + * @param string $namespace_suffix + * Namespace suffix to be appended to each base plugin namespace, to obtain + * the plugin namespaces that will be searched for plugin classes. * @param string $plugin_definition_annotation_name - * (optional) The name of the annotation that contains the plugin definition. - * Defaults to 'Drupal\Component\Annotation\Plugin'. + * The name of the annotation that contains the plugin definition. */ - function __construct($plugin_namespaces = array(), $annotation_namespaces = array(), $plugin_definition_annotation_name = 'Drupal\Component\Annotation\Plugin') { - $this->pluginNamespaces = $plugin_namespaces; - $this->annotationNamespaces = $annotation_namespaces; + function __construct(SearchableNamespacesInterface $root_namespaces, $namespace_suffix, $plugin_definition_annotation_name) { + $this->rootNamespaces = $root_namespaces; + $this->namespaceSuffix = $namespace_suffix; + // Initialize with an empty collection of annotation namespaces. + // More namespaces can be added with addAnnotationNamespace(). + $this->annotationNamespaces = $root_namespaces->buildSearchableNamespaces(); $this->pluginDefinitionAnnotationName = $plugin_definition_annotation_name; } /** + * Add an annotation namespace, after the object has been created. + * + * @param string $namespace + * Annotation namespace to add. + */ + public function addAnnotationNamespace($namespace) { + $this->annotationNamespaces->addNamespace($namespace); + } + + /** * Implements Drupal\Component\Plugin\Discovery\DiscoveryInterface::getDefinition(). */ public function getDefinition($plugin_id) { @@ -74,58 +91,32 @@ 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'); - $reader->addGlobalIgnoredName('file'); // 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; + AnnotationRegistry::reset(); + AnnotationRegistry::registerLoader(array($this->getAnnotationNamespaces(), 'classExistsInNamespaces')); + + // Scan namespaces. + $discoveryAPI = new ClassFileVisitorAPI($this->pluginDefinitionAnnotationName); + $this->getPluginNamespaces()->apiVisitClassFiles($discoveryAPI, FALSE); + return $discoveryAPI->getDefinitions(); } /** * Returns an array of PSR-0 namespaces to search for plugin classes. + * + * @return SearchableNamespacesInterface */ protected function getPluginNamespaces() { - return $this->pluginNamespaces; + return $this->rootNamespaces->buildFromSuffix('\\' . $this->namespaceSuffix); } /** - * Returns an array of PSR-0 namespaces to search for annotation classes. + * Returns a searchable namespace collection to search for annotation classes. + * + * @return SearchableNamespacesInterface */ 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..9331f97 --- /dev/null +++ b/core/lib/Drupal/Component/Plugin/Discovery/ClassFileVisitorAPI.php @@ -0,0 +1,109 @@ +reader = new AnnotationReader(); + // Prevent @endlink and @file from being parsed as an annotation. + $this->reader->addGlobalIgnoredName('endlink'); + $this->reader->addGlobalIgnoredName('file'); + $this->annotationName = $annotationName; + } + + /** + * Get the array of plugin definitions, after everything is scanned. + * + * @return array + */ + public 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 $relativeClassName + * 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. + */ + public 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. + */ + public function fileWithClassCandidates($file, array $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; + } + } + + /** + * Parse a class file, to extract the annotation. + * + * @param string $file + * @param string $class + * @return mixed + * The annotation as returned by AnnotationReader::getClassAnnotation(), + * or NULL. + */ + 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..5847f44 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 Drupal\Core\ClassLoader\SearchableNamespacesInterface; /** * Provides an Action plugin manager. @@ -23,12 +24,13 @@ class ActionManager extends PluginManagerBase { /** * Constructs a ActionManager object. * - * @param \Traversable $namespaces - * An object that implements \Traversable which contains the root paths - * keyed by the corresponding namespace to look for plugin implementations. + * @param SearchableNamespacesInterface $root_namespaces + * Searchable namespaces for enabled extensions and core. + * This will be used to build the plugin namespaces by adding the suffix. + * E.g. the root namespace for a module is Drupal\$module. */ - public function __construct(\Traversable $namespaces) { - $this->discovery = new AnnotatedClassDiscovery('Action', $namespaces, array(), 'Drupal\Core\Annotation\Action'); + public function __construct(SearchableNamespacesInterface $root_namespaces) { + $this->discovery = new AnnotatedClassDiscovery($root_namespaces, 'Action', 'Drupal\Core\Annotation\Action'); $this->discovery = new AlterDecorator($this->discovery, 'action_info'); $this->factory = new ContainerFactory($this); diff --git a/core/lib/Drupal/Core/Archiver/ArchiverManager.php b/core/lib/Drupal/Core/Archiver/ArchiverManager.php index f3bfc96..4c7f30c 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 Drupal\Core\ClassLoader\SearchableNamespacesInterface; /** * Archiver plugin manager. @@ -20,9 +21,10 @@ class ArchiverManager extends DefaultPluginManager { /** * Constructs a ArchiverManager object. * - * @param \Traversable $namespaces - * An object that implements \Traversable which contains the root paths - * keyed by the corresponding namespace to look for plugin implementations. + * @param SearchableNamespacesInterface $root_namespaces + * Searchable namespaces for enabled extensions and core. + * This will be used to build the plugin namespaces by adding the suffix. + * E.g. the root namespace for a module is Drupal\$module. * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend * Cache backend instance to use. * @param \Drupal\Core\Language\LanguageManager $language_manager @@ -30,8 +32,8 @@ 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) { - parent::__construct('Archiver', $namespaces); + public function __construct(SearchableNamespacesInterface $root_namespaces, CacheBackendInterface $cache_backend, LanguageManager $language_manager, ModuleHandlerInterface $module_handler) { + parent::__construct($root_namespaces, 'Archiver'); $this->alterInfo($module_handler, 'archiver_info'); $this->setCacheBackend($cache_backend, $language_manager, 'archiver_info'); } diff --git a/core/lib/Drupal/Core/ClassLoader/ClassLoaderAdapterInterface.php b/core/lib/Drupal/Core/ClassLoader/ClassLoaderAdapterInterface.php new file mode 100644 index 0000000..46bbc63 --- /dev/null +++ b/core/lib/Drupal/Core/ClassLoader/ClassLoaderAdapterInterface.php @@ -0,0 +1,39 @@ +addNamespacePSR0('Drupal\\' . $extension_name, DRUPAL_ROOT . '/' . $extension_dir . '/lib'); + + // Register the extension's "src/" directory for PSR-4 class loading. + // @todo + // - Make a choice between "src/" and "lib/" for PSR-4 classes. + // - Have a transition phase to migrate modules to PSR-4. + // - Remove PSR-0 after this transition phase. + $this->addNamespacePSRX('Drupal\\' . $extension_name, DRUPAL_ROOT . '/' . $extension_dir . '/src'); + } + + /** + * @inheritdoc + */ + function addDrupalExtensionTests($extension_name, $extension_dir) { + $this->addNamespacePSR0('Drupal\\' . $extension_name . '\\Tests', DRUPAL_ROOT . '/' . $extension_dir . '/tests'); + $this->addNamespacePSRX('Drupal\\' . $extension_name . '\\Tests', DRUPAL_ROOT . '/' . $extension_dir . '/tests/src'); + } + + /** + * @inheritdoc + */ + function addDrupalExtensionsByFileName(array $extension_filenames) { + foreach ($extension_filenames as $extension_name => $extension_filename) { + $this->addDrupalExtension($extension_name, dirname($extension_filename)); + } + } + + /** + * @inheritdoc + */ + function addDrupalCore() { + $this->addNamespacePSR0('Drupal\Component', DRUPAL_ROOT . '/core/lib'); + $this->addNamespacePSR0('Drupal\Core', DRUPAL_ROOT . '/core/lib'); + } + + /** + * @inheritdoc + */ + function addDrupalCoreTests() { + $this->addNamespacePSR0('Drupal\\Tests', DRUPAL_ROOT . '/core/tests'); + } + + /** + * @param array $namespaces + * @return SearchableNamespacesInterface + * @throws \Exception + */ + function buildSearchableNamespaces(array $namespaces = array()) { + $searchable = new SearchableNamespaces($this->finder); + $searchable->addNamespaces($namespaces); + return $searchable; + } +} \ No newline at end of file diff --git a/core/lib/Drupal/Core/ClassLoader/NamespaceInspectorAdapterInterface.php b/core/lib/Drupal/Core/ClassLoader/NamespaceInspectorAdapterInterface.php new file mode 100644 index 0000000..88d7a1e --- /dev/null +++ b/core/lib/Drupal/Core/ClassLoader/NamespaceInspectorAdapterInterface.php @@ -0,0 +1,15 @@ +alterInfo($module_handler, 'condition_info'); $this->setCacheBackend($cache_backend, $language_manager, 'condition'); - $annotation_namespaces = array( - 'Drupal\Core\Condition\Annotation' => DRUPAL_ROOT . '/core/lib', - ); - parent::__construct('Condition', $namespaces, $annotation_namespaces, 'Drupal\Core\Condition\Annotation\Condition'); + parent::__construct($root_namespaces, 'Condition', array('Drupal\Core\Condition\Annotation'), 'Drupal\Core\Condition\Annotation\Condition'); } /** diff --git a/core/lib/Drupal/Core/DrupalKernel.php b/core/lib/Drupal/Core/DrupalKernel.php index af1fe52..9170c55 100644 --- a/core/lib/Drupal/Core/DrupalKernel.php +++ b/core/lib/Drupal/Core/DrupalKernel.php @@ -12,7 +12,7 @@ use Drupal\Core\CoreServiceProvider; use Drupal\Core\DependencyInjection\ContainerBuilder; use Drupal\Core\DependencyInjection\YamlFileLoader; -use Symfony\Component\ClassLoader\ClassLoader; +use Drupal\Core\ClassLoader\NamespaceInspectorAdapter; use Symfony\Component\Config\Loader\LoaderInterface; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; use Symfony\Component\DependencyInjection\Dumper\PhpDumper; @@ -97,7 +97,7 @@ class DrupalKernel implements DrupalKernelInterface, TerminableInterface { /** * The classloader object. * - * @var \Symfony\Component\ClassLoader\ClassLoader + * @var NamespaceInspectorAdapter */ protected $classLoader; @@ -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 NamespaceInspectorAdapter $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,7 @@ 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->addDrupalExtensionsByFileName($module_filenames); // Load each module's serviceProvider class. foreach ($this->moduleList as $module => $weight) { @@ -417,8 +417,7 @@ protected function initializeContainer() { // All namespaces must be registered before we attempt to use any service // from the container. $container_modules = $this->container->getParameter('container.modules'); - $namespaces_before = $this->classLoader->getPrefixes(); - $this->registerNamespaces($this->getModuleNamespaces($container_modules)); + $this->classLoader->addDrupalExtensionsByFileName($container_modules); // If 'container.modules' is wrong, the container must be rebuilt. if (!isset($this->moduleList)) { @@ -427,13 +426,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 +447,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 +465,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,21 +516,11 @@ 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()); - // Add all components in \Drupal\Core and \Drupal\Component that have a - // Plugin directory. - foreach (array('Core', 'Component') as $parent_directory) { - $path = DRUPAL_ROOT . '/core/lib/Drupal/' . $parent_directory; - foreach (new \DirectoryIterator($path) as $component) { - if (!$component->isDot() && is_dir($component->getPathname() . '/Plugin')) { - $namespaces['Drupal\\' . $parent_directory .'\\' . $component->getFilename()] = DRUPAL_ROOT . '/core/lib'; - } - } - } + $namespaces = $this->getDiscoveryBaseNamespaces(); $container->setParameter('container.namespaces', $namespaces); // Register synthetic services. - $container->register('class_loader', 'Symfony\Component\ClassLoader\ClassLoader')->setSynthetic(TRUE); + $container->register('class_loader', 'Drupal\Core\ClassLoader\NamespaceInspectorAdapterInterface')->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,20 +646,30 @@ protected function getModuleFileNames() { } /** - * Gets the namespaces of each enabled module. + * Gets the root namespaces that are used for plugin discovery. + * + * @return array + * Array of namespaces, keys are identical to the values. */ - protected function getModuleNamespaces($moduleFileNames) { + protected function getDiscoveryBaseNamespaces() { $namespaces = array(); - foreach ($moduleFileNames as $module => $filename) { - $namespaces["Drupal\\$module"] = DRUPAL_ROOT . '/' . dirname($filename) . '/lib'; + + // Collect root namespaces for extensions. + foreach ($this->getModuleFileNames() as $module => $filename) { + $namespaces[] = 'Drupal\\' . $module; } - return $namespaces; - } - /** - * Registers a list of namespaces. - */ - protected function registerNamespaces(array $namespaces = array()) { - $this->classLoader->addPrefixes($namespaces); + // Add all components in \Drupal\Core and \Drupal\Component that have a + // Plugin directory. + foreach (array('Core', 'Component') as $parent_directory) { + $path = DRUPAL_ROOT . '/core/lib/Drupal/' . $parent_directory; + foreach (new \DirectoryIterator($path) as $component) { + if (!$component->isDot() && is_dir($component->getPathname() . '/Plugin')) { + $namespaces[] = 'Drupal\\' . $parent_directory . '\\' . $component->getFilename(); + } + } + } + + return $namespaces; } } diff --git a/core/lib/Drupal/Core/Entity/EntityManager.php b/core/lib/Drupal/Core/Entity/EntityManager.php index 4766a91..aaef43c 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 Drupal\Core\ClassLoader\SearchableNamespacesInterface; /** * Manages entity type plugin definitions. @@ -90,9 +91,10 @@ class EntityManager extends PluginManagerBase { /** * Constructs a new Entity plugin manager. * - * @param \Traversable $namespaces - * An object that implements \Traversable which contains the root paths - * keyed by the corresponding namespace to look for plugin implementations, + * @param SearchableNamespacesInterface $root_namespaces + * Searchable namespaces for enabled extensions and core. + * This will be used to build the plugin namespaces by adding the suffix. + * E.g. the root namespace for a module is Drupal\$module. * @param \Symfony\Component\DependencyInjection\ContainerInterface $container * The service container this object should use. * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler @@ -102,17 +104,15 @@ 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) { - // Allow the plugin definition to be altered by hook_entity_info_alter(). - $annotation_namespaces = array( - 'Drupal\Core\Entity\Annotation' => DRUPAL_ROOT . '/core/lib', - ); + public function __construct(SearchableNamespacesInterface $root_namespaces, ContainerInterface $container, ModuleHandlerInterface $module_handler, CacheBackendInterface $cache, LanguageManager $language_manager) { $this->moduleHandler = $module_handler; $this->cache = $cache; $this->languageManager = $language_manager; - $this->discovery = new AnnotatedClassDiscovery('Core/Entity', $namespaces, $annotation_namespaces, 'Drupal\Core\Entity\Annotation\EntityType'); + $this->discovery = new AnnotatedClassDiscovery($root_namespaces, 'Core\Entity', 'Drupal\Core\Entity\Annotation\EntityType'); + $this->discovery->addAnnotationNamespace('Drupal\Core\Entity\Annotation'); + // Allow the plugin definition to be altered by hook_entity_info_alter(). $this->discovery = new InfoHookDecorator($this->discovery, 'entity_info'); $this->discovery = new AlterDecorator($this->discovery, 'entity_info'); $this->discovery = new CacheDecorator($this->discovery, 'entity_info:' . $this->languageManager->getLanguage(Language::TYPE_INTERFACE)->id, 'cache', CacheBackendInterface::CACHE_PERMANENT, array('entity_info' => TRUE)); diff --git a/core/lib/Drupal/Core/Entity/Field/FieldTypePluginManager.php b/core/lib/Drupal/Core/Entity/Field/FieldTypePluginManager.php index 9f9ceb8..de0f3c9 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 Drupal\Core\ClassLoader\SearchableNamespacesInterface; /** * Plugin manager for 'field type' plugins. @@ -31,9 +32,10 @@ class FieldTypePluginManager extends DefaultPluginManager { /** * Constructs the FieldTypePluginManager object * - * @param \Traversable $namespaces - * An object that implements \Traversable which contains the root paths - * keyed by the corresponding namespace to look for plugin implementations. + * @param SearchableNamespacesInterface $root_namespaces + * Searchable namespaces for enabled extensions and core. + * This will be used to build the plugin namespaces by adding the suffix. + * E.g. the root namespace for a module is Drupal\$module. * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend * Cache backend instance to use. * @param \Drupal\Core\Language\LanguageManager $language_manager @@ -41,11 +43,8 @@ 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) { - $annotation_namespaces = array( - 'Drupal\Core\Entity\Annotation' => DRUPAL_ROOT . '/core/lib', - ); - parent::__construct('field/field_type', $namespaces, $annotation_namespaces, 'Drupal\Core\Entity\Annotation\FieldType'); + public function __construct(SearchableNamespacesInterface $root_namespaces, CacheBackendInterface $cache_backend, LanguageManager $language_manager, ModuleHandlerInterface $module_handler) { + parent::__construct($root_namespaces, 'field\field_type', array('Drupal\Core\Entity\Annotation'), 'Drupal\Core\Entity\Annotation\FieldType'); $this->alterInfo($module_handler, 'field_info'); $this->setCacheBackend($cache_backend, $language_manager, 'field_types'); diff --git a/core/lib/Drupal/Core/Menu/LocalActionManager.php b/core/lib/Drupal/Core/Menu/LocalActionManager.php index 7857f7a..aff6140 100644 --- a/core/lib/Drupal/Core/Menu/LocalActionManager.php +++ b/core/lib/Drupal/Core/Menu/LocalActionManager.php @@ -12,6 +12,7 @@ use Drupal\Core\Plugin\DefaultPluginManager; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Controller\ControllerResolverInterface; +use Drupal\Core\ClassLoader\SearchableNamespacesInterface; /** * Manages discovery and instantiation of menu local action plugins. @@ -47,9 +48,10 @@ class LocalActionManager extends DefaultPluginManager { /** * Constructs a LocalActionManager object. * - * @param \Traversable $namespaces - * An object that implements \Traversable which contains the root paths - * keyed by the corresponding namespace to look for plugin implementations. + * @param SearchableNamespacesInterface $root_namespaces + * Searchable namespaces for enabled extensions and core. + * This will be used to build the plugin namespaces by adding the suffix. + * E.g. the root namespace for a module is Drupal\$module. * @param \Symfony\Component\HttpKernel\Controller\ControllerResolverInterface $controller_resolver * An object to use in introspecting route methods. * @param \Symfony\Component\HttpFoundation\Request $request @@ -58,8 +60,8 @@ class LocalActionManager extends DefaultPluginManager { * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler * The module handler. */ - public function __construct(\Traversable $namespaces, ControllerResolverInterface $controller_resolver, Request $request, ModuleHandlerInterface $module_handler) { - parent::__construct('Menu\LocalAction', $namespaces, array(), 'Drupal\Core\Annotation\Menu\LocalAction'); + public function __construct(SearchableNamespacesInterface $root_namespaces, ControllerResolverInterface $controller_resolver, Request $request, ModuleHandlerInterface $module_handler) { + parent::__construct($root_namespaces, 'Menu\LocalAction', array(), 'Drupal\Core\Annotation\Menu\LocalAction'); $this->controllerResolver = $controller_resolver; $this->request = $request; diff --git a/core/lib/Drupal/Core/Menu/LocalTaskManager.php b/core/lib/Drupal/Core/Menu/LocalTaskManager.php index 70db0b0..b9594c7 100644 --- a/core/lib/Drupal/Core/Menu/LocalTaskManager.php +++ b/core/lib/Drupal/Core/Menu/LocalTaskManager.php @@ -12,6 +12,7 @@ use Drupal\Core\Routing\RouteProviderInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Controller\ControllerResolverInterface; +use Drupal\Core\ClassLoader\SearchableNamespacesInterface; /** * Manages discovery and instantiation of menu local task plugins. @@ -53,9 +54,10 @@ class LocalTaskManager extends DefaultPluginManager { /** * Constructs a \Drupal\Core\Menu\LocalTaskManager object. * - * @param \Traversable $namespaces - * An object that implements \Traversable which contains the root paths - * keyed by the corresponding namespace to look for plugin implementations, + * @param SearchableNamespacesInterface $root_namespaces + * Searchable namespaces for enabled extensions and core. + * This will be used to build the plugin namespaces by adding the suffix. + * E.g. the root namespace for a module is Drupal\$module. * @param \Symfony\Component\HttpKernel\Controller\ControllerResolverInterface $controller_resolver * An object to use in introspecting route methods. * @param \Symfony\Component\HttpFoundation\Request $request @@ -65,8 +67,8 @@ class LocalTaskManager extends DefaultPluginManager { * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler * The module handler.u */ - public function __construct(\Traversable $namespaces, ControllerResolverInterface $controller_resolver, Request $request, RouteProviderInterface $route_provider, ModuleHandlerInterface $module_handler) { - parent::__construct('Menu\LocalTask', $namespaces, array(), 'Drupal\Core\Annotation\Menu\LocalTask'); + public function __construct(SearchableNamespacesInterface $root_namespaces, ControllerResolverInterface $controller_resolver, Request $request, RouteProviderInterface $route_provider, ModuleHandlerInterface $module_handler) { + parent::__construct($root_namespaces, 'Menu\LocalTask', array(), 'Drupal\Core\Annotation\Menu\LocalTask'); $this->controllerResolver = $controller_resolver; $this->request = $request; $this->routeProvider = $route_provider; diff --git a/core/lib/Drupal/Core/Plugin/DefaultPluginManager.php b/core/lib/Drupal/Core/Plugin/DefaultPluginManager.php index 7a1cdac..ea61e6b 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 Drupal\Core\ClassLoader\SearchableNamespacesInterface; /** * Base class for plugin managers. @@ -60,13 +61,6 @@ class DefaultPluginManager extends PluginManagerBase implements PluginManagerInt protected $alterHook; /** - * The plugin's subdirectory, for example views/filter. - * - * @var string - */ - protected $subdir; - - /** * The module handler to invoke the alter hook. * * @var \Drupal\Core\Extension\ModuleHandlerInterface @@ -81,13 +75,15 @@ class DefaultPluginManager extends PluginManagerBase implements PluginManagerInt protected $languageManager; /** - * Creates the discovery object. + * Creates the DefaultPluginManager object. * - * @param string $subdir - * The plugin's subdirectory, for example views/filter. - * @param \Traversable $namespaces - * An object that implements \Traversable which contains the root paths - * keyed by the corresponding namespace to look for plugin implementations + * @param SearchableNamespacesInterface $root_namespaces + * Searchable namespaces for enabled extensions and core. + * This will be used to build the plugin namespaces by adding the suffix. + * E.g. the root namespace for a module is Drupal\$module. + * @param string $namespace_suffix + * Suffix to build plugin namespaces from root namespaces, e.g. 'views\filter'. + * The resulting plugin namespace will be $root_namespace\Plugin\$namespace_suffix. * @param array $annotation_namespaces * (optional) The namespaces of classes that can be used as annotations. * Defaults to an empty array. @@ -95,9 +91,11 @@ 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') { - $this->subdir = $subdir; - $this->discovery = new AnnotatedClassDiscovery($subdir, $namespaces, $annotation_namespaces, $plugin_definition_annotation_name); + public function __construct(SearchableNamespacesInterface $root_namespaces, $namespace_suffix, $annotation_namespaces = array(), $plugin_definition_annotation_name = 'Drupal\Component\Annotation\Plugin') { + $this->discovery = new AnnotatedClassDiscovery($root_namespaces, $namespace_suffix, $plugin_definition_annotation_name); + foreach ($annotation_namespaces as $namespace) { + $this->discovery->addAnnotationNamespace($namespace); + } $this->discovery = new DerivativeDiscoveryDecorator($this->discovery); $this->factory = new ContainerFactory($this); } diff --git a/core/lib/Drupal/Core/Plugin/Discovery/AnnotatedClassDiscovery.php b/core/lib/Drupal/Core/Plugin/Discovery/AnnotatedClassDiscovery.php index 6695db7..fd7c1fe 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 Drupal\Core\ClassLoader\SearchableNamespacesInterface; /** * Defines a discovery mechanism to find annotated plugins in PSR-0 namespaces. @@ -15,51 +16,23 @@ class AnnotatedClassDiscovery extends ComponentAnnotatedClassDiscovery { /** - * The module name that defines the plugin type. - * - * @var string - */ - protected $owner; - - /** - * The plugin type, for example filter. - * - * @var string - */ - protected $type; - - /** - * An object containing the namespaces to look for plugin implementations. - * - * @var \Traversable - */ - protected $rootNamespacesIterator; - - /** * Constructs an AnnotatedClassDiscovery object. * - * @param string $subdir - * The plugin's subdirectory, for example views/filter. - * @param \Traversable $root_namespaces - * An object that implements \Traversable which contains the root paths - * keyed by the corresponding namespace to look for plugin implementations, - * \Plugin\$subdir will be appended to each namespace. - * @param array $annotation_namespaces - * (optional) The namespaces of classes that can be used as annotations. - * Defaults to an empty array. + * @param SearchableNamespacesInterface $root_namespaces + * Searchable namespaces for enabled extensions and core. + * This will be used to build the plugin namespaces by adding the suffix. + * E.g. the root namespace for a module is Drupal\$module. + * @param string $namespace_suffix + * Suffix to build plugin namespaces from root namespaces, e.g. 'views\filter'. + * The resulting plugin namespace will be $root_namespace\Plugin\$namespace_suffix. * @param string $plugin_definition_annotation_name * (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') { - $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(); - parent::__construct($plugin_namespaces, $annotation_namespaces, $plugin_definition_annotation_name); + function __construct(SearchableNamespacesInterface $root_namespaces, $namespace_suffix, $plugin_definition_annotation_name = 'Drupal\Component\Annotation\Plugin') { + parent::__construct($root_namespaces, 'Plugin\\' . $namespace_suffix, $plugin_definition_annotation_name); + $this->addAnnotationNamespace('Drupal\Component\Annotation'); + $this->addAnnotationNamespace('Drupal\Core\Annotation'); } /** @@ -95,16 +68,4 @@ protected function getProviderFromNamespace($namespace) { return NULL; } - /** - * {@inheritdoc} - */ - protected function getPluginNamespaces() { - $plugin_namespaces = array(); - foreach ($this->rootNamespacesIterator as $namespace => $dir) { - $plugin_namespaces["$namespace\\Plugin\\{$this->subdir}"] = array($dir); - } - - return $plugin_namespaces; - } - } diff --git a/core/lib/Drupal/Core/TypedData/TypedDataManager.php b/core/lib/Drupal/Core/TypedData/TypedDataManager.php index 5c48fb0..1608408 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 Drupal\Core\ClassLoader\SearchableNamespacesInterface; /** * Manages data type plugins. @@ -45,14 +46,25 @@ class TypedDataManager extends DefaultPluginManager { */ protected $prototypes = array(); - public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, LanguageManager $language_manager, ModuleHandlerInterface $module_handler) { + /** + * Overrides \Drupal\Core\Plugin\DefaultPluginManager::__construct(). + * + * @param SearchableNamespacesInterface $root_namespaces + * Searchable namespaces for enabled extensions and core. + * This will be used to build the plugin namespaces by adding the suffix. + * E.g. the root namespace for a module is Drupal\$module. + * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend + * Cache backend instance to use. + * @param \Drupal\Core\Language\LanguageManager $language_manager + * The language manager. + * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler + * The module handler to invoke the alter hook with. + */ + public function __construct(SearchableNamespacesInterface $root_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', - ); - parent::__construct('DataType', $namespaces, $annotation_namespaces, 'Drupal\Core\TypedData\Annotation\DataType'); + parent::__construct($root_namespaces, 'DataType', array('Drupal\Core\TypedData\Annotation'), '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..9944e27 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 Drupal\Core\ClassLoader\SearchableNamespacesInterface; /** * Constraint plugin manager. @@ -34,11 +35,12 @@ class ConstraintManager extends DefaultPluginManager { /** - * Overrides \Drupal\Component\Plugin\PluginManagerBase::__construct(). + * Overrides \Drupal\Core\Plugin\DefaultPluginManager::__construct(). * - * @param \Traversable $namespaces - * An object that implements \Traversable which contains the root paths - * keyed by the corresponding namespace to look for plugin implementations. + * @param SearchableNamespacesInterface $root_namespaces + * Searchable namespaces for enabled extensions and core. + * This will be used to build the plugin namespaces by adding the suffix. + * E.g. the root namespace for a module is Drupal\$module. * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend * Cache backend instance to use. * @param \Drupal\Core\Language\LanguageManager $language_manager @@ -46,8 +48,8 @@ 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) { - parent::__construct('Validation/Constraint', $namespaces); + public function __construct(SearchableNamespacesInterface $root_namespaces, CacheBackendInterface $cache_backend, LanguageManager $language_manager, ModuleHandlerInterface $module_handler) { + parent::__construct($root_namespaces, 'Validation\Constraint'); $this->discovery = new StaticDiscoveryDecorator($this->discovery, array($this, 'registerDefinitions')); $this->alterInfo($module_handler, 'validation_constraint'); $this->setCacheBackend($cache_backend, $language_manager, '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..24cd528 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 Drupal\Core\ClassLoader\SearchableNamespacesInterface; /** * Manages aggregator plugins. @@ -23,22 +24,20 @@ class AggregatorPluginManager extends PluginManagerBase { * * @param string $type * The plugin type, for example fetcher. - * @param \Traversable $namespaces - * An object that implements \Traversable which contains the root paths - * keyed by the corresponding namespace to look for plugin implementations, + * @param SearchableNamespacesInterface $root_namespaces + * Searchable namespaces for enabled extensions and core. + * This will be used to build the plugin namespaces by adding the suffix. + * E.g. the root namespace for a module is Drupal\$module. */ - public function __construct($type, \Traversable $namespaces) { + public function __construct($type, SearchableNamespacesInterface $root_namespaces) { $type_annotations = array( 'fetcher' => 'Drupal\aggregator\Annotation\AggregatorFetcher', 'parser' => 'Drupal\aggregator\Annotation\AggregatorParser', 'processor' => 'Drupal\aggregator\Annotation\AggregatorProcessor', ); - $annotation_namespaces = array( - 'Drupal\aggregator\Annotation' => DRUPAL_ROOT . '/core/modules/aggregator/lib', - ); - - $this->discovery = new AnnotatedClassDiscovery("aggregator/$type", $namespaces, $annotation_namespaces, $type_annotations[$type]); + $this->discovery = new AnnotatedClassDiscovery($root_namespaces, "aggregator\\$type", $type_annotations[$type]); + $this->discovery->addAnnotationNamespace('Drupal\aggregator\Annotation'); $this->discovery = new CacheDecorator($this->discovery, "aggregator_$type:" . language(Language::TYPE_INTERFACE)->id); $this->factory = new DefaultFactory($this->discovery); } 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..c3f4872 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 Drupal\Core\ClassLoader\SearchableNamespacesInterface; /** * Manages discovery and instantiation of block plugins. * @@ -22,9 +23,10 @@ class BlockManager extends DefaultPluginManager { /** * Constructs a new \Drupal\block\Plugin\Type\BlockManager object. * - * @param \Traversable $namespaces - * An object that implements \Traversable which contains the root paths - * keyed by the corresponding namespace to look for plugin implementations. + * @param SearchableNamespacesInterface $root_namespaces + * Searchable namespaces for enabled extensions and core. + * This will be used to build the plugin namespaces by adding the suffix. + * E.g. the root namespace for a module is Drupal\$module. * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend * Cache backend instance to use. * @param \Drupal\Core\Language\LanguageManager $language_manager @@ -32,8 +34,8 @@ 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) { - parent::__construct('Block', $namespaces); + public function __construct(SearchableNamespacesInterface $root_namespaces, CacheBackendInterface $cache_backend, LanguageManager $language_manager, ModuleHandlerInterface $module_handler) { + parent::__construct($root_namespaces, 'Block'); $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..ca2568a 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 Drupal\Core\ClassLoader\SearchableNamespacesInterface; /** * CKEditor Plugin manager. @@ -24,13 +25,14 @@ class CKEditorPluginManager extends PluginManagerBase { /** * Overrides \Drupal\Component\Plugin\PluginManagerBase::__construct(). * - * @param \Traversable $namespaces - * An object that implements \Traversable which contains the root paths - * keyed by the corresponding namespace to look for plugin implementations, + * @param SearchableNamespacesInterface $root_namespaces + * Searchable namespaces for enabled extensions and core. + * This will be used to build the plugin namespaces by adding the suffix. + * E.g. the root namespace for a module is Drupal\$module. */ - public function __construct(\Traversable $namespaces) { - $annotation_namespaces = array('Drupal\ckeditor\Annotation' => $namespaces['Drupal\ckeditor']); - $this->discovery = new AnnotatedClassDiscovery('CKEditorPlugin', $namespaces, $annotation_namespaces, 'Drupal\ckeditor\Annotation\CKEditorPlugin'); + public function __construct(SearchableNamespacesInterface $root_namespaces) { + $this->discovery = new AnnotatedClassDiscovery($root_namespaces, 'CKEditorPlugin', 'Drupal\ckeditor\Annotation\CKEditorPlugin'); + $this->discovery->addAnnotationNamespace('Drupal\ckeditor\Annotation'); $this->discovery = new AlterDecorator($this->discovery, 'ckeditor_plugin_info'); $this->discovery = new CacheDecorator($this->discovery, 'ckeditor_plugin'); $this->factory = new DefaultFactory($this->discovery); diff --git a/core/modules/edit/lib/Drupal/edit/Plugin/InPlaceEditorManager.php b/core/modules/edit/lib/Drupal/edit/Plugin/InPlaceEditorManager.php index f4c544b..6352898 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 Drupal\Core\ClassLoader\SearchableNamespacesInterface; /** * Editor manager. @@ -24,13 +25,14 @@ class InPlaceEditorManager extends PluginManagerBase { /** * Overrides \Drupal\Component\Plugin\PluginManagerBase::__construct(). * - * @param \Traversable $namespaces - * An object that implements \Traversable which contains the root paths - * keyed by the corresponding namespace to look for plugin implementations, + * @param SearchableNamespacesInterface $root_namespaces + * Searchable namespaces for enabled extensions and core. + * This will be used to build the plugin namespaces by adding the suffix. + * E.g. the root namespace for a module is Drupal\$module. */ - public function __construct(\Traversable $namespaces) { - $annotation_namespaces = array('Drupal\edit\Annotation' => $namespaces['Drupal\edit']); - $this->discovery = new AnnotatedClassDiscovery('InPlaceEditor', $namespaces, $annotation_namespaces, 'Drupal\edit\Annotation\InPlaceEditor'); + public function __construct(SearchableNamespacesInterface $root_namespaces) { + $this->discovery = new AnnotatedClassDiscovery($root_namespaces, 'InPlaceEditor', 'Drupal\edit\Annotation\InPlaceEditor'); + $this->discovery->addAnnotationNamespace('Drupal\edit\Annotation'); $this->discovery = new AlterDecorator($this->discovery, 'edit_editor'); $this->discovery = new CacheDecorator($this->discovery, 'edit:editor'); $this->factory = new DefaultFactory($this->discovery); diff --git a/core/modules/editor/lib/Drupal/editor/Plugin/EditorManager.php b/core/modules/editor/lib/Drupal/editor/Plugin/EditorManager.php index 2d05b1a..c55320d 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 Drupal\Core\ClassLoader\SearchableNamespacesInterface; /** * Configurable text editor manager. @@ -22,13 +23,14 @@ class EditorManager extends PluginManagerBase { /** * Overrides \Drupal\Component\Plugin\PluginManagerBase::__construct(). * - * @param \Traversable $namespaces - * An object that implements \Traversable which contains the root paths - * keyed by the corresponding namespace to look for plugin implementations, + * @param SearchableNamespacesInterface $root_namespaces + * Searchable namespaces for enabled extensions and core. + * This will be used to build the plugin namespaces by adding the suffix. + * E.g. the root namespace for a module is Drupal\$module. */ - public function __construct(\Traversable $namespaces) { - $annotation_namespaces = array('Drupal\editor\Annotation' => $namespaces['Drupal\editor']); - $this->discovery = new AnnotatedClassDiscovery('Editor', $namespaces, $annotation_namespaces, 'Drupal\editor\Annotation\Editor'); + public function __construct(SearchableNamespacesInterface $root_namespaces) { + $this->discovery = new AnnotatedClassDiscovery($root_namespaces, 'Editor', 'Drupal\editor\Annotation\Editor'); + $this->discovery->addAnnotationNamespace('Drupal\editor\Annotation'); $this->discovery = new AlterDecorator($this->discovery, 'editor_info'); $this->discovery = new CacheDecorator($this->discovery, 'editor'); $this->factory = new ContainerFactory($this->discovery); 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 c5ecb8f..daeef2d 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 @@ -15,6 +15,7 @@ use Drupal\Core\Plugin\DefaultPluginManager; use Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery; use Drupal\entity_reference\Plugin\Type\Selection\SelectionBroken; +use Drupal\Core\ClassLoader\SearchableNamespacesInterface; /** * Plugin type manager for the Entity Reference Selection plugin. @@ -24,12 +25,9 @@ class SelectionPluginManager extends DefaultPluginManager { /** * {@inheritdoc} */ - public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, LanguageManager $language_manager, ModuleHandlerInterface $module_handler) { - $this->subdir = 'entity_reference/selection'; - $annotation_namespaces = array( - 'Drupal\entity_reference\Annotation' => $namespaces['Drupal\entity_reference'] - ); - $this->discovery = new AnnotatedClassDiscovery($this->subdir, $namespaces, $annotation_namespaces, 'Drupal\entity_reference\Annotation\EntityReferenceSelection'); + public function __construct(SearchableNamespacesInterface $root_namespaces, CacheBackendInterface $cache_backend, LanguageManager $language_manager, ModuleHandlerInterface $module_handler) { + $this->discovery = new AnnotatedClassDiscovery($root_namespaces, 'entity_reference\selection', 'Drupal\entity_reference\Annotation\EntityReferenceSelection'); + $this->discovery->addAnnotationNamespace('Drupal\entity_reference\Annotation'); // We're not using the parent constructor because we use a different factory // method and don't need the derivative discovery decorator. 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 6eb0c9a..6c30aed 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 Drupal\Core\ClassLoader\SearchableNamespacesInterface; /** * Plugin type manager for field formatters. @@ -33,9 +34,10 @@ class FormatterPluginManager extends DefaultPluginManager { /** * Constructs a FormatterPluginManager object. * - * @param \Traversable $namespaces - * An object that implements \Traversable which contains the root paths - * keyed by the corresponding namespace to look for plugin implementations. + * @param SearchableNamespacesInterface $root_namespaces + * Searchable namespaces for enabled extensions and core. + * This will be used to build the plugin namespaces by adding the suffix. + * E.g. the root namespace for a module is Drupal\$module. * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend * Cache backend instance to use. * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler @@ -43,10 +45,9 @@ 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 $root_namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler, LanguageManager $language_manager) { - parent::__construct('field/formatter', $namespaces, $annotation_namespaces, 'Drupal\field\Annotation\FieldFormatter'); + parent::__construct($root_namespaces, 'field\formatter', array('Drupal\field\Annotation'), 'Drupal\field\Annotation\FieldFormatter'); $this->setCacheBackend($cache_backend, $language_manager, 'field_formatter_types'); $this->alterInfo($module_handler, 'field_formatter_info'); 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..aa90554 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 Drupal\Core\ClassLoader\SearchableNamespacesInterface; /** * Plugin type manager for field widgets. @@ -42,9 +43,10 @@ class WidgetPluginManager extends DefaultPluginManager { /** * Constructs a WidgetPluginManager object. * - * @param \Traversable $namespaces - * An object that implements \Traversable which contains the root paths - * keyed by the corresponding namespace to look for plugin implementations. + * @param SearchableNamespacesInterface $root_namespaces + * Searchable namespaces for enabled extensions and core. + * This will be used to build the plugin namespaces by adding the suffix. + * E.g. the root namespace for a module is Drupal\$module. * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend * Cache backend instance to use. * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler @@ -52,8 +54,8 @@ 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) { - parent::__construct('field/widget', $namespaces); + public function __construct(SearchableNamespacesInterface $root_namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler, LanguageManager $language_manager) { + parent::__construct($root_namespaces, 'field\widget'); $this->setCacheBackend($cache_backend, $language_manager, 'field_widget_types'); $this->alterInfo($module_handler, 'field_widget_info'); diff --git a/core/modules/filter/lib/Drupal/filter/FilterPluginManager.php b/core/modules/filter/lib/Drupal/filter/FilterPluginManager.php index 881cdf7..1a76d6a 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 Drupal\Core\ClassLoader\SearchableNamespacesInterface; /** * Manages text processing filters. @@ -25,13 +26,14 @@ class FilterPluginManager extends PluginManagerBase { /** * Constructs a FilterPluginManager object. * - * @param \Traversable $namespaces - * An object that implements \Traversable which contains the root paths - * keyed by the corresponding namespace to look for plugin implementations. + * @param SearchableNamespacesInterface $root_namespaces + * Searchable namespaces for enabled extensions and core. + * This will be used to build the plugin namespaces by adding the suffix. + * E.g. the root namespace for a module is Drupal\$module. */ - public function __construct(\Traversable $namespaces) { - $annotation_namespaces = array('Drupal\filter\Annotation' => $namespaces['Drupal\filter']); - $this->discovery = new AnnotatedClassDiscovery('Filter', $namespaces, $annotation_namespaces, 'Drupal\filter\Annotation\Filter'); + public function __construct(SearchableNamespacesInterface $root_namespaces) { + $this->discovery = new AnnotatedClassDiscovery($root_namespaces, 'Filter', 'Drupal\filter\Annotation\Filter'); + $this->discovery->addAnnotationNamespace('Drupal\filter\Annotation'); $this->discovery = new AlterDecorator($this->discovery, 'filter_info'); $cache_key = 'filter_plugins:' . language(Language::TYPE_INTERFACE)->id; $cache_tags = array('filter_formats' => TRUE); 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..605af8d 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 Drupal\Core\ClassLoader\SearchableNamespacesInterface; /** * Layout plugin manager. @@ -25,13 +26,14 @@ class LayoutManager extends PluginManagerBase { /** * Overrides Drupal\Component\Plugin\PluginManagerBase::__construct(). * - * @param \Traversable $namespaces - * An object that implements \Traversable which contains the root paths - * keyed by the corresponding namespace to look for plugin implementations, + * @param SearchableNamespacesInterface $root_namespaces + * Searchable namespaces for enabled extensions and core. + * This will be used to build the plugin namespaces by adding the suffix. + * E.g. the root namespace for a module is Drupal\$module. */ - public function __construct(\Traversable $namespaces) { + public function __construct(SearchableNamespacesInterface $root_namespaces) { // Create layout plugin derivatives from declaratively defined layouts. - $this->discovery = new AnnotatedClassDiscovery('Layout', $namespaces); + $this->discovery = new AnnotatedClassDiscovery($root_namespaces, 'Layout'); $this->discovery = new DerivativeDiscoveryDecorator($this->discovery); $this->discovery = new ProcessDecorator($this->discovery, array($this, 'processDefinition')); 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..c6f3c96 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 Drupal\Core\ClassLoader\SearchableNamespacesInterface; /** * Manages discovery and instantiation of resource plugins. @@ -22,13 +23,14 @@ class ResourcePluginManager extends PluginManagerBase { /** * Overrides Drupal\Component\Plugin\PluginManagerBase::__construct(). * - * @param \Traversable $namespaces - * An object that implements \Traversable which contains the root paths - * keyed by the corresponding namespace to look for plugin implementations, + * @param SearchableNamespacesInterface $root_namespaces + * Searchable namespaces for enabled extensions and core. + * This will be used to build the plugin namespaces by adding the suffix. + * E.g. the root namespace for a module is Drupal\$module. */ - public function __construct(\Traversable $namespaces) { + public function __construct(SearchableNamespacesInterface $root_namespaces) { // Create resource plugin derivatives from declaratively defined resources. - $this->discovery = new AnnotatedClassDiscovery('rest/resource', $namespaces); + $this->discovery = new AnnotatedClassDiscovery($root_namespaces, 'rest\resource'); $this->discovery = new DerivativeDiscoveryDecorator($this->discovery); $this->discovery = new AlterDecorator($this->discovery, 'rest_resource'); $this->discovery = new CacheDecorator($this->discovery, 'rest'); diff --git a/core/modules/simpletest/simpletest.module b/core/modules/simpletest/simpletest.module index 587b6c0..57ac285 100644 --- a/core/modules/simpletest/simpletest.module +++ b/core/modules/simpletest/simpletest.module @@ -448,38 +448,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,27 +506,19 @@ function simpletest_classloader_register() { 'theme' => array('dir' => 'themes', 'extension' => 'info'), 'profile' => array('dir' => 'profiles', 'extension' => 'profile'), ); + $loader_adapter = drupal_classloader(); foreach ($types as $type => $info) { $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'); + $loader_adapter->addDrupalExtension($name, dirname($file->uri)); + $loader_adapter->addDrupalExtensionTests($name, dirname($file->uri)); // 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/', - )); + // Register the core/tests directory so we can find Drupal\Tests\UnitTestCase. + $loader_adapter->addDrupalCoreTests(); } /** diff --git a/core/modules/system/lib/Drupal/system/Plugin/ImageToolkitManager.php b/core/modules/system/lib/Drupal/system/Plugin/ImageToolkitManager.php index ddad9c6..fae135e 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 Drupal\Core\ClassLoader\SearchableNamespacesInterface; /** * Manages toolkit plugins. @@ -19,16 +20,17 @@ class ImageToolkitManager extends DefaultPluginManager { /** * Constructs the ImageToolkitManager object. * - * @param \Traversable $namespaces - * An object that implements \Traversable which contains the root paths - * keyed by the corresponding namespace to look for plugin implementations. + * @param SearchableNamespacesInterface $root_namespaces + * Searchable namespaces for enabled extensions and core. + * This will be used to build the plugin namespaces by adding the suffix. + * E.g. the root namespace for a module is Drupal\$module. * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend * Cache backend instance to use. * @param \Drupal\Core\Language\LanguageManager $language_manager * The language manager. */ - public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, LanguageManager $language_manager) { - parent::__construct('ImageToolkit', $namespaces); + public function __construct(SearchableNamespacesInterface $root_namespaces, CacheBackendInterface $cache_backend, LanguageManager $language_manager) { + parent::__construct($root_namespaces, 'ImageToolkit'); $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..9bd09b3 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 Drupal\Core\ClassLoader\SearchableNamespacesInterface; /** * Manages discovery and instantiation of Plugin UI plugins. @@ -24,12 +25,13 @@ class PluginUIManager extends PluginManagerBase { /** * Constructs a \Drupal\system\Plugin\Type\PluginUIManager object. * - * @param \Traversable $namespaces - * An object that implements \Traversable which contains the root paths - * keyed by the corresponding namespace to look for plugin implementations, + * @param SearchableNamespacesInterface $root_namespaces + * Searchable namespaces for enabled extensions and core. + * This will be used to build the plugin namespaces by adding the suffix. + * E.g. the root namespace for a module is Drupal\$module. */ - public function __construct(\Traversable $namespaces) { - $this->discovery = new AnnotatedClassDiscovery('PluginUI', $namespaces); + public function __construct(SearchableNamespacesInterface $root_namespaces) { + $this->discovery = new AnnotatedClassDiscovery($root_namespaces, 'PluginUI'); $this->discovery = new DerivativeDiscoveryDecorator($this->discovery); $this->discovery = new AlterDecorator($this->discovery, 'plugin_ui'); $this->discovery = new CacheDecorator($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..42baeb5 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('addNamespacePSR0'), '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('addNamespacePSR0'), '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/lib/Drupal/system/Tests/Plugin/Discovery/AnnotatedClassDiscoveryTest.php b/core/modules/system/lib/Drupal/system/Tests/Plugin/Discovery/AnnotatedClassDiscoveryTest.php index 5f8ee2d..8e5a3b7 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Plugin/Discovery/AnnotatedClassDiscoveryTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Plugin/Discovery/AnnotatedClassDiscoveryTest.php @@ -7,6 +7,7 @@ namespace Drupal\system\Tests\Plugin\Discovery; +use Drupal\Core\ClassLoader\NamespaceInspectorAdapter; use Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery; /** @@ -57,9 +58,18 @@ public function setUp() { 'provider' => 'plugin_test', ), ); - $namespaces = new \ArrayObject(array('Drupal\plugin_test' => DRUPAL_ROOT . '/core/modules/system/tests/modules/plugin_test/lib')); - $this->discovery = new AnnotatedClassDiscovery('plugin_test/fruit', $namespaces); - $this->emptyDiscovery = new AnnotatedClassDiscovery('non_existing_module/non_existing_plugin_type', $namespaces); + + // Build namespace inspector. + $adapter = NamespaceInspectorAdapter::start(); + $adapter->addDrupalExtension('plugin_test', 'core/modules/system/tests/modules/plugin_test'); + $adapter->addDrupalCore(); + + // Build searchable base namespaces. + $root_namespaces = $adapter->buildSearchableNamespaces(array('Drupal\plugin_test')); + + // Build annotated class discovery. + $this->discovery = new AnnotatedClassDiscovery($root_namespaces, 'plugin_test\fruit'); + $this->emptyDiscovery = new AnnotatedClassDiscovery($root_namespaces, 'non_existing_module\non_existing_plugin_type'); } } diff --git a/core/modules/system/lib/Drupal/system/Tests/Plugin/Discovery/CustomAnnotationClassDiscoveryTest.php b/core/modules/system/lib/Drupal/system/Tests/Plugin/Discovery/CustomAnnotationClassDiscoveryTest.php index cbf2a16..65f0847 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Plugin/Discovery/CustomAnnotationClassDiscoveryTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Plugin/Discovery/CustomAnnotationClassDiscoveryTest.php @@ -8,6 +8,7 @@ namespace Drupal\system\Tests\Plugin\Discovery; use Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery; +use Drupal\Core\ClassLoader\NamespaceInspectorAdapter; /** * Tests that a custom annotation class is used. @@ -41,13 +42,20 @@ protected function setUp() { 'provider' => 'plugin_test', ), ); - $root_namespaces = new \ArrayObject(array('Drupal\plugin_test' => DRUPAL_ROOT . '/core/modules/system/tests/modules/plugin_test/lib')); - $annotation_namespaces = array( - 'Drupal\plugin_test\Plugin\Annotation' => DRUPAL_ROOT . '/core/modules/system/tests/modules/plugin_test/lib', - ); - $this->discovery = new AnnotatedClassDiscovery('plugin_test/custom_annotation', $root_namespaces, $annotation_namespaces, 'Drupal\plugin_test\Plugin\Annotation\PluginExample'); - $this->emptyDiscovery = new AnnotatedClassDiscovery('non_existing_module/non_existing_plugin_type', $root_namespaces, $annotation_namespaces, 'Drupal\plugin_test\Plugin\Annotation\PluginExample'); + // Build namespace inspector. + $adapter = NamespaceInspectorAdapter::start(); + $adapter->addDrupalExtension('plugin_test', 'core/modules/system/tests/modules/plugin_test'); + $adapter->addDrupalCore(); + + // Build searchable namespaces. + $root_namespaces = $adapter->buildSearchableNamespaces(array('Drupal\plugin_test')); + + // Build annotated class discovery. + $this->discovery = new AnnotatedClassDiscovery($root_namespaces, 'plugin_test\custom_annotation', 'Drupal\plugin_test\Plugin\Annotation\PluginExample'); + $this->discovery->addAnnotationNamespace('Drupal\plugin_test\Plugin\Annotation'); + $this->emptyDiscovery = new AnnotatedClassDiscovery($root_namespaces, 'non_existing_module\non_existing_plugin_type', 'Drupal\plugin_test\Plugin\Annotation\PluginExample'); + $this->emptyDiscovery->addAnnotationNamespace('Drupal\plugin_test\Plugin\Annotation'); } } 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..607bd91 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 Drupal\Core\ClassLoader\SearchableNamespacesInterface; /** * Configurable tour manager. @@ -22,13 +23,14 @@ class TipPluginManager extends PluginManagerBase { /** * Overrides \Drupal\Component\Plugin\PluginManagerBase::__construct(). * - * @param \Traversable $namespaces - * An object that implements \Traversable which contains the root paths - * keyed by the corresponding namespace to look for plugin implementations, + * @param SearchableNamespacesInterface $root_namespaces + * Searchable namespaces for enabled extensions and core. + * This will be used to build the plugin namespaces by adding the suffix. + * E.g. the root namespace for a module is Drupal\$module. */ - public function __construct(\Traversable $namespaces) { - $annotation_namespaces = array('Drupal\tour\Annotation' => $namespaces['Drupal\tour']); - $this->discovery = new AnnotatedClassDiscovery('tour/tip', $namespaces, $annotation_namespaces, 'Drupal\tour\Annotation\Tip'); + public function __construct(SearchableNamespacesInterface $root_namespaces) { + $this->discovery = new AnnotatedClassDiscovery($root_namespaces, 'tour\tip', 'Drupal\tour\Annotation\Tip'); + $this->discovery->addAnnotationNamespace('Drupal\tour\Annotation'); $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..dec494c 100644 --- a/core/modules/views/lib/Drupal/views/Plugin/Discovery/ViewsHandlerDiscovery.php +++ b/core/modules/views/lib/Drupal/views/Plugin/Discovery/ViewsHandlerDiscovery.php @@ -7,12 +7,14 @@ namespace Drupal\views\Plugin\Discovery; -use Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery; +use Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery as CoreAnnotatedClassDiscovery; +use Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery as ComponentAnnotatedClassDiscovery; +use Drupal\Core\ClassLoader\SearchableNamespacesInterface; /** * Defines a discovery mechanism to find Views handlers in PSR-0 namespaces. */ -class ViewsHandlerDiscovery extends AnnotatedClassDiscovery { +class ViewsHandlerDiscovery extends CoreAnnotatedClassDiscovery { /** * The type of handler being discovered. @@ -22,60 +24,31 @@ class ViewsHandlerDiscovery extends AnnotatedClassDiscovery { protected $type; /** - * An object containing the namespaces to look for plugin implementations. - * - * @var \Traversable - */ - protected $rootNamespacesIterator; - - /** * Constructs a ViewsHandlerDiscovery object. * * @param string $type * The plugin type, for example filter. - * @param \Traversable $root_namespaces - * An object that implements \Traversable which contains the root paths - * keyed by the corresponding namespace to look for plugin implementations, + * @param SearchableNamespacesInterface $root_namespaces + * Searchable namespaces for enabled extensions and core. + * This will be used to build the plugin namespaces by adding the suffix. + * E.g. the root namespace for a module is Drupal\$module. */ - function __construct($type, \Traversable $root_namespaces) { + function __construct($type, SearchableNamespacesInterface $root_namespaces) { $this->type = $type; - $this->rootNamespacesIterator = $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 = $plugin_namespaces; - $this->annotationNamespaces = $annotation_namespaces; - $this->pluginDefinitionAnnotationName = 'Drupal\Component\Annotation\PluginID'; + ComponentAnnotatedClassDiscovery::__construct($root_namespaces, 'views\\' . $type, 'Drupal\Component\Annotation\PluginID'); + $this->addAnnotationNamespace('Drupal\Component\Annotation'); } /** * {@inheritdoc} */ public function getDefinitions() { - // Add the plugin_type to the definition. $definitions = parent::getDefinitions(); + // Add the plugin_type to each definition. foreach ($definitions as $key => $definition) { $definitions[$key]['plugin_type'] = $this->type; } return $definitions; } - /** - * {@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; - } - } diff --git a/core/modules/views/lib/Drupal/views/Plugin/ViewsHandlerManager.php b/core/modules/views/lib/Drupal/views/Plugin/ViewsHandlerManager.php index 4ac2fac..067b11f 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 Drupal\Core\ClassLoader\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..7afddbc 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 Drupal\Core\ClassLoader\SearchableNamespacesInterface; /** * Plugin type manager for all views plugins. @@ -26,12 +27,13 @@ class ViewsPluginManager extends PluginManagerBase { * * @param string $type * The plugin type, for example filter. - * @param \Traversable $namespaces - * An object that implements \Traversable which contains the root paths - * keyed by the corresponding namespace to look for plugin implementations, + * @param SearchableNamespacesInterface $root_namespaces + * Searchable namespaces for enabled extensions and core. + * This will be used to build the plugin namespaces by adding the suffix. + * E.g. the root namespace for a module is Drupal\$module. */ - public function __construct($type, \Traversable $namespaces) { - $this->discovery = new AnnotatedClassDiscovery("views/$type", $namespaces); + public function __construct($type, SearchableNamespacesInterface $root_namespaces) { + $this->discovery = new AnnotatedClassDiscovery($root_namespaces, "views\\$type"); $this->discovery = new DerivativeDiscoveryDecorator($this->discovery); $this->discovery = new ProcessDecorator($this->discovery, array($this, 'processDefinition')); $this->discovery = new AlterDecorator($this->discovery, 'views_plugins_' . $type); diff --git a/core/scripts/switch-psr4.sh b/core/scripts/switch-psr4.sh new file mode 100644 index 0000000..3cec824 --- /dev/null +++ b/core/scripts/switch-psr4.sh @@ -0,0 +1,108 @@ +#!/bin/php +isDot()) { + // do nothing + } + elseif ($fileinfo->isDir()) { + process_candidate_dir($fileinfo->getPathname()); + } + } +} + +function process_candidate_dir($dir) { + foreach (new \DirectoryIterator($dir) as $fileinfo) { + if ($fileinfo->isDot()) { + // Ignore "." and "..". + } + elseif ($fileinfo->isDir()) { + // It's a directory. + switch ($fileinfo->getFilename()) { + case 'lib': + case 'src': + case 'module_autoload_test': + // Ignore these directory names. + continue; + default: + // Look for more extensions in subdirectories. + process_candidate_dir($fileinfo->getPathname()); + } + } + else { + // It's a file. + if (preg_match('/^(.+).info.yml$/', $fileinfo->getFilename(), $m)) { + // It's a *.info.yml file, so we found an extension directory. + $extension_name = $m[1]; + } + } + } + if (isset($extension_name)) { + process_extension($extension_name, $dir); + } +} + +function process_extension($name, $dir) { + + // Move main module class files. + if (is_dir("$dir/lib/Drupal/$name")) { + // This is a module directory with a PSR-0 /lib/ folder. + if (!is_dir($dir . '/src')) { + mkdir($dir . '/src'); + } + rename("$dir/lib/Drupal/$name", "$dir/src"); + } + + // Move class files in tests directory. + if (is_dir("$dir/tests/Drupal/$name/Tests")) { + rename("$dir/tests/Drupal/$name/Tests", "$dir/tests/src"); + } + + // Clean up empty directories. + foreach (array( + "lib/Drupal/$name", + 'lib/Drupal', + 'lib', + "tests/Drupal/$name/Tests", + "tests/Drupal/$name", + "tests/Drupal", + ) as $subdir) { + if (is_dir_empty("$dir/$subdir")) { + rmdir("$dir/$subdir"); + } + } +} + +function is_dir_empty($dir) { + if (!is_readable($dir)) { + return NULL; + } + $handle = opendir($dir); + while (false !== ($entry = readdir($handle))) { + if ($entry != "." && $entry != "..") { + return FALSE; + } + } + return TRUE; +} diff --git a/core/vendor/autoload.php b/core/vendor/autoload.php index 51fea2c..2a887f8 100644 --- a/core/vendor/autoload.php +++ b/core/vendor/autoload.php @@ -4,4 +4,4 @@ require_once __DIR__ . '/composer' . '/autoload_real.php'; -return ComposerAutoloaderInit4ae4005bb4ec82f3372265feb7e84f37::getLoader(); +return ComposerAutoloaderInit6b1785896dc208c3c153c7ce6a3ee061::getLoader(); diff --git a/core/vendor/composer/autoload_namespaces.php b/core/vendor/composer/autoload_namespaces.php index 87cf570..4b26c8a 100644 --- a/core/vendor/composer/autoload_namespaces.php +++ b/core/vendor/composer/autoload_namespaces.php @@ -24,6 +24,7 @@ 'Symfony\\Component\\ClassLoader\\' => array($vendorDir . '/symfony/class-loader'), 'Symfony\\Cmf\\Component\\Routing' => array($vendorDir . '/symfony-cmf/routing'), 'Psr\\Log\\' => array($vendorDir . '/psr/log'), + 'Krautoload\\' => array($vendorDir . '/donquixote/krautoload/src'), 'Guzzle\\Stream' => array($vendorDir . '/guzzle/stream'), 'Guzzle\\Parser' => array($vendorDir . '/guzzle/parser'), 'Guzzle\\Http' => array($vendorDir . '/guzzle/http'), diff --git a/core/vendor/composer/autoload_real.php b/core/vendor/composer/autoload_real.php index 7d1d4ad..86fb01b 100644 --- a/core/vendor/composer/autoload_real.php +++ b/core/vendor/composer/autoload_real.php @@ -2,7 +2,7 @@ // autoload_real.php generated by Composer -class ComposerAutoloaderInit4ae4005bb4ec82f3372265feb7e84f37 +class ComposerAutoloaderInit6b1785896dc208c3c153c7ce6a3ee061 { private static $loader; @@ -19,9 +19,9 @@ public static function getLoader() return self::$loader; } - spl_autoload_register(array('ComposerAutoloaderInit4ae4005bb4ec82f3372265feb7e84f37', 'loadClassLoader'), true, true); + spl_autoload_register(array('ComposerAutoloaderInit6b1785896dc208c3c153c7ce6a3ee061', 'loadClassLoader'), true, true); self::$loader = $loader = new \Composer\Autoload\ClassLoader(); - spl_autoload_unregister(array('ComposerAutoloaderInit4ae4005bb4ec82f3372265feb7e84f37', 'loadClassLoader')); + spl_autoload_unregister(array('ComposerAutoloaderInit6b1785896dc208c3c153c7ce6a3ee061', 'loadClassLoader')); $vendorDir = dirname(__DIR__); $baseDir = dirname(dirname($vendorDir)); diff --git a/core/vendor/composer/installed.json b/core/vendor/composer/installed.json index 322247d..433d493 100644 --- a/core/vendor/composer/installed.json +++ b/core/vendor/composer/installed.json @@ -1731,5 +1731,59 @@ "feed", "zf2" ] + }, + { + "name": "donquixote/krautoload", + "version": "0.0.1-alpha7", + "version_normalized": "0.0.1.0-alpha7", + "source": { + "type": "git", + "url": "https://github.com/donquixote/krautoload.git", + "reference": "0.0.1-alpha7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/donquixote/krautoload/zipball/0.0.1-alpha7", + "reference": "0.0.1-alpha7", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/php-invoker": ">=1.1.2", + "phpunit/phpunit": ">=3.7" + }, + "time": "2013-07-18 21:43:11", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-0": { + "Krautoload\\": "src/" + } + }, + "license": [ + "GPL" + ], + "authors": [ + { + "name": "Andreas Hennings", + "homepage": "http://github.com/donquixote" + } + ], + "description": "Krautoload is a pluggable class loader and class discovery tool.", + "homepage": "http://github.com/donquixote/krautoload", + "keywords": [ + "PEAR", + "PSR-0", + "PSR-4", + "PSR-X", + "autoload", + "classloader" + ], + "support": { + "issues": "http://github.com/donquixote/krautoload/issues", + "source": "https://github.com/donquixote/krautoload/tree/0.0.1-alpha7" + } } ] diff --git a/core/vendor/donquixote/krautoload/.gitignore b/core/vendor/donquixote/krautoload/.gitignore new file mode 100644 index 0000000..188d532 --- /dev/null +++ b/core/vendor/donquixote/krautoload/.gitignore @@ -0,0 +1,4 @@ +clover.xml +composer.lock +vendor +.idea diff --git a/core/vendor/donquixote/krautoload/.travis.yml b/core/vendor/donquixote/krautoload/.travis.yml new file mode 100644 index 0000000..8a8746d --- /dev/null +++ b/core/vendor/donquixote/krautoload/.travis.yml @@ -0,0 +1,14 @@ +language: php + +php: + - 5.3.3 + - 5.3 + - 5.4 + - 5.5 + +before_script: + - composer install --dev --prefer-source + +script: + - vendor/bin/phpunit --coverage-clover clover.xml + - php coverage-checker.php clover.xml 95 diff --git a/core/vendor/donquixote/krautoload/LICENSE b/core/vendor/donquixote/krautoload/LICENSE new file mode 100644 index 0000000..9b3e7af --- /dev/null +++ b/core/vendor/donquixote/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/donquixote/krautoload/README.md b/core/vendor/donquixote/krautoload/README.md new file mode 100644 index 0000000..5106eb4 --- /dev/null +++ b/core/vendor/donquixote/krautoload/README.md @@ -0,0 +1,136 @@ +[![Build Status](https://secure.travis-ci.org/donquixote/krautoload.png)](https://travis-ci.org/donquixote/krautoload) + +Krautoload is a pluggable PHP class autoloader library. +In addition, it can do class discovery based on the same mappings registered in the class loader. + +The class loader has native support for +- Class maps. +- 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. +- [PSR-0](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md) +- PEAR (that is the old-school pattern with underscores instead of namespaces) + (yes, this is just a subset of PSR-0, but Krautoload has explicit support for it) +- Variations of PSR-0 with different levels of "safety" (to avoid duplicate file inclusion, etc) + +It also supports some "non-standard" stuff, *just because we can*, and it was too tempting not to do it: +- Variation of PSR-0 which allows shallow directory structures. +- Variation of PEAR which allows shallow directory structures. + +Besides that, custom plugins can be mapped to any namespaces and prefixes, to allow the most wonky logical mappings. +E.g. there is an example plugin "CamelSwap", that would map a class ".._TinyPetShop" to "../shop/pet/tiny.php". +(the idea is to support old-school libraries which still have *some logic* in where they put their classes) + + +### Performance + +The basic Krautoload "pluggable" class loader is designed to have a decent performance, that does no have to be afraid of competition. +Especially, the main lookup algorithm is designed to perform equally well no matter how many namespaces are registered. +(even if most of these namespaces all share the same prefix) + +If that is not enough, there are different cache decorators, mostly equivalent with those you find in Symfony2: +- APC cache +- XCache +- WinCache +- Generated class maps + - Note: That's not a decorator, but can still speed you up. + - Krautoload can't generate its own class maps, but there are enough tools out there which can. + + +## Project status and history + +The project has changed a lot in recent days and weeks, but it is now probably quite close to a stable shape. +Still, some API details may still change based on community feedback. +Especially, the term "PSR-X" may change in the future, if it gets accepted. + +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 full inclusion, 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 community feedback. + +```php +require_once "$path_to_krautoload/src/Krautoload.php"; + +// Create the class loader and register it. +$krautoload = Krautoload::start(); + +// Register additional namespaces +$krautoload->addNamespacePSR0('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); +``` + + +### Class discovery + +You need +- a "NamespaceInspector" object (which is a more powerful version of the ClassLoader) +- a "SearchableNamespaces" object, using the namespace inspector. +- Then you can do class discovery within those namespaces. + +```php +// Start Krautoload with discovery capabilities. +// This means, it will create a NamespaceInspector instead of a ClassLoader. +// The only overhead this adds is *two* additional files being loaded (one class, one interface). +// So, no reason not to. +$krautoload = Krautoload::start(array('introspection' => TRUE)); +// Register your stuff +$krautoload->addPrefix... +$krautoload->addNamespace... +// Class loading should work now. +new MyVendor\MyPackage\Foo\Bar(); +// Create searchable namespaces object. +$searchableNamespaces = $krautoload->buildSearchableNamespaces(array( + 'MyVendor\MyPackage\Foo1', + 'MyVendor\MyPackage\Foo2', +)); +// Scan those namespaces +$recursive = TRUE; +$classes = $searchableNamespaces->discoverExistingClasses($recursive); +``` + + +## Unit tests + +Tests exist, but coverage is still not fully acceptable. This is why Travis complains. +https://travis-ci.org/donquixote/krautoload + +Any help is appreciated. + + +## 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/donquixote/krautoload/composer.json b/core/vendor/donquixote/krautoload/composer.json new file mode 100644 index 0000000..d793c10 --- /dev/null +++ b/core/vendor/donquixote/krautoload/composer.json @@ -0,0 +1,29 @@ +{ + "name": "donquixote/krautoload", + "description": "Krautoload is a pluggable class loader and class discovery tool.", + "keywords": ["autoload", "classloader", "PSR-0", "PSR-4", "PSR-X", "PEAR"], + "homepage": "http://github.com/donquixote/krautoload", + "license": "GPL", + "authors": [ + { + "name": "Andreas Hennings", + "homepage": "http://github.com/donquixote" + } + ], + "support": { + "issues": "http://github.com/donquixote/krautoload/issues" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/php-invoker": ">=1.1.2", + "phpunit/phpunit": ">=3.7" + }, + "autoload": { + "psr-0": { + "Krautoload\\": "src/" + } + }, + "minimum-stability": "alpha" +} diff --git a/core/vendor/donquixote/krautoload/coverage-checker.php b/core/vendor/donquixote/krautoload/coverage-checker.php new file mode 100644 index 0000000..4efe93c --- /dev/null +++ b/core/vendor/donquixote/krautoload/coverage-checker.php @@ -0,0 +1,42 @@ +xpath('//metrics'); + +$totalElements = 0; +$checkedElements = 0; + +foreach ($metrics as $metric) { + $totalElements += (int)$metric['elements']; + $checkedElements += (int)$metric['coveredelements']; +} + +$coverage = round(($checkedElements / $totalElements) * 100); + +if ($coverage < $percentage) { + printf( + 'Code coverage is %f%%, which is below the accepted %f%%' . PHP_EOL, + $coverage, $percentage + ); + exit(1); +} + +printf('Code coverage is %f%% - OK!' . PHP_EOL, $coverage); diff --git a/core/vendor/donquixote/krautoload/phpunit.xml.dist b/core/vendor/donquixote/krautoload/phpunit.xml.dist new file mode 100644 index 0000000..0e5d119 --- /dev/null +++ b/core/vendor/donquixote/krautoload/phpunit.xml.dist @@ -0,0 +1,15 @@ + + + + + + ./tests/src/ + + + diff --git a/core/vendor/donquixote/krautoload/src/Krautoload.php b/core/vendor/donquixote/krautoload/src/Krautoload.php new file mode 100644 index 0000000..78308bf --- /dev/null +++ b/core/vendor/donquixote/krautoload/src/Krautoload.php @@ -0,0 +1,110 @@ + FALSE, + 'cache_prefix' => NULL, + 'introspection' => FALSE, + ); + + // Include the bare minimum we need before Krautoload can load its own. + $basedir = dirname(__FILE__) . '/Krautoload'; + require_once $basedir . '/ClassLoader/Interface.php'; + require_once $basedir . '/ClassLoader/Abstract.php'; + require_once $basedir . '/ClassLoader/Pluggable/Interface.php'; + require_once $basedir . '/ClassLoader/Pluggable.php'; + require_once $basedir . '/NamespacePathPlugin/Interface.php'; + require_once $basedir . '/NamespacePathPlugin/ShallowPSR0.php'; + require_once $basedir . '/NamespacePathPlugin/ShallowPSR0/AllUnderscore.php'; + + // Build the class loader. + if (!empty($options['introspection'])) { + // Build a fancy class loader that can also do class discovery. + // This can be useful to reuse the registered path-namespace mappings for + // class discovery. + // This being said, it is always possible to create a NamespaceInspector + // independently of the actively registered class loader. + require_once $basedir . '/NamespaceInspector/Interface.php'; + require_once $basedir . '/NamespaceInspector/Pluggable/Interface.php'; + require_once $basedir . '/NamespaceInspector/Pluggable.php'; + $loader = new Krautoload\NamespaceInspector_Pluggable(); + } + else { + // Build a basic class loader, that can only do class loading. + $loader = new Krautoload\ClassLoader_Pluggable(); + } + + // 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(); + $loader->addNamespacePlugin('Krautoload' . DIRECTORY_SEPARATOR, $basedir . DIRECTORY_SEPARATOR, $plugin); + + // Register the loader to the spl stack. + $loader->register(); + + // Create the adapter. + if (!empty($options['introspection'])) { + $adapter = new Krautoload\Adapter_NamespaceInspector_Pluggable($loader); + } + else { + $adapter = new Krautoload\Adapter_ClassLoader_Pluggable($loader); + } + + // Enable the cache, if any. + switch ($options['cache']) { + case 'ApcCache': + case 'XCache': + case 'WinCache': + if (isset($options['cache_prefix'])) { + // Load one more class that we will need. + require_once $basedir . '/InjectedAPI/ClassFinder/LoadClassGetFile.php'; + // Build the cache decorator object. + $cachedLoaderClass = 'Krautoload\ClassLoader_Cached_' . $options['cache']; + /** + * @var \Krautoload\ClassLoader_Interface $cachedLoader + */ + $cachedLoader = new $cachedLoaderClass($loader, $options['cache_prefix']); + // Replace the loader on the spl stack. + $loader->unregister(); + $cachedLoader->register(); + // @todo Add the cached loader to the adapter to make it accessible to the world? + } + } + + self::$adapter = $adapter; + return $adapter; + } + + /** + * Gets the $adapter object, if exists. + * + * @return \Krautoload\Adapter_ClassLoader_Interface + * @throws Exception + */ + static function registration() { + if (!isset(self::$adapter)) { + throw new Exception("Krautoload::start() must run before Krautoload::registration()"); + } + return self::$adapter; + } +} diff --git a/core/vendor/donquixote/krautoload/src/Krautoload/Adapter/ClassLoader/Interface.php b/core/vendor/donquixote/krautoload/src/Krautoload/Adapter/ClassLoader/Interface.php new file mode 100644 index 0000000..8e47a5d --- /dev/null +++ b/core/vendor/donquixote/krautoload/src/Krautoload/Adapter/ClassLoader/Interface.php @@ -0,0 +1,228 @@ +finder = $finder; + $this->plugins['ShallowPEAR'] = new PrefixPathPlugin_ShallowPEAR(); + $this->plugins['ShallowPEAR_Uncertain'] = new PrefixPathPlugin_ShallowPEAR_Uncertain(); + $this->plugins['ShallowPSR0'] = new NamespacePathPlugin_ShallowPSR0(); + $this->plugins['PSRX'] = new NamespacePathPlugin_PSRX(); + } + + /** + * @inheritdoc + */ + function getFinder() { + return $this->finder; + } + + /** + * @inheritdoc + */ + function krautoloadCallback($callback) { + call_user_func($callback, $this); + } + + /** + * @inheritdoc + */ + function krautoloadFile($file) { + $callback = require $file; + call_user_func($callback, $this); + } + + /** + * @inheritdoc + */ + 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'; + $this->addClassMap($class_map, FALSE); + } + } + + /** + * @inheritdoc + */ + function addPrefixesPSR0(array $prefixes) { + foreach ($prefixes as $prefix => $rootDirs) { + $this->addPrefixPSR0($prefix, $rootDirs); + } + } + + /** + * @inheritdoc + */ + function addPrefixPSR0($prefix, $rootDirs) { + + if ('' === $prefix) { + // We consider this as a "fallback". + $this->addNamespacePSR0('', $rootDirs); + $this->addPrefixPEAR('', $rootDirs, TRUE); + } + elseif ('\\' === substr($prefix, -1)) { + // We know that $prefix is meant as a namespace, + // and the paths are PSR-0 directories. + $this->addNamespacePSR0(substr($prefix, 0, -1), $rootDirs); + } + elseif (FALSE !== strrpos($prefix, '\\')) { + // We assume that $prefix is meant as a namespace, + // and the paths are PSR-0 directories. + $namespace = $prefix; + $this->addNamespacePSR0($namespace, $rootDirs); + foreach ((array) $rootDirs as $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. + $this->addPrefixPEAR(substr($prefix, 0, -1), $rootDirs, TRUE); + // @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. + $this->addNamespacePSR0($prefix, $rootDirs); + $this->addPrefixPEAR($prefix, $rootDirs, TRUE); + foreach ((array) $rootDirs as $rootDir) { + $this->addClassFile($prefix, $rootDir . '.php'); + } + // @todo + // Register special plugins to cover other FQCNs + // that happen to begin with with the prefix. + } + } + + /** + * @inheritdoc + */ + function addPrefixes(array $prefixes) { + $this->addPrefixesPSR0($prefixes); + } + + /** + * @inheritdoc + */ + function addPrefix($prefix, $rootDirs) { + $this->addPrefixPSR0($prefix, $rootDirs); + } + + /** + * @inheritdoc + */ + function addNamespacesPSR0(array $namespaces) { + foreach ($namespaces as $namespace => $rootDirs) { + $this->addNamespacePSR0($namespace, $rootDirs); + } + } + + /** + * @inheritdoc + */ + function addNamespacePSR0($namespace, $rootDirs) { + if (empty($namespace)) { + foreach ((array) $rootDirs as $rootDir) { + $rootDir = strlen($rootDir) ? $rootDir . DIRECTORY_SEPARATOR : ''; + $this->finder->addNamespacePlugin('', $rootDir, $this->plugins['ShallowPSR0']); + $this->finder->addPrefixPlugin('', $rootDir, $this->plugins['ShallowPEAR']); + } + } + else { + $logicalBasePath = $this->namespaceLogicalPath($namespace); + foreach ((array) $rootDirs as $rootDir) { + $baseDir = strlen($rootDir) ? $rootDir . DIRECTORY_SEPARATOR : ''; + $baseDir .= $logicalBasePath; + $this->finder->addNamespacePlugin($logicalBasePath, $baseDir, $this->plugins['ShallowPSR0']); + } + } + } + + /** + * @inheritdoc + */ + function addNamespacesShallowPSR0(array $namespaces) { + foreach ($namespaces as $namespace => $baseDirs) { + $this->addNamespaceShallowPSR0($namespace, $baseDirs); + } + } + + /** + * @inheritdoc + */ + function addNamespaceShallowPSR0($namespace, $baseDirs) { + $logicalBasePath = $this->namespaceLogicalPath($namespace); + foreach ((array) $baseDirs as $baseDir) { + $baseDir = strlen($baseDir) ? $baseDir . DIRECTORY_SEPARATOR : ''; + $this->finder->addNamespacePlugin($logicalBasePath, $baseDir, $this->plugins['ShallowPSR0']); + } + } + + /** + * @inheritdoc + */ + function addPrefixesPEAR(array $prefixes, $preventCollision = FALSE) { + foreach ($prefixes as $prefix => $rootDirs) { + $this->addPrefixPEAR($prefix, $rootDirs, $preventCollision); + } + } + + /** + * @inheritdoc + */ + function addPrefixPEAR($prefix, $rootDirs, $preventCollision = FALSE) { + $logicalBasePath = $this->prefixLogicalPath($prefix); + $plugin = $preventCollision ? $this->plugins['ShallowPEAR_Uncertain'] : $this->plugins['ShallowPEAR']; + foreach ((array) $rootDirs as $rootDir) { + $baseDir = strlen($rootDir) ? $rootDir . DIRECTORY_SEPARATOR : ''; + $baseDir .= $logicalBasePath; + $this->finder->addPrefixPlugin($logicalBasePath, $baseDir, $plugin); + } + } + + /** + * @inheritdoc + */ + function addPrefixesShallowPEAR(array $prefixes, $preventCollision = FALSE) { + foreach ($prefixes as $prefix => $baseDirs) { + $this->addPrefixPEAR($prefix, $baseDirs, $preventCollision); + } + } + + /** + * @inheritdoc + */ + function addPrefixShallowPEAR($prefix, $baseDirs, $preventCollision = FALSE) { + $logicalBasePath = $this->prefixLogicalPath($prefix); + $plugin = $preventCollision ? $this->plugins['ShallowPEAR_Uncertain'] : $this->plugins['ShallowPEAR']; + foreach ((array) $baseDirs as $baseDir) { + $baseDir = strlen($baseDir) ? $baseDir . DIRECTORY_SEPARATOR : ''; + $this->finder->addPrefixPlugin($logicalBasePath, $baseDir, $plugin); + } + } + + /** + * @inheritdoc + */ + function addNamespacesPSRX(array $namespaces) { + foreach ($namespaces as $namespace => $baseDirs) { + $this->addNamespacePSRX($namespace, $baseDirs); + } + } + + /** + * @inheritdoc + */ + function addNamespacePSRX($namespace, $baseDirs) { + $logicalBasePath = $this->namespaceLogicalPath($namespace); + foreach ((array) $baseDirs as $baseDir) { + $baseDir = strlen($baseDir) ? $baseDir . DIRECTORY_SEPARATOR : ''; + $this->finder->addNamespacePlugin($logicalBasePath, $baseDir, $this->plugins['PSRX']); + } + } + + /** + * @inheritdoc + */ + function addNamespacePlugin($namespace, $baseDir, $plugin) { + $logicalBasePath = $this->namespaceLogicalPath($namespace); + $baseDir = strlen($baseDir) ? $baseDir . DIRECTORY_SEPARATOR : ''; + $this->finder->addNamespacePlugin($logicalBasePath, $baseDir, $plugin); + + } + + /** + * @inheritdoc + */ + function addPrefixPlugin($prefix, $baseDir, $plugin) { + $logicalBasePath = $this->prefixLogicalPath($prefix); + $baseDir = strlen($baseDir) ? $baseDir . DIRECTORY_SEPARATOR : ''; + $this->finder->addPrefixPlugin($logicalBasePath, $baseDir, $plugin); + } + + /** + * @inheritdoc + */ + function addClassMap(array $classMap, $override = FALSE) { + $this->finder->addClassMap($classMap, $override); + } + + /** + * @inheritdoc + */ + function addClassFile($class, $file, $override = TRUE) { + $this->finder->addClassFile($class, $file, $override); + } + + /** + * 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/donquixote/krautoload/src/Krautoload/Adapter/NamespaceInspector/Interface.php b/core/vendor/donquixote/krautoload/src/Krautoload/Adapter/NamespaceInspector/Interface.php new file mode 100644 index 0000000..dfa2d41 --- /dev/null +++ b/core/vendor/donquixote/krautoload/src/Krautoload/Adapter/NamespaceInspector/Interface.php @@ -0,0 +1,22 @@ +finder; + } + + /** + * @param array $namespaces + * @return SearchableNamespaces_Interface + * @throws \Exception + */ + function buildSearchableNamespaces(array $namespaces = array()) { + $searchable = new SearchableNamespaces_Default($this->finder); + $searchable->addNamespaces($namespaces); + return $searchable; + } +} diff --git a/core/vendor/donquixote/krautoload/src/Krautoload/ClassLoader/Abstract.php b/core/vendor/donquixote/krautoload/src/Krautoload/ClassLoader/Abstract.php new file mode 100644 index 0000000..8c85177 --- /dev/null +++ b/core/vendor/donquixote/krautoload/src/Krautoload/ClassLoader/Abstract.php @@ -0,0 +1,59 @@ += 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')); + } + + function findFile($class) { + $api = new InjectedAPI_ClassFinder_FirstExistingFile($class); + $this->apiFindFile($api, $class); + return $api->getFile(); + } + + /** + * Load a class, and return the file that was successful. + * + * @param string $class + * The class to load. + * + * @return string + * The file that defined the class. + */ + function loadClassGetFile($class) { + $api = new InjectedAPI_ClassFinder_LoadClassGetFile($class); + $this->apiFindFile($api, $class); + return $api->getFile(); + } +} diff --git a/core/vendor/donquixote/krautoload/src/Krautoload/ClassLoader/Cached/Abstract.php b/core/vendor/donquixote/krautoload/src/Krautoload/ClassLoader/Cached/Abstract.php new file mode 100644 index 0000000..8560d8c --- /dev/null +++ b/core/vendor/donquixote/krautoload/src/Krautoload/ClassLoader/Cached/Abstract.php @@ -0,0 +1,33 @@ +decorated = $decorated; + } + + /** + * @inheritdoc + */ + function loadClass($class) { + $this->decorated->loadClass($class); + } + + /** + * @inheritdoc + */ + function apiFindFile(InjectedAPI_ClassFinder_Interface $api, $class) { + return $this->decorated->apiFindFile($api, $class); + } +} diff --git a/core/vendor/donquixote/krautoload/src/Krautoload/ClassLoader/Cached/AbstractPrefixBased.php b/core/vendor/donquixote/krautoload/src/Krautoload/ClassLoader/Cached/AbstractPrefixBased.php new file mode 100644 index 0000000..c4a5cde --- /dev/null +++ b/core/vendor/donquixote/krautoload/src/Krautoload/ClassLoader/Cached/AbstractPrefixBased.php @@ -0,0 +1,36 @@ +checkRequirements(); + + $this->prefix = $prefix; + parent::__construct($decorated); + } + + abstract protected function checkRequirements(); + + /** + * Set the cache prefix after a flush cache. + * + * @param string $prefix + * A prefix for the storage key in APC. + */ + function setCachePrefix($prefix) { + $this->prefix = $prefix; + } +} diff --git a/core/vendor/donquixote/krautoload/src/Krautoload/ClassLoader/Cached/ApcCache.php b/core/vendor/donquixote/krautoload/src/Krautoload/ClassLoader/Cached/ApcCache.php new file mode 100644 index 0000000..fefa9fa --- /dev/null +++ b/core/vendor/donquixote/krautoload/src/Krautoload/ClassLoader/Cached/ApcCache.php @@ -0,0 +1,33 @@ +prefix . $class)) || + (!empty($file) && !is_file($file)) + ) { + // Resolve cache miss. + apc_store($this->prefix . $class, $file = $this->decorated->loadClassGetFile($class)); + } + else { + require $file; + } + } +} diff --git a/core/vendor/donquixote/krautoload/src/Krautoload/ClassLoader/Cached/WinCache.php b/core/vendor/donquixote/krautoload/src/Krautoload/ClassLoader/Cached/WinCache.php new file mode 100644 index 0000000..1ac87ef --- /dev/null +++ b/core/vendor/donquixote/krautoload/src/Krautoload/ClassLoader/Cached/WinCache.php @@ -0,0 +1,30 @@ +prefix . $class)) { + // Resolve cache miss. + wincache_ucache_set($this->prefix . $class, $file = $this->decorated->loadClassGetFile($class), 0); + } + else { + require $file; + } + } +} diff --git a/core/vendor/donquixote/krautoload/src/Krautoload/ClassLoader/Cached/XCache.php b/core/vendor/donquixote/krautoload/src/Krautoload/ClassLoader/Cached/XCache.php new file mode 100644 index 0000000..dade937 --- /dev/null +++ b/core/vendor/donquixote/krautoload/src/Krautoload/ClassLoader/Cached/XCache.php @@ -0,0 +1,31 @@ +prefix . $class)) { + if (FALSE !== $file = xcache_get($this->prefix . $class)) { + require $file; + } + } + else { + xcache_set($this->prefix . $class, $this->decorated->loadClassGetFile($class)); + } + } +} diff --git a/core/vendor/donquixote/krautoload/src/Krautoload/ClassLoader/Interface.php b/core/vendor/donquixote/krautoload/src/Krautoload/ClassLoader/Interface.php new file mode 100644 index 0000000..2ab0990 --- /dev/null +++ b/core/vendor/donquixote/krautoload/src/Krautoload/ClassLoader/Interface.php @@ -0,0 +1,70 @@ +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 boolean|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(InjectedAPI_ClassFinder_Interface $api, $class); +} diff --git a/core/vendor/donquixote/krautoload/src/Krautoload/ClassLoader/Pluggable.php b/core/vendor/donquixote/krautoload/src/Krautoload/ClassLoader/Pluggable.php new file mode 100644 index 0000000..c7240f0 --- /dev/null +++ b/core/vendor/donquixote/krautoload/src/Krautoload/ClassLoader/Pluggable.php @@ -0,0 +1,278 @@ +classMap)) { + $this->classMap = $classMap; + } + elseif ($override) { + $this->classMap = array_merge($classMap, $this->classMap); + } + else { + $this->classMap = array_merge($this->classMap, $classMap); + } + } + + /** + * @inheritdoc + */ + public function addClassFile($class, $file, $override = TRUE) { + if ($override || !isset($this->classMap[$class])) { + $this->classMap[$class] = $file; + } + } + + /** + * @inheritdoc + */ + public function addNamespacePlugin($logicalBasePath, $baseDir, NamespacePathPlugin_Interface $plugin) { + $this->namespaceMap[$logicalBasePath][$baseDir] = $plugin; + } + + /** + * @inheritdoc + */ + public function addPrefixPlugin($logicalBasePath, $baseDir, PrefixPathPlugin_Interface $plugin) { + $this->prefixMap[$logicalBasePath][$baseDir] = $plugin; + } + + /** + * @inheritdoc + */ + function loadClass($class) { + + // Discard initial namespace separator. + if ('\\' === $class[0]) { + $class = substr($class, 1); + } + + // First check if the literal class name is registered. + if (isset($this->classMap[$class])) { + require $this->classMap[$class]; + 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 $class + * @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 bool|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(array $map, $class, $logicalBasePath, $relativePath) { + + $path = $logicalBasePath . $relativePath; + while (TRUE) { + // Check any plugin registered for this fragment. + if (!empty($map[$logicalBasePath])) { + /** + * @var NamespacePathPlugin_Interface|PrefixPathPlugin_Interface $plugin + */ + 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; + } + } + } + + /** + * 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 bool|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(InjectedAPI_ClassFinder_Interface $api, $class) { + + // Discard initial namespace separator. + if ('\\' === $class[0]) { + $class = substr($class, 1); + } + + // First check if the literal class name is registered. + if (isset($this->classMap[$class])) { + if ($api->claimFile($this->classMap[$class])) { + 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 bool|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])) { + /** + * @var NamespacePathPlugin_Interface|PrefixPathPlugin_Interface $plugin + */ + 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/donquixote/krautoload/src/Krautoload/ClassLoader/Pluggable/Interface.php b/core/vendor/donquixote/krautoload/src/Krautoload/ClassLoader/Pluggable/Interface.php new file mode 100644 index 0000000..bb17698 --- /dev/null +++ b/core/vendor/donquixote/krautoload/src/Krautoload/ClassLoader/Pluggable/Interface.php @@ -0,0 +1,73 @@ +namespace = $namespace; + } + + /** + * @inheritdoc + */ + function getNamespace() { + return $this->namespace; + } + + /** + * @inheritdoc + */ + function getClassName($relativeClassName) { + return $this->namespace . $relativeClassName; + } +} diff --git a/core/vendor/donquixote/krautoload/src/Krautoload/InjectedAPI/ClassFileVisitor/CollectCandidateClasses.php b/core/vendor/donquixote/krautoload/src/Krautoload/InjectedAPI/ClassFileVisitor/CollectCandidateClasses.php new file mode 100644 index 0000000..c754518 --- /dev/null +++ b/core/vendor/donquixote/krautoload/src/Krautoload/InjectedAPI/ClassFileVisitor/CollectCandidateClasses.php @@ -0,0 +1,32 @@ +classes); + } + + /** + * @inheritdoc + */ + function fileWithClass($file, $relativeClassName) { + $this->classes[$this->getClassName($relativeClassName)] = TRUE; + } + + /** + * @inheritdoc + */ + function fileWithClassCandidates($file, array $relativeClassNames) { + foreach ($relativeClassNames as $relativeClassName) { + $this->classes[$this->getClassName($relativeClassName)] = TRUE; + } + } +} diff --git a/core/vendor/donquixote/krautoload/src/Krautoload/InjectedAPI/ClassFileVisitor/CollectExistingClasses.php b/core/vendor/donquixote/krautoload/src/Krautoload/InjectedAPI/ClassFileVisitor/CollectExistingClasses.php new file mode 100644 index 0000000..c1437a1 --- /dev/null +++ b/core/vendor/donquixote/krautoload/src/Krautoload/InjectedAPI/ClassFileVisitor/CollectExistingClasses.php @@ -0,0 +1,25 @@ +classes; + } + + /** + * @inheritdoc + */ + function confirmedFileWithClass($file, $class) { + $this->classes[$class] = $class; + } +} diff --git a/core/vendor/donquixote/krautoload/src/Krautoload/InjectedAPI/ClassFileVisitor/IncludeEachAbstract.php b/core/vendor/donquixote/krautoload/src/Krautoload/InjectedAPI/ClassFileVisitor/IncludeEachAbstract.php new file mode 100644 index 0000000..e06a1c9 --- /dev/null +++ b/core/vendor/donquixote/krautoload/src/Krautoload/InjectedAPI/ClassFileVisitor/IncludeEachAbstract.php @@ -0,0 +1,67 @@ +includedFileWithClassCandidate($file, $relativeClassName); + } + + /** + * @inheritdoc + */ + function fileWithClassCandidates($file, array $relativeClassNames) { + include_once $file; + foreach ($relativeClassNames as $relativeClassName) { + $this->includedFileWithClassCandidate($file, $relativeClassName); + } + } + + /** + * @inheritdoc + */ + protected function includedFileWithClassCandidate($file, $relativeClassName) { + if (class_exists($class = $this->getNamespace() . $relativeClassName, FALSE)) { + $this->confirmedFileWithClass($file, $class); + } + elseif (interface_exists($class, FALSE)) { + $this->confirmedFileWithInterface($file, $class); + } + elseif (PHP_VERSION_ID >= 50400 && trait_exists($class, FALSE)) { + $this->confirmedFileWithTrait($file, $class); + } + } + + /** + * @param string $file + * @param string $class + */ + abstract protected function confirmedFileWithClass($file, $class); + + /** + * @param string $file + * @param string $interface + */ + protected function confirmedFileWithInterface($file, $interface) { + // Do nothing by default. + } + + /** + * @param string $file + * @param string $trait + */ + protected function confirmedFileWithTrait($file, $trait) { + // Do nothing by default. + } +} diff --git a/core/vendor/donquixote/krautoload/src/Krautoload/InjectedAPI/ClassFileVisitor/Interface.php b/core/vendor/donquixote/krautoload/src/Krautoload/InjectedAPI/ClassFileVisitor/Interface.php new file mode 100644 index 0000000..6b3ad13 --- /dev/null +++ b/core/vendor/donquixote/krautoload/src/Krautoload/InjectedAPI/ClassFileVisitor/Interface.php @@ -0,0 +1,64 @@ +setNamespace(). + * E.g. 'Foo\\Bar', so that the fully-qualified class name would be + * 'MyVendor\\MyPackage\\Foo\\Bar', + */ + function fileWithClass($file, $relativeClassName); + + /** + * A file was discovered that may define any of the given classes. + * + * @param string $file + * The file that was found and may contain any or none of the classes. + * @param array $relativeClassNames + * Array of relative class names for classes that *could* be in this file. + * With PSR-0, these can be different variations of the underscore. + * The one with the least underscores will always be at index 0. + * Class names are relative to the namespace previously specified with + * ->setNamespace(). + */ + function fileWithClassCandidates($file, array $relativeClassNames); +} diff --git a/core/vendor/donquixote/krautoload/src/Krautoload/InjectedAPI/ClassFileVisitor/Mock.php b/core/vendor/donquixote/krautoload/src/Krautoload/InjectedAPI/ClassFileVisitor/Mock.php new file mode 100644 index 0000000..7899ea6 --- /dev/null +++ b/core/vendor/donquixote/krautoload/src/Krautoload/InjectedAPI/ClassFileVisitor/Mock.php @@ -0,0 +1,38 @@ +called; + } + + /** + * @inheritdoc + */ + function setNamespace($namespace) { + $this->called[] = array(__FUNCTION__, func_get_args()); + parent::setNamespace($namespace); + } + + /** + * @inheritdoc + */ + function fileWithClass($file, $relativeClassName) { + $this->called[] = array(__FUNCTION__, func_get_args()); + } + + /** + * @inheritdoc + */ + function fileWithClassCandidates($file, array $relativeClassNames) { + $this->called[] = array(__FUNCTION__, func_get_args()); + } +} \ No newline at end of file diff --git a/core/vendor/donquixote/krautoload/src/Krautoload/InjectedAPI/ClassFinder/Abstract.php b/core/vendor/donquixote/krautoload/src/Krautoload/InjectedAPI/ClassFinder/Abstract.php new file mode 100644 index 0000000..73b88a0 --- /dev/null +++ b/core/vendor/donquixote/krautoload/src/Krautoload/InjectedAPI/ClassFinder/Abstract.php @@ -0,0 +1,66 @@ +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; + } + + /** + * @inheritdoc + */ + function guessFile($file) { + if (is_file($file)) { + return $this->claimFile($file); + } + return FALSE; + } + + /** + * @inheritdoc + */ + function guessFileCandidate($file) { + if (is_file($file)) { + return $this->claimFileCandidate($file); + } + return FALSE; + } + + /** + * @inheritdoc + */ + function guessFile_checkIncludePath($file) { + if (FALSE !== $file = Util::findFileInIncludePath($file)) { + return $this->claimFile($file); + } + return FALSE; + } + + /** + * @inheritdoc + */ + function guessFileCandidate_checkIncludePath($file) { + if (FALSE !== $file = Util::findFileInIncludePath($file)) { + return $this->claimFileCandidate($file); + } + return FALSE; + } +} diff --git a/core/vendor/donquixote/krautoload/src/Krautoload/InjectedAPI/ClassFinder/CollectFiles.php b/core/vendor/donquixote/krautoload/src/Krautoload/InjectedAPI/ClassFinder/CollectFiles.php new file mode 100644 index 0000000..e19d6c9 --- /dev/null +++ b/core/vendor/donquixote/krautoload/src/Krautoload/InjectedAPI/ClassFinder/CollectFiles.php @@ -0,0 +1,35 @@ +files; + } + + /** + * @inheritdoc + */ + function claimFile($file) { + $this->files[$file] = TRUE; + return TRUE; + } + + /** + * @inheritdoc + */ + function claimFileCandidate($file) { + $this->files[$file] = FALSE; + } +} diff --git a/core/vendor/donquixote/krautoload/src/Krautoload/InjectedAPI/ClassFinder/FindExistingClass.php b/core/vendor/donquixote/krautoload/src/Krautoload/InjectedAPI/ClassFinder/FindExistingClass.php new file mode 100644 index 0000000..5c43d64 --- /dev/null +++ b/core/vendor/donquixote/krautoload/src/Krautoload/InjectedAPI/ClassFinder/FindExistingClass.php @@ -0,0 +1,48 @@ +destination; + } + + /** + * @inheritdoc + */ + function guessFile($file) { + $this->destination = $file; + return TRUE; + } + + /** + * @inheritdoc + */ + function guessFileCandidate($file) { + $this->destination = $file; + return TRUE; + } + + /** + * @inheritdoc + */ + function claimFile($file) { + $this->destination = $file; + return TRUE; + } + + /** + * @inheritdoc + */ + function claimFileCandidate($file) { + $this->destination = $file; + return TRUE; + } + + /** + * @inheritdoc + */ + function guessFile_checkIncludePath($file) { + // Include path is not supported when looking for a destination. + return FALSE; + } + + /** + * @inheritdoc + */ + function guessFileCandidate_checkIncludePath($file) { + // Include path is not supported when looking for a destination. + return FALSE; + } +} diff --git a/core/vendor/donquixote/krautoload/src/Krautoload/InjectedAPI/ClassFinder/FirstExistingFile.php b/core/vendor/donquixote/krautoload/src/Krautoload/InjectedAPI/ClassFinder/FirstExistingFile.php new file mode 100644 index 0000000..a2cab70 --- /dev/null +++ b/core/vendor/donquixote/krautoload/src/Krautoload/InjectedAPI/ClassFinder/FirstExistingFile.php @@ -0,0 +1,38 @@ +file; + } + + /** + * @inheritdoc + */ + function claimFile($file) { + $this->file = $file; + return TRUE; + } + + /** + * @inheritdoc + */ + function claimFileCandidate($file) { + $this->file = $file; + return TRUE; + } +} diff --git a/core/vendor/donquixote/krautoload/src/Krautoload/InjectedAPI/ClassFinder/Interface.php b/core/vendor/donquixote/krautoload/src/Krautoload/InjectedAPI/ClassFinder/Interface.php new file mode 100644 index 0000000..5521f9a --- /dev/null +++ b/core/vendor/donquixote/krautoload/src/Krautoload/InjectedAPI/ClassFinder/Interface.php @@ -0,0 +1,111 @@ +className, FALSE); + } +} diff --git a/core/vendor/donquixote/krautoload/src/Krautoload/InjectedAPI/ClassFinder/LoadClassGetFile.php b/core/vendor/donquixote/krautoload/src/Krautoload/InjectedAPI/ClassFinder/LoadClassGetFile.php new file mode 100644 index 0000000..f92f439 --- /dev/null +++ b/core/vendor/donquixote/krautoload/src/Krautoload/InjectedAPI/ClassFinder/LoadClassGetFile.php @@ -0,0 +1,63 @@ +file; + } + + /** + * @inheritdoc + */ + function guessFile($file) { + if (is_file($file)) { + include $file; + $this->file = $file; + return TRUE; + } + } + + /** + * @inheritdoc + */ + function guessFileCandidate($file) { + if (is_file($file)) { + include_once $file; + if (Util::classIsDefined($this->className)) { + $this->file = $file; + return TRUE; + } + } + } + + /** + * @inheritdoc + */ + function claimFile($file) { + require $file; + $this->file = $file; + return TRUE; + } + + /** + * @inheritdoc + */ + function claimFileCandidate($file) { + require_once $file; + if (Util::classIsDefined($this->className)) { + $this->file = $file; + return TRUE; + } + } +} diff --git a/core/vendor/donquixote/krautoload/src/Krautoload/InjectedAPI/NamespaceInspector/Interface.php b/core/vendor/donquixote/krautoload/src/Krautoload/InjectedAPI/NamespaceInspector/Interface.php new file mode 100644 index 0000000..b17037f --- /dev/null +++ b/core/vendor/donquixote/krautoload/src/Krautoload/InjectedAPI/NamespaceInspector/Interface.php @@ -0,0 +1,25 @@ +api = $api; + } + + /** + * @inheritdoc + */ + function setNamespace($namespace) { + $this->api->setNamespace($namespace); + } +} diff --git a/core/vendor/donquixote/krautoload/src/Krautoload/InjectedAPI/NamespaceInspector/ScanNamespace.php b/core/vendor/donquixote/krautoload/src/Krautoload/InjectedAPI/NamespaceInspector/ScanNamespace.php new file mode 100644 index 0000000..04c04d5 --- /dev/null +++ b/core/vendor/donquixote/krautoload/src/Krautoload/InjectedAPI/NamespaceInspector/ScanNamespace.php @@ -0,0 +1,20 @@ +pluginScanNamespace($this->api, $baseDir, $relativePath); + } + + /** + * @inheritdoc + */ + public function namespaceParentDirectoryPlugin($baseDir, $relativeNamespace, $plugin) { + throw new \Exception("This should only ever be called during recursive scans."); + } +} diff --git a/core/vendor/donquixote/krautoload/src/Krautoload/InjectedAPI/NamespaceInspector/ScanRecursive.php b/core/vendor/donquixote/krautoload/src/Krautoload/InjectedAPI/NamespaceInspector/ScanRecursive.php new file mode 100644 index 0000000..a17429b --- /dev/null +++ b/core/vendor/donquixote/krautoload/src/Krautoload/InjectedAPI/NamespaceInspector/ScanRecursive.php @@ -0,0 +1,20 @@ +pluginScanRecursive($this->api, $baseDir, $relativePath); + } + + /** + * @inheritdoc + */ + public function namespaceParentDirectoryPlugin($baseDir, $relativeNamespace, $plugin) { + $plugin->pluginScanParentRecursive($this->api, $baseDir, $relativeNamespace); + } +} diff --git a/core/vendor/donquixote/krautoload/src/Krautoload/NamespaceInspector/Interface.php b/core/vendor/donquixote/krautoload/src/Krautoload/NamespaceInspector/Interface.php new file mode 100644 index 0000000..1736438 --- /dev/null +++ b/core/vendor/donquixote/krautoload/src/Krautoload/NamespaceInspector/Interface.php @@ -0,0 +1,13 @@ +normalizeNamespaces($namespaces); + if ($recursive) { + $this->apiInspectNamespacesRecursive($api, $namespaces); + } + foreach ($namespaces as $namespace) { + $this->apiInspectNamespace($api, $namespace); + } + } + + /** + * @param array $namespaces + * Array of namespaces with arbitrary keys, and + * with or without trailing or leading namespace separators. + * @return array + * Array of namespaces, where key and value are identical, + * The root namespace is represented by an empty string. + * Every other namespace is represented with a trailing namespace separator, + * but without a leading namespace separator. + */ + protected function normalizeNamespaces(array $namespaces) { + $normalized = array(); + foreach ($namespaces as $namespace) { + $namespace = trim($namespace, '\\') . '\\'; + if ('\\' === $namespace) { + $namespace = ''; + } + $normalized[$namespace] = $namespace; + } + return $normalized; + } + + /** + * @param InjectedAPI_NamespaceInspector_Interface $api + * @param array $namespaces + */ + protected function apiInspectNamespacesRecursive(InjectedAPI_NamespaceInspector_Interface $api, array $namespaces) { + + foreach ($this->namespaceMap as $logicalBasePath => $plugins) { + $baseNamespace = str_replace(DIRECTORY_SEPARATOR, '\\', $logicalBasePath); + $baseNamespacePrefix = $baseNamespace; + while ('' !== $baseNamespacePrefix) { + // Move one fragment from the prefix to the relative base namepsace. + if (FALSE === $pos = strrpos($baseNamespacePrefix, '\\', -2)) { + // $baseNamespacePrefix is e.g. 'MyVendor\\'. + $pos = 0; + $baseNamespacePrefix = ''; + } + else { + // $baseNamespacePrefix is e.g. 'MyVendor\\MyPackage\\Foo\\'. + ++$pos; + $baseNamespacePrefix = substr($baseNamespacePrefix, 0, $pos); + } + if (isset($namespaces[$baseNamespacePrefix])) { + $api->setNamespace($baseNamespacePrefix); + $relativeBaseNamespace = substr($baseNamespace, $pos); + /** + * @var NamespacePathPlugin_Interface $plugin + */ + foreach ($plugins as $baseDir => $plugin) { + $api->namespaceParentDirectoryPlugin($baseDir, $relativeBaseNamespace, $plugin); + } + } + } + } + } + + /** + * @param InjectedAPI_NamespaceInspector_Interface $api + * @param string $namespace + * The namespace, e.g. 'MyVendor\\MyPackage\\'. + */ + protected function apiInspectNamespace(InjectedAPI_NamespaceInspector_Interface $api, $namespace) { + + $logicalPath = str_replace('\\', DIRECTORY_SEPARATOR, $namespace); + $logicalBasePath = $logicalPath; + $relativePath = ''; + + $api->setNamespace($namespace); + + while (TRUE) { + // Check any plugin registered for this fragment. + if (!empty($this->namespaceMap[$logicalBasePath])) { + /** + * @var NamespacePathPlugin_Interface $plugin + */ + 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; + } + } + } +} diff --git a/core/vendor/donquixote/krautoload/src/Krautoload/NamespaceInspector/Pluggable/Interface.php b/core/vendor/donquixote/krautoload/src/Krautoload/NamespaceInspector/Pluggable/Interface.php new file mode 100644 index 0000000..7c9ed0c --- /dev/null +++ b/core/vendor/donquixote/krautoload/src/Krautoload/NamespaceInspector/Pluggable/Interface.php @@ -0,0 +1,5 @@ +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); + + /** + * @param InjectedAPI_ClassFileVisitor_Interface $api + * @param string $baseDir + * @param string $relativePath + */ + function pluginScanNamespace($api, $baseDir, $relativePath); + + /** + * @param InjectedAPI_ClassFileVisitor_Interface $api + * @param string $baseDir + * @param string $relativePath + */ + function pluginScanRecursive($api, $baseDir, $relativePath); + + /** + * @param InjectedAPI_ClassFileVisitor_Interface $api + * @param string $baseDir + * @param string $relativeBaseNamespace + */ + function pluginScanParentRecursive($api, $baseDir, $relativeBaseNamespace); +} diff --git a/core/vendor/donquixote/krautoload/src/Krautoload/NamespacePathPlugin/PSRX.php b/core/vendor/donquixote/krautoload/src/Krautoload/NamespacePathPlugin/PSRX.php new file mode 100644 index 0000000..c66b0dd --- /dev/null +++ b/core/vendor/donquixote/krautoload/src/Krautoload/NamespacePathPlugin/PSRX.php @@ -0,0 +1,80 @@ +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)) { + /** + * @var \DirectoryIterator $fileinfo + */ + 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); + } + } + + /** + * @inheritdoc + */ + function pluginScanParentRecursive($api, $baseDir, $relativeBaseNamespace) { + if (is_dir($baseDir)) { + $this->doScanRecursive($api, $baseDir, $relativeBaseNamespace); + } + } + + /** + * @param InjectedAPI_ClassFileVisitor_Interface $api + * @param string $dir + * @param string $relativeNamespace + */ + protected function doScanRecursive($api, $dir, $relativeNamespace = '') { + /** + * @var \DirectoryIterator $fileinfo + */ + 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/donquixote/krautoload/src/Krautoload/NamespacePathPlugin/ShallowPSR0.php b/core/vendor/donquixote/krautoload/src/Krautoload/NamespacePathPlugin/ShallowPSR0.php new file mode 100644 index 0000000..b02eb46 --- /dev/null +++ b/core/vendor/donquixote/krautoload/src/Krautoload/NamespacePathPlugin/ShallowPSR0.php @@ -0,0 +1,118 @@ +guessFileCandidate($baseDir . $relativePath)) { + return TRUE; + } + } + + /** + * @inheritdoc + */ + 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; + // This check happens inline for micro-optimization. + return class_exists($class, FALSE) + || interface_exists($class, FALSE) + || (PHP_VERSION_ID >= 50400 && trait_exists($class, FALSE)) + ; + } + } + + /** + * @inheritdoc + */ + function pluginScanNamespace($api, $baseDir, $relativePath) { + if (is_dir($dir = $baseDir . $relativePath)) { + /** + * @var \DirectoryIterator $fileinfo + */ + 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'))); + } + } + } + } + + /** + * @inheritdoc + */ + function pluginScanRecursive($api, $baseDir, $relativePath) { + if (is_dir($dir = $baseDir . $relativePath)) { + $this->doScanRecursive($api, $dir); + } + } + + /** + * @inheritdoc + */ + function pluginScanParentRecursive($api, $baseDir, $relativeBaseNamespace) { + if (is_dir($baseDir)) { + $this->doScanRecursive($api, $baseDir, array($relativeBaseNamespace)); + } + } + + /** + * @param InjectedAPI_ClassFileVisitor_Interface $api + * @param string $dir + * @param array $relativeNamespaces + */ + protected function doScanRecursive($api, $dir, $relativeNamespaces = array('')) { + /** + * @var \DirectoryIterator $fileinfo + */ + 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/donquixote/krautoload/src/Krautoload/NamespacePathPlugin/ShallowPSR0/AllUnderscore.php b/core/vendor/donquixote/krautoload/src/Krautoload/NamespacePathPlugin/ShallowPSR0/AllUnderscore.php new file mode 100644 index 0000000..508568c --- /dev/null +++ b/core/vendor/donquixote/krautoload/src/Krautoload/NamespacePathPlugin/ShallowPSR0/AllUnderscore.php @@ -0,0 +1,104 @@ +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)) { + /** + * @var \DirectoryIterator $fileinfo + */ + 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 = '\\') { + /** + * @var \DirectoryIterator $fileinfo + */ + 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/donquixote/krautoload/src/Krautoload/NamespacePathPlugin/ShallowPSR0/MapLeak.php b/core/vendor/donquixote/krautoload/src/Krautoload/NamespacePathPlugin/ShallowPSR0/MapLeak.php new file mode 100644 index 0000000..dd2fcb6 --- /dev/null +++ b/core/vendor/donquixote/krautoload/src/Krautoload/NamespacePathPlugin/ShallowPSR0/MapLeak.php @@ -0,0 +1,37 @@ +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/donquixote/krautoload/src/Krautoload/NamespacePathPlugin/ShallowPSR0/NoConflict.php b/core/vendor/donquixote/krautoload/src/Krautoload/NamespacePathPlugin/ShallowPSR0/NoConflict.php new file mode 100644 index 0000000..a926dbf --- /dev/null +++ b/core/vendor/donquixote/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/donquixote/krautoload/src/Krautoload/NamespacePathPlugin/ShallowPSR0/NoUnderscore.php b/core/vendor/donquixote/krautoload/src/Krautoload/NamespacePathPlugin/ShallowPSR0/NoUnderscore.php new file mode 100644 index 0000000..22c0b50 --- /dev/null +++ b/core/vendor/donquixote/krautoload/src/Krautoload/NamespacePathPlugin/ShallowPSR0/NoUnderscore.php @@ -0,0 +1,28 @@ +guessFile_checkIncludePath($baseDir . $relativePath)) { + return TRUE; + } + } + + /** + * @inheritdoc + */ + 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 (FALSE !== $file = Util::findFileInIncludePath($baseDir . $relativePath)) { + include_once $file; + return Util::classIsDefined($class); + } + } + + /** + * @inheritdoc + */ + function pluginScanNamespace($api, $baseDir, $relativePath) { + throw new \Exception("Class discovery is not supported with 'use include path' setting."); + } + + /** + * @inheritdoc + */ + function pluginScanRecursive($api, $baseDir, $relativePath) { + throw new \Exception("Class discovery is not supported with 'use include path' setting."); + } +} \ No newline at end of file diff --git a/core/vendor/donquixote/krautoload/src/Krautoload/PrefixPathPlugin/Exotic/CamelSwap.php b/core/vendor/donquixote/krautoload/src/Krautoload/PrefixPathPlugin/Exotic/CamelSwap.php new file mode 100644 index 0000000..35f7321 --- /dev/null +++ b/core/vendor/donquixote/krautoload/src/Krautoload/PrefixPathPlugin/Exotic/CamelSwap.php @@ -0,0 +1,42 @@ +guessFile($baseDir . $this->transformRelativePath($relativePath)); + } + + /** + * @inheritdoc + */ + function pluginLoadClass($class, $baseDir, $relativePath) { + if (is_file($file = $baseDir . $this->transformRelativePath($relativePath))) { + include_once $file; + return TRUE; + } + } + + protected function transformRelativePath($relativePath) { + $pieces = Util::camelCaseExplode(substr($relativePath, 0, -4)); + $pieces = array_reverse($pieces); + return implode(DIRECTORY_SEPARATOR, $pieces) . '.php'; + } +} \ No newline at end of file diff --git a/core/vendor/donquixote/krautoload/src/Krautoload/PrefixPathPlugin/Interface.php b/core/vendor/donquixote/krautoload/src/Krautoload/PrefixPathPlugin/Interface.php new file mode 100644 index 0000000..fd29b26 --- /dev/null +++ b/core/vendor/donquixote/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/donquixote/krautoload/src/Krautoload/PrefixPathPlugin/ShallowPEAR.php b/core/vendor/donquixote/krautoload/src/Krautoload/PrefixPathPlugin/ShallowPEAR.php new file mode 100644 index 0000000..dd1417f --- /dev/null +++ b/core/vendor/donquixote/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/donquixote/krautoload/src/Krautoload/PrefixPathPlugin/ShallowPEAR/MapLeak.php b/core/vendor/donquixote/krautoload/src/Krautoload/PrefixPathPlugin/ShallowPEAR/MapLeak.php new file mode 100644 index 0000000..3cff2f2 --- /dev/null +++ b/core/vendor/donquixote/krautoload/src/Krautoload/PrefixPathPlugin/ShallowPEAR/MapLeak.php @@ -0,0 +1,53 @@ +relativePrefixes[$relativePrefix] = strlen($relativePrefix); + } + + /** + * @inheritdoc + */ + function pluginFindFile($api, $baseDir, $relativePath) { + if ($this->checkPrefix($relativePath)) { + return $api->guessFile($baseDir . $relativePath); + } + } + + /** + * @inheritdoc + */ + function pluginLoadClass($class, $baseDir, $relativePath) { + if ($this->checkPrefix($relativePath)) { + if (is_file($file = $baseDir . $relativePath)) { + include $file; + return TRUE; + } + } + } + + /** + * @inheritdoc + */ + protected function checkPrefix($relativePath) { + foreach ($this->relativePrefixes as $relativePrefix => $length) { + if (!strncmp($relativePath, $relativePrefix, $length)) { + return TRUE; + } + } + } +} diff --git a/core/vendor/donquixote/krautoload/src/Krautoload/PrefixPathPlugin/ShallowPEAR/Uncertain.php b/core/vendor/donquixote/krautoload/src/Krautoload/PrefixPathPlugin/ShallowPEAR/Uncertain.php new file mode 100644 index 0000000..b1d38a7 --- /dev/null +++ b/core/vendor/donquixote/krautoload/src/Krautoload/PrefixPathPlugin/ShallowPEAR/Uncertain.php @@ -0,0 +1,27 @@ +guessFileCandidate($baseDir . $relativePath); + } + + /** + * @inheritdoc + */ + function pluginLoadClass($class, $baseDir, $relativePath) { + if (is_file($file = $baseDir . $relativePath)) { + include_once $file; + // This check happens inline for micro-optimization. + return class_exists($class, FALSE) + || interface_exists($class, FALSE) + || (PHP_VERSION_ID >= 50400 && trait_exists($class, FALSE)) + ; + } + } +} diff --git a/core/vendor/donquixote/krautoload/src/Krautoload/PrefixPathPlugin/ShallowPEAR/UseIncludePath.php b/core/vendor/donquixote/krautoload/src/Krautoload/PrefixPathPlugin/ShallowPEAR/UseIncludePath.php new file mode 100644 index 0000000..0c8303d --- /dev/null +++ b/core/vendor/donquixote/krautoload/src/Krautoload/PrefixPathPlugin/ShallowPEAR/UseIncludePath.php @@ -0,0 +1,27 @@ +guessFile_checkIncludePath($baseDir . $relativePath)) { + return TRUE; + } + } + + /** + * @inheritdoc + */ + function pluginLoadClass($class, $baseDir, $relativePath) { + // We don't know if the file exists. + if (FALSE !== $file = Util::findFileInIncludePath($baseDir . $relativePath)) { + include_once $file; + return Util::classIsDefined($class); + } + } +} \ No newline at end of file diff --git a/core/vendor/donquixote/krautoload/src/Krautoload/RegistrationHub.php b/core/vendor/donquixote/krautoload/src/Krautoload/RegistrationHub.php new file mode 100644 index 0000000..520b16a --- /dev/null +++ b/core/vendor/donquixote/krautoload/src/Krautoload/RegistrationHub.php @@ -0,0 +1,36 @@ +finder instanceof NamespaceInspector_Interface) { + throw new \Exception("Introspection not possible with the given class loader object."); + } + return parent::buildSearchableNamespaces($namespaces); + } +} diff --git a/core/vendor/donquixote/krautoload/src/Krautoload/SearchableNamespaces/Default.php b/core/vendor/donquixote/krautoload/src/Krautoload/SearchableNamespaces/Default.php new file mode 100644 index 0000000..f345f9b --- /dev/null +++ b/core/vendor/donquixote/krautoload/src/Krautoload/SearchableNamespaces/Default.php @@ -0,0 +1,141 @@ +finder = $finder; + } + + /** + * @param NamespaceInspector_Interface $finder + */ + function setFinder(NamespaceInspector_Interface $finder) { + $this->finder = $finder; + } + + /** + * @inheritdoc + */ + function addNamespace($namespace) { + $this->namespaces[$namespace] = $namespace; + } + + /** + * @inheritdoc + */ + function setNamespaces(array $namespaces) { + $this->namespaces = array(); + $this->addNamespaces($namespaces); + } + + /** + * @inheritdoc + */ + function addNamespaces(array $namespaces) { + foreach ($namespaces as $namespace) { + $this->namespaces[$namespace] = $namespace; + } + } + + /** + * @inheritdoc + */ + function getNamespaces() { + return $this->namespaces; + } + + /** + * @inheritdoc + */ + function buildSearchableNamespaces(array $namespaces = array()) { + $new = new self($this->finder); + $new->addNamespaces($namespaces); + return $new; + } + + /** + * @inheritdoc + */ + function buildFromSuffix($suffix) { + if ('\\' !== $suffix[0]) { + $suffix = '\\' . $suffix; + } + $new = $this->buildSearchableNamespaces(); + foreach ($this->namespaces as $namespace) { + $new->addNamespace($namespace . $suffix); + } + return $new; + } + + /** + * @inheritdoc + */ + function apiVisitClassFiles(InjectedAPI_ClassFileVisitor_Interface $api, $recursive = FALSE) { + $namespaceVisitorAPI = $recursive + ? new InjectedAPI_NamespaceInspector_ScanRecursive($api) + : new InjectedAPI_NamespaceInspector_ScanNamespace($api) + ; + $this->apiInspectNamespaces($namespaceVisitorAPI, $recursive); + } + + /** + * @inheritdoc + */ + function apiInspectNamespaces(InjectedAPI_NamespaceInspector_Interface $api, $recursive = FALSE) { + $this->finder->apiInspectNamespaces($api, $this->namespaces, $recursive); + } + + /** + * @inheritdoc + */ + function discoverExistingClasses($recursive = FALSE) { + $api = new InjectedAPI_ClassFileVisitor_CollectExistingClasses(); + $this->apiVisitClassFiles($api, $recursive); + return $api->getCollectedClasses(); + } + + /** + * @inheritdoc + */ + function discoverCandidateClasses($recursive = FALSE) { + $api = new InjectedAPI_ClassFileVisitor_CollectCandidateClasses(); + $this->apiVisitClassFiles($api, $recursive); + return $api->getCollectedClasses(); + } + + /** + * @inheritdoc + */ + 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/donquixote/krautoload/src/Krautoload/SearchableNamespaces/Interface.php b/core/vendor/donquixote/krautoload/src/Krautoload/SearchableNamespaces/Interface.php new file mode 100644 index 0000000..0d74926 --- /dev/null +++ b/core/vendor/donquixote/krautoload/src/Krautoload/SearchableNamespaces/Interface.php @@ -0,0 +1,97 @@ + $item) { + if ($item['function'] === 'spl_autoload_call') { + switch ($f = $trace[$i + 1]['function']) { + case 'class_exists': + case 'interface_exists': + case 'method_exists': + case 'trait_exists': + case 'is_callable': + // @todo Add more cases. + return TRUE; + default: + return FALSE; + } + } + } + } + + /** + * Checks whether an identifier is defined as either a class, interface or + * trait. Does not trigger autoloading. + * + * @param string $class + * @return bool + */ + static function classIsDefined($class) { + return class_exists($class, FALSE) + || interface_exists($class, FALSE) + || (PHP_VERSION_ID >= 50400 && trait_exists($class, FALSE)) + ; + } + + /** + * 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. + * FALSE, otherwise. + */ + static 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; + } + } + + /** + * Check if a file exists, considering the full include path. + * + * @param string $file + * The filepath + * @return boolean|string + * The resolved file path, if the file exists in the include path. + * FALSE, otherwise. + */ + static function findFileInIncludePath($file) { + if (function_exists('stream_resolve_include_path')) { + // Use the PHP 5.3.1+ way of doing this. + return stream_resolve_include_path($file); + } + elseif ($file{0} === DIRECTORY_SEPARATOR) { + // That's an absolute path already. + return file_exists($file) ? $file : FALSE; + } + 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 $base_dir . DIRECTORY_SEPARATOR . $file; + } + } + return FALSE; + } + } + + /** + * @param string $string + * The original string, that we want to explode. + * @param boolean $lowercase + * should the result be lowercased? + * @param string $example_string + * Example to specify how to deal with multiple uppercase characters. + * Can be something like "AA Bc" or "A A Bc" or "AABc". + * @param boolean $glue + * Allows to implode the fragments with sth like "_" or "." or " ". + * If $glue is FALSE, it will just return an array. + * + * @throws \Exception + * @return array|string + * An indexed array of pieces, if $glue is FALSE. + * A glued string, if $glue is a string. + */ + static function camelCaseExplode($string, $lowercase = TRUE, $example_string = 'AA Bc', $glue = FALSE) { + static $regexp_available = array( + '/([A-Z][^A-Z]*)/', + '/([A-Z][^A-Z]+)/', + '/([A-Z]+[^A-Z]*)/', + ); + static $regexp_by_example = array(); + if (!isset($regexp_by_example[$example_string])) { + foreach ($regexp_available as $regexp) { + if (implode(' ', preg_split( + $regexp, + str_replace(' ', '', $example_string), + -1, + PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE + )) == $example_string) { + break; + } + } + if (!isset($regexp)) { + throw new \Exception("Invalid example string '$example_string'."); + } + $regexp_by_example[$example_string] = $regexp; + } + $array = preg_split( + $regexp_by_example[$example_string], + $string, + -1, + PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE + ); + if ($lowercase) { + $array = array_map('strtolower', $array); + } + return (FALSE !== $glue) ? implode($glue, $array) : $array; + } +} diff --git a/core/vendor/donquixote/krautoload/tests/fixtures/alpha/NamespaceCollision/A/Bar.php b/core/vendor/donquixote/krautoload/tests/fixtures/alpha/NamespaceCollision/A/Bar.php new file mode 100644 index 0000000..f0f97a5 --- /dev/null +++ b/core/vendor/donquixote/krautoload/tests/fixtures/alpha/NamespaceCollision/A/Bar.php @@ -0,0 +1,8 @@ + 'ApcCache', + 'cache_prefix' => $prefix = 'krautoload-test-07a9812c3469d38u567c64650', +)); + +$krautoload->addClassMap(array( + 'ClassMap\Foo\Bar' => 'tests/fixtures/src-classmap/classmap-foo-bar.php', +)); +$krautoload->addNamespacePSR0('Namespaced2', 'tests/fixtures/src-psr0'); +$krautoload->addPrefixPEAR('Pearlike2', 'tests/fixtures/src-psr0'); +$krautoload->addNamespacePSRX('MyVendor\MyPackage', 'tests/fixtures/src-psrx'); + +new ClassMap\Foo\Bar; +new Namespaced2\Foo; +new Pearlike2_Foo; +new MyVendor\MyPackage\Foo\Bar; + +if (class_exists('ClassMap\Foo\Baz')) { + print "Class 'ClassMap\Foo\Bar' exists, although it should not.\n"; +} + +// Manually add the class to the APC cache. +apc_store($key = $prefix . 'ClassMap\Foo\Baz', 'tests/fixtures/src-classmap/classmap-foo-baz.php', 10); + +new ClassMap\Foo\Baz; + +echo 'SUCCESS'; \ No newline at end of file diff --git a/core/vendor/donquixote/krautoload/tests/scripts/testrun.php b/core/vendor/donquixote/krautoload/tests/scripts/testrun.php new file mode 100644 index 0000000..8a32763 --- /dev/null +++ b/core/vendor/donquixote/krautoload/tests/scripts/testrun.php @@ -0,0 +1,25 @@ +addClassMap(array( + 'ClassMap\Foo\Bar' => 'tests/fixtures/src-classmap/classmap-foo-bar.php', + 'ClassMap\Foo\Baz' => 'tests/fixtures/src-classmap/classmap-foo-baz.php', +)); +$krautoload->addNamespacePSR0('Namespaced2', 'tests/fixtures/src-psr0'); +$krautoload->addPrefixPEAR('Pearlike2', 'tests/fixtures/src-psr0'); +$krautoload->addNamespacePSRX('MyVendor\MyPackage', 'tests/fixtures/src-psrx'); + +new ClassMap\Foo\Bar; +new ClassMap\Foo\Baz; +new Namespaced2\Foo; +new Pearlike2_Foo; +new MyVendor\MyPackage\Foo\Bar; + +echo 'SUCCESS'; \ No newline at end of file diff --git a/core/vendor/donquixote/krautoload/tests/src/BootstrapTest.php b/core/vendor/donquixote/krautoload/tests/src/BootstrapTest.php new file mode 100644 index 0000000..c475fdf --- /dev/null +++ b/core/vendor/donquixote/krautoload/tests/src/BootstrapTest.php @@ -0,0 +1,25 @@ +runTestScript('testrun.php'); + } + + function testBootstrapApc() { + if (extension_loaded('apc') && function_exists('apc_store')) { + $this->runTestScript('testrun-apc.php', '-d apc.enable_cli=1'); + } + } + + protected function runTestScript($script, $options = '', $expected = 'SUCCESS') { + ob_start(); + system('php ' . $options . ' tests/scripts/' . $script); + $response = ob_get_clean(); + $this->assertEquals($expected, $response); + } +} \ No newline at end of file diff --git a/core/vendor/donquixote/krautoload/tests/src/ClassDiscoveryTest.php b/core/vendor/donquixote/krautoload/tests/src/ClassDiscoveryTest.php new file mode 100644 index 0000000..378b6a9 --- /dev/null +++ b/core/vendor/donquixote/krautoload/tests/src/ClassDiscoveryTest.php @@ -0,0 +1,295 @@ +inspector = new k\NamespaceInspector_Pluggable(); + $this->hub = new k\RegistrationHub($this->inspector); + } + + public function testDiscoveryPSR0() { + + // Register PSR-0 namespace. + $dir = $this->getFixturesSubdir('src-psr0'); + $this->hub->addNamespacePSR0('Namespace_With_Underscore', $dir); + $this->hub->addNamespacePSR0('Namespaced', $dir); + $this->hub->addNamespacePSR0('Namespaced2', $dir); + + // Build the mock $api object. + // We can't use the mock stuff shipped with PHPUnit, because we need a specific order of calls. + $api = new k\InjectedAPI_ClassFileVisitor_Mock(); + + // Run the discovery. + $this->hub->buildSearchableNamespaces(array( + 'Namespace_With_Underscore', + 'Namespaced', + 'Namespace_With_Underscore\Sub_Namespace', + ))->apiVisitClassFiles($api, TRUE); + + // Verify the result. + $called = $api->mockGetCalled(); + + // The InjectedAPI object is being told about the to-be-inspected namespace. + $this->assertEquals($called[0], array('setNamespace', array('Namespace_With_Underscore\\'))); + + // The two class files for this namespace may be discovered in any order. + $this->assertArraySlice($called, 1, 2, array( + array( + 'fileWithClassCandidates', + array( + $dir . '/Namespace_With_Underscore/Sub_Namespace/Foo/BarUnsafe.php', + array( + // Class names are given relative to the inspected namespace. + // There are three class names that could be defined in the file. + // They are expected in this exact order. + 'Sub_Namespace\Foo\BarUnsafe', + 'Sub_Namespace\Foo_BarUnsafe', + 'Sub_Namespace_Foo_BarUnsafe', + ), + ), + ), + array( + 'fileWithClassCandidates', + array( + $dir . '/Namespace_With_Underscore/Sub_Namespace/Foo/Bar.php', + array( + 'Sub_Namespace\Foo\Bar', + 'Sub_Namespace\Foo_Bar', + 'Sub_Namespace_Foo_Bar', + ), + ), + ), + )); + + $this->assertEquals($called[3], array('setNamespace', array('Namespaced\\'))); + + // The three class files for this namespace may be discovered in any order. + $this->assertArraySlice($called, 4, 3, array( + array( + 'fileWithClassCandidates', + array( + $dir . '/Namespaced/Foo.php', + array( + 'Foo', + ), + ), + ), + array( + 'fileWithClassCandidates', + array( + $dir . '/Namespaced/Bar.php', + array( + 'Bar', + ), + ), + ), + array( + 'fileWithClassCandidates', + array( + $dir . '/Namespaced/Baz.php', + array( + 'Baz', + ), + ), + ), + )); + + $this->assertEquals($called[7], array('setNamespace', array('Namespace_With_Underscore\\Sub_Namespace\\'))); + + // The two class files for this namespace may be discovered in any order. + $this->assertArraySlice($called, 8, 2, array( + array( + 'fileWithClassCandidates', + array( + $dir . '/Namespace_With_Underscore/Sub_Namespace/Foo/BarUnsafe.php', + array( + 'Foo\BarUnsafe', + 'Foo_BarUnsafe', + ), + ), + ), + array( + 'fileWithClassCandidates', + array( + $dir . '/Namespace_With_Underscore/Sub_Namespace/Foo/Bar.php', + array( + 'Foo\Bar', + 'Foo_Bar', + ), + ), + ), + )); + } + + public function testDiscoveryPSRX() { + + // Register PSR-X namespace. + $dir = $this->getFixturesSubdir('src-psrx'); + $this->hub->addNamespacePSRX('MyVendor\MyPackage', $dir); + + $api = new k\InjectedAPI_ClassFileVisitor_Mock(); + $this->hub->buildSearchableNamespaces(array('MyVendor\MyPackage'))->apiVisitClassFiles($api, TRUE); + $called = $api->mockGetCalled(); + + $this->assertEquals($called[0], array('setNamespace', array('MyVendor\MyPackage\\'))); + + $this->assertArraySlice($called, 1, 1, array( + array( + 'fileWithClass', + array( + $dir . '/Foo/Bar.php', + 'Foo\Bar', + ), + ), + )); + } + + public function testDiscoveryPSRXChild() { + + // Register PSR-X namespace. + $dir = $this->getFixturesSubdir('src-psrx'); + $this->hub->addNamespacePSRX('MyVendor\MyPackage', $dir); + + $api = new k\InjectedAPI_ClassFileVisitor_Mock(); + $this->hub->buildSearchableNamespaces(array('MyVendor\MyPackage\Foo'))->apiVisitClassFiles($api, TRUE); + $called = $api->mockGetCalled(); + + $this->assertEquals($called[0], array('setNamespace', array('MyVendor\MyPackage\Foo\\'))); + + $this->assertArraySlice($called, 1, 1, array( + array( + 'fileWithClass', + array( + $dir . '/Foo/Bar.php', + 'Bar', + ), + ), + )); + } + + public function testDiscoveryPSRXParent() { + + // Register PSR-X namespace. + $dir = $this->getFixturesSubdir('src-psrx'); + $this->hub->addNamespacePSRX('MyVendor\MyPackage', $dir); + + + $api = new k\InjectedAPI_ClassFileVisitor_Mock(); + $this->hub->buildSearchableNamespaces(array('MyVendor'))->apiVisitClassFiles($api, TRUE); + $called = $api->mockGetCalled(); + + $this->assertEquals($called[0], array('setNamespace', array('MyVendor\\'))); + + $this->assertArraySlice($called, 1, 1, array( + array( + 'fileWithClass', + array( + $dir . '/Foo/Bar.php', + 'MyPackage\Foo\Bar', + ), + ), + )); + } + + public function testDiscoveryPSRXRoot() { + + // Register PSR-X namespace. + $dir = $this->getFixturesSubdir('src-psrx'); + $this->hub->addNamespacePSRX('MyVendor\MyPackage', $dir); + + + $api = new k\InjectedAPI_ClassFileVisitor_Mock(); + $this->hub->buildSearchableNamespaces(array(''))->apiVisitClassFiles($api, TRUE); + $called = $api->mockGetCalled(); + + $this->assertEquals($called[0], array('setNamespace', array(''))); + + $this->assertArraySlice($called, 1, 1, array( + array( + 'fileWithClass', + array( + $dir . '/Foo/Bar.php', + 'MyVendor\MyPackage\Foo\Bar', + ), + ), + )); + } + + public function testDiscoverExistingClasses() { + + // Register PSR-0 and PSR-X mappings. + $this->hub->addNamespacePSR0('Namespace_With_Underscore', $this->getFixturesSubdir('src-psr0')); + $this->hub->addNamespacePSRX('MyVendor\MyPackage', $this->getFixturesSubdir('src-psrx')); + + // Build SearchableNamespaces object. + $namespaces = $this->hub->buildSearchableNamespaces(array('Namespace_With_Underscore')); + + // Search. + $classes = $namespaces->discoverExistingClasses(TRUE); + $expected = array( + 'Namespace_With_Underscore\Sub_Namespace\Foo_Bar', + 'Namespace_With_Underscore\Sub_Namespace\Foo_BarUnsafe', + ); + $this->assertArrayElements($classes, array_combine($expected, $expected)); + + // Add another namespace (PSR-X) + $namespaces->addNamespace('MyVendor'); + + // Search again. + $classes = $namespaces->discoverExistingClasses(TRUE); + $expected[] = 'MyVendor\MyPackage\Foo\Bar'; + $this->assertArrayElements($classes, array_combine($expected, $expected)); + } + + public function testClassExistsInNamespace() { + + $this->hub->addNamespacePSR0('Namespace_With_Underscore', $this->getFixturesSubdir('src-psr0')); + $namespaces = $this->hub->buildSearchableNamespaces(); + $this->assertFalse($namespaces->classExistsInNamespaces('Namespace_With_Underscore\Sub_Namespace\Foo_Bar')); + $namespaces->addNamespace('Namespace_With_Underscore\Sub_Namespace'); + $this->assertTrue($namespaces->classExistsInNamespaces('Namespace_With_Underscore\Sub_Namespace\Foo_Bar')); + $this->assertTrue($namespaces->classExistsInNamespaces('Namespace_With_Underscore\Sub_Namespace\Foo_Bar')); + $namespaces->addNamespace('MyVendor\MyPackage'); + $this->assertFalse($namespaces->classExistsInNamespaces('MyVendor\MyPackage\Foo\Bar')); + } + + protected function assertArraySlice(array $array, $offset, $count, array $compare) { + $slice = array_slice($array, $offset, $count); + $this->assertArrayElements($slice, $compare); + } + + protected function assertArrayElements(array $array, array $compare) { + $this->sortBySerializing($array); + $this->sortBySerializing($compare); + $this->assertEquals($compare, $array); + } + + protected function sortBySerializing(array &$array) { + $sorted = array(); + foreach ($array as $item) { + $sorted[] = serialize($item); + } + array_multisort($sorted, $array); + } + + protected function getFixturesSubdir($suffix) { + return 'tests/fixtures/' . $suffix; + } +} diff --git a/core/vendor/donquixote/krautoload/tests/src/PluggableClassLoaderTest.php b/core/vendor/donquixote/krautoload/tests/src/PluggableClassLoaderTest.php new file mode 100644 index 0000000..565055b --- /dev/null +++ b/core/vendor/donquixote/krautoload/tests/src/PluggableClassLoaderTest.php @@ -0,0 +1,224 @@ +loader = new k\ClassLoader_Pluggable(); + $this->hub = new k\RegistrationHub($this->loader); + } + + public function testLoadClass() { + $this->hub->addClassMap(array( + 'ClassMap\Foo\Bar' => $this->getFixturesSubdir('src-classmap') . '/classmap-foo-bar.php', + 'ClassMap\Foo\Baz' => $this->getFixturesSubdir('src-classmap') . '/classmap-foo-baz.php', + )); + $this->hub->addNamespacePSR0('Namespaced2', $this->getFixturesSubdir('src-psr0')); + $this->hub->addPrefixPEAR('Pearlike2', $this->getFixturesSubdir('src-psr0')); + $this->hub->addNamespacePSRX('MyVendor\MyPackage', $this->getFixturesSubdir('src-psrx')); + $this->assertLoadClass('ClassMap\Foo\Bar'); + $this->assertLoadClass('ClassMap\Foo\Baz'); + $this->assertLoadClass('Namespaced2\Foo'); + $this->assertLoadClass('Pearlike2_Foo'); + $this->assertLoadClass('MyVendor\MyPackage\Foo\Bar'); + } + + public function testExotic() { + $plugin = new k\PrefixPathPlugin_Exotic_CamelSwap(); + $this->hub->addPrefixPlugin('CamelSwap', $this->getFixturesSubdir('camel-swap'), $plugin); + // The class is in tests/fixtures/camel-swap/controller/page/help.php. + $this->assertLoadClass('CamelSwap_HelpPageController'); + } + + public function testUnderscoreSafe() { + // Using the paranoid plugin. + $this->hub->addNamespacePSR0('Namespace_With_Underscore', $this->getFixturesSubdir('src-psr0')); + $this->assertLoadClass('Namespace_With_Underscore\Sub_Namespace\Foo_Bar'); + // This would break in other class loaders. + $this->assertNotLoadClass('Namespace_With_Underscore\Sub_Namespace\Foo\Bar'); + } + + public function testUnderscoreUnsafe() { + // Using the non-paranoid plugin. + $plugin = new k\NamespacePathPlugin_ShallowPSR0_NoConflict(); + $this->hub->addNamespacePlugin('Namespace_With_Underscore', $this->getFixturesSubdir('src-psr0/Namespace_With_Underscore'), $plugin); + $this->assertLoadClass('Namespace_With_Underscore\Sub_Namespace\Foo_BarUnsafe'); + try { + $this->assertNotLoadClass('Namespace_With_Underscore\Sub_Namespace\Foo\BarUnsafe'); + } + catch (\Exception $e) { + $this->assertEquals($e->getMessage(), 'Cannot redefine class.'); + return; + } + $this->fail('The NoConflict loader plugin is expected to break with duplicate class definition.'); + } + + public function testUseIncludePath() { + + // Register a plugin that can handle include path. + $plugin = new k\PrefixPathPlugin_ShallowPEAR_UseIncludePath(); + $this->hub->addPrefixPlugin('', '', $plugin); + + $this->assertNotLoadClass('Foo', "Class 'Foo' still undefined after ->loadClass() without include path."); + + // Remember original include path. + $includePath = get_include_path(); + set_include_path($this->getFixturesSubdir('includepath') . PATH_SEPARATOR . $includePath); + + $this->loader->loadClass('Foo'); + $this->assertClassDefined('Foo', "Class 'Foo' successfully loaded after ->loadClass() with include path."); + + // Revert include path to its original value. + set_include_path($includePath); + } + + /** + * @dataProvider getLoadClassFromFallbackTests + */ + public function testLoadClassFromFallback($class, $from = '') { + $this->hub->addNamespacePSR0('Namespaced2', $this->getFixturesSubdir('src-psr0')); + $this->hub->addPrefixPEAR('Pearlike2', $this->getFixturesSubdir('src-psr0')); + // addPrefixPSR0 applies to namespaced and non-namespaced classes. + $this->hub->addPrefixPSR0('', $this->getFixturesSubdir('fallback')); + + $this->assertClassUndefined($class); + $this->loader->loadClass($class); + $this->assertClassDefined($class, "Class '$class' successfully loaded$from."); + } + + public function getLoadClassFromFallbackTests() { + return array( + array('Namespaced2\\Baz'), + array('Pearlike2_Baz'), + array('Namespaced2\\FooBar', ' from fallback dir'), + array('Pearlike2_FooBar', ' from fallback dir'), + ); + } + + /** + * @dataProvider getLoadClassNamespaceCollisionTests + */ + public function testLoadClassNamespaceCollision($namespaces, $class, $message) { + + $this->hub->addPrefixesPSR0($namespaces); + + $this->assertClassUndefined($class); + $this->loader->loadClass($class); + $this->assertClassDefined($class, $message); + } + + public function getLoadClassNamespaceCollisionTests() { + return array( + array( + array( + 'NamespaceCollision\\C' => $this->getFixturesSubdir('alpha'), + 'NamespaceCollision\\C\\B' => $this->getFixturesSubdir('beta'), + ), + 'NamespaceCollision\C\Foo', + '->loadClass() loads NamespaceCollision\C\Foo from alpha.', + ), + array( + array( + 'NamespaceCollision\\C\\B' => $this->getFixturesSubdir('beta'), + 'NamespaceCollision\\C' => $this->getFixturesSubdir('alpha'), + ), + 'NamespaceCollision\C\Bar', + '->loadClass() loads NamespaceCollision\C\Bar from alpha.', + ), + array( + array( + 'NamespaceCollision\\C' => $this->getFixturesSubdir('alpha'), + 'NamespaceCollision\\C\\B' => $this->getFixturesSubdir('beta'), + ), + 'NamespaceCollision\C\B\Foo', + '->loadClass() loads NamespaceCollision\C\B\Foo from beta.', + ), + array( + array( + 'NamespaceCollision\\C\\B' => $this->getFixturesSubdir('beta'), + 'NamespaceCollision\\C' => $this->getFixturesSubdir('alpha'), + ), + 'NamespaceCollision\C\B\Bar', + '->loadClass() loads NamespaceCollision\C\B\Bar from beta.', + ), + array( + array( + 'PrefixCollision_C_' => $this->getFixturesSubdir('alpha'), + 'PrefixCollision_C_B_' => $this->getFixturesSubdir('beta'), + ), + 'PrefixCollision_C_Foo', + '->loadClass() loads PrefixCollision_C_Foo from alpha.', + ), + array( + array( + 'PrefixCollision_C_B_' => $this->getFixturesSubdir('beta'), + 'PrefixCollision_C_' => $this->getFixturesSubdir('alpha'), + ), + 'PrefixCollision_C_Bar', + '->loadClass() loads PrefixCollision_C_Bar from alpha.', + ), + array( + array( + 'PrefixCollision_C_' => $this->getFixturesSubdir('alpha'), + 'PrefixCollision_C_B_' => $this->getFixturesSubdir('beta'), + ), + 'PrefixCollision_C_B_Foo', + '->loadClass() loads PrefixCollision_C_B_Foo from beta.', + ), + array( + array( + 'PrefixCollision_C_B_' => $this->getFixturesSubdir('beta'), + 'PrefixCollision_C_' => $this->getFixturesSubdir('alpha'), + ), + 'PrefixCollision_C_B_Bar', + '->loadClass() loads PrefixCollision_C_B_Bar from beta.', + ), + ); + } + + protected function assertLoadClass($class, $message = NULL) { + $this->assertClassUndefined($class); + $this->loader->loadClass($class); + $this->assertClassDefined($class, $message); + } + + protected function assertNotLoadClass($class, $message = NULL) { + if (!isset($message)) { + $message = "Class '$class' is still undefined after ->loadClass()."; + } + $this->assertClassUndefined($class); + $this->loader->loadClass($class); + $this->assertClassUndefined($class, $message); + } + + protected function assertClassUndefined($class, $message = NULL) { + if (!isset($message)) { + $message = "Class '$class' is not defined before ->loadClass()."; + } + $this->assertFalse(k\Util::classIsDefined($class), $message); + } + + protected function assertClassDefined($class, $message = NULL) { + if (!isset($message)) { + $message = "Class '$class' was successfully loaded with ->loadClass()."; + } + $this->assertTrue(k\Util::classIsDefined($class), $message); + } + + protected function getFixturesSubdir($suffix) { + return __DIR__ . '/../fixtures/' . $suffix; + } +} diff --git a/core/vendor/donquixote/krautoload/trigger-travis.txt b/core/vendor/donquixote/krautoload/trigger-travis.txt new file mode 100644 index 0000000..e69de29