From d3305d48a2c385fa9114ad367aa6b7240428ac9b Mon Sep 17 00:00:00 2001 From: Michael Georgiadis <38563912+michael-georgiadis@users.noreply.github.com> Date: Tue, 7 Apr 2026 14:31:00 +0200 Subject: [PATCH] Make sure that types are frozen before creating the schema --- src/SchemaFactory.php | 2 + src/TypeRegistry.php | 11 ++++++ tests/TypeRegistryTest.php | 77 +++++++++++++++++++++++++++++++++----- 3 files changed, 81 insertions(+), 9 deletions(-) diff --git a/src/SchemaFactory.php b/src/SchemaFactory.php index 037910f281..686f0d7965 100644 --- a/src/SchemaFactory.php +++ b/src/SchemaFactory.php @@ -538,6 +538,8 @@ classBoundCache: $classBoundCache, $aggregateQueryProvider = new AggregateQueryProvider($queryProviders); + $typeRegistry->finalizeTypes(); + return new Schema($aggregateQueryProvider, $recursiveTypeMapper, $typeResolver, $topRootTypeMapper, $this->schemaConfig); } diff --git a/src/TypeRegistry.php b/src/TypeRegistry.php index 1e538a7d39..b01d33bcba 100644 --- a/src/TypeRegistry.php +++ b/src/TypeRegistry.php @@ -97,4 +97,15 @@ public function getMutableInterface(string $typeName): MutableInterface return $type; } + + public function finalizeTypes(): void + { + foreach ($this->types as $type) { + if (! ($type instanceof MutableObjectType) && ! ($type instanceof MutableInterfaceType)) { + continue; + } + + $type->freeze(); + } + } } diff --git a/tests/TypeRegistryTest.php b/tests/TypeRegistryTest.php index c72a64b248..44d8ff344e 100644 --- a/tests/TypeRegistryTest.php +++ b/tests/TypeRegistryTest.php @@ -1,19 +1,24 @@ 'Foo', - 'fields' => function() {return [];} + 'fields' => static function () { + return []; + }, ]); $registry = new TypeRegistry(); @@ -27,7 +32,9 @@ public function testGetType(): void { $type = new ObjectType([ 'name' => 'Foo', - 'fields' => function() {return [];} + 'fields' => static function () { + return []; + }, ]); $registry = new TypeRegistry(); @@ -43,7 +50,9 @@ public function testHasType(): void { $type = new ObjectType([ 'name' => 'Foo', - 'fields' => function() {return [];} + 'fields' => static function () { + return []; + }, ]); $registry = new TypeRegistry(); @@ -51,18 +60,21 @@ public function testHasType(): void $this->assertTrue($registry->hasType('Foo')); $this->assertFalse($registry->hasType('Bar')); - } public function testGetMutableObjectType(): void { $type = new MutableObjectType([ 'name' => 'Foo', - 'fields' => function() {return [];} + 'fields' => static function () { + return []; + }, ]); $type2 = new ObjectType([ 'name' => 'FooBar', - 'fields' => function() {return [];} + 'fields' => static function () { + return []; + }, ]); $registry = new TypeRegistry(); @@ -79,11 +91,15 @@ public function testGetMutableInterface(): void { $type = new MutableObjectType([ 'name' => 'Foo', - 'fields' => function() {return [];} + 'fields' => static function () { + return []; + }, ]); $type2 = new ObjectType([ 'name' => 'FooBar', - 'fields' => function() {return [];} + 'fields' => static function () { + return []; + }, ]); $registry = new TypeRegistry(); @@ -95,4 +111,47 @@ public function testGetMutableInterface(): void $this->expectException(GraphQLRuntimeException::class); $this->assertSame($type, $registry->getMutableInterface('FooBar')); } + + public function testFinalizeTypesFreezesMutableTypesOnly(): void + { + $mutableObjectType = $this->getMockBuilder(MutableObjectType::class) + ->setConstructorArgs([ + [ + 'name' => 'Foo', + 'fields' => static function () { + return []; + }, + ], + ]) + ->onlyMethods(['freeze']) + ->getMock(); + + $mutableObjectType->expects($this->once()) + ->method('freeze'); + + $mutableInterfaceType = $this->getMockBuilder(MutableInterfaceType::class) + ->setConstructorArgs([ + [ + 'name' => 'Bar', + 'fields' => static fn () => [], + ], + ]) + ->onlyMethods(['freeze']) + ->getMock(); + + $mutableInterfaceType->expects($this->once()) + ->method('freeze'); + + $regularObjectType = new ObjectType([ + 'name' => 'Baz', + 'fields' => static fn () => [], + ]); + + $registry = new TypeRegistry(); + $registry->registerType($mutableObjectType); + $registry->registerType($mutableInterfaceType); + $registry->registerType($regularObjectType); + + $registry->finalizeTypes(); + } }