From b54ea43d9d4882cd3322dbbf239d7d74f3bfefb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Gamez?= Date: Sat, 14 Feb 2026 23:43:26 +0100 Subject: [PATCH 1/7] Support Firebase SDK 8.x and PHP 8.3+ --- .github/workflows/tests.yml | 4 +- CHANGELOG.md | 5 +++ README.md | 9 ---- composer.json | 4 +- src/DependencyInjection/Configuration.php | 14 ------ .../Factory/ProjectFactory.php | 28 ------------ src/DependencyInjection/FirebaseExtension.php | 9 ---- .../FirebaseExtensionTest.php | 43 ------------------- 8 files changed, 8 insertions(+), 108 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index a32a2b8..0d0a667 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -40,8 +40,6 @@ jobs: fail-fast: false matrix: php-version: - - "8.1" - - "8.2" - "8.3" - "8.4" - "8.5" @@ -53,7 +51,7 @@ jobs: - "" include: # Test with the lowest set of dependencies - - php-version: "8.1" + - php-version: "8.3" dependencies: "lowest" stability: "stable" diff --git a/CHANGELOG.md b/CHANGELOG.md index 6305bb8..e04a1b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ ## [Unreleased] +* Dropped support for PHP versions older than 8.3 to match `kreait/firebase-php` 8.x +* Updated the bundle to support `kreait/firebase-php` 8.x +* Removed Dynamic Links support because it was removed from the Firebase Admin SDK +* Removed HTTP request logger configuration options because the underlying SDK hooks were removed + ## [5.7.0] * Added support for PHP 8.5 and Symfony 8 diff --git a/README.md b/README.md index b918edb..5f59a12 100644 --- a/README.md +++ b/README.md @@ -78,7 +78,6 @@ The following services will be available for your project: * `kreait_firebase.my_project.messaging` * `kreait_firebase.my_project.remote_config` * `kreait_firebase.my_project.storage` -* `kreait_firebase.my_project.dynamic_links` * `kreait_firebase.other_project.*` The following classes will be available for dependency injection if you have configured only one project: @@ -90,7 +89,6 @@ The following classes will be available for dependency injection if you have con * `Kreait\Firebase\Contract\Messaging` * `Kreait\Firebase\Contract\RemoteConfig` * `Kreait\Firebase\Contract\Storage` -* `Kreait\Firebase\Contract\DynamicLinks` To make it easier to use classes via dependency injection in the constructor of a class when multiple projects exist, you can do this in the constructor: @@ -101,7 +99,6 @@ To make it easier to use classes via dependency injection in the constructor of * `Kreait\Firebase\Contract\Messaging $myProjectMessaging` * `Kreait\Firebase\Contract\RemoteConfig $myProjectRemoteConfig` * `Kreait\Firebase\Contract\Storage $myProjectStorage` -* `Kreait\Firebase\Contract\DynamicLinks $myProjectDynamicLinks` ### Full @@ -133,16 +130,10 @@ kreait_firebase: database_uri: 'https://my_project.firebaseio.com' # Optional: Make the client tenant aware tenant_id: 'tenant-id' - # Optional: Default domain for Dynamic Links - default_dynamic_links_domain: 'https://my_project.page.link' # Optional: Used to cache Google's public keys. verifier_cache: null # Example: cache.app # Optional: Used to cache the authentication tokens for connecting to the Firebase servers. auth_token_cache: null # Example: cache.app - # If set, logs simple HTTP request and response statuses - http_request_logger: null # Example: monolog.logger.firebase - # If set, logs detailed HTTP request and response statuses - http_request_debug_logger: null # Example: monolog.logger.firebase_debug ``` ## Documentation diff --git a/composer.json b/composer.json index 0013eef..e9600d2 100644 --- a/composer.json +++ b/composer.json @@ -12,8 +12,8 @@ } ], "require": { - "php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0", - "kreait/firebase-php": "^7.24", + "php": "~8.3.0 || ~8.4.0 || ~8.5.0", + "kreait/firebase-php": "^8.1", "psr/simple-cache": "^1.0 || ^2.0 || ^3.0", "symfony/cache": "^6.4 || ^7.4 || ^8.0", "symfony/config": "^6.4 || ^7.4 || ^8.0", diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php index 50a390a..5308939 100644 --- a/src/DependencyInjection/Configuration.php +++ b/src/DependencyInjection/Configuration.php @@ -55,10 +55,6 @@ public function getConfigTreeBuilder(): TreeBuilder ->defaultNull() ->info('Make the client tenant aware') ->end() - ->scalarNode('default_dynamic_links_domain') - ->example('https://my-project.page.link') - ->info('The default domain for dynamic links') - ->end() ->scalarNode('verifier_cache') ->defaultNull() ->example('cache.app') @@ -69,16 +65,6 @@ public function getConfigTreeBuilder(): TreeBuilder ->example('cache.app') ->info('Used to cache the authentication tokens for connecting to the Firebase servers.') ->end() - ->scalarNode('http_request_logger') - ->defaultNull() - ->example('monolog.logger.firebase') - ->info('If set, logs simple HTTP request and response statuses') - ->end() - ->scalarNode('http_request_debug_logger') - ->defaultNull() - ->example('monolog.logger.firebase_debug') - ->info('If set, logs detailed HTTP request and response statuses') - ->end() ->end() ->end() ->end() diff --git a/src/DependencyInjection/Factory/ProjectFactory.php b/src/DependencyInjection/Factory/ProjectFactory.php index b665eb7..682d3e6 100644 --- a/src/DependencyInjection/Factory/ProjectFactory.php +++ b/src/DependencyInjection/Factory/ProjectFactory.php @@ -7,7 +7,6 @@ use Kreait\Firebase; use Kreait\Firebase\Factory; use Psr\Cache\CacheItemPoolInterface; -use Psr\Log\LoggerInterface; use Psr\SimpleCache\CacheInterface; use Symfony\Component\Cache\Adapter\Psr16Adapter; @@ -15,8 +14,6 @@ class ProjectFactory { private ?CacheItemPoolInterface $verifierCache = null; private ?CacheItemPoolInterface $authTokenCache = null; - private ?LoggerInterface $httpRequestLogger = null; - private ?LoggerInterface $httpRequestDebugLogger = null; /** * @param CacheInterface|CacheItemPoolInterface $verifierCache @@ -42,16 +39,6 @@ public function setAuthTokenCache($authTokenCache = null): void $this->authTokenCache = $authTokenCache; } - public function setHttpRequestLogger(?LoggerInterface $logger = null): void - { - $this->httpRequestLogger = $logger; - } - - public function setHttpRequestDebugLogger(?LoggerInterface $logger = null): void - { - $this->httpRequestDebugLogger = $logger; - } - public function createAuth(array $config = []): Firebase\Contract\Auth { return $this->createFactory($config)->createAuth(); @@ -85,14 +72,6 @@ public function createFactory(array $config = []): Factory $factory = $factory->withAuthTokenCache($this->authTokenCache); } - if ($this->httpRequestLogger) { - $factory = $factory->withHttpLogger($this->httpRequestLogger); - } - - if ($this->httpRequestDebugLogger) { - $factory = $factory->withHttpDebugLogger($this->httpRequestDebugLogger); - } - return $factory; } @@ -121,13 +100,6 @@ public function createStorage(array $config = []): Firebase\Contract\Storage return $this->createFactory($config)->createStorage(); } - public function createDynamicLinksService(array $config = []): Firebase\Contract\DynamicLinks - { - $defaultDynamicLinksDomain = $config['default_dynamic_links_domain'] ?? null; - - return $this->createFactory($config)->createDynamicLinksService($defaultDynamicLinksDomain); - } - public function createAppCheck(array $config = []): Firebase\Contract\AppCheck { return $this->createFactory($config)->createAppCheck(); diff --git a/src/DependencyInjection/FirebaseExtension.php b/src/DependencyInjection/FirebaseExtension.php index 1df645f..a899fda 100644 --- a/src/DependencyInjection/FirebaseExtension.php +++ b/src/DependencyInjection/FirebaseExtension.php @@ -45,7 +45,6 @@ private function processProjectConfiguration(string $name, array $config, Contai $this->registerService($name, 'remote_config', $config, Firebase\Contract\RemoteConfig::class, $container, 'createRemoteConfig'); $this->registerService($name, 'messaging', $config, Firebase\Contract\Messaging::class, $container, 'createMessaging'); $this->registerService($name, 'firestore', $config, Firebase\Contract\Firestore::class, $container, 'createFirestore'); - $this->registerService($name, 'dynamic_links', $config, Firebase\Contract\DynamicLinks::class, $container, 'createDynamicLinksService'); $this->registerService($name, 'app_check', $config, Firebase\Contract\AppCheck::class, $container, 'createAppCheck'); } @@ -74,14 +73,6 @@ private function registerService(string $name, string $postfix, array $config, s $factory->addMethodCall('setAuthTokenCache', [new Reference($config['auth_token_cache'])]); } - if ($config['http_request_logger'] ?? null) { - $factory->addMethodCall('setHttpRequestLogger', [new Reference($config['http_request_logger'])]); - } - - if ($config['http_request_debug_logger'] ?? null) { - $factory->addMethodCall('setHttpRequestDebugLogger', [new Reference($config['http_request_debug_logger'])]); - } - $container->register($projectServiceId, $contract) ->setFactory([$factory, $method]) ->setLazy(true) diff --git a/tests/DependencyInjection/FirebaseExtensionTest.php b/tests/DependencyInjection/FirebaseExtensionTest.php index 3183fff..dc07b15 100644 --- a/tests/DependencyInjection/FirebaseExtensionTest.php +++ b/tests/DependencyInjection/FirebaseExtensionTest.php @@ -8,7 +8,6 @@ use Kreait\Firebase\Symfony\Bundle\DependencyInjection\FirebaseExtension; use PHPUnit\Framework\TestCase; use Psr\Cache\CacheItemPoolInterface; -use Psr\Log\LoggerInterface; use ReflectionException; use stdClass; use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; @@ -61,10 +60,6 @@ public function test_a_project_is_created_with_a_service_for_each_feature(): voi $this->assertInstanceOf(Firebase\Contract\Messaging::class, $container->get(Firebase\Contract\Messaging::class)); $this->assertInstanceOf(Firebase\Contract\Messaging::class, $container->get(Firebase\Contract\Messaging::class.' $fooMessaging')); - $this->assertInstanceOf(Firebase\Contract\DynamicLinks::class, $container->get($this->extension->getAlias().'.foo.dynamic_links')); - $this->assertInstanceOf(Firebase\Contract\DynamicLinks::class, $container->get(Firebase\Contract\DynamicLinks::class)); - $this->assertInstanceOf(Firebase\Contract\DynamicLinks::class, $container->get(Firebase\Contract\DynamicLinks::class.' $fooDynamicLinks')); - $this->assertInstanceOf(Firebase\Contract\AppCheck::class, $container->get($this->extension->getAlias().'.foo.app_check')); $this->assertInstanceOf(Firebase\Contract\AppCheck::class, $container->get(Firebase\Contract\AppCheck::class)); $this->assertInstanceOf(Firebase\Contract\AppCheck::class, $container->get(Firebase\Contract\AppCheck::class.' $fooAppCheck')); @@ -108,44 +103,6 @@ public function test_an_auth_token_cache_can_be_used(): void $this->addToAssertionCount(1); } - public function test_a_request_logger_can_be_used(): void - { - $loggerServiceId = 'firebase_logger'; - - $container = $this->createContainer([ - 'projects' => [ - 'foo' => [ - 'credentials' => __DIR__.'/../_fixtures/valid_credentials.json', - 'http_request_logger' => $loggerServiceId, - ], - ], - ]); - $logger = $this->createMock(LoggerInterface::class); - $container->set($loggerServiceId, $logger); - - $container->get(Firebase\Contract\Auth::class); - $this->addToAssertionCount(1); - } - - public function test_a_request_debug_logger_can_be_used(): void - { - $loggerServiceId = 'firebase_debug_logger'; - - $container = $this->createContainer([ - 'projects' => [ - 'foo' => [ - 'credentials' => __DIR__.'/../_fixtures/valid_credentials.json', - 'http_request_debug_logger' => $loggerServiceId, - ], - ], - ]); - $logger = $this->createMock(LoggerInterface::class); - $container->set($loggerServiceId, $logger); - - $container->get(Firebase\Contract\Auth::class); - $this->addToAssertionCount(1); - } - public function test_a_project_can_be_private(): void { $container = $this->createContainer([ From 42e03fbdc65376ce8b773fad87a4319a7153a835 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Gamez?= Date: Sat, 14 Feb 2026 23:50:03 +0100 Subject: [PATCH 2/7] Add support for HttpClientOptions configuration --- CHANGELOG.md | 45 +++++++++++++++++++ README.md | 3 ++ src/DependencyInjection/Configuration.php | 5 +++ .../Factory/ProjectFactory.php | 11 +++++ src/DependencyInjection/FirebaseExtension.php | 4 ++ .../Factory/ProjectFactoryTest.php | 14 ++++++ .../FirebaseExtensionTest.php | 19 ++++++++ 7 files changed, 101 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e04a1b5..8529242 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,51 @@ * Updated the bundle to support `kreait/firebase-php` 8.x * Removed Dynamic Links support because it was removed from the Firebase Admin SDK * Removed HTTP request logger configuration options because the underlying SDK hooks were removed +* Added support for per-project `http_client_options` service configuration + +### Migration: HTTP request logging + +If you previously used `http_request_logger` or `http_request_debug_logger`, +migrate to a `Kreait\Firebase\Http\HttpClientOptions` service and wire your logging through Guzzle middleware. + +```yaml +# config/services.yaml +services: + App\Firebase\HttpClientOptionsFactory: ~ + + app.firebase.http_client_options: + class: Kreait\Firebase\Http\HttpClientOptions + factory: ['@App\Firebase\HttpClientOptionsFactory', 'create'] +``` + +```php +withGuzzleMiddleware($this->loggingMiddleware); + } +} +``` + +```yaml +# config/packages/firebase.yaml +kreait_firebase: + projects: + my_project: + http_client_options: 'app.firebase.http_client_options' +``` ## [5.7.0] diff --git a/README.md b/README.md index 5f59a12..7c6a7c3 100644 --- a/README.md +++ b/README.md @@ -134,6 +134,9 @@ kreait_firebase: verifier_cache: null # Example: cache.app # Optional: Used to cache the authentication tokens for connecting to the Firebase servers. auth_token_cache: null # Example: cache.app + # Optional: Service id of Kreait\Firebase\Http\HttpClientOptions + # used to configure the SDK's HTTP client. + http_client_options: null # Example: app.firebase.http_client_options ``` ## Documentation diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php index 5308939..48a01dc 100644 --- a/src/DependencyInjection/Configuration.php +++ b/src/DependencyInjection/Configuration.php @@ -65,6 +65,11 @@ public function getConfigTreeBuilder(): TreeBuilder ->example('cache.app') ->info('Used to cache the authentication tokens for connecting to the Firebase servers.') ->end() + ->scalarNode('http_client_options') + ->defaultNull() + ->example('app.firebase.http_client_options') + ->info('Service id of a Kreait\\Firebase\\Http\\HttpClientOptions instance to configure the SDK HTTP client.') + ->end() ->end() ->end() ->end() diff --git a/src/DependencyInjection/Factory/ProjectFactory.php b/src/DependencyInjection/Factory/ProjectFactory.php index 682d3e6..17b4e0c 100644 --- a/src/DependencyInjection/Factory/ProjectFactory.php +++ b/src/DependencyInjection/Factory/ProjectFactory.php @@ -6,6 +6,7 @@ use Kreait\Firebase; use Kreait\Firebase\Factory; +use Kreait\Firebase\Http\HttpClientOptions; use Psr\Cache\CacheItemPoolInterface; use Psr\SimpleCache\CacheInterface; use Symfony\Component\Cache\Adapter\Psr16Adapter; @@ -14,6 +15,7 @@ class ProjectFactory { private ?CacheItemPoolInterface $verifierCache = null; private ?CacheItemPoolInterface $authTokenCache = null; + private ?HttpClientOptions $httpClientOptions = null; /** * @param CacheInterface|CacheItemPoolInterface $verifierCache @@ -39,6 +41,11 @@ public function setAuthTokenCache($authTokenCache = null): void $this->authTokenCache = $authTokenCache; } + public function setHttpClientOptions(?HttpClientOptions $httpClientOptions = null): void + { + $this->httpClientOptions = $httpClientOptions; + } + public function createAuth(array $config = []): Firebase\Contract\Auth { return $this->createFactory($config)->createAuth(); @@ -72,6 +79,10 @@ public function createFactory(array $config = []): Factory $factory = $factory->withAuthTokenCache($this->authTokenCache); } + if ($this->httpClientOptions) { + $factory = $factory->withHttpClientOptions($this->httpClientOptions); + } + return $factory; } diff --git a/src/DependencyInjection/FirebaseExtension.php b/src/DependencyInjection/FirebaseExtension.php index a899fda..40cc561 100644 --- a/src/DependencyInjection/FirebaseExtension.php +++ b/src/DependencyInjection/FirebaseExtension.php @@ -73,6 +73,10 @@ private function registerService(string $name, string $postfix, array $config, s $factory->addMethodCall('setAuthTokenCache', [new Reference($config['auth_token_cache'])]); } + if ($config['http_client_options'] ?? null) { + $factory->addMethodCall('setHttpClientOptions', [new Reference($config['http_client_options'])]); + } + $container->register($projectServiceId, $contract) ->setFactory([$factory, $method]) ->setLazy(true) diff --git a/tests/DependencyInjection/Factory/ProjectFactoryTest.php b/tests/DependencyInjection/Factory/ProjectFactoryTest.php index 2cbb7ac..f1ebeb7 100644 --- a/tests/DependencyInjection/Factory/ProjectFactoryTest.php +++ b/tests/DependencyInjection/Factory/ProjectFactoryTest.php @@ -5,6 +5,7 @@ namespace Kreait\Firebase\Symfony\Bundle\Tests\DependencyInjection\Factory; use Kreait\Firebase\Factory as FirebaseFactory; +use Kreait\Firebase\Http\HttpClientOptions; use Kreait\Firebase\Symfony\Bundle\DependencyInjection\Factory\ProjectFactory; use PHPUnit\Framework\TestCase; use Psr\Cache\CacheItemPoolInterface; @@ -120,4 +121,17 @@ public function it_accepts_a_PSR6_auth_token_cache(): void $this->addToAssertionCount(1); } + + /** + * @test + */ + public function it_accepts_http_client_options(): void + { + $httpClientOptions = HttpClientOptions::default()->withTimeout(10.0); + + $this->factory->setHttpClientOptions($httpClientOptions); + $this->factory->createAuth($this->defaultConfig); + + $this->addToAssertionCount(1); + } } diff --git a/tests/DependencyInjection/FirebaseExtensionTest.php b/tests/DependencyInjection/FirebaseExtensionTest.php index dc07b15..1d89a0a 100644 --- a/tests/DependencyInjection/FirebaseExtensionTest.php +++ b/tests/DependencyInjection/FirebaseExtensionTest.php @@ -5,6 +5,7 @@ namespace Kreait\Firebase\Symfony\Bundle\Tests\DependencyInjection; use Kreait\Firebase; +use Kreait\Firebase\Http\HttpClientOptions; use Kreait\Firebase\Symfony\Bundle\DependencyInjection\FirebaseExtension; use PHPUnit\Framework\TestCase; use Psr\Cache\CacheItemPoolInterface; @@ -103,6 +104,24 @@ public function test_an_auth_token_cache_can_be_used(): void $this->addToAssertionCount(1); } + public function test_http_client_options_can_be_used(): void + { + $httpClientOptionsServiceId = 'firebase.http_client_options'; + + $container = $this->createContainer([ + 'projects' => [ + 'foo' => [ + 'credentials' => __DIR__.'/../_fixtures/valid_credentials.json', + 'http_client_options' => $httpClientOptionsServiceId, + ], + ], + ]); + $container->set($httpClientOptionsServiceId, HttpClientOptions::default()->withTimeout(10.0)); + + $container->get(Firebase\Contract\Auth::class); + $this->addToAssertionCount(1); + } + public function test_a_project_can_be_private(): void { $container = $this->createContainer([ From b75dfe5009e851c292c1bd795e29d66e5900d601 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Gamez?= Date: Sun, 15 Feb 2026 00:00:57 +0100 Subject: [PATCH 3/7] Migrate Makefile scripts to composer scripts --- Makefile | 14 -------------- composer.json | 5 +++++ 2 files changed, 5 insertions(+), 14 deletions(-) delete mode 100644 Makefile diff --git a/Makefile b/Makefile deleted file mode 100644 index b456fd6..0000000 --- a/Makefile +++ /dev/null @@ -1,14 +0,0 @@ -.DEFAULT_GOAL:= help -.PHONY: tests coverage view-coverage help - -tests: vendor ## Executes the test suite - vendor/bin/phpunit - -coverage: vendor ## Executes the test suite and generates code coverage reports - php -dxdebug.mode=coverage vendor/bin/phpunit -v --coverage-html=build/coverage - -view-coverage: ## Shows the code coverage report - open build/coverage/index.html - -help: - @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-16s\033[0m %s\n", $$1, $$2}' diff --git a/composer.json b/composer.json index e9600d2..3987fb1 100644 --- a/composer.json +++ b/composer.json @@ -45,6 +45,11 @@ "scripts": { "test": [ "vendor/bin/phpunit" + ], + "test:coverage": [ + "Composer\\Config::disableProcessTimeout", + "mkdir -p build", + "XDEBUG_MODE=coverage vendor/bin/phpunit --coverage-html=build/coverage" ] } } From 41501024387e5971961763b06af90577224a4f7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Gamez?= Date: Sun, 15 Feb 2026 00:07:23 +0100 Subject: [PATCH 4/7] Update PHPUnit --- composer.json | 2 +- phpunit.xml.dist | 6 +++- .../Factory/ProjectFactoryTest.php | 36 +++++++++---------- .../FirebaseExtensionTest.php | 11 +++--- 4 files changed, 29 insertions(+), 26 deletions(-) diff --git a/composer.json b/composer.json index 3987fb1..440f07c 100644 --- a/composer.json +++ b/composer.json @@ -21,7 +21,7 @@ "symfony/http-kernel": "^6.4 || ^7.4 || ^8.0" }, "require-dev": { - "phpunit/phpunit": "^10.5.58" + "phpunit/phpunit": "^12.5.11" }, "autoload": { "psr-4": { diff --git a/phpunit.xml.dist b/phpunit.xml.dist index b5562b8..f6abbcb 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -2,6 +2,8 @@ @@ -11,9 +13,11 @@ - + src + + diff --git a/tests/DependencyInjection/Factory/ProjectFactoryTest.php b/tests/DependencyInjection/Factory/ProjectFactoryTest.php index f1ebeb7..4690102 100644 --- a/tests/DependencyInjection/Factory/ProjectFactoryTest.php +++ b/tests/DependencyInjection/Factory/ProjectFactoryTest.php @@ -7,6 +7,7 @@ use Kreait\Firebase\Factory as FirebaseFactory; use Kreait\Firebase\Http\HttpClientOptions; use Kreait\Firebase\Symfony\Bundle\DependencyInjection\Factory\ProjectFactory; +use PHPUnit\Framework\Attributes\DoesNotPerformAssertions; use PHPUnit\Framework\TestCase; use Psr\Cache\CacheItemPoolInterface; use Psr\SimpleCache\CacheInterface; @@ -41,97 +42,94 @@ protected function setUp(): void ]; } + #[DoesNotPerformAssertions] public function test_it_can_handle_a_custom_database_uri(): void { $this->factory->createDatabase($this->defaultConfig + ['database_uri' => 'https://domain.tld']); - $this->addToAssertionCount(1); } + #[DoesNotPerformAssertions] public function test_it_can_handle_a_credentials_path(): void { $this->factory->createAuth(['credentials' => __DIR__.'/../../_fixtures/valid_credentials.json']); - $this->addToAssertionCount(1); } + #[DoesNotPerformAssertions] public function test_it_can_handle_a_credentials_string(): void { $credentials = \file_get_contents(__DIR__.'/../../_fixtures/valid_credentials.json'); $this->factory->createAuth(['credentials' => $credentials]); - $this->addToAssertionCount(1); } + #[DoesNotPerformAssertions] public function test_it_can_handle_a_credentials_array(): void { $credentials = \json_decode(\file_get_contents(__DIR__.'/../../_fixtures/valid_credentials.json'), true); $this->factory->createAuth(['credentials' => $credentials]); - $this->addToAssertionCount(1); } + #[DoesNotPerformAssertions] public function test_it_can_handle_a_tenant_id(): void { $this->factory->createAuth($this->defaultConfig + ['tenant_id' => 'tenant-id']); - $this->addToAssertionCount(1); } + #[DoesNotPerformAssertions] public function test_it_can_handle_a_project_id(): void { - $instance = $this->factory->createAuth($this->defaultConfig + ['project_id' => 'project-b']); - $this->addToAssertionCount(1); + $this->factory->createAuth($this->defaultConfig + ['project_id' => 'project-b']); } + #[DoesNotPerformAssertions] public function test_it_accepts_a_PSR16_verifier_cache(): void { - $cache = $this->createMock(CacheInterface::class); + $cache = $this->createStub(CacheInterface::class); $this->factory->setVerifierCache($cache); $this->factory->createAuth($this->defaultConfig); - $this->addToAssertionCount(1); } + #[DoesNotPerformAssertions] public function test_it_accepts_a_PSR6_verifier_cache(): void { - $cache = $this->createMock(CacheItemPoolInterface::class); + $cache = $this->createStub(CacheItemPoolInterface::class); $this->factory->setVerifierCache($cache); $this->factory->createAuth($this->defaultConfig); - $this->addToAssertionCount(1); } + #[DoesNotPerformAssertions] public function test_it_accepts_a_PSR16_auth_token_cache(): void { - $cache = $this->createMock(CacheInterface::class); + $cache = $this->createStub(CacheInterface::class); $this->factory->setAuthTokenCache($cache); $this->factory->createAuth($this->defaultConfig); - - $this->addToAssertionCount(1); } /** * @test */ + #[DoesNotPerformAssertions] public function it_accepts_a_PSR6_auth_token_cache(): void { - $cache = $this->createMock(CacheItemPoolInterface::class); + $cache = $this->createStub(CacheItemPoolInterface::class); $this->factory->setAuthTokenCache($cache); $this->factory->createAuth($this->defaultConfig); - - $this->addToAssertionCount(1); } /** * @test */ + #[DoesNotPerformAssertions] public function it_accepts_http_client_options(): void { $httpClientOptions = HttpClientOptions::default()->withTimeout(10.0); $this->factory->setHttpClientOptions($httpClientOptions); $this->factory->createAuth($this->defaultConfig); - - $this->addToAssertionCount(1); } } diff --git a/tests/DependencyInjection/FirebaseExtensionTest.php b/tests/DependencyInjection/FirebaseExtensionTest.php index 1d89a0a..e2ea2ea 100644 --- a/tests/DependencyInjection/FirebaseExtensionTest.php +++ b/tests/DependencyInjection/FirebaseExtensionTest.php @@ -7,6 +7,7 @@ use Kreait\Firebase; use Kreait\Firebase\Http\HttpClientOptions; use Kreait\Firebase\Symfony\Bundle\DependencyInjection\FirebaseExtension; +use PHPUnit\Framework\Attributes\DoesNotPerformAssertions; use PHPUnit\Framework\TestCase; use Psr\Cache\CacheItemPoolInterface; use ReflectionException; @@ -66,6 +67,7 @@ public function test_a_project_is_created_with_a_service_for_each_feature(): voi $this->assertInstanceOf(Firebase\Contract\AppCheck::class, $container->get(Firebase\Contract\AppCheck::class.' $fooAppCheck')); } + #[DoesNotPerformAssertions] public function test_a_verifier_cache_can_be_used(): void { $cacheServiceId = 'cache.app.simple.mock'; @@ -78,13 +80,13 @@ public function test_a_verifier_cache_can_be_used(): void ], ], ]); - $cache = $this->createMock(CacheItemPoolInterface::class); + $cache = $this->createStub(CacheItemPoolInterface::class); $container->set($cacheServiceId, $cache); $container->get(Firebase\Contract\Auth::class); - $this->addToAssertionCount(1); } + #[DoesNotPerformAssertions] public function test_an_auth_token_cache_can_be_used(): void { $cacheServiceId = 'cache.app.simple.mock'; @@ -97,13 +99,13 @@ public function test_an_auth_token_cache_can_be_used(): void ], ], ]); - $cache = $this->createMock(CacheItemPoolInterface::class); + $cache = $this->createStub(CacheItemPoolInterface::class); $container->set($cacheServiceId, $cache); $container->get(Firebase\Contract\Auth::class); - $this->addToAssertionCount(1); } + #[DoesNotPerformAssertions] public function test_http_client_options_can_be_used(): void { $httpClientOptionsServiceId = 'firebase.http_client_options'; @@ -119,7 +121,6 @@ public function test_http_client_options_can_be_used(): void $container->set($httpClientOptionsServiceId, HttpClientOptions::default()->withTimeout(10.0)); $container->get(Firebase\Contract\Auth::class); - $this->addToAssertionCount(1); } public function test_a_project_can_be_private(): void From e3de505cf4e26d7d07c102a2f3888b3a7968bce0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Gamez?= Date: Sun, 15 Feb 2026 00:28:40 +0100 Subject: [PATCH 5/7] Update tests --- .../Factory/ProjectFactory.php | 3 + .../Factory/ProjectFactoryTest.php | 63 +++++++++++++------ .../FirebaseExtensionTest.php | 61 +++++++++++++----- tests/FirebaseBundleTest.php | 2 +- 4 files changed, 94 insertions(+), 35 deletions(-) diff --git a/src/DependencyInjection/Factory/ProjectFactory.php b/src/DependencyInjection/Factory/ProjectFactory.php index 17b4e0c..ba1b904 100644 --- a/src/DependencyInjection/Factory/ProjectFactory.php +++ b/src/DependencyInjection/Factory/ProjectFactory.php @@ -91,6 +91,9 @@ public function createDatabase(array $config = []): Firebase\Contract\Database return $this->createFactory($config)->createDatabase(); } + /** + * @codeCoverageIgnore Firestore needs optional runtime dependencies (incl. gRPC), which are not part of the default test setup. + */ public function createFirestore(array $config = []): Firebase\Contract\Firestore { return $this->createFactory($config)->createFirestore(); diff --git a/tests/DependencyInjection/Factory/ProjectFactoryTest.php b/tests/DependencyInjection/Factory/ProjectFactoryTest.php index 4690102..09c13a6 100644 --- a/tests/DependencyInjection/Factory/ProjectFactoryTest.php +++ b/tests/DependencyInjection/Factory/ProjectFactoryTest.php @@ -4,7 +4,6 @@ namespace Kreait\Firebase\Symfony\Bundle\Tests\DependencyInjection\Factory; -use Kreait\Firebase\Factory as FirebaseFactory; use Kreait\Firebase\Http\HttpClientOptions; use Kreait\Firebase\Symfony\Bundle\DependencyInjection\Factory\ProjectFactory; use PHPUnit\Framework\Attributes\DoesNotPerformAssertions; @@ -43,19 +42,43 @@ protected function setUp(): void } #[DoesNotPerformAssertions] - public function test_it_can_handle_a_custom_database_uri(): void + public function testItCanHandleACustomDatabaseUri(): void { $this->factory->createDatabase($this->defaultConfig + ['database_uri' => 'https://domain.tld']); } #[DoesNotPerformAssertions] - public function test_it_can_handle_a_credentials_path(): void + public function testItCanCreateMessaging(): void + { + $this->factory->createMessaging($this->defaultConfig); + } + + #[DoesNotPerformAssertions] + public function testItCanCreateRemoteConfig(): void + { + $this->factory->createRemoteConfig($this->defaultConfig); + } + + #[DoesNotPerformAssertions] + public function testItCanCreateStorage(): void + { + $this->factory->createStorage($this->defaultConfig); + } + + #[DoesNotPerformAssertions] + public function testItCanCreateAppCheck(): void + { + $this->factory->createAppCheck($this->defaultConfig); + } + + #[DoesNotPerformAssertions] + public function testItCanHandleACredentialsPath(): void { $this->factory->createAuth(['credentials' => __DIR__.'/../../_fixtures/valid_credentials.json']); } #[DoesNotPerformAssertions] - public function test_it_can_handle_a_credentials_string(): void + public function testItCanHandleACredentialsString(): void { $credentials = \file_get_contents(__DIR__.'/../../_fixtures/valid_credentials.json'); @@ -63,7 +86,7 @@ public function test_it_can_handle_a_credentials_string(): void } #[DoesNotPerformAssertions] - public function test_it_can_handle_a_credentials_array(): void + public function testItCanHandleACredentialsArray(): void { $credentials = \json_decode(\file_get_contents(__DIR__.'/../../_fixtures/valid_credentials.json'), true); @@ -71,19 +94,19 @@ public function test_it_can_handle_a_credentials_array(): void } #[DoesNotPerformAssertions] - public function test_it_can_handle_a_tenant_id(): void + public function testItCanHandleATenantId(): void { $this->factory->createAuth($this->defaultConfig + ['tenant_id' => 'tenant-id']); } #[DoesNotPerformAssertions] - public function test_it_can_handle_a_project_id(): void + public function testItCanHandleAProjectId(): void { $this->factory->createAuth($this->defaultConfig + ['project_id' => 'project-b']); } #[DoesNotPerformAssertions] - public function test_it_accepts_a_PSR16_verifier_cache(): void + public function testItAcceptsAPSR16VerifierCache(): void { $cache = $this->createStub(CacheInterface::class); @@ -92,7 +115,7 @@ public function test_it_accepts_a_PSR16_verifier_cache(): void } #[DoesNotPerformAssertions] - public function test_it_accepts_a_PSR6_verifier_cache(): void + public function testItAcceptsAPSR6VerifierCache(): void { $cache = $this->createStub(CacheItemPoolInterface::class); @@ -101,7 +124,7 @@ public function test_it_accepts_a_PSR6_verifier_cache(): void } #[DoesNotPerformAssertions] - public function test_it_accepts_a_PSR16_auth_token_cache(): void + public function testItAcceptsAPSR16AuthTokenCache(): void { $cache = $this->createStub(CacheInterface::class); @@ -109,11 +132,8 @@ public function test_it_accepts_a_PSR16_auth_token_cache(): void $this->factory->createAuth($this->defaultConfig); } - /** - * @test - */ #[DoesNotPerformAssertions] - public function it_accepts_a_PSR6_auth_token_cache(): void + public function testItAcceptsAPSR6AuthTokenCache(): void { $cache = $this->createStub(CacheItemPoolInterface::class); @@ -121,15 +141,22 @@ public function it_accepts_a_PSR6_auth_token_cache(): void $this->factory->createAuth($this->defaultConfig); } - /** - * @test - */ #[DoesNotPerformAssertions] - public function it_accepts_http_client_options(): void + public function testItAcceptsHttpClientOptions(): void + { + $httpClientOptions = HttpClientOptions::default()->withTimeout(10.0); + + $this->factory->setHttpClientOptions($httpClientOptions); + $this->factory->createAuth($this->defaultConfig); + } + + #[DoesNotPerformAssertions] + public function testItCanResetHttpClientOptionsToNull(): void { $httpClientOptions = HttpClientOptions::default()->withTimeout(10.0); $this->factory->setHttpClientOptions($httpClientOptions); + $this->factory->setHttpClientOptions(null); $this->factory->createAuth($this->defaultConfig); } } diff --git a/tests/DependencyInjection/FirebaseExtensionTest.php b/tests/DependencyInjection/FirebaseExtensionTest.php index e2ea2ea..e17f33e 100644 --- a/tests/DependencyInjection/FirebaseExtensionTest.php +++ b/tests/DependencyInjection/FirebaseExtensionTest.php @@ -6,8 +6,8 @@ use Kreait\Firebase; use Kreait\Firebase\Http\HttpClientOptions; +use Kreait\Firebase\Symfony\Bundle\DependencyInjection\Factory\ProjectFactory; use Kreait\Firebase\Symfony\Bundle\DependencyInjection\FirebaseExtension; -use PHPUnit\Framework\Attributes\DoesNotPerformAssertions; use PHPUnit\Framework\TestCase; use Psr\Cache\CacheItemPoolInterface; use ReflectionException; @@ -18,6 +18,7 @@ use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; +use Symfony\Component\DependencyInjection\Reference; use TypeError; /** @@ -32,7 +33,7 @@ protected function setUp(): void $this->extension = new FirebaseExtension(); } - public function test_a_project_is_created_with_a_service_for_each_feature(): void + public function testAProjectIsCreatedWithAServiceForEachFeature(): void { $container = $this->createContainer([ 'projects' => [ @@ -67,8 +68,7 @@ public function test_a_project_is_created_with_a_service_for_each_feature(): voi $this->assertInstanceOf(Firebase\Contract\AppCheck::class, $container->get(Firebase\Contract\AppCheck::class.' $fooAppCheck')); } - #[DoesNotPerformAssertions] - public function test_a_verifier_cache_can_be_used(): void + public function testAVerifierCacheCanBeUsed(): void { $cacheServiceId = 'cache.app.simple.mock'; @@ -80,14 +80,14 @@ public function test_a_verifier_cache_can_be_used(): void ], ], ]); + $cache = $this->createStub(CacheItemPoolInterface::class); $container->set($cacheServiceId, $cache); - $container->get(Firebase\Contract\Auth::class); + $this->assertInstanceOf(Firebase\Contract\Auth::class, $container->get(Firebase\Contract\Auth::class)); } - #[DoesNotPerformAssertions] - public function test_an_auth_token_cache_can_be_used(): void + public function testAnAuthTokenCacheCanBeUsed(): void { $cacheServiceId = 'cache.app.simple.mock'; @@ -99,14 +99,14 @@ public function test_an_auth_token_cache_can_be_used(): void ], ], ]); + $cache = $this->createStub(CacheItemPoolInterface::class); $container->set($cacheServiceId, $cache); - $container->get(Firebase\Contract\Auth::class); + $this->assertInstanceOf(Firebase\Contract\Auth::class, $container->get(Firebase\Contract\Auth::class)); } - #[DoesNotPerformAssertions] - public function test_http_client_options_can_be_used(): void + public function testHttpClientOptionsCanBeUsed(): void { $httpClientOptionsServiceId = 'firebase.http_client_options'; @@ -118,12 +118,13 @@ public function test_http_client_options_can_be_used(): void ], ], ]); + $container->set($httpClientOptionsServiceId, HttpClientOptions::default()->withTimeout(10.0)); - $container->get(Firebase\Contract\Auth::class); + $this->assertInstanceOf(Firebase\Contract\Auth::class, $container->get(Firebase\Contract\Auth::class)); } - public function test_a_project_can_be_private(): void + public function testAProjectCanBePrivate(): void { $container = $this->createContainer([ 'projects' => [ @@ -138,7 +139,7 @@ public function test_a_project_can_be_private(): void $this->assertFalse($container->has($this->extension->getAlias().'.foo')); } - public function test_it_can_provide_multiple_projects(): void + public function testItCanProvideMultipleProjects(): void { $container = $this->createContainer([ 'projects' => [ @@ -155,7 +156,35 @@ public function test_it_can_provide_multiple_projects(): void $this->assertTrue($container->hasDefinition($this->extension->getAlias().'.bar.auth')); } - public function test_it_supports_specifying_credentials(): void + public function testProjectFactoryOptionsDoNotLeakBetweenProjects(): void + { + $fooCacheServiceId = 'cache.foo'; + $barCacheServiceId = 'cache.bar'; + + $container = $this->createContainer([ + 'projects' => [ + 'foo' => [ + 'credentials' => __DIR__.'/../_fixtures/valid_credentials.json', + 'verifier_cache' => $fooCacheServiceId, + ], + 'bar' => [ + 'credentials' => __DIR__.'/../_fixtures/valid_credentials.json', + 'auth_token_cache' => $barCacheServiceId, + ], + ], + ]); + + $fooCalls = $container->getDefinition($this->projectFactoryServiceId('foo'))->getMethodCalls(); + $barCalls = $container->getDefinition($this->projectFactoryServiceId('bar'))->getMethodCalls(); + + $this->assertContainsEquals(['setVerifierCache', [new Reference($fooCacheServiceId)]], $fooCalls); + $this->assertNotContainsEquals(['setAuthTokenCache', [new Reference($barCacheServiceId)]], $fooCalls); + + $this->assertContainsEquals(['setAuthTokenCache', [new Reference($barCacheServiceId)]], $barCalls); + $this->assertNotContainsEquals(['setVerifierCache', [new Reference($fooCacheServiceId)]], $barCalls); + } + + public function testItSupportsSpecifyingCredentials(): void { $container = $this->createContainer([ 'projects' => [ @@ -168,7 +197,7 @@ public function test_it_supports_specifying_credentials(): void $this->assertTrue($container->hasDefinition($this->extension->getAlias().'.foo.auth')); } - public function test_it_accepts_only_one_default_project(): void + public function testItAcceptsOnlyOneDefaultProject(): void { $this->expectException(InvalidConfigurationException::class); @@ -186,7 +215,7 @@ public function test_it_accepts_only_one_default_project(): void ]); } - public function test_it_has_no_default_project_if_none_could_be_determined(): void + public function testItHasNoDefaultProjectIfNoneCouldBeDetermined(): void { $container = $this->createContainer([ 'projects' => [ diff --git a/tests/FirebaseBundleTest.php b/tests/FirebaseBundleTest.php index 6c77a86..2f16ed0 100644 --- a/tests/FirebaseBundleTest.php +++ b/tests/FirebaseBundleTest.php @@ -13,7 +13,7 @@ */ final class FirebaseBundleTest extends TestCase { - public function test_it_uses_the_right_container_extension(): void + public function testItUsesTheRightContainerExtension(): void { $bundle = new FirebaseBundle(); From 9591b0394908a095d78334b97e11cfd1665a19a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Gamez?= Date: Sun, 15 Feb 2026 00:36:46 +0100 Subject: [PATCH 6/7] Isolate project factory per project --- CHANGELOG.md | 11 +++++ src/DependencyInjection/FirebaseExtension.php | 47 +++++++++++-------- src/Resources/config/firebase.php | 20 -------- .../FirebaseExtensionTest.php | 8 +++- 4 files changed, 45 insertions(+), 41 deletions(-) delete mode 100644 src/Resources/config/firebase.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 8529242..2d36e6a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ * Removed Dynamic Links support because it was removed from the Firebase Admin SDK * Removed HTTP request logger configuration options because the underlying SDK hooks were removed * Added support for per-project `http_client_options` service configuration +* Fixed project option leakage by isolating project factory instances per configured Firebase project +* Removed the container service `Kreait\Firebase\Factory` ### Migration: HTTP request logging @@ -52,6 +54,15 @@ kreait_firebase: http_client_options: 'app.firebase.http_client_options' ``` +### Migration: custom DI integrations + +If you customized bundle internals with compiler passes or direct container lookups: + +* `Kreait\Firebase\Factory` is no longer registered as a container service. +* Project factories are now per project (`kreait_firebase..project_factory`) instead of using only `Kreait\Firebase\Symfony\Bundle\DependencyInjection\Factory\ProjectFactory`. + +If your app modified the `ProjectFactory` definition directly, update your code to target the per-project service id(s). + ## [5.7.0] * Added support for PHP 8.5 and Symfony 8 diff --git a/src/DependencyInjection/FirebaseExtension.php b/src/DependencyInjection/FirebaseExtension.php index 40cc561..9890e6c 100644 --- a/src/DependencyInjection/FirebaseExtension.php +++ b/src/DependencyInjection/FirebaseExtension.php @@ -7,10 +7,8 @@ use Kreait\Firebase; use Kreait\Firebase\Symfony\Bundle\DependencyInjection\Factory\ProjectFactory; use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; -use Symfony\Component\Config\FileLocator; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Extension\Extension; -use Symfony\Component\DependencyInjection\Loader\PhpFileLoader; use Symfony\Component\DependencyInjection\Reference; class FirebaseExtension extends Extension @@ -20,8 +18,7 @@ public function load(array $configs, ContainerBuilder $container): void $configuration = $this->getConfiguration($configs, $container); $config = $this->processConfiguration($configuration, $configs); - $loader = new PhpFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); - $loader->load('firebase.php'); + $container->register(ProjectFactory::class, ProjectFactory::class)->setPublic(false); $projectConfigurations = $config['projects'] ?? []; $projectConfigurationsCount = \count($projectConfigurations); @@ -39,13 +36,15 @@ public function load(array $configs, ContainerBuilder $container): void private function processProjectConfiguration(string $name, array $config, ContainerBuilder $container): void { - $this->registerService($name, 'database', $config, Firebase\Contract\Database::class, $container, 'createDatabase'); - $this->registerService($name, 'auth', $config, Firebase\Contract\Auth::class, $container, 'createAuth'); - $this->registerService($name, 'storage', $config, Firebase\Contract\Storage::class, $container, 'createStorage'); - $this->registerService($name, 'remote_config', $config, Firebase\Contract\RemoteConfig::class, $container, 'createRemoteConfig'); - $this->registerService($name, 'messaging', $config, Firebase\Contract\Messaging::class, $container, 'createMessaging'); - $this->registerService($name, 'firestore', $config, Firebase\Contract\Firestore::class, $container, 'createFirestore'); - $this->registerService($name, 'app_check', $config, Firebase\Contract\AppCheck::class, $container, 'createAppCheck'); + $projectFactory = $this->registerProjectFactory($name, $config, $container); + + $this->registerService($name, 'database', $config, Firebase\Contract\Database::class, $projectFactory, $container, 'createDatabase'); + $this->registerService($name, 'auth', $config, Firebase\Contract\Auth::class, $projectFactory, $container, 'createAuth'); + $this->registerService($name, 'storage', $config, Firebase\Contract\Storage::class, $projectFactory, $container, 'createStorage'); + $this->registerService($name, 'remote_config', $config, Firebase\Contract\RemoteConfig::class, $projectFactory, $container, 'createRemoteConfig'); + $this->registerService($name, 'messaging', $config, Firebase\Contract\Messaging::class, $projectFactory, $container, 'createMessaging'); + $this->registerService($name, 'firestore', $config, Firebase\Contract\Firestore::class, $projectFactory, $container, 'createFirestore'); + $this->registerService($name, 'app_check', $config, Firebase\Contract\AppCheck::class, $projectFactory, $container, 'createAppCheck'); } public function getAlias(): string @@ -58,27 +57,35 @@ public function getConfiguration(array $config, ContainerBuilder $container): Co return new Configuration($this->getAlias()); } - private function registerService(string $name, string $postfix, array $config, string $contract, ContainerBuilder $container, string $method = 'create'): void + private function registerProjectFactory(string $name, array $config, ContainerBuilder $container): Reference { - $projectServiceId = \sprintf('%s.%s.%s', $this->getAlias(), $name, $postfix); - $isPublic = $config['public']; - - $factory = $container->getDefinition(ProjectFactory::class); + $projectFactoryServiceId = \sprintf('%s.%s.project_factory', $this->getAlias(), $name); + $projectFactory = clone $container->getDefinition(ProjectFactory::class); if ($config['verifier_cache'] ?? null) { - $factory->addMethodCall('setVerifierCache', [new Reference($config['verifier_cache'])]); + $projectFactory->addMethodCall('setVerifierCache', [new Reference($config['verifier_cache'])]); } if ($config['auth_token_cache'] ?? null) { - $factory->addMethodCall('setAuthTokenCache', [new Reference($config['auth_token_cache'])]); + $projectFactory->addMethodCall('setAuthTokenCache', [new Reference($config['auth_token_cache'])]); } if ($config['http_client_options'] ?? null) { - $factory->addMethodCall('setHttpClientOptions', [new Reference($config['http_client_options'])]); + $projectFactory->addMethodCall('setHttpClientOptions', [new Reference($config['http_client_options'])]); } + $container->setDefinition($projectFactoryServiceId, $projectFactory); + + return new Reference($projectFactoryServiceId); + } + + private function registerService(string $name, string $postfix, array $config, string $contract, Reference $projectFactory, ContainerBuilder $container, string $method = 'create'): void + { + $projectServiceId = \sprintf('%s.%s.%s', $this->getAlias(), $name, $postfix); + $isPublic = $config['public']; + $container->register($projectServiceId, $contract) - ->setFactory([$factory, $method]) + ->setFactory([$projectFactory, $method]) ->setLazy(true) ->addArgument($config) ->setPublic($isPublic); diff --git a/src/Resources/config/firebase.php b/src/Resources/config/firebase.php deleted file mode 100644 index f85ec6a..0000000 --- a/src/Resources/config/firebase.php +++ /dev/null @@ -1,20 +0,0 @@ -services(); - - // Equivalent of Kreait\Firebase\Factory - // NO need for these parameters anymore — use FQCN directly. - - $services - ->set(Factory::class) - ->public(); // optional: same as XML service id="Kreait\Firebase\Factory" - - $services - ->set(ProjectFactory::class) - ->public(false); // same as public="false" -}; diff --git a/tests/DependencyInjection/FirebaseExtensionTest.php b/tests/DependencyInjection/FirebaseExtensionTest.php index e17f33e..695eb82 100644 --- a/tests/DependencyInjection/FirebaseExtensionTest.php +++ b/tests/DependencyInjection/FirebaseExtensionTest.php @@ -6,7 +6,6 @@ use Kreait\Firebase; use Kreait\Firebase\Http\HttpClientOptions; -use Kreait\Firebase\Symfony\Bundle\DependencyInjection\Factory\ProjectFactory; use Kreait\Firebase\Symfony\Bundle\DependencyInjection\FirebaseExtension; use PHPUnit\Framework\TestCase; use Psr\Cache\CacheItemPoolInterface; @@ -154,6 +153,8 @@ public function testItCanProvideMultipleProjects(): void $this->assertTrue($container->hasDefinition($this->extension->getAlias().'.foo.auth')); $this->assertTrue($container->hasDefinition($this->extension->getAlias().'.bar.auth')); + $this->assertTrue($container->hasDefinition($this->projectFactoryServiceId('foo'))); + $this->assertTrue($container->hasDefinition($this->projectFactoryServiceId('bar'))); } public function testProjectFactoryOptionsDoNotLeakBetweenProjects(): void @@ -255,4 +256,9 @@ public function process(ContainerBuilder $container): void return $container; } + + private function projectFactoryServiceId(string $projectName): string + { + return $this->extension->getAlias().'.'.$projectName.'.project_factory'; + } } From 4415b5c5b2c5a4067c5e41072ef1aed39ca78c46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Gamez?= Date: Sun, 15 Feb 2026 00:55:05 +0100 Subject: [PATCH 7/7] Simplify test workflow matrix --- .github/workflows/tests.yml | 55 +++++++++++++------------------------ 1 file changed, 19 insertions(+), 36 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 0d0a667..868c5d3 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -24,13 +24,7 @@ concurrency: jobs: test: - name: > - ${{ format('PHP {0} - Sf {1} - deps {2} - stab. {3}', - matrix.php-version || 'Ø', - matrix.symfony-require || 'Ø', - matrix.dependencies || 'Ø', - matrix.stability || 'Ø' - ) }} + name: ${{ matrix.job-name }} runs-on: "ubuntu-latest" env: SYMFONY_REQUIRE: ${{matrix.symfony-require}} @@ -39,36 +33,30 @@ jobs: strategy: fail-fast: false matrix: - php-version: - - "8.3" - - "8.4" - - "8.5" - dependencies: - - "highest" - stability: - - "stable" - symfony-require: - - "" include: - # Test with the lowest set of dependencies + # Symfony 6.4 LTS on minimum supported PHP - php-version: "8.3" - dependencies: "lowest" - stability: "stable" - - # Test 6.4 LTS - - php-version: "8.5" symfony-require: "6.4.*" - dependencies: "highest" + job-name: "PHP 8.3 - Symfony 6.4 LTS" - # Test 7.4 LTS - - php-version: "8.5" + # Symfony 7.4 LTS on minimum supported PHP + - php-version: "8.3" symfony-require: "7.4.*" - dependencies: "highest" + job-name: "PHP 8.3 - Symfony 7.4 LTS" - # Bleeding edge + # Symfony 8 on supported PHP versions + - php-version: "8.4" + symfony-require: "8.0.*" + job-name: "PHP 8.4 - Symfony 8.0" + + - php-version: "8.5" + symfony-require: "8.0.*" + job-name: "PHP 8.5 - Symfony 8.0" + + # Forward-compat lane for upcoming Symfony 8.x releases - php-version: "8.5" - dependencies: "highest" - stability: "dev" + symfony-require: "8.*" + job-name: "PHP 8.5 - Symfony 8.x" steps: - name: "Checkout" @@ -83,15 +71,10 @@ jobs: ini-file: "development" tools: flex - - name: "Enforce using stable dependencies" - run: "composer config minimum-stability stable" - if: "${{ matrix.stability == 'stable' }}" - - name: "Install dependencies with Composer" uses: "ramsey/composer-install@v3" with: - dependency-versions: "${{ matrix.dependencies }}" composer-options: "--prefer-dist" - - name: Run test suite on PHP ${{ matrix.php }} and Symfony ${{ matrix.symfony }} + - name: Run test suite run: ./vendor/bin/phpunit