Current action plan

Objective

  1. Work on some installer related issues revealed that HEAD contains plenty of insane call chains in code relating to discovering and listing extensions.
  2. Some of these call chains are causing an infinite recursion when calling the wrong function at the wrong point in time, because calls are hopping through legacy procedural global state and OO code and back.
  3. On top, ~40% of DrupalKernel consists of very complex code and undocumented logic that only exists in order to cope with the fragile legacy extension discovery/listing code.
  4. We need to fix this before release.

Goal

  • Replace the entire legacy code with dedicated Extension component classes.

Preface: Drupal's Extension Architecture (current)

  1. There are 4 extension types: profile, module, theme, theme_engine.
  2. Every extension type has a standard file extension; e.g.: bartik.info.yml
  3. Extensions are selectively discovered by extension type; e.g.: 'theme'
    1. Extensions of a certain type are expected to be located in a type-specific subdirectory; e.g.: ./themes
    2. Extensions are supplied by (1) Drupal core (2) An installation profile (3) The developer/site builder/end-user.
    3. To discover extensions, the installation profile and its directory must be known; e.g.: /core/profiles/standard
    4. Extensions are searched in multiple filepaths: (1) /core/$type (2) /$profile/$type (3) /$type (4) /sites/$site/$type
    5. If the same extension is found in multiple locations, then the later location overrides the former — unless the later one is not compatible (whereas "compatible" is limited to a Drupal core major version compatibility check as of now).
    6. The list of available/discoverable extensions is not expected to change within a single request at regular runtime.
    7. Extensions are normally not discovered while serving regular page requests.
  4. A list of enabled/installed extensions is stored in configuration instead.
    1. The current extension list and their filesystem locations are compiled into the service container.
    2. If the configured list of extensions is changed, the service container needs to be rebuilt.
  5. Each extension is required to provide an $extension.info.yml file, which contains meta information to describe the extension (package).
    1. The meta info of an extension/package is normally not used while serving regular page requests.
    2. Info Exception #1: Themes require their full meta information in order to operate. Therefore, the full theme meta info is persistently cached and retrieved from cache on every request.
    3. Info Exception #2: Various administrative user interfaces need to output the human-readable label of a module and thus need to retrieve meta info.
    4. Info Exception #3: Various subsystems/services need to know the installed version of an extension.
      (Applies to contributed extensions only. Core has a fixed version constant defined in code. Custom extensions do not have a version, unless a git commit hash can be determined from .git.)

Inventory/Stock-Check

DrupalKernel::getModuleFileNames()DrupalKernel::moduleData()

Manually (1) scans for all installation profiles to (2) retrieve the path of installed profile, so as to (3) discover all applicable modules.

The resulting module/filepath map is compiled into the %container.modules% parameter.
The %container.modules% data is used to register module namespaces.

Automatically invoked whenever the service container is rebuilt.
The service container is rebuilt either by Drupal via DrupalKernel::updateModules() or manually via rebuild.php.

drupal_get_filename()

Legacy. A static variable that maps an extension to a filesystem path.

Formerly primed by system_list() with module and theme data retrieved from persistent cache.
system_list() is only used for theme data today.
Neither DrupalKernel nor ModuleHandler are priming the paths for modules in HEAD, which causes any module/profile filename retrieval to be a cache miss.

On cache miss,

  1. for type module, the %container.modules% parameter is retrieved via ModuleHandler::getModuleList(), if that service available.
  2. a system.$type.files state record is looked up, if the state service is available, and even though that record is only written by system_rebuild_$type_data() (and not by SystemListingInfo).
  3. otherwise, a full filesystem scan is performed for the requested extension type via SystemListingInfo. The results are statically cached, so as to only scan once per request.
drupal_get_path()
Legacy. return dirname(drupal_get_filename($type, $name));
system_rebuild_module_data()
Legacy.
  1. Discovers all available modules (and installation profile(s))
  2. parses + enhances meta info
  3. allows already installed modules to alter meta info
  4. manipulates status + weight of enabled modules
  5. stores the result in a system.module.files state record.

