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 — annotations (#407)
by Vincent
23:05
created

resolveGraphqlTypeFromReflectionType()   B

Complexity

Conditions 7
Paths 4

Size

Total Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 8.323

Importance

Changes 0
Metric Value
dl 0
loc 17
c 0
b 0
f 0
ccs 7
cts 10
cp 0.7
rs 8.8333
cc 7
nc 4
nop 3
crap 8.323
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Overblog\GraphQLBundle\Config\Parser;
6
7
use Doctrine\Common\Annotations\AnnotationReader;
8
use Doctrine\Common\Annotations\AnnotationRegistry;
9
use Overblog\GraphQLBundle\Annotation as GQL;
10
use Symfony\Component\Config\Resource\FileResource;
11
use Symfony\Component\DependencyInjection\ContainerBuilder;
12
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
13
14
class AnnotationParser implements PreParserInterface
15
{
16
    public const CLASSESMAP_CONTAINER_PARAMETER = 'overblog_graphql_types.classes_map';
17
18
    private static $annotationReader = null;
19
    private static $classesMap = [];
20
    private static $providers = [];
21
    private static $doctrineMapping = [];
22
23
    /**
24
     * {@inheritdoc}
25
     *
26
     * @throws \ReflectionException
27
     * @throws InvalidArgumentException
28
     */
29 9
    public static function preParse(\SplFileInfo $file, ContainerBuilder $container, array $configs = []): void
30
    {
31 9
        self::proccessFile($file, $container, $configs, true);
32 9
    }
33
34
    /**
35
     * {@inheritdoc}
36
     *
37
     * @throws \ReflectionException
38
     * @throws InvalidArgumentException
39
     */
40 9
    public static function parse(\SplFileInfo $file, ContainerBuilder $container, array $configs = []): array
41
    {
42 9
        return self::proccessFile($file, $container, $configs);
43
    }
44
45
    /**
46
     * Clear the Annotation parser.
47
     */
48 9
    public static function clear(): void
49
    {
50 9
        self::$classesMap = [];
51 9
        self::$providers = [];
52 9
        self::$annotationReader = null;
53 9
    }
54
55
    /**
56
     * Process a file.
57
     *
58
     * @param \SplFileInfo     $file
59
     * @param ContainerBuilder $container
60
     * @param bool             $resolveClassMap
61
     *
62
     * @throws \ReflectionException
63
     * @throws InvalidArgumentException
64
     */
65 9
    public static function proccessFile(\SplFileInfo $file, ContainerBuilder $container, array $configs, bool $resolveClassMap = false): array
66
    {
67 9
        self::$doctrineMapping = $configs['doctrine']['types_mapping'];
68
69 9
        $rootQueryType = $configs['definitions']['schema']['default']['query'] ?? false;
70 9
        $rootMutationType = $configs['definitions']['schema']['default']['mutation'] ?? false;
71
72 9
        $container->addResource(new FileResource($file->getRealPath()));
73
74 9
        if (!$resolveClassMap) {
75 9
            $container->setParameter(self::CLASSESMAP_CONTAINER_PARAMETER, self::$classesMap);
76
        }
77
78
        try {
79 9
            $fileContent = \file_get_contents($file->getRealPath());
80
81 9
            $shortClassName = \substr($file->getFilename(), 0, -4);
82 9
            if (\preg_match('#namespace (.+);#', $fileContent, $namespace)) {
83 9
                $className = $namespace[1].'\\'.$shortClassName;
84 9
                $namespace = $namespace[1];
85
            } else {
86
                $className = $shortClassName;
87
            }
88
89 9
            $reflexionEntity = new \ReflectionClass($className);
90
91 9
            $classAnnotations = self::getAnnotationReader()->getClassAnnotations($reflexionEntity);
92
93 9
            $properties = [];
94 9
            foreach ($reflexionEntity->getProperties() as $property) {
95 9
                $properties[$property->getName()] = ['property' => $property, 'annotations' => self::getAnnotationReader()->getPropertyAnnotations($property)];
96
            }
97
98 9
            $methods = [];
99 9
            foreach ($reflexionEntity->getMethods() as $method) {
100 9
                $methods[$method->getName()] = ['method' => $method, 'annotations' => self::getAnnotationReader()->getMethodAnnotations($method)];
0 ignored issues
show
Bug introduced by
Consider using $method->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
101
            }
102
103 9
            $gqlTypes = [];
104
105 9
            foreach ($classAnnotations as $classAnnotation) {
106 9
                $gqlConfiguration = $gqlType = $gqlName = false;
107
108
                switch (true) {
109 9
                    case $classAnnotation instanceof GQL\Type:
110 9
                        $gqlType = 'type';
111 9
                        $gqlName = $classAnnotation->name ?: $shortClassName;
112 9
                        if (!$resolveClassMap) {
113 9
                            $isRootQuery = ($rootQueryType && $gqlName === $rootQueryType);
114 9
                            $isRootMutation = ($rootMutationType && $gqlName === $rootMutationType);
115 9
                            $currentValue = ($isRootQuery || $isRootMutation) ? \sprintf("service('%s')", self::formatNamespaceForExpression($className)) : 'value';
116
117 9
                            $gqlConfiguration = self::getGraphqlType($classAnnotation, $classAnnotations, $properties, $methods, $namespace, $currentValue);
0 ignored issues
show
Bug introduced by
It seems like $namespace can also be of type array<integer,string>; however, Overblog\GraphQLBundle\C...arser::getGraphqlType() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
118 9
                            $providerFields = self::getGraphqlFieldsFromProviders($namespace, $className, $isRootMutation ? 'Mutation' : 'Query', $gqlName, ($isRootQuery || $isRootMutation));
0 ignored issues
show
Bug introduced by
It seems like $namespace can also be of type array<integer,string>; however, Overblog\GraphQLBundle\C...qlFieldsFromProviders() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
119 9
                            $gqlConfiguration['config']['fields'] = $providerFields + $gqlConfiguration['config']['fields'];
120
                        }
121 9
                        break;
122 9
                    case $classAnnotation instanceof GQL\Input:
123 9
                        $gqlType = 'input';
124 9
                        $gqlName = $classAnnotation->name ?: self::suffixName($shortClassName, 'Input');
125 9
                        if (!$resolveClassMap) {
126 9
                            $gqlConfiguration = self::getGraphqlInput($classAnnotation, $classAnnotations, $properties, $namespace);
0 ignored issues
show
Bug introduced by
It seems like $namespace can also be of type array<integer,string>; however, Overblog\GraphQLBundle\C...rser::getGraphqlInput() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
127
                        }
128 9
                        break;
129 9
                    case $classAnnotation instanceof GQL\Scalar:
130 9
                        $gqlType = 'scalar';
131 9
                        if (!$resolveClassMap) {
132 9
                            $gqlConfiguration = self::getGraphqlScalar($className, $classAnnotation, $classAnnotations);
133
                        }
134 9
                        break;
135 9
                    case $classAnnotation instanceof GQL\Enum:
136 9
                        $gqlType = 'enum';
137 9
                        if (!$resolveClassMap) {
138 9
                            $gqlConfiguration = self::getGraphqlEnum($classAnnotation, $classAnnotations, $reflexionEntity->getConstants());
139
                        }
140 9
                        break;
141 9
                    case $classAnnotation instanceof GQL\Union:
142 9
                        $gqlType = 'union';
143 9
                        if (!$resolveClassMap) {
144 9
                            $gqlConfiguration = self::getGraphqlUnion($className, $classAnnotation, $classAnnotations, $methods);
145
                        }
146 9
                        break;
147 9
                    case $classAnnotation instanceof GQL\TypeInterface:
148 9
                        $gqlType = 'interface';
149 9
                        if (!$resolveClassMap) {
150 9
                            $gqlConfiguration = self::getGraphqlInterface($classAnnotation, $classAnnotations, $properties, $methods, $namespace);
0 ignored issues
show
Bug introduced by
It seems like $namespace can also be of type array<integer,string>; however, Overblog\GraphQLBundle\C...::getGraphqlInterface() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
151
                        }
152 9
                        break;
153 9
                    case $classAnnotation instanceof GQL\Provider:
154 9
                        if ($resolveClassMap) {
155 9
                            self::$providers[$className] = ['annotation' => $classAnnotation, 'methods' => $methods];
156
                        }
157 9
                        break;
158
                    default:
159 9
                        continue;
160
                }
161
162 9
                if ($gqlType) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $gqlType of type string|false is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
163 9
                    if (!$gqlName) {
164 9
                        $gqlName = $classAnnotation->name ?: $shortClassName;
165
                    }
166
167 9
                    if ($resolveClassMap) {
168 9
                        if (isset(self::$classesMap[$gqlName])) {
169 1
                            throw new InvalidArgumentException(\sprintf('The GraphQL type "%s" has already been registered in class "%s"', $gqlName, self::$classesMap[$gqlName]['class']));
170
                        }
171 9
                        self::$classesMap[$gqlName] = ['type' => $gqlType, 'class' => $className];
172
                    } else {
173 9
                        $gqlTypes = [$gqlName => $gqlConfiguration] + $gqlTypes;
174
                    }
175
                }
176
            }
177
178 9
            return $resolveClassMap ? self::$classesMap : $gqlTypes;
179 1
        } catch (\InvalidArgumentException $e) {
180 1
            throw new InvalidArgumentException(\sprintf('Failed to parse GraphQL annotations from file "%s".', $file), $e->getCode(), $e);
181
        }
182
    }
183
184
    /**
185
     * Retrieve annotation reader.
186
     *
187
     * @return AnnotationReader
188
     */
189 9
    private static function getAnnotationReader()
190
    {
191 9
        if (null === self::$annotationReader) {
192 9
            if (!\class_exists('\\Doctrine\\Common\\Annotations\\AnnotationReader') ||
193 9
                !\class_exists('\\Doctrine\\Common\\Annotations\\AnnotationRegistry')) {
194
                throw new \Exception('In order to use graphql annotation, you need to require doctrine annotations');
195
            }
196
197 9
            AnnotationRegistry::registerLoader('class_exists');
198 9
            self::$annotationReader = new AnnotationReader();
199
        }
200
201 9
        return self::$annotationReader;
202
    }
203
204
    /**
205
     * Create a GraphQL Type configuration from annotations on class, properties and methods.
206
     *
207
     * @param GQL\Type $typeAnnotation
208
     * @param array    $classAnnotations
209
     * @param array    $properties
210
     * @param array    $methods
211
     * @param string   $namespace
212
     * @param string   $currentValue
213
     *
214
     * @return array
215
     */
216 9
    private static function getGraphqlType(GQL\Type $typeAnnotation, array $classAnnotations, array $properties, array $methods, string $namespace, string $currentValue)
217
    {
218 9
        $typeConfiguration = [];
219
220 9
        $fields = self::getGraphqlFieldsFromAnnotations($namespace, $properties, false, false, $currentValue);
221 9
        $fields = self::getGraphqlFieldsFromAnnotations($namespace, $methods, false, true, $currentValue) + $fields;
222
223 9
        $typeConfiguration['fields'] = $fields;
224 9
        $typeConfiguration = self::getDescriptionConfiguration($classAnnotations) + $typeConfiguration;
225
226 9
        if ($typeAnnotation->interfaces) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $typeAnnotation->interfaces of type string[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
227 9
            $typeConfiguration['interfaces'] = $typeAnnotation->interfaces;
228
        }
229
230 9
        if ($typeAnnotation->resolveField) {
231
            $typeConfiguration['resolveField'] = self::formatExpression($typeAnnotation->resolveField);
232
        }
233
234 9
        $publicAnnotation = self::getFirstAnnotationMatching($classAnnotations, 'Overblog\GraphQLBundle\Annotation\IsPublic');
235 9
        if ($publicAnnotation) {
236 9
            $typeConfiguration['fieldsDefaultPublic'] = self::formatExpression($publicAnnotation->value);
237
        }
238
239 9
        $accessAnnotation = self::getFirstAnnotationMatching($classAnnotations, 'Overblog\GraphQLBundle\Annotation\Access');
240 9
        if ($accessAnnotation) {
241 9
            $typeConfiguration['fieldsDefaultAccess'] = self::formatExpression($accessAnnotation->value);
242
        }
243
244 9
        return ['type' => $typeAnnotation->isRelay ? 'relay-mutation-payload' : 'object', 'config' => $typeConfiguration];
245
    }
246
247
    /**
248
     * Create a GraphQL Interface type configuration from annotations on properties.
249
     *
250
     * @param string        $shortClassName
0 ignored issues
show
Bug introduced by
There is no parameter named $shortClassName. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
251
     * @param GQL\Interface $interfaceAnnotation
252
     * @param array         $properties
253
     * @param array         $methods
254
     * @param string        $namespace
255
     *
256
     * @return array
257
     */
258 9
    private static function getGraphqlInterface(GQL\TypeInterface $interfaceAnnotation, array $classAnnotations, array $properties, array $methods, string $namespace)
259
    {
260 9
        $interfaceConfiguration = [];
261
262 9
        $fields = self::getGraphqlFieldsFromAnnotations($namespace, $properties);
263 9
        $fields = self::getGraphqlFieldsFromAnnotations($namespace, $methods, false, true) + $fields;
264
265 9
        $interfaceConfiguration['fields'] = $fields;
266 9
        $interfaceConfiguration = self::getDescriptionConfiguration($classAnnotations) + $interfaceConfiguration;
267
268 9
        $interfaceConfiguration['resolveType'] = self::formatExpression($interfaceAnnotation->resolveType);
269
270 9
        return ['type' => 'interface', 'config' => $interfaceConfiguration];
271
    }
272
273
    /**
274
     * Create a GraphQL Input type configuration from annotations on properties.
275
     *
276
     * @param string    $shortClassName
0 ignored issues
show
Bug introduced by
There is no parameter named $shortClassName. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
277
     * @param GQL\Input $inputAnnotation
278
     * @param array     $properties
279
     * @param string    $namespace
280
     *
281
     * @return array
282
     */
283 9
    private static function getGraphqlInput(GQL\Input $inputAnnotation, array $classAnnotations, array $properties, string $namespace)
284
    {
285 9
        $inputConfiguration = [];
286 9
        $fields = self::getGraphqlFieldsFromAnnotations($namespace, $properties, true);
287
288 9
        $inputConfiguration['fields'] = $fields;
289 9
        $inputConfiguration = self::getDescriptionConfiguration($classAnnotations) + $inputConfiguration;
290
291 9
        return ['type' => $inputAnnotation->isRelay ? 'relay-mutation-input' : 'input-object', 'config' => $inputConfiguration];
292
    }
293
294
    /**
295
     * Get a Graphql scalar configuration from given scalar annotation.
296
     *
297
     * @param string     $shortClassName
0 ignored issues
show
Documentation introduced by
There is no parameter named $shortClassName. Did you maybe mean $className?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
298
     * @param string     $className
299
     * @param GQL\Scalar $scalarAnnotation
300
     * @param array      $classAnnotations
301
     *
302
     * @return array
303
     */
304 9
    private static function getGraphqlScalar(string $className, GQL\Scalar $scalarAnnotation, array $classAnnotations)
305
    {
306 9
        $scalarConfiguration = [];
307
308 9
        if ($scalarAnnotation->scalarType) {
309 9
            $scalarConfiguration['scalarType'] = self::formatExpression($scalarAnnotation->scalarType);
310
        } else {
311
            $scalarConfiguration = [
312 9
                'serialize' => [$className, 'serialize'],
313 9
                'parseValue' => [$className, 'parseValue'],
314 9
                'parseLiteral' => [$className, 'parseLiteral'],
315
            ];
316
        }
317
318 9
        $scalarConfiguration = self::getDescriptionConfiguration($classAnnotations) + $scalarConfiguration;
319
320 9
        return ['type' => 'custom-scalar', 'config' => $scalarConfiguration];
321
    }
322
323
    /**
324
     * Get a Graphql Enum configuration from given enum annotation.
325
     *
326
     * @param string   $shortClassName
0 ignored issues
show
Bug introduced by
There is no parameter named $shortClassName. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
327
     * @param GQL\Enum $enumAnnotation
328
     * @param array    $classAnnotations
329
     * @param array    $constants
330
     *
331
     * @return array
332
     */
333 9
    private static function getGraphqlEnum(GQL\Enum $enumAnnotation, array $classAnnotations, array $constants)
334
    {
335 9
        $enumValues = $enumAnnotation->values ? $enumAnnotation->values : [];
336
337 9
        $values = [];
338
339 9
        foreach ($constants as $name => $value) {
340
            $valueAnnotation = \current(\array_filter($enumValues, function ($enumValueAnnotation) use ($name) {
341 9
                return $enumValueAnnotation->name == $name;
342 9
            }));
343 9
            $valueConfig = [];
344 9
            $valueConfig['value'] = $value;
345
346 9
            if ($valueAnnotation && $valueAnnotation->description) {
347 9
                $valueConfig['description'] = $valueAnnotation->description;
348
            }
349
350 9
            if ($valueAnnotation && $valueAnnotation->deprecationReason) {
351 9
                $valueConfig['deprecationReason'] = $valueAnnotation->deprecationReason;
352
            }
353
354 9
            $values[$name] = $valueConfig;
355
        }
356
357 9
        $enumConfiguration = ['values' => $values];
358 9
        $enumConfiguration = self::getDescriptionConfiguration($classAnnotations) + $enumConfiguration;
359
360 9
        return ['type' => 'enum', 'config' => $enumConfiguration];
361
    }
362
363
    /**
364
     * Get a Graphql Union configuration from given union annotation.
365
     *
366
     * @param string    $className
367
     * @param GQL\Union $unionAnnotation
368
     * @param array     $classAnnotations
369
     * @param array     $methods
370
     *
371
     * @return array
372
     */
373 9
    private static function getGraphqlUnion(string $className, GQL\Union $unionAnnotation, array $classAnnotations, array $methods): array
374
    {
375 9
        $unionConfiguration = ['types' => $unionAnnotation->types];
376 9
        $unionConfiguration = self::getDescriptionConfiguration($classAnnotations) + $unionConfiguration;
377
378 9
        if ($unionAnnotation->resolveType) {
379 9
            $unionConfiguration['resolveType'] = self::formatExpression($unionAnnotation->resolveType);
380
        } else {
381 9
            if (isset($methods['resolveType'])) {
382 9
                $method = $methods['resolveType']['method'];
383 9
                if ($method->isStatic() && $method->isPublic()) {
384 9
                    $unionConfiguration['resolveType'] = self::formatExpression(\sprintf("@=call('%s::%s', [service('overblog_graphql.type_resolver'), value], true)", self::formatNamespaceForExpression($className), 'resolveType'));
385
                } else {
386 9
                    throw new InvalidArgumentException(\sprintf('The "resolveType()" method on class must be static and public. Or you must define a "resolveType" attribute on the @Union annotation.'));
387
                }
388
            } else {
389
                throw new InvalidArgumentException(\sprintf('The annotation @Union has no "resolveType" attribute and the related class has no "resolveType()" public static method. You need to define of them.'));
390
            }
391
        }
392
393 9
        return ['type' => 'union', 'config' => $unionConfiguration];
394
    }
395
396
    /**
397
     * Create Graphql fields configuration based on annotations.
398
     *
399
     * @param string $namespace
400
     * @param array  $propertiesOrMethods
401
     * @param bool   $isInput
402
     * @param bool   $isMethod
403
     * @param string $currentValue
404
     *
405
     * @return array
406
     */
407 9
    private static function getGraphqlFieldsFromAnnotations(string $namespace, array $propertiesOrMethods, bool $isInput = false, bool $isMethod = false, string $currentValue = 'value', string $fieldAnnotationName = 'Field'): array
408
    {
409 9
        $fields = [];
410 9
        foreach ($propertiesOrMethods as $target => $config) {
411 9
            $annotations = $config['annotations'];
412 9
            $method = $isMethod ? $config['method'] : false;
413 9
            $property = $isMethod ? false : $config['property'];
0 ignored issues
show
Unused Code introduced by
$property is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
414
415 9
            $fieldAnnotation = self::getFirstAnnotationMatching($annotations, \sprintf('Overblog\GraphQLBundle\Annotation\%s', $fieldAnnotationName));
416 9
            $accessAnnotation = self::getFirstAnnotationMatching($annotations, 'Overblog\GraphQLBundle\Annotation\Access');
417 9
            $publicAnnotation = self::getFirstAnnotationMatching($annotations, 'Overblog\GraphQLBundle\Annotation\IsPublic');
418
419 9
            if (!$fieldAnnotation) {
420
                if ($accessAnnotation || $publicAnnotation) {
421
                    throw new InvalidArgumentException(\sprintf('The annotations "@Access" and/or "@Visible" defined on "%s" are only usable in addition of annotation "@Field"', $target));
422
                }
423
                continue;
424
            }
425
426 9
            if ($isMethod && !$method->isPublic()) {
427
                throw new InvalidArgumentException(\sprintf('The Annotation "@Field" can only be applied to public method. The method "%s" is not public.', $target));
428
            }
429
430
            // Ignore field with resolver when the type is an Input
431 9
            if ($fieldAnnotation->resolve && $isInput) {
432
                continue;
433
            }
434
435 9
            $fieldName = $target;
436 9
            $fieldType = $fieldAnnotation->type;
437 9
            $fieldConfiguration = [];
438 9
            if ($fieldType) {
439 9
                $resolvedType = self::resolveClassFromType($fieldType);
440 9
                if ($resolvedType) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $resolvedType of type string|false is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
441 9
                    if ($isInput && !\in_array($resolvedType['type'], ['input', 'scalar', 'enum'])) {
442
                        throw new InvalidArgumentException(\sprintf('The type "%s" on "%s" is a "%s" not valid on an Input @Field. Only Input, Scalar and Enum are allowed.', $fieldType, $target, $resolvedType['type']));
443
                    }
444
                }
445
446 9
                $fieldConfiguration['type'] = $fieldType;
447
            }
448
449 9
            $fieldConfiguration = self::getDescriptionConfiguration($annotations, true) + $fieldConfiguration;
450
451 9
            if (!$isInput) {
452 9
                $args = [];
0 ignored issues
show
Unused Code introduced by
$args is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
453 9
                $args = self::getArgs($fieldAnnotation->args, $isMethod && !$fieldAnnotation->argsBuilder ? $method : null);
454
455 9
                if (!empty($args)) {
456 9
                    $fieldConfiguration['args'] = $args;
457
                }
458
459 9
                $fieldName = $fieldAnnotation->name ?: $fieldName;
460
461 9
                if ($fieldAnnotation->resolve) {
462 9
                    $fieldConfiguration['resolve'] = self::formatExpression($fieldAnnotation->resolve);
463
                } else {
464 9
                    if ($isMethod) {
465 9
                        $fieldConfiguration['resolve'] = self::formatExpression(\sprintf('call(%s.%s, %s)', $currentValue, $target, self::formatArgsForExpression($args)));
466
                    } else {
467 9
                        if ($fieldName !== $target || 'value' !== $currentValue) {
468
                            $fieldConfiguration['resolve'] = self::formatExpression(\sprintf('%s.%s', $currentValue, $target));
469
                        }
470
                    }
471
                }
472
473 9
                if ($fieldAnnotation->argsBuilder) {
474 9
                    if (\is_string($fieldAnnotation->argsBuilder)) {
475
                        $fieldConfiguration['argsBuilder'] = $fieldAnnotation->argsBuilder;
476 9
                    } elseif (\is_array($fieldAnnotation->argsBuilder)) {
477 9
                        list($builder, $builderConfig) = $fieldAnnotation->argsBuilder;
478 9
                        $fieldConfiguration['argsBuilder'] = ['builder' => $builder, 'config' => $builderConfig];
479
                    } else {
480
                        throw new InvalidArgumentException(\sprintf('The attribute "argsBuilder" on GraphQL annotation "@%s" defined on "%s" must be a string or an array where first index is the builder name and the second is the config.', $fieldAnnotationName, $target));
481
                    }
482
                }
483
484 9
                if ($fieldAnnotation->fieldBuilder) {
485 9
                    if (\is_string($fieldAnnotation->fieldBuilder)) {
486
                        $fieldConfiguration['builder'] = $fieldAnnotation->fieldBuilder;
487 9
                    } elseif (\is_array($fieldAnnotation->fieldBuilder)) {
488 9
                        list($builder, $builderConfig) = $fieldAnnotation->fieldBuilder;
489 9
                        $fieldConfiguration['builder'] = $builder;
490 9
                        $fieldConfiguration['builderConfig'] = $builderConfig ?: [];
491
                    } else {
492 9
                        throw new InvalidArgumentException(\sprintf('The attribute "argsBuilder" on GraphQL annotation "@%s" defined on "%s" must be a string or an array where first index is the builder name and the second is the config.', $fieldAnnotationName, $target));
493
                    }
494
                } else {
495 9
                    if (!$fieldType) {
496 9
                        if ($isMethod) {
497 9
                            if ($method->hasReturnType()) {
498
                                try {
499 9
                                    $fieldConfiguration['type'] = self::resolveGraphqlTypeFromReflectionType($method->getReturnType(), 'type');
500
                                } catch (\Exception $e) {
501 9
                                    throw new InvalidArgumentException(\sprintf('The attribute "type" on GraphQL annotation "@%s" is missing on method "%s" and cannot be auto-guessed from type hint "%s"', $fieldAnnotationName, $target, (string) $method->getReturnType()));
502
                                }
503
                            } else {
504 9
                                throw new InvalidArgumentException(\sprintf('The attribute "type" on GraphQL annotation "@%s" is missing on method "%s" and cannot be auto-guessed as there is not return type hint.', $fieldAnnotationName, $target));
505
                            }
506
                        } else {
507
                            try {
508 9
                                $fieldConfiguration['type'] = self::guessType($namespace, $annotations);
509
                            } catch (\Exception $e) {
510
                                throw new InvalidArgumentException(\sprintf('The attribute "type" on "@%s" defined on "%s" is required and cannot be auto-guessed : %s.', $fieldAnnotationName, $target, $e->getMessage()));
511
                            }
512
                        }
513
                    }
514
                }
515
516 9
                if ($accessAnnotation) {
517 9
                    $fieldConfiguration['access'] = self::formatExpression($accessAnnotation->value);
518
                }
519
520 9
                if ($publicAnnotation) {
521 9
                    $fieldConfiguration['public'] = self::formatExpression($publicAnnotation->value);
522
                }
523
524 9
                if ($fieldAnnotation->complexity) {
525 9
                    $fieldConfiguration['complexity'] = self::formatExpression($fieldAnnotation->complexity);
526
                }
527
            }
528
529 9
            $fields[$fieldName] = $fieldConfiguration;
530
        }
531
532 9
        return $fields;
533
    }
