Problem/Motivation

Setting this to major (critical?) since this can break contrib, with no easy workaround.

When a service implements \Drupal\Core\DestructableInterface, the service is registered to the kernel.destructable_services container parameter.

However if any of these services are decorated, then you'll get errors like:

Fatal error: Uncaught Error: Call to undefined method Drupal\hux\HuxModuleHandler::destruct() in /data/app/core/lib/Drupal/Core/DrupalKernel.php:687

This error here happens since modules like Hux, Hook Event Dispatcher, Domain, and others, decorate ModuleHandler.

However this issue isnt confined to ModuleHandler, but any service that implements \Drupal\Core\DestructableInterface, where the main interface (e.g.\Drupal\Core\Extension\ModuleHandlerInterface) doesnt also implement DestructableInterface.

Steps to reproduce

  1. Create a service implementing DestructableInterface
  2. Create a service decorating the above service. Where the this service does not implement DestructableInterface.
  3. Clear container cache; crash.

Proposed resolution

Track and execute destruct only on the original (inner) services???

Remaining tasks

Discuss, implement, fix

User interface changes

Nil

API changes

Probably limited to core behaviour only.

Data model changes

Nil

Release notes snippet

Issue fork drupal-3441010

Command icon Show commands

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

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

Comments

dpi created an issue. See original summary.

dpi’s picture

Title: needs_destruction services » Container compile crash when a service decorates a destructable service
Issue summary: View changes
dpi’s picture

An un-fun way to solve this, which we should avoid, is also: adding the making the service interface (such as ModuleHandlerInterface implement DestructableInterface.

dpi’s picture

If we determine a service must now implement separate interfaces, then I think #3412556: Allow needs_destruction services to run on page cache hits needs a changelog entry.

In the case of \Drupal\Core\Extension\ModuleHandler::writeCache, should this method be deprecated and removed from the interface?

catch’s picture

Issue tags: +beta target

Should we be doing an instanceof check for destructable interface in DrupalKernel::terminate()?

And/or should hux be removing the terminate tag from the service definition?

dpi’s picture

@catch I'm wondering if theres a way to just call destruct on the original.

Thinking of Symfony concepts when utilising decorators.

If a service has `calls` applied to it, it doesnt matter if it decorated or not, the calls are always applied to the original/inner service, not the outermost service.

E.g, given:

services:
  dpi.test:
    class: Drupal\dpi\TestService
    calls:
      - [ foo, ['foo!']]

  dpi.deco:
    class: Drupal\dpi\Deco
    decorates: dpi.test
    arguments:
      - '@dpi.deco.inner'
    calls:
      - [ bar, ['bar!']]

At no point will an attempt to call dpi.deco::foo() happen.

larowlan’s picture

Any reason hux can't implement destructable and just call the inner method from its implementation?

larowlan’s picture

StatusFileSize
new2.1 KB

Something like this for hux (1.3.x branch) seems to work

catch’s picture

#7 was my first thought, but it would probably be good to do something in core so it doesn't crash, even if that's an instanceof check before calling the destruct method.

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

longwave’s picture

I think the MR will work but the services are meant to be destructible for a reason, maybe we should issue a warning/deprecation when we find a non DestructableInterface service so downstream users are notified?

catch’s picture

Doh it's because the classes are in the same namespace so we don't need a use statement at all. Thanks @longwave.

Pushed up a second branch with another idea which I think is closer to what @dpi is asking for - if we autoconfigure the tag for services with the interface, then decorating services won't get the tag, but inner services still would. However, it's not actually working. We do have one example of autoconfiguring tags in core, but I've never tried it before, hopefully a similarly silly mistake.

catch’s picture

Status: Active » Needs review

I had a play with

$container->registerForAutoconfiguration(DestructableInterface::class)
-      ->addTag('needs_destruction');

and didn't get anywhere. Maybe every services.yml needs to specify autoconfigure: true for even this to work? I guess we could do that but it feels like a bigger change than the scope here.

Instead, I'm iterating over the service definitions where we collect the tagged services, and adding the tag to them there, this seems to work (at least the one test I'm running locally works, as did some manual testing). This was @longwave's suggestion in slack as one of several possible options.

