├── .github └── workflows │ └── test.yml ├── .gitignore ├── LICENSE ├── README.md ├── composer.json ├── config.neon ├── example-compact.png ├── example.png ├── src └── CompactErrorFormatter.php └── tests └── phpstan-config.neon /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | on: 3 | pull_request: 4 | branches: [master] 5 | push: 6 | branches: [master] 7 | 8 | jobs: 9 | verify: 10 | runs-on: ubuntu-latest 11 | strategy: 12 | matrix: 13 | php: 14 | - '8.2' 15 | - '8.3' 16 | - '8.4' 17 | fail-fast: false 18 | steps: 19 | - uses: actions/checkout@v4 20 | - uses: shivammathur/setup-php@v2 21 | with: 22 | php-version: ${{ matrix.php }} 23 | coverage: none 24 | - run: composer install --no-interaction 25 | - run: composer run verify 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | vendor/ 2 | .idea/ 3 | composer.lock 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 GRIFART spol. s r.o. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PHPStan one-line error formatter 2 | 3 | [![Latest Stable Version](https://poser.pugx.org/grifart/phpstan-oneline/v/stable)](https://packagist.org/packages/grifart/phpstan-oneline) 4 | [![Total Downloads](https://poser.pugx.org/grifart/phpstan-oneline/downloads)](https://packagist.org/packages/grifart/phpstan-oneline) 5 | [![License](https://poser.pugx.org/grifart/phpstan-oneline/license)](https://packagist.org/packages/grifart/phpstan-oneline) 6 | [![Build Status](https://travis-ci.org/grifart/phpstan-oneline.svg?branch=master)](https://travis-ci.org/grifart/phpstan-oneline) 7 | 8 | Compact and **clickable** [PhpStan](http://github.com/phpstan/phpstan) error output handler. 9 | 10 | So when you run for example: 11 | 12 | ```bash 13 | phpstan analyze -l max --configuration phpstan.neon --error-format oneline 14 | ``` 15 | 16 | ![](example.png) 17 | 18 | and now you will get to the location where error occurred by one-click! 19 | 20 | ## Installation 21 | 22 | ```bash 23 | composer require --dev grifart/phpstan-oneline 24 | ``` 25 | 26 | and register error formatter into your `phpstan.neon`: 27 | 28 | ```neon 29 | includes: 30 | - vendor/grifart/phpstan-oneline/config.neon 31 | ``` 32 | 33 | ### Clickable paths in PhpStorm 34 | 35 | 1. Install [Awesome Console](https://github.com/anthraxx/intellij-awesome-console) (available in PhpStorm repositories) 36 | 2. run phpstan in PhpStorm terminal 37 | 38 | 39 | ## Custom error format 40 | 41 | There has been added `compact` error format. It looks like this by default: 42 | 43 | ```bash 44 | phpstan analyze -l max --configuration phpstan.neon --error-format compact 45 | ``` 46 | 47 | ![](example-compact.png) 48 | 49 | You can customize `compact` error format in your `phpstan.neon`: 50 | 51 | ```neon 52 | parameters: 53 | compact: 54 | format: "{path}:{line}\n ↳ {error}" # default 55 | ``` 56 | 57 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "grifart/phpstan-oneline", 3 | "description": "PHPStan compact error formatter", 4 | "type": "phpstan-extension", 5 | "scripts": { 6 | "verify": ["@test"], 7 | "test": "vendor/bin/phpstan analyse -c tests/phpstan-config.neon --error-format oneline --no-progress --no-interaction" 8 | }, 9 | "require": { 10 | "php": "^8.2", 11 | "phpstan/phpstan": "^1.0 || ^2.0" 12 | }, 13 | "license": "MIT", 14 | "authors": [ 15 | { 16 | "name": "Jan Kuchař", 17 | "email": "honza.kuchar@grifart.cz" 18 | } 19 | ], 20 | "autoload": { 21 | "psr-4": { 22 | "Grifart\\PhpstanOneLine\\": "src" 23 | } 24 | }, 25 | "extra": { 26 | "phpstan": { 27 | "includes": ["config.neon"] 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /config.neon: -------------------------------------------------------------------------------- 1 | parametersSchema: 2 | compact: structure([ 3 | format: string() 4 | ]) 5 | 6 | parameters: 7 | compact: 8 | format: "{path}:{line}\n ↳ {error}" 9 | 10 | services: 11 | errorFormatter.compact: 12 | factory: Grifart\PhpstanOneLine\CompactErrorFormatter 13 | arguments: 14 | format: %compact.format% 15 | 16 | errorFormatter.oneline: 17 | factory: Grifart\PhpstanOneLine\CompactErrorFormatter 18 | arguments: 19 | format: "{path}:{line} {error}" 20 | -------------------------------------------------------------------------------- /example-compact.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grifart/phpstan-oneline/aba848144beb4894facb64c27fafd27efe84fbfb/example-compact.png -------------------------------------------------------------------------------- /example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grifart/phpstan-oneline/aba848144beb4894facb64c27fafd27efe84fbfb/example.png -------------------------------------------------------------------------------- /src/CompactErrorFormatter.php: -------------------------------------------------------------------------------- 1 | relativePathHelper = $relativePathHelper; 24 | $this->format = $format; 25 | } 26 | 27 | public function formatErrors( 28 | AnalysisResult $analysisResult, 29 | Output $output 30 | ): int 31 | { 32 | if (!$analysisResult->hasErrors()) { 33 | $output->writeLineFormatted('No errors'); 34 | return 0; 35 | } 36 | 37 | foreach ($analysisResult->getNotFileSpecificErrors() as $notFileSpecificError) { 38 | $output->writeLineFormatted(sprintf(' %s', $notFileSpecificError)); 39 | } 40 | 41 | foreach ($analysisResult->getFileSpecificErrors() as $fileSpecificError) { 42 | $absolutePath = $fileSpecificError->getTraitFilePath() ?? $fileSpecificError->getFilePath(); 43 | 44 | $output->writeLineFormatted( 45 | strtr( 46 | $this->format, 47 | [ 48 | '{absolutePath}' => $absolutePath, 49 | '{path}' => $this->relativePathHelper->getRelativePath($fileSpecificError->getFile()), 50 | '{line}' => $fileSpecificError->getLine() ?? '?', 51 | '{error}' => $fileSpecificError->getMessage(), 52 | ] 53 | ) 54 | ); 55 | } 56 | 57 | $output->writeRaw(sprintf( 58 | 'Found %d error%s', 59 | $analysisResult->getTotalErrorsCount(), 60 | $analysisResult->getTotalErrorsCount() === 1 ? '' : 's' 61 | )); 62 | $output->writeLineFormatted(''); 63 | return 1; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /tests/phpstan-config.neon: -------------------------------------------------------------------------------- 1 | parameters: 2 | level: max 3 | paths: 4 | - ../src 5 | 6 | includes: 7 | - ../config.neon 8 | --------------------------------------------------------------------------------