534
535
    /**
536
     * Return fields config from Provider methods.
537
     *
538
     * @param string $className
539
     * @param array  $methods
0 ignored issues
show
Bug introduced by
There is no parameter named $methods. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
540
     * @param bool   $isMutation
0 ignored issues
show
Bug introduced by
There is no parameter named $isMutation. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
541
     *
542
     * @return array
543
     */
544 9
    private static function getGraphqlFieldsFromProviders(string $namespace, string $className, string $annotationName, string $targetType, bool $isRoot = false)
0 ignored issues
show
Unused Code introduced by
The parameter $className 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...
545
    {
546 9
        $fields = [];
547 9
        foreach (self::$providers as $className => $configuration) {
548 9
            $providerMethods = $configuration['methods'];
549 9
            $providerAnnotation = $configuration['annotation'];
550
551 9
            $filteredMethods = [];
552 9
            foreach ($providerMethods as $methodName => $config) {
553 9
                $annotations = $config['annotations'];
554
555 9
                $annotation = self::getFirstAnnotationMatching($annotations, \sprintf('Overblog\\GraphQLBundle\\Annotation\\%s', $annotationName));
556 9
                if (!$annotation) {
557 9
                    continue;
558
                }
559
560 9
                $annotationTarget = 'Query' === $annotationName ? $annotation->targetType : null;
561 9
                if (!$annotationTarget && $isRoot) {
562 9
                    $annotationTarget = $targetType;
563
                }
564
565 9
                if ($annotationTarget !== $targetType) {
566 9
                    continue;
567
                }
568
569 9
                $filteredMethods[$methodName] = $config;
570
            }
571
572 9
            $currentValue = \sprintf("service('%s')", self::formatNamespaceForExpression($className));
573 9
            $providerFields = self::getGraphqlFieldsFromAnnotations($namespace, $filteredMethods, false, true, $currentValue, $annotationName);
574 9
            foreach ($providerFields as $fieldName => $fieldConfig) {
575 9
                if ($providerAnnotation->prefix) {
576 9
                    $fieldName = \sprintf('%s%s', $providerAnnotation->prefix, $fieldName);
577
                }
578 9
                $fields[$fieldName] = $fieldConfig;
579
            }
580
        }
581
582 9
        return $fields;
583
    }