The generated system.module.files state is consumed by drupal_get_filename().

system_rebuild_theme_data()
Legacy. Calls ThemeHandler::rebuildThemeData() to
  1. Discover all available themes and theme engines
  2. parse + enhance meta info
  3. allow modules to alter meta info
  4. apply basic parent/sub theme info inheritance logic.

Then proceeds and

  1. manipulates status + weight of enabled themes
  2. stores the theme data/info result in a system.theme.data state record
  3. stores the filesystem paths in a system.theme.files state record.

The generated system.theme.data state is consumed by system_list().
The generated system.theme.files state is consumed by drupal_get_filename().

Except for the parent/sub theme info inheritance logic, the basic meta info operations performed for each extension are identical to system_rebuild_module_data().

The parent/sub theme info inheritance logic is incomplete, both system_list() and later on ThemeHandler::listInfo() are performing additional inheritance logic.

install_profile_info()
Legacy. Parses meta info for a particular installation profile, applies defaults, and ensures required base system module dependencies.
system_get_info()
Legacy.
  1. For type module, force-invokes module discovery via system_rebuild_module_data() (once per request) and returns the meta info, if the module is enabled.
  2. For type theme, retrieves the cached data from system_list(), if the theme is enabled.
ProjectInfo
Utility class to perform some specific processing and validations for additional information that is automatically added to extension meta .info.yml files by drupal.org packaging scripts.
system_list()
Legacy. Cache item wrapper. Primes drupal_get_filename() with theme and theme_engine filepaths. On cache miss:
  1. Reads list of enabled themes from system.theme:enabled
  2. Consumes system.theme.data state record or calls system_rebuild_theme_data() to rebuild it.
  3. Calls ThemeHandler::getBaseThemes() to determine the base themes of each enabled theme based on meta info.
  4. Sets the theme engine for each theme.
list_themes()
Legacy. → ThemeHandler::listInfo()
ThemeHandler::listInfo()
Retrieves list of enabled themes and copies meta info properties into theme file object properties.

Calls system_list(), or manually system_rebuild_theme_data() upon failure/exception.

system_list_reset()
Legacy/obsolete.
  1. Resets static caches of system_list(), system_rebuild_module_data(), list_themes().
  2. Clears persistent cache of system_list() and system_get_module_info()/ModuleInfo.
  3. Deletes system.theme.data state record.

In short: All static and persistent extension meta info caches are cleared.

YamlDiscovery
Independent utility class to discover and parse YAML files in a given set of directories. Primarily used by Plugin Managers, like this:
$discovery = new YamlDiscovery('routing', $this->moduleHandler->getModuleDirectories());
foreach ($discovery->findAll() as $provider => $info) {
}

Since the extension directories are injected as a dependency, this info file discovery + parsing tool cannot be used to discover the extensions themselves.

Data sources

$settings['install_profile']
The name of the installation profile Drupal was installed with.
(Only the name, not its filesystem location.)
simpletest.settings:parent_profile
Only exists when a (web) test is executed by Simpletest: The name of the installation profile of the parent site (test runner).
(Only the name, not its filesystem location.)

Enables tests using a particular installation profile to also find extensions in the installation profile of the parent site. Too complex to summarize in a few words; to learn more, see #911354: Tests in profiles/[name]/modules cannot be run and cannot use a different profile for running tests

Used by DrupalKernel::moduleData() and SystemListingInfo.

system.module:enabled
The configured list of enabled/installed modules, sorted by weight.
system.theme:enabled
The configured list of enabled/installed themes.
(Theme engines are not included.)

Application environment conditions

Exactly two:

Regular runtime
$settings['install_profile'] must exist, extension discovery and resulting listing depends on it.
(Early) Installer
There is no profile yet, extension discovery only lists (1) core extensions and (2) installation profiles.

Previously, there was a 3rd condition of update.php, which was not able to assume that the installation profile of a previous major version of Drupal core still exists, but that case is obsolete and gone with Migrate in core.

