These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | namespace Robo\Task\ApiGen; |
||
3 | |||
4 | use Robo\Contract\CommandInterface; |
||
5 | use Robo\Exception\TaskException; |
||
6 | use Robo\Task\BaseTask; |
||
7 | use Traversable; |
||
8 | |||
9 | /** |
||
10 | * Executes ApiGen command to generate documentation |
||
11 | * |
||
12 | * ``` php |
||
13 | * <?php |
||
14 | * // ApiGen Command |
||
15 | * $this->taskApiGen('./vendor/apigen/apigen.phar') |
||
16 | * ->config('./apigen.neon') |
||
17 | * ->templateConfig('vendor/apigen/apigen/templates/bootstrap/config.neon') |
||
18 | * ->wipeout(true) |
||
19 | * ->run(); |
||
20 | * ?> |
||
21 | * ``` |
||
22 | */ |
||
23 | class ApiGen extends BaseTask implements CommandInterface |
||
24 | { |
||
25 | use \Robo\Common\ExecOneCommand; |
||
26 | |||
27 | const BOOL_NO = 'no'; |
||
28 | const BOOL_YES = 'yes'; |
||
29 | |||
30 | /** |
||
31 | * @var string |
||
32 | */ |
||
33 | protected $command; |
||
34 | protected $operation = 'generate'; |
||
35 | |||
36 | /** |
||
37 | * @param null|string $pathToApiGen |
||
38 | * |
||
39 | * @throws \Robo\Exception\TaskException |
||
40 | */ |
||
41 | public function __construct($pathToApiGen = null) |
||
42 | { |
||
43 | $this->command = $pathToApiGen; |
||
44 | $command_parts = []; |
||
45 | preg_match('/((?:.+)?apigen(?:\.phar)?) ?( \w+)? ?(.+)?/', $this->command, $command_parts); |
||
46 | if (count($command_parts) === 3) { |
||
47 | list(, $this->command, $this->operation) = $command_parts; |
||
48 | } |
||
49 | if (count($command_parts) === 4) { |
||
50 | list(, $this->command, $this->operation, $arg) = $command_parts; |
||
51 | $this->arg($arg); |
||
52 | } |
||
53 | if (!$this->command) { |
||
54 | $this->command = $this->findExecutablePhar('apigen'); |
||
0 ignored issues
–
show
|
|||
55 | } |
||
56 | if (!$this->command) { |
||
57 | throw new TaskException(__CLASS__, "No apigen installation found"); |
||
58 | } |
||
59 | } |
||
60 | |||
61 | /** |
||
62 | * Pass methods parameters as arguments to executable. Argument values |
||
63 | * are automatically escaped. |
||
64 | * |
||
65 | * @param string|string[] $args |
||
66 | * |
||
67 | * @return $this |
||
68 | */ |
||
69 | public function args($args) |
||
70 | { |
||
71 | if (!is_array($args)) { |
||
72 | $args = func_get_args(); |
||
73 | } |
||
74 | $args = array_map(function ($arg) { |
||
75 | if (preg_match('/^\w+$/', trim($arg)) === 1) { |
||
76 | $this->operation = $arg; |
||
77 | return null; |
||
78 | } |
||
79 | return $arg; |
||
80 | }, $args); |
||
81 | $args = array_filter($args); |
||
82 | $this->arguments .= ' ' . implode(' ', array_map('static::escape', $args)); |
||
83 | return $this; |
||
84 | } |
||
85 | |||
86 | /** |
||
87 | * @param array|Traversable|string $arg a single object or something traversable |
||
88 | * |
||
89 | * @return array|Traversable the provided argument if it was already traversable, or the given |
||
90 | * argument returned as a one-element array |
||
91 | */ |
||
92 | protected static function forceTraversable($arg) |
||
93 | { |
||
94 | $traversable = $arg; |
||
95 | if (!is_array($traversable) && !($traversable instanceof \Traversable)) { |
||
96 | $traversable = array($traversable); |
||
97 | } |
||
98 | return $traversable; |
||
99 | } |
||
100 | |||
101 | /** |
||
102 | * @param array|string $arg a single argument or an array of multiple string values |
||
103 | * |
||
104 | * @return string a comma-separated string of all of the provided arguments, suitable |
||
105 | * as a command-line "list" type argument for ApiGen |
||
106 | */ |
||
107 | protected static function asList($arg) |
||
108 | { |
||
109 | $normalized = is_array($arg) ? $arg : array($arg); |
||
110 | return implode(',', $normalized); |
||
111 | } |
||
112 | |||
113 | /** |
||
114 | * @param bool|string $val an argument to be normalized |
||
115 | * @param string $default one of self::BOOL_YES or self::BOOK_NO if the provided |
||
116 | * value could not deterministically be converted to a |
||
117 | * yes or no value |
||
118 | * |
||
119 | * @return string the given value as a command-line "yes|no" type of argument for ApiGen, |
||
120 | * or the default value if none could be determined |
||
121 | */ |
||
122 | protected static function asTextBool($val, $default) |
||
123 | { |
||
124 | if ($val === self::BOOL_YES || $val === self::BOOL_NO) { |
||
125 | return $val; |
||
126 | } |
||
127 | if (!$val) { |
||
128 | return self::BOOL_NO; |
||
129 | } |
||
130 | if ($val === true) { |
||
131 | return self::BOOL_YES; |
||
132 | } |
||
133 | if (is_numeric($val) && $val != 0) { |
||
134 | return self::BOOL_YES; |
||
135 | } |
||
136 | if (strcasecmp($val[0], 'y') === 0) { |
||
137 | return self::BOOL_YES; |
||
138 | } |
||
139 | if (strcasecmp($val[0], 'n') === 0) { |
||
140 | return self::BOOL_NO; |
||
141 | } |
||
142 | // meh, good enough, let apigen sort it out |
||
143 | return $default; |
||
144 | } |
||
145 | |||
146 | /** |
||
147 | * @param string $config |
||
148 | * |
||
149 | * @return $this |
||
150 | */ |
||
151 | public function config($config) |
||
152 | { |
||
153 | $this->option('config', $config); |
||
154 | return $this; |
||
155 | } |
||
156 | |||
157 | /** |
||
158 | * @param array|string|Traversable $src one or more source values |
||
159 | * |
||
160 | * @return $this |
||
161 | */ |
||
162 | public function source($src) |
||
163 | { |
||
164 | foreach (self::forceTraversable($src) as $source) { |
||
165 | $this->option('source', $source); |
||
166 | } |
||
167 | return $this; |
||
168 | } |
||
169 | |||
170 | /** |
||
171 | * @param string $dest |
||
172 | * |
||
173 | * @return $this |
||
174 | */ |
||
175 | public function destination($dest) |
||
176 | { |
||
177 | $this->option('destination', $dest); |
||
178 | return $this; |
||
179 | } |
||
180 | |||
181 | /** |
||
182 | * @param array|string $exts one or more extensions |
||
183 | * |
||
184 | * @return $this |
||
185 | */ |
||
186 | public function extensions($exts) |
||
187 | { |
||
188 | $this->option('extensions', self::asList($exts)); |
||
189 | return $this; |
||
190 | } |
||
191 | |||
192 | /** |
||
193 | * @param array|string $exclude one or more exclusions |
||
194 | * |
||
195 | * @return $this |
||
196 | */ |
||
197 | public function exclude($exclude) |
||
198 | { |
||
199 | foreach (self::forceTraversable($exclude) as $excl) { |
||
200 | $this->option('exclude', $excl); |
||
201 | } |
||
202 | return $this; |
||
203 | } |
||
204 | |||
205 | /** |
||
206 | * @param array|string|Traversable $path one or more skip-doc-path values |
||
207 | * |
||
208 | * @return $this |
||
209 | */ |
||
210 | public function skipDocPath($path) |
||
211 | { |
||
212 | foreach (self::forceTraversable($path) as $skip) { |
||
213 | $this->option('skip-doc-path', $skip); |
||
214 | } |
||
215 | return $this; |
||
216 | } |
||
217 | |||
218 | /** |
||
219 | * @param array|string|Traversable $prefix one or more skip-doc-prefix values |
||
220 | * |
||
221 | * @return $this |
||
222 | */ |
||
223 | public function skipDocPrefix($prefix) |
||
224 | { |
||
225 | foreach (self::forceTraversable($prefix) as $skip) { |
||
226 | $this->option('skip-doc-prefix', $skip); |
||
227 | } |
||
228 | return $this; |
||
229 | } |
||
230 | |||
231 | /** |
||
232 | * @param array|string $charset one or more charsets |
||
233 | * |
||
234 | * @return $this |
||
235 | */ |
||
236 | public function charset($charset) |
||
237 | { |
||
238 | $this->option('charset', self::asList($charset)); |
||
239 | return $this; |
||
240 | } |
||
241 | |||
242 | /** |
||
243 | * @param string $name |
||
244 | * |
||
245 | * @return $this |
||
246 | */ |
||
247 | public function mainProjectNamePrefix($name) |
||
248 | { |
||
249 | $this->option('main', $name); |
||
250 | return $this; |
||
251 | } |
||
252 | |||
253 | /** |
||
254 | * @param string $title |
||
255 | * |
||
256 | * @return $this |
||
257 | */ |
||
258 | public function title($title) |
||
259 | { |
||
260 | $this->option('title', $title); |
||
261 | return $this; |
||
262 | } |
||
263 | |||
264 | /** |
||
265 | * @param string $baseUrl |
||
266 | * |
||
267 | * @return $this |
||
268 | */ |
||
269 | public function baseUrl($baseUrl) |
||
270 | { |
||
271 | $this->option('base-url', $baseUrl); |
||
272 | return $this; |
||
273 | } |
||
274 | |||
275 | /** |
||
276 | * @param string $id |
||
277 | * |
||
278 | * @return $this |
||
279 | */ |
||
280 | public function googleCseId($id) |
||
281 | { |
||
282 | $this->option('google-cse-id', $id); |
||
283 | return $this; |
||
284 | } |
||
285 | |||
286 | /** |
||
287 | * @param string $trackingCode |
||
288 | * |
||
289 | * @return $this |
||
290 | */ |
||
291 | public function googleAnalytics($trackingCode) |
||
292 | { |
||
293 | $this->option('google-analytics', $trackingCode); |
||
294 | return $this; |
||
295 | } |
||
296 | |||
297 | /** |
||
298 | * @param mixed $templateConfig |
||
299 | * |
||
300 | * @return $this |
||
301 | */ |
||
302 | public function templateConfig($templateConfig) |
||
303 | { |
||
304 | $this->option('template-config', $templateConfig); |
||
305 | return $this; |
||
306 | } |
||
307 | |||
308 | /** |
||
309 | * @param array|string $tags one or more supported html tags |
||
310 | * |
||
311 | * @return $this |
||
312 | */ |
||
313 | public function allowedHtml($tags) |
||
314 | { |
||
315 | $this->option('allowed-html', self::asList($tags)); |
||
316 | return $this; |
||
317 | } |
||
318 | |||
319 | /** |
||
320 | * @param string $groups |
||
321 | * |
||
322 | * @return $this |
||
323 | */ |
||
324 | public function groups($groups) |
||
325 | { |
||
326 | $this->option('groups', $groups); |
||
327 | return $this; |
||
328 | } |
||
329 | |||
330 | /** |
||
331 | * @param array|string $types or more supported autocomplete types |
||
332 | * |
||
333 | * @return $this |
||
334 | */ |
||
335 | public function autocomplete($types) |
||
336 | { |
||
337 | $this->option('autocomplete', self::asList($types)); |
||
338 | return $this; |
||
339 | } |
||
340 | |||
341 | /** |
||
342 | * @param array|string $levels one or more access levels |
||
343 | * |
||
344 | * @return $this |
||
345 | */ |
||
346 | public function accessLevels($levels) |
||
347 | { |
||
348 | $this->option('access-levels', self::asList($levels)); |
||
349 | return $this; |
||
350 | } |
||
351 | |||
352 | /** |
||
353 | * @param boolean|string $internal 'yes' or true if internal, 'no' or false if not |
||
354 | * |
||
355 | * @return $this |
||
356 | */ |
||
357 | public function internal($internal) |
||
358 | { |
||
359 | $this->option('internal', self::asTextBool($internal, self::BOOL_NO)); |
||
360 | return $this; |
||
361 | } |
||
362 | |||
363 | /** |
||
364 | * @param boolean|string $php 'yes' or true to generate documentation for internal php classes, |
||
365 | * 'no' or false otherwise |
||
366 | * |
||
367 | * @return $this |
||
368 | */ |
||
369 | public function php($php) |
||
370 | { |
||
371 | $this->option('php', self::asTextBool($php, self::BOOL_YES)); |
||
372 | return $this; |
||
373 | } |
||
374 | |||
375 | /** |
||
376 | * @param bool|string $tree 'yes' or true to generate a tree view of classes, 'no' or false otherwise |
||
377 | * |
||
378 | * @return $this |
||
379 | */ |
||
380 | public function tree($tree) |
||
381 | { |
||
382 | $this->option('tree', self::asTextBool($tree, self::BOOL_YES)); |
||
383 | return $this; |
||
384 | } |
||
385 | |||
386 | /** |
||
387 | * @param bool|string $dep 'yes' or true to generate documentation for deprecated classes, 'no' or false otherwise |
||
388 | * |
||
389 | * @return $this |
||
390 | */ |
||
391 | public function deprecated($dep) |
||
392 | { |
||
393 | $this->option('deprecated', self::asTextBool($dep, self::BOOL_NO)); |
||
394 | return $this; |
||
395 | } |
||
396 | |||
397 | /** |
||
398 | * @param bool|string $todo 'yes' or true to document tasks, 'no' or false otherwise |
||
399 | * |
||
400 | * @return $this |
||
401 | */ |
||
402 | public function todo($todo) |
||
403 | { |
||
404 | $this->option('todo', self::asTextBool($todo, self::BOOL_NO)); |
||
405 | return $this; |
||
406 | } |
||
407 | |||
408 | /** |
||
409 | * @param bool|string $src 'yes' or true to generate highlighted source code, 'no' or false otherwise |
||
410 | * |
||
411 | * @return $this |
||
412 | */ |
||
413 | public function sourceCode($src) |
||
414 | { |
||
415 | $this->option('source-code', self::asTextBool($src, self::BOOL_YES)); |
||
416 | return $this; |
||
417 | } |
||
418 | |||
419 | /** |
||
420 | * @param bool|string $zipped 'yes' or true to generate downloadable documentation, 'no' or false otherwise |
||
421 | * |
||
422 | * @return $this |
||
423 | */ |
||
424 | public function download($zipped) |
||
425 | { |
||
426 | $this->option('download', self::asTextBool($zipped, self::BOOL_NO)); |
||
427 | return $this; |
||
428 | } |
||
429 | |||
430 | public function report($path) |
||
431 | { |
||
432 | $this->option('report', $path); |
||
433 | return $this; |
||
434 | } |
||
435 | |||
436 | /** |
||
437 | * @param bool|string $wipeout 'yes' or true to clear out the destination directory, 'no' or false otherwise |
||
438 | * |
||
439 | * @return $this |
||
440 | */ |
||
441 | public function wipeout($wipeout) |
||
442 | { |
||
443 | $this->option('wipeout', self::asTextBool($wipeout, self::BOOL_YES)); |
||
444 | return $this; |
||
445 | } |
||
446 | |||
447 | /** |
||
448 | * @param bool|string $quiet 'yes' or true for quiet, 'no' or false otherwise |
||
449 | * |
||
450 | * @return $this |
||
451 | */ |
||
452 | public function quiet($quiet) |
||
453 | { |
||
454 | $this->option('quiet', self::asTextBool($quiet, self::BOOL_NO)); |
||
455 | return $this; |
||
456 | } |
||
457 | |||
458 | /** |
||
459 | * @param bool|string $bar 'yes' or true to display a progress bar, 'no' or false otherwise |
||
460 | * |
||
461 | * @return $this |
||
462 | */ |
||
463 | public function progressbar($bar) |
||
464 | { |
||
465 | $this->option('progressbar', self::asTextBool($bar, self::BOOL_YES)); |
||
466 | return $this; |
||
467 | } |
||
468 | |||
469 | /** |
||
470 | * @param bool|string $colors 'yes' or true colorize the output, 'no' or false otherwise |
||
471 | * |
||
472 | * @return $this |
||
473 | */ |
||
474 | public function colors($colors) |
||
475 | { |
||
476 | $this->option('colors', self::asTextBool($colors, self::BOOL_YES)); |
||
477 | return $this; |
||
478 | } |
||
479 | |||
480 | /** |
||
481 | * @param bool|string $check 'yes' or true to check for updates, 'no' or false otherwise |
||
482 | * |
||
483 | * @return $this |
||
484 | */ |
||
485 | public function updateCheck($check) |
||
486 | { |
||
487 | $this->option('update-check', self::asTextBool($check, self::BOOL_YES)); |
||
488 | return $this; |
||
489 | } |
||
490 | |||
491 | /** |
||
492 | * @param bool|string $debug 'yes' or true to enable debug mode, 'no' or false otherwise |
||
493 | * |
||
494 | * @return $this |
||
495 | */ |
||
496 | public function debug($debug) |
||
497 | { |
||
498 | $this->option('debug', self::asTextBool($debug, self::BOOL_NO)); |
||
499 | return $this; |
||
500 | } |
||
501 | |||
502 | /** |
||
503 | * {@inheritdoc} |
||
504 | */ |
||
505 | public function getCommand() |
||
506 | { |
||
507 | return "$this->command $this->operation$this->arguments"; |
||
508 | } |
||
509 | |||
510 | /** |
||
511 | * {@inheritdoc} |
||
512 | */ |
||
513 | public function run() |
||
514 | { |
||
515 | $this->printTaskInfo('Running ApiGen {args}', ['args' => $this->arguments]); |
||
516 | return $this->executeCommand($this->getCommand()); |
||
517 | } |
||
518 | } |
||
519 |
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.
For example, imagine you have a variable
$accountId
that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to theid
property of an instance of theAccount
class. This class holds a proper account, so the id value must no longer be false.Either this assignment is in error or a type check should be added for that assignment.