/** * {@inheritdoc} */ public function getDefinitions() { $definitions = array(); $reader = $this->getAnnotationReader(); // Clear the annotation loaders of any previous annotation classes. AnnotationRegistry::reset(); // Register the namespaces of classes that can be used for annotations. AnnotationRegistry::registerLoader('class_exists'); // Search for classes within all PSR-0 namespace locations. foreach ($this->getPluginNamespaces() as $namespace => $dirs) { foreach ($dirs as $dir) { if (file_exists($dir)) { $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($dir, \RecursiveDirectoryIterator::SKIP_DOTS)); foreach ($iterator as $fileinfo) { if ($fileinfo->getExtension() == 'php') { $sub_path = $iterator->getSubIterator()->getSubPath(); $sub_path = $sub_path ? str_replace('/', '\\', $sub_path) . '\\' : ''; $class = $namespace . '\\' . str_replace('/', '\\', $this->pluginManagerDefinition['directory']) . '\\' . $sub_path . $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, TRUE); if ($annotation = $reader->getClassAnnotation($parser->getReflectionClass(), $this->pluginDefinitionAnnotationName)) { $this->prepareAnnotationDefinition($annotation, $class); $definitions[$annotation->getId()] = $annotation->get(); } } } } } } // Don't let annotation loaders pile up. AnnotationRegistry::reset(); return $definitions; }
/** * Discovers all available tests in all extensions. * * @param string $extension * (optional) The name of an extension to limit discovery to; e.g., 'node'. * * @return array * An array of tests keyed by the first @group specified in each test's * PHPDoc comment block, and then keyed by class names. For example: * @code * $groups['block'] => array( * 'Drupal\block\Tests\BlockTest' => array( * 'name' => 'Drupal\block\Tests\BlockTest', * 'description' => 'Tests block UI CRUD functionality.', * 'group' => 'block', * ), * ); * @endcode * * @throws \ReflectionException * If a discovered test class does not match the expected class name. * * @todo Remove singular grouping; retain list of groups in 'group' key. * @see https://www.drupal.org/node/2296615 * @todo Add base class groups 'Kernel' + 'Web', complementing 'PHPUnit'. */ public function getTestClasses($extension = NULL) { $reader = new SimpleAnnotationReader(); $reader->addNamespace('Drupal\\simpletest\\Annotation'); if (!isset($extension)) { if ($this->cacheBackend && ($cache = $this->cacheBackend->get('simpletest:discovery:classes'))) { return $cache->data; } } $list = array(); $classmap = $this->findAllClassFiles($extension); // Prevent expensive class loader lookups for each reflected test class by // registering the complete classmap of test classes to the class loader. // This also ensures that test classes are loaded from the discovered // pathnames; a namespace/classname mismatch will throw an exception. $this->classLoader->addClassMap($classmap); foreach ($classmap as $classname => $pathname) { $finder = MockFileFinder::create($pathname); $parser = new StaticReflectionParser($classname, $finder, TRUE); try { $info = static::getTestInfo($classname, $parser->getDocComment()); } catch (MissingGroupException $e) { // If the class name ends in Test and is not a migrate table dump. if (preg_match('/Test$/', $classname) && strpos($classname, 'migrate_drupal\\Tests\\Table') === FALSE) { throw $e; } // If the class is @group annotation just skip it. Most likely it is an // abstract class, trait or test fixture. continue; } // Skip this test class if it requires unavailable modules. // @todo PHPUnit skips tests with unmet requirements when executing a test // (instead of excluding them upfront). Refactor test runner to follow // that approach. // @see https://www.drupal.org/node/1273478 if (!empty($info['requires']['module'])) { if (array_diff($info['requires']['module'], $this->availableExtensions['module'])) { continue; } } $list[$info['group']][$classname] = $info; } // Sort the groups and tests within the groups by name. uksort($list, 'strnatcasecmp'); foreach ($list as &$tests) { uksort($tests, 'strnatcasecmp'); } // Allow modules extending core tests to disable originals. \Drupal::moduleHandler()->alter('simpletest', $list); if (!isset($extension)) { if ($this->cacheBackend) { $this->cacheBackend->set('simpletest:discovery:classes', $list); } } return $list; }
/** * {@inheritdoc} */ public function getDefinitions() { $definitions = array(); $reader = $this->getAnnotationReader(); // Clear the annotation loaders of any previous annotation classes. AnnotationRegistry::reset(); // Register the namespaces of classes that can be used for annotations. AnnotationRegistry::registerLoader('class_exists'); // Search for classes within all PSR-0 namespace locations. foreach ($this->getPluginNamespaces() as $namespace => $dirs) { foreach ($dirs as $dir) { if (file_exists($dir)) { $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($dir, \RecursiveDirectoryIterator::SKIP_DOTS)); foreach ($iterator as $fileinfo) { if ($fileinfo->getExtension() == 'php') { if ($cached = $this->fileCache->get($fileinfo->getPathName())) { if (isset($cached['id'])) { // Explicitly unserialize this to create a new object instance. $definitions[$cached['id']] = unserialize($cached['content']); } continue; } $sub_path = $iterator->getSubIterator()->getSubPath(); $sub_path = $sub_path ? str_replace(DIRECTORY_SEPARATOR, '\\', $sub_path) . '\\' : ''; $class = $namespace . '\\' . $sub_path . $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 BaseStaticReflectionParser($class, $finder, FALSE); /** @var $annotation \Drupal\Component\Annotation\AnnotationInterface */ if ($annotation = $reader->getClassAnnotation($parser->getReflectionClass(), $this->pluginDefinitionAnnotationName)) { $this->prepareAnnotationDefinition($annotation, $class, $parser); $id = $annotation->getId(); $content = $annotation->get(); $definitions[$id] = $content; // Explicitly serialize this to create a new object instance. $this->fileCache->set($fileinfo->getPathName(), ['id' => $id, 'content' => serialize($content)]); } else { // Store a NULL object, so the file is not reparsed again. $this->fileCache->set($fileinfo->getPathName(), [NULL]); } } } } } } // Don't let annotation loaders pile up. AnnotationRegistry::reset(); return $definitions; }