GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( c517ee...c7ab7d )
by Jad
17:39
created

src/Console/Command/GenerateCommand.php (4 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
/**
4
 * This file is part of the ApiGen (http://apigen.org)
5
 *
6
 * For the full copyright and license information, please view
7
 * the file LICENSE that was distributed with this source code.
8
 */
9
10
namespace ApiGen\Console\Command;
11
12
use ApiGen\Configuration\Configuration;
13
use ApiGen\Configuration\Readers\ReaderFactory;
14
use ApiGen\Contracts\Console\IO\IOInterface;
15
use ApiGen\Contracts\Generator\GeneratorQueueInterface;
16
use ApiGen\Contracts\Parser\ParserInterface;
17
use ApiGen\Contracts\Parser\ParserStorageInterface;
18
use ApiGen\Theme\ThemeResources;
19
use ApiGen\Utils\FileSystem;
20
use ApiGen\Utils\Finder\FinderInterface;
21
use Symfony\Component\Console\Input\InputInterface;
22
use Symfony\Component\Console\Input\InputOption;
23
use Symfony\Component\Console\Output\OutputInterface;
24
use TokenReflection\Exception\FileProcessingException;
25
26
class GenerateCommand extends AbstractCommand
27
{
28
29
    /**
30
     * @var Configuration
31
     */
32
    private $configuration;
33
34
    /**
35
     * @var ParserInterface
36
     */
37
    private $parser;
38
39
    /**
40
     * @var ParserStorageInterface
41
     */
42
    private $parserResult;
43
44
    /**
45
     * @var GeneratorQueueInterface
46
     */
47
    private $generatorQueue;
48
49
    /**
50
     * @var FileSystem
51
     */
52
    private $fileSystem;
53
54
    /**
55
     * @var ThemeResources
56
     */
57
    private $themeResources;
58
59
    /**
60
     * @var IOInterface
61
     */
62
    private $io;
63
64
    /**
65
     * @var FinderInterface
66
     */
67
    private $finder;
68
69
70
    public function __construct(
71
        Configuration $configuration,
72
        ParserInterface $parser,
73
        ParserStorageInterface $parserResult,
74
        GeneratorQueueInterface $generatorQueue,
75
        FileSystem $fileSystem,
76
        ThemeResources $themeResources,
77
        IOInterface $io,
78
        FinderInterface $finder
79
    ) {
80
        parent::__construct();
81
        $this->configuration = $configuration;
82
        $this->parser = $parser;
83
        $this->parserResult = $parserResult;
84
        $this->generatorQueue = $generatorQueue;
85
        $this->fileSystem = $fileSystem;
86
        $this->themeResources = $themeResources;
87
        $this->io = $io;
88
        $this->finder = $finder;
89
    }
90
91
92
    protected function configure()
93
    {
94
        $this->setName('generate')
95
            ->setDescription('Generate API documentation')
96
            ->addOption(
97
                'source',
98
                's',
99
                InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED,
100
                'Dirs or files documentation is generated for.'
101
            )
102
            ->addOption('destination', 'd', InputOption::VALUE_REQUIRED, 'Target dir for documentation.')
103
            ->addOption(
104
                'accessLevels',
105
                null,
106
                InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED,
107
                'Access levels of included method and properties [options: public, protected, private].',
108
                ['public', 'protected']
109
            )
110
            ->addOption(
111
                'annotationGroups',
112
                null,
113
                InputOption::VALUE_REQUIRED,
114
                'Generate page with elements with specific annotation.'
115
            )
116
            ->addOption(
117
                'config',
118
                null,
119
                InputOption::VALUE_REQUIRED,
120
                'Custom path to apigen.neon config file.',
121
                getcwd() . '/apigen.neon'
122
            )
123
            ->addOption(
124
                'googleCseId',
125
                null,
126
                InputOption::VALUE_REQUIRED,
127
                'Custom google search engine id (for search box).'
128
            )
129
            ->addOption(
130
                'baseUrl',
131
                null,
132
                InputOption::VALUE_REQUIRED,
133
                'Base url used for sitemap (for search box).'
134
            )
135
            ->addOption('googleAnalytics', null, InputOption::VALUE_REQUIRED, 'Google Analytics tracking code.')
136
            ->addOption('debug', null, InputOption::VALUE_NONE, 'Turn on debug mode.')
137
            ->addOption(
138
                'download',
139
                null,
140
                InputOption::VALUE_NONE,
141
                'Add link to ZIP archive of documentation.'
142
            )
143
            ->addOption(
144
                'extensions',
145
                null,
146
                InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED,
147
                'Scanned file extensions.',
148
                ['php']
149
            )
150
            ->addOption(
151
                'exclude',
152
                null,
153
                InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED,
154
                'Directories and files matching this mask will not be parsed (e.g. */tests/*).'
155
            )
156
            ->addOption(
157
                'groups',
158
                null,
159
                InputOption::VALUE_REQUIRED,
160
                'The way elements are grouped in menu [options: namespaces, packages].',
161
                'namespaces'
162
            )
163
            ->addOption(
164
                'main',
165
                null,
166
                InputOption::VALUE_REQUIRED,
167
                'Elements with this name prefix will be first in tree.'
168
            )
169
            ->addOption('internal', null, InputOption::VALUE_NONE, 'Include elements marked as @internal.')
170
            ->addOption('php', null, InputOption::VALUE_NONE, 'Generate documentation for PHP internal classes.')
171
            ->addOption(
172
                'noSourceCode',
173
                null,
174
                InputOption::VALUE_NONE,
175
                'Do not generate highlighted source code for elements.'
176
            )
177
            ->addOption('templateTheme', null, InputOption::VALUE_REQUIRED, 'ApiGen template theme name.', 'default')
178
            ->addOption(
179
                'templateConfig',
180
                null,
181
                InputOption::VALUE_REQUIRED,
182
                'Your own template config, has higher priority then --template-theme.'
183
            )
184
            ->addOption('title', null, InputOption::VALUE_REQUIRED, 'Title of generated documentation.')
185
            ->addOption(
186
                'tree',
187
                null,
188
                InputOption::VALUE_NONE,
189
                'Generate tree view of classes, interfaces, traits and exceptions.'
190
            )
191
192
            /**
193
             * @deprecated since version 4.2, to be removed in 5.0
194
             */
195
            ->addOption(
196
                'deprecated',
197
                null,
198
                InputOption::VALUE_NONE,
199
                'Generate documentation for elements marked as @deprecated (deprecated, only present for BC).'
200
            )
201
            ->addOption(
202
                'todo',
203
                null,
204
                InputOption::VALUE_NONE,
205
                'Generate documentation for elements marked as @todo (deprecated, only present for BC).'
206
            )
207
            ->addOption(
208
                'charset',
209
                null,
210
                InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED,
211
                'Charset of scanned files (deprecated, only present for BC).'
212
            )
213
            ->addOption(
214
                'skip-doc-path',
215
                null,
216
                InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED,
217
                'Files matching this mask will be included in class tree,'
218
                . ' but will not create a link to their documentation (deprecated, only present for BC).'
219
            )
220
            ->addOption(
221
                'overwrite',
222
                'o',
223
                InputOption::VALUE_NONE,
224
                'Force overwrite destination directory'
225
            );
226
    }
227
228
229
    protected function execute(InputInterface $input, OutputInterface $output)
230
    {
231
        try {
232
            $options = $this->prepareOptions($input->getOptions());
233
            $this->scanAndParse($options);
234
            $this->generate($options);
235
            return 0;
236
        } catch (\Exception $e) {
237
            $output->writeln(
238
                sprintf(PHP_EOL . '<error>%s</error>', $e->getMessage())
239
            );
240
            return 1;
241
        }
242
    }
243
244
245
    private function scanAndParse(array $options)
246
    {
247
        $this->io->writeln('<info>Scanning sources and parsing</info>');
248
249
        $files = $this->finder->find($options['source'], $options['exclude'], $options['extensions']);
250
        $this->parser->parse($files);
0 ignored issues
show
$files is of type object<SplFileInfo>, but the function expects a array<integer,object<SplFileInfo>>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
251
252
        $this->reportParserErrors($this->parser->getErrors());
253
254
        $stats = $this->parserResult->getDocumentedStats();
255
        $this->io->writeln(sprintf(
256
            'Found <comment>%d classes</comment>, <comment>%d constants</comment> and <comment>%d functions</comment>',
257
            $stats['classes'],
258
            $stats['constants'],
259
            $stats['functions']
260
        ));
261
    }
262
263
264
    private function generate(array $options)
265
    {
266
        $this->prepareDestination($options['destination'], $options['overwrite']);
267
        $this->io->writeln('<info>Generating API documentation</info>');
268
        $this->generatorQueue->run();
269
    }
270
271
272
    private function reportParserErrors(array $errors)
273
    {
274
        /** @var FileProcessingException[] $errors */
275
        foreach ($errors as $error) {
276
            $output = null;
277
            if ($this->configuration->getOption('debug')) {
278
                $output = $error->getDetail();
279
            } else {
280
                /** @var \Exception[] $reasons */
281
                $reasons = $error->getReasons();
282
                if (isset($reasons[0]) && count($reasons)) {
283
                    $output = $reasons[0]->getMessage();
284
                }
285
            }
286
            if ($output) {
287
                $this->io->writeln(sprintf('<error>Parse error: "%s"</error>', $output));
288
            }
289
        }
290
    }
291
292
293
    /**
294
     * @return array
295
     */
296
    private function prepareOptions(array $cliOptions)
297
    {
298
        $options = $this->convertDashKeysToCamel($cliOptions);
299
        $options = $this->loadOptionsFromConfig($options);
300
301
        $this->warnAboutDeprecatedOptions($options);
0 ignored issues
show
Deprecated Code introduced by
The method ApiGen\Console\Command\G...boutDeprecatedOptions() has been deprecated with message: since version 4.2, to be removed in 5.0

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
302
        $options = $this->unsetDeprecatedOptions($options);
0 ignored issues
show
Deprecated Code introduced by
The method ApiGen\Console\Command\G...nsetDeprecatedOptions() has been deprecated with message: since version 4.2, to be removed in 5.0

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
303
304
        return $this->configuration->resolveOptions($options);
305
    }
306
307
308
    /**
309
     * @return array
310
     */
311
    private function convertDashKeysToCamel(array $options)
312
    {
313
        foreach ($options as $key => $value) {
314
            $camelKey = $this->camelFormat($key);
315
            if ($key !== $camelKey) {
316
                $options[$camelKey] = $value;
317
                unset($options[$key]);
318
            }
319
        }
320
        return $options;
321
    }
322
323
324
    /**
325
     * @param string $name
326
     * @return string
327
     */
328
    private function camelFormat($name)
329
    {
330
        return preg_replace_callback('~-([a-z])~', function ($matches) {
331
            return strtoupper($matches[1]);
332
        }, $name);
333
    }
334
335
336
    /**
337
     * @return array
338
     */
339
    private function loadOptionsFromConfig(array $options)
340
    {
341
        $configFilePaths = [
342
            $options['config'],
343
            getcwd() . '/apigen.neon',
344
            getcwd() . '/apigen.yaml',
345
            getcwd() . '/apigen.neon.dist',
346
            getcwd() . '/apigen.yaml.dist'
347
        ];
348
349
        foreach ($configFilePaths as $configFile) {
350
            if (file_exists($configFile)) {
351
                $configFileOptions = ReaderFactory::getReader($configFile)->read();
352
                $options = array_merge($options, $configFileOptions);
353
                break;
354
            }
355
        }
356
        return $options;
357
    }
358
359
360
    /**
361
     * @param string $destination
362
     */
363
    private function prepareDestination($destination, $allowOverwrite = false)
364
    {
365
        if (!$allowOverwrite) {
366
            $this->cleanDestinationWithCaution($destination);
367
        }
368
        $this->themeResources->copyToDestination($destination);
369
    }
370
371
372
    /**
373
     * @param string $destination
374
     */
375
    private function cleanDestinationWithCaution($destination)
376
    {
377
        if (! $this->fileSystem->isDirEmpty($destination)) {
378
            if ($this->io->ask('<warning>Destination is not empty. Do you want to erase it?</warning>', true)) {
0 ignored issues
show
true is of type boolean, but the function expects a null|string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
379
                $this->fileSystem->purgeDir($destination);
380
            }
381
        }
382
    }
383
384
385
    /**
386
     * @deprecated since version 4.2, to be removed in 5.0
387
     */
388
    private function warnAboutDeprecatedOptions(array $options)
389
    {
390
        if (isset($options['charset']) && $options['charset']) {
391
            $this->io->writeln(
392
                '<warning>You are using the deprecated option "charset". ' .
393
                'UTF-8 is default now.</warning>'
394
            );
395
        }
396
397
        if (isset($options['deprecated']) && $options['deprecated']) {
398
            $this->io->writeln(
399
                '<warning>You are using the deprecated option "deprecated". ' .
400
                'Use "--annotation-groups=deprecated" instead</warning>'
401
            );
402
        }
403
404
        if (isset($options['todo']) && $options['todo']) {
405
            $this->io->writeln(
406
                '<warning>You are using the deprecated option "todo". Use "--annotation-groups=todo" instead</warning>'
407
            );
408
        }
409
410
        if (isset($options['skipDocPath']) && $options['skipDocPath']) {
411
            $this->io->writeln(
412
                '<warning>You are using the deprecated option "skipDocPath". Use "exclude" instead.</warning>'
413
            );
414
        }
415
    }
416
417
418
    /**
419
     * @deprecated since version 4.2, to be removed in 5.0
420
     *
421
     * @return array
422
     */
423
    private function unsetDeprecatedOptions(array $options)
424
    {
425
        unset($options['charset'], $options['skipDocPath']);
426
        return $options;
427
    }
428
}
429