Skip to content

Commit aa12cc6

Browse files
author
catch
committed
Issue #3544715 by nicxvan, berdir, godotislate, jurgenhaas, catch, alexpott: Add oop support to hooks currently supported by themes
1 parent 4fa0288 commit aa12cc6

File tree

44 files changed

+1466
-426
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+1466
-426
lines changed

core.services.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -728,7 +728,7 @@ services:
728728
Drupal\Core\Extension\ThemeHandlerInterface: '@theme_handler'
729729
theme_installer:
730730
class: Drupal\Core\Extension\ThemeInstaller
731-
arguments: ['@theme_handler', '@config.factory', '@config.installer', '@module_handler', '@config.manager', '@asset.css.collection_optimizer', '@router.builder', '@logger.channel.default', '@state', '@extension.list.module', '@theme.registry', '@extension.list.theme', '@plugin.manager.sdc']
731+
autowire: true
732732
Drupal\Core\Extension\ThemeInstallerInterface: '@theme_installer'
733733
entity.memory_cache:
734734
class: Drupal\Core\Cache\MemoryCache\LruMemoryCache
@@ -1590,7 +1590,7 @@ services:
15901590
Drupal\Core\Datetime\DateFormatterInterface: '@date.formatter'
15911591
theme.manager:
15921592
class: Drupal\Core\Theme\ThemeManager
1593-
arguments: ['%app.root%', '@theme.negotiator', '@theme.initialization', '@module_handler', '@callable_resolver']
1593+
arguments: ['%app.root%', '@theme.negotiator', '@theme.initialization', '@module_handler', '@callable_resolver', '@keyvalue', '@cache.bootstrap']
15941594
calls:
15951595
- [setThemeRegistry, ['@theme.registry']]
15961596
Drupal\Core\Theme\ThemeManagerInterface: '@theme.manager'

lib/Drupal/Core/CoreServiceProvider.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
use Drupal\Core\DependencyInjection\ServiceModifierInterface;
2929
use Drupal\Core\DependencyInjection\ServiceProviderInterface;
3030
use Drupal\Core\Extension\ModuleUninstallValidatorInterface;
31+
use Drupal\Core\Hook\ThemeHookCollectorPass;
3132
use Drupal\Core\Plugin\PluginManagerPass;
3233
use Drupal\Core\PreWarm\PreWarmableInterface;
3334
use Drupal\Core\Queue\QueueFactoryInterface;
@@ -64,6 +65,7 @@ public function register(ContainerBuilder $container) {
6465
}
6566

6667
$container->addCompilerPass(new HookCollectorPass());
68+
$container->addCompilerPass(new ThemeHookCollectorPass());
6769
$container->addCompilerPass(new HookCollectorKeyValueWritePass(), PassConfig::TYPE_OPTIMIZE);
6870
// Add the compiler pass that lets service providers modify existing
6971
// service definitions. This pass must come before all passes operating on

lib/Drupal/Core/DrupalKernel.php

Lines changed: 232 additions & 49 deletions
Large diffs are not rendered by default.

lib/Drupal/Core/DrupalKernelInterface.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,17 @@ public function getAppRoot();
115115
*/
116116
public function updateModules(array $module_list, array $module_filenames = []);
117117

