Follow-up to #2208429: Extension System, Part III: ExtensionList, ModuleExtensionList and ProfileExtensionList, which provides new extension listing classes ExtensionList, ProfileExtensionList, ModuleExtensionList.

Problem/Motivation

Due to the size of the theme list refactoring scope, we need a separate issue.

Proposed resolution

Create new ThemeExtensionList and ThemeEngineExtensionList classes, refactor theme discovery functionality into this class.

Remaining tasks

Patch
Reviews
Commit

User interface changes

None

API changes

  1. ThemeHandler constructor has new argument $theme_list of type ThemeExtensionList but is an optional argument. Service signature not changed in core.services.yml.
  2. ThemeInstaller constructor has new argument $theme_list of type ThemeExtensionList but is an optional argument. Service signature not changed in core.services.yml.
  3. New ThemeExtensionList class and extension.list.theme service. Class marked internal.
  4. New ThemeEngineExtensionList class and extension.list.theme_engine service. Class marked internal.
  5. The ModuleExtensionList and ProfileExtensionList classes also marked internal.
  6. The protected $defaultFeatures class property removed from ThemeHandler
  7. The protected $extensionDiscovery class property removed from ThemeHandler

Data model changes

None.

Release notes snippet

Theme extension listing moved to ThemeExtensionList class and brought into line with module listing making a complex part of core more consistent. In the future this will allow us to remove unnecessary code from the system module.

CommentFileSizeAuthor
#243 2659940-reroll-3-243.patch84.92 KBalexpott
#243 239-243-interdiff.txt3.2 KBalexpott
#239 2659940-reroll-3-239.patch85.03 KBalexpott
#239 237-239-interdiff.txt2.06 KBalexpott
#237 2659940-reroll-2-237.patch84.11 KBalexpott
#233 2659940-234-all-js-tests-with-changes.patch83.6 KBphenaproxima
#233 2659940-234-all-js-tests-without-changes.patch1.72 KBphenaproxima
#233 2659940-234-ajaxtest-only-with-changes.patch83.67 KBphenaproxima
#233 2659940-234-ajaxtest-only-without-changes.patch1.8 KBphenaproxima
#232 2659940-232-random-failure-js-tests.patch82.75 KBphenaproxima
#229 2659940-229-random-failure-js-tests.patch82.75 KBphenaproxima
#227 2659940-227-random-failure.patch82.85 KBphenaproxima
#194 2659940-194.patch82.03 KBoriol_e9g
#194 interdiff.txt2.06 KBoriol_e9g
#192 2659940-191.patch81.11 KBoriol_e9g
#175 2659940-175.patch81.09 KBjofitz
#175 interdiff-2659940-172-175.txt538 bytesjofitz
#172 2659940-172.patch80.76 KBjofitz
#172 interdiff-2659940-169-172.txt441 bytesjofitz
#169 2659940-169.patch80.7 KBjofitz
#163 2659940-163.patch79.77 KBalexpott
#163 159-163-interdiff.txt2.54 KBalexpott
#159 2659940-159.patch78.72 KBalexpott
#159 155-159-interdiff.txt2.01 KBalexpott
#155 2659940-155.patch78.18 KBalexpott
#155 154-155-interdiff.txt7.98 KBalexpott
#154 2659940-154.patch74.35 KBalexpott
#154 151-154-interdiff.txt15.21 KBalexpott
#151 2659940-151.patch81.57 KBalexpott
#151 145-151-interdiff.txt2.09 KBalexpott
#145 2659940-145.patch81.31 KBalexpott
#145 141-145-interdiff.txt7.31 KBalexpott
#143 2659940-138-141-graphs.png27.85 KBAnonymous (not verified)
#141 2659940-141.patch78.88 KBalexpott
#141 138-141-interdiff.txt2.16 KBalexpott
#138 2659940-138.patch79.24 KBalexpott
#138 137-138-interdiff.txt1.62 KBalexpott
#137 2659940-137.patch79.75 KBalexpott
#137 133-137-interdiff.txt3.16 KBalexpott
#133 2659940-133.patch79.23 KBalexpott
#133 120-133-interdiff.txt7.69 KBalexpott
#120 2659940-120.patch76.2 KBmarkhalliwell
#120 interdiff-2659940-115-120.txt5.9 KBmarkhalliwell
#115 2659940-115.patch75.23 KBmarkhalliwell
#115 interdiff-2659940-112-115.txt1.15 KBmarkhalliwell
#112 2659940-112.patch76.04 KBmarkhalliwell
#110 2659940-110.patch81.5 KBmarkhalliwell
#100 2659940-100.patch73.05 KBalmaudoh
#100 interdiff.txt514 bytesalmaudoh
#98 interdiff.txt5.93 KBalmaudoh
#98 2659940-98.patch73.55 KBalmaudoh
#93 2659940-93.patch76.87 KBalmaudoh
#93 interdiff.txt862 bytesalmaudoh
#88 interdiff.txt5.32 KBalmaudoh
#88 fix.txt1.43 KBalmaudoh
#88 2659940-88.patch76.81 KBalmaudoh
#88 2659940-88-fail.patch76.04 KBalmaudoh
#85 2659940-85-fail.patch73.99 KBalmaudoh
#85 interdiff.txt3.85 KBalmaudoh
#81 2659940-81-fail.patch73.47 KBalmaudoh
#81 interdiff.txt2.37 KBalmaudoh
#80 2659940-80.patch71.1 KBalmaudoh
#80 interdiff.txt6.77 KBalmaudoh
#69 2659940-69.patch65.73 KBalmaudoh
#69 interdiff.txt1.46 KBalmaudoh
#67 2659940-67.patch65.03 KBalmaudoh
#67 interdiff.txt640 bytesalmaudoh
#66 2659940-66.patch65.03 KBalmaudoh
#66 interdiff.txt6.6 KBalmaudoh
#64 2659940-63.patch59.37 KBalmaudoh
#64 interdiff.txt4.05 KBalmaudoh
#62 2659940-62.patch56.88 KBalmaudoh
#62 interdiff.txt4.67 KBalmaudoh
#61 2659940-61.patch56.35 KBalmaudoh
#61 interdiff.txt2.77 KBalmaudoh
#57 2659940-57.patch55.41 KBalmaudoh
#55 2659940-55.patch55.41 KBalmaudoh
#55 2659940-55.patch58.31 KBalmaudoh
#2 extension_system_part-2659940-2-do-not-test.patch39.04 KBalmaudoh
#2 extension_system_part-2659940-2.patch88.04 KBalmaudoh
#4 extension_system_part-2659940-3-do-not-test.patch39.3 KBalmaudoh
#4 extension_system_part-2659940-3.patch88.3 KBalmaudoh
#6 interdiff.txt1.6 KBalmaudoh
#6 extension_system_part-2659940-6-do-not-test.patch38.97 KBalmaudoh
#6 extension_system_part-2659940-6.patch87.97 KBalmaudoh
#8 interdiff.txt776 bytesalmaudoh
#8 extension_system_part-2659940-8-do-not-test.patch39.14 KBalmaudoh
#8 extension_system_part-2659940-8.patch88.14 KBalmaudoh
#11 interdiff.txt2.03 KBalmaudoh
#11 extension_system_part-2659940-11-do-not-test.patch39.87 KBalmaudoh
#11 extension_system_part-2659940-11.patch88.87 KBalmaudoh
#20 2659940-20-do-not-test.patch40.29 KBalmaudoh
#25 2659940-25.patch40.32 KBjofitz
#28 drupal-theme-list-2659940-28.patch40.38 KBdsnopek
#28 interdiff.txt3.07 KBdsnopek
#31 2659940-31.patch0 bytesalmaudoh
#31 interdiff.txt9.79 KBalmaudoh
#32 2659940-31.patch41.21 KBalmaudoh
#32 interdiff.txt9.79 KBalmaudoh
#35 2659940-35.patch43.28 KBalmaudoh
#35 interdiff.txt3.59 KBalmaudoh
#37 2659940-37.patch43.25 KBjofitz
#37 interdiff-35-37.txt1.17 KBjofitz
#39 2659940-39.patch43.18 KBjofitz
#39 interdiff-37-39.txt2.82 KBjofitz
#43 2659940-43.patch45.17 KBdawehner
#43 interdiff-2659940.txt3.78 KBdawehner
#47 interdiff.txt1.19 KBalmaudoh
#47 2659940-46.patch45.49 KBalmaudoh
#49 interdiff.txt17.1 KBalmaudoh
#49 2659940-49.patch57.15 KBalmaudoh

Comments

almaudoh created an issue. See original summary.

almaudoh’s picture

Status: Postponed » Needs review
StatusFileSize
new39.04 KB
new88.04 KB

First trial patch rolled on top of the patch at #2208429: Extension System, Part III: ExtensionList, ModuleExtensionList and ProfileExtensionList. CNR for testbot.

Status: Needs review » Needs work

The last submitted patch, 2: extension_system_part-2659940-2.patch, failed testing.

almaudoh’s picture

Status: Needs work » Needs review
StatusFileSize
new39.3 KB
new88.3 KB

Rerolled to HEAD

Status: Needs review » Needs work

The last submitted patch, 4: extension_system_part-2659940-3.patch, failed testing.

almaudoh’s picture

Status: Needs work » Needs review
StatusFileSize
new1.6 KB
new38.97 KB
new87.97 KB

Status: Needs review » Needs work

The last submitted patch, 6: extension_system_part-2659940-6.patch, failed testing.

almaudoh’s picture

Status: Needs work » Needs review
StatusFileSize
new776 bytes
new39.14 KB
new88.14 KB

I was very sleepy when I posted the last two patches :P

dawehner’s picture

In general this looks pretty good already. Didn't had a deep look into it yet, but I totally agree that the theme extension list part should be extracted
from the theme handler. Great work!

Status: Needs review » Needs work

The last submitted patch, 8: extension_system_part-2659940-8.patch, failed testing.

almaudoh’s picture

Status: Needs work » Needs review
StatusFileSize
new2.03 KB
new39.87 KB
new88.87 KB

So, do we still need the system_list cache?

dawehner’s picture

So, do we still need the system_list cache?

I guess no?

jibran’s picture

Should we move all the drupal_get_profile() calls to wrapper function?

