From efb6ae989c45892e5ba0b11dbd3edd336f1d9473 Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Sat, 20 Jun 2026 11:34:03 +0200 Subject: [PATCH 1/2] misc --- composer.json | 2 +- phpstan.neon | 40 ++++-------------- src/Application/SingleFileProcessor.php | 20 +++++++-- src/Console/Output/JsonOutputFormatter.php | 2 +- .../Application/ParallelFileProcessor.php | 42 +++++++++++-------- 5 files changed, 51 insertions(+), 55 deletions(-) diff --git a/composer.json b/composer.json index 5b411cce9a2..68c18608d82 100644 --- a/composer.json +++ b/composer.json @@ -18,7 +18,7 @@ "composer/xdebug-handler": "^3.0.5", "entropy/entropy": "^0.4", "fidry/cpu-core-counter": "^1.3", - "friendsofphp/php-cs-fixer": "^3.95.5", + "friendsofphp/php-cs-fixer": "^3.95.10", "nette/utils": "^4.1", "react/child-process": "^0.6.7", "react/event-loop": "^1.6", diff --git a/phpstan.neon b/phpstan.neon index d36605a3466..a9e64060c5b 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,15 +1,6 @@ parameters: level: 8 - # reportUnmatchedIgnoredErrors: false - - # requires exact closure types - checkMissingCallableSignature: true - - # symplify - see https://github.com/symplify/phpstan-rules#usage - symplify: - pathStrings: true - paths: - packages - src @@ -23,6 +14,10 @@ parameters: - '*/Source/*' - '*/Fixture/*' + # see https://github.com/symplify/phpstan-rules#usage + symplify: + pathStrings: true + # see https://github.com/tomasVotruba/unused-public unused_public: methods: true @@ -32,23 +27,16 @@ parameters: # see https://github.com/TomasVotruba/type-coverage type_coverage: return: 99 - param: 94.4 + param: 99 property: 99 bootstrapFiles: - tests/bootstrap.php - treatPhpDocTypesAsCertain: true + treatPhpDocTypesAsCertain: false ignoreErrors: - # set above - - - path: src/Parallel/Application/ParallelFileProcessor.php - message: '#Cannot call method (.*?)\(\) on Symplify\\EasyCodingStandard\\Parallel\\ValueObject\\ProcessPool\|null#' - - - '#Method Symplify\\EasyCodingStandard\\Application\\SingleFileProcessor\:\:processFilePath\(\) should return array\{file_diffs\?\: array, coding_standard_errors\?\: array\} but returns array<(.*?), array>#' - - # false positive on custom config tets + # on purpose to override a config - message: '#Missing call to parent\:\:setUp\(\) method#' paths: @@ -65,10 +53,6 @@ parameters: # overly detailed - '#PHPDoc tag @var with type string\|false is not subtype of native type non\-empty\-string\|false#' - # array validation on purpose - - '#Call to static method Webmozart\\Assert\\Assert\:\:allString\(\) with (non-empty-array|list|array) will always evaluate to true#' - - '#Call to static method Webmozart\\Assert\\Assert\:\:allIsArray\(\) with array, array> will always evaluate to true#' - # hack to autoload contants - '#Call to new PHP_CodeSniffer\\Util\\Tokens\(\) on a separate line has no effect#' @@ -77,11 +61,6 @@ parameters: identifier: smaller.alwaysFalse path: src/Configuration/ConfigInitializer.php - # false positive - - - identifier: offsetAssign.dimType - path: src/Console/Output/JsonOutputFormatter.php - # coding-standard: runtime-defined PHP_CodeSniffer/php-cs-fixer token constants - '#Constant T_OPEN_CURLY_BRACKET|T_START_NOWDOC not found#' @@ -92,8 +71,3 @@ parameters: - message: '#Comparison operation ">\=" between int<\d+, \d+> and (.*?) is always true#' path: packages/coding-standard/src/TokenAnalyzer/DocblockRelatedParamNamesResolver.php - - # coding-standard: intentional null removal - - - message: '#Parameter \#1 \$array \(array\) to function array_filter does not contain falsy values, the array will always stay the same#' - path: packages/coding-standard/src/TokenRunner/Traverser/TokenReverser.php diff --git a/src/Application/SingleFileProcessor.php b/src/Application/SingleFileProcessor.php index 1f2259981ab..4f2607bc60b 100644 --- a/src/Application/SingleFileProcessor.php +++ b/src/Application/SingleFileProcessor.php @@ -28,7 +28,8 @@ public function processFilePath(string $filePath, Configuration $configuration): return []; } - $errorsAndDiffs = []; + $fileDiffs = []; + $codingStandardErrors = []; $this->changedFilesDetector->addFilePath($filePath); $fileProcessors = $this->fileProcessorCollector->getFileProcessors(); @@ -43,14 +44,27 @@ public function processFilePath(string $filePath, Configuration $configuration): continue; } - $errorsAndDiffs = [...$errorsAndDiffs, ...$currentErrorsAndFileDiffs]; + $fileDiffs = [...$fileDiffs, ...($currentErrorsAndFileDiffs['file_diffs'] ?? [])]; + $codingStandardErrors = [ + ...$codingStandardErrors, + ...($currentErrorsAndFileDiffs['coding_standard_errors'] ?? []), + ]; } // invalidate broken file, to analyse in next run too - if ($errorsAndDiffs !== []) { + if ($fileDiffs !== [] || $codingStandardErrors !== []) { $this->changedFilesDetector->invalidateFilePath($filePath); } + $errorsAndDiffs = []; + if ($fileDiffs !== []) { + $errorsAndDiffs['file_diffs'] = $fileDiffs; + } + + if ($codingStandardErrors !== []) { + $errorsAndDiffs['coding_standard_errors'] = $codingStandardErrors; + } + return $errorsAndDiffs; } } diff --git a/src/Console/Output/JsonOutputFormatter.php b/src/Console/Output/JsonOutputFormatter.php index e5eeaf4dda4..24f3df9a84c 100644 --- a/src/Console/Output/JsonOutputFormatter.php +++ b/src/Console/Output/JsonOutputFormatter.php @@ -76,7 +76,7 @@ public function createJsonContent(ErrorAndDiffResult $errorAndDiffResult, bool $ } /** - * @return array{totals: array{errors: int, diffs: int}, files: string[]} + * @return array{totals: array{errors: int, diffs: int}, files: array>>>} */ private function createBaseErrorsJson(ErrorAndDiffResult $errorAndDiffResult): array { diff --git a/src/Parallel/Application/ParallelFileProcessor.php b/src/Parallel/Application/ParallelFileProcessor.php index 627fd104d3f..6e6f76b8c2e 100644 --- a/src/Parallel/Application/ParallelFileProcessor.php +++ b/src/Parallel/Application/ParallelFileProcessor.php @@ -34,14 +34,12 @@ * * https://github.com/phpstan/phpstan-src/commit/b84acd2e3eadf66189a64fdbc6dd18ff76323f67#diff-7f625777f1ce5384046df08abffd6c911cfbb1cfc8fcb2bdeaf78f337689e3e2R150 */ -final class ParallelFileProcessor +final readonly class ParallelFileProcessor { private const int SYSTEM_ERROR_LIMIT = 50; - private ProcessPool|null $processPool = null; - public function __construct( - private readonly WorkerCommandLineFactory $workerCommandLineFactory, + private WorkerCommandLineFactory $workerCommandLineFactory, ) { } @@ -70,24 +68,32 @@ public function check( $systemErrors = []; $tcpServer = new TcpServer('127.0.0.1:0', $streamSelectLoop); - $this->processPool = new ProcessPool($tcpServer); + $processPool = new ProcessPool($tcpServer); - $tcpServer->on(ReactEvent::CONNECTION, function (ConnectionInterface $connection) use (&$jobs): void { + $tcpServer->on(ReactEvent::CONNECTION, function (ConnectionInterface $connection) use ( + &$jobs, + $processPool + ): void { $inDecoder = new Decoder($connection, true, 512, 0, 4 * 1024 * 1024); $outEncoder = new Encoder($connection); - $inDecoder->on(ReactEvent::DATA, function (array $data) use (&$jobs, $inDecoder, $outEncoder): void { + $inDecoder->on(ReactEvent::DATA, function (array $data) use ( + &$jobs, + $inDecoder, + $outEncoder, + $processPool + ): void { $action = $data[ReactCommand::ACTION]; if ($action !== Action::HELLO) { return; } $processIdentifier = $data[Option::PARALLEL_IDENTIFIER]; - $parallelProcess = $this->processPool->getProcess($processIdentifier); + $parallelProcess = $processPool->getProcess($processIdentifier); $parallelProcess->bindConnection($inDecoder, $outEncoder); if ($jobs === []) { - $this->processPool->quitProcess($processIdentifier); + $processPool->quitProcess($processIdentifier); return; } @@ -112,13 +118,14 @@ public function check( $handleErrorCallable = function (Throwable $throwable) use ( &$systemErrors, &$systemErrorsCount, - &$reachedSystemErrorsCountLimit + &$reachedSystemErrorsCountLimit, + $processPool ): void { $systemErrors[] = new SystemError($throwable->getLine(), $throwable->getMessage(), $throwable->getFile()); ++$systemErrorsCount; $reachedSystemErrorsCountLimit = true; - $this->processPool->quitAll(); + $processPool->quitAll(); }; $timeoutInSeconds = SimpleParameterProvider::getIntParameter(Option::PARALLEL_TIMEOUT_IN_SECONDS); @@ -161,7 +168,8 @@ function (array $json) use ( $postFileCallback, &$systemErrorsCount, &$reachedInternalErrorsCountLimit, - $processIdentifier + $processIdentifier, + $processPool ): void { // decode arrays to objects foreach ($json[Bridge::SYSTEM_ERRORS] as $jsonError) { @@ -186,11 +194,11 @@ function (array $json) use ( $systemErrorsCount += $json[Bridge::SYSTEM_ERRORS_COUNT]; if ($systemErrorsCount >= self::SYSTEM_ERROR_LIMIT) { $reachedInternalErrorsCountLimit = true; - $this->processPool->quitAll(); + $processPool->quitAll(); } if ($jobs === []) { - $this->processPool->quitProcess($processIdentifier); + $processPool->quitProcess($processIdentifier); return; } @@ -205,8 +213,8 @@ function (array $json) use ( $handleErrorCallable, // 3. callable on exit - function ($exitCode, string $stdErr) use (&$systemErrors, $processIdentifier): void { - $this->processPool->tryQuitProcess($processIdentifier); + function ($exitCode, string $stdErr) use (&$systemErrors, $processIdentifier, $processPool): void { + $processPool->tryQuitProcess($processIdentifier); if ($exitCode === ExitCode::SUCCESS) { return; } @@ -219,7 +227,7 @@ function ($exitCode, string $stdErr) use (&$systemErrors, $processIdentifier): v } ); - $this->processPool->attachProcess($processIdentifier, $parallelProcess); + $processPool->attachProcess($processIdentifier, $parallelProcess); } $streamSelectLoop->run(); From 07cf92d2e337f45a74a81f38af900032ab068180 Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Sat, 20 Jun 2026 11:48:09 +0200 Subject: [PATCH 2/2] phsptan fixes --- composer.json | 4 +-- .../src/Fixer/LineLength/LineLengthFixer.php | 12 +++++++++ phpstan.neon | 25 ++++++------------- src/Application/SingleFileProcessor.php | 20 +++------------ .../FileSystem/FnMatchPathNormalizer.php | 1 - src/Skipper/RealpathMatcher.php | 3 +-- tests/SniffRunner/File/FileFactoryTest.php | 2 -- 7 files changed, 26 insertions(+), 41 deletions(-) diff --git a/composer.json b/composer.json index 68c18608d82..6af062fc7a9 100644 --- a/composer.json +++ b/composer.json @@ -16,7 +16,7 @@ "clue/ndjson-react": "^1.3", "composer/pcre": "^3.4", "composer/xdebug-handler": "^3.0.5", - "entropy/entropy": "^0.4", + "entropy/entropy": "^0.4.6", "fidry/cpu-core-counter": "^1.3", "friendsofphp/php-cs-fixer": "^3.95.10", "nette/utils": "^4.1", @@ -37,7 +37,7 @@ "rector/jack": "^1.0", "rector/rector": "^2.4", "rector/type-perfect": "^2.1", - "symplify/phpstan-rules": "^14.11", + "symplify/phpstan-rules": "^14.12", "symplify/vendor-patches": "^11.5", "tomasvotruba/class-leak": "^2.1.7", "tomasvotruba/type-coverage": "^2.2", diff --git a/packages/coding-standard/src/Fixer/LineLength/LineLengthFixer.php b/packages/coding-standard/src/Fixer/LineLength/LineLengthFixer.php index b5423090b11..bdc211effb4 100644 --- a/packages/coding-standard/src/Fixer/LineLength/LineLengthFixer.php +++ b/packages/coding-standard/src/Fixer/LineLength/LineLengthFixer.php @@ -26,6 +26,18 @@ /** * @see \Symplify\CodingStandard\Tests\Fixer\LineLength\LineLengthFixer\LineLengthFixerTest * @see \Symplify\CodingStandard\Tests\Fixer\LineLength\LineLengthFixer\ConfiguredLineLengthFixerTest + * + * @implements ConfigurableFixerInterface< + * array{ + * self::LINE_LENGTH?: int, + * self::BREAK_LONG_LINES?: bool, + * self::INLINE_SHORT_LINES?: bool + * }, array{ + * self::LINE_LENGTH: int, + * self::BREAK_LONG_LINES: bool, + * self::INLINE_SHORT_LINES: bool + * } + * > */ final class LineLengthFixer extends AbstractSymplifyFixer implements ConfigurableFixerInterface { diff --git a/phpstan.neon b/phpstan.neon index a9e64060c5b..42dfedff556 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -33,9 +33,13 @@ parameters: bootstrapFiles: - tests/bootstrap.php + errorFormat: symplify treatPhpDocTypesAsCertain: false ignoreErrors: + # @todo revisit: split return into typed file_diffs/coding_standard_errors buckets + - '#Method Symplify\\EasyCodingStandard\\Application\\SingleFileProcessor\:\:processFilePath\(\) should return array\{file_diffs\?\: array, coding_standard_errors\?\: array\} but returns array<(.*?), array>#' + # on purpose to override a config - message: '#Missing call to parent\:\:setUp\(\) method#' @@ -45,29 +49,16 @@ parameters: - tests/Skipper/SkipCriteriaResolver/SkippedPathsResolver/SkippedPathsResolverTest.php - src/Testing/PHPUnit/AbstractCheckerTestCase.php - # testing instance of on purpose - - - message: '#Call to method PHPUnit\\Framework\\Assert\:\:assertInstanceOf#' - path: tests/* - - # overly detailed - - '#PHPDoc tag @var with type string\|false is not subtype of native type non\-empty\-string\|false#' - - # hack to autoload contants + # intentional: hack to autoload contants - '#Call to new PHP_CodeSniffer\\Util\\Tokens\(\) on a separate line has no effect#' - # php version condition - - - identifier: smaller.alwaysFalse - path: src/Configuration/ConfigInitializer.php - # coding-standard: runtime-defined PHP_CodeSniffer/php-cs-fixer token constants - '#Constant T_OPEN_CURLY_BRACKET|T_START_NOWDOC not found#' - # coding-standard: php-cs-fixer interface generics intentionally left unspecified - - '#Class (.*?) implements generic interface PhpCsFixer\\Fixer\\ConfigurableFixerInterface but does not specify its types\: TFixerInputConfig, TFixerComputedConfig#' - # coding-standard: intentional cross-version condition - message: '#Comparison operation ">\=" between int<\d+, \d+> and (.*?) is always true#' path: packages/coding-standard/src/TokenAnalyzer/DocblockRelatedParamNamesResolver.php + - + identifier: smaller.alwaysFalse + path: src/Configuration/ConfigInitializer.php diff --git a/src/Application/SingleFileProcessor.php b/src/Application/SingleFileProcessor.php index 4f2607bc60b..1f2259981ab 100644 --- a/src/Application/SingleFileProcessor.php +++ b/src/Application/SingleFileProcessor.php @@ -28,8 +28,7 @@ public function processFilePath(string $filePath, Configuration $configuration): return []; } - $fileDiffs = []; - $codingStandardErrors = []; + $errorsAndDiffs = []; $this->changedFilesDetector->addFilePath($filePath); $fileProcessors = $this->fileProcessorCollector->getFileProcessors(); @@ -44,27 +43,14 @@ public function processFilePath(string $filePath, Configuration $configuration): continue; } - $fileDiffs = [...$fileDiffs, ...($currentErrorsAndFileDiffs['file_diffs'] ?? [])]; - $codingStandardErrors = [ - ...$codingStandardErrors, - ...($currentErrorsAndFileDiffs['coding_standard_errors'] ?? []), - ]; + $errorsAndDiffs = [...$errorsAndDiffs, ...$currentErrorsAndFileDiffs]; } // invalidate broken file, to analyse in next run too - if ($fileDiffs !== [] || $codingStandardErrors !== []) { + if ($errorsAndDiffs !== []) { $this->changedFilesDetector->invalidateFilePath($filePath); } - $errorsAndDiffs = []; - if ($fileDiffs !== []) { - $errorsAndDiffs['file_diffs'] = $fileDiffs; - } - - if ($codingStandardErrors !== []) { - $errorsAndDiffs['coding_standard_errors'] = $codingStandardErrors; - } - return $errorsAndDiffs; } } diff --git a/src/Skipper/FileSystem/FnMatchPathNormalizer.php b/src/Skipper/FileSystem/FnMatchPathNormalizer.php index 70029d8225b..03398f9f820 100644 --- a/src/Skipper/FileSystem/FnMatchPathNormalizer.php +++ b/src/Skipper/FileSystem/FnMatchPathNormalizer.php @@ -16,7 +16,6 @@ public function normalizeForFnmatch(string $path): string } if (\str_contains($path, '..')) { - /** @var string|false $realPath */ $realPath = realpath($path); if ($realPath === false) { return ''; diff --git a/src/Skipper/RealpathMatcher.php b/src/Skipper/RealpathMatcher.php index d3674b96e0e..d99d1ebc5c4 100644 --- a/src/Skipper/RealpathMatcher.php +++ b/src/Skipper/RealpathMatcher.php @@ -8,13 +8,12 @@ final class RealpathMatcher { public function match(string $matchingPath, string $filePath): bool { - /** @var string|false $realPathMatchingPath */ + /** @var non-empty-string|false $realPathMatchingPath */ $realPathMatchingPath = realpath($matchingPath); if ($realPathMatchingPath === false) { return false; } - /** @var string|false $realpathFilePath */ $realpathFilePath = realpath($filePath); if ($realpathFilePath === false) { return false; diff --git a/tests/SniffRunner/File/FileFactoryTest.php b/tests/SniffRunner/File/FileFactoryTest.php index 2b26316d3bb..ded13f22f4a 100644 --- a/tests/SniffRunner/File/FileFactoryTest.php +++ b/tests/SniffRunner/File/FileFactoryTest.php @@ -4,7 +4,6 @@ namespace Symplify\EasyCodingStandard\Tests\SniffRunner\File; -use PHP_CodeSniffer\Files\File as PhpCodeSnifferFile; use PHP_CodeSniffer\Fixer; use Symplify\EasyCodingStandard\SniffRunner\File\FileFactory; use Symplify\EasyCodingStandard\SniffRunner\ValueObject\File; @@ -20,7 +19,6 @@ public function test(): void $file = $fileFactory->createFromFile($filePath); $this->assertInstanceOf(File::class, $file); - $this->assertInstanceOf(PhpCodeSnifferFile::class, $file); $this->assertInstanceOf(Fixer::class, $file->fixer); $this->assertSame($filePath, $file->getFilename()); }