diff --git a/phpstan.neon b/phpstan.neon index 342b536..11fecc2 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -8,4 +8,5 @@ parameters: #- identifier: instanceof.alwaysTrue #- identifier: instanceof.alwaysFalse - identifier: identical.alwaysFalse - # - '#Strict comparison using === between true and false will always evaluate to false#' \ No newline at end of file + # - '#Strict comparison using === between true and false will always evaluate to false#' + - '#Result of && is always false#' \ No newline at end of file diff --git a/src/ArraySerializerTrait.php b/src/ArraySerializerTrait.php index c0b703a..9a32182 100644 --- a/src/ArraySerializerTrait.php +++ b/src/ArraySerializerTrait.php @@ -4,19 +4,21 @@ namespace IfCastle\Exceptions; +use PHPUnit\Event\Code\Throwable; + trait ArraySerializerTrait { /** * The method defines the source of the exception. - * - * + * @return array|string */ abstract protected function getSourceFor(\Throwable $e, bool $isString = false): array|string; /** * The method serialized errors BaseExceptionI to an array. * - * @param array|BaseExceptionInterface $errors array of errors + * @param BaseExceptionInterface[]|\Throwable[]|BaseExceptionInterface $errors array of errors + * @return array */ protected function errorsToArray(mixed $errors): array { @@ -48,7 +50,7 @@ protected function errorsToArray(mixed $errors): array /** * The method deserialized array of array to array of errors. * - * @param array $array array of array + * @param array> $array array of array * @param string $class class for exception * * @return BaseException[] diff --git a/src/BaseException.php b/src/BaseException.php index a1d6005..183c1f7 100644 --- a/src/BaseException.php +++ b/src/BaseException.php @@ -42,6 +42,12 @@ class BaseException extends \Exception implements BaseExceptionInterface use HelperTrait; use ArraySerializerTrait; use TemplateHandlerTrait; + + /** + * @param \Throwable|BaseExceptionInterface|null $throwable + * + * @return array> + */ public static function serializeToArray(\Throwable|BaseExceptionInterface|null $throwable = null): array { if ($throwable instanceof BaseExceptionInterface) { @@ -59,6 +65,7 @@ public static function serializeToArray(\Throwable|BaseExceptionInterface|null $ /** * Layout of the default properties. + * @var array */ protected static array $baseProps = ['message' => '', 'code' => 0, 'previous' => null, 'template' => '', 'tags' => []]; @@ -69,6 +76,7 @@ public static function serializeToArray(\Throwable|BaseExceptionInterface|null $ /** * Extra data to exception. + * @var array> */ protected array $data = []; @@ -80,11 +88,13 @@ public static function serializeToArray(\Throwable|BaseExceptionInterface|null $ /** * Source of error. + * @var array */ protected ?array $source = null; /** * Debug data. + * @var array> */ protected array $debugData = []; @@ -127,7 +137,7 @@ public static function serializeToArray(\Throwable|BaseExceptionInterface|null $ * In this case, exception acts as container, * and inherits data from the $exception * - * @param BaseExceptionInterface|\Throwable|array|string $exception Exception data + * @param BaseExceptionInterface|\Throwable|array|scalar[]>|string $exception Exception data * @param int $code Code * @param \Throwable|null $previous Previous or aggregate exception */ @@ -421,6 +431,7 @@ public function toArray(): array * Returns information about a value type. * * @param mixed $value Value + * @return string|array */ protected function typeInfo(mixed $value): string|array { @@ -442,7 +453,7 @@ protected function isDebug(): bool /** * The method saved debug data if debug mode is active. * - * @param array $data debug data + * @param array $data debug data * @return $this */ protected function setDebugData(array $data): static diff --git a/src/BaseExceptionInterface.php b/src/BaseExceptionInterface.php index b011f23..b83b761 100644 --- a/src/BaseExceptionInterface.php +++ b/src/BaseExceptionInterface.php @@ -7,7 +7,7 @@ /** * Main interface for BaseException. */ -interface BaseExceptionInterface extends \Stringable +interface BaseExceptionInterface extends \Throwable { /** * The System is unusable. @@ -64,19 +64,19 @@ interface BaseExceptionInterface extends \Stringable */ final public const int RESULT = 1; - public function getMessage(); + public function getMessage(): string; - public function getPrevious(); + public function getPrevious(): ?\Throwable; public function getCode(); - public function getFile(); + public function getFile(): string; - public function getLine(); + public function getLine(): int; - public function getTrace(); + public function getTrace(): array; - public function getTraceAsString(); + public function getTraceAsString(): string; /** * Template message. @@ -142,6 +142,8 @@ public function getLevel(): int; * ] * * Attention to the order of elements in the array is important! + * + * @return array|null */ public function getSource(): ?array; @@ -159,21 +161,27 @@ public function getPreviousException(): \Throwable|BaseExceptionInterface|null; /** * The method returns extra data for exception. + * + * @return array */ public function getExceptionData(): array; /** - * @param array $data The additional data + * @param array $data The additional data */ public function appendData(array $data): static; /** * The method returns debug data for exception. + * + * @return array */ public function getDebugData(): array; /** * The method serialized object to an array. + * + * @return array */ public function toArray(): array; } diff --git a/src/ClassNotExist.php b/src/ClassNotExist.php index 557f10b..2278f98 100644 --- a/src/ClassNotExist.php +++ b/src/ClassNotExist.php @@ -14,7 +14,7 @@ class ClassNotExist extends LoggableException /** * ClassNotExist. * - * @param string|array $class Class name + * @param string|array $class Class name */ public function __construct(array|string $class) { diff --git a/src/ClientAvailableInterface.php b/src/ClientAvailableInterface.php index 46d99b3..77ffdea 100644 --- a/src/ClientAvailableInterface.php +++ b/src/ClientAvailableInterface.php @@ -15,6 +15,8 @@ public function getClientMessage(): string; /** * Serialize exception for client. + * + * @return array */ public function clientSerialize(): array; } diff --git a/src/ClientException.php b/src/ClientException.php index aae0014..865a42b 100644 --- a/src/ClientException.php +++ b/src/ClientException.php @@ -9,6 +9,11 @@ */ class ClientException extends BaseException implements ClientAvailableInterface { + /** + * @param string $template + * @param array $parameters + * @param array $debugData + */ public function __construct(string $template, array $parameters = [], array $debugData = []) { parent::__construct(['template' => $template] + $parameters); diff --git a/src/Errors/Error.php b/src/Errors/Error.php index 54053c2..c1a55e7 100644 --- a/src/Errors/Error.php +++ b/src/Errors/Error.php @@ -8,13 +8,15 @@ /** * The class for encapsulating of PHP Errors - * as object BaseExceptionI. + * as object BaseExceptionInterface. */ -class Error implements BaseExceptionInterface +class Error extends \ErrorException implements BaseExceptionInterface { + /** - * Conformity between PHP-errors and BaseExceptionI. - */ + * Conformity between PHP-errors and BaseException + * @var array + */ protected static array $ERRORS = [ E_ERROR => self::ERROR, @@ -33,7 +35,10 @@ class Error implements BaseExceptionInterface E_DEPRECATED => self::INFO, E_USER_DEPRECATED => self::INFO, ]; - + + /** + * @var mixed[]|null + */ protected ?array $trace = null; /** @@ -45,8 +50,13 @@ class Error implements BaseExceptionInterface * Fatal error flag. */ protected bool $isFatal = false; - - public static function createFromLastError(?array $error = null): ?static + + /** + * @param array|null $error + * + * @return BaseExceptionInterface|null + */ + public static function createFromLastError(?array $error = null): ?BaseExceptionInterface { if ($error === null) { return null; @@ -65,9 +75,9 @@ public static function createFromLastError(?array $error = null): ?static * @param string $file File * @param int $line Line * - * @return Error + * @return BaseExceptionInterface */ - public static function createError(int $code, string $message, string $file, int $line): static + public static function createError(int $code, string $message, string $file, int $line): BaseExceptionInterface { if (!\array_key_exists($code, self::$ERRORS)) { $code = self::ERROR; @@ -107,64 +117,24 @@ public static function createError(int $code, string $message, string $file, int } } } - - /** - * Errors constructor. - */ - public function __construct(protected int $code, protected string $message, protected string $file, protected int $line) {} - - #[\Override] - public function getMessage(): string + + public function __construct(int $severity = \E_ERROR, + string $message = '', + ?string $filename = null, + ?int $line = null, + int $code = 0, + ?\Throwable $previous = null + ) { - return $this->message; + parent::__construct($message, $code, $severity, $filename, $line, $previous); } - - #[\Override] - public function getPrevious(): null - { - return null; - } - + #[\Override] public function getTags(): array { return []; } - - #[\Override] - public function getCode(): int - { - return $this->code; - } - - #[\Override] - public function getFile(): string - { - return $this->file; - } - - #[\Override] - public function getLine(): int - { - return $this->line; - } - - #[\Override] - public function getTrace(): ?array - { - return $this->trace; - } - - #[\Override] - public function getTraceAsString(): string - { - if (empty($this->trace)) { - return ''; - } - - return \print_r($this->trace, true); - } - + #[\Override] public function isLoggable(): bool { @@ -211,7 +181,10 @@ public function getLevel(): int return self::$ERRORS[$this->code]; } - + + /** + * @return array{source: string, type: string, function: string} + */ #[\Override] public function getSource(): array { diff --git a/src/FatalRuntimeException.php b/src/FatalRuntimeException.php index 7b37eba..42296e3 100644 --- a/src/FatalRuntimeException.php +++ b/src/FatalRuntimeException.php @@ -5,6 +5,6 @@ namespace IfCastle\Exceptions; /** - * Fatal exception with aspect: "Runtime". + * Fatal exception with an aspect: "Runtime". */ class FatalRuntimeException extends FatalException implements RuntimeExceptionInterface {} diff --git a/src/FatalSystemException.php b/src/FatalSystemException.php index 87155c8..7692b87 100644 --- a/src/FatalSystemException.php +++ b/src/FatalSystemException.php @@ -5,6 +5,6 @@ namespace IfCastle\Exceptions; /** - * Fatal exception with aspect: "System". + * Fatal exception with an aspect: "System". */ class FatalSystemException extends FatalException implements SystemExceptionInterface {} diff --git a/src/HelperTrait.php b/src/HelperTrait.php index 6d827a0..73067de 100644 --- a/src/HelperTrait.php +++ b/src/HelperTrait.php @@ -8,8 +8,7 @@ trait HelperTrait { /** * The method defines the source of the exception. - * - * + * @return array|string */ final protected function getSourceFor(\Throwable $e, bool $isString = false): array|string { @@ -18,21 +17,21 @@ final protected function getSourceFor(\Throwable $e, bool $isString = false): ar if ($isString) { return ($res['class'] ?? $res['file'] . ':' . $res['line']) . ($res['type'] ?? '.') . - ($res['function'] ?? '{}'); + ($res['function'] ?? '{}'); // @phpstan-ignore-line } return [ 'source' => $res['class'] ?? $res['file'] . ':' . $res['line'], 'type' => $res['type'] ?? '.', - 'function' => $res['function'] ?? '{}', + 'function' => $res['function'] ?? '{}' // @phpstan-ignore-line ]; } /** * The method returns a type of $value or class name. * - * It must use in order to exclude objects from the exception. + * It must be used to exclude objects from the exception. * * @param mixed $value value */ @@ -70,6 +69,7 @@ final protected function getValueType(mixed $value): string if (\is_resource($value)) { $type = \get_resource_type($value); $meta = ''; + // @phpstan-ignore-next-line if ($type === 'stream' && \is_array($meta = \stream_get_meta_data($value))) { // array keys normalize $meta = \array_merge( @@ -91,8 +91,8 @@ final protected function getValueType(mixed $value): string * The method convert $value to string. * * @param mixed $value Value - * @param boolean $isQuoted If result has been quoted? - * @param int $arrayMax Max count items of array + * @param boolean $isQuoted If a result has been quoted? + * @param int $arrayMax Max count items of an array */ protected function toString(mixed $value, bool $isQuoted = false, int $arrayMax = 5): string { diff --git a/src/InterfaceNotImplemented.php b/src/InterfaceNotImplemented.php index 54f5f69..a0d0e2f 100644 --- a/src/InterfaceNotImplemented.php +++ b/src/InterfaceNotImplemented.php @@ -14,7 +14,7 @@ class InterfaceNotImplemented extends LoggableException /** * Constructor for InterfaceNotImplemented. * - * @param string|array|object $class Class name + * @param string|array|object $class Class name * @param string $interface Required interface */ public function __construct(object|array|string $class, string $interface) diff --git a/src/MethodNotCallable.php b/src/MethodNotCallable.php index 60fa00d..b2739f9 100644 --- a/src/MethodNotCallable.php +++ b/src/MethodNotCallable.php @@ -14,7 +14,7 @@ class MethodNotCallable extends LogicalException /** * MethodNotCallable. * - * @param string|array $method Method + * @param string|array $method Method * @param string $message Extended Message */ public function __construct(array|string $method, $message = '') diff --git a/src/ObjectNotInitialized.php b/src/ObjectNotInitialized.php index 5749e1a..eb65795 100644 --- a/src/ObjectNotInitialized.php +++ b/src/ObjectNotInitialized.php @@ -13,12 +13,12 @@ class ObjectNotInitialized extends LoggableException protected string $template = 'Object {object} is not initialized'; /** - * If object not initialized. + * If an object isn't initialized. * * @param object $object Object * @param string $message Addition message */ - public function __construct($object = null, $message = '') + public function __construct(mixed $object = null, string $message = '') { parent::__construct(['object' => $this->typeInfo($object), 'message' => $message]); } diff --git a/src/RecursionLimitExceeded.php b/src/RecursionLimitExceeded.php index fe1bec8..f554505 100644 --- a/src/RecursionLimitExceeded.php +++ b/src/RecursionLimitExceeded.php @@ -14,7 +14,7 @@ class RecursionLimitExceeded extends LoggableException /** * Reached a maximum depth of recursion. * - * @param int|string|array $limit maximum depth + * @param int|string|array $limit maximum depth */ public function __construct(array|string|int $limit) { diff --git a/src/Registry.php b/src/Registry.php index 00ab207..c411ffb 100644 --- a/src/Registry.php +++ b/src/Registry.php @@ -4,6 +4,8 @@ namespace IfCastle\Exceptions; +use PHPUnit\Event\Code\Throwable; + /** * Register of exceptions. * @@ -23,20 +25,20 @@ class Registry /** * Options for logger. - * @var array|\ArrayAccess + * @var array */ public static array $LoggerOptions = []; /** * Options for debug mode. - * @var array|\ArrayAccess + * @var array */ public static array $DebugOptions = []; /** * List of exception. * - * @var BaseException[]|\Exception[]|StorageInterface + * @var BaseExceptionInterface[]|\Throwable[]|StorageInterface */ protected static array|StorageInterface $exceptions = []; @@ -57,15 +59,15 @@ class Registry /** * Old error handler. - * @var callback + * @var ?callable(int $code, string $message, string $file, int|string $line): bool */ - protected static $oldErrorHandler; + protected static mixed $oldErrorHandler = null; /** * Old exception handler. - * @var callback + * @var callable(\Throwable): void */ - protected static $oldExceptionHandler; + protected static mixed $oldExceptionHandler = null; /** * Setup global handler flag. @@ -74,6 +76,7 @@ class Registry /** * List of fatal php error. + * @var int[] */ protected static array $FATAL = [\E_ERROR, \E_PARSE, \E_CORE_ERROR, \E_COMPILE_ERROR]; @@ -89,7 +92,7 @@ final private function __construct() {} */ public static function registerException(mixed $exception): void { - if (!$exception instanceof \Throwable && !$exception instanceof BaseExceptionInterface) { + if (false === $exception instanceof \Throwable) { return; } @@ -103,28 +106,15 @@ public static function registerException(mixed $exception): void /** * Returns the list of exception. * - * @return BaseException[]|\Exception[] + * @return BaseException[]|\Throwable[] */ public static function getExceptionLog(): array { if (\is_array(self::$exceptions)) { return self::$exceptions; + } else { + return self::$exceptions->getStorageExceptions(); } - - if (self::$exceptions instanceof StorageInterface) { - - $result = self::$exceptions->getStorageExceptions(); - - if (!\is_array($result)) { - return [new \UnexpectedValueException('StorageI->get_storage() return not array')]; - } - - return $result; - } - - - return []; - } /** @@ -217,15 +207,11 @@ public static function callFatalHandler(?BaseExceptionInterface $exception = nul /** * Return list of logger options. + * @return array */ public static function getLoggerOptions(): array { - if (\is_array(self::$LoggerOptions) || - self::$LoggerOptions instanceof \ArrayAccess) { - return self::$LoggerOptions; - } - - return []; + return self::$LoggerOptions; } /** @@ -260,13 +246,13 @@ public static function restoreGlobalHandlers(): void self::$installGlobalHandlers = false; - if (!empty(self::$oldErrorHandler)) { + if (self::$oldErrorHandler !== null) { \set_error_handler(self::$oldErrorHandler); } else { \restore_error_handler(); } - if (!empty(self::$oldExceptionHandler)) { + if (self::$oldExceptionHandler !== null) { \set_exception_handler(self::$oldExceptionHandler); } else { \restore_exception_handler(); diff --git a/src/StorageInterface.php b/src/StorageInterface.php index 6b5671d..07aab86 100644 --- a/src/StorageInterface.php +++ b/src/StorageInterface.php @@ -13,12 +13,13 @@ interface StorageInterface /** * Add exception into storage. * - * @param BaseExceptionInterface|\Exception $exception Exception + * @param BaseExceptionInterface|\Throwable $exception Exception */ public function addException(BaseExceptionInterface|\Throwable $exception): static; /** * Returns list of exceptions. + * @return BaseException[]|\Throwable[] */ public function getStorageExceptions(): array; diff --git a/tests/BaseExceptionTest.php b/tests/BaseExceptionTest.php index dc8a249..5a8a98d 100644 --- a/tests/BaseExceptionTest.php +++ b/tests/BaseExceptionTest.php @@ -8,7 +8,7 @@ class DebugException extends BaseException { - public const DEBUG_DATA = 'test debug data'; + public const string DEBUG_DATA = 'test debug data'; public function __construct(\IfCastle\Exceptions\BaseExceptionInterface|\Throwable|array|string $exception, int $code = 0, ?\Throwable $previous = null) { @@ -32,19 +32,19 @@ class BaseExceptionTest extends \PHPUnit\Framework\TestCase * Test data for the exception. * @var array */ - protected $test_data; + protected array $testData; /** * Exception info. * @var array */ - protected $test_base_data; + protected array $testBaseData; /** * Exception. * @var \IfCastle\Exceptions\BaseException */ - protected $BaseException; + protected BaseException $BaseException; /** * Sets up the fixture, for example, opens a network connection. @@ -53,21 +53,21 @@ class BaseExceptionTest extends \PHPUnit\Framework\TestCase #[\Override] protected function setUp(): void { - $this->test_data = + $this->testData = [ 'level' => \IfCastle\Exceptions\BaseExceptionInterface::CRITICAL, 'ident' => 'test_ident', 'exdata' => [1, 2, 'string'], ]; - $this->test_base_data = + $this->testBaseData = [ 'message' => 'test message', 'code' => 11223344, 'previous' => new \Exception('previous message', 123), ]; - $this->BaseException = new \IfCastle\Exceptions\BaseException(\array_merge($this->test_data, $this->test_base_data)); + $this->BaseException = new \IfCastle\Exceptions\BaseException(\array_merge($this->testData, $this->testBaseData)); } /** @@ -87,23 +87,23 @@ public function testConstruct(): void $this->assertEquals(10, $e->getCode(), '$e->getCode() failed'); $this->assertTrue(($previous === $e->getPrevious()), '$e->getPrevious() failed'); - $e = new \IfCastle\Exceptions\BaseException($this->test_base_data); - $this->assertEquals($this->test_base_data['message'], $e->getMessage(), '$e->getMessage() failed'); - $this->assertEquals($this->test_base_data['code'], $e->getCode(), '$e->getCode() failed'); - $this->assertTrue(($this->test_base_data['previous'] === $e->getPrevious()), '$e->getPrevious() failed'); + $e = new \IfCastle\Exceptions\BaseException($this->testBaseData); + $this->assertEquals($this->testBaseData['message'], $e->getMessage(), '$e->getMessage() failed'); + $this->assertEquals($this->testBaseData['code'], $e->getCode(), '$e->getCode() failed'); + $this->assertTrue(($this->testBaseData['previous'] === $e->getPrevious()), '$e->getPrevious() failed'); - $e = new \IfCastle\Exceptions\BaseException(\array_merge($this->test_data, $this->test_base_data)); - $this->assertEquals($this->test_base_data['message'], $e->getMessage(), '$e->getMessage() failed'); - $this->assertEquals($this->test_base_data['code'], $e->getCode(), '$e->getCode() failed'); - $this->assertTrue(($this->test_base_data['previous'] === $e->getPrevious()), '$e->getPrevious() failed'); + $e = new \IfCastle\Exceptions\BaseException(\array_merge($this->testData, $this->testBaseData)); + $this->assertEquals($this->testBaseData['message'], $e->getMessage(), '$e->getMessage() failed'); + $this->assertEquals($this->testBaseData['code'], $e->getCode(), '$e->getCode() failed'); + $this->assertTrue(($this->testBaseData['previous'] === $e->getPrevious()), '$e->getPrevious() failed'); } public function testConstructAsContainer(): void { - // 1. Случай контейнер для исключения \Exception + // 1. Container for \Exception $exception = new \UnderflowException( - $this->test_base_data['message'], - $this->test_base_data['code'] + $this->testBaseData['message'], + $this->testBaseData['code'] ); $e = new \IfCastle\Exceptions\BaseException($exception); @@ -115,7 +115,7 @@ public function testConstructAsContainer(): void $this->assertTrue(($exception === $e->getPrevious()), '$e->getPrevious() failed'); $this->assertTrue(($exception === $e->getPreviousException()), '$e->get_previous() failed'); - // 2. Случай контейнер для BaseExceptionI + // 2. Container for BaseExceptionI $exception = new \IfCastle\Exceptions\ClassNotExist('my_class'); $e = new \IfCastle\Exceptions\BaseException($exception); @@ -150,7 +150,7 @@ public function testSetLoggable(): void public function testGetLevel(): void { $this->assertEquals( - $this->test_data['level'], + $this->testData['level'], $this->BaseException->getLevel(), 'BaseException level must be BaseExceptionI::CRITICAL' ); @@ -169,7 +169,7 @@ public function testGetData(): void $this->assertIsArray($data, 'data must be array'); - foreach ($this->test_data as $key => $value) { + foreach ($this->testData as $key => $value) { $this->assertArrayHasKey($key, $data); $this->assertEquals($value, $data[$key]); unset($data[$key]); @@ -186,10 +186,10 @@ public function testToArray(): void [ 'type' => \IfCastle\Exceptions\BaseException::class, 'source' => ['source' => static::class, 'type' => '->', 'function' => 'setUp'], - 'message' => $this->test_base_data['message'], + 'message' => $this->testBaseData['message'], 'template' => '', 'tags' => [], - 'code' => $this->test_base_data['code'], + 'code' => $this->testBaseData['code'], 'data' => '', ]; @@ -199,7 +199,7 @@ public function testToArray(): void $this->assertArrayHasKey($main_key, $data); if ('data' === $main_key) { - foreach ($this->test_data as $key => $value) { + foreach ($this->testData as $key => $value) { $this->assertArrayHasKey($key, $data['data']); $this->assertEquals($value, $data['data'][$key]); } diff --git a/tests/Mockups/Storage.php b/tests/Mockups/Storage.php index 75a3293..b5d1295 100644 --- a/tests/Mockups/Storage.php +++ b/tests/Mockups/Storage.php @@ -9,13 +9,15 @@ class Storage implements StorageInterface { + /** + * List of exceptions. + * + * @var BaseExceptionInterface[]|\Throwable[] + */ public array $Exceptions = []; /** - * - * - * - * @return StorageInterface + * @return $this */ #[\Override] public function addException(BaseExceptionInterface|\Throwable $exception): static