Completed
Push — develop ( fbdd82...317691 )
by Mike
09:29
created

RunCommand   B

Complexity

Total Complexity 12

Size/Duplication

Total Lines 372
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 17

Importance

Changes 3
Bugs 0 Features 0
Metric Value
wmc 12
c 3
b 0
f 0
lcom 1
cbo 17
dl 0
loc 372
rs 7.8571

9 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 19 1
B configure() 0 153 1
B execute() 0 36 2
B attachListeners() 0 37 1
A getDestination() 0 6 1
A getExpandedDestination() 0 6 2
A getTemplates() 0 6 1
A parse() 0 14 2
A render() 0 4 1
1
<?php
2
/**
3
 * This file is part of phpDocumentor.
4
 *
5
 * For the full copyright and license information, please view the LICENSE
6
 * file that was distributed with this source code.
7
 *
8
 * @copyright 2010-2015 Mike van Riel<[email protected]>
9
 * @license   http://www.opensource.org/licenses/mit-license.php MIT
10
 * @link      http://phpdoc.org
11
 */
12
13
namespace phpDocumentor\Application\Console\Command;
14
15
use League\Event\Emitter;
16
use League\Tactician\CommandBus;
17
use phpDocumentor\Application\Configuration\ConfigurationFactory;
18
use phpDocumentor\DomainModel\Dsn;
19
use phpDocumentor\DomainModel\Parser\DocumentationRepository;
20
use phpDocumentor\Infrastructure\FileSystemFactory;
21
use phpDocumentor\Infrastructure\Parser\Documentation\Api\FlySystemDefinition;
22
use phpDocumentor\DomainModel\Parser\ApiFileParsed;
23
use phpDocumentor\DomainModel\Parser\ApiParsingStarted;
24
use phpDocumentor\Application\ConfigureCache;
25
use phpDocumentor\Application\MergeConfigurationWithCommandLineOptions;
26
use phpDocumentor\Application\Render;
27
use phpDocumentor\DomainModel\Parser\DocumentationFactory;
28
use phpDocumentor\DomainModel\Parser\Version\DefinitionRepository;
29
use phpDocumentor\DomainModel\Renderer\RenderActionCompleted;
30
use phpDocumentor\DomainModel\Renderer\RenderingFinished;
31
use phpDocumentor\DomainModel\Renderer\RenderingStarted;
32
use Stash\Driver\FileSystem;
33
use Symfony\Component\Console\Command\Command;
34
use Symfony\Component\Console\Helper\HelperInterface;
35
use Symfony\Component\Console\Helper\ProgressBar;
36
use Symfony\Component\Console\Helper\ProgressHelper;
37
use Symfony\Component\Console\Input\ArrayInput;
38
use Symfony\Component\Console\Input\InputArgument;
39
use Symfony\Component\Console\Input\InputInterface;
40
use Symfony\Component\Console\Input\InputOption;
41
use Symfony\Component\Console\Output\OutputInterface;
42
43
/**
44
 * Parse and transform the given directory (-d|-f) to the given location (-t).
45
 *
46
 * phpDocumentor creates documentation from PHP source files. The simplest way
47
 * to use it is:
48
 *
49
 *     $ phpdoc run -d <directory to parse> -t <output directory>
50
 *
51
 * This will parse every file ending with .php, .php3 and .phtml in <directory
52
 * to parse> and then output a HTML site containing easily readable documentation
53
 * in <output directory>.
54
 *
55
 * phpDocumentor will try to look for a phpdoc.dist.xml or phpdoc.xml file in your
56
 * current working directory and use that to override the default settings if
57
 * present. In the configuration file can you specify the same settings (and
58
 * more) as the command line provides.
59
 */
