diff --git a/core/lib/Drupal/Core/Extension/ExtensionDiscovery.php b/core/lib/Drupal/Core/Extension/ExtensionDiscovery.php index ebd3553c36a612fa89cc105d58ce17febca4e591..f15512a558c3163dfbd5f0fb9229f53aa2010b7e 100644 --- a/core/lib/Drupal/Core/Extension/ExtensionDiscovery.php +++ b/core/lib/Drupal/Core/Extension/ExtensionDiscovery.php @@ -81,6 +81,11 @@ class ExtensionDiscovery { * The file cache object. * * @var \Drupal\Component\FileCache\FileCacheInterface + * + * @deprecated in drupal:11.2.0 and is removed from drupal:12.0.0. There is no + * direct replacement. + * + * @see https://www.drupal.org/node/3490431 */ protected $fileCache; @@ -91,23 +96,36 @@ class ExtensionDiscovery { */ protected $sitePath; + /** + * The info parser. + * + * Reads .info.yml files efficiently. + * + * @var \Drupal\Core\Extension\InfoParser|null + */ + protected ?InfoParser $infoParser; + /** * Constructs a new ExtensionDiscovery object. * * @param string $root * The app root. - * @param bool $use_file_cache - * Whether file cache should be used. + * @param bool $use_info_parser + * Whether the info_parser should be used. Note this argument also + * determines if the deprecated file cache property is set to maintain BC in + * Drupal 11. * @param string[] $profile_directories * The available profile directories * @param string $site_path * The path to the site. */ - public function __construct(string $root, $use_file_cache = TRUE, ?array $profile_directories = NULL, ?string $site_path = NULL) { + public function __construct(string $root, $use_info_parser = TRUE, ?array $profile_directories = NULL, ?string $site_path = NULL) { $this->root = $root; - $this->fileCache = $use_file_cache ? FileCacheFactory::get('extension_discovery') : NULL; + // @phpstan-ignore property.deprecated + $this->fileCache = $use_info_parser ? FileCacheFactory::get('extension_discovery') : NULL; $this->profileDirectories = $profile_directories; $this->sitePath = $site_path; + $this->infoParser = $use_info_parser ? new InfoParser($root) : NULL; } /** @@ -453,12 +471,9 @@ protected function scanDirectory($dir, $include_tests) { continue; } - $extension_arguments = $this->fileCache ? $this->fileCache->get($fileinfo->getPathName()) : FALSE; - // Ensure $extension_arguments is an array. Previously, the Extension - // object was cached and now needs to be replaced with the array. - if (empty($extension_arguments) || !is_array($extension_arguments)) { - // Determine extension type from info file. - $type = FALSE; + // Determine the extension type from the info file. + $type = FALSE; + if ($this->infoParser === NULL) { $file = $fileinfo->openFile('r'); while (!$type && !$file->eof()) { preg_match('@^type:\s*(\'|")?(\w+)\1?\s*(?:\#.*)?$@', $file->fgets(), $matches); @@ -466,43 +481,37 @@ protected function scanDirectory($dir, $include_tests) { $type = $matches[2]; } } - if (empty($type)) { - continue; - } - $name = $fileinfo->getBasename('.info.yml'); - $pathname = $dir_prefix . $fileinfo->getSubPathname(); + } + else { + $type = $this->infoParser->parse($fileinfo->getPathname())['type'] ?? FALSE; + } + if (empty($type)) { + continue; + } - // Determine whether the extension has a main extension file. - // For theme engines, the file extension is .engine. - if ($type == 'theme_engine') { - $filename = $name . '.engine'; - } - // For profiles/modules/themes, it is the extension type. - else { - $filename = $name . '.' . $type; - } - if (!file_exists($this->root . '/' . dirname($pathname) . '/' . $filename)) { - $filename = NULL; - } - $extension_arguments = [ - 'type' => $type, - 'pathname' => $pathname, - 'filename' => $filename, - 'subpath' => $fileinfo->getSubPath(), - ]; - - if ($this->fileCache) { - $this->fileCache->set($fileinfo->getPathName(), $extension_arguments); - } + $name = $fileinfo->getBasename('.info.yml'); + $pathname = $dir_prefix . $fileinfo->getSubPathname(); + + // Determine whether the extension has a main extension file. + // For theme engines, the file extension is .engine. + if ($type == 'theme_engine') { + $filename = $name . '.engine'; + } + // For profiles/modules/themes, it is the extension type. + else { + $filename = $name . '.' . $type; + } + if (!file_exists($this->root . '/' . dirname($pathname) . '/' . $filename)) { + $filename = NULL; } - $extension = new Extension($this->root, $extension_arguments['type'], $extension_arguments['pathname'], $extension_arguments['filename']); + $extension = new Extension($this->root, $type, $pathname, $filename); // Track the originating directory for sorting purposes. - $extension->subpath = $extension_arguments['subpath']; + $extension->subpath = $fileinfo->getSubPath(); $extension->origin = $dir; - $files[$extension_arguments['type']][$key] = $extension; + $files[$type][$key] = $extension; } return $files; } diff --git a/core/modules/system/tests/src/Functional/Form/ModulesListFormWebTest.php b/core/modules/system/tests/src/Functional/Form/ModulesListFormWebTest.php index 1541a1f8bcb9332f14810d5068af4fc47de30247..96ebc050d530b852e566373ca8b86d867143702d 100644 --- a/core/modules/system/tests/src/Functional/Form/ModulesListFormWebTest.php +++ b/core/modules/system/tests/src/Functional/Form/ModulesListFormWebTest.php @@ -95,7 +95,7 @@ public function testModulesListFormStatusMessage(): void { * Tests the module form with a module with an invalid info.yml file. */ public function testModulesListFormWithInvalidInfoFile(): void { - $path = \Drupal::getContainer()->getParameter('site.path') . "/modules/broken"; + $path = $this->root . '/' . \Drupal::getContainer()->getParameter('site.path') . "/modules/broken"; mkdir($path, 0777, TRUE); $file_path = "$path/broken.info.yml"; diff --git a/core/tests/Drupal/Tests/Core/Extension/ExtensionDiscoveryTest.php b/core/tests/Drupal/Tests/Core/Extension/ExtensionDiscoveryTest.php index 7526cd575fdee15a0e8a3e12d652fa06fd2a6439..bc064e085ba90ab5b4de09c91b95deeb71908cea 100644 --- a/core/tests/Drupal/Tests/Core/Extension/ExtensionDiscoveryTest.php +++ b/core/tests/Drupal/Tests/Core/Extension/ExtensionDiscoveryTest.php @@ -4,7 +4,6 @@ namespace Drupal\Tests\Core\Extension; -use Drupal\Component\FileCache\FileCacheFactory; use Drupal\Core\Extension\Extension; use Drupal\Core\Extension\ExtensionDiscovery; use Drupal\Tests\UnitTestCase; @@ -66,40 +65,6 @@ public function testExtensionDiscoveryVfs(): void { $this->assertEquals($extension_expected, $extensions_by_type['theme_engine']['twig'], 'twig'); } - /** - * Tests changing extension discovery file cache objects to arrays. - * - * @covers ::scan - * @runInSeparateProcess - */ - public function testExtensionDiscoveryCache(): void { - // Set up an extension object in the cache to mimic site prior to changing - // \Drupal\Core\Extension\ExtensionDiscovery::scanDirectory() to cache an - // array instead of an object. Note we cannot use the VFS file system - // because FileCache does not support stream wrappers. - $extension = new Extension($this->root, 'module', 'core/modules/user/user.info.yml', 'user.module'); - $extension->subpath = 'modules/user'; - $extension->origin = 'core'; - // Undo \Drupal\Tests\UnitTestCase::setUp() so FileCache works. - FileCacheFactory::setConfiguration([]); - $file_cache = FileCacheFactory::get('extension_discovery'); - $file_cache->set($this->root . '/core/modules/user/user.info.yml', $extension); - - // Create an ExtensionDiscovery object to test. - $extension_discovery = new ExtensionDiscovery($this->root, TRUE, [], 'sites/default'); - $modules = $extension_discovery->scan('module', FALSE); - $this->assertArrayHasKey('user', $modules); - $this->assertEquals((array) $extension, (array) $modules['user']); - $this->assertNotSame($extension, $modules['user']); - // FileCache item should now be an array. - $this->assertSame([ - 'type' => 'module', - 'pathname' => 'core/modules/user/user.info.yml', - 'filename' => 'user.module', - 'subpath' => 'modules/user', - ], $file_cache->get($this->root . '/core/modules/user/user.info.yml')); - } - /** * Tests finding modules that have a trailing comment on the type property. * diff --git a/core/tests/Drupal/Tests/Core/Update/UpdateRegistryTest.php b/core/tests/Drupal/Tests/Core/Update/UpdateRegistryTest.php index ee6e500b9a797826baf733cce77b7bc399f6b51c..277c7fcefd8a939cb095e78d101283ba60e5f93b 100644 --- a/core/tests/Drupal/Tests/Core/Update/UpdateRegistryTest.php +++ b/core/tests/Drupal/Tests/Core/Update/UpdateRegistryTest.php @@ -61,6 +61,7 @@ protected function setupBasicExtensions(): void { $info_d = <<<'EOS' type: theme name: Theme D +core_version_requirement: '*' EOS; $module_a = <<<'EOS' @@ -637,6 +638,7 @@ public function testGetPendingCustomUpdateFunctions(): void { $info_d = <<<'EOS' type: theme name: Theme D +core_version_requirement: '*' EOS; $module_a = <<<'EOS'