Skip to content
Snippets Groups Projects
Commit 3343ecf9 authored by Jan Kuchař's avatar Jan Kuchař
Browse files

assertion errors are now errors not exceptions

parent 6520cf00
Branches
Tags
No related merge requests found
...@@ -3,35 +3,54 @@ namespace Grifart\AssertFunction; ...@@ -3,35 +3,54 @@ namespace Grifart\AssertFunction;
use Throwable; use Throwable;
final class AssertFunctionException extends \LogicException {
final class AssertFunctionError extends \AssertionError {
const PATH_DEPTH = 3;
/** @internal use named constructors instead */ /** @internal use named constructors instead */
public function __construct($message, \Throwable $previous = NULL) public function __construct(\ReflectionFunction $reflection, string $message, \Throwable $previous = NULL)
{ {
parent::__construct($message, 0, $previous); parent::__construct(
self::location($reflection) . ' ' . $message,
0,
$previous
);
} }
private static function location(\ReflectionFunction $reflection): string private static function location(\ReflectionFunction $reflection): string
{ {
return $reflection->getFileName() . ':' . $reflection->getStartLine() . '-' . $reflection->getEndLine() . ' '; $startLine = (string) $reflection->getStartLine();
$endLine = (string) $reflection->getEndLine();
$linePosition = ($startLine === $endLine) ? $startLine
: ($startLine . '-' . $endLine);
return self::shortenPath($reflection->getFileName()) . ':' . $linePosition;
} }
public static function cannotStartReflection(\ReflectionException $exception): self public static function wrongNumberOrArguments(\ReflectionFunction $reflection, int $numberOfParameters, int $actualCount): self
{ {
return new self('Cannot start reflection for given function.', 0, $exception); return new self($reflection, "Given wrong number of parameters. Expected $numberOfParameters, $actualCount given.");
} }
public static function wrongNumberOrArguments(\ReflectionFunction $reflection, int $numberOfParameters, int $actualCount): self public static function missingReturnType(\ReflectionFunction $reflection, string $type): self
{ {
return new self(self::location($reflection) . "Given wrong number of parameters. Expected $numberOfParameters, $actualCount given."); return new self($reflection, "Function is required to have return type of type $type.");
} }
public static function missingReturnType(\ReflectionFunction $reflection, string $type): self private static function shortenPath(string $path): string
{ {
return new self(self::location($reflection) . "Function is required to have return type of type $type."); $pathArr = explode(DIRECTORY_SEPARATOR, $path);
$pathNumParts = count($pathArr);
$pathParts = array_filter($pathArr, function ($val, $idx) use ($pathNumParts) { // only last x parts
return $idx >= $pathNumParts - self::PATH_DEPTH;
}, ARRAY_FILTER_USE_BOTH);
return implode('/', $pathParts); // normalized shortened readable path
} }
}; };
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
// todo: use only assert( ... ) instead of exceptions; to be compatible without configuration with PHP env // todo: use only assert( ... ) instead of exceptions; to be compatible without configuration with PHP env
// todo: [performance] separate code itself into autoloaded class // todo: [performance] separate code itself into autoloaded class
// todo: better assertion messages // todo: better assertion messages + throw AssertFunctionError
// todo: covariance and contra-variance // todo: covariance and contra-variance
namespace Grifart\AssertFunction; namespace Grifart\AssertFunction;
...@@ -22,18 +22,21 @@ function optional(string $classType): string ...@@ -22,18 +22,21 @@ function optional(string $classType): string
function assertFunction(callable $function, array $parameters, ?string $expectedReturnType): void function assertFunction(callable $function, array $parameters, ?string $expectedReturnType): void
{ {
try { // todo: call assert(__impl());
$reflection = new \ReflectionFunction($function); $reflection = new \ReflectionFunction($function);
} catch (\ReflectionException $exception) {
throw AssertFunctionException::cannotStartReflection($exception);
}
// PARAMETERS CHECK: // NUMBER PARAMETERS CHECK:
$numberOfParameters = $reflection->getNumberOfParameters(); $numberOfParameters = $reflection->getNumberOfParameters();
assert(
$numberOfParameters === count($parameters),
AssertFunctionError::wrongNumberOrArguments($reflection, count($parameters), $numberOfParameters)
);
if ($numberOfParameters !== count($parameters)) { if ($numberOfParameters !== count($parameters)) {
throw AssertFunctionException::wrongNumberOrArguments($reflection, count($parameters), $numberOfParameters); return;
} }
// PARAMETER TYPES CHECK:
$i = 0; $i = 0;
/** @var string[] $parameters */ /** @var string[] $parameters */
foreach($parameters AS $parameter) { foreach($parameters AS $parameter) {
...@@ -48,26 +51,28 @@ function assertFunction(callable $function, array $parameters, ?string $expected ...@@ -48,26 +51,28 @@ function assertFunction(callable $function, array $parameters, ?string $expected
// RETURN TYPE: // RETURN TYPE:
if($expectedReturnType !== NULL) { if($expectedReturnType !== NULL) {
assert(
$reflection->hasReturnType(),
AssertFunctionError::missingReturnType($reflection, $expectedReturnType)
);
if (!$reflection->hasReturnType()) { if ($reflection->hasReturnType()) {
throw AssertFunctionException::missingReturnType($reflection, $expectedReturnType); list($_returnType, $_optional) = __parseType($expectedReturnType);
} $returnTypeReflection = $reflection->getReturnType();
list($_returnType, $_optional) = __parseType($expectedReturnType); assert($_optional === $returnTypeReflection->allowsNull());
$returnTypeReflection = $reflection->getReturnType(); assert($_returnType === (string) $reflection->getReturnType());
}
assert($_optional === $returnTypeReflection->allowsNull());
assert($_returnType === (string) $reflection->getReturnType());
} }
} }
function __parseType(string $type): array function __parseType(string $type): array
{ {
$cleanType = $type; $cleanType = $type; // e.g. ?Namespace\Class
$optional = FALSE; $optional = FALSE;
if ($type[0] === '?') { if ($type[0] === '?') {
$optional = TRUE; $optional = TRUE;
$cleanType = substr($type, 1); // without leading ? $cleanType = substr($type, 1); // without leading '?'
} }
return [$cleanType, $optional]; return [$cleanType, $optional];
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment