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 (#867)
by Vincent
23:00
created

DoctrineTypeGuesser::resolveTypeFromDoctrineType()   B

Complexity

Conditions 11
Paths 11

Size

Total Lines 22
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 11

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
ccs 16
cts 16
cp 1
crap 11
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 93
    public function __construct(ClassesTypesMap $map, array $doctrineMapping = [])
29
    {
30 93
        parent::__construct($map);
31 93
        $this->doctrineMapping = $doctrineMapping;
32 93
    }
33
34 4
    public function getName(): string
35
    {
36 4
        return 'Doctrine annotations ';
37
    }
38
39 51
    public function supports(Reflector $reflector): bool
40
    {
41 51
        return $reflector instanceof ReflectionProperty;
42
    }
43
44
    /**
45
     * @param ReflectionProperty $reflector
46
     */
47 52
    public function guessType(ReflectionClass $reflectionClass, Reflector $reflector, array $filterGraphQLTypes = []): ?string
48
    {
49 52
        if (!$reflector instanceof ReflectionProperty) {
50 1
            throw new TypeGuessingException('Doctrine type guesser only apply to properties.');
51
        }
52
        /** @var Column|null $columnAnnotation */
53 52
        $columnAnnotation = $this->getAnnotation($reflector, Column::class);
54
55 52
        if (null !== $columnAnnotation) {
56 51
            $type = $this->resolveTypeFromDoctrineType($columnAnnotation->type ?: 'string');
57 51
            $nullable = $columnAnnotation->nullable;
58 51
            if ($type) {
59 51
                return $nullable ? $type : sprintf('%s!', $type);
60
            } else {
61 2
                throw new TypeGuessingException(sprintf('Unable to auto-guess GraphQL type from Doctrine type "%s"', $columnAnnotation->type));
62
            }
63
        }
64
65 52
        $associationAnnotations = [
66
            OneToMany::class => true,
67
            OneToOne::class => false,
68
            ManyToMany::class => true,
69
            ManyToOne::class => false,
70
        ];
71
72 52
        foreach ($associationAnnotations as $associationClass => $isMultiple) {
73
            /** @var OneToMany|OneToOne|ManyToMany|ManyToOne|null $associationAnnotation */
74 52
            $associationAnnotation = $this->getAnnotation($reflector, $associationClass);
75 52
            if (null !== $associationAnnotation) {
76 51
                $target = $this->fullyQualifiedClassName($associationAnnotation->targetEntity, $reflectionClass->getNamespaceName());
0 ignored issues
show
Bug introduced by
It seems like $associationAnnotation->targetEntity can also be of type null; however, parameter $className of Overblog\GraphQLBundle\C...llyQualifiedClassName() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

76
                $target = $this->fullyQualifiedClassName(/** @scrutinizer ignore-type */ $associationAnnotation->targetEntity, $reflectionClass->getNamespaceName());
Loading history...
77 51
                $type = $this->map->resolveType($target, ['type']);
78
79 51
                if ($type) {
80 51
                    $isMultiple = $associationAnnotations[get_class($associationAnnotation)];
81 51
                    if ($isMultiple) {
82 51
                        return sprintf('[%s]!', $type);
83
                    } else {
84 51
                        $isNullable = false;
85
                        /** @var JoinColumn|null $joinColumn */
86 51
                        $joinColumn = $this->getAnnotation($reflector, JoinColumn::class);
87 51
                        if (null !== $joinColumn) {
88 51
                            $isNullable = $joinColumn->nullable;
89
                        }
90
91 51
                        return sprintf('%s%s', $type, $isNullable ? '' : '!');
92
                    }
93
                } else {
94 2
                    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 1
        throw new TypeGuessingException(sprintf('No Doctrine ORM annotation found.'));
99
    }
100
101 52
    private function getAnnotation(Reflector $reflector, string $annotationClass): ?MappingAnnotation
102
    {
103 52
        $reader = $this->getAnnotationReader();
104 52
        $annotations = [];
105
        switch (true) {
106 52
            case $reflector instanceof ReflectionClass: $annotations = $reader->getClassAnnotations($reflector); break;
107 52
            case $reflector instanceof ReflectionMethod: $annotations = $reader->getMethodAnnotations($reflector); break;
108 52
            case $reflector instanceof ReflectionProperty: $annotations = $reader->getPropertyAnnotations($reflector); break;
109
        }
110 52
        foreach ($annotations as $annotation) {
111 51
            if ($annotation instanceof $annotationClass) {
112
                /** @var MappingAnnotation $annotation */
113 51
                return $annotation;
114
            }
115
        }
116
117 52
        return null;
118
    }
119
120 52
    private function getAnnotationReader(): AnnotationReader
121
    {
122 52
        if (null === $this->annotationReader) {
123 52
            if (!class_exists(AnnotationReader::class) ||
124 52
                !class_exists(AnnotationRegistry::class)) {
125
                throw new RuntimeException('In order to use graphql annotation/attributes, you need to require doctrine annotations');
126
            }
127
128 52
            if (class_exists(AnnotationRegistry::class)) {
129 52
                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

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