diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 4465f75..eeb4a4f 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -15,14 +15,14 @@ jobs: strategy: matrix: php-version: - - "7.4" + - "8.1" dependencies: - "highest" steps: - name: "Checkout" - uses: "actions/checkout@v3" + uses: "actions/checkout@v4" - name: "Setup PHP, with composer and extensions" uses: "shivammathur/setup-php@v2" @@ -32,7 +32,7 @@ jobs: coverage: "none" - name: "Install composer dependencies" - uses: "ramsey/composer-install@v2" + uses: "ramsey/composer-install@v3" with: dependency-versions: "${{ matrix.dependencies }}" @@ -53,8 +53,6 @@ jobs: strategy: matrix: php-version: - - "7.4" - - "8.0" - "8.1" - "8.2" @@ -63,7 +61,7 @@ jobs: steps: - name: "Checkout" - uses: "actions/checkout@v3" + uses: "actions/checkout@v4" - name: "Setup PHP, with composer and extensions" uses: "shivammathur/setup-php@v2" @@ -74,7 +72,7 @@ jobs: tools: "composer-require-checker, composer-unused" - name: "Install composer dependencies" - uses: "ramsey/composer-install@v2" + uses: "ramsey/composer-install@v3" with: dependency-versions: "${{ matrix.dependencies }}" @@ -92,8 +90,6 @@ jobs: strategy: matrix: php-version: - - "7.4" - - "8.0" - "8.1" - "8.2" @@ -102,7 +98,7 @@ jobs: steps: - name: "Checkout" - uses: "actions/checkout@v3" + uses: "actions/checkout@v4" - name: "Setup PHP, with composer and extensions" uses: "shivammathur/setup-php@v2" @@ -112,7 +108,7 @@ jobs: coverage: "none" - name: "Install composer dependencies" - uses: "ramsey/composer-install@v2" + uses: "ramsey/composer-install@v3" with: dependency-versions: "${{ matrix.dependencies }}" @@ -127,8 +123,6 @@ jobs: strategy: matrix: php-version: - - "7.4" - - "8.0" - "8.1" - "8.2" @@ -137,7 +131,7 @@ jobs: steps: - name: "Checkout" - uses: "actions/checkout@v3" + uses: "actions/checkout@v4" - name: "Setup PHP, with composer and extensions" uses: "shivammathur/setup-php@v2" @@ -147,7 +141,7 @@ jobs: coverage: "none" - name: "Install composer dependencies" - uses: "ramsey/composer-install@v2" + uses: "ramsey/composer-install@v3" with: dependency-versions: "${{ matrix.dependencies }}" @@ -169,7 +163,7 @@ jobs: steps: - name: "Checkout" - uses: "actions/checkout@v3" + uses: "actions/checkout@v4" - name: "Setup PHP, with composer and extensions" uses: "shivammathur/setup-php@v2" @@ -182,7 +176,7 @@ jobs: run: "echo \"::add-matcher::${{ runner.tool_cache }}/phpunit.json\"" - name: "Install composer dependencies" - uses: "ramsey/composer-install@v2" + uses: "ramsey/composer-install@v3" with: dependency-versions: "${{ matrix.dependencies }}" @@ -209,7 +203,7 @@ jobs: steps: - name: "Checkout" - uses: "actions/checkout@v3" + uses: "actions/checkout@v4" - name: "Setup PHP, with composer and extensions" uses: "shivammathur/setup-php@v2" @@ -219,7 +213,7 @@ jobs: php-version: "${{ matrix.php-version }}" - name: "Install composer dependencies" - uses: "ramsey/composer-install@v2" + uses: "ramsey/composer-install@v3" with: dependency-versions: "${{ matrix.dependencies }}" diff --git a/composer-require-checker.json b/composer-require-checker.json new file mode 100644 index 0000000..16f3cf4 --- /dev/null +++ b/composer-require-checker.json @@ -0,0 +1,5 @@ +{ + "symbol-whitelist": [ + "Setono\\ClientBundle\\Context\\ClientContextInterface" + ] +} diff --git a/composer.json b/composer.json index cc69dd0..6d593c5 100644 --- a/composer.json +++ b/composer.json @@ -10,25 +10,28 @@ } ], "require": { - "php": ">=7.4", + "php": ">=8.1", + "composer/composer": "^2.0", "doctrine/dbal": "^2.12 || ^3.0", "setono/client-id": "^0.2", "setono/client-id-contracts": "^0.2", - "symfony/config": "^5.4 || ^6.0", - "symfony/dependency-injection": "^5.4 || ^6.0", - "symfony/event-dispatcher": "^5.4 || ^6.0", - "symfony/http-foundation": "^5.4 || ^6.0", - "symfony/http-kernel": "^5.4 || ^6.0", + "symfony/config": "^5.4 || ^6.0 || ^7.0", + "symfony/dependency-injection": "^5.4 || ^6.0 || ^7.0", + "symfony/event-dispatcher": "^5.4 || ^6.0 || ^7.0", + "symfony/http-foundation": "^5.4 || ^6.0 || ^7.0", + "symfony/http-kernel": "^5.4 || ^6.0 || ^7.0", "webmozart/assert": "^1.11" }, "require-dev": { - "infection/infection": "^0.23.0", - "matthiasnoback/symfony-dependency-injection-test": "^4.2", - "nyholm/symfony-bundle-test": "^1.7", - "phpunit/phpunit": "^9.5", - "psalm/plugin-phpunit": "^0.16.0", - "psalm/plugin-symfony": "^2.1", - "setono/code-quality-pack": "^2.4" + "infection/infection": "^0.27.11", + "matthiasnoback/symfony-dependency-injection-test": "^4.3 || ^5.1", + "nyholm/symfony-bundle-test": "^3.0", + "phpspec/prophecy-phpunit": "^2.2", + "phpunit/phpunit": "^9.6", + "psalm/plugin-phpunit": "^0.19", + "psalm/plugin-symfony": "^5.1", + "setono/client-bundle": "dev-master", + "setono/code-quality-pack": "^2.7" }, "prefer-stable": true, "autoload": { diff --git a/ecs.php b/ecs.php index 69faffe..6397e61 100644 --- a/ecs.php +++ b/ecs.php @@ -2,12 +2,12 @@ declare(strict_types=1); -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\EasyCodingStandard\ValueObject\Option; +use Symplify\EasyCodingStandard\Config\ECSConfig; -return static function (ContainerConfigurator $containerConfigurator): void { - $containerConfigurator->import('vendor/sylius-labs/coding-standard/ecs.php'); - $containerConfigurator->parameters()->set(Option::PATHS, [ - 'src', 'tests' +return static function (ECSConfig $config): void { + $config->import('vendor/sylius-labs/coding-standard/ecs.php'); + $config->paths([ + 'src', + 'tests', ]); }; diff --git a/infection.json.dist b/infection.json.dist index a1fa8ab..468ce7b 100644 --- a/infection.json.dist +++ b/infection.json.dist @@ -6,8 +6,8 @@ }, "logs": { "text": "php://stderr", - "badge": { - "branch": "master" + "stryker": { + "badge": "master" } }, "minMsi": 100, diff --git a/psalm.xml b/psalm.xml index 3430de2..a618ed3 100644 --- a/psalm.xml +++ b/psalm.xml @@ -3,6 +3,10 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="https://getpsalm.org/schema/config" xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd" + findUnusedPsalmSuppress="false" + findUnusedVariablesAndParams="false" + findUnusedCode="false" + findUnusedBaselineEntry="false" errorLevel="1" > diff --git a/src/DependencyInjection/SetonoClientIdExtension.php b/src/DependencyInjection/SetonoClientIdExtension.php index edecabc..ccd19ad 100644 --- a/src/DependencyInjection/SetonoClientIdExtension.php +++ b/src/DependencyInjection/SetonoClientIdExtension.php @@ -4,6 +4,7 @@ namespace Setono\ClientIdBundle\DependencyInjection; +use Composer\InstalledVersions; use Symfony\Component\Config\FileLocator; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Extension\Extension; @@ -15,5 +16,11 @@ public function load(array $configs, ContainerBuilder $container): void { $loader = new XmlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config')); $loader->load('services.xml'); + + if (InstalledVersions::isInstalled('setono/client-bundle', false)) { + $loader->load('services/conditional/provider.xml'); + } else { + $loader->load('services/conditional/event_listener.xml'); + } } } diff --git a/src/Doctrine/Type/ClientIdType.php b/src/Doctrine/Type/ClientIdType.php index fc1ba94..f15823f 100644 --- a/src/Doctrine/Type/ClientIdType.php +++ b/src/Doctrine/Type/ClientIdType.php @@ -9,7 +9,6 @@ use Doctrine\DBAL\Types\Type; use Setono\ClientId\ClientId; -// todo see this https://github.com/symfony/symfony/blob/5.x/src/Symfony/Bridge/Doctrine/Types/AbstractUidType.php as an example final class ClientIdType extends Type { public const CLIENT_ID = 'client_id'; @@ -21,6 +20,7 @@ public function getName(): string public function getSQLDeclaration(array $column, AbstractPlatform $platform): string { + /** @psalm-suppress DeprecatedMethod */ return $platform->getVarcharTypeDeclarationSQL($column); } diff --git a/src/EventListener/SaveClientIdSubscriber.php b/src/EventListener/SaveClientIdSubscriber.php index e04fe87..d6e0130 100644 --- a/src/EventListener/SaveClientIdSubscriber.php +++ b/src/EventListener/SaveClientIdSubscriber.php @@ -44,7 +44,7 @@ public function save(ResponseEvent $event): void $response->headers->setCookie(Cookie::create( $this->cookieName, $this->clientIdProvider->getClientId()->toString(), - new \DateTime('+360 days') + new \DateTime('+360 days'), )); } } diff --git a/src/Provider/CompatibilityProvider.php b/src/Provider/CompatibilityProvider.php new file mode 100644 index 0000000..92e5fb4 --- /dev/null +++ b/src/Provider/CompatibilityProvider.php @@ -0,0 +1,24 @@ +clientContext = $clientContext; + } + + public function getClientId(): ClientId + { + return new ClientId($this->clientContext->getClient()->id); + } +} diff --git a/src/Resources/config/services.xml b/src/Resources/config/services.xml index a42ad99..1b8e73b 100644 --- a/src/Resources/config/services.xml +++ b/src/Resources/config/services.xml @@ -4,7 +4,6 @@ xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> - diff --git a/src/Resources/config/services/event_listener.xml b/src/Resources/config/services/conditional/event_listener.xml similarity index 88% rename from src/Resources/config/services/event_listener.xml rename to src/Resources/config/services/conditional/event_listener.xml index 8d77ac6..049f66d 100644 --- a/src/Resources/config/services/event_listener.xml +++ b/src/Resources/config/services/conditional/event_listener.xml @@ -3,6 +3,7 @@ + diff --git a/src/Resources/config/services/conditional/provider.xml b/src/Resources/config/services/conditional/provider.xml new file mode 100644 index 0000000..3999054 --- /dev/null +++ b/src/Resources/config/services/conditional/provider.xml @@ -0,0 +1,13 @@ + + + + + + + + + + diff --git a/tests/Doctrine/Type/ClientIdTypeTest.php b/tests/Doctrine/Type/ClientIdTypeTest.php index 9a631ec..3a3500c 100644 --- a/tests/Doctrine/Type/ClientIdTypeTest.php +++ b/tests/Doctrine/Type/ClientIdTypeTest.php @@ -68,7 +68,7 @@ public function it_converts_string_to_database_value(): void public function it_throws_exception_when_trying_to_convert_unsupported_type_for_database_value(): void { $this->expectException(ConversionException::class); - $this->expectExceptionMessage("Could not convert PHP value of type 'stdClass' to type 'client_id'. Expected one of the following types: null, string, Setono\ClientId\ClientId"); + $this->expectExceptionMessage('Could not convert PHP value of type stdClass to type client_id. Expected one of the following types: null, string, Setono\ClientId\ClientId'); $this->type->convertToDatabaseValue(new \stdClass(), $this->platform); } @@ -99,7 +99,7 @@ public function it_converts_string_to_php_value(): void public function it_throws_exception_when_trying_to_convert_unsupported_type_for_php_value(): void { $this->expectException(ConversionException::class); - $this->expectExceptionMessage("Could not convert PHP value of type 'stdClass' to type 'client_id'. Expected one of the following types: null, string, Setono\ClientId\ClientId"); + $this->expectExceptionMessage('Could not convert PHP value of type stdClass to type client_id. Expected one of the following types: null, string, Setono\ClientId\ClientId'); $this->type->convertToPHPValue(new \stdClass(), $this->platform); } diff --git a/tests/EventListener/SaveClientIdSubscriberTest.php b/tests/EventListener/SaveClientIdSubscriberTest.php index de85d6e..3fd5e23 100644 --- a/tests/EventListener/SaveClientIdSubscriberTest.php +++ b/tests/EventListener/SaveClientIdSubscriberTest.php @@ -5,26 +5,24 @@ namespace Setono\ClientIdBundle\Tests\EventListener; use PHPUnit\Framework\TestCase; +use Prophecy\PhpUnit\ProphecyTrait; use Setono\ClientId\ClientId; use Setono\ClientId\Provider\ClientIdProviderInterface; use Setono\ClientIdBundle\EventListener\SaveClientIdSubscriber; -use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait; -use Symfony\Component\Config\Loader\LoaderInterface; -use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Event\ResponseEvent; use Symfony\Component\HttpKernel\HttpKernelInterface; -use Symfony\Component\HttpKernel\Kernel; use Symfony\Component\HttpKernel\KernelEvents; use Symfony\Component\HttpKernel\KernelInterface; -use Symfony\Component\Routing\RouteCollectionBuilder; /** * @covers \Setono\ClientIdBundle\EventListener\SaveClientIdSubscriber */ final class SaveClientIdSubscriberTest extends TestCase { + use ProphecyTrait; + /** * @test */ @@ -40,7 +38,9 @@ public function it_saves(): void { $response = new Response(); - $event = new ResponseEvent(self::getKernel(), new Request(), HttpKernelInterface::MAIN_REQUEST, $response); + $kernel = $this->prophesize(KernelInterface::class); + + $event = new ResponseEvent($kernel->reveal(), new Request(), HttpKernelInterface::MAIN_REQUEST, $response); $subscriber = new SaveClientIdSubscriber(self::getClientIdProvider(), 'cookie_name'); $subscriber->save($event); @@ -60,7 +60,9 @@ public function it_does_not_save_cookie_if_request_is_not_a_master_request(): vo { $response = new Response(); - $event = new ResponseEvent(self::getKernel(), new Request(), HttpKernelInterface::SUB_REQUEST, $response); + $kernel = $this->prophesize(KernelInterface::class); + + $event = new ResponseEvent($kernel->reveal(), new Request(), HttpKernelInterface::SUB_REQUEST, $response); $subscriber = new SaveClientIdSubscriber(self::getClientIdProvider(), 'cookie_name'); $subscriber->save($event); @@ -75,11 +77,13 @@ public function it_does_not_save_cookie_if_request_is_an_ajax_request(): void { $response = new Response(); + $kernel = $this->prophesize(KernelInterface::class); + $event = new ResponseEvent( - self::getKernel(), + $kernel->reveal(), new Request([], [], [], [], [], ['HTTP_X-Requested-With' => 'XMLHttpRequest']), HttpKernelInterface::MAIN_REQUEST, - $response + $response, ); $subscriber = new SaveClientIdSubscriber(self::getClientIdProvider(), 'cookie_name'); @@ -97,34 +101,4 @@ public function getClientId(): ClientId } }; } - - private static function getKernel(): KernelInterface - { - return new class() extends Kernel { - use MicroKernelTrait; - - public function __construct() - { - parent::__construct('test', true); - } - - public function registerBundles(): iterable - { - return []; - } - - /** - * We have to use this because we support SF4.4 - * - * @psalm-suppress DeprecatedClass - */ - protected function configureRoutes(RouteCollectionBuilder $routes): void - { - } - - protected function configureContainer(ContainerBuilder $c, LoaderInterface $loader): void - { - } - }; - } } diff --git a/tests/SetonoClientIdBundleTest.php b/tests/SetonoClientIdBundleTest.php index 59554f6..89dce83 100644 --- a/tests/SetonoClientIdBundleTest.php +++ b/tests/SetonoClientIdBundleTest.php @@ -4,8 +4,7 @@ namespace Setono\ClientIdBundle\Tests; -use Nyholm\BundleTest\BaseBundleTestCase; -use Nyholm\BundleTest\CompilerPass\PublicServicePass; +use Nyholm\BundleTest\TestKernel; use Setono\ClientId\Cookie\Adapter\SymfonyCookieReader; use Setono\ClientId\Cookie\CookieReaderInterface; use Setono\ClientId\Generator\ClientIdGeneratorInterface; @@ -16,20 +15,50 @@ use Setono\ClientIdBundle\Doctrine\Type\ClientIdType; use Setono\ClientIdBundle\EventListener\SaveClientIdSubscriber; use Setono\ClientIdBundle\SetonoClientIdBundle; +use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\HttpKernel\KernelInterface; -final class SetonoClientIdBundleTest extends BaseBundleTestCase +final class SetonoClientIdBundleTest extends KernelTestCase { + protected static function getKernelClass(): string + { + return TestKernel::class; + } + protected function getBundleClass(): string { return SetonoClientIdBundle::class; } - protected function setUp(): void + protected static function createKernel(array $options = []): KernelInterface { - parent::setUp(); + /** + * @var TestKernel $kernel + */ + $kernel = parent::createKernel($options); + $kernel->addTestBundle(SetonoClientIdBundle::class); + $kernel->addTestCompilerPass(new class() implements CompilerPassInterface { + public function process(ContainerBuilder $container): void + { + foreach ($container->getDefinitions() as $id => $definition) { + if (str_starts_with($id, 'setono')) { + $definition->setPublic(true); + } + } + + foreach ($container->getAliases() as $id => $alias) { + if (str_starts_with($id, 'setono')) { + $alias->setPublic(true); + } + } + } + }); + $kernel->handleOptions($options); - $this->addCompilerPass(new PublicServicePass('#setono.*#i')); + return $kernel; } /** @@ -37,8 +66,7 @@ protected function setUp(): void */ public function it_has_services(): void { - $this->bootKernel(); - $container = $this->getContainer(); + $container = self::getContainer(); $services = [ // cookie.xml @@ -101,7 +129,7 @@ public function it_has_services(): void self::assertInstanceOf( $data['class'], $service, - sprintf('Service with id "%s" is not an instance of %s. It is an instance of %s', $id, $data['class'], get_class($service)) + sprintf('Service with id "%s" is not an instance of %s. It is an instance of %s', $id, $data['class'], get_class($service)), ); } @@ -109,7 +137,7 @@ public function it_has_services(): void self::assertInstanceOf( $data['interface'], $service, - sprintf('Service with id "%s" is not an instance of %s. It is an instance of %s', $id, $data['interface'], get_class($service)) + sprintf('Service with id "%s" is not an instance of %s. It is an instance of %s', $id, $data['interface'], get_class($service)), ); } } @@ -120,12 +148,10 @@ public function it_has_services(): void */ public function it_adds_compiler_pass(): void { - $kernel = $this->createKernel(); - $kernel->addConfigFile(__DIR__ . '/config/config.yaml'); - - $this->bootKernel(); - - $container = $this->getContainer(); + $kernel = self::bootKernel(['config' => static function (TestKernel $kernel) { + $kernel->addTestConfig(__DIR__ . '/config/config.yaml'); + }]); + $container = $kernel->getContainer(); /** @psalm-suppress UndefinedDocblockClass */ $typeDefinitions = $container->getParameter('doctrine.dbal.connection_factory.types');