class ThemeExtensionList

Same name in other branches
  1. 9 core/lib/Drupal/Core/Extension/ThemeExtensionList.php \Drupal\Core\Extension\ThemeExtensionList
  2. 10 core/lib/Drupal/Core/Extension/ThemeExtensionList.php \Drupal\Core\Extension\ThemeExtensionList
  3. 11.x core/lib/Drupal/Core/Extension/ThemeExtensionList.php \Drupal\Core\Extension\ThemeExtensionList

Provides a list of available themes.

@internal This class is not yet stable and therefore there are no guarantees that the internal implementations including constructor signature and protected properties / methods will not change over time. This will be reviewed after https://www.drupal.org/project/drupal/issues/2940481

Hierarchy

Expanded class hierarchy of ThemeExtensionList

7 files declare their use of ThemeExtensionList
BaseThemeDefaultDeprecationTest.php in core/tests/Drupal/KernelTests/Core/Theme/BaseThemeDefaultDeprecationTest.php
InstallerThemeExtensionList.php in core/lib/Drupal/Core/Installer/InstallerThemeExtensionList.php
ModuleRequiredByThemesUninstallValidatorTest.php in core/tests/Drupal/Tests/Core/Extension/ModuleRequiredByThemesUninstallValidatorTest.php
ThemeController.php in core/modules/system/src/Controller/ThemeController.php
ThemeExperimentalConfirmForm.php in core/modules/system/src/Form/ThemeExperimentalConfirmForm.php

... See full list

1 string reference to 'ThemeExtensionList'
core.services.yml in core/core.services.yml
core/core.services.yml
1 service uses ThemeExtensionList
extension.list.theme in core/core.services.yml
Drupal\Core\Extension\ThemeExtensionList

File

core/lib/Drupal/Core/Extension/ThemeExtensionList.php, line 18

Namespace

Drupal\Core\Extension
View source
class ThemeExtensionList extends ExtensionList {
    
    /**
     * {@inheritdoc}
     */
    protected $defaults = [
        'engine' => 'twig',
        'regions' => [
            'sidebar_first' => 'Left sidebar',
            'sidebar_second' => 'Right sidebar',
            'content' => 'Content',
            'header' => 'Header',
            'primary_menu' => 'Primary menu',
            'secondary_menu' => 'Secondary menu',
            'footer' => 'Footer',
            'highlighted' => 'Highlighted',
            'help' => 'Help',
            'page_top' => 'Page top',
            'page_bottom' => 'Page bottom',
            'breadcrumb' => 'Breadcrumb',
        ],
        'description' => '',
        // The following array should be kept inline with
        // _system_default_theme_features().
'features' => [
            'favicon',
            'logo',
            'node_user_picture',
            'comment_user_picture',
            'comment_user_verification',
        ],
        'screenshot' => 'screenshot.png',
        'version' => NULL,
        'php' => DRUPAL_MINIMUM_PHP,
        'libraries' => [],
        'libraries_extend' => [],
        'libraries_override' => [],
        'dependencies' => [],
    ];
    
    /**
     * The config factory.
     *
     * @var \Drupal\Core\Config\ConfigFactoryInterface
     */
    protected $configFactory;
    
    /**
     * The theme engine list needed by this theme list.
     *
     * @var \Drupal\Core\Extension\ThemeEngineExtensionList
     */
    protected $engineList;
    
    /**
     * The list of installed themes.
     *
     * @var string[]
     */
    protected $installedThemes;
    
