|
4 | 4 |
|
5 | 5 | namespace Behastan; |
6 | 6 |
|
| 7 | +use Behastan\PhpParser\SimplePhpParser; |
| 8 | +use Behastan\Resolver\ClassMethodMasksResolver; |
7 | 9 | use Behastan\ValueObject\ClassMethodContextDefinition; |
8 | 10 | use Behastan\ValueObject\Mask\ExactMask; |
9 | 11 | use Behastan\ValueObject\Mask\NamedMask; |
10 | 12 | use Behastan\ValueObject\Mask\RegexMask; |
11 | 13 | use Behastan\ValueObject\Mask\SkippedMask; |
12 | 14 | use Behastan\ValueObject\MaskCollection; |
13 | | -use PhpParser\Comment\Doc; |
14 | 15 | use PhpParser\Node\Name; |
15 | | -use PhpParser\Node\Scalar\String_; |
16 | | -use PhpParser\Node\Stmt; |
17 | 16 | use PhpParser\Node\Stmt\Class_; |
18 | 17 | use PhpParser\NodeFinder; |
19 | | -use PhpParser\NodeTraverser; |
20 | | -use PhpParser\NodeVisitor\NameResolver; |
21 | | -use PhpParser\ParserFactory; |
22 | 18 | use SplFileInfo; |
23 | 19 |
|
24 | 20 | final class DefinitionMasksResolver |
25 | 21 | { |
26 | | - /** |
27 | | - * @var string |
28 | | - */ |
29 | | - private const INSTRUCTION_DOCBLOCK_REGEX = '#\@(Given|Then|When)\s+(?<instruction>.*?)\n#m'; |
30 | | - |
31 | | - /** |
32 | | - * @var string[] |
33 | | - */ |
34 | | - private const ATTRIBUTE_NAMES = ['Behat\Step\Then', 'Behat\Step\Given', 'Behat\Step\And']; |
| 22 | + public function __construct( |
| 23 | + private readonly SimplePhpParser $simplePhpParser, |
| 24 | + private readonly NodeFinder $nodeFinder, |
| 25 | + private readonly ClassMethodMasksResolver $classMethodMasksResolver, |
| 26 | + ) { |
| 27 | + } |
35 | 28 |
|
36 | 29 | /** |
37 | 30 | * @param SplFileInfo[] $contextFiles |
@@ -94,97 +87,42 @@ public function resolve(array $contextFiles): MaskCollection |
94 | 87 |
|
95 | 88 | /** |
96 | 89 | * @param SplFileInfo[] $fileInfos |
97 | | - * |
98 | 90 | * @return ClassMethodContextDefinition[] |
99 | 91 | */ |
100 | 92 | private function resolveMasksFromFiles(array $fileInfos): array |
101 | 93 | { |
102 | 94 | $classMethodContextDefinitions = []; |
103 | 95 |
|
104 | | - $parserFactory = new ParserFactory(); |
105 | | - $nodeFinder = new NodeFinder(); |
106 | | - |
107 | | - $phpParser = $parserFactory->createForHostVersion(); |
108 | | - $nodeTraverser = new NodeTraverser(); |
109 | | - $nodeTraverser->addVisitor(new NameResolver()); |
110 | | - |
111 | 96 | foreach ($fileInfos as $fileInfo) { |
112 | | - /** @var string $fileContents */ |
113 | | - $fileContents = file_get_contents($fileInfo->getRealPath()); |
114 | | - |
115 | | - /** @var Stmt[] $stmts */ |
116 | | - $stmts = $phpParser->parse($fileContents); |
117 | | - $nodeTraverser->traverse($stmts); |
| 97 | + $stmts = $this->simplePhpParser->parseFilePath($fileInfo->getRealPath()); |
118 | 98 |
|
119 | 99 | // 1. get class name |
120 | | - $class = $nodeFinder->findFirstInstanceOf($stmts, Class_::class); |
| 100 | + $class = $this->nodeFinder->findFirstInstanceOf($stmts, Class_::class); |
121 | 101 | if (! $class instanceof Class_) { |
122 | 102 | continue; |
123 | 103 | } |
124 | 104 |
|
125 | | - if ($class->isAnonymous()) { |
126 | | - continue; |
127 | | - } |
128 | | - |
129 | | - if (! $class->namespacedName instanceof Name) { |
| 105 | + // is magic class? |
| 106 | + if ($class->isAnonymous() || ! $class->namespacedName instanceof Name) { |
130 | 107 | continue; |
131 | 108 | } |
132 | 109 |
|
133 | 110 | $className = $class->namespacedName->toString(); |
134 | 111 |
|
135 | 112 | foreach ($class->getMethods() as $classMethod) { |
136 | | - $methodName = $classMethod->name->toString(); |
137 | | - |
138 | | - // 1. collect from docblock |
139 | | - if ($classMethod->getDocComment() instanceof Doc) { |
140 | | - preg_match_all(self::INSTRUCTION_DOCBLOCK_REGEX, $classMethod->getDocComment()->getText(), $match); |
141 | | - |
142 | | - foreach ($match['instruction'] as $instruction) { |
143 | | - $mask = $this->clearMask($instruction); |
144 | | - |
145 | | - $classMethodContextDefinitions[] = new ClassMethodContextDefinition( |
146 | | - $fileInfo->getRealPath(), |
147 | | - $className, |
148 | | - $methodName, |
149 | | - $mask |
150 | | - ); |
151 | | - } |
152 | | - } |
153 | | - |
154 | | - // 2. collect from attributes |
155 | | - foreach ($classMethod->attrGroups as $attrGroup) { |
156 | | - foreach ($attrGroup->attrs as $attr) { |
157 | | - $attributeName = $attr->name->toString(); |
158 | | - if (! in_array($attributeName, self::ATTRIBUTE_NAMES)) { |
159 | | - continue; |
160 | | - } |
161 | | - |
162 | | - $firstArgValue = $attr->args[0]->value; |
163 | | - |
164 | | - if (! $firstArgValue instanceof String_) { |
165 | | - continue; |
166 | | - } |
167 | | - |
168 | | - $classMethodContextDefinitions[] = new ClassMethodContextDefinition( |
169 | | - $fileInfo->getRealPath(), |
170 | | - $className, |
171 | | - $methodName, |
172 | | - $firstArgValue->value |
173 | | - ); |
174 | | - } |
| 113 | + $rawMasks = $this->classMethodMasksResolver->resolve($classMethod); |
| 114 | + |
| 115 | + foreach ($rawMasks as $rawMask) { |
| 116 | + $classMethodContextDefinitions[] = new ClassMethodContextDefinition( |
| 117 | + $fileInfo->getRealPath(), |
| 118 | + $className, |
| 119 | + $classMethod->name->toString(), |
| 120 | + $rawMask |
| 121 | + ); |
175 | 122 | } |
176 | 123 | } |
177 | 124 | } |
178 | 125 |
|
179 | 126 | return $classMethodContextDefinitions; |
180 | 127 | } |
181 | | - |
182 | | - private function clearMask(string $mask): string |
183 | | - { |
184 | | - $mask = trim($mask); |
185 | | - |
186 | | - // clear extra quote escaping that would cause miss-match with feature masks |
187 | | - $mask = str_replace('\\\'', "'", $mask); |
188 | | - return str_replace('\\/', '/', $mask); |
189 | | - } |
190 | 128 | } |
0 commit comments