584
585
    /**
586
     * Get the config for description & deprecation reason.
587
     *
588
     * @param array $annotations
589
     * @param bool  $withDeprecation
590
     *
591
     * @return array
592
     */
593 9
    private static function getDescriptionConfiguration(array $annotations, bool $withDeprecation = false)
594
    {
595 9
        $config = [];
596 9
        $descriptionAnnotation = self::getFirstAnnotationMatching($annotations, 'Overblog\GraphQLBundle\Annotation\Description');
597 9
        if ($descriptionAnnotation) {
598 9
            $config['description'] = $descriptionAnnotation->value;
599
        }
600
601 9
        if ($withDeprecation) {
602 9
            $deprecatedAnnotation = self::getFirstAnnotationMatching($annotations, 'Overblog\GraphQLBundle\Annotation\Deprecated');
603 9
            if ($deprecatedAnnotation) {
604
                $config['deprecationReason'] = $deprecatedAnnotation->value;
605
            }
606
        }
607
608 9
        return $config;
609
    }
610
611
    /**
612
     * Get args config from an array of @Arg annotation or by auto-guessing if a method is provided.
613
     *
614
     * @param array             $args
615
     * @param \ReflectionMethod $method
616
     *
617
     * @return array
618
     */
619 9
    private static function getArgs(array $args = null, \ReflectionMethod $method = null)
