Passed
Push — master ( 18ff69...81bea8 )
by Adrien
02:23
created

AbstractFactory::adjustNamespace()   A

Complexity

Conditions 4
Paths 6

Size

Total Lines 16
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 4

Importance

Changes 0
Metric Value
eloc 8
c 0
b 0
f 0
dl 0
loc 16
ccs 9
cts 9
cp 1
rs 10
cc 4
nc 6
nop 2
crap 4
1
<?php
2
3
declare(strict_types=1);
4
5
namespace GraphQL\Doctrine\Factory;
6
7
use Doctrine\Common\Annotations\Reader;
8
use Doctrine\ORM\EntityManager;
9
use Doctrine\Persistence\Mapping\Driver\AnnotationDriver;
10
use Doctrine\Persistence\Mapping\Driver\MappingDriverChain;
11
use GraphQL\Doctrine\Annotation\Exclude;
12
use GraphQL\Doctrine\Exception;
13
use GraphQL\Doctrine\Factory\MetadataReader\MappingDriverChainAdapter;
14
use GraphQL\Doctrine\Types;
15
use GraphQL\Type\Definition\Type;
16
use ReflectionClass;
17
use ReflectionProperty;
18
19
/**
20
 * Abstract factory to be aware of types and entityManager
21
 */
22
abstract class AbstractFactory
23
{
24
    /**
25
     * @var Types
26
     */
27
    protected $types;
28
29
    /**
30
     * @var EntityManager
31
     */
32
    protected $entityManager;
33
34 109
    public function __construct(Types $types, EntityManager $entityManager)
35
    {
36 109
        $this->types = $types;
37 109
        $this->entityManager = $entityManager;
38 109
    }
39
40
    /**
41
     * Get annotation reader
42
     */
43 46
    final protected function getAnnotationReader(): Reader
44
    {
45 46
        $driver = $this->entityManager->getConfiguration()->getMetadataDriverImpl();
46 46
        if ($driver instanceof AnnotationDriver) {
47 42
            return $driver->getReader();
48
        }
49
50 4
        if ($driver instanceof MappingDriverChain) {
51 3
            return new MappingDriverChainAdapter($driver);
52
        }
53
54 1
        throw new Exception('graphql-doctrine requires Doctrine to be configured with a `' . AnnotationDriver::class . '`.');
55
    }
56
57
    /**
58
     * Returns whether the property is excluded
59
     */
60 17
    final protected function isPropertyExcluded(ReflectionProperty $property): bool
61
    {
62 17
        $exclude = $this->getAnnotationReader()->getPropertyAnnotation($property, Exclude::class);
63
64 17
        return $exclude !== null;
65
    }
66
67
    /**
68
     * Get instance of GraphQL type from a PHP class name
69
     *
70
     * Supported syntaxes are the following:
71
     *
72
     *  - `?MyType`
73
     *  - `null|MyType`
74
     *  - `MyType|null`
75
     *  - `MyType[]`
76
     *  - `?MyType[]`
77
     *  - `null|MyType[]`
78
     *  - `MyType[]|null`
79
     */
80 32
    final protected function getTypeFromPhpDeclaration(ReflectionClass $class, ?string $typeDeclaration, bool $isEntityId = false): ?Type
81
    {
82 32
        if (!$typeDeclaration) {
83 21
            return null;
84
        }
85
86 26
        $isNullable = 0;
87 26
        $name = preg_replace('~(^\?|^null\||\|null$)~', '', $typeDeclaration, -1, $isNullable);
88
89 26
        $isList = 0;
90 26
        $name = preg_replace('~^(.*)\[\]$~', '$1', $name, -1, $isList);
91 26
        $name = $this->adjustNamespace($class, $name);
92 26
        $type = $this->getTypeFromRegistry($name, $isEntityId);
93
94 26
        if ($isList) {
95 6
            $type = Type::listOf(Type::nonNull($type));
96
        }
97
98 26
        if (!$isNullable) {
99 15
            $type = Type::nonNull($type);
100
        }
101
102 26
        return $type;
103
    }
104
105
    /**
106
     * Prepend namespace of the method if the class actually exists
107
     */
108 26
    private function adjustNamespace(ReflectionClass $class, string $type): string
109
    {
110 26
        if ($type === 'self') {
111 2
            $type = $class->getName();
112
        }
113
114 26
        $namespace = $class->getNamespaceName();
115 26
        if ($namespace) {
116 26
            $namespacedType = $namespace . '\\' . $type;
117
118 26
            if (class_exists($namespacedType)) {
119 2
                return $namespacedType;
120
            }
121
        }
122
123 26
        return $type;
124
    }
125
126
    /**
127
     * Returns a type from our registry
128
     */
129 27
    final protected function getTypeFromRegistry(string $type, bool $isEntityId): Type
130
    {
131 27
        if ($this->types->isEntity($type) && $isEntityId) {
132 7
            return $this->types->getId($type);
133
        }
134
135 27
        if ($this->types->isEntity($type) && !$isEntityId) {
136 9
            return $this->types->getOutput($type);
137
        }
138
139 25
        return $this->types->get($type);
140
    }
141
}
142