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
379:29 queued 376:31
created

AnnotationParser::formatExpression()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 4
c 0
b 0
f 0
ccs 2
cts 2
cp 1
rs 10
cc 2
nc 2
nop 1
crap 2
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 8
    public static function preParse(\SplFileInfo $file, ContainerBuilder $container, array $configs = []): void
30
    {
31 8
        self::proccessFile($file, $container, $configs, true);
32 8
    }
33
34
    /**
35
     * {@inheritdoc}
36
     *
37
     * @throws \ReflectionException
38
     * @throws InvalidArgumentException
39
     */
40 8
    public static function parse(\SplFileInfo $file, ContainerBuilder $container, array $configs = []): array
41
    {
42 8
        return self::proccessFile($file, $container, $configs);
43
    }
44
45
    /**
46
     * Clear the Annotation parser.
47
     */
48 8
    public static function clear(): void
49
    {
50 8
        self::$classesMap = [];
51 8
        self::$providers = [];
52 8
        self::$annotationReader = null;
53 8
    }
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 8
    public static function proccessFile(\SplFileInfo $file, ContainerBuilder $container, array $configs, bool $resolveClassMap = false): array
66
    {
67 8
        self::$doctrineMapping = $configs['doctrine']['types_mapping'];
68
69 8
        $rootQueryType = $configs['definitions']['schema']['default']['query'] ?? false;
70 8
        $rootMutationType = $configs['definitions']['schema']['default']['mutation'] ?? false;
71
72 8
        $container->addResource(new FileResource($file->getRealPath()));
73
74 8
        if (!$resolveClassMap) {
75 8
            $container->setParameter(self::CLASSESMAP_CONTAINER_PARAMETER, self::$classesMap);
76
        }
77
78
        try {
79 8
            $fileContent = \file_get_contents($file->getRealPath());
80
81 8
            $shortClassName = \substr($file->getFilename(), 0, -4);
82 8
            if (\preg_match('#namespace (.+);#', $fileContent, $namespace)) {
83 8
                $className = $namespace[1].'\\'.$shortClassName;
84 8
                $namespace = $namespace[1];
85
            } else {
86
                $className = $shortClassName;
87
            }
88
89 8
            $reflexionEntity = new \ReflectionClass($className);
90
91 8
            $classAnnotations = self::getAnnotationReader()->getClassAnnotations($reflexionEntity);
92
93 8
            $properties = [];
94 8
            foreach ($reflexionEntity->getProperties() as $property) {
95 8
                $properties[$property->getName()] = ['property' => $property, 'annotations' => self::getAnnotationReader()->getPropertyAnnotations($property)];
96
            }
97
98 8
            $methods = [];
99 8
            foreach ($reflexionEntity->getMethods() as $method) {
100 8
                $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 8
            $gqlTypes = [];
104
105 8
            foreach ($classAnnotations as $classAnnotation) {
106 8
                $gqlConfiguration = $gqlType = $gqlName = false;
107
108
                switch (true) {
109 8
                    case $classAnnotation instanceof GQL\Type:
110 8
                        $gqlType = 'type';
111 8
                        $gqlName = $classAnnotation->name ?: $shortClassName;
112 8
                        if (!$resolveClassMap) {
113 8
                            $isRootQuery = ($rootQueryType && $gqlName === $rootQueryType);
114 8
                            $isRootMutation = ($rootMutationType && $gqlName === $rootMutationType);
115 8
                            $currentValue = ($isRootQuery || $isRootMutation) ? \sprintf("service('%s')", self::formatNamespaceForExpression($className)) : 'value';
116
117 8
                            $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
119 8
                            if ($isRootQuery || $isRootMutation) {
120 8
                                foreach (self::$providers as $className => $providerMethods) {
121 8
                                    $gqlConfiguration['config']['fields'] += self::getGraphqlFieldsFromProvider($className, $providerMethods, $isRootMutation);
122
                                }
123
                            }
124
                        }
125 8
                        break;
126 8
                    case $classAnnotation instanceof GQL\Input:
127 8
                        $gqlType = 'input';
128 8
                        $gqlName = $classAnnotation->name ?: self::suffixName($shortClassName, 'Input');
129 8
                        if (!$resolveClassMap) {
130 8
                            $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...
131
                        }
132 8
                        break;
133 8
                    case $classAnnotation instanceof GQL\Scalar:
134 8
                        $gqlType = 'scalar';
135 8
                        if (!$resolveClassMap) {
136 8
                            $gqlConfiguration = self::getGraphqlScalar($className, $classAnnotation, $classAnnotations);
137
                        }
138 8
                        break;
139 8
                    case $classAnnotation instanceof GQL\Enum:
140 8
                        $gqlType = 'enum';
141 8
                        if (!$resolveClassMap) {
142 8
                            $gqlConfiguration = self::getGraphqlEnum($classAnnotation, $classAnnotations, $reflexionEntity->getConstants());
143
                        }
144 8
                        break;
145 8
                    case $classAnnotation instanceof GQL\Union:
146 8
                        $gqlType = 'union';
147 8
                        if (!$resolveClassMap) {
148 8
                            $gqlConfiguration = self::getGraphqlUnion($classAnnotation, $classAnnotations);
149
                        }
150 8
                        break;
151 8
                    case $classAnnotation instanceof GQL\TypeInterface:
152 8
                        $gqlType = 'interface';
153 8
                        if (!$resolveClassMap) {
154 8
                            $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...
155
                        }
156 8
                        break;
157 8
                    case $classAnnotation instanceof GQL\Provider:
158 8
                        if ($resolveClassMap) {
159 8
                            self::$providers[$className] = $methods;
160
                        }
161 8
                        break;
162
                    default:
163 8
                        continue;
164
                }
165
166 8
                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...
167 8
                    if (!$gqlName) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $gqlName 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...
168 8
                        $gqlName = $classAnnotation->name ?: $shortClassName;
169
                    }
170
171 8
                    if ($resolveClassMap) {
172 8
                        if (isset(self::$classesMap[$gqlName])) {
173
                            throw new InvalidArgumentException(\sprintf('The GraphQL type "%s" has already been registered in class "%s"', $gqlName, self::$classesMap[$gqlName]['class']));
174
                        }
175 8
                        self::$classesMap[$gqlName] = ['type' => $gqlType, 'class' => $className];
176
                    } else {
177 8
                        $gqlTypes += [$gqlName => $gqlConfiguration];
178
                    }
179
                }
180
            }
181
182 8
            return $resolveClassMap ? self::$classesMap : $gqlTypes;
183
        } catch (\InvalidArgumentException $e) {
184
            throw new InvalidArgumentException(\sprintf('Unable to parse file "%s".', $file), $e->getCode(), $e);
185
        }