620
    {
621 9
        $config = [];
622 9
        if ($args && !empty($args)) {
623 9
            foreach ($args as $arg) {
624 9
                $config[$arg->name] = ['type' => $arg->type] + ($arg->description ? ['description' => $arg->description] : []);
625
            }
626 9
        } elseif ($method) {
627 9
            $config = self::guessArgs($method);
628
        }
629
630 9
        return $config;
631
    }
632
633 9
    private static function formatArgsForExpression(array $args)
634
    {
635 9
        $mapping = [];
636 9
        foreach ($args as $name => $config) {
637 9
            $mapping[] = \sprintf('%s: "%s"', $name, $config['type']);
638
        }
639
640 9
        return \sprintf('arguments({%s}, args)', \implode(', ', $mapping));
641
    }
642
643
    /**
644
     * Format an array of args to a list of arguments in an expression.
645
     *
646
     * @param array $args
647
     *
648
     * @return string
649
     */
650
    /*
651
    private static function formatArgsForExpression(array $args)
652
    {
653
        $resolvedArgs = [];
654
        foreach ($args as $name => $config) {
655
            $cleanedType = \str_replace(['[', ']', '!'], '', $config['type']);
656
            $definition = self::resolveClassFromType($cleanedType);
657
            $defaultFormat = \sprintf("args['%s']", $name);
658
            if (!$definition) {
659
                $resolvedArgs[] = $defaultFormat;
660
            } else {
661
                switch ($definition['type']) {
662
                    case 'input':
663
                    case 'enum':
664
                        $resolvedArgs[] = \sprintf("input('%s', args['%s'], '%s')", $config['type'], $name, $name);
665
                        break;
666
                    default:
667
                        $resolvedArgs[] = $defaultFormat;
668
                        break;
669
                }
670
            }
671
        }
672
673
        return sprintf("inputs(%s)", \implode(', ', $resolvedArgs));
674
    }
675
     */
