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