diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index 5f2d77a453ea..d4dafb2aa002 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -1,6 +1,6 @@
| Q | A
| ------------- | ---
-| Branch? | 7.3 for features / 6.4, and 7.2 for bug fixes
+| Branch? | 7.4 for features / 6.4, 7.2, or 7.3 for bug fixes
| Bug fix? | yes/no
| New feature? | yes/no
| Deprecations? | yes/no
diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml
index a82202d055cc..1033e761a2d0 100644
--- a/.github/workflows/scorecards.yml
+++ b/.github/workflows/scorecards.yml
@@ -6,7 +6,7 @@ on:
schedule:
- cron: '34 4 * * 6'
push:
- branches: [ "7.3" ]
+ branches: [ "7.4" ]
# Declare default permissions as read only.
permissions: read-all
diff --git a/CHANGELOG-7.3.md b/CHANGELOG-7.3.md
index 91adc8732cd3..bee0295a9848 100644
--- a/CHANGELOG-7.3.md
+++ b/CHANGELOG-7.3.md
@@ -7,6 +7,12 @@ in 7.3 minor versions.
To get the diff for a specific change, go to https://github.com/symfony/symfony/commit/XXX where XXX is the change hash
To get the diff between two versions, go to https://github.com/symfony/symfony/compare/v7.3.0...v7.3.1
+* 7.3.0 (2025-05-29)
+
+ * bug #60549 [Translation] Add intl-icu fallback for MessageCatalogue metadata (pontus-mp)
+ * bug #60571 [ErrorHandler] Do not transform file to link if it does not exist (lyrixx)
+ * bug #60542 [Webhook] Fix controller service name (HypeMC)
+
* 7.3.0-RC1 (2025-05-25)
* bug #60529 [AssetMapper] Fix SequenceParser possible infinite loop (smnandre)
diff --git a/UPGRADE-7.3.md b/UPGRADE-7.3.md
index 77a3f14c3445..5c279372b762 100644
--- a/UPGRADE-7.3.md
+++ b/UPGRADE-7.3.md
@@ -179,9 +179,7 @@ Security
```php
protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token, ?Vote $vote = null): bool
{
- $vote ??= new Vote();
-
- $vote->reasons[] = 'A brief explanation of why access is granted or denied, as appropriate.';
+ $vote?->addReason('A brief explanation of why access is granted or denied, as appropriate.');
}
```
diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/routing/webhook.php b/src/Symfony/Bundle/FrameworkBundle/Resources/config/routing/webhook.php
index ea80311599fa..177606b26214 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/routing/webhook.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/routing/webhook.php
@@ -24,7 +24,7 @@
}
$routes->add('_webhook_controller', '/{type}')
- ->controller('webhook_controller::handle')
+ ->controller('webhook.controller::handle')
->requirements(['type' => '.+'])
;
};
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/messenger_multiple_buses_without_deduplicate_middleware.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/messenger_multiple_buses_without_deduplicate_middleware.php
index b8e7530bb3e0..fd4a008341cb 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/messenger_multiple_buses_without_deduplicate_middleware.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/messenger_multiple_buses_without_deduplicate_middleware.php
@@ -5,6 +5,7 @@
'http_method_override' => false,
'handle_all_throwables' => true,
'php_errors' => ['log' => true],
+ 'lock' => false,
'messenger' => [
'default_bus' => 'messenger.bus.commands',
'buses' => [
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/messenger_multiple_buses_without_deduplicate_middleware.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/messenger_multiple_buses_without_deduplicate_middleware.xml
index dcf402e1a36e..3f0d96249959 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/messenger_multiple_buses_without_deduplicate_middleware.xml
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/messenger_multiple_buses_without_deduplicate_middleware.xml
@@ -8,6 +8,7 @@
+
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/messenger_multiple_buses_without_deduplicate_middleware.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/messenger_multiple_buses_without_deduplicate_middleware.yml
index f06d534a55ec..38fca57379fc 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/messenger_multiple_buses_without_deduplicate_middleware.yml
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/messenger_multiple_buses_without_deduplicate_middleware.yml
@@ -4,6 +4,7 @@ framework:
handle_all_throwables: true
php_errors:
log: true
+ lock: false
messenger:
default_bus: messenger.bus.commands
buses:
diff --git a/src/Symfony/Component/ErrorHandler/ErrorRenderer/HtmlErrorRenderer.php b/src/Symfony/Component/ErrorHandler/ErrorRenderer/HtmlErrorRenderer.php
index d0d1246a1ddb..7bd9a083e53a 100644
--- a/src/Symfony/Component/ErrorHandler/ErrorRenderer/HtmlErrorRenderer.php
+++ b/src/Symfony/Component/ErrorHandler/ErrorRenderer/HtmlErrorRenderer.php
@@ -233,6 +233,10 @@ private function formatFile(string $file, int $line, ?string $text = null): stri
$text .= ' at line '.$line;
}
+ if (!file_exists($file)) {
+ return $text;
+ }
+
$link = $this->fileLinkFormat->format($file, $line);
return \sprintf('%s ', $this->escape($link), $text);
diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php
index 566e721bf3bb..bfef40fac58a 100644
--- a/src/Symfony/Component/HttpKernel/Kernel.php
+++ b/src/Symfony/Component/HttpKernel/Kernel.php
@@ -73,12 +73,12 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl
*/
private static array $freshCache = [];
- public const VERSION = '7.3.0-RC1';
+ public const VERSION = '7.3.0';
public const VERSION_ID = 70300;
public const MAJOR_VERSION = 7;
public const MINOR_VERSION = 3;
public const RELEASE_VERSION = 0;
- public const EXTRA_VERSION = 'RC1';
+ public const EXTRA_VERSION = '';
public const END_OF_MAINTENANCE = '05/2025';
public const END_OF_LIFE = '01/2026';
diff --git a/src/Symfony/Component/ObjectMapper/composer.json b/src/Symfony/Component/ObjectMapper/composer.json
index eb89582d8aad..6d1b445d9278 100644
--- a/src/Symfony/Component/ObjectMapper/composer.json
+++ b/src/Symfony/Component/ObjectMapper/composer.json
@@ -32,5 +32,6 @@
},
"conflict": {
"symfony/property-access": "<7.2"
- }
+ },
+ "minimum-stability": "dev"
}
diff --git a/src/Symfony/Component/Security/Core/Authorization/Voter/AuthenticatedVoter.php b/src/Symfony/Component/Security/Core/Authorization/Voter/AuthenticatedVoter.php
index 1403aaaaf0b1..3ab6b92c1d95 100644
--- a/src/Symfony/Component/Security/Core/Authorization/Voter/AuthenticatedVoter.php
+++ b/src/Symfony/Component/Security/Core/Authorization/Voter/AuthenticatedVoter.php
@@ -45,11 +45,10 @@ public function __construct(
*/
public function vote(TokenInterface $token, mixed $subject, array $attributes/* , ?Vote $vote = null */): int
{
- $vote = 3 < \func_num_args() ? func_get_arg(3) : new Vote();
- $vote ??= new Vote();
+ $vote = 3 < \func_num_args() ? func_get_arg(3) : null;
if ($attributes === [self::PUBLIC_ACCESS]) {
- $vote->reasons[] = 'Access is public.';
+ $vote?->addReason('Access is public.');
return VoterInterface::ACCESS_GRANTED;
}
@@ -73,7 +72,7 @@ public function vote(TokenInterface $token, mixed $subject, array $attributes/*
if ((self::IS_AUTHENTICATED_FULLY === $attribute || self::IS_AUTHENTICATED_REMEMBERED === $attribute)
&& $this->authenticationTrustResolver->isFullFledged($token)
) {
- $vote->reasons[] = 'The user is fully authenticated.';
+ $vote?->addReason('The user is fully authenticated.');
return VoterInterface::ACCESS_GRANTED;
}
@@ -81,32 +80,32 @@ public function vote(TokenInterface $token, mixed $subject, array $attributes/*
if (self::IS_AUTHENTICATED_REMEMBERED === $attribute
&& $this->authenticationTrustResolver->isRememberMe($token)
) {
- $vote->reasons[] = 'The user is remembered.';
+ $vote?->addReason('The user is remembered.');
return VoterInterface::ACCESS_GRANTED;
}
if (self::IS_AUTHENTICATED === $attribute && $this->authenticationTrustResolver->isAuthenticated($token)) {
- $vote->reasons[] = 'The user is authenticated.';
+ $vote?->addReason('The user is authenticated.');
return VoterInterface::ACCESS_GRANTED;
}
if (self::IS_REMEMBERED === $attribute && $this->authenticationTrustResolver->isRememberMe($token)) {
- $vote->reasons[] = 'The user is remembered.';
+ $vote?->addReason('The user is remembered.');
return VoterInterface::ACCESS_GRANTED;
}
if (self::IS_IMPERSONATOR === $attribute && $token instanceof SwitchUserToken) {
- $vote->reasons[] = 'The user is impersonating another user.';
+ $vote?->addReason('The user is impersonating another user.');
return VoterInterface::ACCESS_GRANTED;
}
}
if (VoterInterface::ACCESS_DENIED === $result) {
- $vote->reasons[] = 'The user is not appropriately authenticated.';
+ $vote?->addReason('The user is not appropriately authenticated.');
}
return $result;
diff --git a/src/Symfony/Component/Security/Core/Authorization/Voter/ClosureVoter.php b/src/Symfony/Component/Security/Core/Authorization/Voter/ClosureVoter.php
index 03a9f7571a57..4fb5502fd91c 100644
--- a/src/Symfony/Component/Security/Core/Authorization/Voter/ClosureVoter.php
+++ b/src/Symfony/Component/Security/Core/Authorization/Voter/ClosureVoter.php
@@ -42,7 +42,6 @@ public function supportsType(string $subjectType): bool
public function vote(TokenInterface $token, mixed $subject, array $attributes, ?Vote $vote = null): int
{
- $vote ??= new Vote();
$context = new IsGrantedContext($token, $token->getUser(), $this->authorizationChecker);
$failingClosures = [];
$result = VoterInterface::ACCESS_ABSTAIN;
@@ -54,7 +53,7 @@ public function vote(TokenInterface $token, mixed $subject, array $attributes, ?
$name = (new \ReflectionFunction($attribute))->name;
$result = VoterInterface::ACCESS_DENIED;
if ($attribute($context, $subject)) {
- $vote->reasons[] = \sprintf('Closure %s returned true.', $name);
+ $vote?->addReason(\sprintf('Closure %s returned true.', $name));
return VoterInterface::ACCESS_GRANTED;
}
@@ -63,7 +62,7 @@ public function vote(TokenInterface $token, mixed $subject, array $attributes, ?
}
if ($failingClosures) {
- $vote->reasons[] = \sprintf('Closure%s %s returned false.', 1 < \count($failingClosures) ? 's' : '', implode(', ', $failingClosures));
+ $vote?->addReason(\sprintf('Closure%s %s returned false.', 1 < \count($failingClosures) ? 's' : '', implode(', ', $failingClosures)));
}
return $result;
diff --git a/src/Symfony/Component/Security/Core/Authorization/Voter/ExpressionVoter.php b/src/Symfony/Component/Security/Core/Authorization/Voter/ExpressionVoter.php
index 35d727a8eb15..719aae7d4687 100644
--- a/src/Symfony/Component/Security/Core/Authorization/Voter/ExpressionVoter.php
+++ b/src/Symfony/Component/Security/Core/Authorization/Voter/ExpressionVoter.php
@@ -49,8 +49,7 @@ public function supportsType(string $subjectType): bool
*/
public function vote(TokenInterface $token, mixed $subject, array $attributes/* , ?Vote $vote = null */): int
{
- $vote = 3 < \func_num_args() ? func_get_arg(3) : new Vote();
- $vote ??= new Vote();
+ $vote = 3 < \func_num_args() ? func_get_arg(3) : null;
$result = VoterInterface::ACCESS_ABSTAIN;
$variables = null;
$failingExpressions = [];
@@ -64,7 +63,7 @@ public function vote(TokenInterface $token, mixed $subject, array $attributes/*
$result = VoterInterface::ACCESS_DENIED;
if ($this->expressionLanguage->evaluate($attribute, $variables)) {
- $vote->reasons[] = \sprintf('Expression (%s) is true.', $attribute);
+ $vote?->addReason(\sprintf('Expression (%s) is true.', $attribute));
return VoterInterface::ACCESS_GRANTED;
}
@@ -73,7 +72,7 @@ public function vote(TokenInterface $token, mixed $subject, array $attributes/*
}
if ($failingExpressions) {
- $vote->reasons[] = \sprintf('Expression (%s) is false.', implode(') || (', $failingExpressions));
+ $vote?->addReason(\sprintf('Expression (%s) is false.', implode(') || (', $failingExpressions)));
}
return $result;
diff --git a/src/Symfony/Component/Security/Core/Authorization/Voter/RoleVoter.php b/src/Symfony/Component/Security/Core/Authorization/Voter/RoleVoter.php
index 46c08d15b48e..2225e8d4d4c4 100644
--- a/src/Symfony/Component/Security/Core/Authorization/Voter/RoleVoter.php
+++ b/src/Symfony/Component/Security/Core/Authorization/Voter/RoleVoter.php
@@ -30,8 +30,7 @@ public function __construct(
*/
public function vote(TokenInterface $token, mixed $subject, array $attributes/* , ?Vote $vote = null */): int
{
- $vote = 3 < \func_num_args() ? func_get_arg(3) : new Vote();
- $vote ??= new Vote();
+ $vote = 3 < \func_num_args() ? func_get_arg(3) : null;
$result = VoterInterface::ACCESS_ABSTAIN;
$roles = $this->extractRoles($token);
$missingRoles = [];
@@ -44,7 +43,7 @@ public function vote(TokenInterface $token, mixed $subject, array $attributes/*
$result = VoterInterface::ACCESS_DENIED;
if (\in_array($attribute, $roles, true)) {
- $vote->reasons[] = \sprintf('The user has %s.', $attribute);
+ $vote?->addReason(\sprintf('The user has %s.', $attribute));
return VoterInterface::ACCESS_GRANTED;
}
@@ -53,7 +52,7 @@ public function vote(TokenInterface $token, mixed $subject, array $attributes/*
}
if (VoterInterface::ACCESS_DENIED === $result) {
- $vote->reasons[] = \sprintf('The user doesn\'t have%s %s.', 1 < \count($missingRoles) ? ' any of' : '', implode(', ', $missingRoles));
+ $vote?->addReason(\sprintf('The user doesn\'t have%s %s.', 1 < \count($missingRoles) ? ' any of' : '', implode(', ', $missingRoles)));
}
return $result;
diff --git a/src/Symfony/Component/Security/Core/Authorization/Voter/TraceableVoter.php b/src/Symfony/Component/Security/Core/Authorization/Voter/TraceableVoter.php
index 47572797ee90..ec9260635985 100644
--- a/src/Symfony/Component/Security/Core/Authorization/Voter/TraceableVoter.php
+++ b/src/Symfony/Component/Security/Core/Authorization/Voter/TraceableVoter.php
@@ -32,9 +32,9 @@ public function __construct(
public function vote(TokenInterface $token, mixed $subject, array $attributes, ?Vote $vote = null): int
{
- $result = $this->voter->vote($token, $subject, $attributes, $vote ??= new Vote());
+ $result = $this->voter->vote($token, $subject, $attributes, $vote);
- $this->eventDispatcher->dispatch(new VoteEvent($this->voter, $subject, $attributes, $result, $vote->reasons), 'debug.security.authorization.vote');
+ $this->eventDispatcher->dispatch(new VoteEvent($this->voter, $subject, $attributes, $result, $vote->reasons ?? []), 'debug.security.authorization.vote');
return $result;
}
diff --git a/src/Symfony/Component/Security/Core/Authorization/Voter/Voter.php b/src/Symfony/Component/Security/Core/Authorization/Voter/Voter.php
index 3d7fd9e2d7a1..55930def8fda 100644
--- a/src/Symfony/Component/Security/Core/Authorization/Voter/Voter.php
+++ b/src/Symfony/Component/Security/Core/Authorization/Voter/Voter.php
@@ -29,10 +29,9 @@ abstract class Voter implements VoterInterface, CacheableVoterInterface
*/
public function vote(TokenInterface $token, mixed $subject, array $attributes/* , ?Vote $vote = null */): int
{
- $vote = 3 < \func_num_args() ? func_get_arg(3) : new Vote();
- $vote ??= new Vote();
+ $vote = 3 < \func_num_args() ? func_get_arg(3) : null;
// abstain vote by default in case none of the attributes are supported
- $vote->result = self::ACCESS_ABSTAIN;
+ $voteResult = self::ACCESS_ABSTAIN;
foreach ($attributes as $attribute) {
try {
@@ -48,15 +47,27 @@ public function vote(TokenInterface $token, mixed $subject, array $attributes/*
}
// as soon as at least one attribute is supported, default is to deny access
- $vote->result = self::ACCESS_DENIED;
+ $voteResult = self::ACCESS_DENIED;
+
+ if (null !== $vote) {
+ $vote->result = $voteResult;
+ }
if ($this->voteOnAttribute($attribute, $subject, $token, $vote)) {
// grant access as soon as at least one attribute returns a positive response
- return $vote->result = self::ACCESS_GRANTED;
+ if (null !== $vote) {
+ $vote->result = self::ACCESS_GRANTED;
+ }
+
+ return self::ACCESS_GRANTED;
}
}
- return $vote->result;
+ if (null !== $vote) {
+ $vote->result = $voteResult;
+ }
+
+ return $voteResult;
}
/**
diff --git a/src/Symfony/Component/Security/Core/Tests/Authorization/Voter/VoterTest.php b/src/Symfony/Component/Security/Core/Tests/Authorization/Voter/VoterTest.php
index a8f87e09da7e..eaada3061dbf 100644
--- a/src/Symfony/Component/Security/Core/Tests/Authorization/Voter/VoterTest.php
+++ b/src/Symfony/Component/Security/Core/Tests/Authorization/Voter/VoterTest.php
@@ -33,35 +33,51 @@ public static function getTests(): array
return [
[$voter, ['EDIT'], VoterInterface::ACCESS_GRANTED, new \stdClass(), 'ACCESS_GRANTED if attribute and class are supported and attribute grants access'],
+ [$voter, ['EDIT'], VoterInterface::ACCESS_GRANTED, new \stdClass(), 'ACCESS_GRANTED if attribute and class are supported and attribute grants access', new Vote()],
[$voter, ['CREATE'], VoterInterface::ACCESS_DENIED, new \stdClass(), 'ACCESS_DENIED if attribute and class are supported and attribute does not grant access'],
+ [$voter, ['CREATE'], VoterInterface::ACCESS_DENIED, new \stdClass(), 'ACCESS_DENIED if attribute and class are supported and attribute does not grant access', new Vote()],
[$voter, ['DELETE', 'EDIT'], VoterInterface::ACCESS_GRANTED, new \stdClass(), 'ACCESS_GRANTED if one attribute is supported and grants access'],
+ [$voter, ['DELETE', 'EDIT'], VoterInterface::ACCESS_GRANTED, new \stdClass(), 'ACCESS_GRANTED if one attribute is supported and grants access', new Vote()],
[$voter, ['DELETE', 'CREATE'], VoterInterface::ACCESS_DENIED, new \stdClass(), 'ACCESS_DENIED if one attribute is supported and denies access'],
+ [$voter, ['DELETE', 'CREATE'], VoterInterface::ACCESS_DENIED, new \stdClass(), 'ACCESS_DENIED if one attribute is supported and denies access', new Vote()],
[$voter, ['CREATE', 'EDIT'], VoterInterface::ACCESS_GRANTED, new \stdClass(), 'ACCESS_GRANTED if one attribute grants access'],
+ [$voter, ['CREATE', 'EDIT'], VoterInterface::ACCESS_GRANTED, new \stdClass(), 'ACCESS_GRANTED if one attribute grants access', new Vote()],
[$voter, ['DELETE'], VoterInterface::ACCESS_ABSTAIN, new \stdClass(), 'ACCESS_ABSTAIN if no attribute is supported'],
+ [$voter, ['DELETE'], VoterInterface::ACCESS_ABSTAIN, new \stdClass(), 'ACCESS_ABSTAIN if no attribute is supported', new Vote()],
[$voter, ['EDIT'], VoterInterface::ACCESS_ABSTAIN, new class {}, 'ACCESS_ABSTAIN if class is not supported'],
+ [$voter, ['EDIT'], VoterInterface::ACCESS_ABSTAIN, new class {}, 'ACCESS_ABSTAIN if class is not supported', new Vote()],
[$voter, ['EDIT'], VoterInterface::ACCESS_ABSTAIN, null, 'ACCESS_ABSTAIN if object is null'],
+ [$voter, ['EDIT'], VoterInterface::ACCESS_ABSTAIN, null, 'ACCESS_ABSTAIN if object is null', new Vote()],
[$voter, [], VoterInterface::ACCESS_ABSTAIN, new \stdClass(), 'ACCESS_ABSTAIN if no attributes were provided'],
+ [$voter, [], VoterInterface::ACCESS_ABSTAIN, new \stdClass(), 'ACCESS_ABSTAIN if no attributes were provided', new Vote()],
[$voter, [new StringableAttribute()], VoterInterface::ACCESS_GRANTED, new \stdClass(), 'ACCESS_GRANTED if attribute and class are supported and attribute grants access'],
+ [$voter, [new StringableAttribute()], VoterInterface::ACCESS_GRANTED, new \stdClass(), 'ACCESS_GRANTED if attribute and class are supported and attribute grants access', new Vote()],
[$voter, [new \stdClass()], VoterInterface::ACCESS_ABSTAIN, new \stdClass(), 'ACCESS_ABSTAIN if attributes were not strings'],
+ [$voter, [new \stdClass()], VoterInterface::ACCESS_ABSTAIN, new \stdClass(), 'ACCESS_ABSTAIN if attributes were not strings', new Vote()],
[$integerVoter, [42], VoterInterface::ACCESS_GRANTED, new \stdClass(), 'ACCESS_GRANTED if attribute is an integer'],
+ [$integerVoter, [42], VoterInterface::ACCESS_GRANTED, new \stdClass(), 'ACCESS_GRANTED if attribute is an integer', new Vote()],
];
}
/**
* @dataProvider getTests
*/
- public function testVote(VoterInterface $voter, array $attributes, $expectedVote, $object, $message)
+ public function testVote(VoterInterface $voter, array $attributes, $expectedVote, $object, $message, ?Vote $vote = null)
{
- $this->assertEquals($expectedVote, $voter->vote($this->token, $object, $attributes), $message);
+ $this->assertSame($expectedVote, $voter->vote($this->token, $object, $attributes, $vote), $message);
+
+ if (null !== $vote) {
+ self::assertSame($expectedVote, $vote->result);
+ }
}
public function testVoteWithTypeError()
diff --git a/src/Symfony/Component/Security/Http/Tests/EventListener/IsGrantedAttributeListenerTest.php b/src/Symfony/Component/Security/Http/Tests/EventListener/IsGrantedAttributeListenerTest.php
index 73494f405468..d34b31f2bdeb 100644
--- a/src/Symfony/Component/Security/Http/Tests/EventListener/IsGrantedAttributeListenerTest.php
+++ b/src/Symfony/Component/Security/Http/Tests/EventListener/IsGrantedAttributeListenerTest.php
@@ -232,7 +232,7 @@ protected function supports(string $attribute, mixed $subject): bool
protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token, ?Vote $vote = null): bool
{
- $vote->reasons[] = 'Because I can 😈.';
+ $vote?->addReason('Because I can 😈.');
return false;
}
diff --git a/src/Symfony/Component/Translation/MessageCatalogue.php b/src/Symfony/Component/Translation/MessageCatalogue.php
index 2d229f2dd183..eac50bb1f9b9 100644
--- a/src/Symfony/Component/Translation/MessageCatalogue.php
+++ b/src/Symfony/Component/Translation/MessageCatalogue.php
@@ -217,6 +217,16 @@ public function getMetadata(string $key = '', string $domain = 'messages'): mixe
return $this->metadata;
}
+ if (isset($this->metadata[$domain.self::INTL_DOMAIN_SUFFIX])) {
+ if ('' === $key) {
+ return $this->metadata[$domain.self::INTL_DOMAIN_SUFFIX];
+ }
+
+ if (isset($this->metadata[$domain.self::INTL_DOMAIN_SUFFIX][$key])) {
+ return $this->metadata[$domain.self::INTL_DOMAIN_SUFFIX][$key];
+ }
+ }
+
if (isset($this->metadata[$domain])) {
if ('' == $key) {
return $this->metadata[$domain];
diff --git a/src/Symfony/Component/Translation/Tests/Catalogue/MessageCatalogueTest.php b/src/Symfony/Component/Translation/Tests/Catalogue/MessageCatalogueTest.php
new file mode 100644
index 000000000000..1ac61673999b
--- /dev/null
+++ b/src/Symfony/Component/Translation/Tests/Catalogue/MessageCatalogueTest.php
@@ -0,0 +1,27 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\Catalogue;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Translation\MessageCatalogue;
+
+class MessageCatalogueTest extends TestCase
+{
+ public function testIcuMetadataKept()
+ {
+ $mc = new MessageCatalogue('en', ['messages' => ['a' => 'new_a']]);
+ $metadata = ['metadata' => 'value'];
+ $mc->setMetadata('a', $metadata, 'messages+intl-icu');
+ $this->assertEquals($metadata, $mc->getMetadata('a', 'messages'));
+ $this->assertEquals($metadata, $mc->getMetadata('a', 'messages+intl-icu'));
+ }
+}
diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.nl.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.nl.xlf
index 0e0de772720c..1781b1f29ec6 100644
--- a/src/Symfony/Component/Validator/Resources/translations/validators.nl.xlf
+++ b/src/Symfony/Component/Validator/Resources/translations/validators.nl.xlf
@@ -468,7 +468,7 @@
This value is not a valid Twig template.
- Deze waarde is geen geldige Twig-template.
+ Deze waarde is geen geldige Twig-template.
Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies:
Alternative Proxy
pFad Proxy
pFad v3 Proxy
pFad v4 Proxy