"Fossies" - the Fresh Open Source Software Archive

Member "drupal-8.9.10/core/lib/Drupal/Core/Extension/ThemeExtensionList.php" (26 Nov 2020, 11411 Bytes) of package /linux/www/drupal-8.9.10.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) PHP source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "ThemeExtensionList.php" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 9.0.8_vs_9.1.0-rc1.

    1 <?php
    2 
    3 namespace Drupal\Core\Extension;
    4 
    5 use Drupal\Core\Cache\CacheBackendInterface;
    6 use Drupal\Core\Config\ConfigFactoryInterface;
    7 use Drupal\Core\State\StateInterface;
    8 
    9 /**
   10  * Provides a list of available themes.
   11  *
   12  * @internal
   13  *   This class is not yet stable and therefore there are no guarantees that the
   14  *   internal implementations including constructor signature and protected
   15  *   properties / methods will not change over time. This will be reviewed after
   16  *   https://www.drupal.org/project/drupal/issues/2940481
   17  */
   18 class ThemeExtensionList extends ExtensionList {
   19 
   20   /**
   21    * {@inheritdoc}
   22    */
   23   protected $defaults = [
   24     'engine' => 'twig',
   25     'regions' => [
   26       'sidebar_first' => 'Left sidebar',
   27       'sidebar_second' => 'Right sidebar',
   28       'content' => 'Content',
   29       'header' => 'Header',
   30       'primary_menu' => 'Primary menu',
   31       'secondary_menu' => 'Secondary menu',
   32       'footer' => 'Footer',
   33       'highlighted' => 'Highlighted',
   34       'help' => 'Help',
   35       'page_top' => 'Page top',
   36       'page_bottom' => 'Page bottom',
   37       'breadcrumb' => 'Breadcrumb',
   38     ],
   39     'description' => '',
   40     // The following array should be kept inline with
   41     // _system_default_theme_features().
   42     'features' => [
   43       'favicon',
   44       'logo',
   45       'node_user_picture',
   46       'comment_user_picture',
   47       'comment_user_verification',
   48     ],
   49     'screenshot' => 'screenshot.png',
   50     'version' => NULL,
   51     'php' => DRUPAL_MINIMUM_PHP,
   52     'libraries' => [],
   53     'libraries_extend' => [],
   54     'libraries_override' => [],
   55     'dependencies' => [],
   56   ];
   57 
   58   /**
   59    * The config factory.
   60    *
   61    * @var \Drupal\Core\Config\ConfigFactoryInterface
   62    */
   63   protected $configFactory;
   64 
   65   /**
   66    * The theme engine list needed by this theme list.
   67    *
   68    * @var \Drupal\Core\Extension\ThemeEngineExtensionList
   69    */
   70   protected $engineList;
   71 
   72   /**
   73    * The list of installed themes.
   74    *
   75    * @var string[]
   76    */
   77   protected $installedThemes;
   78 
   79   /**
   80    * Constructs a new ThemeExtensionList instance.
   81    *
   82    * @param string $root
   83    *   The app root.
   84    * @param string $type
   85    *   The extension type.
   86    * @param \Drupal\Core\Cache\CacheBackendInterface $cache
   87    *   The cache.
   88    * @param \Drupal\Core\Extension\InfoParserInterface $info_parser
   89    *   The info parser.
   90    * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
   91    *   The module handler.
   92    * @param \Drupal\Core\State\StateInterface $state
   93    *   The state service.
   94    * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
   95    *   The config factory.
   96    * @param \Drupal\Core\Extension\ThemeEngineExtensionList $engine_list
   97    *   The theme engine extension listing.
   98    * @param string $install_profile
   99    *   The install profile used by the site.
  100    */
  101   public function __construct($root, $type, CacheBackendInterface $cache, InfoParserInterface $info_parser, ModuleHandlerInterface $module_handler, StateInterface $state, ConfigFactoryInterface $config_factory, ThemeEngineExtensionList $engine_list, $install_profile) {
  102     parent::__construct($root, $type, $cache, $info_parser, $module_handler, $state, $install_profile);
  103 
  104     $this->configFactory = $config_factory;
  105     $this->engineList = $engine_list;
  106   }
  107 
  108   /**
  109    * {@inheritdoc}
  110    */
  111   protected function doList() {
  112     // Find themes.
  113     $themes = parent::doList();
  114 
  115     $engines = $this->engineList->getList();
  116     // Always get the freshest list of themes (rather than the already cached
  117     // list in $this->installedThemes) when building the theme listing because a
  118     // theme could have just been installed or uninstalled.
  119     $this->installedThemes = $this->configFactory->get('core.extension')->get('theme') ?: [];
  120 
  121     $sub_themes = [];
  122     // Read info files for each theme.
  123     foreach ($themes as $name => $theme) {
  124       // Defaults to 'twig' (see self::defaults above).
  125       $engine = $theme->info['engine'];
  126       if (isset($engines[$engine])) {
  127         $theme->owner = $engines[$engine]->getExtensionPathname();
  128         $theme->prefix = $engines[$engine]->getName();
  129       }
  130       // Add this theme as a sub-theme if it has a base theme.
  131       if (!empty($theme->info['base theme'])) {
  132         $sub_themes[] = $name;
  133       }
  134       // Add status.
  135       $theme->status = (int) isset($this->installedThemes[$name]);
  136     }
  137 
  138     // Build dependencies.
  139     $themes = $this->moduleHandler->buildModuleDependencies($themes);
  140 
  141     // After establishing the full list of available themes, fill in data for
  142     // sub-themes.
  143     $this->fillInSubThemeData($themes, $sub_themes);
  144 
  145     foreach ($themes as $key => $theme) {
  146       // After $theme is processed by buildModuleDependencies(), there can be a
  147       // `$theme->requires` array containing both module and base theme
  148       // dependencies. The module dependencies are copied to their own property
  149       // so they are available to operations specific to module dependencies.
  150       if (isset($theme->requires)) {
  151         $theme->module_dependencies = array_diff_key($theme->requires, $themes);
  152       }
  153       else {
  154         // Even if no requirements are specified, the theme installation process
  155         // expects the presence of the `requires` and `module_dependencies`
  156         // properties, so they should be initialized here as empty arrays.
  157         $theme->requires = [];
  158         $theme->module_dependencies = [];
  159       }
  160     }
  161     return $themes;
  162   }
  163 
  164   /**
  165    * Fills in data for themes that are also sub-themes.
  166    *
  167    * @param array $themes
  168    *   The array of partly processed theme information.
  169    * @param array $sub_themes
  170    *   A list of themes from the $theme array that are also sub-themes.
  171    */
  172   protected function fillInSubThemeData(array &$themes, array $sub_themes) {
  173     foreach ($sub_themes as $name) {
  174       $sub_theme = $themes[$name];
  175       // The $base_themes property is optional; only set for sub themes.
  176       // @see ThemeHandlerInterface::listInfo()
  177       $sub_theme->base_themes = $this->doGetBaseThemes($themes, $name);
  178       // empty() cannot be used here, since static::doGetBaseThemes() adds
  179       // the key of a base theme with a value of NULL in case it is not found,
  180       // in order to prevent needless iterations.
  181       if (!current($sub_theme->base_themes)) {
  182         continue;
  183       }
  184       // Determine the root base theme.
  185       $root_key = key($sub_theme->base_themes);
  186       // Build the list of sub-themes for each of the theme's base themes.
  187       foreach (array_keys($sub_theme->base_themes) as $base_theme) {
  188         $themes[$base_theme]->sub_themes[$name] = $sub_theme->info['name'];
  189       }
  190       // Add the theme engine info from the root base theme.
  191       if (isset($themes[$root_key]->owner)) {
  192         $sub_theme->info['engine'] = $themes[$root_key]->info['engine'];
  193         $sub_theme->owner = $themes[$root_key]->owner;
  194         $sub_theme->prefix = $themes[$root_key]->prefix;
  195       }
  196     }
  197   }
  198 
  199   /**
  200    * Finds all the base themes for the specified theme.
  201    *
  202    * Themes can inherit templates and function implementations from earlier
  203    * themes.
  204    *
  205    * @param \Drupal\Core\Extension\Extension[] $themes
  206    *   An array of available themes.
  207    * @param string $theme
  208    *   The name of the theme whose base we are looking for.
  209    *
  210    * @return array
  211    *   Returns an array of all of the theme's ancestors; the first element's
  212    *   value will be NULL if an error occurred.
  213    */
  214   public function getBaseThemes(array $themes, $theme) {
  215     return $this->doGetBaseThemes($themes, $theme);
  216   }
  217 
  218   /**
  219    * Finds the base themes for the specific theme.
  220    *
  221    * @param array $themes
  222    *   An array of available themes.
  223    * @param string $theme
  224    *   The name of the theme whose base we are looking for.
  225    * @param array $used_themes
  226    *   (optional) A recursion parameter preventing endless loops. Defaults to
  227    *   an empty array.
  228    *
  229    * @return array
  230    *   An array of base themes.
  231    */
  232   protected function doGetBaseThemes(array $themes, $theme, array $used_themes = []) {
  233     if (!isset($themes[$theme]->info['base theme'])) {
  234       return [];
  235     }
  236 
  237     $base_key = $themes[$theme]->info['base theme'];
  238     // Does the base theme exist?
  239     if (!isset($themes[$base_key])) {
  240       return [$base_key => NULL];
  241     }
  242 
  243     $current_base_theme = [$base_key => $themes[$base_key]->info['name']];
  244 
  245     // Is the base theme itself a child of another theme?
  246     if (isset($themes[$base_key]->info['base theme'])) {
  247       // Do we already know the base themes of this theme?
  248       if (isset($themes[$base_key]->base_themes)) {
  249         return $themes[$base_key]->base_themes + $current_base_theme;
  250       }
  251       // Prevent loops.
  252       if (!empty($used_themes[$base_key])) {
  253         return [$base_key => NULL];
  254       }
  255       $used_themes[$base_key] = TRUE;
  256       return $this->doGetBaseThemes($themes, $base_key, $used_themes) + $current_base_theme;
  257     }
  258     // If we get here, then this is our parent theme.
  259     return $current_base_theme;
  260   }
  261 
  262   /**
  263    * {@inheritdoc}
  264    */
  265   protected function createExtensionInfo(Extension $extension) {
  266     $info = parent::createExtensionInfo($extension);
  267 
  268     // In the past, Drupal used to default to the `stable` theme as the base
  269     // theme. Explicitly opting out by specifying `base theme: false` was (and
  270     // still is) possible. However, defaulting to `base theme: stable` prevents
  271     // automatic updates to the next major version of Drupal, since each major
  272     // version may have a different version of "the stable theme", for example:
  273     // - for Drupal 8: `stable`
  274     // - for Drupal 9: `stable9`
  275     // - for Drupal 10: `stable10`
  276     // - et cetera
  277     // It is impossible to reliably determine which should be used by default,
  278     // hence we now require the base theme to be explicitly specified.
  279     if (!isset($info['base theme'])) {
  280       @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);
  281       $info['base theme'] = 'stable';
  282     }
  283 
  284     // Remove the default Stable base theme when 'base theme: false' is set in
  285     // a theme .info.yml file.
  286     if ($info['base theme'] === FALSE) {
  287       unset($info['base theme']);
  288     }
  289 
  290     if (!empty($info['base theme'])) {
  291       // Add the base theme as a proper dependency.
  292       $info['dependencies'][] = $info['base theme'];
  293     }
  294 
  295     // Prefix screenshot with theme path.
  296     if (!empty($info['screenshot'])) {
  297       $info['screenshot'] = $extension->getPath() . '/' . $info['screenshot'];
  298     }
  299     return $info;
  300   }
  301 
  302   /**
  303    * {@inheritdoc}
  304    */
  305   protected function getInstalledExtensionNames() {
  306     // Cache the installed themes to avoid multiple calls to the config system.
  307     if (!isset($this->installedThemes)) {
  308       $this->installedThemes = $this->configFactory->get('core.extension')->get('theme') ?: [];
  309     }
  310     return array_keys($this->installedThemes);
  311   }
  312 
  313   /**
  314    * {@inheritdoc}
  315    */
  316   public function reset() {
  317     parent::reset();
  318     $this->installedThemes = NULL;
  319     return $this;
  320   }
  321 
  322 }