Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
rodber committed Jan 4, 2024
1 parent d1db73e commit 515b67f
Show file tree
Hide file tree
Showing 7 changed files with 251 additions and 13 deletions.
30 changes: 30 additions & 0 deletions src/Exceptions/ParameterException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

/*
* This file is part of Chevere.
*
* (c) Rodolfo Berrios <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace Chevere\Parameter\Exceptions;

use Exception;
use Throwable;

/**
* Exception thrown when validating parameter(s).
*/
final class ParameterException extends Exception
{
public function __construct(string $message, Throwable $previous, string $file, int $line)
{
parent::__construct(message: $message, previous: $previous);
$this->file = $file;
$this->line = $line;
}
}
30 changes: 30 additions & 0 deletions src/Exceptions/ReturnException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

/*
* This file is part of Chevere.
*
* (c) Rodolfo Berrios <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace Chevere\Parameter\Exceptions;

use Exception;
use Throwable;

/**
* Exception thrown when validating return.
*/
final class ReturnException extends Exception
{
public function __construct(string $message, Throwable $previous, string $file, int $line)
{
parent::__construct(message: $message, previous: $previous);
$this->file = $file;
$this->line = $line;
}
}
74 changes: 68 additions & 6 deletions src/functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@

use ArrayAccess;
use Chevere\Parameter\Attributes\ReturnAttr;
use Chevere\Parameter\Exceptions\ParameterException;
use Chevere\Parameter\Exceptions\ReturnException;
use Chevere\Parameter\Interfaces\ArgumentsInterface;
use Chevere\Parameter\Interfaces\ArrayParameterInterface;
use Chevere\Parameter\Interfaces\CastInterface;
Expand Down Expand Up @@ -232,7 +234,10 @@ function parameterAttr(string $parameter, array $caller): ParameterAttributeInte
}

throw new LogicException(
(string) message('No parameter attribute for `%name%`', name: $parameter)
(string) message(
"Parameter `%name%` doesn't exists",
name: $parameter
)
);
}

Expand Down Expand Up @@ -309,7 +314,11 @@ function reflectedParameterAttribute(
$attributes = $reflection->getAttributes(ParameterAttributeInterface::class, ReflectionAttribute::IS_INSTANCEOF);
if ($attributes === []) {
throw new LogicException(
(string) message('No attribute for parameter `%name%`', name: $reflection->getName())
(string) message(
'No `%type%` attribute for parameter `%name%`',
type: ParameterAttributeInterface::class,
name: $reflection->getName()
)
);
}
/** @var ReflectionAttribute<ParameterAttributeInterface> $attribute */
Expand All @@ -322,9 +331,62 @@ function validated(
ReflectionFunction|ReflectionMethod $reflection,
mixed ...$args
): mixed {
$parameters = reflectionToParameters($reflection);
$return = reflectionToReturnParameter($reflection);
$result = $parameters(...$args);
try {
$parameters = reflectionToParameters($reflection);
$return = reflectionToReturnParameter($reflection);
$parameters(...$args);
} catch (Throwable $e) {
// // @infection-ignore-all
throw new ParameterException(
...getExceptionArguments($e, $reflection),
);
}
if ($reflection instanceof ReflectionMethod) {
$result = $reflection->invoke(null, ...$args);
} else {
$result = $reflection->invoke(...$args);
}

try {
$return->__invoke($result);
} catch (Throwable $e) {
// @infection-ignore-all
throw new ReturnException(
...getExceptionArguments($e, $reflection),
);
}

return $return;
}

return $return->__invoke($result);
/**
* @return array{0: string, 1: Throwable, 2: string, 3: int}
*/
function getExceptionArguments(Throwable $e, ReflectionFunction|ReflectionMethod $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,
message: $e->getMessage(),
);

// @infection-ignore-all
return [
$message,
$e,
$caller['file'] ?? 'na',
$caller['line'] ?? 0,
];
}
67 changes: 62 additions & 5 deletions tests/FunctionsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,13 @@
namespace Chevere\Tests;

use ArgumentCountError;
use Chevere\Parameter\Exceptions\ParameterException;
use Chevere\Parameter\Exceptions\ReturnException;
use InvalidArgumentException;
use LogicException;
use OutOfBoundsException;
use PHPUnit\Framework\TestCase;
use ReflectionFunction;
use stdClass;
use TypeError;
use function Chevere\Parameter\arguments;
Expand All @@ -25,17 +29,20 @@
use function Chevere\Parameter\assertArray;
use function Chevere\Parameter\assertNamedArgument;
use function Chevere\Parameter\bool;
use function Chevere\Parameter\cast;
use function Chevere\Parameter\float;
use function Chevere\Parameter\getType;
use function Chevere\Parameter\int;
use function Chevere\Parameter\iterable;
use function Chevere\Parameter\null;
use function Chevere\Parameter\object;
use function Chevere\Parameter\parameterAttr;
use function Chevere\Parameter\parameters;
use function Chevere\Parameter\parametersFrom;
use function Chevere\Parameter\string;
use function Chevere\Parameter\takeFrom;
use function Chevere\Parameter\takeKeys;
use function Chevere\Parameter\validated;

