Parent issue

#2024083: [META] Improve the extension system (modules, profiles, themes)

Problem/Motivation

The DX of ModuleHandler or any other place that reads or works with module/theme/profile data is horrible. We need some kind of container through which we can easily access information about extensions, namely themes, modules and possibly profiles too.

Proposed resolution

Introduce classed objects for modules, themes and profiles to increase DX and maintainability of our code. We should be able to create a shared ExtensionInterface which would expect basic methods like ->getVersion(), ->getDescription(), etc.

I believe that this would make module/theme-related code much more readable and less error prone. Also, we would get autocompletion in our IDEs for reading the relevant .info.yml properties.

Detailed concept TBD.

CommentFileSizeAuthor
#82 2024043-82.patch5.44 KBJanvi Dasani
#77 2024043-77-interdiff.txt734 byteskim.pepper
#77 2024043-77.patch13 KBkim.pepper
#76 2024043-76.patch13 KBkim.pepper
#70 2024043-70-baseline.patch373 bytesandypost
#69 Screenshot 2022-10-13 at 9.35.55 PM.png88.46 KBpradhumanjain2311
#66 2024043-66-interdiff.txt6.75 KBkim.pepper
#66 2024043-66.patch10.99 KBkim.pepper
#54 2024043-54-interdiff.txt4.91 KBkim.pepper
#54 2024043-54.patch5.95 KBkim.pepper
#49 2024043-49.patch6.06 KBandypost
#49 interdiff.txt8.26 KBandypost
#37 theme-2024043-37.patch10.04 KBpcambra
#34 interdiff.txt1.56 KBdawehner
#34 theme-2024043.patch9.96 KBdawehner
#30 extension-2024043.patch9.95 KBdawehner
#30 interdiff.txt1.27 KBdawehner
#15 2024043-15.patch8.43 KBfubhy
#14 2024043-14.patch8.89 KBfubhy
#13 drupal-extensions-2024043-13.patch15.5 KBMile23
#6 drupal-extensions-2024043-6.patch9 KBMile23
#5 drupal-extensions-2024043-5.patch8.05 KBParisLiakos
#5 interdiff.txt877 bytesParisLiakos
#2 drupal-extensions-2024043-2.patch8.05 KBParisLiakos

Issue fork drupal-2024043

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

ParisLiakos’s picture

Issue tags: +Extension system

tagging

ParisLiakos’s picture

how about sth like that? i ll start adding some usage in core in a bit

ParisLiakos’s picture

Status: Active » Needs review

lets

Status: Needs review » Needs work

The last submitted patch, drupal-extensions-2024043-2.patch, failed testing.

ParisLiakos’s picture

Status: Needs work » Needs review
FileSize
877 bytes
8.05 KB

hmm, two fixes..

Mile23’s picture

This problem looks ready-made for Symfony's ParameterBag. Not sure if it's verboten in D8.

But here's a patch anyway.

I kept all the interfaces, but just made Module, Profile, and Theme into subclasses of ExtensionBase, itself a subclass of ParameterBag. This makes it (almost) trivially easy to manage the parameter arrays.

With tests.

fubhy’s picture

Title: Consider adding ModuleInterface, ThemeInterface, ExtensionInterface and corresponding classes » Add ModuleInterface, ThemeInterface, ExtensionInterface and corresponding classes

Re-titling. This should really happen.

dawehner’s picture

+use Symfony\Component\HttpFoundation\ParameterBag;

This feels wrong, just saying.

There is no profile interface, any reason for that?

Additional the constructors on the different Extensions needs docs.

The namespace in the test and the Contain statement disagree.

The use statements in the test is not needed.

fubhy’s picture

Yeah, I also don't think ParameterBag is the right thing to do here. It's an array collection, aye, but not the right one for the job. Maybe better extend from the Doctrine ArrayCollection.

Mile23’s picture

Title: Add ModuleInterface, ThemeInterface, ExtensionInterface and corresponding classes » Add ModuleInterface, ThemeInterface, ProfileInterface, ExtensionInterface and corresponding classes
Assigned: Unassigned » Mile23

ArrayCollection looks better, with iterators and map() and filter().

