Skip to content

Commit

Permalink
add tests and docs for LazyLazy
Browse files Browse the repository at this point in the history
  • Loading branch information
frederikbosch committed May 28, 2024
1 parent e3bffdf commit 8425c14
Show file tree
Hide file tree
Showing 8 changed files with 70 additions and 25 deletions.
26 changes: 26 additions & 0 deletions docs/lazy.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
4 changes: 2 additions & 2 deletions docs/migrating.md
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
10 changes: 4 additions & 6 deletions src/Container.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

/**
Expand All @@ -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);
}
Expand Down
4 changes: 2 additions & 2 deletions src/Injection/InjectionFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
4 changes: 2 additions & 2 deletions src/Injection/LazyCallable.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
16 changes: 3 additions & 13 deletions src/Injection/LazyLazy.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,6 @@ class LazyLazy
*/
protected LazyInterface $lazy;

/**
*
* Arguments for the callable.
*
* @var array
*
*/
protected array $params;

/**
*
* Constructor.
Expand All @@ -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;
}

/**
Expand All @@ -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);
}
}
14 changes: 14 additions & 0 deletions tests/ContainerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
17 changes: 17 additions & 0 deletions tests/Fake/FakeControllerClass.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php
namespace Aura\Di\Fake;

class FakeControllerClass
{
private $foo;

public function __construct($foo)
{
$this->foo = $foo;
}

public function process($param1)
{
return \pow($param1, 2);
}
}

0 comments on commit 8425c14

Please sign in to comment.