diff --git a/README.md b/README.md index 3ed4929..dd042dc 100644 --- a/README.md +++ b/README.md @@ -5,9 +5,9 @@ This is a experiment! Lets check and fix the php documentation automatically. ### install ```bash -git clone https://github.com/php/doc-en.git // optional: only if you want to check it -git clone https://github.com/php/php-src.git // optional: by default we use the PhpStorm Stubs from vendor directory but you can also use different stubs -git clone https://github.com/jetbrains/phpstorm-stubs.git // optional: by default we use the PhpStorm Stubs from vendor directory but you can also use external stubs +git clone https://github.com/php/doc-en.git // required for the doc-en sync workflow +git clone https://github.com/php/php-src.git // required for the doc-en sync workflow; this is the source of truth for synopsis updates +git clone https://github.com/jetbrains/phpstorm-stubs.git // optional: useful for static analysis comparisons or alternative stub sources git clone https://github.com/vimeo/psalm.git // optional: only if you want to check it git clone https://github.com/phpstan/phpstan-src.git // optional: only if you want to check it git clone https://github.com/voku/php-doc-fixer.git @@ -22,8 +22,14 @@ composer scan-docs -- ../doc-en/reference/ composer fix-docs -- ../doc-en/reference/ ``` +`scan-docs` and `fix-docs` use `../php-src` with `.stub.php` files so `php-src` is the source of truth for `php/doc-en` pull requests. + +The `run` command normalizes refined and pseudo-types back to manual-safe synopsis types by default, including collapsing array value info such as `int[]` to `array`. Pass `--remove-array-value-info="false"` if you need to keep that information for a custom comparison. + `fix-docs` re-checks the XML after updating files, so it exits successfully when all detected mismatches were applied automatically and reports the remaining count if manual follow-up is still needed. It can update both single types and union types in either direction when the XML synopsis and stub signature disagree. +If the full `reference/` diff is too large for one review, run the same commands against extension or module subdirectories and open smaller pull requests, for example `../doc-en/reference/bc/` or `../doc-en/reference/mysqli/`. + ### command for analysing static code analysis stubs (PHPStan, Psalm, ...) ``` php bin/phpdocfixer static_analysis [--remove-array-value-info="true"] [--stubs-path="vendor/jetbrains/phpstorm-stubs/"] [--stubs-file-extension=".php"] ../phpstan-src/resources/functionMap.php @@ -34,6 +40,8 @@ php bin/phpdocfixer static_analysis [--remove-array-value-info="true"] [--stubs- php bin/phpdocfixer static_analysis --remove-array-value-info="true" --stubs-path="../php-src/" --stubs-file-extension=".stub.php" ../phpstan-src/resources/functionMap.php ``` +The static-analysis flow also normalizes PHPStan-only pseudo-types such as `class-string`, `list`, array shapes, callable signatures, and `int-mask<...>` back to comparable native types before reporting mismatches. + #### example: check types from phpstorm-stubs (mysqli) against static code analysis stubs from PHPStan ``` php bin/phpdocfixer static_analysis --stubs-path="../phpstorm-stubs/mysqli/" ../phpstan-src/resources/functionMap.php @@ -50,9 +58,9 @@ php bin/phpdocfixer static_analysis ../psalm/dictionaries/CallMap_84.php php bin/phpdocfixer run [--auto-fix="true"] [--remove-array-value-info="true"] [--stubs-path="../php-src/"] [--stubs-file-extension=".stub.php"] ../doc-en/reference/ ``` -#### example: sync types from PhpStorm Stubs into the php-documentation +#### example: sync types from php-src stubs into the php-documentation ``` -php bin/phpdocfixer run --auto-fix="true" --remove-array-value-info="true" ../doc-en/reference/ +php bin/phpdocfixer run --auto-fix="true" --stubs-path="../php-src/" --stubs-file-extension=".stub.php" ../doc-en/reference/ ``` #### example: run the full scan and then apply all directly fixable updates diff --git a/composer.json b/composer.json index 9d0bb45..866cc19 100644 --- a/composer.json +++ b/composer.json @@ -38,8 +38,8 @@ }, "scripts": { "test": "phpunit", - "scan-docs": "@php bin/phpdocfixer run", - "fix-docs": "@php bin/phpdocfixer run --auto-fix=true" + "scan-docs": "@php bin/phpdocfixer run --stubs-path=../php-src --stubs-file-extension=.stub.php", + "fix-docs": "@php bin/phpdocfixer run --auto-fix=true --stubs-path=../php-src --stubs-file-extension=.stub.php" }, "bin": [ "bin/phpdocfixer" diff --git a/src/voku/PhpDocFixer/CliCommand/PhpDocFixerCommand.php b/src/voku/PhpDocFixer/CliCommand/PhpDocFixerCommand.php index 9b92c9d..7bb3e7c 100644 --- a/src/voku/PhpDocFixer/CliCommand/PhpDocFixerCommand.php +++ b/src/voku/PhpDocFixer/CliCommand/PhpDocFixerCommand.php @@ -44,8 +44,8 @@ public function configure(): void 'remove-array-value-info', null, \Symfony\Component\Console\Input\InputOption::VALUE_OPTIONAL, - 'Automatically convert e.g. int[] into array. (false or true)', - 'false' + 'Automatically convert e.g. int[] into array for manual-safe synopsis types. (false or true)', + 'true' )->addOption( 'stubs-path', null, diff --git a/src/voku/PhpDocFixer/Type/TypeNormalizer.php b/src/voku/PhpDocFixer/Type/TypeNormalizer.php index 1b1da37..8e5bf4d 100644 --- a/src/voku/PhpDocFixer/Type/TypeNormalizer.php +++ b/src/voku/PhpDocFixer/Type/TypeNormalizer.php @@ -15,8 +15,10 @@ public function __construct(bool $removeArrayValueInfo = false) public function normalize(string $type): string { - if (\strpos($type, '?') !== false) { - $type = \str_replace('?', '', $type); + $type = \trim($type); + + if (isset($type[0]) && $type[0] === '?') { + $type = \trim(\substr($type, 1)); $type .= '|null'; } @@ -88,11 +90,11 @@ private function normalizeTypePart(string $typePart): string $typePart = (string) \preg_replace('/\blist<.*>/', 'array', $typePart); $typePart = (string) \preg_replace('/\barray\{.*\}/', 'array', $typePart); $typePart = (string) \preg_replace('/\biterable<.*>/', 'iterable', $typePart); - $typePart = (string) \preg_replace('/\b(?:pure-)?callable\(.*\)/', 'callable', $typePart); - $typePart = (string) \preg_replace('/\bClosure\(.*\)/', 'callable', $typePart); + $typePart = (string) \preg_replace('/^(?:pure-)?callable\(.*\)(?::.+)?$/', 'callable', $typePart); + $typePart = (string) \preg_replace('/^Closure\(.*\)(?::.+)?$/', 'callable', $typePart); $typePart = (string) \preg_replace('/\bobject\{.*\}/', 'object', $typePart); $typePart = (string) \preg_replace('/\barray-key\b/', 'int|string', $typePart); - $typePart = (string) \preg_replace('/\bresource \(closed\)\b/', 'resource', $typePart); + $typePart = (string) \preg_replace('/^resource \(closed\)$/', 'resource', $typePart); if (\preg_match('/^[A-Za-z_][A-Za-z0-9_\\\\]*<.*>$/', $typePart)) { $typePart = (string) \preg_replace('/<.*>$/', '', $typePart); diff --git a/tests/CliCommandTest.php b/tests/CliCommandTest.php index ee0e67d..c78ec8e 100644 --- a/tests/CliCommandTest.php +++ b/tests/CliCommandTest.php @@ -202,6 +202,18 @@ public function testPhpDocFixerCommandPrefersNativeTypesOverStubPhpDoc(): void static::assertStringContainsString('0 errors found', $commandTester->getDisplay()); } + public function testPhpDocFixerCommandNormalizesArrayValueInfoByDefault(): void + { + $commandTester = new CommandTester(new PhpDocFixerCommand()); + $exitCode = $commandTester->execute([ + 'path' => __DIR__ . '/fixtures/doc-safe-array.xml', + '--stubs-path' => __DIR__ . '/fixtures/stubs/doc-safe-array', + ]); + + static::assertSame(Command::SUCCESS, $exitCode); + static::assertStringContainsString('0 errors found', $commandTester->getDisplay()); + } + public function testStaticAnalysisCommandSucceedsForMatchingFixture(): void { $commandTester = new CommandTester(new StaticAnalysisFixerCommand()); @@ -262,4 +274,16 @@ public function testStaticAnalysisCommandNormalizesRefinedTypesToNativeTypes(): static::assertSame(Command::SUCCESS, $exitCode); static::assertStringContainsString('0 errors found', $commandTester->getDisplay()); } + + public function testStaticAnalysisCommandNormalizesPhpStanPseudoTypesToNativeTypes(): void + { + $commandTester = new CommandTester(new StaticAnalysisFixerCommand()); + $exitCode = $commandTester->execute([ + 'path' => __DIR__ . '/fixtures/functionMap-phpstan-types.php', + '--stubs-path' => __DIR__ . '/fixtures/stubs/phpstan-types', + ]); + + static::assertSame(Command::SUCCESS, $exitCode); + static::assertStringContainsString('0 errors found', $commandTester->getDisplay()); + } } diff --git a/tests/fixtures/doc-safe-array.xml b/tests/fixtures/doc-safe-array.xml new file mode 100644 index 0000000..60af663 --- /dev/null +++ b/tests/fixtures/doc-safe-array.xml @@ -0,0 +1,9 @@ + + + + + arraydoc_safe_array + arrayitems + + + diff --git a/tests/fixtures/functionMap-phpstan-types.php b/tests/fixtures/functionMap-phpstan-types.php new file mode 100644 index 0000000..107125d --- /dev/null +++ b/tests/fixtures/functionMap-phpstan-types.php @@ -0,0 +1,15 @@ + [ + 'list', + 'value' => 'non-empty-string', + 'callback' => 'pure-callable(int):literal-string', + 'class' => 'class-string', + 'items' => 'iterable', + 'map' => 'array{foo:int,bar?:string}', + 'payload' => 'object{foo:int}', + 'mask' => 'int-mask<1, 2, 4>', + 'stream' => 'resource (closed)', + ], +]; diff --git a/tests/fixtures/stubs/doc-safe-array/doc-safe-array.php b/tests/fixtures/stubs/doc-safe-array/doc-safe-array.php new file mode 100644 index 0000000..ab8917e --- /dev/null +++ b/tests/fixtures/stubs/doc-safe-array/doc-safe-array.php @@ -0,0 +1,10 @@ +