Skip to content

Commit

Permalink
Add Container->resolve(Closure)
Browse files Browse the repository at this point in the history
  • Loading branch information
Chemaclass committed May 18, 2023
1 parent 56777d0 commit 1124fbb
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 2 deletions.
23 changes: 22 additions & 1 deletion src/Container/Container.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use Closure;
use Gacela\Container\Exception\ContainerException;
use ReflectionFunction;
use SplObjectStorage;

use function count;
Expand All @@ -17,7 +18,7 @@ class Container implements ContainerInterface
{
private ?DependencyResolver $dependencyResolver = null;

/** @var array<class-string,list<mixed>> */
/** @var array<class-string|string, list<mixed>> */
private array $cachedDependencies = [];

/** @var array<string,mixed> */
Expand Down Expand Up @@ -52,6 +53,11 @@ public static function create(string $className): mixed
return (new self())->get($className);
}

public static function resolveClosure(Closure $closure): mixed
{
return (new self())->resolve($closure);
}

public function has(string $id): bool
{
return isset($this->instances[$id]);
Expand Down Expand Up @@ -84,6 +90,21 @@ public function get(string $id): mixed
return $this->createInstance($id);
}

public function resolve(Closure $closure): mixed
{
$reflectionFn = new ReflectionFunction($closure);
$callableKey = md5(serialize($reflectionFn->__toString()));

if (!isset($this->cachedDependencies[$callableKey])) {
$this->cachedDependencies[$callableKey] = $this
->getDependencyResolver()
->resolveDependencies($closure);
}

/** @psalm-suppress MixedMethodCall */
return $closure(...$this->cachedDependencies[$callableKey]);
}

public function factory(Closure $instance): Closure
{
$this->factoryInstances->attach($instance);
Expand Down
2 changes: 2 additions & 0 deletions src/Container/ContainerInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ interface ContainerInterface extends PsrContainerInterface
*/
public function get(string $id): mixed;

public function resolve(Closure $closure): mixed;

/**
* Check if an instance exists.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
use GacelaTest\Fake\PersonInterface;
use PHPUnit\Framework\TestCase;

final class ContainerTest extends TestCase
final class ClassContainerTest extends TestCase
{
public function test_static_create_without_dependencies(): void
{
Expand Down
79 changes: 79 additions & 0 deletions tests/Unit/ClosureContainerTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<?php

declare(strict_types=1);

namespace GacelaTest\Unit;

use Gacela\Container\Container;
use GacelaTest\Fake\ClassWithInterfaceDependencies;
use GacelaTest\Fake\ClassWithoutDependencies;
use GacelaTest\Fake\ClassWithRelationship;
use GacelaTest\Fake\Person;
use GacelaTest\Fake\PersonInterface;
use PHPUnit\Framework\TestCase;

final class ClosureContainerTest extends TestCase
{
public function test_static_create_without_dependencies(): void
{
$actual = Container::resolveClosure(static fn () => '');

self::assertSame('', $actual);
}

public function test_static_resolve_callable_with_inner_dependencies_without_dependencies(): void
{
$actual = Container::resolveClosure(
static fn (ClassWithoutDependencies $object) => serialize($object),
);

self::assertEquals(
new ClassWithoutDependencies(),
unserialize($actual),
);
}

public function test_static_resolve_callable_with_inner_dependencies_with_many_dependencies(): void
{
$actual = Container::resolveClosure(
static fn (ClassWithRelationship $object) => serialize($object),
);

self::assertEquals(
new ClassWithRelationship(new Person(), new Person()),
unserialize($actual),
);
}

public function test_use_mapped_interface_dependency(): void
{
$container = new Container([
PersonInterface::class => Person::class,
]);

$actual = $container->resolve(
static fn (ClassWithInterfaceDependencies $object) => serialize($object),
);

self::assertEquals(
new ClassWithInterfaceDependencies(new Person()),
unserialize($actual),
);
}

public function test_resolve_object_from_callable(): void
{
$person = new Person();
$person->name = 'person-name';

$container = new Container([
PersonInterface::class => static fn () => $person,
]);

$actual = $container->resolve(
static fn (PersonInterface $object) => serialize($object),
);

self::assertEquals($person, unserialize($actual));
}
}

0 comments on commit 1124fbb

Please sign in to comment.