676
677
    /**
678
     * Format a namespace to be used in an expression (double escape).
679
     *
680
     * @param string $namespace
681
     *
682
     * @return string
683
     */
684 9
    private static function formatNamespaceForExpression(string $namespace)
685
    {
686 9
        return \str_replace('\\', '\\\\', $namespace);
687
    }
688
689
    /**
690
     * Get the first annotation matching given class.
691
     *
692
     * @param array        $annotations
693
     * @param string|array $annotationClass
694
     *
695
     * @return mixed
696
     */
697 9
    private static function getFirstAnnotationMatching(array $annotations, $annotationClass)
698
    {
699 9
        if (\is_string($annotationClass)) {
700 9
            $annotationClass = [$annotationClass];
701
        }
702
703 9
        foreach ($annotations as $annotation) {
704 9
            foreach ($annotationClass as $class) {
705 9
                if ($annotation instanceof $class) {
706 9
                    return $annotation;
707
                }
708
            }
709
        }
710
711 9
        return false;
712
    }
713
714
    /**
715
     * Format an expression (ie. add "@=" if not set).
716
     *
717
     * @param string $expression
718
     *
719
     * @return string
720
     */
721 9
    private static function formatExpression(string $expression)
722
    {
723 9
        return '@=' === \substr($expression, 0, 2) ? $expression : \sprintf('@=%s', $expression);
724
    }
