Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,4 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- feat(api): add framework-neutral `PanelComparison` for ordered panel IDs, failure precedence, capture states, and combined structural/state counts without changing adapter output or diagnostic values.
- refactor: share captured URL-to-path display conversion through `Text::urlToPath()` while retaining original diagnostic URLs and adapter-owned presentation models.
- feat(events): add the shared execution inspector, stable chronology, grouped filters, bounded optional context and traces, and explicit lifecycle correlation.
- refactor: centralize exception messages and panel titles in typed enums while preserving diagnostics, labels, and configurable names.
3 changes: 2 additions & 1 deletion src/Capture/CapturePolicy.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace PHPForge\Debug\Capture;

use InvalidArgumentException;
use PHPForge\Debug\Exception\Message;
use PHPForge\Debug\Helper\SensitiveDataRedactor;
use SensitiveParameter;

Expand Down Expand Up @@ -50,7 +51,7 @@ public function __construct(
) {
if ($this->maxBodyBytes < 1) {
throw new InvalidArgumentException(
'The maximum body size must be greater than zero.',
Message::BODY_SIZE_INVALID->getMessage(),
);
}

Expand Down
9 changes: 7 additions & 2 deletions src/Collector/CollectorCoordinator.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace PHPForge\Debug\Collector;

use InvalidArgumentException;
use PHPForge\Debug\Exception\Message;
use PHPForge\Debug\Storage\{DebugSnapshot, PanelFailure, RequestSummary};
use Throwable;

Expand Down Expand Up @@ -37,11 +38,15 @@ public function __construct(iterable $collectors)
$id = $collector->id();

if (trim($id) === '') {
throw new InvalidArgumentException('Debug collector ID must not be empty.');
throw new InvalidArgumentException(
Message::COLLECTOR_ID_EMPTY->getMessage(),
);
}

if (isset($this->collectors[$id])) {
throw new InvalidArgumentException("Duplicate debug collector ID: {$id}.");
throw new InvalidArgumentException(
Message::COLLECTOR_ID_DUPLICATE->getMessage($id),
);
}

$this->collectors[$id] = $collector;
Expand Down
237 changes: 237 additions & 0 deletions src/Exception/Message.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
<?php

declare(strict_types=1);

namespace PHPForge\Debug\Exception;

use function sprintf;

/**
* Exception message templates authored by this package.
*
* Use {@see Message::getMessage()} to format a template with `sprintf()` arguments.
*/
enum Message: string
{
/**
* Indicates that the active tab index does not identify a supplied tab.
*
* Format: "The active tab index must identify a supplied tab."
*/
case ACTIVE_TAB_INDEX_INVALID = 'The active tab index must identify a supplied tab.';

/**
* Indicates that the maximum captured body size is not positive.
*
* Format: "The maximum body size must be greater than zero."
*/
case BODY_SIZE_INVALID = 'The maximum body size must be greater than zero.';

/**
* Indicates that a collector ID is registered more than once.
*
* Format: "Duplicate debug collector ID: %s."
*/
case COLLECTOR_ID_DUPLICATE = 'Duplicate debug collector ID: %s.';

/**
* Indicates that a collector ID is empty.
*
* Format: "Debug collector ID must not be empty."
*/
case COLLECTOR_ID_EMPTY = 'Debug collector ID must not be empty.';

/**
* Indicates that the debug data directory cannot be created.
*
* Format: "Unable to create debug data directory: %s"
*/
case DATA_DIRECTORY_CREATE_FAILED = 'Unable to create debug data directory: %s';

/**
* Indicates that permissions cannot be applied to the debug data directory.
*
* Format: "Unable to apply debug data directory mode: %s"
*/
case DATA_DIRECTORY_MODE_FAILED = 'Unable to apply debug data directory mode: %s';

/**
* Indicates that permissions cannot be applied to a debug data file.
*
* Format: "Unable to apply debug data file mode for: %s"
*/
case DATA_FILE_MODE_FAILED = 'Unable to apply debug data file mode for: %s';

/**
* Indicates that a debug data file cannot be read.
*
* Format: "Unable to read debug data file: %s"
*/
case DATA_FILE_READ_FAILED = 'Unable to read debug data file: %s';

/**
* Indicates that a debug data file cannot be removed.
*
* Format: "Unable to remove debug data file: %s"
*/
case DATA_FILE_REMOVE_FAILED = 'Unable to remove debug data file: %s';

/**
* Indicates that a debug data file cannot be replaced.
*
* Format: "Unable to replace debug data file: %s"
*/
case DATA_FILE_REPLACE_FAILED = 'Unable to replace debug data file: %s';

/**
* Indicates that a debug data file cannot be restored during rollback.
*
* Format: "Unable to roll back debug data file: %s"
*/
case DATA_FILE_ROLLBACK_FAILED = 'Unable to roll back debug data file: %s';

/**
* Indicates that the configured debug data path is not a directory.
*
* Format: "Debug data path is not a directory: %s"
*/
case DATA_PATH_NOT_DIRECTORY = 'Debug data path is not a directory: %s';

/**
* Indicates that the configured history size is invalid.
*
* Format: "Invalid debug history size: %s"
*/
case HISTORY_SIZE_INVALID = 'Invalid debug history size: %s';

/**
* Indicates that the debug data lock cannot be acquired.
*
* Format: "Unable to acquire debug data lock: %s"
*/
case LOCK_ACQUIRE_FAILED = 'Unable to acquire debug data lock: %s';

/**
* Indicates that the debug data lock file cannot be opened.
*
* Format: "Unable to open debug data lock file: %s"
*/
case LOCK_FILE_OPEN_FAILED = 'Unable to open debug data lock file: %s';

/**
* Indicates that the persisted debug manifest is empty.
*
* Format: "Debug manifest is empty: %s"
*/
case MANIFEST_EMPTY = 'Debug manifest is empty: %s';

/**
* Indicates a manifest read failure while preserving the original exception.
*
* Format: "Unable to read debug manifest."
*/
case MANIFEST_READ_ERROR = 'Unable to read debug manifest.';

/**
* Indicates that the specified debug manifest cannot be read.
*
* Format: "Unable to read debug manifest: %s"
*/
case MANIFEST_READ_FAILED = 'Unable to read debug manifest: %s';

/**
* Indicates that the N+1 detection threshold is less than two.
*
* Format: "The N+1 threshold must be at least two."
*/
case N_PLUS_ONE_THRESHOLD_INVALID = 'The N+1 threshold must be at least two.';

/**
* Indicates that a route definition field does not satisfy its expected shape.
*
* Format: "Route definition key '%s' must be %s."
*/
case ROUTE_DEFINITION_INVALID = 'Route definition key \'%s\' must be %s.';

/**
* Indicates that a sensitive key pattern is not a valid PCRE pattern.
*
* Format: "Sensitive key pattern "%s" is not a valid PCRE pattern."
*/
case SENSITIVE_KEY_PATTERN_INVALID = 'Sensitive key pattern "%s" is not a valid PCRE pattern.';

/**
* Indicates that a sensitive key prefix is empty.
*
* Format: "Sensitive key prefixes must not be empty."
*/
case SENSITIVE_KEY_PREFIX_EMPTY = 'Sensitive key prefixes must not be empty.';

/**
* Indicates that a sidebar navigation group label is empty.
*
* Format: "Sidebar navigation group labels must not be empty."
*/
case SIDEBAR_GROUP_LABEL_EMPTY = 'Sidebar navigation group labels must not be empty.';

/**
* Indicates that a persisted debug snapshot is empty.
*
* Format: "Debug snapshot is empty: %s"
*/
case SNAPSHOT_EMPTY = 'Debug snapshot is empty: %s';

/**
* Indicates that a debug snapshot cannot be read.
*
* Format: "Unable to read debug snapshot: %s"
*/
case SNAPSHOT_READ_FAILED = 'Unable to read debug snapshot: %s';

/**
* Indicates that a debug snapshot tag is invalid.
*
* Format: "Invalid debug snapshot tag: %s"
*/
case SNAPSHOT_TAG_INVALID = 'Invalid debug snapshot tag: %s';

/**
* Indicates that a snapshot tag does not match its filename.
*
* Format: "Debug snapshot tag does not match its filename: %s"
*/
case SNAPSHOT_TAG_MISMATCH = 'Debug snapshot tag does not match its filename: %s';

/**
* Indicates that a snapshot value violates the expected schema at a payload path.
*
* Format: "Invalid debug snapshot value at '%s': expected %s."
*/
case SNAPSHOT_VALUE_INVALID = 'Invalid debug snapshot value at \'%s\': expected %s.';

/**
* Indicates that a temporary debug data file cannot be written.
*
* Format: "Unable to write temporary debug data file for: %s"
*/
case TEMPORARY_FILE_WRITE_FAILED = 'Unable to write temporary debug data file for: %s';

/**
* Indicates that the storage transaction journal is invalid.
*
* Format: "Invalid debug storage transaction journal: %s"
*/
case TRANSACTION_JOURNAL_INVALID = 'Invalid debug storage transaction journal: %s';

/**
* Formats the message without changing diagnostic argument values.
*
* @param int|string ...$argument Values to insert into the message template.
*
* @return string Formatted exception message.
*/
public function getMessage(int|string ...$argument): string
{
return sprintf($this->value, ...$argument);
}
}
8 changes: 5 additions & 3 deletions src/Helper/SensitiveDataRedactor.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace PHPForge\Debug\Helper;

use InvalidArgumentException;
use PHPForge\Debug\Exception\Message;
use SensitiveParameter;

use function array_fill_keys;
Expand All @@ -14,7 +15,6 @@
use function is_object;
use function is_string;
use function preg_match;
use function sprintf;
use function str_starts_with;
use function strtolower;

Expand Down Expand Up @@ -204,7 +204,9 @@ private static function prefixes(array $prefixes): array
{
foreach ($prefixes as $prefix) {
if ($prefix === '') {
throw new InvalidArgumentException('Sensitive key prefixes must not be empty.');
throw new InvalidArgumentException(
Message::SENSITIVE_KEY_PREFIX_EMPTY->getMessage(),
);
}
}

Expand Down Expand Up @@ -243,7 +245,7 @@ private static function validatePatterns(array $patterns): void
foreach ($patterns as $pattern) {
if ($pattern === '' || @preg_match($pattern, '') === false) {
throw new InvalidArgumentException(
sprintf('Sensitive key pattern "%s" is not a valid PCRE pattern.', $pattern),
Message::SENSITIVE_KEY_PATTERN_INVALID->getMessage($pattern),
);
}
}
Expand Down
3 changes: 2 additions & 1 deletion src/Helper/Tabs.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace PHPForge\Debug\Helper;

use InvalidArgumentException;
use PHPForge\Debug\Exception\Message;
use UIAwesome\Html\Flow\Div;
use UIAwesome\Html\List\{Li, Ul};
use UIAwesome\Html\Palpable\A;
Expand All @@ -26,7 +27,7 @@ public static function render(string $id, string $ariaLabel, array $tabs, int $a
{
if ($tabs !== [] && !array_key_exists($activeIndex, $tabs)) {
throw new InvalidArgumentException(
'The active tab index must identify a supplied tab.',
Message::ACTIVE_TAB_INDEX_INVALID->getMessage(),
);
}

Expand Down
3 changes: 2 additions & 1 deletion src/Panel/Asset/AssetSectionRenderer.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace PHPForge\Debug\Panel\Asset;

use PHPForge\Debug\Helper\Icon;
use PHPForge\Debug\Panel\PanelTitle;
use UIAwesome\Html\Flow\Div;
use UIAwesome\Html\Heading\H1;
use UIAwesome\Html\List\{Li, Ol};
Expand Down Expand Up @@ -49,7 +50,7 @@ public static function renderHeader(AssetSummary $summary): string

return H1::tag()
->class('yii-debug-sr-only')
->content('Asset Bundles')
->content(PanelTitle::ASSETS)
->render()
. Header::tag()
->class('yii-debug-asset-stats')
Expand Down
3 changes: 2 additions & 1 deletion src/Panel/Db/DbExplainRenderer.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace PHPForge\Debug\Panel\Db;

use PHPForge\Debug\Helper\Dump;
use PHPForge\Debug\Panel\PanelTitle;
use UIAwesome\Html\Flow\{Div, P, Pre};
use UIAwesome\Html\Heading\H1;
use UIAwesome\Html\Phrasing\Em;
Expand Down Expand Up @@ -50,7 +51,7 @@ private static function renderPlan(string $query, array $results, string|null $e
$children = [
H1::tag()
->class('yii-debug-explain-title')
->content('EXPLAIN'),
->content(PanelTitle::EXPLAIN),
];

if ($query !== '') {
Expand Down
5 changes: 4 additions & 1 deletion src/Panel/Db/NPlusOneDetector.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace PHPForge\Debug\Panel\Db;

use InvalidArgumentException;
use PHPForge\Debug\Exception\Message;

use function min;
use function strtoupper;
Expand All @@ -23,7 +24,9 @@ final class NPlusOneDetector
public static function detect(array $rows, int $threshold = 3): array
{
if ($threshold < 2) {
throw new InvalidArgumentException('The N+1 threshold must be at least two.');
throw new InvalidArgumentException(
Message::N_PLUS_ONE_THRESHOLD_INVALID->getMessage(),
);
}

/**
Expand Down
Loading
Loading