Completed
Push — master ( 6c2ef1...d70f56 )
by David
15s queued 10s
created

GlobTypeMapper   F

Complexity

Total Complexity 93

Size/Duplication

Total Lines 749
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 93
eloc 295
dl 0
loc 749
rs 2
c 0
b 0
f 0

32 Methods

Rating   Name   Duplication   Size   Complexity  
A getMapInputNameToFactory() 0 3 1
A __construct() 0 12 1
A getMapNameToType() 0 3 1
B getMaps() 0 32 6
A getMapNameToExtendType() 0 3 1
A getMapClassToFactory() 0 3 1
B buildMap() 0 26 7
A getExtendMaps() 0 22 4
A getMapClassToType() 0 3 1
A getClassList() 0 18 5
A getMapClassToExtendTypeArray() 0 3 1
A storeTypeInCache() 0 16 1
A getSupportedClasses() 0 3 1
A canMapClassToInputType() 0 9 2
A canMapClassToType() 0 11 2
A getTypeFromCacheByObjectClass() 0 23 4
A mapClassToType() 0 13 3
A mapClassToInputType() 0 13 3
A getTypeFromCacheByGraphQLTypeName() 0 23 4
A buildExtendMap() 0 10 3
A getExtendTypesFromCacheByGraphQLTypeName() 0 23 4
A getExtendTypesFromCacheByObjectClass() 0 23 4
A storeInputTypeInCache() 0 15 1
A getFactoryFromCacheByObjectClass() 0 23 4
A storeExtendTypeInCache() 0 19 1
A getFactoryFromCacheByGraphQLInputTypeName() 0 23 4
A extendTypeForName() 0 18 4
A canExtendTypeForClass() 0 9 2
A canMapNameToType() 0 16 4
B mapNameToType() 0 26 7
A extendTypeForClass() 0 14 4
A canExtendTypeForName() 0 16 2

How to fix   Complexity   

Complex Class

Complex classes like GlobTypeMapper often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use GlobTypeMapper, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
4
namespace TheCodingMachine\GraphQL\Controllers\Mappers;
5
6
use function array_keys;
7
use function filemtime;
8
use GraphQL\Type\Definition\InputObjectType;
9
use GraphQL\Type\Definition\InputType;
10
use GraphQL\Type\Definition\ObjectType;
11
use GraphQL\Type\Definition\OutputType;
12
use Mouf\Composer\ClassNameMapper;
13
use Psr\Container\ContainerInterface;
14
use Psr\SimpleCache\CacheInterface;
15
use ReflectionClass;
16
use ReflectionMethod;
17
use TheCodingMachine\ClassExplorer\Glob\GlobClassExplorer;
18
use TheCodingMachine\GraphQL\Controllers\AnnotationReader;
19
use TheCodingMachine\GraphQL\Controllers\Annotations\ExtendType;
20
use TheCodingMachine\GraphQL\Controllers\Annotations\Factory;
21
use TheCodingMachine\GraphQL\Controllers\Annotations\Type;
22
use TheCodingMachine\GraphQL\Controllers\InputTypeGenerator;
23
use TheCodingMachine\GraphQL\Controllers\InputTypeUtils;
24
use TheCodingMachine\GraphQL\Controllers\NamingStrategy;
25
use TheCodingMachine\GraphQL\Controllers\TypeGenerator;
26
use TheCodingMachine\GraphQL\Controllers\Types\MutableObjectType;
27
28
/**
29
 * Scans all the classes in a given namespace of the main project (not the vendor directory).
30
 * Analyzes all classes and uses the @Type annotation to find the types automatically.
31
 *
32
 * Assumes that the container contains a class whose identifier is the same as the class name.
33
 */