    /**
     * Constructs a new ThemeExtensionList instance.
     *
     * @param string $root
     *   The app root.
     * @param string $type
     *   The extension type.
     * @param \Drupal\Core\Cache\CacheBackendInterface $cache
     *   The cache.
     * @param \Drupal\Core\Extension\InfoParserInterface $info_parser
     *   The info parser.
     * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
     *   The module handler.
     * @param \Drupal\Core\State\StateInterface $state
     *   The state service.
     * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
     *   The config factory.
     * @param \Drupal\Core\Extension\ThemeEngineExtensionList $engine_list
     *   The theme engine extension listing.
     * @param string $install_profile
     *   The install profile used by the site.
     */
    public function __construct($root, $type, CacheBackendInterface $cache, InfoParserInterface $info_parser, ModuleHandlerInterface $module_handler, StateInterface $state, ConfigFactoryInterface $config_factory, ThemeEngineExtensionList $engine_list, $install_profile) {
        parent::__construct($root, $type, $cache, $info_parser, $module_handler, $state, $install_profile);
        $this->configFactory = $config_factory;
        $this->engineList = $engine_list;
    }
    
    /**
     * {@inheritdoc}
     */
    protected function doList() {
        // Find themes.
        $themes = parent::doList();
        $engines = $this->engineList
            ->getList();
        // Always get the freshest list of themes (rather than the already cached
        // list in $this->installedThemes) when building the theme listing because a
        // theme could have just been installed or uninstalled.
        $this->installedThemes = $this->configFactory
            ->get('core.extension')
            ->get('theme') ?: [];
        $sub_themes = [];
        // Read info files for each theme.
        foreach ($themes as $name => $theme) {
            // Defaults to 'twig' (see self::defaults above).
            $engine = $theme->info['engine'];
            if (isset($engines[$engine])) {
                $theme->owner = $engines[$engine]->getExtensionPathname();
                $theme->prefix = $engines[$engine]->getName();
            }
            // Add this theme as a sub-theme if it has a base theme.
            if (!empty($theme->info['base theme'])) {
                $sub_themes[] = $name;
            }
            // Add status.
            $theme->status = (int) isset($this->installedThemes[$name]);
        }
        // Build dependencies.
        $themes = $this->moduleHandler
            ->buildModuleDependencies($themes);
        // After establishing the full list of available themes, fill in data for
        // sub-themes.
        $this->fillInSubThemeData($themes, $sub_themes);
        foreach ($themes as $key => $theme) {
            // After $theme is processed by buildModuleDependencies(), there can be a
            // `$theme->requires` array containing both module and base theme
            // dependencies. The module dependencies are copied to their own property
            // so they are available to operations specific to module dependencies.
            if (isset($theme->requires)) {
                $theme->module_dependencies = array_diff_key($theme->requires, $themes);
            }
            else {
                // Even if no requirements are specified, the theme installation process
                // expects the presence of the `requires` and `module_dependencies`
                // properties, so they should be initialized here as empty arrays.
                $theme->requires = [];
                $theme->module_dependencies = [];
            }
        }
        return $themes;
    }
    
    /**
     * Fills in data for themes that are also sub-themes.
     *
     * @param array $themes
     *   The array of partly processed theme information.
     * @param array $sub_themes
     *   A list of themes from the $theme array that are also sub-themes.
     */
    protected function fillInSubThemeData(array &$themes, array $sub_themes) {
        foreach ($sub_themes as $name) {
            $sub_theme = $themes[$name];
            // The $base_themes property is optional; only set for sub themes.
            // @see ThemeHandlerInterface::listInfo()
            $sub_theme->base_themes = $this->doGetBaseThemes($themes, $name);
            // empty() cannot be used here, since static::doGetBaseThemes() adds
            // the key of a base theme with a value of NULL in case it is not found,
            // in order to prevent needless iterations.
            if (!current($sub_theme->base_themes)) {
                continue;
            }
            // Determine the root base theme.
            $root_key = key($sub_theme->base_themes);
            // Build the list of sub-themes for each of the theme's base themes.
            foreach (array_keys($sub_theme->base_themes) as $base_theme) {
                $themes[$base_theme]->sub_themes[$name] = $sub_theme->info['name'];
            }
            // Add the theme engine info from the root base theme.
            if (isset($themes[$root_key]->owner)) {
                $sub_theme->info['engine'] = $themes[$root_key]->info['engine'];
                $sub_theme->owner = $themes[$root_key]->owner;
                $sub_theme->prefix = $themes[$root_key]->prefix;
            }
        }
    }
    
