diff --git a/src/Attributes/functions.php b/src/Attributes/functions.php index 22d3325..b43c25c 100644 --- a/src/Attributes/functions.php +++ b/src/Attributes/functions.php @@ -13,6 +13,7 @@ namespace Chevere\Parameter\Attributes; +use Chevere\Parameter\Exceptions\ParameterException; use Chevere\Parameter\Interfaces\ArgumentsInterface; use Chevere\Parameter\Interfaces\ParameterInterface; use LogicException; @@ -185,23 +186,28 @@ function valid(?string $name = null): void return; } + if ($parameters->optionalKeys()->contains($name) + && ! array_key_exists($name, $arguments) + ) { + return; + } try { - $parameter = $parameters->get($name); - if ($parameters->optionalKeys()->contains($name) && ! array_key_exists($name, $arguments)) { - return; + if (! $parameters->has($name)) { + throw new LogicException( + (string) message( + 'Parameter `%name%` not found', + name: $name, + ) + ); } + $parameter = $parameters->get($name); $parameter->__invoke($arguments[$name]); } catch (Throwable $e) { $invoker = $trace[0]; + $file = $invoker['file'] ?? 'na'; + $line = $invoker['line'] ?? 0; - throw new $e( - (string) message( - '%message% → %invokedAt%', - message: $e->getMessage(), - // @phpstan-ignore-next-line - invokedAt: $invoker['file'] . ':' . $invoker['line'], - ) - ); + throw new ParameterException($e->getMessage(), $e, $file, $line); } } diff --git a/src/functions.php b/src/functions.php index 3ca80ee..7f5c8e7 100644 --- a/src/functions.php +++ b/src/functions.php @@ -327,10 +327,11 @@ function reflectedParameterAttribute( return $attribute->newInstance(); } -function validated( - ReflectionFunction|ReflectionMethod $reflection, - mixed ...$args -): mixed { +function validated(callable $callable, mixed ...$args): mixed +{ + // @phpstan-ignore-next-line + $reflection = new ReflectionFunction($callable); + try { $parameters = reflectionToParameters($reflection); $return = reflectionToReturnParameter($reflection); @@ -341,11 +342,7 @@ function validated( ...getExceptionArguments($e, $reflection), ); } - if ($reflection instanceof ReflectionMethod) { - $result = $reflection->invoke(null, ...$args); - } else { - $result = $reflection->invoke(...$args); - } + $result = $callable(...$args); try { $return->__invoke($result); @@ -362,23 +359,15 @@ function validated( /** * @return array{0: string, 1: Throwable, 2: string, 3: int} */ -function getExceptionArguments(Throwable $e, ReflectionFunction|ReflectionMethod $reflection): array +function getExceptionArguments(Throwable $e, ReflectionFunction $reflection): array { // @infection-ignore-all $caller = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2)[1]; - $class = $reflection instanceof ReflectionMethod - ? $reflection->getDeclaringClass()->getName() - : null; $function = $reflection->getName(); - $actor = match (true) { - $class === null => $function, - default => $class . '::' . $function, - }; - $message = (string) message( '`%actor%` %exception% → %message%', exception: $e::class, - actor: $actor, + actor: $function, message: $e->getMessage(), ); diff --git a/tests/FunctionsTest.php b/tests/FunctionsTest.php index f6f7c2b..883f397 100644 --- a/tests/FunctionsTest.php +++ b/tests/FunctionsTest.php @@ -20,7 +20,6 @@ use LogicException; use OutOfBoundsException; use PHPUnit\Framework\TestCase; -use ReflectionFunction; use stdClass; use TypeError; use function Chevere\Parameter\arguments; @@ -379,35 +378,32 @@ public function testParameterAttr(): void public function testValidatedParameterError(): void { $function = 'Chevere\Tests\src\validates'; - $reflection = new ReflectionFunction($function); $base = 0; $times = 1; $name = 'Test'; $this->expectException(ParameterException::class); $this->expectExceptionMessage("`{$function}` InvalidArgumentException → [base]: Argument value provided `0` is less than `1`"); - validated($reflection, $base, $times, $name); + validated($function, $base, $times, $name); } public function testValidatedReturnError(): void { $function = 'Chevere\Tests\src\validates'; - $reflection = new ReflectionFunction($function); $base = 99; $times = 1; $name = 'Test'; $this->expectException(ReturnException::class); $this->expectExceptionMessage("`{$function}` InvalidArgumentException → Argument value provided `99` is less than `100`"); - validated($reflection, $base, $times, $name); + validated($function, $base, $times, $name); } - public function testValidated(): void + public function testValidatedFunction(): void { $function = 'Chevere\Tests\src\validates'; - $reflection = new ReflectionFunction($function); $base = 100; $times = 1; $name = 'Test'; $this->expectNotToPerformAssertions(); - validated($reflection, $base, $times, $name); + validated($function, $base, $times, $name); } } diff --git a/tests/NullParameterTest.php b/tests/NullParameterTest.php index 9fb5182..c6d1c3e 100644 --- a/tests/NullParameterTest.php +++ b/tests/NullParameterTest.php @@ -23,14 +23,34 @@ public function testConstruct(): void { $parameter = new NullParameter(); $this->assertSame(null, $parameter->default()); - $compatible = new NullParameter(); - $parameter->assertCompatible($compatible); $this->assertSame([ 'type' => 'null', 'description' => '', 'default' => null, ], $parameter->schema()); $parameter(null); + } + + public function testWithDefault(): void + { + $parameter = new NullParameter(); + $with = $parameter->withDefault(null); + $this->assertNotSame($parameter, $with); + $parameter->assertCompatible($with); + $with(null); + } + + public function testCompatible(): void + { + $this->expectNotToPerformAssertions(); + $parameter = new NullParameter(); + $compatible = new NullParameter(); + $parameter->assertCompatible($compatible); + } + + public function testError(): void + { + $parameter = new NullParameter(); $this->expectException(TypeError::class); $this->expectExceptionMessage( <<getMessage() + ); + } // Get attribute, validate and return try { $name = stringAttr('name')($name);