Maybe we can do this to un-fatal things then open a follow-up to use autoconfigure more?

longwave’s picture

#15 strikes me as being the same root cause as #3446026: Adding media library openers use autoconfigure and tags in 10.3.x has BC consequences so I think worth seeing if we can solve both problems with one solution.

catch’s picture

Priority: Major » Critical
Issue tags: +10.3.0 release notes

I think we might need to just do the quick fix here tbh. Bumping priority since a couple more contrib have reported this now. https://drupal.slack.com/archives/C1BMUQ9U6/p1716826820826669

dqd’s picture

Thanks at all tracking this so quick and raising prio. Contacted some bigger contrib checking against 10.3 beta to be aware of it. Rephrase my question @catch @longwave:Is your attempt (green MR) applyable to at least get rid of WSOD as a dirty fix while working on a final solution?

@catch: How to help here. Any steps where others can chime in?

dqd’s picture

Issue summary: View changes

Added Domain to summary as a big contrib affected by this, since this will not only break one but all sites (domains) in the project.

needs-review-queue-bot’s picture

Status: Needs review » Needs work
StatusFileSize
new90 bytes

The Needs Review Queue Bot tested this issue. It no longer applies to Drupal core. Therefore, this issue status is now "Needs work".

This does not mean that the patch necessarily needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.

Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.

dqd’s picture

removed (wrong issue)

znerol’s picture

I'd suggest to revert #3412556: Allow needs_destruction services to run on page cache hits. The justification for that change isn't that convincing, and the risk assessment expressed in the CR clearly missed the effects on contrib.

catch’s picture

Status: Needs work » Needs review
The website encountered an unexpected error. Try again later.

Error: Call to undefined method Drupal\domain\DomainListBuilder::formBuilder() in Drupal\domain\DomainListBuilder->render() (line 318 of modules/contrib/domain/domain/src/DomainListBuilder.php).

This looks completely unrelated.

@znerol that might help with the 2-3 modules affected here, but it won't with similar issues like #3446026: Adding media library openers use autoconfigure and tags in 10.3.x has BC consequences.

dqd’s picture

This looks completely unrelated.

@catch: Yes, it is... wrong issue, sry (mixed tabs, removed comment to prevent confusion)

agentrickard’s picture

Yes, the domain issue here is actually https://www.drupal.org/node/3425054 -- though we still need to check these decorations:

  domain.route_provider:
    class: Drupal\domain\Routing\DomainRouteProvider
    decorates: router.route_provider
    decoration_priority: 10

  domain_config.library.discovery.collector:
    decorates: library.discovery.collector
    class: \Drupal\domain_config\DomainConfigLibraryDiscoveryCollector

  domain_config_ui.factory:
    class: Drupal\domain_config_ui\Config\ConfigFactory
    decorates: config.factory

Those should be two issues in the Domain queue.

needs-review-queue-bot’s picture

Status: Needs review » Needs work
StatusFileSize
new90 bytes

The Needs Review Queue Bot tested this issue. It no longer applies to Drupal core. Therefore, this issue status is now "Needs work".

This does not mean that the patch necessarily needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.

Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.

catch changed the visibility of the branch 3441010-autoconfigure to hidden.

catch’s picture

Status: Needs work » Needs review

Hiding the autoconfigure branch.

needs-review-queue-bot’s picture

Status: Needs review » Needs work
StatusFileSize
new90 bytes

The Needs Review Queue Bot tested this issue. It no longer applies to Drupal core. Therefore, this issue status is now "Needs work".

This does not mean that the patch necessarily needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.

Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.

el7cosmos’s picture

In the case of module handler, even if without changes from #3412556: Allow needs_destruction services to run on page cache hits, decorators that don't implement DestructableInterface will also fail in 10.2 if we apply #3436599: Replace RequestCloseSubscriber with needs_destruction tag on ModuleHandler.

this just because the call changed from writeCache which declared in ModuleHandlerInterface to destruct which is new implementation.

As for #3412556: Allow needs_destruction services to run on page cache hits, I don't think we can access decorated/original service from DrupalKernel, so even if we register the original service in kernel.destructable_services (eg hux.module_handler.inner) we won't be able to get it from the Kernel with $this->container->get('hux.module_handler.inner').

Perhaps, instead of a parameter we can create a service that can receive the original service as the argument, but that will initialize the instance and defeat the purpose of $this->container->initialized($id).

I can't see any other solution than:

  • Implement DestructableInterface from the service's interface, not implementation, this will enforce decorators to implement that also.
  • Let it be the decorator's responsibility to implement DestructableInterface, but the container will crash if it is not.
longwave’s picture

Issue tags: +DevDaysBurgas2024

Discussed with @alexpott at Dev Days Burgas.

We agreed that option 1 from #30 is the correct thing to do here. Under the interfaces BC policy we are allowed to add methods to interfaces in minors where there is a 1-1 relationship with an implementation, which there is here with ModuleHandler.

We also think that this can be implemented in a 10.3.x patch release, given it is no more broken than it is already if someone is replacing the implementation of ModuleHandler with an alternative version that only implements ModuleHandlerInterface without implementing DestructableInterface as well.

longwave changed the visibility of the branch 3441010-container-compile-crash to hidden.

longwave’s picture

Status: Needs work » Needs review
geek-merlin’s picture

Status: Needs review » Reviewed & tested by the community

Ran into this when i had a site with hux:1.4 on core:10.3. Updating to hux:1.5 fixes the site problem.

As for MR !8558:

I reviewed the code and can confirm that it trivially implements the #30.1 proposal (single extended interface).

Requiring a test for this imho is only boilerplate.

Behavior-wise: I can confirm that adding this patch changed the core:10.3+hux:1.4 error like below:

Before:

$ drush cim
 [notice] There are no changes to import.
PHP Fatal error:  Uncaught Error: Call to undefined method Drupal\hux\HuxModuleHandler::destruct() in /home/merlin/Code/geeks4change/sites/site-h4d-greenopolis/web/core/lib/Drupal/Core/DrupalKernel.php:723

After:

$ drush cr
PHP Fatal error:  Class Drupal\hux\HuxModuleHandler contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (Drupal\Core\DestructableInterface::destruct) in /home/merlin/Code/geeks4change/sites/site-h4d-greenopolis/web/modules/contrib/hux/src/HuxModuleHandler.php on line 21

Thus RTBC-ing.

geek-merlin’s picture

FYI but out of scope here: For my own needs i use Composable Inheritance to avoid (amongst others) this kind of pain on highly self-coupled classes that are a pain to decorate but nice to inherit.

  • alexpott committed d0f39b40 on 10.3.x
    Issue #3441010 by catch, longwave, larowlan, dpi, dqd, geek-merlin,...

  • alexpott committed b8241d8c on 10.4.x
    Issue #3441010 by catch, longwave, larowlan, dpi, dqd, geek-merlin,...

  • alexpott committed 1dd699f0 on 11.0.x
    Issue #3441010 by catch, longwave, larowlan, dpi, dqd, geek-merlin,...

  • alexpott committed e8237237 on 11.x
    Issue #3441010 by catch, longwave, larowlan, dpi, dqd, geek-merlin,...
alexpott’s picture

Status: Reviewed & tested by the community » Fixed

Discussed with @catch and @longwave - we agreed to make this change because whilst it does not fix the issue it is a better break and help module maintainers fix it quickly.

Committed and pushed e82372379d to 11.x and 1dd699f07a to 11.0.x and b8241d8c6d to 10.4.x and d0f39b40d2 to 10.3.x. Thanks!

Status: Fixed » Closed (fixed)

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