Currently, if a service A depends on another service B, then B needs to be instantiated at the same time or before the instantiation of A.
This happens even if A might never actually use B during the script lifetime.
The alternative is to have A depend on the container instead of B, so A can get B from the container at the time it is needed, and not before.
The downside: This makes A less predictable, because it now has access to *all the stuff in the container*, not just B.
Suggested solution:
A "LazyService" wrapper, that gives A access to exactly one service from the container, but not more.
The wrapper itself has access to the container, but it will only ever use it for one specific key.
I am going to upload an implementation with some added magic.
The idea is that
- the wrapper can be used in place of the real service.
- the first operation on the wrapper will trigger instantiation of the real service.
- the reference to the wrapper will be replaced with the real service, once instantiated.
- any further operations can happen directly on the real service, without any indirection overhead.
- nothing (except the wrapper) has a reference to the container.
Usage:
<?php
$builder = new LazyService(Drupal::getContainer(), 'breadcrumb');
// This will tell the wrapper to replace $builder with the real service, once instantiated.
$builder->bindReference($builder);
assert('Drupal\Component\Container\LazyService'=== get_class($builder));
// This will trigger the lazy instantiation via magic __call().
$breadcrumb = $builder->build(...);
assert('Drupal\Core\Breadcrumb\BreadcrumbManager'=== get_class($builder));
// Additional calls happen directly on the service, without indirection.
$breadcrumb2 = $builder->build(...);
?>
Related:
#1863816: Change notice: Allow plugins to have services injected