186
    }
187
188
    /**
189
     * Retrieve annotation reader.
190
     *
191
     * @return AnnotationReader
192
     */
193 8
    private static function getAnnotationReader()
194
    {
195 8
        if (null === self::$annotationReader) {
196 8
            if (!\class_exists('\\Doctrine\\Common\\Annotations\\AnnotationReader') ||
197 8
                !\class_exists('\\Doctrine\\Common\\Annotations\\AnnotationRegistry')) {
198
                throw new \Exception('In order to use graphql annotation, you need to require doctrine annotations');
199
            }
200
201 8
            AnnotationRegistry::registerLoader('class_exists');
202 8
            self::$annotationReader = new AnnotationReader();
203
        }
204
205 8
        return self::$annotationReader;
206
    }
207
208
    /**
209
     * Create a GraphQL Type configuration from annotations on class, properties and methods.
210
     *
211
     * @param GQL\Type $typeAnnotation
212
     * @param array    $classAnnotations
213
     * @param array    $properties
214
     * @param array    $methods
215
     * @param string   $namespace
216
     * @param string   $currentValue
217
     *
218
     * @return array
219
     */
220 8
    private static function getGraphqlType(GQL\Type $typeAnnotation, array $classAnnotations, array $properties, array $methods, string $namespace, string $currentValue)
221
    {
222 8
        $typeConfiguration = [];
223
224 8
        $fields = self::getGraphqlFieldsFromAnnotations($namespace, $properties, false, false, $currentValue);
225 8
        $fields += self::getGraphqlFieldsFromAnnotations($namespace, $methods, false, true, $currentValue);
226
227 8
        $typeConfiguration['fields'] = $fields;
228 8
        $typeConfiguration += self::getDescriptionConfiguration($classAnnotations);
229
230 8
        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...
231 8
            $typeConfiguration['interfaces'] = $typeAnnotation->interfaces;
232
        }
233
234 8
        if ($typeAnnotation->resolveField) {
235
            $typeConfiguration['resolveField'] = self::formatExpression($typeAnnotation->resolveField);
236
        }
237
238 8
        $publicAnnotation = self::getFirstAnnotationMatching($classAnnotations, 'Overblog\GraphQLBundle\Annotation\IsPublic');
