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 Timur
20:26
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 supports(Reflector $reflector): bool
40
    {
41
        return $reflector instanceof ReflectionProperty;
42
    }
43
44
    /**
45
     * @param ReflectionProperty $reflector
46
     */
47
    public function guessType(ReflectionClass $reflectionClass, Reflector $reflector, array $filterGraphQLTypes = []): ?string
48
    {
49
        if (!$reflector instanceof ReflectionProperty) {
50
            throw new TypeGuessingException('Doctrine type guesser only apply to properties');
51
        }
52
        /** @var Column|null $columnAnnotation */
53
        $columnAnnotation = $this->getAnnotation($reflector, Column::class);
54
55
        if (null !== $columnAnnotation) {
56
            $type = $this->resolveTypeFromDoctrineType($columnAnnotation->type);
57
            $nullable = $columnAnnotation->nullable;
58
            if ($type) {
59
                return $nullable ? $type : sprintf('%s!', $type);
60
            } else {
61
                throw new TypeGuessingException(sprintf('Unable to auto-guess GraphQL type from Doctrine type "%s"', $columnAnnotation->type));
62
            }
63
        }
64
65
        $associationAnnotations = [
66
            OneToMany::class => true,
67
            OneToOne::class => false,
68
            ManyToMany::class => true,
69
            ManyToOne::class => false,
70
        ];
71
72
        foreach ($associationAnnotations as $associationClass => $isMultiple) {
73
            /** @var OneToMany|OneToOne|ManyToMany|ManyToOne|null $associationAnnotation */
74
            $associationAnnotation = $this->getAnnotation($reflector, $associationClass);
75
            if (null !== $associationAnnotation) {
76
                $target = $this->fullyQualifiedClassName($associationAnnotation->targetEntity, $reflectionClass->getNamespaceName());
77
                $type = $this->map->resolveType($target, ['type']);
78
79
                if ($type) {
80
                    $isMultiple = $associationAnnotations[get_class($associationAnnotation)];
81
                    if ($isMultiple) {
82
                        return sprintf('[%s]!', $type);
83
                    } else {
84
                        $isNullable = false;
85
                        /** @var JoinColumn|null $joinColumn */
86
                        $joinColumn = $this->getAnnotation($reflector, JoinColumn::class);
87
                        if (null !== $joinColumn) {
88
                            $isNullable = $joinColumn->nullable;
89
                        }
90
91
                        return sprintf('%s%s', $type, $isNullable ? '' : '!');
92
                    }
93
                } else {
94
                    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));
95
                }
96
            }
97
        }
98
        throw new TypeGuessingException(sprintf('No Doctrine ORM annotation found.'));
99
    }
100
101
    private function getAnnotation(Reflector $reflector, string $annotationClass): ?MappingAnnotation
102
    {
103
        $reader = $this->getAnnotationReader();
104
        $annotations = [];
105
        switch (true) {
106
            case $reflector instanceof ReflectionClass: $annotations = $reader->getClassAnnotations($reflector); break;
107
            case $reflector instanceof ReflectionMethod: $annotations = $reader->getMethodAnnotations($reflector); break;
108
            case $reflector instanceof ReflectionProperty: $annotations = $reader->getPropertyAnnotations($reflector); break;
109
        }
110
        foreach ($annotations as $annotation) {
111
            if ($annotation instanceof $annotationClass) {
112
                /** @var MappingAnnotation $annotation */
113
                return $annotation;
114
            }
115
        }
116
117
        return null;
118
    }
119
120
    private function getAnnotationReader(): AnnotationReader
121
    {
122
        if (null === $this->annotationReader) {
123
            if (!class_exists(AnnotationReader::class) ||
124
                !class_exists(AnnotationRegistry::class)) {
125
                throw new RuntimeException('In order to use graphql annotation/attributes, you need to require doctrine annotations');
126
            }
127
128
            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

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