Scrutinizer GitHub App not installed

We could not synchronize checks via GitHub's checks API since Scrutinizer's GitHub App is not installed for this repository.

Install GitHub App

Passed
Pull Request — master (#264)
by Jérémiah
16:33
created

Configuration::addSecurityQuerySection()   B

Complexity

Conditions 3
Paths 1

Size

Total Lines 34
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 28
CRAP Score 3

Importance

Changes 0
Metric Value
dl 0
loc 34
ccs 28
cts 28
cp 1
rs 8.8571
c 0
b 0
f 0
cc 3
eloc 24
nc 1
nop 2
crap 3
1
<?php
2
3
namespace Overblog\GraphQLBundle\DependencyInjection;
4
5
use GraphQL\Validator\Rules\QueryComplexity;
6
use GraphQL\Validator\Rules\QueryDepth;
7
use Overblog\GraphQLBundle\Error\ErrorHandler;
8
use Overblog\GraphQLBundle\Resolver\Resolver;
9
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
10
use Symfony\Component\Config\Definition\Builder\EnumNodeDefinition;
11
use Symfony\Component\Config\Definition\Builder\ScalarNodeDefinition;
12
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
13
use Symfony\Component\Config\Definition\ConfigurationInterface;
14
15
class Configuration implements ConfigurationInterface
16
{
17
    const NAME = 'overblog_graphql';
18
19
    /** bool */
20
    private $debug;
21
22
    /** null|string */
23
    private $cacheDir;
24
25
    /**
26
     * Constructor.
27
     *
28
     * @param bool        $debug    Whether to use the debug mode
29
     * @param null|string $cacheDir
30
     */
31 27
    public function __construct($debug, $cacheDir = null)
32
    {
33 27
        $this->debug = (bool) $debug;
34 27
        $this->cacheDir = $cacheDir;
35 27
    }
36
37 27
    public function getConfigTreeBuilder()
38
    {
39 27
        $treeBuilder = new TreeBuilder();
40 27
        $rootNode = $treeBuilder->root(self::NAME);
41
42
        $rootNode
43 27
            ->children()
44 27
                ->append($this->batchingMethodSection())
45 27
                ->append($this->definitionsSection())
46 27
                ->append($this->errorsHandlerSection())
47 27
                ->append($this->servicesSection())
48 27
                ->append($this->securitySection())
49 27
            ->end();
50
51 27
        return $treeBuilder;
52
    }
53
54 27
    private function batchingMethodSection()
55
    {
56 27
        $builder = new TreeBuilder();
57
        /** @var EnumNodeDefinition $node */
58 27
        $node = $builder->root('batching_method', 'enum');
59
60
        $node
0 ignored issues
show
Unused Code introduced by
The call to the method Symfony\Component\Config...umNodeDefinition::end() seems un-needed as the method has no side-effects.

PHP Analyzer performs a side-effects analysis of your code. A side-effect is basically anything that might be visible after the scope of the method is left.

Let’s take a look at an example:

class User
{
    private $email;

    public function getEmail()
    {
        return $this->email;
    }

    public function setEmail($email)
    {
        $this->email = $email;
    }
}

If we look at the getEmail() method, we can see that it has no side-effect. Whether you call this method or not, no future calls to other methods are affected by this. As such code as the following is useless:

$user = new User();
$user->getEmail(); // This line could safely be removed as it has no effect.

On the hand, if we look at the setEmail(), this method _has_ side-effects. In the following case, we could not remove the method call:

$user = new User();
$user->setEmail('email@domain'); // This line has a side-effect (it changes an
                                 // instance variable).
Loading history...
61 27
            ->values(['relay', 'apollo'])
62 27
            ->defaultValue('relay')
63 27
        ->end();
64
65 27
        return $node;
66
    }
67
68 27
    private function errorsHandlerSection()
69
    {
70 27
        $builder = new TreeBuilder();
71
        /** @var ArrayNodeDefinition $node */
72 27
        $node = $builder->root('errors_handler');
73
        $node
74 27
            ->treatFalseLike(['enabled' => false])
75 27
            ->treatTrueLike(['enabled' => true])
76 27
            ->treatNullLike(['enabled' => true])
77 27
            ->addDefaultsIfNotSet()
78 27
            ->children()
79 27
                ->booleanNode('enabled')->defaultTrue()->end()
80 27
                ->scalarNode('internal_error_message')->defaultValue(ErrorHandler::DEFAULT_ERROR_MESSAGE)->end()
81 27
                ->booleanNode('rethrow_internal_exceptions')->defaultFalse()->end()
82 27
                ->booleanNode('debug')->defaultValue($this->debug)->end()
83 27
                ->booleanNode('log')->defaultTrue()->end()
84 27
                ->scalarNode('logger_service')->defaultValue('logger')->end()
85 27
                ->booleanNode('map_exceptions_to_parent')->defaultFalse()->end()
86 27
                ->arrayNode('exceptions')
87 27
                    ->addDefaultsIfNotSet()
88 27
                    ->children()
89 27
                        ->arrayNode('warnings')
90 27
                            ->treatNullLike([])
91 27
                            ->prototype('scalar')->end()
92 27
                        ->end()
93 27
                        ->arrayNode('errors')
94 27
                            ->treatNullLike([])
95 27
                            ->prototype('scalar')->end()
96 27
                        ->end()
97 27
                        ->arrayNode('types')
98 27
                            ->addDefaultsIfNotSet()
99 27
                            ->children()
100 27
                                ->scalarNode('warnings')
101 27
                                    ->defaultValue(ErrorHandler::DEFAULT_USER_WARNING_CLASS)
102 27
                                ->end()
103 27
                                ->scalarNode('errors')
104 27
                                    ->defaultValue(ErrorHandler::DEFAULT_USER_ERROR_CLASS)
105 27
                                ->end()
106 27
                            ->end()
107 27
                        ->end()
108 27
                    ->end()
109 27
                ->end()
110 27
            ->end();
111
112 27
        return $node;
113
    }
114
115 27
    private function definitionsSection()
116
    {
117 27
        $builder = new TreeBuilder();
118
        /** @var ArrayNodeDefinition $node */
119 27
        $node = $builder->root('definitions');
120
        $node
121 27
            ->addDefaultsIfNotSet()
122 27
            ->children()
123 27
                ->variableNode('default_resolver')->defaultValue([Resolver::class, 'defaultResolveFn'])->end()
124 27
                ->scalarNode('class_namespace')->defaultValue('Overblog\\GraphQLBundle\\__DEFINITIONS__')->end()
125 27
                ->scalarNode('cache_dir')->defaultValue($this->cacheDir.'/overblog/graphql-bundle/__definitions__')->end()
126 27
                ->booleanNode('use_classloader_listener')->defaultTrue()->end()
127 27
                ->booleanNode('auto_compile')->defaultTrue()->end()
128 27
                ->booleanNode('show_debug_info')->info('Show some performance stats in extensions')->defaultFalse()->end()
129 27
                ->booleanNode('config_validation')->defaultValue($this->debug)->end()
130 27
                ->append($this->definitionsSchemaSection())
131 27
                ->append($this->definitionsAutoMappingSection())
132 27
                ->append($this->definitionsMappingsSection())
133 27
                ->arrayNode('builders')
134 27
                    ->children()
135 27
                        ->append($this->builderSection('field'))
136 27
                        ->append($this->builderSection('args'))
137 27
                    ->end()
138 27
                ->end()
139
140 27
            ->end()
141 27
        ->end();
142
143 27
        return $node;
144
    }
145
146 27
    private function servicesSection()
147
    {
148 27
        $builder = new TreeBuilder();
149
        /** @var ArrayNodeDefinition $node */
150 27
        $node = $builder->root('services');
151
        $node
152 27
            ->addDefaultsIfNotSet()
153 27
            ->children()
154 27
                ->scalarNode('executor')
155 27
                    ->defaultValue(self::NAME.'.executor.default')
156 27
                ->end()
157 27
                ->scalarNode('promise_adapter')
158 27
                    ->defaultValue(self::NAME.'.promise_adapter.default')
159 27
                ->end()
160 27
                ->scalarNode('expression_language')
161 27
                    ->defaultValue(self::NAME.'.expression_language.default')
162 27
                ->end()
163 27
                ->scalarNode('cache_expression_language_parser')
164 27
                    ->defaultValue(self::NAME.'.cache_expression_language_parser.default')
165 27
                ->end()
166 27
            ->end()
167 27
        ->end();
168
169 27
        return $node;
170
    }
171
172 27
    private function securitySection()
173
    {
174 27
        $builder = new TreeBuilder();
175
        /** @var ArrayNodeDefinition $node */
176 27
        $node = $builder->root('security');
177
        $node
178 27
            ->addDefaultsIfNotSet()
179 27
            ->children()
180 27
                ->append($this->securityQuerySection('query_max_depth', QueryDepth::DISABLED))
181 27
                ->append($this->securityQuerySection('query_max_complexity', QueryComplexity::DISABLED))
182 27
                ->booleanNode('handle_cors')->defaultFalse()->end()
183 27
            ->end()
184 27
        ->end();
185
186 27
        return $node;
187
    }
188
189 27
    private function definitionsSchemaSection()
190
    {
191 27
        $builder = new TreeBuilder();
192
        /** @var ArrayNodeDefinition $node */
193 27
        $node = $builder->root('schema');
194
        $node
0 ignored issues
show
Bug introduced by
The method useAttributeAsKey() does not exist on Symfony\Component\Config...\Builder\NodeDefinition. Did you maybe mean attribute()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
195 27
            ->beforeNormalization()
196 27
                ->ifTrue(function ($v) {
197 23
                    return isset($v['query']) && is_string($v['query']) || isset($v['mutation']) && is_string($v['mutation']);
198 27
                })
199 27
                ->then(function ($v) {
200 23
                    return ['default' => $v];
201 27
                })
202 27
            ->end()
203 27
            ->useAttributeAsKey('name')
204 27
            ->prototype('array')
205 27
                ->addDefaultsIfNotSet()
206 27
                ->children()
207 27
                    ->scalarNode('query')->defaultNull()->end()
208 27
                    ->scalarNode('mutation')->defaultNull()->end()
209 27
                    ->scalarNode('subscription')->defaultNull()->end()
210 27
                ->end()
211 27
            ->end()
212 27
        ->end();
213
214 27
        return $node;
215
    }
216
217 27
    private function definitionsAutoMappingSection()
218
    {
219 27
        $builder = new TreeBuilder();
220
        /** @var ArrayNodeDefinition $node */
221 27
        $node = $builder->root('auto_mapping');
222
        $node
223 27
            ->treatFalseLike(['enabled' => false])
224 27
            ->treatTrueLike(['enabled' => true])
225 27
            ->treatNullLike(['enabled' => true])
226 27
            ->addDefaultsIfNotSet()
227 27
            ->children()
228 27
                ->booleanNode('enabled')->defaultTrue()->end()
229 27
                ->arrayNode('directories')
230 27
                    ->info('List of directories containing GraphQL classes.')
231 27
                    ->prototype('scalar')->end()
232 27
                ->end()
233 27
            ->end()
234 27
        ->end();
235
236 27
        return $node;
237
    }
238
239 27
    private function definitionsMappingsSection()
240
    {
241 27
        $builder = new TreeBuilder();
242
        /** @var ArrayNodeDefinition $node */
243 27
        $node = $builder->root('mappings');
244
        $node
245 27
            ->children()
246 27
                ->arrayNode('auto_discover')
247 27
                    ->treatFalseLike(['bundles' => false, 'root_dir' => false])
248 27
                    ->treatTrueLike(['bundles' => true, 'root_dir' => true])
249 27
                    ->treatNullLike(['bundles' => true, 'root_dir' => true])
250 27
                    ->addDefaultsIfNotSet()
251 27
                    ->children()
252 27
                        ->booleanNode('bundles')->defaultTrue()->end()
253 27
                        ->booleanNode('root_dir')->defaultTrue()->end()
254 27
                    ->end()
255 27
                ->end()
256 27
                ->arrayNode('types')
257 27
                    ->prototype('array')
258 27
                        ->addDefaultsIfNotSet()
259 27
                        ->beforeNormalization()
260 27
                            ->ifTrue(function ($v) {
261 22
                                return isset($v['type']) && 'yml' === $v['type'];
262 27
                            })
263 27
                            ->then(function ($v) {
264 7
                                $v['type'] = 'yaml';
265
266 7
                                return $v;
267 27
                            })
268 27
                        ->end()
269 27
                        ->children()
270 27
                            ->enumNode('type')->values(['yaml', 'xml'])->defaultNull()->end()
271 27
                            ->scalarNode('dir')->defaultNull()->end()
272 27
                            ->scalarNode('suffix')->defaultValue(OverblogGraphQLTypesExtension::DEFAULT_TYPES_SUFFIX)->end()
273 27
                        ->end()
274 27
                    ->end()
275 27
                ->end()
276 27
            ->end()
277 27
        ->end();
278
279 27
        return $node;
280
    }
281
282
    /**
283
     * @param string $name
284
     *
285
     * @return ArrayNodeDefinition
286
     */
287 27
    private function builderSection($name)
288
    {
289 27
        $builder = new TreeBuilder();
290
        /** @var ArrayNodeDefinition $node */
291 27
        $node = $builder->root($name);
292 27
        $node->beforeNormalization()
293 27
            ->ifTrue(function ($v) {
294 1
                return is_array($v) && !empty($v);
295 27
            })
296 27
            ->then(function ($v) {
297 1
                foreach ($v as $key => &$config) {
298 1
                    if (is_string($config)) {
299
                        $config = [
300 1
                            'alias' => $key,
301 1
                            'class' => $config,
302
                        ];
303
                    }
304
                }
305
306 1
                return $v;
307 27
            })
308 27
        ->end();
309
310 27
        $node->prototype('array')
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Symfony\Component\Config...\Builder\NodeDefinition as the method children() 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...
311 27
            ->children()
312 27
                ->scalarNode('alias')->isRequired()->end()
313 27
                ->scalarNode('class')->isRequired()->end()
314 27
            ->end()
315 27
        ->end()
316
        ;
317
318 27
        return $node;
319
    }
320
321
    /**
322
     * @param string $name
323
     * @param bool   $disabledValue
324
     *
325
     * @return ScalarNodeDefinition
326
     */
327 27
    private function securityQuerySection($name, $disabledValue)
328
    {
329 27
        $builder = new TreeBuilder();
330
        /** @var ScalarNodeDefinition $node */
331 27
        $node = $builder->root($name, 'scalar');
332 27
        $node->beforeNormalization()
333 27
                ->ifTrue(function ($v) {
334 25
                    return is_string($v) && is_numeric($v);
335 27
                })
336 27
                ->then(function ($v) {
337 2
                    return (int) $v;
338 27
                })
339 27
            ->end();
340
341
        $node
342 27
            ->info('Disabled if equal to false.')
343 27
            ->beforeNormalization()
344 27
                ->ifTrue(function ($v) {
345 25
                    return false === $v;
346 27
                })
347 27
                ->then(function () use ($disabledValue) {
348 25
                    return $disabledValue;
349 27
                })
350 27
            ->end()
351 27
            ->defaultFalse()
352 27
            ->validate()
353 27
                ->ifTrue(function ($v) {
354 25
                    return is_int($v) && $v < 0;
355 27
                })
356 27
                ->thenInvalid(sprintf('"%s.security.%s" must be greater or equal to 0.', self::NAME, $name))
357 27
            ->end()
358
        ;
359
360 27
        return $node;
361
    }
362
}
363