6.x remove league/container - #19559
Open
LordSimal wants to merge 8 commits into
Open
Conversation
LordSimal
commented
Jul 21, 2026
| use Psr\Container\ContainerInterface as PsrContainerInterface; | ||
|
|
||
| interface DefinitionContainerInterface extends ContainerInterface | ||
| interface ContainerInterface extends PsrContainerInterface |
Contributor
Author
There was a problem hiding this comment.
Wanted to make this as straight forward as possible, so i renamed this interface
Member
|
Member
|
Hah... The PR deletes the |
Contributor
Author
|
ContainerFactory logic + config to set a custom class has been restored. |
LordSimal
force-pushed
the
6.x-remove-league-container
branch
2 times, most recently
from
July 22, 2026 10:36
baeabad to
ccb1490
Compare
LordSimal
marked this pull request as ready for review
August 2, 2026 18:34
CakePHP has carried two DI container implementations since 5.4.0:
league/container (the default) and Cake\Container\Container (opt-in
via Configure::read('App.container') === 'cake'), bridged together by
CakeContainerBridge/CakeDefinitionBridge/CakeInflectorBridge so the
'cake' option could satisfy Cake\Core\ContainerInterface, which itself
extended League\Container\DefinitionContainerInterface. With 6.0 free
to break compatibility, this collapses to a single implementation.
- Cake\Core\ContainerInterface now extends
Cake\Container\DefinitionContainerInterface instead of League's.
- Cake\Core\Container now extends Cake\Container\Container directly
(adds an explicit delegate() override since the interface narrows
its return type and the parent method declares none).
- Cake\Core\ServiceProvider now extends
Cake\Container\ServiceProvider\AbstractServiceProvider /
BootableServiceProviderInterface.
- ContainerFactory and the three Cake*Bridge classes are deleted -
there's no longer a choice to factor, so BaseApplication::
buildContainer() constructs Container directly.
- ComponentRegistry drops League's ArgumentReflectorTrait/
ArgumentResolverTrait/LiteralArgument/ResolvableArgument/
ReflectionContainer for the Cake\Container equivalents, and its
getMode()/ATTRIBUTE_RESOLUTION shim goes away since Cake\Container
has no mode-flag concept (auto-wiring is always on).
- league/container dropped from composer.json (root and
cakephp/core); cakephp/core now requires cakephp/container instead.
Ported the one real feature gap: Cake\Container had no equivalent of
League's attribute-based argument resolution, which
Cake\Core\Attribute\Configure depends on for #[Configure] constructor
injection. Added Cake\Container\Attribute\AttributeInterface and wired
attribute resolution into ArgumentResolverTrait::reflectArguments(),
mirroring League's ArgumentReflectorTrait::resolveArgumentFromAttribute().
Cake\Container\Container enables auto-wiring by default (unlike
League, where it was opt-in), which changes has() to return true for
any autoloadable class rather than only explicitly-registered
services. This broke three call sites that used has() to mean
"explicitly registered": ControllerFactory::create() (controller
construction), ControllerFactory::getActionArgs() (action parameter
DI), and Console\CommandFactory::create() - all switched to
hasDefinition(), which checks only explicit definitions. Also updated
TableContainer's docblock and its tests to call disableAutoWiring()
first, since it's meant to be checked before auto-wiring resolves a
class by bare reflection.
Full test suite passes (10,183 tests); only a pre-existing,
environment-dependent stdin-availability test is unrelated and
unaffected.
The previous commit kept a thin Cake\Core\Container subclass and a Cake\Core\ContainerInterface that added a delegate() requirement on top of Cake\Container\DefinitionContainerInterface, purely so the concrete class had something to implement. With only one container implementation left, that layer added nothing. - delegate() moved into Cake\Container\DefinitionContainerInterface itself (Cake\Container\Container already had a concrete delegate() method, just undeclared in its interface and missing a return type). - Cake\Core\Container and Cake\Core\ContainerInterface are deleted. Every services(ContainerInterface $container)/getContainer(): ... / bootstrap(ContainerInterface $container) signature across ContainerApplicationInterface, PluginInterface, BasePlugin, ServiceProvider, ComponentRegistry, ControllerFactory, BaseApplication, MiddlewareQueue, CommandFactory, EventListenerRegistrationTrait, and ContainerStubTrait now types against Cake\Container\DefinitionContainerInterface directly, and BaseApplication/WebExceptionRenderer instantiate Cake\Container\ Container directly instead of a Core-level wrapper. - ServiceProvider::getContainer() no longer needs its override - it was only asserting the container satisfied Cake\Core\ ContainerInterface, which no longer exists. Full test suite passes (10,183 tests, same single pre-existing stdin-availability failure as before); phpcs and phpstan show the same pre-existing findings as the unmodified baseline on every touched file.
Now that Cake\Core\ContainerInterface is gone, "DefinitionContainerInterface" read as an oddly-qualified name for what is simply the container's public interface. Renamed to Cake\Container\ContainerInterface across the package and every consumer (Core, Http, Controller, Console, Event, and their tests). This introduces a name collision with Psr\Container\ContainerInterface in the three files within the Container package that reference both: the interface's own file, Container.php, and ArgumentResolverTrait.php. Each now aliases the PSR import as `PsrContainerInterface` and keeps bare `ContainerInterface` referring to the local (same-namespace) interface - except where a method genuinely operates on any PSR-11 container rather than Cake's own (delegate()'s parameter, defaultToShared()'s return type, and the has()/get() check in ArgumentResolverTrait::resolveArguments() that must also match a bare ReflectionContainer, which only implements the PSR interface), which explicitly use PsrContainerInterface instead. Full test suite passes (10,183 tests, same pre-existing stdin-availability failure as before); phpcs and phpstan show identical findings to the pre-rename baseline on every touched file.
ServiceProvider only ever depended on Cake\Container classes (AbstractServiceProvider, BootableServiceProviderInterface, ContainerInterface) - it's a thin CakePHP-flavored convenience layer over the container package's own service provider base class, not something that belongs to Core. Moved to Cake\Container\ServiceProvider alongside it, dropping the now-redundant ContainerInterface import since it's the same namespace. Test file and the two TestApp service provider fixtures moved/updated to match. No composer.json changes needed - cakephp/core already requires cakephp/container for its other Container-typed interfaces (ContainerApplicationInterface, PluginInterface, BasePlugin), and cakephp/container's PSR-4 autoload already covers the new file. Full test suite passes (10,183 tests, same pre-existing stdin-availability failure); phpcs clean, phpstan identical to the pre-move baseline.
Console (CommandFactory), Event (EventListenerRegistrationTrait), Http (BaseApplication, MiddlewareQueue), and ORM (TableContainer) all reference Cake\Container\* classes directly, but none of their composer.json files declared cakephp/container as a dependency - it only worked because this monorepo autoloads everything from one flat src/ regardless of which subpackage composer.json declares what. Caught by split-package static analysis, which installs each subpackage's own declared dependencies in isolation: phpstan couldn't resolve Cake\Container\ContainerInterface at all when analyzing Http standalone, since cakephp/http never required cakephp/container.
`composer stan` runs phpstan, psalm, and structarmed (an architecture layer linter) across the whole repo - broader coverage than the scoped checks used while landing each individual commit in this series, which is how these slipped through: - ArgumentResolverInterface::resolveArguments()'s docblock declared an overly-narrow param type (array<LiteralArgument|ResolvableArgument>) that never matched what the method's own implementation actually handles (any ArgumentInterface, checked generically via instanceof). Adding attribute-resolution support widened the argument list to include LiteralArgumentInterface, which is what finally tripped phpstan's stricter cross-class analysis. Corrected the docblock to the interface both call sites and the implementation actually use. - structarmed.php's layer ruleset predates this whole league/container removal: it only allowed the `Core` layer to depend on `Container`, because previously everything else reached the container through Core's own Container/ContainerInterface wrapper. Now that Console, Controller, Error, Event, Http, and TestSuite depend on Cake\Container directly by design, the ruleset needed the same layers added to reflect the now-intentional architecture instead of the one it's replacing. - psalm-baseline.xml had two stale entries for ComponentRegistry::getContainer()/getMode() left over from earlier cleanup in this series; psalm's own baseline pruning removed them since they no longer match current code. Verified with `source setup/env && composer stan` (phpstan, psalm, and structarmed all clean, exit 0) and the full test suite (10,183 tests, same single pre-existing stdin-availability failure).
LordSimal
force-pushed
the
6.x-remove-league-container
branch
from
August 2, 2026 18:34
ccb1490 to
cf85be0
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Refs: #18090
This is the final step in our process of moving away from
league/container.I tried to move everything container specific to the container package and still keep the things in core which made sense to keep there.
One thing that came to my mind is the fact, that for now we force the
$container = new Container();in our BaseApplication class. Should we allow users to define their own container implementation to be used and just fallback to ours? Or is it expected that users just overwrite thatbuildContainer()method if they want to do that?