Proposed solution

Clean Inventory

  1. Extension\Discovery
  2. Extension\List
  3. Extension\Info
Drupal\Core\Extension\ExtensionDiscovery

Pretty much replaces SystemListingInfo as-is, but tailors its architecture, implementation, and API to its primary use-case.

Since the assumption that available/discoverable extensions are not supposed to change within a single request is sound/reasonable, this could be a static utility class or singleton.

Regardless of implementation, the results should be statically cached, so as to prevent multiple filesystem scans triggered by different services that would yield the exact same result within a single request.

The extension discovery process will return SplFileInfo objects instead of the custom anonymous objects that just happen to have $uri, $filename, and $name properties. If time permits, we will wrap (extend) SplFileInfo in an Extension class.

To profile/benchmark: A single + highly optimized filesystem scan for *.info.yml

  1. could be a lot faster than separate scans per type, especially given that we almost always scan for all extension types in case extension discovery is triggered anyway
  2. could leverage the new required type: [type] property in .info.yml files (even more so, if we'd require it to be defined on the first file line)
  3. would allow us to get rid of hook_system_theme_info(), which allows modules to ship with themes and only exists for simpletest.
Drupal\Core\Extension\ExtensionList / ExtensionHandler

Replaces legacy system_list(), drupal_get_path(), drupal_get_filename().

Central service to lookup filesystem location of a particular extension.

Uses core extension configuration to determine enabled extensions.
Uses the (1) service container [for modules/profiles] and/or (2) state records to retrieve filesystem paths.

-drupal_get_path('module', $name);
+$extensionList->getPath('module', $name);
+$extensionList->getModulePath($name);

Allows the /admin/modules page and rebuild.php to explicitly clear state information and rebuild it via ExtensionDiscovery, but otherwise, the existing state records are always used for lookups, and any kind of automatic rebuilding is strictly prohibited.

Partially represented in the form of ModuleHandler and ThemeHandler already, whereas the list facility of ModuleHandler is actually just the %container.modules% service container parameter at regular runtime.

ThemeHandler has no direct equivalent to ModuleHandler::getModuleList(); i.e., just the map of extension names to filesystem locations.

There is neither a ThemeEngineHandler nor a ProfileHandler yet. We either need to add those, or especially because the extension list logic is the same for all, that asks for a base ExtensionList/ExtensionHandler.

Also:

// Check all enabled + supported core extensions for whether they have Fu.
$skills = array();
foreach ($extensionList->getTypes() as $type) {
  $skills[$type] = $extensionList->discover($type, 'fu.info.yml'); // might be YamlDiscovery
}
return $skills;
Drupal\Core\Extension\ExtensionInfo

Replaces legacy system_rebuild_module_data(), system_rebuild_theme_data(), system_get_info(), install_profile_info(), system_get_module_info(), ModuleInfo, system_list(), list_themes(), ThemeHandler::listInfo(), etc.pp.

Central service to retrieve, process, and prepare meta info of all core extension types, and also store them in state (probably à la CacheCollector, but no, that is state information.).

State invalidation follows the ExtensionList/ExtensionHandler concept; i.e., only when explicitly requested. — That said, invalidation will probably have to be synchronized with ExtensionList/ExtensionHandler, so I'm not sure whether this facility should not be an integral part of ExtensionList/ExtensionHandler instead of a separate class/service.

Because our time to release is very limited, and because we have exactly 4 extension types in core, and because all of this is strictly bound and tailored towards the extension types we have in core, the concrete proposal is to simply hard-code the necessary post-processing for each extension type into this class/service — i.e., no subclasses or domain objects for each extension type.
(An extensible extension system is D9 material.)

Data sources

  1. -$settings['install_profile'] = 'standard';
    +$settings['install_profile']['standard'] = 'core/profiles/standard';
    

    The filesystem location of the installation profile is required in order to efficiently discover + rebuild extension lists.

    This setting is not supposed to be touched by site builders/developers either way, so the exact data type/value in settings.php should not matter.

    Additionally, this opens the door to add support for #1356276: Allow profiles to define a base/parent profile, potentially even post-8.0.0.

  2. -$conf['simpletest.settings']['parent_profile'] = 'openatrium';
    +$settings['simpletest_parent_install_profile']['openatrium'] = 'profiles/openatrium';
    

    ...or completely obsolete with (1), because with that, reality is just this:

    $settings['install_profile']['openatrium'] = 'profiles/openatrium';
    $settings['install_profile']['standard'] = 'core/profiles/standard';
    
  3. -system.module:enabled
    -system.theme:enabled
    +core.extension:modules
    +core.extension:themes
    

    /core/config/core.extension.yml (← "core.extension" == Drupal\Core\Extension)

    Introducing core configuration. (restricted to critical base system components)

    Resolving a major circular dependency problem in the installer, tests, and elsewhere: System module cannot be installed through regular means, because System module itself supplies the system.module configuration file.

    Likewise, #2184387: Remove SchemaStorage discovered that System module defines some basic configuration schema data types that have to be available at all times. But yet, for concistency, DX, discovery, and code re-use purposes, it would be preferable to keep defining them in a YAML file like all other config schema files.

    /core/config/schema/core.data_types.yml

    The idea is to turn "core" into extension and extension type itself.

    So as to allow it to be a regular data provider like any other extension type. The only exception is that there is only one core, so $extensionList->getPath('core', 'whatever') will always return "core" (the extension name is ignored).

    Starting from core services (core.services.yml), to core libraries (core.libraries.yml), to base config schema, and base system extension (default) configuration, core already is a data provider like any other extension:

    /core/config/schema
    /core/config
    /core/lib
    /core/tests
    /core/core.libraries.yml
    /core/core.services.yml
    

Blockers

The primary purpose of this meta issue is to (1) raise awareness of the (non-obvious) problem space, (2) discuss the concrete proposal presented here, and (3) come to an agreement ASAP.

Given that other issues required me to make myself familiar with all of this code/insanity, I'd be happy to volunteer on my own to code up these gems, but before doing so, I'd like to achieve at least some basic level of agreement. (The devil is in the details anyway.)

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

sun’s picture

Title: [meta] D8 Extension Discovery/Listing » [meta] D8 Extension System: Discovery/Listing/Info
Issue tags: +Needs architectural review
dawehner’s picture

Impressive research, seriously!

ExtensionDiscovery: Certainly we should decouple the implementation (maybe yml discovery) from the outside interface. As far as I understand you the ExtensionHandler would be pretty much the primary user of this?

So as to allow it to be a regular data provider like any other extension type. The only exception is that there is only one core, so $extensionList->getPath('core', 'whatever') will always return "core" (the extension name is ignored).

+1 as we have done this for plugins already.

Because our time to release is very limited, and because we have exactly 4 extension types in core, and because all of this is strictly bound and tailored towards the extension types we have in core, the concrete proposal is to simply hard-code the necessary post-processing for each extension type into this class/service — i.e., no subclasses or domain objects for each extension type.

Subclassing should be pretty simple and no API break so we can do that later if times allows it. Does that mean that we have one ExtensionInfo per type registered or just one for all of them?

sun’s picture

Issue summary: View changes
  1. #2025779: Remove ModuleInfo as it is no longer necessary is done, so I've removed the corresponding ModuleInfo and system_get_module_info() items from the inventory.
  2. After diving into #2185559: Extension System, Part I: SystemListing clean-up + performance; remove SystemListingInfo + drupal_system_listing()
    I substantially advanced on that in #2188661: Extension System, Part II: ExtensionDiscovery

    Part II might make Part I obsolete, depends on reviews/feedback. The new ExtensionDiscovery pretty much implements all aspects of the corresponding proposed solution in the meta issue summary here.

sun’s picture

Oh wow. I just realized that we can instantiate the Extension class I'm introducing in #2188661: Extension System, Part II: ExtensionDiscovery at regular runtime:

class ModuleHandler {
  public function __construct($module_list) {
    foreach ($module_list as $name => $pathname) {
      // Extension needs type, info pathname, and main extension file.
      // Derive that.
      $this->moduleList[$name] = new Extension('module', dirname($pathname) . "/$name.info.yml", basename($pathname));
    }
  }
}

Consequently:

class ModuleHandler {
  /**
   * Replaces drupal_get_path().
   */
  public function getPath($name) {
    // Technically just the $uri contained in the originally injected $module_list,
    // but yeah, for extra kicks:
    return $this->moduleList[$name]->getPath();
  }
}

I did have some use-cases for retrieving the path of a uninstalled module in my Drupal career, but I think we can find & offer a separate solution for that... At regular runtime, modules should only require the paths of installed modules.

If located on a generic/base ExtensionHandler class, then the signature would become getPath($type, $name) of course → resembling the current drupal_get_path($type, $name).

The more I'm studying the current mess, the more confident I am that all extension lists should be compiled into Container parameters; i.e., so that we'd end up with:

%container.profiles%
%container.modules%
%container.theme_engines%
%container.themes%

And of course, once we arrive there, we could as well simply say:

%container.extensions%

→ keyed by extension type, injected into ExtensionHandler

andypost’s picture

jibran’s picture

According to #1067408-125: Themes do not have an installation status this should be critical.

cosmicdreams’s picture

Are there plans to open issues for parts 3 and 4?

Xano’s picture

I disagree with assuming that extension data does not change within requests. I have often had to change it in web tests, and there have been one or two cases in the past five years where I have had to work around this on a live site.

I agree that the current two extension handlers have a lot of shared features, or should have those. The attached patch briefly describes my original intention to remove drupal_get_filename(), before I decided to look for an existing issue and found this one. I think that the code in #4 makes a lot of sense and resembles my approach in that extension-specific handlers should take care of all the logic (preferably inheriting as much as they can from base classes and/or traits), although I have yet to wrap my head around all the other plans about a generic extension handler which is a completely opposite approach.

dawehner’s picture

@xano
Given that this is a meta issue we should better move such congrete bits into separate issues.

xjm’s picture

Issue tags: +beta target

@jibran, it has been left as major (and a non-beta blocker) because it a soft blocker for #1067408: Themes do not have an installation status rather than a hard blocker. That said, I'd love to know how much more it would take to use this very welcome architecture overhaul for the theme installation issue. @sun, thoughts?

sun’s picture

Created #2208429: Extension System, Part III: ExtensionList, ModuleExtensionList and ProfileExtensionList

Though that is very closely related to #1067408: Themes do not have an installation status, because the theme system still is the biggest offender with regard to wildly intermixing the concepts of discovery/listing/info, so I also restarted work on that.

sun’s picture

Issue summary: View changes

Created #2228921: Consolidate system.module + system.theme + system.theme.disabled into core.extension config


Also removing obsolete parts from the issue summary:

  1. drupal_system_listing() is gone. YAY!
  2. SystemListing is gone. YAY!
  3. SystemListingInfo is gone. YAY!
pwolanin’s picture

mgifford’s picture

Assigned: sun » Unassigned

Just unassigning issues that haven't been developed for a bit in the D8 queue.

benjy’s picture

Is this something we could do in 8.1? Or is it D9...

dawehner’s picture

Well if we have a plan 8.1 is totally doable IMHO.

#2208429: Extension System, Part III: ExtensionList, ModuleExtensionList and ProfileExtensionList has an idea, but it doesn't have an idea how to get rid of all the various level of caching we currently have, which would be nice though.

Mile23’s picture

Version: 8.0.x-dev » 8.1.x-dev
Category: Task » Plan
Issue summary: View changes
Issue tags: -beta target
Related issues: +#2208429: Extension System, Part III: ExtensionList, ModuleExtensionList and ProfileExtensionList

Reading through to update the IS, just sooooo much to change. :-)