berdir’s picture

  1. +++ b/core/includes/module.inc
    @@ -22,43 +23,19 @@
      */
     function system_list($type) {
    -  $lists = &drupal_static(__FUNCTION__);
    ...
      */
     function system_list_reset() {
    -  drupal_static_reset('system_list');
    

    Nice.

    We should deprecate those functions and mark for removal in 9.x

  2. +++ b/core/lib/Drupal/Core/Extension/ThemeHandler.php
    @@ -16,21 +16,6 @@
    -   */
    -  protected $defaultFeatures = array(
    -    'favicon',
    -    'logo',
    -    'node_user_picture',
    -    'comment_user_picture',
    -    'comment_user_verification',
    -  );
    

    Wondering if the changes in here could be considered a BC break in some way...

  3. +++ b/core/lib/Drupal/Core/Extension/ThemeHandler.php
    @@ -184,16 +169,11 @@ public function uninstall(array $theme_list) {
    -      }
    +      $themes = $this->getThemeListing()->listExtensions();
           foreach ($themes as $theme) {
    -        $this->addTheme($theme);
    +        if ($theme->status == TRUE) {
    +          $this->addTheme($theme);
    +        }
    

    Should we have a way to get enabled themes only? a method for this?

    The extension list is different for themes than for modules, as it also knows about enabled or not.

  4. +++ b/core/lib/Drupal/Core/Extension/ThemeHandler.php
    @@ -246,186 +216,27 @@ public function reset() {
    +  protected function getThemeListing() {
    +    if (!isset($this->themeList)) {
    +      $this->themeList = \Drupal::service('theme_listing');
         }
    -    return $this->extensionDiscovery;
    +    return $this->themeList;
       }
    

    is this a fallback when the extension isn't injected yet?

  5. +++ b/core/lib/Drupal/Core/Extension/ThemeInstaller.php
    @@ -179,17 +179,12 @@ public function install(array $theme_list, $install_dependencies = TRUE) {
    -      // @todo Remove system_list().
    -      $this->systemListReset();
    +      // Reset theme listing.
    +      \Drupal::service('theme_listing')->reset();
     
    

    can we inject this?

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.

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.

dawehner’s picture

Status: Needs review » Postponed

Let's postpone it for now

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.

almaudoh’s picture

StatusFileSize
new40.29 KB

Since #2208429: Extension System, Part III: ExtensionList, ModuleExtensionList and ProfileExtensionList is RTBC, I thought I should re-roll this patch and bring it back. So here's a re-roll of the -do-not-test.patch in #11

It still needs to be applied to the RTBC patch in #2208429: Extension System, Part III: ExtensionList, ModuleExtensionList and ProfileExtensionList

dawehner’s picture

Thank you for your reroll @almaudoh!

  1. +++ b/core/includes/module.inc
    @@ -22,43 +23,19 @@
    +  return array_filter(\Drupal::service($type . '_listing')->listExtensions(), function(Extension $extension) {
    +    return $extension->status == TRUE;
    +  });
    

    What about just do a return $extension->status ?

  2. +++ b/core/lib/Drupal/Core/Extension/ThemeEngineExtensionList.php
    @@ -0,0 +1,18 @@
    +
    +class ThemeEngineExtensionList extends ExtensionList {
    
    +++ b/core/lib/Drupal/Core/Extension/ThemeExtensionList.php
    @@ -0,0 +1,230 @@
    +
    +class ThemeExtensionList extends ExtensionList {
    

    Let's still mark this internal? Let's also add some documentation :)

  3. +++ b/core/lib/Drupal/Core/Extension/ThemeExtensionList.php
    @@ -0,0 +1,230 @@
    +  protected function doListExtensions() {
    

    This methods could be made easier to rad by splitting it up into two bits: $themes = readThemeInfoFiles($themes) and $themes = $this->fillInSubThemes($themes). Maybe we could do that in another issue

  4. +++ b/core/lib/Drupal/Core/Extension/ThemeExtensionList.php
    @@ -0,0 +1,230 @@
    +    // @todo Move into a generic ExtensionHandler base class.
    +    // @see https://www.drupal.org/node/2208429
    

    I fear we would need to create a new follow up, given that this is extension part 3 :)

  5. +++ b/core/lib/Drupal/Core/Extension/ThemeExtensionList.php
    @@ -0,0 +1,230 @@
    +  public function getBaseThemes(array $themes, $theme) {
    

    I see, this is public due to BC reasons. We should clearly communicate that with another @internal statement + an explanation.

  6. +++ b/core/lib/Drupal/Core/Extension/ThemeInstaller.php
    @@ -178,17 +178,12 @@ public function install(array $theme_list, $install_dependencies = TRUE) {
     
    -      // Update the current theme data accordingly.
    -      $current_theme_data = $this->state->get('system.theme.data', []);
    -      $current_theme_data[$key] = $theme_data[$key];
    -      $this->state->set('system.theme.data', $current_theme_data);
    -
    ...
     
    -      // @todo Remove system_list().
    -      $this->systemListReset();
    +      // Reset theme listing.
    +      \Drupal::service('theme_listing')->reset();
    

    👍 Doing less magic on theme installation is an obvious improvement

  7. +++ b/core/tests/Drupal/Tests/Core/Extension/ThemeHandlerTest.php
    @@ -153,158 +131,6 @@ public function testThemeLibrariesEmpty() {
    -  /**
    -   * Tests rebuild the theme data with theme parents.
    -   */
    -  public function testRebuildThemeDataWithThemeParents() {
    -    $this->extensionDiscovery->expects($this->at(0))
    -      ->method('scan')
    

    We need to add back test coverage for those ...

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

jofitz’s picture

Status: Needs work » Needs review
Issue tags: -Needs reroll
StatusFileSize
new40.32 KB

Re-roll.

Status: Needs review » Needs work

The last submitted patch, 25: 2659940-25.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

dsnopek’s picture

I haven't done a detailed read of the patch, I just peeked in, but I saw something right away at the top:

  1. --- a/core/core.services.yml
    +++ b/core/core.services.yml
    @@ -511,6 +511,12 @@ services:
       extension.list.profile:
         class: Drupal\Core\Extension\ProfileExtensionList
         arguments: ['@app.root', 'profile', '@cache.default', '@info_parser', '@module_handler', '@state', '%install_profile%']
    +  theme_listing:
    +    class: Drupal\Core\Extension\ThemeExtensionList
    +    arguments: ['@app.root', 'theme', '@cache.default', '@info_parser', '@module_handler', '@config.factory', '@theme_engine_listing']
    +  theme_engine_listing:
    +    class: Drupal\Core\Extension\ThemeEngineExtensionList
    +    arguments: ['@app.root', 'theme_engine', '@cache.default', '@info_parser', '@module_handler']
    

    Shouldn't these services be named 'extension.list.theme' and 'extension.list.theme_engine' to match the extension list services for modules and profiles?

  2. +++ b/core/includes/bootstrap.inc
    @@ -226,45 +226,24 @@ function drupal_get_filename($type, $name, $filename = NULL) {
    +  $service_id = 'extension.list.' . $type;
    +  /** @var \Drupal\Core\Extension\ExtensionList $extension_list */
    +  $extension_list = \Drupal::service($service_id);
    

    This code will depend on that!

dsnopek’s picture

Status: Needs work » Needs review
StatusFileSize
new40.38 KB
new3.07 KB

This fixes the review from #27 - hopefully, this one will test a little better

Status: Needs review » Needs work

The last submitted patch, 28: drupal-theme-list-2659940-28.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

dsnopek’s picture

Ah, apparently what is breaking the tests is:

Drupal\Tests\action\Functional\ActionListTest::testEmptyActionList
PHPUnit_Framework_Exception: Fatal error: Class Drupal\Core\Extension\ThemeEngineExtensionList contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (Drupal\Core\Extension\ExtensionList::getInstalledExtensionNames)
almaudoh’s picture

Status: Needs work » Needs review
StatusFileSize
new0 bytes
new9.79 KB

Trying to fix the test fails first. Interdiff is against #25 and contains changes mentioned in #28.

almaudoh’s picture

StatusFileSize
new41.21 KB
new9.79 KB

Ok. Here's the actual patch.

The last submitted patch, 31: 2659940-31.patch, failed testing. View results

Status: Needs review » Needs work

The last submitted patch, 32: 2659940-31.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

almaudoh’s picture

Status: Needs work » Needs review
StatusFileSize
new43.28 KB
new3.59 KB

Addressed some of the review comments here:
#14:
1. :)
2. If so, then the removal of $extensionDiscovery from ThemeHandler would also be a BC break. Not so sure.
3. I thought about this too, just like ModuleHandler::getModuleList(), but I'm wondering if the module/theme handler is the right place for that information or would it rather be in the module/theme installer. Also, as it is now, both modules and themes have the same 'installed' status, no more enabled status.
4. Yes, because to maintain BC, the ThemeHandler constructor had to have an optional parameter for the ThemeExtensionList
5. I guess we can. It would be the same pattern as in 4. I guess.

#21:
1. Done
2. I'm just wondering why these two would be marked @internal while the ModuleExtensionList is not. Should we make all three internal.
3. It seemed like it would be too complicated splitting out to two methods (because of the $sub_themes variable), so I went halfway and broke out $this->fillInSubThemeData() to a separate method.
4. I'm wondering if we still really need to do that. I think we should just remove the @todo.
5. IMO this should be the public interface while the ThemeHandlerInterface::getBaseThemes() should be deprecated. What do you think?
6. Yes! Less magic.
7. Will do this in the next patch.

Status: Needs review » Needs work

The last submitted patch, 35: 2659940-35.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

jofitz’s picture

Status: Needs work » Needs review
StatusFileSize
new43.25 KB
new1.17 KB

A minor improvement and a correction ($theme->status is normally 0 or 1 so we don't want === TRUE).

Status: Needs review » Needs work

The last submitted patch, 37: 2659940-37.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

jofitz’s picture

Status: Needs work » Needs review
StatusFileSize
new43.18 KB
new2.82 KB

Fixed the coding standards errors.

But the Functional test failures are beyond me, on this occasion.

Status: Needs review » Needs work

The last submitted patch, 39: 2659940-39.patch, failed testing. View results

dawehner’s picture

2. I'm just wondering why these two would be marked @internal while the ModuleExtensionList is not. Should we make all three internal.

Oh yeah totally, till we are sure these services are stable.

dawehner’s picture

Assigned: Unassigned » dawehner

Assigning to myself to fix the failure

dawehner’s picture

Assigned: dawehner » Unassigned
Status: Needs work » Needs review
StatusFileSize
new45.17 KB
new3.78 KB

Status: Needs review » Needs work

The last submitted patch, 43: 2659940-43.patch, failed testing. View results

almaudoh’s picture

     $engines = $this->engineList->getList();
-    $this->installedThemes = $this->configFactory->get('core.extension')->get('theme') ?: [];
+    $this->getInstalledExtensionNames();

This doesn't work because we always want to get the freshest list of themes (rather than the cached list) when building the theme listing (for instance where a theme was just installed or uninstalled).

dawehner’s picture

@almaudoh Ah interesting. Do you mind on your next patch iteration make a comment why we are doing something this specific way? Thank you!

almaudoh’s picture

Status: Needs work » Needs review
StatusFileSize
new1.19 KB
new45.49 KB

So correcting per #45, let's see if tests all pass...

jibran’s picture

Status: Needs review » Needs work

It's green! Nice work!

  1. Do we need profiling here?
  2. +++ b/core/lib/Drupal/Core/Extension/ThemeExtensionList.php
    @@ -0,0 +1,272 @@
    +    // @todo Move into a generic ExtensionHandler base class.
    +    // @see https://www.drupal.org/node/2208429
    

    #2208429: Extension System, Part III: ExtensionList, ModuleExtensionList and ProfileExtensionList is fixed. Can we remove this now?

  3. +++ b/core/lib/Drupal/Core/Extension/ThemeInstaller.php
    @@ -178,17 +178,12 @@ public function install(array $theme_list, $install_dependencies = TRUE) {
    +      \Drupal::service('extension.list.theme')->reset();
    
    @@ -279,7 +269,7 @@ protected function resetSystem() {
    +    \Drupal::service('extension.list.theme')->reset();
    

    Can we inject this if not then can we add a comment here?

  4. +++ b/core/includes/module.inc
    @@ -22,43 +23,19 @@
     function system_list($type) {
    ...
     function system_list_reset() {
    
  5. Are we going to deprecate these? If yes then can we have a change notice?

almaudoh’s picture

Status: Needs work » Needs review
StatusFileSize
new17.1 KB
new57.15 KB

In this patch, the test coverage that was removed for ::rebuildThemeData() and ::getBaseThemes() has been reinstated in a new ThemeExtensionListTest class.

Other comments have also been addressed:
1. ModuleExtensionList, ProfileExtensionList, ThemeExtensionList and ThemeEngineExtensionList have all been marked @internal
2. The @todo referencing #2208429: Extension System, Part III: ExtensionList, ModuleExtensionList and ProfileExtensionList has been removed
3. The 'extension.list.theme' service is now injected into ThemeInstaller service class.

#48:
1. This most definitely would need profiling :)
2. Removed
3. Injected
4. I think deprecations should be in a follow-up issue so as not to delay this one. There are already a bunch of deprecation issues scattered around, I'll just make a meta issue to capture all of them.

almaudoh’s picture

Issue summary: View changes
almaudoh’s picture

Title: Extension System, Part III: ThemeExtensionList » Extension System, Part III: ThemeExtensionList and ThemeEngineExtensionList
Issue summary: View changes
jibran’s picture

We should also add information about ThemeExtensionList and ThemeEngineExtensionList in _system_rebuild_module_data() and co. are replaced by services to give you all available modules.

markhalliwell’s picture

Status: Needs review » Needs work

Re #41:
I'm really not a huge fan of flagging these as @internal.

However, if these are only being temporarily flagged as such for "stability" reasons, then a comment and @todo linking to an issue that removes this state once the criteria deeming them as "stable" has been met should also be added.

Otherwise, we risk leaving them this way and I know for a fact that I have plans on extending some of these classes in the future since they're now proper services that can easily be extended or replaced entirely by contrib.

almaudoh’s picture

Status: Needs work » Needs review
StatusFileSize
new58.31 KB
new55.41 KB

Rerolled after #2939904: Fix system_get_info() for non-installed modules got committed. NW to test.

almaudoh’s picture

Hiding the wrong patch.

almaudoh’s picture

StatusFileSize
new55.41 KB

Head was moving too fast on the previous reroll.

The last submitted patch, 55: 2659940-55.patch, failed testing. View results

markhalliwell’s picture

Status: Needs review » Needs work

Please see #54.

I would even suggest that all these issues for extension listings get backported to 8.5.x so we can actually start using them and then before 8.6.0 is released, these @internal tags are removed because we'll have fixed any major bugs that may have crept up.

dawehner’s picture

I opened up #2940481: Road to stabilize ExtensionList services. To be honest I'm a huge fan of adding an interface on the longrun, but let's discuss that over there.

almaudoh’s picture

Status: Needs work » Needs review
StatusFileSize
new2.77 KB
new56.35 KB

Added some explanation for making the classes @internal.

almaudoh’s picture

StatusFileSize
new4.67 KB
new56.88 KB

Also made ExtensionList @internal and slightly improved the formatting of the message.

markhalliwell’s picture

Status: Needs review » Needs work
  1. +++ b/core/includes/module.inc
    @@ -22,43 +23,19 @@
    +  return array_filter(\Drupal::service('extension.list.' . $type)->getList(), function (Extension $extension) {
    +    return $extension->status;
    +  });
    

    This feels like the extension list should already support returning a filtered list based on status.

    Perhaps a parameter could be added to ->getList(), e.g. $enabled = NULL?

    • NULL - return all regardless of status
    • TRUE - return all enabled
    • FALSE - return all disabled

    This can't be the only place where having native support for this kind of filter would be necessary.

  2. +++ b/core/lib/Drupal/Core/Extension/ThemeEngineExtensionList.php
    @@ -0,0 +1,34 @@
    +    return ['twig'];
    

    Again, this shouldn't be a static list. It should actually return the installed theme engine names.

  3. +++ b/core/lib/Drupal/Core/Extension/ThemeHandler.php
    @@ -179,16 +164,11 @@ public function uninstall(array $theme_list) {
    +      $themes = $this->getThemeExtensionList()->getList();
           foreach ($themes as $theme) {
    -        $this->addTheme($theme);
    +        if ($theme->status) {
    +          $this->addTheme($theme);
    +        }
           }
    

    Ah, here is another place that could benefit from an already filtered list as mentioned above.

    It could probably even be reduced to a one-liner for that matter:

    array_map([$this, 'addTheme'], $this->getThemeExtensionList()->getList(TRUE));
    
almaudoh’s picture

Status: Needs work » Needs review
Issue tags: -Needs change record updates
StatusFileSize
new4.05 KB
new59.37 KB

#63:
1. This is something I thought we would need also, but I felt it was out of scope. Decided it's cleaner to introduce new methods ::getInstalled and ::getUninstalled(), while ::getList() will still return all extensions. I've also added test coverage for the new methods.
2. So far I've not found where the theme-engine list is stored - it's not in the core.extension configuration storage. I've not seen any code that actually installs theme engines and saves the list somewhere. Will have to create a follow-up for this.
3. Great idea. Did that.

I've also updated the change record. https://www.drupal.org/node/2709919

Status: Needs review » Needs work

The last submitted patch, 64: 2659940-63.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

almaudoh’s picture

Status: Needs work » Needs review
StatusFileSize
new6.6 KB
new65.03 KB

Added more test coverage that helped me identify the bug. Fixed it.

almaudoh’s picture

StatusFileSize
new640 bytes
new65.03 KB

Missed one test :).

dawehner’s picture

  1. +++ b/core/includes/module.inc
    @@ -5,6 +5,7 @@
     
    +use Drupal\Core\Extension\Extension;
     use Drupal\Core\Extension\ExtensionDiscovery;
    

    Why do you need this use statement?

  2. +++ b/core/includes/module.inc
    @@ -22,43 +23,17 @@
    -  foreach ($lists['filepaths'] as $item) {
    -    system_register($item['type'], $item['name'], $item['filepath']);
    -  }
    

    What happened with the system_register call?

  3. +++ b/core/includes/module.inc
    @@ -22,43 +23,17 @@
     /**
      * Resets all system_list() caches.
      */
     function system_list_reset() {
    -  drupal_static_reset('system_list');
    +  \Drupal::service('extension.list.profile')->reset();
       \Drupal::service('extension.list.module')->reset();
    -  \Drupal::cache('bootstrap')->delete('system_list');
    +  \Drupal::service('extension.list.theme_engine')->reset();
    +  \Drupal::service('extension.list.theme')->reset();
     }
     
    

    This reads quite nice now.

  4. +++ b/core/lib/Drupal/Core/Extension/ExtensionList.php
    @@ -10,6 +10,12 @@
    + * @internal
    + *   This class is not yet stable and therefore there are no guarantees that the
    + *   internal implementations including constructor signature and protected
    + *   properties / methods will not change over time. This will be reviewed after
    + *   https://www.drupal.org/project/drupal/issues/2940481
      */
    

    👍 This makes this easy to understand what is going on.

  5. +++ b/core/lib/Drupal/Core/Extension/ExtensionList.php
    @@ -257,6 +263,26 @@ public function get($extension_name) {
    +   * Returns a list of installed extensions.
    +   *
    +   * @return \Drupal\Core\Extension\Extension[]
    +   *   A list of installed extension objects, keyed by machine name.
    +   */
    +  public function getInstalled() {
    +    return array_intersect_key($this->getList(), array_flip($this->getInstalledExtensionNames()));
    +  }
    +
    +  /**
    +   * Returns a list of uninstalled extensions.
    +   *
    +   * @return \Drupal\Core\Extension\Extension[]
    +   *   A list of uninstalled extension objects, keyed by machine name.
    +   */
    +  public function getUninstalled() {
    +    return array_diff_key($this->getList(), array_flip($this->getInstalledExtensionNames()));
    +  }
    

    What means installed/uninstalled in the context of profiles/theme engines?

  6. +++ b/core/lib/Drupal/Core/Extension/ThemeEngineExtensionList.php
    @@ -0,0 +1,34 @@
    +   * {@inheritdoc}
    +   */
    +  protected function getInstalledExtensionNames() {
    +    return ['twig'];
    +  }
    

    Can't you have multiple theme engines? There is probably not a single person using one, but this would break this, wouldn't it?

  7. +++ b/core/lib/Drupal/Core/Extension/ThemeHandler.php
    @@ -122,16 +107,16 @@ class ThemeHandler implements ThemeHandlerInterface {
        *   The info parser to parse the theme.info.yml files.
    -   * @param \Drupal\Core\Extension\ExtensionDiscovery $extension_discovery
    +   * @param \Drupal\Core\Extension\ThemeExtensionList $theme_list
        *   (optional) A extension discovery instance (for unit tests).
        */
    ...
    +    $this->themeList = $theme_list;
    
    @@ -249,186 +208,27 @@ public function reset() {
    -      $this->extensionDiscovery = new ExtensionDiscovery($this->root);
    +  protected function getThemeExtensionList() {
    +    if (!isset($this->themeList)) {
    +      $this->themeList = \Drupal::service('extension.list.theme');
         }
    -    return $this->extensionDiscovery;
    +    return $this->themeList;
       }
    

    ❓Why do we need both?

  8. +++ b/core/lib/Drupal/Core/Extension/ThemeHandler.php
    @@ -249,186 +208,27 @@ public function reset() {
        * {@inheritdoc}
        */
       public function rebuildThemeData() {
    

    Should we mark the interface method deprecated?

  9. +++ b/core/tests/Drupal/KernelTests/Core/Extension/ModuleExtensionListTest.php
    @@ -35,4 +35,49 @@ public function testGetlist() {
    +  /**
    +   * @covers ::getInstalled
    +   * @covers ::getUninstalled
    +   */
    +  public function testInstalledUninstalled() {
    

    👍 Nice!

almaudoh’s picture

StatusFileSize
new1.46 KB
new65.73 KB

#68:
1. Removed unused use. This was left over from earlier version of the patch.
2. The only purpose of this section of code is to statically cache the theme .info.yml file paths for faster lookup. system_register() calls drupal_get_filename() with the theme name and file path (as mentioned by the comment above that code block). Since the new implementation already has the filepaths from ExtensionDiscovery, and is cached internally by ExtensionList, this bit of code is no more needed. The other thing system_register() does is to register the theme in the PSR4 classloader. I don't think this is needed either since all modules and themes are already registered on installation.
3. :)
4. Yes!
5. Well, in the context of install profiles / theme engines, we may consider the possibility of installing / uninstalling them in future. We should leave that possibility open.
6. This would mean creating a new config storage entry for the list of installed theme engines. Will try that out in the next patch.
7. Since it's an optional argument, we have to cover for where it is not provided. I didn't want to break the constructor signature. But leaving it in makes it easier to enforce the dependency injection when we can break BC.
8. My thoughts too. I have marked the interface @deprecated, even though I didn't implement @trigger_error to avoid test fails. I tried that but the patch got too big. I'll open up a separate issue for that.
9. :)

markhalliwell’s picture

Status: Needs review » Needs work

The other thing system_register() does is to register the theme in the PSR4 classloader. I don't think this is needed either since all modules and themes are already registered on installation.

Where are they registered on installation? I cannot find this in any of the *Installer classes.

I'm very worried/cautious about this change. Themes like https://www.drupal.org/project/bootstrap rely heavily on PSR autoloading and, from what I have always been able to determine, this was where theme namespaces were registered. There's even a "fix" for this in the base theme:
https://drupal-bootstrap.org/api/bootstrap/autoload-fix.php/8#source.16

It purposefully invokes \Drupal::service('theme_handler')->listInfo(); to trigger this registration because, during certain circumstances (e.g. AJAX requests), theme namespaces haven't yet been registered (regardless if they were installed).

From past conversations (#2002606: Allow themes to provide services.yml and other IRC/slack convos), I've been told this is primarily due to which theme is "active".

That being said, this has always registered all "installed" themes... not just the "active" ones... so.... idk.

I'm almost tempted to say that theme namespaces are a very vital aspect of this issue and need to be flushed out more first. AFAIK, there are no [extensive] theme namespace tests, so... the current "passing" may be a false positive as it's likely to break contrib.

markhalliwell’s picture

I think maybe you're confusing "registered on installation" with DrupalKernel::compileContainer. This automatically registers module namespaces, because they can provide services (not themes):
http://cgit.drupalcode.org/drupal/tree/core/lib/Drupal/Core/DrupalKernel...

markhalliwell’s picture

Should probably be postponed on this, but leaving status to CNW for now. If you agree, you can change.

almaudoh’s picture

Good observation, @markcarver, I actually haven't encountered this issue of theme class namespacing before. It does tell me that we need to add test coverage for theme classes. Maybe this will provide an opportunity to fix the Bootstrap hack here.

I think maybe you're confusing "registered on installation" with DrupalKernel::discoverServiceProviders. This automatically registers module namespaces, because they can provide services (not themes):

Yes. This is what I thought.

I will take some time to study the issues referenced for some insights. Maybe others can chime in here as well.

almaudoh’s picture

I'm thinking maybe would be better to re-instate the system_register() calls and then fix the whole theme namespacing thing in #2941757: Extension System, Part IV: Properly register all installed extensions/namespaces during container generation. That way we can keep this issue focused, while the other issue will have the extension system in a saner form to work with.

markhalliwell’s picture

Sure, I'm OK with doing that as a follow-up.

markhalliwell’s picture

FTR, I think just reinstating drupal_classloader_register() will be enough.

dawehner’s picture

I think this should not be a follow up. Instead we should do two things:

  • Have a test which adds a theme with namespaces
  • Ensure this test doesn't fail

Maybe this should be done before this issue, but I think we should avoid temporary regressions.

almaudoh’s picture

I think we should avoid temporary regressions.

It wouldn't be a regression if we re-instated the call to system_register() or just directly called drupal_classloader_register().

I'm seeing the theme namespace issue as really out of scope. But, well, we could still add it.

  • Have a test which adds a theme with namespaces
  • Ensure this test doesn't fail

Maybe this should be done before this issue

Doing that before this issue would mean more places where we have to change the old API. We could add the test and make sure it doesn't fail in this issue, while the more elaborate edge cases (e.g. Ajax, multiple themes, admin / default themes, etc.) can be punted to #2941757: Extension System, Part IV: Properly register all installed extensions/namespaces during container generation.

markhalliwell’s picture

I'm seeing the theme namespace issue as really out of scope

It's not, but I suppose considering that there has been very little documented "support" for this, I could see how you would say that.

We could add the test and make sure it doesn't fail in this issue

I think that's what @dawehner was suggesting.

That being said, #1538478: Register lib/ directories of themes as PSR-0 roots for Drupal\$theme namespace supposedly already added such a test:
http://cgit.drupalcode.org/drupal/tree/core/modules/system/src/Tests/The...

Which instantiates a new ThemeClass() object.

I think, however, this is still a false positive since it's not actually testing against core's container namespaces using the extension registration/autoloading, but rather it may just be successfully autoloading due to the nature of directly calling the class in the test itself (i.e. using the test namespacing/autoloader, not core's).

A real test would be to load a page where the theme itself can instantiate one of its own classes from within the *.theme file, like Drupal Bootstrap does. This ensures that the container properly registers the classes within the scope of the request.

almaudoh’s picture

Status: Needs work » Needs review
StatusFileSize
new6.77 KB
new71.1 KB

In this patch I've added a config storage entry for theme engines in core.extension.yml, with a default to 'twig'. That means a module can easily override the active theme engine by writing to that config storage \Drupal::config('core.extension')->set('theme_engine', 'my_theme_engine');. Also added test coverage for that.

Will work on the namespacing issue next.

almaudoh’s picture

StatusFileSize
new2.37 KB
new73.47 KB

Modified ThemeTest based on @markcarver's suggestion to get a failing test. I have to go sleep now. Will work on a fix tomorrow.

The last submitted patch, 80: 2659940-80.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

Status: Needs review » Needs work

The last submitted patch, 81: 2659940-81-fail.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

almaudoh’s picture

Status: Needs work » Needs review
StatusFileSize
new3.85 KB
new73.99 KB

Fixed the failures in #80 and coding standards. Still a fail patch though per #81. Expecting 3 test fails now.

Status: Needs review » Needs work

The last submitted patch, 85: 2659940-85-fail.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

markhalliwell’s picture

+++ b/core/modules/system/src/Tests/Theme/ThemeTest.php
--- a/core/modules/system/tests/modules/theme_test/src/ThemeTestController.php
+++ b/core/modules/system/tests/modules/theme_test/src/ThemeTestController.php

+++ b/core/modules/system/tests/modules/theme_test/src/ThemeTestController.php
@@ -159,4 +160,19 @@ public function preprocessSuggestions() {
+      new ThemeClass();

This still isn't exactly what I meant.

Themes cannot provide controllers/routes. In addition to the theme_test.test_theme_class route in the module, I suggest the content be rendered by the test theme itself. Then ThemeClass should be invoked inside the .theme file in a preprocess hook or something similar.

This would be a "true" baseline test for this IMO because \Drupal::service('theme_handler')->listInfo() is what used to register installed theme namespaces and they wouldn't necessarily be available in just a normal module callback.

almaudoh’s picture

Status: Needs work » Needs review
StatusFileSize
new76.04 KB
new76.81 KB
new1.43 KB
new5.32 KB

Ok. So a "true" baseline test implemented per #87. The ThemeClass is invoked in a .theme file in a preprocess hook. A fail patch is attached with the new test in addition to a patch that fixes it based on the existing implementation in HEAD. I moved drupal_classloader_register() into the ThemeExtensionList post-discovery decorations section with a todo to fix it better in #2941757: Extension System, Part IV: Properly register all installed extensions/namespaces during container generation.

Interdiff is against #85. Actual fix is in fix.txt

The last submitted patch, 88: 2659940-88-fail.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

markhalliwell’s picture

Status: Needs review » Needs work
+++ b/core/lib/Drupal/Core/Extension/ThemeExtensionList.php
@@ -179,6 +184,24 @@ protected function fillInSubThemeData(array &$themes, array $sub_themes) {
+  protected function registerThemeNamespaces(array $themes) {

I'd say let's mark this as @internal and/or @deprecated since we know this is likely going away when the other issue is fixed.

Other than that... I'd say this looks good!

It's also great to know that the existing test was definitely a false positive :D

almaudoh’s picture

Status: Needs work » Needs review

I'd say let's mark this as @internal and/or @deprecated since we know this is likely going away when the other issue is fixed.

I actually thought about doing that for a moment, then remembered that the whole class is already marked @internal, so that should cover the method as well. Right?

It's also great to know that the existing test was definitely a false positive :D

Yeah. :) Great find there. This will be a good opportunity to fix it for themers.

markhalliwell’s picture

Status: Needs review » Needs work

I actually thought about doing that for a moment, then remembered that the whole class is already marked @internal

No, the class is only temporarily marked as @internal until #2940481: Road to stabilize ExtensionList services; at which point that label will be removed.

Ideally, the registerThemeNamespaces() method will be removed entirely by #2941757: Extension System, Part IV: Properly register all installed extensions/namespaces during container generation. However, in the off chance that it doesn't, I think this should still be marked as such now (since we already have plans for this) with @internal and/or @deprecated. If it doesn't, it can then just stay as an empty method that does nothing (since the other issue would take care of this at the container level).

almaudoh’s picture

Status: Needs work » Needs review
StatusFileSize
new862 bytes
new76.87 KB

Ok. Makes sense.

This patch still needs profiling. Anybody up for it ;) ?

markhalliwell’s picture

Status: Needs review » Needs work
  1. +++ b/core/lib/Drupal/Core/Extension/ThemeEngineExtensionList.php
    @@ -0,0 +1,101 @@
    +    $this->themeEngine = $this->configFactory->get('core.extension')->get('theme_engine') ?: 'twig';
    

    There is no need for a config entry like this. There is no "default" engine per se. The theme engine used is determined by the theme itself, which automatically defaults to "twig" if not defined.

  2. +++ b/core/lib/Drupal/Core/Extension/ThemeEngineExtensionList.php
    @@ -0,0 +1,101 @@
    +    return [$this->getThemeEngine()];
    

    This still isn't right. It's still only returning a single theme engine.

    There has always been the ability to have multiple theme engines installed (https://www.drupal.org/project/project_theme_engine).

    The one that is actually used is determined by the active theme during rendering.

    E.g. the admin theme may use a different engine than the default theme.

    edit: I've even seen some cases where certain AJAX requests were rendered using a different theme engine.

    One can still even use PHPTemplate (although I don't know why they would): https://cgit.drupalcode.org/phptemplate/tree/phptemplate.engine

    This should return a list of all available theme engines (since they don't actually have an "install" state).

  3. +++ b/core/lib/Drupal/Core/Extension/ThemeHandler.php
    @@ -249,186 +208,27 @@ public function reset() {
    -    $this->state->set('system.theme_engine.files', $files_theme_engine);
    

    ThemeHandler::rebuildThemeData is where theme engines where originally stored. Since they don't have an "install" state, they're all "available" and were just returned as needed.

almaudoh’s picture

There is no need for a config entry like this. There is no "default" engine per se.

This config entry is the same convention that has been used for storing installed modules and themes, it would only make sense to use this for theme engines as well.

There has always been the ability to have multiple theme engines installed (https://www.drupal.org/project/project_theme_engine).

The one that is actually used is determined by the active theme during rendering.

So the question is, what does the concept of an "installed" theme engine really mean? This is what @dawehner asked in #68.5. So do we want to hold up this patch trying to figure that out?

ThemeHandler::rebuildThemeData is where theme engines where originally stored. Since they don't have an "install" state, they're all "available" and were just returned as needed.

The 'system.theme_engine.files' state entry is actually already built into the extension list system (whether theme, profile, module or theme engine). So if we're going with this, then we just return the list of all available theme engines.

dawehner’s picture

There is no need for a config entry like this. There is no "default" engine per se. The theme engine used is determined by the theme itself, which automatically defaults to "twig" if not defined.

I agree with @markcarver, we should not introduce a new concept as part of this issue. I also believe that installing a theme extension doesn't make necessarily even sense, just as pointed out like @markcarver

markhalliwell’s picture

So the question is, what does the concept of an "installed" theme engine really mean?

For ThemeEngineExtensionList, it just means that it should return all discovered theme engines. They have no "install" state.

we should not introduce a new concept as part of this issue

That's actually what #1685492: Convert theme engines into services is attempting to solve. Since that issue is likely the "end goal" with theme engines, it would ultimately make ThemeEngineExtensionList moot since they would no longer be an "extension" as any module would be able to define a theme engine service.

For now, and in anticipation of that issue, I think we should keep ThemeEngineExtensionList simply for furture BC reasons.

almaudoh’s picture

Status: Needs work » Needs review
StatusFileSize
new73.55 KB
new5.93 KB

In this patch I've reverted the config entry that was added in #80 for theme engines. Now ThemeEngineList::getInstalled() will return all discovered theme engines. I have also updated the test coverage for that.

Status: Needs review » Needs work

The last submitted patch, 98: 2659940-98.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

almaudoh’s picture

Status: Needs work » Needs review
StatusFileSize
new514 bytes
new73.05 KB

Forgot to revert the DefaultConfigTest.

jibran’s picture

Glad to see this green. Thanks, for all the work @almaudoh. Hopefully, all the feedback has been addressed. Let's get to some profiling.

markhalliwell’s picture

I'd do the profiling, but not currently set up to do so (nor very familiar with how to, if I'm being honest).

zviryatko’s picture

Issue tags: -needs profiling

Profiled patch added in #100
tldr: ~2% performance decrease for everything except the memory.

Cold cache:

Time+553 µs (+0.02%)
2.71 s → 2.71 s

I/O Wait+55.5 ms (+3.61%)
1.54 s → 1.6 s

CPU Time-55 ms (-4.7%)
1.17 s → 1.12 s

Memory-109 kB (-0.488%)
22.2 MB → 22.1 MB
SQL Queries+56.9 ms / +13 rq

Blackfire:
- without patch and aggregation:
https://blackfire.io/profiles/c6bfa8c9-8834-41d2-a766-57fc96e646d2/graph
- with patch and without aggregation:
https://blackfire.io/profiles/957ba241-e66b-48d7-a6a9-acf25582763b/graph
- compare:
https://blackfire.io/profiles/compare/d4bea6a7-3035-4ca5-a562-23d568f779...

Warm cache:

Time+4.41 ms (+1.61%)
274 ms → 279 ms

I/O Wait+1.97 ms (+1.98%)
99.5 ms → 101 ms

CPU Time+2.44 ms (+1.39%)
175 ms → 177 ms

Memory-26 kB (-0.229%)
11.5 MB → 11.5 MB

Blackfire:
- without patch and with aggregation:
https://blackfire.io/profiles/607b82b5-f77e-4962-8f00-589cccba95d2/graph
- with patch and aggregation:
https://blackfire.io/profiles/5ecae1a8-f70b-440d-996f-37c5b86e671f/graph
- compare:
https://blackfire.io/profiles/compare/f9b21477-65a5-4fb2-8159-776bcbc650...

jibran’s picture

Thanks, this doesn't look too bad to me. Let's get some opinions from core committers.

dawehner’s picture

@zviryatko Nice work!
I was looking at https://blackfire.io/profiles/compare/f9b21477-65a5-4fb2-8159-776bcbc650...() and I'm wondering. Have the original request been really warm? state set shouldn't happen on normal requests. Any idea why they are there?

zviryatko’s picture

@dawehner, check \Drupal\update\UpdateManager::projectStorage() it removes state for listed routes.

Update: added list of routes from mentioned function:

    $route_names = [
      'update.theme_update',
      'system.modules_list',
      'system.theme_install',
      'update.module_update',
      'update.module_install',
      'update.status',
      'update.report_update',
      'update.report_install',
      'update.settings',
      'system.status',
      'update.manual_status',
      'update.confirmation_page',
      'system.themes_page',
    ];
    if (in_array(\Drupal::routeMatch()->getRouteName(), $route_names)) {
      $this->keyValueStore->delete($key);
    }
markhalliwell’s picture

Went ahead and re-queued the last patch (just to double check).

Other than that, I'd say that this is RTBC.

markhalliwell’s picture

Status: Needs review » Reviewed & tested by the community

Ok. I'll do the honors...

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 100: 2659940-100.patch, failed testing. View results

markhalliwell’s picture

Status: Needs work » Reviewed & tested by the community
StatusFileSize
new81.5 KB

Re-roll, but should still probably RTBC

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 110: 2659940-110.patch, failed testing. View results

markhalliwell’s picture

Status: Needs work » Reviewed & tested by the community
StatusFileSize
new76.04 KB

Hm... seems I was behind 1 commit from HEAD somehow, whoops.

alexpott’s picture

Status: Reviewed & tested by the community » Needs work
Issue tags: +Needs change record

I've not done a deep dive review yet just a couple of quick thoughts that jump out.

+++ b/core/lib/Drupal/Core/Extension/ThemeHandlerInterface.php
@@ -119,6 +119,11 @@ public function reset();
+   * @deprecated in Drupal 8.6.0 and will be removed before Drupal 9.0.0.
+   *   Use \Drupal::service('extension.list.theme')->reset() (if you actually
+   *   need to rebuild) and \Drupal::service('extension.list.theme')->getList()
+   *   instead.
    */
   public function rebuildThemeData();

@@ -136,6 +141,9 @@ public function rebuildThemeData();
+   * @deprecated in Drupal 8.6.0 and will be removed before Drupal 9.0.0.
+   *   Use \Drupal::service('extension.list.theme')->getBaseThemes() instead.
    */
   public function getBaseThemes(array $themes, $theme);

As per the policy we should not add deprecations unless we also do an @trigger_error() to the code. In this case I would suggest filing followups to cope with each deprecation separately. See https://www.drupal.org/core/deprecation

There also needs to be a change record to detail the introduce of the new extension lists - ThemeExtensionList and ThemeEngineExtensionList

markhalliwell’s picture

Assigned: Unassigned » markhalliwell
Issue tags: -Needs change record

In this case I would suggest filing followups to cope with each deprecation separately.

Agreed. I think it can be a single issue though as it should a relatively small patch. I'll fix this shortly.

There also needs to be a change record to detail the introduce of the new extension lists

There is: https://www.drupal.org/node/2709919

markhalliwell’s picture

markhalliwell’s picture

Assigned: markhalliwell » Unassigned

I went ahead and updated the CR as well: https://www.drupal.org/node/2709919

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 115: 2659940-115.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

daffie’s picture

Status: Needs work » Reviewed & tested by the community

Back to RTBC.

larowlan’s picture

Status: Reviewed & tested by the community » Needs review
  1. +++ b/core/includes/module.inc
    @@ -22,43 +22,17 @@
     function system_list($type) {
    ...
    +  return \Drupal::service('extension.list.' . $type)->getInstalled();
    

    Should we catch when $type yields a 'service not found exception, i.e. is of a type we don't support and trigger and error?

    At present it would cause an uncaught exception.

  2. +++ b/core/includes/module.inc
    @@ -22,43 +22,17 @@
    +  \Drupal::service('extension.list.profile')->reset();
       \Drupal::service('extension.list.module')->reset();
    ...
    +  \Drupal::service('extension.list.theme_engine')->reset();
    +  \Drupal::service('extension.list.theme')->reset();
    

    Starting to feel like these might need to be tagged services with a collector to clear them all in one go? That would be the path to deprecating system_list_reset right? Follow up?

  3. +++ b/core/lib/Drupal/Core/Extension/ThemeExtensionList.php
    @@ -0,0 +1,300 @@
    +      if (isset($engines[$engine])) {
    

    what happens if the engine doesn't exist?

  4. +++ b/core/lib/Drupal/Core/Extension/ThemeExtensionList.php
    @@ -0,0 +1,300 @@
    +   * @internal
    ...
    +  protected function registerThemeNamespaces(array $themes) {
    

    Would that be a case to declare this as private and prevent subclassing or calling from children?

  5. +++ b/core/modules/system/tests/themes/test_theme/test_theme.theme
    @@ -154,3 +156,16 @@ function test_theme_preprocess_theme_test_preprocess_suggestions__kitten__flamin
    +    new ThemeClass();
    ...
    +  catch (\Exception $exception) {
    

    Are we sure this would throw an Exception? In PHP 7 it would be an \Error right? In PHP 5 it would be a fatal, not an exception right?

    Should we instead be using class_exists?

  6. +++ b/core/tests/Drupal/Tests/Core/Extension/ThemeExtensionListTest.php
    @@ -0,0 +1,263 @@
    +  define('DRUPAL_MINIMUM_PHP', '5.3.10');
    

    I know this is test only, but shouldn't this be 5.5.9?

markhalliwell’s picture

StatusFileSize
new5.9 KB
new76.2 KB
  1. Probably. Same should probably happen in drupal_get_filename() as well.
  2. Yes, follow-up.
  3. It defaults to twig, as per the comment above it: // Defaults to 'twig' (see self::defaults above).
  4. Considering it's already marked as @internal and will be removed in #2941757: Extension System, Part IV: Properly register all installed extensions/namespaces during container generation, yes, that makes sense.
  5. I really don't think it matters, but I'll change it. The test checks if $variables['message'] = 'Loading ThemeClass was successful.', which only happens after the class is constructed. If it errors, it doesn't get that value and the test would fail, yes?
  6. Yep
alexpott’s picture

Issue tags: +needs profiling

Given that we're changing low-level extension system stuff in this issue we should also do some profiling to make sure we're not removing some static that the installer or the cold cache rebuild relies on to be performant.

#103 only does the cold cache. In the previous issue we found profiling the install also important.

alexpott’s picture

Also looking at https://blackfire.io/profiles/compare/d4bea6a7-3035-4ca5-a562-23d568f779...() I'm not sure we've profiled what we think we're profiling. Since I would expect some of the new functions to be listed. That said this might be blackfire because on the free version it does not list all methods called. I miss XHProf.

markhalliwell’s picture

make sure we're not removing some static that the installer or the cold cache rebuild relies on to be performant

The only statics we're actually removing are in system_list() and drupal_get_filename(). Now that they're using services and once all the extensions are found, it's cached.

We could keep the statics in both of these functions for performance/BC reasons if you want? This could help mitigate any currently unforeseen performance impacts until we actually remove usages throughout core, when #2940190: [meta] Deprecations of old functions in Extension system and sub-issues are dealt with.

That being said, I seriously doubt any of this will have any negative impact moving forward.

Also, we could just XHProf PHP 5.6, since PHP 7 is likely to not really be an issue here.

In the previous issue we found profiling the install also important.

What previous issue? #2208429: Extension System, Part III: ExtensionList, ModuleExtensionList and ProfileExtensionList didn't mention profiling around the installation.

alexpott’s picture

markhalliwell’s picture

Well, considering that you already optimized ExtensionList to take into account of this scenario, which this extends from, I don't think there's much more we can do here (aside from what I mentioned in #123).

markhalliwell’s picture

Adding this tag merely for historical purposes (as this issue is ultimately the first step in this journey).

dawehner’s picture

  1. +++ b/core/includes/bootstrap.inc
    @@ -235,45 +232,30 @@ function drupal_get_filename($type, $name, $filename = NULL) {
    +    trigger_error(new FormattableMarkup('Unknown type specified: "@type". Must be one of: "core", "profile", "module", "theme", or "theme_engine".', [
    +      '@type' => $type,
    +    ]), E_USER_WARNING);
    +  }
    +  catch (\InvalidArgumentException $e) {
    +    // Catch the exception. This will result in triggering an error.
    +    // If the filename is still unknown, create a user-level error message.
    +    trigger_error(new FormattableMarkup('The following @type is missing from the file system: @name', [
    +      '@type' => $type,
    +      '@name' => $name
    +    ]), E_USER_WARNING);
    

    +1 for making the errors way more helpful!

  2. +++ b/core/lib/Drupal/Core/Extension/ThemeEngineExtensionList.php
    @@ -0,0 +1,36 @@
    +  protected $defaults = [
    +    'dependencies' => [],
    +    'description' => '',
    +    'package' => 'Other',
    +    'version' => NULL,
    +    'php' => DRUPAL_MINIMUM_PHP,
    +  ];
    

    As a follow up we could explore to move these to the base class. These seemed to be shared between implementations

Starting to feel like these might need to be tagged services with a collector to clear them all in one go? That would be the path to deprecating system_list_reset right? Follow up?

Sure, there we go: #2973439: Make extension list services tagged services

markhalliwell’s picture

@alexpott, could you respond to #125 please? I don't think this needs more profiling for the install scenario you mentioned.

alexpott’s picture

So here's a comparison of installing minimal via drush with and without the patch. https://blackfire.io/profiles/compare/1de227a7-af08-4780-a6b3-93a9ea8ca8...

Interestingly it seems we have additional container builds that we did not have before. And we're calling drupal_get_filename more too. And doing more queries. We need to work out why.

+++ b/core/includes/bootstrap.inc
@@ -235,45 +232,30 @@ function drupal_get_filename($type, $name, $filename = NULL) {
+    trigger_error(new FormattableMarkup('Unknown type specified: "@type". Must be one of: "core", "profile", "module", "theme", or "theme_engine".', [
...
+    trigger_error(new FormattableMarkup('The following @type is missing from the file system: @name', [

+++ b/core/includes/module.inc
@@ -22,43 +24,27 @@
+    trigger_error(new FormattableMarkup('Unknown type specified: "@type". Must be one of: "core", "profile", "module", "theme", or "theme_engine".', [

We actually shouldn't be using FormattableMarkup in errors. We should be using sprintf() or just combining the strings. All error messages are escaped anyways. Yep this was already an issue but we're changing it here so let's fix it.

alexpott’s picture

It got the profile by doing the following:

  1. Setting up https://blackfire.io/ as per their docs
  2. Doing composer require blackfire/php-sdk so I can add instrumentation to our code.
  3. Adding
      $blackfire = new \Blackfire\Client();
      $config = new \Blackfire\Profile\Configuration();
      $probe = $blackfire->createProbe($config);
    

    to the top of the install_drupal() function

  4. Adding $profile = $blackfire->endProbe($probe); to the end of the function
  5. Using drush to install the minimal profile.
alexpott’s picture

The problem grows are bit with a standard install... https://blackfire.io/profiles/compare/ad7e32c1-41a0-4ffb-81f4-e144f0a968...()

alexpott’s picture

+++ b/core/lib/Drupal/Core/Extension/ThemeHandler.php
@@ -179,17 +164,7 @@ public function uninstall(array $theme_list) {
-      $themes = $this->systemThemeList();
-      // @todo Ensure that systemThemeList() does not contain an empty list
-      //   during the batch installer, see https://www.drupal.org/node/2322619.
-      if (empty($themes)) {
-        $this->refreshInfo();
-        $this->list = $this->list ?: [];
-        $themes = \Drupal::state()->get('system.theme.data', []);
-      }
-      foreach ($themes as $theme) {
-        $this->addTheme($theme);
-      }
+      array_map([$this, 'addTheme'], $this->getThemeExtensionList()->reset()->getInstalled());

@@ -442,23 +242,6 @@ public function getName($theme) {
-  /**
-   * Wraps system_list().
-   *
-   * @return array
-   *   A list of themes keyed by name.
-   */
-  protected function systemThemeList() {
-    return system_list('theme');
-  }

The reset() here looks really wrong. We were not doing a system list reset here before. How come this was added?

alexpott’s picture

StatusFileSize
new7.69 KB
new79.23 KB

So here's a start on fixing the problem with the increased number of queries and activity during install plus it fixes a couple of coding standards minor things.

We use the same technique as the module list to do static caching during the install process and I've also fixed #132.

Here are the comparisons:
Head to #133 - https://blackfire.io/profiles/compare/11be1ed0-74ea-4227-835e-1d900defcd... - still 52 more queries.
#120 to #133 - https://blackfire.io/profiles/compare/748235c8-3021-4756-a962-2888ab0cbf... - we've lost 139 queries - so we're heading the right way :)

alexpott’s picture

#133 is going to fail badly. One thing that sticks out now looking at this issue more carefully is what's the point of the ThemeHandler?

alexpott’s picture

One reason this change is having an effect on the install is because we do:

  // Prepare for themed output. We need to run this at the beginning of the
  // page request to avoid a different theme accidentally getting set. (We also
  // need to run it even in the case of command-line installations, to prevent
  // any code in the installer that happens to initialize the theme system from
  // accessing the database before it is set up yet.)
  drupal_maintenance_theme();

in install_begin_request() - we're alway initialising the theme layer during the installer to work around things :(

Status: Needs review » Needs work

The last submitted patch, 133: 2659940-133.patch, failed testing. View results

alexpott’s picture

Status: Needs work » Needs review
StatusFileSize
new3.16 KB
new79.75 KB
+++ b/core/lib/Drupal/Core/Extension/ThemeHandler.php
@@ -216,32 +191,16 @@ public function addTheme(Extension $theme) {
-    $extension_config = $this->configFactory->get('core.extension');
-    $installed = $extension_config->get('theme');
-    // Only refresh the info if a theme has been installed. Modules are
-    // installed before themes by the installer and this method is called during
-    // module installation.
-    if (empty($installed) && empty($this->list)) {
-      return;
-    }

Removing this isn't great - that was added in #2872611: Optimise \Drupal\Core\Extension\ThemeHandler::refreshInfo() for the early installer. We need to ensure we maintain this.

+++ b/core/lib/Drupal/Core/Extension/ThemeExtensionList.php
@@ -0,0 +1,300 @@
+  /**
+   * The list of installed themes.
+   *
+   * @var string[]
+   */
+  protected $installedThemes = [];
...
+    // Cache the installed themes to avoid multiple calls to the config system.
+    if (!isset($this->installedThemes)) {
+      $this->installedThemes = $this->configFactory->get('core.extension')->get('theme') ?: [];
+    }

Checking isset() and initialising to an empty array is flawed. And the reason for all the failures in the previous patch.

https://blackfire.io/profiles/compare/455a3ab8-52f1-4c70-9bb7-ed9f142a29... - closing in only 38 more queries than HEAD. Still lots more container work.

alexpott’s picture

StatusFileSize
new1.62 KB
new79.24 KB

Okay with this patch we're now at less queries than HEAD! https://blackfire.io/profiles/compare/381a44ab-efe2-4dd1-b868-3627879385...

The last submitted patch, 137: 2659940-137.patch, failed testing. View results

Status: Needs review » Needs work

The last submitted patch, 138: 2659940-138.patch, failed testing. View results

alexpott’s picture

Status: Needs work » Needs review
StatusFileSize
new2.16 KB
new78.88 KB

Fixing the last error. This change makes no difference to the callgraph of minimal install.

markhalliwell’s picture

Status: Needs review » Needs work
Issue tags: -needs profiling

Oh wow! I was just expecting a yes/no :D Glad you took that on though because profiling isn't really my strong suit (especially without XHprof).

I just couldn't see how this could have been further optimized, I was wrong. My apologies.

I've reviewed the interdiffs and gave the overall patch a good once over again. Only thing that really jumpped out at me was:

+++ b/core/lib/Drupal/Core/Installer/ExtensionListTrait.php
@@ -2,12 +2,10 @@
+ * @todo

Setting to CNW to fix this, but other than that I think this could be RTBC! Woo hoo!

@alexpott++

Anonymous’s picture

StatusFileSize
new27.85 KB

Interestingly, #141 is 14 minutes slower than #138:

  • #138: Test run duration: 38 min 31 sec
  • #141: Test run duration: 52 min 36 sec

Graph with discrepancy in speed test runs:

alexpott’s picture

@vaplas we need further samples for that :) so much variation on bot runs. I've kicked off some more test runs.

alexpott’s picture

StatusFileSize
new7.31 KB
new81.31 KB

For me there is still quite a bit of work to do here. One of the big things is should the ThemeHandler be the theme extension list service. I'm not sure that the decoupling here is worth it.

Looking at the existing ThemeHandler:

  • install() deprecated
  • uninstall() deprecated
  • listInfo - this is a listing function
  • addTheme - this adds a theme to the list
  • refreshInfo - resetting the list
  • reset - resetting the list
  • rebuildThemeData - rebuilding data used for the list
  • getBaseThemes - gets a special list
  • getName - gets info from the list
  • getDefault - gets the default theme
  • setDefault - sets the default theme
  • themeExists - checks if a theme is in the list
  • getTheme - gets a theme from the list
  • hasUi - checks if the theme should be listed in the UI.

So there does seem to be some differentiation in terms of getDefault / setDefault / hasUi - but everything else is really on the extension list. Having the ThemeHandler and the theme extension list would mean that we can only have one static list cache and not both \Drupal\Core\Extension\ThemeHandler::$list and \Drupal\Core\Extension\ExtensionList::$extensions which is wasteful and too many layers of caching. Also I would argue that things should just read from config to get and set the default theme so those methods and the config factory dependency shouldn't really be on the ThemeHandler.

Before I start work on merging here's a patch the properly guts themehandler and calls the theme list were possible.

alexpott’s picture

Status: Needs work » Needs review

So #145 is doing a bit less container work than #138 but is doing more YAML parsing which is odd. https://blackfire.io/profiles/compare/7abcdc68-2a88-4abd-a4bf-6ec75d9ee7...

markhalliwell’s picture

I agree that ThemeHandler is basically useless now, just like ThemeManager will be once #2869859: [PP-1] Refactor theme hooks/registry into plugin managers happens.

Both ThemeHandler and ThemeManager will ultimately become artifacts because when they were created it was out of immediate necessity to migrate procedural code into OO code. It really didn't have a lot of architectural thought behind it (like much of the theme system's "OO conversion").

Gutting/deprecating ThemeHandler should probably be a separate follow-up issue IMO.

I'd like to see if we could possibly move the default getter/setter and hasUI methods elsewhere and get rid of the entire class.

At the very least, I think we should probably do it as a follow-up just to make sure we don't step on any toes.

The last submitted patch, 138: 2659940-138.patch, failed testing. View results

Status: Needs review » Needs work

The last submitted patch, 145: 2659940-145.patch, failed testing. View results

almaudoh’s picture

Awesome work by @alexpott to profile and improve the patch performance. I always knew the Extension system would bring performance benefits, just didn't know where to optimise.

I'll have to find time and learn a thing or two about profiling with blackfire.io soon.

   public function refreshInfo() {
+    $extension_config = $this->configFactory->get('core.extension');
+    $installed = $extension_config->get('theme');
+    // Only refresh the info if a theme has been installed. Modules are
+    // installed before themes by the installer and this method is called during
+    // module installation.
+    if (empty($installed) && empty($this->extensions)) {
+      return;
+    }
     $this->reset();

I don't think $this->extensions exists in the ThemeHandler class. Maybe should be $this->list as it was before.

One thing that sticks out now looking at this issue more carefully is what's the point of the ThemeHandler?

I agree that ThemeHandler is basically useless now,

I had these same ideas myself. Maybe we could leave the ThemeHandler in just for BC and then remove it in a follow-up issue.

alexpott’s picture

Status: Needs work » Needs review
StatusFileSize
new2.09 KB
new81.57 KB

Fixing the tests and #150 - nice spot @almaudoh. We still need to understand the extra info parsing this patch causes.

alexpott’s picture

I talked to @dawehner yesterday and he pointed out that sun's original plan had extension discovery and handling as separate things. The current patch makes extension discovery aware of installation status so it's mixing responsibilities. I've rolled a patch locally that combines ThemeHandler and ThemeExtensionList which completes the mixing of responsibilities. Thinking some more I don't think this is a good way to go. I think the ThemeHandler should handle installed themes etc and the ThemeExtensionList should handle the listing.

alexpott’s picture

So in light of #152 we already have ModuleHandler and ModuleExtensionList well and truly entangled. And the extension listing stuff already has the concept of installation status :( ho hum but let's try not to make it worse in this issue.

  1. +++ b/core/includes/module.inc
    @@ -22,43 +24,27 @@
     function system_list($type) {
    

    In HEAD this function only works for themes it doesn't work for modules or anything. With this patch we change it to work for every type again. That's not a good idea. We also break it for the filepaths type. Not that that is documented. Imo we shouldn't change what this method can return.

  2. +++ b/core/lib/Drupal/Core/Extension/ExtensionList.php
    @@ -256,6 +262,26 @@ public function get($extension_name) {
    +  /**
    +   * Returns a list of installed extensions.
    +   *
    +   * @return \Drupal\Core\Extension\Extension[]
    +   *   A list of installed extension objects, keyed by machine name.
    +   */
    +  public function getInstalled() {
    +    return array_intersect_key($this->getList(), array_flip($this->getInstalledExtensionNames()));
    +  }
    +
    +  /**
    +   * Returns a list of uninstalled extensions.
    +   *
    +   * @return \Drupal\Core\Extension\Extension[]
    +   *   A list of uninstalled extension objects, keyed by machine name.
    +   */
    +  public function getUninstalled() {
    +    return array_diff_key($this->getList(), array_flip($this->getInstalledExtensionNames()));
    +  }
    

    Let's not add these methods we don't need to. system_list() is dead code and we can do the logic that's need in ThemeHandler.

  3. +++ b/core/lib/Drupal/Core/Extension/ExtensionList.php
    @@ -314,6 +340,9 @@ protected function doList() {
    +      // Add extension-type-specific information.
    +      $this->alterExtensionInfo($extension);
    +
           // Invoke hook_system_info_alter() to give installed modules a chance to
           // modify the data in the .info.yml files if necessary.
           $this->moduleHandler->alter('system_info', $extension->info, $extension, $this->type);
    
    @@ -540,4 +569,12 @@ public function getPath($extension_name) {
    +  protected function alterExtensionInfo(Extension $extension) {}
    

    I'm not sure we should call this alter... and then trigger a proper alter next in the code. That's confusing.

alexpott’s picture

StatusFileSize
new15.21 KB
new74.35 KB

Here's a start on #153.

alexpott’s picture

StatusFileSize
new7.98 KB
new78.18 KB

Here's a proper deprecation of system_list(). I think because this only lists themes we need to do this here - also we need to ensure we don't change it's behaviour.

The last submitted patch, 154: 2659940-154.patch, failed testing. View results

Status: Needs review » Needs work

The last submitted patch, 155: 2659940-155.patch, failed testing. View results

markhalliwell’s picture

I'm confused, you said in #113 that [triggering] deprecations should be done in a follow-up issue (so we can remove code that uses it and not trigger errors). That's why I created the follow-up issues.

alexpott’s picture

Status: Needs work » Needs review
StatusFileSize
new2.01 KB
new78.72 KB

Fixing the test.

Re #158 - the only use of system_list() in HEAD is to list themes. This patch makes fundamental changes to it and in earlier version unnecessarily expanded system_lists()'s capabilities. In reviewing all the code it makes it obvious that with this patch core has no reason to call system_list() anymore. Therefore this patch should take care to deprecate it. Especially as this patch extended the public API of the extension listing system to support system_list() - which doesn't make sense to me. It hard to explain to someone the difference between ExtensionList::getInstalled() and ExtensionList::getAllInstalledInfo() but now we don't have to because ExtensionList::getInstalled() is no longer being added.

Anonymous’s picture

#144: you are right. The subsidence appears only when all the tests are run. I opened a separate issue for it #2975163: InstallUninstallTest is taking 20 minutes to run.

alexpott’s picture

@vaplas does this patch make InstallUninstallTest worse?

Anonymous’s picture

TL;DR: Yes, but perhaps it is beyond the scope of the current issue :)

Not (or nit worse), if run only InstallUninstallTest.

  • InstallUninstallTest without all tests: 827 seconds
  • InstallUninstallTest + #141 without all tests: 835 seconds

Yes if run all tests - yes:

  • InstallUninstallTest + all tests: 1199 seconds
  • InstallUninstallTest + #141 + all tests: 1943 seconds

And many other tests run more slowly:

Test Name Patch #141 (maybe also #151, #159) HEAD
InstallUninstallTest 1943 1199
StyleSerializerTest 1229 539
UpdatePathTestBaseFilledTest 562 430
ConfigTranslationUiTest 549 502
UpdateCoreTest 536 246

In HEAD only 5 tests run > 300 seconds:

    [Drupal\Tests\system\Functional\Module\InstallUninstallTest] => 1199
    [Drupal\Tests\rest\Functional\Views\StyleSerializerTest] => 539
    [Drupal\Tests\config_translation\Functional\ConfigTranslationUiTest] => 502
    [Drupal\Tests\system\Functional\Update\UpdatePathTestBaseFilledTest] => 430
    [Drupal\Tests\media\Functional\MediaUiFunctionalTest] => 334
    [Drupal\Tests\workspace\Kernel\WorkspaceIntegrationTest] => 321

After #141 - 31 tests:

    [Drupal\Tests\system\Functional\Module\InstallUninstallTest] => 1943
    [Drupal\Tests\rest\Functional\Views\StyleSerializerTest] => 1229
    [Drupal\Tests\system\Functional\Update\UpdatePathTestBaseFilledTest] => 562
    [Drupal\Tests\config_translation\Functional\ConfigTranslationUiTest] => 549
    [Drupal\Tests\update\Functional\UpdateCoreTest] => 536
    [Drupal\Tests\shortcut\Functional\ShortcutLinksTest] => 531
    [Drupal\Tests\toolbar\Functional\ToolbarAdminMenuTest] => 492
    [Drupal\Tests\shortcut\Functional\ShortcutSetsTest] => 418
    [Drupal\system\Tests\Theme\ThemeTest] => 418
    [Drupal\FunctionalTests\Update\UpdatePathTestBaseTest] => 413
    [Drupal\Tests\media\Functional\MediaUiFunctionalTest] => 409
    [Drupal\Tests\block\Functional\Update\BlockContextMappingUpdateFilledTest] => 402
    [Drupal\Tests\block_content\Functional\Update\BlockContentUpdateTest] => 400
    [Drupal\Tests\system\Functional\Update\UpdatePathRC1TestBaseFilledTest] => 394
    [Drupal\Tests\node\Functional\NodeTranslationUITest] => 389
    [Drupal\Tests\system\Functional\Update\ConfigOverridesUpdateTest] => 379
    [Drupal\Tests\language\Functional\Update\LanguageSelectWidgetUpdateTest] => 366
    [Drupal\Tests\syslog\Functional\Update\SyslogUpdateTest] => 360
    [Drupal\taxonomy\Tests\TermTest] => 352
    [Drupal\Tests\taxonomy\Functional\Rest\TermJsonBasicAuthTest] => 342
    [Drupal\Tests\system\Functional\Update\UpdateActionsWithEntityPluginsTest] => 325
    [Drupal\Tests\system\Functional\Update\MenuTreeSerializationTitleFilledTest] => 324
    [Drupal\field_ui\Tests\ManageFieldsTest] => 323
    [Drupal\Tests\taxonomy\Functional\Rest\TermXmlBasicAuthTest] => 323
    [Drupal\Tests\taxonomy\Functional\Rest\TermJsonCookieTest] => 323
    [Drupal\Tests\system\Functional\Update\UpdateEntityDisplayTest] => 322
    [Drupal\Tests\system\Functional\Update\RouterIndexOptimizationFilledTest] => 320
    [Drupal\Tests\tracker\Functional\TrackerTest] => 314
    [Drupal\Tests\workspace\Kernel\WorkspaceIntegrationTest] => 314
    [Drupal\Tests\system\Functional\Update\UpdatePathWithBrokenRoutingFilledTest] => 303
    [Drupal\Tests\taxonomy\Functional\Rest\TermXmlCookieTest] => 303

I assume that this is due to the APCu fragmentation, because it almost does not affect the WTB tests (they works without APCu) (Edit: My bad, WTB::InstallUninstallTest also much slower, when run all tests). But so far I have not found evidence.

Perhaps related issues:

  1. #2421479: Use a bootstrap.php file to load the most used classes in one go
  2. #1818628: Use Composer's optimized ClassLoader for Core/Component classes
alexpott’s picture

StatusFileSize
new2.54 KB
new79.77 KB

Some more improvements. We now have less queries and less info parsing. Obviously we still have more container stuff because we have another service - https://blackfire.io/profiles/compare/b19f2764-50c2-4241-9168-0746436d22...

Anonymous’s picture

#163 has no problems described in #162 🚀

RTBC++ from this point of view, although I'm not competent in this issue. Are there any unaddressed feedbacks?

Anonymous’s picture

Status: Needs review » Reviewed & tested by the community

No objections, so RTBC 💎

almaudoh’s picture

Obviously we still have more container stuff because we have another service - https://blackfire.io/profiles/compare/b19f2764-50c2-4241-9168-0746436d22...

It sounded like @alexpott still had some things he wanted to do here. But if not, I'm RTBC+ myself.

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 163: 2659940-163.patch, failed testing. View results

markhalliwell’s picture

Issue tags: +Needs reroll
jofitz’s picture

Status: Needs work » Needs review
Issue tags: -Needs reroll
StatusFileSize
new80.7 KB

Re-rolled.

phenaproxima’s picture

Status: Needs review » Reviewed & tested by the community

Re-roll passed Drupal CI, so restoring RTBC.

alexpott’s picture

Status: Reviewed & tested by the community » Needs work
+++ b/core/lib/Drupal/Core/Installer/ExtensionListTrait.php
@@ -0,0 +1,56 @@
+/**
+ * @todo
+ */
+trait ExtensionListTrait {

This needs to be done.

jofitz’s picture

Status: Needs work » Needs review
StatusFileSize
new441 bytes
new80.76 KB

Replaced the @todo.

phenaproxima’s picture

Status: Needs review » Reviewed & tested by the community

Nice. Restoring RTBC once all backends are green.

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 172: 2659940-172.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

jofitz’s picture

Status: Needs work » Needs review
StatusFileSize
new538 bytes
new81.09 KB

Tests are passing again now after a blip over the weekend. Here's a little patch to correct a coding standards error.

jofitz’s picture

Status: Needs review » Reviewed & tested by the community

Seeing as this is such a minor tweak I'm going to cheekily return this to RTBC, safe in the knowledge the tests will pass.

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 175: 2659940-175.patch, failed testing. View results

daffie’s picture

Status: Needs work » Reviewed & tested by the community

Back to RTBC. The testbot fails are not related to this patch.

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 175: 2659940-175.patch, failed testing. View results

jibran’s picture

Status: Needs work » Reviewed & tested by the community

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.

markhalliwell’s picture

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

Can we please get this in 8.6.x so contrib code doesn't have to use special use cases for themes when dealing with the extension system. This has been sitting at RTBC for basically a month.

daffie’s picture

I think that the committers are waiting for 8.7, because it is a large change to a very fundamental part of Drupal's core. If there is some strange side affect as a result of this patch we have 6 months to fix it.

markhalliwell’s picture

No. It just got "overlooked/deprioritized" (like most theme related issues) and was automatically "flagged" as such by the system.

The majority of the ExtensionList code/services was introduced in #2208429: Extension System, Part III: ExtensionList, ModuleExtensionList and ProfileExtensionList (which has already been committed and will be in 8.6.0).

This issue split from that one because it was deemed "significantly large" all on its own.

IMO, that was a mistake, but meh... whatever. Baby steps I guess.

Regardless of that fact, I don't believe this issue should be pushed to 8.7.x simply because it "didn't make the cut" for the initial alpha.

This is a task, not a feature (perhaps that's why it wasn't prioritized... idk).

It should be committed so it flushes out the entire ExtensionList code/services API that was introduced in 8.6.x.

Not doing so would be a huge PITA/oversight for contrib upgrading their code.

phenaproxima’s picture

For whatever it's worth, I entirely agree with @markcarver. This patch seems to be ready and has been for a while. What do we gain by leaving an incomplete API in core? People will just work around it (we Drupalists are a resourceful bunch), those hacks will ossify over time, and we'll just end up introducing even more complexity around the lumbering beast that is the extension system. Why not just land this in 8.6 for API completeness?

dsnopek’s picture

I concur that this would be really valuable to get in sooner rather than later. It is just the last finishing touch on a many-years-long effort to refactor the extension system. While it does touch some pretty low-level code, the changes are much smaller than any of the previous already-committed issues in this refactor. Let's finally finish this!

almaudoh’s picture

It would be really unfortunate if this didn't get into 8.6 because then we'd be having two different API''s for the module/profile extension system and the theme extension system. That would be a huge WTF with additional tech debt we would have to manage later.

almaudoh’s picture

Issue summary: View changes

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 175: 2659940-175.patch, failed testing. View results

daffie’s picture

Issue tags: +Needs reroll
oriol_e9g’s picture

Assigned: Unassigned » oriol_e9g

OK. We want this, go to reroll.

oriol_e9g’s picture

Assigned: oriol_e9g » Unassigned
Status: Needs work » Reviewed & tested by the community
Issue tags: -Needs reroll
StatusFileSize
new81.11 KB

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 192: 2659940-191.patch, failed testing. View results

oriol_e9g’s picture

Status: Needs work » Reviewed & tested by the community
StatusFileSize
new2.06 KB
new82.03 KB

We have moved the theme tests recently and we need to test the testClassLoading as a Functional test.

oriol_e9g’s picture

Only to clarify.

The old testClassLoading() was in core/modules/system/src/Tests/Theme/ThemeTest.php

Today we splited ThemeTest.php into Kernel and Functional tests, and moved the testClassLoading() as a Kernel test here: #2863429: Theme: Convert system functional tests to phpunit

In this patch we have updated this test code and the new version needs to live as a Functional test. I have not changed any piece of code, only moved code around it.

catch’s picture

+++ b/core/lib/Drupal/Core/Extension/ThemeExtensionList.php
@@ -0,0 +1,287 @@
+   */
+  public function __construct($root, $type, CacheBackendInterface $cache, InfoParserInterface $info_parser, ModuleHandlerInterface $module_handler, StateInterface $state, ConfigFactoryInterface $config_factory, ThemeEngineExtensionList $engine_list, $install_profile) {
+    parent::__construct($root, $type, $cache, $info_parser, $module_handler, $state, $install_profile);
+
+    $this->configFactory = $config_factory;
+    $this->engineList = $engine_list;
+  }

This doesn't include the calls to setPathname that ModuleExtensionList does, and we don't have a container.themes extension parameter. Is this OK? Where does the theme location actually come from for each request now?

alexpott’s picture

In each request when \Drupal\Core\Theme\ThemeManager::getActiveTheme() is called we go through theme initialisation. I.e. we then call \Drupal\Core\Theme\ThemeNegotiator::determineActiveTheme() which ends up calling \Drupal\Core\Extension\ThemeHandler::listInfo() which will call \Drupal\Core\Extension\ThemeHandler::addTheme() for all installed themes. This uses the cached extension info to set up the class loader and populate the theme list with the relevant info which includes the theme's pathname.

catch’s picture

Thanks I think that covers #196 fine. It's still slightly odd the way we handle active vs. installed themes but this patch overall will help clarify some of that.

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 194: 2659940-194.patch, failed testing. View results

daffie’s picture

Status: Needs work » Reviewed & tested by the community

Back to RTBC.

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 194: 2659940-194.patch, failed testing. View results

daffie’s picture

Status: Needs work » Reviewed & tested by the community

Back to RTBC.

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 194: 2659940-194.patch, failed testing. View results

daffie’s picture

Status: Needs work » Reviewed & tested by the community

Back to RTBC again.

alexpott’s picture

Hmmm.... the other rtbc's don't seem to be random failing as much as this one. Maybe this is introducing a random?

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 194: 2659940-194.patch, failed testing. View results

markhalliwell’s picture

Or maybe this issue has just been sitting long enough that it keeps having to get re-rolled/tweaked due to the other commits.

alexpott’s picture

@markcarver I'm not sure about that. There are issues that have been rtbc for longer... for example https://www.drupal.org/pift-ci-job/1037332

markhalliwell’s picture

I still think that they're just random test failures due to other code/commits...

I mean from above: https://www.drupal.org/pift-ci-job/1037348

1) Drupal\Tests\media\FunctionalJavascript\MediaSourceOEmbedVideoTest::testOEmbedSecurityWarning
Failed asserting that a NULL is not empty.

This issue has absolutely nothing to do with that lol

This happens (a lot) in other issues too. Just the nature of the new Drupal CI + new JavaScript testing work that's being done.

Whenever these "failures" are manually re-queued for a re-test, everything passes.

markhalliwell’s picture

Status: Needs work » Reviewed & tested by the community

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 194: 2659940-194.patch, failed testing. View results

phenaproxima’s picture

The random failures are happening on this issue so often that I'm starting to wonder if @alexpott hasn't got a point...

oriol_e9g’s picture

Always fails randomly functional javascript tests related with Media or Layout Builder.

I don't know how can be related with this issue :(

ndf’s picture

All failed tests since #199 do contain a failure in Drupal\FunctionalJavascriptTests\Ajax\AjaxTest::testInsertAjaxResponse

The test 1034656 (5 Aug 2018 at 15:55 CEST) has additional failures in Media and Layout_builder tests.

All of the failing test are FunctionalJavascript tests.

Failing AjaxTest
Drupal\FunctionalJavascriptTests\Ajax\AjaxTest::testInsertAjaxResponse tests multiple inserts through Drupal.theme.ajaxWrapperMultipleRootElements and Drupal.theme.ajaxWrapperNewContent

It fails after

  $this->getSession()->executeScript($script);
  $this->clickLink("Link replaceWith $render_type");
  $this->assertWaitPageContains('<div class="ajax-target-wrapper">' . $expected . '</div>');

and

  $page = $this->getSession()->getPage();
  $this->assertTrue($page->waitFor(10, function () use ($page, $expected) {

Failing media test
failing test: https://www.drupal.org/pift-ci-job/1034656
Drupal\Tests\media\FunctionalJavascript\MediaSourceFileTest::testMediaFileSource fails when validating
$assert_session->waitForElementVisible('css', 'fieldset[data-drupal-selector="edit-source-configuration"]')

Failing Layout builder test
failing test: https://www.drupal.org/pift-ci-job/1034656
Drupal\Tests\layout_builder\FunctionalJavascript\AjaxBlockTest::testAddAjaxBlock fails at

$radio->click();
$assert_session->assertWaitOnAjaxRequest();

Don't know why or if this is related with this issue or truly random.
I do see a pattern that the fails are related to waiting on ajax-callbacks.

ndf’s picture

Just re-scheduled all tests and again same failing ajax test in Drupal\FunctionalJavascriptTests\Ajax\AjaxTest::testInsertAjaxResponse

oriol_e9g’s picture

All tests are green and we have launched all tests in 12 environments (see #194). I can not see any error. I'm still thinking that they are randomly javascript tests errors.

berdir’s picture

This is a low-level issue, it affects places in code that are used by basically everything and therefore can affect basically anything else.

Those test fails might be random, but if the random fails in those tests are caused by this patch, then it's still a problem because I haven't seen those tests failing in other issues. Could be a race condition that only exists with the changes done here.

almaudoh’s picture

Those test fails might be random, but if the random fails in those tests are caused by this patch, then it's still a problem because I haven't seen those tests failing in other issues. Could be a race condition that only exists with the changes done here.

Is there a way we could set up multiple tests running on this patch to confirm this? Or will it overload the test server?

catch’s picture

You can hack the testing system so it runs the same test loads of times (and none of the others), this has been used on random test failures before, and might help to determine whether this is some kind of concurrency issue or specific to the tests that are failing.

markhalliwell’s picture

Status: Needs work » Needs review

I'm not convinced this issue is the actual cause of these supposed random test failures. At most, it may simply expose already broken tests/code elsewhere. The patch itself does not touch the code that's failing. Even though it is low-level, if it were truly broken, we'd see a lot more catastrophic failures than this since it is such an integral part of the extension system.

Setting back to CNR to trigger tests.

dawehner’s picture

I do agree with the worry of @berdir, @alexpott and others. This really low level piece of code is triggered in code paths, you can't necessarily control. Looking through the comments there is an awful amount of failures over time :(

After that I tried to review the patch explicitly for sources of race conditions.

+++ b/core/lib/Drupal/Core/Extension/ThemeInstaller.php
@@ -264,11 +250,10 @@ public function uninstall(array $theme_list) {
     // keys.
     $extension_config->save(TRUE);
-    $this->state->set('system.theme.data', $current_theme_data);
 
-    // @todo Remove system_list().
-    $this->themeHandler->refreshInfo();
+    // Refresh theme info.
     $this->resetSystem();
+    $this->themeHandler->reset();
 
     $this->moduleHandler->invokeAll('themes_uninstalled', [$theme_list]);
   }
@@ -280,7 +265,6 @@ protected function resetSystem() {

@@ -280,7 +265,6 @@ protected function resetSystem() {
     if ($this->routeBuilder) {
       $this->routeBuilder->setRebuildNeeded();
     }
-    $this->systemListReset();
 

It feels like the removal of systemListReset() at the end could be a reason for a race condition?

markhalliwell’s picture

Do you mean that ThemeInstaller::uninstall is resetting the theme handler outside of ThemeInstaller::resetSystem instead of in it?

I think it was removed in and around #137 when @alexpott was doing all the performance optimization patches, which is kind of also when all these "random failures" starting happening.

Not sure why it was added to ThemeInstaller::uninstall and didn't replace $this->systemListReset() in ThemeInstaller::resetSystem. Maybe he could help elaborate a bit more. It was a bit difficult to follow the rapid changes that were made.

Also, to clarify, I'm really not throwing @alexpott under the bus either (his work there was amazing). Just trying to help pinpoint what's going on so we don't ship with a half-baked extension list services in 8.6.0.

I mean, I guess we could try moving that one line?

markhalliwell’s picture

Version: 8.6.x-dev » 8.7.x-dev
Status: Needs review » Needs work

This is extremely frustrating and absolutely ridiculous to have these kinds of double standards in core. This is yet another example of the theme system being intentionally excluded and thought of as a "non-priority sub-system". I'd say a hell of a lot more, but I'm way too upset and don't want to offend anyone unintentionally.

markhalliwell’s picture

FTR... not having this issue in has affected my ENTIRE contrib plans for at least the next 6 months across a multitude of projects. I'll now need additional boilerplate code and special handling for theming extensions in 8.6.x. Thanks a lot!

markhalliwell’s picture

Can anyone actually pin-point what the real issue is here?

Seems like this was stalled on hypothetical "random test failures" without any real proof and then abandoned because... "reasons".

phenaproxima’s picture

Well, looking at the many failures of the most recent patch, I see a lot of failures in Drupal\FunctionalJavascriptTests\Ajax\AjaxTest. It might be prudent to run that specific test about 100 times locally, with this patch applied, and see if there are any failures. If there are, then we know this patch is breaking stuff and will need to track it down.

That's maybe not the most rigorous way to find a random failure, but it's better than nothing.

phenaproxima’s picture

Status: Needs work » Needs review
StatusFileSize
new82.85 KB

Rerolled against 8.7.x, and adjusted drupalci.yml so it will only run AjaxTest 100 times. Let's see what happens.

alexpott’s picture

@phenaproxima I think we need to run all the functional javascript tests. There are know random fails in that test suite. For example the "not clickable thing" in a media test but apart from that its pretty stable.

There is another awful possibility - the changes this makes has some effect when running multiple tests. In general the tests are pretty isolated but some things are hard to predict.

phenaproxima’s picture

StatusFileSize
new82.75 KB

Here is a variant which runs all JS tests (and only the JS tests) 50 times, with a concurrency of 15.

dsnopek’s picture

@phenaproxima Thanks for taking action to move this forward!

Status: Needs review » Needs work

The last submitted patch, 229: 2659940-229-random-failure-js-tests.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

phenaproxima’s picture

Status: Needs work » Needs review
StatusFileSize
new82.75 KB

Looks like the build timed out because 110 minutes is the limit. Reducing the number of test suite runs to 42, just to see if we can at least get a complete one.

phenaproxima’s picture

I have done some detective work, largely consisting of talking to @xjm, and I have reason to believe that the random failures which plagued #194 are no longer an issue.

I'm told that, in late July 2018 (July 17-22 or so, according to @xjm), Drupal\FunctionalJavascriptTests\Ajax\AjaxTest began suffering a spate of random failures. Apparently they were addressed when some sort of change, the nature of which I am not clear on, was made to testbot.

Here is an example of the random failures that AjaxTest experienced during that time frame: https://www.drupal.org/pift-ci-job/1023212. Notice that it looks very, very similar to the failures in #194.

I've searched open and closed issues and I've been unable to find any further documentation or logs to back this up, but it seems that the circumstances of #194's random failures coincide suspiciously with the period of time during which AjaxTest was randomly failing.

However, "it seems fixed now" isn't quite good enough to get this patch committed. Although I'm doubtful that this patch is introducing a random failure, it carries the burden of proof. So, I'm attaching the following patches:

  1. 2659940-234-ajaxtest-only-without-changes.patch: Runs AjaxTest, and only AjaxTest, 100 times. (I'd like to do more, but the test takes about a minute to run on testbot, which enforces a hard time limit of 110 minutes for any one build). This patch makes no other changes to core, and doesn't include the changes to the extension system made by #194.
  2. 2659940-234-ajaxtest-only-with-changes.patch: Includes the changes to the theme extension system, and runs AjaxTest (and only AjaxTest) 100 times.
  3. 2659940-234-all-js-tests-without-changes.patch: Runs all functional JavaScript tests 42 times (again, this is to stay within the build time limit), and doesn't make any changes to core.
  4. 2659940-234-all-js-tests-with-changes.patch: Runs all functional JavaScript tests 42 times, and includes the changes to the theme system.

Each of these patches:

  • Uses a concurrency of 15 (same as in HEAD) to run the tests. If there's a random failure here, I expect that a >1 concurrency will help trigger it.
  • Is based on a reroll of #194. Apart from the rerolled bit, the only difference between these patches and #194 is what's in drupalci.yml (or the file comment in core/lib/Drupal.php, to trick testbot into testing a patch that introduces no real changes).
  • Is queued to run on MySQL, PostgreSQL, and SQLite backends, since backend speed can affect random failures.

Let's see what happens. If it all comes back green, I for one will be pretty convinced that there is no random failure being introduced by this patch. AjaxTest's July failures may have been caused, then, by (take your pick):

  • The introduction of Nightwatch testing to core and Drupal CI
  • Some other Drupal CI environment change
  • Some other bug in HEAD that was fixed, possibly with a reverted commit, on July 22nd.
  • ...?

If you (especially if you're a committer) know more about the circumstances at that time, feel free to chime in. In the meantime, let's put this patch through its paces.

The last submitted patch, 233: 2659940-234-all-js-tests-without-changes.patch, failed testing. View results

Status: Needs review » Needs work

The last submitted patch, 233: 2659940-234-all-js-tests-with-changes.patch, failed testing. View results

markhalliwell’s picture

So it isn't this issue that's causing the failures... interesting...

alexpott’s picture

Issue summary: View changes
Status: Needs work » Needs review
StatusFileSize
new84.11 KB

So #233 proves that we have random fails in JS tests without this patch. And in the same tests as seen on this patch. I'm still wondering if this patch will cause the frequency of fails to increase because as stated at the time, I did not see any other issue fail with the same regularity as this one. Hopefully not.

Let's make the latest patch the correct patch to rtbc so any rtbc retesting will be done against the correct patch.

Patch attached fixes the reroll so that ThemeHandlerTest passes - it was where to the conflict was - due to #2575105: Use cache collector for state.

+++ b/core/lib/Drupal/Core/Extension/ThemeHandler.php
@@ -200,6 +117,11 @@ public function listInfo() {
+    $this->getClassLoader()->addPsr4('Drupal\\' . $theme->getName() . '\\', $this->root . '/' . $theme->getPath() . '/src');

@@ -214,36 +136,35 @@ public function addTheme(Extension $theme) {
+  /**
+   * Gets the class loader.
+   *
+   * @return \Composer\Autoload\ClassLoader
+   *   The class loader.
+   */
+  protected function getClassLoader() {
+    return \Drupal::service('class_loader');
+  }

If we're unsure that we're going to continue to need the class loader in ThemeHandler then we should inline \Drupal::service('class_loader') and not add a method. If we think ThemeHandler is going always need the class loader then we should inject it. I've gone for inlining the class_loader since we've already got #2941757: Extension System, Part IV: Properly register all installed extensions/namespaces during container generation to address theme class loading.

Patch attached also fixes some comments that this patch makes incorrect.

We also need to add something for the new release note snippet section on issue summaries.

alexpott’s picture

Issue summary: View changes

Wrote something for the release note snippet.

alexpott’s picture

StatusFileSize
new2.06 KB
new85.03 KB

For some reason my re-roll didn't put the new testClassLoading on the right ThemeTest. Weird. Re-did it and now it did.

The last submitted patch, 237: 2659940-reroll-2-237.patch, failed testing. View results

markhalliwell’s picture

Status: Needs review » Needs work

Just did a quick re-review and a couple of notes:

  1. +++ b/core/includes/module.inc
    @@ -14,41 +14,31 @@
    + * @deprecated in Drupal 8.6.0 and will be removed before Drupal 9.0.0. Use
    ...
    +  @trigger_error('system_list() is deprecated in Drupal 8.6.0 and will be removed before Drupal 9.0.0. Use \Drupal::service(\'theme_handler\')->listInfo() instead. See https://www.drupal.org/node/2709919', E_USER_DEPRECATED);
    

    These all need to be changed to 8.7.0 now.

  2. +++ b/core/lib/Drupal/Core/Extension/ThemeHandler.php
    @@ -118,22 +46,17 @@ class ThemeHandler implements ThemeHandlerInterface {
    +    if (!isset($theme_list)) {
    +      // @todo trigger deprecation.
    +      $this->themeList = \Drupal::service('extension.list.theme');
    +    }
    

    Is there a specific follow-up issue for this?

markhalliwell’s picture

Also, inlining the class loader is fine with me. I wasn't sure what snippet you were referring to at first but then realized you already inlined it in the latest patch, which makes perfect sense when read as a whole:

+++ b/core/lib/Drupal/Core/Extension/ThemeHandler.php
@@ -200,6 +117,11 @@ public function listInfo() {
   public function addTheme(Extension $theme) {
+    // Register the namespaces of installed themes.
+    // @todo Implement proper theme registration
+    // https://www.drupal.org/project/drupal/issues/2941757
+    \Drupal::service('class_loader')->addPsr4('Drupal\\' . $theme->getName() . '\\', $this->root . '/' . $theme->getPath() . '/src');
alexpott’s picture

Status: Needs work » Needs review
StatusFileSize
new3.2 KB
new84.92 KB

Fixed #241.1
#241.2 is interesting. Given we're ripping up the ThemeHandler constructor I see no need for it to be an optional argument. I don't think the service is ever set this way in core.

It's good to see that #239 fail on sqlite with Javascript tests much the same way that HEAD is failing more often on SQLite - see https://www.drupal.org/pift-ci-job/1132428

markhalliwell’s picture

Status: Needs review » Reviewed & tested by the community

I'm tentatively switching this back to RTBC.

Is there an issue for figuring out the random test failures already created?

While it may be more fequent in SQLite, #233.3 indicates that it can happen in MySQL too.

catch’s picture

Status: Reviewed & tested by the community » Fixed

Went over this again and no serious complaints, also good to get this in early-ish in the 8.7.x cycle so there's time to flush out any issues it might introduce.

Committed 10722cd and pushed to 8.7.x. Thanks!

  • catch committed 10722cd on 8.7.x
    Issue #2659940 by almaudoh, alexpott, phenaproxima, Jo Fitzgerald,...
alexpott’s picture

A very quick follow-up. This patched added a public weight property to the Theme extension object with no discussion. We should remove it asap - see #3016968: Remove weight public property from theme extensions

alexpott’s picture

Also #3015812: Introduce new Theme extension object and properly deprecate REGIONS_VISIBLE and REGIONS_ALL starts to build a more function extension object for Themes which hopefully will be the start of making extension objects less of a dumping ground and a bit easier to deal with.

wim leers’s picture

Published https://www.drupal.org/node/2941753. Note that "introduced in" says 8.6, perhaps that should become 8.7 since this portion only landed in 8.7?

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.