Skip to content

Commit 2817690

Browse files
committed
Generate functionMetadata.php from PhpStorm stubs' #[Pure] attribute
1 parent da9d32f commit 2817690

File tree

4 files changed

+1006
-38
lines changed

4 files changed

+1006
-38
lines changed

bin/functionMetadata_original.php

+101
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
<?php declare(strict_types = 1);
2+
3+
return [
4+
'abs' => ['hasSideEffects' => false],
5+
'acos' => ['hasSideEffects' => false],
6+
'acosh' => ['hasSideEffects' => false],
7+
'addcslashes' => ['hasSideEffects' => false],
8+
'addslashes' => ['hasSideEffects' => false],
9+
'array_change_key_case' => ['hasSideEffects' => false],
10+
'array_chunk' => ['hasSideEffects' => false],
11+
'array_column' => ['hasSideEffects' => false],
12+
'array_combine' => ['hasSideEffects' => false],
13+
'array_count_values' => ['hasSideEffects' => false],
14+
'array_diff' => ['hasSideEffects' => false],
15+
'array_diff_assoc' => ['hasSideEffects' => false],
16+
'array_diff_key' => ['hasSideEffects' => false],
17+
'array_diff_uassoc' => ['hasSideEffects' => false],
18+
'array_diff_ukey' => ['hasSideEffects' => false],
19+
'array_fill' => ['hasSideEffects' => false],
20+
'array_fill_keys' => ['hasSideEffects' => false],
21+
'array_flip' => ['hasSideEffects' => false],
22+
'array_intersect' => ['hasSideEffects' => false],
23+
'array_intersect_assoc' => ['hasSideEffects' => false],
24+
'array_intersect_key' => ['hasSideEffects' => false],
25+
'array_intersect_uassoc' => ['hasSideEffects' => false],
26+
'array_intersect_ukey' => ['hasSideEffects' => false],
27+
'array_key_first' => ['hasSideEffects' => false],
28+
'array_key_last' => ['hasSideEffects' => false],
29+
'array_key_exists' => ['hasSideEffects' => false],
30+
'array_keys' => ['hasSideEffects' => false],
31+
'array_merge' => ['hasSideEffects' => false],
32+
'array_merge_recursive' => ['hasSideEffects' => false],
33+
'array_pad' => ['hasSideEffects' => false],
34+
'array_product' => ['hasSideEffects' => false],
35+
'array_rand' => ['hasSideEffects' => false],
36+
'array_replace' => ['hasSideEffects' => false],
37+
'array_replace_recursive' => ['hasSideEffects' => false],
38+
'array_reverse' => ['hasSideEffects' => false],
39+
'array_slice' => ['hasSideEffects' => false],
40+
'array_sum' => ['hasSideEffects' => false],
41+
'array_udiff' => ['hasSideEffects' => false],
42+
'array_udiff_assoc' => ['hasSideEffects' => false],
43+
'array_udiff_uassoc' => ['hasSideEffects' => false],
44+
'array_uintersect' => ['hasSideEffects' => false],
45+
'array_uintersect_assoc' => ['hasSideEffects' => false],
46+
'array_uintersect_uassoc' => ['hasSideEffects' => false],
47+
'array_unique' => ['hasSideEffects' => false],
48+
'array_values' => ['hasSideEffects' => false],
49+
'asin' => ['hasSideEffects' => false],
50+
'asinh' => ['hasSideEffects' => false],
51+
'atan' => ['hasSideEffects' => false],
52+
'atan2' => ['hasSideEffects' => false],
53+
'atanh' => ['hasSideEffects' => false],
54+
'base64_decode' => ['hasSideEffects' => false],
55+
'base64_encode' => ['hasSideEffects' => false],
56+
'base_convert' => ['hasSideEffects' => false],
57+
'basename' => ['hasSideEffects' => false],
58+
'bcadd' => ['hasSideEffects' => false],
59+
'bccomp' => ['hasSideEffects' => false],
60+
'bcdiv' => ['hasSideEffects' => false],
61+
'bcmod' => ['hasSideEffects' => false],
62+
'bcmul' => ['hasSideEffects' => false],
63+
// continue functionMap.php, line 424
64+
'count' => ['hasSideEffects' => false],
65+
'sprintf' => ['hasSideEffects' => false],
66+
67+
// methods
68+
'DateTime::createFromFormat' => ['hasSideEffects' => false],
69+
'DateTime::createFromImmutable' => ['hasSideEffects' => false],
70+
'DateTime::getLastErrors' => ['hasSideEffects' => false],
71+
'DateTime::add' => ['hasSideEffects' => true],
72+
'DateTime::modify' => ['hasSideEffects' => true],
73+
'DateTime::setDate' => ['hasSideEffects' => true],
74+
'DateTime::setISODate' => ['hasSideEffects' => true],
75+
'DateTime::setTime' => ['hasSideEffects' => true],
76+
'DateTime::setTimestamp' => ['hasSideEffects' => true],
77+
'DateTime::setTimezone' => ['hasSideEffects' => true],
78+
'DateTime::sub' => ['hasSideEffects' => true],
79+
'DateTime::diff' => ['hasSideEffects' => false],
80+
'DateTime::format' => ['hasSideEffects' => false],
81+
'DateTime::getOffset' => ['hasSideEffects' => false],
82+
'DateTime::getTimestamp' => ['hasSideEffects' => false],
83+
'DateTime::getTimezone' => ['hasSideEffects' => false],
84+
85+
'DateTimeImmutable::createFromFormat' => ['hasSideEffects' => false],
86+
'DateTimeImmutable::createFromMutable' => ['hasSideEffects' => false],
87+
'DateTimeImmutable::getLastErrors' => ['hasSideEffects' => false],
88+
'DateTimeImmutable::add' => ['hasSideEffects' => false],
89+
'DateTimeImmutable::modify' => ['hasSideEffects' => false],
90+
'DateTimeImmutable::setDate' => ['hasSideEffects' => false],
91+
'DateTimeImmutable::setISODate' => ['hasSideEffects' => false],
92+
'DateTimeImmutable::setTime' => ['hasSideEffects' => false],
93+
'DateTimeImmutable::setTimestamp' => ['hasSideEffects' => false],
94+
'DateTimeImmutable::setTimezone' => ['hasSideEffects' => false],
95+
'DateTimeImmutable::sub' => ['hasSideEffects' => false],
96+
'DateTimeImmutable::diff' => ['hasSideEffects' => false],
97+
'DateTimeImmutable::format' => ['hasSideEffects' => false],
98+
'DateTimeImmutable::getOffset' => ['hasSideEffects' => false],
99+
'DateTimeImmutable::getTimestamp' => ['hasSideEffects' => false],
100+
'DateTimeImmutable::getTimezone' => ['hasSideEffects' => false],
101+
];