725
726
    /**
727
     * Suffix a name if it is not already.
728
     *
729
     * @param string $name
730
     * @param string $suffix
731
     *
732
     * @return string
733
     */
734 9
    private static function suffixName(string $name, string $suffix)
735
    {
736 9
        return \substr($name, -\strlen($suffix)) === $suffix ? $name : \sprintf('%s%s', $name, $suffix);
737
    }
738
739
    /**
740
     * Try to guess a field type base on is annotations.
741
     *
742
     * @param string $namespace
743
     * @param array  $annotations
744
     *
745
     * @return string|false
746
     */
747 9
    private static function guessType(string $namespace, array $annotations)
748
    {
749 9
        $columnAnnotation = self::getFirstAnnotationMatching($annotations, 'Doctrine\ORM\Mapping\Column');
750 9
        if ($columnAnnotation) {
751 9
            $type = self::resolveTypeFromDoctrineType($columnAnnotation->type);
752 9
            $nullable = $columnAnnotation->nullable;
753 9
            if ($type) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $type of type string|false is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
754 9
                return $nullable ? $type : \sprintf('%s!', $type);
755
            } else {
756
                throw new \Exception(\sprintf('Unable to auto-guess GraphQL type from Doctrine type "%s"', $columnAnnotation->type));
757
            }
758
        }
