1515use function count ;
1616use function explode ;
1717use function getenv ;
18+ use function implode ;
1819use function in_array ;
1920use function is_string ;
2021use function ltrim ;
2728final class TableErrorFormatter implements ErrorFormatter
2829{
2930
31+ private const MAX_ERRORS_TO_SHOW = 1000 ;
32+
3033 public function __construct (
3134 private RelativePathHelper $ relativePathHelper ,
3235 #[AutowiredParameter(ref: '@simpleRelativePathHelper ' )]
@@ -84,6 +87,13 @@ public function formatErrors(
8487 $ fileErrors [$ fileSpecificError ->getFile ()][] = $ fileSpecificError ;
8588 }
8689
90+ $ errorsBudget = getenv ('PHPSTAN_ERRORS_LIMIT ' );
91+ if ($ errorsBudget === false ) {
92+ $ errorsBudget = self ::MAX_ERRORS_TO_SHOW ;
93+ }
94+ $ errorsBudget = (int ) $ errorsBudget ;
95+
96+ $ printedErrors = 0 ;
8797 foreach ($ fileErrors as $ file => $ errors ) {
8898 $ rows = [];
8999 foreach ($ errors as $ error ) {
@@ -150,6 +160,11 @@ public function formatErrors(
150160 }
151161
152162 $ style ->table (['Line ' , $ this ->relativePathHelper ->getRelativePath ($ file )], $ rows );
163+ $ printedErrors += count ($ rows );
164+
165+ if ($ errorsBudget > 0 && $ printedErrors > $ errorsBudget ) {
166+ break ;
167+ }
153168 }
154169
155170 if (count ($ analysisResult ->getNotFileSpecificErrors ()) > 0 ) {
@@ -172,6 +187,15 @@ public function formatErrors(
172187 $ style ->warning ($ finalMessage );
173188 }
174189
190+ if ($ errorsBudget > 0 && $ printedErrors > $ errorsBudget ) {
191+ $ note = [];
192+ $ note [] = sprintf ('Result is limited to the first %d errors ' , $ errorsBudget );
193+ $ note [] = '- Consider lowering the PHPStan level ' ;
194+ $ note [] = '- Consider using PHPStan Pro for more comfortable error browsing ' ;
195+ $ note [] = '- Pass PHPSTAN_ERRORS_LIMIT=0 environment variable to show all errors ' ;
196+ $ style ->note (implode ("\n" , $ note ));
197+ }
198+
175199 return $ analysisResult ->getTotalErrorsCount () > 0 ? 1 : 0 ;
176200 }
177201
0 commit comments