239 8
        if ($publicAnnotation) {
240 8
            $typeConfiguration['fieldsDefaultPublic'] = self::formatExpression($publicAnnotation->value);
241
        }
242
243 8
        $accessAnnotation = self::getFirstAnnotationMatching($classAnnotations, 'Overblog\GraphQLBundle\Annotation\Access');
244 8
        if ($accessAnnotation) {
245 8
            $typeConfiguration['fieldsDefaultAccess'] = self::formatExpression($accessAnnotation->value);
246
        }
247
248 8
        return ['type' => $typeAnnotation->isRelay ? 'relay-mutation-payload' : 'object', 'config' => $typeConfiguration];
249
    }
250
251
    /**
252
     * Create a GraphQL Interface type configuration from annotations on properties.
253
     *
254
     * @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...
255
     * @param GQL\Interface $interfaceAnnotation
256
     * @param array         $properties
257
     * @param array         $methods
258
     * @param string        $namespace
259
     *
260
     * @return array
261
     */
262 8
    private static function getGraphqlInterface(GQL\TypeInterface $interfaceAnnotation, array $classAnnotations, array $properties, array $methods, string $namespace)
263
    {
264 8
        $interfaceConfiguration = [];
265
266 8
        $fields = self::getGraphqlFieldsFromAnnotations($namespace, $properties);
267 8
        $fields += self::getGraphqlFieldsFromAnnotations($namespace, $methods, false, true);
268
269 8
        $interfaceConfiguration['fields'] = $fields;
270 8
        $interfaceConfiguration += self::getDescriptionConfiguration($classAnnotations);
271
272 8
        $interfaceConfiguration['resolveType'] = $interfaceAnnotation->resolveType;
273
274 8
        return ['type' => 'interface', 'config' => $interfaceConfiguration];
275
    }
276
277
    /**
278
     * Create a GraphQL Input type configuration from annotations on properties.
279
     *
280
     * @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...
281
     * @param GQL\Input $inputAnnotation
282
     * @param array     $properties
283
     * @param string    $namespace
284
     *
285
     * @return array
286
     */
287 8
    private static function getGraphqlInput(GQL\Input $inputAnnotation, array $classAnnotations, array $properties, string $namespace)
288
    {
289 8
        $inputConfiguration = [];
290 8
        $fields = self::getGraphqlFieldsFromAnnotations($namespace, $properties, true);
291
292 8
        if (empty($fields)) {
293
            return [];
294
        }
295
296 8
        $inputConfiguration['fields'] = $fields;
297 8
        $inputConfiguration += self::getDescriptionConfiguration($classAnnotations);
298
299 8
        return ['type' => $inputAnnotation->isRelay ? 'relay-mutation-input' : 'input-object', 'config' => $inputConfiguration];
300
    }
301
302
    /**
303
     * Get a Graphql scalar configuration from given scalar annotation.
304
     *
305
     * @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...
306
     * @param string     $className
307
     * @param GQL\Scalar $scalarAnnotation
308
     * @param array      $classAnnotations
309
     *
310
     * @return array
311
     */
312 8
    private static function getGraphqlScalar(string $className, GQL\Scalar $scalarAnnotation, array $classAnnotations)
313
    {
314 8
        $scalarConfiguration = [];
315
316 8
        if ($scalarAnnotation->scalarType) {
317 8
            $scalarConfiguration['scalarType'] = self::formatExpression($scalarAnnotation->scalarType);
318
        } else {
319
            $scalarConfiguration = [
320 8
                'serialize' => [$className, 'serialize'],
321 8
                'parseValue' => [$className, 'parseValue'],
322 8
                'parseLiteral' => [$className, 'parseLiteral'],
323
            ];
324
        }
325
326 8
        $scalarConfiguration += self::getDescriptionConfiguration($classAnnotations);
327
328 8
        return ['type' => 'custom-scalar', 'config' => $scalarConfiguration];
329
    }
330
331
    /**
332
     * Get a Graphql Enum configuration from given enum annotation.
333
     *
334
     * @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...
335
     * @param GQL\Enum $enumAnnotation
336
     * @param array    $classAnnotations
337
     * @param array    $constants
338
     *
339
     * @return array
340
     */
341 8
    private static function getGraphqlEnum(GQL\Enum $enumAnnotation, array $classAnnotations, array $constants)
