├── phpstan.neon
├── src
├── Request
│ ├── RequestFactory.php
│ ├── Exception
│ │ ├── InvalidMethod.php
│ │ ├── QueryMissing.php
│ │ ├── QueryNotString.php
│ │ ├── VariablesNotObject.php
│ │ ├── InvalidMultipartRequest.php
│ │ ├── UnknownKey.php
│ │ ├── OperationNameNotString.php
│ │ └── RequestError.php
│ ├── Request.php
│ ├── PsrRequestFactory.php
│ └── JsonRequestFactory.php
├── Typesystem
│ ├── Contract
│ │ ├── ExecutableDirective.php
│ │ ├── TypeSystemDirective.php
│ │ ├── LeafType.php
│ │ ├── TypeConditionable.php
│ │ ├── Type.php
│ │ ├── Entity.php
│ │ ├── Component.php
│ │ ├── ModifierType.php
│ │ ├── AbstractTypeVisitor.php
│ │ ├── TypeVisitor.php
│ │ ├── EntityVisitor.php
│ │ ├── AbstractType.php
│ │ ├── Directive.php
│ │ ├── InterfaceImplementor.php
│ │ ├── NamedTypeVisitor.php
│ │ ├── ComponentVisitor.php
│ │ └── NamedType.php
│ ├── Location
│ │ ├── SelectionDirectiveResult.php
│ │ ├── EnumLocation.php
│ │ ├── UnionLocation.php
│ │ ├── EnumItemLocation.php
│ │ ├── ScalarLocation.php
│ │ ├── SchemaLocation.php
│ │ ├── FragmentDefinitionLocation.php
│ │ ├── FragmentSpreadLocation.php
│ │ ├── InlineFragmentLocation.php
│ │ ├── QueryLocation.php
│ │ ├── MutationLocation.php
│ │ ├── ExecutableDirectiveLocation.php
│ │ ├── SubscriptionLocation.php
│ │ ├── InputObjectLocation.php
│ │ ├── ObjectLocation.php
│ │ ├── TypeSystemDirectiveLocation.php
│ │ ├── VariableDefinitionLocation.php
│ │ ├── FieldLocation.php
│ │ ├── ArgumentDefinitionLocation.php
│ │ └── FieldDefinitionLocation.php
│ ├── Exception
│ │ ├── VarianceError.php
│ │ ├── TypeError.php
│ │ ├── TypeNamesNotUnique.php
│ │ ├── DirectiveNamesNotUnique.php
│ │ ├── DirectiveIncorrectType.php
│ │ ├── OneOfInputInvalidFields.php
│ │ ├── UnionTypeMustDefineOneOrMoreTypes.php
│ │ ├── RootOperationTypesMustBeWithinContainer.php
│ │ ├── InputTypeMustDefineOneOreMoreFields.php
│ │ ├── InterfaceDirectivesNotPreserved.php
│ │ ├── InterfaceOrTypeMustDefineOneOrMoreFields.php
│ │ ├── RootOperationTypesMustBeDifferent.php
│ │ ├── DuplicateNonRepeatableDirective.php
│ │ ├── OneOfDirectiveNotSatisfied.php
│ │ ├── EnumItemInvalid.php
│ │ ├── FieldResolverVoidReturnType.php
│ │ ├── FieldResolverNotIterable.php
│ │ ├── FieldResolverNullabilityMismatch.php
│ │ ├── InputCycleDetected.php
│ │ ├── FieldInvalidTypeUsage.php
│ │ ├── InterfaceCycleDetected.php
│ │ ├── ArgumentInvalidTypeUsage.php
│ │ ├── InterfaceContractMissingField.php
│ │ ├── InterfaceContractFieldTypeMismatch.php
│ │ ├── FieldDirectiveNotCovariant.php
│ │ ├── InterfaceContractMissingArgument.php
│ │ ├── InterfaceContractArgumentTypeMismatch.php
│ │ ├── InterfaceContractNewArgumentWithoutDefault.php
│ │ └── ArgumentDirectiveNotContravariant.php
│ ├── Field
│ │ ├── ResolvableFieldSet.php
│ │ ├── FieldSet.php
│ │ ├── ResolvableField.php
│ │ └── Field.php
│ ├── Utils
│ │ ├── THasDirectives.php
│ │ ├── THasDescription.php
│ │ ├── TOptionalDescription.php
│ │ ├── TInterfaceImplementor.php
│ │ ├── TMetaFields.php
│ │ └── TDeprecatable.php
│ ├── DirectiveUsage
│ │ ├── DirectiveUsageSet.php
│ │ └── DirectiveUsage.php
│ ├── NotNullType.php
│ ├── Attribute
│ │ └── Description.php
│ ├── ListType.php
│ ├── TypeSet.php
│ ├── InterfaceSet.php
│ ├── Spec
│ │ ├── BooleanType.php
│ │ ├── StringType.php
│ │ ├── SpecifiedByDirective.php
│ │ ├── IdType.php
│ │ ├── FloatType.php
│ │ ├── IntType.php
│ │ ├── OneOfDirective.php
│ │ ├── SkipDirective.php
│ │ ├── IncludeDirective.php
│ │ └── DeprecatedDirective.php
│ ├── Introspection
│ │ ├── TypeKind.php
│ │ ├── DirectiveLocation.php
│ │ ├── EnumValue.php
│ │ ├── Directive.php
│ │ ├── Schema.php
│ │ └── InputValue.php
│ ├── Argument
│ │ ├── ArgumentSet.php
│ │ └── Argument.php
│ ├── EnumItem
│ │ ├── EnumItemSet.php
│ │ └── EnumItem.php
│ ├── UnionType.php
│ ├── Visitor
│ │ ├── IsInputableVisitor.php
│ │ ├── IsOutputableVisitor.php
│ │ ├── TypeKindVisitor.php
│ │ ├── GetShapingTypeVisitor.php
│ │ ├── GetNamedTypeVisitor.php
│ │ ├── PrintNameVisitor.php
│ │ ├── IsImplementedByVisitor.php
│ │ └── IsInstanceOfVisitor.php
│ ├── InterfaceType.php
│ ├── ScalarType.php
│ ├── InputType.php
│ ├── Schema.php
│ └── EnumType.php
├── Normalizer
│ ├── Refiner
│ │ ├── Module
│ │ │ ├── RefinerModule.php
│ │ │ ├── EmptyFragmentModule.php
│ │ │ ├── DuplicateFragmentSpreadModule.php
│ │ │ └── DuplicateFieldModule.php
│ │ └── SelectionSetRefiner.php
│ ├── Selection
│ │ ├── Selection.php
│ │ ├── SelectionVisitor.php
│ │ ├── InlineFragment.php
│ │ ├── FragmentSpread.php
│ │ ├── SelectionSet.php
│ │ └── Field.php
│ ├── Validator
│ │ ├── Module
│ │ │ ├── ValidatorModule.php
│ │ │ └── FieldForName.php
│ │ ├── SelectionSetValidator.php
│ │ └── FragmentCycleValidator.php
│ ├── Exception
│ │ ├── FragmentCycle.php
│ │ ├── SelectionOnLeaf.php
│ │ ├── ConflictingFieldArguments.php
│ │ ├── ConflictingFieldDirectives.php
│ │ ├── SelectionOnComposite.php
│ │ ├── VariableTypeMismatch.php
│ │ ├── ConflictingFieldAlias.php
│ │ ├── ConflictingFieldType.php
│ │ ├── TypeConditionOutputable.php
│ │ ├── SelectionOnUnion.php
│ │ ├── VariableInConstContext.php
│ │ ├── NormalizerError.php
│ │ ├── UnknownType.php
│ │ ├── UnknownDirective.php
│ │ ├── UnknownVariable.php
│ │ ├── UnknownArgument.php
│ │ ├── DirectiveNotExecutable.php
│ │ ├── DuplicatedDirective.php
│ │ ├── UnknownFragment.php
│ │ ├── VariableTypeInputable.php
│ │ ├── UnknownField.php
│ │ ├── DirectiveIncorrectLocation.php
│ │ ├── DirectiveIncorrectUsage.php
│ │ ├── InvalidFragmentType.php
│ │ └── OperationNotSupported.php
│ ├── FinalizedRequest.php
│ ├── NormalizedRequest.php
│ ├── VariableValueSet.php
│ ├── Variable
│ │ ├── VariableSet.php
│ │ └── Variable.php
│ ├── Directive
│ │ ├── Directive.php
│ │ └── DirectiveSet.php
│ ├── Operation
│ │ ├── OperationSet.php
│ │ └── Operation.php
│ ├── Visitor
│ │ ├── ApplyVariablesVisitor.php
│ │ └── GetFieldVisitor.php
│ └── Finalizer.php
├── Resolver
│ ├── Exception
│ │ ├── ResolverError.php
│ │ └── FieldResultTypeMismatch.php
│ ├── ErrorHandlingMode.php
│ ├── Result.php
│ ├── ExceptionHandler.php
│ └── Visitor
│ │ └── ResolveVisitor.php
├── Value
│ ├── Exception
│ │ ├── ValueCannotBeNull.php
│ │ ├── ValueCannotBeOmitted.php
│ │ ├── ValueError.php
│ │ └── InvalidValue.php
│ ├── ResolvedValue.php
│ ├── OutputValue.php
│ ├── Value.php
│ ├── ListResolvedValue.php
│ ├── ListIntermediateValue.php
│ ├── TypeIntermediateValue.php
│ ├── ListValue.php
│ ├── InputedValue.php
│ ├── FieldValue.php
│ ├── NullValue.php
│ ├── TypeValue.php
│ ├── ListInputedValue.php
│ ├── ArgumentValue.php
│ ├── ArgumentValueSet.php
│ ├── VariableValue.php
│ ├── ScalarValue.php
│ ├── EnumValue.php
│ └── Visitor
│ │ └── CreateResolvedValueVisitor.php
└── Module
│ ├── ModuleSet.php
│ └── Module.php
├── phpcs.xml
├── SECURITY.md
├── LICENSE
└── composer.json
/phpstan.neon:
--------------------------------------------------------------------------------
1 | parameters:
2 | rememberPossiblyImpureFunctionValues: false
3 | checkMissingOverrideMethodAttribute: true
4 |
--------------------------------------------------------------------------------
/src/Request/RequestFactory.php:
--------------------------------------------------------------------------------
1 |
2 |
3 | src
4 | tests
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/Normalizer/Exception/SelectionOnUnion.php:
--------------------------------------------------------------------------------
1 | value);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/Normalizer/FinalizedRequest.php:
--------------------------------------------------------------------------------
1 | $visitor
13 | * @return T
14 | */
15 | public function accept(TypeVisitor $visitor) : mixed;
16 | }
17 |
--------------------------------------------------------------------------------
/src/Value/Exception/ValueCannotBeOmitted.php:
--------------------------------------------------------------------------------
1 | directiveUsages;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/Typesystem/Exception/OneOfDirectiveNotSatisfied.php:
--------------------------------------------------------------------------------
1 | visitNotNull($this);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/Normalizer/Exception/UnknownField.php:
--------------------------------------------------------------------------------
1 | variables[$offset];
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/Typesystem/Exception/EnumItemInvalid.php:
--------------------------------------------------------------------------------
1 | value;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/Typesystem/Exception/FieldResolverVoidReturnType.php:
--------------------------------------------------------------------------------
1 | $inputCycle
13 | */
14 | public function __construct(
15 | array $inputCycle,
16 | )
17 | {
18 | parent::__construct([\implode(' -> ', $inputCycle)]);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/Normalizer/Exception/OperationNotSupported.php:
--------------------------------------------------------------------------------
1 | value]);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/Typesystem/Location/FragmentSpreadLocation.php:
--------------------------------------------------------------------------------
1 | $interfaceCycle
13 | */
14 | public function __construct(
15 | array $interfaceCycle,
16 | )
17 | {
18 | parent::__construct([\implode(' -> ', $interfaceCycle)]);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/Typesystem/Exception/ArgumentInvalidTypeUsage.php:
--------------------------------------------------------------------------------
1 | visitList($this);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/Typesystem/Location/ExecutableDirectiveLocation.php:
--------------------------------------------------------------------------------
1 | innerType;
20 | }
21 |
22 | public function list() : ListType
23 | {
24 | return new ListType($this);
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/Typesystem/Exception/InterfaceContractMissingField.php:
--------------------------------------------------------------------------------
1 | getAttributes(Description::class);
15 |
16 | if (\count($attrs) === 1) {
17 | return $attrs[0]->newInstance()->getValue();
18 | }
19 |
20 | return null;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/Value/Exception/ValueError.php:
--------------------------------------------------------------------------------
1 | outputable;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/Typesystem/Contract/AbstractTypeVisitor.php:
--------------------------------------------------------------------------------
1 | getName();
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/Typesystem/Contract/TypeVisitor.php:
--------------------------------------------------------------------------------
1 |
13 | */
14 | interface TypeVisitor extends NamedTypeVisitor
15 | {
16 | /**
17 | * @return T
18 | */
19 | public function visitNotNull(NotNullType $notNull) : mixed;
20 |
21 | /**
22 | * @return T
23 | */
24 | public function visitList(ListType $list) : mixed;
25 | }
26 |
--------------------------------------------------------------------------------
/src/Typesystem/Contract/EntityVisitor.php:
--------------------------------------------------------------------------------
1 |
13 | */
14 | interface EntityVisitor extends NamedTypeVisitor
15 | {
16 | /**
17 | * @return T
18 | */
19 | public function visitSchema(Schema $schema) : mixed;
20 |
21 | /**
22 | * @return T
23 | */
24 | public function visitDirective(Directive $directive) : mixed;
25 | }
26 |
--------------------------------------------------------------------------------
/src/Typesystem/Exception/InterfaceContractFieldTypeMismatch.php:
--------------------------------------------------------------------------------
1 | name;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/Typesystem/Location/InputObjectLocation.php:
--------------------------------------------------------------------------------
1 | description;
17 | }
18 |
19 | public function setDescription(string $description) : self
20 | {
21 | $this->description = $description;
22 |
23 | return $this;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/Typesystem/Exception/FieldDirectiveNotCovariant.php:
--------------------------------------------------------------------------------
1 | getName();
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/Typesystem/Location/TypeSystemDirectiveLocation.php:
--------------------------------------------------------------------------------
1 | arguments->applyVariables($variables);
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/Value/ListIntermediateValue.php:
--------------------------------------------------------------------------------
1 | rawValue;
22 | }
23 |
24 | #[\Override]
25 | public function getType() : ListType
26 | {
27 | return $this->type;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/Normalizer/Directive/DirectiveSet.php:
--------------------------------------------------------------------------------
1 | applyVariables($variables);
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/Typesystem/Exception/InterfaceContractArgumentTypeMismatch.php:
--------------------------------------------------------------------------------
1 | implements;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/Normalizer/Operation/OperationSet.php:
--------------------------------------------------------------------------------
1 | name
24 | ?? '';
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/Normalizer/Selection/InlineFragment.php:
--------------------------------------------------------------------------------
1 | visitInlineFragment($this);
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/Typesystem/Contract/AbstractType.php:
--------------------------------------------------------------------------------
1 | $visitor
15 | * @return T
16 | */
17 | #[\Override]
18 | abstract public function accept(AbstractTypeVisitor $visitor) : mixed;
19 |
20 | abstract public function createResolvedValue(mixed $rawValue) : TypeIntermediateValue;
21 | }
22 |
--------------------------------------------------------------------------------
/src/Typesystem/InterfaceSet.php:
--------------------------------------------------------------------------------
1 | getName();
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/Typesystem/Exception/ArgumentDirectiveNotContravariant.php:
--------------------------------------------------------------------------------
1 | visitFragmentSpread($this);
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/Normalizer/Validator/SelectionSetValidator.php:
--------------------------------------------------------------------------------
1 | selections),
22 | ];
23 |
24 | foreach ($modules as $module) {
25 | $module->validate();
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/Normalizer/Operation/Operation.php:
--------------------------------------------------------------------------------
1 |
21 | */
22 | public function getLocations() : array;
23 |
24 | public function getArguments() : ArgumentSet;
25 | }
26 |
--------------------------------------------------------------------------------
/src/Typesystem/Spec/BooleanType.php:
--------------------------------------------------------------------------------
1 |
12 | */
13 | #[Description('Boolean built-in type')]
14 | final class BooleanType extends ScalarType
15 | {
16 | protected const NAME = 'Boolean';
17 |
18 | #[\Override]
19 | public function validateAndCoerceInput(mixed $rawValue) : ?bool
20 | {
21 | return \is_bool($rawValue)
22 | ? $rawValue
23 | : null;
24 | }
25 |
26 | #[\Override]
27 | public function coerceOutput(mixed $rawValue) : bool
28 | {
29 | return $rawValue;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/Typesystem/Spec/StringType.php:
--------------------------------------------------------------------------------
1 |
12 | */
13 | #[Description('String built-in type')]
14 | final class StringType extends ScalarType
15 | {
16 | protected const NAME = 'String';
17 |
18 | #[\Override]
19 | public function validateAndCoerceInput(mixed $rawValue) : ?string
20 | {
21 | return \is_string($rawValue)
22 | ? $rawValue
23 | : null;
24 | }
25 |
26 | #[\Override]
27 | public function coerceOutput(mixed $rawValue) : string
28 | {
29 | return $rawValue;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/Normalizer/Selection/SelectionSet.php:
--------------------------------------------------------------------------------
1 | accept($visitor);
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/Typesystem/Contract/InterfaceImplementor.php:
--------------------------------------------------------------------------------
1 | accept(new IsInputableVisitor())) {
23 | throw new VariableTypeInputable($this->name);
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/Value/TypeIntermediateValue.php:
--------------------------------------------------------------------------------
1 | validateNonNullValue($rawValue)) {
18 | throw new InvalidValue($type, $rawValue, false);
19 | }
20 | }
21 |
22 | #[\Override]
23 | public function getRawValue(bool $forResolvers = false) : mixed
24 | {
25 | return $this->rawValue;
26 | }
27 |
28 | #[\Override]
29 | public function getType() : Type
30 | {
31 | return $this->type;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/Typesystem/Field/ResolvableField.php:
--------------------------------------------------------------------------------
1 | resolveFn = $resolveFn;
21 | }
22 |
23 | #[\Override]
24 | public static function create(string $name, Type $type, ?callable $resolveFn = null) : self
25 | {
26 | return new self($name, $type, $resolveFn);
27 | }
28 |
29 | public function getResolveFunction() : \Closure
30 | {
31 | return $this->resolveFn;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/Typesystem/Introspection/TypeKind.php:
--------------------------------------------------------------------------------
1 |
15 | */
16 | interface NamedTypeVisitor extends AbstractTypeVisitor
17 | {
18 | /**
19 | * @return T
20 | */
21 | public function visitType(Type $type) : mixed;
22 |
23 | /**
24 | * @return T
25 | */
26 | public function visitInput(InputType $input) : mixed;
27 |
28 | /**
29 | * @return T
30 | */
31 | public function visitScalar(ScalarType $scalar) : mixed;
32 |
33 | /**
34 | * @return T
35 | */
36 | public function visitEnum(EnumType $enum) : mixed;
37 | }
38 |
--------------------------------------------------------------------------------
/src/Typesystem/Spec/IdType.php:
--------------------------------------------------------------------------------
1 |
12 | */
13 | #[Description('ID built-in type')]
14 | final class IdType extends ScalarType
15 | {
16 | protected const NAME = 'ID';
17 |
18 | #[\Override]
19 | public function validateAndCoerceInput(mixed $rawValue) : ?string
20 | {
21 | // coerce int to string
22 | $rawValue = \is_int($rawValue)
23 | ? (string) $rawValue
24 | : $rawValue;
25 |
26 | return \is_string($rawValue)
27 | ? $rawValue
28 | : null;
29 | }
30 |
31 | #[\Override]
32 | public function coerceOutput(mixed $rawValue) : string
33 | {
34 | return $rawValue;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/Normalizer/Selection/Field.php:
--------------------------------------------------------------------------------
1 | field->getName();
26 | }
27 |
28 | #[\Override]
29 | public function accept(SelectionVisitor $visitor) : mixed
30 | {
31 | return $visitor->visitField($this);
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/Typesystem/Spec/FloatType.php:
--------------------------------------------------------------------------------
1 |
12 | */
13 | #[Description('Float built-in type')]
14 | final class FloatType extends ScalarType
15 | {
16 | protected const NAME = 'Float';
17 |
18 | #[\Override]
19 | public function validateAndCoerceInput(mixed $rawValue) : ?float
20 | {
21 | // coerce int to float
22 | $rawValue = \is_int($rawValue)
23 | ? (float) $rawValue
24 | : $rawValue;
25 |
26 | return \is_float($rawValue) && \is_finite($rawValue)
27 | ? $rawValue
28 | : null;
29 | }
30 |
31 | #[\Override]
32 | public function coerceOutput(mixed $rawValue) : float
33 | {
34 | return $rawValue;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/Resolver/Result.php:
--------------------------------------------------------------------------------
1 | data instanceof TypeValue) {
25 | $return->data = $this->data;
26 | }
27 |
28 | if (\is_array($this->errors)) {
29 | $return->errors = $this->errors;
30 | }
31 |
32 | return $return;
33 | }
34 |
35 | public function toString() : string
36 | {
37 | return Json::fromNative($this->jsonSerialize())->toString();
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/Normalizer/Refiner/SelectionSetRefiner.php:
--------------------------------------------------------------------------------
1 | selections),
24 | new DuplicateFieldModule($this->selections),
25 | new EmptyFragmentModule($this->selections),
26 | ];
27 |
28 | foreach ($modules as $module) {
29 | $module->refine();
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/Value/ListValue.php:
--------------------------------------------------------------------------------
1 | type;
22 | }
23 |
24 | #[\Override]
25 | public function getRawValue(bool $forResolvers = false) : array
26 | {
27 | $return = [];
28 |
29 | foreach ($this->value as $listItem) {
30 | $return[] = $listItem->getRawValue($forResolvers);
31 | }
32 |
33 | return $return;
34 | }
35 |
36 | #[\Override]
37 | public function getIterator() : \ArrayIterator
38 | {
39 | return new \ArrayIterator($this->value);
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/Typesystem/Contract/ComponentVisitor.php:
--------------------------------------------------------------------------------
1 |
15 | */
16 | interface ComponentVisitor extends EntityVisitor
17 | {
18 | /**
19 | * @return T
20 | */
21 | public function visitField(Field $field) : mixed;
22 |
23 | /**
24 | * @return T
25 | */
26 | public function visitArgument(Argument $argument) : mixed;
27 |
28 | /**
29 | * @return T
30 | */
31 | public function visitDirectiveUsage(DirectiveUsage $directiveUsage) : mixed;
32 |
33 | /**
34 | * @return T
35 | */
36 | public function visitEnumItem(EnumItem $enumItem) : mixed;
37 | }
38 |
--------------------------------------------------------------------------------
/src/Typesystem/Spec/IntType.php:
--------------------------------------------------------------------------------
1 |
12 | */
13 | #[Description('Int built-in type (32 bit)')]
14 | final class IntType extends ScalarType
15 | {
16 | protected const NAME = 'Int';
17 | // by specification there is a limit to 32 bits. ExtraTypes package contains a custom BigInt type.
18 | private const INT_LIMIT = 2 ** 31;
19 |
20 | #[\Override]
21 | public function validateAndCoerceInput(mixed $rawValue) : ?int
22 | {
23 | return \is_int($rawValue) && $rawValue >= (- self::INT_LIMIT) && $rawValue <= self::INT_LIMIT
24 | ? $rawValue
25 | : null;
26 | }
27 |
28 | #[\Override]
29 | public function coerceOutput(mixed $rawValue) : int
30 | {
31 | return $rawValue;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Security Policy
2 |
3 | ## Supported Versions
4 |
5 | | Version | Supported | PHP version | Status |
6 | | ------- | ------------------ |-------------|--------|
7 | | > 1.3 | :heavy_check_mark: | 8.1 | Maintained and actively improved with new features.
8 | | 1.3.x | :white_check_mark: | 8.0 | Maintained in bugfix only mode.
9 | | < 1.3 | :x: | 8.0 | Please upgrade to 1.3 or later.
10 | | < 0.25 | :x: | 7.4 | Please upgrade to 1.3 or later.
11 |
12 | ## Reporting a Vulnerability
13 |
14 | Report security bugs by emailing the maintainer at peldax@gmail.com.
15 |
16 | The maintainer will respond to your email as soon as he can, usually within 24 hours.
17 | If the vulnerability is accepted, a bugfix will be released to all actively maintained versions.
18 |
19 | Depending on the severity, GitHub security advisory or `roave/security-advisories` can be used to inform users about the important bugfix release.
20 |
--------------------------------------------------------------------------------
/src/Value/InputedValue.php:
--------------------------------------------------------------------------------
1 | metaFields instanceof ResolvableFieldSet) {
18 | $this->metaFields = $this->getMetaFieldDefinition();
19 | }
20 |
21 | return $this->metaFields;
22 | }
23 |
24 | private function getMetaFieldDefinition() : ResolvableFieldSet
25 | {
26 | return new ResolvableFieldSet([
27 | new ResolvableField(
28 | '__typename',
29 | Container::String()->notNull(),
30 | function() : string {
31 | return $this->getName();
32 | },
33 | ),
34 | ]);
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/Value/FieldValue.php:
--------------------------------------------------------------------------------
1 | getDirectiveUsages() as $directiveUsage) {
18 | $directive = $directiveUsage->getDirective();
19 | \assert($directive instanceof FieldDefinitionLocation);
20 | $directive->resolveFieldDefinitionValue($directiveUsage->getArgumentValues(), $this);
21 | }
22 | }
23 |
24 | #[\Override]
25 | public function jsonSerialize() : ResolvedValue
26 | {
27 | return $this->value;
28 | }
29 |
30 | public function getField() : Field
31 | {
32 | return $this->field;
33 | }
34 |
35 | public function getValue() : ResolvedValue
36 | {
37 | return $this->value;
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/Value/Exception/InvalidValue.php:
--------------------------------------------------------------------------------
1 | accept(new PrintNameVisitor()), $this->printValue($rawValue)]);
21 | }
22 |
23 | private function printValue(mixed $rawValue) : string
24 | {
25 | if ($rawValue === null || \is_scalar($rawValue)) {
26 | return \json_encode($rawValue, \JSON_THROW_ON_ERROR);
27 | }
28 |
29 | if (\is_array($rawValue)) {
30 | return 'list';
31 | }
32 |
33 | if ($rawValue instanceof \stdClass) {
34 | return 'object';
35 | }
36 |
37 | return $rawValue::class;
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Infinityloop.dev
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/Typesystem/Contract/NamedType.php:
--------------------------------------------------------------------------------
1 | $visitor
20 | * @return T
21 | */
22 | #[\Override]
23 | abstract public function accept(NamedTypeVisitor $visitor) : mixed;
24 |
25 | final public function getName() : string
26 | {
27 | return static::NAME;
28 | }
29 |
30 | final public function notNull() : NotNullType
31 | {
32 | return new NotNullType($this);
33 | }
34 |
35 | final public function notNullList() : NotNullType
36 | {
37 | return new NotNullType(new ListType(new NotNullType($this)));
38 | }
39 |
40 | final public function list() : ListType
41 | {
42 | return new ListType($this);
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/Typesystem/Location/FieldDefinitionLocation.php:
--------------------------------------------------------------------------------
1 | getCases() as $case) {
25 | $values[] = new EnumItem($case->getBackingValue());
26 | }
27 |
28 | $ref = new \ReflectionEnum(TypeSystemDirectiveLocation::class);
29 |
30 | foreach ($ref->getCases() as $case) {
31 | $values[] = new EnumItem($case->getBackingValue());
32 | }
33 |
34 | parent::__construct(new EnumItemSet($values));
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/Typesystem/Argument/ArgumentSet.php:
--------------------------------------------------------------------------------
1 | defaults;
23 | }
24 |
25 | #[\Override]
26 | public function offsetSet($offset, $value) : void
27 | {
28 | \assert($value instanceof Argument);
29 |
30 | parent::offsetSet($offset, $value);
31 |
32 | $defaultValue = $value->getDefaultValue();
33 |
34 | if ($defaultValue instanceof ArgumentValue) {
35 | $this->defaults[$value->getName()] = $defaultValue->getValue()->getRawValue();
36 | }
37 | }
38 |
39 | #[\Override]
40 | protected function getKey(object $object) : string
41 | {
42 | \assert($object instanceof Argument);
43 |
44 | return $object->getName();
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/Typesystem/EnumItem/EnumItemSet.php:
--------------------------------------------------------------------------------
1 | $data
19 | * @param ?string $enumClass
20 | */
21 | public function __construct(
22 | array $data = [],
23 | private ?string $enumClass = null,
24 | )
25 | {
26 | parent::__construct($data);
27 | }
28 |
29 | public function getEnumClass() : ?string
30 | {
31 | return $this->enumClass;
32 | }
33 |
34 | /**
35 | * @return list
36 | */
37 | public function getArray() : array
38 | {
39 | $return = [];
40 |
41 | foreach ($this as $enumItem) {
42 | $return[] = $enumItem->getName();
43 | }
44 |
45 | return $return;
46 | }
47 |
48 | #[\Override]
49 | protected function getKey(object $object) : string
50 | {
51 | \assert($object instanceof EnumItem);
52 |
53 | return $object->getName();
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/Value/NullValue.php:
--------------------------------------------------------------------------------
1 | type;
28 | }
29 |
30 | #[\Override]
31 | public function jsonSerialize() : null
32 | {
33 | return null;
34 | }
35 |
36 | #[\Override]
37 | public function printValue() : string
38 | {
39 | return 'null';
40 | }
41 |
42 | #[\Override]
43 | public function applyVariables(VariableValueSet $variables) : void
44 | {
45 | // nothing here
46 | }
47 |
48 | #[\Override]
49 | public function resolveRemainingDirectives() : void
50 | {
51 | // nothing here
52 | }
53 |
54 | #[\Override]
55 | public function isSame(Value $compare) : bool
56 | {
57 | return $compare instanceof self;
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/Normalizer/Visitor/ApplyVariablesVisitor.php:
--------------------------------------------------------------------------------
1 | arguments->applyVariables($this->variables);
25 | $field->directives->applyVariables($this->variables);
26 | $field->children?->applyVariables($this->variables);
27 |
28 | return null;
29 | }
30 |
31 | #[\Override]
32 | public function visitFragmentSpread(FragmentSpread $fragmentSpread) : null
33 | {
34 | $fragmentSpread->children->applyVariables($this->variables);
35 |
36 | return null;
37 | }
38 |
39 | #[\Override]
40 | public function visitInlineFragment(InlineFragment $inlineFragment) : null
41 | {
42 | $inlineFragment->children->applyVariables($this->variables);
43 |
44 | return null;
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/Typesystem/Utils/TDeprecatable.php:
--------------------------------------------------------------------------------
1 | addDirective(
18 | Container::directiveDeprecated(),
19 | ['reason' => $reason],
20 | );
21 |
22 | return $this;
23 | }
24 |
25 | public function isDeprecated() : bool
26 | {
27 | foreach ($this->directiveUsages as $directive) {
28 | if ($directive->getDirective() instanceof DeprecatedDirective) {
29 | return true;
30 | }
31 | }
32 |
33 | return false;
34 | }
35 |
36 | public function getDeprecationReason() : ?string
37 | {
38 | foreach ($this->directiveUsages as $directive) {
39 | if ($directive->getDirective() instanceof DeprecatedDirective) {
40 | $value = $directive->getArgumentValues()->offsetGet('reason')->getValue()->getRawValue();
41 |
42 | return \is_string($value)
43 | ? $value
44 | : null;
45 | }
46 | }
47 |
48 | return null;
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/Typesystem/UnionType.php:
--------------------------------------------------------------------------------
1 | directiveUsages = new DirectiveUsageSet();
25 | }
26 |
27 | final public function getTypes() : TypeSet
28 | {
29 | return $this->types;
30 | }
31 |
32 | #[\Override]
33 | final public function accept(AbstractTypeVisitor $visitor) : mixed
34 | {
35 | return $visitor->visitUnion($this);
36 | }
37 |
38 | /**
39 | * @param UnionLocation $directive
40 | * @phpcs:ignore
41 | * @param array $arguments
42 | */
43 | final public function addDirective(UnionLocation $directive, array $arguments = []) : static
44 | {
45 | $this->directiveUsages[] = new DirectiveUsage($directive, $arguments);
46 |
47 | return $this;
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/Value/TypeValue.php:
--------------------------------------------------------------------------------
1 | getDirectiveUsages() as $directiveUsage) {
19 | $directive = $directiveUsage->getDirective();
20 | \assert($directive instanceof ObjectLocation);
21 | $directive->resolveObject($directiveUsage->getArgumentValues(), $this);
22 | }
23 | }
24 |
25 | #[\Override]
26 | public function getRawValue() : \stdClass
27 | {
28 | return $this->value;
29 | }
30 |
31 | #[\Override]
32 | public function getType() : Type
33 | {
34 | return $this->type;
35 | }
36 |
37 | public function getIntermediateValue() : TypeIntermediateValue
38 | {
39 | return $this->intermediateValue;
40 | }
41 |
42 | #[\Override]
43 | public function jsonSerialize() : \stdClass
44 | {
45 | return $this->value;
46 | }
47 |
48 | public function __get(string $name) : FieldValue
49 | {
50 | return $this->value->{$name};
51 | }
52 |
53 | public function __isset(string $name) : bool
54 | {
55 | return \property_exists($this->value, $name);
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/Typesystem/DirectiveUsage/DirectiveUsage.php:
--------------------------------------------------------------------------------
1 | $arguments
22 | */
23 | public function __construct(
24 | private TypeSystemDirective $directive,
25 | array $arguments,
26 | )
27 | {
28 | $this->argumentValues = new ArgumentValueSet(
29 | (array) ConvertRawValueVisitor::convertArgumentSet(
30 | $directive->getArguments(),
31 | (object) $arguments,
32 | new Path(),
33 | ),
34 | );
35 | }
36 |
37 | public function getDirective() : TypeSystemDirective
38 | {
39 | return $this->directive;
40 | }
41 |
42 | public function getArgumentValues() : ArgumentValueSet
43 | {
44 | return $this->argumentValues;
45 | }
46 |
47 | #[\Override]
48 | public function accept(ComponentVisitor $visitor) : mixed
49 | {
50 | return $visitor->visitDirectiveUsage($this);
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/Typesystem/EnumItem/EnumItem.php:
--------------------------------------------------------------------------------
1 | description = $description;
28 | $this->directiveUsages = new DirectiveUsageSet();
29 | }
30 |
31 | public function getName() : string
32 | {
33 | return $this->name;
34 | }
35 |
36 | #[\Override]
37 | public function accept(ComponentVisitor $visitor) : mixed
38 | {
39 | return $visitor->visitEnumItem($this);
40 | }
41 |
42 | /**
43 | * @param EnumItemLocation $directive
44 | * @phpcs:ignore
45 | * @param array $arguments
46 | */
47 | public function addDirective(EnumItemLocation $directive, array $arguments = []) : self
48 | {
49 | $this->directiveUsages[] = new DirectiveUsage($directive, $arguments);
50 |
51 | return $this;
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/Typesystem/Visitor/IsInputableVisitor.php:
--------------------------------------------------------------------------------
1 |
19 | */
20 | final readonly class IsInputableVisitor implements TypeVisitor
21 | {
22 | #[\Override]
23 | public function visitType(Type $type) : false
24 | {
25 | return false;
26 | }
27 |
28 | #[\Override]
29 | public function visitInterface(InterfaceType $interface) : false
30 | {
31 | return false;
32 | }
33 |
34 | #[\Override]
35 | public function visitUnion(UnionType $union) : false
36 | {
37 | return false;
38 | }
39 |
40 | #[\Override]
41 | public function visitInput(InputType $input) : true
42 | {
43 | return true;
44 | }
45 |
46 | #[\Override]
47 | public function visitScalar(ScalarType $scalar) : true
48 | {
49 | return true;
50 | }
51 |
52 | #[\Override]
53 | public function visitEnum(EnumType $enum) : true
54 | {
55 | return true;
56 | }
57 |
58 | #[\Override]
59 | public function visitNotNull(NotNullType $notNull) : bool
60 | {
61 | return $notNull->getInnerType()->accept($this);
62 | }
63 |
64 | #[\Override]
65 | public function visitList(ListType $list) : bool
66 | {
67 | return $list->getInnerType()->accept($this);
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/Typesystem/Visitor/IsOutputableVisitor.php:
--------------------------------------------------------------------------------
1 |
19 | */
20 | final readonly class IsOutputableVisitor implements TypeVisitor
21 | {
22 | #[\Override]
23 | public function visitType(Type $type) : true
24 | {
25 | return true;
26 | }
27 |
28 | #[\Override]
29 | public function visitInterface(InterfaceType $interface) : true
30 | {
31 | return true;
32 | }
33 |
34 | #[\Override]
35 | public function visitUnion(UnionType $union) : true
36 | {
37 | return true;
38 | }
39 |
40 | #[\Override]
41 | public function visitInput(InputType $input) : false
42 | {
43 | return false;
44 | }
45 |
46 | #[\Override]
47 | public function visitScalar(ScalarType $scalar) : true
48 | {
49 | return true;
50 | }
51 |
52 | #[\Override]
53 | public function visitEnum(EnumType $enum) : true
54 | {
55 | return true;
56 | }
57 |
58 | #[\Override]
59 | public function visitNotNull(NotNullType $notNull) : bool
60 | {
61 | return $notNull->getInnerType()->accept($this);
62 | }
63 |
64 | #[\Override]
65 | public function visitList(ListType $list) : bool
66 | {
67 | return $list->getInnerType()->accept($this);
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/Normalizer/Refiner/Module/EmptyFragmentModule.php:
--------------------------------------------------------------------------------
1 | selections as $index => $selection) {
27 | $this->index = $index;
28 | $selection->accept($this);
29 | }
30 | }
31 |
32 | #[\Override]
33 | public function visitField(Field $field) : null
34 | {
35 | return null;
36 | }
37 |
38 | #[\Override]
39 | public function visitFragmentSpread(FragmentSpread $fragmentSpread) : null
40 | {
41 | $refiner = new self($fragmentSpread->children);
42 | $refiner->refine();
43 |
44 | if ($fragmentSpread->children->count() === 0) {
45 | $this->selections->offsetUnset($this->index);
46 | }
47 |
48 | return null;
49 | }
50 |
51 | #[\Override]
52 | public function visitInlineFragment(InlineFragment $inlineFragment) : null
53 | {
54 | $refiner = new self($inlineFragment->children);
55 | $refiner->refine();
56 |
57 | if ($inlineFragment->children->count() === 0) {
58 | $this->selections->offsetUnset($this->index);
59 | }
60 |
61 | return null;
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/Value/ListInputedValue.php:
--------------------------------------------------------------------------------
1 | value as $value) {
17 | \assert($value instanceof InputedValue);
18 |
19 | $component[] = $value->printValue();
20 | }
21 |
22 | return '[' . \implode(',', $component) . ']';
23 | }
24 |
25 | #[\Override]
26 | public function applyVariables(VariableValueSet $variables) : void
27 | {
28 | foreach ($this->value as $value) {
29 | \assert($value instanceof InputedValue);
30 |
31 | $value->applyVariables($variables);
32 | }
33 | }
34 |
35 | #[\Override]
36 | public function resolveRemainingDirectives() : void
37 | {
38 | foreach ($this->value as $value) {
39 | \assert($value instanceof InputedValue);
40 |
41 | $value->resolveRemainingDirectives();
42 | }
43 | }
44 |
45 | #[\Override]
46 | public function isSame(Value $compare) : bool
47 | {
48 | if (!$compare instanceof self) {
49 | return false;
50 | }
51 |
52 | $secondArray = $compare->value;
53 |
54 | if (\count($secondArray) !== \count($this->value)) {
55 | return false;
56 | }
57 |
58 | foreach ($this->value as $key => $value) {
59 | \assert($value instanceof InputedValue);
60 |
61 | if (!\array_key_exists($key, $secondArray) || !$value->isSame($secondArray[$key])) {
62 | return false;
63 | }
64 | }
65 |
66 | return true;
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/src/Typesystem/Visitor/TypeKindVisitor.php:
--------------------------------------------------------------------------------
1 |
20 | */
21 | final class TypeKindVisitor implements TypeVisitor
22 | {
23 | #[\Override]
24 | public function visitType(Type $type) : string
25 | {
26 | return TypeKind::OBJECT;
27 | }
28 |
29 | #[\Override]
30 | public function visitInterface(InterfaceType $interface) : string
31 | {
32 | return TypeKind::INTERFACE;
33 | }
34 |
35 | #[\Override]
36 | public function visitUnion(UnionType $union) : string
37 | {
38 | return TypeKind::UNION;
39 | }
40 |
41 | #[\Override]
42 | public function visitInput(InputType $input) : string
43 | {
44 | return TypeKind::INPUT_OBJECT;
45 | }
46 |
47 | #[\Override]
48 | public function visitScalar(ScalarType $scalar) : string
49 | {
50 | return TypeKind::SCALAR;
51 | }
52 |
53 | #[\Override]
54 | public function visitEnum(EnumType $enum) : string
55 | {
56 | return TypeKind::ENUM;
57 | }
58 |
59 | #[\Override]
60 | public function visitNotNull(NotNullType $notNull) : string
61 | {
62 | return TypeKind::NON_NULL;
63 | }
64 |
65 | #[\Override]
66 | public function visitList(ListType $list) : string
67 | {
68 | return TypeKind::LIST;
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/src/Typesystem/Visitor/GetShapingTypeVisitor.php:
--------------------------------------------------------------------------------
1 |
20 | */
21 | final readonly class GetShapingTypeVisitor implements TypeVisitor
22 | {
23 | #[\Override]
24 | public function visitType(Type $type) : Type
25 | {
26 | return $type;
27 | }
28 |
29 | #[\Override]
30 | public function visitInterface(InterfaceType $interface) : InterfaceType
31 | {
32 | return $interface;
33 | }
34 |
35 | #[\Override]
36 | public function visitUnion(UnionType $union) : UnionType
37 | {
38 | return $union;
39 | }
40 |
41 | #[\Override]
42 | public function visitInput(InputType $input) : InputType
43 | {
44 | return $input;
45 | }
46 |
47 | #[\Override]
48 | public function visitScalar(ScalarType $scalar) : ScalarType
49 | {
50 | return $scalar;
51 | }
52 |
53 | #[\Override]
54 | public function visitEnum(EnumType $enum) : EnumType
55 | {
56 | return $enum;
57 | }
58 |
59 | #[\Override]
60 | public function visitNotNull(NotNullType $notNull) : TypeContract
61 | {
62 | return $notNull->getInnerType()->accept($this);
63 | }
64 |
65 | #[\Override]
66 | public function visitList(ListType $list) : ListType
67 | {
68 | return $list;
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/src/Typesystem/Visitor/GetNamedTypeVisitor.php:
--------------------------------------------------------------------------------
1 |
20 | */
21 | final readonly class GetNamedTypeVisitor implements TypeVisitor
22 | {
23 | #[\Override]
24 | public function visitType(Type $type) : Type
25 | {
26 | return $type;
27 | }
28 |
29 | #[\Override]
30 | public function visitInterface(InterfaceType $interface) : InterfaceType
31 | {
32 | return $interface;
33 | }
34 |
35 | #[\Override]
36 | public function visitUnion(UnionType $union) : UnionType
37 | {
38 | return $union;
39 | }
40 |
41 | #[\Override]
42 | public function visitInput(InputType $input) : InputType
43 | {
44 | return $input;
45 | }
46 |
47 | #[\Override]
48 | public function visitScalar(ScalarType $scalar) : ScalarType
49 | {
50 | return $scalar;
51 | }
52 |
53 | #[\Override]
54 | public function visitEnum(EnumType $enum) : EnumType
55 | {
56 | return $enum;
57 | }
58 |
59 | #[\Override]
60 | public function visitNotNull(NotNullType $notNull) : NamedType
61 | {
62 | return $notNull->getInnerType()->accept($this);
63 | }
64 |
65 | #[\Override]
66 | public function visitList(ListType $list) : NamedType
67 | {
68 | return $list->getInnerType()->accept($this);
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/src/Typesystem/Visitor/PrintNameVisitor.php:
--------------------------------------------------------------------------------
1 |
19 | */
20 | final readonly class PrintNameVisitor implements TypeVisitor
21 | {
22 | #[\Override]
23 | public function visitType(Type $type) : string
24 | {
25 | return $type->getName();
26 | }
27 |
28 | #[\Override]
29 | public function visitInterface(InterfaceType $interface) : string
30 | {
31 | return $interface->getName();
32 | }
33 |
34 | #[\Override]
35 | public function visitUnion(UnionType $union) : string
36 | {
37 | return $union->getName();
38 | }
39 |
40 | #[\Override]
41 | public function visitInput(InputType $input) : string
42 | {
43 | return $input->getName();
44 | }
45 |
46 | #[\Override]
47 | public function visitScalar(ScalarType $scalar) : string
48 | {
49 | return $scalar->getName();
50 | }
51 |
52 | #[\Override]
53 | public function visitEnum(EnumType $enum) : string
54 | {
55 | return $enum->getName();
56 | }
57 |
58 | #[\Override]
59 | public function visitNotNull(NotNullType $notNull) : string
60 | {
61 | return $notNull->getInnerType()->accept($this) . '!';
62 | }
63 |
64 | #[\Override]
65 | public function visitList(ListType $list) : string
66 | {
67 | return '[' . $list->getInnerType()->accept($this) . ']';
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/Typesystem/Visitor/IsImplementedByVisitor.php:
--------------------------------------------------------------------------------
1 |
15 | */
16 | final readonly class IsImplementedByVisitor implements AbstractTypeVisitor
17 | {
18 | private TypeContract $typeToCompare;
19 |
20 | public function __construct(
21 | TypeContract $typeToCompare,
22 | )
23 | {
24 | $this->typeToCompare = $typeToCompare->accept(new GetShapingTypeVisitor());
25 | }
26 |
27 | #[\Override]
28 | public function visitInterface(InterfaceType $interface) : bool
29 | {
30 | return $this->typeToCompare instanceof InterfaceImplementor
31 | && self::implements($this->typeToCompare, $interface);
32 | }
33 |
34 | #[\Override]
35 | public function visitUnion(UnionType $union) : bool
36 | {
37 | foreach ($union->getTypes() as $unionItem) {
38 | if ($unionItem::class === $this->typeToCompare::class) {
39 | return true;
40 | }
41 | }
42 |
43 | return false;
44 | }
45 |
46 | /**
47 | * Checks whether given type implements given interface.
48 | * @param InterfaceImplementor $implementor
49 | * @param InterfaceType $interface
50 | */
51 | public static function implements(InterfaceImplementor $implementor, InterfaceType $interface) : bool
52 | {
53 | foreach ($implementor->getInterfaces() as $temp) {
54 | if ($temp::class === $interface::class || self::implements($temp, $interface)) {
55 | return true;
56 | }
57 | }
58 |
59 | return false;
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/Value/ArgumentValue.php:
--------------------------------------------------------------------------------
1 | hasVariables) {
20 | $this->resolvePureDirectives();
21 | }
22 | }
23 |
24 | public function getValue() : InputedValue
25 | {
26 | return $this->value;
27 | }
28 |
29 | public function getArgument() : Argument
30 | {
31 | return $this->argument;
32 | }
33 |
34 | public function applyVariables(VariableValueSet $variables) : void
35 | {
36 | if ($this->hasVariables) {
37 | $this->value->applyVariables($variables);
38 | $this->resolvePureDirectives();
39 | }
40 | }
41 |
42 | public function resolvePureDirectives() : void
43 | {
44 | foreach ($this->argument->getDirectiveUsages() as $directiveUsage) {
45 | $directive = $directiveUsage->getDirective();
46 | \assert($directive instanceof ArgumentDefinitionLocation);
47 |
48 | if ($directive::isPure()) {
49 | $directive->resolveArgumentDefinition($directiveUsage->getArgumentValues(), $this);
50 | }
51 | }
52 | }
53 |
54 | public function resolveNonPureDirectives() : void
55 | {
56 | $this->value->resolveRemainingDirectives();
57 |
58 | foreach ($this->argument->getDirectiveUsages() as $directiveUsage) {
59 | $directive = $directiveUsage->getDirective();
60 | \assert($directive instanceof ArgumentDefinitionLocation);
61 |
62 | if (!$directive::isPure()) {
63 | $directive->resolveArgumentDefinition($directiveUsage->getArgumentValues(), $this);
64 | }
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/Typesystem/Introspection/EnumValue.php:
--------------------------------------------------------------------------------
1 | notNull(),
37 | static function (EnumItem $item) : string {
38 | return $item->getName();
39 | },
40 | ),
41 | ResolvableField::create(
42 | 'description',
43 | Container::String(),
44 | static function (EnumItem $item) : ?string {
45 | return $item->getDescription();
46 | },
47 | ),
48 | ResolvableField::create(
49 | 'isDeprecated',
50 | Container::Boolean()->notNull(),
51 | static function (EnumItem $item) : bool {
52 | return $item->isDeprecated();
53 | },
54 | ),
55 | ResolvableField::create(
56 | 'deprecationReason',
57 | Container::String(),
58 | static function (EnumItem $item) : ?string {
59 | return $item->getDeprecationReason();
60 | },
61 | ),
62 | ]);
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/src/Value/ArgumentValueSet.php:
--------------------------------------------------------------------------------
1 | $argumentValue) {
23 | $return[$name] = $argumentValue->getValue()->getRawValue(true);
24 | }
25 |
26 | return $return;
27 | }
28 |
29 | public function applyVariables(VariableValueSet $variables) : void
30 | {
31 | foreach ($this as $value) {
32 | $value->applyVariables($variables);
33 | }
34 | }
35 |
36 | public function isSame(self $compare) : bool
37 | {
38 | foreach ($compare as $lhs) {
39 | if ($this->offsetExists($lhs->getArgument()->getName())) {
40 | if ($lhs->getValue()->isSame($this->offsetGet($lhs->getArgument()->getName())->getValue())) {
41 | continue;
42 | }
43 |
44 | return false;
45 | }
46 |
47 | if ($lhs->getValue()->isSame($lhs->getArgument()->getDefaultValue()?->getValue())) {
48 | continue;
49 | }
50 |
51 | return false;
52 | }
53 |
54 | foreach ($this as $lhs) {
55 | if ($compare->offsetExists($lhs->getArgument()->getName()) ||
56 | $lhs->getValue()->isSame($lhs->getArgument()->getDefaultValue()?->getValue())) {
57 | continue;
58 | }
59 |
60 | return false;
61 | }
62 |
63 | return true;
64 | }
65 |
66 | #[\Override]
67 | protected function getKey(object $object) : string
68 | {
69 | \assert($object instanceof ArgumentValue);
70 |
71 | return $object->getArgument()->getName();
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/src/Typesystem/Spec/OneOfDirective.php:
--------------------------------------------------------------------------------
1 | getArguments() as $argument) {
29 | if ($argument->getType() instanceof NotNullType ||
30 | $argument->getDefaultValue() instanceof ArgumentValue) {
31 | throw new OneOfInputInvalidFields();
32 | }
33 | }
34 |
35 | return true;
36 | }
37 |
38 | #[\Override]
39 | public function resolveInputObject(ArgumentValueSet $arguments, InputValue $inputValue) : void
40 | {
41 | $currentCount = 0;
42 |
43 | foreach ($inputValue as $innerValue) {
44 | \assert($innerValue instanceof ArgumentValue);
45 |
46 | if ($currentCount >= 1 || $innerValue->getValue() instanceof NullValue) {
47 | throw new OneOfDirectiveNotSatisfied();
48 | }
49 |
50 | ++$currentCount;
51 | }
52 |
53 | if ($currentCount !== 1) {
54 | throw new OneOfDirectiveNotSatisfied();
55 | }
56 | }
57 |
58 | #[\Override]
59 | protected function getFieldDefinition() : ArgumentSet
60 | {
61 | return new ArgumentSet([]);
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/Normalizer/Validator/FragmentCycleValidator.php:
--------------------------------------------------------------------------------
1 | fragmentSet as $fragment) {
28 | $this->validateFragment($fragment);
29 | }
30 | }
31 |
32 | private function validateFragment(Fragment $fragment) : void
33 | {
34 | if (\array_key_exists($fragment->name, $this->validated)) {
35 | return;
36 | }
37 |
38 | if (\array_key_exists($fragment->name, $this->stack)) {
39 | throw new FragmentCycle();
40 | }
41 |
42 | $this->stack[$fragment->name] = true;
43 | $this->validateFieldSet($fragment->fields);
44 | unset($this->stack[$fragment->name]);
45 | $this->validated[$fragment->name] = true;
46 | }
47 |
48 | private function validateFieldSet(FieldSet $fieldSet) : void
49 | {
50 | foreach ($fieldSet as $field) {
51 | if ($field->children instanceof FieldSet) {
52 | $this->validateFieldSet($field->children);
53 | }
54 | }
55 |
56 | foreach ($fieldSet->getFragmentSpreads() as $spread) {
57 | if (!$spread instanceof NamedFragmentSpread) {
58 | continue;
59 | }
60 |
61 | if (!$this->fragmentSet->offsetExists($spread->name)) {
62 | throw new UnknownFragment($spread->name);
63 | }
64 |
65 | $this->validateFragment($this->fragmentSet->offsetGet($spread->name));
66 | }
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/src/Normalizer/Visitor/GetFieldVisitor.php:
--------------------------------------------------------------------------------
1 |
21 | */
22 | final readonly class GetFieldVisitor implements NamedTypeVisitor
23 | {
24 | public function __construct(
25 | private string $name,
26 | )
27 | {
28 | }
29 |
30 | #[\Override]
31 | public function visitType(Type $type) : Field
32 | {
33 | return $type->getMetaFields()[$this->name]
34 | ?? $type->getFields()[$this->name]
35 | ?? throw new UnknownField($this->name, $type->getName());
36 | }
37 |
38 | #[\Override]
39 | public function visitInterface(InterfaceType $interface) : Field
40 | {
41 | return $interface->getMetaFields()[$this->name]
42 | ?? $interface->getFields()[$this->name]
43 | ?? throw new UnknownField($this->name, $interface->getName());
44 | }
45 |
46 | #[\Override]
47 | public function visitUnion(UnionType $union) : Field
48 | {
49 | return $union->getMetaFields()[$this->name]
50 | ?? throw new SelectionOnUnion();
51 | }
52 |
53 | #[\Override]
54 | public function visitInput(InputType $input) : never
55 | {
56 | throw new \LogicException(); // @codeCoverageIgnore
57 | }
58 |
59 | #[\Override]
60 | public function visitScalar(ScalarType $scalar) : Field
61 | {
62 | throw new SelectionOnLeaf();
63 | }
64 |
65 | #[\Override]
66 | public function visitEnum(EnumType $enum) : Field
67 | {
68 | throw new SelectionOnLeaf();
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/src/Value/VariableValue.php:
--------------------------------------------------------------------------------
1 | type->accept(new IsInputableVisitor());
24 | $isCompatible = $variable->type->accept(new IsInstanceOfVisitor($type));
25 |
26 | if (!$isInputable || !$isCompatible) {
27 | throw new VariableTypeMismatch();
28 | }
29 | }
30 |
31 | #[\Override]
32 | public function getRawValue(bool $forResolvers = false) : mixed
33 | {
34 | return $this->value->getRawValue($forResolvers);
35 | }
36 |
37 | public function getConcreteValue() : InputedValue
38 | {
39 | return $this->value;
40 | }
41 |
42 | public function getVariable() : Variable
43 | {
44 | return $this->variable;
45 | }
46 |
47 | #[\Override]
48 | public function getType() : Type
49 | {
50 | return $this->type;
51 | }
52 |
53 | #[\Override]
54 | public function printValue() : string
55 | {
56 | throw new \RuntimeException('Not implemented');
57 | }
58 |
59 | #[\Override]
60 | public function applyVariables(VariableValueSet $variables) : void
61 | {
62 | $this->value = $variables->get($this->variable->name);
63 | }
64 |
65 | #[\Override]
66 | public function resolveRemainingDirectives() : void
67 | {
68 | $this->value->resolveRemainingDirectives();
69 | }
70 |
71 | #[\Override]
72 | public function isSame(Value $compare) : bool
73 | {
74 | return $compare instanceof self
75 | && $compare->variable->name === $this->variable->name;
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/src/Normalizer/Refiner/Module/DuplicateFragmentSpreadModule.php:
--------------------------------------------------------------------------------
1 | visitedFragments = [];
28 |
29 | foreach ($this->selections as $index => $selection) {
30 | $this->index = $index;
31 | $selection->accept($this);
32 | }
33 | }
34 |
35 | #[\Override]
36 | public function visitField(Field $field) : null
37 | {
38 | return null;
39 | }
40 |
41 | #[\Override]
42 | public function visitFragmentSpread(FragmentSpread $fragmentSpread) : null
43 | {
44 | if (!\array_key_exists($fragmentSpread->name, $this->visitedFragments)) {
45 | $this->visitedFragments[$fragmentSpread->name] = true;
46 |
47 | return null;
48 | }
49 |
50 | /** Found identical fragment spread, we can safely exclude it */
51 | $this->selections->offsetUnset($this->index);
52 |
53 | return null;
54 | }
55 |
56 | #[\Override]
57 | public function visitInlineFragment(InlineFragment $inlineFragment) : null
58 | {
59 | $oldSelections = $this->selections;
60 | $oldIndex = $this->index;
61 |
62 | $this->selections = $inlineFragment->children;
63 |
64 | foreach ($inlineFragment->children as $index => $selection) {
65 | $this->index = $index;
66 | $selection->accept($this);
67 | }
68 |
69 | $this->selections = $oldSelections;
70 | $this->index = $oldIndex;
71 |
72 | return null;
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/src/Request/PsrRequestFactory.php:
--------------------------------------------------------------------------------
1 | request->getMethod();
25 |
26 | if (!\in_array($method, ['GET', 'POST'], true)) {
27 | throw new InvalidMethod();
28 | }
29 |
30 | $contentTypes = $this->request->getHeader('Content-Type');
31 | $contentType = \array_pop($contentTypes);
32 |
33 | if (\is_string($contentType) && \str_starts_with($contentType, 'multipart/form-data')) {
34 | if ($method === 'POST' && \array_key_exists('operations', $this->request->getParsedBody())) {
35 | return $this->applyJsonFactory(Json::fromString($this->request->getParsedBody()['operations']));
36 | }
37 |
38 | throw new InvalidMultipartRequest();
39 | }
40 |
41 | switch ($contentType) {
42 | case 'application/graphql':
43 | return new Request($this->request->getBody()->getContents());
44 | case 'application/json':
45 | return $this->applyJsonFactory(Json::fromString($this->request->getBody()->getContents()));
46 | default:
47 | $params = $this->request->getQueryParams();
48 |
49 | if (\array_key_exists('variables', $params)) {
50 | $params['variables'] = Json::fromString($params['variables'])->toNative();
51 | }
52 |
53 | return $this->applyJsonFactory(Json::fromNative((object) $params));
54 | }
55 | }
56 |
57 | private function applyJsonFactory(Json $json) : Request
58 | {
59 | return (new JsonRequestFactory($json, $this->strict))->create();
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/Typesystem/InterfaceType.php:
--------------------------------------------------------------------------------
1 | implements = $implements;
29 | $this->directiveUsages = new DirectiveUsageSet();
30 | }
31 |
32 | #[\Override]
33 | final public function getFields() : FieldSet
34 | {
35 | if (!$this->fields instanceof FieldSet) {
36 | $this->fields = new FieldSet([]);
37 |
38 | foreach ($this->implements as $interfaceType) {
39 | $this->fields->merge($interfaceType->getFields(), true);
40 | }
41 |
42 | $this->fields->merge($this->getFieldDefinition(), true);
43 | }
44 |
45 | return $this->fields;
46 | }
47 |
48 | #[\Override]
49 | final public function accept(AbstractTypeVisitor $visitor) : mixed
50 | {
51 | return $visitor->visitInterface($this);
52 | }
53 |
54 | /**
55 | * @param ObjectLocation $directive
56 | * @phpcs:ignore
57 | * @param array $arguments
58 | */
59 | final public function addDirective(ObjectLocation $directive, array $arguments = []) : static
60 | {
61 | $this->directiveUsages[] = new DirectiveUsage($directive, $arguments);
62 |
63 | return $this;
64 | }
65 |
66 | abstract protected function getFieldDefinition() : FieldSet;
67 | }
68 |
--------------------------------------------------------------------------------
/src/Normalizer/Refiner/Module/DuplicateFieldModule.php:
--------------------------------------------------------------------------------
1 | fieldForName = [];
29 |
30 | foreach ($this->selections as $index => $selection) {
31 | $this->index = $index;
32 | $selection->accept($this);
33 | }
34 | }
35 |
36 | #[\Override]
37 | public function visitField(Field $field) : null
38 | {
39 | if (!\array_key_exists($field->outputName, $this->fieldForName)) {
40 | $this->fieldForName[$field->outputName] = $field;
41 |
42 | return null;
43 | }
44 |
45 | /** Merge duplicate field together */
46 | if ($field->children instanceof SelectionSet) {
47 | $conflict = $this->fieldForName[$field->outputName];
48 | \assert($conflict instanceof Field);
49 | \assert($conflict->children instanceof SelectionSet);
50 | $mergedSelectionSet = $conflict->children->merge($field->children);
51 | $refiner = new SelectionSetRefiner($mergedSelectionSet);
52 | $refiner->refine();
53 | }
54 |
55 | /** Exclude duplicate field */
56 | $this->selections->offsetUnset($this->index);
57 |
58 | return null;
59 | }
60 |
61 | #[\Override]
62 | public function visitFragmentSpread(FragmentSpread $fragmentSpread) : null
63 | {
64 | return null;
65 | }
66 |
67 | #[\Override]
68 | public function visitInlineFragment(InlineFragment $inlineFragment) : null
69 | {
70 | return null;
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/src/Typesystem/Field/Field.php:
--------------------------------------------------------------------------------
1 | arguments = new ArgumentSet([]);
32 | $this->directiveUsages = new DirectiveUsageSet();
33 | }
34 |
35 | public static function create(string $name, Type $type) : self
36 | {
37 | return new self($name, $type);
38 | }
39 |
40 | final public function getName() : string
41 | {
42 | return $this->name;
43 | }
44 |
45 | final public function getType() : Type
46 | {
47 | return $this->type;
48 | }
49 |
50 | final public function getArguments() : ArgumentSet
51 | {
52 | return $this->arguments;
53 | }
54 |
55 | final public function setArguments(ArgumentSet $arguments) : static
56 | {
57 | $this->arguments = $arguments;
58 |
59 | return $this;
60 | }
61 |
62 | #[\Override]
63 | final public function accept(ComponentVisitor $visitor) : mixed
64 | {
65 | return $visitor->visitField($this);
66 | }
67 |
68 | /**
69 | * @param FieldDefinitionLocation $directive
70 | * @phpcs:ignore
71 | * @param array $arguments
72 | */
73 | final public function addDirective(FieldDefinitionLocation $directive, array $arguments = []) : self
74 | {
75 | $this->directiveUsages[] = new DirectiveUsage($directive, $arguments);
76 |
77 | return $this;
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/src/Request/JsonRequestFactory.php:
--------------------------------------------------------------------------------
1 | json[self::QUERY])) {
36 | throw new QueryMissing();
37 | }
38 |
39 | if (!\is_string($this->json[self::QUERY])) {
40 | throw new QueryNotString();
41 | }
42 |
43 | if (isset($this->json[self::VARIABLES]) && !$this->json[self::VARIABLES] instanceof \stdClass) {
44 | throw new VariablesNotObject();
45 | }
46 |
47 | if (isset($this->json[self::OPERATION_NAME]) && !\is_string($this->json[self::OPERATION_NAME])) {
48 | throw new OperationNameNotString();
49 | }
50 |
51 | if ($this->strict) {
52 | foreach ($this->json as $key => $value) {
53 | if (!\in_array($key, [self::QUERY, self::VARIABLES, self::OPERATION_NAME], true)) {
54 | throw new UnknownKey();
55 | }
56 | }
57 | }
58 |
59 | $query = $this->json[self::QUERY];
60 | $variables = $this->json[self::VARIABLES]
61 | ?? new \stdClass();
62 | $operationName = $this->json[self::OPERATION_NAME]
63 | ?? null;
64 |
65 | return new Request($query, $variables, $operationName);
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/Typesystem/ScalarType.php:
--------------------------------------------------------------------------------
1 | directiveUsages = new DirectiveUsageSet();
27 | }
28 |
29 | #[\Override]
30 | final public function accept(NamedTypeVisitor $visitor) : mixed
31 | {
32 | return $visitor->visitScalar($this);
33 | }
34 |
35 | /**
36 | * @phpcs:ignore
37 | * @param mixed $rawValue
38 | * @return ?T
39 | */
40 | abstract public function validateAndCoerceInput(mixed $rawValue) : mixed;
41 |
42 | /**
43 | * @param T $rawValue
44 | */
45 | abstract public function coerceOutput(mixed $rawValue) : string|int|float|bool;
46 |
47 | /**
48 | * @param ScalarLocation $directive
49 | * @phpcs:ignore
50 | * @param array $arguments
51 | */
52 | final public function addDirective(ScalarLocation $directive, array $arguments = []) : static
53 | {
54 | $this->directiveUsages[] = new DirectiveUsage($directive, $arguments);
55 |
56 | return $this;
57 | }
58 |
59 | public function setSpecifiedBy(string $url) : self
60 | {
61 | $this->addDirective(
62 | Container::directiveSpecifiedBy(),
63 | ['url' => $url],
64 | );
65 |
66 | return $this;
67 | }
68 |
69 | public function getSpecifiedByUrl() : ?string
70 | {
71 | foreach ($this->getDirectiveUsages() as $directive) {
72 | if ($directive->getDirective() instanceof SpecifiedByDirective) {
73 | return $directive->getArgumentValues()->offsetGet('url')->getValue()->getRawValue();
74 | }
75 | }
76 |
77 | return null;
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/src/Value/ScalarValue.php:
--------------------------------------------------------------------------------
1 | rawValue = $type->validateAndCoerceInput($rawValue)
24 | ?? throw new InvalidValue($type, $rawValue, $inputed);
25 | }
26 |
27 | #[\Override]
28 | public function getRawValue(bool $forResolvers = false) : mixed
29 | {
30 | return ($forResolvers && $this->hasResolverValue)
31 | ? $this->resolverValue
32 | : $this->rawValue;
33 | }
34 |
35 | #[\Override]
36 | public function getType() : ScalarType
37 | {
38 | return $this->type;
39 | }
40 |
41 | #[\Override]
42 | public function jsonSerialize() : string|int|float|bool
43 | {
44 | return $this->type->coerceOutput($this->rawValue);
45 | }
46 |
47 | #[\Override]
48 | public function printValue() : string
49 | {
50 | return \json_encode($this->jsonSerialize(), \JSON_THROW_ON_ERROR |
51 | \JSON_UNESCAPED_UNICODE |
52 | \JSON_UNESCAPED_SLASHES |
53 | \JSON_PRESERVE_ZERO_FRACTION);
54 | }
55 |
56 | #[\Override]
57 | public function applyVariables(VariableValueSet $variables) : void
58 | {
59 | // nothing here
60 | }
61 |
62 | #[\Override]
63 | public function resolveRemainingDirectives() : void
64 | {
65 | // nothing here
66 | }
67 |
68 | #[\Override]
69 | public function isSame(Value $compare) : bool
70 | {
71 | return $compare instanceof self
72 | && $this->rawValue === $compare->getRawValue();
73 | }
74 |
75 | public function setResolverValue(mixed $value) : void
76 | {
77 | $this->hasResolverValue = true;
78 | $this->resolverValue = $value;
79 | }
80 |
81 | public function hasResolverValue() : bool
82 | {
83 | return $this->hasResolverValue;
84 | }
85 |
86 | public function getResolverValue() : mixed
87 | {
88 | return $this->resolverValue;
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/src/Typesystem/Argument/Argument.php:
--------------------------------------------------------------------------------
1 | directiveUsages = new DirectiveUsageSet();
34 | }
35 |
36 | public static function create(string $name, Type $type) : self
37 | {
38 | return new self($name, $type);
39 | }
40 |
41 | public function getName() : string
42 | {
43 | return $this->name;
44 | }
45 |
46 | public function getType() : Type
47 | {
48 | return $this->type;
49 | }
50 |
51 | public function getDefaultValue() : ?ArgumentValue
52 | {
53 | return $this->defaultValue;
54 | }
55 |
56 | public function setDefaultValue(mixed $defaultValue) : self
57 | {
58 | $this->defaultValue = new ArgumentValue(
59 | $this,
60 | $this->getType()->accept(new ConvertRawValueVisitor($defaultValue, new Path())),
61 | false,
62 | );
63 |
64 | return $this;
65 | }
66 |
67 | #[\Override]
68 | public function accept(ComponentVisitor $visitor) : mixed
69 | {
70 | return $visitor->visitArgument($this);
71 | }
72 |
73 | /**
74 | * @param ArgumentDefinitionLocation $directive
75 | * @phpcs:ignore
76 | * @param array $arguments
77 | */
78 | public function addDirective(ArgumentDefinitionLocation $directive, array $arguments = []) : self
79 | {
80 | $this->directiveUsages[] = new DirectiveUsage($directive, $arguments);
81 |
82 | return $this;
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/src/Typesystem/InputType.php:
--------------------------------------------------------------------------------
1 | directiveUsages = new DirectiveUsageSet();
27 | }
28 |
29 | final public function getArguments() : ArgumentSet
30 | {
31 | if (!$this->arguments instanceof ArgumentSet) {
32 | $this->arguments = $this->getFieldDefinition();
33 | $this->afterGetFieldDefinition();
34 | }
35 |
36 | return $this->arguments;
37 | }
38 |
39 | #[\Override]
40 | final public function accept(NamedTypeVisitor $visitor) : mixed
41 | {
42 | return $visitor->visitInput($this);
43 | }
44 |
45 | final public function getDataClass() : string
46 | {
47 | return static::DATA_CLASS;
48 | }
49 |
50 | /**
51 | * @param InputObjectLocation $directive
52 | * @phpcs:ignore
53 | * @param array $arguments
54 | */
55 | final public function addDirective(InputObjectLocation $directive, array $arguments = []) : static
56 | {
57 | $this->directiveUsages[] = new DirectiveUsage($directive, $arguments);
58 |
59 | return $this;
60 | }
61 |
62 | public function isOneOf() : bool
63 | {
64 | foreach ($this->getDirectiveUsages() as $directive) {
65 | if ($directive->getDirective() instanceof OneOfDirective) {
66 | return true;
67 | }
68 | }
69 |
70 | return false;
71 | }
72 |
73 | abstract protected function getFieldDefinition() : ArgumentSet;
74 |
75 | /**
76 | * This function serves to prevent infinite cycles.
77 | *
78 | * It doesn't have to be used at all, unless input have arguments self referencing fields and wish to put default value for them.
79 | */
80 | protected function afterGetFieldDefinition() : void
81 | {
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/src/Typesystem/Spec/SkipDirective.php:
--------------------------------------------------------------------------------
1 | offsetGet('if')->getValue()->getRawValue() === true
38 | ? SelectionDirectiveResult::SKIP
39 | : SelectionDirectiveResult::NONE;
40 | }
41 |
42 | #[\Override]
43 | public function resolveFieldAfter(ArgumentValueSet $arguments, FieldValue $fieldValue) : SelectionDirectiveResult
44 | {
45 | return SelectionDirectiveResult::NONE;
46 | }
47 |
48 | #[\Override]
49 | public function resolveFragmentSpreadBefore(ArgumentValueSet $arguments) : SelectionDirectiveResult
50 | {
51 | return $this->resolveFieldBefore($arguments);
52 | }
53 |
54 | #[\Override]
55 | public function resolveFragmentSpreadAfter(ArgumentValueSet $arguments) : void
56 | {
57 | // nothing here
58 | }
59 |
60 | #[\Override]
61 | public function resolveInlineFragmentBefore(ArgumentValueSet $arguments) : SelectionDirectiveResult
62 | {
63 | return $this->resolveFieldBefore($arguments);
64 | }
65 |
66 | #[\Override]
67 | public function resolveInlineFragmentAfter(ArgumentValueSet $arguments) : void
68 | {
69 | // nothing here
70 | }
71 |
72 | #[\Override]
73 | protected function getFieldDefinition() : ArgumentSet
74 | {
75 | return new ArgumentSet([
76 | new Argument('if', Container::Boolean()->notNull()),
77 | ]);
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/src/Typesystem/Introspection/Directive.php:
--------------------------------------------------------------------------------
1 | notNull(),
40 | static function (DirectiveDef $directive) : string {
41 | return $directive->getName();
42 | },
43 | ),
44 | ResolvableField::create(
45 | 'description',
46 | Container::String(),
47 | static function (DirectiveDef $directive) : ?string {
48 | return $directive->getDescription();
49 | },
50 | ),
51 | ResolvableField::create(
52 | 'locations',
53 | $this->container->getType('__DirectiveLocation')->notNullList(),
54 | static function (DirectiveDef $directive) : array {
55 | return $directive->getLocations();
56 | },
57 | ),
58 | ResolvableField::create(
59 | 'args',
60 | $this->container->getType('__InputValue')->notNullList(),
61 | static function (DirectiveDef $directive) : ArgumentSet {
62 | return $directive->getArguments();
63 | },
64 | ),
65 | ResolvableField::create(
66 | 'isRepeatable',
67 | Container::Boolean()->notNull(),
68 | static function (DirectiveDef $directive) : bool {
69 | return $directive->isRepeatable();
70 | },
71 | ),
72 | ]);
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/src/Typesystem/Spec/IncludeDirective.php:
--------------------------------------------------------------------------------
1 | offsetGet('if')->getValue()->getRawValue() === true
38 | ? SelectionDirectiveResult::NONE
39 | : SelectionDirectiveResult::SKIP;
40 | }
41 |
42 | #[\Override]
43 | public function resolveFieldAfter(ArgumentValueSet $arguments, FieldValue $fieldValue) : SelectionDirectiveResult
44 | {
45 | return SelectionDirectiveResult::NONE;
46 | }
47 |
48 | #[\Override]
49 | public function resolveFragmentSpreadBefore(ArgumentValueSet $arguments) : SelectionDirectiveResult
50 | {
51 | return $this->resolveFieldBefore($arguments);
52 | }
53 |
54 | #[\Override]
55 | public function resolveFragmentSpreadAfter(ArgumentValueSet $arguments) : void
56 | {
57 | // nothing here
58 | }
59 |
60 | #[\Override]
61 | public function resolveInlineFragmentBefore(ArgumentValueSet $arguments) : SelectionDirectiveResult
62 | {
63 | return $this->resolveFieldBefore($arguments);
64 | }
65 |
66 | #[\Override]
67 | public function resolveInlineFragmentAfter(ArgumentValueSet $arguments) : void
68 | {
69 | // nothing here
70 | }
71 |
72 | #[\Override]
73 | protected function getFieldDefinition() : ArgumentSet
74 | {
75 | return new ArgumentSet([
76 | new Argument('if', Container::Boolean()->notNull()),
77 | ]);
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/src/Typesystem/Schema.php:
--------------------------------------------------------------------------------
1 | directiveUsages = new DirectiveUsageSet();
32 | $query->addMetaField(ResolvableField::create(
33 | '__schema',
34 | $container->getType('__Schema')->notNull(),
35 | function() : Schema {
36 | return $this;
37 | },
38 | ));
39 | $query->addMetaField(ResolvableField::create(
40 | '__type',
41 | $container->getType('__Type'),
42 | function($parent, string $name) : ?TypeContract {
43 | return $this->container->getType($name);
44 | },
45 | )->setArguments(new ArgumentSet([
46 | Argument::create('name', Container::String()->notNull()),
47 | ])));
48 | }
49 |
50 | final public function getContainer() : Container
51 | {
52 | return $this->container;
53 | }
54 |
55 | final public function getQuery() : Type
56 | {
57 | return $this->query;
58 | }
59 |
60 | final public function getMutation() : ?Type
61 | {
62 | return $this->mutation;
63 | }
64 |
65 | final public function getSubscription() : ?Type
66 | {
67 | return $this->subscription;
68 | }
69 |
70 | #[\Override]
71 | final public function accept(EntityVisitor $visitor) : mixed
72 | {
73 | return $visitor->visitSchema($this);
74 | }
75 |
76 | /**
77 | * @param SchemaLocation $directive
78 | * @phpcs:ignore
79 | * @param array $arguments
80 | */
81 | final public function addDirective(SchemaLocation $directive, array $arguments = []) : self
82 | {
83 | $this->directiveUsages[] = new DirectiveUsage($directive, $arguments);
84 |
85 | return $this;
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/src/Normalizer/Finalizer.php:
--------------------------------------------------------------------------------
1 | path = new Path();
22 |
23 | try {
24 | $operation = $this->selectOperation($normalizedRequest, $operationName);
25 | $this->path->add($operation->name . ' ');
26 | $this->applyVariables($operation, $variables);
27 | } catch (GraphpinatorBase $e) {
28 | throw $e->setPath($this->path);
29 | }
30 |
31 | return new FinalizedRequest($operation);
32 | }
33 |
34 | private function selectOperation(NormalizedRequest $normalizedRequest, ?string $operationName) : Operation
35 | {
36 | return $operationName === null
37 | ? $normalizedRequest->operations->getFirst()
38 | : $normalizedRequest->operations->offsetGet($operationName);
39 | }
40 |
41 | private function applyVariables(Operation $operation, \stdClass $variables) : void
42 | {
43 | $normalized = [];
44 |
45 | foreach ($operation->variables as $variable) {
46 | $this->path->add($variable->name . ' ');
47 | $value = $this->normalizeVariableValue($variable, $variables);
48 |
49 | foreach ($variable->directives as $directive) {
50 | $directiveDef = $directive->directive;
51 | \assert($directiveDef instanceof VariableDefinitionLocation);
52 | $directiveDef->resolveVariableDefinition($directive->arguments, $value);
53 | }
54 |
55 | $normalized[$variable->name] = $value;
56 | $this->path->pop();
57 | }
58 |
59 | $operation->children->applyVariables(new VariableValueSet($normalized));
60 | }
61 |
62 | private function normalizeVariableValue(
63 | Variable $variable,
64 | \stdClass $variables,
65 | ) : InputedValue
66 | {
67 | if (isset($variables->{$variable->name})) {
68 | return $variable->type->accept(new ConvertRawValueVisitor($variables->{$variable->name}, $this->path));
69 | }
70 |
71 | if ($variable->defaultValue instanceof InputedValue) {
72 | return $variable->defaultValue;
73 | }
74 |
75 | return $variable->type->accept(new ConvertRawValueVisitor(null, $this->path));
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/src/Typesystem/Introspection/Schema.php:
--------------------------------------------------------------------------------
1 | getDescription();
41 | },
42 | ),
43 | ResolvableField::create(
44 | 'types',
45 | $this->container->getType('__Type')->notNullList(),
46 | static function (SchemaDef $schema) : array {
47 | return $schema->getContainer()->getTypes(true);
48 | },
49 | ),
50 | ResolvableField::create(
51 | 'queryType',
52 | $this->container->getType('__Type')->notNull(),
53 | static function (SchemaDef $schema) : Type {
54 | return $schema->getQuery();
55 | },
56 | ),
57 | ResolvableField::create(
58 | 'mutationType',
59 | $this->container->getType('__Type'),
60 | static function (SchemaDef $schema) : ?Type {
61 | return $schema->getMutation();
62 | },
63 | ),
64 | ResolvableField::create(
65 | 'subscriptionType',
66 | $this->container->getType('__Type'),
67 | static function (SchemaDef $schema) : ?Type {
68 | return $schema->getSubscription();
69 | },
70 | ),
71 | ResolvableField::create(
72 | 'directives',
73 | $this->container->getType('__Directive')->notNullList(),
74 | static function (SchemaDef $schema) : array {
75 | return $schema->getContainer()->getDirectives(true);
76 | },
77 | ),
78 | ]);
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/src/Typesystem/Spec/DeprecatedDirective.php:
--------------------------------------------------------------------------------
1 | rawValue = self::coerceInput($type, $rawValue, $inputed);
22 | }
23 |
24 | #[\Override]
25 | public function getRawValue(bool $forResolvers = false) : string|\BackedEnum
26 | {
27 | return $this->rawValue;
28 | }
29 |
30 | #[\Override]
31 | public function getType() : EnumType
32 | {
33 | return $this->type;
34 | }
35 |
36 | #[\Override]
37 | public function jsonSerialize() : string
38 | {
39 | return self::coerceOutput($this->rawValue);
40 | }
41 |
42 | #[\Override]
43 | public function printValue() : string
44 | {
45 | return self::coerceOutput($this->rawValue);
46 | }
47 |
48 | #[\Override]
49 | public function applyVariables(VariableValueSet $variables) : void
50 | {
51 | // nothing here
52 | }
53 |
54 | #[\Override]
55 | public function resolveRemainingDirectives() : void
56 | {
57 | // nothing here
58 | }
59 |
60 | #[\Override]
61 | public function isSame(Value $compare) : bool
62 | {
63 | return $compare instanceof self
64 | && $this->rawValue === $compare->getRawValue();
65 | }
66 |
67 | private static function coerceInput(EnumType $type, mixed $rawValue, bool $inputed) : string|\BackedEnum
68 | {
69 | $enumClass = $type->getEnumClass();
70 |
71 | if (\is_string($rawValue) && $type->getItems()->offsetExists($rawValue)) {
72 | return \is_string($enumClass)
73 | ? \call_user_func([$enumClass, 'from'], $rawValue) // value should be a native enum
74 | : $rawValue; // value is correctly string
75 | }
76 |
77 | if ($rawValue instanceof \BackedEnum && $type->getItems()->offsetExists($rawValue->value)) {
78 | if (\is_string($enumClass)) {
79 | return $rawValue::class === $enumClass
80 | ? $rawValue // value is correctly native enum
81 | : \call_user_func([$enumClass, 'from'], $rawValue->value); // value is enum of different type -> convert
82 | }
83 |
84 | return $rawValue->value; // value should be string
85 | }
86 |
87 | throw new InvalidValue($type, $rawValue, $inputed);
88 | }
89 |
90 | private static function coerceOutput(string|\BackedEnum $rawValue) : string
91 | {
92 | return $rawValue instanceof \BackedEnum
93 | ? $rawValue->value
94 | : $rawValue;
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/src/Typesystem/Introspection/InputValue.php:
--------------------------------------------------------------------------------
1 | notNull(),
41 | static function (Argument $argument) : string {
42 | return $argument->getName();
43 | },
44 | ),
45 | ResolvableField::create(
46 | 'description',
47 | Container::String(),
48 | static function (Argument $argument) : ?string {
49 | return $argument->getDescription();
50 | },
51 | ),
52 | ResolvableField::create(
53 | 'type',
54 | $this->container->getType('__Type')->notNull(),
55 | static function (Argument $argument) : TypeContract {
56 | return $argument->getType();
57 | },
58 | ),
59 | ResolvableField::create(
60 | 'defaultValue',
61 | Container::String(),
62 | static function (Argument $argument) : ?string {
63 | return $argument->getDefaultValue() instanceof ArgumentValue
64 | ? $argument->getDefaultValue()->getValue()->printValue()
65 | : null;
66 | },
67 | ),
68 | ResolvableField::create(
69 | 'isDeprecated',
70 | Container::Boolean()->notNull(),
71 | static function (Argument $argument) : bool {
72 | return $argument->isDeprecated();
73 | },
74 | ),
75 | ResolvableField::create(
76 | 'deprecationReason',
77 | Container::String(),
78 | static function (Argument $argument) : ?string {
79 | return $argument->getDeprecationReason();
80 | },
81 | ),
82 | ]);
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/src/Resolver/ExceptionHandler.php:
--------------------------------------------------------------------------------
1 | errorHandlingMode = $errorHandlingMode instanceof ErrorHandlingMode
20 | ? $errorHandlingMode
21 | : ErrorHandlingMode::fromBool($errorHandlingMode);
22 | }
23 |
24 | public function handle(\Throwable $exception) : Result
25 | {
26 | return match ($this->errorHandlingMode) {
27 | ErrorHandlingMode::ALL => self::handleAll($exception),
28 | ErrorHandlingMode::OUTPUTABLE => self::handleOutputable($exception),
29 | ErrorHandlingMode::CLIENT_AWARE => self::handleClientAware($exception),
30 | ErrorHandlingMode::NONE => self::handleNone($exception),
31 | };
32 | }
33 |
34 | private static function handleAll(\Throwable $exception) : Result
35 | {
36 | return new Result(null, [
37 | $exception instanceof ClientAware
38 | ? self::serializeError($exception)
39 | : self::notOutputableResponse(),
40 | ]);
41 | }
42 |
43 | private static function handleOutputable(\Throwable $exception) : Result
44 | {
45 | return $exception instanceof ClientAware && $exception->isOutputable()
46 | ? new Result(null, [self::serializeError($exception)])
47 | : throw $exception;
48 | }
49 |
50 | private static function handleClientAware(\Throwable $exception) : Result
51 | {
52 | return $exception instanceof ClientAware
53 | ? new Result(null, [self::serializeError($exception)])
54 | : throw $exception;
55 | }
56 |
57 | private static function handleNone(\Throwable $exception) : never
58 | {
59 | throw $exception;
60 | }
61 |
62 | private static function serializeError(ClientAware $exception) : array
63 | {
64 | if (!$exception->isOutputable()) {
65 | return self::notOutputableResponse();
66 | }
67 |
68 | $result = [
69 | 'message' => $exception->getMessage(),
70 | ];
71 |
72 | if ($exception->getLocation() instanceof Location) {
73 | $result['locations'] = [$exception->getLocation()];
74 | }
75 |
76 | if ($exception->getPath() instanceof Path) {
77 | $result['path'] = $exception->getPath();
78 | }
79 |
80 | if (\is_array($exception->getExtensions())) {
81 | $result['extensions'] = $exception->getExtensions();
82 | }
83 |
84 | return $result;
85 | }
86 |
87 | private static function notOutputableResponse() : array
88 | {
89 | return [
90 | 'message' => 'Server responded with unknown error.',
91 | ];
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/src/Typesystem/Visitor/IsInstanceOfVisitor.php:
--------------------------------------------------------------------------------
1 | as it is a subset
24 | * - Int is NOT instanceof Int!
25 | * - UnionType is NOT instanceof UnionTypeItem
26 | *
27 | * @implements TypeVisitor
28 | */
29 | final readonly class IsInstanceOfVisitor implements TypeVisitor
30 | {
31 | public function __construct(
32 | private TypeContract $typeToCompare,
33 | )
34 | {
35 | }
36 |
37 | #[\Override]
38 | public function visitType(Type $type) : bool
39 | {
40 | return $type::class === $this->typeToCompare::class
41 | || ($this->typeToCompare instanceof AbstractType && $this->typeToCompare->accept(new IsImplementedByVisitor($type)));
42 | }
43 |
44 | #[\Override]
45 | public function visitInterface(InterfaceType $interface) : bool
46 | {
47 | return $interface::class === $this->typeToCompare::class
48 | || ($this->typeToCompare instanceof AbstractType && $this->typeToCompare->accept(new IsImplementedByVisitor($interface)));
49 | }
50 |
51 | #[\Override]
52 | public function visitUnion(UnionType $union) : bool
53 | {
54 | return $union::class === $this->typeToCompare::class;
55 | }
56 |
57 | #[\Override]
58 | public function visitInput(InputType $input) : bool
59 | {
60 | return $input::class === $this->typeToCompare::class;
61 | }
62 |
63 | #[\Override]
64 | public function visitScalar(ScalarType $scalar) : bool
65 | {
66 | return $scalar::class === $this->typeToCompare::class;
67 | }
68 |
69 | #[\Override]
70 | public function visitEnum(EnumType $enum) : bool
71 | {
72 | return $enum::class === $this->typeToCompare::class;
73 | }
74 |
75 | #[\Override]
76 | public function visitNotNull(NotNullType $notNull) : bool
77 | {
78 | return $this->typeToCompare instanceof NotNullType
79 | ? $notNull->getInnerType()->accept(new self($this->typeToCompare->getInnerType()))
80 | : $notNull->getInnerType()->accept($this);
81 | }
82 |
83 | #[\Override]
84 | public function visitList(ListType $list) : bool
85 | {
86 | return $this->typeToCompare instanceof ListType
87 | ? $list->getInnerType()->accept(new self($this->typeToCompare->getInnerType()))
88 | : false;
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "infinityloop-dev/graphpinator",
3 | "description": "Easy-to-use & Fast GraphQL server implementation for PHP.",
4 | "homepage": "https://github.com/graphpql/",
5 | "type": "library",
6 | "license": ["MIT"],
7 | "authors": [
8 | {
9 | "name": "Václav Pelíšek",
10 | "homepage": "https://www.peldax.com",
11 | "role": "lead"
12 | }
13 | ],
14 | "require": {
15 | "php": ">=8.2",
16 | "ext-json": "*",
17 | "infinityloop-dev/graphpinator-common": "^2.0",
18 | "infinityloop-dev/graphpinator-parser": "^2.0",
19 | "infinityloop-dev/graphpinator-source": "^1.2",
20 | "infinityloop-dev/utils": "^2.3",
21 | "psr/http-message": "^2.0",
22 | "psr/log": "^3.0"
23 | },
24 | "require-dev": {
25 | "guzzlehttp/psr7": "^2.8",
26 | "infection/infection": "^0.29",
27 | "infinityloop-dev/graphpinator-tokenizer": "^1.3",
28 | "phpstan/extension-installer": "^1.4",
29 | "phpstan/phpstan": "^2.0",
30 | "phpstan/phpstan-deprecation-rules": "^2.0",
31 | "phpstan/phpstan-strict-rules": "^2.0",
32 | "phpunit/phpunit": "^10.4",
33 | "shipmonk/composer-dependency-analyser": "^1.8",
34 | "thecodingmachine/phpstan-safe-rule": "^1.4",
35 | "webthinx/codestyle": "^1.1"
36 | },
37 | "suggest": {
38 | "infinityloop-dev/graphpinator-nette": "Adapters for Nette framework.",
39 | "infinityloop-dev/graphpinator-printer": "Schema printing in GraphQL language.",
40 | "infinityloop-dev/graphpinator-constraint-directives": "Directives for additional value validation.",
41 | "infinityloop-dev/graphpinator-where-directives": "Directives for filtering list values.",
42 | "infinityloop-dev/graphpinator-extra-types": "Commonly used types, both scalar or composite.",
43 | "infinityloop-dev/graphpinator-upload": "Module to handle multipart formdata requests.",
44 | "infinityloop-dev/graphpinator-query-cost": "Modules to limit query cost by restricting maximum depth or number of nodes.",
45 | "infinityloop-dev/graphpinator-persisted-queries": "Module to persist validated query in cache and improve performace of repeating queries."
46 | },
47 | "scripts": {
48 | "phpunit": "phpunit tests",
49 | "phpunit-no-coverage": "phpunit tests --no-coverage",
50 | "infection": [
51 | "Composer\\Config::disableProcessTimeout",
52 | "infection -j$(nproc)"
53 | ],
54 | "phpstan": "phpstan analyze --level 4 src",
55 | "phpstan-next": "phpstan analyze --level 5 src",
56 | "phpstan-max": "phpstan analyze --level max src",
57 | "codestyle": "phpcs --extensions=php src tests",
58 | "codestyle-fix": "phpcbf --extensions=php src tests",
59 | "dependencies": "composer-dependency-analyser"
60 | },
61 | "autoload": {
62 | "psr-4": {
63 | "Graphpinator\\": "src/"
64 | }
65 | },
66 | "autoload-dev": {
67 | "psr-4": {
68 | "Graphpinator\\Tests\\": "tests/"
69 | }
70 | },
71 | "config": {
72 | "allow-plugins": {
73 | "dealerdirect/phpcodesniffer-composer-installer": true,
74 | "infection/extension-installer": true,
75 | "phpstan/extension-installer": true
76 | },
77 | "sort-packages": true
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/src/Typesystem/EnumType.php:
--------------------------------------------------------------------------------
1 | directiveUsages = new DirectiveUsageSet();
27 | }
28 |
29 | final public static function fromConstants() : EnumItemSet
30 | {
31 | $values = [];
32 |
33 | foreach ((new \ReflectionClass(static::class))->getReflectionConstants() as $constant) {
34 | $value = $constant->getValue();
35 |
36 | if (!$constant->isPublic()) {
37 | continue;
38 | }
39 |
40 | $values[] = new EnumItem($value, self::getItemDescription($constant));
41 | }
42 |
43 | return new EnumItemSet($values);
44 | }
45 |
46 | final public static function fromEnum(string $enumClass) : EnumItemSet
47 | {
48 | $values = [];
49 | $ref = new \ReflectionEnum($enumClass);
50 |
51 | if ((string) $ref->getBackingType() !== 'string') {
52 | throw new \InvalidArgumentException('Enum must be backed by string.');
53 | }
54 |
55 | foreach ($ref->getCases() as $case) {
56 | \assert($case instanceof \ReflectionEnumBackedCase);
57 |
58 | $values[] = new EnumItem($case->getBackingValue(), self::getItemDescription($case));
59 | }
60 |
61 | return new EnumItemSet($values, $enumClass);
62 | }
63 |
64 | final public function getItems() : EnumItemSet
65 | {
66 | return $this->options;
67 | }
68 |
69 | final public function getEnumClass() : ?string
70 | {
71 | return $this->options->getEnumClass();
72 | }
73 |
74 | #[\Override]
75 | final public function accept(NamedTypeVisitor $visitor) : mixed
76 | {
77 | return $visitor->visitEnum($this);
78 | }
79 |
80 | /**
81 | * @param EnumLocation $directive
82 | * @phpcs:ignore
83 | * @param array $arguments
84 | */
85 | final public function addDirective(EnumLocation $directive, array $arguments = []) : static
86 | {
87 | $this->directiveUsages[] = new DirectiveUsage($directive, $arguments);
88 |
89 | return $this;
90 | }
91 |
92 | private static function getItemDescription(\ReflectionClassConstant|\ReflectionEnumBackedCase $reflection) : ?string
93 | {
94 | $attrs = $reflection->getAttributes(Description::class);
95 |
96 | if (\count($attrs) === 1) {
97 | return $attrs[0]->newInstance()->getValue();
98 | }
99 |
100 | return null;
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/src/Value/Visitor/CreateResolvedValueVisitor.php:
--------------------------------------------------------------------------------
1 |
27 | */
28 | final class CreateResolvedValueVisitor implements TypeVisitor
29 | {
30 | public function __construct(
31 | private mixed $rawValue,
32 | )
33 | {
34 | }
35 |
36 | #[\Override]
37 | public function visitType(Type $type) : ResolvedValue
38 | {
39 | return $this->rawValue === null
40 | ? new NullValue($type)
41 | : new TypeIntermediateValue($type, $this->rawValue);
42 | }
43 |
44 | #[\Override]
45 | public function visitInterface(InterfaceType $interface) : ResolvedValue
46 | {
47 | return $this->rawValue === null
48 | ? new NullValue($interface)
49 | : $interface->createResolvedValue($this->rawValue);
50 | }
51 |
52 | #[\Override]
53 | public function visitUnion(UnionType $union) : ResolvedValue
54 | {
55 | return $this->rawValue === null
56 | ? new NullValue($union)
57 | : $union->createResolvedValue($this->rawValue);
58 | }
59 |
60 | #[\Override]
61 | public function visitInput(InputType $input) : never
62 | {
63 | throw new \LogicException(); // @codeCoverageIgnore
64 | }
65 |
66 | #[\Override]
67 | public function visitScalar(ScalarType $scalar) : ResolvedValue
68 | {
69 | return $this->rawValue === null
70 | ? new NullValue($scalar)
71 | : new ScalarValue($scalar, $this->rawValue, false);
72 | }
73 |
74 | #[\Override]
75 | public function visitEnum(EnumType $enum) : ResolvedValue
76 | {
77 | return $this->rawValue === null
78 | ? new NullValue($enum)
79 | : new EnumValue($enum, $this->rawValue, false);
80 | }
81 |
82 | #[\Override]
83 | public function visitNotNull(NotNullType $notNull) : ResolvedValue
84 | {
85 | return $this->rawValue === null
86 | ? throw new ValueCannotBeNull(false)
87 | : $notNull->getInnerType()->accept($this);
88 | }
89 |
90 | #[\Override]
91 | public function visitList(ListType $list) : ResolvedValue
92 | {
93 | if ($this->rawValue === null) {
94 | return new NullValue($list);
95 | }
96 |
97 | if (\is_iterable($this->rawValue)) {
98 | return new ListIntermediateValue($list, $this->rawValue);
99 | }
100 |
101 | throw new InvalidValue($list, $this->rawValue, false);
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/src/Resolver/Visitor/ResolveVisitor.php:
--------------------------------------------------------------------------------
1 | parentResult instanceof TypeIntermediateValue);
39 | \assert($this->selectionSet instanceof SelectionSet);
40 |
41 | foreach ($this->selectionSet as $selectionEntity) {
42 | $selectionEntity->accept(new ResolveSelectionVisitor($this->parentResult, $this->result));
43 | }
44 |
45 | return new TypeValue($type, $this->result, $this->parentResult);
46 | }
47 |
48 | #[\Override]
49 | public function visitInterface(InterfaceType $interface) : never
50 | {
51 | throw new \LogicException(); // @codeCoverageIgnore
52 | }
53 |
54 | #[\Override]
55 | public function visitUnion(UnionType $union) : never
56 | {
57 | throw new \LogicException(); // @codeCoverageIgnore
58 | }
59 |
60 | #[\Override]
61 | public function visitInput(InputType $input) : never
62 | {
63 | throw new \LogicException(); // @codeCoverageIgnore
64 | }
65 |
66 | #[\Override]
67 | public function visitScalar(ScalarType $scalar) : ResolvedValue
68 | {
69 | return $this->parentResult;
70 | }
71 |
72 | #[\Override]
73 | public function visitEnum(EnumType $enum) : ResolvedValue
74 | {
75 | return $this->parentResult;
76 | }
77 |
78 | #[\Override]
79 | public function visitNotNull(NotNullType $notNull) : ResolvedValue
80 | {
81 | return $notNull->getInnerType()->accept($this);
82 | }
83 |
84 | #[\Override]
85 | public function visitList(ListType $list) : ListResolvedValue
86 | {
87 | \assert($this->parentResult instanceof ListIntermediateValue);
88 |
89 | $result = [];
90 |
91 | foreach ($this->parentResult->getRawValue() as $rawValue) {
92 | $value = $list->getInnerType()->accept(new CreateResolvedValueVisitor($rawValue));
93 | $result[] = $value instanceof NullValue
94 | ? $value
95 | : $value->getType()->accept(new self($this->selectionSet, $value));
96 | }
97 |
98 | return new ListResolvedValue($list, $result);
99 | }
100 | }
101 |
--------------------------------------------------------------------------------