Open vv12131415 opened 5 years ago
Any progress on implementing such a feature so far?
Any progress on implementing such a feature so far?
I have not yet even started, not sure if it can be as separate package
I think we would need really good benchmarks on how much of a difference this even makes. I think it would be negligible on a standard web app. Laravel already allows method injection at the controller level with the container.
I think we would need really good benchmarks on how much of a difference this even makes. I think it would be negligible on a standard web app.
So I need to implement it and then benchmark it, is that what you mean?
Laravel already allows method injection at the controller level with the container.
yes, but that not exactly what I what since I want it in my own code that is kind of frameworkless (I mean simple POPOs).
$object = $this->container->make(MyRandomPopo::class); $object->callRandomMethod()
and it will resolve all the constructor dependencies, but this way it will resolve all of them even if I don't need them all.$this->container->call([MyRandomPopo::class, 'callRandomMethod'])
(as you probably suggest) but this way you will relay on a stringly-typed API (so there is a change that you be wrong when typing callRandomMethod
and it will fail in runtime and also static analyses wouldn't understand what this code means)app/resolve
helper, but that's service locator, and I what to avoid it.the other way to do it is separate the Application and Container and then decorate the Application instance, but, as I think it's a lot of work to do, but this way it don't needs to be in the core
I kinda think you're premature optimizing here a bit.
@taylorotwell I'm now in case, where I have circular dependency without this. Maybe you changed your mind about this feature?
@vladyslavstartsev a simple workaround to resolve circular dependencies is to introduce a service factory, e.g.:
<?php
namespace Practice\Tests\Unit;
use Closure;
use Illuminate\Container\Container;
class Foo1
{
private Bar1 $bar;
public function __construct(Bar1 $bar)
{
$this->bar = $bar;
}
public function hello()
{
}
}
class Bar1
{
private Foo1 $foo;
public function __construct(Foo1 $foo)
{
$this->foo = $foo;
}
public function world()
{
$this->foo->hello();
}
}
class Foo2
{
private Bar2 $bar;
public function __construct(Bar2 $bar)
{
$this->bar = $bar;
}
public function hello()
{
}
}
class Bar2
{
private Closure $fooFactory;
private Foo2 $foo;
public function __construct(Closure $fooFactory)
{
$this->fooFactory = $fooFactory;
}
public function world()
{
$this->foo()->hello();
}
private function foo(): Foo2
{
if (!isset($this->foo)) {
$this->foo = ($this->fooFactory)();
}
return $this->foo;
}
}
class CircularTest extends TestCase
{
public function testCircularFails()
{
$container = new Container();
$this->expectExceptionMessageMatches("/Maximum function nesting level of '\\d+' reached, aborting!/");
$container->get(Bar1::class)->world();
}
public function testFactorySucceeds()
{
$container = new Container();
$container->bind(Bar2::class, fn() => new Bar2(fn() => $container->get(Foo2::class)));
$container->get(Bar2::class)->world();
self::assertTrue(true);
}
}
The why do we need it can be found here (tl;dr to not (fully) construct objects with dependency injection container when we don't use them, to defer their resolve to the moment when we will need them)
The idea is to implement something that will be similar to
The zend version and symfony uses the https://github.com/Ocramius/ProxyManager to achieve the goal
Some of the ideas of how to implement it in laravel you can see here (they are kinda old, but still )
How it was implemented in symfony and zend (the PRs)