342
    {
343 8
        $enumValues = $enumAnnotation->values ? $enumAnnotation->values : [];
344
345 8
        $values = [];
346
347 8
        foreach ($constants as $name => $value) {
348
            $valueAnnotation = \current(\array_filter($enumValues, function ($enumValueAnnotation) use ($name) {
349 8
                return $enumValueAnnotation->name == $name;
350 8
            }));
351 8
            $valueConfig = [];
352 8
            $valueConfig['value'] = $value;
353
354 8
            if ($valueAnnotation && $valueAnnotation->description) {
355 8
                $valueConfig['description'] = $valueAnnotation->description;
356
            }
357
358 8
            if ($valueAnnotation && $valueAnnotation->deprecationReason) {
359 8
                $valueConfig['deprecationReason'] = $valueAnnotation->deprecationReason;
360
            }
361
362 8
            $values[$name] = $valueConfig;
363
        }
364
365 8
        $enumConfiguration = ['values' => $values];
366 8
        $enumConfiguration += self::getDescriptionConfiguration($classAnnotations);
367
368 8
        return ['type' => 'enum', 'config' => $enumConfiguration];
369
    }
370
371
    /**
372
     * Get a Graphql Union configuration from given union annotation.
373
     *
374
     * @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...
375
     * @param GQL\Union $unionAnnotation
376
     * @param array     $classAnnotations
377
     *
378
     * @return array
379
     */
380 8
    private static function getGraphqlUnion(GQL\Union $unionAnnotation, array $classAnnotations): array
381
    {
382 8
        $unionConfiguration = ['types' => $unionAnnotation->types];
383 8
        $unionConfiguration += self::getDescriptionConfiguration($classAnnotations);
384
385 8
        return ['type' => 'union', 'config' => $unionConfiguration];
386
    }
387
388
    /**
389
     * Create Graphql fields configuration based on annotation.
390
     *
391
     * @param string $namespace
392
     * @param array  $propertiesOrMethods
393
     * @param bool   $isInput
394
     * @param bool   $isMethod
395
     * @param string $currentValue
396
     *
397
     * @return array
398
     */
399 8
    private static function getGraphqlFieldsFromAnnotations(string $namespace, array $propertiesOrMethods, bool $isInput = false, bool $isMethod = false, string $currentValue = 'value'): array
