From daf8d6f9c2f88067d5ed9858e1981082bfd3e562 Mon Sep 17 00:00:00 2001 From: Vinayak Iyer Date: Wed, 23 Sep 2026 18:40:27 +0530 Subject: [PATCH 01/15] EE-282 Support Symfony 7.4 Symfony 7 declares getConfigTreeBuilder(): TreeBuilder on ConfigurationInterface, so the untyped Configuration::getConfigTreeBuilder() is a fatal error there. A class return type parses on PHP 7.0, so the PHP requirement stays as it is. symfony/framework-bundle allows ^7.4. The bundle's code uses symfony/config, symfony/dependency-injection and symfony/http-kernel without requiring them; they are now required at the framework-bundle constraint, because framework-bundle 7.4 accepts the Symfony 8 versions of all three. The test-only symfony/yaml follows the same lines. Co-Authored-By: Claude Opus 5.5 (1M context) --- composer.json | 7 +++++-- src/DependencyInjection/Configuration.php | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/composer.json b/composer.json index 406c43b..a4fd979 100644 --- a/composer.json +++ b/composer.json @@ -17,12 +17,15 @@ "require": { "php": "^7.0 || ^8.0", "paysera/lib-normalization": "^1.2", - "symfony/framework-bundle": "^3.4.26|^4.2.7|^5.4|^6.0", + "symfony/framework-bundle": "^3.4.26|^4.2.7|^5.4|^6.0|^7.4", + "symfony/config": "^3.4.26|^4.2.7|^5.4|^6.0|^7.4", + "symfony/dependency-injection": "^3.4.26|^4.2.7|^5.4|^6.0|^7.4", + "symfony/http-kernel": "^3.4.26|^4.2.7|^5.4|^6.0|^7.4", "paysera/lib-dependency-injection": "^1.3.0" }, "require-dev": { "phpunit/phpunit": "^6.0 || ^9.0", - "symfony/yaml": "^2.7|^3.0|^4.0|^5.0", + "symfony/yaml": "^2.7|^3.0|^4.0|^5.0|^6.0|^7.4", "mockery/mockery": "^1.2", "yoast/phpunit-polyfills": "^1.0" }, diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php index 2b7844c..aafc514 100644 --- a/src/DependencyInjection/Configuration.php +++ b/src/DependencyInjection/Configuration.php @@ -9,7 +9,7 @@ class Configuration implements ConfigurationInterface { - public function getConfigTreeBuilder() + public function getConfigTreeBuilder(): TreeBuilder { $treeBuilder = new TreeBuilder('paysera_normalization'); $rootNode = method_exists($treeBuilder, 'getRootNode') From 04ee5b01b7fd07a17bee48393c0a274c5f991d59 Mon Sep 17 00:00:00 2001 From: Vinayak Iyer Date: Wed, 23 Sep 2026 18:53:37 +0530 Subject: [PATCH 02/15] EE-282 Stop the deprecation notices that name the bundle's own classes With Symfony's DebugClassLoader on (an application's dev environment, symfony/phpunit-bridge), Symfony 6.4 and 7.4 report two notices against this bundle: ExtensionInterface::load() "might add void as a native return type" in PayseraNormalizationExtension, and, on 7.4, that the HttpKernel Extension it extends is internal since Symfony 7.1 (deprecated in 8.1). The extension now extends Symfony\Component\DependencyInjection\Extension\Extension, the parent of the HttpKernel class, which it already used for everything (the HttpKernel class adds only the annotated-classes cache this extension never fills), and load() documents its void return the way 1.3.1 did for the bundle's build(). The native void type waits for Symfony 8, where it would also require PHP 7.1. Co-Authored-By: Claude Opus 5.5 (1M context) --- src/DependencyInjection/PayseraNormalizationExtension.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/DependencyInjection/PayseraNormalizationExtension.php b/src/DependencyInjection/PayseraNormalizationExtension.php index 6a25374..5946677 100644 --- a/src/DependencyInjection/PayseraNormalizationExtension.php +++ b/src/DependencyInjection/PayseraNormalizationExtension.php @@ -6,7 +6,7 @@ use Paysera\Component\Normalization\TypeAwareInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\Config\FileLocator; -use Symfony\Component\HttpKernel\DependencyInjection\Extension; +use Symfony\Component\DependencyInjection\Extension\Extension; use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; class PayseraNormalizationExtension extends Extension @@ -16,6 +16,9 @@ public static function supportsAutoconfiguration() return method_exists(ContainerBuilder::class, 'registerForAutoconfiguration'); } + /** + * @return void + */ public function load(array $configs, ContainerBuilder $container) { $configuration = new Configuration(); From dba19b91a795ffa70c4ce0981d8fa27b9a0d04f6 Mon Sep 17 00:00:00 2001 From: Vinayak Iyer Date: Wed, 23 Sep 2026 18:54:25 +0530 Subject: [PATCH 03/15] EE-282 Run the workflow on Symfony 7 and let the Symfony 3 and 4 jobs install again The matrix gains Symfony 7.* on PHP 8.2 to 8.4; Symfony 7.4 needs PHP 8.2, so the older PHP lines exclude it. Composer 2.10, which setup-php installs for PHP 7.2 and later, refuses packages with security advisories, and every symfony/cache release of Symfony 3.4 and 4.4 has one, so those jobs failed at install. The step paysera/lib-api-bundle#18 added turns the blocking off for this test install only; PHP 7.0 and 7.1 get Composer 2.2, which neither blocks advisories nor knows the setting, so the step skips them. Co-Authored-By: Claude Opus 5.5 (1M context) --- .github/workflows/ci.yml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 31a3cd6..f0122c6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,6 +33,7 @@ jobs: - '4.*' - '5.*' - '6.*' + - '7.*' dependency: - 'highest' include: @@ -43,6 +44,13 @@ jobs: - { php: '7.2', symfony: '6.*' } - { php: '7.3', symfony: '6.*' } - { php: '7.4', symfony: '6.*' } + - { php: '7.0', symfony: '7.*' } + - { php: '7.1', symfony: '7.*' } + - { php: '7.2', symfony: '7.*' } + - { php: '7.3', symfony: '7.*' } + - { php: '7.4', symfony: '7.*' } + - { php: '8.0', symfony: '7.*' } + - { php: '8.1', symfony: '7.*' } steps: - name: Checkout @@ -54,6 +62,13 @@ jobs: php-version: ${{ matrix.php }} tools: flex + # Symfony 3.4 and 4.4 are end-of-life, and their symfony/cache releases have security advisories. + # Composer 2.10 refuses to install them by default. This only affects the test install here. + # PHP 7.0 and 7.1 get Composer 2.2, which does not block advisories and does not know this setting. + - name: Allow packages with security advisories + if: matrix.php != '7.0' && matrix.php != '7.1' + run: composer config policy.advisories.block false + - name: Install dependencies uses: ramsey/composer-install@v2 env: From 59fe72aafc8fab1f2212b78f3ca18561c6f3aac7 Mon Sep 17 00:00:00 2001 From: Vinayak Iyer Date: Wed, 23 Sep 2026 18:54:50 +0530 Subject: [PATCH 04/15] EE-282 Add the 1.4.0 changelog entry Co-Authored-By: Claude Opus 5.5 (1M context) --- CHANGELOG.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 715f4b3..6e955ea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,20 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 1.4.0 +### Added +- Support for Symfony 7.4. + +### Changed +- `Configuration::getConfigTreeBuilder()` declares the `TreeBuilder` return type, which Symfony 7 requires. + Breaking for subclasses that override `getConfigTreeBuilder()` without a return type: add `: TreeBuilder` to the override. +- `PayseraNormalizationExtension` extends `Symfony\Component\DependencyInjection\Extension\Extension` instead of + `Symfony\Component\HttpKernel\DependencyInjection\Extension` (internal since Symfony 7.1, deprecated in 8.1), and + `load()` documents its `void` return. This removes the two deprecation notices Symfony 6.4 and 7.4 report for the + bundle's own classes. +- `symfony/config`, `symfony/dependency-injection` and `symfony/http-kernel`, which the bundle's code uses, are required + explicitly, at the same versions as `symfony/framework-bundle`. + ## 1.3.1 ### Added - `void` phpdoc typehint to `PayseraNormalizationBundle::build` method to fix the deprecation message on Symfony 6 From 9f5e898132c83b6da808091eea156a0cf495122c Mon Sep 17 00:00:00 2001 From: Vinayak Iyer Date: Wed, 23 Sep 2026 19:11:25 +0530 Subject: [PATCH 05/15] EE-282 Say which change removes which deprecation notice On Symfony 6.4 one of the two notices is about getConfigTreeBuilder(), which the return type removes, not the extension change; the entry now credits both. Co-Authored-By: Claude Opus 5.5 (1M context) --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e955ea..54a763c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,8 +13,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 Breaking for subclasses that override `getConfigTreeBuilder()` without a return type: add `: TreeBuilder` to the override. - `PayseraNormalizationExtension` extends `Symfony\Component\DependencyInjection\Extension\Extension` instead of `Symfony\Component\HttpKernel\DependencyInjection\Extension` (internal since Symfony 7.1, deprecated in 8.1), and - `load()` documents its `void` return. This removes the two deprecation notices Symfony 6.4 and 7.4 report for the - bundle's own classes. + `load()` documents its `void` return. Together with the return type above, this removes the deprecation notices that + Symfony 6.4 and 7.4 report for the bundle's own classes when their debug class loader is on. - `symfony/config`, `symfony/dependency-injection` and `symfony/http-kernel`, which the bundle's code uses, are required explicitly, at the same versions as `symfony/framework-bundle`. From 630871f69a2279ce9b5330a5f2a4e7ed9aadf735 Mon Sep 17 00:00:00 2001 From: Vinayak Iyer Date: Wed, 23 Sep 2026 20:53:47 +0530 Subject: [PATCH 06/15] EE-282 Test that Symfony's debug class loader reports nothing against the bundle The suite did not assert what the previous commits are for: with the HttpKernel parent put back or the @return void docblock removed, all 13 tests stayed green. The new test loads the bundle's three classes in a separate process with Symfony's DebugClassLoader enabled and fails on any deprecation notice that names them. Against the pre-change code it fails on Symfony 6.4 (load() "might add void", getConfigTreeBuilder() "might add TreeBuilder") and on 7.4 (the internal HttpKernel parent, load()); it is skipped below Symfony 4.4, where symfony/error-handler does not exist. Co-Authored-By: Claude Opus 5.5 (1M context) --- .../FunctionalDeprecationNoticesTest.php | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 tests/Functional/FunctionalDeprecationNoticesTest.php diff --git a/tests/Functional/FunctionalDeprecationNoticesTest.php b/tests/Functional/FunctionalDeprecationNoticesTest.php new file mode 100644 index 0000000..48b5d89 --- /dev/null +++ b/tests/Functional/FunctionalDeprecationNoticesTest.php @@ -0,0 +1,52 @@ +markTestSkipped('symfony/error-handler is not installed (Symfony below 4.4)'); + } + + $deprecations = []; + set_error_handler(function ($type, $message) use (&$deprecations) { + if ($type === E_USER_DEPRECATED || $type === E_DEPRECATED) { + $deprecations[] = $message; + } + + return true; + }); + DebugClassLoader::enable(); + try { + class_exists(PayseraNormalizationBundle::class); + class_exists(PayseraNormalizationExtension::class); + class_exists(Configuration::class); + } finally { + DebugClassLoader::disable(); + restore_error_handler(); + } + + $bundleDeprecations = array_values(array_filter($deprecations, function ($message) { + return strpos($message, 'Paysera\\Bundle\\NormalizationBundle\\') !== false; + })); + $this->assertSame([], $bundleDeprecations); + } +} From cbbdd2cf9c0c14791d82852012ea81fdf58d2978 Mon Sep 17 00:00:00 2001 From: Vinayak Iyer Date: Wed, 23 Sep 2026 20:54:20 +0530 Subject: [PATCH 07/15] EE-282 Record what the change breaks and leaves, and why the workflow step is needed - CHANGELOG: the breaking note now covers both classes (the extension's subclasses lose the HttpKernel class-cache methods too) and says why this is still a minor: neither class is in the public API the README defines. It also says that Symfony 7.4's notice about XML service definitions remains. - load() documents its $configs element type, as ExtensionInterface does. - The workflow comment names every job the advisories step is for, including Symfony 6.0 on PHP 8.0 and PHPUnit 6.5 on PHP 7.2, and the Composer version that started blocking (2.9). Co-Authored-By: Claude Opus 5.5 (1M context) --- .github/workflows/ci.yml | 5 +++-- CHANGELOG.md | 8 ++++++-- src/DependencyInjection/PayseraNormalizationExtension.php | 1 + 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f0122c6..17225ab 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -62,8 +62,9 @@ jobs: php-version: ${{ matrix.php }} tools: flex - # Symfony 3.4 and 4.4 are end-of-life, and their symfony/cache releases have security advisories. - # Composer 2.10 refuses to install them by default. This only affects the test install here. + # Composer 2.9 and later refuse packages with security advisories. The last Symfony 3.4 and 4.4 releases of + # symfony/cache, http-foundation, routing and yaml have them, and so do the Symfony 6.0 releases PHP 8.0 installs + # and the PHPUnit 6.5 release PHP 7.2 installs. This only affects the test install here. # PHP 7.0 and 7.1 get Composer 2.2, which does not block advisories and does not know this setting. - name: Allow packages with security advisories if: matrix.php != '7.0' && matrix.php != '7.1' diff --git a/CHANGELOG.md b/CHANGELOG.md index 54a763c..49c9629 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,11 +10,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - `Configuration::getConfigTreeBuilder()` declares the `TreeBuilder` return type, which Symfony 7 requires. - Breaking for subclasses that override `getConfigTreeBuilder()` without a return type: add `: TreeBuilder` to the override. - `PayseraNormalizationExtension` extends `Symfony\Component\DependencyInjection\Extension\Extension` instead of `Symfony\Component\HttpKernel\DependencyInjection\Extension` (internal since Symfony 7.1, deprecated in 8.1), and `load()` documents its `void` return. Together with the return type above, this removes the deprecation notices that - Symfony 6.4 and 7.4 report for the bundle's own classes when their debug class loader is on. + Symfony 6.4 and 7.4 report for the bundle's own classes when their debug class loader is on. On Symfony 7.4 the bundle's + XML service definitions still raise Symfony's own "XML configuration format is deprecated" notice. +- Breaking for subclasses of these two classes: an override of `getConfigTreeBuilder()` must declare `: TreeBuilder`, and a + subclass of the extension can no longer call the class-cache methods of Symfony's HttpKernel `Extension` + (`addClassesToCompile()`, `addAnnotatedClassesToCompile()` and their getters). The classes are not part of the bundle's + public API (see "Semantic versioning" in the README), so this is a minor release. - `symfony/config`, `symfony/dependency-injection` and `symfony/http-kernel`, which the bundle's code uses, are required explicitly, at the same versions as `symfony/framework-bundle`. diff --git a/src/DependencyInjection/PayseraNormalizationExtension.php b/src/DependencyInjection/PayseraNormalizationExtension.php index 5946677..8dde0df 100644 --- a/src/DependencyInjection/PayseraNormalizationExtension.php +++ b/src/DependencyInjection/PayseraNormalizationExtension.php @@ -17,6 +17,7 @@ public static function supportsAutoconfiguration() } /** + * @param array> $configs * @return void */ public function load(array $configs, ContainerBuilder $container) From 52c2b15fc3978058fb2b3d28e7139b67446a69db Mon Sep 17 00:00:00 2001 From: Vinayak Iyer Date: Wed, 23 Sep 2026 21:11:29 +0530 Subject: [PATCH 08/15] EE-282 Keep the deprecation test green on PHP 8.4 with Symfony 4.4 On PHP 8.4, compiling Symfony 4.4's DebugClassLoader raises an "implicitly nullable parameter" notice. The test loaded that class before installing its error handler, so the notice reached the separate process's output and PHPUnit failed the test (workflow job PHP 8.4 x Symfony 4.*). The handler now goes in first. Co-Authored-By: Claude Opus 5.5 (1M context) --- .../FunctionalDeprecationNoticesTest.php | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/tests/Functional/FunctionalDeprecationNoticesTest.php b/tests/Functional/FunctionalDeprecationNoticesTest.php index 48b5d89..d70a312 100644 --- a/tests/Functional/FunctionalDeprecationNoticesTest.php +++ b/tests/Functional/FunctionalDeprecationNoticesTest.php @@ -22,10 +22,8 @@ class FunctionalDeprecationNoticesTest extends TestCase */ public function testLoadingTheBundleClassesRaisesNoDeprecationNamingThem() { - if (!class_exists(DebugClassLoader::class)) { - $this->markTestSkipped('symfony/error-handler is not installed (Symfony below 4.4)'); - } - + // collect before anything loads: on PHP 8.4, compiling Symfony 4.4's own files raises deprecation notices, and a + // notice that reaches the child process's output fails the test whatever it says $deprecations = []; set_error_handler(function ($type, $message) use (&$deprecations) { if ($type === E_USER_DEPRECATED || $type === E_DEPRECATED) { @@ -34,13 +32,19 @@ public function testLoadingTheBundleClassesRaisesNoDeprecationNamingThem() return true; }); - DebugClassLoader::enable(); try { - class_exists(PayseraNormalizationBundle::class); - class_exists(PayseraNormalizationExtension::class); - class_exists(Configuration::class); + if (!class_exists(DebugClassLoader::class)) { + $this->markTestSkipped('symfony/error-handler is not installed (Symfony below 4.4)'); + } + DebugClassLoader::enable(); + try { + class_exists(PayseraNormalizationBundle::class); + class_exists(PayseraNormalizationExtension::class); + class_exists(Configuration::class); + } finally { + DebugClassLoader::disable(); + } } finally { - DebugClassLoader::disable(); restore_error_handler(); } From 084fff974cd2057d656126ed9c3025194e28be33 Mon Sep 17 00:00:00 2001 From: Vinayak Iyer Date: Thu, 24 Sep 2026 12:58:49 +0530 Subject: [PATCH 09/15] EE-282 Answer the second review: the test checks its own preconditions, wording made exact - The deprecation test now asserts that the bundle's classes are not loaded before the debug class loader is enabled and that they load inside it. Without that, dropping `@preserveGlobalState disabled` let the child process re-include classes the parent had loaded, and the test passed with the fix reverted. - The test's closures are typed. - load()'s `$configs` may hold null entries: on Symfony 3.4 and 4.4 a `paysera_normalization: ~` block arrives as null. - The workflow comment no longer lists four packages where more are affected, and says the setting it writes exists from Composer 2.10. - CHANGELOG: the notices removed are reported from Symfony 5.4 on, not only on 6.4 and 7.4; the class-cache methods are named per Symfony line, and an override of their getter is ignored as well. Co-Authored-By: Claude Opus 5.5 (1M context) --- .github/workflows/ci.yml | 9 +++++---- CHANGELOG.md | 11 ++++++----- .../PayseraNormalizationExtension.php | 2 +- .../FunctionalDeprecationNoticesTest.php | 18 +++++++++++++----- 4 files changed, 25 insertions(+), 15 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 17225ab..0660ba2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -62,10 +62,11 @@ jobs: php-version: ${{ matrix.php }} tools: flex - # Composer 2.9 and later refuse packages with security advisories. The last Symfony 3.4 and 4.4 releases of - # symfony/cache, http-foundation, routing and yaml have them, and so do the Symfony 6.0 releases PHP 8.0 installs - # and the PHPUnit 6.5 release PHP 7.2 installs. This only affects the test install here. - # PHP 7.0 and 7.1 get Composer 2.2, which does not block advisories and does not know this setting. + # Composer refuses packages with security advisories by default since 2.9. The last Symfony 3.4 and 4.4 releases + # of several components the tests install have them, and so do the Symfony 6.0 releases PHP 8.0 installs and the + # PHPUnit 6.5 release PHP 7.2 installs. This only affects the test install here. The setting exists from Composer + # 2.10, which setup-php installs for PHP 7.2 and later; PHP 7.0 and 7.1 get Composer 2.2, which does not block + # advisories and does not know the setting. - name: Allow packages with security advisories if: matrix.php != '7.0' && matrix.php != '7.1' run: composer config policy.advisories.block false diff --git a/CHANGELOG.md b/CHANGELOG.md index 49c9629..d0dcb16 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,12 +13,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `PayseraNormalizationExtension` extends `Symfony\Component\DependencyInjection\Extension\Extension` instead of `Symfony\Component\HttpKernel\DependencyInjection\Extension` (internal since Symfony 7.1, deprecated in 8.1), and `load()` documents its `void` return. Together with the return type above, this removes the deprecation notices that - Symfony 6.4 and 7.4 report for the bundle's own classes when their debug class loader is on. On Symfony 7.4 the bundle's - XML service definitions still raise Symfony's own "XML configuration format is deprecated" notice. + Symfony 5.4 and later report for the bundle's own classes when their debug class loader is on. On Symfony 7.4 the + bundle's XML service definitions still raise Symfony's own "XML configuration format is deprecated" notice. - Breaking for subclasses of these two classes: an override of `getConfigTreeBuilder()` must declare `: TreeBuilder`, and a - subclass of the extension can no longer call the class-cache methods of Symfony's HttpKernel `Extension` - (`addClassesToCompile()`, `addAnnotatedClassesToCompile()` and their getters). The classes are not part of the bundle's - public API (see "Semantic versioning" in the README), so this is a minor release. + subclass of the extension no longer inherits the class-cache methods of Symfony's HttpKernel `Extension` + (`addAnnotatedClassesToCompile()` and its getter, deprecated since Symfony 7.1, and on Symfony 3.4 also + `addClassesToCompile()` and its getter): a call to one of them fails and an override of the getter is ignored. The + classes are not part of the bundle's public API (see "Semantic versioning" in the README), so this is a minor release. - `symfony/config`, `symfony/dependency-injection` and `symfony/http-kernel`, which the bundle's code uses, are required explicitly, at the same versions as `symfony/framework-bundle`. diff --git a/src/DependencyInjection/PayseraNormalizationExtension.php b/src/DependencyInjection/PayseraNormalizationExtension.php index 8dde0df..28398b0 100644 --- a/src/DependencyInjection/PayseraNormalizationExtension.php +++ b/src/DependencyInjection/PayseraNormalizationExtension.php @@ -17,7 +17,7 @@ public static function supportsAutoconfiguration() } /** - * @param array> $configs + * @param array|null> $configs * @return void */ public function load(array $configs, ContainerBuilder $container) diff --git a/tests/Functional/FunctionalDeprecationNoticesTest.php b/tests/Functional/FunctionalDeprecationNoticesTest.php index d70a312..c787b86 100644 --- a/tests/Functional/FunctionalDeprecationNoticesTest.php +++ b/tests/Functional/FunctionalDeprecationNoticesTest.php @@ -22,10 +22,12 @@ class FunctionalDeprecationNoticesTest extends TestCase */ public function testLoadingTheBundleClassesRaisesNoDeprecationNamingThem() { + $classes = [PayseraNormalizationBundle::class, PayseraNormalizationExtension::class, Configuration::class]; + // collect before anything loads: on PHP 8.4, compiling Symfony 4.4's own files raises deprecation notices, and a // notice that reaches the child process's output fails the test whatever it says $deprecations = []; - set_error_handler(function ($type, $message) use (&$deprecations) { + set_error_handler(function (int $type, string $message) use (&$deprecations): bool { if ($type === E_USER_DEPRECATED || $type === E_DEPRECATED) { $deprecations[] = $message; } @@ -36,11 +38,16 @@ public function testLoadingTheBundleClassesRaisesNoDeprecationNamingThem() if (!class_exists(DebugClassLoader::class)) { $this->markTestSkipped('symfony/error-handler is not installed (Symfony below 4.4)'); } + // the check means something only if the classes are loaded here, after the debug class loader is enabled + $this->assertSame([false, false, false], array_map(function (string $class): bool { + return class_exists($class, false); + }, $classes)); + DebugClassLoader::enable(); try { - class_exists(PayseraNormalizationBundle::class); - class_exists(PayseraNormalizationExtension::class); - class_exists(Configuration::class); + $loaded = array_map(function (string $class): bool { + return class_exists($class); + }, $classes); } finally { DebugClassLoader::disable(); } @@ -48,7 +55,8 @@ class_exists(Configuration::class); restore_error_handler(); } - $bundleDeprecations = array_values(array_filter($deprecations, function ($message) { + $this->assertSame([true, true, true], $loaded); + $bundleDeprecations = array_values(array_filter($deprecations, function (string $message): bool { return strpos($message, 'Paysera\\Bundle\\NormalizationBundle\\') !== false; })); $this->assertSame([], $bundleDeprecations); From f055c781c3856f6c1181bfe53a708f82d817049d Mon Sep 17 00:00:00 2001 From: Vinayak Iyer Date: Fri, 25 Sep 2026 16:33:52 +0530 Subject: [PATCH 10/15] EE-282 Keep the advisory step's reasoning in the PR description Co-Authored-By: Claude Opus 5.5 (1M context) --- .github/workflows/ci.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0660ba2..9384402 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -62,11 +62,6 @@ jobs: php-version: ${{ matrix.php }} tools: flex - # Composer refuses packages with security advisories by default since 2.9. The last Symfony 3.4 and 4.4 releases - # of several components the tests install have them, and so do the Symfony 6.0 releases PHP 8.0 installs and the - # PHPUnit 6.5 release PHP 7.2 installs. This only affects the test install here. The setting exists from Composer - # 2.10, which setup-php installs for PHP 7.2 and later; PHP 7.0 and 7.1 get Composer 2.2, which does not block - # advisories and does not know the setting. - name: Allow packages with security advisories if: matrix.php != '7.0' && matrix.php != '7.1' run: composer config policy.advisories.block false From 80efbdf3df54661b9ab33ef38c7394eba42ce9a8 Mon Sep 17 00:00:00 2001 From: Vinayak Iyer Date: Fri, 25 Sep 2026 18:30:30 +0530 Subject: [PATCH 11/15] EE-282 Keep the deprecation test's reasoning in the PR description Co-Authored-By: Claude Opus 5.5 (1M context) --- tests/Functional/FunctionalDeprecationNoticesTest.php | 8 -------- 1 file changed, 8 deletions(-) diff --git a/tests/Functional/FunctionalDeprecationNoticesTest.php b/tests/Functional/FunctionalDeprecationNoticesTest.php index c787b86..5bc7843 100644 --- a/tests/Functional/FunctionalDeprecationNoticesTest.php +++ b/tests/Functional/FunctionalDeprecationNoticesTest.php @@ -12,11 +12,6 @@ class FunctionalDeprecationNoticesTest extends TestCase { /** - * Symfony's debug class loader, which applications run in their dev environment and under symfony/phpunit-bridge, - * checks every class as it is loaded against the parents and interfaces it implements. None of its notices may - * name this bundle's classes. The test runs in its own process so that the classes are loaded after the debug - * class loader is enabled. - * * @runInSeparateProcess * @preserveGlobalState disabled */ @@ -24,8 +19,6 @@ public function testLoadingTheBundleClassesRaisesNoDeprecationNamingThem() { $classes = [PayseraNormalizationBundle::class, PayseraNormalizationExtension::class, Configuration::class]; - // collect before anything loads: on PHP 8.4, compiling Symfony 4.4's own files raises deprecation notices, and a - // notice that reaches the child process's output fails the test whatever it says $deprecations = []; set_error_handler(function (int $type, string $message) use (&$deprecations): bool { if ($type === E_USER_DEPRECATED || $type === E_DEPRECATED) { @@ -38,7 +31,6 @@ public function testLoadingTheBundleClassesRaisesNoDeprecationNamingThem() if (!class_exists(DebugClassLoader::class)) { $this->markTestSkipped('symfony/error-handler is not installed (Symfony below 4.4)'); } - // the check means something only if the classes are loaded here, after the debug class loader is enabled $this->assertSame([false, false, false], array_map(function (string $class): bool { return class_exists($class, false); }, $classes)); From dabe1ef9ace26ad1b056b81623da64ac78beebed Mon Sep 17 00:00:00 2001 From: Vinayak Iyer Date: Mon, 28 Sep 2026 15:47:22 +0530 Subject: [PATCH 12/15] EE-282 Run the workflow jobs with lowest dependencies too The matrix gains dependency-versions lowest next to highest, and the single PHP 7.0 lowest entry became one of those jobs. Six exclude entries take out the 21 lowest combinations whose oldest allowed dependencies cannot run on PHP 8: Symfony 3.4.26 and 4.4.9 on PHP 8.0 and later (libxml deprecation), lib-object-wrapper 0.3.0 on PHP 8.1 and later (ArrayAccess return types), and PHPUnit 9.3.0 on PHP 8.4. The 17 lowest jobs that remain pass. Co-Authored-By: Claude Opus 5.5 (1M context) --- .github/workflows/ci.yml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9384402..c9dd554 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,9 +35,8 @@ jobs: - '6.*' - '7.*' dependency: + - 'lowest' - 'highest' - include: - - { php: '7.0', symfony: '3.*', dependency: 'lowest' } exclude: - { php: '7.1', symfony: '5.*' } - { php: '7.1', symfony: '6.*' } @@ -51,6 +50,12 @@ jobs: - { php: '7.4', symfony: '7.*' } - { php: '8.0', symfony: '7.*' } - { php: '8.1', symfony: '7.*' } + - { php: '8.0', symfony: '3.*', dependency: 'lowest' } + - { php: '8.0', symfony: '4.*', dependency: 'lowest' } + - { php: '8.1', dependency: 'lowest' } + - { php: '8.2', dependency: 'lowest' } + - { php: '8.3', dependency: 'lowest' } + - { php: '8.4', dependency: 'lowest' } steps: - name: Checkout From b6f8902203cfd0407e29d9b6210ec0791c4b05da Mon Sep 17 00:00:00 2001 From: Vinayak Iyer Date: Tue, 29 Sep 2026 23:21:10 +0530 Subject: [PATCH 13/15] EE-282 Define the services in the extension instead of XML Co-Authored-By: Claude Opus 5.5 (1M context) --- CHANGELOG.md | 6 +- .../PayseraNormalizationExtension.php | 65 +++++++- src/Resources/config/services.xml | 37 ----- .../config/services/date_time_normalizer.xml | 24 --- .../PayseraNormalizationExtensionTest.php | 146 ++++++++++++++++++ 5 files changed, 210 insertions(+), 68 deletions(-) delete mode 100644 src/Resources/config/services.xml delete mode 100644 src/Resources/config/services/date_time_normalizer.xml create mode 100644 tests/DependencyInjection/PayseraNormalizationExtensionTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md index d0dcb16..7c753be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,13 +13,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `PayseraNormalizationExtension` extends `Symfony\Component\DependencyInjection\Extension\Extension` instead of `Symfony\Component\HttpKernel\DependencyInjection\Extension` (internal since Symfony 7.1, deprecated in 8.1), and `load()` documents its `void` return. Together with the return type above, this removes the deprecation notices that - Symfony 5.4 and later report for the bundle's own classes when their debug class loader is on. On Symfony 7.4 the - bundle's XML service definitions still raise Symfony's own "XML configuration format is deprecated" notice. + Symfony 5.4 and later report for the bundle's own classes when their debug class loader is on. - Breaking for subclasses of these two classes: an override of `getConfigTreeBuilder()` must declare `: TreeBuilder`, and a subclass of the extension no longer inherits the class-cache methods of Symfony's HttpKernel `Extension` (`addAnnotatedClassesToCompile()` and its getter, deprecated since Symfony 7.1, and on Symfony 3.4 also `addClassesToCompile()` and its getter): a call to one of them fails and an override of the getter is ignored. The classes are not part of the bundle's public API (see "Semantic versioning" in the README), so this is a minor release. +- The bundle's services are defined in `PayseraNormalizationExtension` instead of `Resources/config/services.xml` and + `Resources/config/services/date_time_normalizer.xml`, which are removed, so Symfony 7.4 reports no "XML configuration + format is deprecated" notice. The service ids, classes, arguments, tags and aliases are unchanged. - `symfony/config`, `symfony/dependency-injection` and `symfony/http-kernel`, which the bundle's code uses, are required explicitly, at the same versions as `symfony/framework-bundle`. diff --git a/src/DependencyInjection/PayseraNormalizationExtension.php b/src/DependencyInjection/PayseraNormalizationExtension.php index 28398b0..4be791b 100644 --- a/src/DependencyInjection/PayseraNormalizationExtension.php +++ b/src/DependencyInjection/PayseraNormalizationExtension.php @@ -3,11 +3,20 @@ namespace Paysera\Bundle\NormalizationBundle\DependencyInjection; +use Paysera\Component\Normalization\CoreDenormalizer; +use Paysera\Component\Normalization\CoreNormalizer; +use Paysera\Component\Normalization\DataFilter; +use Paysera\Component\Normalization\Normalizer\DateTimeImmutableNormalizer; +use Paysera\Component\Normalization\Normalizer\DateTimeNormalizer; +use Paysera\Component\Normalization\NormalizerRegistryInterface; +use Paysera\Component\Normalization\Registry\GroupedNormalizerRegistryProvider; use Paysera\Component\Normalization\TypeAwareInterface; +use Paysera\Component\Normalization\TypeGuesser; +use Symfony\Component\DependencyInjection\Alias; use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\Config\FileLocator; +use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Extension\Extension; -use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; +use Symfony\Component\DependencyInjection\Reference; class PayseraNormalizationExtension extends Extension { @@ -25,8 +34,7 @@ public function load(array $configs, ContainerBuilder $container) $configuration = new Configuration(); $config = $this->processConfiguration($configuration, $configs); - $loader = new XmlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config')); - $loader->load('services.xml'); + $this->registerServices($container); if (self::supportsAutoconfiguration()) { $container @@ -38,7 +46,54 @@ public function load(array $configs, ContainerBuilder $container) if (isset($config['register_normalizers']['date_time'])) { $dateTimeFormat = $config['register_normalizers']['date_time']['format']; $container->setParameter('paysera_normalization.date_time_normalizer.format', $dateTimeFormat); - $loader->load('services/date_time_normalizer.xml'); + $this->registerDateTimeNormalizers($container); } } + + private function registerServices(ContainerBuilder $container) + { + $container->setDefinition( + 'paysera_normalization.normalizer_registry_provider', + (new Definition(GroupedNormalizerRegistryProvider::class))->setLazy(true) + ); + $container->setDefinition( + 'paysera_normalization.normalizer_registry', + (new Definition(NormalizerRegistryInterface::class)) + ->setLazy(true) + ->setFactory([ + new Reference('paysera_normalization.normalizer_registry_provider'), + 'getDefaultNormalizerRegistry', + ]) + ); + $container->setDefinition('paysera_normalization.type_guesser', new Definition(TypeGuesser::class)); + $container->setDefinition('paysera_normalization.data_filter', new Definition(DataFilter::class)); + + $container->setDefinition('paysera_normalization.core_normalizer', new Definition(CoreNormalizer::class, [ + new Reference('paysera_normalization.normalizer_registry_provider'), + new Reference('paysera_normalization.type_guesser'), + new Reference('paysera_normalization.data_filter'), + ])); + $container->setAlias(CoreNormalizer::class, new Alias('paysera_normalization.core_normalizer')); + + $container->setDefinition('paysera_normalization.core_denormalizer', new Definition(CoreDenormalizer::class, [ + new Reference('paysera_normalization.normalizer_registry_provider'), + ])); + $container->setAlias(CoreDenormalizer::class, new Alias('paysera_normalization.core_denormalizer')); + } + + private function registerDateTimeNormalizers(ContainerBuilder $container) + { + $format = '%paysera_normalization.date_time_normalizer.format%'; + $container->setDefinition( + 'paysera_normalization.date_time_normalizer', + (new Definition(DateTimeNormalizer::class, [$format])) + ->addTag('paysera_normalization.autoconfigured_normalizer') + ); + $container->setDefinition( + 'paysera_normalization.date_time_immutable_normalizer', + (new Definition(DateTimeImmutableNormalizer::class, [$format])) + ->addTag('paysera_normalization.autoconfigured_normalizer') + ->addTag('paysera_normalization.mixed_type_denormalizer', ['type' => 'DateTimeInterface']) + ); + } } diff --git a/src/Resources/config/services.xml b/src/Resources/config/services.xml deleted file mode 100644 index ef31d50..0000000 --- a/src/Resources/config/services.xml +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/Resources/config/services/date_time_normalizer.xml b/src/Resources/config/services/date_time_normalizer.xml deleted file mode 100644 index c1f8326..0000000 --- a/src/Resources/config/services/date_time_normalizer.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - %paysera_normalization.date_time_normalizer.format% - - - - - %paysera_normalization.date_time_normalizer.format% - - - - - diff --git a/tests/DependencyInjection/PayseraNormalizationExtensionTest.php b/tests/DependencyInjection/PayseraNormalizationExtensionTest.php new file mode 100644 index 0000000..f30bfab --- /dev/null +++ b/tests/DependencyInjection/PayseraNormalizationExtensionTest.php @@ -0,0 +1,146 @@ + ['date_time' => ['format' => 'U']]]]; + + /** + * @dataProvider loadProvider + * @param array> $configs + * @param array> $expected + */ + public function testLoadRegistersTheServices(array $configs, array $expected) + { + $container = new ContainerBuilder(); + $empty = new ContainerBuilder(); + + (new PayseraNormalizationExtension())->load($configs, $container); + + $this->assertSame(self::normalize($expected), self::normalize([ + 'definitions' => array_diff_key($container->getDefinitions(), $empty->getDefinitions()), + 'aliases' => array_diff_key($container->getAliases(), $empty->getAliases()), + 'parameters' => $container->getParameterBag()->all(), + ])); + } + + /** + * @return array>, array>}> + */ + public static function loadProvider(): array + { + $provider = new Reference('paysera_normalization.normalizer_registry_provider'); + $definitions = [ + 'paysera_normalization.normalizer_registry_provider' => + (new Definition(GroupedNormalizerRegistryProvider::class))->setLazy(true), + 'paysera_normalization.normalizer_registry' => (new Definition(NormalizerRegistryInterface::class)) + ->setLazy(true) + ->setFactory([$provider, 'getDefaultNormalizerRegistry']), + 'paysera_normalization.type_guesser' => new Definition(TypeGuesser::class), + 'paysera_normalization.data_filter' => new Definition(DataFilter::class), + 'paysera_normalization.core_normalizer' => new Definition(CoreNormalizer::class, [ + $provider, + new Reference('paysera_normalization.type_guesser'), + new Reference('paysera_normalization.data_filter'), + ]), + 'paysera_normalization.core_denormalizer' => new Definition(CoreDenormalizer::class, [$provider]), + ]; + $aliases = [ + CoreNormalizer::class => new Alias('paysera_normalization.core_normalizer'), + CoreDenormalizer::class => new Alias('paysera_normalization.core_denormalizer'), + ]; + $format = '%paysera_normalization.date_time_normalizer.format%'; + + return [ + 'without the date_time normalizers' => [ + [], + ['definitions' => $definitions, 'aliases' => $aliases, 'parameters' => []], + ], + 'with the date_time normalizers' => [ + self::DATE_TIME_CONFIGS, + [ + 'definitions' => $definitions + [ + 'paysera_normalization.date_time_normalizer' => + (new Definition(DateTimeNormalizer::class, [$format])) + ->addTag('paysera_normalization.autoconfigured_normalizer'), + 'paysera_normalization.date_time_immutable_normalizer' => + (new Definition(DateTimeImmutableNormalizer::class, [$format])) + ->addTag('paysera_normalization.autoconfigured_normalizer') + ->addTag( + 'paysera_normalization.mixed_type_denormalizer', + ['type' => 'DateTimeInterface'] + ), + ], + 'aliases' => $aliases, + 'parameters' => ['paysera_normalization.date_time_normalizer.format' => 'U'], + ], + ], + ]; + } + + public function testLoadRaisesNoDeprecation() + { + $deprecations = []; + set_error_handler(function (int $type, string $message) use (&$deprecations): bool { + $deprecations[] = $message; + + return true; + }, E_USER_DEPRECATED); + try { + (new PayseraNormalizationExtension())->load(self::DATE_TIME_CONFIGS, new ContainerBuilder()); + } finally { + restore_error_handler(); + } + + $this->assertSame([], $deprecations); + } + + /** + * @param array> $services + * @return array + */ + private static function normalize(array $services): array + { + ksort($services['definitions']); + ksort($services['aliases']); + + return self::export($services); + } + + /** + * @param mixed $value + * @return mixed + */ + private static function export($value) + { + if (is_object($value)) { + return [get_class($value) => self::export((array)$value)]; + } + if (!is_array($value)) { + return $value; + } + $exported = []; + foreach ($value as $key => $item) { + $exported[$key] = self::export($item); + } + + return $exported; + } +} From aabd3523600e16310d364c8606adfaeaf8186a99 Mon Sep 17 00:00:00 2001 From: Vinayak Iyer Date: Tue, 29 Sep 2026 23:21:10 +0530 Subject: [PATCH 14/15] EE-282 Raise the floors the lowest jobs need instead of excluding them Co-Authored-By: Claude Opus 5.5 (1M context) --- .github/workflows/ci.yml | 9 +++------ CHANGELOG.md | 4 +++- composer.json | 6 +++--- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c9dd554..59a25ae 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,7 +38,10 @@ jobs: - 'lowest' - 'highest' exclude: + - { php: '7.0', symfony: '4.*' } + - { php: '7.0', symfony: '5.*' } - { php: '7.1', symfony: '5.*' } + - { php: '7.0', symfony: '6.*' } - { php: '7.1', symfony: '6.*' } - { php: '7.2', symfony: '6.*' } - { php: '7.3', symfony: '6.*' } @@ -50,12 +53,6 @@ jobs: - { php: '7.4', symfony: '7.*' } - { php: '8.0', symfony: '7.*' } - { php: '8.1', symfony: '7.*' } - - { php: '8.0', symfony: '3.*', dependency: 'lowest' } - - { php: '8.0', symfony: '4.*', dependency: 'lowest' } - - { php: '8.1', dependency: 'lowest' } - - { php: '8.2', dependency: 'lowest' } - - { php: '8.3', dependency: 'lowest' } - - { php: '8.4', dependency: 'lowest' } steps: - name: Checkout diff --git a/CHANGELOG.md b/CHANGELOG.md index 7c753be..b4e243b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,7 +23,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 `Resources/config/services/date_time_normalizer.xml`, which are removed, so Symfony 7.4 reports no "XML configuration format is deprecated" notice. The service ids, classes, arguments, tags and aliases are unchanged. - `symfony/config`, `symfony/dependency-injection` and `symfony/http-kernel`, which the bundle's code uses, are required - explicitly, at the same versions as `symfony/framework-bundle`. + explicitly. `symfony/framework-bundle`, which only the bundle's tests use, moves to `require-dev`. +- `paysera/lib-normalization` starts at 1.3.1: in earlier releases `DateTimeNormalizer` fails on PHP 8.2 and later, + where `date_get_last_errors()` returns `false`. ## 1.3.1 ### Added diff --git a/composer.json b/composer.json index a4fd979..0f85fbd 100644 --- a/composer.json +++ b/composer.json @@ -16,15 +16,15 @@ }, "require": { "php": "^7.0 || ^8.0", - "paysera/lib-normalization": "^1.2", - "symfony/framework-bundle": "^3.4.26|^4.2.7|^5.4|^6.0|^7.4", + "paysera/lib-normalization": "^1.3.1", "symfony/config": "^3.4.26|^4.2.7|^5.4|^6.0|^7.4", "symfony/dependency-injection": "^3.4.26|^4.2.7|^5.4|^6.0|^7.4", "symfony/http-kernel": "^3.4.26|^4.2.7|^5.4|^6.0|^7.4", "paysera/lib-dependency-injection": "^1.3.0" }, "require-dev": { - "phpunit/phpunit": "^6.0 || ^9.0", + "phpunit/phpunit": "^6.0 || ^9.6.33", + "symfony/framework-bundle": "^3.4.26|^4.2.7|^5.4|^6.0|^7.4", "symfony/yaml": "^2.7|^3.0|^4.0|^5.0|^6.0|^7.4", "mockery/mockery": "^1.2", "yoast/phpunit-polyfills": "^1.0" From 1df96f05cae97fd4b63c29cff8c9c95e8969e78e Mon Sep 17 00:00:00 2001 From: Vinayak Iyer Date: Wed, 30 Sep 2026 13:21:49 +0530 Subject: [PATCH 15/15] EE-282 Let the deprecation test fail on warnings and notices The test's error handler took every error type and returned true, so a warning or a notice raised while the bundle's classes load was discarded and the test passed. It now handles E_USER_DEPRECATED and E_DEPRECATED only. Co-Authored-By: Claude Opus 5.5 (1M context) --- tests/Functional/FunctionalDeprecationNoticesTest.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/Functional/FunctionalDeprecationNoticesTest.php b/tests/Functional/FunctionalDeprecationNoticesTest.php index 5bc7843..d1c549b 100644 --- a/tests/Functional/FunctionalDeprecationNoticesTest.php +++ b/tests/Functional/FunctionalDeprecationNoticesTest.php @@ -21,12 +21,10 @@ public function testLoadingTheBundleClassesRaisesNoDeprecationNamingThem() $deprecations = []; set_error_handler(function (int $type, string $message) use (&$deprecations): bool { - if ($type === E_USER_DEPRECATED || $type === E_DEPRECATED) { - $deprecations[] = $message; - } + $deprecations[] = $message; return true; - }); + }, E_USER_DEPRECATED | E_DEPRECATED); try { if (!class_exists(DebugClassLoader::class)) { $this->markTestSkipped('symfony/error-handler is not installed (Symfony below 4.4)');