Deprecate Drupal ProxyBuilder in favor of Symfony lazy services

Created on 26 October 2023, about 1 year ago
Updated 29 February 2024, 9 months ago

Problem/Motivation

In πŸ“Œ Update to Symfony 6.4 Needs work we decided to convert both BookNavigationCacheContext and MenuActiveTrailsCacheContext into lazy services.

Whilst looking into this, I've stumbled over the way core currently handles this: Using a script to create a Proxy class.

Looking at https://symfony.com/doc/current/service_container/lazy_services.html Symfony supports this natively (since 6.2 without additional packages/traits).

Steps to reproduce

Proposed resolution

Replace our proxy lazy service solution with the native one from Symfony itself.

This would get us more "of the island", reduce custom solutions we have to maintain.

Remaining tasks

User interface changes

API changes

Data model changes

Release notes snippet

πŸ“Œ Task
Status

Closed: won't fix

Version

11.0 πŸ”₯

Component
BaseΒ  β†’

Last updated about 1 hour ago

Created by

πŸ‡³πŸ‡±Netherlands spokje

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

  • Issue created by @spokje
  • last update about 1 year ago
    30,432 pass, 4 fail
  • @spokje opened merge request.
  • last update about 1 year ago
    30,435 pass, 2 fail
  • last update about 1 year ago
    Custom Commands Failed
  • last update about 1 year ago
    30,438 pass
  • last update about 1 year ago
    30,438 pass
  • last update about 1 year ago
    Custom Commands Failed
  • last update about 1 year ago
    30,438 pass
  • last update about 1 year ago
    30,438 pass
  • Assigned to spokje
  • πŸ‡³πŸ‡±Netherlands spokje

    This needs some TLC tomorrow, but I'm not unhappy so far...

  • last update about 1 year ago
    30,438 pass
  • last update about 1 year ago
    Custom Commands Failed
  • last update about 1 year ago
    Custom Commands Failed
  • last update about 1 year ago
    Custom Commands Failed
  • last update about 1 year ago
    Custom Commands Failed
  • last update about 1 year ago
    Custom Commands Failed
  • last update about 1 year ago
    30,438 pass
  • last update about 1 year ago
    30,438 pass
  • last update about 1 year ago
    30,438 pass
  • πŸ‡³πŸ‡±Netherlands spokje
  • Issue was unassigned.
  • Status changed to Needs review about 1 year ago
  • πŸ‡³πŸ‡±Netherlands spokje
  • πŸ‡¬πŸ‡§United Kingdom longwave UK

    This seemed too good to be true, and unfortunately I think it is. As per the docs:

    To check if your lazy service works you can check the interface of the received object:

    dump(class_implements($service));
    // the output should include "Symfony\Component\VarExporter\LazyObjectInterface"
    

    With this MR checked out and after a container rebuild:

    > class_implements(\Drupal::service('cron'));
    = [
        "Drupal\Core\CronInterface" => "Drupal\Core\CronInterface",
      ]
    

    ie. I don't think the ghost objects are working. I suspect our container and/or dumper need to add support for them, somewhere.

  • πŸ‡³πŸ‡±Netherlands spokje

    @longwave: Hmm, at the very least #6 πŸ“Œ Deprecate Drupal ProxyBuilder in favor of Symfony lazy services Closed: won't fix points out that this MR is missing test coverage.

    ie. I don't think the ghost objects are working. I suspect our container and/or dumper need to add support for them, somewhere.

    I'm a tad more optimistic.

    The attached (incredibly nasty hacky do-not-try-this-at-home) test patch shows that the following service-IDs implement Symfony\Component\VarExporter\LazyObjectInterface:

    1) Drupal\Tests\Core\LazyService\LazyServiceTest::testBuildComplexMethod
    page_cache_response_policy
    config.installer
    cron
    module_installer
    content_uninstall_validator
    required_module_uninstall_validator
    module_required_by_themes_uninstall_validator
    database_driver_uninstall_validator
    plugin.cache_clearer
    paramconverter.menu_link
    lock.persistent
    router.dumper
    router.builder
    paramconverter.configentity_admin
    bare_html_page_renderer
    batch.storage
    file.mime_type.guesser
    file.mime_type.guesser.extension
    Drupal\Core\PageCache\ResponsePolicyInterface
    Drupal\Core\Config\ConfigInstallerInterface
    Drupal\Core\CronInterface
    Drupal\Core\Extension\ModuleInstallerInterface
    Drupal\Core\Plugin\CachedDiscoveryClearerInterface
    Drupal\Core\Routing\MatcherDumperInterface
    Drupal\Core\Routing\RouteBuilderInterface
    Drupal\Core\Render\BareHtmlPageRendererInterface
    Drupal\Core\Batch\BatchStorageInterface
    
    Failed asserting that two strings are equal.
    --- Expected
    +++ Actual
    @@ @@
    -''
    +'certainly not equal'
    

    That list matches on first glance what I expected.
    Can the difference between your and my findings be explained by looking at different times in the Container lifecycle maybe?

    As a sidenote: I also didn't believe this to work, but putting breakpoints in tests using the lazy services indeed shows the presence of LazyObjectState and [ActualClassName]Ghost[hashID] classes. (See https://www.drupal.org/files/issues/2023-10-27/LazyStateGhost.jpg β†’ )
    Of course, that's no proof at all.

  • πŸ‡¬πŸ‡§United Kingdom longwave UK

    In kernel tests, the container is a \Drupal\Core\DependencyInjection\ContainerBuilder - you can see this by dumping $container::class in that sample test. This is a thin layer over Symfony's ContainerBuilder which does support lazy services.

    However at runtime the container is a \Drupal\Core\DependencyInjection\Container which is not based on Symfony at all, it only implements their interface. The ContainerBuilder is dumped and stored in cache by the rebuild process, and reloaded from there as a normal Container on subsequent page loads.

  • πŸ‡¬πŸ‡§United Kingdom longwave UK

    Not that this should put us off; the fact the kernel tests are passing here are a good sign that these lazy services are working, just they are no longer lazy at runtime or in functional tests. But if we add support for them to the dumper/container then they should be usable at runtime too!

  • πŸ‡¬πŸ‡§United Kingdom longwave UK
  • Assigned to spokje
  • Status changed to Needs work about 1 year ago
  • πŸ‡³πŸ‡±Netherlands spokje

    Thanks @longwave, that's the reason I always danced around the container bit, never got a firm grasp of it.

    The explanation helped, still got hope this is going to work and shouldn't be that hard.
    (It seems the actual container is fed the drupal proxy classes instead of the actual services, let's see if that can be altered quickly)
    #FamousLastWords
    #OnHisWayToTheDrawingBoard

    Final question (for now):

    With this MR checked out and after a container rebuild:

    > class_implements(\Drupal::service('cron'));
    = [
    "Drupal\Core\CronInterface" => "Drupal\Core\CronInterface",
    ]

    How what and where did you do that class_implements?

  • πŸ‡¬πŸ‡§United Kingdom longwave UK

    I use DDEV for all Drupal dev work.

    $ ddev drush cr
     [success] Cache rebuild complete.
    
    $ ddev drush php
    Psy Shell v0.11.9 (PHP 8.1.23 β€” cli) by Justin Hileman
    Drupal 10 (Drupal 11.0-dev)
    > class_implements(\Drupal::service('cron'));
    = [
        "Drupal\Core\CronInterface" => "Drupal\Core\CronInterface",
      ]
    
  • πŸ‡³πŸ‡±Netherlands spokje

    *hat is tipped*
    Much obliged, kind sir.

  • πŸ‡¬πŸ‡§United Kingdom joachim

    > In kernel tests, the container is a \Drupal\Core\DependencyInjection\ContainerBuilder - you can see this by dumping $container::class in that sample test. This is a thin layer over Symfony's ContainerBuilder which does support lazy services.

    Aside: is that documented somewhere?

  • πŸ‡¬πŸ‡§United Kingdom longwave UK

    I don't remember seeing it anywhere, we could add some docs to KernelTestBase I guess but really we should fix #2529516: Decouple tests from relying that $container is a container builder β†’

  • πŸ‡³πŸ‡±Netherlands spokje
  • Issue was unassigned.
  • Status changed to Closed: won't fix about 1 year ago
  • πŸ‡³πŸ‡±Netherlands spokje

    Sadly SF uses eval for the proxy classes which at least I can't see working with our serialized container solution

  • πŸ‡ΊπŸ‡ΈUnited States bkosborne New Jersey, USA

    I wonder if the change record can be deleted? It comes up in Google Search and it's not super obvious when looking at a draft CR that it's a draft

  • πŸ‡¬πŸ‡§United Kingdom alexpott πŸ‡ͺπŸ‡ΊπŸŒ

    @bkosborne I unpublished the CR.

Production build 0.71.5 2024