    /**
     * Finds all the base themes for the specified theme.
     *
     * Themes can inherit templates and function implementations from earlier
     * themes.
     *
     * @param \Drupal\Core\Extension\Extension[] $themes
     *   An array of available themes.
     * @param string $theme
     *   The name of the theme whose base we are looking for.
     *
     * @return array
     *   Returns an array of all of the theme's ancestors; the first element's
     *   value will be NULL if an error occurred.
     */
    public function getBaseThemes(array $themes, $theme) {
        return $this->doGetBaseThemes($themes, $theme);
    }
    
    /**
     * Finds the base themes for the specific theme.
     *
     * @param array $themes
     *   An array of available themes.
     * @param string $theme
     *   The name of the theme whose base we are looking for.
     * @param array $used_themes
     *   (optional) A recursion parameter preventing endless loops. Defaults to
     *   an empty array.
     *
     * @return array
     *   An array of base themes.
     */
    protected function doGetBaseThemes(array $themes, $theme, array $used_themes = []) {
        if (!isset($themes[$theme]->info['base theme'])) {
            return [];
        }
        $base_key = $themes[$theme]->info['base theme'];
        // Does the base theme exist?
        if (!isset($themes[$base_key])) {
            return [
                $base_key => NULL,
            ];
        }
        $current_base_theme = [
            $base_key => $themes[$base_key]->info['name'],
        ];
        // Is the base theme itself a child of another theme?
        if (isset($themes[$base_key]->info['base theme'])) {
            // Do we already know the base themes of this theme?
            if (isset($themes[$base_key]->base_themes)) {
                return $themes[$base_key]->base_themes + $current_base_theme;
            }
            // Prevent loops.
            if (!empty($used_themes[$base_key])) {
                return [
                    $base_key => NULL,
                ];
            }
            $used_themes[$base_key] = TRUE;
            return $this->doGetBaseThemes($themes, $base_key, $used_themes) + $current_base_theme;
        }
        // If we get here, then this is our parent theme.
        return $current_base_theme;
    }
    
    /**
     * {@inheritdoc}
     */
    protected function createExtensionInfo(Extension $extension) {
        $info = parent::createExtensionInfo($extension);
        // In the past, Drupal used to default to the `stable` theme as the base
        // theme. Explicitly opting out by specifying `base theme: false` was (and
        // still is) possible. However, defaulting to `base theme: stable` prevents
        // automatic updates to the next major version of Drupal, since each major
        // version may have a different version of "the stable theme", for example:
        // - for Drupal 8: `stable`
        // - for Drupal 9: `stable9`
        // - for Drupal 10: `stable10`
        // - et cetera
        // It is impossible to reliably determine which should be used by default,
        // hence we now require the base theme to be explicitly specified.
        if (!isset($info['base theme'])) {
            @trigger_error(sprintf('There is no `base theme` property specified in the %s.info.yml file. The optionality of the `base theme` property is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. All Drupal 8 themes must add `base theme: stable` to their *.info.yml file for them to continue to work as-is in future versions of Drupal. Drupal 9 requires the `base theme` property to be specified. See https://www.drupal.org/node/3066038', $extension->getName()), E_USER_DEPRECATED);
            $info['base theme'] = 'stable';
        }
        // Remove the default Stable base theme when 'base theme: false' is set in
        // a theme .info.yml file.
        if ($info['base theme'] === FALSE) {
            unset($info['base theme']);
        }
        if (!empty($info['base theme'])) {
            // Add the base theme as a proper dependency.
            $info['dependencies'][] = $info['base theme'];
        }
        // Prefix screenshot with theme path.
        if (!empty($info['screenshot'])) {
            $info['screenshot'] = $extension->getPath() . '/' . $info['screenshot'];
        }
        return $info;
    }
    
