Last active
April 5, 2026 12:41
-
-
Save paranoiq/d4a5c5396af2019cb367100e7b3dffbb to your computer and use it in GitHub Desktop.
PHPStan output tweaker - version, colors, better dividers
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?php | |
| // Ensure output is treated as a TTY/ANSI capable stream | |
| // Usage: phpstan analyze --ansi | php phpstan-tweak.php | |
| $width = static function (): int | |
| { | |
| static $col; | |
| if ($col !== null) { | |
| return $col; | |
| } | |
| $col = (int) @exec('tput cols'); | |
| if ($col !== 0) { | |
| return $col; | |
| } | |
| if (PHP_OS_FAMILY === 'Windows') { | |
| exec('mode CON', $output); | |
| [, $col] = explode(':', $output[4]); | |
| $col = (int) trim($col); | |
| } | |
| return $col = $col ?: 120; | |
| }; | |
| // PHPStan version | |
| $version = null; | |
| $lockFile = __DIR__ . '/../composer.lock'; | |
| if (file_exists($lockFile)) { | |
| $data = json_decode(file_get_contents($lockFile), true, 512, JSON_THROW_ON_ERROR); | |
| $packages = array_merge($data['packages'] ?? [], $data['packages-dev'] ?? []); | |
| foreach ($packages as $package) { | |
| if ($package['name'] === 'phpstan/phpstan') { | |
| $version = $package['version']; | |
| break; | |
| } | |
| } | |
| } | |
| echo " \e[32mPHPStan {$version}\e[39m on PHP " . PHP_VERSION . "\n\n"; | |
| while ($line = fgets(STDIN)) { | |
| // drop less useful tips | |
| if (str_contains($line, 'user-guide/discovering-symbols')) { | |
| continue; | |
| } | |
| // remove original separators | |
| if (preg_match('~^ ------ -+\s+$~', $line)) { | |
| continue; | |
| } | |
| // add better | |
| if (preg_match("~\e\[32mLine\e\[39m~", $line)) { | |
| $line .= " ------ " . str_repeat('-', $width() - 9) . "\n"; | |
| } | |
| // highlight rule identifiers (e.g., ? class.notFound) | |
| $line = preg_replace('/?\s*([a-zA-Z0-9._-]+)/u', "\e[30m$0\e[0m", $line); | |
| // highlight variables | |
| $line = preg_replace('/(\\$\w+)/u', "\e[33m$0\e[0m", $line); | |
| // highlight types | |
| $types = 'bool|true|false|null|scalar|empty-scalar|non-empty-scalar' | |
| . '|(?:non-)?(?:negative-|positive-|zero-)?int(?:<(?:\d+|min|max)>)?|float|number|numeric' | |
| . '|(?:non-empty-|non-falsy-|numeric-|class-|callable-|)string|array-key' | |
| . '|(?:non-empty-)?(?:array|list|iterable|key-of|value-of)(?:<[^>]>|\\[\\]|{[^}]})?' // todo: nested <>, collections | |
| . '|object|resource|callable|mixed|never|void|self|static|parent'; | |
| $codePattern = '/(::[a-zA-Z0-9_]+)|([a-zA-Z0-9_]+\(\))|([a-zA-Z0-9_\\\|]*[\\\|][a-zA-Z0-9_\\\|]+)|(\b(' . $types . ')\b)/'; | |
| $line = preg_replace_callback($codePattern, static function ($m): string { | |
| $str = $m[0]; | |
| $str = preg_replace('~[|&<>[\\]]~', "\e[95m$0\e[34m", $str); | |
| return "\e[34m{$str}\e[0m"; | |
| }, $line); | |
| echo $line; | |
| } |
Author
paranoiq
commented
Apr 5, 2026
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