759
760
        $associationAnnotations = [
761 9
            'Doctrine\ORM\Mapping\OneToMany' => true,
762
            'Doctrine\ORM\Mapping\OneToOne' => false,
763
            'Doctrine\ORM\Mapping\ManyToMany' => true,
764
            'Doctrine\ORM\Mapping\ManyToOne' => false,
765
        ];
766
767 9
        $associationAnnotation = self::getFirstAnnotationMatching($annotations, \array_keys($associationAnnotations));
768 9
        if ($associationAnnotation) {
769 9
            $target = self::fullyQualifiedClassName($associationAnnotation->targetEntity, $namespace);
770 9
            $type = self::resolveTypeFromClass($target, 'type');
771
772 9
            if ($type) {
773 9
                $isMultiple = $associationAnnotations[\get_class($associationAnnotation)];
774 9
                if ($isMultiple) {
775 9
                    return \sprintf('[%s]!', $type);
776
                } else {
777 9
                    $isNullable = false;
778 9
                    $joinColumn = self::getFirstAnnotationMatching($annotations, 'Doctrine\ORM\Mapping\JoinColumn');
779 9
                    if ($joinColumn) {
780 9
                        $isNullable = $joinColumn->nullable;
781
                    }
782
783 9
                    return \sprintf('%s%s', $type, $isNullable ? '' : '!');
784
                }
785
            } else {
786
                throw new \Exception(\sprintf('Unable to auto-guess GraphQL type from Doctrine target class "%s" (check if the target class is a GraphQL type itself (with a @GQL\Type annotation).', $target));
787
            }
788
        }
789
790
        throw new InvalidArgumentException(\sprintf('No Doctrine ORM annotation found.'));
791
    }
792
793
    /**
794
     * Resolve a FQN from classname and namespace.
795
     *
796
     * @param string $className
797
     * @param string $namespace
798
     *
799
     * @return string
800
     */
801 9
    public static function fullyQualifiedClassName(string $className, string $namespace)
802
    {
803 9
        if (false === \strpos($className, '\\') && $namespace) {
804 9
            return $namespace.'\\'.$className;
805
        }
806
807
        return $className;
808
    }