400
    {
401 8
        $fields = [];
402 8
        foreach ($propertiesOrMethods as $target => $config) {
403 8
            $annotations = $config['annotations'];
404 8
            $method = $isMethod ? $config['method'] : false;
405 8
            $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...
406
407 8
            $fieldAnnotation = self::getFirstAnnotationMatching($annotations, 'Overblog\GraphQLBundle\Annotation\Field');
408 8
            $accessAnnotation = self::getFirstAnnotationMatching($annotations, 'Overblog\GraphQLBundle\Annotation\Access');
409 8
            $publicAnnotation = self::getFirstAnnotationMatching($annotations, 'Overblog\GraphQLBundle\Annotation\IsPublic');
410
411 8
            if (!$fieldAnnotation) {
412
                if ($accessAnnotation || $publicAnnotation) {
413
                    throw new InvalidArgumentException(\sprintf('The annotations "@Access" and/or "@Visible" defined on "%s" are only usable in addition of annotation "@Field"', $target));
414
                }
415
                continue;
416
            }
417
418 8
            if ($isMethod && !$method->isPublic()) {
419
                throw new InvalidArgumentException(\sprintf('The Annotation "@Field" can only be applied to public method. The method "%s" is not public.', $target));
420
            }
421
422
            // Ignore field with resolver when the type is an Input
423 8
            if ($fieldAnnotation->resolve && $isInput) {
424
                continue;
425
            }
426
427 8
            $propertyName = $target;
428 8
            $fieldType = $fieldAnnotation->type;
429 8
            $fieldConfiguration = [];
430 8
            if ($fieldType) {
431 8
                $resolvedType = self::resolveClassFromType($fieldType);
432 8
                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...
433 8
                    if ($isInput && !\in_array($resolvedType['type'], ['input', 'scalar', 'enum'])) {
434
                        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']));
435
                    }
436
                }
437
438 8
                $fieldConfiguration['type'] = $fieldType;
439
            }
440
441 8
            $fieldConfiguration += self::getDescriptionConfiguration($annotations, true);
442
443 8
            if (!$isInput) {
444 8
                $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...
445 8
                $args = self::getArgs($fieldAnnotation->args, $isMethod && !$fieldAnnotation->argsBuilder ? $method : null);
446
447 8
                if (!empty($args)) {
448 8
                    $fieldConfiguration['args'] = $args;
449
                }
450
451 8
                $propertyName = $fieldAnnotation->name ?: $propertyName;
452
453 8
                if ($fieldAnnotation->resolve) {
454 8
                    $fieldConfiguration['resolve'] = self::formatExpression($fieldAnnotation->resolve);
455
                } else {
456 8
                    if ($isMethod) {
457 8
                        $fieldConfiguration['resolve'] = self::formatExpression(\sprintf('%s.%s(%s)', $currentValue, $target, self::formatArgsForExpression($args)));
458 8
                    } elseif ($fieldAnnotation->name) {
459
                        $fieldConfiguration['resolve'] = self::formatExpression(\sprintf('%s.%s', $currentValue, $target));
460
                    }
461
                }
462
463 8
                if ($fieldAnnotation->argsBuilder) {
464 8
                    if (\is_string($fieldAnnotation->argsBuilder)) {
465
                        $fieldConfiguration['argsBuilder'] = $fieldAnnotation->argsBuilder;
466 8
                    } elseif (\is_array($fieldAnnotation->argsBuilder)) {
467 8
                        list($builder, $builderConfig) = $fieldAnnotation->argsBuilder;
468 8
                        $fieldConfiguration['argsBuilder'] = ['builder' => $builder, 'config' => $builderConfig];
469
                    } else {
470
                        throw new InvalidArgumentException(\sprintf('The attribute "argsBuilder" on GraphQL annotation "@Field" defined on "%s" must be a string or an array where first index is the builder name and the second is the config.', $target));
471
                    }
472
                }
473
474 8
                if ($fieldAnnotation->fieldBuilder) {
475 8
                    if (\is_string($fieldAnnotation->fieldBuilder)) {
476
                        $fieldConfiguration['builder'] = $fieldAnnotation->fieldBuilder;
477 8
                    } elseif (\is_array($fieldAnnotation->fieldBuilder)) {
478 8
                        list($builder, $builderConfig) = $fieldAnnotation->fieldBuilder;
479 8
                        $fieldConfiguration['builder'] = $builder;
480 8
                        $fieldConfiguration['builderConfig'] = $builderConfig ?: [];
481
                    } else {
482 8
                        throw new InvalidArgumentException(\sprintf('The attribute "argsBuilder" on GraphQL annotation "@Field" defined on "%s" must be a string or an array where first index is the builder name and the second is the config.', $target));
483
                    }
484
                } else {
485 8
                    if (!$fieldType) {
486 8
                        if ($isMethod) {
487 8
                            if ($method->hasReturnType()) {
488
                                try {
489 8
                                    $fieldConfiguration['type'] = self::resolveGraphqlTypeFromReflectionType($method->getReturnType(), 'type').'!';
490
                                } catch (\Exception $e) {
491 8
                                    throw new InvalidArgumentException(\sprintf('The attribute "type" on GraphQL annotation "@Field" is missing on method "%s" and cannot be auto-guessed from type hint "%s"', $target, (string) $method->getReturnType()));
492
                                }
493
                            } else {
494 8
                                throw new InvalidArgumentException(\sprintf('The attribute "type" on GraphQL annotation "@Field" is missing on method "%s" and cannot be auto-guessed as there is not return type hint.', $target));
495
                            }
496
                        } else {
497
                            try {
498 8
                                $fieldConfiguration['type'] = self::guessType($namespace, $annotations);
499
                            } catch (\Exception $e) {
500
                                throw new InvalidArgumentException(\sprintf('The attribute "type" on "@Field" defined on "%s" is required and cannot be auto-guessed : %s.', $target, $e->getMessage()));
501
                            }
502
                        }
503
                    }
504
                }
505
506 8
                if ($accessAnnotation) {
507 8
                    $fieldConfiguration['access'] = self::formatExpression($accessAnnotation->value);
508
                }
509
510 8
                if ($publicAnnotation) {
511 8
                    $fieldConfiguration['public'] = self::formatExpression($publicAnnotation->value);
512
                }
513
            }
514
515 8
            $fields[$propertyName] = $fieldConfiguration;
516
        }
517
518 8
        return $fields;
519
    }
520
521
    /**
522
     * ArgTransformer
523
     *   Transform Arg type hint with enum as newObject(enumClassTypeHint, arg['a'])new EnumClass(arg['a'])
524
     *   Transform Arg type hint input as populate(InputClass, arg['a']).
525
     */
526
527
    /**
528
     * Return fields config from Provider methods.
529
     *
530
     * @param string $className
531
     * @param array  $methods
532
     * @param bool   $isMutation
533
     *
534
     * @return array
535
     */
536 8
    private static function getGraphqlFieldsFromProvider(string $className, array $methods, bool $isMutation = false)
