Completed
Push — master ( 9c1f83...0c1135 )
by Paulo Rodrigues
10:00
created

DependencyInjection/Configuration.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
namespace Rj\FrontendBundle\DependencyInjection;
4
5
use Rj\FrontendBundle\Util\Util;
6
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
7
use Symfony\Component\Config\Definition\ConfigurationInterface;
8
9
class Configuration implements ConfigurationInterface
10
{
11
    const DEFAULT_PREFIX = 'assets';
12
13
    private $kernelRootDir;
14
15 72
    public function __construct($kernelRootDir)
16
    {
17 72
        $this->kernelRootDir = $kernelRootDir;
18 72
    }
19
20
    /**
21
     * {@inheritdoc}
22
     */
23 72
    public function getConfigTreeBuilder()
24
    {
25 72
        $self = $this;
26
27 72
        return $this->createRoot('rj_frontend', 'array')
28 72
            ->children()
29 72
                ->booleanNode('override_default_package')->defaultTrue()->end()
30 72
                ->arrayNode('fallback_patterns')
31 72
                    ->prototype('scalar')->end()
32 72
                    ->defaultValue(array('.*bundles\/.*'))
33 72
                ->end()
34 72
                ->append($this->addLivereloadSection())
35 72
                ->append($this->addPackagePrefixSection(self::DEFAULT_PREFIX))
36 72
                ->append($this->addPackageManifestSection())
37 72
                ->arrayNode('packages')
38 72
                    ->useAttributeAsKey('name')
39 72
                    ->prototype('array')
40 72
                        ->children()
41 72
                            ->append($this->addPackagePrefixSection())
42 72
                            ->append($this->addPackageManifestSection())
43 72
                        ->end()
44 72
                        ->beforeNormalization()
45
                            ->ifTrue(function ($config) use ($self) {
46 38
                                return $self->mustApplyManifestDefaultPath($config);
47 72
                            })
48
                            ->then(function ($config) use ($self) {
49 2
                                return $self->applyManifestDefaultPath($config);
50 72
                            })
51 72
                        ->end()
52 72
                    ->end()
53 72
                    ->validate()
54
                        ->ifTrue(function ($config) {
55 32
                            return in_array('default', array_keys($config));
56 72
                        })
57 72
                        ->thenInvalid("'default' is a reserved package name")
58 72
                    ->end()
59 72
                ->end()
60 72
            ->end()
61 72
            ->beforeNormalization()
62
                ->ifTrue(function ($config) use ($self) {
63 72
                    return $self->mustApplyManifestDefaultPath($config);
64 72
                })
65
                ->then(function ($config) use ($self) {
66 6
                    return $self->applyManifestDefaultPath($config);
67 72
                })
68 72
            ->end()
69 72
        ->end();
70
    }
71
72 72
    private function addLivereloadSection()
73
    {
74 72
        return $this->createRoot('livereload')
75 72
            ->canBeDisabled()
76 72
            ->children()
77 72
                ->scalarNode('url')
78 72
                    ->defaultValue('//localhost:35729/livereload.js')
79 72
                ->end()
80 72
            ->end()
81
        ;
82
    }
83
84 72
    private function addPackagePrefixSection($defaultValue = null)
85
    {
86 72
        $node = $this->createRoot('prefix')
87 72
            ->prototype('scalar')->end()
88 72
            ->defaultValue(array($defaultValue))
89 72
            ->cannotBeEmpty()
90 72
            ->beforeNormalization()
91 72
                ->ifString()
92
                ->then(function ($v) { return array($v); })
93 72
            ->end()
94 72
            ->validate()
95
                ->ifTrue(function ($prefixes) {
96 46
                    return Util::containsUrl($prefixes)
97 46
                        && Util::containsNotUrl($prefixes);
98 72
                })
99 72
                ->thenInvalid('Packages cannot have both URL and path prefixes')
100 72
            ->end()
101 72
            ->validate()
102
                ->ifTrue(function ($prefixes) {
103 44
                    return count($prefixes) > 1
104 44
                        && Util::containsNotUrl($prefixes);
105 72
                })
106 72
                ->thenInvalid('Packages can only have one path prefix')
107 72
            ->end()
108
        ;
109
110 72
        return $defaultValue === null
111 72
            ? $node->isRequired()
112 72
            : $node
113
        ;
114
    }
115
116 72
    private function addPackageManifestSection()
117
    {
118 72
        return $this->createRoot('manifest')
1 ignored issue
show
It seems like you code against a specific sub-type and not the parent class Symfony\Component\Config...\Builder\NodeDefinition as the method canBeEnabled() does only exist in the following sub-classes of Symfony\Component\Config...\Builder\NodeDefinition: Symfony\Component\Config...der\ArrayNodeDefinition. 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...
119 72
            ->canBeEnabled()
120 72
            ->children()
121 72
                ->scalarNode('format')
122 72
                    ->defaultValue('json')
123 72
                    ->validate()
124 72
                        ->ifNotInArray(array('json'))
125 72
                        ->thenInvalid('For the moment only JSON manifest files are supported')
126 72
                    ->end()
127 72
                ->end()
128 72
                ->scalarNode('path')->isRequired()->end()
129 72
                ->scalarNode('root_key')->defaultNull()->end()
130 72
            ->end()
131 72
            ->beforeNormalization()
132 72
                ->ifString()
133
                ->then(function ($v) { return array('enabled' => true, 'path' => $v); })
134 72
            ->end()
135
        ;
136
    }
137
138
    /**
139
     * Returns true if the manifest's path has not been defined AND:
140
     *  - a prefix has not been defined
141
     *  - OR if a prefix has been defined, it's not a URL
142
     *
143
     * Note that the manifest's configuration can be a string, in which case it
144
     * represents the path to the manifest file.
145
     *
146
     * This method is public because of the inability to use $this in closures
147
     * in PHP 5.3.
148
     *
149
     * @param  array   $config
150
     * @return boolean
151
     */
152 72
    public function mustApplyManifestDefaultPath($config)
153
    {
154 72
        return isset($config['manifest']) &&
155 72
            !is_string($config['manifest']) &&
156 72
            !isset($config['manifest']['path']) &&
157 72
            (!isset($config['prefix']) || !Util::containsUrl($config['prefix']))
158
        ;
159
    }
160
161
    /**
162
     * Apply a default manifest path computed from the defined prefix.
163
     *
164
     * After calling this method, the manifest's path will be
165
     * %kernel.root_dir%/../web/$prefix/manifest.json, where $prefix is the
166
     * configured prefix.
167
     *
168
     * Note that this method is used for both the default package's config and
169
     * for each custom package's config.
170
     *
171
     * This method is public because of the inability to use $this in closures
172
     * in PHP 5.3
173
     *
174
     * @param  array $config
175
     * @return array
176
     */
177 8
    public function applyManifestDefaultPath($config)
178
    {
179 8
        $prefix = isset($config['prefix']) ? $config['prefix'] : self::DEFAULT_PREFIX;
180
181 8
        if (is_array($prefix)) {
182
            $prefix = $prefix[0];
183
        }
184
185 8
        if (!is_array($config['manifest'])) {
186 6
            $config['manifest'] = array('enabled' => true);
187
        }
188
189 8
        $config['manifest']['path'] = implode('/', array(
190 8
            $this->kernelRootDir,
191 8
            '..',
192 8
            'web',
193 8
            $prefix,
194 8
            'manifest.json',
195
        ));
196
197 8
        return $config;
198
    }
199
200 72
    private function createRoot($root, $type = null)
201
    {
202 72
        $treeBuilder = new TreeBuilder();
203
204 72
        if ($type !== null) {
205 72
            return $treeBuilder->root($root, $type);
206
        }
207
208 72
        return $treeBuilder->root($root);
209
    }
210
}
211