From 3343ecf98c04c94a75f800db555c0f7dfc99a558 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Kucha=C5=99?= <honza.kuchar@grifart.cz> Date: Thu, 27 Apr 2017 08:33:36 +0200 Subject: [PATCH] assertion errors are now errors not exceptions --- src/exceptions.php | 39 +++++++++++++++++++++++++++++---------- src/functions.php | 41 +++++++++++++++++++++++------------------ 2 files changed, 52 insertions(+), 28 deletions(-) diff --git a/src/exceptions.php b/src/exceptions.php index ab8b81d..1aa7d8b 100644 --- a/src/exceptions.php +++ b/src/exceptions.php @@ -3,35 +3,54 @@ namespace Grifart\AssertFunction; use Throwable; -final class AssertFunctionException extends \LogicException { + + +final class AssertFunctionError extends \AssertionError { + + const PATH_DEPTH = 3; /** @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 { - 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 } }; diff --git a/src/functions.php b/src/functions.php index ed9a64f..103cd51 100644 --- a/src/functions.php +++ b/src/functions.php @@ -5,7 +5,7 @@ // todo: use only assert( ... ) instead of exceptions; to be compatible without configuration with PHP env // todo: [performance] separate code itself into autoloaded class -// todo: better assertion messages +// todo: better assertion messages + throw AssertFunctionError // todo: covariance and contra-variance namespace Grifart\AssertFunction; @@ -22,18 +22,21 @@ function optional(string $classType): string function assertFunction(callable $function, array $parameters, ?string $expectedReturnType): void { - try { - $reflection = new \ReflectionFunction($function); - } catch (\ReflectionException $exception) { - throw AssertFunctionException::cannotStartReflection($exception); - } + // todo: call assert(__impl()); + $reflection = new \ReflectionFunction($function); - // PARAMETERS CHECK: + // NUMBER PARAMETERS CHECK: $numberOfParameters = $reflection->getNumberOfParameters(); + assert( + $numberOfParameters === count($parameters), + AssertFunctionError::wrongNumberOrArguments($reflection, count($parameters), $numberOfParameters) + ); if ($numberOfParameters !== count($parameters)) { - throw AssertFunctionException::wrongNumberOrArguments($reflection, count($parameters), $numberOfParameters); + return; } + + // PARAMETER TYPES CHECK: $i = 0; /** @var string[] $parameters */ foreach($parameters AS $parameter) { @@ -48,26 +51,28 @@ function assertFunction(callable $function, array $parameters, ?string $expected // RETURN TYPE: if($expectedReturnType !== NULL) { + assert( + $reflection->hasReturnType(), + AssertFunctionError::missingReturnType($reflection, $expectedReturnType) + ); - if (!$reflection->hasReturnType()) { - throw AssertFunctionException::missingReturnType($reflection, $expectedReturnType); - } + if ($reflection->hasReturnType()) { + list($_returnType, $_optional) = __parseType($expectedReturnType); + $returnTypeReflection = $reflection->getReturnType(); - list($_returnType, $_optional) = __parseType($expectedReturnType); - $returnTypeReflection = $reflection->getReturnType(); - - assert($_optional === $returnTypeReflection->allowsNull()); - assert($_returnType === (string) $reflection->getReturnType()); + assert($_optional === $returnTypeReflection->allowsNull()); + assert($_returnType === (string) $reflection->getReturnType()); + } } } function __parseType(string $type): array { - $cleanType = $type; + $cleanType = $type; // e.g. ?Namespace\Class $optional = FALSE; if ($type[0] === '?') { $optional = TRUE; - $cleanType = substr($type, 1); // without leading ? + $cleanType = substr($type, 1); // without leading '?' } return [$cleanType, $optional]; } -- GitLab