Completed
Push — master ( bcdd48...673801 )
by David
20:07
created

GlobTypeMapper::mapClassToType()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

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