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