Skip to content

Commit ddab08b

Browse files
committed
[+]: add check for phpstan stubs
1 parent 2d1efac commit ddab08b

File tree

6 files changed

+14036
-3
lines changed

6 files changed

+14036
-3
lines changed

README.md

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,23 @@ cd php-doc-fixer/
1212
composer update --prefer-dist
1313
```
1414

15-
### command
15+
### command for analysing phpstan stubs
16+
```
17+
php bin/phpdocfixer phpstan [--remove-array-value-info="true"] [--stubs-path="../php-src/"] [--stubs-file-extension=".stub.php"] ../phpstan-src/resources/functionMap.php
18+
```
19+
20+
21+
### command for analysing and fixing the php documentation
1622
```
1723
php bin/phpdocfixer run [--auto-fix="true"] [--remove-array-value-info="true"] [--stubs-path="../php-src/"] [--stubs-file-extension=".stub.php"] ../doc-en/reference/
1824
```
1925

20-
### example: sync types from PhpStorm Stubs into the php-documentation
26+
#### example: sync types from PhpStorm Stubs into the php-documentation
2127
```
2228
php bin/phpdocfixer run --auto-fix="true" --remove-array-value-info="true" ../doc-en/reference/
2329
```
2430

25-
### example: sync types from php-src into the php-documentation, but only for BCMath (bc)
31+
#### example: sync types from php-src into the php-documentation, but only for BCMath (bc)
2632
```
2733
php bin/phpdocfixer run --auto-fix="true" --stubs-path="../php-src/" --stubs-file-extension=".stub.php" ../doc-en/reference/bc/
2834
```

bin/phpdocfixer.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@
1919
/** @noinspection UnusedFunctionResultInspection */
2020
$app->add(new \voku\PhpDocFixer\CliCommand\PhpDocFixerCommand());
2121

22+
/** @noinspection UnusedFunctionResultInspection */
23+
$app->add(new \voku\PhpDocFixer\CliCommand\PhpStanFixerCommand());
24+
2225
/** @noinspection PhpUnhandledExceptionInspection */
2326
$app->run();
2427
})();
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
<?php
2+
3+
/** @noinspection TransitiveDependenciesUsageInspection */
4+
5+
declare(strict_types=1);
6+
7+
namespace voku\PhpDocFixer\CliCommand;
8+
9+
use Symfony\Component\Console\Command\Command;
10+
use Symfony\Component\Console\Input\InputArgument;
11+
use Symfony\Component\Console\Input\InputDefinition;
12+
use Symfony\Component\Console\Input\InputInterface;
13+
use Symfony\Component\Console\Output\OutputInterface;
14+
15+
final class PhpStanFixerCommand extends Command
16+
{
17+
public const COMMAND_NAME = 'phpstan';
18+
19+
public function __construct()
20+
{
21+
parent::__construct();
22+
}
23+
24+
public function configure(): void
25+
{
26+
$this
27+
->setName(self::COMMAND_NAME)
28+
->setDescription('Try to fix types in the phpstan stubs.')
29+
->setDefinition(
30+
new InputDefinition(
31+
[
32+
new InputArgument('path', InputArgument::REQUIRED, 'The path to analyse'),
33+
]
34+
)
35+
)
36+
->addOption(
37+
'remove-array-value-info',
38+
null,
39+
\Symfony\Component\Console\Input\InputOption::VALUE_OPTIONAL,
40+
'Automatically convert e.g. int[] into array. (false or true)',
41+
'false'
42+
)->addOption(
43+
'stubs-path',
44+
null,
45+
\Symfony\Component\Console\Input\InputOption::VALUE_OPTIONAL,
46+
'Overwrite the source of the stubs, by default we use PhpStorm Stubs via composer.',
47+
''
48+
)->addOption(
49+
'stubs-file-extension',
50+
null,
51+
\Symfony\Component\Console\Input\InputOption::VALUE_OPTIONAL,
52+
'Overwrite the default file extension for stubs.',
53+
'.php'
54+
);
55+
}
56+
57+
public function execute(InputInterface $input, OutputInterface $output): int
58+
{
59+
$path = $input->getArgument('path');
60+
\assert(\is_string($path));
61+
$realPath = \realpath($path);
62+
\assert(\is_string($realPath));
63+
64+
$removeArrayValueInfo = $input->getOption('remove-array-value-info') !== 'false';
65+
$stubsPath = $input->getOption('stubs-path');
66+
$stubsFileExtension = $input->getOption('stubs-file-extension');
67+
68+
if (!$realPath || !\file_exists($realPath)) {
69+
$output->writeln('-------------------------------');
70+
$output->writeln('The path "' . $path . '" does not exists.');
71+
$output->writeln('-------------------------------');
72+
73+
return 2;
74+
}
75+
76+
$phpTypesSource = new \voku\PhpDocFixer\PhpStanStubs\PhpStanReader($realPath);
77+
$phpTypesSourceInfo = $phpTypesSource->parse();
78+
79+
if (!$stubsPath) {
80+
$stubsPath = __DIR__ . '/../../../../vendor/jetbrains/phpstorm-stubs/';
81+
}
82+
$phpTypesFromStubs = new \voku\PhpDocFixer\PhpStubs\PhpStubsReader(
83+
$stubsPath,
84+
$removeArrayValueInfo,
85+
$stubsFileExtension
86+
);
87+
$stubsInfo = $phpTypesFromStubs->parse();
88+
89+
$errors = [];
90+
foreach ($phpTypesSourceInfo as $functionName_or_classAndMethodName => $types) {
91+
if (!isset($stubsInfo[$functionName_or_classAndMethodName])) {
92+
// TODO: error in stubs?
93+
//\var_dump($functionName_or_classAndMethodName); exit;
94+
continue;
95+
}
96+
97+
if (
98+
($stubsInfo[$functionName_or_classAndMethodName]['return'] ?? []) !== ($types['return'] ?? [])
99+
||
100+
(array_values($stubsInfo[$functionName_or_classAndMethodName]['params'] ?? [])) !== (array_values($types['params'] ?? []))
101+
) {
102+
$errors[$functionName_or_classAndMethodName] = [
103+
'phpStubTypes' => $stubsInfo[$functionName_or_classAndMethodName],
104+
'phpStanTypes' => $types,
105+
];
106+
}
107+
}
108+
109+
$output->writeln(\count($errors) . ' ' . 'errors found');
110+
111+
foreach ($errors as $name => $typesArray) {
112+
$output->writeln('----------------');
113+
$output->writeln($name);
114+
$output->writeln(\print_r($typesArray, true));
115+
$output->writeln('----------------');
116+
}
117+
118+
return 0;
119+
}
120+
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace voku\PhpDocFixer\PhpStanStubs;
6+
7+
final class PhpStanReader
8+
{
9+
private string $path;
10+
11+
private bool $removeArrayValueInfo;
12+
13+
private string $stubsFileExtension;
14+
15+
public function __construct(
16+
string $path,
17+
bool $removeArrayValueInfo = false
18+
)
19+
{
20+
$this->path = $path;
21+
$this->removeArrayValueInfo = $removeArrayValueInfo;
22+
}
23+
24+
/**
25+
* @return array
26+
*
27+
* @phpstan-return array<string, array{return: string, params?: array<string, string>}>
28+
*/
29+
public function parse(): array
30+
{
31+
/** @noinspection PhpIncludeInspection */
32+
/** @noinspection UsingInclusionReturnValueInspection */
33+
$data = require $this->path;
34+
35+
$return = [];
36+
foreach ($data as $functionName => $info) {
37+
$returnType = array_shift($info);
38+
39+
$return[$functionName]['return'] = \ltrim($returnType, '\\');
40+
if ($this->removeArrayValueInfo) {
41+
$return[$functionName]['return'] = $this->removeArrayValueInfo($return[$functionName]['return']);
42+
}
43+
if ($return[$functionName]['return'] === '') {
44+
$return[$functionName]['return'] = 'void';
45+
}
46+
47+
foreach ($info as $paramName => $paramTypes) {
48+
if (strpos($paramName, '=') !== false) {
49+
$paramTypes .= '|null';
50+
}
51+
52+
$return[$functionName]['params'][$paramName] = \ltrim($paramTypes ?? '', '\\');
53+
if ($this->removeArrayValueInfo) {
54+
$return[$functionName]['params'][$paramName] = $this->removeArrayValueInfo($return[$functionName]['params'][$paramName]);
55+
}
56+
}
57+
}
58+
59+
return $return;
60+
}
61+
62+
/**
63+
* @param string $phpdoc_type
64+
*
65+
* @return string
66+
*/
67+
public function removeArrayValueInfo(string $phpdoc_type): string
68+
{
69+
return (string) \preg_replace('#([\w_]*\[\])#', 'array', $phpdoc_type);
70+
}
71+
}

src/voku/PhpDocFixer/PhpStubs/PhpStubsReader.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@ public function parse(): array
4444
if ($this->removeArrayValueInfo) {
4545
$return[$functionName]['return'] = $this->removeArrayValueInfo($return[$functionName]['return']);
4646
}
47+
if ($return[$functionName]['return'] === '') {
48+
$return[$functionName]['return'] = 'void';
49+
}
50+
4751
foreach ($info['paramsTypes'] as $paramName => $paramTypes) {
4852
$return[$functionName]['params'][$paramName] = \ltrim($paramTypes['typeFromPhpDocSimple'] ?? '', '\\');
4953
if ($this->removeArrayValueInfo) {
@@ -60,6 +64,10 @@ public function parse(): array
6064
if ($this->removeArrayValueInfo) {
6165
$return[$className . '::' . $methodName]['return'] = $this->removeArrayValueInfo($return[$className . '::' . $methodName]['return']);
6266
}
67+
if ($return[$className . '::' . $methodName]['return'] === '') {
68+
$return[$className . '::' . $methodName]['return'] = 'void';
69+
}
70+
6371
foreach ($info['paramsTypes'] as $paramName => $paramTypes) {
6472
$return[$className . '::' . $methodName]['params'][$paramName] = \ltrim($paramTypes['typeFromPhpDocSimple'] ?? '', '\\');
6573
if ($this->removeArrayValueInfo) {

0 commit comments

Comments
 (0)