These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | use Nette\PhpGenerator\ClassType; |
||
4 | use Nette\PhpGenerator\PsrPrinter; |
||
5 | use Psalm\DocComment; |
||
6 | use Psalm\Internal\Analyzer\CommentAnalyzer; |
||
7 | use Webmozart\Assert\Assert; |
||
8 | |||
9 | require_once __DIR__ . '/vendor/autoload.php'; |
||
10 | |||
11 | $g = new ExtraGenerator(); |
||
12 | $g->generate(); |
||
13 | |||
14 | final class ExtraGenerator |
||
15 | { |
||
16 | public function generate() |
||
17 | { |
||
18 | $class = new ReflectionClass(Assert::class); |
||
19 | $staticMethods = $class->getMethods(ReflectionMethod::IS_STATIC); |
||
20 | |||
21 | $supportedMethods = $this->getSupportedMethods($class); |
||
22 | |||
23 | $file = $this->createFile(); |
||
24 | $namespace = $this->createNamespace($file, $class); |
||
25 | $newClass = $this->createClass($namespace); |
||
26 | |||
27 | foreach ($staticMethods as $method) { |
||
28 | if (!$this->isPublicMethod($method)) { |
||
29 | continue; |
||
30 | } |
||
31 | |||
32 | $this->createNullOr($method, $supportedMethods, $newClass); |
||
33 | $this->createAll($method, $supportedMethods, $newClass); |
||
34 | } |
||
35 | |||
36 | $printer = new PsrPrinter(); |
||
37 | |||
38 | $fp = fopen(__DIR__ . '/src/Extra.php', 'wb'); |
||
39 | |||
40 | fwrite($fp, $printer->printFile($file)); |
||
41 | |||
42 | fclose($fp); |
||
43 | } |
||
44 | |||
45 | private function createWrapper(ReflectionMethod $method, array $supportedMethods, ClassType $newClass, $methodNameTemplate, $typeTemplate) |
||
46 | { |
||
47 | $newMethodName = sprintf($methodNameTemplate, ucfirst($method->name)); |
||
48 | |||
49 | if (!in_array($newMethodName, $supportedMethods, true)) { |
||
50 | return; |
||
51 | } |
||
52 | |||
53 | $comment = $method->getDocComment(); |
||
54 | |||
55 | $p = DocComment::parse($comment); |
||
56 | |||
57 | if (!in_array('psalm-assert', array_keys($p['specials']))) { |
||
58 | return; |
||
59 | } |
||
60 | |||
61 | $newMethod = $newClass->addMethod($newMethodName) |
||
62 | ->setStatic() |
||
63 | ; |
||
64 | |||
65 | $parameters = $method->getParameters(); |
||
66 | |||
67 | $parametersCall = []; |
||
68 | foreach ($parameters as $parameter) { |
||
69 | $newParameter = $newMethod->addParameter($parameter->name); |
||
70 | |||
71 | if ($parameter->isDefaultValueAvailable()) { |
||
72 | $newParameter->setDefaultValue($parameter->getDefaultValue()); |
||
73 | } |
||
74 | |||
75 | $parametersCall[] = sprintf('$%s', $parameter->name); |
||
76 | } |
||
77 | |||
78 | $newMethod->addBody(sprintf('parent::%s(%s);', $newMethodName, implode(', ', $parametersCall))); |
||
79 | |||
80 | foreach ($p['specials'] as $key => $values) { |
||
81 | foreach ($values as $value) { |
||
82 | $parts = CommentAnalyzer::splitDocLine($value); |
||
83 | $type = $parts[0]; |
||
84 | |||
85 | if ($key === 'psalm-assert') { |
||
86 | $type = sprintf($typeTemplate, $type); |
||
87 | } |
||
88 | |||
89 | $comment = sprintf('@%s %s', $key, $type); |
||
90 | if (count($parts) >= 2) { |
||
91 | $comment .= sprintf(' %s', implode(' ', array_slice($parts, 1))); |
||
92 | } |
||
93 | |||
94 | $newMethod->addComment($comment); |
||
95 | } |
||
96 | } |
||
97 | } |
||
98 | |||
99 | private function createNullOr(ReflectionMethod $method, array $supportedMethods, ClassType $newClass) |
||
100 | { |
||
101 | $this->createWrapper($method, $supportedMethods, $newClass, 'nullOr%s', 'null|%s'); |
||
102 | } |
||
103 | |||
104 | private function createAll(ReflectionMethod $method, array $supportedMethods, ClassType $newClass) |
||
105 | { |
||
106 | $this->createWrapper($method, $supportedMethods, $newClass, 'all%s', 'array<array-key,%s>'); |
||
107 | } |
||
108 | |||
109 | private function getSupportedMethods(ReflectionClass $class) |
||
110 | { |
||
111 | $comment = $class->getDocComment(); |
||
112 | |||
113 | preg_match_all('~@method static void ([^(]+)~', $comment, $matches); |
||
114 | |||
115 | $temporaryUnsupported = [ |
||
116 | 'nullOrNotInstanceOf', |
||
117 | 'allNotInstanceOf', |
||
118 | 'nullOrNotEmpty', |
||
119 | 'allNotEmpty', |
||
120 | 'allNotNull', |
||
121 | 'nullOrSame', |
||
122 | 'allSame', |
||
123 | 'nullOrUnicodeLetters', |
||
124 | 'allUnicodeLetters', |
||
125 | ]; |
||
126 | |||
127 | return array_diff($matches[1], $temporaryUnsupported); |
||
128 | } |
||
129 | |||
130 | private function createFile() |
||
131 | { |
||
132 | $file = new \Nette\PhpGenerator\PhpFile(); |
||
133 | $file->addComment('Automatically generated'); |
||
134 | |||
135 | return $file; |
||
136 | } |
||
137 | |||
138 | private function createNamespace(\Nette\PhpGenerator\PhpFile $file, ReflectionClass $class) |
||
139 | { |
||
140 | return $file->addNamespace($class->getNamespaceName()); |
||
141 | } |
||
142 | |||
143 | private function createClass(\Nette\PhpGenerator\PhpNamespace $namespace) |
||
0 ignored issues
–
show
|
|||
144 | { |
||
145 | return $namespace->addClass('Extra') |
||
146 | ->setExtends(Assert::class); |
||
147 | } |
||
148 | |||
149 | private function isPublicMethod(ReflectionMethod $method) |
||
150 | { |
||
151 | $modifiers = $method->getModifiers(); |
||
152 | |||
153 | return ($modifiers & ReflectionMethod::IS_PUBLIC) !== 0; |
||
154 | } |
||
155 | } |
||
156 |
Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a
@return
annotation as described here.