RecursiveExtensionFilterIterator.php

Same filename and directory in other branches
  1. 9 core/lib/Drupal/Core/Extension/Discovery/RecursiveExtensionFilterIterator.php
  2. 8.9.x core/lib/Drupal/Core/Extension/Discovery/RecursiveExtensionFilterIterator.php
  3. 11.x core/lib/Drupal/Core/Extension/Discovery/RecursiveExtensionFilterIterator.php

Namespace

Drupal\Core\Extension\Discovery

File

core/lib/Drupal/Core/Extension/Discovery/RecursiveExtensionFilterIterator.php

View source
<?php

namespace Drupal\Core\Extension\Discovery;


/**
 * Filters a RecursiveDirectoryIterator to discover extensions.
 *
 * To ensure the best possible performance for extension discovery, this
 * filter implementation hard-codes a range of assumptions about directories
 * in which Drupal extensions may appear and in which not. Every unnecessary
 * subdirectory tree recursion is avoided.
 *
 * The list of globally ignored directory names is defined in the
 * RecursiveExtensionFilterIterator::$skippedFolders property.
 *
 * In addition, all 'config' directories are skipped, unless the directory path
 * ends with 'modules/config', so as to still find the config module provided by
 * Drupal core and still allow that module to be overridden with a custom config
 * module.
 *
 * Lastly, ExtensionDiscovery instructs this filter to additionally skip all
 * 'tests' directories at regular runtime, since just with Drupal core only, the
 * discovery process yields 4x more extensions when tests are not ignored.
 *
 * @see ExtensionDiscovery::scan()
 * @see ExtensionDiscovery::scanDirectory()
 *
 * @deprecated in drupal:10.2.0 and is removed from drupal:11.0.0. Use
 *   \Drupal\Core\Extension\Discovery\RecursiveExtensionFilterCallback instead.
 *
 * @see https://www.drupal.org/node/3343023
 */
class RecursiveExtensionFilterIterator extends \RecursiveFilterIterator {
    
    /**
     * List of base extension type directory names to scan.
     *
     * Only these directory names are considered when starting a filesystem
     * recursion in a search path.
     *
     * @var array
     */
    protected $allowedExtensionTypes = [
        'profiles',
        'modules',
        'themes',
    ];
    
    /**
     * List of directory names to skip when recursing.
     *
     * These directories are globally ignored in the recursive filesystem scan;
     * i.e., extensions (of all types) are not able to use any of these names,
     * because their directory names will be skipped.
     *
     * @var array
     */
    protected $skippedFolders = [
        // Object-oriented code subdirectories.
'src',
        'lib',
        'vendor',
        // Front-end.
'assets',
        'css',
        'files',
        'images',
        'js',
        'misc',
        'templates',
        // Legacy subdirectories.
'includes',
        // Test subdirectories.
'fixtures',
        // @todo ./tests/Drupal should be ./tests/src/Drupal
'Drupal',
    ];
    
    /**
     * Whether to include test directories when recursing.
     *
     * @var bool
     */
    protected $acceptTests = FALSE;
    
    /**
     * Construct a RecursiveExtensionFilterIterator.
     *
     * @param \RecursiveIterator $iterator
     *   The iterator to filter.
     * @param string[] $skipped_folders
     *   (optional) Add to the list of directories that should be filtered out
     *   during the iteration.
     */
    public function __construct(\RecursiveIterator $iterator, array $skipped_folders = []) {
        @trigger_error('The ' . __NAMESPACE__ . '\\RecursiveExtensionFilterIterator is deprecated in drupal:10.2.0 and is removed from drupal:11.0.0. Use \\Drupal\\Core\\Extension\\Discovery\\RecursiveExtensionFilterCallback instead. See https://www.drupal.org/node/3343023', E_USER_DEPRECATED);
        parent::__construct($iterator);
        $this->skippedFolders = array_merge($this->skippedFolders, $skipped_folders);
    }
    
    /**
     * Controls whether test directories will be scanned.
     *
     * @param bool $flag
     *   Pass FALSE to skip all test directories in the discovery. If TRUE,
     *   extensions in test directories will be discovered and only the global
     *   directory skip list in RecursiveExtensionFilterIterator::$skippedFolders
     *   is applied.
     */
    public function acceptTests($flag = FALSE) {
        $this->acceptTests = $flag;
        if (!$this->acceptTests) {
            $this->skippedFolders[] = 'tests';
        }
    }
    
    /**
     * {@inheritdoc}
     */
    public function getChildren() {
        $filter = parent::getChildren();
        // Pass on the skipped folders list.
        $filter->skippedFolders = $this->skippedFolders;
        // Pass the $acceptTests flag forward to child iterators.
        $filter->acceptTests($this->acceptTests);
        return $filter;
    }
    
    /**
     * {@inheritdoc}
     */
    public function accept() {
        $name = $this->current()
            ->getFilename();
        // FilesystemIterator::SKIP_DOTS only skips '.' and '..', but not hidden
        // directories (like '.git').
        if ($name[0] == '.') {
            return FALSE;
        }
        if ($this->current()
            ->isDir()) {
            // If this is a subdirectory of a base search path, only recurse into the
            // fixed list of expected extension type directory names. Required for
            // scanning the top-level/root directory; without this condition, we would
            // recurse into the whole filesystem tree that possibly contains other
            // files aside from Drupal.
            if ($this->current()
                ->getSubPath() == '') {
                return in_array($name, $this->allowedExtensionTypes, TRUE);
            }
            // 'config' directories are special-cased here, because every extension
            // contains one. However, those default configuration directories cannot
            // contain extensions. The directory name cannot be globally skipped,
            // because core happens to have a directory of an actual module that is
            // named 'config'. By explicitly testing for that case, we can skip all
            // other config directories, and at the same time, still allow the core
            // config module to be overridden/replaced in a profile/site directory
            // (whereas it must be located directly in a modules directory).
            if ($name == 'config') {
                return str_ends_with($this->current()
                    ->getPathname(), 'modules/config');
            }
            // Accept the directory unless the folder is skipped.
            return !in_array($name, $this->skippedFolders, TRUE);
        }
        else {
            // Only accept extension info files.
            return str_ends_with($name, '.info.yml');
        }
    }

}

Classes

Title Deprecated Summary
RecursiveExtensionFilterIterator

in drupal:10.2.0 and is removed from drupal:11.0.0. Use \Drupal\Core\Extension\Discovery\RecursiveExtensionFilterCallback instead.

Filters a RecursiveDirectoryIterator to discover extensions.

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.