Added a link to #2208429: Extension System, Part III: ExtensionList, ModuleExtensionList and ProfileExtensionList because it was easy.

I've been trying to poke at improving ExtensionDiscovery, in order to be able to write better tests for test discovery code. #2605654: Modify Drupal\Core\Extension\ExtensionDiscovery to allow for scanning multiple file systems, enabling vfsStream testing

From my perspective:

As far as plans: Extension discovery is needed by the kernel, installers, updaters, test discovery, test runners, and probably something else I can't think of.

Some specs:

  • A way to scan the filesystem for extensions, without a lot of dependencies (such as cache backends or databases).
  • A way to scan while those dependencies are present.
  • A way to scan where we can persist the data we want to persist (so we don't re-scan if we don't need to).

In that regard we need to:

  • Decouple ExtensionDiscovery from its caching, and turn caching into an optional decoration.
  • Turn cached ExtensionDiscovery into a service for D8, with all the caching benefits in one place.
  • Use an adaptor service which uses ExtensionDiscovery and the database to give you a list of enabled/non-enabled extensions.
  • Deprecate system_rebuild_module_data() and similar procedural code.

Version: 8.1.x-dev » 8.2.x-dev

Drupal 8.1.0-beta1 was released on March 2, 2016, which means new developments and disruptive changes should now be targeted against the 8.2.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

