Completed
Push — develop ( 4b36a1...568a5c )
by Jaap
07:06
created

ServiceProvider::provideTemplatingSystem()   B

Complexity

Conditions 2
Paths 2

Size

Total Lines 32
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 2.0008

Importance

Changes 0
Metric Value
cc 2
eloc 16
nc 2
nop 1
dl 0
loc 32
ccs 16
cts 17
cp 0.9412
crap 2.0008
rs 8.8571
c 0
b 0
f 0
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\Transformer;
13
14
use Cilex\Application;
15
use Pimple\Container;
16
use Pimple\ServiceProviderInterface;
17
use phpDocumentor\Compiler\Compiler;
18
use phpDocumentor\Compiler\Linker\Linker;
19
use phpDocumentor\Compiler\Pass\ClassTreeBuilder;
20
use phpDocumentor\Compiler\Pass\Debug;
21
use phpDocumentor\Compiler\Pass\ElementsIndexBuilder;
22
use phpDocumentor\Compiler\Pass\ExampleTagsEnricher;
23
use phpDocumentor\Compiler\Pass\InterfaceTreeBuilder;
24
use phpDocumentor\Compiler\Pass\NamespaceTreeBuilder;
25
use phpDocumentor\Compiler\Pass\PackageTreeBuilder;
26
use phpDocumentor\Compiler\Pass\MarkerFromTagsExtractor;
27
use phpDocumentor\Compiler\Pass\ResolveInlineLinkAndSeeTags;
28
use phpDocumentor\Descriptor\ProjectDescriptorBuilder;
29
use phpDocumentor\Event\Dispatcher;
30
use phpDocumentor\Fileset\Collection;
31
use phpDocumentor\Transformer\Command\Project\TransformCommand;
32
use phpDocumentor\Transformer\Command\Template\ListCommand;
33
use phpDocumentor\Transformer\Event\PreTransformEvent;
34
use phpDocumentor\Transformer\Template\Factory;
35
use phpDocumentor\Transformer\Template\PathResolver;
36
37
/**
38
 * This provider is responsible for registering the transformer component with the given Application.
39
 */