537
    {
538 8
        $fields = [];
539 8
        foreach ($methods as $methodName => $config) {
540 8
            $annotations = $config['annotations'];
541 8
            $method = $config['method'];
542
543 8
            $annotation = self::getFirstAnnotationMatching($annotations, \sprintf('Overblog\\GraphQLBundle\\Annotation\\%s', $isMutation ? 'Mutation' : 'Query'));
544 8
            if (!$annotation) {
545 8
                continue;
546
            }
547
548 8
            $name = $annotation->name ?: $methodName;
549 8
            $type = $annotation->type;
550 8
            $args = self::getArgs($annotation->args, $method);
551 8
            if (!$type) {
552
                if ($method->hasReturnType()) {
553
                    try {
554
                        $type = self::resolveGraphqlTypeFromReflectionType($method->getReturnType(), 'type');
555
                    } catch (\Exception $e) {
556
                        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"', $isMutation ? 'Mutation' : 'Query', $method, (string) $method->getReturnType()));
557
                    }
558
                } else {
559
                    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.', $isMutation ? 'Mutation' : 'Query', $method));
560
                }
561
            }
562
563 8
            $resolve = \sprintf("@=service('%s').%s(%s)", self::formatNamespaceForExpression($className), $methodName, self::formatArgsForExpression($args));
564
565 8
            $fields[$name] = [
566 8
                'type' => $type,
567 8
                'args' => $args,
568 8
                'resolve' => $resolve,
569
            ];
570
        }
571
572 8
        return $fields;
573
    }
574
575
    /**
576
     * Get the config for description & deprecation reason.
577
     *
578
     * @param array $annotations
579
     * @param bool  $withDeprecation
580
     *
581
     * @return array
582
     */
583 8
    private static function getDescriptionConfiguration(array $annotations, bool $withDeprecation = false)
584
    {
585 8
        $config = [];
586 8
        $descriptionAnnotation = self::getFirstAnnotationMatching($annotations, 'Overblog\GraphQLBundle\Annotation\Description');
587 8
        if ($descriptionAnnotation) {
588 8
            $config['description'] = $descriptionAnnotation->value;
589
        }
590
591 8
        if ($withDeprecation) {
592 8
            $deprecatedAnnotation = self::getFirstAnnotationMatching($annotations, 'Overblog\GraphQLBundle\Annotation\Deprecated');
593 8
            if ($deprecatedAnnotation) {
594
                $config['deprecationReason'] = $deprecatedAnnotation->value;
595
            }
596
        }
597
598 8
        return $config;
599
    }
600
601
    /**
602
     * Get args config from an array of @Arg annotation or by auto-guessing if a method is provided.
603
     *
604
     * @param array             $args
605
     * @param \ReflectionMethod $method
606
     *
607
     * @return array
608
     */
609 8
    private static function getArgs(array $args = null, \ReflectionMethod $method = null)
610
    {
611 8
        $config = [];
612 8
        if ($args && !empty($args)) {
613 8
            foreach ($args as $arg) {
614 8
                $config[$arg->name] = ['type' => $arg->type] + ($arg->description ? ['description' => $arg->description] : []);
615
            }
616 8
        } elseif ($method) {
617 8
            $config = self::guessArgs($method);
618
        }
619
620 8
        return $config;
621
    }
622
623
    /**
624
     * Format an array of args to a list of arguments in an expression.
625
     *
626
     * @param array $args
627
     *
628
     * @return string
629
     */
630 8
    private static function formatArgsForExpression(array $args)
631
    {
632 8
        $resolvedArgs = [];
633 8
        foreach ($args as $name => $config) {
634 8
            $cleanedType = \str_replace(['[', ']', '!'], '', $config['type']);
635 8
            $definition = self::resolveClassFromType($cleanedType);
636 8
            $defaultFormat = \sprintf("args['%s']", $name);
637 8
            if (!$definition) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $definition 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...
638 8
                $resolvedArgs[] = $defaultFormat;
639
            } else {
640 8
                switch ($definition['type']) {
641 8
                    case 'input':
642
                    case 'enum':
643 8
                        $resolvedArgs[] = \sprintf("input('%s', args['%s'])", $config['type'], $name);
644 8
                        break;
645
                    default:
646
                        $resolvedArgs[] = $defaultFormat;
647 8
                        break;
648
                }
649
            }
650
        }
651
652 8
        return \implode(', ', $resolvedArgs);
653
    }
654
655
    /**
656
     * Format a namespace to be used in an expression (double escape).
657
     *
658
     * @param string $namespace
659
     *
660
     * @return string
661
     */