donquixote’s picture

Some observations / possible flaws of the reworked architecture.

class Extension

Currently, a number of the properties we write onto the extension objects are undeclared. E.g. $extension->info is an undeclared property.

We are treating the Extension object as mutable, instead of creating it once and then leaving it unchanged.

class ExtensionDiscovery

This currently returns an array of Extension objects, which later are enhanced with ->info array, and run through hook_system_info_alter().
The modifications / altering leaks back into ExtensionDiscovery::$files, where these objects are referenced.

Besides, I do not agree that something has to be static just so we avoid calculating it again.

The ExtensionDiscovery class has a number of specifities hardcoded, which should rather live in separate classes.

A solution can be to let ExtensionDiscovery, or sth equivalent, return paths instead of objects.
And a layer on top which returns raw (unprocessed) info arrays directly parsed from the *.info.yml.
The discovery layer would not care about extension types, but simply return all *.info.yml files it finds within one search directory. The search directory would be passed as a parameter.

Discovery and sorting

I think discovery order currently still depends, to a part, on some randomness in the filesystem. If two module "packages" contain "sub"modules with the same name, the precedence is undefined.
Maybe we could instead first collect all paths and then sort alphabetically (within one search directory).
The precedencs of search directories (e.g. ./core/modules vs ./modules) would still be the same. Also, the precedence of subfolders vs parent folders will be preserved, if we do the alphabetic sorting correctly.

