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 (#254)
by Jérémiah
09:17
created

Configuration::addBuilderSection()   B

Complexity

Conditions 4
Paths 1

Size

Total Lines 32
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 22
CRAP Score 4

Importance

Changes 0
Metric Value
dl 0
loc 32
ccs 22
cts 22
cp 1
rs 8.5806
c 0
b 0
f 0
cc 4
eloc 21
nc 1
nop 1
crap 4
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\NodeParentInterface;
10
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
11
use Symfony\Component\Config\Definition\ConfigurationInterface;
12
13
class Configuration implements ConfigurationInterface
14
{
15
    /** bool */
16
    private $debug;
17
18
    /** null|string */
19
    private $cacheDir;
20
21
    /**
22
     * Constructor.
23
     *
24
     * @param bool        $debug    Whether to use the debug mode
25
     * @param null|string $cacheDir
26
     */
27 27
    public function __construct($debug, $cacheDir = null)
28
    {
29 27
        $this->debug = (bool) $debug;
30 27
        $this->cacheDir = $cacheDir;
31 27
    }
32
33 27
    public function getConfigTreeBuilder()
34
    {
35 27
        $treeBuilder = new TreeBuilder();
36 27
        $rootNode = $treeBuilder->root('overblog_graphql');
37
38
        $rootNode
39 27
            ->children()
40 27
                ->enumNode('batching_method')
41 27
                    ->values(['relay', 'apollo'])
42 27
                    ->defaultValue('relay')
43 27
                ->end()
44 27
                ->arrayNode('definitions')
45 27
                    ->addDefaultsIfNotSet()
46 27
                    ->children()
47 27
                        ->scalarNode('internal_error_message')->defaultNull()->end()
48 27
                        ->variableNode('default_resolver')->defaultValue([Resolver::class, 'defaultResolveFn'])->end()
49 27
                        ->scalarNode('class_namespace')->defaultValue('Overblog\\GraphQLBundle\\__DEFINITIONS__')->end()
50 27
                        ->scalarNode('cache_dir')->defaultValue($this->cacheDir.'/overblog/graphql-bundle/__definitions__')->end()
51 27
                        ->booleanNode('use_classloader_listener')->defaultTrue()->end()
52 27
                        ->booleanNode('auto_compile')->defaultTrue()->end()
53 27
                        ->booleanNode('show_debug_info')->defaultFalse()->end()
54 27
                        ->booleanNode('config_validation')->defaultValue($this->debug)->end()
55 27
                        ->arrayNode('schema')
56 27
                            ->beforeNormalization()
57 27
                                ->ifTrue(function ($v) {
58 23
                                    return isset($v['query']) && is_string($v['query']) || isset($v['mutation']) && is_string($v['mutation']);
59 27
                                })
60 27
                                ->then(function ($v) {
61 23
                                    return ['default' => $v];
62 27
                                })
63 27
                            ->end()
64 27
                            ->useAttributeAsKey('name')
65 27
                            ->prototype('array')
66 27
                                ->addDefaultsIfNotSet()
67 27
                                ->children()
68 27
                                    ->scalarNode('query')->defaultNull()->end()
69 27
                                    ->scalarNode('mutation')->defaultNull()->end()
70 27
                                    ->scalarNode('subscription')->defaultNull()->end()
71 27
                                ->end()
72 27
                            ->end()
73 27
                        ->end()
74 27
                        ->arrayNode('auto_mapping')
75 27
                            ->treatFalseLike(['enabled' => false])
76 27
                            ->treatTrueLike(['enabled' => true])
77 27
                            ->treatNullLike(['enabled' => true])
78 27
                            ->addDefaultsIfNotSet()
79 27
                            ->children()
80 27
                                ->booleanNode('enabled')->defaultTrue()->end()
81 27
                                ->arrayNode('directories')
82 27
                                    ->info('List of directories containing GraphQL classes.')
83 27
                                    ->prototype('scalar')->end()
84 27
                                ->end()
85 27
                            ->end()
86 27
                        ->end()
87 27
                        ->append($this->addMappingSection())
88 27
                        ->booleanNode('map_exceptions_to_parent')->defaultFalse()->end()
89 27
                        ->arrayNode('exceptions')
90 27
                            ->addDefaultsIfNotSet()
91 27
                            ->children()
92 27
                                ->arrayNode('warnings')
93 27
                                    ->treatNullLike([])
94 27
                                    ->prototype('scalar')->end()
95 27
                                ->end()
96 27
                                ->arrayNode('errors')
97 27
                                    ->treatNullLike([])
98 27
                                    ->prototype('scalar')->end()
99 27
                                ->end()
100 27
                                ->arrayNode('types')
101 27
                                    ->addDefaultsIfNotSet()
102 27
                                    ->children()
103 27
                                        ->scalarNode('warnings')
104 27
                                            ->defaultValue(ErrorHandler::DEFAULT_USER_WARNING_CLASS)
105 27
                                        ->end()
106 27
                                        ->scalarNode('errors')
107 27
                                            ->defaultValue(ErrorHandler::DEFAULT_USER_ERROR_CLASS)
108 27
                                        ->end()
109 27
                                    ->end()
110 27
                                ->end()
111 27
                            ->end()
112 27
                        ->end()
113
114 27
                        ->arrayNode('builders')
115 27
                            ->children()
116 27
                                ->append($this->addBuilderSection('field'))
117 27
                                ->append($this->addBuilderSection('args'))
118 27
                            ->end()
119 27
                        ->end()
120
121 27
                    ->end()
122 27
                ->end()
123 27
                ->arrayNode('services')
124 27
                    ->addDefaultsIfNotSet()
125 27
                    ->children()
126 27
                        ->scalarNode('executor')
127 27
                            ->defaultValue('overblog_graphql.executor.default')
128 27
                        ->end()
129 27
                        ->scalarNode('promise_adapter')
130 27
                            ->defaultValue('overblog_graphql.promise_adapter.default')
131 27
                        ->end()
132 27
                        ->scalarNode('expression_language')
133 27
                            ->defaultValue('overblog_graphql.expression_language.default')
134 27
                        ->end()
135 27
                        ->scalarNode('cache_expression_language_parser')
136 27
                            ->defaultValue('overblog_graphql.cache_expression_language_parser.default')
137 27
                        ->end()
138 27
                    ->end()
139 27
                ->end()
140 27
                ->arrayNode('security')
141 27
                    ->addDefaultsIfNotSet()
142 27
                    ->children()
143 27
                        ->append($this->addSecurityQuerySection('query_max_depth', QueryDepth::DISABLED))
144 27
                        ->append($this->addSecurityQuerySection('query_max_complexity', QueryComplexity::DISABLED))
145 27
                        ->booleanNode('handle_cors')->defaultFalse()->end()
146 27
                    ->end()
147 27
                ->end()
148 27
            ->end();
149
150 27
        return $treeBuilder;
151
    }
152
153 27
    private function addMappingSection()
154
    {
155 27
        $builder = new TreeBuilder();
156 27
        $node = $builder->root('mappings');
157
        $node
158 27
            ->children()
159 27
                ->arrayNode('auto_discover')
160 27
                    ->treatFalseLike(['bundles' => false, 'root_dir' => false])
161 27
                    ->treatTrueLike(['bundles' => true, 'root_dir' => true])
162 27
                    ->treatNullLike(['bundles' => true, 'root_dir' => true])
163 27
                    ->addDefaultsIfNotSet()
164 27
                    ->children()
165 27
                        ->booleanNode('bundles')->defaultTrue()->end()
166 27
                        ->booleanNode('root_dir')->defaultTrue()->end()
167 27
                    ->end()
168 27
                ->end()
169 27
                ->arrayNode('types')
170 27
                    ->prototype('array')
171 27
                        ->addDefaultsIfNotSet()
172 27
                        ->beforeNormalization()
173 27
                            ->ifTrue(function ($v) {
174 22
                                return isset($v['type']) && is_string($v['type']);
175 27
                            })
176 27
                            ->then(function ($v) {
177 20
                                if ('yml' === $v['type']) {
178 7
                                    $v['types'] = ['yaml'];
179
                                } else {
180 13
                                    $v['types'] = [$v['type']];
181
                                }
182 20
                                unset($v['type']);
183
184 20
                                return $v;
185 27
                            })
186 27
                        ->end()
187 27
                        ->children()
188 27
                            ->arrayNode('types')
189 27
                                ->prototype('enum')->values(array_keys(OverblogGraphQLTypesExtension::SUPPORTED_TYPES_EXTENSIONS))->isRequired()->end()
190 27
                            ->end()
191 27
                            ->scalarNode('dir')->defaultNull()->end()
192 27
                            ->scalarNode('suffix')->defaultValue(OverblogGraphQLTypesExtension::DEFAULT_TYPES_SUFFIX)->end()
193 27
                        ->end()
194 27
                    ->end()
195 27
                ->end()
196 27
            ->end()
197
        ;
198
199 27
        return $node;
200
    }
201
202
    /**
203
     * @param string $name
204
     *
205
     * @return NodeParentInterface
206
     */
207 27
    private function addBuilderSection($name)
208
    {
209 27
        $builder = new TreeBuilder();
210 27
        $node = $builder->root($name);
211 27
        $node->beforeNormalization()
212 27
            ->ifTrue(function ($v) {
213 1
                return is_array($v) && !empty($v);
214 27
            })
215 27
            ->then(function ($v) {
216 1
                foreach ($v as $key => &$config) {
217 1
                    if (is_string($config)) {
218
                        $config = [
219 1
                            'alias' => $key,
220 1
                            'class' => $config,
221
                        ];
222
                    }
223
                }
224
225 1
                return $v;
226 27
            })
227 27
        ->end();
228
229 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...
230 27
            ->children()
231 27
                ->scalarNode('alias')->isRequired()->end()
232 27
                ->scalarNode('class')->isRequired()->end()
233 27
            ->end()
234 27
        ->end()
235
        ;
236
237 27
        return $node;
238
    }
239
240
    /**
241
     * @param string $name
242
     * @param bool   $disabledValue
243
     *
244
     * @return NodeParentInterface
245
     */
246 27
    private function addSecurityQuerySection($name, $disabledValue)
247
    {
248 27
        $builder = new TreeBuilder();
249 27
        $node = $builder->root($name, 'scalar');
250 27
        $node->beforeNormalization()
251 27
                ->ifTrue(function ($v) {
252 25
                    return is_string($v) && is_numeric($v);
253 27
                })
254 27
                ->then(function ($v) {
255 2
                    return (int) $v;
256 27
                })
257 27
            ->end();
258
259
        $node
260 27
            ->info('Disabled if equal to false.')
261 27
            ->beforeNormalization()
262 27
                ->ifTrue(function ($v) {
263 25
                    return false === $v;
264 27
                })
265 27
                ->then(function () use ($disabledValue) {
266 25
                    return $disabledValue;
267 27
                })
268 27
            ->end()
269 27
            ->defaultFalse()
270 27
            ->validate()
271 27
                ->ifTrue(function ($v) {
272 25
                    return is_int($v) && $v < 0;
273 27
                })
274 27
                ->thenInvalid('"overblog_graphql.security.'.$name.'" must be greater or equal to 0.')
275 27
            ->end()
276
        ;
277
278 27
        return $node;
279
    }
280
}
281