From 490c7a4515833f9fa345b1609287b865dfb72e76 Mon Sep 17 00:00:00 2001 From: Andrii Dembitskyi Date: Fri, 28 Nov 2025 11:09:32 +0100 Subject: [PATCH 1/8] :sparkles: Allow Symfony 8 --- composer.json | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/composer.json b/composer.json index 59b6100..ade7829 100644 --- a/composer.json +++ b/composer.json @@ -20,22 +20,22 @@ "ext-json": "*", "thecodingmachine/graphqlite" : "^8", "thecodingmachine/graphqlite-symfony-validator-bridge": "^7.1.1", - "symfony/config": "^6.4 || ^7", - "symfony/console": "^6.4 || ^7", - "symfony/framework-bundle": "^6.4 || ^7", - "symfony/validator": "^6.4 || ^7", - "symfony/translation": "^6.4 || ^7", - "symfony/psr-http-message-bridge": "^2.0 || ^7.0", + "symfony/config": "^6.4 || ^7.0 || ^8.0", + "symfony/console": "^6.4 || ^7.0 || ^8.0", + "symfony/framework-bundle": "^6.4 || ^7.0 || ^8.0", + "symfony/validator": "^6.4 || ^7.0 || ^8.0", + "symfony/translation": "^6.4 || ^7.0 || ^8.0", + "symfony/psr-http-message-bridge": "^2.0 || ^7.0 || ^8.0", "nyholm/psr7": "^1.1", "laminas/laminas-diactoros": "^2.2.2 || ^3", "overblog/graphiql-bundle": "^0.2 || ^0.3 || ^1", "thecodingmachine/cache-utils": "^1" }, "require-dev": { - "symfony/security-bundle": "^6.4 || ^7", - "symfony/yaml": "^6.4 || ^7", + "symfony/security-bundle": "^6.4 || ^7.0 || ^8.0", + "symfony/yaml": "^6.4 || ^7.0 || ^8.0", "beberlei/porpaginas": "^1.2 || ^2.0", - "symfony/phpunit-bridge": "^6.4 || ^7", + "symfony/phpunit-bridge": "^6.4 || ^7 || ^8.0", "phpstan/phpstan": "^2", "phpstan/phpstan-symfony": "^2.0", "composer/package-versions-deprecated": "^1.8", From 0a4e0036a5b142e69c8affa056b5fb988c63b1a8 Mon Sep 17 00:00:00 2001 From: Andrii Dembitskyi Date: Fri, 28 Nov 2025 12:00:42 +0100 Subject: [PATCH 2/8] :package: Avoid PHP 8.4 deprecations about implicit nullable --- tests/Fixtures/Entities/Contact.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/Fixtures/Entities/Contact.php b/tests/Fixtures/Entities/Contact.php index fc90f60..2677537 100644 --- a/tests/Fixtures/Entities/Contact.php +++ b/tests/Fixtures/Entities/Contact.php @@ -30,11 +30,11 @@ public function getName(): string #[Field] public function injectService( #[Autowire] - TestGraphqlController $testService = null, + ?TestGraphqlController $testService = null, #[Autowire(identifier: 'someService')] - stdClass $someService = null, + ?stdClass $someService = null, #[Autowire(identifier: 'someAlias')] - stdClass $someAlias = null, + ?stdClass $someAlias = null, ): string { if (!$testService instanceof TestGraphqlController || $someService === null || $someAlias === null) { return 'KO'; @@ -52,7 +52,7 @@ public function injectServicePrefetch($prefetchData): string public function prefetchData( iterable $iterable, #[Autowire(identifier: 'someOtherService')] - stdClass $someOtherService = null, + ?stdClass $someOtherService = null, ) { if ($someOtherService === null) { return 'KO'; From 8b2efa4700c72f40409af164e8835c5ef013bfb0 Mon Sep 17 00:00:00 2001 From: Andrii Dembitskyi Date: Fri, 28 Nov 2025 12:12:09 +0100 Subject: [PATCH 3/8] :package: Make tests a bit more restrictive and safe --- tests/FunctionalTest.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/FunctionalTest.php b/tests/FunctionalTest.php index 07848bc..b5ecc32 100644 --- a/tests/FunctionalTest.php +++ b/tests/FunctionalTest.php @@ -374,6 +374,7 @@ public function testNoLoginNoSessionQuery(): void $result = json_decode($response->getContent(), true); + $this->assertIsArray($result); $this->assertArrayHasKey('errors', $result); $this->assertSame('Cannot query field "login" on type "Mutation".', $result['errors'][0]['message']); } @@ -508,8 +509,11 @@ public function testWithIntrospection(): void $response = $kernel->handle($request); $result = json_decode($response->getContent(), true); + $this->assertIsArray($result); + $this->assertArrayHasKey('data', $result); $data = $result['data']; + $this->assertIsArray($result); $this->assertArrayHasKey('__schema', $data); } From a4150f11c058e99a89dafa2d13a6e606fa8613f9 Mon Sep 17 00:00:00 2001 From: Andrii Dembitskyi Date: Fri, 28 Nov 2025 12:13:49 +0100 Subject: [PATCH 4/8] :package: Migrate DI configuration to the PHP intead of XML XML is deprecated by the Symfony for 7.4+ --- .../GraphQLiteExtension.php | 6 +- src/Resources/config/container/graphqlite.php | 135 ++++++++++++++++++ src/Resources/config/container/graphqlite.xml | 132 ----------------- 3 files changed, 138 insertions(+), 135 deletions(-) create mode 100644 src/Resources/config/container/graphqlite.php delete mode 100644 src/Resources/config/container/graphqlite.xml diff --git a/src/DependencyInjection/GraphQLiteExtension.php b/src/DependencyInjection/GraphQLiteExtension.php index 7c20e39..82dd202 100644 --- a/src/DependencyInjection/GraphQLiteExtension.php +++ b/src/DependencyInjection/GraphQLiteExtension.php @@ -10,7 +10,7 @@ use Symfony\Component\Config\FileLocator; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Extension\Extension; -use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; +use Symfony\Component\DependencyInjection\Loader\PhpFileLoader; use TheCodingMachine\GraphQLite\Mappers\Root\RootTypeMapperFactoryInterface; use function array_map; use function rtrim; @@ -34,7 +34,7 @@ public function load(array $configs, ContainerBuilder $container): void $configuration = new Configuration(); $config = $this->processConfiguration($configuration, $configs); - $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config/container')); + $loader = new PhpFileLoader($container, new FileLocator(__DIR__.'/../Resources/config/container')); if (!isset($config['namespace'])) { $config['namespace'] = []; @@ -92,7 +92,7 @@ function($namespace): string { $container->setParameter('graphqlite.security.maximum_query_depth', $config['security']['maximum_query_depth'] ?? null); $container->setParameter('graphqlite.security.firewall_name', $config['security']['firewall_name'] ?? 'main'); - $loader->load('graphqlite.xml'); + $loader->load('graphqlite.php'); $definition = $container->getDefinition(ServerConfig::class); if (isset($config['debug']) && \is_array($config['debug'])) { diff --git a/src/Resources/config/container/graphqlite.php b/src/Resources/config/container/graphqlite.php new file mode 100644 index 0000000..0797a34 --- /dev/null +++ b/src/Resources/config/container/graphqlite.php @@ -0,0 +1,135 @@ +services(); + + $services->defaults() + ->private() + ->autowire() + ->autoconfigure(); + + $services->set(\TheCodingMachine\GraphQLite\SchemaFactory::class) + ->args( + [ + service('graphqlite.psr16cache'), + service('service_container'), + ], + ) + ->call( + 'setAuthenticationService', + [service(\TheCodingMachine\GraphQLite\Security\AuthenticationServiceInterface::class)], + ) + ->call( + 'setAuthorizationService', + [service(\TheCodingMachine\GraphQLite\Security\AuthorizationServiceInterface::class)], + ); + + $services->set(\TheCodingMachine\GraphQLite\Schema::class) + ->public() + ->factory([service(\TheCodingMachine\GraphQLite\SchemaFactory::class), 'createSchema']); + + $services->set(\TheCodingMachine\GraphQLite\AggregateControllerQueryProviderFactory::class) + ->args( + [ + [], + service_locator([]), + ], + ) + ->tag('graphql.queryprovider_factory'); + + $services->alias(\GraphQL\Type\Schema::class, \TheCodingMachine\GraphQLite\Schema::class); + + $services->set(\TheCodingMachine\GraphQLite\AnnotationReader::class); + + $services->set(\TheCodingMachine\GraphQLite\Bundle\Security\AuthenticationService::class) + ->args([service('security.token_storage')->nullOnInvalid()]); + + $services->alias( + \TheCodingMachine\GraphQLite\Security\AuthenticationServiceInterface::class, + \TheCodingMachine\GraphQLite\Bundle\Security\AuthenticationService::class, + ); + + $services->set(\TheCodingMachine\GraphQLite\Bundle\Security\AuthorizationService::class) + ->args([service('security.authorization_checker')->nullOnInvalid()]); + + $services->alias( + \TheCodingMachine\GraphQLite\Security\AuthorizationServiceInterface::class, + \TheCodingMachine\GraphQLite\Bundle\Security\AuthorizationService::class, + ); + + $services->set(\GraphQL\Server\ServerConfig::class, \TheCodingMachine\GraphQLite\Bundle\Server\ServerConfig::class) + ->call('setSchema', [service(\TheCodingMachine\GraphQLite\Schema::class)]) + ->call( + 'setErrorFormatter', + [ + [ + \TheCodingMachine\GraphQLite\Exceptions\WebonyxErrorHandler::class, + 'errorFormatter', + ], + ], + ) + ->call( + 'setErrorsHandler', + [ + [ + \TheCodingMachine\GraphQLite\Exceptions\WebonyxErrorHandler::class, + 'errorHandler', + ], + ], + ); + + $services->set(\GraphQL\Validator\Rules\DisableIntrospection::class) + ->args(['$enabled' => '%graphqlite.security.disableIntrospection%']); + + $services->set(\GraphQL\Validator\Rules\QueryComplexity::class); + + $services->set(\GraphQL\Validator\Rules\QueryDepth::class); + + $services->set(\TheCodingMachine\GraphQLite\Bundle\Controller\GraphQLiteController::class) + ->public() + ->tag('routing.route_loader'); + + $services->set(\TheCodingMachine\GraphQLite\Bundle\Mappers\RequestParameterMiddleware::class) + ->tag('graphql.parameter_middleware'); + + $services->set(\TheCodingMachine\GraphQLite\Validator\Mappers\Parameters\AssertParameterMiddleware::class) + ->args([service('validator.validator_factory')]) + ->tag('graphql.parameter_middleware'); + + $services->set(\TheCodingMachine\GraphQLite\Bundle\Controller\GraphQL\LoginController::class) + ->public() + ->args(['$firewallName' => '%graphqlite.security.firewall_name%']); + + $services->set(\TheCodingMachine\GraphQLite\Bundle\Controller\GraphQL\MeController::class) + ->public(); + + $services->set(\TheCodingMachine\GraphQLite\Bundle\Types\SymfonyUserInterfaceType::class) + ->public(); + + $services->set(\TheCodingMachine\GraphQLite\Mappers\StaticClassListTypeMapperFactory::class) + ->args([[]]) + ->tag('graphql.type_mapper_factory'); + + $services->set('graphqlite.phpfilescache', \Symfony\Component\Cache\Adapter\PhpFilesAdapter::class) + ->args( + [ + 'graphqlite', + 0, + '%kernel.cache_dir%', + ], + ); + + $services->set('graphqlite.apcucache', \Symfony\Component\Cache\Adapter\ApcuAdapter::class) + ->args(['graphqlite']); + + $services->set('graphqlite.psr16cache', \Symfony\Component\Cache\Psr16Cache::class) + ->args([service('graphqlite.cache')]); + + $services->set('graphqlite.cacheclearer', \Symfony\Component\HttpKernel\CacheClearer\Psr6CacheClearer::class) + ->args([[service('graphqlite.cache')]]) + ->tag('kernel.cache_clearer'); + + $services->set(\TheCodingMachine\GraphQLite\Bundle\Command\DumpSchemaCommand::class); +}; diff --git a/src/Resources/config/container/graphqlite.xml b/src/Resources/config/container/graphqlite.xml deleted file mode 100644 index 984caa7..0000000 --- a/src/Resources/config/container/graphqlite.xml +++ /dev/null @@ -1,132 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \TheCodingMachine\GraphQLite\Exceptions\WebonyxErrorHandler - errorFormatter - - - - - \TheCodingMachine\GraphQLite\Exceptions\WebonyxErrorHandler - errorHandler - - - - - - %graphqlite.security.disableIntrospection% - - - - - - - - - - - - - - - - - - - - - %graphqlite.security.firewall_name% - - - - - - - - - - - - - - graphqlite - 0 - %kernel.cache_dir% - - - - graphqlite - - - - - - - - - - - - - - - - From 2a2ab1a063dad19708db9cd7ff001fa9b63e9a35 Mon Sep 17 00:00:00 2001 From: Andrii Dembitskyi Date: Fri, 28 Nov 2025 12:28:23 +0100 Subject: [PATCH 5/8] :package: Adjust phpstan suppress according to changed error messages in phpstan --- phpstan.neon | 8 ++++++-- src/DependencyInjection/Configuration.php | 2 ++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/phpstan.neon b/phpstan.neon index 53427c0..544bd75 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -23,6 +23,10 @@ parameters: reportUnmatchedIgnoredErrors: false ignoreErrors: # Wrong return type hint in Symfony's TreeBuilder - - '#Call to an undefined method Symfony\\Component\\Config\\Definition\\Builder\\NodeDefinition::\w+\(\).#' + - message: '#Call to method arrayNode\(\) on an unknown class#' + path: src/DependencyInjection/Configuration.php + - message: '#Cannot call method \w+\(\) on mixed#' + path: src/DependencyInjection/Configuration.php # PhpStan doesn't know bundle resolved config structure and it's pretty complex to describe it - - '#Parameter \#2 \$value of method Symfony\\Component\\DependencyInjection\\Container::setParameter\(\) expects array\|bool\|float\|int\|string\|UnitEnum\|null, mixed given\.#' + - message: '#Parameter \#2 \$value of method Symfony\\Component\\DependencyInjection\\Container::setParameter\(\) expects array\|bool\|float\|int\|string\|UnitEnum\|null, mixed given\.#' + path: src/DependencyInjection/GraphQLiteExtension.php diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php index 1991328..e331543 100644 --- a/src/DependencyInjection/Configuration.php +++ b/src/DependencyInjection/Configuration.php @@ -8,8 +8,10 @@ class Configuration implements ConfigurationInterface { + /** @return TreeBuilder<'array'> */ public function getConfigTreeBuilder(): TreeBuilder { + /** @var TreeBuilder<'array'> $treeBuilder */ $treeBuilder = new TreeBuilder('graphqlite'); $rootNode = $treeBuilder->getRootNode(); From f6e464040f84c859c7f282ad5a1ea87952c67de1 Mon Sep 17 00:00:00 2001 From: Andrii Dembitskyi Date: Fri, 28 Nov 2025 12:43:19 +0100 Subject: [PATCH 6/8] :book: Add link to the bundle docs --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index a39a2d5..06044e4 100644 --- a/README.md +++ b/README.md @@ -6,4 +6,6 @@ Symfony bundle for the `thecodingmachine/graphqlite` package. +Bundle docs: https://graphqlite.thecodingmachine.io/docs/symfony-bundle + See [thecodingmachine/graphqlite](https://github.com/thecodingmachine/graphqlite). From f3103030b4a58c91778989d20e52f452e75964c9 Mon Sep 17 00:00:00 2001 From: Andrii Dembitskyi Date: Fri, 28 Nov 2025 12:48:38 +0100 Subject: [PATCH 7/8] :package: Migrate routes configuration to the PHP intead of XML XML is deprecated by the Symfony for 7.4+ --- config-template/routes/graphqlite.yaml | 2 +- src/Resources/config/routes.php | 11 +++++++++++ tests/GraphQLiteTestingKernel.php | 2 +- 3 files changed, 13 insertions(+), 2 deletions(-) create mode 100644 src/Resources/config/routes.php diff --git a/config-template/routes/graphqlite.yaml b/config-template/routes/graphqlite.yaml index b366d2d..fa9b36a 100644 --- a/config-template/routes/graphqlite.yaml +++ b/config-template/routes/graphqlite.yaml @@ -1,2 +1,2 @@ graphqlite_bundle: - resource: '@GraphQLiteBundle/Resources/config/routes.xml' + resource: '@GraphQLiteBundle/Resources/config/routes.php' diff --git a/src/Resources/config/routes.php b/src/Resources/config/routes.php new file mode 100644 index 0000000..8f4a722 --- /dev/null +++ b/src/Resources/config/routes.php @@ -0,0 +1,11 @@ +import( + \sprintf('%s::loadRoutes', GraphQLiteController::class), + 'service', + ); +}; diff --git a/tests/GraphQLiteTestingKernel.php b/tests/GraphQLiteTestingKernel.php index c92ad95..8cb9093 100644 --- a/tests/GraphQLiteTestingKernel.php +++ b/tests/GraphQLiteTestingKernel.php @@ -199,7 +199,7 @@ public function configureContainer(ContainerBuilder $c, LoaderInterface $loader) // Note: typing is disabled because using different classes in Symfony 4 and 5 protected function configureRoutes(/*RoutingConfigurator*/ $routes) { - $routes->import(__DIR__.'/../src/Resources/config/routes.xml'); + $routes->import(__DIR__.'/../src/Resources/config/routes.php'); } public function getCacheDir(): string From f5a70a87563b9303d36d6e48e696f6f40f6aab29 Mon Sep 17 00:00:00 2001 From: Andrii Dembitskyi Date: Fri, 28 Nov 2025 12:50:06 +0100 Subject: [PATCH 8/8] :package: Add parameter type - support for Symfony 4 was dropped before --- tests/GraphQLiteTestingKernel.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/GraphQLiteTestingKernel.php b/tests/GraphQLiteTestingKernel.php index 8cb9093..16e358c 100644 --- a/tests/GraphQLiteTestingKernel.php +++ b/tests/GraphQLiteTestingKernel.php @@ -13,6 +13,7 @@ use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\HttpKernel\Kernel; +use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator; use TheCodingMachine\GraphQLite\Bundle\GraphQLiteBundle; use Symfony\Component\Security\Core\User\InMemoryUser; use function class_exists; @@ -196,8 +197,7 @@ public function configureContainer(ContainerBuilder $c, LoaderInterface $loader) $loader->load($confDir.'/{services}_'.$this->environment.self::CONFIG_EXTS, 'glob'); } - // Note: typing is disabled because using different classes in Symfony 4 and 5 - protected function configureRoutes(/*RoutingConfigurator*/ $routes) + protected function configureRoutes(RoutingConfigurator $routes): void { $routes->import(__DIR__.'/../src/Resources/config/routes.php'); }