RE: ProfileInterface... I think I left it out because I wasn't sure how it was currently implemented, and how different it would be from a module. Plus it's not in the issue title.

If the whole point of this exercise is to make an abstract container class and special-case containers for modules, profiles, and themes so some manager class can deal with them, then yes. That's what we'll do, with ArrayCollections.

Also, just linking to what you've obviously seen: #2024083: [META] Improve the extension system (modules, profiles, themes)

fubhy’s picture

Also, just linking to what you've obviously seen: #2024083: [META] Improve the extension system (modules, profiles, themes)

Yeah, that's the META (parent) issue of this one ;) (also linked in the issue summary).

Mile23’s picture

doh. :-)

Mile23’s picture

Assigned: Mile23 » Unassigned
FileSize
15.5 KB

OK, here we go. Full hierarchy of interfaces, and built with ArrayCollection, and probably too many tests. :-)

There's one question though... Maybe arrays of dependencies should be other ExtensionInterface objects, or maybe not. I'm not quite sure how this is going to be generated or consumed, so for now it assumes that dependencies will be arrays of strings, as you'd get when you parse a YAML .info file.

fubhy’s picture

FileSize
8.89 KB

I removed the tests (really, we don't need any, these are just getters... it's an array collection, nothing else, no logic). And removed the whole checkType() / getType() thing (which removed all the logic, hence no tests) because, well, we have interfaces... So there is no point in checking types.

This is a super simple patch now that simply provides objects that we will start using in follow-up issues.

  1. First, we should get this in,
  2. then #2021959: Refactor module handling responsibilities out of DrupalKernel,
  3. then open an issue for using the newly created ModuleHandlerFactory to utilize Module objects and pass those into ModuleHandler (and refactoring ModuleHandler and all other places in core to use them as objects).
  4. then split up ModuleHandler as per #2004784: Move module install/uninstall implementations into ModuleInstaller
  5. then see how we can further simplify ModuleHandler to basically turn it into a pure ModuleCollection that just has some helper methods to execute stuff on it's collection of modules.
fubhy’s picture

FileSize
8.43 KB

Whoops, had an unrelated change in there.

msonnabaum’s picture

I really really like this issue. I wanted to do the exact same thing after working on #2021959: Refactor module handling responsibilities out of DrupalKernel.

This ModuleHandlerFactory issue isn't really dependent on this though, so committing it could possibly wait until we have usage, but it certainly wouldnt hurt to do this preemptively if it makes that patch easier.

dawehner’s picture

