GlobTypeMapper::__construct()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 13
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 11
dl 0
loc 13
rs 9.9
c 0
b 0
f 0
cc 1
nc 1
nop 11

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

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