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

Completed
Pull Request — master (#1)
by Jérémiah
08:55
created

ConfigResolver::resolveFields()   D

Complexity

Conditions 22
Paths 189

Size

Total Lines 85
Code Lines 53

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 38
CRAP Score 54.4388

Importance

Changes 3
Bugs 2 Features 1
Metric Value
c 3
b 2
f 1
dl 0
loc 85
ccs 38
cts 64
cp 0.5938
rs 4.4894
cc 22
eloc 53
nc 189
nop 1
crap 54.4388

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/*
4
 * This file is part of the OverblogGraphQLBundle package.
5
 *
6
 * (c) Overblog <http://github.com/overblog/>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Overblog\GraphQLBundle\Resolver;
13
14
use GraphQL\Type\Definition\ResolveInfo;
15
use Overblog\GraphQLBundle\Definition\Argument;
16
use Overblog\GraphQLBundle\Definition\ArgsInterface;
17
use Overblog\GraphQLBundle\Definition\FieldInterface;
18
use Overblog\GraphQLBundle\Error\UserError;
19
use Overblog\GraphQLBundle\Relay\Connection\Output\Connection;
20
use Overblog\GraphQLBundle\Relay\Connection\Output\Edge;
21
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
22
use Symfony\Component\OptionsResolver\OptionsResolver;
23
24
class ConfigResolver implements ResolverInterface
25
{
26
    /**
27
     * @var ExpressionLanguage
28
     */
29
    private $expressionLanguage;
30
31
    /**
32
     * @var TypeResolver
33
     */
34
    private $typeResolver;
35
36
    /**
37
     * @var FieldResolver
38
     */
39
    private $fieldResolver;
40
41
    /**
42
     * @var ArgResolver
43
     */
44
    private $argResolver;
45
46
    /**
47
     * @var array
48
     *            [name => callable]
49
     */
50
    private $resolverMap = [];
51
52 32
    public function __construct(
53
        ResolverInterface $typeResolver,
54
        ResolverInterface $fieldResolver,
55
        ResolverInterface $argResolver,
56
        ExpressionLanguage $expressionLanguage,
57
        $enabledDebug = false
0 ignored issues
show
Unused Code introduced by
The parameter $enabledDebug is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
58
    ) {
59 32
        $this->typeResolver = $typeResolver;
0 ignored issues
show
Documentation Bug introduced by
$typeResolver is of type object<Overblog\GraphQLB...lver\ResolverInterface>, but the property $typeResolver was declared to be of type object<Overblog\GraphQLB...\Resolver\TypeResolver>. Are you sure that you always receive this specific sub-class here, or does it make sense to add an instanceof check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a given class or a super-class is assigned to a property that is type hinted more strictly.

Either this assignment is in error or an instanceof check should be added for that assignment.

class Alien {}

class Dalek extends Alien {}

class Plot
{
    /** @var  Dalek */
    public $villain;
}

$alien = new Alien();
$plot = new Plot();
if ($alien instanceof Dalek) {
    $plot->villain = $alien;
}
Loading history...
60 32
        $this->fieldResolver = $fieldResolver;
0 ignored issues
show
Documentation Bug introduced by
$fieldResolver is of type object<Overblog\GraphQLB...lver\ResolverInterface>, but the property $fieldResolver was declared to be of type object<Overblog\GraphQLB...Resolver\FieldResolver>. Are you sure that you always receive this specific sub-class here, or does it make sense to add an instanceof check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a given class or a super-class is assigned to a property that is type hinted more strictly.

Either this assignment is in error or an instanceof check should be added for that assignment.

class Alien {}

class Dalek extends Alien {}

class Plot
{
    /** @var  Dalek */
    public $villain;
}

$alien = new Alien();
$plot = new Plot();
if ($alien instanceof Dalek) {
    $plot->villain = $alien;
}
Loading history...
61 32
        $this->argResolver = $argResolver;
0 ignored issues
show
Documentation Bug introduced by
$argResolver is of type object<Overblog\GraphQLB...lver\ResolverInterface>, but the property $argResolver was declared to be of type object<Overblog\GraphQLB...e\Resolver\ArgResolver>. Are you sure that you always receive this specific sub-class here, or does it make sense to add an instanceof check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a given class or a super-class is assigned to a property that is type hinted more strictly.

Either this assignment is in error or an instanceof check should be added for that assignment.

class Alien {}

class Dalek extends Alien {}

class Plot
{
    /** @var  Dalek */
    public $villain;
}

$alien = new Alien();
$plot = new Plot();
if ($alien instanceof Dalek) {
    $plot->villain = $alien;
}
Loading history...
62 32
        $this->expressionLanguage = $expressionLanguage;
63 32
        $this->resolverMap = [
64 32
            'fields' => [$this, 'resolveFields'],
65 32
            'isTypeOf' => [$this, 'resolveResolveCallback'],
66 32
            'interfaces' => [$this, 'resolveInterfaces'],
67 32
            'types' => [$this, 'resolveTypes'],
68 32
            'values' => [$this, 'resolveValues'],
69 32
            'resolveType' => [$this, 'resolveResolveCallback'],
70 32
            'resolveCursor' => [$this, 'resolveResolveCallback'],
71 32
            'resolveNode' => [$this, 'resolveResolveCallback'],
72 32
            'nodeType' => [$this, 'resolveTypeCallback'],
73 32
            'connectionFields' => [$this, 'resolveFields'],
74 32
            'edgeFields' => [$this, 'resolveFields'],
75 32
            'mutateAndGetPayload' => [$this, 'resolveResolveCallback'],
76 32
            'idFetcher' => [$this, 'resolveResolveCallback'],
77 32
            'nodeInterfaceType' => [$this, 'resolveTypeCallback'],
78 32
            'inputType' => [$this, 'resolveTypeCallback'],
79 32
            'outputType' => [$this, 'resolveTypeCallback'],
80 32
            'payloadType' => [$this, 'resolveTypeCallback'],
81 32
            'resolveSingleInput' => [$this, 'resolveResolveCallback'],
82
        ];
83 32
    }
84
85
    public function addResolverMap($name, callable $resolver)
86
    {
87
        $this->resolverMap[$name] = $resolver;
88
    }
89
90 27
    public function resolve($config)
91
    {
92 27
        if (!is_array($config) || $config instanceof \ArrayAccess) {
93 1
            throw new \RuntimeException('Config must be an array or implement \ArrayAccess interface');
94
        }
95
96 26
        foreach ($config as $name => &$values) {
97 26
            if (!isset($this->resolverMap[$name]) || empty($values)) {
98 25
                continue;
99
            }
100 26
            $values = call_user_func_array($this->resolverMap[$name], [$values]);
101 26
        }
102
103 26
        return $config;
104
    }
105
106 25
    private function resolveFields(array $fields)
107
    {
108 25
        foreach ($fields as $field => &$options) {
109 25
            if (isset($options['builder']) && is_string($options['builder'])) {
110 19
                $alias = $options['builder'];
111
112 19
                $fieldBuilder = $this->fieldResolver->resolve($alias);
113 19
                $builderConfig = [];
114 19
                if (isset($options['builderConfig'])) {
115 19
                    if (!is_array($options['builderConfig'])) {
116
                        $options['builderConfig'] = [$options['builderConfig']];
117
                    }
118 19
                    $builderConfig = $this->resolve($options['builderConfig']);
119 19
                }
120 19
                $builderConfig['name'] = $field;
121
122 19
                $access = isset($options['access']) ? $options['access'] : null;
123
124 19
                if ($fieldBuilder instanceof FieldInterface) {
125 19
                    $options = $fieldBuilder->toFieldDefinition($builderConfig);
126 19
                } elseif (is_callable($fieldBuilder)) {
127
                    $options = call_user_func_array($fieldBuilder, [$builderConfig]);
128
                } elseif (is_object($fieldBuilder)) {
129
                    $options = get_object_vars($fieldBuilder);
130
                } else {
131
                    throw new \RuntimeException(sprintf('Could not build field "%s".', $alias));
132
                }
133
134 19
                $options['access'] = $access;
135 19
                $options = $this->resolveResolveAndAccessIfNeeded($options);
136
137 19
                unset($options['builderConfig'], $options['builder']);
138
139 19
                continue;
140
            }
141
142 25
            if (isset($options['type'])) {
143 25
                $options['type'] = $this->resolveTypeCallback($options['type']);
144 25
            }
145
146 25
            if (isset($options['args'])) {
147
                foreach ($options['args'] as &$argsOptions) {
148
                    $argsOptions['type'] = $this->resolveTypeCallback($argsOptions['type']);
149
                    if (isset($argsOptions['defaultValue'])) {
150
                        $argsOptions['defaultValue'] = $this->resolveUsingExpressionLanguageIfNeeded($argsOptions['defaultValue']);
151
                    }
152
                }
153
            }
154
155 25
            if (isset($options['argsBuilder'])) {
156 6
                $alias = $options['argsBuilder']['name'];
157
158 6
                $argsBuilder = $this->argResolver->resolve($alias);
159 6
                $argsBuilderConfig = [];
160 6
                if (isset($options['argsBuilder']['config'])) {
161
                    if (!is_array($options['argsBuilder']['config'])) {
162
                        $options['argsBuilder']['config'] = [$options['argsBuilder']['config']];
163
                    }
164
                    $argsBuilderConfig = $this->resolve($options['argsBuilder']['config']);
165
                }
166
167 6
                $options['args'] = isset($options['args']) ? $options['args'] : [];
168
169 6
                if ($argsBuilder instanceof ArgsInterface) {
170 6
                    $options['args'] = array_merge($argsBuilder->toArgsDefinition($argsBuilderConfig), $options['args']);
171 6
                } elseif (is_callable($argsBuilder)) {
172
                    $options['args'] = array_merge(call_user_func_array($argsBuilder, [$argsBuilderConfig]), $options['args']);
173
                } elseif (is_object($argsBuilder)) {
174
                    $options['args'] = array_merge(get_object_vars($argsBuilder), $options['args']);
175
                } else {
176
                    throw new \RuntimeException(sprintf('Could not build args "%s".', $alias));
177
                }
178
179 6
                unset($options['argsBuilder']);
180 6
            }
181
182 25
            $options = $this->resolveResolveAndAccessIfNeeded($options);
183
184 25
            if (isset($options['deprecationReason'])) {
185
                $options['deprecationReason'] = $this->resolveUsingExpressionLanguageIfNeeded($options['deprecationReason']);
186
            }
187 25
        }
188
189 25
        return $fields;
190
    }
191
192 25
    private function resolveResolveAndAccessIfNeeded(array $options)
193
    {
194 25
        $treatedOptions = $options;
195
196 25
        if (isset($treatedOptions['resolve'])) {
197 25
            $treatedOptions['resolve'] = $this->resolveResolveCallback($treatedOptions['resolve']);
198 25
        }
199
200 25
        if (isset($treatedOptions['access'])) {
201
            $resolveCallback = ['GraphQL\Executor\Executor', 'defaultResolveFn'];
202
203
            if (isset($treatedOptions['resolve'])) {
204
                $resolveCallback = $treatedOptions['resolve'];
205
            }
206
207
            $treatedOptions['resolve'] = $this->resolveAccessAndWrapResolveCallback($treatedOptions['access'], $resolveCallback);
208
        }
209 25
        unset($treatedOptions['access']);
210
211 25
        return $treatedOptions;
212
    }
213
214 25
    private function resolveTypeCallback($expr)
215
    {
216
        return function () use ($expr) {
217 25
            return $this->resolveType($expr);
218 25
        };
219
    }
220
221 11
    private function resolveInterfaces(array $rawInterfaces)
222
    {
223 11
        return $this->resolveTypes($rawInterfaces, 'GraphQL\\Type\\Definition\\InterfaceType');
224
    }
225
226 11
    private function resolveTypes(array $rawTypes, $parentClass = 'GraphQL\\Type\\Definition\\Type')
227
    {
228 11
        $types = [];
229
230 11
        foreach ($rawTypes as $alias) {
231 11
            $types[] = $this->resolveType($alias, $parentClass);
232 11
        }
233
234 11
        return $types;
235
    }
236
237 25
    private function resolveType($expr, $parentClass = 'GraphQL\\Type\\Definition\\Type')
238
    {
239 25
        $type = $this->typeResolver->resolve($expr);
240
241 25
        if (class_exists($parentClass) && !$type instanceof $parentClass) {
242
            throw new \InvalidArgumentException(
243
                sprintf('Invalid type! Must be instance of "%s"', $parentClass)
244
            );
245
        }
246
247 25
        return $type;
248
    }
249
250 5
    private function resolveAccessAndWrapResolveCallback($expression, callable $resolveCallback = null)
251
    {
252
        return function () use ($expression, $resolveCallback) {
253 5
            $args = func_get_args();
254
255 5
            $result = null !== $resolveCallback  ? call_user_func_array($resolveCallback, $args) : null;
256
257 5
            $values = call_user_func_array([$this, 'resolveResolveCallbackArgs'], $args);
258
259
            $checkAccess = function ($object, $throwException = false) use ($expression, $values) {
260
                try {
261 5
                    $access = $this->resolveUsingExpressionLanguageIfNeeded(
262 5
                        $expression,
263 5
                        array_merge($values, ['object' => $object])
264 5
                    );
265 5
                } catch (\Exception $e) {
266 1
                    $access = false;
267
                }
268
269 5
                if ($throwException && !$access) {
270 2
                    throw new UserError('Access denied to this field.');
271
                }
272
273 3
                return $access;
274 5
            };
275
276 5
            switch (true) {
277 5
                case is_array($result) || $result instanceof \ArrayAccess:
278 1
                    $result = array_filter(
279 1
                        array_map(
280
                            function ($object) use ($checkAccess) {
281 1
                                return $checkAccess($object) ? $object : null;
282 1
                            },
283
                            $result
284 1
                        )
285 1
                    );
286 1
                    break;
287
288 4
                case $result instanceof Connection:
289 1
                    $result->edges = array_map(
290
                        function (Edge $edge) use ($checkAccess) {
291 1
                            $edge->node = $checkAccess($edge->node) ? $edge->node : null;
292
293 1
                            return $edge;
294 1
                        },
295 1
                        $result->edges
296 1
                    );
297 1
                    break;
298
299 3
                default:
300 3
                    $checkAccess($result, true);
301 1
                    break;
302 3
            }
303
304 3
            return $result;
305 5
        };
306
    }
307
308 25
    private function resolveResolveCallback($value)
309
    {
310 25
        if (is_callable($value)) {
311 19
            return $value;
312
        }
313
314 25
        return function () use ($value) {
315 18
            $args = func_get_args();
316 18
            $result = $this->resolveUsingExpressionLanguageIfNeeded(
317 18
                $value,
318 18
                call_user_func_array([$this, 'resolveResolveCallbackArgs'], $args)
319 18
            );
320
321 18
            return $result;
322 25
        };
323
    }
324
325 23
    private function resolveResolveCallbackArgs()
326
    {
327 23
        $args = func_get_args();
328 23
        $optionResolver = new OptionsResolver();
329 23
        $optionResolver->setDefaults([null, null, null]);
330
331 23
        $args = $optionResolver->resolve($args);
332
333 23
        $arg1IsResolveInfo = $args[1] instanceof ResolveInfo;
334
335 23
        $value = $args[0];
336
        /** @var ResolveInfo $info */
337 23
        $info = $arg1IsResolveInfo ? $args[1] : $args[2];
338
        /** @var Argument $resolverArgs */
339 23
        $resolverArgs = new Argument(!$arg1IsResolveInfo ? $args[1] : []);
340
341
        return [
342 23
            'value' => $value,
343 23
            'args' => $resolverArgs,
344 23
            'info' => $info,
345 23
        ];
346
    }
347
348 1
    private function resolveValues(array $rawValues)
349
    {
350 1
        $values = $rawValues;
351
352 1
        foreach ($values as $name => &$options) {
353 1
            if (isset($options['value'])) {
354 1
                $options['value'] = $this->resolveUsingExpressionLanguageIfNeeded($options['value']);
355 1
            }
356 1
        }
357
358 1
        return $values;
359
    }
360
361 24
    private function resolveUsingExpressionLanguageIfNeeded($expression, array $values = [])
362
    {
363 24
        if (is_string($expression) &&  0 === strpos($expression, '@=')) {
364 21
            return $this->expressionLanguage->evaluate(substr($expression, 2), $values);
365
        }
366
367 8
        return $expression;
368
    }
369
}
370