1 | <?php |
||
24 | class ResourceGenerator |
||
25 | { |
||
26 | /** |
||
27 | * @var Context |
||
28 | */ |
||
29 | protected $context; |
||
30 | |||
31 | /** |
||
32 | * @var Stdio |
||
33 | */ |
||
34 | protected $stdio; |
||
35 | |||
36 | /** |
||
37 | * @var array |
||
38 | */ |
||
39 | protected $definitions = []; |
||
40 | |||
41 | /** |
||
42 | * @var string |
||
43 | */ |
||
44 | protected $path; |
||
45 | |||
46 | /** |
||
47 | * @var Fixer |
||
48 | */ |
||
49 | protected $fixer; |
||
50 | |||
51 | /** |
||
52 | * @var array |
||
53 | */ |
||
54 | protected $fixers; |
||
55 | |||
56 | 2 | public function __construct(Context $context, Stdio $stdio) |
|
64 | |||
65 | 2 | protected function setUpArguments() |
|
79 | |||
80 | 2 | protected function setUpFixers() |
|
115 | |||
116 | 1 | public function run() |
|
117 | { |
||
118 | 1 | $this->checkValidity(); |
|
119 | |||
120 | 1 | foreach ($this->definitions as $definition) { |
|
121 | 1 | $this->stdio->outln('-----'); |
|
122 | 1 | $this->stdio->outln('- Definition: ' . $definition); |
|
123 | 1 | $this->stdio->outln('-----'); |
|
124 | 1 | $this->generateFromDefinition($definition); |
|
125 | 1 | $this->stdio->outln('-----'); |
|
126 | } |
||
127 | 1 | } |
|
128 | |||
129 | 1 | public function checkValidity() |
|
130 | { |
||
131 | 1 | if (count($this->definitions) < 1) { |
|
132 | throw new \InvalidArgumentException('Not enough arguments'); |
||
133 | } |
||
134 | |||
135 | 1 | if ($this->path === null) { |
|
136 | throw new \InvalidArgumentException('No path set'); |
||
137 | } |
||
138 | |||
139 | 1 | if (!file_exists($this->path)) { |
|
140 | throw new \InvalidArgumentException('Path "' . $this->path . '" doesn\'t exist'); |
||
141 | } |
||
142 | |||
143 | 1 | if (!is_dir($this->path)) { |
|
144 | throw new \InvalidArgumentException('Path "' . $this->path . '" isn\'t a directory'); |
||
145 | } |
||
146 | |||
147 | 1 | foreach ($this->definitions as $definition) { |
|
148 | 1 | if (!file_exists($definition)) { |
|
149 | 1 | throw new \InvalidArgumentException('Definition "' . $definition . '" doesn\'t exist'); |
|
150 | } |
||
151 | } |
||
152 | 1 | } |
|
153 | |||
154 | 1 | public function generateFromDefinition($definition) |
|
155 | { |
||
156 | 1 | $yaml = $this->readYaml($definition); |
|
157 | |||
158 | 1 | $namespacePadding = explode('\\', $yaml['class']); |
|
159 | 1 | $namespace = explode('\\', $yaml['namespace']); |
|
160 | |||
161 | 1 | $yaml['class'] = array_pop($namespacePadding); |
|
162 | 1 | $yaml['namespace'] = implode('\\', array_merge($namespace, $namespacePadding)); |
|
163 | |||
164 | 1 | $namespacePathPadding = implode(DIRECTORY_SEPARATOR, $namespacePadding); |
|
165 | 1 | $baseClass = implode( |
|
166 | 1 | '\\', |
|
167 | array_merge( |
||
168 | $namespace, |
||
169 | $namespacePadding, |
||
170 | [ |
||
171 | 1 | $yaml['class'] |
|
172 | ] |
||
173 | ) |
||
174 | ); |
||
175 | |||
176 | 1 | $this->stdio->out('Interface: generating'); |
|
177 | 1 | $this->save( |
|
178 | 1 | $this->path . |
|
179 | 1 | DIRECTORY_SEPARATOR . |
|
180 | 1 | $namespacePathPadding . |
|
181 | 1 | DIRECTORY_SEPARATOR, |
|
182 | 1 | $yaml['class'] . |
|
183 | 1 | 'Interface.php', |
|
184 | 1 | $this->createInterface($yaml) |
|
185 | ); |
||
186 | |||
187 | 1 | $this->stdio->out('Base class: generating'); |
|
188 | 1 | $this->save( |
|
189 | 1 | $this->path . |
|
190 | 1 | DIRECTORY_SEPARATOR . |
|
191 | 1 | $namespacePathPadding . |
|
192 | 1 | DIRECTORY_SEPARATOR, |
|
193 | 1 | $yaml['class'] . |
|
194 | 1 | '.php', |
|
195 | 1 | $this->createBaseClass($yaml) |
|
196 | ); |
||
197 | |||
198 | 1 | $this->stdio->out('Async class: generating'); |
|
199 | 1 | $this->save( |
|
200 | 1 | $this->path . |
|
201 | 1 | DIRECTORY_SEPARATOR . |
|
202 | 1 | 'Async' . |
|
203 | 1 | DIRECTORY_SEPARATOR . |
|
204 | 1 | $namespacePathPadding . |
|
205 | 1 | DIRECTORY_SEPARATOR, |
|
206 | 1 | $yaml['class'] . |
|
207 | 1 | '.php', |
|
208 | 1 | $this->createExtendingClass( |
|
209 | implode( |
||
210 | 1 | '\\', |
|
211 | array_merge( |
||
212 | $namespace, |
||
213 | [ |
||
214 | 1 | 'Async', |
|
215 | ], |
||
216 | $namespacePadding |
||
217 | ) |
||
218 | ), |
||
219 | 1 | $yaml['class'], |
|
220 | $baseClass |
||
221 | ) |
||
222 | ); |
||
223 | |||
224 | 1 | $this->stdio->out('Sync class: generating'); |
|
225 | 1 | $this->save( |
|
226 | 1 | $this->path . |
|
227 | 1 | DIRECTORY_SEPARATOR . |
|
228 | 1 | 'Sync' . |
|
229 | 1 | DIRECTORY_SEPARATOR . |
|
230 | 1 | $namespacePathPadding . |
|
231 | 1 | DIRECTORY_SEPARATOR, |
|
232 | 1 | $yaml['class'] . |
|
233 | 1 | '.php', |
|
234 | 1 | $this->createExtendingClass( |
|
235 | implode( |
||
236 | 1 | '\\', |
|
237 | array_merge( |
||
238 | $namespace, |
||
239 | [ |
||
240 | 1 | 'Sync', |
|
241 | ], |
||
242 | $namespacePadding |
||
243 | ) |
||
244 | ), |
||
245 | 1 | $yaml['class'], |
|
246 | $baseClass |
||
247 | ) |
||
248 | ); |
||
249 | 1 | } |
|
250 | |||
251 | 1 | protected function readYaml(string $filename): array |
|
252 | { |
||
253 | 1 | return Yaml::parse(file_get_contents($filename)); |
|
254 | } |
||
255 | |||
256 | 1 | protected function createBaseClass(array $yaml) |
|
257 | { |
||
258 | 1 | $factory = new BuilderFactory; |
|
259 | |||
260 | 1 | $class = $factory->class($yaml['class']) |
|
261 | 1 | ->implement($yaml['class'] . 'Interface') |
|
262 | 1 | ->makeAbstract(); |
|
263 | 1 | $class->addStmt( |
|
264 | 1 | new Node\Stmt\TraitUse([ |
|
265 | 1 | new Node\Name('TransportAwareTrait') |
|
266 | ]) |
||
267 | ); |
||
268 | |||
269 | 1 | foreach ($yaml['properties'] as $name => $details) { |
|
270 | 1 | $type = $details; |
|
271 | 1 | if (is_array($details)) { |
|
272 | 1 | $type = $details['type']; |
|
273 | } |
||
274 | 1 | $class->addStmt($this->createProperty($factory, $type, $name, $details)); |
|
275 | 1 | $class->addStmt($this->createMethod($factory, $type, $name, $details)); |
|
276 | } |
||
277 | |||
278 | 1 | $node = $factory->namespace($yaml['namespace']) |
|
279 | 1 | ->addStmt($factory->use('WyriHaximus\ApiClient\Resource\TransportAwareTrait')) |
|
280 | 1 | ->addStmt($class) |
|
281 | |||
282 | 1 | ->getNode() |
|
283 | ; |
||
284 | |||
285 | 1 | $prettyPrinter = new PrettyPrinter\Standard(); |
|
286 | 1 | return $prettyPrinter->prettyPrintFile([ |
|
287 | 1 | $node |
|
288 | 1 | ]) . PHP_EOL; |
|
289 | } |
||
290 | |||
291 | 1 | protected function createInterface(array $yaml) |
|
292 | { |
||
293 | 1 | $factory = new BuilderFactory; |
|
294 | |||
295 | 1 | $class = $factory->interface($yaml['class'] . 'Interface') |
|
296 | 1 | ->extend('ResourceInterface'); |
|
297 | |||
298 | 1 | foreach ($yaml['properties'] as $name => $details) { |
|
299 | 1 | $type = $details; |
|
300 | 1 | if (is_array($details)) { |
|
301 | 1 | $type = $details['type']; |
|
302 | } |
||
303 | 1 | $class->addStmt($this->createMethod($factory, $type, $name, $details)); |
|
304 | } |
||
305 | |||
306 | 1 | $node = $factory->namespace($yaml['namespace']) |
|
307 | 1 | ->addStmt($factory->use(ResourceInterface::class)) |
|
308 | 1 | ->addStmt($class) |
|
309 | 1 | ->getNode() |
|
310 | ; |
||
311 | |||
312 | 1 | $prettyPrinter = new PrettyPrinter\Standard(); |
|
313 | 1 | return $prettyPrinter->prettyPrintFile([ |
|
314 | 1 | $node |
|
315 | 1 | ]) . PHP_EOL; |
|
316 | } |
||
317 | |||
318 | 1 | protected function createProperty(BuilderFactory $factory, string $type, string $name, $details): Property |
|
319 | { |
||
320 | 1 | $property = $factory->property($name) |
|
321 | 1 | ->makeProtected() |
|
322 | 1 | ->setDocComment('/** |
|
323 | 1 | * @var ' . $type . ' |
|
324 | 1 | */'); |
|
325 | 1 | if (isset($details['default'])) { |
|
326 | 1 | $property->setDefault($details['default']); |
|
327 | } |
||
328 | |||
329 | 1 | return $property; |
|
330 | } |
||
331 | |||
332 | 1 | protected function createMethod(BuilderFactory $factory, string $type, string $name, $details): Method |
|
|
|||
333 | { |
||
334 | 1 | return $factory->method(Inflector::camelize($name)) |
|
335 | 1 | ->makePublic() |
|
336 | 1 | ->setReturnType($type) |
|
337 | 1 | ->setDocComment('/** |
|
338 | 1 | * @return ' . $type . ' |
|
339 | 1 | */') |
|
340 | 1 | ->addStmt( |
|
341 | 1 | new Node\Stmt\Return_( |
|
342 | 1 | new Node\Expr\PropertyFetch( |
|
343 | 1 | new Node\Expr\Variable('this'), |
|
344 | $name |
||
345 | ) |
||
346 | ) |
||
347 | ); |
||
348 | } |
||
349 | |||
350 | 1 | protected function createExtendingClass(string $namespace, string $className, string $baseClass) |
|
351 | { |
||
352 | 1 | $factory = new BuilderFactory; |
|
353 | |||
354 | 1 | $class = $factory->class($className) |
|
355 | 1 | ->extend('Base' . $className); |
|
356 | |||
357 | 1 | $class->addStmt($factory->method('refresh') |
|
358 | 1 | ->makePublic() |
|
359 | 1 | ->setReturnType($className) |
|
360 | 1 | ->addStmt( |
|
361 | 1 | new Node\Stmt\Return_( |
|
362 | 1 | new Node\Expr\MethodCall( |
|
363 | 1 | new Node\Expr\Variable('this'), |
|
364 | 1 | 'wait', |
|
365 | [ |
||
366 | 1 | new Node\Expr\MethodCall( |
|
367 | 1 | new Node\Expr\Variable('this'), |
|
368 | 1 | 'callAsync', |
|
369 | [ |
||
370 | 1 | new Node\Scalar\String_('refresh'), |
|
371 | ] |
||
372 | ), |
||
373 | ] |
||
374 | ) |
||
375 | ) |
||
376 | )); |
||
377 | |||
378 | 1 | $node = $factory->namespace($namespace) |
|
379 | 1 | ->addStmt($factory->use($baseClass)->as('Base' . $className)) |
|
380 | 1 | ->addStmt($class) |
|
381 | |||
382 | 1 | ->getNode() |
|
383 | ; |
||
384 | |||
385 | 1 | $prettyPrinter = new PrettyPrinter\Standard(); |
|
386 | 1 | return $prettyPrinter->prettyPrintFile([ |
|
387 | 1 | $node |
|
388 | 1 | ]) . PHP_EOL; |
|
389 | } |
||
390 | |||
391 | 1 | protected function save(string $directory, string $fileName, string $fileContents) |
|
392 | { |
||
393 | 1 | $fileName = str_replace('\\', DIRECTORY_SEPARATOR, $fileName); |
|
394 | 1 | if (file_exists($directory . $fileName)) { |
|
395 | $this->stdio->outln(', exists!'); |
||
396 | return; |
||
397 | } |
||
398 | |||
399 | 1 | $path = $directory . $fileName; |
|
400 | 1 | $pathChunks = explode(DIRECTORY_SEPARATOR, $path); |
|
401 | 1 | array_pop($pathChunks); |
|
402 | 1 | $path = implode(DIRECTORY_SEPARATOR, $pathChunks); |
|
403 | 1 | if (!file_exists($path)) { |
|
404 | 1 | mkdir($path, 0777, true); |
|
405 | } |
||
406 | |||
407 | 1 | if (!file_exists($path)) { |
|
408 | throw new Exception('Unable to create: ' . $path); |
||
409 | } |
||
410 | |||
411 | 1 | $this->stdio->out(', writing'); |
|
412 | 1 | file_put_contents($directory . $fileName, $fileContents); |
|
413 | |||
414 | do { |
||
415 | 1 | usleep(500); |
|
416 | 1 | } while (!file_exists($directory . $fileName)); |
|
417 | |||
418 | 1 | $this->stdio->out(', applying PSR-2'); |
|
419 | 1 | $this->applyPsr2($directory . $fileName); |
|
420 | 1 | $this->stdio->outln(', done!'); |
|
421 | 1 | } |
|
422 | |||
423 | /** |
||
424 | * @param string $fileName |
||
425 | */ |
||
426 | 1 | protected function applyPsr2($fileName) |
|
427 | { |
||
428 | 1 | $file = new \SplFileInfo($fileName); |
|
429 | 1 | $this->fixer->fixFile( |
|
430 | $file, |
||
431 | 1 | $this->fixers, |
|
432 | 1 | false, |
|
433 | 1 | false, |
|
434 | 1 | new FileCacheManager( |
|
435 | 1 | false, |
|
436 | 1 | '', |
|
437 | 1 | $this->fixers |
|
438 | ) |
||
439 | ); |
||
440 | 1 | } |
|
441 | |||
442 | |||
443 | /** |
||
444 | * @param ConfigInterface $config |
||
445 | * |
||
446 | * @return FixerInterface[] |
||
447 | */ |
||
448 | 2 | private function prepareFixers(ConfigInterface $config) |
|
460 | } |
||
461 |
This check looks from parameters that have been defined for a function or method, but which are not used in the method body.