From c40661b6f2c0fc9af7755879b76bf0e31ab31e6d Mon Sep 17 00:00:00 2001 From: David Grudl Date: Mon, 12 Oct 2026 21:25:02 +0200 Subject: [PATCH 01/32] CI: coverage via pcov --- .github/workflows/tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index bb13517de..1cb7b1139 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -43,10 +43,10 @@ jobs: with: php-version: 8.2 extensions: ds - coverage: none + coverage: pcov - run: composer install --no-progress --prefer-dist - - run: composer tester -- -p phpdbg ${{ env.php-options }} --coverage ./coverage.xml --coverage-src ./src + - run: composer tester -- ${{ env.php-options }} --coverage ./coverage.xml --coverage-src ./src continue-on-error: true - uses: coverallsapp/github-action@v2 with: From e906a54645185f1ce8c7548c2dcedc3d40155921 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Tue, 13 Oct 2026 01:58:42 +0200 Subject: [PATCH 02/32] phpstan: removed stale ignores, requires PHPStan 2.3 --- composer.json | 2 +- phpstan.neon | 8 -------- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/composer.json b/composer.json index a4f2e7530..5400741c9 100644 --- a/composer.json +++ b/composer.json @@ -27,7 +27,7 @@ "nette/tester": "^2.6", "latte/latte": "^2.5 || ^3.0", "psr/log": "^1.0 || ^2.0 || ^3.0", - "phpstan/phpstan": "^2.1@stable", + "phpstan/phpstan": "^2.3@stable", "phpstan/extension-installer": "^1.4@stable", "nette/phpstan-rules": "^1.0" }, diff --git a/phpstan.neon b/phpstan.neon index 780589249..52c9c40c1 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -63,14 +63,6 @@ parameters: identifier: ternary.alwaysTrue path: src/Tracy/Dumper/Renderer.php - # PHP 8.4+ adds 'trace' key to error_get_last() return - - - identifier: empty.offset - path: src/Tracy/Debugger/Debugger.php - - - identifier: offsetAccess.notFound - path: src/Tracy/Debugger/Debugger.php - # Variable is always defined when used (same function_exists() condition guards both paths) - identifier: variable.undefined From 9677c950574841f3744cb63f346bd0fdde4e226a Mon Sep 17 00:00:00 2001 From: David Grudl Date: Tue, 13 Oct 2026 02:00:32 +0200 Subject: [PATCH 03/32] cs: only compiler-optimized functions in use-function imports --- phpstan.neon | 5 ----- src/Bridges/Nette/Bridge.php | 1 - src/Tracy/Bar/panels/info.panel.php | 4 ++-- src/Tracy/BlueScreen/BlueScreen.php | 10 +++++----- src/Tracy/BlueScreen/CodeHighlighter.php | 3 +-- src/Tracy/Debugger/Debugger.php | 8 ++++---- src/Tracy/Debugger/DeferredContent.php | 2 +- src/Tracy/Debugger/DevelopmentStrategy.php | 2 +- src/Tracy/Debugger/ProductionStrategy.php | 3 +-- src/Tracy/Dumper/Describer.php | 4 ++-- src/Tracy/Dumper/Dumper.php | 2 -- src/Tracy/Dumper/Exposer.php | 2 +- src/Tracy/Dumper/Renderer.php | 2 +- src/Tracy/Helpers.php | 6 +++--- src/Tracy/Logger/Logger.php | 6 +++--- src/Tracy/OutputDebugger/OutputDebugger.php | 3 +-- src/Tracy/Session/FileSession.php | 1 - src/Tracy/Session/NativeSession.php | 1 - 18 files changed, 26 insertions(+), 39 deletions(-) diff --git a/phpstan.neon b/phpstan.neon index 52c9c40c1..3f9d0ad99 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -63,11 +63,6 @@ parameters: identifier: ternary.alwaysTrue path: src/Tracy/Dumper/Renderer.php - # Variable is always defined when used (same function_exists() condition guards both paths) - - - identifier: variable.undefined - path: src/Tracy/Debugger/DevelopmentStrategy.php - # Deprecated property without type - identifier: missingType.property diff --git a/src/Bridges/Nette/Bridge.php b/src/Bridges/Nette/Bridge.php index 31108464e..bfde5fd5a 100644 --- a/src/Bridges/Nette/Bridge.php +++ b/src/Bridges/Nette/Bridge.php @@ -11,7 +11,6 @@ use Tracy; use Tracy\BlueScreen; use Tracy\Helpers; -use const ENT_IGNORE; /** diff --git a/src/Tracy/Bar/panels/info.panel.php b/src/Tracy/Bar/panels/info.panel.php index 325a9cad1..d20836fbe 100644 --- a/src/Tracy/Bar/panels/info.panel.php +++ b/src/Tracy/Bar/panels/info.panel.php @@ -3,7 +3,7 @@ namespace Tracy; use Composer\Autoload\ClassLoader; -use function count; +use function count, extension_loaded, function_exists; use const PHP_VERSION, PHP_ZTS; /** @var DefaultBarPanel $this */ @@ -50,7 +50,7 @@ $info = array_map('strval', array_filter($info + (array) $this->data)); $packages = $devPackages = []; -if (class_exists(ClassLoader::class, autoload: false)) { +if (class_exists(ClassLoader::class, false)) { $baseDir = (function () { $baseDir = null; @include dirname((string) (new \ReflectionClass(ClassLoader::class))->getFileName()) . '/autoload_psr4.php'; // @ may not exist diff --git a/src/Tracy/BlueScreen/BlueScreen.php b/src/Tracy/BlueScreen/BlueScreen.php index a670f58f0..d648db80e 100644 --- a/src/Tracy/BlueScreen/BlueScreen.php +++ b/src/Tracy/BlueScreen/BlueScreen.php @@ -7,8 +7,8 @@ namespace Tracy; -use function count, in_array; -use const ARRAY_FILTER_USE_KEY, ENT_IGNORE, PHP_VERSION_ID; +use function count, function_exists, in_array; +use const INFO_CONFIGURATION, INFO_MODULES, PHP_VERSION_ID; /** @@ -72,7 +72,7 @@ public function __construct() public function addPanel(callable $panel): static { $panel = $panel(...); - if (!in_array($panel, $this->panels, strict: true)) { + if (!in_array($panel, $this->panels, true)) { $this->panels[] = $panel; } @@ -290,7 +290,7 @@ private function renderActions(\Throwable $ex): array if (preg_match('# ([\'"])(\w{3,}(?:\\\\\w{2,})+)\1#i', $ex->getMessage(), $m)) { $class = $m[2]; if ( - !class_exists($class, autoload: false) && !interface_exists($class, autoload: false) && !trait_exists($class, autoload: false) + !class_exists($class, false) && !interface_exists($class, autoload: false) && !trait_exists($class, autoload: false) && ($file = Helpers::guessClassFile($class)) && !@is_file($file) // @ - may trigger error ) { [$content, $line] = $this->generateNewFileContents($file, $class); @@ -481,7 +481,7 @@ public function formatMessage(\Throwable $exception): string $msg = preg_replace_callback( '#(\w+\\\[\w\\\]+\w)(?:::(\w+))?#', function ($m) { - $classLike = class_exists($m[1], autoload: false) || interface_exists($m[1], autoload: false) || trait_exists($m[1], autoload: false); + $classLike = class_exists($m[1], false) || interface_exists($m[1], autoload: false) || trait_exists($m[1], autoload: false); if ($classLike && isset($m[2]) && method_exists($m[1], $m[2])) { $r = new \ReflectionMethod($m[1], $m[2]); } elseif ($classLike) { diff --git a/src/Tracy/BlueScreen/CodeHighlighter.php b/src/Tracy/BlueScreen/CodeHighlighter.php index 0820b4a4a..312ad33b3 100644 --- a/src/Tracy/BlueScreen/CodeHighlighter.php +++ b/src/Tracy/BlueScreen/CodeHighlighter.php @@ -7,8 +7,7 @@ namespace Tracy; -use function array_pop, array_shift, array_unshift, count, explode, floor, implode, max, min, preg_replace, preg_replace_callback, rtrim, sprintf, str_replace, strip_tags, strlen, strtr; -use const T_CLASS_C, T_CLOSE_TAG, T_COMMENT, T_CONSTANT_ENCAPSED_STRING, T_DIR, T_DNUMBER, T_DOC_COMMENT, T_ENCAPSED_AND_WHITESPACE, T_FILE, T_FUNC_C, T_INLINE_HTML, T_LINE, T_LNUMBER, T_METHOD_C, T_NAME_FULLY_QUALIFIED, T_NAME_QUALIFIED, T_NAME_RELATIVE, T_NS_C, T_OPEN_TAG, T_OPEN_TAG_WITH_ECHO, T_STRING, T_TRAIT_C, T_VARIABLE, T_WHITESPACE; +use function count, strlen; /** @internal */ diff --git a/src/Tracy/Debugger/Debugger.php b/src/Tracy/Debugger/Debugger.php index b48dbb75e..c7e7d7997 100644 --- a/src/Tracy/Debugger/Debugger.php +++ b/src/Tracy/Debugger/Debugger.php @@ -8,7 +8,7 @@ namespace Tracy; use ErrorException; -use function in_array, is_bool, is_int, is_string; +use function dirname, function_exists, in_array, ini_get, is_bool, is_int, is_string; use const PHP_VERSION; @@ -280,7 +280,7 @@ public static function isEnabled(): bool public static function shutdownHandler(): void { $error = error_get_last(); - if (in_array($error['type'] ?? null, [E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR, E_PARSE, E_RECOVERABLE_ERROR, E_USER_ERROR], strict: true)) { + if (in_array($error['type'] ?? null, [E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR, E_PARSE, E_RECOVERABLE_ERROR, E_USER_ERROR], true)) { $e = new ErrorException($error['message'], 0, $error['type'], $error['file'], $error['line']); if (!empty($error['trace'])) { (new \ReflectionClass(\Exception::class))->getProperty('trace')->setValue($e, $error['trace']); @@ -368,7 +368,7 @@ public static function removeOutputBuffers(bool $errorOccurred): void { while (ob_get_level() > self::$obLevel) { $status = ob_get_status(); - if (in_array($status['name'], ['ob_gzhandler', 'zlib output compression'], strict: true)) { + if (in_array($status['name'], ['ob_gzhandler', 'zlib output compression'], true)) { break; } @@ -632,7 +632,7 @@ public static function detectDebugMode(string|array|null $list = null): bool $list[] = '[::1]'; // workaround for PHP < 7.3.4 } - return in_array($addr, $list, strict: true) || in_array("$secret@$addr", $list, strict: true); + return in_array($addr, $list, true) || in_array("$secret@$addr", $list, true); } diff --git a/src/Tracy/Debugger/DeferredContent.php b/src/Tracy/Debugger/DeferredContent.php index cb40be198..d9c31ee81 100644 --- a/src/Tracy/Debugger/DeferredContent.php +++ b/src/Tracy/Debugger/DeferredContent.php @@ -7,7 +7,7 @@ namespace Tracy; -use function array_slice, is_string, strlen; +use function is_string, strlen; /** diff --git a/src/Tracy/Debugger/DevelopmentStrategy.php b/src/Tracy/Debugger/DevelopmentStrategy.php index 8f81711fa..cfea3d789 100644 --- a/src/Tracy/Debugger/DevelopmentStrategy.php +++ b/src/Tracy/Debugger/DevelopmentStrategy.php @@ -8,7 +8,7 @@ namespace Tracy; use ErrorException; -use function is_bool; +use function function_exists, is_bool; /** diff --git a/src/Tracy/Debugger/ProductionStrategy.php b/src/Tracy/Debugger/ProductionStrategy.php index ed760e00a..32b01d988 100644 --- a/src/Tracy/Debugger/ProductionStrategy.php +++ b/src/Tracy/Debugger/ProductionStrategy.php @@ -8,8 +8,7 @@ namespace Tracy; use ErrorException; -use function is_resource; -use const STDERR; +use function function_exists, ini_get, is_resource; /** diff --git a/src/Tracy/Dumper/Describer.php b/src/Tracy/Dumper/Describer.php index 76a938c36..fbba0ec94 100644 --- a/src/Tracy/Dumper/Describer.php +++ b/src/Tracy/Dumper/Describer.php @@ -8,7 +8,7 @@ namespace Tracy\Dumper; use Tracy\Helpers; -use function array_map, array_slice, class_exists, count, file, get_debug_type, get_resource_type, htmlspecialchars, implode, interface_exists, is_array, is_bool, is_float, is_int, is_object, is_resource, is_string, is_subclass_of, json_encode, method_exists, preg_match, spl_object_id, str_replace, strlen, strpos, strtolower, trim, uksort; +use function count, is_array, is_bool, is_float, is_int, is_object, is_resource, is_string, strlen; /** @@ -55,7 +55,7 @@ public function describe(mixed $var): \stdClass { // exposers are sorted from the most specific type to the most general; '' acts as the universal supertype $isSubtypeOf = fn(string $type, string $parent): bool => $parent === '' - || ((class_exists($type, autoload: false) || interface_exists($type, autoload: false)) && is_subclass_of($type, $parent)); + || ((class_exists($type, false) || interface_exists($type, autoload: false)) && is_subclass_of($type, $parent)); uksort($this->objectExposers, fn($a, $b): int => $isSubtypeOf($b, $a) <=> $isSubtypeOf($a, $b)); try { diff --git a/src/Tracy/Dumper/Dumper.php b/src/Tracy/Dumper/Dumper.php index 552967c9a..8cc8764a7 100644 --- a/src/Tracy/Dumper/Dumper.php +++ b/src/Tracy/Dumper/Dumper.php @@ -13,8 +13,6 @@ use Tracy\Dumper\Exposer; use Tracy\Dumper\Renderer; use Uri; -use function array_flip, array_map, file_get_contents, fwrite, str_replace; -use const STDOUT; /** diff --git a/src/Tracy/Dumper/Exposer.php b/src/Tracy/Dumper/Exposer.php index 87106d7f3..abc5f5a1f 100644 --- a/src/Tracy/Dumper/Exposer.php +++ b/src/Tracy/Dumper/Exposer.php @@ -10,7 +10,7 @@ use Dom; use Ds; use Uri; -use function array_diff_key, array_key_exists, count, end, explode, get_debug_type, get_mangled_object_vars, implode, iterator_to_array, sort; +use function array_key_exists, count; use const PHP_VERSION_ID; diff --git a/src/Tracy/Dumper/Renderer.php b/src/Tracy/Dumper/Renderer.php index e849c6f79..4505c3905 100644 --- a/src/Tracy/Dumper/Renderer.php +++ b/src/Tracy/Dumper/Renderer.php @@ -8,7 +8,7 @@ namespace Tracy\Dumper; use Tracy\Helpers; -use function count, htmlspecialchars, is_array, is_bool, is_float, is_int, is_object, is_string, str_repeat, str_replace, strlen, strrpos, substr, substr_count; +use function count, is_array, is_bool, is_float, is_int, is_object, is_string, strlen; /** diff --git a/src/Tracy/Helpers.php b/src/Tracy/Helpers.php index 96258dd70..0a1e259ef 100644 --- a/src/Tracy/Helpers.php +++ b/src/Tracy/Helpers.php @@ -7,8 +7,8 @@ namespace Tracy; -use function array_filter, array_map, array_merge, array_pop, array_slice, array_unique, basename, bin2hex, class_exists, constant, count, dechex, defined, dirname, end, escapeshellarg, explode, extension_loaded, func_get_args, function_exists, get_class_methods, get_declared_classes, get_defined_functions, getenv, getmypid, headers_list, htmlspecialchars, htmlspecialchars_decode, iconv_strlen, implode, in_array, ini_set, is_a, is_array, is_callable, is_file, is_object, is_string, json_encode, levenshtein, ltrim, mb_strlen, mb_substr, method_exists, ob_end_clean, ob_get_clean, ob_start, ord, preg_match, preg_replace, preg_replace_callback, random_bytes, rawurlencode, rtrim, sapi_windows_vt100_support, spl_object_id, str_contains, str_pad, str_replace, strcasecmp, stream_isatty, strip_tags, strlen, strtoupper, strtr, substr, trait_exists, utf8_decode; -use const DIRECTORY_SEPARATOR, ENT_HTML5, ENT_QUOTES, ENT_SUBSTITUTE, JSON_HEX_AMP, JSON_HEX_APOS, JSON_HEX_TAG, JSON_INVALID_UTF8_SUBSTITUTE, JSON_UNESCAPED_SLASHES, JSON_UNESCAPED_UNICODE, PHP_EOL, PHP_SAPI, STDOUT, STR_PAD_LEFT; +use function count, defined, extension_loaded, func_get_args, function_exists, is_array, is_object, is_string, strlen; +use const ENT_HTML5, ENT_QUOTES, ENT_SUBSTITUTE, PHP_SAPI; /** @@ -682,7 +682,7 @@ public static function detectColors(): bool public static function getExceptionChain(\Throwable $ex): array { $res = [$ex]; - while (($ex = $ex->getPrevious()) && !in_array($ex, $res, strict: true)) { + while (($ex = $ex->getPrevious()) && !in_array($ex, $res, true)) { $res[] = $ex; } diff --git a/src/Tracy/Logger/Logger.php b/src/Tracy/Logger/Logger.php index 39b5d458c..f856ba16a 100644 --- a/src/Tracy/Logger/Logger.php +++ b/src/Tracy/Logger/Logger.php @@ -7,8 +7,8 @@ namespace Tracy; -use function in_array, is_string; -use const DIRECTORY_SEPARATOR, FILE_APPEND, LOCK_EX, LOCK_UN, PHP_EOL; +use function is_string; +use const DIRECTORY_SEPARATOR, FILE_APPEND, LOCK_EX; /** @@ -74,7 +74,7 @@ public function log(mixed $message, string $level = self::INFO) $this->logException($message, $exceptionFile); } - if (in_array($level, [self::ERROR, self::EXCEPTION, self::CRITICAL], strict: true)) { + if (in_array($level, [self::ERROR, self::EXCEPTION, self::CRITICAL], true)) { $this->sendEmail($message); } diff --git a/src/Tracy/OutputDebugger/OutputDebugger.php b/src/Tracy/OutputDebugger/OutputDebugger.php index 87b21605e..a4841ac46 100644 --- a/src/Tracy/OutputDebugger/OutputDebugger.php +++ b/src/Tracy/OutputDebugger/OutputDebugger.php @@ -7,8 +7,7 @@ namespace Tracy; -use function array_slice, count; -use const PHP_OUTPUT_HANDLER_FINAL; +use function count; /** diff --git a/src/Tracy/Session/FileSession.php b/src/Tracy/Session/FileSession.php index 2c13182f4..fff5e4e04 100644 --- a/src/Tracy/Session/FileSession.php +++ b/src/Tracy/Session/FileSession.php @@ -8,7 +8,6 @@ namespace Tracy; use function is_string; -use const LOCK_EX, LOCK_UN; /** diff --git a/src/Tracy/Session/NativeSession.php b/src/Tracy/Session/NativeSession.php index b662ed2e8..49e248f68 100644 --- a/src/Tracy/Session/NativeSession.php +++ b/src/Tracy/Session/NativeSession.php @@ -7,7 +7,6 @@ namespace Tracy; -use const PHP_SESSION_ACTIVE; /** From 484447fe21b62eb26af567d3a0fdba5b0778a079 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Tue, 13 Oct 2026 02:02:18 +0200 Subject: [PATCH 04/32] cs: suppression comments --- src/Tracy/BlueScreen/BlueScreen.php | 4 ++-- src/Tracy/Debugger/Debugger.php | 8 ++++---- src/Tracy/Debugger/DevelopmentStrategy.php | 4 ++-- src/Tracy/Helpers.php | 4 ++-- src/Tracy/Session/FileSession.php | 6 +++--- tests/Tracy/BlueScreen.renderActions().phpt | 6 +++--- tests/Tracy/Debugger.exception.html.phpt | 2 +- tests/Tracy/Debugger.scream.E_USER_DEPRECATED.phpt | 4 ++-- tests/Tracy/Debugger.scream.phpt | 6 +++--- tests/Tracy/Debugger.shut-up.error.phpt | 2 +- tests/Tracy/Debugger.shut-up.warnings.phpt | 6 +++--- tests/Tracy/Helpers.improveError.phpt | 8 ++++---- tests/Tracy/Helpers.improveException.phpt | 10 +++++----- tests/Tracy/expected/Debugger.exception.html.expect | 2 +- tests/bootstrap.php | 6 +++--- tools/latte-convert/tests/bootstrap.php | 2 +- 16 files changed, 40 insertions(+), 40 deletions(-) diff --git a/src/Tracy/BlueScreen/BlueScreen.php b/src/Tracy/BlueScreen/BlueScreen.php index d648db80e..7e34728b6 100644 --- a/src/Tracy/BlueScreen/BlueScreen.php +++ b/src/Tracy/BlueScreen/BlueScreen.php @@ -148,7 +148,7 @@ public function renderToAjax(\Throwable $exception, DeferredContent $defer): voi */ public function renderToFile(\Throwable $exception, string $file, ?array $logLocation = null): bool { - if ($handle = @fopen($file, 'x')) { + if ($handle = @fopen($file, 'x')) { // @ file may already exist ob_start(); // double buffer prevents sending HTTP headers in some PHP ob_start(function ($buffer) use ($handle) { fwrite($handle, $buffer); @@ -159,7 +159,7 @@ public function renderToFile(\Throwable $exception, string $file, ?array $logLoc ob_end_clean(); fclose($handle); - if ($handle = @fopen(substr($file, 0, -5) . '.md', 'x')) { + if ($handle = @fopen(substr($file, 0, -5) . '.md', 'x')) { // @ file may already exist fwrite($handle, $this->renderAgent($exception, $logLocation)); fclose($handle); } diff --git a/src/Tracy/Debugger/Debugger.php b/src/Tracy/Debugger/Debugger.php index c7e7d7997..8d4e19bfa 100644 --- a/src/Tracy/Debugger/Debugger.php +++ b/src/Tracy/Debugger/Debugger.php @@ -314,7 +314,7 @@ public static function exceptionHandler(\Throwable $exception): void self::$reserved = null; self::$obStatus = ob_get_status(full_status: true); - @http_response_code(500); + @http_response_code(500); // @ headers may already be sent Helpers::improveException($exception); self::removeOutputBuffers(errorOccurred: true); @@ -458,9 +458,9 @@ public static function setSessionStorage(SessionStorage $storage): void public static function getSessionStorage(): SessionStorage { if (empty(self::$sessionStorage)) { - self::$sessionStorage = @is_dir($dir = (string) session_save_path()) - || @is_dir($dir = (string) ini_get('upload_tmp_dir')) - || @is_dir($dir = sys_get_temp_dir()) + self::$sessionStorage = @is_dir($dir = (string) session_save_path()) // @ the path may be too long or outside open_basedir + || @is_dir($dir = (string) ini_get('upload_tmp_dir')) // @ the path may be too long or outside open_basedir + || @is_dir($dir = sys_get_temp_dir()) // @ the path may be too long or outside open_basedir || ($dir = (string) self::$logDirectory) ? new FileSession($dir) : new NativeSession; diff --git a/src/Tracy/Debugger/DevelopmentStrategy.php b/src/Tracy/Debugger/DevelopmentStrategy.php index cfea3d789..689f16a2d 100644 --- a/src/Tracy/Debugger/DevelopmentStrategy.php +++ b/src/Tracy/Debugger/DevelopmentStrategy.php @@ -72,7 +72,7 @@ private function renderExceptionCli(\Throwable $exception): void return; } - if (Helpers::detectColors() && @is_file($exception->getFile())) { + if (Helpers::detectColors() && @is_file($exception->getFile())) { // @ the path may be too long or outside open_basedir echo "\n\n" . CodeHighlighter::highlightPhpCli((string) file_get_contents($exception->getFile()), $exception->getLine()) . "\n"; } @@ -99,7 +99,7 @@ public function handleError( && !isset($_GET['_tracy_skip_error']) ) { $e = new ErrorException($message, 0, $severity, $file, $line); - @$e->skippable = true; // dynamic properties are deprecated since PHP 8.2 + @$e->skippable = true; // @ dynamic properties are deprecated Debugger::exceptionHandler($e); exit(255); } diff --git a/src/Tracy/Helpers.php b/src/Tracy/Helpers.php index 0a1e259ef..460e514df 100644 --- a/src/Tracy/Helpers.php +++ b/src/Tracy/Helpers.php @@ -303,7 +303,7 @@ public static function improveException(\Throwable $e): void if (isset($replace)) { $loc = Debugger::mapSource($e->getFile(), $e->getLine()) ?? ['file' => $e->getFile(), 'line' => $e->getLine()]; - @$e->tracyAction = [ // dynamic properties are deprecated since PHP 8.2 + @$e->tracyAction = [ // @ dynamic properties are deprecated 'link' => self::editorUri($loc['file'], $loc['line'], 'fix', $replace[0], $replace[1]), 'label' => 'fix it', ]; @@ -527,7 +527,7 @@ public static function utf8Length(string $s): int return match (true) { extension_loaded('mbstring') => mb_strlen($s, 'UTF-8'), extension_loaded('iconv') => iconv_strlen($s, 'UTF-8') ?: strlen($s), - default => strlen(@utf8_decode($s)), // deprecated + default => strlen(@utf8_decode($s)), // @ utf8_decode() is deprecated }; } diff --git a/src/Tracy/Session/FileSession.php b/src/Tracy/Session/FileSession.php index fff5e4e04..f778f6df2 100644 --- a/src/Tracy/Session/FileSession.php +++ b/src/Tracy/Session/FileSession.php @@ -52,7 +52,7 @@ private function open(): void if ( !is_string($id) || !preg_match('#^\w{10}\z#i', $id) - || !($file = @fopen($path = $this->dir . '/' . self::FilePrefix . $id, 'r+')) // intentionally @ + || !($file = @fopen($path = $this->dir . '/' . self::FilePrefix . $id, 'r+')) // @ file may not exist ) { $id = Helpers::createId(); setcookie($this->cookieName, $id, [ @@ -63,7 +63,7 @@ private function open(): void 'samesite' => 'Lax', ]); - $file = @fopen($path = $this->dir . '/' . self::FilePrefix . $id, 'c+'); // intentionally @ + $file = @fopen($path = $this->dir . '/' . self::FilePrefix . $id, 'c+'); // @ is escalated to exception if ($file === false) { throw new \RuntimeException("Unable to create file '$path'. " . (error_get_last()['message'] ?? '')); } @@ -71,7 +71,7 @@ private function open(): void @chmod($path, 0o600); // @ - may not be supported by the filesystem } - if (!@flock($file, LOCK_EX)) { // intentionally @ + if (!@flock($file, LOCK_EX)) { // @ is escalated to exception throw new \RuntimeException("Unable to acquire exclusive lock on '$path'. " . (error_get_last()['message'] ?? '')); } diff --git a/tests/Tracy/BlueScreen.renderActions().phpt b/tests/Tracy/BlueScreen.renderActions().phpt index a0bfc53ed..7d095faa0 100644 --- a/tests/Tracy/BlueScreen.renderActions().phpt +++ b/tests/Tracy/BlueScreen.renderActions().phpt @@ -61,7 +61,7 @@ Assert::with($blueScreen, function () { ); if (PHP_SAPI !== 'cli' && PHP_SAPI !== 'phpdbg') { - @$e->skippable = true; // deprecated since PHP 8.2 + @$e->skippable = true; // @ dynamic properties are deprecated Assert::same( [ $search, @@ -127,11 +127,11 @@ Assert::with($blueScreen, function () { // $e->tracyAction Assert::with($blueScreen, function () { $e = new Exception; - @$e->tracyAction = []; // deprecated since PHP 8.2 + @$e->tracyAction = []; // @ dynamic properties are deprecated Assert::count(1, $this->renderActions($e)); $e = new Exception; - @$e->tracyAction = ['link' => 'a', 'label' => 'b']; // deprecated since PHP 8.2 + @$e->tracyAction = ['link' => 'a', 'label' => 'b']; // @ dynamic properties are deprecated Assert::same( ['link' => 'a', 'label' => 'b'], $this->renderActions($e)[0], diff --git a/tests/Tracy/Debugger.exception.html.phpt b/tests/Tracy/Debugger.exception.html.phpt index 44598c878..0b97d81bc 100644 --- a/tests/Tracy/Debugger.exception.html.phpt +++ b/tests/Tracy/Debugger.exception.html.phpt @@ -41,5 +41,5 @@ function third($arg1) define('MY_CONST', 123); -@hex2bin('a'); // E_WARNING +@hex2bin('a'); // @ E_WARNING muted on purpose first(10, 'any string'); diff --git a/tests/Tracy/Debugger.scream.E_USER_DEPRECATED.phpt b/tests/Tracy/Debugger.scream.E_USER_DEPRECATED.phpt index 84bf18afb..3dd71fb58 100644 --- a/tests/Tracy/Debugger.scream.E_USER_DEPRECATED.phpt +++ b/tests/Tracy/Debugger.scream.E_USER_DEPRECATED.phpt @@ -15,6 +15,6 @@ Debugger::$scream = E_USER_DEPRECATED; Debugger::enable(); trigger_error('E_USER_WARNING that should be reported', E_USER_WARNING); -@trigger_error('Muted E_USER_WARNING that should be ignored', E_USER_WARNING); +@trigger_error('Muted E_USER_WARNING that should be ignored', E_USER_WARNING); // @ muted on purpose -@trigger_error('Muted E_USER_DEPRECATED that should be reported', E_USER_DEPRECATED); +@trigger_error('Muted E_USER_DEPRECATED that should be reported', E_USER_DEPRECATED); // @ muted on purpose diff --git a/tests/Tracy/Debugger.scream.phpt b/tests/Tracy/Debugger.scream.phpt index 3c9d089c4..875a656c3 100644 --- a/tests/Tracy/Debugger.scream.phpt +++ b/tests/Tracy/Debugger.scream.phpt @@ -14,7 +14,7 @@ Debugger::$productionMode = false; Debugger::$scream = true; Debugger::enable(); -@$x = &pi(); // E_NOTICE -@hex2bin('a'); // E_WARNING -@require __DIR__ . '/fixtures/E_COMPILE_WARNING.php'; // E_COMPILE_WARNING +@$x = &pi(); // @ E_NOTICE muted on purpose +@hex2bin('a'); // @ E_WARNING muted on purpose +@require __DIR__ . '/fixtures/E_COMPILE_WARNING.php'; // @ E_COMPILE_WARNING muted on purpose // E_COMPILE_WARNING is handled in shutdownHandler() and does not know that @ was used diff --git a/tests/Tracy/Debugger.shut-up.error.phpt b/tests/Tracy/Debugger.shut-up.error.phpt index f44ceaee9..6a8d9e428 100644 --- a/tests/Tracy/Debugger.shut-up.error.phpt +++ b/tests/Tracy/Debugger.shut-up.error.phpt @@ -15,4 +15,4 @@ require __DIR__ . '/../bootstrap.php'; Debugger::$productionMode = false; Debugger::enable(); -@missing_function(); +@missing_function(); // @ muted on purpose diff --git a/tests/Tracy/Debugger.shut-up.warnings.phpt b/tests/Tracy/Debugger.shut-up.warnings.phpt index f67288590..a0c1531d6 100644 --- a/tests/Tracy/Debugger.shut-up.warnings.phpt +++ b/tests/Tracy/Debugger.shut-up.warnings.phpt @@ -13,7 +13,7 @@ require __DIR__ . '/../bootstrap.php'; Debugger::$productionMode = false; Debugger::enable(); -@$x = &pi(); // E_NOTICE -@hex2bin('a'); // E_WARNING -@require __DIR__ . '/fixtures/E_COMPILE_WARNING.php'; // E_COMPILE_WARNING +@$x = &pi(); // @ E_NOTICE muted on purpose +@hex2bin('a'); // @ E_WARNING muted on purpose +@require __DIR__ . '/fixtures/E_COMPILE_WARNING.php'; // @ E_COMPILE_WARNING muted on purpose // E_COMPILE_WARNING is handled in shutdownHandler() and does not know that @ was used diff --git a/tests/Tracy/Helpers.improveError.phpt b/tests/Tracy/Helpers.improveError.phpt index 9cdc3c502..d0d934b98 100644 --- a/tests/Tracy/Helpers.improveError.phpt +++ b/tests/Tracy/Helpers.improveError.phpt @@ -44,19 +44,19 @@ $obj = new TestClass; test('reading', function () use ($obj) { - @$val = $obj->publicX; + @$val = $obj->publicX; // @ the error is what the test reads $message = Helpers::improveError(error_get_last()['message']); Assert::same('Undefined property: TestClass::$publicX, did you mean $public?', $message); }); test('suggest only non-static property', function () use ($obj) { - @$val = $obj->publicStaticX; + @$val = $obj->publicStaticX; // @ the error is what the test reads $message = Helpers::improveError(error_get_last()['message']); Assert::same('Undefined property: TestClass::$publicStaticX', $message); }); test('suggest only public property', function () use ($obj) { - @$val = $obj->protectedX; + @$val = $obj->protectedX; // @ the error is what the test reads $message = Helpers::improveError(error_get_last()['message']); Assert::same('Undefined property: TestClass::$protectedX', $message); }); @@ -64,7 +64,7 @@ test('suggest only public property', function () use ($obj) { test('do not suggest anything when accessing anonymous class', function () { $obj = new class { }; - @$val = $obj->property; + @$val = $obj->property; // @ the error is what the test reads $message = Helpers::improveError(error_get_last()['message']); Assert::same('Undefined property: class@anonymous::$property', $message); }); diff --git a/tests/Tracy/Helpers.improveException.phpt b/tests/Tracy/Helpers.improveException.phpt index 4057bb046..2c122b918 100644 --- a/tests/Tracy/Helpers.improveException.phpt +++ b/tests/Tracy/Helpers.improveException.phpt @@ -146,7 +146,7 @@ test('do not suggest anything when accessing anonymous class', function () { test('reading', function () use ($obj) { - @$val = $obj->publicX; + @$val = $obj->publicX; // @ the error is what the test reads $e = new ErrorException(error_get_last()['message'], 0, error_get_last()['type']); Helpers::improveException($e); Assert::same('Undefined property: TestClass::$publicX, did you mean $public?', $e->getMessage()); @@ -155,7 +155,7 @@ test('reading', function () use ($obj) { }); test('suggest only non-static property', function () use ($obj) { - @$val = $obj->publicStaticX; + @$val = $obj->publicStaticX; // @ the error is what the test reads $e = new ErrorException(error_get_last()['message'], 0, error_get_last()['type']); Helpers::improveException($e); Assert::same('Undefined property: TestClass::$publicStaticX', $e->getMessage()); @@ -163,7 +163,7 @@ test('suggest only non-static property', function () use ($obj) { }); test('suggest only public property', function () use ($obj) { - @$val = $obj->protectedX; + @$val = $obj->protectedX; // @ the error is what the test reads $e = new ErrorException(error_get_last()['message'], 0, error_get_last()['type']); Helpers::improveException($e); Assert::same('Undefined property: TestClass::$protectedX', $e->getMessage()); @@ -196,7 +196,7 @@ test('suggest only public static property', function () use ($obj) { test('do not suggest anything when accessing anonymous class', function () { $obj = new class { }; - @$val = $obj->property; + @$val = $obj->property; // @ the error is what the test reads $e = new ErrorException(error_get_last()['message'], 0, error_get_last()['type']); Helpers::improveException($e); Assert::same('Undefined property: class@anonymous::$property', $e->getMessage()); @@ -207,7 +207,7 @@ test('do not suggest anything when accessing anonymous class', function () { try { $obj = new class { }; - @$val = $obj::$property; + $val = $obj::$property; } catch (Error $e) { } diff --git a/tests/Tracy/expected/Debugger.exception.html.expect b/tests/Tracy/expected/Debugger.exception.html.expect index 493667edd..6f17b9ef5 100644 --- a/tests/Tracy/expected/Debugger.exception.html.expect +++ b/tests/Tracy/expected/Debugger.exception.html.expect @@ -177,7 +177,7 @@ %d%: %d%: %d%: define('MY_CONST', 123); -%d%: @hex2bin('a'); // E_WARNING +%d%: @hex2bin('a'); // @ E_WARNING muted on purpose %d%: first(10, 'any string'); diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 6376c5ade..a2140d414 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -3,7 +3,7 @@ // The Nette Tester command-line runner can be // invoked through the command: ../vendor/bin/tester . -if (@!include __DIR__ . '/../vendor/autoload.php') { +if (@!include __DIR__ . '/../vendor/autoload.php') { // @ dependencies may not be installed echo 'Install Nette Tester using `composer install`'; exit(1); } @@ -27,12 +27,12 @@ function getTempDir(): string $GLOBALS['\lock'] = $lock = fopen(__DIR__ . '/lock', 'w'); if (rand(0, 100)) { flock($lock, LOCK_SH); - @mkdir(dirname($dir)); + @mkdir(dirname($dir)); // @ directory may already exist } elseif (flock($lock, LOCK_EX)) { Tester\Helpers::purge(dirname($dir)); } - @mkdir($dir); + @mkdir($dir); // @ directory may already exist } return $dir; diff --git a/tools/latte-convert/tests/bootstrap.php b/tools/latte-convert/tests/bootstrap.php index e45ea119d..a9d456aed 100644 --- a/tools/latte-convert/tests/bootstrap.php +++ b/tools/latte-convert/tests/bootstrap.php @@ -1,6 +1,6 @@ Date: Tue, 13 Oct 2026 02:02:43 +0200 Subject: [PATCH 05/32] cs: grouped use statements --- src/Bridges/Nette/Bridge.php | 3 +-- src/Tracy/Dumper/Dumper.php | 4 +--- tests/Dumper/Dumper.dump().cli.phpt | 3 +-- tests/Dumper/Dumper.dump().html.phpt | 3 +-- tests/Dumper/Dumper.dump().text.phpt | 3 +-- tests/Dumper/Dumper.exposeObject.phpt | 4 +--- tests/Dumper/Dumper.toHtml().live.phpt | 3 +-- tests/Dumper/Dumper.toHtml().snapshot.phpt | 3 +-- tests/Dumper/dump().cli.phpt | 3 +-- tests/Tracy/BlueScreen.prepareStack.phpt | 3 +-- tests/Tracy/Debugger.barDump().phpt | 3 +-- tests/Tracy/Debugger.barDump().showLocation.phpt | 3 +-- tests/Tracy/Debugger.warnings.html.phpt | 3 +-- tests/Tracy/Helpers.countTransparentFrames.phpt | 3 +-- tools/latte-convert/src/Engine.php | 3 +-- tools/latte-convert/src/Extension.php | 8 ++------ tools/latte-convert/src/Nodes.php | 11 +++-------- 17 files changed, 20 insertions(+), 46 deletions(-) diff --git a/src/Bridges/Nette/Bridge.php b/src/Bridges/Nette/Bridge.php index bfde5fd5a..d8de7b972 100644 --- a/src/Bridges/Nette/Bridge.php +++ b/src/Bridges/Nette/Bridge.php @@ -9,8 +9,7 @@ use Nette; use Tracy; -use Tracy\BlueScreen; -use Tracy\Helpers; +use Tracy\{BlueScreen, Helpers}; /** diff --git a/src/Tracy/Dumper/Dumper.php b/src/Tracy/Dumper/Dumper.php index 8cc8764a7..fab593aa6 100644 --- a/src/Tracy/Dumper/Dumper.php +++ b/src/Tracy/Dumper/Dumper.php @@ -9,9 +9,7 @@ use Dom; use Ds; -use Tracy\Dumper\Describer; -use Tracy\Dumper\Exposer; -use Tracy\Dumper\Renderer; +use Tracy\Dumper\{Describer, Exposer, Renderer}; use Uri; diff --git a/tests/Dumper/Dumper.dump().cli.phpt b/tests/Dumper/Dumper.dump().cli.phpt index 102b1c2bd..efa184976 100644 --- a/tests/Dumper/Dumper.dump().cli.phpt +++ b/tests/Dumper/Dumper.dump().cli.phpt @@ -5,8 +5,7 @@ */ use Tester\Assert; -use Tracy\Debugger; -use Tracy\Dumper; +use Tracy\{Debugger, Dumper}; require __DIR__ . '/../bootstrap.php'; diff --git a/tests/Dumper/Dumper.dump().html.phpt b/tests/Dumper/Dumper.dump().html.phpt index 7e6293144..d65414c53 100644 --- a/tests/Dumper/Dumper.dump().html.phpt +++ b/tests/Dumper/Dumper.dump().html.phpt @@ -5,8 +5,7 @@ */ use Tester\Assert; -use Tracy\Debugger; -use Tracy\Dumper; +use Tracy\{Debugger, Dumper}; require __DIR__ . '/../bootstrap.php'; diff --git a/tests/Dumper/Dumper.dump().text.phpt b/tests/Dumper/Dumper.dump().text.phpt index 2277ceb48..986db7dca 100644 --- a/tests/Dumper/Dumper.dump().text.phpt +++ b/tests/Dumper/Dumper.dump().text.phpt @@ -5,8 +5,7 @@ */ use Tester\Assert; -use Tracy\Debugger; -use Tracy\Dumper; +use Tracy\{Debugger, Dumper}; require __DIR__ . '/../bootstrap.php'; diff --git a/tests/Dumper/Dumper.exposeObject.phpt b/tests/Dumper/Dumper.exposeObject.phpt index 164185423..06ed1dbd0 100644 --- a/tests/Dumper/Dumper.exposeObject.phpt +++ b/tests/Dumper/Dumper.exposeObject.phpt @@ -5,9 +5,7 @@ */ use Tester\Assert; -use Tracy\Dumper\Describer; -use Tracy\Dumper\Exposer; -use Tracy\Dumper\Value; +use Tracy\Dumper\{Describer, Exposer, Value}; require __DIR__ . '/../bootstrap.php'; require __DIR__ . '/fixtures/DumpClass.php'; diff --git a/tests/Dumper/Dumper.toHtml().live.phpt b/tests/Dumper/Dumper.toHtml().live.phpt index d6bb6f6ca..eb7613bfc 100644 --- a/tests/Dumper/Dumper.toHtml().live.phpt +++ b/tests/Dumper/Dumper.toHtml().live.phpt @@ -4,8 +4,7 @@ * Test: Tracy\Dumper::toHtml() live static snapshot */ -use Tester\Assert; -use Tester\Expect; +use Tester\{Assert, Expect}; use Tracy\Dumper; require __DIR__ . '/../bootstrap.php'; diff --git a/tests/Dumper/Dumper.toHtml().snapshot.phpt b/tests/Dumper/Dumper.toHtml().snapshot.phpt index c2b6c2809..f2eb975ed 100644 --- a/tests/Dumper/Dumper.toHtml().snapshot.phpt +++ b/tests/Dumper/Dumper.toHtml().snapshot.phpt @@ -4,8 +4,7 @@ * Test: Tracy\Dumper::toHtml() snapshop */ -use Tester\Assert; -use Tester\Expect; +use Tester\{Assert, Expect}; use Tracy\Dumper; require __DIR__ . '/../bootstrap.php'; diff --git a/tests/Dumper/dump().cli.phpt b/tests/Dumper/dump().cli.phpt index 815670655..ceec74f61 100644 --- a/tests/Dumper/dump().cli.phpt +++ b/tests/Dumper/dump().cli.phpt @@ -5,8 +5,7 @@ */ use Tester\Assert; -use Tracy\Debugger; -use Tracy\Dumper; +use Tracy\{Debugger, Dumper}; require __DIR__ . '/../bootstrap.php'; diff --git a/tests/Tracy/BlueScreen.prepareStack.phpt b/tests/Tracy/BlueScreen.prepareStack.phpt index b2e09e30f..45f756019 100644 --- a/tests/Tracy/BlueScreen.prepareStack.phpt +++ b/tests/Tracy/BlueScreen.prepareStack.phpt @@ -5,8 +5,7 @@ */ use Tester\Assert; -use Tracy\BlueScreen; -use Tracy\Debugger; +use Tracy\{BlueScreen, Debugger}; require __DIR__ . '/../bootstrap.php'; require __DIR__ . '/fixtures/transparentWrapper.php'; diff --git a/tests/Tracy/Debugger.barDump().phpt b/tests/Tracy/Debugger.barDump().phpt index e7defddae..c79b8055b 100644 --- a/tests/Tracy/Debugger.barDump().phpt +++ b/tests/Tracy/Debugger.barDump().phpt @@ -5,8 +5,7 @@ * @outputMatch OK! */ -use Tester\Assert; -use Tester\DomQuery; +use Tester\{Assert, DomQuery}; use Tracy\Debugger; require __DIR__ . '/../bootstrap.php'; diff --git a/tests/Tracy/Debugger.barDump().showLocation.phpt b/tests/Tracy/Debugger.barDump().showLocation.phpt index 19f7f9d95..5e878fb86 100644 --- a/tests/Tracy/Debugger.barDump().showLocation.phpt +++ b/tests/Tracy/Debugger.barDump().showLocation.phpt @@ -5,8 +5,7 @@ * @outputMatch OK! */ -use Tester\Assert; -use Tester\DomQuery; +use Tester\{Assert, DomQuery}; use Tracy\Debugger; require __DIR__ . '/../bootstrap.php'; diff --git a/tests/Tracy/Debugger.warnings.html.phpt b/tests/Tracy/Debugger.warnings.html.phpt index a7d5f28de..fb8148dd7 100644 --- a/tests/Tracy/Debugger.warnings.html.phpt +++ b/tests/Tracy/Debugger.warnings.html.phpt @@ -5,8 +5,7 @@ * @outputMatch OK! */ -use Tester\Assert; -use Tester\DomQuery; +use Tester\{Assert, DomQuery}; use Tracy\Debugger; require __DIR__ . '/../bootstrap.php'; diff --git a/tests/Tracy/Helpers.countTransparentFrames.phpt b/tests/Tracy/Helpers.countTransparentFrames.phpt index 3f8ea0a8d..6676c3887 100644 --- a/tests/Tracy/Helpers.countTransparentFrames.phpt +++ b/tests/Tracy/Helpers.countTransparentFrames.phpt @@ -11,8 +11,7 @@ */ use Tester\Assert; -use Tracy\Debugger; -use Tracy\Helpers; +use Tracy\{Debugger, Helpers}; require __DIR__ . '/../bootstrap.php'; require __DIR__ . '/fixtures/transparentWrapper.php'; diff --git a/tools/latte-convert/src/Engine.php b/tools/latte-convert/src/Engine.php index 7731a2c8f..11f7c6d1c 100644 --- a/tools/latte-convert/src/Engine.php +++ b/tools/latte-convert/src/Engine.php @@ -4,8 +4,7 @@ use Latte; use Latte\Compiler\Nodes\TemplateNode; -use Latte\Compiler\PhpHelpers; -use Latte\Compiler\PrintContext; +use Latte\Compiler\{PhpHelpers, PrintContext}; /** diff --git a/tools/latte-convert/src/Extension.php b/tools/latte-convert/src/Extension.php index 6dbe20216..ffd9cc78b 100644 --- a/tools/latte-convert/src/Extension.php +++ b/tools/latte-convert/src/Extension.php @@ -3,14 +3,10 @@ namespace Tracy\Tools\Compiler; use Latte; -use Latte\Compiler\Node; -use Latte\Compiler\Nodes\AuxiliaryNode; +use Latte\Compiler\{Node, NodeTraverser, PrintContext}; +use Latte\Compiler\Nodes\{AuxiliaryNode, PrintNode, TemplateNode}; use Latte\Compiler\Nodes\Html\ExpressionAttributeNode; use Latte\Compiler\Nodes\Php\Expression\ArrayNode; -use Latte\Compiler\Nodes\PrintNode; -use Latte\Compiler\Nodes\TemplateNode; -use Latte\Compiler\NodeTraverser; -use Latte\Compiler\PrintContext; use Latte\Essential\Nodes; use Latte\Runtime\HtmlHelpers; diff --git a/tools/latte-convert/src/Nodes.php b/tools/latte-convert/src/Nodes.php index 20c507cf4..359a11647 100644 --- a/tools/latte-convert/src/Nodes.php +++ b/tools/latte-convert/src/Nodes.php @@ -8,14 +8,9 @@ namespace Tracy\Tools\Compiler; use Latte\CompileException; -use Latte\Compiler\Nodes\AreaNode; -use Latte\Compiler\Nodes\Php\ExpressionNode; -use Latte\Compiler\Nodes\Php\Scalar; -use Latte\Compiler\Nodes\StatementNode; -use Latte\Compiler\PrintContext; -use Latte\Compiler\Tag; -use Latte\Compiler\TemplateParser; -use Latte\Compiler\Token; +use Latte\Compiler\Nodes\{AreaNode, StatementNode}; +use Latte\Compiler\Nodes\Php\{ExpressionNode, Scalar}; +use Latte\Compiler\{PrintContext, Tag, TemplateParser, Token}; /** From 273ce6743e66a0db1fee41cdb18d7faa5ac65abb Mon Sep 17 00:00:00 2001 From: David Grudl Date: Tue, 13 Oct 2026 02:04:09 +0200 Subject: [PATCH 06/32] cs --- src/Bridges/Psr/PsrToTracyLoggerAdapter.php | 9 ++- src/Tracy/Bar/Bar.php | 7 ++- src/Tracy/Bar/panels/info.panel.php | 13 +++- src/Tracy/BlueScreen/BlueScreen.php | 60 ++++++++++++++----- src/Tracy/BlueScreen/CodeHighlighter.php | 4 +- src/Tracy/Debugger/Debugger.php | 10 ++-- src/Tracy/Debugger/DeferredContent.php | 5 +- src/Tracy/Dumper/Describer.php | 9 ++- src/Tracy/Dumper/Dumper.php | 4 +- src/Tracy/Dumper/Renderer.php | 28 ++++++--- src/Tracy/Helpers.php | 43 +++++++++---- src/Tracy/Logger/Logger.php | 5 +- src/Tracy/Session/NativeSession.php | 1 - tests/Dumper/Dumper.scrubber.phpt | 1 - tests/Dumper/Dumper.sensitiveValue.phpt | 3 +- tests/Dumper/Dumper.toHtml().key.phpt | 1 - tests/Dumper/Dumper.toHtml().phpt | 15 ++++- .../Dumper/Dumper.toText().specials.dom.phpt | 2 +- .../Dumper/Dumper.toText().specials.enum.phpt | 10 ++-- .../PsrToTracyLoggerAdapter.phpt | 7 ++- .../TracyExtension.services.phpt | 5 +- .../TracyToPsrLoggerAdapter.phpt | 1 - tests/Tracy/Bar.renderAgent().ajax.phpt | 6 +- tests/Tracy/BlueScreen.renderActions().phpt | 1 - .../Debugger.E_COMPILE_ERROR.console.phpt | 12 ++-- tests/Tracy/Debugger.barDump().phpt | 5 +- tests/Tracy/Debugger.log().samefile.phpt | 2 +- tests/Tracy/Debugger.logging.error.phpt | 5 +- tests/Tracy/Debugger.logging.warnings.phpt | 5 +- .../Tracy/Helpers.countTransparentFrames.phpt | 2 +- tests/Tracy/Helpers.encodeString().phpt | 15 ++++- tests/Tracy/Helpers.escapeMd().phpt | 1 - tests/Tracy/Helpers.guessClassFile().phpt | 2 +- tests/Tracy/Helpers.improveException.phpt | 55 +++++++++++++---- .../Helpers.improveException.smartobject.phpt | 15 ++++- tests/Tracy/Helpers.minifyCss.phpt | 6 +- tests/Tracy/Helpers.minifyJs.phpt | 1 - tests/Tracy/Logger.extensible.phpt | 2 - tests/Tracy/Logger.log().phpt | 25 ++++++-- tools/create-phar/create-phar.php | 3 +- tools/latte-convert/src/Escaper.php | 3 +- 41 files changed, 295 insertions(+), 114 deletions(-) diff --git a/src/Bridges/Psr/PsrToTracyLoggerAdapter.php b/src/Bridges/Psr/PsrToTracyLoggerAdapter.php index d6d19c3c1..b6ae661e9 100644 --- a/src/Bridges/Psr/PsrToTracyLoggerAdapter.php +++ b/src/Bridges/Psr/PsrToTracyLoggerAdapter.php @@ -37,7 +37,14 @@ public function __construct( public function log(mixed $value, string $level = self::INFO): void { if ($value instanceof \Throwable) { - $message = get_debug_type($value) . ': ' . $value->getMessage() . ($value->getCode() ? ' #' . $value->getCode() : '') . ' in ' . $value->getFile() . ':' . $value->getLine(); + $message = get_debug_type($value) + . ': ' + . $value->getMessage() + . ($value->getCode() ? ' #' . $value->getCode() : '') + . ' in ' + . $value->getFile() + . ':' + . $value->getLine(); $context = ['exception' => $value]; } elseif (!is_string($value)) { diff --git a/src/Tracy/Bar/Bar.php b/src/Tracy/Bar/Bar.php index c85df6c74..4befd6369 100644 --- a/src/Tracy/Bar/Bar.php +++ b/src/Tracy/Bar/Bar.php @@ -65,6 +65,7 @@ public function renderLoader(DeferredContent $defer): void /** * Renders debug bar. + * @internal */ public function render(DeferredContent $defer): void { @@ -89,7 +90,10 @@ public function render(DeferredContent $defer): void } } elseif (Helpers::isHtmlMode()) { if (preg_match('#^Content-Length:#im', implode("\n", headers_list()))) { - Debugger::log(new \LogicException('Tracy cannot display the Bar because the Content-Length header is being sent'), Debugger::EXCEPTION); + Debugger::log( + new \LogicException('Tracy cannot display the Bar because the Content-Length header is being sent'), + Debugger::EXCEPTION, + ); } $content = $this->renderPartial('main'); @@ -180,6 +184,7 @@ private function renderPanels(string $suffix = ''): array /** * Captures debug bar as plain text (markdown) for AI agents. + * @internal */ public function renderAgent(): string { diff --git a/src/Tracy/Bar/panels/info.panel.php b/src/Tracy/Bar/panels/info.panel.php index d20836fbe..c43fb56dc 100644 --- a/src/Tracy/Bar/panels/info.panel.php +++ b/src/Tracy/Bar/panels/info.panel.php @@ -16,7 +16,10 @@ $systemUsage = -round(($this->cpuUsage['ru_stime.tv_sec'] * 1e6 + $this->cpuUsage['ru_stime.tv_usec']) / $this->time / 10000); } -$countClasses = fn(array $list): int => count(array_filter($list, fn(string $name): bool => (new \ReflectionClass($name))->isUserDefined())); +$countClasses = fn(array $list): int => count(array_filter( + $list, + fn(string $name): bool => (new \ReflectionClass($name))->isUserDefined(), +)); $ipFormatter = static function (?string $ip): ?string { if ($ip === '127.0.0.1' || $ip === '::1') { @@ -31,13 +34,17 @@ $info = [ 'Execution time' => number_format($this->time * 1000, 1, '.', "\u{202f}") . "\u{202f}ms", - 'CPU usage user + system' => isset($userUsage, $systemUsage) ? (int) $userUsage . "\u{202f}% + " . (int) $systemUsage . "\u{202f}%" : null, + 'CPU usage user + system' => isset($userUsage, $systemUsage) + ? (int) $userUsage . "\u{202f}% + " . (int) $systemUsage . "\u{202f}%" + : null, 'Peak of allocated memory' => number_format(memory_get_peak_usage() / 1_000_000, 2, '.', "\u{202f}") . "\u{202f}MB", 'Included files' => count(get_included_files()), 'Classes + interfaces + traits' => $countClasses(get_declared_classes()) . ' + ' . $countClasses(get_declared_interfaces()) . ' + ' . $countClasses(get_declared_traits()), 'OPcache' => $opcache ? round(count($cachedFiles) * 100 / count(get_included_files())) . "\u{202f}% cached" : '–', - 'JIT' => empty($jit['buffer_size']) ? '–' : round(($jit['buffer_size'] - $jit['buffer_free']) / $jit['buffer_size'] * 100) . "\u{202f}% used", + 'JIT' => empty($jit['buffer_size']) + ? '–' + : round(($jit['buffer_size'] - $jit['buffer_free']) / $jit['buffer_size'] * 100) . "\u{202f}% used", 'Your IP' => $ipFormatter($_SERVER['REMOTE_ADDR'] ?? null), 'Server IP' => $ipFormatter($_SERVER['SERVER_ADDR'] ?? null), 'HTTP method / response code' => isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] . ' / ' . http_response_code() : null, diff --git a/src/Tracy/BlueScreen/BlueScreen.php b/src/Tracy/BlueScreen/BlueScreen.php index 7e34728b6..f1df4a7d4 100644 --- a/src/Tracy/BlueScreen/BlueScreen.php +++ b/src/Tracy/BlueScreen/BlueScreen.php @@ -135,7 +135,10 @@ public function renderAgent(\Throwable $exception, ?array $logLocation = null): /** @internal */ public function renderToAjax(\Throwable $exception, DeferredContent $defer): void { - $defer->addSetup('Tracy.BlueScreen.loadAjax', Helpers::capture(fn() => $this->renderTemplate($exception, __DIR__ . '/dist/content.phtml'))); + $defer->addSetup( + 'Tracy.BlueScreen.loadAjax', + Helpers::capture(fn() => $this->renderTemplate($exception, __DIR__ . '/dist/content.phtml')), + ); if (Helpers::isAgent()) { $defer->addSetup('console.error', $this->renderAgent($exception)); } @@ -195,7 +198,10 @@ private function renderTemplate( $httpHeaders = apache_request_headers(); } else { $httpHeaders = array_filter($_SERVER, fn($k) => str_starts_with($k, 'HTTP_'), ARRAY_FILTER_USE_KEY); - $httpHeaders = array_combine(array_map(fn($k) => strtolower(strtr(substr($k, 5), '_', '-')), array_keys($httpHeaders)), $httpHeaders); + $httpHeaders = array_combine( + array_map(fn($k) => strtolower(strtr(substr($k, 5), '_', '-')), array_keys($httpHeaders)), + $httpHeaders, + ); } $this->snapshot = []; @@ -203,14 +209,15 @@ private function renderTemplate( $dump = $this->getDumper(); $agentDump = $this->getAgentDumper(); - $css = array_map(file_get_contents(...), array_merge([ + $css = array_map(file_get_contents(...), [ __DIR__ . '/../assets/reset.css', __DIR__ . '/assets/bluescreen.css', __DIR__ . '/../assets/toggle.css', __DIR__ . '/../assets/table-sort.css', __DIR__ . '/../assets/tabs.css', __DIR__ . '/../Dumper/assets/dumper-light.css', - ], Debugger::$customCssFiles)); + ...Debugger::$customCssFiles, + ]); $css = Helpers::minifyCss(implode('', $css)); $js = array_map(fn($file) => '(function(){' . file_get_contents($file) . '})();', [ @@ -368,10 +375,17 @@ public static function highlightFile( $source = $php ? CodeHighlighter::highlightPhp($source, $line, $column) - : '
' . CodeHighlighter::highlightLine(htmlspecialchars($source, ENT_IGNORE, 'UTF-8'), $line, $column) . '
'; + : '
' + . CodeHighlighter::highlightLine(htmlspecialchars($source, ENT_IGNORE, 'UTF-8'), $line, $column) + . '
'; if ($editor = Helpers::editorUri($file, $line)) { - $source = substr_replace($source, ' title="Ctrl-Click to open in editor" data-tracy-href="' . Helpers::escapeHtml($editor) . '"', 4, 0); + $source = substr_replace( + $source, + ' title="Ctrl-Click to open in editor" data-tracy-href="' . Helpers::escapeHtml($editor) . '"', + 4, + 0, + ); } return $source; @@ -417,10 +431,14 @@ public function isCollapsed(string $file): bool public function prepareStack(\Throwable $ex): array { $stack = $ex->getTrace(); - while ($stack && ( - in_array($stack[0]['class'] ?? null, [DevelopmentStrategy::class, ProductionStrategy::class], true) - || (($stack[0]['class'] ?? null) === Debugger::class && in_array($stack[0]['function'], ['shutdownHandler', 'errorHandler'], true)) - )) { + while ( + $stack + && ( + in_array($stack[0]['class'] ?? null, [DevelopmentStrategy::class, ProductionStrategy::class], true) + || (($stack[0]['class'] ?? null) === Debugger::class + && in_array($stack[0]['function'], ['shutdownHandler', 'errorHandler'], true)) + ) + ) { array_shift($stack); } @@ -429,7 +447,10 @@ public function prepareStack(\Throwable $ex): array !$ex instanceof \ErrorException || in_array($ex->getSeverity(), [E_USER_NOTICE, E_USER_WARNING, E_USER_DEPRECATED], true) ) { - $n = Helpers::countTransparentFrames([['file' => $ex->getFile(), 'line' => $ex->getLine()], ...$stack], [...$this->collapsePaths, ...Debugger::$transparentPaths]); + $n = Helpers::countTransparentFrames( + [['file' => $ex->getFile(), 'line' => $ex->getLine()], ...$stack], + [...$this->collapsePaths, ...Debugger::$transparentPaths], + ); $expanded = $n > 0 && $n <= count($stack) ? $n - 1 : null; } @@ -455,7 +476,10 @@ public function getDumper(): \Closure } - /** @return \Closure(mixed, int|string): string */ + /** + * @return \Closure(mixed, int|string): string + * @internal + */ public function getAgentDumper(): \Closure { return fn($v, $k = null): string => Dumper::toText($v, [ @@ -492,7 +516,11 @@ function ($m) { return $m[0]; } - return '' . $m[0] . ''; + return '' + . $m[0] + . ''; }, $msg, ); @@ -501,7 +529,11 @@ function ($m) { $msg = preg_replace_callback( '#([\w\\\/.:-]+\.(?:php|phpt|phtml|latte|neon))(?|:(\d+)| on line (\d+))?#', fn($m) => @is_file($m[1]) // @ - may trigger error - ? '' . $m[0] . '' + ? '' + . $m[0] + . '' : $m[0], $msg, ); diff --git a/src/Tracy/BlueScreen/CodeHighlighter.php b/src/Tracy/BlueScreen/CodeHighlighter.php index 312ad33b3..63ba331c1 100644 --- a/src/Tracy/BlueScreen/CodeHighlighter.php +++ b/src/Tracy/BlueScreen/CodeHighlighter.php @@ -92,8 +92,8 @@ private static function highlightPhpCode(string $code): string foreach (\PhpToken::tokenize($code) as $token) { $next = match ($token->id) { T_COMMENT, T_DOC_COMMENT, T_INLINE_HTML => 'tracy-code-comment', - T_OPEN_TAG, T_OPEN_TAG_WITH_ECHO, T_CLOSE_TAG, T_LINE, T_FILE, T_DIR, T_TRAIT_C, T_METHOD_C, T_FUNC_C, T_NS_C, T_CLASS_C, - T_STRING, T_NAME_FULLY_QUALIFIED, T_NAME_QUALIFIED, T_NAME_RELATIVE => '', + T_OPEN_TAG, T_OPEN_TAG_WITH_ECHO, T_CLOSE_TAG, T_LINE, T_FILE, T_DIR, T_TRAIT_C, T_METHOD_C, T_FUNC_C, + T_NS_C, T_CLASS_C, T_STRING, T_NAME_FULLY_QUALIFIED, T_NAME_QUALIFIED, T_NAME_RELATIVE => '', T_LNUMBER, T_DNUMBER => 'tracy-dump-number', T_VARIABLE => 'tracy-code-var', T_ENCAPSED_AND_WHITESPACE, T_CONSTANT_ENCAPSED_STRING => 'tracy-dump-string', diff --git a/src/Tracy/Debugger/Debugger.php b/src/Tracy/Debugger/Debugger.php index 8d4e19bfa..6bc316e72 100644 --- a/src/Tracy/Debugger/Debugger.php +++ b/src/Tracy/Debugger/Debugger.php @@ -67,7 +67,7 @@ class Debugger /** determines whether any error will cause immediate death in development mode; if integer that it's matched against error severity */ public static bool|int $strictMode = false; - /** disables the @ (shut-up) operator so that notices and warnings are no longer hidden; if integer than it's matched against error severity */ + /** disables the @ (shut-up) operator so notices and warnings are not hidden; if integer than it's matched against error severity */ public static bool|int $scream = false; /** @var array functions that are automatically called after fatal error */ @@ -139,7 +139,7 @@ class Debugger /** @var string[] */ public static array $customJsFiles = []; - /** @var string[] path prefixes of "transparent" packages whose frames are skipped when locating user code and collapsed in stack traces */ + /** @var string[] path prefixes of "transparent" packages whose frames are skipped when locating user code and collapsed in traces */ public static array $transparentPaths = []; /** @var array<\Closure(string, int): ?array{file: string, label: string, line?: int, column?: int, active?: bool}> */ @@ -167,7 +167,7 @@ final public function __construct() /** * Enables displaying or logging errors and exceptions. - * @param bool|string|string[] $mode use constant Debugger::Production, Development, Detect (autodetection) or IP address(es) whitelist. + * @param bool|string|string[] $mode Debugger::Production, Development, Detect (autodetection) or IP address(es) whitelist * @param ?string $logDirectory error log directory * @param string|string[]|null $email administrator email; enables email sending in production mode */ @@ -462,8 +462,8 @@ public static function getSessionStorage(): SessionStorage || @is_dir($dir = (string) ini_get('upload_tmp_dir')) // @ the path may be too long or outside open_basedir || @is_dir($dir = sys_get_temp_dir()) // @ the path may be too long or outside open_basedir || ($dir = (string) self::$logDirectory) - ? new FileSession($dir) - : new NativeSession; + ? new FileSession($dir) + : new NativeSession; } return self::$sessionStorage; diff --git a/src/Tracy/Debugger/DeferredContent.php b/src/Tracy/Debugger/DeferredContent.php index d9c31ee81..08c3f5fa9 100644 --- a/src/Tracy/Debugger/DeferredContent.php +++ b/src/Tracy/Debugger/DeferredContent.php @@ -122,7 +122,7 @@ public function sendAssets(): bool private function buildJsCss(): string { - $css = array_map(file_get_contents(...), array_merge([ + $css = array_map(file_get_contents(...), [ __DIR__ . '/../assets/reset.css', __DIR__ . '/../Bar/assets/bar.css', __DIR__ . '/../assets/toggle.css', @@ -131,7 +131,8 @@ private function buildJsCss(): string __DIR__ . '/../Dumper/assets/dumper-light.css', __DIR__ . '/../Dumper/assets/dumper-dark.css', __DIR__ . '/../BlueScreen/assets/bluescreen.css', - ], Debugger::$customCssFiles)); + ...Debugger::$customCssFiles, + ]); $js1 = array_map(fn($file) => '(function() {' . file_get_contents($file) . '})();', [ __DIR__ . '/../Bar/assets/bar.js', diff --git a/src/Tracy/Dumper/Describer.php b/src/Tracy/Dumper/Describer.php index fbba0ec94..b3f5436f2 100644 --- a/src/Tracy/Dumper/Describer.php +++ b/src/Tracy/Dumper/Describer.php @@ -202,7 +202,14 @@ private function describeObject(object $obj, int $depth = 0): Value $described = $this->isSensitive((string) $k, $v, get_debug_type($obj)) // props come from user callbacks (__debugInfo, custom exposers) ? new Value(Value::TypeText, self::hideValue($v)) : null; - $this->addPropertyTo($value, (string) $k, $v, Value::PropertyVirtual, $this->getReferenceId($props ?? [], $k), described: $described); + $this->addPropertyTo( + $value, + (string) $k, + $v, + Value::PropertyVirtual, + $this->getReferenceId($props ?? [], $k), + described: $described, + ); } } diff --git a/src/Tracy/Dumper/Dumper.php b/src/Tracy/Dumper/Dumper.php index fab593aa6..971859bf4 100644 --- a/src/Tracy/Dumper/Dumper.php +++ b/src/Tracy/Dumper/Dumper.php @@ -101,8 +101,8 @@ class Dumper /** @var array */ private static array $enumProperties = []; - private Describer $describer; - private Renderer $renderer; + private readonly Describer $describer; + private readonly Renderer $renderer; /** diff --git a/src/Tracy/Dumper/Renderer.php b/src/Tracy/Dumper/Renderer.php index 4505c3905..d71315994 100644 --- a/src/Tracy/Dumper/Renderer.php +++ b/src/Tracy/Dumper/Renderer.php @@ -53,7 +53,7 @@ public function renderAsHtml(\stdClass $model): string $html = $this->renderVar($value); $json = $snapshot = null; - } elseif ($this->lazy && (is_array($value) && $value || is_object($value))) { // full lazy-loading + } elseif ($this->lazy && ((is_array($value) && $value) || is_object($value))) { // full lazy-loading $html = ''; $snapshot = $this->collectingMode ? null : $this->snapshot; $json = $value; @@ -82,10 +82,10 @@ public function renderAsHtml(\stdClass $model): string } return '
 100 ? "\n" : '')