34
final class GlobTypeMapper implements TypeMapperInterface
35
{
36
    /**
37
     * @var string
38
     */
39
    private $namespace;
40
    /**
41
     * @var AnnotationReader
42
     */
43
    private $annotationReader;
44
    /**
45
     * @var CacheInterface
46
     */
47
    private $cache;
48
    /**
49
     * @var int|null
50
     */
51
    private $globTtl;
52
    /**
53
     * @var array<string,string> Maps a domain class to the GraphQL type annotated class
54
     */
55
    private $mapClassToTypeArray = [];
56
    /**
57
     * @var array<string,array<string,string>> Maps a domain class to one or many type extenders (with the @ExtendType annotation) The array of type extenders has a key and value equals to FQCN
58
     */
59
    private $mapClassToExtendTypeArray = [];
60
    /**
61
     * @var array<string,string> Maps a GraphQL type name to the GraphQL type annotated class
62
     */
63
    private $mapNameToType = [];
64
    /**
65
     * @var array<string,array<string,string>> Maps a GraphQL type name to one or many type extenders (with the @ExtendType annotation) The array of type extenders has a key and value equals to FQCN
66
     */
67
    private $mapNameToExtendType = [];
68
    /**
69
     * @var array<string,string[]> Maps a domain class to the factory method that creates the input type in the form [classname, methodname]
70
     */
71
    private $mapClassToFactory = [];
72
    /**
73
     * @var array<string,string[]> Maps a GraphQL input type name to the factory method that creates the input type in the form [classname, methodname]
74
     */
75
    private $mapInputNameToFactory = [];
76
    /**
77
     * @var ContainerInterface
78
     */
79
    private $container;
80
    /**
81
     * @var TypeGenerator
82
     */
83
    private $typeGenerator;
84
    /**
85
     * @var int|null
86
     */
87
    private $mapTtl;
88
    /**
89
     * @var bool
90
     */
91
    private $fullMapComputed = false;
92
    /**
93
     * @var bool
94
     */
95
    private $fullExtendMapComputed = false;
96
    /**
97
     * @var NamingStrategy
98
     */
99
    private $namingStrategy;
100
    /**
101
     * @var InputTypeGenerator
102
     */
103
    private $inputTypeGenerator;
104
    /**
105
     * @var InputTypeUtils
106
     */
107
    private $inputTypeUtils;
108
    /**
109
     * The array of globbed classes.
110
     * Only instantiable classes are returned.
111
     * Key: fully qualified class name
112
     *
113
     * @var array<string,ReflectionClass>
114
     */
115
    private $classes;
116
117
    /**
118
     * @param string $namespace The namespace that contains the GraphQL types (they must have a `@Type` annotation)
119
     */
120
    public function __construct(string $namespace, TypeGenerator $typeGenerator, InputTypeGenerator $inputTypeGenerator, InputTypeUtils $inputTypeUtils, ContainerInterface $container, AnnotationReader $annotationReader, NamingStrategy $namingStrategy, CacheInterface $cache, ?int $globTtl = 2, ?int $mapTtl = null)
121
    {
122
        $this->namespace = $namespace;
123
        $this->typeGenerator = $typeGenerator;
124
        $this->container = $container;
125
        $this->annotationReader = $annotationReader;
126
        $this->namingStrategy = $namingStrategy;
127
        $this->cache = $cache;
128
        $this->globTtl = $globTtl;
129
        $this->mapTtl = $mapTtl;
130
        $this->inputTypeGenerator = $inputTypeGenerator;
131
        $this->inputTypeUtils = $inputTypeUtils;
132
    }
133
134
    /**
135
     * Returns an array of fully qualified class names.
136
     *
137
     * @return array<string, array<string,string>>
138
     */
139
    private function getMaps(): array
140
    {
141
        if ($this->fullMapComputed === false) {
142
            $namespace = str_replace('\\', '_', $this->namespace);
143
            $keyClassCache = 'globTypeMapper_'.$namespace;
144
            $keyNameCache = 'globTypeMapper_names_'.$namespace;
145
            $keyInputClassCache = 'globInputTypeMapper_'.$namespace;
146
            $keyInputNameCache = 'globInputTypeMapper_names_'.$namespace;
147
            $this->mapClassToTypeArray = $this->cache->get($keyClassCache);
148
            $this->mapNameToType = $this->cache->get($keyNameCache);
149
            $this->mapClassToFactory = $this->cache->get($keyInputClassCache);
150
            $this->mapInputNameToFactory = $this->cache->get($keyInputNameCache);
151
            if ($this->mapClassToTypeArray === null ||
152
                $this->mapNameToType === null ||
153
                $this->mapClassToFactory === null ||
154
                $this->mapInputNameToFactory
155
            ) {
156
                $this->buildMap();
157
                // This is a very short lived cache. Useful to avoid overloading a server in case of heavy load.
158
                // Defaults to 2 seconds.
159
                $this->cache->set($keyClassCache, $this->mapClassToTypeArray, $this->globTtl);
160
                $this->cache->set($keyNameCache, $this->mapNameToType, $this->globTtl);
161
                $this->cache->set($keyInputClassCache, $this->mapClassToFactory, $this->globTtl);
162
                $this->cache->set($keyInputNameCache, $this->mapInputNameToFactory, $this->globTtl);
163
            }
164
            $this->fullMapComputed = true;
165
        }
166
        return [
167
            'mapClassToTypeArray' => $this->mapClassToTypeArray,
168
            'mapNameToType' => $this->mapNameToType,
169
            'mapClassToFactory' => $this->mapClassToFactory,
170
            'mapInputNameToFactory' => $this->mapInputNameToFactory,
171
        ];
172
    }
173
174
    private function getMapClassToType(): array
175
    {
176
        return $this->getMaps()['mapClassToTypeArray'];
177
    }
178
179
    private function getMapNameToType(): array
180
    {
181
        return $this->getMaps()['mapNameToType'];
182
    }
183
184
    private function getMapClassToFactory(): array
185
    {
186
        return $this->getMaps()['mapClassToFactory'];
187
    }
188
189
    private function getMapInputNameToFactory(): array
190
    {
191
        return $this->getMaps()['mapInputNameToFactory'];
192
    }
193
194
    /**
195
     * Returns an array of fully qualified class names.
196
     *
197
     * @return array<string,array<string,string>>
198
     */
199
    private function getExtendMaps(RecursiveTypeMapperInterface $recursiveTypeMapper): array
200
    {
201
        if ($this->fullExtendMapComputed === false) {
202
            $namespace = str_replace('\\', '_', $this->namespace);
203
            $keyExtendClassCache = 'globTypeMapperExtend_'.$namespace;
204
            $keyExtendNameCache = 'globTypeMapperExtend_names_'.$namespace;
205
            $this->mapClassToExtendTypeArray = $this->cache->get($keyExtendClassCache);
206
            $this->mapNameToExtendType = $this->cache->get($keyExtendNameCache);
207
            if ($this->mapClassToExtendTypeArray === null ||
208
                $this->mapNameToExtendType === null
209
            ) {
210
                $this->buildExtendMap($recursiveTypeMapper);
211
                // This is a very short lived cache. Useful to avoid overloading a server in case of heavy load.
212
                // Defaults to 2 seconds.
213
                $this->cache->set($keyExtendClassCache, $this->mapClassToExtendTypeArray, $this->globTtl);
214
                $this->cache->set($keyExtendNameCache, $this->mapNameToExtendType, $this->globTtl);
215
            }
216
            $this->fullExtendMapComputed = true;
217
        }
218
        return [
0 ignored issues
show
Bug Best Practice introduced by
The expression return array('mapClassTo...s->mapNameToExtendType) returns the type array<string,array<string,array<string,string>>> which is incompatible with the documented return type array<string,array<string,string>>.
Loading history...
219
            'mapClassToExtendTypeArray' => $this->mapClassToExtendTypeArray,
220
            'mapNameToExtendType' => $this->mapNameToExtendType,
221
        ];
222
    }
223
224
    private function getMapClassToExtendTypeArray(RecursiveTypeMapperInterface $recursiveTypeMapper): array
225
    {
226
        return $this->getExtendMaps($recursiveTypeMapper)['mapClassToExtendTypeArray'];
227
    }
228
229
    private function getMapNameToExtendType(RecursiveTypeMapperInterface $recursiveTypeMapper): array
230
    {
231
        return $this->getExtendMaps($recursiveTypeMapper)['mapNameToExtendType'];
232
    }
233
234
    /**
235
     * Returns the array of globbed classes.
236
     * Only instantiable classes are returned.
237
     *
238
     * @return array<string,ReflectionClass> Key: fully qualified class name
239
     */
240
    private function getClassList(): array
241
    {
242
        if ($this->classes === null) {
243
            $this->classes = [];
244
            $explorer = new GlobClassExplorer($this->namespace, $this->cache, $this->globTtl, ClassNameMapper::createFromComposerFile(null, null, true));
245
            $classes = $explorer->getClasses();
246
            foreach ($classes as $className) {
247
                if (!\class_exists($className)) {
248
                    continue;
249
                }
250
                $refClass = new \ReflectionClass($className);
251
                if (!$refClass->isInstantiable()) {
252
                    continue;
253
                }
254
                $this->classes[$className] = $refClass;
255
            }
256
        }
257
        return $this->classes;
258
    }
259
260
    private function buildMap(): void
261
    {
262
        $classes = $this->getClassList();
263
        foreach ($classes as $className => $refClass) {
264
            $type = $this->annotationReader->getTypeAnnotation($refClass);
265
266
            if ($type !== null) {
267
                if (isset($this->mapClassToTypeArray[$type->getClass()])) {
268
                    /*if ($this->mapClassToTypeArray[$type->getClass()] === $className) {
269
                        // Already mapped. Let's continue
270
                        continue;
271
                    }*/
272
                    throw DuplicateMappingException::createForType($type->getClass(), $this->mapClassToTypeArray[$type->getClass()], $className);
273
                }
274
                $this->storeTypeInCache($className, $type, $refClass->getFileName());
275
            }
276
277
            foreach ($refClass->getMethods() as $method) {
278
                $factory = $this->annotationReader->getFactoryAnnotation($method);
279
                if ($factory !== null) {
280
                    [$inputName, $className] = $this->inputTypeUtils->getInputTypeNameAndClassName($method);
281
282
                    if (isset($this->mapClassToFactory[$className])) {
283
                        throw DuplicateMappingException::createForFactory($className, $this->mapClassToFactory[$className][0], $this->mapClassToFactory[$className][1], $refClass->getName(), $method->name);
284
                    }
285
                    $this->storeInputTypeInCache($method, $inputName, $className, $refClass->getFileName());
286
                }
287
            }
288
289
        }
290
    }
291
292
    private function buildExtendMap(RecursiveTypeMapperInterface $recursiveTypeMapper): void
293
    {
294
        $this->mapClassToExtendTypeArray = [];
295
        $this->mapNameToExtendType = [];
296
        $classes = $this->getClassList();
297
        foreach ($classes as $className => $refClass) {
298
            $extendType = $this->annotationReader->getExtendTypeAnnotation($refClass);
299
300
            if ($extendType !== null) {
301
                $this->storeExtendTypeInCache($className, $extendType, $refClass->getFileName(), $recursiveTypeMapper);
302
            }
303
        }
304
    }
305
306
    /**
307
     * Stores in cache the mapping TypeClass <=> Object class <=> GraphQL type name.
308
     */
309
    private function storeTypeInCache(string $typeClassName, Type $type, string $typeFileName): void
310
    {
311
        $objectClassName = $type->getClass();
312
        $this->mapClassToTypeArray[$objectClassName] = $typeClassName;
313
        $this->cache->set('globTypeMapperByClass_'.str_replace('\\', '_', $objectClassName), [
314
            'filemtime' => filemtime($typeFileName),
315
            'fileName' => $typeFileName,
316
            'typeClass' => $typeClassName
317
        ], $this->mapTtl);
318
        $typeName = $this->namingStrategy->getOutputTypeName($typeClassName, $type);
319
        $this->mapNameToType[$typeName] = $typeClassName;
320
        $this->cache->set('globTypeMapperByName_'.$typeName, [
321
            'filemtime' => filemtime($typeFileName),
322
            'fileName' => $typeFileName,
323
            'typeClass' => $typeClassName
324
        ], $this->mapTtl);
325
    }
326
327
    /**
328
     * Stores in cache the mapping between InputType name <=> Object class
329
     */
330
    private function storeInputTypeInCache(ReflectionMethod $refMethod, string $inputName, string $className, string $fileName): void
331
    {
332
        $refArray = [$refMethod->getDeclaringClass()->getName(), $refMethod->getName()];
333
        $this->mapClassToFactory[$className] = $refArray;
334
        $this->cache->set('globInputTypeMapperByClass_'.str_replace('\\', '_', $className), [
335
            'filemtime' => filemtime($fileName),
336
            'fileName' => $fileName,
337
            'factory' => $refArray
338
        ], $this->mapTtl);
339
        $this->mapInputNameToFactory[$inputName] = $refArray;
340
        $this->cache->set('globInputTypeMapperByName_'.$inputName, [
341
            'filemtime' => filemtime($fileName),
342
            'fileName' => $fileName,
343
            'factory' => $refArray
344
        ], $this->mapTtl);
345
    }
346
347
    /**
348
     * Stores in cache the mapping ExtendTypeClass <=> Object class <=> GraphQL type name.
349
     */
350
    private function storeExtendTypeInCache(string $extendTypeClassName, ExtendType $extendType, string $typeFileName, RecursiveTypeMapperInterface $recursiveTypeMapper): void
351
    {
352
        $objectClassName = $extendType->getClass();
353
        $this->mapClassToExtendTypeArray[$objectClassName][$extendTypeClassName] = $extendTypeClassName;
354
        $this->cache->set('globExtendTypeMapperByClass_'.str_replace('\\', '_', $objectClassName), [
355
            'filemtime' => filemtime($typeFileName),
356
            'fileName' => $typeFileName,
357
            'extendTypeClasses' => $this->mapClassToExtendTypeArray[$objectClassName]
358
        ], $this->mapTtl);
359
360
        $targetType = $recursiveTypeMapper->mapClassToType($extendType->getClass(), null);
361
        $typeName = $targetType->name;
362
363
        $this->mapNameToExtendType[$typeName][$extendTypeClassName] = $extendTypeClassName;
364
        $this->cache->set('globExtendTypeMapperByName_'.$typeName, [
365
            'filemtime' => filemtime($typeFileName),
366
            'fileName' => $typeFileName,
367
            'extendTypeClasses' => $this->mapClassToExtendTypeArray[$objectClassName]
368
        ], $this->mapTtl);
369
    }
370
371
    private function getTypeFromCacheByObjectClass(string $className): ?string
372
    {
373
        if (isset($this->mapClassToTypeArray[$className])) {
374
            return $this->mapClassToTypeArray[$className];
375
        }
376
377
        // Let's try from the cache
378
        $item = $this->cache->get('globTypeMapperByClass_'.str_replace('\\', '_', $className));
379
        if ($item !== null) {
380
            [
381
                'filemtime' => $filemtime,
382
                'fileName' => $typeFileName,
383
                'typeClass' => $typeClassName
384
            ] = $item;
385
386
            if ($filemtime === filemtime($typeFileName)) {
387
                $this->mapClassToTypeArray[$className] = $typeClassName;
388
                return $typeClassName;
389
            }
390
        }
391
392
        // cache miss
393
        return null;
394
    }
395
396
    private function getTypeFromCacheByGraphQLTypeName(string $graphqlTypeName): ?string
397
    {
398
        if (isset($this->mapNameToType[$graphqlTypeName])) {
399
            return $this->mapNameToType[$graphqlTypeName];
400
        }
401
402
        // Let's try from the cache
403
        $item = $this->cache->get('globTypeMapperByName_'.$graphqlTypeName);
404
        if ($item !== null) {
405
            [
406
                'filemtime' => $filemtime,
407
                'fileName' => $typeFileName,
408
                'typeClass' => $typeClassName
409
            ] = $item;
410
411
            if ($filemtime === filemtime($typeFileName)) {
412
                $this->mapNameToType[$graphqlTypeName] = $typeClassName;
413
                return $typeClassName;
414
            }
415
        }
416
417
        // cache miss
418
        return null;
419
    }
420
421
    /**
422
     * @return string[]|null A pointer to the factory [$className, $methodName] or null on cache miss
423
     */
424
    private function getFactoryFromCacheByObjectClass(string $className): ?array
425
    {
426
        if (isset($this->mapClassToFactory[$className])) {
427
            return $this->mapClassToFactory[$className];
428
        }
429
430
        // Let's try from the cache
431
        $item = $this->cache->get('globInputTypeMapperByClass_'.str_replace('\\', '_', $className));
432
        if ($item !== null) {
433
            [
434
                'filemtime' => $filemtime,
435
                'fileName' => $typeFileName,
436
                'factory' => $factory
437
            ] = $item;
438
439
            if ($filemtime === filemtime($typeFileName)) {
440
                $this->mapClassToFactory[$className] = $factory;
441
                return $factory;
442
            }
443
        }
444
445
        // cache miss
446
        return null;
447
    }
448
449
    /**
450
     * @param string $className
451
     * @return array<string,string>|null An array of classes with the @ExtendType annotation (key and value = FQCN)
452
     */
453
    private function getExtendTypesFromCacheByObjectClass(string $className): ?array
454
    {
455
        if (isset($this->mapClassToExtendTypeArray[$className])) {
456
            return $this->mapClassToExtendTypeArray[$className];
457
        }
458
459
        // Let's try from the cache
460
        $item = $this->cache->get('globExtendTypeMapperByClass_'.str_replace('\\', '_', $className));
461
        if ($item !== null) {
462
            [
463
                'filemtime' => $filemtime,
464
                'fileName' => $typeFileName,
465
                'extendTypeClasses' => $extendTypeClassNames
466
            ] = $item;
467
468
            if ($filemtime === filemtime($typeFileName)) {
469
                $this->mapClassToExtendTypeArray[$className] = $extendTypeClassNames;
470
                return $extendTypeClassNames;
471
            }
472
        }
473
474
        // cache miss
475
        return null;
476
    }
477
478
    /**
479
     * @param string $graphqlTypeName
480
     * @return array<string,string>|null An array of classes with the @ExtendType annotation (key and value = FQCN)
481
     */
482
    private function getExtendTypesFromCacheByGraphQLTypeName(string $graphqlTypeName): ?array
483
    {
484
        if (isset($this->mapNameToExtendType[$graphqlTypeName])) {
485
            return $this->mapNameToExtendType[$graphqlTypeName];
486
        }
487
488
        // Let's try from the cache
489
        $item = $this->cache->get('globExtendTypeMapperByName_'.$graphqlTypeName);
490
        if ($item !== null) {
491
            [
492
                'filemtime' => $filemtime,
493
                'fileName' => $typeFileName,
494
                'extendTypeClasses' => $extendTypeClassNames
495
            ] = $item;
496
497
            if ($filemtime === filemtime($typeFileName)) {
498
                $this->mapNameToExtendType[$graphqlTypeName] = $extendTypeClassNames;
499
                return $extendTypeClassNames;
500
            }
501
        }
502
503
        // cache miss
504
        return null;
505
    }
506
507
    /**
508
     * @return string[]|null A pointer to the factory [$className, $methodName] or null on cache miss
509
     */
510
    private function getFactoryFromCacheByGraphQLInputTypeName(string $graphqlTypeName): ?array
511
    {
512
        if (isset($this->mapInputNameToFactory[$graphqlTypeName])) {
513
            return $this->mapInputNameToFactory[$graphqlTypeName];
514
        }
515
516
        // Let's try from the cache
517
        $item = $this->cache->get('globInputTypeMapperByName_'.$graphqlTypeName);
518
        if ($item !== null) {
519
            [
520
                'filemtime' => $filemtime,
521
                'fileName' => $typeFileName,
522
                'factory' => $factory
523
            ] = $item;
524
525
            if ($filemtime === filemtime($typeFileName)) {
526
                $this->mapInputNameToFactory[$graphqlTypeName] = $factory;
527
                return $factory;
528
            }
529
        }
530
531
        // cache miss
532
        return null;
533
    }
534
535
    /**
536
     * Returns true if this type mapper can map the $className FQCN to a GraphQL type.
537
     *
538
     * @param string $className
539
     * @return bool
540
     */
541
    public function canMapClassToType(string $className): bool
542
    {
543
        $typeClassName = $this->getTypeFromCacheByObjectClass($className);
544
545
        if ($typeClassName !== null) {
546
            return true;
547
        }
548
549
        $map = $this->getMapClassToType();
550
551
        return isset($map[$className]);
552
    }
553
554
    /**
555
     * Maps a PHP fully qualified class name to a GraphQL type.
556
     *
557
     * @param string $className The exact class name to look for (this function does not look into parent classes).
558
     * @param OutputType|null $subType An optional sub-type if the main class is an iterator that needs to be typed.
559
     * @param RecursiveTypeMapperInterface $recursiveTypeMapper
560
     * @return MutableObjectType
561
     * @throws CannotMapTypeExceptionInterface
562
     */
563
    public function mapClassToType(string $className, ?OutputType $subType, RecursiveTypeMapperInterface $recursiveTypeMapper): MutableObjectType
564
    {
565
        $typeClassName = $this->getTypeFromCacheByObjectClass($className);
566
567
        if ($typeClassName === null) {
568
            $map = $this->getMapClassToType();
569
            if (!isset($map[$className])) {
570
                throw CannotMapTypeException::createForType($className);
571
            }
572
            $typeClassName = $map[$className];
573
        }
574
575
        return $this->typeGenerator->mapAnnotatedObject($this->container->get($typeClassName), $recursiveTypeMapper);
576
    }
577
578
    /**
579
     * Returns the list of classes that have matching input GraphQL types.
580
     *
581
     * @return string[]
582
     */
583
    public function getSupportedClasses(): array
584
    {
585
        return array_keys($this->getMapClassToType());
586
    }
587
588
    /**
589
     * Returns true if this type mapper can map the $className FQCN to a GraphQL input type.
590
     *
591
     * @param string $className
592
     * @return bool
593
     */
594
    public function canMapClassToInputType(string $className): bool
595
    {
596
        $factory = $this->getFactoryFromCacheByObjectClass($className);
597
598
        if ($factory !== null) {
599
            return true;
600
        }
601
        $map = $this->getMapClassToFactory();
602
        return isset($map[$className]);
603
    }
604
605
    /**
606
     * Maps a PHP fully qualified class name to a GraphQL input type.
607
     *
608
     * @param string $className
609
     * @param RecursiveTypeMapperInterface $recursiveTypeMapper
610
     * @return InputObjectType
611
     * @throws CannotMapTypeExceptionInterface
612
     */
613
    public function mapClassToInputType(string $className, RecursiveTypeMapperInterface $recursiveTypeMapper): InputObjectType
614
    {
615
        $factory = $this->getFactoryFromCacheByObjectClass($className);
616
617
        if ($factory === null) {
618
            $map = $this->getMapClassToFactory();
619
            if (!isset($map[$className])) {
620
                throw CannotMapTypeException::createForInputType($className);
621
            }
622
            $factory = $map[$className];
623
        }
624
625
        return $this->inputTypeGenerator->mapFactoryMethod($this->container->get($factory[0]), $factory[1], $recursiveTypeMapper);
626
    }
627
628
    /**
629
     * Returns a GraphQL type by name (can be either an input or output type)
630
     *
631
     * @param string $typeName The name of the GraphQL type
632
     * @param RecursiveTypeMapperInterface $recursiveTypeMapper
633
     * @return \GraphQL\Type\Definition\Type&(InputType|OutputType)
634
     * @throws CannotMapTypeExceptionInterface
635
     * @throws \ReflectionException
636
     */
637
    public function mapNameToType(string $typeName, RecursiveTypeMapperInterface $recursiveTypeMapper): \GraphQL\Type\Definition\Type
638
    {
639
        $typeClassName = $this->getTypeFromCacheByGraphQLTypeName($typeName);
640
        if ($typeClassName === null) {
641
            $factory = $this->getFactoryFromCacheByGraphQLInputTypeName($typeName);
642
            if ($factory === null) {
643
                $mapNameToType = $this->getMapNameToType();
644
                if (isset($mapNameToType[$typeName])) {
645
                    $typeClassName = $mapNameToType[$typeName];
646
                } else {
647
                    $mapInputNameToFactory = $this->getMapInputNameToFactory();
648
                    if (isset($mapInputNameToFactory[$typeName])) {
649
                        $factory = $mapInputNameToFactory[$typeName];
650
                    }
651
                }
652
            }
653
        }
654
655
        if (isset($typeClassName)) {
656
            return $this->typeGenerator->mapAnnotatedObject($this->container->get($typeClassName), $recursiveTypeMapper);
657
        }
658
        if (isset($factory)) {
659
            return $this->inputTypeGenerator->mapFactoryMethod($this->container->get($factory[0]), $factory[1], $recursiveTypeMapper);
660
        }
661
662
        throw CannotMapTypeException::createForName($typeName);
663
    }
664
665
    /**
666
     * Returns true if this type mapper can map the $typeName GraphQL name to a GraphQL type.
667
     *
668
     * @param string $typeName The name of the GraphQL type
669
     * @return bool
670
     */
671
    public function canMapNameToType(string $typeName): bool
672
    {
673
        $typeClassName = $this->getTypeFromCacheByGraphQLTypeName($typeName);
674
675
        if ($typeClassName !== null) {
676
            return true;
677
        }
678
679
        $factory = $this->getFactoryFromCacheByGraphQLInputTypeName($typeName);
680
        if ($factory !== null) {
681
            return true;
682
        }
683
684
        $this->getMaps();
685
686
        return isset($this->mapNameToType[$typeName]) || isset($this->mapInputNameToFactory[$typeName]);
687
    }
688
689
    /**
690
     * Returns true if this type mapper can extend an existing type for the $className FQCN
691
     *
692
     * @param string $className
693
     * @param MutableObjectType $type
694
     * @return bool
695
     */
696
    public function canExtendTypeForClass(string $className, MutableObjectType $type, RecursiveTypeMapperInterface $recursiveTypeMapper): bool
697
    {
698
        $extendTypeClassName = $this->getExtendTypesFromCacheByObjectClass($className);
699
700
        if ($extendTypeClassName === null) {
701
            $map = $this->getMapClassToExtendTypeArray($recursiveTypeMapper);
0 ignored issues
show
Unused Code introduced by
The assignment to $map is dead and can be removed.
Loading history...
702
        }
703
704
        return isset($this->mapClassToExtendTypeArray[$className]);
705
    }
706
707
    /**
708
     * Extends the existing GraphQL type that is mapped to $className.
709
     *
710
     * @param string $className
711
     * @param MutableObjectType $type
712
     * @param RecursiveTypeMapperInterface $recursiveTypeMapper
713
     * @throws CannotMapTypeExceptionInterface
714
     */
715
    public function extendTypeForClass(string $className, MutableObjectType $type, RecursiveTypeMapperInterface $recursiveTypeMapper): void
716
    {
717
        $extendTypeClassNames = $this->getExtendTypesFromCacheByObjectClass($className);
718
719
        if ($extendTypeClassNames === null) {
720
            $this->getExtendMaps($recursiveTypeMapper);
721
        }
722
723
        if (!isset($this->mapClassToExtendTypeArray[$className])) {
724
            throw CannotMapTypeException::createForExtendType($className, $type);
725
        }
726
727
        foreach ($this->mapClassToExtendTypeArray[$className] as $extendedTypeClass) {
728
            $this->typeGenerator->extendAnnotatedObject($this->container->get($extendedTypeClass), $type, $recursiveTypeMapper);
729
        }
730
    }
731
732
    /**
733
     * Returns true if this type mapper can extend an existing type for the $typeName GraphQL type
734
     *
735
     * @param string $typeName
736
     * @param MutableObjectType $type
737
     * @return bool
738
     */
739
    public function canExtendTypeForName(string $typeName, MutableObjectType $type, RecursiveTypeMapperInterface $recursiveTypeMapper): bool
740
    {
741
        $typeClassNames = $this->getExtendTypesFromCacheByGraphQLTypeName($typeName);
742
743
        if ($typeClassNames !== null) {
744
            return true;
745
        }
746
747
        /*$factory = $this->getFactoryFromCacheByGraphQLInputTypeName($typeName);
748
        if ($factory !== null) {
749
            return true;
750
        }*/
751
752
        $this->getExtendMaps($recursiveTypeMapper);
753
754
        return isset($this->mapNameToExtendType[$typeName])/* || isset($this->mapInputNameToFactory[$typeName])*/;
755
    }
756
757
    /**
758
     * Extends the existing GraphQL type that is mapped to the $typeName GraphQL type.
759
     *
760
     * @param string $typeName
761
     * @param MutableObjectType $type
762
     * @param RecursiveTypeMapperInterface $recursiveTypeMapper
763
     * @throws CannotMapTypeExceptionInterface
764
     */
765
    public function extendTypeForName(string $typeName, MutableObjectType $type, RecursiveTypeMapperInterface $recursiveTypeMapper): void
766
    {
767
        $extendTypeClassNames = $this->getExtendTypesFromCacheByGraphQLTypeName($typeName);
768
        if ($extendTypeClassNames === null) {
769
            /*$factory = $this->getFactoryFromCacheByGraphQLInputTypeName($typeName);
770
            if ($factory === null) {*/
771
                $map = $this->getMapNameToExtendType($recursiveTypeMapper);
772
                if (!isset($map[$typeName])) {
773
                    throw CannotMapTypeException::createForExtendName($typeName, $type);
774
                }
775
                $extendTypeClassNames = $map[$typeName];
776
777
            //}
778
        }
779
780
781
        foreach ($extendTypeClassNames as $extendedTypeClass) {
782
            $this->typeGenerator->extendAnnotatedObject($this->container->get($extendedTypeClass), $type, $recursiveTypeMapper);
783
        }
784
785
        /*if (isset($this->mapInputNameToFactory[$typeName])) {
786
            $factory = $this->mapInputNameToFactory[$typeName];
787
            return $this->inputTypeGenerator->mapFactoryMethod($this->container->get($factory[0]), $factory[1], $recursiveTypeMapper);
788
        }*/
789
    }
790
}
791