+++ b/core/lib/Drupal/Core/Extension/ExtensionBase.php
@@ -0,0 +1,47 @@
+use Doctrine\Common\Collections\ArrayCollection;
...
+abstract class ExtensionBase extends ArrayCollection implements ExtensionInterface {

This basically bounds our module system to doctrine ... are we sure we want to go into that direction?

fubhy’s picture

This basically bounds our module system to doctrine ... are we sure we want to go into that direction?

It is just a collection object. Swapping that out is not a big deal.

sdboyer’s picture

  1. +++ b/core/lib/Drupal/Core/Extension/ExtensionInterface.php
    @@ -0,0 +1,51 @@
    +   * extensions's .info.yml file and does not represent the entire dependency
    

    docs nit: "extension's", not "extensions's".

  2. +++ b/core/lib/Drupal/Core/Extension/ThemeInterface.php
    @@ -0,0 +1,79 @@
    +  public function getScreenshot();
    

    i would have guessed this gets the image data of the screenshot itself, but it seems it actually gets the path TO the screenshot. right?

This basically bounds our module system to doctrine ... are we sure we want to go into that direction?

i also think this is fine; we depend on libraries, that's why we bring them in. plus, ArrayCollection is really a pretty generic datastructure, so it's no big thing.

what is a little awkward here, though, is that these objects are straddling data that is read-only in a php context (information extracted from info yamls) and rw data (e.g., installation/active state). it's not that big of a deal, but it's worth noting.

Mile23’s picture

Good point. There will be a time when installation and active state info will be unknown.

fubhy’s picture

what is a little awkward here, though, is that these objects are straddling data that is read-only in a php context (information extracted from info yamls) and rw data (e.g., installation/active state). it's not that big of a deal, but it's worth noting.

The installation status is also read-only. The only way to change it will be to use ModuleInstaller which will cause a recompilation of the container and only subsequently change that data. We are looking at a pure, read-only object (which should or should not be enforced, I don't know...).

sdboyer’s picture

The installation status is also read-only. The only way to change it will be to use ModuleInstaller which will cause a recompilation of the container and only subsequently change that data. We are looking at a pure, read-only object (which should or should not be enforced, I don't know...).

yeah, that's a good point - the source may be different, but the data should be equally inviolate. OK so, the requirements really do dictate that the object should be read-only, which suggests state is set and frozen in the constructor. in that case, ArrayCollection is not necessarily the best thing to subclass from; we need the subset of methods that are read-only.

unfortunately, that raises the bar on what objects need to be present in order to correctly construct one of these. kinda a weird chicken or egg thing, since ModuleHandler is supposed to be the canonical point of record on what modules are enabled...right?

msonnabaum’s picture

ArrayCollection should just be removed for now. I like the idea of it, but it's clearly a distraction here.

Mile23’s picture

So: What should ExtensionInterface encapsulate?

fubhy’s picture

ArrayCollection should just be removed for now. I like the idea of it, but it's clearly a distraction here.

Yeah... You even predicted that ;)

The question now is... Do we need a raw get() method too? I know that some themes and modules come up with custom .info file contents. We need to support that.

Anyways, we should still implement ArrayAccess to ease adoption / conversion. I wonder if we should, in this patch, already convert _system_module_rebuild_data() to use these objects to check if that works?

fubhy’s picture

Note: I want to kill system_rebuild_module_data() entirely, but that's a follow-up to this one (@see #14)

msonnabaum’s picture

If we truly need to support contrib-added parameters to .info.yml, then yeah, a get() method seems appropriate. I'd just make sure we need it for each extension type before adding it. For example, if it's just a theme thing, let's only do it there.

msonnabaum’s picture

Issue summary: View changes

Updated issue summary.

xjm’s picture

Issue summary: View changes
xjm’s picture

Component: other » extension system
Issue tags: -Extension system
dawehner’s picture

FileSize
1.27 KB
9.95 KB

Just a rerole + one usecase. Should this patch also patch all places like the entity info one?

Status: Needs review » Needs work

The last submitted patch, 30: extension-2024043.patch, failed testing.

dawehner’s picture

Status: Needs work » Needs review

30: extension-2024043.patch queued for re-testing.

Status: Needs review » Needs work

The last submitted patch, 30: extension-2024043.patch, failed testing.

dawehner’s picture

Status: Needs work » Needs review
FileSize
9.96 KB
1.56 KB

Back to green.

alansaviolobo queued 34: theme-2024043.patch for re-testing.

Status: Needs review » Needs work

The last submitted patch, 34: theme-2024043.patch, failed testing.

pcambra’s picture

Status: Needs work » Needs review
FileSize
10.04 KB

Plain reroll

Status: Needs review » Needs work

The last submitted patch, 37: theme-2024043-37.patch, failed testing.

dawehner’s picture

At that point in time we kinda dislike interfaces for those kind of value objects. Ideally we would rather have just the value objects to indicate that those objects.

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

Drupal 8.0.6 was released on April 6 and is the final bugfix release for the Drupal 8.0.x series. Drupal 8.0.x will not receive any further development aside from security fixes. Drupal 8.1.0-rc1 is now available and sites should prepare to update to 8.1.0.

Bug reports should be targeted against the 8.1.x-dev branch from now on, and new development or disruptive changes should 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.

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

Drupal 8.1.9 was released on September 7 and is the final bugfix release for the Drupal 8.1.x series. Drupal 8.1.x will not receive any further development aside from security fixes. Drupal 8.2.0-rc1 is now available and sites should prepare to upgrade to 8.2.0.

Bug reports should be targeted against the 8.2.x-dev branch from now on, and new development or disruptive changes should 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.2.x-dev » 8.3.x-dev

Drupal 8.2.6 was released on February 1, 2017 and is the final full bugfix release for the Drupal 8.2.x series. Drupal 8.2.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.3.0 on April 5, 2017. (Drupal 8.3.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.3.x-dev branch from now on, and new development or disruptive changes should 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.3.x-dev » 8.4.x-dev

Drupal 8.3.6 was released on August 2, 2017 and is the final full bugfix release for the Drupal 8.3.x series. Drupal 8.3.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.4.0 on October 4, 2017. (Drupal 8.4.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.4.x-dev branch from now on, and new development or disruptive changes should 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.4.x-dev » 8.5.x-dev

Drupal 8.4.4 was released on January 3, 2018 and is the final full bugfix release for the Drupal 8.4.x series. Drupal 8.4.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.5.0 on March 7, 2018. (Drupal 8.5.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.5.x-dev branch from now on, and new development or disruptive changes should 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.

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

Drupal 8.5.6 was released on August 1, 2018 and is the final bugfix release for the Drupal 8.5.x series. Drupal 8.5.x will not receive any further development aside from security fixes. Sites should prepare to update to 8.6.0 on September 5, 2018. (Drupal 8.6.0-rc1 is available for testing.)

Bug reports should be targeted against the 8.6.x-dev branch from now on, and new development or disruptive changes should 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.

alexpott’s picture

#39 very much in favour of that. Value objects are exactly what we want here. We don't want alternate implementation of Modules in contrib or custom. The extension system is complicated enough as it is. #3015812: Introduce new Theme extension object and properly deprecate REGIONS_VISIBLE and REGIONS_ALL begins to implement this for themes.

andypost’s picture

Version: 8.6.x-dev » 8.7.x-dev
tim.plunkett’s picture

Title: Add ModuleInterface, ThemeInterface, ProfileInterface, ExtensionInterface and corresponding classes » Add Module, Theme, Profile, and Extension value objects

Agreed

andypost’s picture

Re-roll, removal of interfaces, making classes final.
Looks ExtensionBase no longer needed because there's Extension object which has mostly all methods.
Diggin extension discovery...

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.

larowlan’s picture

Status: Needs work » Needs review
larowlan’s picture

  1. +++ b/core/lib/Drupal/Core/Extension/ExtensionInterface.php
    @@ -0,0 +1,46 @@
    +interface ExtensionInterface {
    

    I thought comments above indicated we were dropping the interface?

  2. +++ b/core/lib/Drupal/Core/Extension/Module.php
    @@ -0,0 +1,22 @@
    +final class Module extends ExtensionBase {
    
    +++ b/core/lib/Drupal/Core/Extension/Profile.php
    @@ -0,0 +1,22 @@
    +final class Profile extends ExtensionBase {
    

    Module also has requires/required_by and sort values - see \Drupal\Core\Extension\ModuleHandler::buildModuleDependencies

larowlan’s picture

Perhaps we should pursue #2972143: Create \Drupal\Component\Utility\ArrayObject instead of using the doctrine array collection?

kim.pepper’s picture

Fixes for #52. Also some linting issues.

Re: #53 do we really want our own implementation for this? Seems super low-level.

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.

fgm’s picture

Status: Needs review » Needs work

Just spent some time working around theses questions without finding this issue initially.

As mentioned in #52, module Extension instances carry these undeclared properties:

  • info
  • required
  • required_by
  • sort

This issue avoids declaring them on Extension because theme and theme_engine extensions don't have them, but now that [#2937955] is in, the required[_by] fields can also exist on themes, and be empty on theme_engines, while profiles are modules in everything but name and load order.

AIUI the proposed patch changes the API, so it needs to be moved beyond 9.1, but could we just start by adding these four fields on the existing Extension class, as this doesn't break any API and adds a minimum of documentation and discoverability to these fields ?

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.

andypost’s picture

  1. +++ b/core/lib/Drupal/Core/Extension/ExtensionBase.php
    @@ -0,0 +1,58 @@
    + * Things which are extensions: Modules, themes, and profiles.
    

    now database drivers are extensions

  2. +++ b/core/lib/Drupal/Core/Extension/ExtensionBase.php
    @@ -0,0 +1,58 @@
    +    return $this->get('name');
    ...
    +    return $this->get('version');
    ...
    +    return $this->get('description');
    ...
    +    return $this->get('dependencies') ?: [];
    

    it will notice on PHP 8.2

andypost’s picture

Running test on PHP 8.2 brings lots of deprecations because properties are missing

  1448x: Creation of dynamic property Drupal\Core\Extension\Extension::$subpath is deprecated
    724x in TrackerNodeAccessTest::testTrackerNodeAccessIndexing from Drupal\Tests\tracker\Functional
    724x in TrackerNodeAccessTest::testTrackerNodeAccess from Drupal\Tests\tracker\Functional

  1448x: Creation of dynamic property Drupal\Core\Extension\Extension::$origin is deprecated
    724x in TrackerNodeAccessTest::testTrackerNodeAccessIndexing from Drupal\Tests\tracker\Functional
    724x in TrackerNodeAccessTest::testTrackerNodeAccess from Drupal\Tests\tracker\Functional

  1182x: Creation of dynamic property Drupal\Core\Extension\Extension::$info is deprecated
    591x in TrackerNodeAccessTest::testTrackerNodeAccessIndexing from Drupal\Tests\tracker\Functional
    591x in TrackerNodeAccessTest::testTrackerNodeAccess from Drupal\Tests\tracker\Functional

  1150x: Creation of dynamic property Drupal\Core\Extension\Extension::$status is deprecated
    575x in TrackerNodeAccessTest::testTrackerNodeAccessIndexing from Drupal\Tests\tracker\Functional
    575x in TrackerNodeAccessTest::testTrackerNodeAccess from Drupal\Tests\tracker\Functional

  1150x: Creation of dynamic property Drupal\Core\Extension\Extension::$required_by is deprecated
    575x in TrackerNodeAccessTest::testTrackerNodeAccessIndexing from Drupal\Tests\tracker\Functional
    575x in TrackerNodeAccessTest::testTrackerNodeAccess from Drupal\Tests\tracker\Functional

  1150x: Creation of dynamic property Drupal\Core\Extension\Extension::$requires is deprecated
    575x in TrackerNodeAccessTest::testTrackerNodeAccessIndexing from Drupal\Tests\tracker\Functional
    575x in TrackerNodeAccessTest::testTrackerNodeAccess from Drupal\Tests\tracker\Functional

  1150x: Creation of dynamic property Drupal\Core\Extension\Extension::$sort is deprecated
    575x in TrackerNodeAccessTest::testTrackerNodeAccessIndexing from Drupal\Tests\tracker\Functional
    575x in TrackerNodeAccessTest::testTrackerNodeAccess from Drupal\Tests\tracker\Functional

  994x: Creation of dynamic property Drupal\Core\Extension\Extension::$weight is deprecated
    497x in TrackerNodeAccessTest::testTrackerNodeAccessIndexing from Drupal\Tests\tracker\Functional
    497x in TrackerNodeAccessTest::testTrackerNodeAccess from Drupal\Tests\tracker\Functional

  994x: Creation of dynamic property Drupal\Core\Extension\Extension::$schema_version is deprecated
    497x in TrackerNodeAccessTest::testTrackerNodeAccessIndexing from Drupal\Tests\tracker\Functional
    497x in TrackerNodeAccessTest::testTrackerNodeAccess from Drupal\Tests\tracker\Functional
kim.pepper’s picture

As per #57 I propose a new issue to create those methods on the extension class first, and postpone this on that issue.

Update: I think we should continue working on this issue.

kim.pepper’s picture

Title: Add Module, Theme, Profile, and Extension value objects » [PP-1] Add Module, Theme, Profile, and Extension value objects
kim.pepper’s picture

Title: [PP-1] Add Module, Theme, Profile, and Extension value objects » Add Module, Theme, Profile, and Extension value objects
Version: 9.5.x-dev » 10.0.x-dev
Status: Needs work » Needs review
FileSize
10.99 KB
6.75 KB

I've combined this with our own ArrayObject implementation in #2972143: Create \Drupal\Component\Utility\ArrayObject and added fixes for #62

Wim Leers’s picture

Re-testing #66, because I cannot reproduce those failures.

Status: Needs review » Needs work

The last submitted patch, 66: 2024043-66.patch, failed testing. View results

pradhumanjain2311’s picture

Patch #66 applied cleanly for version 10.0.x.

andypost’s picture

Queued last patch for PHP 8.2-rc5 & MySQL 8

and here's a patch test the sate

kim.pepper’s picture

I assume these are random test fails in HEAD, as these are new objects and not getting used anywhere yet.

andypost’s picture

They are not random - 2794 failed https://www.drupal.org/pift-ci-job/2510630

andypost’s picture

Status: Needs work » Needs review

re-queued patch #66 again

kim.pepper’s picture

Ah yes, the php 8.2 fails are real.

andypost’s picture

Status: Needs review » Needs work
  1. +++ b/core/lib/Drupal/Component/Utility/ArrayObject.php
    @@ -0,0 +1,100 @@
    + * Provides a utility object for arrays.
    ...
    +   * Creates a new ArrayObject from the array.
    

    Having the same name as SPL class is confusing and DX-- on reading code, please rename

    Ref https://www.php.net/manual/en/class.arrayobject.php

  2. +++ b/core/lib/Drupal/Component/Utility/ArrayObject.php
    @@ -0,0 +1,100 @@
    +class ArrayObject implements \Countable, \IteratorAggregate, \ArrayAccess {
    
    +++ b/core/tests/Drupal/Tests/Component/Utility/ArrayObjectTest.php
    @@ -0,0 +1,83 @@
    + * @coversDefaultClass \Drupal\Component\Utility\ArrayObject
    + * @group Utility
    ...
    +class ArrayObjectTest extends TestCase {
    
    +++ b/core/tests/Drupal/Tests/Component/Utility/DummyArrayObject.php
    @@ -0,0 +1,22 @@
    +class DummyArrayObject extends ArrayObject {
    

    I think it needs to be split because nearly the same is happening in #3275858: View's ResultRow uses deprecated dynamic properties which is much simple then new extension value objects for extensions

    Moreover it could be applied to plugins - views has a lot dynamic properties so would be great to reuse same approach

  3. +++ b/core/lib/Drupal/Core/Extension/Theme.php
    @@ -0,0 +1,107 @@
    +  public function getStylesheets() {
    +    return $this->get('stylesheets') ?: [];
    ...
    +  public function getScripts() {
    +    return $this->get('scripts') ?: [];
    

    wondered why there's no libraries

  4. +++ b/core/lib/Drupal/Core/Extension/Theme.php
    @@ -0,0 +1,107 @@
    +  public function getFeatures() {
    +    return $this->get('regions') ?: [];
    

    copy/paste

kim.pepper’s picture

Just to be clear, these value objects aren't meant to replace \Drupal\Core\Extension\Extension. That class is much more to do with loading extensions with ExtensionDiscovery. It might make sense to rename it to ExtensionFile (or similar)?

These value objects are for 'Extension Info' which is currently just an array that gets passed around for modules, themes, engines, and profiles.

I don't think we need our own ArrayObject after all. We can just use SPL \ArrayObject for the abstract base class.

Also since we are representing extension info, I renamed everything to include 'Info', eg. \Drupal\Core\Extension\ThemeInfo

I provided a usage example for ThemeInfo for feedback. I didn't include an interdiff since it was pretty basic and I re-wrote most of it.

kim.pepper’s picture

Version: 10.0.x-dev » 10.1.x-dev
FileSize
13 KB
734 bytes

Fix spelling error.

RiyaDongre made their first commit to this issue’s fork.

andypost’s picture

Status: Needs review » Needs work

Something is slightly broken Undefined array key "dependencies" and others all over https://dispatcher.drupalci.org/job/drupal_patches/154004/

kim.pepper’s picture

Yeah, this is because we removed the "dependencies" array key in favour of using a property and getDependencies().

However, we need to have a BC layer, so I guess that means supporting both but triggering a deprecation error if array access is used. We can use Drupal\Component\Utility\DeprecatedArray for this.

When I do, I get hundreds of instances where this is getting called. Not sure how to break this down into more bite-sized chunks.

andypost’s picture

I think we can use DeprecatedArray and silence deprecations as contrib also can have tons of usages

Janvi Dasani’s picture

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.

donquixote’s picture

I don't like the mixing of array access and regular object methods.
If we need to support custom properties, I would rather want to see $info->get($key) than $info[$key], if $info is not an array.

Is there any good reason for it?
BC support where we previously used arrays?
It would not really work because we already break BC by not providing the basic keys like version etc.

I think for issues like this we should have two PRs or two branches:

  • One with the actual implementation we want to be merged in this issue.
  • One "proof of concept" that shows how this would be used to improve DX elsewhere in the codebase. This could be done in a separate issue (subtask), if we want to keep the main issue clean.

In the end we only merge the first MR, and split out the work from the second branch into follow-up issues.

donquixote’s picture

Also, the issue summary and title are out of date.
The original issue asked to introduce extension objects.
Now we have them.

So the remaining problem seems to be that we are not happy with the current Extension class.
Which is fine, but it seems this should be addressed in a new issue where we clearly summarize what is wrong with the current Extension class, and what are the main use cases where we want to improve DX.

Or at least update the summary of this one.

----

On the topic:

I have the impression that different functions/methods/classes actually need different types of extension objects:

  • Some just need the machine name of each module.
  • Others need machine name and label.
  • Others need machine name and path, to look for yml files.
  • Others need various info from the *.info.yml file.
  • Some need only enabled modules, others need all available modules, possibly with a status for enabled/disabled.
  • Some need raw data from a filesystem scan and from the *.info.yml files, others want processed data that already ran through hook_system_info_alter().

At different points in the application and the bootstrap process we actually have different parts of this info available.

And depending where and when you get your Extension object from, some of its properties might be filled at that time, or not.

E.g.:

  • ModuleHandler->getModuleList() gives us a list of Extension objects, but these don't have any of the special properties filled.
  • ExtensionDiscovery->scan() gives Extension objects with no info filled in, but later these same objects get updated in ModuleExtensionList. This "pollutes" the static cache in ExtensionDiscovery::$files. Yes, at some point, ExtensionDiscovery::$files[...]['core'][0]['module']['.../system.info.yml']->info will be filled in.
  • ModuleExtensionList->getList() gives us distinct Extension objects with don't have their info filled in, and never will - afaik.

This maybe works ok for now, but it makes for messy dependency chain, and prevents proper refactoring.

It would be cleaner to use different classes, or at least diference instances, in different places of the application.
These classes can be immutable, or at least be treated as such most of the time, and only contain the properties that are needed and available in the context in which they are used and provided:

  • One DiscoveredExtension class returned from and cached by ExtensionDiscovery. This does not allow dynamic properties.
  • The "feature-complete" AvailableExtension class to be returned from ExtensionList, which includes all the info. This could have subclasses for theme, module, profile etc.
  • Another InstalledExtension class (named differently I guess) to be returned from ModuleHandler->getModuleList(), without the extra info. For my taste, this also should not contain the app root and the ->load() method.

This would allow to refactor the respective classes, and make a clean chain of dependencies, where the dependent layers don't "pollute" the objects that they obtain from a source.

There are different ways to do this in a BC-friendly way. E.g. leave the ModuleHandlerInterface unchanged, let it return the current Extension objects, but provide an alternative that returns the "clean" Extension objects.

This is all still quite rough and might be difficult to do.
But if we are already considering a redesign of this, we should do it in a clean way.

donquixote’s picture

Re #84 (myself)

The original issue asked to introduce extension objects.
Now we have them.

So I think the idea is:
Currently we have one Extension class for everything.
Instead, we want dedicated Extension classes for different types of extension.

I'd say this makes sense, and it is fine to keep the existing issue for this.

So

should be addressed in a new issue

Not needed :)

Still, re #85 (myself):
If you ask me, we should only use these new extension classes in the places that really need them.
So yes for ExtensionList, but:
- Not in ExtensionDiscovery.
- Not in ModuleHandler.

Both of these can use more slim objects, or perhaps just plain arrays.
Or ModuleHandler can use the old Extension class for BC.