Container vs Discovery

Currently we have paths for enabled extensions baked into the container. These are from a discovery process which happened in the past.
The ExtensionDiscovery and the planned ExtensionList, on the other hand, may have a fresher list of extensions, from a scan in the current request.

This can lead to a situation where the container's extension list points to a different path than the result of a fresh scan.
This could lead to weird results, if we mix the two.

Profiles are special

The discovery of other extension types depends on the active profile's name and directory. And then the active profile becomes a module. Duh.
The attempt in #2208429: Extension System, Part III: ExtensionList, ModuleExtensionList and ProfileExtensionList to let profiles and modules share a lot of code is problematic. see e.g. #2208429-150.

Overall architecture

I think we can slice it up into more layers than there currently are.
Possible intermediate pieces of data:

  • *.info.yml file paths for given search directory.
    (Parameter: Search directory, e.g. 'sites/default/modules')
  • raw info array for given file path
    (parsed from *.info.yml file)
    (Parameter: Path to a *.info.yml file)
  • List of raw info arrays by file path, for given search directory.
  • Available profile paths by name
  • etc... (see attached pdf)

Each of these pieces of data would be provided by a separate service, with optional decorators for buffering / caching.
I am attaching a pdf which shows the list of (intermediate) data pieces and their dependencies.
The same thing exists as a google docs spreadsheet, you can ask for access on IRC. I just want to attach the pdf as a stable snapshot.

