Failed Conditions
Pull Request — master (#126)
by
unknown
08:09
created

ExtraGenerator::getSupportedMethods()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 20
rs 9.6
c 0
b 0
f 0
cc 1
nc 1
nop 1
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
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

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.

Loading history...
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