809
810
    /**
811
     * Resolve a GraphqlType from a doctrine type.
812
     *
813
     * @param string $doctrineType
814
     *
815
     * @return string|false
816
     */
817 9
    private static function resolveTypeFromDoctrineType(string $doctrineType)
818
    {
819 9
        if (isset(self::$doctrineMapping[$doctrineType])) {
820 9
            return self::$doctrineMapping[$doctrineType];
821
        }
822
823 9
        switch ($doctrineType) {
824 9
            case 'integer':
825 9
            case 'smallint':
826 9
            case 'bigint':
827 9
                return 'Int';
828
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
829 9
            case 'string':
830
            case 'text':
831 9
                return 'String';
832
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
833
            case 'bool':
834
            case 'boolean':
835
                return 'Boolean';
836
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
837
            case 'float':
838
            case 'decimal':
839
                return 'Float';
840
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
841
            default:
842
                return false;
843
        }
844
    }
845
846
    /**
847
     * Transform a method arguments from reflection to a list of GraphQL argument.
848
     *
849
     * @param \ReflectionMethod $method
850
     *
851
     * @return array
852
     */
853 9
    private static function guessArgs(\ReflectionMethod $method)
854
    {
855 9
        $arguments = [];
856 9
        foreach ($method->getParameters() as $index => $parameter) {
857 9
            if (!$parameter->hasType()) {
858
                throw new InvalidArgumentException(\sprintf('Argument n°%s "$%s" on method "%s" cannot be auto-guessed as there is not type hint".', $index + 1, $parameter->getName(), $method->getName()));
0 ignored issues
show
Bug introduced by
Consider using $parameter->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
859
            }
860
861
            try {
862 9
                $gqlType = self::resolveGraphqlTypeFromReflectionType($parameter->getType(), 'input', $parameter->isDefaultValueAvailable());
863
            } catch (\Exception $e) {
864
                throw new InvalidArgumentException(\sprintf('Argument n°%s "$%s" on method "%s" cannot be auto-guessed : %s".', $index + 1, $parameter->getName(), $method->getName(), $e->getMessage()));
0 ignored issues
show
Bug introduced by
Consider using $parameter->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
865
            }
866
867 9
            $argumentConfig = [];
868 9
            if ($parameter->isDefaultValueAvailable()) {
869 9
                $argumentConfig['defaultValue'] = $parameter->getDefaultValue();
870
            }
871
872 9
            $argumentConfig['type'] = $gqlType;
873
874 9
            $arguments[$parameter->getName()] = $argumentConfig;
0 ignored issues
show
Bug introduced by
Consider using $parameter->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
875
        }
876
877 9
        return $arguments;
878
    }
879
880
    /**
881
     * Try to guess a GraphQL type from a Reflected Type.
882
     *
883
     * @param \ReflectionType $type
884
     *
885
     * @return string
886
     */
887 9
    private static function resolveGraphqlTypeFromReflectionType(\ReflectionType $type, string $filterGraphqlType = null, bool $isOptionnal = false)
888
    {
889 9
        $stype = (string) $type;
890 9
        if ($type->isBuiltin()) {
891 9
            $gqlType = self::resolveTypeFromPhpType($stype);
892 9
            if (!$gqlType) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $gqlType of type string|false is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
893 9
                throw new \Exception(\sprintf('No corresponding GraphQL type found for builtin type "%s"', $stype));
894
            }
895
        } else {
896
            $gqlType = self::resolveTypeFromClass($stype, $filterGraphqlType);
897
            if (!$gqlType) {
898
                throw new \Exception(\sprintf('No corresponding GraphQL %s found for class "%s"', $filterGraphqlType ?: 'object', $stype));
899
            }
900
        }
901
902 9
        return \sprintf('%s%s', $gqlType, ($type->allowsNull() || $isOptionnal) ? '' : '!');
903
    }
904
905
    /**
906
     * Resolve a GraphQL Type from a class name.
907
     *
908
     * @param string $className
909
     * @param string $wantedType
910
     *
911
     * @return string|false
912
     */
913 9
    private static function resolveTypeFromClass(string $className, string $wantedType = null)
914
    {
915 9
        foreach (self::$classesMap as $gqlType => $config) {
916 9
            if ($config['class'] === $className) {
917 9
                if (!$wantedType || ($wantedType && $wantedType === $config['type'])) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $wantedType of type null|string is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
918 9
                    return $gqlType;
919
                }
920
            }
921
        }
922
923
        return false;
924
    }
925
926
    /**
927
     * Resolve a PHP class from a GraphQL type.
928
     *
929
     * @param string $type
930
     *
931
     * @return string|false
932
     */
933 9
    private static function resolveClassFromType(string $type)
934
    {
935 9
        return self::$classesMap[$type] ?? false;
936
    }
937
938
    /**
939
     * Convert a PHP Builtin type to a GraphQL type.
940
     *
941
     * @param string $phpType
942
     *
943
     * @return string
944
     */
945 9
    private static function resolveTypeFromPhpType(string $phpType)
946
    {
947 9
        switch ($phpType) {
948 9
            case 'boolean':
949 9
            case 'bool':
950
                return 'Boolean';
951 9
            case 'integer':
952 9
            case 'int':
953 9
                return 'Int';
954 9
            case 'float':
955 9
            case 'double':
956
                return 'Float';
957 9
            case 'string':
958 9
                return 'String';
959
            default:
960
                return false;
961
        }
962
    }
963
}
964