Skip to content

Commit d460299

Browse files
committed
[+]: add support for psalm checks
1 parent ddab08b commit d460299

File tree

8 files changed

+6322
-3332
lines changed

8 files changed

+6322
-3332
lines changed

README.md

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,29 @@ This is a experiment! Lets check and fix the php documentation automatically.
55

66
### install
77
```bash
8-
git clone https://github.com/php/doc-en.git
9-
git clone https://github.com/php/php-src.git // optional: by default we use the PhpStorm Stubs
8+
git clone https://github.com/php/doc-en.git // optional: only if you want to check it
9+
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
10+
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
11+
git clone https://github.com/vimeo/psalm.git // optional: only if you want to check it
12+
git clone https://github.com/phpstan/phpstan-src.git // optional: only if you want to check it
1013
git clone https://github.com/voku/php-doc-fixer.git
1114
cd php-doc-fixer/
1215
composer update --prefer-dist
1316
```
1417
15-
### command for analysing phpstan stubs
18+
### command for analysing static code analysis stubs (PHPStan, Psalm, ...)
1619
```
17-
php bin/phpdocfixer phpstan [--remove-array-value-info="true"] [--stubs-path="../php-src/"] [--stubs-file-extension=".stub.php"] ../phpstan-src/resources/functionMap.php
20+
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
21+
```
22+
23+
#### example: check types from php-src against static code analysis stubs from PHPStan
24+
```
25+
php bin/phpdocfixer static_analysis --remove-array-value-info="true" --stubs-path="../php-src/" --stubs-file-extension=".stub.php" ../phpstan-src/resources/functionMap.php
26+
```
27+
28+
#### example: check types from PhpStorm stubs against static code analysis stubs from Psalm
29+
```
30+
php bin/phpdocfixer static_analysis ../psalm/src/Psalm/Internal/CallMap.php
1831
```
1932
2033

bin/phpdocfixer.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
$app->add(new \voku\PhpDocFixer\CliCommand\PhpDocFixerCommand());
2121

2222
/** @noinspection UnusedFunctionResultInspection */
23-
$app->add(new \voku\PhpDocFixer\CliCommand\PhpStanFixerCommand());
23+
$app->add(new \voku\PhpDocFixer\CliCommand\StaticAnalysisFixerCommand());
2424

2525
/** @noinspection PhpUnhandledExceptionInspection */
2626
$app->run();

src/voku/PhpDocFixer/CliCommand/PhpStanFixerCommand.php renamed to src/voku/PhpDocFixer/CliCommand/StaticAnalysisFixerCommand.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@
1212
use Symfony\Component\Console\Input\InputInterface;
1313
use Symfony\Component\Console\Output\OutputInterface;
1414

