Failed Conditions
Push — master ( 05fc61...964283 )
by Adrien
02:24
created

AbstractFactory::getAnnotationReader()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 3

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 6
c 1
b 0
f 0
dl 0
loc 12
rs 10
ccs 7
cts 7
cp 1
cc 3
nc 3
nop 0
crap 3
1
<?php
2
3
declare(strict_types=1);
4
5
namespace GraphQL\Doctrine\Factory;
6
7
use Doctrine\ORM\EntityManager;
8
use GraphQL\Doctrine\Attribute\Exclude;
9
use GraphQL\Doctrine\Attribute\Reader\Reader;
10
use GraphQL\Doctrine\Types;
11
use GraphQL\Type\Definition\Type;
12
use ReflectionClass;
13
use ReflectionProperty;
14
15
/**
16
 * Abstract factory to be aware of types and entityManager.
17
 */
18
abstract class AbstractFactory
19
{
20
    protected readonly Reader $reader;
21
22 102
    public function __construct(protected Types $types, protected EntityManager $entityManager)
23
    {
24 102
        $this->reader = new Reader();
0 ignored issues
show
Bug introduced by
The property reader is declared read-only in GraphQL\Doctrine\Factory\AbstractFactory.
Loading history...
25
    }
26
27
    /**
28
     * Returns whether the property is excluded.
29
     */
30 17
    final protected function isPropertyExcluded(ReflectionProperty $property): bool
31
    {
32 17
        $exclude = $this->reader->getAttribute($property, Exclude::class);
33
34 17
        return $exclude !== null;
35
    }
36
37
    /**
38
     * Get instance of GraphQL type from a PHP class name.
39
     *
40
     * Supported syntaxes are the following:
41
     *
42
     *  - `?MyType`
43
     *  - `null|MyType`
44
     *  - `MyType|null`
45
     *  - `MyType[]`
46
     *  - `?MyType[]`
47
     *  - `null|MyType[]`
48
     *  - `MyType[]|null`
49
     *  - `Collection<MyType>`
50
     */
51 31
    final protected function getTypeFromPhpDeclaration(ReflectionClass $class, null|string|Type $typeDeclaration, bool $isEntityId = false): ?Type
52
    {
53 31
        if ($typeDeclaration === null || $typeDeclaration instanceof Type) {
54 20
            return $typeDeclaration;
55
        }
56
57 26
        $isNullable = 0;
58 26
        $name = preg_replace('~(^\?|^null\||\|null$)~', '', $typeDeclaration, -1, $isNullable);
59
60 26
        $isList = 0;
61 26
        $name = preg_replace('~^([^<]*)\[]$|^Collection<(.*)>$~', '$1$2', $name, -1, $isList);
62 26
        $name = $this->adjustNamespace($class, $name);
63 26
        $type = $this->getTypeFromRegistry($name, $isEntityId);
64
65 26
        if ($isList) {
66 6
            $type = Type::listOf(Type::nonNull($type));
67
        }
68
69 26
        if (!$isNullable) {
70 15
            $type = Type::nonNull($type);
71
        }
72
73 26
        return $type;
74
    }
75
76
    /**
77
     * Prepend namespace of the method if the class actually exists.
78
     */
79 26
    private function adjustNamespace(ReflectionClass $class, string $type): string
80
    {
81 26
        if ($type === 'self') {
82 2
            $type = $class->getName();
83
        }
84
85 26
        $namespace = $class->getNamespaceName();
86 26
        if ($namespace) {
87 26
            $namespacedType = $namespace . '\\' . $type;
88
89 26
            if (class_exists($namespacedType)) {
90 2
                return $namespacedType;
91
            }
92
        }
93
94 26
        return $type;
95
    }
96
97
    /**
98
     * Returns a type from our registry.
99
     */
100 26
    final protected function getTypeFromRegistry(string $type, bool $isEntityId): Type
101
    {
102 26
        if ($this->types->isEntity($type) && $isEntityId) {
103
            // @phpstan-ignore-next-line
104 8
            return $this->types->getId($type);
105
        }
106
107 26
        if ($this->types->isEntity($type) && !$isEntityId) {
108
            // @phpstan-ignore-next-line
109 9
            return $this->types->getOutput($type);
110
        }
111
112 24
        return $this->types->get($type);
113
    }
114
}
115