diff --git a/docs/lazy.md b/docs/lazy.md index 566c503..5448829 100644 --- a/docs/lazy.md +++ b/docs/lazy.md @@ -223,3 +223,29 @@ $di->set('service', $di->lazy($di->lazyNew('InvokableServiceClass'), ``` Beware of relying on generic Lazy calls too much; if we do, it probably means we need to separate our configuration concerns better than we are currently doing. + +## Lazy lazy + +Since version 5, the LazyInterface `__invoke` method requires a `Resolver` to be passed. + +This has two advantages: + +1. You can instantiate a LazyInterface without needing the Resolver or the Container. This was required to implement the attributes `#[Service]`, `#[Instance]` and `#[Value]`. +2. It reduces circular references when serializing. + +But, sometimes, you do need a directly invokable object, e.g. when creating a route handler. Such an object needs to be +invokable without passing the Resolver as a parameter. This is what the new LazyLazy solves. It wraps a LazyInterface +with the Resolver or Container. + +```php +$routeHandler = $container->lazyLazy( + $container->lazyCallable([ + $container->lazyNew(OrderController::class), + 'process' + ]) +); +``` + +In the above example `$routeHandler` can be injected in any place that receives a `callable` and can be called freely +because it does not have any parameters when `__invoke` is called. So, in that particular case, a `OrderController` class +is instantiated and the `process` method is called. diff --git a/docs/migrating.md b/docs/migrating.md index be9cd75..284598d 100644 --- a/docs/migrating.md +++ b/docs/migrating.md @@ -27,10 +27,10 @@ exposed publicly, but the changes are listed below nonetheless. - The `LazyInterface` now requires a `Resolver` to be passed to `__invoke`. Its implementations therefore also have different constructor signatures. If you need an object that is directly invokable, without the need of passing a Resolver or any other object from the -container, use `$container->lazyLazy()` to create an invokable object that is injectable into an external method or class. +container, use [`$container->lazyLazy()`](lazy.md#lazy-lazy) to create an invokable object that is injectable into an external method or class. ```php -$routeHandler = $container->lazyLazy( +$routeHandler = $di->lazyLazy( $di->lazyCallable([ $di->lazyNew(OrderController::class), 'process' diff --git a/src/Container.php b/src/Container.php index a5632e4..b6e2b17 100644 --- a/src/Container.php +++ b/src/Container.php @@ -317,13 +317,11 @@ public function lazy($callable, ...$params): Lazy * * @param LazyInterface $lazy The lazy object generated by this Container. * - * @param array $params - * * @return LazyLazy */ - public function lazyLazy(LazyInterface $lazy, ...$params): LazyLazy + public function lazyLazy(LazyInterface $lazy): LazyLazy { - return new LazyLazy($this->resolver, $lazy, $params); + return new LazyLazy($this->resolver, $lazy); } /** @@ -346,12 +344,12 @@ public function lazyArray(array $callables): LazyArray * Returns a lazy object that invokes a (potentially lazy) callable with * parameters supplied at calltime. * - * @param callable $callable The (potentially lazy) callable. + * @param callable|array{0: LazyInterface, 1: string} $callable The (potentially lazy) callable. * * @return LazyCallable * */ - public function lazyCallable(callable $callable): LazyCallable + public function lazyCallable(callable|array $callable): LazyCallable { return $this->injectionFactory->newLazyCallable($callable); } diff --git a/src/Injection/InjectionFactory.php b/src/Injection/InjectionFactory.php index 6296a7e..c853ec0 100644 --- a/src/Injection/InjectionFactory.php +++ b/src/Injection/InjectionFactory.php @@ -78,12 +78,12 @@ public function newLazyArray(array $callables): LazyArray * * Returns a new LazyCallable. * - * @param callable $callable The callable to invoke. + * @param callable|array{0: LazyInterface, 1: string} $callable The callable to invoke. * * @return LazyCallable * */ - public function newLazyCallable($callable): LazyCallable + public function newLazyCallable(callable|array $callable): LazyCallable { return new LazyCallable($callable); } diff --git a/src/Injection/LazyCallable.php b/src/Injection/LazyCallable.php index c98dae2..c229b0c 100644 --- a/src/Injection/LazyCallable.php +++ b/src/Injection/LazyCallable.php @@ -43,10 +43,10 @@ class LazyCallable implements LazyInterface * * Constructor. * - * @param callable $callable The callable to invoke. + * @param callable|array{0: LazyInterface, 1: string} $callable The callable to invoke. * */ - public function __construct($callable) + public function __construct(callable|array $callable) { $this->callable = $callable; } diff --git a/src/Injection/LazyLazy.php b/src/Injection/LazyLazy.php index 6a9653b..b990038 100644 --- a/src/Injection/LazyLazy.php +++ b/src/Injection/LazyLazy.php @@ -38,15 +38,6 @@ class LazyLazy */ protected LazyInterface $lazy; - /** - * - * Arguments for the callable. - * - * @var array - * - */ - protected array $params; - /** * * Constructor. @@ -56,11 +47,10 @@ class LazyLazy * @param LazyInterface $lazy The service container. * */ - public function __construct(Resolver $resolver, LazyInterface $lazy, array $params = []) + public function __construct(Resolver $resolver, LazyInterface $lazy) { $this->resolver = $resolver; $this->lazy = $lazy; - $this->params = $params; } /** @@ -70,8 +60,8 @@ public function __construct(Resolver $resolver, LazyInterface $lazy, array $para * @return mixed The value created by the lazy object * */ - public function __invoke(): mixed + public function __invoke(...$params): mixed { - return \call_user_func($this->lazy, $this->resolver, ...$this->params); + return \call_user_func($this->lazy, $this->resolver, ...$params); } } diff --git a/tests/ContainerTest.php b/tests/ContainerTest.php index 04bd785..338cc77 100644 --- a/tests/ContainerTest.php +++ b/tests/ContainerTest.php @@ -185,6 +185,20 @@ public function testLazyLazy() $this->assertInstanceOf('Aura\Di\Fake\FakeOtherClass', $callable()); } + public function testLazyLazyParams() + { + $callable = $this->container->lazyLazy( + $this->container->lazyCallable([ + $this->container->lazyNew('Aura\Di\Fake\FakeControllerClass', [ + 'foo' => 'bar' + ]), + 'process' + ]) + ); + + $this->assertSame(4, $callable(2)); + } + public function testLazyNewWithVariadic() { // Variadics are only available in PHP >= 5.6, and not in HHVM diff --git a/tests/Fake/FakeControllerClass.php b/tests/Fake/FakeControllerClass.php new file mode 100644 index 0000000..5485d6c --- /dev/null +++ b/tests/Fake/FakeControllerClass.php @@ -0,0 +1,17 @@ +foo = $foo; + } + + public function process($param1) + { + return \pow($param1, 2); + } +}