118+
/**
119+
* Updates the kernel's list of themes to the new list.
120+
*
121+
* The kernel needs to update its list and container to match the new
122+
* list.
123+
*
124+
* array<string, \Drupal\Core\Extension\Extension> $register_themes
125+
* List of theme extensions, keyed by theme name.
126+
*/
127+
public function updateThemes(array $register_themes = []): void;
128+
118129
/**
119130
* Force a container rebuild.
120131
*

lib/Drupal/Core/Extension/ThemeHandler.php

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -89,11 +89,6 @@ public function listInfo() {
8989
* {@inheritdoc}
9090
*/
9191
public function addTheme(Extension $theme) {
92-
// Register the namespaces of installed themes.
93-
// @todo Implement proper theme registration
94-
// https://www.drupal.org/project/drupal/issues/2941757
95-
\Drupal::service('class_loader')->addPsr4('Drupal\\' . $theme->getName() . '\\', $this->root . '/' . $theme->getPath() . '/src');
96-
9792
if (!empty($theme->info['libraries'])) {
9893
foreach ($theme->info['libraries'] as $library => $name) {
9994
$theme->libraries[$library] = $name;

lib/Drupal/Core/Extension/ThemeInstaller.php

Lines changed: 56 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,14 @@
88
use Drupal\Core\Config\ConfigFactoryInterface;
99
use Drupal\Core\Config\ConfigInstallerInterface;
1010
use Drupal\Core\Config\ConfigManagerInterface;
11+
use Drupal\Core\DrupalKernelInterface;
1112
use Drupal\Core\Extension\Exception\UnknownExtensionException;
1213
use Drupal\Core\Routing\RouteBuilderInterface;
1314
use Drupal\Core\State\StateInterface;
1415
use Drupal\Core\StringTranslation\StringTranslationTrait;
1516
use Drupal\Core\Theme\Registry;
1617
use Psr\Log\LoggerInterface;
18+
use Symfony\Component\DependencyInjection\Attribute\Autowire;
1719

1820
/**
1921
* Manages theme installation/uninstallation.
@@ -24,23 +26,26 @@ class ThemeInstaller implements ThemeInstallerInterface {
2426
use StringTranslationTrait;
2527

2628
public function __construct(
27-
protected readonly ThemeHandlerInterface $themeHandler,
28-
protected readonly ConfigFactoryInterface $configFactory,
29-
protected readonly ConfigInstallerInterface $configInstaller,
30-
protected readonly ModuleHandlerInterface $moduleHandler,
31-
protected readonly ConfigManagerInterface $configManager,
32-
protected readonly AssetCollectionOptimizerInterface $cssCollectionOptimizer,
33-
protected readonly RouteBuilderInterface $routeBuilder,
34-
protected readonly LoggerInterface $logger,
35-
protected readonly StateInterface $state,
36-
protected readonly ModuleExtensionList $moduleExtensionList,
37-
protected readonly Registry $themeRegistry,
38-
protected readonly ThemeExtensionList $themeExtensionList,
39-
protected ?CachedDiscoveryInterface $componentPluginManager = NULL,
29+
protected ThemeHandlerInterface $themeHandler,
30+
protected ConfigFactoryInterface $configFactory,
31+
protected ConfigInstallerInterface $configInstaller,
32+
protected ModuleHandlerInterface $moduleHandler,
33+
protected ConfigManagerInterface $configManager,
34+
#[Autowire(service: 'asset.css.collection_optimizer')]
35+
protected AssetCollectionOptimizerInterface $cssCollectionOptimizer,
36+
protected RouteBuilderInterface $routeBuilder,
37+
#[Autowire(service: 'logger.channel.default')]
38+
protected LoggerInterface $logger,
39+
protected StateInterface $state,
40+
protected ModuleExtensionList $moduleExtensionList,
41+
protected Registry $themeRegistry,
42+
protected ThemeExtensionList $themeExtensionList,
43+
#[Autowire(service: 'kernel')]
44+
protected DrupalKernelInterface|CachedDiscoveryInterface|null $kernel = NULL,
4045
) {
41-
if ($this->componentPluginManager === NULL) {
42-
@trigger_error('Calling ' . __METHOD__ . ' without the $componentPluginManager argument is deprecated in drupal:11.2.0 and it will be required in drupal:12.0.0. See https://www.drupal.org/node/3525649', E_USER_DEPRECATED);
43-
$this->componentPluginManager = \Drupal::service('plugin.manager.sdc');
46+
if (!$this->kernel instanceof DrupalKernelInterface) {
47+
@trigger_error('Calling ' . __METHOD__ . ' without the $kernel argument is deprecated in drupal:11.3.0 and it will be required in drupal:12.0.0. See https://www.drupal.org/node/3551652', E_USER_DEPRECATED);
48+
$this->kernel = \Drupal::service('kernel');
4449
}
4550
}
4651

@@ -170,7 +175,11 @@ public function install(array $theme_list, $install_dependencies = TRUE) {
170175
}
171176

172177
$this->cssCollectionOptimizer->deleteAll();
173-
$this->resetSystem();
178+
// Add new themes to the list of installed themes.
179+
$register_themes = array_merge(array_keys($installed_themes), $themes_installed);
180+
// Get list of extensions for the new list of themes.
181+
$register_themes = array_intersect_key($theme_data, array_flip($register_themes));
182+
$this->resetSystem($register_themes);
174183

175184
// Invoke hook_themes_installed() after the themes have been installed.
176185
$this->moduleHandler->invokeAll('themes_installed', [$themes_installed]);
@@ -185,6 +194,8 @@ public function uninstall(array $theme_list) {
185194
$extension_config = $this->configFactory->getEditable('core.extension');
186195
$theme_config = $this->configFactory->getEditable('system.theme');
187196
$list = $this->themeHandler->listInfo();
197+
$installed_themes = $extension_config->get('theme') ?: [];
198+
$theme_data = $this->themeExtensionList->reset()->getList();
188199
foreach ($theme_list as $key) {
189200
if ($extension_config->get("theme.$key") === NULL) {
190201
throw new UnknownExtensionException("Unknown theme: $key.");
@@ -219,19 +230,43 @@ public function uninstall(array $theme_list) {
219230
$extension_config->save(TRUE);
220231

221232
// Refresh theme info.
222-
$this->resetSystem();
223233
$this->themeHandler->reset();
234+
// Remove themes that were uninstalled from the list.
235+
$register_themes = array_diff(array_keys($installed_themes), $theme_list);
236+
// Get list of extensions for the new list of themes.
237+
$register_themes = array_intersect_key($theme_data, array_flip($register_themes));
238+
$this->resetSystem($register_themes);
224239

225240
$this->moduleHandler->invokeAll('themes_uninstalled', [$theme_list]);
226241
}
227242

228243
/**
229244
* Resets some other systems like rebuilding the route information or caches.
245+
*
246+
* @param array<string, \Drupal\Core\Extension\Extension> $register_themes
247+
* Extension data for themes that should be registered, keyed by name.
230248
*/
231-
protected function resetSystem() {
232-
$this->routeBuilder->setRebuildNeeded();
249+
protected function resetSystem(array $register_themes) {
233250
$this->themeRegistry->reset();
234-
$this->componentPluginManager->clearCachedDefinitions();
251+
$this->kernel->updateThemes($register_themes);
252+
$container = $this->kernel->getContainer();
253+
$this->themeHandler = $container->get('theme_handler');
254+
$this->configFactory = $container->get('config.factory');
255+
$this->configInstaller = $container->get('config.installer');
256+
$this->moduleHandler = $container->get('module_handler');
257+
$this->configManager = $container->get('config.manager');
258+
$this->cssCollectionOptimizer = $container->get('asset.css.collection_optimizer');
259+
$this->routeBuilder = $container->get('router.builder');
260+
$this->logger = $container->get('logger.channel.default');
261+
$this->state = $container->get('state');
262+
$this->moduleExtensionList = $container->get('extension.list.module');
263+
$this->themeRegistry = $container->get('theme.registry');
264+
$this->themeExtensionList = $container->get('extension.list.theme');
265+
266+
$container->get('stream_wrapper_manager')->register();
267+
// Clear all plugin caches.
268+
$container->get('plugin.cache_clearer')->clearCachedDefinitions();
269+
$this->routeBuilder->setRebuildNeeded();
235270
}
236271

237272
}

lib/Drupal/Core/Hook/HookCollectorKeyValueWritePass.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,15 @@ class HookCollectorKeyValueWritePass implements CompilerPassInterface {
2525
*/
2626
public function process(ContainerBuilder $container): void {
2727
$hookData = $container->getParameter('.hook_data');
28+
if ($container->hasParameter('.theme_hook_data')) {
29+
$themeHookData = $container->getParameter('.theme_hook_data');
30+
$hookData = array_merge($hookData, $themeHookData);
31+
$hookData['preprocess_for_suggestions'] = array_merge($hookData['preprocess_for_suggestions'], $hookData['theme_preprocess_for_suggestions']);
32+
}
2833
$keyvalue = $container->get('keyvalue')->get('hook_data');
2934
assert($keyvalue instanceof KeyValueStoreInterface);
3035
$keyvalue->setMultiple($hookData);
31-
$container->get('cache.bootstrap')->delete('hook_data');
36+
$container->get('cache.bootstrap')->deleteMultiple(['hook_data', 'theme_hook_data']);
3237
}
3338

3439
}

0 commit comments

Comments
 (0)