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
16:54
created

AnnotationParser::resolveClassFromType()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
c 0
b 0
f 0
ccs 2
cts 2
cp 1
rs 10
cc 1
nc 1
nop 1
crap 1
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 18
    public static function preParse(\SplFileInfo $file, ContainerBuilder $container, array $configs = []): void
30
    {
31 18
        self::proccessFile($file, $container, $configs, true);
32 18
    }
33
34
    /**
35
     * {@inheritdoc}
36
     *
37
     * @throws \ReflectionException
38
     * @throws InvalidArgumentException
39
     */
40 18
    public static function parse(\SplFileInfo $file, ContainerBuilder $container, array $configs = []): array
41
    {
42 18
        return self::proccessFile($file, $container, $configs);
43
    }
44
45
    /**
46
     * Clear the Annotation parser.
47
     */
48 17
    public static function clear(): void
49
    {
50 17
        self::$classesMap = [];
51 17
        self::$providers = [];
52 17
        self::$annotationReader = null;
53 17
    }
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 18
    public static function proccessFile(\SplFileInfo $file, ContainerBuilder $container, array $configs, bool $resolveClassMap = false): array
66
    {
67 18
        self::$doctrineMapping = $configs['doctrine']['types_mapping'];
68
69 18
        $rootQueryType = $configs['definitions']['schema']['default']['query'] ?? false;
70 18
        $rootMutationType = $configs['definitions']['schema']['default']['mutation'] ?? false;
71
72 18
        $container->addResource(new FileResource($file->getRealPath()));
73
74 18
        if (!$resolveClassMap) {
75 18
            $container->setParameter(self::CLASSESMAP_CONTAINER_PARAMETER, self::$classesMap);
76
        }
77
78
        try {
79 18
            $fileContent = \file_get_contents($file->getRealPath());
80
81 18
            $shortClassName = \substr($file->getFilename(), 0, -4);
82 18
            if (\preg_match('#namespace (.+);#', $fileContent, $namespace)) {
83 18
                $className = $namespace[1].'\\'.$shortClassName;
84 18
                $namespace = $namespace[1];
85
            } else {
86
                $className = $shortClassName;
87
            }
88
89 18
            $reflexionEntity = new \ReflectionClass($className);
90
91 18
            $classAnnotations = self::getAnnotationReader()->getClassAnnotations($reflexionEntity);
92
93 18
            $properties = [];
94 18
            foreach ($reflexionEntity->getProperties() as $property) {
95 17
                $properties[$property->getName()] = ['property' => $property, 'annotations' => self::getAnnotationReader()->getPropertyAnnotations($property)];
96
            }
97
98 18
            $methods = [];
99 18
            foreach ($reflexionEntity->getMethods() as $method) {
100 17
                $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 18
            $gqlTypes = [];
104
105 18
            foreach ($classAnnotations as $classAnnotation) {
106 18
                $gqlConfiguration = $gqlType = $gqlName = false;
107
108
                switch (true) {
109 18
                    case $classAnnotation instanceof GQL\Type:
110 18
                        $gqlType = 'type';
111 18
                        $gqlName = $classAnnotation->name ?: $shortClassName;
112 18
                        if (!$resolveClassMap) {
113 18
                            $isRootQuery = ($rootQueryType && $gqlName === $rootQueryType);
114 18
                            $isRootMutation = ($rootMutationType && $gqlName === $rootMutationType);
115 18
                            $currentValue = ($isRootQuery || $isRootMutation) ? \sprintf("service('%s')", self::formatNamespaceForExpression($className)) : 'value';
116
117 18
                            $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 18
                            $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 18
                            $gqlConfiguration['config']['fields'] = $providerFields + $gqlConfiguration['config']['fields'];
120
                        }
121 18
                        break;
122 17
                    case $classAnnotation instanceof GQL\Input:
123 17
                        $gqlType = 'input';
124 17
                        $gqlName = $classAnnotation->name ?: self::suffixName($shortClassName, 'Input');
125 17
                        if (!$resolveClassMap) {
126 17
                            $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 17
                        break;
129 17
                    case $classAnnotation instanceof GQL\Scalar:
130 17
                        $gqlType = 'scalar';
131 17
                        if (!$resolveClassMap) {
132 17
                            $gqlConfiguration = self::getGraphqlScalar($className, $classAnnotation, $classAnnotations);
133
                        }
134 17
                        break;
135 17
                    case $classAnnotation instanceof GQL\Enum:
136 17
                        $gqlType = 'enum';
137 17
                        if (!$resolveClassMap) {
138 17
                            $gqlConfiguration = self::getGraphqlEnum($classAnnotation, $classAnnotations, $reflexionEntity->getConstants());
139
                        }
140 17
                        break;
141 17
                    case $classAnnotation instanceof GQL\Union:
142 17
                        $gqlType = 'union';
143 17
                        if (!$resolveClassMap) {
144 17
                            $gqlConfiguration = self::getGraphqlUnion($className, $classAnnotation, $classAnnotations, $methods);
145
                        }
146 17
                        break;
147 17
                    case $classAnnotation instanceof GQL\TypeInterface:
148 17
                        $gqlType = 'interface';
149 17
                        if (!$resolveClassMap) {
150 17
                            $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 17
                        break;
153 17
                    case $classAnnotation instanceof GQL\Provider:
154 17
                        if ($resolveClassMap) {
155 17
                            self::$providers[$className] = ['annotation' => $classAnnotation, 'methods' => $methods];
156
                        }
157 17
                        break;
158
                    default:
159 17
                        continue 2;
160
                }
161
162 18
                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 18
                    if (!$gqlName) {
164 17
                        $gqlName = $classAnnotation->name ?: $shortClassName;
165
                    }
166
167 18
                    if ($resolveClassMap) {
168 18
                        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 18
                        self::$classesMap[$gqlName] = ['type' => $gqlType, 'class' => $className];
172
                    } else {
173 18
                        $gqlTypes = [$gqlName => $gqlConfiguration] + $gqlTypes;
174
                    }
175
                }
176
            }
177
178 18
            return $resolveClassMap ? self::$classesMap : $gqlTypes;
179 8
        } catch (\InvalidArgumentException $e) {
180 8
            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 18
    private static function getAnnotationReader()
190
    {
191 18
        if (null === self::$annotationReader) {
192 17
            if (!\class_exists('\\Doctrine\\Common\\Annotations\\AnnotationReader') ||
193 17
                !\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 17
            AnnotationRegistry::registerLoader('class_exists');
198 17
            self::$annotationReader = new AnnotationReader();
199
        }
200
201 18
        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 18
    private static function getGraphqlType(GQL\Type $typeAnnotation, array $classAnnotations, array $properties, array $methods, string $namespace, string $currentValue)
217
    {
218 18
        $typeConfiguration = [];
219
220 18
        $fields = self::getGraphqlFieldsFromAnnotations($namespace, $properties, false, false, $currentValue);
221 18
        $fields = self::getGraphqlFieldsFromAnnotations($namespace, $methods, false, true, $currentValue) + $fields;
222
223 18
        $typeConfiguration['fields'] = $fields;
224 18
        $typeConfiguration = self::getDescriptionConfiguration($classAnnotations) + $typeConfiguration;
225
226 18
        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 17
            $typeConfiguration['interfaces'] = $typeAnnotation->interfaces;
228
        }
229
230 18
        if ($typeAnnotation->resolveField) {
231 17
            $typeConfiguration['resolveField'] = self::formatExpression($typeAnnotation->resolveField);
232
        }
233
234 18
        $publicAnnotation = self::getFirstAnnotationMatching($classAnnotations, 'Overblog\GraphQLBundle\Annotation\IsPublic');
235 18
        if ($publicAnnotation) {
236 17
            $typeConfiguration['fieldsDefaultPublic'] = self::formatExpression($publicAnnotation->value);
237
        }
238
239 18
        $accessAnnotation = self::getFirstAnnotationMatching($classAnnotations, 'Overblog\GraphQLBundle\Annotation\Access');
240 18
        if ($accessAnnotation) {
241 17
            $typeConfiguration['fieldsDefaultAccess'] = self::formatExpression($accessAnnotation->value);
242
        }
243
244 18
        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 17
    private static function getGraphqlInterface(GQL\TypeInterface $interfaceAnnotation, array $classAnnotations, array $properties, array $methods, string $namespace)
259
    {
260 17
        $interfaceConfiguration = [];
261
262 17
        $fields = self::getGraphqlFieldsFromAnnotations($namespace, $properties);
263 17
        $fields = self::getGraphqlFieldsFromAnnotations($namespace, $methods, false, true) + $fields;
264
265 17
        $interfaceConfiguration['fields'] = $fields;
266 17
        $interfaceConfiguration = self::getDescriptionConfiguration($classAnnotations) + $interfaceConfiguration;
267
268 17
        $interfaceConfiguration['resolveType'] = self::formatExpression($interfaceAnnotation->resolveType);
269
270 17
        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 17
    private static function getGraphqlInput(GQL\Input $inputAnnotation, array $classAnnotations, array $properties, string $namespace)
284
    {
285 17
        $inputConfiguration = [];
286 17
        $fields = self::getGraphqlFieldsFromAnnotations($namespace, $properties, true);
287
288 17
        $inputConfiguration['fields'] = $fields;
289 17
        $inputConfiguration = self::getDescriptionConfiguration($classAnnotations) + $inputConfiguration;
290
291 17
        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 17
    private static function getGraphqlScalar(string $className, GQL\Scalar $scalarAnnotation, array $classAnnotations)
305
    {
306 17
        $scalarConfiguration = [];
307
308 17
        if ($scalarAnnotation->scalarType) {
309 17
            $scalarConfiguration['scalarType'] = self::formatExpression($scalarAnnotation->scalarType);
310
        } else {
311
            $scalarConfiguration = [
312 17
                'serialize' => [$className, 'serialize'],
313 17
                'parseValue' => [$className, 'parseValue'],
314 17
                'parseLiteral' => [$className, 'parseLiteral'],
315
            ];
316
        }
317
318 17
        $scalarConfiguration = self::getDescriptionConfiguration($classAnnotations) + $scalarConfiguration;
319
320 17
        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 17
    private static function getGraphqlEnum(GQL\Enum $enumAnnotation, array $classAnnotations, array $constants)
334
    {
335 17
        $enumValues = $enumAnnotation->values ? $enumAnnotation->values : [];
336
337 17
        $values = [];
338
339 17
        foreach ($constants as $name => $value) {
340
            $valueAnnotation = \current(\array_filter($enumValues, function ($enumValueAnnotation) use ($name) {
341 17
                return $enumValueAnnotation->name == $name;
342 17
            }));
343 17
            $valueConfig = [];
344 17
            $valueConfig['value'] = $value;
345
346 17
            if ($valueAnnotation && $valueAnnotation->description) {
347 17
                $valueConfig['description'] = $valueAnnotation->description;
348
            }
349
350 17
            if ($valueAnnotation && $valueAnnotation->deprecationReason) {
351 17
                $valueConfig['deprecationReason'] = $valueAnnotation->deprecationReason;
352
            }
353
354 17
            $values[$name] = $valueConfig;
355
        }
356
357 17
        $enumConfiguration = ['values' => $values];
358 17
        $enumConfiguration = self::getDescriptionConfiguration($classAnnotations) + $enumConfiguration;
359
360 17
        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 17
    private static function getGraphqlUnion(string $className, GQL\Union $unionAnnotation, array $classAnnotations, array $methods): array
374
    {
375 17
        $unionConfiguration = ['types' => $unionAnnotation->types];
376 17
        $unionConfiguration = self::getDescriptionConfiguration($classAnnotations) + $unionConfiguration;
377
378 17
        if ($unionAnnotation->resolveType) {
379 17
            $unionConfiguration['resolveType'] = self::formatExpression($unionAnnotation->resolveType);
380
        } else {
381 17
            if (isset($methods['resolveType'])) {
382 17
                $method = $methods['resolveType']['method'];
383 17
                if ($method->isStatic() && $method->isPublic()) {
384 17
                    $unionConfiguration['resolveType'] = self::formatExpression(\sprintf("@=call('%s::%s', [service('overblog_graphql.type_resolver'), value], true)", self::formatNamespaceForExpression($className), 'resolveType'));
385
                } else {
386 17
                    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 1
                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 17
        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 18
    private static function getGraphqlFieldsFromAnnotations(string $namespace, array $propertiesOrMethods, bool $isInput = false, bool $isMethod = false, string $currentValue = 'value', string $fieldAnnotationName = 'Field'): array
408
    {
409 18
        $fields = [];
410 18
        foreach ($propertiesOrMethods as $target => $config) {
411 17
            $annotations = $config['annotations'];
412 17
            $method = $isMethod ? $config['method'] : false;
413 17
            $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 17
            $fieldAnnotation = self::getFirstAnnotationMatching($annotations, \sprintf('Overblog\GraphQLBundle\Annotation\%s', $fieldAnnotationName));
416 17
            $accessAnnotation = self::getFirstAnnotationMatching($annotations, 'Overblog\GraphQLBundle\Annotation\Access');
417 17
            $publicAnnotation = self::getFirstAnnotationMatching($annotations, 'Overblog\GraphQLBundle\Annotation\IsPublic');
418
419 17
            if (!$fieldAnnotation) {
420 1
                if ($accessAnnotation || $publicAnnotation) {
421 1
                    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 17
            if ($isMethod && !$method->isPublic()) {
427 1
                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 17
            if ($fieldAnnotation->resolve && $isInput) {
432
                continue;
433
            }
434
435 17
            $fieldName = $target;
436 17
            $fieldType = $fieldAnnotation->type;
437 17
            $fieldConfiguration = [];
438 17
            if ($fieldType) {
439 17
                $resolvedType = self::resolveClassFromType($fieldType);
440 17
                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 17
                    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 17
                $fieldConfiguration['type'] = $fieldType;
447
            }
448
449 17
            $fieldConfiguration = self::getDescriptionConfiguration($annotations, true) + $fieldConfiguration;
450
451 17
            if (!$isInput) {
452 17
                $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 17
                $args = self::getArgs($fieldAnnotation->args, $isMethod && !$fieldAnnotation->argsBuilder ? $method : null);
454
455 17
                if (!empty($args)) {
456 17
                    $fieldConfiguration['args'] = $args;
457
                }
458
459 17
                $fieldName = $fieldAnnotation->name ?: $fieldName;
460
461 17
                if ($fieldAnnotation->resolve) {
462 17
                    $fieldConfiguration['resolve'] = self::formatExpression($fieldAnnotation->resolve);
463
                } else {
464 17
                    if ($isMethod) {
465 17
                        $fieldConfiguration['resolve'] = self::formatExpression(\sprintf('call(%s.%s, %s)', $currentValue, $target, self::formatArgsForExpression($args)));
466
                    } else {
467 17
                        if ($fieldName !== $target || 'value' !== $currentValue) {
468
                            $fieldConfiguration['resolve'] = self::formatExpression(\sprintf('%s.%s', $currentValue, $target));
469
                        }
470
                    }
471
                }
472
473 17
                if ($fieldAnnotation->argsBuilder) {
474 17
                    if (\is_string($fieldAnnotation->argsBuilder)) {
475
                        $fieldConfiguration['argsBuilder'] = $fieldAnnotation->argsBuilder;
476 17
                    } elseif (\is_array($fieldAnnotation->argsBuilder)) {
477 17
                        list($builder, $builderConfig) = $fieldAnnotation->argsBuilder;
478 17
                        $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 17
                if ($fieldAnnotation->fieldBuilder) {
485 17
                    if (\is_string($fieldAnnotation->fieldBuilder)) {
486
                        $fieldConfiguration['builder'] = $fieldAnnotation->fieldBuilder;
487 17
                    } elseif (\is_array($fieldAnnotation->fieldBuilder)) {
488 17
                        list($builder, $builderConfig) = $fieldAnnotation->fieldBuilder;
489 17
                        $fieldConfiguration['builder'] = $builder;
490 17
                        $fieldConfiguration['builderConfig'] = $builderConfig ?: [];
491
                    } else {
492 17
                        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 17
                    if (!$fieldType) {
496 17
                        if ($isMethod) {
497 17
                            if ($method->hasReturnType()) {
498
                                try {
499 17
                                    $fieldConfiguration['type'] = self::resolveGraphqlTypeFromReflectionType($method->getReturnType(), 'type');
500
                                } catch (\Exception $e) {
501 17
                                    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 17
                                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 17
                                $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 17
                if ($accessAnnotation) {
517 17
                    $fieldConfiguration['access'] = self::formatExpression($accessAnnotation->value);
518
                }
519
520 17
                if ($publicAnnotation) {
521 17
                    $fieldConfiguration['public'] = self::formatExpression($publicAnnotation->value);
522
                }
523
524 17
                if ($fieldAnnotation->complexity) {
525 17
                    $fieldConfiguration['complexity'] = self::formatExpression($fieldAnnotation->complexity);
526
                }
527
            }
528
529 17
            $fields[$fieldName] = $fieldConfiguration;
530
        }
531
532 18
        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 18
    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 18
        $fields = [];
547 18
        foreach (self::$providers as $className => $configuration) {
548 18
            $providerMethods = $configuration['methods'];
549 18
            $providerAnnotation = $configuration['annotation'];
550
551 18
            $filteredMethods = [];
552 18
            foreach ($providerMethods as $methodName => $config) {
553 18
                $annotations = $config['annotations'];
554
555 18
                $annotation = self::getFirstAnnotationMatching($annotations, \sprintf('Overblog\\GraphQLBundle\\Annotation\\%s', $annotationName));
556 18
                if (!$annotation) {
557 18
                    continue;
558
                }
559
560 18
                $annotationTarget = 'Query' === $annotationName ? $annotation->targetType : null;
561 18
                if (!$annotationTarget && $isRoot) {
562 17
                    $annotationTarget = $targetType;
563
                }
564
565 18
                if ($annotationTarget !== $targetType) {
566 18
                    continue;
567
                }
568
569 17
                $filteredMethods[$methodName] = $config;
570
            }
571
572 18
            $currentValue = \sprintf("service('%s')", self::formatNamespaceForExpression($className));
573 18
            $providerFields = self::getGraphqlFieldsFromAnnotations($namespace, $filteredMethods, false, true, $currentValue, $annotationName);
574 18
            foreach ($providerFields as $fieldName => $fieldConfig) {
575 17
                if ($providerAnnotation->prefix) {
576 17
                    $fieldName = \sprintf('%s%s', $providerAnnotation->prefix, $fieldName);
577
                }
578 18
                $fields[$fieldName] = $fieldConfig;
579
            }
580
        }
581
582 18
        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 18
    private static function getDescriptionConfiguration(array $annotations, bool $withDeprecation = false)
594
    {
595 18
        $config = [];
596 18
        $descriptionAnnotation = self::getFirstAnnotationMatching($annotations, 'Overblog\GraphQLBundle\Annotation\Description');
597 18
        if ($descriptionAnnotation) {
598 17
            $config['description'] = $descriptionAnnotation->value;
599
        }
600
601 18
        if ($withDeprecation) {
602 17
            $deprecatedAnnotation = self::getFirstAnnotationMatching($annotations, 'Overblog\GraphQLBundle\Annotation\Deprecated');
603 17
            if ($deprecatedAnnotation) {
604 17
                $config['deprecationReason'] = $deprecatedAnnotation->value;
605
            }
606
        }
607
608 18
        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 17
    private static function getArgs(array $args = null, \ReflectionMethod $method = null)
620
    {
621 17
        $config = [];
622 17
        if ($args && !empty($args)) {
623 17
            foreach ($args as $arg) {
624 17
                $config[$arg->name] = ['type' => $arg->type] + ($arg->description ? ['description' => $arg->description] : []);
625
            }
626 17
        } elseif ($method) {
627 17
            $config = self::guessArgs($method);
628
        }
629
630 17
        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 17
    private static function formatArgsForExpression(array $args)
641
    {
642 17
        $mapping = [];
643 17
        foreach ($args as $name => $config) {
644 17
            $mapping[] = \sprintf('%s: "%s"', $name, $config['type']);
645
        }
646
647 17
        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 18
    private static function formatNamespaceForExpression(string $namespace)
658
    {
659 18
        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 18
    private static function getFirstAnnotationMatching(array $annotations, $annotationClass)
671
    {
672 18
        if (\is_string($annotationClass)) {
673 18
            $annotationClass = [$annotationClass];
674
        }
675
676 18
        foreach ($annotations as $annotation) {
677 18
            foreach ($annotationClass as $class) {
678 18
                if ($annotation instanceof $class) {
679 18
                    return $annotation;
680
                }
681
            }
682
        }
683
684 18
        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 17
    private static function formatExpression(string $expression)
695
    {
696 17
        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 17
    private static function suffixName(string $name, string $suffix)
708
    {
709 17
        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 17
    private static function guessType(string $namespace, array $annotations)
721
    {
722 17
        $columnAnnotation = self::getFirstAnnotationMatching($annotations, 'Doctrine\ORM\Mapping\Column');
723 17
        if ($columnAnnotation) {
724 17
            $type = self::resolveTypeFromDoctrineType($columnAnnotation->type);
725 17
            $nullable = $columnAnnotation->nullable;
726 17
            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 17
                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 17
            '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 17
        $associationAnnotation = self::getFirstAnnotationMatching($annotations, \array_keys($associationAnnotations));
741 17
        if ($associationAnnotation) {
742 17
            $target = self::fullyQualifiedClassName($associationAnnotation->targetEntity, $namespace);
743 17
            $type = self::resolveTypeFromClass($target, 'type');
744
745 17
            if ($type) {
746 17
                $isMultiple = $associationAnnotations[\get_class($associationAnnotation)];
747 17
                if ($isMultiple) {
748 17
                    return \sprintf('[%s]!', $type);
749
                } else {
750 17
                    $isNullable = false;
751 17
                    $joinColumn = self::getFirstAnnotationMatching($annotations, 'Doctrine\ORM\Mapping\JoinColumn');
752 17
                    if ($joinColumn) {
753 17
                        $isNullable = $joinColumn->nullable;
754
                    }
755
756 17
                    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 17
    public static function fullyQualifiedClassName(string $className, string $namespace)
775
    {
776 17
        if (false === \strpos($className, '\\') && $namespace) {
777 17
            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 17
    private static function resolveTypeFromDoctrineType(string $doctrineType)
791
    {
792 17
        if (isset(self::$doctrineMapping[$doctrineType])) {
793 17
            return self::$doctrineMapping[$doctrineType];
794
        }
795
796 17
        switch ($doctrineType) {
797 17
            case 'integer':
798 17
            case 'smallint':
799 17
            case 'bigint':
800 17
                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 17
            case 'string':
803 1
            case 'text':
804 17
                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 17
    private static function guessArgs(\ReflectionMethod $method)
827
    {
828 17
        $arguments = [];
829 17
        foreach ($method->getParameters() as $index => $parameter) {
830 17
            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 17
                $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 17
            $argumentConfig = [];
841 17
            if ($parameter->isDefaultValueAvailable()) {
842 17
                $argumentConfig['defaultValue'] = $parameter->getDefaultValue();
843
            }
844
845 17
            $argumentConfig['type'] = $gqlType;
846
847 17
            $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 17
        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 17
    private static function resolveGraphqlTypeFromReflectionType(\ReflectionType $type, string $filterGraphqlType = null, bool $isOptionnal = false)
861
    {
862 17
        $stype = (string) $type;
863 17
        if ($type->isBuiltin()) {
864 17
            $gqlType = self::resolveTypeFromPhpType($stype);
865 17
            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 17
                throw new \Exception(\sprintf('No corresponding GraphQL type found for builtin type "%s"', $stype));
867
            }
868
        } else {
869 17
            $gqlType = self::resolveTypeFromClass($stype, $filterGraphqlType);
870 17
            if (!$gqlType) {
871
                throw new \Exception(\sprintf('No corresponding GraphQL %s found for class "%s"', $filterGraphqlType ?: 'object', $stype));
872
            }
873
        }
874
875 17
        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 17
    private static function resolveTypeFromClass(string $className, string $wantedType = null)
887
    {
888 17
        foreach (self::$classesMap as $gqlType => $config) {
889 17
            if ($config['class'] === $className) {
890 17
                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 17
                    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 17
    private static function resolveClassFromType(string $type)
907
    {
908 17
        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 17
    private static function resolveTypeFromPhpType(string $phpType)
919
    {
920 17
        switch ($phpType) {
921 17
            case 'boolean':
922 17
            case 'bool':
923 17
                return 'Boolean';
924 17
            case 'integer':
925 17
            case 'int':
926 17
                return 'Int';
927 17
            case 'float':
928 17
            case 'double':
929 17
                return 'Float';
930 17
            case 'string':
931 17
                return 'String';
932
            default:
933
                return false;
934
        }
935
    }
936
}
937