From aad9d4824d8d7160d672b6be3e13e11bd53e0d11 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Mon, 2 Jun 2025 16:08:14 +0200 Subject: [PATCH 1/9] Allow Symfony ^8.0 --- composer.json | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/composer.json b/composer.json index 0aaff1e3..9d662f7e 100644 --- a/composer.json +++ b/composer.json @@ -20,20 +20,20 @@ "symfony/deprecation-contracts": "^2.5|^3", "symfony/event-dispatcher-contracts": "^2.5|^3", "symfony/service-contracts": "^2.5|^3", - "symfony/password-hasher": "^6.4|^7.0" + "symfony/password-hasher": "^6.4|^7.0|^8.0" }, "require-dev": { "psr/container": "^1.1|^2.0", "psr/cache": "^1.0|^2.0|^3.0", - "symfony/cache": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/event-dispatcher": "^6.4|^7.0", - "symfony/expression-language": "^6.4|^7.0", - "symfony/http-foundation": "^6.4|^7.0", - "symfony/ldap": "^6.4|^7.0", - "symfony/string": "^6.4|^7.0", - "symfony/translation": "^6.4.3|^7.0.3", - "symfony/validator": "^6.4|^7.0", + "symfony/cache": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/event-dispatcher": "^6.4|^7.0|^8.0", + "symfony/expression-language": "^6.4|^7.0|^8.0", + "symfony/http-foundation": "^6.4|^7.0|^8.0", + "symfony/ldap": "^6.4|^7.0|^8.0", + "symfony/string": "^6.4|^7.0|^8.0", + "symfony/translation": "^6.4.3|^7.0.3|^8.0", + "symfony/validator": "^6.4|^7.0|^8.0", "psr/log": "^1|^2|^3" }, "conflict": { From f90d78b84cdd61498c624c1c9f4314609a5438e7 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Mon, 9 Jun 2025 12:37:44 +0200 Subject: [PATCH 2/9] deprecate handling options in the base Constraint class --- Tests/Validator/Constraints/UserPasswordTest.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Tests/Validator/Constraints/UserPasswordTest.php b/Tests/Validator/Constraints/UserPasswordTest.php index ed4ca442..2c990808 100644 --- a/Tests/Validator/Constraints/UserPasswordTest.php +++ b/Tests/Validator/Constraints/UserPasswordTest.php @@ -35,8 +35,6 @@ public function testValidatedByService(UserPassword $constraint) public static function provideServiceValidatedConstraints(): iterable { - yield 'Doctrine style' => [new UserPassword(['service' => 'my_service'])]; - yield 'named arguments' => [new UserPassword(service: 'my_service')]; $metadata = new ClassMetadata(UserPasswordDummy::class); @@ -45,6 +43,14 @@ public static function provideServiceValidatedConstraints(): iterable yield 'attribute' => [$metadata->properties['b']->constraints[0]]; } + /** + * @group legacy + */ + public function testValidatedByServiceDoctrineStyle() + { + self::assertSame('my_service', (new UserPassword(['service' => 'my_service']))->validatedBy()); + } + public function testAttributes() { $metadata = new ClassMetadata(UserPasswordDummy::class); From c374c9645b3484de77b471832c0e5771c19a2411 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 8 Jul 2025 11:08:29 +0200 Subject: [PATCH 3/9] Various CS fixes --- Authentication/Token/AbstractToken.php | 1 - 1 file changed, 1 deletion(-) diff --git a/Authentication/Token/AbstractToken.php b/Authentication/Token/AbstractToken.php index 683e46d4..d730c111 100644 --- a/Authentication/Token/AbstractToken.php +++ b/Authentication/Token/AbstractToken.php @@ -11,7 +11,6 @@ namespace Symfony\Component\Security\Core\Authentication\Token; -use Symfony\Component\Security\Core\User\EquatableInterface; use Symfony\Component\Security\Core\User\InMemoryUser; use Symfony\Component\Security\Core\User\UserInterface; From 986bf5857f061f1e959d6e65a9b0fbedad9b1a0c Mon Sep 17 00:00:00 2001 From: Gregor Harlan Date: Sat, 12 Jul 2025 15:55:19 +0200 Subject: [PATCH 4/9] optimize `in_array` calls --- Tests/Authorization/Voter/VoterTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/Authorization/Voter/VoterTest.php b/Tests/Authorization/Voter/VoterTest.php index eaada306..e5d22b6e 100644 --- a/Tests/Authorization/Voter/VoterTest.php +++ b/Tests/Authorization/Voter/VoterTest.php @@ -97,7 +97,7 @@ protected function voteOnAttribute(string $attribute, $object, TokenInterface $t protected function supports(string $attribute, $object): bool { - return $object instanceof \stdClass && \in_array($attribute, ['EDIT', 'CREATE']); + return $object instanceof \stdClass && \in_array($attribute, ['EDIT', 'CREATE'], true); } } From bfa94f9e3f52becabe34bb6f43701ee435ce2e3a Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Wed, 9 Oct 2024 11:06:51 +0200 Subject: [PATCH 5/9] run tests using PHPUnit 11.5 --- Tests/Authentication/Token/AbstractTokenTest.php | 10 ++++------ Tests/Authentication/Token/RememberMeTokenTest.php | 7 ++++--- Tests/User/InMemoryUserTest.php | 12 +++++------- Tests/Validator/Constraints/UserPasswordTest.php | 7 ++++--- phpunit.xml.dist | 11 ++++++++--- 5 files changed, 25 insertions(+), 22 deletions(-) diff --git a/Tests/Authentication/Token/AbstractTokenTest.php b/Tests/Authentication/Token/AbstractTokenTest.php index 3972b1cd..2c9a3fff 100644 --- a/Tests/Authentication/Token/AbstractTokenTest.php +++ b/Tests/Authentication/Token/AbstractTokenTest.php @@ -11,8 +11,9 @@ namespace Symfony\Component\Security\Core\Tests\Authentication\Token; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\IgnoreDeprecations; use PHPUnit\Framework\TestCase; -use Symfony\Bridge\PhpUnit\ExpectUserDeprecationMessageTrait; use Symfony\Component\Security\Core\Authentication\Token\AbstractToken; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\User\InMemoryUser; @@ -20,8 +21,6 @@ class AbstractTokenTest extends TestCase { - use ExpectUserDeprecationMessageTrait; - /** * @dataProvider provideUsers */ @@ -37,9 +36,8 @@ public static function provideUsers() yield [new InMemoryUser('fabien', null), 'fabien']; } - /** - * @group legacy - */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testEraseCredentials() { $token = new ConcreteToken(['ROLE_FOO']); diff --git a/Tests/Authentication/Token/RememberMeTokenTest.php b/Tests/Authentication/Token/RememberMeTokenTest.php index b0cdbaf1..c1c99569 100644 --- a/Tests/Authentication/Token/RememberMeTokenTest.php +++ b/Tests/Authentication/Token/RememberMeTokenTest.php @@ -11,6 +11,8 @@ namespace Symfony\Component\Security\Core\Tests\Authentication\Token; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\IgnoreDeprecations; use PHPUnit\Framework\TestCase; use Symfony\Component\Security\Core\Authentication\Token\RememberMeToken; use Symfony\Component\Security\Core\User\UserInterface; @@ -27,9 +29,8 @@ public function testConstructor() $this->assertSame($user, $token->getUser()); } - /** - * @group legacy - */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testSecret() { $user = $this->getUser(); diff --git a/Tests/User/InMemoryUserTest.php b/Tests/User/InMemoryUserTest.php index f06e98c3..bf78d80b 100644 --- a/Tests/User/InMemoryUserTest.php +++ b/Tests/User/InMemoryUserTest.php @@ -11,15 +11,14 @@ namespace Symfony\Component\Security\Core\Tests\User; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\IgnoreDeprecations; use PHPUnit\Framework\TestCase; -use Symfony\Bridge\PhpUnit\ExpectUserDeprecationMessageTrait; use Symfony\Component\Security\Core\User\InMemoryUser; use Symfony\Component\Security\Core\User\UserInterface; class InMemoryUserTest extends TestCase { - use ExpectUserDeprecationMessageTrait; - public function testConstructorException() { $this->expectException(\InvalidArgumentException::class); @@ -56,13 +55,12 @@ public function testIsEnabled() $this->assertFalse($user->isEnabled()); } - /** - * @group legacy - */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testEraseCredentials() { $user = new InMemoryUser('fabien', 'superpass'); - $this->expectUserDeprecationMessage(\sprintf('%sMethod %s::eraseCredentials() is deprecated since symfony/security-core 7.3', \PHP_VERSION_ID >= 80400 ? 'Unsilenced deprecation: ' : '', InMemoryUser::class)); + $this->expectUserDeprecationMessage(\sprintf('Method %s::eraseCredentials() is deprecated since symfony/security-core 7.3', InMemoryUser::class)); $user->eraseCredentials(); $this->assertEquals('superpass', $user->getPassword()); } diff --git a/Tests/Validator/Constraints/UserPasswordTest.php b/Tests/Validator/Constraints/UserPasswordTest.php index 2c990808..91f1b5e6 100644 --- a/Tests/Validator/Constraints/UserPasswordTest.php +++ b/Tests/Validator/Constraints/UserPasswordTest.php @@ -11,6 +11,8 @@ namespace Symfony\Component\Security\Core\Tests\Validator\Constraints; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\IgnoreDeprecations; use PHPUnit\Framework\TestCase; use Symfony\Component\Security\Core\Validator\Constraints\UserPassword; use Symfony\Component\Validator\Mapping\ClassMetadata; @@ -43,9 +45,8 @@ public static function provideServiceValidatedConstraints(): iterable yield 'attribute' => [$metadata->properties['b']->constraints[0]]; } - /** - * @group legacy - */ + #[IgnoreDeprecations] + #[Group('legacy')] public function testValidatedByServiceDoctrineStyle() { self::assertSame('my_service', (new UserPassword(['service' => 'my_service']))->validatedBy()); diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 223091f3..ae6997a0 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,10 +1,11 @@ @@ -18,7 +19,7 @@ - + ./ @@ -27,5 +28,9 @@ ./Tests ./vendor - + + + + + From 72792ef69e05cfddd9f2ba4db020c8949e55761e Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Thu, 31 Jul 2025 14:36:46 +0200 Subject: [PATCH 6/9] replace PHPUnit annotations with attributes --- Test/AccessDecisionStrategyTestCase.php | 2 -- .../Authentication/Token/AbstractTokenTest.php | 9 +++------ .../Authorization/AuthorizationCheckerTest.php | 9 +++------ Tests/Authorization/ExpressionLanguageTest.php | 5 ++--- .../TraceableAccessDecisionManagerTest.php | 9 +++------ .../Voter/AuthenticatedVoterTest.php | 17 +++++------------ Tests/Authorization/Voter/ClosureVoterTest.php | 5 ++--- .../Authorization/Voter/ExpressionVoterTest.php | 5 ++--- .../Voter/RoleHierarchyVoterTest.php | 9 +++------ Tests/Authorization/Voter/RoleVoterTest.php | 9 +++------ Tests/Authorization/Voter/VoterTest.php | 5 ++--- Tests/Resources/TranslationFilesTest.php | 9 +++------ Tests/User/InMemoryUserTest.php | 4 ++-- .../Validator/Constraints/UserPasswordTest.php | 5 ++--- .../UserPasswordValidatorTestCase.php | 13 ++++--------- 15 files changed, 39 insertions(+), 76 deletions(-) diff --git a/Test/AccessDecisionStrategyTestCase.php b/Test/AccessDecisionStrategyTestCase.php index 563a6138..4cd7def6 100644 --- a/Test/AccessDecisionStrategyTestCase.php +++ b/Test/AccessDecisionStrategyTestCase.php @@ -27,8 +27,6 @@ abstract class AccessDecisionStrategyTestCase extends TestCase { /** - * @dataProvider provideStrategyTests - * * @param VoterInterface[] $voters */ #[DataProvider('provideStrategyTests')] diff --git a/Tests/Authentication/Token/AbstractTokenTest.php b/Tests/Authentication/Token/AbstractTokenTest.php index 2c9a3fff..08abc241 100644 --- a/Tests/Authentication/Token/AbstractTokenTest.php +++ b/Tests/Authentication/Token/AbstractTokenTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Security\Core\Tests\Authentication\Token; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Group; use PHPUnit\Framework\Attributes\IgnoreDeprecations; use PHPUnit\Framework\TestCase; @@ -21,9 +22,7 @@ class AbstractTokenTest extends TestCase { - /** - * @dataProvider provideUsers - */ + #[DataProvider('provideUsers')] public function testGetUserIdentifier($user, string $username) { $token = new ConcreteToken(['ROLE_FOO']); @@ -90,9 +89,7 @@ public function testAttributes() } } - /** - * @dataProvider provideUsers - */ + #[DataProvider('provideUsers')] public function testSetUser($user) { $token = new ConcreteToken(); diff --git a/Tests/Authorization/AuthorizationCheckerTest.php b/Tests/Authorization/AuthorizationCheckerTest.php index 00f0f50e..370f101b 100644 --- a/Tests/Authorization/AuthorizationCheckerTest.php +++ b/Tests/Authorization/AuthorizationCheckerTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Security\Core\Tests\Authorization; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Symfony\Component\Security\Core\Authentication\Token\NullToken; @@ -44,9 +45,7 @@ public function testVoteWithoutAuthenticationToken() $authorizationChecker->isGranted('ROLE_FOO'); } - /** - * @dataProvider isGrantedProvider - */ + #[DataProvider('isGrantedProvider')] public function testIsGranted($decide) { $token = new UsernamePasswordToken(new InMemoryUser('username', 'password', ['ROLE_USER']), 'provider', ['ROLE_USER']); @@ -79,9 +78,7 @@ public function testIsGrantedWithObjectAttribute() $this->assertTrue($this->authorizationChecker->isGranted($attribute)); } - /** - * @dataProvider isGrantedForUserProvider - */ + #[DataProvider('isGrantedForUserProvider')] public function testIsGrantedForUser(bool $decide, array $roles) { $user = new InMemoryUser('username', 'password', $roles); diff --git a/Tests/Authorization/ExpressionLanguageTest.php b/Tests/Authorization/ExpressionLanguageTest.php index 1a4db41e..152c0f7b 100644 --- a/Tests/Authorization/ExpressionLanguageTest.php +++ b/Tests/Authorization/ExpressionLanguageTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Security\Core\Tests\Authorization; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolver; use Symfony\Component\Security\Core\Authentication\Token\RememberMeToken; @@ -25,9 +26,7 @@ class ExpressionLanguageTest extends TestCase { - /** - * @dataProvider provider - */ + #[DataProvider('provider')] public function testIsAuthenticated($token, $expression, $result) { $expressionLanguage = new ExpressionLanguage(); diff --git a/Tests/Authorization/TraceableAccessDecisionManagerTest.php b/Tests/Authorization/TraceableAccessDecisionManagerTest.php index 496d970c..006ceec5 100644 --- a/Tests/Authorization/TraceableAccessDecisionManagerTest.php +++ b/Tests/Authorization/TraceableAccessDecisionManagerTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Security\Core\Tests\Authorization; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; @@ -23,9 +24,7 @@ class TraceableAccessDecisionManagerTest extends TestCase { - /** - * @dataProvider provideObjectsAndLogs - */ + #[DataProvider('provideObjectsAndLogs')] public function testDecideLog(array $expectedLog, array $attributes, $object, array $voterVotes, bool $result) { $token = $this->createMock(TokenInterface::class); @@ -290,9 +289,7 @@ public function testThrowsExceptionWhenMultipleAttributesNotAllowed() $traceableAccessDecisionManager->decide($tokenMock, ['attr1', 'attr2']); } - /** - * @dataProvider allowMultipleAttributesProvider - */ + #[DataProvider('allowMultipleAttributesProvider')] public function testAllowMultipleAttributes(array $attributes, bool $allowMultipleAttributes) { $accessDecisionManager = new AccessDecisionManager(); diff --git a/Tests/Authorization/Voter/AuthenticatedVoterTest.php b/Tests/Authorization/Voter/AuthenticatedVoterTest.php index b5e0bf42..29cb1459 100644 --- a/Tests/Authorization/Voter/AuthenticatedVoterTest.php +++ b/Tests/Authorization/Voter/AuthenticatedVoterTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Security\Core\Tests\Authorization\Voter; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolver; use Symfony\Component\Security\Core\Authentication\Token\AbstractToken; @@ -25,9 +26,7 @@ class AuthenticatedVoterTest extends TestCase { - /** - * @dataProvider getVoteTests - */ + #[DataProvider('getVoteTests')] public function testVote($authenticated, $attributes, $expected) { $voter = new AuthenticatedVoter(new AuthenticationTrustResolver()); @@ -55,9 +54,7 @@ public static function getVoteTests() ]; } - /** - * @dataProvider provideAttributes - */ + #[DataProvider('provideAttributes')] public function testSupportsAttribute(string $attribute, bool $expected) { $voter = new AuthenticatedVoter(new AuthenticationTrustResolver()); @@ -87,9 +84,7 @@ public function testSupportsType() $this->assertTrue($voter->supportsType(get_debug_type(new \stdClass()))); } - /** - * @dataProvider provideOfflineAttributes - */ + #[DataProvider('provideOfflineAttributes')] public function testOfflineToken($attributes, $expected) { $voter = new AuthenticatedVoter(new AuthenticationTrustResolver()); @@ -103,9 +98,7 @@ public static function provideOfflineAttributes() yield [['ROLE_FOO'], VoterInterface::ACCESS_ABSTAIN]; } - /** - * @dataProvider provideUnsupportedOfflineAttributes - */ + #[DataProvider('provideUnsupportedOfflineAttributes')] public function testUnsupportedOfflineToken(string $attribute) { $voter = new AuthenticatedVoter(new AuthenticationTrustResolver()); diff --git a/Tests/Authorization/Voter/ClosureVoterTest.php b/Tests/Authorization/Voter/ClosureVoterTest.php index 7a22f2d4..a7e10583 100644 --- a/Tests/Authorization/Voter/ClosureVoterTest.php +++ b/Tests/Authorization/Voter/ClosureVoterTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Security\Core\Tests\Authorization\Voter; +use PHPUnit\Framework\Attributes\RequiresMethod; use PHPUnit\Framework\TestCase; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface; @@ -19,9 +20,7 @@ use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Http\Attribute\IsGrantedContext; -/** - * @requires function Symfony\Component\Security\Http\Attribute\IsGrantedContext::isGranted - */ +#[RequiresMethod(IsGrantedContext::class, 'isGranted')] class ClosureVoterTest extends TestCase { private ClosureVoter $voter; diff --git a/Tests/Authorization/Voter/ExpressionVoterTest.php b/Tests/Authorization/Voter/ExpressionVoterTest.php index 369b17f0..9a56e4f8 100644 --- a/Tests/Authorization/Voter/ExpressionVoterTest.php +++ b/Tests/Authorization/Voter/ExpressionVoterTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Security\Core\Tests\Authorization\Voter; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Symfony\Component\ExpressionLanguage\Expression; use Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolverInterface; @@ -22,9 +23,7 @@ class ExpressionVoterTest extends TestCase { - /** - * @dataProvider getVoteTests - */ + #[DataProvider('getVoteTests')] public function testVoteWithTokenThatReturnsRoleNames($roles, $attributes, $expected, $tokenExpectsGetRoles = true, $expressionLanguageExpectsEvaluate = true) { $voter = new ExpressionVoter($this->createExpressionLanguage($expressionLanguageExpectsEvaluate), $this->createTrustResolver(), $this->createAuthorizationChecker()); diff --git a/Tests/Authorization/Voter/RoleHierarchyVoterTest.php b/Tests/Authorization/Voter/RoleHierarchyVoterTest.php index b811bd74..5c9af31c 100644 --- a/Tests/Authorization/Voter/RoleHierarchyVoterTest.php +++ b/Tests/Authorization/Voter/RoleHierarchyVoterTest.php @@ -11,15 +11,14 @@ namespace Symfony\Component\Security\Core\Tests\Authorization\Voter; +use PHPUnit\Framework\Attributes\DataProvider; use Symfony\Component\Security\Core\Authorization\Voter\RoleHierarchyVoter; use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface; use Symfony\Component\Security\Core\Role\RoleHierarchy; class RoleHierarchyVoterTest extends RoleVoterTest { - /** - * @dataProvider getVoteTests - */ + #[DataProvider('getVoteTests')] public function testVoteUsingTokenThatReturnsRoleNames($roles, $attributes, $expected) { $voter = new RoleHierarchyVoter(new RoleHierarchy(['ROLE_FOO' => ['ROLE_FOOBAR']])); @@ -34,9 +33,7 @@ public static function getVoteTests() ]); } - /** - * @dataProvider getVoteWithEmptyHierarchyTests - */ + #[DataProvider('getVoteWithEmptyHierarchyTests')] public function testVoteWithEmptyHierarchyUsingTokenThatReturnsRoleNames($roles, $attributes, $expected) { $voter = new RoleHierarchyVoter(new RoleHierarchy([])); diff --git a/Tests/Authorization/Voter/RoleVoterTest.php b/Tests/Authorization/Voter/RoleVoterTest.php index dfa05556..dc22f203 100644 --- a/Tests/Authorization/Voter/RoleVoterTest.php +++ b/Tests/Authorization/Voter/RoleVoterTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Security\Core\Tests\Authorization\Voter; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolver; use Symfony\Component\Security\Core\Authentication\Token\AbstractToken; @@ -20,9 +21,7 @@ class RoleVoterTest extends TestCase { - /** - * @dataProvider getVoteTests - */ + #[DataProvider('getVoteTests')] public function testVoteUsingTokenThatReturnsRoleNames($roles, $attributes, $expected) { $voter = new RoleVoter(); @@ -46,9 +45,7 @@ public static function getVoteTests() ]; } - /** - * @dataProvider provideAttributes - */ + #[DataProvider('provideAttributes')] public function testSupportsAttribute(string $prefix, string $attribute, bool $expected) { $voter = new RoleVoter($prefix); diff --git a/Tests/Authorization/Voter/VoterTest.php b/Tests/Authorization/Voter/VoterTest.php index e5d22b6e..bef79c4e 100644 --- a/Tests/Authorization/Voter/VoterTest.php +++ b/Tests/Authorization/Voter/VoterTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Security\Core\Tests\Authorization\Voter; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Authorization\Voter\Vote; @@ -68,9 +69,7 @@ public static function getTests(): array ]; } - /** - * @dataProvider getTests - */ + #[DataProvider('getTests')] public function testVote(VoterInterface $voter, array $attributes, $expectedVote, $object, $message, ?Vote $vote = null) { $this->assertSame($expectedVote, $voter->vote($this->token, $object, $attributes, $vote), $message); diff --git a/Tests/Resources/TranslationFilesTest.php b/Tests/Resources/TranslationFilesTest.php index 695cdd98..7623ad94 100644 --- a/Tests/Resources/TranslationFilesTest.php +++ b/Tests/Resources/TranslationFilesTest.php @@ -11,14 +11,13 @@ namespace Symfony\Component\Security\Core\Tests\Resources; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Symfony\Component\Translation\Util\XliffUtils; class TranslationFilesTest extends TestCase { - /** - * @dataProvider provideTranslationFiles - */ + #[DataProvider('provideTranslationFiles')] public function testTranslationFileIsValid($filePath) { $document = new \DOMDocument(); @@ -29,9 +28,7 @@ public function testTranslationFileIsValid($filePath) $this->assertCount(0, $errors, \sprintf('"%s" is invalid:%s', $filePath, \PHP_EOL.implode(\PHP_EOL, array_column($errors, 'message')))); } - /** - * @dataProvider provideTranslationFiles - */ + #[DataProvider('provideTranslationFiles')] public function testTranslationFileIsValidWithoutEntityLoader($filePath) { $document = new \DOMDocument(); diff --git a/Tests/User/InMemoryUserTest.php b/Tests/User/InMemoryUserTest.php index bf78d80b..78bfff0d 100644 --- a/Tests/User/InMemoryUserTest.php +++ b/Tests/User/InMemoryUserTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Security\Core\Tests\User; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Group; use PHPUnit\Framework\Attributes\IgnoreDeprecations; use PHPUnit\Framework\TestCase; @@ -72,12 +73,11 @@ public function testToString() } /** - * @dataProvider isEqualToData - * * @param bool $expectation * @param UserInterface $a * @param UserInterface $b */ + #[DataProvider('isEqualToData')] public function testIsEqualTo($expectation, $a, $b) { $this->assertSame($expectation, $a->isEqualTo($b)); diff --git a/Tests/Validator/Constraints/UserPasswordTest.php b/Tests/Validator/Constraints/UserPasswordTest.php index 91f1b5e6..ee29bda2 100644 --- a/Tests/Validator/Constraints/UserPasswordTest.php +++ b/Tests/Validator/Constraints/UserPasswordTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Security\Core\Tests\Validator\Constraints; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Group; use PHPUnit\Framework\Attributes\IgnoreDeprecations; use PHPUnit\Framework\TestCase; @@ -27,9 +28,7 @@ public function testValidatedByStandardValidator() self::assertSame('security.validator.user_password', $constraint->validatedBy()); } - /** - * @dataProvider provideServiceValidatedConstraints - */ + #[DataProvider('provideServiceValidatedConstraints')] public function testValidatedByService(UserPassword $constraint) { self::assertSame('my_service', $constraint->validatedBy()); diff --git a/Tests/Validator/Constraints/UserPasswordValidatorTestCase.php b/Tests/Validator/Constraints/UserPasswordValidatorTestCase.php index c78f6b5f..0f8f47fa 100644 --- a/Tests/Validator/Constraints/UserPasswordValidatorTestCase.php +++ b/Tests/Validator/Constraints/UserPasswordValidatorTestCase.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Security\Core\Tests\Validator\Constraints; +use PHPUnit\Framework\Attributes\DataProvider; use Symfony\Component\PasswordHasher\Hasher\PasswordHasherFactoryInterface; use Symfony\Component\PasswordHasher\PasswordHasherInterface; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; @@ -48,9 +49,7 @@ protected function setUp(): void parent::setUp(); } - /** - * @dataProvider provideConstraints - */ + #[DataProvider('provideConstraints')] public function testPasswordIsValid(UserPassword $constraint) { $this->hasher->expects($this->once()) @@ -63,9 +62,7 @@ public function testPasswordIsValid(UserPassword $constraint) $this->assertNoViolation(); } - /** - * @dataProvider provideConstraints - */ + #[DataProvider('provideConstraints')] public function testPasswordIsNotValid(UserPassword $constraint) { $this->hasher->expects($this->once()) @@ -87,9 +84,7 @@ public static function provideConstraints(): iterable yield 'named arguments' => [new UserPassword(message: 'myMessage')]; } - /** - * @dataProvider emptyPasswordData - */ + #[DataProvider('emptyPasswordData')] public function testEmptyPasswordsAreNotValid($password) { $constraint = new UserPassword([ From 03a133bc53390a6e95e688ef0de5eb1d89ff8f14 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Fri, 1 Aug 2025 14:58:41 +0200 Subject: [PATCH 7/9] run tests with PHPUnit 12.3 --- Tests/Authentication/Token/AbstractTokenTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/Authentication/Token/AbstractTokenTest.php b/Tests/Authentication/Token/AbstractTokenTest.php index 08abc241..0d4c54bd 100644 --- a/Tests/Authentication/Token/AbstractTokenTest.php +++ b/Tests/Authentication/Token/AbstractTokenTest.php @@ -90,7 +90,7 @@ public function testAttributes() } #[DataProvider('provideUsers')] - public function testSetUser($user) + public function testSetUser($user, string $username) { $token = new ConcreteToken(); $token->setUser($user); From 201a65b5e0456bcf92fe80fa742a6b157816c774 Mon Sep 17 00:00:00 2001 From: Simon Jamain Date: Mon, 7 Jul 2025 14:21:30 +0200 Subject: [PATCH 8/9] [Security] Improve performance of `RoleHierarchy::buildRoleMap` method --- Role/RoleHierarchy.php | 2 +- Tests/Role/RoleHierarchyTest.php | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Role/RoleHierarchy.php b/Role/RoleHierarchy.php index a2a58457..e625c0d6 100644 --- a/Role/RoleHierarchy.php +++ b/Role/RoleHierarchy.php @@ -54,7 +54,7 @@ protected function buildRoleMap(): void $this->map[$main] = $roles; $visited = []; $additionalRoles = $roles; - while ($role = array_shift($additionalRoles)) { + while ($role = array_pop($additionalRoles)) { if (!isset($this->hierarchy[$role])) { continue; } diff --git a/Tests/Role/RoleHierarchyTest.php b/Tests/Role/RoleHierarchyTest.php index 5c42e0b3..945ed174 100644 --- a/Tests/Role/RoleHierarchyTest.php +++ b/Tests/Role/RoleHierarchyTest.php @@ -23,11 +23,11 @@ public function testGetReachableRoleNames() 'ROLE_SUPER_ADMIN' => ['ROLE_ADMIN', 'ROLE_FOO'], ]); - $this->assertEquals(['ROLE_USER'], $role->getReachableRoleNames(['ROLE_USER'])); - $this->assertEquals(['ROLE_FOO'], $role->getReachableRoleNames(['ROLE_FOO'])); - $this->assertEquals(['ROLE_ADMIN', 'ROLE_USER'], $role->getReachableRoleNames(['ROLE_ADMIN'])); - $this->assertEquals(['ROLE_FOO', 'ROLE_ADMIN', 'ROLE_USER'], $role->getReachableRoleNames(['ROLE_FOO', 'ROLE_ADMIN'])); - $this->assertEquals(['ROLE_SUPER_ADMIN', 'ROLE_ADMIN', 'ROLE_FOO', 'ROLE_USER'], $role->getReachableRoleNames(['ROLE_SUPER_ADMIN'])); - $this->assertEquals(['ROLE_SUPER_ADMIN', 'ROLE_ADMIN', 'ROLE_FOO', 'ROLE_USER'], $role->getReachableRoleNames(['ROLE_SUPER_ADMIN', 'ROLE_SUPER_ADMIN'])); + $this->assertEqualsCanonicalizing(['ROLE_USER'], $role->getReachableRoleNames(['ROLE_USER'])); + $this->assertEqualsCanonicalizing(['ROLE_FOO'], $role->getReachableRoleNames(['ROLE_FOO'])); + $this->assertEqualsCanonicalizing(['ROLE_ADMIN', 'ROLE_USER'], $role->getReachableRoleNames(['ROLE_ADMIN'])); + $this->assertEqualsCanonicalizing(['ROLE_FOO', 'ROLE_ADMIN', 'ROLE_USER'], $role->getReachableRoleNames(['ROLE_FOO', 'ROLE_ADMIN'])); + $this->assertEqualsCanonicalizing(['ROLE_SUPER_ADMIN', 'ROLE_ADMIN', 'ROLE_FOO', 'ROLE_USER'], $role->getReachableRoleNames(['ROLE_SUPER_ADMIN'])); + $this->assertEqualsCanonicalizing(['ROLE_SUPER_ADMIN', 'ROLE_ADMIN', 'ROLE_FOO', 'ROLE_USER'], $role->getReachableRoleNames(['ROLE_SUPER_ADMIN', 'ROLE_SUPER_ADMIN'])); } } From b5ee9f6b87ce7576c547a533b6982af1dbcb958a Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Sat, 23 Aug 2025 10:13:16 +0200 Subject: [PATCH 9/9] [Security] Preserve ordering of roles in RoleHierarchy --- Role/RoleHierarchy.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Role/RoleHierarchy.php b/Role/RoleHierarchy.php index e625c0d6..a0d2794d 100644 --- a/Role/RoleHierarchy.php +++ b/Role/RoleHierarchy.php @@ -54,8 +54,11 @@ protected function buildRoleMap(): void $this->map[$main] = $roles; $visited = []; $additionalRoles = $roles; - while ($role = array_pop($additionalRoles)) { + while (null !== $role = key($additionalRoles)) { + $role = $additionalRoles[$role]; + if (!isset($this->hierarchy[$role])) { + next($additionalRoles); continue; } @@ -68,6 +71,8 @@ protected function buildRoleMap(): void foreach (array_diff($this->hierarchy[$role], $visited) as $additionalRole) { $additionalRoles[] = $additionalRole; } + + next($additionalRoles); } $this->map[$main] = array_unique($this->map[$main]);