Completed
Push — develop ( 4b49c4...89d32a )
by Jaap
09:06 queued 05:30
created

Parser/Command/Project/ParseCommand.php (1 issue)

Checks multi line function declaration space after function

Coding Style Informational

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
 * phpDocumentor
4
 *
5
 * PHP Version 5.3
6
 *
7
 * @copyright 2010-2014 Mike van Riel / Naenius (http://www.naenius.com)
8
 * @license   http://www.opensource.org/licenses/mit-license.php MIT
9
 * @link      http://phpdoc.org
10
 */
11
12
namespace phpDocumentor\Parser\Command\Project;
13
14
use League\Flysystem\MountManager;
15
use phpDocumentor\Command\Command;
16
use phpDocumentor\Command\Helper\ConfigurationHelper;
17
use phpDocumentor\Descriptor\Cache\ProjectDescriptorMapper;
18
use phpDocumentor\Descriptor\Example\Finder;
19
use phpDocumentor\Descriptor\ProjectDescriptor;
20
use phpDocumentor\Descriptor\ProjectDescriptorBuilder;
21
use phpDocumentor\DomainModel\Dsn;
22
use phpDocumentor\DomainModel\Parser\FileCollector;
23
use phpDocumentor\Fileset\Collection;
24
use phpDocumentor\Infrastructure\FlySystemFactory;
25
use phpDocumentor\Infrastructure\Parser\FlySystemFile;
26
use phpDocumentor\Infrastructure\Parser\SpecificationFactory;
27
use phpDocumentor\Parser\Event\PreFileEvent;
28
use phpDocumentor\Parser\Exception\FilesNotFoundException;
29
use phpDocumentor\Parser\Middleware\CacheMiddleware;
30
use phpDocumentor\Parser\Parser;
31
use phpDocumentor\Parser\Util\ParserPopulator;
32
use phpDocumentor\Partials\Collection as PartialsCollection;
33
use phpDocumentor\Reflection\DocBlock\ExampleFinder;
34
use phpDocumentor\Reflection\File;
35
use Symfony\Component\Console\Helper\ProgressHelper;
36
use Symfony\Component\Console\Input\InputInterface;
37
use Symfony\Component\Console\Input\InputOption;
38
use Symfony\Component\Console\Output\OutputInterface;
39
use Symfony\Component\Filesystem\Filesystem;
40
use Zend\Cache\Storage\StorageInterface;
41
use Zend\I18n\Translator\Translator;
42
43
/**
44
 * Parses the given source code and creates a structure file.
45
 *
46
 * The parse task uses the source files defined either by -f or -d options and
47
 * generates a structure file (structure.xml) at the target location (which is
48
 * the folder 'output' unless the option -t is provided).
49
 */
50
class ParseCommand extends Command
51
{
52
    /** @var ProjectDescriptorBuilder $builder */
53
    protected $builder;
54
55
    /** @var Parser $parser */
56
    protected $parser;
57
58
    /** @var Translator */
59
    protected $translator;
60
61
    /** @var StorageInterface */
62
    private $cache;
63
64
    /**
65
     * @var ExampleFinder
66
     */
67
    private $exampleFinder;
68
    /**
69
     * @var PartialsCollection
70
     */
71
    private $partials;
72
    /**
73
     * @var FileCollector
74
     */
75
    private $fileCollector;
76
77
    /**
78
     * ParseCommand constructor.
79
     * @param ProjectDescriptorBuilder $builder
80
     * @param Parser $parser
81
     * @param FileCollector $fileCollector
82
     * @param Translator $translator
83
     * @param StorageInterface $cache
84
     * @param ExampleFinder $exampleFinder
85
     * @param PartialsCollection $partials
86
     */
87
    public function __construct(
88
        ProjectDescriptorBuilder $builder,
89
        Parser $parser,
90
        FileCollector $fileCollector,
91
        Translator $translator,
92
        StorageInterface $cache,
93
        ExampleFinder $exampleFinder,
94
        PartialsCollection $partials
95
    ) {
96
        $this->builder = $builder;
97
        $this->parser = $parser;
98
        $this->translator = $translator;
99
        $this->cache = $cache;
100
        $this->exampleFinder = $exampleFinder;
101
        $this->partials = $partials;
102
        $this->fileCollector = $fileCollector;
103
104
        parent::__construct('project:parse');
105
    }
106
107
    /**
108
     * @return ProjectDescriptorBuilder
109
     */
110
    public function getBuilder(): ProjectDescriptorBuilder
111
    {
112
        return $this->builder;
113
    }
114
115
    /**
116
     * @return \phpDocumentor\Parser\Parser
117
     */
118
    public function getParser(): Parser
119
    {
120
        return $this->parser;
121
    }
122
123
    /**
124
     * Returns the Cache.
125
     *
126
     * @return StorageInterface
127
     * @throws \InvalidArgumentException
128
     */
129 1
    protected function getCache(): StorageInterface
130
    {
131 1
        return $this->cache;
132
    }
133
134
    /**
135
     * Initializes this command and sets the name, description, options and
136
     * arguments.
137
     *
138
     * @return void
139
     */
140 1
    protected function configure()
141
    {
142
        // minimization of the following expression
143 1
        $VALUE_OPTIONAL_ARRAY = InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY;
144
145 1
        $this->setAliases(array('parse'))
146 1
            ->setDescription($this->__('PPCPP-DESCRIPTION'))
147 1
            ->setHelp($this->__('PPCPP-HELPTEXT'))
148 1
            ->addOption('filename', 'f', $VALUE_OPTIONAL_ARRAY, $this->__('PPCPP:OPT-FILENAME'))
149 1
            ->addOption('directory', 'd', $VALUE_OPTIONAL_ARRAY, $this->__('PPCPP:OPT-DIRECTORY'))
150 1
            ->addOption('target', 't', InputOption::VALUE_OPTIONAL, $this->__('PPCPP:OPT-TARGET'))
151 1
            ->addOption('encoding', null, InputOption::VALUE_OPTIONAL, $this->__('PPCPP:OPT-ENCODING'))
152 1
            ->addOption('extensions', 'e', $VALUE_OPTIONAL_ARRAY, $this->__('PPCPP:OPT-EXTENSIONS'))
153 1
            ->addOption('ignore', 'i', $VALUE_OPTIONAL_ARRAY, $this->__('PPCPP:OPT-IGNORE'))
154 1
            ->addOption('ignore-tags', null, $VALUE_OPTIONAL_ARRAY, $this->__('PPCPP:OPT-IGNORETAGS'))
155 1
            ->addOption('hidden', null, InputOption::VALUE_NONE, $this->__('PPCPP:OPT-HIDDEN'))
156 1
            ->addOption('ignore-symlinks', null, InputOption::VALUE_NONE, $this->__('PPCPP:OPT-IGNORESYMLINKS'))
157 1
            ->addOption('markers', 'm', $VALUE_OPTIONAL_ARRAY, $this->__('PPCPP:OPT-MARKERS'), array('TODO', 'FIXME'))
158 1
            ->addOption('title', null, InputOption::VALUE_OPTIONAL, $this->__('PPCPP:OPT-TITLE'))
159 1
            ->addOption('force', null, InputOption::VALUE_NONE, $this->__('PPCPP:OPT-FORCE'))
160 1
            ->addOption('validate', null, InputOption::VALUE_NONE, $this->__('PPCPP:OPT-VALIDATE'))
161 1
            ->addOption('visibility', null, $VALUE_OPTIONAL_ARRAY, $this->__('PPCPP:OPT-VISIBILITY'))
162 1
            ->addOption('sourcecode', null, InputOption::VALUE_NONE, $this->__('PPCPP:OPT-SOURCECODE'))
163 1
            ->addOption('progressbar', 'p', InputOption::VALUE_NONE, $this->__('PPCPP:OPT-PROGRESSBAR'))
164 1
            ->addOption('parseprivate', null, InputOption::VALUE_NONE, 'PPCPP:OPT-PARSEPRIVATE')
165 1
            ->addOption(
166 1
                'defaultpackagename',
167 1
                null,
168 1
                InputOption::VALUE_OPTIONAL,
169 1
                $this->__('PPCPP:OPT-DEFAULTPACKAGENAME'),
170 1
                'Default'
171
            );
172
173 1
        parent::configure();
174 1
    }
175
176
    /**
177
     * Executes the business logic involved with this command.
178
     *
179
     * @param InputInterface $input
180
     * @param OutputInterface $output
181
     *
182
     * @throws \Exception if the target location is not a folder.
183
     *
184
     * @return integer
185
     */
186 1
    protected function execute(InputInterface $input, OutputInterface $output): int
187
    {
188
        /** @var ConfigurationHelper $configurationHelper */
189 1
        $configurationHelper = $this->getHelper('phpdocumentor_configuration');
190 1
        $target = $configurationHelper->getOption($input, 'target', 'parser/target');
191 1
        if (strpos($target, '/tmp/') === 0) {
192
            $target = str_replace('/tmp/', sys_get_temp_dir() . DIRECTORY_SEPARATOR, $target);
193
        }
194
195 1
        $fileSystem = new Filesystem();
196 1
        if (!$fileSystem->isAbsolutePath($target)) {
197 1
            $target = getcwd() . DIRECTORY_SEPARATOR . $target;
198
        }
199 1
        if (!file_exists($target)) {
200
            mkdir($target, 0777, true);
201
        }
202 1
        if (!is_dir($target)) {
203
            throw new \Exception($this->__('PPCPP:EXC-BADTARGET'));
204
        }
205 1
        $this->getCache()->getOptions()->setCacheDir($target);
206
207 1
        if ($input->getOption('force')) {
208
            $this->getCacheMiddleware()->disable();
209
        }
210
211 1
        $builder = $this->getBuilder();
212 1
        $builder->createProjectDescriptor();
213 1
        $projectDescriptor = $builder->getProjectDescriptor();
214
215 1
        $output->write($this->__('PPCPP:LOG-COLLECTING'));
216 1
        $files = $this->getFileCollection($input);
217
218
        //TODO: Should determine root based on filesystems. Could be an issue for multiple. Need some config update here.
219 1
        $this->exampleFinder->setSourceDirectory(getcwd());
220 1
        $this->exampleFinder->setExampleDirectories($configurationHelper->getConfigValueFromPath('files/examples'));
221 1
        $output->writeln($this->__('PPCPP:LOG-OK'));
222
223
        /** @var ProgressHelper $progress */
224 1
        $progress = $this->getProgressBar($input);
225 1
        if (!$progress) {
226 1
            $this->getHelper('phpdocumentor_logger')->connectOutputToLogging($output, $this);
227
        }
228
229 1
        $output->write($this->__('PPCPP:LOG-INITIALIZING'));
230 1
        $this->populateParser($input);
231
232 1
        if ($progress) {
233
            $progress->start($output, \count($files));
234
        }
235
236
        try {
237 1
            $output->writeln($this->__('PPCPP:LOG-OK'));
238 1
            $output->writeln($this->__('PPCPP:LOG-PARSING'));
239
240 1
            $mapper = new ProjectDescriptorMapper($this->getCache());
241
            //TODO: Re-enable garbage collection here.
242
            //$mapper->garbageCollect($files);
243 1
            $mapper->populate($projectDescriptor);
244
245 1
            $visibility = (array)$configurationHelper->getOption($input, 'visibility', 'parser/visibility');
246 1
            $visibilities = array();
247 1
            foreach ($visibility as $item) {
248 1
                $visibilities = $visibilities + explode(',', $item);
249
            }
250 1
            $visibility = null;
251 1
            foreach ($visibilities as $item) {
252
                switch ($item) {
253 1
                    case 'public':
254
                        $visibility |= ProjectDescriptor\Settings::VISIBILITY_PUBLIC;
255
                        break;
256 1
                    case 'protected':
257
                        $visibility |= ProjectDescriptor\Settings::VISIBILITY_PROTECTED;
258
                        break;
259 1
                    case 'private':
260
                        $visibility |= ProjectDescriptor\Settings::VISIBILITY_PRIVATE;
261 1
                        break;
262
                }
263
            }
264 1
            if ($visibility === null) {
265 1
                $visibility = ProjectDescriptor\Settings::VISIBILITY_DEFAULT;
266
            }
267 1
            if ($input->getOption('parseprivate')) {
268
                $visibility |= ProjectDescriptor\Settings::VISIBILITY_INTERNAL;
269
            }
270 1
            $projectDescriptor->getSettings()->setVisibility($visibility);
271 1
            $input->getOption('sourcecode')
272
                ? $projectDescriptor->getSettings()->includeSource()
273 1
                : $projectDescriptor->getSettings()->excludeSource();
274
275 1
            $this->getParser()->parse($builder, $files);
276
        } catch (FilesNotFoundException $e) {
277
            throw new \Exception($this->__('PPCPP:EXC-NOFILES'));
278
        } catch (\Exception $e) {
279
            throw new \Exception($e->getMessage(), 0, $e);
280
        }
281
282 1
        if ($progress) {
283
            $progress->finish();
284
        }
285
286 1
        $projectDescriptor->setPartials($this->partials);
287
288 1
        $output->write($this->__('PPCPP:LOG-STORECACHE', (array)$this->getCache()->getOptions()->getCacheDir()));
289 1
        $projectDescriptor->getSettings()->clearModifiedFlag();
290 1
        $mapper->save($projectDescriptor);
291
292 1
        $output->writeln($this->__('PPCPP:LOG-OK'));
293
294 1
        return 0;
295
    }
296
297
    /**
298
     * Returns the collection of files based on the input and configuration.
299
     *
300
     * @param InputInterface $input
301
     *
302
     * @return File[]
303
     * @throws \InvalidArgumentException
304
     */
305 1
    protected function getFileCollection($input): array
306
    {
307
        /** @var ConfigurationHelper $configurationHelper */
308 1
        $configurationHelper = $this->getHelper('phpdocumentor_configuration');
309
310 1
        $ignoreHidden = $configurationHelper->getOption($input, 'hidden', 'files/ignore-hidden', 'off');
311 1
        $file_options = (array)$configurationHelper->getOption($input, 'filename', 'files/files', array(), true);
312 1
        $directory_options = $configurationHelper->getOption($input, 'directory', 'files/directories', array(), true);
313
314
315 1
        $ignorePaths = array_map(
316 1
            function($value) {
0 ignored issues
show
Expected 1 space after FUNCTION keyword; 0 found
Loading history...
317
                if (substr($value, -1) === '*') {
318
                    return substr($value, 0, -1);
319
                }
320
321
                return $value;
322 1
            },
323 1
            $configurationHelper->getOption($input, 'ignore', 'files/ignore', array(), true)
324
        );
325
326
        //TODO: Fix this, should we support symlinks? Or just print an error here.
327 1
        if ($configurationHelper->getOption($input, 'ignore-symlinks', 'files/ignore-symlinks', 'off') == 'on') {
328
            echo "Symlinks are not supported";
329
        }
330
331 1
        $files = [];
332
333 1
        foreach ($file_options as $file) {
334
            $files[] = new File\LocalFile($file);
335
        }
336
337 1
        foreach (array_merge($directory_options) as $option) {
338
            $files = array_merge(
339
                $files,
340
                $this->fileCollector->getFiles(
341
                    new Dsn('file://' . realpath($option)),
342
                    array_merge($directory_options),
343
                    [
344
                        'paths' => $ignorePaths,
345
                        'hidden' => $ignoreHidden !== 'off' && $ignoreHidden === false
346
                    ],
347
                    $configurationHelper->getOption(
348
                        $input,
349
                        'extensions',
350
                        'parser/extensions',
351
                        array('php', 'php3', 'phtml'),
352
                        true
353
                    )
354
                )
355
            );
356
        }
357
358 1
        return $files;
359
    }
360
361
    /**
362
     * Adds the parser.file.pre event to the advance the progressbar.
363
     *
364
     * @param InputInterface $input
365
     *
366
     * @return \Symfony\Component\Console\Helper\HelperInterface|null
367
     */
368 1
    protected function getProgressBar(InputInterface $input)
369
    {
370 1
        $progress = parent::getProgressBar($input);
371 1
        if (!$progress) {
372 1
            return null;
373
        }
374
375
        $this->getService('event_dispatcher')->addListener(
376
            'parser.file.pre',
377
            function (PreFileEvent $event) use ($progress) {
378
                $progress->advance();
379
            }
380
        );
381
382
        return $progress;
383
    }
384
385
    /**
386
     * Translates the provided text and replaces any contained parameters using printf notation.
387
     *
388
     * @param string $text
389
     * @param string[] $parameters
390
     *
391
     * @return string
392
     */
393
    // @codingStandardsIgnoreStart
394 1
    protected function __($text, $parameters = array())
395
        // @codingStandardsIgnoreEnd
396
    {
397 1
        return vsprintf($this->translator->translate($text), $parameters);
398
    }
399
400
    /**
401
     * @param InputInterface $input
402
     */
403 1
    protected function populateParser(InputInterface $input)
404
    {
405
        /** @var ConfigurationHelper $configurationHelper */
406 1
        $configurationHelper = $this->getHelper('phpdocumentor_configuration');
407
408 1
        $title = (string)$configurationHelper->getOption($input, 'title', 'title');
409 1
        $this->getBuilder()->getProjectDescriptor()->setName($title ?: 'API Documentation');
410 1
        $parserPopulator = new ParserPopulator();
411 1
        $parserPopulator->populate(
412 1
            $this->getParser(),
413 1
            $input,
414 1
            $configurationHelper
415
        );
416 1
    }
417
418
    private function getCacheMiddleware(): CacheMiddleware
419
    {
420
        return $this->getContainer()->offsetGet('parser.middleware.cache');
421
    }
422
}
423