+			. ($json && $this->collapseTop === true ? ' tracy-collapsed' : '') . '"'
+			. ($snapshot !== null ? " data-tracy-snapshot='" . Helpers::jsonEncode($snapshot) . "'" : '')
+			. ($json ? " data-tracy-dump='" . Helpers::jsonEncode($json) . "'" : '')
+			. ($location || strlen($html) > 100 ? "\n" : '')
 			. '>'
 			. $location
 			. $html
@@ -231,7 +231,11 @@ private function renderArray(array|Value $array, int $depth): string
 				if ($this->lazy !== false) {
 					$ref = new Value(Value::TypeRef, $array->id);
 					$this->copySnapshot($ref);
-					return '" . $out . '';
+					return '"
+						. $out
+						. '';
 
 				} elseif ($this->hash) {
 					return $out . (isset($this->above[$array->id]) ? ' see above' : ' see below');
@@ -314,7 +318,11 @@ private function renderObject(Value $object, int $depth): string
 			if ($this->lazy !== false) {
 				$ref = new Value(Value::TypeRef, $object->id);
 				$this->copySnapshot($ref);
-				return '" . $out . '';
+				return '"
+					. $out
+					. '';
 
 			} elseif ($this->hash) {
 				return $out . (isset($this->above[$object->id]) ? ' see above' : ' see below');
@@ -368,7 +376,11 @@ private function renderResource(Value $resource, int $depth): string
 			if ($this->lazy !== false) {
 				$ref = new Value(Value::TypeRef, $resource->id);
 				$this->copySnapshot($ref);
-				return '" . $out . '';
+				return '"
+					. $out
+					. '';
 			}
 
 			return $out . ' see above';
diff --git a/src/Tracy/Helpers.php b/src/Tracy/Helpers.php
index 460e514df..5c7a8da1b 100644
--- a/src/Tracy/Helpers.php
+++ b/src/Tracy/Helpers.php
@@ -40,6 +40,7 @@ public static function editorLink(string $file, ?int $line = null): string
 				basename($file),
 				$line ? ":$line" : '',
 			);
+
 		} else {
 			return self::formatHtml('%', $file . ($line ? ":$line" : ''));
 		}
