Completed
Push — develop ( 114abf...08add9 )
by Jaap
03:44
created

src/phpDocumentor/Parser/ServiceProvider.php (1 issue)

Labels
Severity

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;
13
14
use Cilex\Application;
15
use Pimple\Container;
16
use Pimple\ServiceProviderInterface;
17
use phpDocumentor\Fileset\Collection;
18
use phpDocumentor\Parser\Command\Project\ParseCommand;
19
use phpDocumentor\Parser\Middleware\CacheMiddleware;
20
use phpDocumentor\Parser\Middleware\ErrorHandlingMiddleware;
21
use phpDocumentor\Parser\Middleware\StopwatchMiddleware;
22
use phpDocumentor\Parser\Middleware\EmittingMiddleware;
23
use phpDocumentor\Plugin\Core\Descriptor\Validator\ValidatorAbstract;
24
use phpDocumentor\Reflection\DocBlockFactory;
25
use phpDocumentor\Reflection\Php\Factory;
26
use phpDocumentor\Reflection\Php\NodesFactory;
27
use phpDocumentor\Reflection\Php\ProjectFactory;
28
use phpDocumentor\Reflection\PrettyPrinter;
29
use phpDocumentor\Translator\Translator;
30
use Stash\Driver\FileSystem;
31
use Stash\Pool;
32
33
/**
34
 * This provider is responsible for registering the parser component with the given Application.
35
 */
36
class ServiceProvider implements ServiceProviderInterface
37
{
38
    /**
39
     * Registers services on the given app.
40
     *
41
     * @param Container|Application $app An Application instance
42
     *
43
     * @throws Exception\MissingDependencyException if the Descriptor Builder is not present.
44
     * @throws \Stash\Exception\RuntimeException
45
     *
46
     * @return void
47
     *
48
     */
49
    public function register(Container $app)
50
    {
51
        if (!isset($app['descriptor.builder'])) {
52
            throw new Exception\MissingDependencyException(
53
                'The builder object that is used to construct the ProjectDescriptor is missing'
54
            );
55
        }
56
57
        $app['parser'] = function ($app) {
58
            $stopWatch = $app['kernel.stopwatch'];
59
60
            $adapter = new FileSystem(['path' => 'build/api-cache']);
61
            $cachePool = new Pool($adapter);
62
63
            $strategies = [
64
                new Factory\Argument(new PrettyPrinter()),
65
                new Factory\Class_(),
66
                new Factory\Constant(new PrettyPrinter()),
67
                new Factory\DocBlock(DocBlockFactory::createInstance()),
68
                new Factory\Function_(),
69
                new Factory\Interface_(),
70
                new Factory\Method(),
71
                new Factory\Property(new PrettyPrinter()),
72
                new Factory\Trait_(),
73
                new Factory\File(
74
                    NodesFactory::createInstance(),
75
                    [
76
                        new EmittingMiddleware(),
77
                        new StopwatchMiddleware(
78
                            $stopWatch
79
                        ),
80
                        new CacheMiddleware(
81
                            $cachePool
82
                        ),
83
                        new ErrorHandlingMiddleware()
84
                    ]
85
                )
86
            ];
87
88
            $parser = new Parser(
89
                new ProjectFactory($strategies),
90
                $stopWatch
91
            );
92
93
            return $parser;
94
        };
95
96
        /** @var Translator $translator */
97
        $translator = $app['translator'];
98
        $translator->addTranslationFolder(__DIR__ . DIRECTORY_SEPARATOR . 'Messages');
99
100
        $app['parser.files'] = new Collection();
101
        $app->command(
0 ignored issues
show
It seems like you code against a specific sub-type and not the parent class Pimple\Container as the method command() does only exist in the following sub-classes of Pimple\Container: Cilex\Application, phpDocumentor\Application. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
102
            new ParseCommand(
103
                $app['descriptor.builder'],
104
                $app['parser'],
105
                $translator,
106
                $app['parser.files'],
107
                $app['descriptor.cache'],
108
                $app['parser.example.finder'],
109
                $app['partials']
110
            )
111
        );
112
    }
113
114
    /**
115
     * Checks all phpDocumentor whether they match the given rules.
116
     *
117
     * @param PostDocBlockExtractionEvent $data Event object containing the parameters.
118
     *
119
     * @todo convert this method to the new style validators; this method is not invoked anymore
120
     *
121
     * @return void
122
     */
123
    public function validateDocBlocks($data)
124
    {
125
        /** @var \phpDocumentor\Reflection\BaseReflector $element */
126
        $element = $data->getSubject();
127
128
        /** @var \phpDocumentor\Reflection\DocBlock $docblock */
129
        $docblock = $data->getDocblock();
130
131
        // get the type of element
132
        $type = substr(
133
            get_class($element),
134
            strrpos(get_class($element), '\\') + 1,
135
            -9 // Reflector
136
        );
137
138
        // no docblock, or docblock should be ignored, so no reason to validate
139
        if ($docblock && $docblock->hasTag('ignore')) {
140
            return;
141
        }
142
143
        $validatorOptions = $this->loadConfiguration();
144
145
        foreach (array('Deprecated', 'Required', $type) as $validator) {
146
147
            // todo: move to a factory or builder class
148
            $class = 'phpDocumentor\Plugin\Core\Parser\DocBlock\Validator\\' . $validator . 'Validator';
149
            if (class_exists($class)) {
150
                /** @var ValidatorAbstract $val */
151
                $val = new $class($element->getName(), $docblock, $element);
152
                $val->setOptions($validatorOptions);
153
                $val->isValid($element);
154
            }
155
        }
156
    }
157
158
    /**
159
     * Load the configuration from the plugin.xml file
160
     *
161
     * @todo restore required/deprecated validators
162
     *
163
     * @return array
164
     */
165
    protected function loadConfiguration()
166
    {
167
        //$configOptions = $this->plugin->getOptions();
168
        $validatorOptions = array();
169
170
        //foreach (array('deprecated', 'required') as $tag) {
171
        //    $validatorOptions[$tag] = $this->loadConfigurationByElement($configOptions, $tag);
172
        //}
173
174
        return $validatorOptions;
175
    }
176
177
    /**
178
     * Load the configuration for given element (deprecated/required)
179
     *
180
     * @param array  $configOptions The configuration from the plugin.xml file
181
     * @param string $configType    Required/Deprecated for the time being
182
     *
183
     * @return array
184
     */
185
    protected function loadConfigurationByElement($configOptions, $configType)
186
    {
187
        $validatorOptions = array();
188
189
        if (isset($configOptions[$configType]->tag)) {
190
191
            foreach ($configOptions[$configType]->tag as $tag) {
192
                $tagName = (string)$tag['name'];
193
194
                if (isset($tag->element)) {
195
                    foreach ($tag->element as $type) {
196
                        $typeName = (string)$type;
197
                        $validatorOptions[$typeName][] = $tagName;
198
                    }
199
                } else {
200
                    $validatorOptions['__ALL__'][] = $tagName;
201
                }
202
            }
203
        }
204
205
        return $validatorOptions;
206
    }
207
}
208