Passed
Pull Request — master (#215)
by Christoffer
03:40
created

DefinitionBuilder::buildWrappedType()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
1
<?php
2
3
namespace Digia\GraphQL\Schema;
4
5
use Digia\GraphQL\Cache\CacheAwareTrait;
6
use Digia\GraphQL\Error\CoercingException;
7
use Digia\GraphQL\Error\ExecutionException;
8
use Digia\GraphQL\Error\InvalidTypeException;
9
use Digia\GraphQL\Error\InvariantException;
10
use Digia\GraphQL\Error\LanguageException;
11
use Digia\GraphQL\Language\Node\DirectiveDefinitionNode;
12
use Digia\GraphQL\Language\Node\EnumTypeDefinitionNode;
13
use Digia\GraphQL\Language\Node\EnumValueDefinitionNode;
14
use Digia\GraphQL\Language\Node\FieldDefinitionNode;
15
use Digia\GraphQL\Language\Node\InputObjectTypeDefinitionNode;
16
use Digia\GraphQL\Language\Node\InputValueDefinitionNode;
17
use Digia\GraphQL\Language\Node\InterfaceTypeDefinitionNode;
18
use Digia\GraphQL\Language\Node\ListTypeNode;
19
use Digia\GraphQL\Language\Node\NamedTypeNode;
20
use Digia\GraphQL\Language\Node\NameNode;
21
use Digia\GraphQL\Language\Node\NodeInterface;
22
use Digia\GraphQL\Language\Node\NonNullTypeNode;
23
use Digia\GraphQL\Language\Node\ObjectTypeDefinitionNode;
24
use Digia\GraphQL\Language\Node\ScalarTypeDefinitionNode;
25
use Digia\GraphQL\Language\Node\TypeDefinitionNodeInterface;
26
use Digia\GraphQL\Language\Node\TypeNodeInterface;
27
use Digia\GraphQL\Language\Node\UnionTypeDefinitionNode;
28
use Digia\GraphQL\Schema\Resolver\ResolverRegistryInterface;
29
use Digia\GraphQL\Type\Definition\Directive;
30
use Digia\GraphQL\Type\Definition\EnumType;
31
use Digia\GraphQL\Type\Definition\InputObjectType;
32
use Digia\GraphQL\Type\Definition\InterfaceType;
33
use Digia\GraphQL\Type\Definition\NamedTypeInterface;
34
use Digia\GraphQL\Type\Definition\ObjectType;
35
use Digia\GraphQL\Type\Definition\ScalarType;
36
use Digia\GraphQL\Type\Definition\TypeInterface;
37
use Digia\GraphQL\Type\Definition\UnionType;
38
use Psr\SimpleCache\CacheInterface;
39
use Psr\SimpleCache\InvalidArgumentException;
40
use function Digia\GraphQL\Execution\coerceDirectiveValues;
41
use function Digia\GraphQL\Type\assertNullableType;
42
use function Digia\GraphQL\Type\introspectionTypes;
43
use function Digia\GraphQL\Type\newDirective;
44
use function Digia\GraphQL\Type\newEnumType;
45
use function Digia\GraphQL\Type\newInputObjectType;
46
use function Digia\GraphQL\Type\newInterfaceType;
47
use function Digia\GraphQL\Type\newList;
48
use function Digia\GraphQL\Type\newNonNull;
49
use function Digia\GraphQL\Type\newObjectType;
50
use function Digia\GraphQL\Type\newScalarType;
51
use function Digia\GraphQL\Type\newUnionType;
52
use function Digia\GraphQL\Type\specifiedScalarTypes;
53
use function Digia\GraphQL\Util\keyMap;
54
use function Digia\GraphQL\Util\keyValueMap;
55
use function Digia\GraphQL\Util\valueFromAST;
56
57
class DefinitionBuilder implements DefinitionBuilderInterface
58
{
59
    use CacheAwareTrait;
60
61
    private const CACHE_PREFIX = 'GraphQL_DefinitionBuilder_';
62
63
    private const TYPE_CACHE_SUFFIX      = '_Type';
64
    private const DIRECTIVE_CACHE_SUFFIX = '_Directive';
65
66
    /**
67
     * @var array
68
     */
69
    protected $typeDefinitionsMap;
70
71
    /**
72
     * @var ResolverRegistryInterface
73
     */
74
    protected $resolverRegistry;
75
76
    /**
77
     * @var callable
78
     */
79
    protected $resolveTypeFunction;
80
81
    /**
82
     * DefinitionBuilder constructor.
83
     * @param array                          $typeDefinitionsMap
84
     * @param ResolverRegistryInterface|null $resolverRegistry
85
     * @param callable|null                  $resolveTypeFunction
86
     * @param CacheInterface                 $cache
87
     * @throws InvalidArgumentException
88
     */
89
    public function __construct(
90
        array $typeDefinitionsMap,
91
        ?ResolverRegistryInterface $resolverRegistry = null,
92
        array $customTypes = [],
93
        array $customDirectives = [],
94
        ?callable $resolveTypeFunction = null,
95
        CacheInterface $cache
96
    ) {
97
        $this->typeDefinitionsMap  = $typeDefinitionsMap;
98
        $this->resolverRegistry    = $resolverRegistry;
99
        $this->resolveTypeFunction = $resolveTypeFunction ?? [$this, 'defaultTypeResolver'];
0 ignored issues
show
Documentation Bug introduced by
It seems like $resolveTypeFunction ?? ... 'defaultTypeResolver') can also be of type array<integer,Digia\Grap...finitionBuilder|string>. However, the property $resolveTypeFunction is declared as type callable. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
100
        $this->cache               = $cache;
101
102
        $this->registerTypes($customTypes);
103
        $this->registerDirectives($customDirectives);
104
    }
105
106
    /**
107
     * @inheritdoc
108
     */
109
    public function buildTypes(array $nodes): array
110
    {
111
        return \array_map(function (NodeInterface $node) {
112
            return $this->buildType($node);
113
        }, $nodes);
114
    }
115
116
    /**
117
     * @inheritdoc
118
     * @param NamedTypeNode|TypeDefinitionNodeInterface $node
119
     */
120
    public function buildType(NodeInterface $node): NamedTypeInterface
121
    {
122
        $typeName = $node->getNameValue();
0 ignored issues
show
Bug introduced by
The method getNameValue() does not exist on Digia\GraphQL\Language\Node\NodeInterface. It seems like you code against a sub-type of Digia\GraphQL\Language\Node\NodeInterface such as Digia\GraphQL\Language\Node\DirectiveNode or Digia\GraphQL\Language\Node\ArgumentNode or Digia\GraphQL\Language\Node\ObjectFieldNode or Digia\GraphQL\Language\Node\FieldNode or Digia\GraphQL\Language\N...DefinitionNodeInterface or Digia\GraphQL\Language\N...DefinitionNodeInterface or Digia\GraphQL\Language\N...EnumValueDefinitionNode or Digia\GraphQL\Language\N...DirectiveDefinitionNode or Digia\GraphQL\Language\N...nputValueDefinitionNode or Digia\GraphQL\Language\Node\FieldDefinitionNode or Digia\GraphQL\Language\Node\FragmentSpreadNode or Digia\GraphQL\Language\Node\VariableNode or Digia\GraphQL\Language\Node\NamedTypeNode or Digia\GraphQL\Language\N...ObjectTypeExtensionNode or Digia\GraphQL\Language\N...ScalarTypeExtensionNode or Digia\GraphQL\Language\Node\EnumTypeExtensionNode or Digia\GraphQL\Language\N...ObjectTypeExtensionNode or Digia\GraphQL\Language\N...erfaceTypeExtensionNode or Digia\GraphQL\Language\Node\UnionTypeExtensionNode. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

122
        /** @scrutinizer ignore-call */ 
123
        $typeName = $node->getNameValue();
Loading history...
123
124
        if (!$this->isTypeInCache($typeName)) {
125
            if ($node instanceof NamedTypeNode) {
126
                $definition = $this->getTypeDefinition($typeName);
127
128
                $type = null !== $definition
129
                    ? $this->buildNamedType($definition)
130
                    : $this->resolveType($node);
131
132
                $this->setTypeInCache($typeName, $type);
133
            } else {
134
                $this->setTypeInCache($typeName, $this->buildNamedType($node));
135
            }
136
        }
137
138
        return $this->getTypeFromCache($typeName);
139
    }
140
141
    /**
142
     * @inheritdoc
143
     */
144
    public function buildDirective(DirectiveDefinitionNode $node): Directive
145
    {
146
        $directiveName = $node->getNameValue();
147
148
        if (!$this->isDirectiveInCache($directiveName)) {
149
            $directive = newDirective([
150
                'name'        => $node->getNameValue(),
151
                'description' => $node->getDescriptionValue(),
152
                'locations'   => \array_map(function (NameNode $node) {
153
                    return $node->getValue();
154
                }, $node->getLocations()),
155
                'args'        => $node->hasArguments() ? $this->buildArguments($node->getArguments()) : [],
156
                'astNode'     => $node,
157
            ]);
158
159
            $this->setDirectiveInCache($directiveName, $directive);
160
        }
161
162
        return $this->getDirectiveFromCache($directiveName);
163
    }
164
165
    /**
166
     * @inheritdoc
167
     */
168
    public function buildField($node, ?callable $resolve = null): array
169
    {
170
        return [
171
            'type'              => $this->buildWrappedType($node->getType()),
172
            'description'       => $node->getDescriptionValue(),
173
            'args'              => $node->hasArguments() ? $this->buildArguments($node->getArguments()) : [],
174
            'deprecationReason' => $this->getDeprecationReason($node),
175
            'resolve'           => $resolve,
176
            'astNode'           => $node,
177
        ];
178
    }
179
180
    /**
181
     * @param TypeNodeInterface $typeNode
182
     * @return TypeInterface
183
     * @throws InvariantException
184
     * @throws InvalidTypeException
185
     */
186
    protected function buildWrappedType(TypeNodeInterface $typeNode): TypeInterface
187
    {
188
        $typeDefinition = $this->buildType($this->getNamedTypeNode($typeNode));
189
        return $this->buildWrappedTypeRecursive($typeDefinition, $typeNode);
190
    }
191
192
    /**
193
     * @param TypeInterface      $innerType
194
     * @param NamedTypeInterface $inputTypeNode
195
     * @return TypeInterface
196
     * @throws InvariantException
197
     * @throws InvalidTypeException
198
     */
199
    protected function buildWrappedTypeRecursive(
200
        NamedTypeInterface $innerType,
201
        TypeNodeInterface $inputTypeNode
202
    ): TypeInterface {
203
        if ($inputTypeNode instanceof ListTypeNode) {
204
            return newList($this->buildWrappedTypeRecursive($innerType, $inputTypeNode->getType()));
205
        }
206
207
        if ($inputTypeNode instanceof NonNullTypeNode) {
208
            $wrappedType = $this->buildWrappedTypeRecursive($innerType, $inputTypeNode->getType());
209
            return newNonNull(assertNullableType($wrappedType));
210
        }
211
212
        return $innerType;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $innerType returns the type Digia\GraphQL\Type\Definition\NamedTypeInterface which is incompatible with the type-hinted return Digia\GraphQL\Type\Definition\TypeInterface.
Loading history...
213
    }
214
215
    /**
216
     * @param array $types
217
     * @throws InvalidArgumentException
218
     */
219
    protected function registerTypes(array $customDirectives)
220
    {
221
        $typesMap = keyMap(
222
            \array_merge($customDirectives, specifiedScalarTypes(), introspectionTypes()),
223
            function (NamedTypeInterface $type) {
224
                return $type->getName();
225
            }
226
        );
227
228
        foreach ($typesMap as $typeName => $type) {
229
            $this->setTypeInCache($typeName, $type);
230
        }
231
    }
232
233
    /**
234
     * @param array $directives
235
     * @throws InvalidArgumentException
236
     */
237
    protected function registerDirectives(array $customDirectives)
238
    {
239
        $directivesMap = keyMap(
240
            \array_merge($customDirectives, specifiedDirectives()),
241
            function (Directive $directive) {
242
                return $directive->getName();
243
            }
244
        );
245
246
        foreach ($directivesMap as $directiveName => $directive) {
247
            $this->setDirectiveInCache($directiveName, $directive);
248
        }
249
    }
250
251
    /**
252
     * @param array $nodes
253
     * @return array
254
     * @throws CoercingException
255
     */
256
    protected function buildArguments(array $nodes): array
257
    {
258
        return keyValueMap(
259
            $nodes,
260
            function (InputValueDefinitionNode $value) {
261
                return $value->getNameValue();
262
            },
263
            function (InputValueDefinitionNode $value): array {
264
                $type         = $this->buildWrappedType($value->getType());
265
                $defaultValue = $value->getDefaultValue();
266
                return [
267
                    'type'         => $type,
268
                    'description'  => $value->getDescriptionValue(),
269
                    'defaultValue' => null !== $defaultValue
270
                        ? valueFromAST($defaultValue, $type)
271
                        : null,
272
                    'astNode'      => $value,
273
                ];
274
            });
275
    }
276
277
    /**
278
     * @param TypeDefinitionNodeInterface $node
279
     * @return NamedTypeInterface
280
     * @throws LanguageException
281
     */
282
    protected function buildNamedType(TypeDefinitionNodeInterface $node): NamedTypeInterface
283
    {
284
        if ($node instanceof ObjectTypeDefinitionNode) {
285
            return $this->buildObjectType($node);
286
        }
287
        if ($node instanceof InterfaceTypeDefinitionNode) {
288
            return $this->buildInterfaceType($node);
289
        }
290
        if ($node instanceof EnumTypeDefinitionNode) {
291
            return $this->buildEnumType($node);
292
        }
293
        if ($node instanceof UnionTypeDefinitionNode) {
294
            return $this->buildUnionType($node);
295
        }
296
        if ($node instanceof ScalarTypeDefinitionNode) {
297
            return $this->buildScalarType($node);
298
        }
299
        if ($node instanceof InputObjectTypeDefinitionNode) {
300
            return $this->buildInputObjectType($node);
301
        }
302
303
        throw new LanguageException(\sprintf('Type kind "%s" not supported.', $node->getKind()));
304
    }
305
306
    /**
307
     * @param ObjectTypeDefinitionNode $node
308
     * @return ObjectType
309
     */
310
    protected function buildObjectType(ObjectTypeDefinitionNode $node): ObjectType
311
    {
312
        return newObjectType([
313
            'name'        => $node->getNameValue(),
314
            'description' => $node->getDescriptionValue(),
315
            'fields'      => $node->hasFields() ? function () use ($node) {
316
                return $this->buildFields($node);
317
            } : [],
318
            // Note: While this could make early assertions to get the correctly
319
            // typed values, that would throw immediately while type system
320
            // validation with validateSchema() will produce more actionable results.
321
            'interfaces'  => function () use ($node) {
322
                return $node->hasInterfaces() ? \array_map(function (NodeInterface $interface) {
323
                    return $this->buildType($interface);
324
                }, $node->getInterfaces()) : [];
325
            },
326
            'astNode'     => $node,
327
        ]);
328
    }
329
330
    /**
331
     * @param ObjectTypeDefinitionNode|InterfaceTypeDefinitionNode|InputObjectTypeDefinitionNode $node
332
     * @return array
333
     */
334
    protected function buildFields($node): array
335
    {
336
        return keyValueMap(
337
            $node->getFields(),
338
            function ($value) {
339
                /** @var FieldDefinitionNode|InputValueDefinitionNode $value */
340
                return $value->getNameValue();
341
            },
342
            function ($value) use ($node) {
343
                /** @var FieldDefinitionNode|InputValueDefinitionNode $value */
344
                return $this->buildField($value,
345
                    $this->getFieldResolver($node->getNameValue(), $value->getNameValue()));
346
            }
347
        );
348
    }
349
350
    /**
351
     * @param string $typeName
352
     * @param string $fieldName
353
     * @return callable|null
354
     */
355
    protected function getFieldResolver(string $typeName, string $fieldName): ?callable
356
    {
357
        return null !== $this->resolverRegistry
358
            ? $this->resolverRegistry->getFieldResolver($typeName, $fieldName)
359
            : null;
360
    }
361
362
    /**
363
     * @param InterfaceTypeDefinitionNode $node
364
     * @return InterfaceType
365
     */
366
    protected function buildInterfaceType(InterfaceTypeDefinitionNode $node): InterfaceType
367
    {
368
        return newInterfaceType([
369
            'name'        => $node->getNameValue(),
370
            'description' => $node->getDescriptionValue(),
371
            'fields'      => $node->hasFields() ? function () use ($node): array {
372
                return $this->buildFields($node);
373
            } : [],
374
            'resolveType' => $this->getTypeResolver($node->getNameValue()),
375
            'astNode'     => $node,
376
        ]);
377
    }
378
379
    /**
380
     * @param EnumTypeDefinitionNode $node
381
     * @return EnumType
382
     */
383
    protected function buildEnumType(EnumTypeDefinitionNode $node): EnumType
384
    {
385
        return newEnumType([
386
            'name'        => $node->getNameValue(),
387
            'description' => $node->getDescriptionValue(),
388
            'values'      => $node->hasValues() ? keyValueMap(
389
                $node->getValues(),
390
                function (EnumValueDefinitionNode $value): string {
391
                    return $value->getNameValue();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $value->getNameValue() could return the type null which is incompatible with the type-hinted return string. Consider adding an additional type-check to rule them out.
Loading history...
392
                },
393
                function (EnumValueDefinitionNode $value): array {
394
                    return [
395
                        'description'       => $value->getDescriptionValue(),
396
                        'deprecationReason' => $this->getDeprecationReason($value),
397
                        'astNode'           => $value,
398
                    ];
399
                }
400
            ) : [],
401
            'astNode'     => $node,
402
        ]);
403
    }
404
405
    /**
406
     * @param UnionTypeDefinitionNode $node
407
     * @return UnionType
408
     */
409
    protected function buildUnionType(UnionTypeDefinitionNode $node): UnionType
410
    {
411
        return newUnionType([
412
            'name'        => $node->getNameValue(),
413
            'description' => $node->getDescriptionValue(),
414
            'types'       => $node->hasTypes() ? \array_map(function (TypeNodeInterface $type) {
415
                return $this->buildType($type);
416
            }, $node->getTypes()) : [],
417
            'resolveType' => $this->getTypeResolver($node->getNameValue()),
418
            'astNode'     => $node,
419
        ]);
420
    }
421
422
    /**
423
     * @param string $typeName
424
     * @return callable|null
425
     */
426
    protected function getTypeResolver(string $typeName): ?callable
427
    {
428
        return null !== $this->resolverRegistry
429
            ? $this->resolverRegistry->getTypeResolver($typeName)
430
            : null;
431
    }
432
433
    /**
434
     * @param ScalarTypeDefinitionNode $node
435
     * @return ScalarType
436
     */
437
    protected function buildScalarType(ScalarTypeDefinitionNode $node): ScalarType
438
    {
439
        return newScalarType([
440
            'name'        => $node->getNameValue(),
441
            'description' => $node->getDescriptionValue(),
442
            'serialize'   => function ($value) {
443
                return $value;
444
            },
445
            'astNode'     => $node,
446
        ]);
447
    }
448
449
    /**
450
     * @param InputObjectTypeDefinitionNode $node
451
     * @return InputObjectType
452
     */
453
    protected function buildInputObjectType(InputObjectTypeDefinitionNode $node): InputObjectType
454
    {
455
        return newInputObjectType([
456
            'name'        => $node->getNameValue(),
457
            'description' => $node->getDescriptionValue(),
458
            'fields'      => $node->hasFields() ? function () use ($node) {
459
                return keyValueMap(
460
                    $node->getFields(),
461
                    function (InputValueDefinitionNode $value): string {
462
                        return $value->getNameValue();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $value->getNameValue() could return the type null which is incompatible with the type-hinted return string. Consider adding an additional type-check to rule them out.
Loading history...
463
                    },
464
                    function (InputValueDefinitionNode $value): array {
465
                        $type         = $this->buildWrappedType($value->getType());
466
                        $defaultValue = $value->getDefaultValue();
467
                        return [
468
                            'type'         => $type,
469
                            'description'  => $value->getDescriptionValue(),
470
                            'defaultValue' => null !== $defaultValue
471
                                ? valueFromAST($defaultValue, $type)
472
                                : null,
473
                            'astNode'      => $value,
474
                        ];
475
                    }
476
                );
477
            } : [],
478
            'astNode'     => $node,
479
        ]);
480
    }
481
482
    /**
483
     * @param NamedTypeNode $node
484
     * @return NamedTypeInterface
485
     */
486
    protected function resolveType(NamedTypeNode $node): NamedTypeInterface
487
    {
488
        return \call_user_func($this->resolveTypeFunction, $node);
489
    }
490
491
    /**
492
     * @param NamedTypeNode $node
493
     * @return NamedTypeInterface|null
494
     * @throws InvalidArgumentException
495
     */
496
    public function defaultTypeResolver(NamedTypeNode $node): ?NamedTypeInterface
497
    {
498
        return $this->getFromCache($node->getNameValue()) ?? null;
499
    }
500
501
    /**
502
     * @param string $typeName
503
     * @return TypeDefinitionNodeInterface|null
504
     */
505
    protected function getTypeDefinition(string $typeName): ?TypeDefinitionNodeInterface
506
    {
507
        return $this->typeDefinitionsMap[$typeName] ?? null;
508
    }
509
510
    /**
511
     * @param TypeNodeInterface $typeNode
512
     * @return NamedTypeNode
513
     */
514
    protected function getNamedTypeNode(TypeNodeInterface $typeNode): NamedTypeNode
515
    {
516
        $namedType = $typeNode;
517
518
        while ($namedType instanceof ListTypeNode || $namedType instanceof NonNullTypeNode) {
519
            $namedType = $namedType->getType();
520
        }
521
522
        return $namedType;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $namedType returns the type Digia\GraphQL\Language\Node\TypeNodeInterface which includes types incompatible with the type-hinted return Digia\GraphQL\Language\Node\NamedTypeNode.
Loading history...
523
    }
524
525
    /**
526
     * @param NodeInterface|EnumValueDefinitionNode|FieldDefinitionNode $node
527
     * @return null|string
528
     * @throws InvariantException
529
     * @throws ExecutionException
530
     * @throws InvalidTypeException
531
     */
532
    protected function getDeprecationReason(NodeInterface $node): ?string
533
    {
534
        $deprecated = coerceDirectiveValues(DeprecatedDirective(), $node);
535
        return $deprecated['reason'] ?? null;
536
    }
537
538
    /**
539
     * @param string $typeName
540
     * @return bool
541
     * @throws InvalidArgumentException
542
     */
543
    protected function isTypeInCache(string $typeName): bool
544
    {
545
        return $this->isInCache($this->createTypeCacheKey($typeName));
546
    }
547
548
    /**
549
     * @param string $typeName
550
     * @return NamedTypeInterface
551
     * @throws InvalidArgumentException
552
     */
553
    protected function getTypeFromCache(string $typeName): NamedTypeInterface
554
    {
555
        return $this->getFromCache($this->createTypeCacheKey($typeName));
556
    }
557
558
    /**
559
     * @param string             $typeName
560
     * @param NamedTypeInterface $type
561
     * @return bool
562
     * @throws InvalidArgumentException
563
     */
564
    protected function setTypeInCache(string $typeName, NamedTypeInterface $type): bool
565
    {
566
        return $this->setInCache($this->createTypeCacheKey($typeName), $type);
567
    }
568
569
    /**
570
     * @param string $typeName
571
     * @return string
572
     */
573
    protected function createTypeCacheKey(string $typeName): string
574
    {
575
        return $typeName . self::TYPE_CACHE_SUFFIX;
576
    }
577
578
    /**
579
     * @param string $directiveName
580
     * @return bool
581
     * @throws InvalidArgumentException
582
     */
583
    protected function isDirectiveInCache(string $directiveName): bool
584
    {
585
        return $this->isInCache($this->createDirectiveCacheKey($directiveName));
586
    }
587
588
    /**
589
     * @param string $directiveName
590
     * @return Directive
591
     * @throws InvalidArgumentException
592
     */
593
    protected function getDirectiveFromCache(string $directiveName): Directive
594
    {
595
        return $this->getFromCache($this->createDirectiveCacheKey($directiveName));
596
    }
597
598
    /**
599
     * @param string    $directiveName
600
     * @param Directive $directive
601
     * @return bool
602
     * @throws InvalidArgumentException
603
     */
604
    protected function setDirectiveInCache(string $directiveName, Directive $directive): bool
605
    {
606
        return $this->setInCache($this->createDirectiveCacheKey($directiveName), $directive);
607
    }
608
609
    /**
610
     * @param string $directiveName
611
     * @return string
612
     */
613
    protected function createDirectiveCacheKey(string $directiveName): string
614
    {
615
        return $directiveName . self::DIRECTIVE_CACHE_SUFFIX;
616
    }
617
618
    /**
619
     * @return string
620
     */
621
    protected function getCachePrefix(): string
622
    {
623
        return self::CACHE_PREFIX;
624
    }
625
}
626