The trick is that a good number of the services providing these data pieces can be agnostic of the current Drupal installation, and should be quite simple to test individually.

By "buffer" I mean a lazily intialized variable. By "cache" I mean a lazily initialized persistent storage.
Obviously these two columns of the spreadsheet are unfinished..
EDIT:
By "by file path" I mean an array keyed by file path.
By "for given search directory" I mean there is a method which takes a search directory as parameter and returns data specific to this search directory.

EDIT:
The pdf also shows how a lot of this depends on the profile.

EDIT: I almost expect this pdf to be confusing. IRC !

EDIT: Yes, there should be interfaces for the various services. Some of them can be reused, but generally there will be a lot of them.

donquixote’s picture

Transition plan

We could possibly build a lot of the stuff proposed in the previous comment in a vacuum and in multiple steps, leaving existing code in place. Only when all the pieces are finished and properly tested, would we swap out the old components.

dawehner’s picture

One small step I think we should try out after #2208429: Extension System, Part III: ExtensionList, ModuleExtensionList and ProfileExtensionList landed is to have a Module and Theme object, that either extends Extension or decorates it, which contains the runtime
information about the info array. The extension object was not designed to hold the information, as its not available from within ModuleHandler::getModule.

In general layers in software design don't have to be public, so IMHO internal methods could be enough. It can still at least make the intention clear. One important part here is IMHO that we don't have to optimize for swappability. These layers introduce implicit assumptions for other tools (drush, CLI scripts for deployment etc.) so the level of swappability doesn't make this easier potentially.

By "buffer" I mean a lazily intialized variable. By "cache" I mean a lazily initialized persistent storage.
Obviously these two columns of the spreadsheet are unfinished..

Note: Ideally we would just use a cache chain with a static cache.

donquixote’s picture

In general layers in software design don't have to be public, so IMHO internal methods could be enough. It can still at least make the intention clear. One important part here is IMHO that we don't have to optimize for swappability. These layers introduce implicit assumptions for other tools (drush, CLI scripts for deployment etc.) so the level of swappability doesn't make this easier potentially.

I disagree with the idea that "public" as in public methods on interfaces, is only desirable if it should be part of of the "public API", or if we want swappability. This idea is the reason why classes and class hierarchies in Drupal are generally too big for my taste. (and has been a reason for me not to spend too much time with core)

Maybe others have opinions about this..

donquixote’s picture

Another observation:

Extensions of a certain type are expected to be located in a type-specific subdirectory; e.g.: ./themes

This used to be the case once, but it is not anymore.
In the current implementation of ExtensionDiscovery, extensions of any type can be located in any subdirectory they want.
E.g. a profile can be located in ./modules, a module can be located in ./themes.

There are exceptions to this:

  • If an active profile is active, then modules, themes and engines from other profiles are discarded via ExtensionDiscovery::filterByProfileDirectories().
  • It could be that things are different in the installer. I did not check.

I think we simply have to accept this for the sake of BC.

donquixote’s picture

The unexpected behavior in #23 should be covered by tests!
I think currently it is not - or is it?

donquixote’s picture

More observations:

ExtensionDiscovery finds extensions in the root folder itself. E.g. DRUPAL_ROOT . '/xyz_module.info.yml'

ExtensionDiscovery->scan('modules') has a side effect on the discovery object, thanks to setProfileDirectoriesFromSettings().

$extension_discovery->scan('profiles'); // -> finds all profiles.
$extension_discovery->scan('modules');
$extension_discovery->scan('profiles'); // -> finds only the active profile.

I think these behaviors are not intended, and should be considered bugs.

(Fixing this should happen in separate issues, but I think summarizing existing behavior does belong here.)

almaudoh’s picture

Added #2721603: ExtensionList cleanup and separation of concerns for architectural improvements in ExtensionList, et al.

Version: 8.2.x-dev » 8.3.x-dev

Drupal 8.2.0-beta1 was released on August 3, 2016, which means new developments and disruptive changes should now be targeted against the 8.3.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.3.x-dev » 8.4.x-dev

