Completed
Push — develop ( 488cb7...121593 )
by Jaap
16s
created

src/phpDocumentor/Application/Stage/Parser.php (3 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
declare(strict_types=1);
3
4
/**
5
 * This file is part of phpDocumentor.
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 *
10
 * @author    Mike van Riel <[email protected]>
11
 * @copyright 2010-2018 Mike van Riel / Naenius (http://www.naenius.com)
12
 * @license   http://www.opensource.org/licenses/mit-license.php MIT
13
 * @link      http://phpdoc.org
14
 */
15
16
namespace phpDocumentor\Application\Stage;
17
18
use Exception;
19
use InvalidArgumentException;
20
use phpDocumentor\Descriptor\Cache\ProjectDescriptorMapper;
21
use phpDocumentor\Descriptor\Collection as PartialsCollection;
22
use phpDocumentor\Descriptor\ProjectDescriptor;
23
use phpDocumentor\Descriptor\ProjectDescriptorBuilder;
24
use phpDocumentor\DomainModel\Parser\FileCollector;
25
use phpDocumentor\Event\LogEvent;
26
use phpDocumentor\Parser\Parser as DocParser;
27
use phpDocumentor\Reflection\DocBlock\ExampleFinder;
28
use Psr\Log\LogLevel;
29
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
30
use Zend\Cache\Storage\StorageInterface;
31
32
/**
33
 * Parses the given source code and creates a structure file.
34
 *
35
 * The parse task uses the source files defined either by -f or -d options and
36
 * generates a structure file (structure.xml) at the target location (which is
37
 * the folder 'output' unless the option -t is provided).
38
 */
39
final class Parser
40
{
41
    /** @var ProjectDescriptorBuilder $builder */
42
    private $builder;
43
44
    /** @var DocParser $parser */
45
    private $parser;
46
47
    /** @var StorageInterface */
48
    private $cache;
49
50
    /**
51
     * @var ExampleFinder
52
     */
53
    private $exampleFinder;
54
55
    /**
56
     * @var PartialsCollection
57
     */
58
    private $partials;
59
60
    /**
61
     * @var FileCollector
62
     */
63
    private $fileCollector;
64
65
    /**
66
     * @var EventDispatcherInterface
67
     */
68
    private $eventDispatcher;
69
70
    /**
71
     * ParseCommand constructor.
72
     */
73
    public function __construct(
74
        ProjectDescriptorBuilder $builder,
75
        DocParser $parser,
76
        FileCollector $fileCollector,
77
        StorageInterface $cache,
78
        ExampleFinder $exampleFinder,
79
        PartialsCollection $partials,
80
        EventDispatcherInterface $eventDispatcher
81
    ) {
82
        $this->builder = $builder;
83
        $this->parser = $parser;
84
        $this->cache = $cache;
85
        $this->exampleFinder = $exampleFinder;
86
        $this->partials = $partials;
87
        $this->fileCollector = $fileCollector;
88
        $this->eventDispatcher = $eventDispatcher;
89
    }
90
91
    private function getBuilder(): ProjectDescriptorBuilder
92
    {
93
        return $this->builder;
94
    }
95
96
    private function getParser(): DocParser
97
    {
98
        return $this->parser;
99
    }
100
101
    /**
102
     * Returns the Cache.
103
     *
104
     * @throws InvalidArgumentException
105
     */
106
    private function getCache(): StorageInterface
107
    {
108
        return $this->cache;
109
    }
110
111
    /**
112
     * Executes the business logic involved with this command.
113
     *
114
     * @throws Exception if the target location is not a folder.
115
     */
116
    public function __invoke(array $configuration): array
117
    {
118
        //Grep only the first version for now. Multi version support will be added later
119
        $version = current($configuration['phpdocumentor']['versions']);
120
121
        //We are currently in the parser stage so grep the api config.
122
        //And for now we support a single api definition. Could be more in the future.
123
        $apiConfig = $version['api'][0];
124
125
        $parser = $this->getParser();
126
        $parser->setForced(!$configuration['phpdocumentor']['use-cache']);
127
        $parser->setEncoding($apiConfig['encoding']);
128
        $parser->setMarkers($apiConfig['markers']);
129
        $parser->setIgnoredTags($apiConfig['ignore-tags']);
130
        $parser->setValidate($apiConfig['validate']);
131
        $parser->setDefaultPackageName($apiConfig['default-package-name']);
132
133
        $builder = $this->getBuilder();
134
        $builder->createProjectDescriptor();
135
        $projectDescriptor = $builder->getProjectDescriptor();
136
        $projectDescriptor->setName($configuration['phpdocumentor']['title']);
137
        $projectDescriptor->setPartials($this->partials);
138
139
        $visibility = $this->getVisibility($apiConfig);
0 ignored issues
show
Are you sure the assignment to $visibility is correct as $this->getVisibility($apiConfig) (which targets phpDocumentor\Applicatio...Parser::getVisibility()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
140
        $projectDescriptor->getSettings()->setVisibility($visibility);
141
        $projectDescriptor->getSettings()->setMarkers($apiConfig['markers']);
142
        $projectDescriptor->getSettings()->includeSource();
143
144
        $mapper = new ProjectDescriptorMapper($this->getCache());
145
146
        if ($configuration['phpdocumentor']['use-cache']) {
147
            //TODO: Re-enable garbage collection here.
148
            //$mapper->garbageCollect($files);
0 ignored issues
show
Unused Code Comprehensibility introduced by
86% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
149
            $mapper->populate($projectDescriptor);
150
        }
151
152
        //TODO: Should determine root based on filesystems. Could be an issue for multiple.
153
        // Need some config update here.
154
        $this->exampleFinder->setSourceDirectory(getcwd());
155
        $this->exampleFinder->setExampleDirectories(['.']);
156
157
        $this->log('PPCPP:LOG-COLLECTING');
158
        $files = $this->getFileCollection($apiConfig);
159
        $this->log('PPCPP:LOG-OK');
160
        $this->log('PPCPP:LOG-INITIALIZING');
161
162
        $this->log('PPCPP:LOG-OK');
163
        $this->log('PPCPP:LOG-PARSING');
164
165
        $parser->parse($builder, $files);
166
167
        $this->log('PPCPP:LOG-STORECACHE', LogLevel::INFO);
168
        $projectDescriptor->getSettings()->clearModifiedFlag();
169
        $mapper->save($projectDescriptor);
170
        $this->log('PPCPP:LOG-OK');
171
172
        return $configuration;
173
    }
174
175
    /**
176
     * Returns the collection of files based on the input and configuration.
177
     */
178
    private function getFileCollection(array $apiConfig): array
179
    {
180
        $ignorePaths = array_map(
181
            function ($value) {
182
                if (substr((string) $value, -1) === '*') {
183
                    return substr($value, 0, -1);
184
                }
185
186
                return $value;
187
            },
188
            $apiConfig['ignore']['paths']
189
        );
190
191
        return $this->fileCollector->getFiles(
192
            $apiConfig['source']['dsn'],
193
            $apiConfig['source']['paths'],
194
            [
195
                'paths' => $ignorePaths,
196
                'hidden' => $apiConfig['ignore']['hidden'],
197
            ],
198
            $apiConfig['extensions']
199
        );
200
    }
201
202
    private function getVisibility(array $apiConfig): ?int
203
    {
204
        $visibilities = $apiConfig['visibility'];
205
        $visibility = null;
206
        foreach ($visibilities as $item) {
207
            switch ($item) {
208
                case 'public':
0 ignored issues
show
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
209
                    $visibility |= ProjectDescriptor\Settings::VISIBILITY_PUBLIC;
210
                    break;
211
                case 'protected':
212
                    $visibility |= ProjectDescriptor\Settings::VISIBILITY_PROTECTED;
213
                    break;
214
                case 'private':
215
                    $visibility |= ProjectDescriptor\Settings::VISIBILITY_PRIVATE;
216
                    break;
217
            }
218
        }
219
        return $visibility;
220
    }
221
222
    /**
223
     * Dispatches a logging request.
224
     *
225
     * @param string   $priority The logging priority as declared in the LogLevel PSR-3 class.
226
     * @param string[] $parameters
227
     */
228
    private function log(string $message, string $priority = LogLevel::INFO, array $parameters = []): void
229
    {
230
        /** @var LogEvent $logEvent */
231
        $logEvent = LogEvent::createInstance($this);
232
        $logEvent->setContext($parameters);
233
        $logEvent->setMessage($message);
234
        $logEvent->setPriority($priority);
235
        $this->eventDispatcher->dispatch(
236
            'system.log',
237
            $logEvent
238
        );
239
    }
240
}
241