662 8
    private static function formatNamespaceForExpression(string $namespace)
663
    {
664 8
        return \str_replace('\\', '\\\\', $namespace);
665
    }
666
667
    /**
668
     * Get the first annotation matching given class.
669
     *
670
     * @param array        $annotations
671
     * @param string|array $annotationClass
672
     *
673
     * @return mixed
674
     */
675 8
    private static function getFirstAnnotationMatching(array $annotations, $annotationClass)
676
    {
677 8
        if (\is_string($annotationClass)) {
678 8
            $annotationClass = [$annotationClass];
679
        }
680
681 8
        foreach ($annotations as $annotation) {
682 8
            foreach ($annotationClass as $class) {
683 8
                if ($annotation instanceof $class) {
684 8
                    return $annotation;
685
                }
686
            }
687
        }
688
689 8
        return false;
690
    }
691
692
    /**
693
     * Format an expression (ie. add "@=" if not set).
694
     *
695
     * @param string $expression
696
     *
697
     * @return string
698
     */
699 8
    private static function formatExpression(string $expression)
700
    {
701 8
        return '@=' === \substr($expression, 0, 2) ? $expression : \sprintf('@=%s', $expression);
702
    }
703
704
    /**
705
     * Suffix a name if it is not already.
706
     *
707
     * @param string $name
708
     * @param string $suffix
709
     *
710
     * @return string
711
     */
712 8
    private static function suffixName(string $name, string $suffix)
713
    {
714 8
        return \substr($name, -\strlen($suffix)) === $suffix ? $name : \sprintf('%s%s', $name, $suffix);
715
    }
716
717
    /**
718
     * Try to guess a field type base on is annotations.
719
     *
720
     * @param string $namespace
721
     * @param array  $annotations
722
     *
723
     * @return string|false
724
     */
725 8
    private static function guessType(string $namespace, array $annotations)
726
    {
727 8
        $columnAnnotation = self::getFirstAnnotationMatching($annotations, 'Doctrine\ORM\Mapping\Column');
728 8
        if ($columnAnnotation) {
729 8
            $type = self::resolveTypeFromDoctrineType($columnAnnotation->type);
730 8
            $nullable = $columnAnnotation->nullable;
731 8
            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...
732 8
                return $nullable ? $type : \sprintf('%s!', $type);
733
            } else {
734
                throw new \Exception(\sprintf('Unable to auto-guess GraphQL type from Doctrine type "%s"', $columnAnnotation->type));
735
            }
736
        }
737
738
        $associationAnnotations = [
739 8
            'Doctrine\ORM\Mapping\OneToMany' => true,
740
            'Doctrine\ORM\Mapping\OneToOne' => false,
741
            'Doctrine\ORM\Mapping\ManyToMany' => true,
742
            'Doctrine\ORM\Mapping\ManyToOne' => false,
743
        ];
744
745 8
        $associationAnnotation = self::getFirstAnnotationMatching($annotations, \array_keys($associationAnnotations));
746 8
        if ($associationAnnotation) {
747 8
            $target = self::fullyQualifiedClassName($associationAnnotation->targetEntity, $namespace);
748 8
            $type = self::resolveTypeFromClass($target, 'type');
749
750 8
            if ($type) {
751 8
                $isMultiple = $associationAnnotations[\get_class($associationAnnotation)];
752 8
                if ($isMultiple) {
753 8
                    return \sprintf('[%s]!', $type);
754
                } else {
755 8
                    $isNullable = false;
756 8
                    $joinColumn = self::getFirstAnnotationMatching($annotations, 'Doctrine\ORM\Mapping\JoinColumn');
757 8
                    if ($joinColumn) {
758 8
                        $isNullable = $joinColumn->nullable;
759
                    }
760
761 8
                    return \sprintf('%s%s', $type, $isNullable ? '' : '!');
762
                }
763
            } else {
764
                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));
765
            }
766
        }
767
768
        throw new InvalidArgumentException(\sprintf('No Doctrine ORM annotation found.'));
769
    }
770
771
    /**
772
     * Resolve a FQN from classname and namespace.
773
     *
774
     * @param string $className
775
     * @param string $namespace
776
     *
777
     * @return string
778
     */
779 8
    public static function fullyQualifiedClassName(string $className, string $namespace)
780
    {
781 8
        if (false === \strpos($className, '\\') && $namespace) {
782 8
            return $namespace.'\\'.$className;
783
        }
784
785
        return $className;
786
    }
