Passed
Pull Request — master (#25)
by David
01:57
created

GlobTypeMapper::canMapClassToType()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
1
<?php
2
3
4
namespace TheCodingMachine\GraphQL\Controllers\Mappers;
5
use Doctrine\Common\Annotations\AnnotationReader;
6
use Psr\Container\ContainerInterface;
7
use Psr\SimpleCache\CacheInterface;
8
use TheCodingMachine\ClassExplorer\Glob\GlobClassExplorer;
9
use TheCodingMachine\GraphQL\Controllers\Annotations\Type;
10
use Youshido\GraphQL\Type\InputTypeInterface;
11
use Youshido\GraphQL\Type\TypeInterface;
12
13
/**
14
 * Scans all the classes in a given namespace of the main project (not the vendor directory).
15
 * Analyzes all classes and uses the @Type annotation to find the types automatically.
16
 *
17
 * Assumes that the container contains a class whose identifier is the same as the class name.
18
 */
19
final class GlobTypeMapper implements TypeMapperInterface
20
{
21
    /**
22
     * @var string
23
     */
24
    private $namespace;
25
    /**
26
     * @var AnnotationReader
27
     */
28
    private $annotationReader;
29
    /**
30
     * @var CacheInterface
31
     */
32
    private $cache;
33
    /**
34
     * @var int|null
35
     */
36
    private $cacheTtl;
37
    /**
38
     * @var array<string,string>|null
39
     */
40
    private $map;
41
    /**
42
     * @var ContainerInterface
43
     */
44
    private $container;
45
46
    /**
47
     * @param string $namespace The namespace that contains the GraphQL types (they must have a `@Type` annotation)
48
     */
49
    public function __construct(string $namespace, ContainerInterface $container, AnnotationReader $annotationReader, CacheInterface $cache, ?int $cacheTtl = null)
50
    {
51
        $this->namespace = $namespace;
52
        $this->container = $container;
53
        $this->annotationReader = $annotationReader;
54
        $this->cache = $cache;
55
        $this->cacheTtl = $cacheTtl;
56
    }
57
58
    /**
59
     * Returns an array of fully qualified class names.
60
     *
61
     * @return array<string,string>
62
     */
63
    private function getMap(): array
64
    {
65
        if ($this->map === null) {
66
            $key = 'globTypeMapper_'.$this->namespace;
67
            $this->map = $this->cache->get($key);
68
            if ($this->map === null) {
69
                $this->map = $this->buildMap();
70
                $this->cache->set($key, $this->map, $this->cacheTtl);
71
            }
72
        }
73
        return $this->map;
74
    }
75
76
    /**
77
     * @return array<string,string>
78
     */
79
    private function buildMap(): array
80
    {
81
        $explorer = new GlobClassExplorer($this->namespace, $this->cache, $this->cacheTtl);
82
        $classes = $explorer->getClasses();
83
        $map = [];
84
        foreach ($classes as $className) {
85
            if (!\class_exists($className)) {
86
                continue;
87
            }
88
            $refClass = new \ReflectionClass($className);
89
            /** @var Type $type */
90
            $type = $this->annotationReader->getClassAnnotation($refClass, Type::class);
91
            if ($type === null) {
92
                continue;
93
            }
94
            $map[$type->getClass()] = $className;
95
        }
96
        return $map;
97
    }
98
99
    /**
100
     * Returns true if this type mapper can map the $className FQCN to a GraphQL type.
101
     *
102
     * @param string $className
103
     * @return bool
104
     */
105
    public function canMapClassToType(string $className): bool
106
    {
107
        return isset($this->getMap()[$className]);
108
    }
109
110
    /**
111
     * Maps a PHP fully qualified class name to a GraphQL type.
112
     *
113
     * @param string $className
114
     * @return TypeInterface
115
     * @throws CannotMapTypeException
116
     */
117
    public function mapClassToType(string $className): TypeInterface
118
    {
119
        $map = $this->getMap()[$className];
120
        if (!isset($map[$className])) {
121
            throw CannotMapTypeException::createForType($className);
122
        }
123
        return $this->container->get($map[$className]);
124
    }
125
126
    /**
127
     * Returns true if this type mapper can map the $className FQCN to a GraphQL input type.
128
     *
129
     * @param string $className
130
     * @return bool
131
     */
132
    public function canMapClassToInputType(string $className): bool
133
    {
134
        return false;
135
    }
136
137
    /**
138
     * Maps a PHP fully qualified class name to a GraphQL input type.
139
     *
140
     * @param string $className
141
     * @return InputTypeInterface
142
     * @throws CannotMapTypeException
143
     */
144
    public function mapClassToInputType(string $className): InputTypeInterface
145
    {
146
        throw CannotMapTypeException::createForInputType($className);
147
    }
148
}
149