From 89af4c8a95467800535cf4688a1adb92eeb5042f Mon Sep 17 00:00:00 2001 From: David Badura Date: Fri, 17 Jan 2025 11:40:53 +0100 Subject: [PATCH] improve flatten args in error context transformer --- baseline.xml | 5 ++ phpstan-baseline.neon | 8 ++- src/Subscription/SubscriptionError.php | 2 +- .../ThrowableToErrorContextTransformer.php | 66 +++++++++++++++---- tests/Unit/Subscription/ErrorContextTest.php | 22 +++++-- 5 files changed, 85 insertions(+), 18 deletions(-) diff --git a/baseline.xml b/baseline.xml index f1994c1f7..f47baf0f1 100644 --- a/baseline.xml +++ b/baseline.xml @@ -182,6 +182,11 @@ ]]> + + + + + diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 3de7e5b0c..e265d4ecf 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -145,7 +145,7 @@ parameters: path: src/Subscription/RetryStrategy/ClockBasedRetryStrategy.php - - message: '#^Parameter \#3 \$errorContext of class Patchlevel\\EventSourcing\\Subscription\\SubscriptionError constructor expects list\\}\>\}\>\|null, mixed given\.$#' + message: '#^Parameter \#3 \$errorContext of class Patchlevel\\EventSourcing\\Subscription\\SubscriptionError constructor expects list\\}\>\}\>\|null, mixed given\.$#' identifier: argument.type count: 1 path: src/Subscription/Store/DoctrineSubscriptionStore.php @@ -155,3 +155,9 @@ parameters: identifier: trait.unused count: 1 path: src/Subscription/Subscriber/SubscriberUtil.php + + - + message: '#^Cannot cast mixed to string\.$#' + identifier: cast.string + count: 1 + path: src/Subscription/ThrowableToErrorContextTransformer.php diff --git a/src/Subscription/SubscriptionError.php b/src/Subscription/SubscriptionError.php index 013fdd979..7985ee28f 100644 --- a/src/Subscription/SubscriptionError.php +++ b/src/Subscription/SubscriptionError.php @@ -8,7 +8,7 @@ /** * @psalm-type Trace = array{file?: string, line?: int, function?: string, class?: string, type?: string, args?: array} - * @psalm-type Context = array{class: class-string, message: string, code: int|string, file: string, line: int, trace: list} + * @psalm-type Context = array{namespace: string, short_name: string, class: class-string, message: string, code: int|string, file: string, line: int, trace: list} */ final class SubscriptionError { diff --git a/src/Subscription/ThrowableToErrorContextTransformer.php b/src/Subscription/ThrowableToErrorContextTransformer.php index 96f1571b8..c847c40b4 100644 --- a/src/Subscription/ThrowableToErrorContextTransformer.php +++ b/src/Subscription/ThrowableToErrorContextTransformer.php @@ -8,11 +8,17 @@ use function array_key_exists; use function array_map; -use function array_walk_recursive; +use function array_pop; +use function explode; +use function get_debug_type; use function get_resource_type; +use function implode; +use function is_array; +use function is_bool; +use function is_float; +use function is_int; use function is_object; use function is_resource; -use function sprintf; /** * @psalm-import-type Context from SubscriptionError @@ -39,7 +45,13 @@ private static function transformThrowable(Throwable $error): array /** @var list $traces */ $traces = $error->getTrace(); + $classParts = explode('\\', $error::class); + $shortClass = array_pop($classParts); + $namespace = implode('\\', $classParts); + return [ + 'short_name' => $shortClass, + 'namespace' => $namespace, 'class' => $error::class, 'message' => $error->getMessage(), 'code' => $error->getCode(), @@ -60,18 +72,50 @@ private static function transformTrace(array $trace): array return $trace; } - array_walk_recursive($trace['args'], static function (mixed &$value): void { - if (is_object($value)) { - $value = sprintf('object(%s)', $value::class); - } + $trace['args'] = self::flattenArgs($trace['args']); + + return $trace; + } + + /** + * @param array $args + * + * @return array + */ + private static function flattenArgs(array $args, int $level = 0, int &$count = 0): array + { + $result = []; + + foreach ($args as $key => $value) { + $count++; - if (!is_resource($value)) { - return; + if ($count > 10_000) { + return ['array', '*SKIPPED over 10000 entries*']; } - $value = sprintf('resource(%s)', get_resource_type($value)); - }); + if (is_object($value)) { + $result[$key] = ['object', get_debug_type($value)]; + } elseif (is_array($value)) { + if ($level > 10) { + $result[$key] = ['array', '*DEEP NESTED ARRAY*']; + } else { + $result[$key] = ['array', self::flattenArgs($value, $level + 1, $count)]; + } + } elseif ($value === null) { + $result[$key] = ['null', null]; + } elseif (is_bool($value)) { + $result[$key] = ['boolean', $value]; + } elseif (is_int($value)) { + $result[$key] = ['integer', $value]; + } elseif (is_float($value)) { + $result[$key] = ['float', $value]; + } elseif (is_resource($value)) { + $result[$key] = ['resource', get_resource_type($value)]; + } else { + $result[$key] = ['string', (string)$value]; + } + } - return $trace; + return $result; } } diff --git a/tests/Unit/Subscription/ErrorContextTest.php b/tests/Unit/Subscription/ErrorContextTest.php index 28330915b..def9b4d97 100644 --- a/tests/Unit/Subscription/ErrorContextTest.php +++ b/tests/Unit/Subscription/ErrorContextTest.php @@ -51,11 +51,23 @@ public function testErrorContext(): void $this->assertSame('->', $firstTrace['type'] ?? null); $this->assertArrayHasKey('args', $firstTrace); $this->assertSame([ - 'test', - 'object(Patchlevel\EventSourcing\Aggregate\CustomId)', - 'resource(stream)', - ['test' => [1, 2, 3]], - 'object(Closure)', + ['string', 'test'], + ['object', 'Patchlevel\EventSourcing\Aggregate\CustomId'], + ['resource', 'stream'], + [ + 'array', + [ + 'test' => [ + 'array', + [ + ['integer', 1], + ['integer', 2], + ['integer', 3], + ], + ], + ], + ], + ['object', 'Closure'], ], $firstTrace['args'] ?? null); }