Passed
Pull Request — master (#78)
by David
02:46
created

GlobTypeMapper::getMapNameToExtendType()   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
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
0 ignored issues
show
Unused Code introduced by
The method getMapNameToType() is not used, and could be removed.

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
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
0 ignored issues
show
Unused Code introduced by
The method getMapInputNameToFactory() is not used, and could be removed.

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
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
                $this->getMaps();
644
            }
645
        }
646
647
        if (isset($this->mapNameToType[$typeName])) {
648
            return $this->typeGenerator->mapAnnotatedObject($this->container->get($this->mapNameToType[$typeName]), $recursiveTypeMapper);
649
        }
650
        if (isset($this->mapInputNameToFactory[$typeName])) {
651
            $factory = $this->mapInputNameToFactory[$typeName];
652
            return $this->inputTypeGenerator->mapFactoryMethod($this->container->get($factory[0]), $factory[1], $recursiveTypeMapper);
653
        }
654
655
        throw CannotMapTypeException::createForName($typeName);
656
    }
657
658
    /**
659
     * Returns true if this type mapper can map the $typeName GraphQL name to a GraphQL type.
660
     *
661
     * @param string $typeName The name of the GraphQL type
662
     * @return bool
663
     */
664
    public function canMapNameToType(string $typeName): bool
665
    {
666
        $typeClassName = $this->getTypeFromCacheByGraphQLTypeName($typeName);
667
668
        if ($typeClassName !== null) {
669
            return true;
670
        }
671
672
        $factory = $this->getFactoryFromCacheByGraphQLInputTypeName($typeName);
673
        if ($factory !== null) {
674
            return true;
675
        }
676
677
        $this->getMaps();
678
679
        return isset($this->mapNameToType[$typeName]) || isset($this->mapInputNameToFactory[$typeName]);
680
    }
681
682
    /**
683
     * Returns true if this type mapper can extend an existing type for the $className FQCN
684
     *
685
     * @param string $className
686
     * @param MutableObjectType $type
687
     * @return bool
688
     */
689
    public function canExtendTypeForClass(string $className, MutableObjectType $type, RecursiveTypeMapperInterface $recursiveTypeMapper): bool
690
    {
691
        $extendTypeClassName = $this->getExtendTypesFromCacheByObjectClass($className);
692
693
        if ($extendTypeClassName === null) {
694
            $map = $this->getMapClassToExtendTypeArray($recursiveTypeMapper);
0 ignored issues
show
Unused Code introduced by
The assignment to $map is dead and can be removed.
Loading history...
695
        }
696
697
        return isset($this->mapClassToExtendTypeArray[$className]);
698
    }
699
700
    /**
701
     * Extends the existing GraphQL type that is mapped to $className.
702
     *
703
     * @param string $className
704
     * @param MutableObjectType $type
705
     * @param RecursiveTypeMapperInterface $recursiveTypeMapper
706
     * @throws CannotMapTypeExceptionInterface
707
     */
708
    public function extendTypeForClass(string $className, MutableObjectType $type, RecursiveTypeMapperInterface $recursiveTypeMapper): void
709
    {
710
        $extendTypeClassNames = $this->getExtendTypesFromCacheByObjectClass($className);
711
712
        if ($extendTypeClassNames === null) {
713
            $this->getExtendMaps($recursiveTypeMapper);
714
        }
715
716
        if (!isset($this->mapClassToExtendTypeArray[$className])) {
717
            throw CannotMapTypeException::createForExtendType($className, $type);
718
        }
719
720
        foreach ($this->mapClassToExtendTypeArray[$className] as $extendedTypeClass) {
721
            $this->typeGenerator->extendAnnotatedObject($this->container->get($extendedTypeClass), $type, $recursiveTypeMapper);
722
        }
723
    }
724
725
    /**
726
     * Returns true if this type mapper can extend an existing type for the $typeName GraphQL type
727
     *
728
     * @param string $typeName
729
     * @param MutableObjectType $type
730
     * @return bool
731
     */
732
    public function canExtendTypeForName(string $typeName, MutableObjectType $type, RecursiveTypeMapperInterface $recursiveTypeMapper): bool
733
    {
734
        $typeClassNames = $this->getExtendTypesFromCacheByGraphQLTypeName($typeName);
735
736
        if ($typeClassNames !== null) {
737
            return true;
738
        }
739
740
        /*$factory = $this->getFactoryFromCacheByGraphQLInputTypeName($typeName);
741
        if ($factory !== null) {
742
            return true;
743
        }*/
744
745
        $this->getExtendMaps($recursiveTypeMapper);
746
747
        return isset($this->mapNameToExtendType[$typeName])/* || isset($this->mapInputNameToFactory[$typeName])*/;
748
    }
749
750
    /**
751
     * Extends the existing GraphQL type that is mapped to the $typeName GraphQL type.
752
     *
753
     * @param string $typeName
754
     * @param MutableObjectType $type
755
     * @param RecursiveTypeMapperInterface $recursiveTypeMapper
756
     * @throws CannotMapTypeExceptionInterface
757
     */
758
    public function extendTypeForName(string $typeName, MutableObjectType $type, RecursiveTypeMapperInterface $recursiveTypeMapper): void
759
    {
760
        $extendTypeClassNames = $this->getExtendTypesFromCacheByGraphQLTypeName($typeName);
761
        if ($extendTypeClassNames === null) {
762
            /*$factory = $this->getFactoryFromCacheByGraphQLInputTypeName($typeName);
763
            if ($factory === null) {*/
764
                $map = $this->getMapNameToExtendType($recursiveTypeMapper);
765
                if (!isset($map[$typeName])) {
766
                    throw CannotMapTypeException::createForExtendName($typeName, $type);
767
                }
768
                $extendTypeClassNames = $map[$typeName];
769
770
            //}
771
        }
772
773
774
        foreach ($extendTypeClassNames as $extendedTypeClass) {
775
            $this->typeGenerator->extendAnnotatedObject($this->container->get($extendedTypeClass), $type, $recursiveTypeMapper);
776
        }
777
778
        /*if (isset($this->mapInputNameToFactory[$typeName])) {
779
            $factory = $this->mapInputNameToFactory[$typeName];
780
            return $this->inputTypeGenerator->mapFactoryMethod($this->container->get($factory[0]), $factory[1], $recursiveTypeMapper);
781
        }*/
782
    }
783
}
784