This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | namespace Robo\Task\Development; |
||
4 | |||
5 | use Robo\Task\BaseTask; |
||
6 | use Robo\Result; |
||
7 | use Robo\Contract\BuilderAwareInterface; |
||
8 | use Robo\Common\BuilderAwareTrait; |
||
9 | |||
10 | /** |
||
11 | * Simple documentation generator from source files. |
||
12 | * Takes classes, properties and methods with their docblocks and writes down a markdown file. |
||
13 | * |
||
14 | * ``` php |
||
15 | * <?php |
||
16 | * $this->taskGenDoc('models.md') |
||
17 | * ->docClass('Model\User') // take class Model\User |
||
18 | * ->docClass('Model\Post') // take class Model\Post |
||
19 | * ->filterMethods(function(\ReflectionMethod $r) { |
||
20 | * return $r->isPublic() or $r->isProtected(); // process public and protected methods |
||
21 | * })->processClass(function(\ReflectionClass $r, $text) { |
||
22 | * return "Class ".$r->getName()."\n\n$text\n\n###Methods\n"; |
||
23 | * })->run(); |
||
24 | * ``` |
||
25 | * |
||
26 | * By default this task generates a documentation for each public method of a class, interface or trait. |
||
27 | * It combines method signature with a docblock. Both can be post-processed. |
||
28 | * |
||
29 | * ``` php |
||
30 | * <?php |
||
31 | * $this->taskGenDoc('models.md') |
||
32 | * ->docClass('Model\User') |
||
33 | * ->processClassSignature(false) // false can be passed to not include class signature |
||
34 | * ->processClassDocBlock(function(\ReflectionClass $r, $text) { |
||
35 | * return "[This is part of application model]\n" . $text; |
||
36 | * })->processMethodSignature(function(\ReflectionMethod $r, $text) { |
||
37 | * return "#### {$r->name}()"; |
||
38 | * })->processMethodDocBlock(function(\ReflectionMethod $r, $text) { |
||
39 | * return strpos($r->name, 'save')===0 ? "[Saves to the database]\n" . $text : $text; |
||
40 | * })->run(); |
||
41 | * ``` |
||
42 | */ |
||
43 | class GenerateMarkdownDoc extends BaseTask implements BuilderAwareInterface |
||
44 | { |
||
45 | use BuilderAwareTrait; |
||
46 | |||
47 | /** |
||
48 | * @var string[] |
||
49 | */ |
||
50 | protected $docClass = []; |
||
51 | |||
52 | /** |
||
53 | * @var callable |
||
54 | */ |
||
55 | protected $filterMethods; |
||
56 | |||
57 | /** |
||
58 | * @var callable |
||
59 | */ |
||
60 | protected $filterClasses; |
||
61 | |||
62 | /** |
||
63 | * @var callable |
||
64 | */ |
||
65 | protected $filterProperties; |
||
66 | |||
67 | /** |
||
68 | * @var callable |
||
69 | */ |
||
70 | protected $processClass; |
||
71 | |||
72 | /** |
||
73 | * @var callable|false |
||
74 | */ |
||
75 | protected $processClassSignature; |
||
76 | |||
77 | /** |
||
78 | * @var callable|false |
||
79 | */ |
||
80 | protected $processClassDocBlock; |
||
81 | |||
82 | /** |
||
83 | * @var callable|false |
||
84 | */ |
||
85 | protected $processMethod; |
||
86 | |||
87 | /** |
||
88 | * @var callable|false |
||
89 | */ |
||
90 | protected $processMethodSignature; |
||
91 | |||
92 | /** |
||
93 | * @var callable|false |
||
94 | */ |
||
95 | protected $processMethodDocBlock; |
||
96 | |||
97 | /** |
||
98 | * @var callable|false |
||
99 | */ |
||
100 | protected $processProperty; |
||
101 | |||
102 | /** |
||
103 | * @var callable|false |
||
104 | */ |
||
105 | protected $processPropertySignature; |
||
106 | |||
107 | /** |
||
108 | * @var callable|false |
||
109 | */ |
||
110 | protected $processPropertyDocBlock; |
||
111 | |||
112 | /** |
||
113 | * @var callable |
||
114 | */ |
||
115 | protected $reorder; |
||
116 | |||
117 | /** |
||
118 | * @var callable |
||
119 | */ |
||
120 | protected $reorderMethods; |
||
121 | |||
122 | /** |
||
123 | * @todo Unused property. |
||
124 | * |
||
125 | * @var callable |
||
126 | */ |
||
127 | protected $reorderProperties; |
||
128 | |||
129 | /** |
||
130 | * @var string |
||
131 | */ |
||
132 | protected $filename; |
||
133 | |||
134 | /** |
||
135 | * @var string |
||
136 | */ |
||
137 | protected $prepend = ""; |
||
138 | |||
139 | /** |
||
140 | * @var string |
||
141 | */ |
||
142 | protected $append = ""; |
||
143 | |||
144 | /** |
||
145 | * @var string |
||
146 | */ |
||
147 | protected $text; |
||
148 | |||
149 | /** |
||
150 | * @var string[] |
||
151 | */ |
||
152 | protected $textForClass = []; |
||
153 | |||
154 | /** |
||
155 | * @param string $filename |
||
156 | * |
||
157 | * @return static |
||
158 | */ |
||
159 | public static function init($filename) |
||
160 | { |
||
161 | return new static($filename); |
||
162 | } |
||
163 | |||
164 | /** |
||
165 | * @param string $filename |
||
166 | */ |
||
167 | public function __construct($filename) |
||
168 | { |
||
169 | $this->filename = $filename; |
||
170 | } |
||
171 | |||
172 | /** |
||
173 | * Put a class you want to be documented. |
||
174 | * |
||
175 | * @param string $item |
||
176 | * |
||
177 | * @return $this |
||
178 | */ |
||
179 | public function docClass($item) |
||
180 | { |
||
181 | $this->docClass[] = $item; |
||
182 | return $this; |
||
183 | } |
||
184 | |||
185 | /** |
||
186 | * Using a callback function filter out methods that won't be documented. |
||
187 | * |
||
188 | * @param callable $filterMethods |
||
189 | * |
||
190 | * @return $this |
||
191 | */ |
||
192 | public function filterMethods($filterMethods) |
||
193 | { |
||
194 | $this->filterMethods = $filterMethods; |
||
195 | return $this; |
||
196 | } |
||
197 | |||
198 | /** |
||
199 | * Using a callback function filter out classes that won't be documented. |
||
200 | * |
||
201 | * @param callable $filterClasses |
||
202 | * |
||
203 | * @return $this |
||
204 | */ |
||
205 | public function filterClasses($filterClasses) |
||
206 | { |
||
207 | $this->filterClasses = $filterClasses; |
||
208 | return $this; |
||
209 | } |
||
210 | |||
211 | /** |
||
212 | * Using a callback function filter out properties that won't be documented. |
||
213 | * |
||
214 | * @param callable $filterProperties |
||
215 | * |
||
216 | * @return $this |
||
217 | */ |
||
218 | public function filterProperties($filterProperties) |
||
219 | { |
||
220 | $this->filterProperties = $filterProperties; |
||
221 | return $this; |
||
222 | } |
||
223 | |||
224 | /** |
||
225 | * Post-process class documentation. |
||
226 | * |
||
227 | * @param callable $processClass |
||
228 | * |
||
229 | * @return $this |
||
230 | */ |
||
231 | public function processClass($processClass) |
||
232 | { |
||
233 | $this->processClass = $processClass; |
||
234 | return $this; |
||
235 | } |
||
236 | |||
237 | /** |
||
238 | * Post-process class signature. Provide *false* to skip. |
||
239 | * |
||
240 | * @param callable|false $processClassSignature |
||
241 | * |
||
242 | * @return $this |
||
243 | */ |
||
244 | public function processClassSignature($processClassSignature) |
||
245 | { |
||
246 | $this->processClassSignature = $processClassSignature; |
||
247 | return $this; |
||
248 | } |
||
249 | |||
250 | /** |
||
251 | * Post-process class docblock contents. Provide *false* to skip. |
||
252 | * |
||
253 | * @param callable|false $processClassDocBlock |
||
254 | * |
||
255 | * @return $this |
||
256 | */ |
||
257 | public function processClassDocBlock($processClassDocBlock) |
||
258 | { |
||
259 | $this->processClassDocBlock = $processClassDocBlock; |
||
260 | return $this; |
||
261 | } |
||
262 | |||
263 | /** |
||
264 | * Post-process method documentation. Provide *false* to skip. |
||
265 | * |
||
266 | * @param callable|false $processMethod |
||
267 | * |
||
268 | * @return $this |
||
269 | */ |
||
270 | public function processMethod($processMethod) |
||
271 | { |
||
272 | $this->processMethod = $processMethod; |
||
273 | return $this; |
||
274 | } |
||
275 | |||
276 | /** |
||
277 | * Post-process method signature. Provide *false* to skip. |
||
278 | * |
||
279 | * @param callable|false $processMethodSignature |
||
280 | * |
||
281 | * @return $this |
||
282 | */ |
||
283 | public function processMethodSignature($processMethodSignature) |
||
284 | { |
||
285 | $this->processMethodSignature = $processMethodSignature; |
||
286 | return $this; |
||
287 | } |
||
288 | |||
289 | /** |
||
290 | * Post-process method docblock contents. Provide *false* to skip. |
||
291 | * |
||
292 | * @param callable|false $processMethodDocBlock |
||
293 | * |
||
294 | * @return $this |
||
295 | */ |
||
296 | public function processMethodDocBlock($processMethodDocBlock) |
||
297 | { |
||
298 | $this->processMethodDocBlock = $processMethodDocBlock; |
||
299 | return $this; |
||
300 | } |
||
301 | |||
302 | /** |
||
303 | * Post-process property documentation. Provide *false* to skip. |
||
304 | * |
||
305 | * @param callable|false $processProperty |
||
306 | * |
||
307 | * @return $this |
||
308 | */ |
||
309 | public function processProperty($processProperty) |
||
310 | { |
||
311 | $this->processProperty = $processProperty; |
||
312 | return $this; |
||
313 | } |
||
314 | |||
315 | /** |
||
316 | * Post-process property signature. Provide *false* to skip. |
||
317 | * |
||
318 | * @param callable|false $processPropertySignature |
||
319 | * |
||
320 | * @return $this |
||
321 | */ |
||
322 | public function processPropertySignature($processPropertySignature) |
||
323 | { |
||
324 | $this->processPropertySignature = $processPropertySignature; |
||
325 | return $this; |
||
326 | } |
||
327 | |||
328 | /** |
||
329 | * Post-process property docblock contents. Provide *false* to skip. |
||
330 | * |
||
331 | * @param callable|false $processPropertyDocBlock |
||
332 | * |
||
333 | * @return $this |
||
334 | */ |
||
335 | public function processPropertyDocBlock($processPropertyDocBlock) |
||
336 | { |
||
337 | $this->processPropertyDocBlock = $processPropertyDocBlock; |
||
338 | return $this; |
||
339 | } |
||
340 | |||
341 | /** |
||
342 | * Use a function to reorder classes. |
||
343 | * |
||
344 | * @param callable $reorder |
||
345 | * |
||
346 | * @return $this |
||
347 | */ |
||
348 | public function reorder($reorder) |
||
349 | { |
||
350 | $this->reorder = $reorder; |
||
351 | return $this; |
||
352 | } |
||
353 | |||
354 | /** |
||
355 | * Use a function to reorder methods in class. |
||
356 | * |
||
357 | * @param callable $reorderMethods |
||
358 | * |
||
359 | * @return $this |
||
360 | */ |
||
361 | public function reorderMethods($reorderMethods) |
||
362 | { |
||
363 | $this->reorderMethods = $reorderMethods; |
||
364 | return $this; |
||
365 | } |
||
366 | |||
367 | /** |
||
368 | * @param callable $reorderProperties |
||
369 | * |
||
370 | * @return $this |
||
371 | */ |
||
372 | public function reorderProperties($reorderProperties) |
||
373 | { |
||
374 | $this->reorderProperties = $reorderProperties; |
||
375 | return $this; |
||
376 | } |
||
377 | |||
378 | /** |
||
379 | * @param string $filename |
||
380 | * |
||
381 | * @return $this |
||
382 | */ |
||
383 | public function filename($filename) |
||
384 | { |
||
385 | $this->filename = $filename; |
||
386 | return $this; |
||
387 | } |
||
388 | |||
389 | /** |
||
390 | * Inserts text at the beginning of markdown file. |
||
391 | * |
||
392 | * @param string $prepend |
||
393 | * |
||
394 | * @return $this |
||
395 | */ |
||
396 | public function prepend($prepend) |
||
397 | { |
||
398 | $this->prepend = $prepend; |
||
399 | return $this; |
||
400 | } |
||
401 | |||
402 | /** |
||
403 | * Inserts text at the end of markdown file. |
||
404 | * |
||
405 | * @param string $append |
||
406 | * |
||
407 | * @return $this |
||
408 | */ |
||
409 | public function append($append) |
||
410 | { |
||
411 | $this->append = $append; |
||
412 | return $this; |
||
413 | } |
||
414 | |||
415 | /** |
||
416 | * @param string $text |
||
417 | * |
||
418 | * @return $this |
||
419 | */ |
||
420 | public function text($text) |
||
421 | { |
||
422 | $this->text = $text; |
||
423 | return $this; |
||
424 | } |
||
425 | |||
426 | /** |
||
427 | * @param string $item |
||
428 | * |
||
429 | * @return $this |
||
430 | */ |
||
431 | public function textForClass($item) |
||
432 | { |
||
433 | $this->textForClass[] = $item; |
||
434 | return $this; |
||
435 | } |
||
436 | |||
437 | /** |
||
438 | * {@inheritdoc} |
||
439 | */ |
||
440 | public function run() |
||
441 | { |
||
442 | foreach ($this->docClass as $class) { |
||
443 | $this->printTaskInfo("Processing {class}", ['class' => $class]); |
||
444 | $this->textForClass[$class] = $this->documentClass($class); |
||
445 | } |
||
446 | |||
447 | if (is_callable($this->reorder)) { |
||
448 | $this->printTaskInfo("Applying reorder function"); |
||
449 | call_user_func_array($this->reorder, [$this->textForClass]); |
||
450 | } |
||
451 | |||
452 | $this->text = implode("\n", $this->textForClass); |
||
453 | |||
454 | /** @var \Robo\Result $result */ |
||
455 | $result = $this->collectionBuilder()->taskWriteToFile($this->filename) |
||
456 | ->line($this->prepend) |
||
457 | ->text($this->text) |
||
458 | ->line($this->append) |
||
459 | ->run(); |
||
460 | |||
461 | $this->printTaskSuccess('{filename} created. {class-count} classes documented', ['filename' => $this->filename, 'class-count' => count($this->docClass)]); |
||
462 | |||
463 | return new Result($this, $result->getExitCode(), $result->getMessage(), $this->textForClass); |
||
464 | } |
||
465 | |||
466 | /** |
||
467 | * @param string $class |
||
468 | * |
||
469 | * @return null|string |
||
470 | */ |
||
471 | protected function documentClass($class) |
||
472 | { |
||
473 | if (!class_exists($class) && !trait_exists($class)) { |
||
474 | return ""; |
||
475 | } |
||
476 | $refl = new \ReflectionClass($class); |
||
477 | |||
478 | if (is_callable($this->filterClasses)) { |
||
479 | $ret = call_user_func($this->filterClasses, $refl); |
||
480 | if (!$ret) { |
||
481 | return; |
||
482 | } |
||
483 | } |
||
484 | $doc = $this->documentClassSignature($refl); |
||
485 | $doc .= "\n" . $this->documentClassDocBlock($refl); |
||
486 | $doc .= "\n"; |
||
487 | |||
488 | if (is_callable($this->processClass)) { |
||
489 | $doc = call_user_func($this->processClass, $refl, $doc); |
||
490 | } |
||
491 | |||
492 | $properties = []; |
||
493 | foreach ($refl->getProperties() as $reflProperty) { |
||
494 | $properties[] = $this->documentProperty($reflProperty); |
||
495 | } |
||
496 | |||
497 | $properties = array_filter($properties); |
||
498 | $doc .= implode("\n", $properties); |
||
499 | |||
500 | $methods = []; |
||
501 | foreach ($refl->getMethods() as $reflMethod) { |
||
502 | $methods[$reflMethod->name] = $this->documentMethod($reflMethod); |
||
503 | } |
||
504 | if (is_callable($this->reorderMethods)) { |
||
505 | call_user_func_array($this->reorderMethods, [&$methods]); |
||
506 | } |
||
507 | |||
508 | $methods = array_filter($methods); |
||
509 | |||
510 | $doc .= implode("\n", $methods) . "\n"; |
||
511 | |||
512 | return $doc; |
||
513 | } |
||
514 | |||
515 | /** |
||
516 | * @param \ReflectionClass $reflectionClass |
||
517 | * |
||
518 | * @return string |
||
519 | */ |
||
520 | protected function documentClassSignature(\ReflectionClass $reflectionClass) |
||
521 | { |
||
522 | if ($this->processClassSignature === false) { |
||
523 | return ""; |
||
524 | } |
||
525 | |||
526 | $signature = "## {$reflectionClass->name}\n\n"; |
||
527 | |||
528 | if ($parent = $reflectionClass->getParentClass()) { |
||
529 | $signature .= "* *Extends* `{$parent->name}`"; |
||
530 | } |
||
531 | $interfaces = $reflectionClass->getInterfaceNames(); |
||
532 | if (count($interfaces)) { |
||
533 | $signature .= "\n* *Implements* `" . implode('`, `', $interfaces) . '`'; |
||
534 | } |
||
535 | $traits = $reflectionClass->getTraitNames(); |
||
536 | if (count($traits)) { |
||
537 | $signature .= "\n* *Uses* `" . implode('`, `', $traits) . '`'; |
||
538 | } |
||
539 | if (is_callable($this->processClassSignature)) { |
||
540 | $signature = call_user_func($this->processClassSignature, $reflectionClass, $signature); |
||
541 | } |
||
542 | |||
543 | return $signature; |
||
544 | } |
||
545 | |||
546 | /** |
||
547 | * @param \ReflectionClass $reflectionClass |
||
548 | * |
||
549 | * @return string |
||
550 | */ |
||
551 | protected function documentClassDocBlock(\ReflectionClass $reflectionClass) |
||
552 | { |
||
553 | if ($this->processClassDocBlock === false) { |
||
554 | return ""; |
||
555 | } |
||
556 | $doc = self::indentDoc($reflectionClass->getDocComment()); |
||
557 | if (is_callable($this->processClassDocBlock)) { |
||
558 | $doc = call_user_func($this->processClassDocBlock, $reflectionClass, $doc); |
||
559 | } |
||
560 | return $doc; |
||
561 | } |
||
562 | |||
563 | /** |
||
564 | * @param \ReflectionMethod $reflectedMethod |
||
565 | * |
||
566 | * @return string |
||
567 | */ |
||
568 | protected function documentMethod(\ReflectionMethod $reflectedMethod) |
||
569 | { |
||
570 | if ($this->processMethod === false) { |
||
571 | return ""; |
||
572 | } |
||
573 | View Code Duplication | if (is_callable($this->filterMethods)) { |
|
0 ignored issues
–
show
|
|||
574 | $ret = call_user_func($this->filterMethods, $reflectedMethod); |
||
575 | if (!$ret) { |
||
576 | return ""; |
||
577 | } |
||
578 | } else { |
||
579 | if (!$reflectedMethod->isPublic()) { |
||
580 | return ""; |
||
581 | } |
||
582 | } |
||
583 | |||
584 | $signature = $this->documentMethodSignature($reflectedMethod); |
||
585 | $docblock = $this->documentMethodDocBlock($reflectedMethod); |
||
586 | $methodDoc = "$signature $docblock"; |
||
587 | if (is_callable($this->processMethod)) { |
||
588 | $methodDoc = call_user_func($this->processMethod, $reflectedMethod, $methodDoc); |
||
589 | } |
||
590 | return $methodDoc; |
||
591 | } |
||
592 | |||
593 | /** |
||
594 | * @param \ReflectionProperty $reflectedProperty |
||
595 | * |
||
596 | * @return string |
||
597 | */ |
||
598 | protected function documentProperty(\ReflectionProperty $reflectedProperty) |
||
599 | { |
||
600 | if ($this->processProperty === false) { |
||
601 | return ""; |
||
602 | } |
||
603 | View Code Duplication | if (is_callable($this->filterProperties)) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository.
Loading history...
|
|||
604 | $ret = call_user_func($this->filterProperties, $reflectedProperty); |
||
605 | if (!$ret) { |
||
606 | return ""; |
||
607 | } |
||
608 | } else { |
||
609 | if (!$reflectedProperty->isPublic()) { |
||
610 | return ""; |
||
611 | } |
||
612 | } |
||
613 | $signature = $this->documentPropertySignature($reflectedProperty); |
||
614 | $docblock = $this->documentPropertyDocBlock($reflectedProperty); |
||
615 | $propertyDoc = $signature . $docblock; |
||
616 | if (is_callable($this->processProperty)) { |
||
617 | $propertyDoc = call_user_func($this->processProperty, $reflectedProperty, $propertyDoc); |
||
618 | } |
||
619 | return $propertyDoc; |
||
620 | } |
||
621 | |||
622 | /** |
||
623 | * @param \ReflectionProperty $reflectedProperty |
||
624 | * |
||
625 | * @return string |
||
626 | */ |
||
627 | protected function documentPropertySignature(\ReflectionProperty $reflectedProperty) |
||
628 | { |
||
629 | if ($this->processPropertySignature === false) { |
||
630 | return ""; |
||
631 | } |
||
632 | $modifiers = implode(' ', \Reflection::getModifierNames($reflectedProperty->getModifiers())); |
||
633 | $signature = "#### *$modifiers* {$reflectedProperty->name}"; |
||
634 | if (is_callable($this->processPropertySignature)) { |
||
635 | $signature = call_user_func($this->processPropertySignature, $reflectedProperty, $signature); |
||
636 | } |
||
637 | return $signature; |
||
638 | } |
||
639 | |||
640 | /** |
||
641 | * @param \ReflectionProperty $reflectedProperty |
||
642 | * |
||
643 | * @return string |
||
644 | */ |
||
645 | protected function documentPropertyDocBlock(\ReflectionProperty $reflectedProperty) |
||
646 | { |
||
647 | if ($this->processPropertyDocBlock === false) { |
||
648 | return ""; |
||
649 | } |
||
650 | $propertyDoc = $reflectedProperty->getDocComment(); |
||
651 | // take from parent |
||
652 | if (!$propertyDoc) { |
||
653 | $parent = $reflectedProperty->getDeclaringClass(); |
||
654 | while ($parent = $parent->getParentClass()) { |
||
655 | if ($parent->hasProperty($reflectedProperty->name)) { |
||
656 | $propertyDoc = $parent->getProperty($reflectedProperty->name)->getDocComment(); |
||
657 | } |
||
658 | } |
||
659 | } |
||
660 | $propertyDoc = self::indentDoc($propertyDoc, 7); |
||
661 | $propertyDoc = preg_replace("~^@(.*?)([$\s])~", ' * `$1` $2', $propertyDoc); // format annotations |
||
662 | if (is_callable($this->processPropertyDocBlock)) { |
||
663 | $propertyDoc = call_user_func($this->processPropertyDocBlock, $reflectedProperty, $propertyDoc); |
||
664 | } |
||
665 | return ltrim($propertyDoc); |
||
666 | } |
||
667 | |||
668 | /** |
||
669 | * @param \ReflectionParameter $param |
||
670 | * |
||
671 | * @return string |
||
672 | */ |
||
673 | protected function documentParam(\ReflectionParameter $param) |
||
674 | { |
||
675 | $text = ""; |
||
676 | if ($param->isArray()) { |
||
677 | $text .= 'array '; |
||
678 | } |
||
679 | if ($param->isCallable()) { |
||
680 | $text .= 'callable '; |
||
681 | } |
||
682 | $text .= '$' . $param->name; |
||
683 | if ($param->isDefaultValueAvailable()) { |
||
684 | if ($param->allowsNull()) { |
||
685 | $text .= ' = null'; |
||
686 | } else { |
||
687 | $text .= ' = ' . str_replace("\n", ' ', print_r($param->getDefaultValue(), true)); |
||
688 | } |
||
689 | } |
||
690 | |||
691 | return $text; |
||
692 | } |
||
693 | |||
694 | /** |
||
695 | * @param string $doc |
||
696 | * @param int $indent |
||
697 | * |
||
698 | * @return string |
||
699 | */ |
||
700 | public static function indentDoc($doc, $indent = 3) |
||
701 | { |
||
702 | if (!$doc) { |
||
703 | return $doc; |
||
704 | } |
||
705 | return implode( |
||
706 | "\n", |
||
707 | array_map( |
||
708 | function ($line) use ($indent) { |
||
709 | return substr($line, $indent); |
||
710 | }, |
||
711 | explode("\n", $doc) |
||
712 | ) |
||
713 | ); |
||
714 | } |
||
715 | |||
716 | /** |
||
717 | * @param \ReflectionMethod $reflectedMethod |
||
718 | * |
||
719 | * @return string |
||
720 | */ |
||
721 | protected function documentMethodSignature(\ReflectionMethod $reflectedMethod) |
||
722 | { |
||
723 | if ($this->processMethodSignature === false) { |
||
724 | return ""; |
||
725 | } |
||
726 | $modifiers = implode(' ', \Reflection::getModifierNames($reflectedMethod->getModifiers())); |
||
727 | $params = implode( |
||
728 | ', ', |
||
729 | array_map( |
||
730 | function ($p) { |
||
731 | return $this->documentParam($p); |
||
732 | }, |
||
733 | $reflectedMethod->getParameters() |
||
734 | ) |
||
735 | ); |
||
736 | $signature = "#### *$modifiers* {$reflectedMethod->name}($params)"; |
||
737 | if (is_callable($this->processMethodSignature)) { |
||
738 | $signature = call_user_func($this->processMethodSignature, $reflectedMethod, $signature); |
||
739 | } |
||
740 | return $signature; |
||
741 | } |
||
742 | |||
743 | /** |
||
744 | * @param \ReflectionMethod $reflectedMethod |
||
745 | * |
||
746 | * @return string |
||
747 | */ |
||
748 | protected function documentMethodDocBlock(\ReflectionMethod $reflectedMethod) |
||
749 | { |
||
750 | if ($this->processMethodDocBlock === false) { |
||
751 | return ""; |
||
752 | } |
||
753 | $methodDoc = $reflectedMethod->getDocComment(); |
||
754 | // take from parent |
||
755 | if (!$methodDoc) { |
||
756 | $parent = $reflectedMethod->getDeclaringClass(); |
||
757 | while ($parent = $parent->getParentClass()) { |
||
758 | if ($parent->hasMethod($reflectedMethod->name)) { |
||
759 | $methodDoc = $parent->getMethod($reflectedMethod->name)->getDocComment(); |
||
760 | } |
||
761 | } |
||
762 | } |
||
763 | // take from interface |
||
764 | if (!$methodDoc) { |
||
765 | $interfaces = $reflectedMethod->getDeclaringClass()->getInterfaces(); |
||
766 | foreach ($interfaces as $interface) { |
||
767 | $i = new \ReflectionClass($interface->name); |
||
768 | if ($i->hasMethod($reflectedMethod->name)) { |
||
769 | $methodDoc = $i->getMethod($reflectedMethod->name)->getDocComment(); |
||
770 | break; |
||
771 | } |
||
772 | } |
||
773 | } |
||
774 | |||
775 | $methodDoc = self::indentDoc($methodDoc, 7); |
||
776 | $methodDoc = preg_replace("~^@(.*?) ([$\s])~m", ' * `$1` $2', $methodDoc); // format annotations |
||
777 | if (is_callable($this->processMethodDocBlock)) { |
||
778 | $methodDoc = call_user_func($this->processMethodDocBlock, $reflectedMethod, $methodDoc); |
||
779 | } |
||
780 | |||
781 | return $methodDoc; |
||
782 | } |
||
783 | } |
||
784 |
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.