diff --git a/src/SchemaFactory.php b/src/SchemaFactory.php index 037910f28..686f0d796 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 1e538a7d3..b01d33bcb 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 c72a64b24..44d8ff344 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(); + } }