bin/generate-function-metadata.php

+82
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
#!/usr/bin/env php
2+
<?php declare(strict_types=1);
3+
4+
use PhpParser\Node;
5+
use PhpParser\NodeTraverser;
6+
use PhpParser\NodeVisitor\NameResolver;
7+
use PhpParser\ParserFactory;
8+
9+
(function () {
10+
require_once __DIR__ . '/../vendor/autoload.php';
11+
12+
$parser = (new ParserFactory())->create(ParserFactory::ONLY_PHP7);
13+
$finder = new Symfony\Component\Finder\Finder();
14+
$finder->in(__DIR__ . '/../vendor/jetbrains/phpstorm-stubs')->files()->name('*.php');
15+
16+
$visitor = new class() extends \PhpParser\NodeVisitorAbstract {
17+
18+
/** @var string[] */
19+
public $functions = [];
20+
21+
public function enterNode(Node $node)
22+
{
23+
if (!$node instanceof Node\Stmt\Function_) {
24+
return;
25+
}
26+
27+
foreach ($node->attrGroups as $attrGroup) {
28+
foreach ($attrGroup->attrs as $attr) {
29+
if ($attr->name->toString() === \JetBrains\PhpStorm\Pure::class) {
30+
$this->functions[] = $node->namespacedName->toLowerString();
31+
break;
32+
}
33+
}
34+
}
35+
36+
return null;
37+
}
38+
};
39+
40+
foreach ($finder as $stubFile) {
41+
$path = $stubFile->getPathname();
42+
$traverser = new NodeTraverser();
43+
$traverser->addVisitor(new NameResolver());
44+
$traverser->addVisitor($visitor);
45+
46+
$traverser->traverse(
47+
$parser->parse(\PHPStan\File\FileReader::read($path))
48+
);
49+
}
50+
51+
$metadata = require __DIR__ . '/functionMetadata_original.php';
52+
foreach ($visitor->functions as $functionName) {
53+
if (array_key_exists($functionName, $metadata)) {
54+
if ($metadata[$functionName]['hasSideEffects']) {
55+
throw new \PHPStan\ShouldNotHappenException($functionName);
56+
}
57+
}
58+
$metadata[$functionName] = ['hasSideEffects' => false];
59+
}
60+
61+
ksort($metadata);
62+
63+
$template = <<<'php'
64+
<?php declare(strict_types = 1);
65+
66+
return [
67+
%s
68+
];
69+
php;
70+
$content = '';
71+
foreach ($metadata as $name => $meta) {
72+
$content .= sprintf(
73+
"\t%s => [%s => %s],\n",
74+
var_export($name, true),
75+
var_export('hasSideEffects', true),
76+
var_export($meta['hasSideEffects'], true),
77+
);
78+
}
79+
80+
\PHPStan\File\FileWriter::write(__DIR__ . '/../resources/functionMetadata.php', sprintf($template, $content));
81+
82+
})();

0 commit comments

Comments
 (0)