@@ -141,7 +142,10 @@ public static function countTransparentFrames(array $trace, ?array $paths = null
 			$next = $trace[$key + 1] ?? null;
 			$nextReflection = match (true) {
 				$next === null => null,
-				isset($next['class'], $next['function']) && method_exists($next['class'], $next['function']) => new \ReflectionMethod($next['class'], $next['function']),
+				isset($next['class'], $next['function']) && method_exists($next['class'], $next['function']) => new \ReflectionMethod(
+					$next['class'],
+					$next['function'],
+				),
 				isset($next['function']) && function_exists($next['function']) => new \ReflectionFunction($next['function']),
 				default => null,
 			};
@@ -256,15 +260,19 @@ public static function improveException(\Throwable $e): void
 			} elseif (is_array($arg) && !method_exists($arg[0], $arg[1])) {
 				$hint = self::getSuggestion(get_class_methods($arg[0]) ?: [], $arg[1]);
 				$class = is_object($arg[0]) ? $arg[0]::class : $arg[0];
-				$message = str_replace($m[2], "but method $class::$arg[1]() does not exist" . ($hint ? " (did you mean $hint?)" : ''), $message);
+				$message = str_replace(
+					$m[2],
+					"but method $class::$arg[1]() does not exist" . ($hint ? " (did you mean $hint?)" : ''),
+					$message,
+				);
 			} elseif (is_string($arg) && !function_exists($arg)) {
-				$funcs = array_merge(get_defined_functions()['internal'], get_defined_functions()['user']);
+				$funcs = [...get_defined_functions()['internal'], ...get_defined_functions()['user']];
 				$hint = self::getSuggestion($funcs, $arg);
 				$message = str_replace($m[2], "but function '$arg' does not exist" . ($hint ? " (did you mean $hint?)" : ''), $message);
 			}
 
 		} elseif (preg_match('#^Call to undefined function (\S+\\\)?(\w+)\(#', $message, $m)) {
-			$funcs = array_merge(get_defined_functions()['internal'], get_defined_functions()['user']);
+			$funcs = [...get_defined_functions()['internal'], ...get_defined_functions()['user']];
 			if ($hint = self::getSuggestion($funcs, $m[1] . $m[2]) ?: self::getSuggestion($funcs, $m[2])) {
 				$message = "Call to undefined function $m[2](), did you mean $hint()?";
 				$replace = ["$m[2](", "$hint("];
@@ -341,7 +349,7 @@ public static function guessClassFile(string $class): ?string
 
 			if ($i > $max && $i < count($segments) && ($file = (new \ReflectionClass($class))->getFileName())) {
 				$max = $i;
-				$res = array_merge(array_slice(explode(DIRECTORY_SEPARATOR, $file), 0, $i - count($parts)), array_slice($segments, $i));
+				$res = [...array_slice(explode(DIRECTORY_SEPARATOR, $file), 0, $i - count($parts)), ...array_slice($segments, $i)];
 				$res = implode(DIRECTORY_SEPARATOR, $res) . '.php';
 			}
 		}
@@ -359,7 +367,10 @@ public static function getSuggestion(array $items, string $value): ?string
 	{
 		$best = null;
 		$min = (strlen($value) / 4 + 1) * 10 + .1;
-		$items = array_map(fn($item) => $item instanceof \ReflectionMethod || $item instanceof \ReflectionProperty ? $item->getName() : (string) $item, $items);
+		$items = array_map(
+			fn($item) => $item instanceof \ReflectionMethod || $item instanceof \ReflectionProperty ? $item->getName() : (string) $item,
+			$items,
+		);
 		foreach (array_unique($items) as $item) {
 			if (($len = levenshtein($item, $value, 10, 11, 10)) > 0 && $len < $min) {
 				$min = $len;
@@ -421,7 +432,11 @@ public static function isCli(): bool
 	/** @internal */
 	public static function getNonce(bool $attr = false): ?string
 	{
-		return preg_match('#^Content-Security-Policy(?:-Report-Only)?:.*\sscript-src(?:-elem)?\s+(?:[^;]+\s)?\'nonce-([\w+/]+=*)\'#mi', implode("\n", headers_list()), $m)
+		return preg_match(
+			'#^Content-Security-Policy(?:-Report-Only)?:.*\sscript-src(?:-elem)?\s+(?:[^;]+\s)?\'nonce-([\w+/]+=*)\'#mi',
+			implode("\n", headers_list()),
+			$m,
+		)
 			? ($attr ? ' nonce="' . self::escapeHtml($m[1]) . '"' : $m[1])
 			: null;
 	}
@@ -496,8 +511,8 @@ private static function doEncodeString(string $s, bool $utf8, bool $showWhitespa
 		$s = preg_replace_callback(
 			$utf8 ? '#[\p{C}<&]#u' : '#[\x00-\x1F\x7F-\xFF<&]#',
 			fn($m) => $special[$m[0]] ?? (strlen($m[0]) === 1
-					? '\x' . str_pad(strtoupper(dechex(ord($m[0]))), 2, '0', STR_PAD_LEFT) . ''
-					: '\u{' . strtoupper(ltrim(dechex(self::utf8Ord($m[0])), '0')) . '}'),
+				? '\x' . str_pad(strtoupper(dechex(ord($m[0]))), 2, '0', STR_PAD_LEFT) . ''
+				: '\u{' . strtoupper(ltrim(dechex(self::utf8Ord($m[0])), '0')) . '}'),
 			$s,
 		);
 		$s = str_replace('', '', $s);
@@ -759,7 +774,15 @@ public static function jsonEncode(mixed $value, bool $inScript = false): string
 	{
 		$old = @ini_set('serialize_precision', '-1'); // @ may be disabled
 		try {
-			return json_encode($value, ($inScript ? JSON_HEX_TAG : 0) | JSON_HEX_APOS | JSON_HEX_AMP | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_INVALID_UTF8_SUBSTITUTE);
+			return json_encode(
+				$value,
+				($inScript ? JSON_HEX_TAG : 0)
+				| JSON_HEX_APOS
+				| JSON_HEX_AMP
+				| JSON_UNESCAPED_UNICODE
+				| JSON_UNESCAPED_SLASHES
+				| JSON_INVALID_UTF8_SUBSTITUTE,
+			);
 		} finally {
 			if ($old !== false) {
 				ini_set('serialize_precision', $old);
diff --git a/src/Tracy/Logger/Logger.php b/src/Tracy/Logger/Logger.php
index f856ba16a..c77d9f703 100644
--- a/src/Tracy/Logger/Logger.php
+++ b/src/Tracy/Logger/Logger.php
@@ -92,7 +92,10 @@ public static function formatMessage($message): string
 			foreach (Helpers::getExceptionChain($message) as $exception) {
 				$tmp[] = ($exception instanceof \ErrorException
 					? Helpers::errorTypeToString($exception->getSeverity()) . ': ' . $exception->getMessage()
-					: get_debug_type($exception) . ': ' . $exception->getMessage() . ($exception->getCode() ? ' #' . $exception->getCode() : '')
+					: get_debug_type($exception)
+						. ': '
+						. $exception->getMessage()
+						. ($exception->getCode() ? ' #' . $exception->getCode() : '')
 				) . ' in ' . $exception->getFile() . ':' . $exception->getLine();
 			}
 
diff --git a/src/Tracy/Session/NativeSession.php b/src/Tracy/Session/NativeSession.php
index 49e248f68..9020ae2c2 100644
--- a/src/Tracy/Session/NativeSession.php
+++ b/src/Tracy/Session/NativeSession.php
@@ -8,7 +8,6 @@
 namespace Tracy;
 
 
-
 /**
  * Session storage backed by native PHP session ($_SESSION).
  */
diff --git a/tests/Dumper/Dumper.scrubber.phpt b/tests/Dumper/Dumper.scrubber.phpt
index c21445cc8..38ca53373 100644
--- a/tests/Dumper/Dumper.scrubber.phpt
+++ b/tests/Dumper/Dumper.scrubber.phpt
@@ -3,7 +3,6 @@
 use Tester\Assert;
 use Tracy\Dumper;
 
-
 require __DIR__ . '/../bootstrap.php';
 
 
diff --git a/tests/Dumper/Dumper.sensitiveValue.phpt b/tests/Dumper/Dumper.sensitiveValue.phpt
index 9eff19d6f..c89423180 100644
--- a/tests/Dumper/Dumper.sensitiveValue.phpt
+++ b/tests/Dumper/Dumper.sensitiveValue.phpt
@@ -10,7 +10,8 @@ function sensitiveParameters(
 	#[SensitiveParameter]
 	string $secret,
 	string $normal,
-) {
+): never
+{
 	throw new Exception;
 }
 
diff --git a/tests/Dumper/Dumper.toHtml().key.phpt b/tests/Dumper/Dumper.toHtml().key.phpt
index e36f8482f..26afc6b07 100644
--- a/tests/Dumper/Dumper.toHtml().key.phpt
+++ b/tests/Dumper/Dumper.toHtml().key.phpt
@@ -3,7 +3,6 @@
 use Tester\Assert;
 use Tracy\Dumper;
 
-
 require __DIR__ . '/../bootstrap.php';
 
 
diff --git a/tests/Dumper/Dumper.toHtml().phpt b/tests/Dumper/Dumper.toHtml().phpt
index f21f4bf80..bf3bb7065 100644
--- a/tests/Dumper/Dumper.toHtml().phpt
+++ b/tests/Dumper/Dumper.toHtml().phpt
@@ -32,11 +32,20 @@ Assert::same('
NAN
' . "\n", Dumper::toHtml(NAN)); -Assert::same('
\'\'
' . "\n", Dumper::toHtml('')); +Assert::same( + '
\'\'
' . "\n", + Dumper::toHtml(''), +); -Assert::same('
\'0\'
' . "\n", Dumper::toHtml('0')); +Assert::same( + '
\'0\'
' . "\n", + Dumper::toHtml('0'), +); -Assert::same('
\'\x00\'
' . "\n", Dumper::toHtml("\x00")); +Assert::same( + '
\'\x00\'
' . "\n", + Dumper::toHtml("\x00"), +); Assert::match('
\'a\n' . "\n b'
\n", Dumper::toHtml("a\nb")); diff --git a/tests/Dumper/Dumper.toText().specials.dom.phpt b/tests/Dumper/Dumper.toText().specials.dom.phpt index 054bcc925..98a0e3a7c 100644 --- a/tests/Dumper/Dumper.toText().specials.dom.phpt +++ b/tests/Dumper/Dumper.toText().specials.dom.phpt @@ -11,7 +11,7 @@ require __DIR__ . '/../bootstrap.php'; $dom = new DOMDocument; -$dom->loadHtml('
  • Ahoj
'); +$dom->loadHTML('
  • Ahoj
'); $xpath = new DOMXPath($dom); $nodeList = $xpath->query('//li'); diff --git a/tests/Dumper/Dumper.toText().specials.enum.phpt b/tests/Dumper/Dumper.toText().specials.enum.phpt index 6fe4e1ca1..f76d829f6 100644 --- a/tests/Dumper/Dumper.toText().specials.enum.phpt +++ b/tests/Dumper/Dumper.toText().specials.enum.phpt @@ -20,17 +20,17 @@ enum Suit enum Methods: string { - case GET = 'get'; - case POST = 'post'; + case Get = 'get'; + case Post = 'post'; } Assert::match( <<<'XX' array (3) 0 => Suit::Clubs #%d% - 1 => Methods::GET #%d% + 1 => Methods::Get #%d% | value: 'get' - 2 => Methods::GET #%d% see above + 2 => Methods::Get #%d% see above XX, - Dumper::toText([Suit::Clubs, Methods::GET, Methods::GET]), + Dumper::toText([Suit::Clubs, Methods::Get, Methods::Get]), ); diff --git a/tests/Tracy.Bridges/PsrToTracyLoggerAdapter.phpt b/tests/Tracy.Bridges/PsrToTracyLoggerAdapter.phpt index 0407d6896..19aea0cf9 100644 --- a/tests/Tracy.Bridges/PsrToTracyLoggerAdapter.phpt +++ b/tests/Tracy.Bridges/PsrToTracyLoggerAdapter.phpt @@ -8,7 +8,6 @@ use Tester\Assert; use Tracy\Bridges\Psr\PsrToTracyLoggerAdapter; use Tracy\ILogger; - require __DIR__ . '/../bootstrap.php'; class DummyPsrLogger extends Psr\Log\AbstractLogger @@ -39,5 +38,9 @@ Assert::same([ [Psr\Log\LogLevel::WARNING, 'warning', []], [Psr\Log\LogLevel::INFO, '123', []], [Psr\Log\LogLevel::INFO, "array (1)\n 'x' => 'y'", []], - [Psr\Log\LogLevel::INFO, 'Exception: Something went wrong #123 in ' . __DIR__ . DIRECTORY_SEPARATOR . 'PsrToTracyLoggerAdapter.phpt:29', ['exception' => $exception]], + [ + Psr\Log\LogLevel::INFO, + 'Exception: Something went wrong #123 in ' . __DIR__ . DIRECTORY_SEPARATOR . 'PsrToTracyLoggerAdapter.phpt:28', + ['exception' => $exception], + ], ], $psrLogger->entries); diff --git a/tests/Tracy.Bridges/TracyExtension.services.phpt b/tests/Tracy.Bridges/TracyExtension.services.phpt index 009b79b53..99fca9809 100644 --- a/tests/Tracy.Bridges/TracyExtension.services.phpt +++ b/tests/Tracy.Bridges/TracyExtension.services.phpt @@ -5,7 +5,6 @@ use Tester\Assert; use Tracy\Bridges\Nette\TracyExtension; use Tracy\ILogger; - require __DIR__ . '/../bootstrap.php'; class CustomLogger implements ILogger @@ -39,8 +38,8 @@ $container = new Container; $container->initialize(); Assert::type('CustomLogger', $container->getService('tracy.logger')); -Assert::type('Tracy\BlueScreen', $container->getService('tracy.blueScreen')); -Assert::type('Tracy\Bar', $container->getService('tracy.bar')); +Assert::type(Tracy\BlueScreen::class, $container->getService('tracy.blueScreen')); +Assert::type(Tracy\Bar::class, $container->getService('tracy.bar')); Assert::same(Tracy\Debugger::getLogger(), $container->getService('tracy.logger')); Assert::same(Tracy\Debugger::getBlueScreen(), $container->getService('tracy.blueScreen')); diff --git a/tests/Tracy.Bridges/TracyToPsrLoggerAdapter.phpt b/tests/Tracy.Bridges/TracyToPsrLoggerAdapter.phpt index e7357bd49..f8a3ee551 100644 --- a/tests/Tracy.Bridges/TracyToPsrLoggerAdapter.phpt +++ b/tests/Tracy.Bridges/TracyToPsrLoggerAdapter.phpt @@ -8,7 +8,6 @@ use Tester\Assert; use Tracy\Bridges\Psr\TracyToPsrLoggerAdapter; use Tracy\ILogger; - require __DIR__ . '/../bootstrap.php'; class DummyTracyLogger implements ILogger diff --git a/tests/Tracy/Bar.renderAgent().ajax.phpt b/tests/Tracy/Bar.renderAgent().ajax.phpt index e5b57611d..fe22a7440 100644 --- a/tests/Tracy/Bar.renderAgent().ajax.phpt +++ b/tests/Tracy/Bar.renderAgent().ajax.phpt @@ -115,7 +115,11 @@ test('', function () { $storage = new MockSessionStorage; $storage->data['redirect'] = [ - ['content' => ['bar' => '', 'panels' => ''], 'agent' => "Tracy Bar | 10.0 ms | 5.00 MB\n\n## Warnings\n\n- test\n", 'time' => time()], + [ + 'content' => ['bar' => '', 'panels' => ''], + 'agent' => "Tracy Bar | 10.0 ms | 5.00 MB\n\n## Warnings\n\n- test\n", + 'time' => time(), + ], ]; $defer = createDefer($storage); diff --git a/tests/Tracy/BlueScreen.renderActions().phpt b/tests/Tracy/BlueScreen.renderActions().phpt index 7d095faa0..e9b1f614c 100644 --- a/tests/Tracy/BlueScreen.renderActions().phpt +++ b/tests/Tracy/BlueScreen.renderActions().phpt @@ -160,7 +160,6 @@ Assert::with($blueScreen, function () { }); - // isset() error class FooException extends Exception { diff --git a/tests/Tracy/Debugger.E_COMPILE_ERROR.console.phpt b/tests/Tracy/Debugger.E_COMPILE_ERROR.console.phpt index c3bc44ef8..823b7179d 100644 --- a/tests/Tracy/Debugger.E_COMPILE_ERROR.console.phpt +++ b/tests/Tracy/Debugger.E_COMPILE_ERROR.console.phpt @@ -33,13 +33,13 @@ register_shutdown_function(function () use (&$onFatalErrorCalled) { Tracy is unable to log error: Logging directory is not specified. XX : <<<'XX' - ErrorException: Cannot re-assign $this in %a% - Stack trace: - #0 [internal function]: Tracy\Debugger::shutdownHandler() - #1 {main} - Tracy is unable to log error: Logging directory is not specified. + ErrorException: Cannot re-assign $this in %a% + Stack trace: + #0 [internal function]: Tracy\Debugger::shutdownHandler() + #1 {main} + Tracy is unable to log error: Logging directory is not specified. - XX, + XX, ob_get_clean(), ); echo 'OK!'; // prevents PHP bug #62725 diff --git a/tests/Tracy/Debugger.barDump().phpt b/tests/Tracy/Debugger.barDump().phpt index c79b8055b..75a9f9ff0 100644 --- a/tests/Tracy/Debugger.barDump().phpt +++ b/tests/Tracy/Debugger.barDump().phpt @@ -32,7 +32,10 @@ register_shutdown_function(function () { }); -$arr = [10, 20.2, true, false, null, 'hello - - ./src/Tracy/Debugger/Debugger.php - - - - - ./src/Tracy/Helpers.php - - From ba05c0aad6dfe92f6815ba761365449f7aae911c Mon Sep 17 00:00:00 2001 From: David Grudl Date: Tue, 13 Oct 2026 02:37:39 +0200 Subject: [PATCH 08/32] BlueScreen: markdown output hides /*sensitive{*/ regions in source (thx @spaze) The source snippets of the markdown variant were printed straight from the file, so values the HTML page masks, such as #[\SensitiveParameter] arguments in the compiled DI container, ended up in the .md files written next to logged exceptions. The masking now keeps the newlines of a multi-line region, so the line numbers after it are not shifted. --- docs/internals/bluescreen.md | 5 +- src/Tracy/BlueScreen/CodeHighlighter.php | 15 +- src/Tracy/BlueScreen/assets/agent.latte | 6 +- src/Tracy/BlueScreen/dist/agent.phtml | 197 +++++++++--------- tests/Tracy/BlueScreen.renderAgent().phpt | 10 + .../CodeHighlighter.hideSensitive().phpt | 34 +++ 6 files changed, 166 insertions(+), 101 deletions(-) create mode 100644 tests/Tracy/CodeHighlighter.hideSensitive().phpt diff --git a/docs/internals/bluescreen.md b/docs/internals/bluescreen.md index 26f758e2c..e540c474b 100644 --- a/docs/internals/bluescreen.md +++ b/docs/internals/bluescreen.md @@ -47,7 +47,10 @@ template by comparing against that index. `CodeHighlighter` tokenizes PHP, shows `DisplayLines` (15) lines around the error — the public `$lines` parameters of the `highlight*` methods are ignored — highlights the line and column, and replaces `/*sensitive{*/…/*}*/` regions with `*****` (`Describer::HiddenValue`) before -highlighting (PHP path only, not the plain-text one). +highlighting (not for the `php: false` path). The markdown variant does +not highlight, but its source snippets go through the same +`CodeHighlighter::hideSensitive()`; any new place printing PHP source must too. +The masking keeps the newlines of a multi-line region, so line numbers stay true. Two dumpers exist: `getDumper()` renders **HTML** (`BlueScreen::$maxDepth` etc., `LOCATION_CLASS`, the shared `SNAPSHOT`, scrubber, `keysToHide`) and feeds the page diff --git a/src/Tracy/BlueScreen/CodeHighlighter.php b/src/Tracy/BlueScreen/CodeHighlighter.php index 63ba331c1..8a8eb22e1 100644 --- a/src/Tracy/BlueScreen/CodeHighlighter.php +++ b/src/Tracy/BlueScreen/CodeHighlighter.php @@ -86,7 +86,7 @@ private static function highlightPhpCode(string $code): string $code = str_replace("\r\n", "\n", $code); $code = preg_replace('#(__halt_compiler\s*\(\)\s*;).*#is', '$1', $code); $code = rtrim($code); - $code = preg_replace('#/\*sensitive\{\*/.*?/\*}\*/#s', Dumper\Describer::HiddenValue, $code); + $code = self::hideSensitive($code); $last = $out = ''; foreach (\PhpToken::tokenize($code) as $token) { @@ -120,6 +120,19 @@ private static function highlightPhpCode(string $code): string } + /** + * Replaces sensitive regions with a placeholder, keeping the line numbering intact. + */ + public static function hideSensitive(string $code): string + { + return preg_replace_callback( + '#/\*sensitive\{\*/.*?/\*}\*/#s', + fn($m) => Dumper\Describer::HiddenValue . str_repeat("\n", substr_count($m[0], "\n")), + $code, + ); + } + + /** * Returns syntax highlighted source code to Terminal. */ diff --git a/src/Tracy/BlueScreen/assets/agent.latte b/src/Tracy/BlueScreen/assets/agent.latte index 214b93919..03eb3089d 100644 --- a/src/Tracy/BlueScreen/assets/agent.latte +++ b/src/Tracy/BlueScreen/assets/agent.latte @@ -1,3 +1,4 @@ +{use Tracy\CodeHighlighter} {use Tracy\Debugger} {use Tracy\Helpers} {varType Tracy\BlueScreen $blueScreen} @@ -15,8 +16,9 @@ This is an error page generated by Tracy (https://tracy.nette.org). {/if} {define renderSource $file, $line} - {do $srcLines = @file($file)} - {exitIf !$srcLines} + {do $code = @file_get_contents($file)} + {exitIf !$code} + {do $srcLines = explode("\n", CodeHighlighter::hideSensitive($code))} {do $start = (int) max(0, $line - 4)} {do $end = (int) min(count($srcLines), $line + 3)} diff --git a/src/Tracy/BlueScreen/dist/agent.phtml b/src/Tracy/BlueScreen/dist/agent.phtml index d0ab02451..9d82e64e7 100644 --- a/src/Tracy/BlueScreen/dist/agent.phtml +++ b/src/Tracy/BlueScreen/dist/agent.phtml @@ -1,5 +1,6 @@ $srcLine) /* pos 24:2 */ { - echo sprintf('%s%4d | %s', $i + 1 === $line ? '► ' : ' ', $i + 1, rtrim($srcLine)) /* pos 25:3 */; + foreach (array_slice($srcLines, $start, $end - $start, true) as $i => $srcLine) /* pos 26:2 */ { + echo sprintf('%s%4d | %s', $i + 1 === $line ? '► ' : ' ', $i + 1, rtrim($srcLine)) /* pos 27:3 */; echo "\n"; } echo '``` '; - $mapped = Debugger::mapSource($file, $line) /* pos 28:2 */; - if (!$mapped || !@is_file($mapped['file'])) /* pos 29:2 */ return; - $mLine = $mapped['line'] /* pos 30:2 */; + $mapped = Debugger::mapSource($file, $line) /* pos 30:2 */; + if (!$mapped || !@is_file($mapped['file'])) /* pos 31:2 */ return; + $mLine = $mapped['line'] /* pos 32:2 */; echo ' Mapped source: '; - echo Tracy\Helpers::escapeMd($mapped['file'] . ($mLine ? ':' . $mLine : '')) /* pos 32:17 */; + echo Tracy\Helpers::escapeMd($mapped['file'] . ($mLine ? ':' . $mLine : '')) /* pos 34:17 */; echo "\n"; - $mappedLines = @file($mapped['file']) /* pos 33:2 */; - if ($mappedLines && $mLine) /* pos 34:2 */ { - $mStart = (int) max(0, $mLine - 4) /* pos 35:3 */; - $mEnd = (int) min(count($mappedLines), $mLine + 3) /* pos 36:3 */; + $mappedLines = @file($mapped['file']) /* pos 35:2 */; + if ($mappedLines && $mLine) /* pos 36:2 */ { + $mStart = (int) max(0, $mLine - 4) /* pos 37:3 */; + $mEnd = (int) min(count($mappedLines), $mLine + 3) /* pos 38:3 */; echo ' ``` '; - foreach (array_slice($mappedLines, $mStart, $mEnd - $mStart, true) as $i => $srcLine) /* pos 39:3 */ { - echo sprintf('%s%4d | %s', $i + 1 === $mLine ? '► ' : ' ', $i + 1, rtrim($srcLine)) /* pos 40:4 */; + foreach (array_slice($mappedLines, $mStart, $mEnd - $mStart, true) as $i => $srcLine) /* pos 41:3 */ { + echo sprintf('%s%4d | %s', $i + 1 === $mLine ? '► ' : ' ', $i + 1, rtrim($srcLine)) /* pos 42:4 */; echo "\n"; } @@ -61,107 +64,107 @@ Mapped source: '; } }; echo "\n"; -foreach (Helpers::getExceptionChain($exception) as $i => $ex) /* pos 46:1 */ { - $title = $blueScreen->getExceptionTitle($ex) /* pos 47:2 */; - $code = $ex->getCode() ? ' #' . $ex->getCode() : null /* pos 48:2 */; - if ($i === 0) /* pos 49:2 */ { +foreach (Helpers::getExceptionChain($exception) as $i => $ex) /* pos 48:1 */ { + $title = $blueScreen->getExceptionTitle($ex) /* pos 49:2 */; + $code = $ex->getCode() ? ' #' . $ex->getCode() : null /* pos 50:2 */; + if ($i === 0) /* pos 51:2 */ { echo '# '; - echo Tracy\Helpers::escapeMd($title) /* pos 50:5 */; + echo Tracy\Helpers::escapeMd($title) /* pos 52:5 */; echo ': '; - echo Tracy\Helpers::escapeMd($ex->getMessage()) /* pos 50:15 */; - echo Tracy\Helpers::escapeMd($code) /* pos 50:34 */; + echo Tracy\Helpers::escapeMd($ex->getMessage()) /* pos 52:15 */; + echo Tracy\Helpers::escapeMd($code) /* pos 52:34 */; echo "\n"; - } else /* pos 51:2 */ { + } else /* pos 53:2 */ { echo ' ## Caused by: '; - echo Tracy\Helpers::escapeMd($title) /* pos 53:17 */; + echo Tracy\Helpers::escapeMd($title) /* pos 55:17 */; echo ': '; - echo Tracy\Helpers::escapeMd($ex->getMessage()) /* pos 53:27 */; - echo Tracy\Helpers::escapeMd($code) /* pos 53:46 */; + echo Tracy\Helpers::escapeMd($ex->getMessage()) /* pos 55:27 */; + echo Tracy\Helpers::escapeMd($code) /* pos 55:46 */; echo "\n"; } echo ' in '; - echo Tracy\Helpers::escapeMd($ex->getFile()) /* pos 56:5 */; + echo Tracy\Helpers::escapeMd($ex->getFile()) /* pos 58:5 */; echo ':'; - echo Tracy\Helpers::escapeMd($ex->getLine()) /* pos 56:22 */; + echo Tracy\Helpers::escapeMd($ex->getLine()) /* pos 58:22 */; echo "\n"; $_blocks['renderSource']($ex->getFile(),$ex->getLine()); echo "\n"; - $base = new \Exception /* pos 59:2 */; - if (count(get_mangled_object_vars($ex)) > count(get_mangled_object_vars($base))) /* pos 60:2 */ { + $base = new \Exception /* pos 61:2 */; + if (count(get_mangled_object_vars($ex)) > count(get_mangled_object_vars($base))) /* pos 62:2 */ { echo ' ## Exception Properties '; - echo Tracy\Helpers::escapeMd($agentDump($ex)) /* pos 64:3 */; + echo Tracy\Helpers::escapeMd($agentDump($ex)) /* pos 66:3 */; echo "\n"; } echo "\n"; - [$stack] = $blueScreen->prepareStack($ex) /* pos 67:2 */; - if ($stack) /* pos 68:2 */ { + [$stack] = $blueScreen->prepareStack($ex) /* pos 69:2 */; + if ($stack) /* pos 70:2 */ { echo ' ## Stack Trace '; - foreach ($stack as $j => $row) /* pos 72:3 */ { - $funcName = isset($row['class']) ? $row['class'] . ($row['type'] ?? '::') . $row['function'] : $row['function'] /* pos 73:4 */; - $location = isset($row['file']) ? $row['file'] . ':' . ($row['line'] ?? '?') : 'inner-code' /* pos 74:4 */; - $inlineArgs = '' /* pos 75:4 */; - $complexArgs = [] /* pos 76:4 */; - if (!empty($row['args'])) /* pos 77:4 */ { + foreach ($stack as $j => $row) /* pos 74:3 */ { + $funcName = isset($row['class']) ? $row['class'] . ($row['type'] ?? '::') . $row['function'] : $row['function'] /* pos 75:4 */; + $location = isset($row['file']) ? $row['file'] . ':' . ($row['line'] ?? '?') : 'inner-code' /* pos 76:4 */; + $inlineArgs = '' /* pos 77:4 */; + $complexArgs = [] /* pos 78:4 */; + if (!empty($row['args'])) /* pos 79:4 */ { try { - $params = isset($row['class']) ? (new \ReflectionMethod($row['class'], $row['function']))->getParameters() : (new \ReflectionFunction($row['function']))->getParameters() /* pos 79:6 */; + $params = isset($row['class']) ? (new \ReflectionMethod($row['class'], $row['function']))->getParameters() : (new \ReflectionFunction($row['function']))->getParameters() /* pos 81:6 */; } catch (\Throwable) { - $params = [] /* pos 81:6 */; + $params = [] /* pos 83:6 */; } - $allSimple = true /* pos 83:5 */; - $argParts = [] /* pos 84:5 */; - foreach ($row['args'] as $k => $v) /* pos 85:5 */ { - $name = isset($params[$k]) && !$params[$k]->isVariadic() ? '$' . $params[$k]->getName() : '#' . $k /* pos 86:6 */; - $dump = rtrim($agentDump($v)) /* pos 87:6 */; + $allSimple = true /* pos 85:5 */; + $argParts = [] /* pos 86:5 */; + foreach ($row['args'] as $k => $v) /* pos 87:5 */ { + $name = isset($params[$k]) && !$params[$k]->isVariadic() ? '$' . $params[$k]->getName() : '#' . $k /* pos 88:6 */; + $dump = rtrim($agentDump($v)) /* pos 89:6 */; if (str_contains($dump, ' -') || strlen($dump) > 50) /* pos 88:6 */ { - $allSimple = false /* pos 89:7 */; - } else /* pos 90:6 */ { - $argParts[] = $name . ' = ' . $dump /* pos 91:7 */; +') || strlen($dump) > 50) /* pos 90:6 */ { + $allSimple = false /* pos 91:7 */; + } else /* pos 92:6 */ { + $argParts[] = $name . ' = ' . $dump /* pos 93:7 */; } - $complexArgs[] = $name . ' = ' . $dump /* pos 93:6 */; + $complexArgs[] = $name . ' = ' . $dump /* pos 95:6 */; } - if ($allSimple) /* pos 95:5 */ { - $inlineArgs = implode(', ', $argParts) /* pos 96:6 */; + if ($allSimple) /* pos 97:5 */ { + $inlineArgs = implode(', ', $argParts) /* pos 98:6 */; } } - if ($inlineArgs) /* pos 99:4 */ { - echo Tracy\Helpers::escapeMd($j + 1) /* pos 100:5 */; + if ($inlineArgs) /* pos 101:4 */ { + echo Tracy\Helpers::escapeMd($j + 1) /* pos 102:5 */; echo '. '; - echo Tracy\Helpers::escapeMd($funcName) /* pos 100:16 */; + echo Tracy\Helpers::escapeMd($funcName) /* pos 102:16 */; echo '('; - echo Tracy\Helpers::escapeMd($inlineArgs) /* pos 100:28 */; + echo Tracy\Helpers::escapeMd($inlineArgs) /* pos 102:28 */; echo ') '; - } elseif ($complexArgs) /* pos 101:4 */ { - echo Tracy\Helpers::escapeMd($j + 1) /* pos 102:5 */; + } elseif ($complexArgs) /* pos 103:4 */ { + echo Tracy\Helpers::escapeMd($j + 1) /* pos 104:5 */; echo '. '; - echo Tracy\Helpers::escapeMd($funcName) /* pos 102:16 */; + echo Tracy\Helpers::escapeMd($funcName) /* pos 104:16 */; echo '('; echo Tracy\Helpers::escapeMd(implode(', -', $complexArgs)) /* pos 102:28 */; +', $complexArgs)) /* pos 104:28 */; echo ') '; - } else /* pos 103:4 */ { - echo Tracy\Helpers::escapeMd($j + 1) /* pos 104:5 */; + } else /* pos 105:4 */ { + echo Tracy\Helpers::escapeMd($j + 1) /* pos 106:5 */; echo '. '; - echo Tracy\Helpers::escapeMd($funcName) /* pos 104:16 */; + echo Tracy\Helpers::escapeMd($funcName) /* pos 106:16 */; echo '() '; } echo ' in '; - echo Tracy\Helpers::escapeMd($location) /* pos 106:10 */; + echo Tracy\Helpers::escapeMd($location) /* pos 108:10 */; echo "\n"; - if (isset($row['file'], $row['line']) && @is_file($row['file'])) /* pos 107:4 */ { + if (isset($row['file'], $row['line']) && @is_file($row['file'])) /* pos 109:4 */ { $_blocks['renderSource']($row['file'],$row['line']); } echo "\n"; @@ -173,106 +176,106 @@ in '; } echo "\n"; -if ($lastError) /* pos 115:1 */ { +if ($lastError) /* pos 117:1 */ { echo ' ## Last Muted Error '; - echo Tracy\Helpers::escapeMd(Helpers::errorTypeToString($lastError['type'])) /* pos 119:2 */; + echo Tracy\Helpers::escapeMd(Helpers::errorTypeToString($lastError['type'])) /* pos 121:2 */; echo ': '; - echo Tracy\Helpers::escapeMd($lastError['message']) /* pos 119:51 */; + echo Tracy\Helpers::escapeMd($lastError['message']) /* pos 121:51 */; echo ' in '; - echo Tracy\Helpers::escapeMd($lastError['file']) /* pos 120:5 */; + echo Tracy\Helpers::escapeMd($lastError['file']) /* pos 122:5 */; echo ':'; - echo Tracy\Helpers::escapeMd($lastError['line']) /* pos 120:24 */; + echo Tracy\Helpers::escapeMd($lastError['line']) /* pos 122:24 */; echo "\n"; - if (@is_file($lastError['file'])) /* pos 121:2 */ { + if (@is_file($lastError['file'])) /* pos 123:2 */ { $_blocks['renderSource']($lastError['file'],$lastError['line']); } } echo "\n"; -if (!Helpers::isCli() && isset($_SERVER['REQUEST_METHOD'])) /* pos 126:1 */ { +if (!Helpers::isCli() && isset($_SERVER['REQUEST_METHOD'])) /* pos 128:1 */ { echo ' ## HTTP Request '; - echo Tracy\Helpers::escapeMd($_SERVER['REQUEST_METHOD']) /* pos 130:2 */; + echo Tracy\Helpers::escapeMd($_SERVER['REQUEST_METHOD']) /* pos 132:2 */; echo ' '; - echo Tracy\Helpers::escapeMd($source) /* pos 130:31 */; + echo Tracy\Helpers::escapeMd($source) /* pos 132:31 */; echo "\n"; - if ($httpHeaders) /* pos 131:2 */ { + if ($httpHeaders) /* pos 133:2 */ { echo ' ### Headers '; - foreach ($httpHeaders as $k => $v) /* pos 135:3 */ { + foreach ($httpHeaders as $k => $v) /* pos 137:3 */ { echo '- '; - echo Tracy\Helpers::escapeMd($k) /* pos 136:6 */; + echo Tracy\Helpers::escapeMd($k) /* pos 138:6 */; echo ': '; - echo Tracy\Helpers::escapeMd($v) /* pos 136:12 */; + echo Tracy\Helpers::escapeMd($v) /* pos 138:12 */; echo "\n"; } } echo "\n"; - if (!empty($_GET)) /* pos 140:2 */ { + if (!empty($_GET)) /* pos 142:2 */ { echo ' ### $_GET '; - echo Tracy\Helpers::escapeMd($agentDump($_GET)) /* pos 144:3 */; + echo Tracy\Helpers::escapeMd($agentDump($_GET)) /* pos 146:3 */; echo "\n"; } echo "\n"; - if (!empty($_POST)) /* pos 147:2 */ { + if (!empty($_POST)) /* pos 149:2 */ { echo ' ### $_POST '; - echo Tracy\Helpers::escapeMd($agentDump($_POST)) /* pos 151:3 */; + echo Tracy\Helpers::escapeMd($agentDump($_POST)) /* pos 153:3 */; echo "\n"; } echo "\n"; - if (!empty($_COOKIE)) /* pos 154:2 */ { + if (!empty($_COOKIE)) /* pos 156:2 */ { echo ' ### $_COOKIE '; - echo Tracy\Helpers::escapeMd($agentDump($_COOKIE)) /* pos 158:3 */; + echo Tracy\Helpers::escapeMd($agentDump($_COOKIE)) /* pos 160:3 */; echo "\n"; } } echo "\n"; -if ($showEnvironment) /* pos 162:1 */ { +if ($showEnvironment) /* pos 164:1 */ { echo ' ## Environment | Key | Value | |-----|-------| | PHP | '; - echo Tracy\Helpers::escapeMd(\PHP_VERSION . ' (' . (\PHP_ZTS ? 'TS' : 'NTS') . ')') /* pos 168:10 */; + echo Tracy\Helpers::escapeMd(\PHP_VERSION . ' (' . (\PHP_ZTS ? 'TS' : 'NTS') . ')') /* pos 170:10 */; echo ' | '; - if (isset($_SERVER['REQUEST_METHOD'])) /* pos 169:2 */ { + if (isset($_SERVER['REQUEST_METHOD'])) /* pos 171:2 */ { echo '| Method | '; - echo Tracy\Helpers::escapeMd($_SERVER['REQUEST_METHOD']) /* pos 170:14 */; + echo Tracy\Helpers::escapeMd($_SERVER['REQUEST_METHOD']) /* pos 172:14 */; echo ' | '; } echo '| Source | '; - echo Tracy\Helpers::escapeMd($source) /* pos 172:13 */; + echo Tracy\Helpers::escapeMd($source) /* pos 174:13 */; echo ' | '; - if (isset($_SERVER['SERVER_SOFTWARE'])) /* pos 173:2 */ { + if (isset($_SERVER['SERVER_SOFTWARE'])) /* pos 175:2 */ { echo '| Server | '; - echo Tracy\Helpers::escapeMd($_SERVER['SERVER_SOFTWARE']) /* pos 174:14 */; + echo Tracy\Helpers::escapeMd($_SERVER['SERVER_SOFTWARE']) /* pos 176:14 */; echo ' | '; } echo '| Tracy | '; - echo Tracy\Helpers::escapeMd(Debugger::Version) /* pos 176:12 */; + echo Tracy\Helpers::escapeMd(Debugger::Version) /* pos 178:12 */; echo ' | '; } diff --git a/tests/Tracy/BlueScreen.renderAgent().phpt b/tests/Tracy/BlueScreen.renderAgent().phpt index 03ee631cb..7cdc6292b 100644 --- a/tests/Tracy/BlueScreen.renderAgent().phpt +++ b/tests/Tracy/BlueScreen.renderAgent().phpt @@ -57,6 +57,16 @@ test('environment section hidden when disabled', function () { }); +test('sensitive regions are hidden in source', function () use ($blueScreen) { + $file = getTempDir() . '/sensitive.php'; + $secret = bin2hex(random_bytes(8)); + file_put_contents($file, "renderAgent(require $file); + Assert::notContains($secret, $output); + Assert::contains('$password = *****;', $output); +}); + + test('ErrorException uses severity name', function () use ($blueScreen) { $output = $blueScreen->renderAgent(new ErrorException('Bad value', 0, E_WARNING)); Assert::match('%A%# Warning: Bad value%A%', $output); diff --git a/tests/Tracy/CodeHighlighter.hideSensitive().phpt b/tests/Tracy/CodeHighlighter.hideSensitive().phpt new file mode 100644 index 000000000..058c2c573 --- /dev/null +++ b/tests/Tracy/CodeHighlighter.hideSensitive().phpt @@ -0,0 +1,34 @@ +5: throw \$e;%A%", $html); +}); From 4517330296015d85feaac2f82a538876fdcaa731 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Tue, 13 Oct 2026 03:00:06 +0200 Subject: [PATCH 09/32] added support for PHP 8.6 --- .github/workflows/tests.yml | 2 +- AGENTS.md | 2 +- composer.json | 2 +- readme.md | 2 +- tests/Tracy/BlueScreen.renderAgent().output.phpt | 1 + 5 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 1cb7b1139..4700272c2 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -11,7 +11,7 @@ jobs: strategy: matrix: os: [ubuntu-latest, windows-latest] - php: ['8.2', '8.3', '8.4', '8.5'] + php: ['8.2', '8.3', '8.4', '8.5', '8.6'] sapi: ['php', 'php-cgi'] fail-fast: false diff --git a/AGENTS.md b/AGENTS.md index 09012304c..c04a0d4c9 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -21,7 +21,7 @@ Dumper, and a production error Logger. It auto-detects development vs production emits **markdown for agents** (automated browsers via `navigator.webdriver`, or any client sending an `X-Tracy-Agent` header) instead of HTML. -- **PHP Version**: 8.2 - 8.5 +- **PHP Version**: 8.2 - 8.6 - **Package**: `tracy/tracy` (currently v2.12) ## Essential Commands diff --git a/composer.json b/composer.json index 5400741c9..c51fc2f9d 100644 --- a/composer.json +++ b/composer.json @@ -15,7 +15,7 @@ } ], "require": { - "php": "8.2 - 8.5", + "php": "8.2 - 8.6", "ext-session": "*", "ext-json": "*" }, diff --git a/readme.md b/readme.md index 2298b2d43..30b787200 100644 --- a/readme.md +++ b/readme.md @@ -48,7 +48,7 @@ composer require tracy/tracy Alternatively, you can download the whole package or [tracy.phar](https://github.com/nette/tracy/releases) file. -Tracy is compatible with PHP 8.2 to 8.5. +Tracy is compatible with PHP 8.2 to 8.6.   diff --git a/tests/Tracy/BlueScreen.renderAgent().output.phpt b/tests/Tracy/BlueScreen.renderAgent().output.phpt index fe87e4cc3..e5982148b 100644 --- a/tests/Tracy/BlueScreen.renderAgent().output.phpt +++ b/tests/Tracy/BlueScreen.renderAgent().output.phpt @@ -26,6 +26,7 @@ function third($arg1) } +error_clear_last(); // muted error raised while loading vendor would appear in the output $bs = new Tracy\BlueScreen; $bs->showEnvironment = false; From a82e1bc5c73b777017731ed4a1af22c68d55c174 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Tue, 13 Oct 2026 02:55:53 +0200 Subject: [PATCH 10/32] Released version 2.12.2 --- src/Tracy/Debugger/Debugger.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Tracy/Debugger/Debugger.php b/src/Tracy/Debugger/Debugger.php index 6bc316e72..07205c554 100644 --- a/src/Tracy/Debugger/Debugger.php +++ b/src/Tracy/Debugger/Debugger.php @@ -17,7 +17,7 @@ */ class Debugger { - public const Version = '2.12.1'; + public const Version = '2.12.2'; /** server modes for Debugger::enable() */ public const From 1a0a50162cf74d6d1af544a9a6cc4430f86f96ff Mon Sep 17 00:00:00 2001 From: David Grudl Date: Wed, 16 Sep 2026 19:25:45 +0200 Subject: [PATCH 11/32] added upgrading.md --- AGENTS.md | 6 +++ docs/upgrading.md | 128 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 134 insertions(+) create mode 100644 docs/upgrading.md diff --git a/AGENTS.md b/AGENTS.md index c04a0d4c9..5f60c6377 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -13,6 +13,12 @@ deferred content, the dumper, BlueScreen, the Bar, the logger). Read the relevan `docs/internals/` seam before editing one - especially `deferred-content.md`, the most counterintuitive part. +`docs/upgrading.md` records, per released version, every change visible to +users of the package (renames, removals, deprecations, signatures, behavior, +configuration), each with its commit hash; internal changes are not in it. It +is the place to look up the history of a public symbol. Do not write to it; it +is extended at release time from the git history. + ## Project Overview Tracy is a debugging and error-visualization library for PHP: BlueScreen error diff --git a/docs/upgrading.md b/docs/upgrading.md new file mode 100644 index 000000000..21a212d08 --- /dev/null +++ b/docs/upgrading.md @@ -0,0 +1,128 @@ +# Upgrading tracy/tracy + +## 2.12.1 + +- **Changed** `Tracy\Debugger::dump()` called with `return: true` now applies `Tracy\Debugger::$keysToHide` and, in CLI, also `$maxDepth`, `$maxLength` and `$maxItems`; previously it ignored them (69f595b0) +- **Changed** `Tracy\Debugger::enable()` called again after output has already started is now a silent no-op; previously it threw `LogicException` (1ddaa887) +- **Changed** `Tracy\Dumper` now checks properties coming from `__debugInfo()` or from a custom object exporter against `Tracy\Debugger::$keysToHide` and the scrubber; previously such properties were always shown unmasked (e726c8f0) +- **Changed** `Tracy\Logger::$emailSnooze` now throws `InvalidArgumentException` when its value cannot be parsed by `strtotime()`; previously throttling was silently disabled and an email was sent for every error (ed5e3a48) +- **Changed** `Tracy\Bridges\Psr\TracyToPsrLoggerAdapter::log()` now logs an exception passed in the PSR-3 context only once when the message is empty or equal to the exception message; previously the message was logged a second time (6ccd6e55) + +## 2.12.0 + +- **Deprecated** `Tracy\BlueScreen::$collapsePaths`: use `Tracy\Debugger::$transparentPaths`, a static property filled when the class is loaded; the old name is silently deprecated and its value is still merged in (0deea90e) +- **Changed** `Tracy\BlueScreen::renderToFile()` now writes a Markdown companion file with the same basename next to the HTML file (1dbb02d7) +- **Changed** `Tracy\Logger::getExceptionFile()` now reuses an existing log file only when its name ends with the exception hash and `.html`; previously the hash could appear anywhere in the name (1dbb02d7) + +## 2.11.1 + +- **Signature** `Tracy\Bridges\Psr\PsrToTracyLoggerAdapter::log()` now declares the return type `void`; affects subclasses overriding it (2f4c7df4) +- **Config** `tracy.keysToHide` now fills `Tracy\Debugger::$keysToHide` as well, not only `Tracy\BlueScreen::$keysToHide`, so the listed keys are masked in all dumps (ebd29547) + +## 2.11.0 + +- **Renamed** the Tracy Bar panel id `Tracy:errors` → `Tracy:warnings`; the old id was removed (0dda9390) + +## 2.10.5 + +- **Renamed** `Tracy\Debugger::VERSION` → `Tracy\Debugger::Version`; the old name is silently deprecated (3a6fb902) +- **Deprecated** `Tracy\Debugger::PRODUCTION`: use `Tracy\Debugger::Production`; the old name is silently deprecated (alias since 2.9.6) (3a6fb902) +- **Deprecated** `Tracy\Debugger::DEVELOPMENT`: use `Tracy\Debugger::Development`; the old name is silently deprecated (alias since 2.9.6) (3a6fb902) +- **Deprecated** `Tracy\Debugger::DETECT`: use `Tracy\Debugger::Detect`; the old name is silently deprecated (alias since 2.9.6) (3a6fb902) +- **Deprecated** `Tracy\Debugger::COOKIE_SECRET`: use `Tracy\Debugger::CookieSecret`; the old name is silently deprecated (alias since 2.9.6) (3a6fb902) + +## 2.10.0 + +- **Removed** `Tracy\FireLogger`; FireLogger support was dropped without replacement (8eaf4f25) +- **Removed** `Tracy\Debugger::fireLog()`; FireLogger support was dropped without replacement (8eaf4f25) +- **Removed** `Tracy\Debugger::getFireLogger()`; FireLogger support was dropped without replacement (8eaf4f25) +- **Removed** `Tracy\Debugger::$showFireLogger`; FireLogger support was dropped without replacement (8eaf4f25) +- **Signature** `Tracy\ILogger::log()` is now `log(mixed $value, string $level = self::INFO)`; a custom implementation must declare the same types or PHP reports an incompatible signature (9cf27348) +- **Signature** native types were added to parameters, return values and properties; notably `Tracy\Debugger::enable(bool|string|array|null $mode = null, ?string $logDirectory = null, string|array|null $email = null)`, `Tracy\Debugger::$email`, `Tracy\Debugger::$logDirectory`, `Tracy\Debugger::$browser`, `Tracy\Debugger::$errorTemplate`, `Tracy\Debugger::$productionMode`, `Tracy\Debugger::$showLocation`, `Tracy\Debugger::$scream`, `Tracy\Debugger::$logSeverity`, `Tracy\Logger::__construct()` and `Tracy\BlueScreen::addFiber()`. Assigning a value of another type to a now typed static property throws `TypeError` (9cf27348) + +## 2.9.8 + +- **Changed** `Tracy\BlueScreen::highlightLine()` no longer appends the closing `` tag, which `Tracy\BlueScreen::highlightPhp()` adds instead; also in 2.10.5 (ba708e94) + +## 2.9.6 + +- **Renamed** `Tracy\Debugger::PRODUCTION` → `Tracy\Debugger::Production`; the old name still works as an alias (b6cd4a42) +- **Renamed** `Tracy\Debugger::DEVELOPMENT` → `Tracy\Debugger::Development`; the old name still works as an alias (b6cd4a42) +- **Renamed** `Tracy\Debugger::DETECT` → `Tracy\Debugger::Detect`; the old name still works as an alias (b6cd4a42) +- **Renamed** `Tracy\Debugger::COOKIE_SECRET` → `Tracy\Debugger::CookieSecret`; the old name still works as an alias (b6cd4a42) + +## 2.9.2 + +- **Changed** `Tracy\Dumper::dump()` with the option `Tracy\Dumper::HASH => false` now renders a repeated array or object in full; previously it was collapsed to "see above" / "see below" (d9c2e36b) + +## 2.9.1 + +- **Changed** `Tracy\Debugger::$maxDepth` now defaults to 15; previously 7. Set it to 7 explicitly to keep the previous depth (d6d21012) + +## 2.9.0 + +- **Changed** `Tracy\Debugger::$scream` no longer changes `error_reporting()` globally; it now only controls whether `Tracy\Debugger::errorHandler()` processes a silenced error (52a9cc0c) +- **Config** `tracy.logSeverity` accepts as list items only strings naming `E_*` constants; a raw integer or an arbitrary string is rejected, pass a single int bitmask instead of a list (74a9fbee) + +## 2.8.4 + +- **Changed** `Tracy\Dumper::ITEMS` with the value `0` now means no limit; previously it truncated the output to zero items (0bc1b627) +- **Changed** the scrubber callback (`Tracy\Dumper::SCRUBBER`, `Tracy\BlueScreen::$scrubber`) is now called for non-string keys as well, cast to string; previously it was not called for them (c2f56c43) + +## 2.8.2 + +- **Changed** `Tracy\Debugger::$maxDepth` now defaults to 7; previously 3 (845130ec) +- **Changed** `Tracy\BlueScreen::$collapsePaths` now defaults to the `tracy/`, `nette/` and `latte/` directories; previously to the whole `vendor` directory, so stack frames of other packages are no longer collapsed (8c7bdf03) +- **Changed** `Tracy\Debugger::dump()` with `$return = true` now returns plain text in CLI; previously the captured HTML output (34ed4467) + +## 2.8.1 + +- **Changed** `Tracy\BlueScreen::$maxDepth` now defaults to 5; previously 3 (e23800ff) +- **Changed** `Tracy\BlueScreen::$keysToHide` is no longer matched against virtual (computed) properties, so such a property is dumped in full (47cab47b) +- **Changed** `Tracy\BlueScreen::$scrubber` is no longer called for virtual (computed) properties (47cab47b) + +## 2.8.0 + +- **Renamed** `Tracy\Dumper::LOCATION_LINK` → `Tracy\Dumper::LOCATION_SOURCE`; the old name still works as an alias (67e9e791) +- **Signature** `Tracy\BlueScreen::highlightFile()` and `Tracy\BlueScreen::highlightPhp()` lost the `$vars` and `$keysToHide` parameters together with the feature of inlining dumped variables into the source; drop them from calls (7d2862e9) +- **Changed** `Tracy\Dumper::LOCATION_SOURCE` now implies `Tracy\Dumper::LOCATION_CLASS`, whose bit value changed from `0b0100` to `0b0001` (67e9e791) +- **Changed** `Tracy\Debugger::$showLocation` now defaults to `null` and HTML dumps show the source location by default; previously the default was `false` and the location was hidden (2deb1d2d) +- **Changed** `Tracy\Dumper::DEPTH` now defaults to 7; previously 4 (5f275f6c) +- **Changed** `Tracy\Dumper::dump()`, `Tracy\Dumper::toHtml()` and `Tracy\Dumper::toText()` now truncate arrays and objects to 100 items; pass `Tracy\Dumper::ITEMS` to change or lift the limit (1c4dc56c) +- **Changed** `Tracy\Dumper::dump()` now emits inline `\n"; + echo "\n"; - if (!Debugger::isEnabled() || !Debugger::$showBar) { - $s = '(function(){' . file_get_contents(__DIR__ . '/../assets/toggle.js') . '})();' - . '(function(){' . file_get_contents(__DIR__ . '/../assets/helpers.js') . '})();' + if (!Debugger::isEnabled() || !Debugger::$showBar) { // otherwise the deferred loader provides the registry & scripts + $css = file_get_contents(__DIR__ . '/../assets/reset.css') . $s; + $s = '(function(){' . file_get_contents(__DIR__ . '/../assets/helpers.js') . '})();' + . '(function(){' . file_get_contents(__DIR__ . '/../assets/toggle.js') . '})();' . '(function(){' . file_get_contents(__DIR__ . '/../Dumper/assets/dumper.js') . '})();'; - echo "", str_replace(['

@@ -20,26 +21,26 @@ echo ' '; -echo Tracy\Helpers::escapeHtml($title) /* pos 22:9 */; +echo Tracy\Helpers::escapeHtml($title) /* pos 23:9 */; echo ': '; -echo Tracy\Helpers::escapeHtml($exception->getMessage()) /* pos 22:19 */; -echo Tracy\Helpers::escapeHtml($code) /* pos 22:45 */; +echo Tracy\Helpers::escapeHtml($exception->getMessage()) /* pos 23:19 */; +echo Tracy\Helpers::escapeHtml($code) /* pos 23:45 */; echo ' '; -if (count($chain) > 1) /* pos 24:2 */ { +if (count($chain) > 1) /* pos 25:2 */ { echo ' @@ -63,21 +64,21 @@ echo ' require __DIR__ . '/content.phtml'; echo ' \'use strict\'; '; -echo str_replace(['