Drupal 8.3.0-alpha1 will be released the week of January 30, 2017, which means new developments and disruptive changes should now be targeted against the 8.4.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.4.x-dev » 8.5.x-dev

Drupal 8.4.0-alpha1 will be released the week of July 31, 2017, which means new developments and disruptive changes should now be targeted against the 8.5.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.5.x-dev » 8.6.x-dev

Drupal 8.5.0-alpha1 will be released the week of January 17, 2018, which means new developments and disruptive changes should now be targeted against the 8.6.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

jibran’s picture

dsnopek’s picture

I don't think so. There's still #2659940: Extension System, Part III: ThemeExtensionList and ThemeEngineExtensionList in order to close out Part III of this META, but then there's still Part IV which doesn't have any child issues created for it yet. Although, I suppose when Part IV is all that remains, this could be closed out and just the (still to be created) Part IV issues could be used to track this?

dawehner’s picture

The current extension list does IMHO already what ExtensionInfo was meant to be. I agree we should leave it open, at least for the theme issue, but also for potential issues for cleanup of the installer.

markhalliwell’s picture

Where exactly are we with this? The IS seems a bit outdated. Does "Extension System, Part IV: ExtensionInfo" still actually need to be done?

Version: 8.6.x-dev » 8.7.x-dev

Drupal 8.6.0-alpha1 will be released the week of July 16, 2018, which means new developments and disruptive changes should now be targeted against the 8.7.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

voleger’s picture

dsnopek’s picture

#2941757: Extension System, Part IV: Properly register all installed extensions/namespaces during container generation split out of #2659940, I think, so that seems still pertinent.

There's also some other issues that have this issue marked as its parent, even though they weren't strictly part of @sun's original plan. Those probably aren't real children of this issue and just related.

@sun might need to chime in on what exactly he had in mind for Part IV, and if it's done enough or being addressed in other ways?

andypost’s picture

markhalliwell’s picture

Issue summary: View changes
markhalliwell’s picture

Issue summary: View changes
markhalliwell’s picture

Issue summary: View changes

Version: 8.7.x-dev » 8.8.x-dev

Drupal 8.7.0-alpha1 will be released the week of March 11, 2019, which means new developments and disruptive changes should now be targeted against the 8.8.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

jhodgdon’s picture

It looks like the Discovery process has already been marked as done on this issue. But I have run into two issues.

I have added them as Related but didn't add them to the issue summary yet...

Version: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.0-alpha1 will be released the week of October 14th, 2019, which means new developments and disruptive changes should now be targeted against the 8.9.x-dev branch. (Any changes to 8.9.x will also be committed to 9.0.x in preparation for Drupal 9’s release, but some changes like significant feature additions will be deferred to 9.1.x.). For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 8.9.x-dev » 9.1.x-dev

Drupal 8.9.0-beta1 was released on March 20, 2020. 8.9.x is the final, long-term support (LTS) minor release of Drupal 8, which means new developments and disruptive changes should now be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 9.1.x-dev » 9.2.x-dev

Drupal 9.1.0-alpha1 will be released the week of October 19, 2020, which means new developments and disruptive changes should now be targeted for the 9.2.x-dev branch. For more information see the Drupal 9 minor version schedule and the Allowed changes during the Drupal 9 release cycle.

Version: 9.2.x-dev » 9.3.x-dev

Drupal 9.2.0-alpha1 will be released the week of May 3, 2021, which means new developments and disruptive changes should now be targeted for the 9.3.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.0-rc1 was released on November 26, 2021, which means new developments and disruptive changes should now be targeted for the 9.4.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.0-alpha1 was released on May 6, 2022, which means new developments and disruptive changes should now be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.5.x-dev » 10.1.x-dev

Drupal 9.5.0-beta2 and Drupal 10.0.0-beta2 were released on September 29, 2022, which means new developments and disruptive changes should now be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 10.1.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch, which currently accepts only minor-version allowed changes. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

andypost’s picture