From 85759ecadef3156ef0b94ef42b583d739e725c07 Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Sat, 25 Jan 2025 20:19:43 +0300 Subject: [PATCH] Refactor summary data structure --- src/Collector/CollectorInterface.php | 1 + src/Collector/CollectorTrait.php | 6 ++++ src/Collector/Console/CommandCollector.php | 10 +++--- .../Console/ConsoleAppInfoCollector.php | 20 +++++------ src/Collector/EventCollector.php | 6 ++-- src/Collector/ExceptionCollector.php | 15 +++++---- src/Collector/HttpClientCollector.php | 18 +++++----- src/Collector/LogCollector.php | 6 ++-- src/Collector/ServiceCollector.php | 6 ++-- .../Stream/FilesystemStreamCollector.php | 2 +- src/Collector/Stream/HttpStreamCollector.php | 2 +- src/Collector/VarDumperCollector.php | 6 ++-- src/Collector/Web/WebAppInfoCollector.php | 20 +++++------ src/Debugger.php | 33 ++++++++++++++----- src/Storage/StorageInterface.php | 4 +-- tests/Unit/Collector/CommandCollectorTest.php | 9 +++-- .../Collector/ConsoleAppInfoCollectorTest.php | 17 +++++----- .../Unit/Collector/ExceptionCollectorTest.php | 19 +++++------ .../FilesystemStreamCollectorTest.php | 4 +-- .../Collector/HttpClientCollectorTest.php | 12 +++---- .../Collector/HttpStreamCollectorTest.php | 4 +-- .../Unit/Collector/VarDumperCollectorTest.php | 5 ++- .../VarDumperHandlerInterfaceProxyTest.php | 4 +-- 23 files changed, 118 insertions(+), 111 deletions(-) diff --git a/src/Collector/CollectorInterface.php b/src/Collector/CollectorInterface.php index f9f60c10a..e5f6fbdf4 100644 --- a/src/Collector/CollectorInterface.php +++ b/src/Collector/CollectorInterface.php @@ -11,6 +11,7 @@ interface CollectorInterface { /** * @return string Collector's name. + * @psalm-return non-empty-string */ public function getName(): string; diff --git a/src/Collector/CollectorTrait.php b/src/Collector/CollectorTrait.php index 3eac8544c..d7c2f5523 100644 --- a/src/Collector/CollectorTrait.php +++ b/src/Collector/CollectorTrait.php @@ -4,6 +4,9 @@ namespace Yiisoft\Yii\Debug\Collector; +/** + * @psalm-require-implements CollectorInterface + */ trait CollectorTrait { private bool $isActive = false; @@ -19,6 +22,9 @@ public function shutdown(): void $this->isActive = false; } + /** + * @psalm-return non-empty-string + */ public function getName(): string { return self::class; diff --git a/src/Collector/Console/CommandCollector.php b/src/Collector/Console/CommandCollector.php index db7631321..148d92d7b 100644 --- a/src/Collector/Console/CommandCollector.php +++ b/src/Collector/Console/CommandCollector.php @@ -118,12 +118,10 @@ public function getSummary(): array } return [ - 'command' => [ - 'name' => $commandEvent['name'], - 'class' => $commandEvent['command'] instanceof Command ? $commandEvent['command']::class : null, - 'input' => $commandEvent['input'], - 'exitCode' => $commandEvent['exitCode'] ?? self::UNDEFINED_EXIT_CODE, - ], + 'name' => $commandEvent['name'], + 'class' => $commandEvent['command'] instanceof Command ? $commandEvent['command']::class : null, + 'input' => $commandEvent['input'], + 'exitCode' => $commandEvent['exitCode'] ?? self::UNDEFINED_EXIT_CODE, ]; } diff --git a/src/Collector/Console/ConsoleAppInfoCollector.php b/src/Collector/Console/ConsoleAppInfoCollector.php index 5594c482f..2c99e00f6 100644 --- a/src/Collector/Console/ConsoleAppInfoCollector.php +++ b/src/Collector/Console/ConsoleAppInfoCollector.php @@ -74,17 +74,15 @@ public function getSummary(): array return []; } return [ - 'console' => [ - 'php' => [ - 'version' => PHP_VERSION, - ], - 'request' => [ - 'startTime' => $this->requestProcessingTimeStarted, - 'processingTime' => $this->requestProcessingTimeStopped - $this->requestProcessingTimeStarted, - ], - 'memory' => [ - 'peakUsage' => memory_get_peak_usage(), - ], + 'php' => [ + 'version' => PHP_VERSION, + ], + 'request' => [ + 'startTime' => $this->requestProcessingTimeStarted, + 'processingTime' => $this->requestProcessingTimeStopped - $this->requestProcessingTimeStarted, + ], + 'memory' => [ + 'peakUsage' => memory_get_peak_usage(), ], ]; } diff --git a/src/Collector/EventCollector.php b/src/Collector/EventCollector.php index e7978539f..009ab4dad 100644 --- a/src/Collector/EventCollector.php +++ b/src/Collector/EventCollector.php @@ -8,6 +8,8 @@ use Yiisoft\Yii\Console\Event\ApplicationStartup as ConsoleApplicationStartup; use Yiisoft\Yii\Http\Event\ApplicationStartup as HttpApplicationStartup; +use function count; + final class EventCollector implements SummaryCollectorInterface { use CollectorTrait; @@ -53,9 +55,7 @@ public function getSummary(): array return []; } return [ - 'event' => [ - 'total' => count($this->events), - ], + 'total' => count($this->events), ]; } diff --git a/src/Collector/ExceptionCollector.php b/src/Collector/ExceptionCollector.php index 1dd6a8f47..03f1c87af 100644 --- a/src/Collector/ExceptionCollector.php +++ b/src/Collector/ExceptionCollector.php @@ -52,14 +52,15 @@ public function getSummary(): array if (!$this->isActive()) { return []; } + if ($this->exception === null) { + return []; + } return [ - 'exception' => $this->exception === null ? [] : [ - 'class' => $this->exception::class, - 'message' => $this->exception->getMessage(), - 'file' => $this->exception->getFile(), - 'line' => $this->exception->getLine(), - 'code' => $this->exception->getCode(), - ], + 'class' => $this->exception::class, + 'message' => $this->exception->getMessage(), + 'file' => $this->exception->getFile(), + 'line' => $this->exception->getLine(), + 'code' => $this->exception->getCode(), ]; } diff --git a/src/Collector/HttpClientCollector.php b/src/Collector/HttpClientCollector.php index 7f14da53c..1e4a89208 100644 --- a/src/Collector/HttpClientCollector.php +++ b/src/Collector/HttpClientCollector.php @@ -89,17 +89,15 @@ public function getSummary(): array return []; } return [ - 'http' => [ - 'count' => array_sum(array_map(static fn (array $requests) => count($requests), $this->requests)), - 'totalTime' => array_sum( - array_merge( - ...array_map( - static fn (array $entry) => array_column($entry, 'totalTime'), - array_values($this->requests) - ) + 'count' => array_sum(array_map(static fn (array $requests) => count($requests), $this->requests)), + 'totalTime' => array_sum( + array_merge( + ...array_map( + static fn (array $entry) => array_column($entry, 'totalTime'), + array_values($this->requests) ) - ), - ], + ) + ), ]; } } diff --git a/src/Collector/LogCollector.php b/src/Collector/LogCollector.php index d6aa5fa66..2a453d07e 100644 --- a/src/Collector/LogCollector.php +++ b/src/Collector/LogCollector.php @@ -4,6 +4,8 @@ namespace Yiisoft\Yii\Debug\Collector; +use function count; + class LogCollector implements SummaryCollectorInterface { use CollectorTrait; @@ -50,9 +52,7 @@ public function getSummary(): array return []; } return [ - 'logger' => [ - 'total' => count($this->messages), - ], + 'total' => count($this->messages), ]; } } diff --git a/src/Collector/ServiceCollector.php b/src/Collector/ServiceCollector.php index 31a48e6c2..484dcb2e0 100644 --- a/src/Collector/ServiceCollector.php +++ b/src/Collector/ServiceCollector.php @@ -4,6 +4,8 @@ namespace Yiisoft\Yii\Debug\Collector; +use function count; + final class ServiceCollector implements SummaryCollectorInterface { use CollectorTrait; @@ -58,9 +60,7 @@ public function getSummary(): array return []; } return [ - 'service' => [ - 'total' => count($this->items), - ], + 'total' => count($this->items), ]; } diff --git a/src/Collector/Stream/FilesystemStreamCollector.php b/src/Collector/Stream/FilesystemStreamCollector.php index 699a56167..952800835 100644 --- a/src/Collector/Stream/FilesystemStreamCollector.php +++ b/src/Collector/Stream/FilesystemStreamCollector.php @@ -78,7 +78,7 @@ public function getSummary(): array return []; } return [ - 'fs_stream' => array_merge( + 'streams' => array_merge( ...array_map( fn (string $operation) => [$operation => count($this->operations[$operation])], array_keys($this->operations) diff --git a/src/Collector/Stream/HttpStreamCollector.php b/src/Collector/Stream/HttpStreamCollector.php index 61634066d..fb728e663 100644 --- a/src/Collector/Stream/HttpStreamCollector.php +++ b/src/Collector/Stream/HttpStreamCollector.php @@ -87,7 +87,7 @@ public function getSummary(): array return []; } return [ - 'http_stream' => array_merge( + 'streams' => array_merge( ...array_map( fn (string $operation) => [ $operation => count($this->requests[$operation]), diff --git a/src/Collector/VarDumperCollector.php b/src/Collector/VarDumperCollector.php index 045804912..7254baa20 100644 --- a/src/Collector/VarDumperCollector.php +++ b/src/Collector/VarDumperCollector.php @@ -4,6 +4,8 @@ namespace Yiisoft\Yii\Debug\Collector; +use function count; + final class VarDumperCollector implements SummaryCollectorInterface { use CollectorTrait; @@ -40,9 +42,7 @@ public function getSummary(): array } return [ - 'var-dumper' => [ - 'total' => count($this->vars), - ], + 'total' => count($this->vars), ]; } } diff --git a/src/Collector/Web/WebAppInfoCollector.php b/src/Collector/Web/WebAppInfoCollector.php index 6a0c55337..56e8217f2 100644 --- a/src/Collector/Web/WebAppInfoCollector.php +++ b/src/Collector/Web/WebAppInfoCollector.php @@ -65,17 +65,15 @@ public function getSummary(): array return []; } return [ - 'web' => [ - 'php' => [ - 'version' => PHP_VERSION, - ], - 'request' => [ - 'startTime' => $this->requestProcessingTimeStarted, - 'processingTime' => $this->requestProcessingTimeStopped - $this->requestProcessingTimeStarted, - ], - 'memory' => [ - 'peakUsage' => memory_get_peak_usage(), - ], + 'php' => [ + 'version' => PHP_VERSION, + ], + 'request' => [ + 'startTime' => $this->requestProcessingTimeStarted, + 'processingTime' => $this->requestProcessingTimeStopped - $this->requestProcessingTimeStarted, + ], + 'memory' => [ + 'peakUsage' => memory_get_peak_usage(), ], ]; } diff --git a/src/Debugger.php b/src/Debugger.php index cd3d93a58..f5f21ec82 100644 --- a/src/Debugger.php +++ b/src/Debugger.php @@ -15,13 +15,19 @@ /** * Debugger collects data from collectors and stores it in a storage. + * + * @psalm-type TSummary = array{ + * id: non-empty-string, + * collectors: list, + * summary: array, + * } */ final class Debugger { /** * @var CollectorInterface[] Collectors, indexed by their names. * - * @psalm-var array + * @psalm-var array */ private readonly array $collectors; @@ -30,6 +36,12 @@ final class Debugger */ private readonly DataNormalizer $dataNormalizer; + /** + * @var string|null ID of the current request. `null` if debugger is not active. + * @psalm-var non-empty-string|null + */ + private ?string $id = null; + /** * @param StorageInterface $storage The storage to store collected data. * @param CollectorInterface[] $collectors Collectors to be used. @@ -57,11 +69,6 @@ public function __construct( register_shutdown_function([$this, 'stop']); } - /** - * @var string|null ID of the current request. `null` if debugger is not active. - */ - private ?string $id = null; - /** * Returns whether debugger is active. * @@ -78,6 +85,8 @@ public function isActive(): bool * Throws `LogicException` if debugger is not started. Use {@see isActive()} to check if debugger is active. * * @return string ID of the current request. + * + * @psalm-return non-empty-string */ public function getId(): string { @@ -95,6 +104,7 @@ public function start(object $event): void return; } + /** @var non-empty-string */ $this->id = str_replace('.', '', uniqid('', true)); foreach ($this->collectors as $collector) { @@ -163,18 +173,25 @@ private function deactivate(): void } /** - * Collects summary data of current request. + * Collects summary data of current request. Structure of the summary data is: + * + * - `id` - ID of the current request, + * - `collectors` - list of collector names used in the current request, + * - `summary` - summary data collected by collectors indexed by collector names. + * + * @psalm-return TSummary */ private function collectSummaryData(): array { $summaryData = [ 'id' => $this->getId(), 'collectors' => array_keys($this->collectors), + 'summary' => [], ]; foreach ($this->collectors as $collector) { if ($collector instanceof SummaryCollectorInterface) { - $summaryData = [...$summaryData, ...$collector->getSummary()]; + $summaryData['summary'][$collector->getName()] = $collector->getSummary(); } } diff --git a/src/Storage/StorageInterface.php b/src/Storage/StorageInterface.php index ad95c399f..dbf37ad18 100644 --- a/src/Storage/StorageInterface.php +++ b/src/Storage/StorageInterface.php @@ -27,12 +27,12 @@ interface StorageInterface /** * Read all data from storage * - * @param string $type type of data being read. Available types: + * @param string $type Type of data being read. Available types: * - {@see TYPE_SUMMARY} * - {@see TYPE_DATA} * - {@see TYPE_OBJECTS} * - * @return array data from storage + * @return array Data from storage * * @psalm-param self::TYPE_* $type */ diff --git a/tests/Unit/Collector/CommandCollectorTest.php b/tests/Unit/Collector/CommandCollectorTest.php index 5d99fd78d..19ef7e200 100644 --- a/tests/Unit/Collector/CommandCollectorTest.php +++ b/tests/Unit/Collector/CommandCollectorTest.php @@ -71,10 +71,9 @@ protected function checkCollectedData(array $data): void protected function checkSummaryData(array $data): void { - $this->assertArrayHasKey('command', $data); - $this->assertArrayHasKey('input', $data['command']); - $this->assertArrayHasKey('class', $data['command']); - $this->assertEquals('test1', $data['command']['input']); - $this->assertEquals(null, $data['command']['class']); + $this->assertArrayHasKey('input', $data); + $this->assertArrayHasKey('class', $data); + $this->assertEquals('test1', $data['input']); + $this->assertEquals(null, $data['class']); } } diff --git a/tests/Unit/Collector/ConsoleAppInfoCollectorTest.php b/tests/Unit/Collector/ConsoleAppInfoCollectorTest.php index 5cf9238a7..39eb0c16e 100644 --- a/tests/Unit/Collector/ConsoleAppInfoCollectorTest.php +++ b/tests/Unit/Collector/ConsoleAppInfoCollectorTest.php @@ -58,15 +58,14 @@ protected function checkSummaryData(array $data): void { parent::checkSummaryData($data); - $this->assertArrayHasKey('console', $data); - $this->assertArrayHasKey('php', $data['console']); - $this->assertArrayHasKey('version', $data['console']['php']); - $this->assertArrayHasKey('request', $data['console']); - $this->assertArrayHasKey('startTime', $data['console']['request']); - $this->assertArrayHasKey('processingTime', $data['console']['request']); - $this->assertArrayHasKey('memory', $data['console']); - $this->assertArrayHasKey('peakUsage', $data['console']['memory']); + $this->assertArrayHasKey('php', $data); + $this->assertArrayHasKey('version', $data['php']); + $this->assertArrayHasKey('request', $data); + $this->assertArrayHasKey('startTime', $data['request']); + $this->assertArrayHasKey('processingTime', $data['request']); + $this->assertArrayHasKey('memory', $data); + $this->assertArrayHasKey('peakUsage', $data['memory']); - $this->assertEquals(PHP_VERSION, $data['console']['php']['version']); + $this->assertEquals(PHP_VERSION, $data['php']['version']); } } diff --git a/tests/Unit/Collector/ExceptionCollectorTest.php b/tests/Unit/Collector/ExceptionCollectorTest.php index c25b0a8d2..aead81f59 100644 --- a/tests/Unit/Collector/ExceptionCollectorTest.php +++ b/tests/Unit/Collector/ExceptionCollectorTest.php @@ -55,19 +55,16 @@ protected function checkCollectedData(array $data): void protected function checkSummaryData(array $data): void { parent::checkSummaryData($data); - $this->assertCount(1, $data); - $this->assertArrayHasKey('exception', $data); - $exception = $data['exception']; - $this->assertArrayHasKey('class', $exception); - $this->assertArrayHasKey('message', $exception); - $this->assertArrayHasKey('file', $exception); - $this->assertArrayHasKey('line', $exception); - $this->assertArrayHasKey('code', $exception); + $this->assertArrayHasKey('class', $data); + $this->assertArrayHasKey('message', $data); + $this->assertArrayHasKey('file', $data); + $this->assertArrayHasKey('line', $data); + $this->assertArrayHasKey('code', $data); - $this->assertEquals(Exception::class, $exception['class']); - $this->assertEquals('test', $exception['message']); - $this->assertEquals(777, $exception['code']); + $this->assertEquals(Exception::class, $data['class']); + $this->assertEquals('test', $data['message']); + $this->assertEquals(777, $data['code']); } public function testNoExceptionCollected(): void diff --git a/tests/Unit/Collector/FilesystemStreamCollectorTest.php b/tests/Unit/Collector/FilesystemStreamCollectorTest.php index 06b0ca8ea..5b7c4880a 100644 --- a/tests/Unit/Collector/FilesystemStreamCollectorTest.php +++ b/tests/Unit/Collector/FilesystemStreamCollectorTest.php @@ -333,10 +333,10 @@ protected function checkCollectedData(array $data): void protected function checkSummaryData(array $data): void { parent::checkSummaryData($data); - $this->assertArrayHasKey('fs_stream', $data); + $this->assertArrayHasKey('streams', $data); $this->assertEquals( ['read' => 2, 'mkdir' => 1], - $data['fs_stream'], + $data['streams'], print_r($data, true), ); } diff --git a/tests/Unit/Collector/HttpClientCollectorTest.php b/tests/Unit/Collector/HttpClientCollectorTest.php index 52c0c5b92..c9bad69ba 100644 --- a/tests/Unit/Collector/HttpClientCollectorTest.php +++ b/tests/Unit/Collector/HttpClientCollectorTest.php @@ -96,13 +96,11 @@ protected function checkCollectedData(array $data): void protected function checkSummaryData(array $data): void { parent::checkSummaryData($data); - $this->assertCount(1, $data); - $this->assertArrayHasKey('http', $data); - $this->assertCount(2, $data['http']); - $this->assertArrayHasKey('count', $data['http']); - $this->assertArrayHasKey('totalTime', $data['http']); + $this->assertCount(2, $data); + $this->assertArrayHasKey('count', $data); + $this->assertArrayHasKey('totalTime', $data); - $this->assertEquals(3, $data['http']['count']); - $this->assertEquals(3.1, round($data['http']['totalTime'], 1)); + $this->assertEquals(3, $data['count']); + $this->assertEquals(3.1, round($data['totalTime'], 1)); } } diff --git a/tests/Unit/Collector/HttpStreamCollectorTest.php b/tests/Unit/Collector/HttpStreamCollectorTest.php index 86d15ff58..517c19aa9 100644 --- a/tests/Unit/Collector/HttpStreamCollectorTest.php +++ b/tests/Unit/Collector/HttpStreamCollectorTest.php @@ -160,7 +160,7 @@ protected function checkCollectedData(array $data): void protected function checkSummaryData(array $data): void { parent::checkSummaryData($data); - $this->assertArrayHasKey('http_stream', $data); - $this->assertEquals(['read' => 2], $data['http_stream']); + $this->assertArrayHasKey('streams', $data); + $this->assertEquals(['read' => 2], $data['streams']); } } diff --git a/tests/Unit/Collector/VarDumperCollectorTest.php b/tests/Unit/Collector/VarDumperCollectorTest.php index e07c6c33b..ad5559507 100644 --- a/tests/Unit/Collector/VarDumperCollectorTest.php +++ b/tests/Unit/Collector/VarDumperCollectorTest.php @@ -37,8 +37,7 @@ protected function checkSummaryData(array $data): void { parent::checkSummaryData($data); $this->assertCount(1, $data); - $this->assertArrayHasKey('var-dumper', $data); - $this->assertArrayHasKey('total', $data['var-dumper']); - $this->assertEquals(1, $data['var-dumper']['total']); + $this->assertArrayHasKey('total', $data); + $this->assertEquals(1, $data['total']); } } diff --git a/tests/Unit/Collector/VarDumperHandlerInterfaceProxyTest.php b/tests/Unit/Collector/VarDumperHandlerInterfaceProxyTest.php index b8e7f3fae..c1c26fb65 100644 --- a/tests/Unit/Collector/VarDumperHandlerInterfaceProxyTest.php +++ b/tests/Unit/Collector/VarDumperHandlerInterfaceProxyTest.php @@ -34,9 +34,7 @@ public function testMethodHandle(): void ], ], $collector->getCollected()); $this->assertEquals([ - 'var-dumper' => [ - 'total' => 1, - ], + 'total' => 1, ], $collector->getSummary()); $this->assertCount(1, $timeline->getCollected());