40
class ServiceProvider extends \stdClass implements ServiceProviderInterface
0 ignored issues
show
Complexity introduced by
The class ServiceProvider has a coupling between objects value of 28. Consider to reduce the number of dependencies under 13.
Loading history...
41
{
42
    /**
43
     * Registers services on the given app.
44
     *
45
     * @param Container $app An Application instance.
46
     *
47
     * @throws Exception\MissingDependencyException if the application does not have a descriptor.builder service.
48
     * @throws Exception\MissingDependencyException if the application does not have a serializer service.
49
     */
50 17
    public function register(Container $app)
51
    {
52 17
        if (!isset($app['descriptor.builder'])) {
53 1
            throw new Exception\MissingDependencyException(
54 1
                'The builder object that is used to construct the ProjectDescriptor is missing'
55
            );
56
        }
57 16
        if (!isset($app['serializer'])) {
58 1
            throw new Exception\MissingDependencyException(
59 1
                'The serializer object that is used to read the template configuration with is missing'
60
            );
61
        }
62
63
        // parameters
64 15
        $app['linker.substitutions'] = array(
65
            'phpDocumentor\Descriptor\ProjectDescriptor'      => array('files'),
66
            'phpDocumentor\Descriptor\FileDescriptor'         => array(
67
                'tags',
68
                'classes',
69
                'interfaces',
70
                'traits',
71
                'functions',
72
                'constants'
73
            ),
74
            'phpDocumentor\Descriptor\ClassDescriptor'        => array(
75
                'tags',
76
                'parent',
77
                'interfaces',
78
                'constants',
79
                'properties',
80
                'methods',
81
                'usedTraits',
82
            ),
83
            'phpDocumentor\Descriptor\InterfaceDescriptor'       => array(
84
                'tags',
85
                'parent',
86
                'constants',
87
                'methods',
88
            ),
89
            'phpDocumentor\Descriptor\TraitDescriptor'           => array(
90
                'tags',
91
                'properties',
92
                'methods',
93
                'usedTraits',
94
            ),
95
            'phpDocumentor\Descriptor\FunctionDescriptor'        => array('tags', 'arguments'),
96
            'phpDocumentor\Descriptor\MethodDescriptor'          => array('tags', 'arguments'),
97
            'phpDocumentor\Descriptor\ArgumentDescriptor'        => array('types'),
98
            'phpDocumentor\Descriptor\PropertyDescriptor'        => array('tags', 'types'),
99
            'phpDocumentor\Descriptor\ConstantDescriptor'        => array('tags', 'types'),
100
            'phpDocumentor\Descriptor\Tag\ParamDescriptor'       => array('types'),
101
            'phpDocumentor\Descriptor\Tag\ReturnDescriptor'      => array('types'),
102
            'phpDocumentor\Descriptor\Tag\SeeDescriptor'         => array('reference'),
103
            'phpDocumentor\Descriptor\Tag\UsesDescriptor'        => array('reference'),
104
            'phpDocumentor\Descriptor\Type\CollectionDescriptor' => array('baseType', 'types', 'keyTypes'),
105
        );
106
107
        // services
108 15
        $app['compiler'] = function ($container) {
109 15
            $compiler = new Compiler();
110 15
            $compiler->insert(new ElementsIndexBuilder(), ElementsIndexBuilder::COMPILER_PRIORITY);
111 15
            $compiler->insert(new MarkerFromTagsExtractor(), MarkerFromTagsExtractor::COMPILER_PRIORITY);
112 15
            $compiler->insert(
113 15
                new ExampleTagsEnricher($container['parser.example.finder']),
114 15
                ExampleTagsEnricher::COMPILER_PRIORITY
115
            );
116 15
            $compiler->insert(new PackageTreeBuilder(), PackageTreeBuilder::COMPILER_PRIORITY);
117 15
            $compiler->insert(new NamespaceTreeBuilder(), NamespaceTreeBuilder::COMPILER_PRIORITY);
118 15
            $compiler->insert(new ClassTreeBuilder(), ClassTreeBuilder::COMPILER_PRIORITY);
119 15
            $compiler->insert(new InterfaceTreeBuilder(), InterfaceTreeBuilder::COMPILER_PRIORITY);
120 15
            $compiler->insert(
121 15
                new ResolveInlineLinkAndSeeTags($container['transformer.routing.queue']),
122 15
                ResolveInlineLinkAndSeeTags::COMPILER_PRIORITY
123
            );
124 15
            $compiler->insert($container['linker'], Linker::COMPILER_PRIORITY);
125 15
            $compiler->insert($container['transformer'], Transformer::COMPILER_PRIORITY);
126 15
            $compiler->insert(
127 15
                new Debug($container['monolog'], $container['descriptor.analyzer']),
128 15
                Debug::COMPILER_PRIORITY
129
            );
130
131 15
            return $compiler;
132
        };
133
134 15
        $app['linker'] = function ($app) {
135 15
            return new Linker($app['linker.substitutions']);
136
        };
137
138 15
        $app['transformer.behaviour.collection'] = function () {
139 15
            return new Behaviour\Collection();
140
        };
141
142 15
        $app['transformer.routing.standard'] = function ($container) {
143
            /** @var ProjectDescriptorBuilder $projectDescriptorBuilder */
144 15
            $projectDescriptorBuilder = $container['descriptor.builder'];
0 ignored issues
show
Comprehensibility Naming introduced by
The variable name $projectDescriptorBuilder 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...
145
146 15
            return new Router\StandardRouter($projectDescriptorBuilder);
147
        };
148
149 15
        $app['transformer.routing.external'] = function ($container) {
150 15
            return new Router\ExternalRouter($container['config']);
151
        };
152
153 15
        $app['transformer.routing.queue'] = function ($container) {
154 15
            $queue = new Router\Queue();
155
156
            // TODO: load from app configuration instead of hardcoded
157 15
            $queue->insert($container['transformer.routing.external'], 10500);
158 15
            $queue->insert($container['transformer.routing.standard'], 10000);
159
160 15
            return $queue;
161
        };
162
163 15
        $app['transformer.writer.collection'] = function ($container) {
164 15
            return new Writer\Collection($container['transformer.routing.queue']);
165
        };
166
167 15
        $this->provideTemplatingSystem($app);
0 ignored issues
show
Compatibility introduced by
$app of type object<Pimple\Container> is not a sub-type of object<Cilex\Application>. It seems like you assume a child class of the class Pimple\Container to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
168
169 15
        $app['transformer'] = function ($container) {
170 15
            $transformer = new Transformer(
171 15
                $container['transformer.template.collection'],
172 15
                $container['transformer.writer.collection']
173
            );
174
175
            /** @var Behaviour\Collection $behaviourCollection */
176 15
            $behaviourCollection = $container['transformer.behaviour.collection'];
177 15
            Dispatcher::getInstance()->addListener(
178 15
                Transformer::EVENT_PRE_TRANSFORM,
179 15
                function (PreTransformEvent $event) use ($behaviourCollection) {
180
                    $behaviourCollection->process($event->getProject());
181 15
                }
182
            );
183
184 15
            return $transformer;
185
        };
186
187 15
        $app->command(new TransformCommand($app['descriptor.builder'], $app['transformer'], $app['compiler']));
0 ignored issues
show
Bug introduced by
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...
188 15
        $app->command(new ListCommand($app['transformer.template.factory']));
0 ignored issues
show
Bug introduced by
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...
189 15
    }
190
191
    /**
192
     * Initializes the templating system in the container.
193
     *
194
     * @param Application $app
195
     *
196
     * @return void
197
     */
198 15
    protected function provideTemplatingSystem(Application $app)
199
    {
200 15
        $templateDir = __DIR__ . '/../../../data/templates';
201
202
        // when installed using composer the templates are in a different folder
203 15
        $composerTemplatePath = __DIR__ . '/../../../../templates';
204 15
        if (file_exists($composerTemplatePath)) {
205
            $templateDir = $composerTemplatePath;
206
        }
207
208
        // parameters
209 15
        $app['transformer.template.location'] = $templateDir;
210
211
        // services
212 15
        $app['transformer.template.path_resolver'] = function ($container) {
213 15
            return new PathResolver($container['transformer.template.location']);
214
        };
215
216 15
        $app['transformer.template.factory'] = function ($container) {
217 15
            return new Factory(
218 15
                $container['transformer.template.path_resolver'],
219 15
                $container['serializer']
220
            );
221
        };
222
223 15
        $app['transformer.template.collection'] = function ($container) {
224 15
            return new Template\Collection(
225 15
                $container['transformer.template.factory'],
226 15
                $container['transformer.writer.collection']
227
            );
228
        };
229 15
    }
230
}
231