neos / flow-development-collection

The unified repository containing the Flow core packages, used for Flow development.
https://flow.neos.io/
MIT License
134 stars 187 forks source link

RFC: Explicitly enable dependency injection for classes with private constructors #3337

Open mhsdesign opened 3 months ago

mhsdesign commented 3 months ago

Currently Flow allows us to write code like this (inspired by our current ActionRequest):

final class ActionRequest
{
    /**
     * @Flow\Inject
     * @var ObjectManagerInterface
     */
    protected $objectManager;

    /**
     * @Flow\Inject
     * @var PackageManager
     */
    protected $packageManager;

    private function __construct()
    {
    }

    public static function create(): self
    {
        return new self();
    }

    // ...
}

Flow will make sure that after instantiating the object from the outside via ActionRequest::create() all dependencies are injected. This is cool but also rather unexpected from a users perspective. Also it allows for a hacky codestyle while possibly not being aware of it.

Why is this hacky?

Because Flows DI / Proxybuilding goes to great - if not impossible - lengths to archive this. You will likely find no other DI Framework to support this. The code is equivalent to adding this to construct:

private function __construct()
{
    $this->objectManager = \Neos\Flow\Core\Bootstrap::$staticObjectManager;
    $this->packageManager = \Neos\Flow\Core\Bootstrap::$staticObjectManager->get(PackageManager::class);
}

And it will become imminent that this is just super tight coupling which is impossible to unit test.

So i would propose that Flow does not allow Flow\Inject or constructor injection for private constructors but one has to enable this explicitly, possibly by using the existing autowire annotation:

#[Flow\Autowire(true)]
private function __construct()
{
}

Eventually i hope that this will bring more awareness to the crazy parts of Flows DI so that these Features are only used explicitly and we write better more testable and decoupled code at the end.