diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2704aa5..d8e5db0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,13 +31,22 @@ jobs: symfony: # Only Symfony supported versions: https://symfony.com/releases # API Platform 3.* only supports Symfony >= 6.1 - - '6.3.*' + - '6.4.*' + - '7.0.*' include: # Ensure the bundle is bootable - php: '8.3' - symfony: '6.3.*' + symfony: '6.4.*' bootable: true quality: true + - php: '8.3' + symfony: '7.0.*' + bootable: true + quality: true + exclude: + # Symfony 7 requires php 8.2 + - php: '8.1' + symfony: '7.0.*' fail-fast: false steps: - name: Checkout diff --git a/composer.json b/composer.json index c7de9ad..f6e5298 100644 --- a/composer.json +++ b/composer.json @@ -16,14 +16,14 @@ } ], "require": { - "php": ">=7.4", + "php": ">=8.1", "ircmaxell/random-lib": "^1.2", - "symfony/config": "^5.1 || ^6.0", - "symfony/dependency-injection": "^5.1 || ^6.0", - "symfony/event-dispatcher": "^5.1 || ^6.0", - "symfony/http-foundation": "^5.1 || ^6.0", - "symfony/http-kernel": "^5.1.5 || ^6.0", - "symfony/serializer": "^5.1 || ^6.0" + "symfony/config": "^5.1 || ^6.0 || ^7.0", + "symfony/dependency-injection": "^5.1 || ^6.0 || ^7.0", + "symfony/event-dispatcher": "^5.1 || ^6.0 || ^7.0", + "symfony/http-foundation": "^5.1 || ^6.0 || ^7.0", + "symfony/http-kernel": "^5.1.5 || ^6.0 || ^7.0", + "symfony/serializer": "^5.1 || ^6.0 || ^7.0" }, "require-dev": { "ext-json": "*", @@ -31,25 +31,24 @@ "behat/behat": "^3.1", "dg/bypass-finals": "^1.1", "doctrine/data-fixtures": "^1.2", - "doctrine/doctrine-bundle": "^1.7.2 || <2.7.1", + "doctrine/doctrine-bundle": "^2.11", "doctrine/orm": "^2.6.3", - "egulias/email-validator": "^2.1.10", "friends-of-behat/symfony-extension": "^2.0.11 || ^2.1.0", - "jms/serializer-bundle": "^1.4 || ^2.3 || ^3.0 || ^4.0", + "jms/serializer-bundle": "^1.4 || ^2.3 || ^3.0 || ^4.0 || ^5.0", "laminas/laminas-code": "^3.4 || ^4.0", "ocramius/proxy-manager": "^2.0.4", "sebastian/comparator": "^3.0", - "symfony/asset": "^5.1 || ^6.0", - "symfony/browser-kit": "^5.1 || ^6.0", - "symfony/framework-bundle": "^5.1 || ^6.0", - "symfony/mailer": "^5.1 || ^6.0", - "symfony/phpunit-bridge": "^5.1 || ^6.0", - "symfony/property-access": "^5.1 || ^6.0", - "symfony/security-bundle": "^5.1 || ^6.0", - "symfony/stopwatch": "^5.1 || ^6.0", - "symfony/templating": "^5.1 || ^6.0", - "symfony/twig-bundle": "^5.1 || ^6.0", - "symfony/var-dumper": "^5.1 || ^6.0" + "symfony/asset": "^5.1 || ^6.0 || ^7.0", + "symfony/browser-kit": "^5.1 || ^6.0 || ^7.0", + "symfony/framework-bundle": "^5.1 || ^6.0 || ^7.0", + "symfony/mailer": "^5.1 || ^6.0 || ^7.0", + "symfony/phpunit-bridge": "^5.1 || ^6.0 || ^7.0", + "symfony/property-access": "^5.1 || ^6.0 || ^7.0", + "symfony/security-bundle": "^5.1 || ^6.0 || ^7.0", + "symfony/stopwatch": "^5.1 || ^6.0 || ^7.0", + "symfony/templating": "^5.1 || ^6.0 || ^7.0", + "symfony/twig-bundle": "^5.1 || ^6.0 || ^7.0", + "symfony/var-dumper": "^5.1 || ^6.0 || ^7.0" }, "suggest": { "doctrine/doctrine-bundle": "To connect with Doctrine in Symfony project", diff --git a/features/app/AppKernel.php b/features/app/AppKernel.php index 7381ae0..4ec39b6 100644 --- a/features/app/AppKernel.php +++ b/features/app/AppKernel.php @@ -103,7 +103,7 @@ protected function configureContainer($container, LoaderInterface $loader): void new Reference('twig'), new Reference('doctrine'), ])->tag('kernel.event_subscriber'); - $container->services()->set(\FeatureContext::class, \FeatureContext::class)->args([ + $container->services()->set(FeatureContext::class, FeatureContext::class)->args([ new Reference('test.client'), new Reference('doctrine'), new Reference('coop_tilleuls_forgot_password.manager.password_token'), @@ -116,7 +116,7 @@ protected function configureContainer($container, LoaderInterface $loader): void new Reference('twig'), new Reference('doctrine'), ]))->addTag('kernel.event_subscriber')); - $container->setDefinition(\FeatureContext::class, (new Definition(\FeatureContext::class, [ + $container->setDefinition(FeatureContext::class, (new Definition(FeatureContext::class, [ new Reference('test.client'), new Reference('doctrine'), new Reference('coop_tilleuls_forgot_password.manager.password_token'), @@ -175,7 +175,7 @@ protected function configureContainer($container, LoaderInterface $loader): void 'mappings' => [ 'App' => [ 'is_bundle' => false, - 'type' => 'annotation', + 'type' => 'attribute', 'dir' => $this->getProjectDir().'/src/Entity', 'prefix' => 'App\Entity', 'alias' => 'App', diff --git a/features/app/src/Entity/Admin.php b/features/app/src/Entity/Admin.php index c2cc5b1..de8ea74 100644 --- a/features/app/src/Entity/Admin.php +++ b/features/app/src/Entity/Admin.php @@ -16,41 +16,33 @@ use Doctrine\ORM\Mapping as ORM; use Symfony\Component\Security\Core\User\UserInterface; -/** - * @ORM\Entity - */ +#[ORM\Entity] final class Admin implements UserInterface { /** * @var int - * - * @ORM\Id - * - * @ORM\Column(type="integer") - * - * @ORM\GeneratedValue(strategy="AUTO") */ + #[ORM\Id] + #[ORM\Column(type: 'integer')] + #[ORM\GeneratedValue(strategy: 'AUTO')] private $id; /** * @var string - * - * @ORM\Column(type="string") */ + #[ORM\Column(type: 'string')] private $email; /** * @var string - * - * @ORM\Column(type="string") */ + #[ORM\Column(type: 'string')] private $username; /** * @var string - * - * @ORM\Column(type="string") */ + #[ORM\Column(type: 'string')] private $password; /** diff --git a/features/app/src/Entity/PasswordAdminToken.php b/features/app/src/Entity/PasswordAdminToken.php index daa909b..2e6f0ee 100644 --- a/features/app/src/Entity/PasswordAdminToken.php +++ b/features/app/src/Entity/PasswordAdminToken.php @@ -16,29 +16,22 @@ use CoopTilleuls\ForgotPasswordBundle\Entity\AbstractPasswordToken; use Doctrine\ORM\Mapping as ORM; -/** - * @ORM\Entity - */ +#[ORM\Entity] final class PasswordAdminToken extends AbstractPasswordToken { /** * @var int - * - * @ORM\Id - * - * @ORM\Column(type="integer") - * - * @ORM\GeneratedValue(strategy="AUTO") */ + #[ORM\Id] + #[ORM\Column(type: 'integer')] + #[ORM\GeneratedValue(strategy: 'AUTO')] private $id; /** * @var Admin - * - * @ORM\ManyToOne(targetEntity=Admin::class) - * - * @ORM\JoinColumn(nullable=false, name="user_id") */ + #[ORM\ManyToOne(targetEntity: Admin::class)] + #[ORM\JoinColumn(nullable: false, name: 'user_id')] private $admin; /** diff --git a/features/app/src/Entity/PasswordToken.php b/features/app/src/Entity/PasswordToken.php index 522f164..d6fe331 100644 --- a/features/app/src/Entity/PasswordToken.php +++ b/features/app/src/Entity/PasswordToken.php @@ -16,31 +16,22 @@ use CoopTilleuls\ForgotPasswordBundle\Entity\AbstractPasswordToken; use Doctrine\ORM\Mapping as ORM; -/** - * @ORM\Entity - * - * @author Vincent CHALAMON - */ +#[ORM\Entity] final class PasswordToken extends AbstractPasswordToken { /** * @var int - * - * @ORM\Id - * - * @ORM\Column(type="integer") - * - * @ORM\GeneratedValue(strategy="AUTO") */ + #[ORM\Id] + #[ORM\Column(type: 'integer')] + #[ORM\GeneratedValue(strategy: 'AUTO')] private $id; /** * @var User - * - * @ORM\ManyToOne(targetEntity="User") - * - * @ORM\JoinColumn(nullable=false, name="user_id") */ + #[ORM\ManyToOne(targetEntity: 'User')] + #[ORM\JoinColumn(nullable: false, name: 'user_id')] private $user; /** diff --git a/features/app/src/Entity/User.php b/features/app/src/Entity/User.php index 5a5b4b8..21bcd5d 100644 --- a/features/app/src/Entity/User.php +++ b/features/app/src/Entity/User.php @@ -16,43 +16,33 @@ use Doctrine\ORM\Mapping as ORM; use Symfony\Component\Security\Core\User\UserInterface; -/** - * @ORM\Entity - * - * @author Vincent CHALAMON - */ +#[ORM\Entity] final class User implements UserInterface { /** * @var int - * - * @ORM\Id - * - * @ORM\Column(type="integer") - * - * @ORM\GeneratedValue(strategy="AUTO") */ + #[ORM\Id] + #[ORM\Column(type: 'integer')] + #[ORM\GeneratedValue(strategy: 'AUTO')] private $id; /** * @var string - * - * @ORM\Column(type="string") */ + #[ORM\Column(type: 'string')] private $email; /** * @var string - * - * @ORM\Column(type="string") */ + #[ORM\Column(type: 'string')] private $username; /** * @var string - * - * @ORM\Column(type="string") */ + #[ORM\Column(type: 'string')] private $password; /** diff --git a/features/bootstrap/FeatureContext.php b/features/bootstrap/FeatureContext.php index 20bc7f3..03fe84c 100644 --- a/features/bootstrap/FeatureContext.php +++ b/features/bootstrap/FeatureContext.php @@ -84,7 +84,7 @@ public function resetDatabase(): void $purger->setPurgeMode(ORMPurger::PURGE_MODE_TRUNCATE); try { $purger->purge(); - } catch (\Exception $e) { + } catch (Exception $e) { $schemaTool = new SchemaTool($this->doctrine->getManager()); $schemaTool->createSchema($this->doctrine->getManager()->getMetadataFactory()->getAllMetadata()); } @@ -95,7 +95,7 @@ public function resetDatabase(): void */ public function iHaveAValidToken(): void { - $this->passwordTokenManager->createPasswordToken($this->createUser(), new \DateTime('+1 day')); + $this->passwordTokenManager->createPasswordToken($this->createUser(), new DateTime('+1 day')); } /** @@ -103,7 +103,7 @@ public function iHaveAValidToken(): void */ public function iHaveAnExpiredToken(): void { - $this->passwordTokenManager->createPasswordToken($this->createUser(), new \DateTime('-1 minute')); + $this->passwordTokenManager->createPasswordToken($this->createUser(), new DateTime('-1 minute')); } /** @@ -151,7 +151,7 @@ public function iShouldReceiveAnEmail($value = 'john.doe@example.com'): void $messages = $mailCollector->getEvents()->getMessages(); Assert::assertCount(1, $messages, 'No email has been sent'); - /** @var \Symfony\Component\Mime\Email $message */ + /** @var Symfony\Component\Mime\Email $message */ $message = $messages[0]; Assert::assertInstanceOf(RawMessage::class, $message); Assert::assertEquals('RĂ©initialisation de votre mot de passe', $message->getSubject()); @@ -237,7 +237,7 @@ public function iResetMyPasswordUsingNoParameter(): void */ public function iUpdateMyPassword(): void { - $token = $this->passwordTokenManager->createPasswordToken($this->createUser(), new \DateTime('+1 day')); + $token = $this->passwordTokenManager->createPasswordToken($this->createUser(), new DateTime('+1 day')); $this->client->request( 'POST', @@ -270,7 +270,7 @@ public function thePasswordShouldHaveBeenUpdated(): void */ public function iUpdateMyPasswordUsingNoPassword(): void { - $token = $this->passwordTokenManager->createPasswordToken($this->createUser(), new \DateTime('+1 day')); + $token = $this->passwordTokenManager->createPasswordToken($this->createUser(), new DateTime('+1 day')); $this->client->request('POST', sprintf('/api/forgot-password/%s', $token->getToken())); } @@ -299,7 +299,7 @@ public function iUpdateMyPasswordUsingAnInvalidToken(): void */ public function iUpdateMyPasswordUsingWrongProvider(): void { - $token = $this->passwordTokenManager->createPasswordToken($this->createAdmin(), new \DateTime('+1 day'), $this->providerChain->get('admin')); + $token = $this->passwordTokenManager->createPasswordToken($this->createAdmin(), new DateTime('+1 day'), $this->providerChain->get('admin')); $this->client->request( 'POST', @@ -320,7 +320,7 @@ public function iUpdateMyPasswordUsingWrongProvider(): void */ public function iUpdateMyPasswordUsingAValidProviderButAnInvalidPasswordField(): void { - $token = $this->passwordTokenManager->createPasswordToken($this->createAdmin(), new \DateTime('+1 day'), $this->providerChain->get('admin')); + $token = $this->passwordTokenManager->createPasswordToken($this->createAdmin(), new DateTime('+1 day'), $this->providerChain->get('admin')); $this->client->request( 'POST', @@ -341,7 +341,7 @@ public function iUpdateMyPasswordUsingAValidProviderButAnInvalidPasswordField(): */ public function iUpdateMyPasswordUsingAnExpiredToken(): void { - $token = $this->passwordTokenManager->createPasswordToken($this->createUser(), new \DateTime('-1 minute')); + $token = $this->passwordTokenManager->createPasswordToken($this->createUser(), new DateTime('-1 minute')); $this->client->request( 'POST', @@ -362,7 +362,7 @@ public function iUpdateMyPasswordUsingAnExpiredToken(): void */ public function iGetAPasswordToken(): void { - $token = $this->passwordTokenManager->createPasswordToken($this->createUser(), new \DateTime('+1 day')); + $token = $this->passwordTokenManager->createPasswordToken($this->createUser(), new DateTime('+1 day')); $token->setToken('d7xtQlJVyN61TzWtrY6xy37zOxB66BqMSDXEbXBbo2Mw4Jjt9C'); $this->doctrine->getManager()->persist($token); $this->doctrine->getManager()->flush(); @@ -387,7 +387,7 @@ public function iShouldGetAPasswordToken(): void */ public function iGetAPasswordTokenUsingAnExpiredToken(): void { - $token = $this->passwordTokenManager->createPasswordToken($this->createUser(), new \DateTime('-1 minute')); + $token = $this->passwordTokenManager->createPasswordToken($this->createUser(), new DateTime('-1 minute')); $this->client->request('GET', sprintf('/api/forgot-password/%s', $token->getToken())); } diff --git a/src/Bridge/ApiPlatform/Serializer/DocumentationNormalizer.php b/src/Bridge/ApiPlatform/Serializer/DocumentationNormalizer.php index ecd5242..5315410 100644 --- a/src/Bridge/ApiPlatform/Serializer/DocumentationNormalizer.php +++ b/src/Bridge/ApiPlatform/Serializer/DocumentationNormalizer.php @@ -15,6 +15,7 @@ use CoopTilleuls\ForgotPasswordBundle\Provider\ProviderChainInterface; use Symfony\Component\Routing\RouterInterface; +use Symfony\Component\Serializer\Normalizer\CacheableSupportsMethodInterface; use Symfony\Component\Serializer\Normalizer\NormalizerInterface; /** @@ -191,4 +192,16 @@ public function supportsNormalization($data, $format = null, array $context = [] { return $this->decorated->supportsNormalization($data, $format); } + + public function getSupportedTypes(?string $format): array + { + // @deprecated remove condition when support for symfony versions under 6.4 is dropped + if (!method_exists($this->decorated, 'getSupportedTypes')) { + return [ + '*' => $this->decorated instanceof CacheableSupportsMethodInterface && $this->decorated->hasCacheableSupportsMethod(), + ]; + } + + return $this->decorated->getSupportedTypes($format); + } } diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php index 2ea5f4f..7d218e1 100644 --- a/src/DependencyInjection/Configuration.php +++ b/src/DependencyInjection/Configuration.php @@ -21,12 +21,7 @@ */ final class Configuration implements ConfigurationInterface { - /** - * {@inheritdoc} - * - * @return TreeBuilder - */ - public function getConfigTreeBuilder() + public function getConfigTreeBuilder(): TreeBuilder { if (method_exists(TreeBuilder::class, 'root')) { $treeBuilder = new TreeBuilder(); diff --git a/tests/Bridge/ApiPlatform/Serializer/DocumentationNormalizerTest.php b/tests/Bridge/ApiPlatform/Serializer/DocumentationNormalizerTest.php index 32fb4ad..4ba359c 100755 --- a/tests/Bridge/ApiPlatform/Serializer/DocumentationNormalizerTest.php +++ b/tests/Bridge/ApiPlatform/Serializer/DocumentationNormalizerTest.php @@ -84,6 +84,19 @@ public function testItSupportsDecoratedSupport(): void $this->assertTrue($this->normalizer->supportsNormalization('foo', 'bar')); } + public function testItSupportsDecoratedType(): void + { + // @deprecated remove condition when support for symfony versions under 6.4 is dropped + if (!method_exists($this->normalizerMock, 'getSupportedTypes')) { + $this->assertSame(['*' => false], $this->normalizer->getSupportedTypes('foo')); + + return; + } + + $this->normalizerMock->expects($this->once())->method('getSupportedTypes')->with('foo')->willReturn(['bar', 'baz']); + $this->assertSame(['bar', 'baz'], $this->normalizer->getSupportedTypes('foo')); + } + public function testItDecoratesNormalizedData(): void { $this->routerMock->expects($this->once())->method('getRouteCollection')->willReturn($this->routeCollectionMock);