From 1a176bdca4cddc20b9324bd682fc1fcfbe766b9b Mon Sep 17 00:00:00 2001 From: Steven Renaux Date: Fri, 25 Apr 2025 11:18:22 +0200 Subject: [PATCH 01/10] Fix ServiceMethodsSubscriberTrait for nullable service --- Service/ServiceSubscriberTrait.php | 2 +- Tests/Service/ServiceSubscriberTraitTest.php | 15 +++++++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/Service/ServiceSubscriberTrait.php b/Service/ServiceSubscriberTrait.php index f3b450c..ec6a114 100644 --- a/Service/ServiceSubscriberTrait.php +++ b/Service/ServiceSubscriberTrait.php @@ -51,7 +51,7 @@ public static function getSubscribedServices(): array $attribute = $attribute->newInstance(); $attribute->key ??= self::class.'::'.$method->name; $attribute->type ??= $returnType instanceof \ReflectionNamedType ? $returnType->getName() : (string) $returnType; - $attribute->nullable = $returnType->allowsNull(); + $attribute->nullable = $attribute->nullable ?: $returnType->allowsNull(); if ($attribute->attributes) { $services[] = $attribute; diff --git a/Tests/Service/ServiceSubscriberTraitTest.php b/Tests/Service/ServiceSubscriberTraitTest.php index ba37026..6b9785e 100644 --- a/Tests/Service/ServiceSubscriberTraitTest.php +++ b/Tests/Service/ServiceSubscriberTraitTest.php @@ -27,7 +27,8 @@ public function testMethodsOnParentsAndChildrenAreIgnoredInGetSubscribedServices { $expected = [ TestService::class.'::aService' => Service2::class, - TestService::class.'::nullableService' => '?'.Service2::class, + TestService::class.'::nullableInAttribute' => '?'.Service2::class, + TestService::class.'::nullableReturnType' => '?'.Service2::class, new SubscribedService(TestService::class.'::withAttribute', Service2::class, true, new Required()), ]; @@ -103,8 +104,18 @@ public function aService(): Service2 { } + #[SubscribedService(nullable: true)] + public function nullableInAttribute(): Service2 + { + if (!$this->container->has(__METHOD__)) { + throw new \LogicException(); + } + + return $this->container->get(__METHOD__); + } + #[SubscribedService] - public function nullableService(): ?Service2 + public function nullableReturnType(): ?Service2 { } From 4c287a7604bd0aa4c0d2acd1d362b53087354a6f Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 10 Jul 2025 09:12:18 +0200 Subject: [PATCH 02/10] CS fixes --- Cache/CacheTrait.php | 4 ++-- HttpClient/Test/HttpClientTestCase.php | 2 +- Service/ServiceLocatorTrait.php | 10 +++++----- Service/ServiceSubscriberTrait.php | 4 ++-- Tests/Service/ServiceSubscriberTraitTest.php | 4 ++-- Translation/Test/TranslatorTest.php | 4 ++-- Translation/TranslatorTrait.php | 2 +- 7 files changed, 15 insertions(+), 15 deletions(-) diff --git a/Cache/CacheTrait.php b/Cache/CacheTrait.php index c2f6580..4c5449b 100644 --- a/Cache/CacheTrait.php +++ b/Cache/CacheTrait.php @@ -38,7 +38,7 @@ public function delete(string $key): bool private function doGet(CacheItemPoolInterface $pool, string $key, callable $callback, ?float $beta, ?array &$metadata = null, ?LoggerInterface $logger = null): mixed { if (0 > $beta ??= 1.0) { - throw new class(sprintf('Argument "$beta" provided to "%s::get()" must be a positive number, %f given.', static::class, $beta)) extends \InvalidArgumentException implements InvalidArgumentException {}; + throw new class(\sprintf('Argument "$beta" provided to "%s::get()" must be a positive number, %f given.', static::class, $beta)) extends \InvalidArgumentException implements InvalidArgumentException {}; } $item = $pool->getItem($key); @@ -54,7 +54,7 @@ private function doGet(CacheItemPoolInterface $pool, string $key, callable $call $item->expiresAt(null); $logger?->info('Item "{key}" elected for early recomputation {delta}s before its expiration', [ 'key' => $key, - 'delta' => sprintf('%.1f', $expiry - $now), + 'delta' => \sprintf('%.1f', $expiry - $now), ]); } } diff --git a/HttpClient/Test/HttpClientTestCase.php b/HttpClient/Test/HttpClientTestCase.php index b150f0c..a1464a3 100644 --- a/HttpClient/Test/HttpClientTestCase.php +++ b/HttpClient/Test/HttpClientTestCase.php @@ -25,7 +25,7 @@ abstract class HttpClientTestCase extends TestCase { public static function setUpBeforeClass(): void { - if (!function_exists('ob_gzhandler')) { + if (!\function_exists('ob_gzhandler')) { static::markTestSkipped('The "ob_gzhandler" function is not available.'); } diff --git a/Service/ServiceLocatorTrait.php b/Service/ServiceLocatorTrait.php index b62ec3e..acb37fe 100644 --- a/Service/ServiceLocatorTrait.php +++ b/Service/ServiceLocatorTrait.php @@ -91,16 +91,16 @@ private function createNotFoundException(string $id): NotFoundExceptionInterface } else { $last = array_pop($alternatives); if ($alternatives) { - $message = sprintf('only knows about the "%s" and "%s" services.', implode('", "', $alternatives), $last); + $message = \sprintf('only knows about the "%s" and "%s" services.', implode('", "', $alternatives), $last); } else { - $message = sprintf('only knows about the "%s" service.', $last); + $message = \sprintf('only knows about the "%s" service.', $last); } } if ($this->loading) { - $message = sprintf('The service "%s" has a dependency on a non-existent service "%s". This locator %s', end($this->loading), $id, $message); + $message = \sprintf('The service "%s" has a dependency on a non-existent service "%s". This locator %s', end($this->loading), $id, $message); } else { - $message = sprintf('Service "%s" not found: the current service locator %s', $id, $message); + $message = \sprintf('Service "%s" not found: the current service locator %s', $id, $message); } return new class($message) extends \InvalidArgumentException implements NotFoundExceptionInterface { @@ -109,7 +109,7 @@ private function createNotFoundException(string $id): NotFoundExceptionInterface private function createCircularReferenceException(string $id, array $path): ContainerExceptionInterface { - return new class(sprintf('Circular reference detected for service "%s", path: "%s".', $id, implode(' -> ', $path))) extends \RuntimeException implements ContainerExceptionInterface { + return new class(\sprintf('Circular reference detected for service "%s", path: "%s".', $id, implode(' -> ', $path))) extends \RuntimeException implements ContainerExceptionInterface { }; } } diff --git a/Service/ServiceSubscriberTrait.php b/Service/ServiceSubscriberTrait.php index ec6a114..c3c12ce 100644 --- a/Service/ServiceSubscriberTrait.php +++ b/Service/ServiceSubscriberTrait.php @@ -40,11 +40,11 @@ public static function getSubscribedServices(): array } if ($method->isStatic() || $method->isAbstract() || $method->isGenerator() || $method->isInternal() || $method->getNumberOfRequiredParameters()) { - throw new \LogicException(sprintf('Cannot use "%s" on method "%s::%s()" (can only be used on non-static, non-abstract methods with no parameters).', SubscribedService::class, self::class, $method->name)); + throw new \LogicException(\sprintf('Cannot use "%s" on method "%s::%s()" (can only be used on non-static, non-abstract methods with no parameters).', SubscribedService::class, self::class, $method->name)); } if (!$returnType = $method->getReturnType()) { - throw new \LogicException(sprintf('Cannot use "%s" on methods without a return type in "%s::%s()".', SubscribedService::class, $method->name, self::class)); + throw new \LogicException(\sprintf('Cannot use "%s" on methods without a return type in "%s::%s()".', SubscribedService::class, $method->name, self::class)); } /* @var SubscribedService $attribute */ diff --git a/Tests/Service/ServiceSubscriberTraitTest.php b/Tests/Service/ServiceSubscriberTraitTest.php index 6b9785e..7779de8 100644 --- a/Tests/Service/ServiceSubscriberTraitTest.php +++ b/Tests/Service/ServiceSubscriberTraitTest.php @@ -49,7 +49,7 @@ public function testParentNotCalledIfHasMagicCall() $container = new class([]) implements ContainerInterface { use ServiceLocatorTrait; }; - $service = new class() extends ParentWithMagicCall { + $service = new class extends ParentWithMagicCall { use ServiceSubscriberTrait; }; @@ -62,7 +62,7 @@ public function testParentNotCalledIfNoParent() $container = new class([]) implements ContainerInterface { use ServiceLocatorTrait; }; - $service = new class() { + $service = new class { use ServiceSubscriberTrait; }; diff --git a/Translation/Test/TranslatorTest.php b/Translation/Test/TranslatorTest.php index 18e6690..b68d027 100644 --- a/Translation/Test/TranslatorTest.php +++ b/Translation/Test/TranslatorTest.php @@ -45,7 +45,7 @@ protected function tearDown(): void public function getTranslator(): TranslatorInterface { - return new class() implements TranslatorInterface { + return new class implements TranslatorInterface { use TranslatorTrait; }; } @@ -365,7 +365,7 @@ protected function validateMatrix(string $nplural, array $matrix, bool $expectSu protected function generateTestData($langCodes) { - $translator = new class() { + $translator = new class { use TranslatorTrait { getPluralizationRule as public; } diff --git a/Translation/TranslatorTrait.php b/Translation/TranslatorTrait.php index 63f6fb3..06210b0 100644 --- a/Translation/TranslatorTrait.php +++ b/Translation/TranslatorTrait.php @@ -111,7 +111,7 @@ public function trans(?string $id, array $parameters = [], ?string $domain = nul return strtr($standardRules[0], $parameters); } - $message = sprintf('Unable to choose a translation for "%s" with locale "%s" for value "%d". Double check that this translation has the correct plural options (e.g. "There is one apple|There are %%count%% apples").', $id, $locale, $number); + $message = \sprintf('Unable to choose a translation for "%s" with locale "%s" for value "%d". Double check that this translation has the correct plural options (e.g. "There is one apple|There are %%count%% apples").', $id, $locale, $number); if (class_exists(InvalidArgumentException::class)) { throw new InvalidArgumentException($message); From 6c6270f9d3cf2b57b0ec1a11222015f62a5fa4f5 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sat, 12 Jul 2025 11:35:41 +0200 Subject: [PATCH 03/10] Fix @var phpdoc --- Service/ServiceSubscriberTrait.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Service/ServiceSubscriberTrait.php b/Service/ServiceSubscriberTrait.php index c3c12ce..93d0d5f 100644 --- a/Service/ServiceSubscriberTrait.php +++ b/Service/ServiceSubscriberTrait.php @@ -47,7 +47,7 @@ public static function getSubscribedServices(): array throw new \LogicException(\sprintf('Cannot use "%s" on methods without a return type in "%s::%s()".', SubscribedService::class, $method->name, self::class)); } - /* @var SubscribedService $attribute */ + /** @var SubscribedService $attribute */ $attribute = $attribute->newInstance(); $attribute->key ??= self::class.'::'.$method->name; $attribute->type ??= $returnType instanceof \ReflectionNamedType ? $returnType->getName() : (string) $returnType; From 888afe4b9606f923340860bb0b856ab9d7a41273 Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Sun, 13 Jul 2025 16:04:15 +0200 Subject: [PATCH 04/10] [Translation] fix support of `TranslatableInterface` in `IdentityTranslator` --- Translation/Test/TranslatorTest.php | 11 +++++++---- Translation/TranslatorTrait.php | 6 ++++++ 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/Translation/Test/TranslatorTest.php b/Translation/Test/TranslatorTest.php index b68d027..fad5d75 100644 --- a/Translation/Test/TranslatorTest.php +++ b/Translation/Test/TranslatorTest.php @@ -12,6 +12,7 @@ namespace Symfony\Contracts\Translation\Test; use PHPUnit\Framework\TestCase; +use Symfony\Component\Translation\TranslatableMessage; use Symfony\Contracts\Translation\TranslatorInterface; use Symfony\Contracts\Translation\TranslatorTrait; @@ -116,10 +117,12 @@ public function testGetLocaleReturnsDefaultLocaleIfNotSet() public static function getTransTests() { - return [ - ['Symfony is great!', 'Symfony is great!', []], - ['Symfony is awesome!', 'Symfony is %what%!', ['%what%' => 'awesome']], - ]; + yield ['Symfony is great!', 'Symfony is great!', []]; + yield ['Symfony is awesome!', 'Symfony is %what%!', ['%what%' => 'awesome']]; + + if (class_exists(TranslatableMessage::class)) { + yield ['He said "Symfony is awesome!".', 'He said "%what%".', ['%what%' => new TranslatableMessage('Symfony is %what%!', ['%what%' => 'awesome'])]]; + } } public static function getTransChoiceTests() diff --git a/Translation/TranslatorTrait.php b/Translation/TranslatorTrait.php index 06210b0..afedd99 100644 --- a/Translation/TranslatorTrait.php +++ b/Translation/TranslatorTrait.php @@ -41,6 +41,12 @@ public function trans(?string $id, array $parameters = [], ?string $domain = nul return ''; } + foreach ($parameters as $k => $v) { + if ($v instanceof TranslatableInterface) { + $parameters[$k] = $v->trans($this, $locale); + } + } + if (!isset($parameters['%count%']) || !is_numeric($parameters['%count%'])) { return strtr($id, $parameters); } From be9fbfbfb84732d7dbe1f29f8503afa8608e8fe5 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Wed, 9 Oct 2024 11:06:51 +0200 Subject: [PATCH 05/10] run tests using PHPUnit 11.5 --- Service/Test/ServiceLocatorTest.php | 14 +++++--------- Tests/Service/ServiceSubscriberTraitTest.php | 7 ++++--- phpunit.xml.dist | 10 +++++++--- 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/Service/Test/ServiceLocatorTest.php b/Service/Test/ServiceLocatorTest.php index 07d12b4..015ca71 100644 --- a/Service/Test/ServiceLocatorTest.php +++ b/Service/Test/ServiceLocatorTest.php @@ -11,13 +11,9 @@ namespace Symfony\Contracts\Service\Test; -class_alias(ServiceLocatorTestCase::class, ServiceLocatorTest::class); - -if (false) { - /** - * @deprecated since PHPUnit 9.6 - */ - class ServiceLocatorTest - { - } +/** + * @deprecated since PHPUnit 9.6 + */ +class ServiceLocatorTest extends ServiceLocatorTestCase +{ } diff --git a/Tests/Service/ServiceSubscriberTraitTest.php b/Tests/Service/ServiceSubscriberTraitTest.php index bf0db2c..b506e6e 100644 --- a/Tests/Service/ServiceSubscriberTraitTest.php +++ b/Tests/Service/ServiceSubscriberTraitTest.php @@ -11,6 +11,8 @@ namespace Symfony\Contracts\Tests\Service; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\IgnoreDeprecations; use PHPUnit\Framework\TestCase; use Psr\Container\ContainerInterface; use Symfony\Contracts\Service\Attribute\Required; @@ -19,9 +21,8 @@ use Symfony\Contracts\Service\ServiceSubscriberInterface; use Symfony\Contracts\Service\ServiceSubscriberTrait; -/** - * @group legacy - */ +#[IgnoreDeprecations] +#[Group('legacy')] class ServiceSubscriberTraitTest extends TestCase { public static function setUpBeforeClass(): void diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 947db86..8a2d548 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,10 +1,11 @@ @@ -20,7 +21,7 @@ - + ./ @@ -30,6 +31,9 @@ ./Translation/Test/ ./vendor - + + + + From 4671917e49e03a37eafd5236a45ae357e62bc9b1 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Mon, 4 Aug 2025 09:53:42 +0200 Subject: [PATCH 06/10] CS fixes --- HttpClient/Test/HttpClientTestCase.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/HttpClient/Test/HttpClientTestCase.php b/HttpClient/Test/HttpClientTestCase.php index 9a528f6..3a1ad13 100644 --- a/HttpClient/Test/HttpClientTestCase.php +++ b/HttpClient/Test/HttpClientTestCase.php @@ -12,6 +12,7 @@ namespace Symfony\Contracts\HttpClient\Test; use PHPUnit\Framework\Attributes\RequiresPhpExtension; +use PHPUnit\Framework\Attributes\TestWithJson; use PHPUnit\Framework\TestCase; use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface; use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface; @@ -346,6 +347,8 @@ public function test304() * @testWith [[]] * [["Content-Length: 7"]] */ + #[TestWithJson('[[]]')] + #[TestWithJson('[["Content-Length: 7"]]')] public function testRedirects(array $headers = []) { $client = $this->getHttpClient(__FUNCTION__); From 8a0093c89d904c5fea1650da4f8679536d4befaa Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Mon, 4 Aug 2025 10:27:42 +0200 Subject: [PATCH 07/10] replace #[TestWithJson] with #[TestWith] --- HttpClient/Test/HttpClientTestCase.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/HttpClient/Test/HttpClientTestCase.php b/HttpClient/Test/HttpClientTestCase.php index 3a1ad13..7f5473a 100644 --- a/HttpClient/Test/HttpClientTestCase.php +++ b/HttpClient/Test/HttpClientTestCase.php @@ -12,7 +12,7 @@ namespace Symfony\Contracts\HttpClient\Test; use PHPUnit\Framework\Attributes\RequiresPhpExtension; -use PHPUnit\Framework\Attributes\TestWithJson; +use PHPUnit\Framework\Attributes\TestWith; use PHPUnit\Framework\TestCase; use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface; use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface; @@ -347,8 +347,8 @@ public function test304() * @testWith [[]] * [["Content-Length: 7"]] */ - #[TestWithJson('[[]]')] - #[TestWithJson('[["Content-Length: 7"]]')] + #[TestWith([[]])] + #[TestWith([['Content-Length: 7']])] public function testRedirects(array $headers = []) { $client = $this->getHttpClient(__FUNCTION__); From 2c50feacba0eae471587fe2acc17416040bc85b1 Mon Sep 17 00:00:00 2001 From: Dariusz Ruminski Date: Sun, 10 Aug 2025 00:28:14 +0200 Subject: [PATCH 08/10] chore: heredoc indentation as of PHP 7.3 https://www.php.net/manual/en/language.types.string.php#language.types.string.syntax.heredoc --- Translation/TranslatorTrait.php | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/Translation/TranslatorTrait.php b/Translation/TranslatorTrait.php index afedd99..06d3e30 100644 --- a/Translation/TranslatorTrait.php +++ b/Translation/TranslatorTrait.php @@ -62,22 +62,22 @@ public function trans(?string $id, array $parameters = [], ?string $domain = nul } $intervalRegexp = <<<'EOF' -/^(?P - ({\s* - (\-?\d+(\.\d+)?[\s*,\s*\-?\d+(\.\d+)?]*) - \s*}) - - | - - (?P[\[\]]) - \s* - (?P-Inf|\-?\d+(\.\d+)?) - \s*,\s* - (?P\+?Inf|\-?\d+(\.\d+)?) - \s* - (?P[\[\]]) -)\s*(?P.*?)$/xs -EOF; + /^(?P + ({\s* + (\-?\d+(\.\d+)?[\s*,\s*\-?\d+(\.\d+)?]*) + \s*}) + + | + + (?P[\[\]]) + \s* + (?P-Inf|\-?\d+(\.\d+)?) + \s*,\s* + (?P\+?Inf|\-?\d+(\.\d+)?) + \s* + (?P[\[\]]) + )\s*(?P.*?)$/xs + EOF; $standardRules = []; foreach ($parts as $part) { From 7bbe08220901461d801f383deda34369011d6a19 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Thu, 11 Dec 2025 08:52:01 +0100 Subject: [PATCH 09/10] Add split.sh config for contracts --- contracts.json | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 contracts.json diff --git a/contracts.json b/contracts.json new file mode 100644 index 0000000..1b1a163 --- /dev/null +++ b/contracts.json @@ -0,0 +1,13 @@ +{ + "subtrees": { + "cache-contracts": "Cache", + "deprecation-contracts": "Deprecation", + "event-dispatcher-contracts": "EventDispatcher", + "http-client-contracts": "HttpClient", + "service-contracts": "Service", + "translation-contracts": "Translation" + }, + "defaults": { + "git_constraint": "<1.8.2" + } +} From afd9a2470fbb9483700eba14860c0ef4714c0947 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Thu, 11 Dec 2025 09:08:58 +0100 Subject: [PATCH 10/10] Fix split.sh config name --- contracts.json => splitsh.json | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename contracts.json => splitsh.json (100%) diff --git a/contracts.json b/splitsh.json similarity index 100% rename from contracts.json rename to splitsh.json