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
15:03
created

AnnotationParser::guessType()   B

Complexity

Conditions 9
Paths 8

Size

Total Lines 45

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 23
CRAP Score 9.0058

Importance

Changes 0
Metric Value
dl 0
loc 45
ccs 23
cts 24
cp 0.9583
rs 7.6444
c 0
b 0
f 0
cc 9
nc 8
nop 2
crap 9.0058
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 15
    public static function preParse(\SplFileInfo $file, ContainerBuilder $container, array $configs = []): void
30
    {
31 15
        self::proccessFile($file, $container, $configs, true);
32 15
    }
33
34
    /**
35
     * {@inheritdoc}
36
     *
37
     * @throws \ReflectionException
38
     * @throws InvalidArgumentException
39
     */
40 15
    public static function parse(\SplFileInfo $file, ContainerBuilder $container, array $configs = []): array
41
    {
42 15
        return self::proccessFile($file, $container, $configs);
43
    }
44
45
    /**
46
     * Clear the Annotation parser.
47
     */
48 14
    public static function clear(): void
49
    {
50 14
        self::$classesMap = [];
51 14
        self::$providers = [];
52 14
        self::$annotationReader = null;
53 14
    }
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 15
    public static function proccessFile(\SplFileInfo $file, ContainerBuilder $container, array $configs, bool $resolveClassMap = false): array
