From 275a5a307b6c323bd995f3038239fae4551479d5 Mon Sep 17 00:00:00 2001 From: Rodolfo Berrios <20590102+rodber@users.noreply.github.com> Date: Tue, 21 Jan 2025 09:09:56 -0300 Subject: [PATCH] improve assert --- src/Dependencies.php | 91 ++++++++++++++++-------- src/Interfaces/DependenciesInterface.php | 9 +-- 2 files changed, 67 insertions(+), 33 deletions(-) diff --git a/src/Dependencies.php b/src/Dependencies.php index ee6f38c..720bf75 100644 --- a/src/Dependencies.php +++ b/src/Dependencies.php @@ -37,7 +37,7 @@ final class Dependencies implements DependenciesInterface /** * @var array */ - private array $definedAt = []; + private array $requirer = []; /** * [className => ParametersInterface,] @@ -95,17 +95,36 @@ public function extract(string $className, array $container): array return (new Arguments($parameters, $extracted))->toArray(); } - public function assert(array $container): void + public function assert(mixed ...$argument): void { $errors = []; - foreach ($container as $key => $value) { + foreach ($this->parameters as $key => $parameter) { $key = (string) $key; - if (! $this->parameters->has($key)) { + $hasArgument = array_key_exists($key, $argument); + if (! $hasArgument + && $this->parameters->optionalKeys()->contains($key) + ) { + continue; + } + $requirer = $this->requirer($key); + $reflector = new ReflectionMethod($requirer, '__construct'); + $fileLine = $reflector->getFileName() . ':' . $reflector->getStartLine(); + if (! $hasArgument) { + $errors[] = (string) message( + <<parameters->get($key); try { + /** @var mixed $value */ + $value = $argument[$key]; // @phpstan-ignore-next-line $parameter($value); } catch (Throwable) { @@ -113,37 +132,47 @@ public function assert(array $container): void if (is_object($value)) { $type = get_class($value); } - $definedAt = $this->definedAt($key); - $reflector = new ReflectionMethod($definedAt, '__construct'); $errors[] = (string) message( <<type()->typeHinting(), - definedAt: $definedAt, - fileLine: $reflector->getFileName() . ':' . $reflector->getStartLine(), + requirer: $requirer, + fileLine: $fileLine, ); } } if ($errors !== []) { - throw new LogicException( - implode("\n\n", array_map( - fn ($i, $error) => '[' . ($i + 1) . '] ' . $error, - array_keys($errors), - $errors - )) - ); + $message = $this->errorMessage($errors); + + throw new LogicException($message); + } + } + + public function requirer(string $name): string + { + if (array_key_exists($name, $this->requirer)) { + return $this->requirer[$name]; } + + throw new OutOfBoundsException( + "Dependency `\${$name}` not defined" + ); } - public function definedAt(string $name): string + /** + * @param array $errors + */ + private function errorMessage(array $errors): string { - return array_key_exists($name, $this->definedAt) - ? $this->definedAt[$name] - : throw new OutOfBoundsException( - "Dependency `\${$name}` not defined" - ); + return count($errors) === 1 + ? $errors[0] + : implode("\n\n", array_map( + fn ($i, $error) => '- [' . ($i + 1) . '] ' . $error, + array_keys($errors), + $errors + )); } private function addRoute(RouteInterface $route): void @@ -166,6 +195,7 @@ private function setMiddleware(EndpointInterface $endpoint): void private function handleParameters(string $className): void { + $errors = []; if (! method_exists($className, '__construct')) { return; } @@ -183,17 +213,20 @@ private function handleParameters(string $className): void try { $existing->assertCompatible($parameter); } catch (Throwable $e) { - throw new TypeError( - <<type()->typeHinting()}` - PLAIN - ); + $errors[] = <<type()->typeHinting()}` + PLAIN; } $parameters = $parameters->without($name); } + if ($errors !== []) { + $message = $this->errorMessage($errors); + + throw new TypeError($message); + } $this->parameters = $this->parameters->withMerge($parameters); foreach ($parameters->keys() as $key) { - $this->definedAt[$key] = $className; + $this->requirer[$key] = $className; } } } diff --git a/src/Interfaces/DependenciesInterface.php b/src/Interfaces/DependenciesInterface.php index 6209006..2c86f03 100644 --- a/src/Interfaces/DependenciesInterface.php +++ b/src/Interfaces/DependenciesInterface.php @@ -50,12 +50,13 @@ public function get(string $className): ParametersInterface; */ public function extract(string $className, array $container): array; - public function definedAt(string $name): string; + /** + * Indicates the class name which declared the given dependency. + */ + public function requirer(string $name): string; /** * Asserts that the given container has all dependencies. - * - * @param array $container Service container */ - public function assert(array $container): void; + public function assert(mixed ...$container): void; }