final class FunctionsTest extends TestCase
{
Expand Down Expand Up @@ -221,7 +228,7 @@ public function testAssertArrayExtraArguments(): void
]);
}

public function testAssertArrayConflictType(): void
public function testAssertArrayErrorType(): void
{
$parameter = arrayp(
OK: string(),
Expand All @@ -232,7 +239,7 @@ public function testAssertArrayConflictType(): void
]);
}

public function testAssertArrayConflictNull(): void
public function testAssertArrayErrorNull(): void
{
$parameter = arrayp(
OK: string(),
Expand Down Expand Up @@ -342,15 +349,65 @@ public function testArrayFrom(): void
);
}

public function testVariableType(): void
public function testGetType(): void
{
$table = [
'object' => $this,
'float' => 10.10,
'null' => null,
];
foreach ($table as $type => $variable) {
$this->assertSame($type, getType($variable));
foreach ($table as $type => $value) {
$this->assertSame($type, getType($value));
}
}

public function testCast(): void
{
$value = 'string';
$cast = cast($value);
$this->assertSame($value, $cast->string());
}

public function testParameterAttr(): void
{
$caller = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1)[0];
$this->expectException(LogicException::class);
$this->expectExceptionMessage("Parameter `foo` doesn't exists");
parameterAttr('foo', $caller);
}

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);
}

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);
}

public function testValidated(): void
{
$function = 'Chevere\Tests\src\validates';
$reflection = new ReflectionFunction($function);
$base = 100;
$times = 1;
$name = 'Test';
$this->expectNotToPerformAssertions();
validated($reflection, $base, $times, $name);
}
}
35 changes: 34 additions & 1 deletion tests/ReflectionFunctionsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
use ReflectionFunction;
use ReflectionMethod;
use function Chevere\Parameter\arguments;
use function Chevere\Parameter\bool;
use function Chevere\Parameter\reflectionToParameters;
use function Chevere\Parameter\reflectionToReturnParameter;

Expand Down Expand Up @@ -50,7 +51,7 @@ public function testAnonClassParameters(): void
$class = new class() {
public function wea(
#[IntAttr(accept: [1, 10, 100])]
int $base
int $base = 1
): void {
}
};
Expand Down Expand Up @@ -81,4 +82,36 @@ function (int $base): int {
$this->expectExceptionMessage('Argument value provided `100` is less than `1000`');
$return($function(10));
}

public function dataProviderFunctionToReturnUses(): array
{
return [
['Chevere\Tests\src\usesAttr'],
['Chevere\Tests\src\noUsesAttr'],
];
}

/**
* @dataProvider dataProviderFunctionToReturnUses
*/
public function testFunctionToReturnUses(string $function): void
{
$this->expectNotToPerformAssertions();
$reflection = new ReflectionFunction($function);
$return = reflectionToReturnParameter($reflection);
$return->assertCompatible(bool());
}

public function testWithDefaultError(): void
{
$function = 'Chevere\Tests\src\withDefaultError';
$reflection = new ReflectionFunction($function);
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage(
<<<PLAIN
Unable to use default value for parameter `int` in `Chevere\Tests\src\withDefaultError`: Argument value provided `1` is less than `2`
PLAIN
);
reflectionToParameters($reflection);
}
}
6 changes: 5 additions & 1 deletion tests/src/NoUsesAttr.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

namespace Chevere\Tests\src;

use Chevere\Parameter\Interfaces\ParameterAttributeInterface;
use Throwable;
use function Chevere\Parameter\Attributes\arrayArguments;
use function Chevere\Parameter\Attributes\arrayAttr;
Expand Down Expand Up @@ -54,7 +55,10 @@ public function __construct(
try {
$name = stringAttr('name')($name);
} catch (Throwable $e) {
assertSame('No attribute for parameter `name`', $e->getMessage());
assertSame(
'No `' . ParameterAttributeInterface::class . '` attribute for parameter `name`',
$e->getMessage()
);
}
// $age = intAttr('age')($age);
// $cols = arrayAttr('cols')($cols);
Expand Down
22 changes: 22 additions & 0 deletions tests/src/functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
use function Chevere\Parameter\Attributes\arrayArguments;
use function Chevere\Parameter\Attributes\arrayAttr;
use function Chevere\Parameter\Attributes\returnAttr;
use function Chevere\Parameter\Attributes\stringAttr;
use function Chevere\Parameter\Attributes\valid;
use function PHPUnit\Framework\assertSame;

Expand All @@ -41,7 +42,9 @@ function usesAttr(
)]
array $spooky
): bool {
valid();
valid('spooky');
arrayAttr('spooky')($spooky);
assertSame(
$spooky['id'],
arrayArguments('spooky')->required('id')->int()
Expand All @@ -57,3 +60,22 @@ function noUsesAttr(

return returnAttr()(true);
}

function withDefaultError(
#[IntAttr(min: 2)]
int $int = 1
): void {
}

#[ReturnAttr(
new IntAttr(min: 100, max: 200)
)]
function validates(
#[IntAttr(min: 1, max: 100)]
int $base,
#[IntAttr(min: 1, max: 5)]
int $times = 1,
string $name = '',
): int {
return $base * $times;
}

0 comments on commit 515b67f

Please sign in to comment.