66
    {
67 15
        self::$doctrineMapping = $configs['doctrine']['types_mapping'];
68
69 15
        $rootQueryType = $configs['definitions']['schema']['default']['query'] ?? false;
70 15
        $rootMutationType = $configs['definitions']['schema']['default']['mutation'] ?? false;
71
72 15
        $container->addResource(new FileResource($file->getRealPath()));
73
74 15
        if (!$resolveClassMap) {
75 15
            $container->setParameter(self::CLASSESMAP_CONTAINER_PARAMETER, self::$classesMap);
76
        }
77
78
        try {
79 15
            $fileContent = \file_get_contents($file->getRealPath());
80
81 15
            $shortClassName = \substr($file->getFilename(), 0, -4);
82 15
            if (\preg_match('#namespace (.+);#', $fileContent, $namespace)) {
83 15
                $className = $namespace[1].'\\'.$shortClassName;
84 15
                $namespace = $namespace[1];
85
            } else {
86
                $className = $shortClassName;
87
            }
88
89 15
            $reflexionEntity = new \ReflectionClass($className);
90
91 15
            $classAnnotations = self::getAnnotationReader()->getClassAnnotations($reflexionEntity);
92
93 15
            $properties = [];
94 15
            foreach ($reflexionEntity->getProperties() as $property) {
95 14
                $properties[$property->getName()] = ['property' => $property, 'annotations' => self::getAnnotationReader()->getPropertyAnnotations($property)];
96
            }
97
98 15
            $methods = [];
99 15
            foreach ($reflexionEntity->getMethods() as $method) {
100 14
                $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 15
            $gqlTypes = [];
104
105 15
            foreach ($classAnnotations as $classAnnotation) {
106 15
                $gqlConfiguration = $gqlType = $gqlName = false;
107
108
                switch (true) {
109 15
                    case $classAnnotation instanceof GQL\Type:
110 15
                        $gqlType = 'type';
111 15
                        $gqlName = $classAnnotation->name ?: $shortClassName;
112 15
                        if (!$resolveClassMap) {
113 15
                            $isRootQuery = ($rootQueryType && $gqlName === $rootQueryType);
114 15
                            $isRootMutation = ($rootMutationType && $gqlName === $rootMutationType);
115 15
                            $currentValue = ($isRootQuery || $isRootMutation) ? \sprintf("service('%s')", self::formatNamespaceForExpression($className)) : 'value';
116
117 15
                            $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 15
                            $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 15
                            $gqlConfiguration['config']['fields'] = $providerFields + $gqlConfiguration['config']['fields'];
120
                        }
121 15
                        break;
122 14
                    case $classAnnotation instanceof GQL\Input:
123 14
                        $gqlType = 'input';
124 14
                        $gqlName = $classAnnotation->name ?: self::suffixName($shortClassName, 'Input');
125 14
                        if (!$resolveClassMap) {
126 14
                            $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 14
                        break;
129 14
                    case $classAnnotation instanceof GQL\Scalar:
130 14
                        $gqlType = 'scalar';
131 14
                        if (!$resolveClassMap) {
132 14
                            $gqlConfiguration = self::getGraphqlScalar($className, $classAnnotation, $classAnnotations);
133
                        }
134 14
                        break;
135 14
                    case $classAnnotation instanceof GQL\Enum:
136 14
                        $gqlType = 'enum';
137 14
                        if (!$resolveClassMap) {
138 14
                            $gqlConfiguration = self::getGraphqlEnum($classAnnotation, $classAnnotations, $reflexionEntity->getConstants());
139
                        }
140 14
                        break;
141 14
                    case $classAnnotation instanceof GQL\Union:
142 14
                        $gqlType = 'union';
143 14
                        if (!$resolveClassMap) {
144 14
                            $gqlConfiguration = self::getGraphqlUnion($className, $classAnnotation, $classAnnotations, $methods);
145
                        }
146 14
                        break;
147 14
                    case $classAnnotation instanceof GQL\TypeInterface:
148 14
                        $gqlType = 'interface';
149 14
                        if (!$resolveClassMap) {
150 14
                            $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 14
                        break;
153 14
                    case $classAnnotation instanceof GQL\Provider:
154 14
                        if ($resolveClassMap) {
155 14
                            self::$providers[$className] = ['annotation' => $classAnnotation, 'methods' => $methods];
156
                        }
157 14
                        break;
158
                    default:
159 14
                        continue 2;
160
                }
161
162 15
                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 15
                    if (!$gqlName) {
164 14
                        $gqlName = $classAnnotation->name ?: $shortClassName;
165
                    }
166
167 15
                    if ($resolveClassMap) {
168 15
                        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 15
                        self::$classesMap[$gqlName] = ['type' => $gqlType, 'class' => $className];
172
                    } else {
173 15
                        $gqlTypes = [$gqlName => $gqlConfiguration] + $gqlTypes;
174
                    }
175
                }
176
            }
177
178 15
            return $resolveClassMap ? self::$classesMap : $gqlTypes;
179 5
        } catch (\InvalidArgumentException $e) {
180 5
            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 15
    private static function getAnnotationReader()
190
    {
191 15
        if (null === self::$annotationReader) {
192 14
            if (!\class_exists('\\Doctrine\\Common\\Annotations\\AnnotationReader') ||
193 14
                !\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 14
            AnnotationRegistry::registerLoader('class_exists');
198 14
            self::$annotationReader = new AnnotationReader();
199
        }
200
201 15
        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 15
    private static function getGraphqlType(GQL\Type $typeAnnotation, array $classAnnotations, array $properties, array $methods, string $namespace, string $currentValue)
217
    {
218 15
        $typeConfiguration = [];
219
220 15
        $fields = self::getGraphqlFieldsFromAnnotations($namespace, $properties, false, false, $currentValue);
221 15
        $fields = self::getGraphqlFieldsFromAnnotations($namespace, $methods, false, true, $currentValue) + $fields;
222
223 15
        $typeConfiguration['fields'] = $fields;
224 15
        $typeConfiguration = self::getDescriptionConfiguration($classAnnotations) + $typeConfiguration;
225
226 15
        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 14
            $typeConfiguration['interfaces'] = $typeAnnotation->interfaces;
228
        }
229
230 15
        if ($typeAnnotation->resolveField) {
231
            $typeConfiguration['resolveField'] = self::formatExpression($typeAnnotation->resolveField);
232
        }
233
234 15
        $publicAnnotation = self::getFirstAnnotationMatching($classAnnotations, 'Overblog\GraphQLBundle\Annotation\IsPublic');
235 15
        if ($publicAnnotation) {
236 14
            $typeConfiguration['fieldsDefaultPublic'] = self::formatExpression($publicAnnotation->value);
237
        }
238
239 15
        $accessAnnotation = self::getFirstAnnotationMatching($classAnnotations, 'Overblog\GraphQLBundle\Annotation\Access');
240 15
        if ($accessAnnotation) {
241 14
            $typeConfiguration['fieldsDefaultAccess'] = self::formatExpression($accessAnnotation->value);
242
        }
243
244 15
        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 14
    private static function getGraphqlInterface(GQL\TypeInterface $interfaceAnnotation, array $classAnnotations, array $properties, array $methods, string $namespace)
259
    {
260 14
        $interfaceConfiguration = [];
261
262 14
        $fields = self::getGraphqlFieldsFromAnnotations($namespace, $properties);
263 14
        $fields = self::getGraphqlFieldsFromAnnotations($namespace, $methods, false, true) + $fields;
264
265 14
        $interfaceConfiguration['fields'] = $fields;
266 14
        $interfaceConfiguration = self::getDescriptionConfiguration($classAnnotations) + $interfaceConfiguration;
267
268 14
        $interfaceConfiguration['resolveType'] = self::formatExpression($interfaceAnnotation->resolveType);
269
270 14
        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 14
    private static function getGraphqlInput(GQL\Input $inputAnnotation, array $classAnnotations, array $properties, string $namespace)
284
    {
285 14
        $inputConfiguration = [];
286 14
        $fields = self::getGraphqlFieldsFromAnnotations($namespace, $properties, true);
287
288 14
        $inputConfiguration['fields'] = $fields;
289 14
        $inputConfiguration = self::getDescriptionConfiguration($classAnnotations) + $inputConfiguration;
290
291 14
        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 14
    private static function getGraphqlScalar(string $className, GQL\Scalar $scalarAnnotation, array $classAnnotations)
305
    {
306 14
        $scalarConfiguration = [];
307
308 14
        if ($scalarAnnotation->scalarType) {
309 14
            $scalarConfiguration['scalarType'] = self::formatExpression($scalarAnnotation->scalarType);
310
        } else {
311
            $scalarConfiguration = [
312 14
                'serialize' => [$className, 'serialize'],
313 14
                'parseValue' => [$className, 'parseValue'],
314 14
                'parseLiteral' => [$className, 'parseLiteral'],
315
            ];
316
        }
317
318 14
        $scalarConfiguration = self::getDescriptionConfiguration($classAnnotations) + $scalarConfiguration;
319
320 14
        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 14
    private static function getGraphqlEnum(GQL\Enum $enumAnnotation, array $classAnnotations, array $constants)
334
    {
335 14
        $enumValues = $enumAnnotation->values ? $enumAnnotation->values : [];
336
337 14
        $values = [];
338
339 14
        foreach ($constants as $name => $value) {
340
            $valueAnnotation = \current(\array_filter($enumValues, function ($enumValueAnnotation) use ($name) {
341 14
                return $enumValueAnnotation->name == $name;
342 14
            }));
343 14
            $valueConfig = [];
344 14
            $valueConfig['value'] = $value;
345
346 14
            if ($valueAnnotation && $valueAnnotation->description) {
347 14
                $valueConfig['description'] = $valueAnnotation->description;
348
            }
349
350 14
            if ($valueAnnotation && $valueAnnotation->deprecationReason) {
351 14
                $valueConfig['deprecationReason'] = $valueAnnotation->deprecationReason;
352
            }
353
354 14
            $values[$name] = $valueConfig;
355
        }
356
357 14
        $enumConfiguration = ['values' => $values];
358 14
        $enumConfiguration = self::getDescriptionConfiguration($classAnnotations) + $enumConfiguration;
359
360 14
        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 14
    private static function getGraphqlUnion(string $className, GQL\Union $unionAnnotation, array $classAnnotations, array $methods): array
374
    {
375 14
        $unionConfiguration = ['types' => $unionAnnotation->types];
376 14
        $unionConfiguration = self::getDescriptionConfiguration($classAnnotations) + $unionConfiguration;
377
378 14
        if ($unionAnnotation->resolveType) {
379 14
            $unionConfiguration['resolveType'] = self::formatExpression($unionAnnotation->resolveType);
380
        } else {
381 14
            if (isset($methods['resolveType'])) {
382 14
                $method = $methods['resolveType']['method'];
383 14
                if ($method->isStatic() && $method->isPublic()) {
384 14
                    $unionConfiguration['resolveType'] = self::formatExpression(\sprintf("@=call('%s::%s', [service('overblog_graphql.type_resolver'), value], true)", self::formatNamespaceForExpression($className), 'resolveType'));
385
                } else {
386 14
                    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 14
        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 15
    private static function getGraphqlFieldsFromAnnotations(string $namespace, array $propertiesOrMethods, bool $isInput = false, bool $isMethod = false, string $currentValue = 'value', string $fieldAnnotationName = 'Field'): array
408
    {
409 15
        $fields = [];
410 15
        foreach ($propertiesOrMethods as $target => $config) {
411 14
            $annotations = $config['annotations'];
412 14
            $method = $isMethod ? $config['method'] : false;
413 14
            $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 14
            $fieldAnnotation = self::getFirstAnnotationMatching($annotations, \sprintf('Overblog\GraphQLBundle\Annotation\%s', $fieldAnnotationName));
416 14
            $accessAnnotation = self::getFirstAnnotationMatching($annotations, 'Overblog\GraphQLBundle\Annotation\Access');
417 14
            $publicAnnotation = self::getFirstAnnotationMatching($annotations, 'Overblog\GraphQLBundle\Annotation\IsPublic');
418
419 14
            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 14
            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 14
            if ($fieldAnnotation->resolve && $isInput) {
432
                continue;
433
            }
434
435 14
            $fieldName = $target;
436 14
            $fieldType = $fieldAnnotation->type;
437 14
            $fieldConfiguration = [];
438 14
            if ($fieldType) {
439 14
                $resolvedType = self::resolveClassFromType($fieldType);
440 14
                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 14
                    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 14
                $fieldConfiguration['type'] = $fieldType;
447
            }
448
449 14
            $fieldConfiguration = self::getDescriptionConfiguration($annotations, true) + $fieldConfiguration;
450
451 14
            if (!$isInput) {
452 14
                $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 14
                $args = self::getArgs($fieldAnnotation->args, $isMethod && !$fieldAnnotation->argsBuilder ? $method : null);
454
455 14
                if (!empty($args)) {
456 14
                    $fieldConfiguration['args'] = $args;
457
                }
458
459 14
                $fieldName = $fieldAnnotation->name ?: $fieldName;
460
461 14
                if ($fieldAnnotation->resolve) {
462 14
                    $fieldConfiguration['resolve'] = self::formatExpression($fieldAnnotation->resolve);
463
                } else {
464 14
                    if ($isMethod) {
465 14
                        $fieldConfiguration['resolve'] = self::formatExpression(\sprintf('call(%s.%s, %s)', $currentValue, $target, self::formatArgsForExpression($args)));
466
                    } else {
467 14
                        if ($fieldName !== $target || 'value' !== $currentValue) {
468
                            $fieldConfiguration['resolve'] = self::formatExpression(\sprintf('%s.%s', $currentValue, $target));
469
                        }
470
                    }
471
                }
472
473 14
                if ($fieldAnnotation->argsBuilder) {
474 14
                    if (\is_string($fieldAnnotation->argsBuilder)) {
475
                        $fieldConfiguration['argsBuilder'] = $fieldAnnotation->argsBuilder;
476 14
                    } elseif (\is_array($fieldAnnotation->argsBuilder)) {
477 14
                        list($builder, $builderConfig) = $fieldAnnotation->argsBuilder;
478 14
                        $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 14
                if ($fieldAnnotation->fieldBuilder) {
485 14
                    if (\is_string($fieldAnnotation->fieldBuilder)) {
486
                        $fieldConfiguration['builder'] = $fieldAnnotation->fieldBuilder;
487 14
                    } elseif (\is_array($fieldAnnotation->fieldBuilder)) {
488 14
                        list($builder, $builderConfig) = $fieldAnnotation->fieldBuilder;
489 14
                        $fieldConfiguration['builder'] = $builder;
490 14
                        $fieldConfiguration['builderConfig'] = $builderConfig ?: [];
491
                    } else {
492 14
                        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 14
                    if (!$fieldType) {
496 14
                        if ($isMethod) {
497 14
                            if ($method->hasReturnType()) {
498
                                try {
499 14
                                    $fieldConfiguration['type'] = self::resolveGraphqlTypeFromReflectionType($method->getReturnType(), 'type');
500
                                } catch (\Exception $e) {
501 14
                                    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 14
                                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 14
                                $fieldConfiguration['type'] = self::guessType($namespace, $annotations);
509 2
                            } catch (\Exception $e) {
510 2
                                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 14
                if ($accessAnnotation) {
517 14
                    $fieldConfiguration['access'] = self::formatExpression($accessAnnotation->value);
518
                }
519
520 14
                if ($publicAnnotation) {
521 14
                    $fieldConfiguration['public'] = self::formatExpression($publicAnnotation->value);
522
                }
523
524 14
                if ($fieldAnnotation->complexity) {
525 14
                    $fieldConfiguration['complexity'] = self::formatExpression($fieldAnnotation->complexity);
526
                }
527
            }
528
529 14
            $fields[$fieldName] = $fieldConfiguration;
530
        }
531
532 15
        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 15
    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 15
        $fields = [];
547 15
        foreach (self::$providers as $className => $configuration) {
548 15
            $providerMethods = $configuration['methods'];
549 15
            $providerAnnotation = $configuration['annotation'];
550
551 15
            $filteredMethods = [];
552 15
            foreach ($providerMethods as $methodName => $config) {
553 15
                $annotations = $config['annotations'];
554
555 15
                $annotation = self::getFirstAnnotationMatching($annotations, \sprintf('Overblog\\GraphQLBundle\\Annotation\\%s', $annotationName));
556 15
                if (!$annotation) {
557 15
                    continue;
558
                }
559
560 15
                $annotationTarget = 'Query' === $annotationName ? $annotation->targetType : null;
561 15
                if (!$annotationTarget && $isRoot) {
562 14
                    $annotationTarget = $targetType;
563
                }
564
565 15
                if ($annotationTarget !== $targetType) {
566 15
                    continue;
567
                }
568
569 14
                $filteredMethods[$methodName] = $config;
570
            }
571
572 15
            $currentValue = \sprintf("service('%s')", self::formatNamespaceForExpression($className));
573 15
            $providerFields = self::getGraphqlFieldsFromAnnotations($namespace, $filteredMethods, false, true, $currentValue, $annotationName);
574 15
            foreach ($providerFields as $fieldName => $fieldConfig) {
575 14
                if ($providerAnnotation->prefix) {
576 14
                    $fieldName = \sprintf('%s%s', $providerAnnotation->prefix, $fieldName);
577
                }
578 15
                $fields[$fieldName] = $fieldConfig;
579
            }
580
        }
581
582 15
        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 15
    private static function getDescriptionConfiguration(array $annotations, bool $withDeprecation = false)
594
    {
595 15
        $config = [];
596 15
        $descriptionAnnotation = self::getFirstAnnotationMatching($annotations, 'Overblog\GraphQLBundle\Annotation\Description');
597 15
        if ($descriptionAnnotation) {
598 14
            $config['description'] = $descriptionAnnotation->value;
599
        }
600
601 15
        if ($withDeprecation) {
602 14
            $deprecatedAnnotation = self::getFirstAnnotationMatching($annotations, 'Overblog\GraphQLBundle\Annotation\Deprecated');
603 14
            if ($deprecatedAnnotation) {
604 14
                $config['deprecationReason'] = $deprecatedAnnotation->value;
605
            }
606
        }
607
608 15
        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 14
    private static function getArgs(array $args = null, \ReflectionMethod $method = null)
620
    {
621 14
        $config = [];
622 14
        if ($args && !empty($args)) {
623 14
            foreach ($args as $arg) {
624 14
                $config[$arg->name] = ['type' => $arg->type] + ($arg->description ? ['description' => $arg->description] : []);
625
            }
626 14
        } elseif ($method) {
627 14
            $config = self::guessArgs($method);
628
        }
629
630 14
        return $config;
631
    }
632
633
    /**
634
     * Format an array of args to a list of arguments in an expression.
635
     *
636
     * @param array $args
637
     *
638
     * @return string
639
     */
640 14
    private static function formatArgsForExpression(array $args)
641
    {
642 14
        $mapping = [];
643 14
        foreach ($args as $name => $config) {
644 14
            $mapping[] = \sprintf('%s: "%s"', $name, $config['type']);
645
        }
646
647 14
        return \sprintf('arguments({%s}, args)', \implode(', ', $mapping));
648
    }
649
650
    /**
651
     * Format a namespace to be used in an expression (double escape).
652
     *
653
     * @param string $namespace
654
     *
655
     * @return string
656
     */
657 15
    private static function formatNamespaceForExpression(string $namespace)
658
    {
659 15
        return \str_replace('\\', '\\\\', $namespace);
660
    }
661
662
    /**
663
     * Get the first annotation matching given class.
664
     *
665
     * @param array        $annotations
666
     * @param string|array $annotationClass
667
     *
668
     * @return mixed
669
     */
670 15
    private static function getFirstAnnotationMatching(array $annotations, $annotationClass)
671
    {
672 15
        if (\is_string($annotationClass)) {
673 15
            $annotationClass = [$annotationClass];
674
        }
675
676 15
        foreach ($annotations as $annotation) {
677 15
            foreach ($annotationClass as $class) {
678 15
                if ($annotation instanceof $class) {
679 15
                    return $annotation;
680
                }
681
            }
682
        }
683
684 15
        return false;
685
    }
686
687
    /**
688
     * Format an expression (ie. add "@=" if not set).
689
     *
690
     * @param string $expression
691
     *
692
     * @return string
693
     */
694 14
    private static function formatExpression(string $expression)
695
    {
696 14
        return '@=' === \substr($expression, 0, 2) ? $expression : \sprintf('@=%s', $expression);
697
    }
698
699
    /**
700
     * Suffix a name if it is not already.
701
     *
702
     * @param string $name
703
     * @param string $suffix
704
     *
705
     * @return string
706
     */
707 14
    private static function suffixName(string $name, string $suffix)
708
    {
709 14
        return \substr($name, -\strlen($suffix)) === $suffix ? $name : \sprintf('%s%s', $name, $suffix);
710
    }
711
712
    /**
713
     * Try to guess a field type base on is annotations.
714
     *
715
     * @param string $namespace
716
     * @param array  $annotations
717
     *
718
     * @return string|false
719
     */
720 14
    private static function guessType(string $namespace, array $annotations)
721
    {
722 14
        $columnAnnotation = self::getFirstAnnotationMatching($annotations, 'Doctrine\ORM\Mapping\Column');
723 14
        if ($columnAnnotation) {
724 14
            $type = self::resolveTypeFromDoctrineType($columnAnnotation->type);
725 14
            $nullable = $columnAnnotation->nullable;
726 14
            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...
727 14
                return $nullable ? $type : \sprintf('%s!', $type);
728
            } else {
729 1
                throw new \Exception(\sprintf('Unable to auto-guess GraphQL type from Doctrine type "%s"', $columnAnnotation->type));
730
            }
731
        }
732
733
        $associationAnnotations = [
734 14
            'Doctrine\ORM\Mapping\OneToMany' => true,
735
            'Doctrine\ORM\Mapping\OneToOne' => false,
736
            'Doctrine\ORM\Mapping\ManyToMany' => true,
737
            'Doctrine\ORM\Mapping\ManyToOne' => false,
738
        ];
739
740 14
        $associationAnnotation = self::getFirstAnnotationMatching($annotations, \array_keys($associationAnnotations));
741 14
        if ($associationAnnotation) {
742 14
            $target = self::fullyQualifiedClassName($associationAnnotation->targetEntity, $namespace);
743 14
            $type = self::resolveTypeFromClass($target, 'type');
744
745 14
            if ($type) {
746 14
                $isMultiple = $associationAnnotations[\get_class($associationAnnotation)];
747 14
                if ($isMultiple) {
748 14
                    return \sprintf('[%s]!', $type);
749
                } else {
750 14
                    $isNullable = false;
751 14
                    $joinColumn = self::getFirstAnnotationMatching($annotations, 'Doctrine\ORM\Mapping\JoinColumn');
752 14
                    if ($joinColumn) {
753 14
                        $isNullable = $joinColumn->nullable;
754
                    }
755
756 14
                    return \sprintf('%s%s', $type, $isNullable ? '' : '!');
757
                }
758
            } else {
759 1
                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));
760
            }
761
        }
762
763
        throw new InvalidArgumentException(\sprintf('No Doctrine ORM annotation found.'));
764
    }
765
766
    /**
767
     * Resolve a FQN from classname and namespace.
768
     *
769
     * @param string $className
770
     * @param string $namespace
771
     *
772
     * @return string
773
     */
774 14
    public static function fullyQualifiedClassName(string $className, string $namespace)
775
    {
776 14
        if (false === \strpos($className, '\\') && $namespace) {
777 14
            return $namespace.'\\'.$className;
778
        }
779
780 1
        return $className;
781
    }
782
783
    /**
784
     * Resolve a GraphqlType from a doctrine type.
785
     *
786
     * @param string $doctrineType
787
     *
788
     * @return string|false
789
     */
790 14
    private static function resolveTypeFromDoctrineType(string $doctrineType)
791
    {
792 14
        if (isset(self::$doctrineMapping[$doctrineType])) {
793 14
            return self::$doctrineMapping[$doctrineType];
794
        }
795
796 14
        switch ($doctrineType) {
797 14
            case 'integer':
798 14
            case 'smallint':
799 14
            case 'bigint':
800 14
                return 'Int';
801
                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...
802 14
            case 'string':
803 1
            case 'text':
804 14
                return 'String';
805
                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...
806 1
            case 'bool':
807 1
            case 'boolean':
808
                return 'Boolean';
809
                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...
810 1
            case 'float':
811 1
            case 'decimal':
812
                return 'Float';
813
                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...
814
            default:
815 1
                return false;
816
        }
817
    }
818
819
    /**
820
     * Transform a method arguments from reflection to a list of GraphQL argument.
821
     *
822
     * @param \ReflectionMethod $method
823
     *
824
     * @return array
825
     */
826 14
    private static function guessArgs(\ReflectionMethod $method)
827
    {
828 14
        $arguments = [];
829 14
        foreach ($method->getParameters() as $index => $parameter) {
830 14
            if (!$parameter->hasType()) {
831 1
                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...
832
            }
833
834
            try {
835 14
                $gqlType = self::resolveGraphqlTypeFromReflectionType($parameter->getType(), 'input', $parameter->isDefaultValueAvailable());
836
            } catch (\Exception $e) {
837
                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...
838
            }
839
840 14
            $argumentConfig = [];
841 14
            if ($parameter->isDefaultValueAvailable()) {
842 14
                $argumentConfig['defaultValue'] = $parameter->getDefaultValue();
843
            }
844
845 14
            $argumentConfig['type'] = $gqlType;
846
847 14
            $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...
848
        }
849
850 14
        return $arguments;
851
    }
852
853
    /**
854
     * Try to guess a GraphQL type from a Reflected Type.
855
     *
856
     * @param \ReflectionType $type
857
     *
858
     * @return string
859
     */
860 14
    private static function resolveGraphqlTypeFromReflectionType(\ReflectionType $type, string $filterGraphqlType = null, bool $isOptionnal = false)
861
    {
862 14
        $stype = (string) $type;
863 14
        if ($type->isBuiltin()) {
864 14
            $gqlType = self::resolveTypeFromPhpType($stype);
865 14
            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...
866 14
                throw new \Exception(\sprintf('No corresponding GraphQL type found for builtin type "%s"', $stype));
867
            }
868
        } else {
869 14
            $gqlType = self::resolveTypeFromClass($stype, $filterGraphqlType);
870 14
            if (!$gqlType) {
871
                throw new \Exception(\sprintf('No corresponding GraphQL %s found for class "%s"', $filterGraphqlType ?: 'object', $stype));
872
            }
873
        }
874
875 14
        return \sprintf('%s%s', $gqlType, ($type->allowsNull() || $isOptionnal) ? '' : '!');
876
    }
877
878
    /**
879
     * Resolve a GraphQL Type from a class name.
880
     *
881
     * @param string $className
882
     * @param string $wantedType
883
     *
884
     * @return string|false
885
     */
886 14
    private static function resolveTypeFromClass(string $className, string $wantedType = null)
887
    {
888 14
        foreach (self::$classesMap as $gqlType => $config) {
889 14
            if ($config['class'] === $className) {
890 14
                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...
891 14
                    return $gqlType;
892
                }
893
            }
894
        }
895
896 1
        return false;
897
    }
898
899
    /**
900
     * Resolve a PHP class from a GraphQL type.
901
     *
902
     * @param string $type
903
     *
904
     * @return string|false
905
     */
906 14
    private static function resolveClassFromType(string $type)
907
    {
908 14
        return self::$classesMap[$type] ?? false;
909
    }
910
911
    /**
912
     * Convert a PHP Builtin type to a GraphQL type.
913
     *
914
     * @param string $phpType
915
     *
916
     * @return string
917
     */
918 14
    private static function resolveTypeFromPhpType(string $phpType)
919
    {
920 14
        switch ($phpType) {
921 14
            case 'boolean':
922 14
            case 'bool':
923 14
                return 'Boolean';
924 14
            case 'integer':
925 14
            case 'int':
926 14
                return 'Int';
927 14
            case 'float':
928 14
            case 'double':
929 14
                return 'Float';
930 14
            case 'string':
931 14
                return 'String';
932
            default:
933
                return false;
934
        }
935
    }
936
}
937