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

Passed
Pull Request — master (#801)
by Vincent
20:50
created

DoctrineTypeGuesser::resolveTypeFromDoctrineType()   B

Complexity

Conditions 11
Paths 11

Size

Total Lines 22
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 11
eloc 18
c 1
b 0
f 0
nc 11
nop 1
dl 0
loc 22
rs 7.3166

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Overblog\GraphQLBundle\Config\Parser\MetadataParser\TypeGuesser;
6
7
use Doctrine\Common\Annotations\AnnotationReader;
8
use Doctrine\Common\Annotations\AnnotationRegistry;
9
use Doctrine\ORM\Mapping\Annotation as MappingAnnotation;
10
use Doctrine\ORM\Mapping\Column;
11
use Doctrine\ORM\Mapping\JoinColumn;
12
use Doctrine\ORM\Mapping\ManyToMany;
13
use Doctrine\ORM\Mapping\ManyToOne;
14
use Doctrine\ORM\Mapping\OneToMany;
15
use Doctrine\ORM\Mapping\OneToOne;
16
use Overblog\GraphQLBundle\Config\Parser\MetadataParser\ClassesTypesMap;
17
use ReflectionClass;
18
use ReflectionMethod;
19
use ReflectionProperty;
20
use Reflector;
21
use RuntimeException;
22
23
class DoctrineTypeGuesser extends TypeGuesser
24
{
25
    protected ?AnnotationReader $annotationReader = null;
26
    protected array $doctrineMapping = [];
27
28
    public function __construct(ClassesTypesMap $map, array $doctrineMapping = [])
29
    {
30
        parent::__construct($map);
31
        $this->doctrineMapping = $doctrineMapping;
32
    }
33
34
    public function getName(): string
35
    {
36
        return 'Doctrine annotations ';
37
    }
38
39
    public function guessType(ReflectionClass $reflectionClass, Reflector $reflector, array $filterGraphQLTypes = []): ?string
40
    {
41
        if (!$reflector instanceof ReflectionProperty) {
42
            throw new TypeGuessingException('Doctrine type guesser only apply to properties');
43
        }
44
        /** @var Column|null $columnAnnotation */
45
        $columnAnnotation = $this->getAnnotation($reflector, Column::class);
46
47
        if (null !== $columnAnnotation) {
48
            $type = $this->resolveTypeFromDoctrineType($columnAnnotation->type);
49
            $nullable = $columnAnnotation->nullable;
50
            if ($type) {
51
                return $nullable ? $type : sprintf('%s!', $type);
52
            } else {
53
                throw new TypeGuessingException(sprintf('Unable to auto-guess GraphQL type from Doctrine type "%s"', $columnAnnotation->type));
54
            }
55
        }
56
57
        $associationAnnotations = [
58
            OneToMany::class => true,
59
            OneToOne::class => false,
60
            ManyToMany::class => true,
61
            ManyToOne::class => false,
62
        ];
63
64
        foreach ($associationAnnotations as $associationClass => $isMultiple) {
65
            /** @var OneToMany|OneToOne|ManyToMany|ManyToOne|null $associationAnnotation */
66
            $associationAnnotation = $this->getAnnotation($reflector, $associationClass);
67
            if (null !== $associationAnnotation) {
68
                $target = $this->fullyQualifiedClassName($associationAnnotation->targetEntity, $reflectionClass->getNamespaceName());
69
                $type = $this->map->resolveType($target, ['type']);
70
71
                if ($type) {
72
                    $isMultiple = $associationAnnotations[get_class($associationAnnotation)];
73
                    if ($isMultiple) {
74
                        return sprintf('[%s]!', $type);
75
                    } else {
76
                        $isNullable = false;
77
                        /** @var JoinColumn|null $joinColumn */
78
                        $joinColumn = $this->getAnnotation($reflector, JoinColumn::class);
79
                        if (null !== $joinColumn) {
80
                            $isNullable = $joinColumn->nullable;
81
                        }
82
83
                        return sprintf('%s%s', $type, $isNullable ? '' : '!');
84
                    }
85
                } else {
86
                    throw new TypeGuessingException(sprintf('Unable to auto-guess GraphQL type from Doctrine target class "%s" (check if the target class is a GraphQL type itself (with a @Metadata\Type metadata).', $target));
87
                }
88
            }
89
        }
90
        throw new TypeGuessingException(sprintf('No Doctrine ORM annotation found.'));
91
    }
92
93
    private function getAnnotation(Reflector $reflector, string $annotationClass): ?MappingAnnotation
94
    {
95
        $reader = $this->getAnnotationReader();
96
        $annotations = [];
97
        switch (true) {
98
            case $reflector instanceof ReflectionClass: $annotations = $reader->getClassAnnotations($reflector); break;
99
            case $reflector instanceof ReflectionMethod: $annotations = $reader->getMethodAnnotations($reflector); break;
100
            case $reflector instanceof ReflectionProperty: $annotations = $reader->getPropertyAnnotations($reflector); break;
101
        }
102
        foreach ($annotations as $annotation) {
103
            if ($annotation instanceof $annotationClass) {
104
                /** @var MappingAnnotation $annotation */
105
                return $annotation;
106
            }
107
        }
108
109
        return null;
110
    }
111
112
    private function getAnnotationReader(): AnnotationReader
113
    {
114
        if (null === $this->annotationReader) {
115
            if (!class_exists(AnnotationReader::class) ||
116
                !class_exists(AnnotationRegistry::class)) {
117
                throw new RuntimeException('In order to use graphql annotation/attributes, you need to require doctrine annotations');
118
            }
119
120
            AnnotationRegistry::registerLoader('class_exists');
0 ignored issues
show
Deprecated Code introduced by
The function Doctrine\Common\Annotati...istry::registerLoader() has been deprecated: This method is deprecated and will be removed in doctrine/annotations 2.0. Annotations will be autoloaded in 2.0. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

120
            /** @scrutinizer ignore-deprecated */ AnnotationRegistry::registerLoader('class_exists');

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

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

Loading history...
121
            $this->annotationReader = new AnnotationReader();
122
        }
123
124
        return $this->annotationReader;
125
    }
126
127
    /**
128
     * Resolve a FQN from classname and namespace.
129
     *
130
     * @internal
131
     */
132
    public function fullyQualifiedClassName(string $className, string $namespace): string
133
    {
134
        if (false === strpos($className, '\\') && $namespace) {
135
            return $namespace.'\\'.$className;
136
        }
137
138
        return $className;
139
    }
140
141
    /**
142
     * Resolve a GraphQLType from a doctrine type.
143
     */
144
    private function resolveTypeFromDoctrineType(string $doctrineType): ?string
145
    {
146
        if (isset($this->doctrineMapping[$doctrineType])) {
147
            return $this->doctrineMapping[$doctrineType];
148
        }
149
150
        switch ($doctrineType) {
151
            case 'integer':
152
            case 'smallint':
153
            case 'bigint':
154
                return 'Int';
155
            case 'string':
156
            case 'text':
157
                return 'String';
158
            case 'bool':
159
            case 'boolean':
160
                return 'Boolean';
161
            case 'float':
162
            case 'decimal':
163
                return 'Float';
164
            default:
165
                return null;
166
        }
167
    }
168
}
169