    /**
     * {@inheritdoc}
     */
    protected function getInstalledExtensionNames() {
        // Cache the installed themes to avoid multiple calls to the config system.
        if (!isset($this->installedThemes)) {
            $this->installedThemes = $this->configFactory
                ->get('core.extension')
                ->get('theme') ?: [];
        }
        return array_keys($this->installedThemes);
    }
    
    /**
     * {@inheritdoc}
     */
    public function reset() {
        parent::reset();
        $this->installedThemes = NULL;
        return $this;
    }

}

Members

Title Sort descending Modifiers Object type Summary Overriden Title Overrides
ExtensionList::$addedPathNames protected property A list of extension folder names directly added in code (not discovered).
ExtensionList::$cache protected property The cache.
ExtensionList::$extensionInfo protected property Static caching for extension info.
ExtensionList::$extensions protected property The cached extensions.
ExtensionList::$infoParser protected property The info parser.
ExtensionList::$installProfile protected property The install profile used by the site.
ExtensionList::$moduleHandler protected property The module handler.
ExtensionList::$pathNames protected property A list of extension folder names keyed by extension name.
ExtensionList::$root protected property The app root.
ExtensionList::$state protected property The state store.
ExtensionList::$type protected property The type of the extension: "module", "theme" or "profile".
ExtensionList::checkIncompatibility public function Tests the compatibility of an extension.
ExtensionList::doScanExtensions protected function Scans the available extensions. 1
ExtensionList::exists public function Determines if an extension exists in the filesystem.
ExtensionList::get public function Returns a single extension.
ExtensionList::getAllAvailableInfo public function Returns an array of info files information of available extensions.
ExtensionList::getAllInstalledInfo public function Returns an array of info files information of installed extensions.
ExtensionList::getExtensionDiscovery protected function Returns the extension discovery. 3
ExtensionList::getExtensionInfo public function Returns information about a specified extension.
ExtensionList::getInfoCacheId protected function Returns the extension info cache ID.
ExtensionList::getList public function Returns all available extensions.
ExtensionList::getListCacheId protected function Returns the extension list cache ID.
ExtensionList::getName public function Returns the human-readable name of the extension.
ExtensionList::getPath public function Gets the path to an extension of a specific type (module, theme, etc.).
ExtensionList::getPathname public function Gets the info file path for an extension.
ExtensionList::getPathnames public function Returns a list of extension file paths keyed by machine name.
ExtensionList::getPathnamesCacheId protected function Returns the extension filenames cache ID.
ExtensionList::recalculateInfo protected function Generates the information from .info.yml files for extensions of this type.
ExtensionList::recalculatePathnames protected function Generates a sorted list of .info.yml file locations for all extensions.
ExtensionList::setPathname public function Sets the pathname for an extension.
ThemeExtensionList::$configFactory protected property The config factory.
ThemeExtensionList::$defaults protected property Default values to be merged into *.info.yml file arrays. Overrides ExtensionList::$defaults
ThemeExtensionList::$engineList protected property The theme engine list needed by this theme list.
ThemeExtensionList::$installedThemes protected property The list of installed themes.
ThemeExtensionList::createExtensionInfo protected function Creates the info value for an extension object. Overrides ExtensionList::createExtensionInfo
ThemeExtensionList::doGetBaseThemes protected function Finds the base themes for the specific theme.
ThemeExtensionList::doList protected function Builds the list of extensions. Overrides ExtensionList::doList
ThemeExtensionList::fillInSubThemeData protected function Fills in data for themes that are also sub-themes.
ThemeExtensionList::getBaseThemes public function Finds all the base themes for the specified theme.
ThemeExtensionList::getInstalledExtensionNames protected function Returns a list of machine names of installed extensions. Overrides ExtensionList::getInstalledExtensionNames
ThemeExtensionList::reset public function Resets the stored extension list. Overrides ExtensionList::reset
ThemeExtensionList::__construct public function Constructs a new ThemeExtensionList instance. Overrides ExtensionList::__construct

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