Skip to content

Commit

Permalink
Improved dependency injections
Browse files Browse the repository at this point in the history
  • Loading branch information
brendt committed May 7, 2024
1 parent 7654f66 commit 4c2a2b9
Show file tree
Hide file tree
Showing 6 changed files with 50 additions and 35 deletions.
15 changes: 15 additions & 0 deletions src/Container/Dependency.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,21 @@ public function equals(self $other): bool
return $this->getName() === $other->getName();
}

public function getTypeName(): string
{
$dependency = $this->dependency;

return match($dependency::class) {
ReflectionClass::class => $dependency->getShortName(),
ReflectionMethod::class => $dependency->getDeclaringClass()->getShortName(),
ReflectionParameter::class => $this->resolveName($dependency->getType()),
ReflectionNamedType::class => $dependency->getName(),
ReflectionIntersectionType::class => $this->intersectionTypeToString($dependency),
ReflectionUnionType::class => $this->unionTypeToString($dependency),
default => 'unknown',
};
}

private function resolveName(ReflectionType|Reflector|string|Closure $dependency): string
{
if (is_string($dependency)) {
Expand Down
24 changes: 14 additions & 10 deletions src/Container/Exceptions/CannotAutowireException.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,19 @@
namespace Tempest\Container\Exceptions;

use Exception;
use Tempest\Container\Dependency;
use Tempest\Container\DependencyChain;

final class CannotAutowireException extends Exception
{
public function __construct(DependencyChain $chain)
public function __construct(DependencyChain $chain, Dependency $brokenDependency)
{
$stack = $chain->all();

$firstDependency = $chain->first();
$lastDependency = $chain->last();

$message = PHP_EOL . PHP_EOL . "Cannot autowire {$firstDependency->getName()} because {$lastDependency->getName()} cannot be resolved" . PHP_EOL;
$message = PHP_EOL . PHP_EOL . "Cannot autowire {$firstDependency->getName()} because {$brokenDependency->getName()} cannot be resolved" . PHP_EOL;

$i = 0;

Expand All @@ -32,15 +33,18 @@ public function __construct(DependencyChain $chain)
$i++;
}

$currentDependency = $lastDependency;
$currentDependencyName = $currentDependency->getShortName();
$firstPart = explode($currentDependencyName, $lastDependency->getShortName())[0] ?? null;
$fillerSpaces = str_repeat(' ', strlen($firstPart) + 3);
$fillerArrows = str_repeat('', strlen($currentDependencyName));
$message .= PHP_EOL . "\t {$fillerSpaces}{$fillerArrows}";

$message .= PHP_EOL . PHP_EOL;
$selectionLine = preg_replace_callback(
pattern: '/(?<prefix>(.*))(?<selection>'. $brokenDependency->getTypeName() .'\s\$\w+)(.*)/',
callback: function ($matches) {
return str_repeat(' ', strlen($matches['prefix']) + 4)
. str_repeat('', strlen($matches['selection']));
},
subject: $chain->last()->getShortName(),
);

$message .= PHP_EOL;
$message .= "\t{$selectionLine}";
$message .= PHP_EOL;
$message .= "Originally called in {$chain->getOrigin()}";
$message .= PHP_EOL;

Expand Down
26 changes: 12 additions & 14 deletions src/Container/Exceptions/CircularDependencyException.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,21 +33,19 @@ public function __construct(DependencyChain $chain, Dependency $circularDependen
$message .= PHP_EOL . "\t{$prefix} " . $currentDependency->getShortName();
}

$circularDependencyName = $circularDependency->getShortName();
$lastDependencyName = $chain->last()->getShortName();

$firstPart = explode($circularDependencyName, $lastDependencyName)[0] ?? null;

if ($lastDependencyName === $firstPart) {
$fillerLines = str_repeat('', 3);
} else {
$fillerLines = str_repeat('', strlen($firstPart) + 3);
}

$fillerArrows = str_repeat('', strlen($circularDependencyName));
$message .= PHP_EOL . "\t{$fillerLines}{$fillerArrows}";
$message .= PHP_EOL . PHP_EOL;
$selectionLine = preg_replace_callback(
pattern: '/(?<prefix>(.*))(?<selection>'. $circularDependency->getTypeName() .'\s\$\w+)(.*)/',
callback: function ($matches) {
return ""
. str_repeat('', strlen($matches['prefix']) + 3)
. str_repeat('', strlen($matches['selection']));
},
subject: $chain->last()->getShortName(),
);

$message .= PHP_EOL;
$message .= "\t{$selectionLine}";
$message .= PHP_EOL;
$message .= "Originally called in {$chain->getOrigin()}";
$message .= PHP_EOL;

Expand Down
8 changes: 3 additions & 5 deletions src/Container/GenericContainer.php
Original file line number Diff line number Diff line change
Expand Up @@ -244,8 +244,6 @@ private function autowireDependencies(ReflectionMethod $method, array $parameter

private function autowireDependency(ReflectionParameter $parameter, mixed $providedValue = null): mixed
{
// $this->resolveChain()->add($parameter);

$parameterType = $parameter->getType();

// If the parameter is a built-in type, immediately skip reflection
Expand Down Expand Up @@ -281,7 +279,7 @@ private function autowireDependency(ReflectionParameter $parameter, mixed $provi

// At this point, there is nothing else we can do; we don't know
// how to autowire this dependency.
throw $lastThrowable ?? new CannotAutowireException($this->chain);
throw $lastThrowable ?? new CannotAutowireException($this->chain, new Dependency($parameter));
}

private function autowireObjectDependency(ReflectionNamedType $type, mixed $providedValue): mixed
Expand All @@ -300,7 +298,7 @@ private function autowireObjectDependency(ReflectionNamedType $type, mixed $prov

// At this point, there is nothing else we can do; we don't know
// how to autowire this dependency.
throw new CannotAutowireException($this->chain);
throw new CannotAutowireException($this->chain, new Dependency($type));
}

private function autowireBuiltinDependency(ReflectionParameter $parameter, mixed $providedValue): mixed
Expand Down Expand Up @@ -335,7 +333,7 @@ private function autowireBuiltinDependency(ReflectionParameter $parameter, mixed

// At this point, there is nothing else we can do; we don't know
// how to autowire this dependency.
throw new CannotAutowireException($this->chain);
throw new CannotAutowireException($this->chain, new Dependency($parameter));
}

private function clone(): self
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,13 @@ public function test_autowire_without_exception()

$container->get(AutowireA::class);
} catch (CannotAutowireException $exception) {
$this->assertStringContainsString('Cannot autowire Tests\Tempest\Unit\Container\Fixtures\AutowireA::__construct because Tests\Tempest\Unit\Container\Fixtures\AutowireC::__construct cannot be resolved', $exception->getMessage());
$this->assertStringContainsString('Cannot autowire Tests\Tempest\Unit\Container\Fixtures\AutowireA::__construct because string cannot be resolved', $exception->getMessage());

$expected = <<<'TXT'
┌── AutowireA::__construct(AutowireB $b)
├── AutowireB::__construct(AutowireC $c)
└── AutowireC::__construct(ContainerObjectA $other, string $unknown)
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
TXT;

$this->assertStringContainsString($expected, $exception->getMessage());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
*/
class CircularDependencyExceptionTest extends TestCase
{
public function test_circular_dependency_test()
public function test_circular_dependency_test(): void
{
$this->expectException(CircularDependencyException::class);

Expand All @@ -31,7 +31,7 @@ public function test_circular_dependency_test()
┌─► CircularA::__construct(ContainerObjectA $other, CircularB $b)
│ CircularB::__construct(CircularC $c)
│ CircularC::__construct(ContainerObjectA $other, CircularA $a)
└───▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
└───────────────────────────────────────────────────▒▒▒▒▒▒▒▒▒▒▒▒
TXT;

$this->assertStringContainsString($expected, $exception->getMessage());
Expand All @@ -42,7 +42,7 @@ public function test_circular_dependency_test()
}
}

public function test_circular_dependency_as_a_child_test()
public function test_circular_dependency_as_a_child_test(): void
{
$this->expectException(CircularDependencyException::class);

Expand All @@ -58,7 +58,7 @@ public function test_circular_dependency_as_a_child_test()
┌─► CircularA::__construct(ContainerObjectA $other, CircularB $b)
│ CircularB::__construct(CircularC $c)
│ CircularC::__construct(ContainerObjectA $other, CircularA $a)
└───▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
└───────────────────────────────────────────────────▒▒▒▒▒▒▒▒▒▒▒▒
TXT;

$this->assertStringContainsString($expected, $exception->getMessage());
Expand Down

0 comments on commit 4c2a2b9

Please sign in to comment.