15-
final class PhpStanFixerCommand extends Command
15+
final class StaticAnalysisFixerCommand extends Command
1616
{
17-
public const COMMAND_NAME = 'phpstan';
17+
public const COMMAND_NAME = 'static_analysis';
1818

1919
public function __construct()
2020
{
@@ -25,7 +25,7 @@ public function configure(): void
2525
{
2626
$this
2727
->setName(self::COMMAND_NAME)
28-
->setDescription('Try to fix types in the phpstan stubs.')
28+
->setDescription('Try to find type errors from static code analysis stubs.')
2929
->setDefinition(
3030
new InputDefinition(
3131
[
@@ -73,7 +73,7 @@ public function execute(InputInterface $input, OutputInterface $output): int
7373
return 2;
7474
}
7575

76-
$phpTypesSource = new \voku\PhpDocFixer\PhpStanStubs\PhpStanReader($realPath);
76+
$phpTypesSource = new \voku\PhpDocFixer\StaticCodeAnalysisStubs\StaticCodeAnalysisReader($realPath);
7777
$phpTypesSourceInfo = $phpTypesSource->parse();
7878

7979
if (!$stubsPath) {

src/voku/PhpDocFixer/PhpStubs/PhpStubsReader.php

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,12 @@ public function parse(): array
4040
$return = [];
4141
$functionInfo = $phpCode->getFunctionsInfo();
4242
foreach ($functionInfo as $functionName => $info) {
43-
$return[$functionName]['return'] = \ltrim($info['returnTypes']['typeFromPhpDocSimple'] ?? '', '\\');
43+
44+
$returnTypeTmp = explode('|', $info['returnTypes']['typeFromPhpDocSimple'] ?? '');
45+
sort($returnTypeTmp);
46+
$returnTypeTmp = implode('|', $returnTypeTmp);
47+
48+
$return[$functionName]['return'] = \ltrim($returnTypeTmp, '\\');
4449
if ($this->removeArrayValueInfo) {
4550
$return[$functionName]['return'] = $this->removeArrayValueInfo($return[$functionName]['return']);
4651
}
@@ -49,7 +54,12 @@ public function parse(): array
4954
}
5055

5156
foreach ($info['paramsTypes'] as $paramName => $paramTypes) {
52-
$return[$functionName]['params'][$paramName] = \ltrim($paramTypes['typeFromPhpDocSimple'] ?? '', '\\');
57+
58+
$paramTypeTmp = explode('|', $paramTypes['typeFromPhpDocSimple'] ?? '');
59+
sort($paramTypeTmp);
60+
$paramTypeTmp = implode('|', $paramTypeTmp);
61+
62+
$return[$functionName]['params'][$paramName] = \ltrim($paramTypeTmp, '\\');
5363
if ($this->removeArrayValueInfo) {
5464
$return[$functionName]['params'][$paramName] = $this->removeArrayValueInfo($return[$functionName]['params'][$paramName]);
5565
}
@@ -60,7 +70,12 @@ public function parse(): array
6070
$methodInfo = $class->getMethodsInfo();
6171
$className = (string) $class->name;
6272
foreach ($methodInfo as $methodName => $info) {
63-
$return[$className . '::' . $methodName]['return'] = \ltrim($info['returnTypes']['typeFromPhpDocSimple'] ?? '', '\\');
73+
74+
$returnTypeTmp = explode('|', $info['returnTypes']['typeFromPhpDocSimple'] ?? '');
75+
sort($returnTypeTmp);
76+
$returnTypeTmp = implode('|', $returnTypeTmp);
77+
78+
$return[$className . '::' . $methodName]['return'] = \ltrim($returnTypeTmp, '\\');
6479
if ($this->removeArrayValueInfo) {
6580
$return[$className . '::' . $methodName]['return'] = $this->removeArrayValueInfo($return[$className . '::' . $methodName]['return']);
6681
}
@@ -69,7 +84,12 @@ public function parse(): array
6984
}
7085

7186
foreach ($info['paramsTypes'] as $paramName => $paramTypes) {
72-
$return[$className . '::' . $methodName]['params'][$paramName] = \ltrim($paramTypes['typeFromPhpDocSimple'] ?? '', '\\');
87+
88+
$paramTypeTmp = explode('|', $paramTypes['typeFromPhpDocSimple'] ?? '');
89+
sort($paramTypeTmp);
90+
$paramTypeTmp = implode('|', $paramTypeTmp);
91+
92+
$return[$className . '::' . $methodName]['params'][$paramName] = \ltrim($paramTypeTmp, '\\');
7393
if ($this->removeArrayValueInfo) {
7494
$return[$className . '::' . $methodName]['params'][$paramName] = $this->removeArrayValueInfo($return[$className . '::' . $methodName]['params'][$paramName]);
7595
}
@@ -87,6 +107,14 @@ public function parse(): array
87107
*/
88108
public function removeArrayValueInfo(string $phpdoc_type): string
89109
{
90-
return (string) \preg_replace('#([\w_]*\[\])#', 'array', $phpdoc_type);
110+
$phpdoc_type = (string) \preg_replace('#([\w_]*\[\])#', 'array', $phpdoc_type);
111+
112+
$phpdoc_type = (string) \preg_replace('#(array{.*})#', 'array', $phpdoc_type);
113+
114+
$phpdoc_type = (string) \preg_replace('#(array<.*>)#', 'array', $phpdoc_type);
115+
116+
$phpdoc_type = (string) \preg_replace('#(list<.*>)#', 'array', $phpdoc_type);
117+
118+
return $phpdoc_type;
91119
}
92120
}

src/voku/PhpDocFixer/PhpStanStubs/PhpStanReader.php renamed to src/voku/PhpDocFixer/StaticCodeAnalysisStubs/StaticCodeAnalysisReader.php

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22

33
declare(strict_types=1);
44

5-
namespace voku\PhpDocFixer\PhpStanStubs;
5+
namespace voku\PhpDocFixer\StaticCodeAnalysisStubs;
66

7-
final class PhpStanReader
7+
final class StaticCodeAnalysisReader
88
{
99
private string $path;
1010

@@ -36,6 +36,15 @@ public function parse(): array
3636
foreach ($data as $functionName => $info) {
3737
$returnType = array_shift($info);
3838

39+
if (strpos($returnType, '?') !== false) {
40+
$returnType = str_replace('?', '', $returnType);
41+
$returnType .= '|null';
42+
}
43+
44+
$returnType = explode('|', $returnType);
45+
sort($returnType);
46+
$returnType = implode('|', $returnType);
47+
3948
$return[$functionName]['return'] = \ltrim($returnType, '\\');
4049
if ($this->removeArrayValueInfo) {
4150
$return[$functionName]['return'] = $this->removeArrayValueInfo($return[$functionName]['return']);
@@ -45,10 +54,16 @@ public function parse(): array
4554
}
4655

4756
foreach ($info as $paramName => $paramTypes) {
48-
if (strpos($paramName, '=') !== false) {
57+
if (strpos($paramTypes, '?') !== false) {
58+
$paramTypes = str_replace('?', '', $paramTypes);
4959
$paramTypes .= '|null';
5060
}
5161

62+
$paramTypes = explode('|', $paramTypes);
63+
sort($paramTypes);
64+
$paramTypes = implode('|', $paramTypes);
65+
66+
5267
$return[$functionName]['params'][$paramName] = \ltrim($paramTypes ?? '', '\\');
5368
if ($this->removeArrayValueInfo) {
5469
$return[$functionName]['params'][$paramName] = $this->removeArrayValueInfo($return[$functionName]['params'][$paramName]);
@@ -66,6 +81,14 @@ public function parse(): array
6681
*/
6782
public function removeArrayValueInfo(string $phpdoc_type): string
6883
{
69-
return (string) \preg_replace('#([\w_]*\[\])#', 'array', $phpdoc_type);
84+
$phpdoc_type = (string) \preg_replace('#([\w_]*\[\])#', 'array', $phpdoc_type);
85+
86+
$phpdoc_type = (string) \preg_replace('#(array{.*})#', 'array', $phpdoc_type);
87+
88+
$phpdoc_type = (string) \preg_replace('#(array<.*>)#', 'array', $phpdoc_type);
89+
90+
$phpdoc_type = (string) \preg_replace('#(list<.*>)#', 'array', $phpdoc_type);
91+
92+
return $phpdoc_type;
7093
}
7194
}

src/voku/PhpDocFixer/XmlDocs/XmlReader.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,10 @@ private function findTypes(
117117
}
118118
$data[$name]['return'] = \implode('|', $returnTypesArray ?? []);
119119

120+
$data[$name]['return'] = explode('|', $data[$name]['return']);
121+
sort($data[$name]['return']);
122+
$data[$name]['return'] = implode('|', $data[$name]['return']);
123+
120124
$params = $xmlParser->findMultiOrFalse('//methodparam');
121125
if ($params !== false) {
122126
foreach ($params as $param) {
@@ -135,6 +139,10 @@ private function findTypes(
135139
}
136140

137141
$data[$name]['params'][$paramName] = \implode('|', $paramTypesArray ?? []);
142+
143+
$data[$name]['params'][$paramName] = explode('|', $data[$name]['params'][$paramName]);
144+
sort($data[$name]['params'][$paramName]);
145+
$data[$name]['params'][$paramName] = implode('|', $data[$name]['params'][$paramName]);
138146
}
139147
}
140148

tests/CheckerTest.php

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,12 @@ public static function testPhpStubsReader(): void
1616
$PhpStubsInfo = $phpTypesFromPhpStubs->parse();
1717

1818
$expected = [
19-
'return' => 'int|false',
19+
'return' => 'false|int',
2020
'params' => [
2121
'haystack' => 'string',
2222
'needle' => 'string',
2323
'offset' => 'int',
24-
'encoding' => 'string',
24+
'encoding' => 'null|string',
2525
],
2626
];
2727

@@ -32,11 +32,11 @@ public static function testPhpDocXmlReader(): void
3232
{
3333
$xmlPath = __DIR__ . '/fixtures/bcpow.xml';
3434
$phpDocXmlReader = new \voku\PhpDocFixer\XmlDocs\XmlReader($xmlPath);
35-
$phpDocXmlReaderInfo = $phpDocXmlReader->parse();
35+
$phpDocXmlReaderInfo = self::removeLocalPathForTheTest($phpDocXmlReader->parse());
3636

3737
$expected = [
3838
'bcpow' => [
39-
'absoluteFilePath' => '/home/lmoelleken/testing/git/php-doc-fixer/tests/fixtures/bcpow.xml',
39+
'absoluteFilePath' => 'php-doc-fixer/tests/fixtures/bcpow.xml',
4040
'return' => 'string',
4141
'params' => [
4242
'num' => 'string',
@@ -48,4 +48,37 @@ public static function testPhpDocXmlReader(): void
4848

4949
static::assertSame($expected, $phpDocXmlReaderInfo);
5050
}
51+
52+
/**
53+
* @param array $result
54+
*
55+
* @return array
56+
*/
57+
public static function removeLocalPathForTheTest(array $result): array
58+
{
59+
// hack for CI
60+
$pathReplace = \realpath(\getcwd() . '/../') . '/';
61+
if (!\is_array($result)) {
62+
return $result;
63+
}
64+
65+
$helper = [];
66+
foreach ($result as $key => $value) {
67+
if (\is_string($key)) {
68+
$key = (string) \str_replace($pathReplace, '', $key);
69+
}
70+
71+
if (\is_array($value)) {
72+
$helper[$key] = self::removeLocalPathForTheTest($value);
73+
} else {
74+
if (\is_string($value)) {
75+
$helper[$key] = \str_replace($pathReplace, '', $value);
76+
} else {
77+
$helper[$key] = $value;
78+
}
79+
}
80+
}
81+
82+
return $helper;
83+
}
5184
}

0 commit comments

Comments
 (0)