Scrutinizer GitHub App not installed

We could not synchronize checks via GitHub's checks API since Scrutinizer's GitHub App is not installed for this repository.

Install GitHub App

Completed
Pull Request — master (#353)
by
unknown
15:58
created

AnnotationParser::getGraphQLAccessControl()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 8

Duplication

Lines 8
Ratio 100 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
dl 8
loc 8
c 0
b 0
f 0
ccs 0
cts 7
cp 0
rs 10
cc 3
nc 2
nop 1
crap 12
1
<?php
2
3
namespace Overblog\GraphQLBundle\Config\Parser;
4
5
use Overblog\GraphQLBundle\Config\Parser\ParserInterface;
6
use Symfony\Component\Config\Resource\FileResource;
7
use Symfony\Component\DependencyInjection\ContainerBuilder;
8
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
9
10
class AnnotationParser implements ParserInterface
11
{
12
    public static function getAnnotationReader()
13
    {
14
        if (!class_exists('\\Doctrine\\Common\\Annotations\\AnnotationReader') ||
15
            !class_exists('\\Doctrine\\Common\\Annotations\\AnnotationRegistry')
16
        ) {
17
            throw new \Exception('In order to use annotation, you need to require doctrine ORM');
18
        }
19
20
        $loader = require __DIR__ . '/../../../../autoload.php';
21
22
        \Doctrine\Common\Annotations\AnnotationRegistry::registerLoader(array($loader, 'loadClass'));
0 ignored issues
show
Deprecated Code introduced by
The method Doctrine\Common\Annotati...istry::registerLoader() has been deprecated with message: this method is deprecated and will be removed in doctrine/annotations 2.0 autoloading should be deferred to the globally registered autoloader by then. For now, use @example AnnotationRegistry::registerLoader('class_exists')

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
23
        \Doctrine\Common\Annotations\AnnotationRegistry::registerFile(__DIR__.'/../../Annotation/GraphQLAnnotation.php');
0 ignored issues
show
Deprecated Code introduced by
The method Doctrine\Common\Annotati...egistry::registerFile() has been deprecated with message: this method is deprecated and will be removed in doctrine/annotations 2.0 autoloading should be deferred to the globally registered autoloader by then. For now, use @example AnnotationRegistry::registerLoader('class_exists')

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
24
        $reader = new \Doctrine\Common\Annotations\AnnotationReader();
25
26
        return $reader;
27
    }
28
29
    /**
30
     * {@inheritdoc}
31
     *
32
     * @throws \ReflectionException
33
     * @throws InvalidArgumentException
34
     */
35
    public static function parse(\SplFileInfo $file, ContainerBuilder $container)
36
    {
37
        $reader = self::getAnnotationReader();
38
        $container->addResource(new FileResource($file->getRealPath()));
39
        try {
40
            $fileContent = file_get_contents($file->getRealPath());
41
42
            $entityName = substr($file->getFilename(), 0, -4);
43
            if (preg_match('#namespace (.+);#', $fileContent, $namespace)) {
44
                $className = $namespace[1] . '\\' . $entityName;
45
            } else {
46
                $className = $entityName;
47
            }
48
49
            $reflexionEntity = new \ReflectionClass($className);
50
51
            $annotations = $reader->getClassAnnotations($reflexionEntity);
52
            $annotations = self::parseAnnotation($annotations);
53
54
            $alias = self::getGraphQLAlias($annotations) ?: $entityName;
55
            $type = self::getGraphQLType($annotations);
56
57
            switch ($type) {
58
                case 'enum':
59
                    return self::formatEnumType($alias, $entityName, $reflexionEntity->getProperties());
60
                case 'custom-scalar':
61
                    return self::formatCustomScalarType($alias, $type, $className);
62
                default:
63
                    return self::formatScalarType($alias, $type, $entityName, $reflexionEntity->getProperties());
64
            }
65
        } catch (\InvalidArgumentException $e) {
66
            throw new InvalidArgumentException(sprintf('Unable to parse file "%s".', $file), $e->getCode(), $e);
67
        }
68
    }
69
70
    /**
71
     * Get the graphQL alias
72
     *
73
     * @param $annotation
74
     *
75
     * @return string|null
76
     */
77 View Code Duplication
    protected static function getGraphQLAlias($annotation)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
78
    {
79
        if (array_key_exists('GraphQLAlias', $annotation) && !empty($annotation['GraphQLAlias']['name'])) {
80
            return $annotation['GraphQLAlias']['name'];
81
        }
82
83
        return null;
84
    }
85
86
    /**
87
     * Get the graphQL type
88
     *
89
     * @param $annotation
90
     *
91
     * @return string
92
     */
93 View Code Duplication
    protected static function getGraphQLType($annotation)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
94
    {
95
        if (array_key_exists('GraphQLType', $annotation) && !empty($annotation['GraphQLType']['type'])) {
96
            return $annotation['GraphQLType']['type'];
97
        }
98
99
        return 'object';
100
    }
101
102
    /**
103
     * Format enum type
104
     *
105
     * @param string                     $alias
106
     * @param string                     $entityName
107
     * @param \ReflectionProperty[]      $properties
108
     *
109
     * @return array
110
     */
111
    protected static function formatEnumType($alias, $entityName, $properties)
112
    {
113
        $reader = self::getAnnotationReader();
114
115
        $typesConfig = [
116
            $alias => [
117
                'type' => 'enum',
118
                'config' => [
119
                    'description' => $entityName . ' type',
120
                ],
121
            ]
122
        ];
123
124
        $values = [];
125
        /** @var \ReflectionProperty $property */
126
        foreach ($properties as $property) {
127
            $propertyName = $property->getName();
128
129
            $propertyAnnotation = $reader->getPropertyAnnotations($property);
130
            $propertyAnnotation = self::parseAnnotation($propertyAnnotation);
131
132
            $values[$propertyName] = [
133
                'value' => $propertyAnnotation,
134
            ];
135
136
            if (array_key_exists('GraphQLDescription', $propertyAnnotation) && !empty($test['GraphQLDescription']['description'])) {
137
                $values[$propertyName]['description'] = $test['GraphQLDescription']['description'];
138
            }
139
        }
140
141
        $typesConfig[$alias]['config']['values'] = $values;
142
143
        return $typesConfig;
144
    }
145
146
    /**
147
     * Format custom scalar type
148
     *
149
     * @param string $alias
150
     * @param string $type
151
     * @param string $className
152
     *
153
     * @return array
154
     */
155
    protected static function formatCustomScalarType($alias, $type, $className)
156
    {
157
        $config = [
158
            'serialize' => [$className, 'serialize'],
159
            'parseValue' => [$className, 'parseValue'],
160
            'parseLiteral' => [$className, 'parseLiteral'],
161
        ];
162
163
        return [
164
            $alias => [
165
                'type' => $type,
166
                'config' => $config,
167
            ]
168
        ];
169
    }
170
171
    /**
172
     * Format scalar type
173
     *
174
     * @param string                $alias
175
     * @param string                $type
176
     * @param string                $entityName
177
     * @param \ReflectionProperty[] $properties
178
     *
179
     * @return array
180
     */
181
    protected static function formatScalarType($alias, $type, $entityName, $properties)
182
    {
183
        $reader = self::getAnnotationReader();
184
185
        $typesConfig = [
186
            $alias => [
187
                'type' => $type,
188
                'config' => [
189
                    'description' => $entityName . ' type',
190
                    'fields' => [],
191
                ],
192
            ]
193
        ];
194
195
        foreach ($properties as $property) {
196
            $propertyName = $property->getName();
197
            $propertyAnnotation = $reader->getPropertyAnnotations($property);
198
            $propertyAnnotation = self::parseAnnotation($propertyAnnotation);
199
200
            if (!$graphQlType = self::getGraphQLFieldType($propertyName, $propertyAnnotation)) {
201
                continue;
202
            }
203
204
            if ($graphQlAccessControl = self::getGraphQLAccessControl($propertyAnnotation)) {
205
                $graphQlType['access'] = $graphQlAccessControl;
206
            }
207
208
            if ($graphQlPublicControl = self::getGraphQLPublicControl($propertyAnnotation)) {
209
                $graphQlType['public'] = $graphQlPublicControl;
210
            }
211
212
            $typesConfig[$alias]['config']['fields'][$propertyName] = $graphQlType;
213
        }
214
215
        return empty($typesConfig[$alias]['config']['fields'])
216
            ? []
217
            : $typesConfig;
218
    }
219
220
    /**
221
     * Return the graphQL type for the named field
222
     *
223
     * @param string $name
224
     * @param array  $annotation
225
     *
226
     * @return array|null
227
     */
228
    protected static function getGraphQLFieldType($name, $annotation)
229
    {
230
        if (!$type = self::getGraphQLScalarFieldType($name, $annotation)) {
231
            if (!$type = self::getGraphQLQueryField($annotation)) {
232
                if (!$type = self::getGraphQLMutationField($annotation)) {
233
                    return null;
234
                }
235
            }
236
        }
237
238
        return $type;
239
    }
240
241
    /**
242
     * Return the common field type, like ID, Int, String, and other user-created type
243
     *
244
     * @param string $name
245
     * @param array  $annotation
246
     *
247
     * @return array|null
248
     */
249
    protected static function getGraphQLScalarFieldType($name, $annotation)
250
    {
251
        // Get the current type, depending on current annotation
252
        $type = $graphQLType = null;
0 ignored issues
show
Unused Code introduced by
$graphQLType 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...
253
        $nullable = $isMultiple = false;
254
        if (array_key_exists('GraphQLColumn', $annotation) && array_key_exists('type', $annotation['GraphQLColumn'])) {
255
            $annotation = $annotation['GraphQLColumn'];
256
            $type = $annotation['type'];
257 View Code Duplication
        } elseif (array_key_exists('GraphQLToMany', $annotation) && array_key_exists('target', $annotation['GraphQLToMany'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
258
            $annotation = $annotation['GraphQLToMany'];
259
            $type = $annotation['target'];
260
            $isMultiple = $nullable = true;
261
        } elseif (array_key_exists('GraphQLToOne', $annotation) && array_key_exists('target', $annotation['GraphQLToOne'])) {
262
            $annotation = $annotation['GraphQLToOne'];
263
            $type = $annotation['target'];
264
            $nullable = true;
265 View Code Duplication
        } elseif (array_key_exists('OneToMany', $annotation) && array_key_exists('targetEntity', $annotation['OneToMany'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
266
            $annotation = $annotation['OneToMany'];
267
            $type = $annotation['targetEntity'];
268
            $isMultiple = $nullable = true;
269
        } elseif (array_key_exists('OneToOne', $annotation) && array_key_exists('targetEntity', $annotation['OneToOne'])) {
270
            $annotation = $annotation['OneToOne'];
271
            $type = $annotation['targetEntity'];
272
            $nullable = true;
273 View Code Duplication
        } elseif (array_key_exists('ManyToMany', $annotation) && array_key_exists('targetEntity', $annotation['ManyToMany'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
274
            $annotation = $annotation['ManyToMany'];
275
            $type = $annotation['targetEntity'];
276
            $isMultiple = $nullable = true;
277
        } elseif (array_key_exists('ManyToOne', $annotation) && array_key_exists('targetEntity', $annotation['ManyToOne'])) {
278
            $annotation = $annotation['ManyToOne'];
279
            $type = $annotation['targetEntity'];
280
            $nullable = true;
281
        } elseif (array_key_exists('Column', $annotation) && array_key_exists('type', $annotation['Column'])) {
282
            $annotation = $annotation['Column'];
283
            $type = $annotation['type'];
284
        }
285
286
        if (!$type) {
287
            return null;
288
        }
289
290
        if (array_key_exists('nullable', $annotation)) {
291
            $nullable = $annotation['nullable'] == 'true'
292
                ? true
293
                : false;
294
        }
295
296
        $type = explode('\\', $type);
297
        $type = $type[count($type)-1];
298
299
        // Get the graphQL type representation
300
        // Specific case for ID and relation
301
        if ($name === 'id' && $type === 'integer') {
302
            $graphQLType = 'ID';
303
        } else {
304
            // Make the relation between doctrine Column type and graphQL type
305
            switch ($type) {
306
                case 'integer';
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
307
                    $graphQLType = 'Int';
308
                    break;
309
                case 'string':
310
                case 'text':
311
                    $graphQLType = 'String';
312
                    break;
313
                case 'bool':
314
                case 'boolean':
315
                    $graphQLType = 'Boolean';
316
                    break;
317
                case 'float':
318
                    $graphQLType = 'Float';
319
                    break;
320
                default:
321
                    // No maching: considering is custom-scalar graphQL type
322
                    $graphQLType = $type;
323
            }
324
        }
325
326
        if ($isMultiple) {
327
            $graphQLType = '['.$graphQLType.']';
328
        }
329
330
        if (!$nullable) {
331
            $graphQLType .= '!';
332
        }
333
334
        return ['type' => $graphQLType];
335
    }
336
337
    /**
338
     * Get the graphql query formatted field
339
     *
340
     * @param array $annotation
341
     *
342
     * @return array|null
343
     */
344
    protected static function getGraphQLQueryField($annotation)
345
    {
346
        if (!array_key_exists('GraphQLQuery', $annotation)) {
347
            return null;
348
        }
349
350
        $annotationQuery = $annotation['GraphQLQuery'];
351
352
        $ret = [
353
            'type' => $annotationQuery['type'],
354
        ];
355
356
        $method = $annotationQuery['method'];
357
        $args = $queryArgs = [];
358
        if (array_key_exists('GraphQLInputArgs', $annotation)) {
359
            $annotationArgs = $annotation['GraphQLInputArgs'];
360
            if (!array_key_exists(0, $annotationArgs)) {
361
                $annotationArgs = [$annotationArgs];
362
            }
363
364
            foreach ($annotationArgs as $arg) {
365
                $args[$arg['name']] = [
366
                    'type' => $arg['type'],
367
                ];
368
369
                if (!empty($arg['description'])) {
370
                    $args[$arg['name']]['description'] = $arg['description'];
371
                }
372
373
                $queryArgs[] = $arg['target'];
374
            }
375
376
            $ret['args'] = $args;
377
        }
378
379
        if (!empty($queryArgs)) {
380
            $query = "'".$method."', [".implode(', ', $queryArgs)."]";
381
        } else {
382
            $query = "'".$method."'";
383
        }
384
385
        $ret['resolve'] = "@=resolver(".$query.")";
386
387
        return $ret;
388
    }
389
390
    /**
391
     * Get the formatted graphQL mutation field
392
     *
393
     * @param array $annotation
394
     *
395
     * @return array
396
     */
397
    protected static function getGraphQLMutationField($annotation)
398
    {
399
        if (!array_key_exists('GraphQLMutation', $annotation)) {
400
            return self::getGraphQLRelayMutationField($annotation);
401
        }
402
403
        // @TODO
404
    }
405
406
    /**
407
     * Get the formatted graphQL relay mutation field
408
     *
409
     * @param array $annotation
410
     *
411
     * @return array|null
412
     */
413
    protected static function getGraphQLRelayMutationField($annotation)
414
    {
415
        if (!array_key_exists('GraphQLRelayMutation', $annotation)) {
416
            return null;
417
        }
418
419
        $annotation = $annotation['GraphQLRelayMutation'];
420
        if (array_key_exists('args', $annotation)) {
421
            $mutate = "'".$annotation['method']."', [".implode(', ', $annotation['args'])."]";
422
        } else {
423
            $mutate = "'".$annotation['method']."'";
424
        }
425
426
        return [
427
            "builder" => "Relay::Mutation",
428
            "builderConfig" => [
429
                "inputType" => $annotation['input'],
430
                "payloadType" => $annotation['payload'],
431
                "mutateAndGetPayload" => "@=mutation(".$mutate.")",
432
            ],
433
        ];
434
    }
435
436
    /**
437
     * Get graphql access control annotation
438
     *
439
     * @param $annotation
440
     *
441
     * @return null|string
442
     */
443 View Code Duplication
    protected static function getGraphQLAccessControl($annotation)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
444
    {
445
        if (array_key_exists('GraphQLAccessControl', $annotation) && array_key_exists('method', $annotation['GraphQLAccessControl'])) {
446
            return '@='.$annotation['GraphQLAccessControl']['method'];
447
        }
448
449
        return null;
450
    }
451
452
    /**
453
     * Get graphql public control
454
     *
455
     * @param $annotation
456
     *
457
     * @return null|string
458
     */
459 View Code Duplication
    protected static function getGraphQLPublicControl($annotation)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
460
    {
461
        if (array_key_exists('GraphQLPublicControl', $annotation) && array_key_exists('method', $annotation['GraphQLPublicControl'])) {
462
            return '@='.$annotation['GraphQLPublicControl']['method'];
463
        }
464
465
        return null;
466
    }
467
468
    /**
469
     * Parse annotation
470
     *
471
     * @param mixed $annotation
0 ignored issues
show
Documentation introduced by
There is no parameter named $annotation. Did you maybe mean $annotations?

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...
472
     *
473
     * @return array
474
     */
475
    protected static function parseAnnotation($annotations)
476
    {
477
        $returnAnnotation = [];
478
        foreach ($annotations as $index => $annotation) {
479
            if (!is_array($annotation)) {
480
                $index = explode('\\', get_class($annotation));
481
                $index = $index[count($index) - 1];
482
            }
483
484
            $returnAnnotation[$index] = [];
485
486
            foreach ($annotation as $indexAnnotation => $value) {
487
                if (is_string($value) && strpos($value, '\\')) {
488
                    $value = explode('\\', $value);
489
                    $value = $value[count($value) - 1];
490
                }
491
492
                $returnAnnotation[$index][$indexAnnotation] = $value;
493
            }
494
        }
495
496
        return $returnAnnotation;
497
    }
498
}
499