diff --git a/src/Type/Definition/PhpEnumType.php b/src/Type/Definition/PhpEnumType.php index 19e2d84d5..7ab207b78 100644 --- a/src/Type/Definition/PhpEnumType.php +++ b/src/Type/Definition/PhpEnumType.php @@ -3,10 +3,22 @@ namespace GraphQL\Type\Definition; use GraphQL\Error\SerializationError; +use GraphQL\Language\AST\EnumTypeDefinitionNode; +use GraphQL\Language\AST\EnumTypeExtensionNode; use GraphQL\Utils\PhpDoc; use GraphQL\Utils\Utils; -/** @phpstan-import-type PartialEnumValueConfig from EnumType */ +/** + * @phpstan-import-type PartialEnumValueConfig from EnumType + * + * @phpstan-type PhpEnumTypeConfig array{ + * name?: string|null, + * description?: string|null, + * enumClass: class-string<\UnitEnum>, + * astNode?: EnumTypeDefinitionNode|null, + * extensionASTNodes?: array|null + * } + */ class PhpEnumType extends EnumType { public const MULTIPLE_DESCRIPTIONS_DISALLOWED = 'Using more than 1 Description attribute is not supported.'; @@ -16,16 +28,15 @@ class PhpEnumType extends EnumType protected string $enumClass; /** - * @param class-string<\UnitEnum> $enum - * @param string|null $name The name the enum will have in the schema, defaults to the basename of the given class + * @phpstan-param PhpEnumTypeConfig $config * * @throws \Exception * @throws \ReflectionException */ - public function __construct(string $enum, ?string $name = null) + public function __construct(array $config) { - $this->enumClass = $enum; - $reflection = new \ReflectionEnum($enum); + $this->enumClass = $config['enumClass']; + $reflection = new \ReflectionEnum($this->enumClass); /** * @var array $enumDefinitions @@ -40,31 +51,33 @@ public function __construct(string $enum, ?string $name = null) } parent::__construct([ - 'name' => $name ?? $this->baseName($enum), + 'name' => $config['name'] ?? $this->baseName($this->enumClass), 'values' => $enumDefinitions, - 'description' => $this->extractDescription($reflection), + 'description' => $config['description'] ?? $this->extractDescription($reflection), + 'enumClass' => $this->enumClass, ]); } public function serialize($value): string { - if ($value instanceof $this->enumClass) { + $enumClass = $this->enumClass; + if ($value instanceof $enumClass) { return $value->name; } - if (is_a($this->enumClass, \BackedEnum::class, true)) { + if (is_a($enumClass, \BackedEnum::class, true)) { try { - $instance = $this->enumClass::from($value); + $instance = $enumClass::from($value); } catch (\ValueError|\TypeError $_) { $notEnumInstanceOrValue = Utils::printSafe($value); - throw new SerializationError("Cannot serialize value as enum: {$notEnumInstanceOrValue}, expected instance or valid value of {$this->enumClass}."); + throw new SerializationError("Cannot serialize value as enum: {$notEnumInstanceOrValue}, expected instance or valid value of {$enumClass}."); } return $instance->name; } $notEnum = Utils::printSafe($value); - throw new SerializationError("Cannot serialize value as enum: {$notEnum}, expected instance of {$this->enumClass}."); + throw new SerializationError("Cannot serialize value as enum: {$notEnum}, expected instance of {$enumClass}."); } public function parseValue($value) diff --git a/tests/Type/EnumTypeTest.php b/tests/Type/EnumTypeTest.php index 597f917f8..ebd22838e 100644 --- a/tests/Type/EnumTypeTest.php +++ b/tests/Type/EnumTypeTest.php @@ -8,7 +8,7 @@ use GraphQL\Language\Parser; use GraphQL\Language\SourceLocation; use GraphQL\Tests\Type\PhpEnumType\BackedPhpEnum; -use GraphQL\Tests\Type\PhpEnumType\PhpEnum; +use GraphQL\Tests\Type\PhpEnumType\MyCustomPhpEnum; use GraphQL\Tests\Type\TestClasses\OtherEnumType; use GraphQL\Type\Definition\EnumType; use GraphQL\Type\Definition\EnumValueDefinition; @@ -715,7 +715,7 @@ enum PhpEnum { $this->schema = BuildSchema::build($documentNode); $resolvers = [ - 'phpEnum' => fn (): PhpEnum => PhpEnum::B, + 'phpEnum' => fn (): MyCustomPhpEnum => MyCustomPhpEnum::B, ]; self::assertSame( diff --git a/tests/Type/PhpEnumType/PhpEnum.php b/tests/Type/PhpEnumType/MyCustomPhpEnum.php similarity index 93% rename from tests/Type/PhpEnumType/PhpEnum.php rename to tests/Type/PhpEnumType/MyCustomPhpEnum.php index dd3f4b4e6..99eb178f9 100644 --- a/tests/Type/PhpEnumType/PhpEnum.php +++ b/tests/Type/PhpEnumType/MyCustomPhpEnum.php @@ -6,7 +6,7 @@ use GraphQL\Type\Definition\Description; #[Description(description: 'foo')] -enum PhpEnum +enum MyCustomPhpEnum { #[Description(description: 'bar')] case A; diff --git a/tests/Type/PhpEnumTypeTest.php b/tests/Type/PhpEnumTypeTest.php index c49aa5f93..33b3478cf 100644 --- a/tests/Type/PhpEnumTypeTest.php +++ b/tests/Type/PhpEnumTypeTest.php @@ -11,7 +11,7 @@ use GraphQL\Tests\Type\PhpEnumType\MultipleDeprecationsPhpEnum; use GraphQL\Tests\Type\PhpEnumType\MultipleDescriptionsCasePhpEnum; use GraphQL\Tests\Type\PhpEnumType\MultipleDescriptionsPhpEnum; -use GraphQL\Tests\Type\PhpEnumType\PhpEnum; +use GraphQL\Tests\Type\PhpEnumType\MyCustomPhpEnum; use GraphQL\Type\Definition\ObjectType; use GraphQL\Type\Definition\PhpEnumType; use GraphQL\Type\Definition\ResolveInfo; @@ -32,10 +32,12 @@ protected function setUp(): void public function testConstructEnumTypeFromPhpEnum(): void { - $enumType = new PhpEnumType(PhpEnum::class); + $enumType = new PhpEnumType([ + 'enumClass' => MyCustomPhpEnum::class, + ]); self::assertSame(<<<'GRAPHQL' "foo" -enum PhpEnum { +enum MyCustomPhpEnum { "bar" A B @deprecated @@ -46,7 +48,7 @@ enum PhpEnum { public function testConstructEnumTypeFromIntPhpEnum(): void { - $enumType = new PhpEnumType(IntPhpEnum::class); + $enumType = new PhpEnumType(['enumClass' => IntPhpEnum::class]); self::assertSame(<<<'GRAPHQL' enum IntPhpEnum { A @@ -56,7 +58,10 @@ enum IntPhpEnum { public function testConstructEnumTypeFromPhpEnumWithCustomName(): void { - $enumType = new PhpEnumType(PhpEnum::class, 'CustomNamedPhpEnum'); + $enumType = new PhpEnumType([ + 'enumClass' => MyCustomPhpEnum::class, + 'name' => 'CustomNamedPhpEnum', + ]); self::assertSame(<<<'GRAPHQL' "foo" enum CustomNamedPhpEnum { @@ -70,7 +75,7 @@ enum CustomNamedPhpEnum { public function testConstructEnumTypeFromPhpEnumWithDocBlockDescriptions(): void { - $enumType = new PhpEnumType(DocBlockPhpEnum::class); + $enumType = new PhpEnumType(['enumClass' => DocBlockPhpEnum::class]); self::assertSame(<<<'GRAPHQL' "foo" enum DocBlockPhpEnum { @@ -89,24 +94,32 @@ enum DocBlockPhpEnum { public function testMultipleDescriptionsDisallowed(): void { self::expectExceptionObject(new \Exception(PhpEnumType::MULTIPLE_DESCRIPTIONS_DISALLOWED)); - new PhpEnumType(MultipleDescriptionsPhpEnum::class); + new PhpEnumType([ + 'enumClass' => MultipleDescriptionsPhpEnum::class, + ]); } public function testMultipleDescriptionsDisallowedOnCase(): void { self::expectExceptionObject(new \Exception(PhpEnumType::MULTIPLE_DESCRIPTIONS_DISALLOWED)); - new PhpEnumType(MultipleDescriptionsCasePhpEnum::class); + new PhpEnumType([ + 'enumClass' => MultipleDescriptionsCasePhpEnum::class, + ]); } public function testMultipleDeprecationsDisallowed(): void { self::expectExceptionObject(new \Exception(PhpEnumType::MULTIPLE_DEPRECATIONS_DISALLOWED)); - new PhpEnumType(MultipleDeprecationsPhpEnum::class); + new PhpEnumType([ + 'enumClass' => MultipleDeprecationsPhpEnum::class, + ]); } public function testExecutesWithEnumTypeFromPhpEnum(): void { - $enumType = new PhpEnumType(PhpEnum::class); + $enumType = new PhpEnumType([ + 'enumClass' => MyCustomPhpEnum::class, + ]); $schema = new Schema([ 'query' => new ObjectType([ 'name' => 'Query', @@ -118,9 +131,9 @@ public function testExecutesWithEnumTypeFromPhpEnum(): void 'type' => Type::nonNull($enumType), ], ], - 'resolve' => static function ($_, array $args): PhpEnum { + 'resolve' => static function ($_, array $args): MyCustomPhpEnum { $bar = $args['bar']; - assert($bar === PhpEnum::A); + assert($bar === MyCustomPhpEnum::A); return $bar; }, @@ -138,7 +151,9 @@ public function testExecutesWithEnumTypeFromPhpEnum(): void public function testSerializesBackedEnumsByValue(): void { - $enumType = new PhpEnumType(IntPhpEnum::class); + $enumType = new PhpEnumType([ + 'enumClass' => IntPhpEnum::class, + ]); $schema = new Schema([ 'query' => new ObjectType([ 'name' => 'Query', @@ -160,7 +175,7 @@ public function testSerializesBackedEnumsByValue(): void public function testAcceptsEnumFromVariableValues(): void { - $enumType = new PhpEnumType(PhpEnum::class); + $enumType = new PhpEnumType(['enumClass' => MyCustomPhpEnum::class]); $schema = null; $schema = new Schema([ @@ -174,16 +189,16 @@ public function testAcceptsEnumFromVariableValues(): void 'type' => Type::nonNull($enumType), ], ], - 'resolve' => static function (bool $executeAgain, array $args, $context, ResolveInfo $resolveInfo) use (&$schema): PhpEnum { + 'resolve' => static function (bool $executeAgain, array $args, $context, ResolveInfo $resolveInfo) use (&$schema): MyCustomPhpEnum { $bar = $args['bar']; - assert($bar === PhpEnum::A); + assert($bar === MyCustomPhpEnum::A); assert($schema instanceof Schema); if ($executeAgain) { $executionResult = GraphQL::executeQuery( $schema, - 'query ($bar: PhpEnum!) { foo(bar: $bar) }', + 'query ($bar: MyCustomPhpEnum!) { foo(bar: $bar) }', false, null, $resolveInfo->variableValues @@ -204,7 +219,7 @@ public function testAcceptsEnumFromVariableValues(): void $executionResult = GraphQL::executeQuery( $schema, - 'query ($bar: PhpEnum!) { foo(bar: $bar) }', + 'query ($bar: MyCustomPhpEnum!) { foo(bar: $bar) }', true, null, ['bar' => 'A'] @@ -218,7 +233,7 @@ public function testAcceptsEnumFromVariableValues(): void public function testFailsToSerializeNonEnum(): void { - $enumType = new PhpEnumType(PhpEnum::class); + $enumType = new PhpEnumType(['enumClass' => MyCustomPhpEnum::class]); $schema = new Schema([ 'query' => new ObjectType([ 'name' => 'Query', @@ -233,13 +248,13 @@ public function testFailsToSerializeNonEnum(): void $result = GraphQL::executeQuery($schema, '{ foo }'); - self::expectExceptionObject(new SerializationError('Cannot serialize value as enum: "A", expected instance of GraphQL\\Tests\\Type\\PhpEnumType\\PhpEnum.')); + self::expectExceptionObject(new SerializationError('Cannot serialize value as enum: "A", expected instance of GraphQL\\Tests\\Type\\PhpEnumType\\MyCustomPhpEnum.')); $result->toArray(DebugFlag::RETHROW_INTERNAL_EXCEPTIONS); } public function testFailsToSerializeNonEnumValue(): void { - $enumType = new PhpEnumType(IntPhpEnum::class); + $enumType = new PhpEnumType(['enumClass' => IntPhpEnum::class]); $schema = new Schema([ 'query' => new ObjectType([ 'name' => 'Query',