60
final class RunCommand extends Command
0 ignored issues
show
Complexity introduced by
The class RunCommand has a coupling between objects value of 27. Consider to reduce the number of dependencies under 13.
Loading history...
61
{
62
    /** @var CommandBus */
63
    private $commandBus;
64
65
    /** @var Emitter */
66
    private $emitter;
67
68
    /** @var DefinitionRepository */
69
    private $definitionRepository;
70
71
    /** @var DocumentationFactory */
72
    private $documentationFactory;
73
74
    /** @var DocumentationRepository */
75
    private $documentationRepository;
76
77
    /** @var ConfigurationFactory */
78
    private $configurationFactory;
79
80
    /** @var FileSystemFactory */
81
    private $fileSystemFactory;
82
83
    /**
84
     * Initializes the command with all necessary dependencies
85
     *
86
     * @param DefinitionRepository $definitionRepository
87
     * @param DocumentationRepository $documentationRepository
88
     * @param DocumentationFactory $documentationFactory
89
     * @param CommandBus $commandBus
90
     * @param Emitter $emitter
91
     * @param ConfigurationFactory $configurationFactory
92
     * @param FileSystemFactory $fileSystemFactory
93
     */
94
    public function __construct(
95
        DefinitionRepository $definitionRepository,
96
        DocumentationRepository $documentationRepository,
0 ignored issues
show
Comprehensibility Naming introduced by
The variable name $documentationRepository exceeds the maximum configured length of 20.

Very long variable names usually make code harder to read. It is therefore recommended not to make variable names too verbose.

Loading history...
97
        DocumentationFactory $documentationFactory,
98
        CommandBus $commandBus,
99
        Emitter $emitter,
100
        ConfigurationFactory $configurationFactory,
101
        FileSystemFactory $fileSystemFactory
102
    ) {
103
        $this->commandBus                   = $commandBus;
104
        $this->emitter                      = $emitter;
105
        $this->definitionRepository         = $definitionRepository;
106
        $this->documentationFactory         = $documentationFactory;
107
        $this->documentationRepository      = $documentationRepository;
108
109
        parent::__construct('project:run');
110
        $this->configurationFactory = $configurationFactory;
111
        $this->fileSystemFactory = $fileSystemFactory;
112
    }
113
114
    /**
115
     * Initializes this command and sets the name, description, options and
116
     * arguments.
117
     *
118
     * @return void
119
     */
120
    protected function configure()
121
    {
122
        $this
123
            ->setAliases(array('run'))
124
            ->setDescription(
125
                'Parses and transforms the given files to a specified location'
126
            )
127
            ->setHelp(
128
                <<<HELP
129
phpDocumentor creates documentation from PHP source files. The simplest way
130
to use it is:
131
132
    <info>$ phpdoc run -d [directory to parse] -t [output directory]</info>
133
134
This will parse every file ending with .php, .php3 and .phtml in <directory
135
to parse> and then output a HTML site containing easily readable documentation
136
in <output directory>.
137
138
phpDocumentor will try to look for a phpdoc.dist.xml or phpdoc.xml file in your
139
current working directory and use that to override the default settings if
140
present. In the configuration file can you specify the same settings (and
141
more) as the command line provides.
142
143
<comment>Other commands</comment>
144
In addition to this command phpDocumentor also supports additional commands:
145
146
<comment>Available commands:</comment>
147
<info>  help
148
  list
149
  parse
150
  run
151
  transform
152
<comment>project</comment>
153
  project:parse
154
  project:run
155
  project:transform
156
<comment>template</comment>
157
  template:generate
158
  template:list
159
  template:package</info>
160
161
You can get a more detailed listing of the commands using the <info>list</info>
162
command and get help by prepending the word <info>help</info> to the command
163
name.
164
HELP
165
            )
166
            ->addOption(
167
                'target',
168
                't',
169
                InputOption::VALUE_OPTIONAL,
170
                'Path where to store the generated output'
171
            )
172
            ->addOption(
173
                'cache-folder',
174
                null,
175
                InputOption::VALUE_OPTIONAL,
176
                'Path where to store the cache files'
177
            )
178
            ->addOption(
179
                'filename',
180
                'f',
181
                InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY,
182
                'Comma-separated list of files to parse. The wildcards ? and * are supported'
183
            )
184
            ->addOption(
185
                'directory',
186
                'd',
187
                InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY,
188
                'Comma-separated list of directories to (recursively) parse'
189
            )
190
            ->addOption(
191
                'encoding',
192
                null,
193
                InputOption::VALUE_OPTIONAL,
194
                'encoding to be used to interpret source files with'
195
            )
196
            ->addOption(
197
                'extensions',
198
                'e',
199
                InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY,
200
                'Comma-separated list of extensions to parse, defaults to php, php3 and phtml'
201
            )
202
            ->addOption(
203
                'ignore',
204
                'i',
205
                InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY,
206
                'Comma-separated list of file(s) and directories (relative to the source-code directory) that will be '
207
                . 'ignored. Wildcards * and ? are supported'
208
            )
209
            ->addOption(
210
                'markers',
211
                'm',
212
                InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY,
213
                'Comma-separated list of markers/tags to filter'
214
            )
215
            ->addOption(
216
                'title',
217
                null,
218
                InputOption::VALUE_OPTIONAL,
219
                'Sets the title for this project; default is the phpDocumentor logo'
220
            )
221
            ->addOption(
222
                'force',
223
                null,
224
                InputOption::VALUE_NONE,
225
                'Forces a full build of the documentation, does not increment existing documentation'
226
            )
227
            ->addOption(
228
                'visibility',
229
                null,
230
                InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY,
231
                'Specifies the parse visibility that should be displayed in the documentation (comma separated e.g. '
232
                . '"public,protected")'
233
            )
234
            ->addOption(
235
                'defaultpackagename',
236
                null,
237
                InputOption::VALUE_OPTIONAL,
238
                'Name to use for the default package.'
239
            )
240
            ->addOption(
241
                'sourcecode',
242
                null,
243
                InputOption::VALUE_NONE,
244
                'Whether to include syntax highlighted source code'
245
            )
246
            ->addOption(
247
                'progressbar',
248
                'p',
249
                InputOption::VALUE_NONE,
250
                'Whether to show a progress bar; will automatically quiet logging to stdout'
251
            )
252
            ->addOption(
253
                'template',
254
                null,
255
                InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY,
256
                'Name of the template to use (optional)'
257
            )
258
            ->addOption(
259
                'parseprivate',
260
                null,
261
                InputOption::VALUE_NONE,
262
                'Whether to parse DocBlocks marked with @internal tag'
263
            )
264
            ->addArgument(
265
                'paths',
266
                InputArgument::OPTIONAL | InputArgument::IS_ARRAY,
267
                'One or more files and folders to process',
268
                array()
269
            );
270
271
        parent::configure();
272
    }
273
274
    /**
275
     * Executes the business logic involved with this command.
276
     *
277
     * @param InputInterface $input
278
     * @param OutputInterface $output
279
     *
280
     * @return int
281
     */
282
    protected function execute(InputInterface $input, OutputInterface $output)
283
    {
284
        $output->writeln(
285
            sprintf(
286
                '<info>%s</info> version <comment>%s</comment>' . PHP_EOL,
287
                $this->getApplication()->getName(),
288
                $this->getApplication()->getVersion()
289
            )
290
        );
291
        $this->attachListeners($input, $output);
292
293
        $this->commandBus->handle(
294
            new MergeConfigurationWithCommandLineOptions($input->getOptions(), $input->getArguments())
295
        );
296
        $config = $this->configurationFactory->get();
297
        $this->commandBus->handle(
298
            new ConfigureCache($config['phpdocumentor']['paths']['cache'], $config['phpdocumentor']['use-cache'])
299
        );
300
301
        $destination = $this->getDestination();
302
        $destinationFilesystem = $this->fileSystemFactory->create($destination);
0 ignored issues
show
Comprehensibility Naming introduced by
The variable name $destinationFilesystem exceeds the maximum configured length of 20.

Very long variable names usually make code harder to read. It is therefore recommended not to make variable names too verbose.

Loading history...
303
        $templates = $this->getTemplates();
304
305
        foreach ($this->definitionRepository->fetchAll() as $definition) {
306
            $this->render(
307
                $this->parse($definition),
308
                $destinationFilesystem,
309
                $templates
310
            );
311
        }
312
313
        $expandedDestination = $this->getExpandedDestination($destination);
314
        $output->writeln(sprintf(PHP_EOL . '<fg=black;bg=green>OK (%s)</>', $expandedDestination));
315
316
        return 0;
317
    }
318
319
    /**
320
     * Connect a series of output messages to various events to display progress.
321
     *
322
     * @param InputInterface  $input
323
     * @param OutputInterface $output
324
     *
325
     * @return void
326
     */
327
    private function attachListeners(InputInterface $input, OutputInterface $output)
0 ignored issues
show
Unused Code introduced by
The parameter $input is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
328
    {
329
        $this->emitter->addListener(
330
            ApiParsingStarted::class,
331
            function (ApiParsingStarted $event) use ($output) {
332
                /** @var FlySystemDefinition $definition */
333
                $definition = $event->definition();
334
                $output->writeln(
335
                    sprintf('Parsing <info>%d</info> files', count($definition->getFiles()))
336
                );
337
            }
338
        );
339
        $this->emitter->addListener(
340
            ApiFileParsed::class,
341
            function (ApiFileParsed $event) use ($output) {
342
                $output->writeln(sprintf('  Parsed <info>%s</info>', (string)$event->filename()));
343
            }
344
        );
345
        $this->emitter->addListener(
346
            RenderingStarted::class,
347
            function (RenderingStarted $event) use ($output) {
0 ignored issues
show
Unused Code introduced by
The parameter $event is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
348
                $output->writeln('Started rendering');
349
            }
350
        );
351
        $this->emitter->addListener(
352
            RenderingFinished::class,
353
            function (RenderingFinished $event) use ($output) {
0 ignored issues
show
Unused Code introduced by
The parameter $event is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
354
                $output->writeln('Completed rendering');
355
            }
356
        );
357
        $this->emitter->addListener(
358
            RenderActionCompleted::class,
359
            function (RenderActionCompleted $event) use ($output) {
360
                $output->writeln(sprintf('  %s', (string)$event->action()));
361
            }
362
        );
363
    }
364
365
    /**
366
     * @return Dsn
367
     */
368
    private function getDestination()
369
    {
370
        $config = $this->configurationFactory->get();
371
372
        return $config['phpdocumentor']['paths']['output'];
373
    }
374
375
    /**
376
     * Returns the destination as a string and expands it to show the absolute path if the destination is on disk.
377
     *
378
     * Because the destination location is created during rendering we can only expand the path using realpath
379
     * because realpath will return false on a non-existent location.
380
     *
381
     * @param Dsn $destination
382
     *
383
     * @return string
384
     */
385
    private function getExpandedDestination($destination)
386
    {
387
        return $destination->getScheme() == 'file'
388
            ? realpath($destination->getPath())
389
            : $destination;
390
    }
391
392
    /**
393
     * @return mixed
394
     */
395
    private function getTemplates()
396
    {
397
        $config = $this->configurationFactory->get();
398
399
        return $config['phpdocumentor']['templates'];
400
    }
401
402
    /**
403
     * @param $definition
404
     *
405
     * @return null|\phpDocumentor\DomainModel\Parser\Documentation
406
     */
407
    private function parse($definition)
408
    {
409
        $documentation = $this->documentationRepository->findByVersionNumber($definition->getVersionNumber());
410
411
        // TODO: does this mean that if a documentation comes from cache it is never updated?
412
        if ($documentation === null) {
413
            $documentation = $this->documentationFactory->create($definition);
414
            $this->documentationRepository->save($documentation);
415
416
            return $documentation;
417
        }
418
419
        return $documentation;
420
    }
421
422
    /**
423
     * @param $documentation
424
     * @param $destinationFilesystem
425
     * @param $templates
426
     */
427
    private function render($documentation, $destinationFilesystem, $templates)
0 ignored issues
show
Comprehensibility Naming introduced by
The variable name $destinationFilesystem exceeds the maximum configured length of 20.

Very long variable names usually make code harder to read. It is therefore recommended not to make variable names too verbose.

Loading history...
428
    {
429
        $this->commandBus->handle(new Render($documentation, $destinationFilesystem, $templates));
430
    }
431
}
432