787
788
    /**
789
     * Resolve a GraphqlType from a doctrine type.
790
     *
791
     * @param string $doctrineType
792
     *
793
     * @return string|false
794
     */
795 8
    private static function resolveTypeFromDoctrineType(string $doctrineType)
796
    {
797 8
        if (isset(self::$doctrineMapping[$doctrineType])) {
798 8
            return self::$doctrineMapping[$doctrineType];
799
        }
800
801 8
        switch ($doctrineType) {
802 8
            case 'integer':
803 8
            case 'smallint':
804 8
            case 'bigint':
805 8
                return 'Int';
806
                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...
807 8
            case 'string':
808
            case 'text':
809 8
                return 'String';
810
                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...
811
            case 'bool':
812
            case 'boolean':
813
                return 'Boolean';
814
                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...
815
            case 'float':
816
            case 'decimal':
817
                return 'Float';
818
                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...
819
            default:
820
                return false;
821
        }
822
    }
823
824
    /**
825
     * Transform a method arguments from reflection to a list of GraphQL argument.
826
     *
827
     * @param \ReflectionMethod $method
828
     *
829
     * @return array
830
     */
831 8
    private static function guessArgs(\ReflectionMethod $method)
832
    {
833 8
        $arguments = [];
834 8
        foreach ($method->getParameters() as $index => $parameter) {
835 8
            if (!$parameter->hasType()) {
836
                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...
837
            }
838
839
            try {
840 8
                $gqlType = self::resolveGraphqlTypeFromReflectionType($parameter->getType(), 'input');
841
            } catch (\Exception $e) {
842
                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...
843
            }
844
845 8
            $argumentConfig = [];
846 8
            if ($parameter->isDefaultValueAvailable()) {
847 8
                $argumentConfig['default'] = $parameter->getDefaultValue();
848
            } else {
849 8
                $gqlType .= '!';
850
            }
851
852 8
            $argumentConfig['type'] = $gqlType;
853
854 8
            $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...
855
        }
856
857 8
        return $arguments;
858
    }
859
860
    /**
861
     * Try to guess a GraphQL type from a Reflected Type.
862
     *
863
     * @param \ReflectionType $type
864
     *
865
     * @return string
866
     */
867 8
    private static function resolveGraphqlTypeFromReflectionType(\ReflectionType $type, string $filterGraphqlType = null)
868
    {
869 8
        $stype = (string) $type;
870 8
        if ($type->isBuiltin()) {
871 8
            $gqlType = self::resolveTypeFromPhpType($stype);
872 8
            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...
873 8
                throw new \Exception(\sprintf('No corresponding GraphQL type found for builtin type "%s"', $stype));
874
            }
875
        } else {
876
            $gqlType = self::resolveTypeFromClass($stype, $filterGraphqlType);
877
            if (!$gqlType) {
878
                throw new \Exception(\sprintf('No corresponding GraphQL %s found for class "%s"', $filterGraphqlType ?: 'object', $stype));
879
            }
880
        }
881
882 8
        return $gqlType;
883
    }
884
885
    /**
886
     * Resolve a GraphQL Type from a class name.
887
     *
888
     * @param string $className
889
     * @param string $wantedType
890
     *
891
     * @return string|false
892
     */
893 8
    private static function resolveTypeFromClass(string $className, string $wantedType = null)
894
    {
895 8
        foreach (self::$classesMap as $gqlType => $config) {
896 8
            if ($config['class'] === $className) {
897 8
                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...
898 8
                    return $gqlType;
899
                }
900
            }
901
        }
902
903
        return false;
904
    }
905
906
    /**
907
     * Resolve a PHP class from a GraphQL type.
908
     *
909
     * @param string $type
910
     *
911
     * @return string|false
912
     */
913 8
    private static function resolveClassFromType(string $type)
914
    {
915 8
        return self::$classesMap[$type] ?? false;
916
    }
917
918
    /**
919
     * Convert a PHP Builtin type to a GraphQL type.
920
     *
921
     * @param string $phpType
922
     *
923
     * @return string
924
     */
925 8
    private static function resolveTypeFromPhpType(string $phpType)
926
    {
927 8
        switch ($phpType) {
928 8
            case 'boolean':
929 8
            case 'bool':
930
                return 'Boolean';
931 8
            case 'integer':
932 8
            case 'int':
933 8
                return 'Int';
934 8
            case 'float':
935 8
            case 'double':
936
                return 'Float';
937 8
            case 'string':
938 8
                return 'String';
939
            default:
940
                return false;
941
        }
942
    }
943
}
944