diff --git a/composer-dependency-analyser.php b/composer-dependency-analyser.php new file mode 100644 index 0000000..fc86a1f --- /dev/null +++ b/composer-dependency-analyser.php @@ -0,0 +1,10 @@ +ignoreErrorsOnPackage('symfony/translation', [ErrorType::SHADOW_DEPENDENCY]) + ->ignoreErrorsOnPackage('contao/newsletter-bundle', [ErrorType::DEV_DEPENDENCY_IN_PROD]) + ->ignoreUnknownClasses([Symfony\Component\HttpKernel\UriSigner::class, Contao\ModulePassword::class]) +; diff --git a/composer.json b/composer.json index 9a809f4..a8b92e0 100644 --- a/composer.json +++ b/composer.json @@ -32,6 +32,7 @@ "doctrine/orm": "^2.19", "knplabs/knp-menu": "^3.1", "psr/container": "^1.0 || ^2.0", + "psr/log": "^2.0 || ^3.0", "ramsey/collection": "^1.2", "soundasleep/html2text": "^2.0", "symfony/asset": "^5.4 || ^6.0 || ^7.0", diff --git a/phpstan.neon b/phpstan.neon index 88596f0..907334c 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,3 +1,5 @@ parameters: excludePaths: - - src/Legacy/LostPasswordModule.php \ No newline at end of file + - src/Legacy/LostPasswordModule.php + ignoreErrors: + - '#Symfony\\Component\\HttpKernel\\UriSigner#' diff --git a/src/BulkyItem/BulkyItemStorage.php b/src/BulkyItem/BulkyItemStorage.php index 2e8323c..0cf59e6 100644 --- a/src/BulkyItem/BulkyItemStorage.php +++ b/src/BulkyItem/BulkyItemStorage.php @@ -7,7 +7,8 @@ use Contao\CoreBundle\Filesystem\ExtraMetadata; use Contao\CoreBundle\Filesystem\VirtualFilesystemException; use Contao\CoreBundle\Filesystem\VirtualFilesystemInterface; -use Symfony\Component\HttpFoundation\UriSigner; +use Symfony\Component\HttpFoundation\UriSigner as HttpFoundationUriSigner; +use Symfony\Component\HttpKernel\UriSigner as HttpKernelUriSigner; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Component\Routing\RouterInterface; use Symfony\Component\Uid\Uuid; @@ -19,7 +20,7 @@ class BulkyItemStorage public function __construct( private readonly VirtualFilesystemInterface $filesystem, private readonly RouterInterface $router, - private readonly UriSigner $uriSigner, + private readonly HttpFoundationUriSigner|HttpKernelUriSigner $uriSigner, private readonly int $retentionPeriodInDays = 7, ) { } diff --git a/src/Controller/DownloadBulkyItemController.php b/src/Controller/DownloadBulkyItemController.php index d038ef0..8cf37ab 100644 --- a/src/Controller/DownloadBulkyItemController.php +++ b/src/Controller/DownloadBulkyItemController.php @@ -7,8 +7,9 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\StreamedResponse; -use Symfony\Component\HttpFoundation\UriSigner; +use Symfony\Component\HttpFoundation\UriSigner as HttpFoundationUriSigner; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +use Symfony\Component\HttpKernel\UriSigner as HttpKernelUriSigner; use Symfony\Component\Routing\Attribute\Route; use Terminal42\NotificationCenterBundle\BulkyItem\BulkyItemStorage; @@ -16,7 +17,7 @@ class DownloadBulkyItemController { public function __construct( - private readonly UriSigner $uriSigner, + private readonly HttpFoundationUriSigner|HttpKernelUriSigner $uriSigner, private readonly BulkyItemStorage $bulkyItemStorage, ) { } diff --git a/tests/BulkyItem/BulkItemStorageTest.php b/tests/BulkyItem/BulkItemStorageTest.php index 2e1db79..aaba9da 100644 --- a/tests/BulkyItem/BulkItemStorageTest.php +++ b/tests/BulkyItem/BulkItemStorageTest.php @@ -8,8 +8,10 @@ use Contao\CoreBundle\Filesystem\FilesystemItem; use Contao\CoreBundle\Filesystem\FilesystemItemIterator; use Contao\CoreBundle\Filesystem\VirtualFilesystemInterface; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; -use Symfony\Component\HttpFoundation\UriSigner; +use Symfony\Component\HttpFoundation\UriSigner as HttpFoundationUriSigner; +use Symfony\Component\HttpKernel\UriSigner as HttpKernelUriSigner; use Symfony\Component\Routing\RouterInterface; use Terminal42\NotificationCenterBundle\BulkyItem\BulkyItemStorage; use Terminal42\NotificationCenterBundle\BulkyItem\FileItem; @@ -74,7 +76,7 @@ function (ExtraMetadata $meta) { ) ; - $storage = new BulkyItemStorage($vfs, $this->createMock(RouterInterface::class), $this->createMock(UriSigner::class)); + $storage = new BulkyItemStorage($vfs, $this->createMock(RouterInterface::class), $this->mockUriSigner()); $voucher = $storage->store($this->createFileItem()); $this->assertTrue(BulkyItemStorage::validateVoucherFormat($voucher)); @@ -90,7 +92,7 @@ public function testHas(): void ->willReturn(true) ; - $storage = new BulkyItemStorage($vfs, $this->createMock(RouterInterface::class), $this->createMock(UriSigner::class)); + $storage = new BulkyItemStorage($vfs, $this->createMock(RouterInterface::class), $this->mockUriSigner()); $this->assertTrue($storage->has('a10aed4d-abe1-498f-adfc-b2e54fbbcbde')); } @@ -120,7 +122,7 @@ public function testRetrieve(): void ->willReturn($this->createStream()) ; - $storage = new BulkyItemStorage($vfs, $this->createMock(RouterInterface::class), $this->createMock(UriSigner::class)); + $storage = new BulkyItemStorage($vfs, $this->createMock(RouterInterface::class), $this->mockUriSigner()); $item = $storage->retrieve('a10aed4d-abe1-498f-adfc-b2e54fbbcbde'); $this->assertInstanceOf(FileItem::class, $item); @@ -156,7 +158,7 @@ public function testPrune(): void ->with('20220101') ; - $storage = new BulkyItemStorage($vfs, $this->createMock(RouterInterface::class), $this->createMock(UriSigner::class)); + $storage = new BulkyItemStorage($vfs, $this->createMock(RouterInterface::class), $this->mockUriSigner()); $storage->prune(); } @@ -176,4 +178,14 @@ private function createStream() return $stream; } + + /** + * For compatibility with Symfony 5, 6 and 7. + */ + private function mockUriSigner(): HttpFoundationUriSigner|HttpKernelUriSigner|MockObject + { + $class = class_exists(HttpFoundationUriSigner::class) ? HttpFoundationUriSigner::class : HttpKernelUriSigner::class; + + return $this->createMock($class); + } } diff --git a/tests/Gateway/MailerGatewayTest.php b/tests/Gateway/MailerGatewayTest.php index 991572e..7eb6e40 100644 --- a/tests/Gateway/MailerGatewayTest.php +++ b/tests/Gateway/MailerGatewayTest.php @@ -13,11 +13,14 @@ use Contao\FrontendTemplate; use Contao\TestCase\ContaoTestCase; use League\Flysystem\InMemory\InMemoryFilesystemAdapter; +use PHPUnit\Framework\MockObject\MockObject; use Symfony\Component\DependencyInjection\Container; use Symfony\Component\ExpressionLanguage\ExpressionLanguage; -use Symfony\Component\HttpFoundation\UriSigner; +use Symfony\Component\HttpFoundation\UriSigner as HttpFoundationUriSigner; +use Symfony\Component\HttpKernel\UriSigner as HttpKernelUriSigner; use Symfony\Component\Mailer\MailerInterface; use Symfony\Component\Mime\Email; +use Symfony\Component\Mime\Header\ParameterizedHeader; use Symfony\Component\Routing\RouterInterface; use Terminal42\NotificationCenterBundle\BulkyItem\BulkyItemStorage; use Terminal42\NotificationCenterBundle\Config\LanguageConfig; @@ -57,7 +60,20 @@ static function (Email $email) use ($parsedTemplateHtml, $expectedAttachmentsCon $attachments = []; foreach ($email->getAttachments() as $attachment) { - $attachments[$attachment->getBody()] = $attachment->getName(); + // getName() method does not exist in Symfony 5 (Contao 4.13) + // see https://github.com/symfony/symfony/commit/ebd8697c7ee8daa7011da3222ebbb6dfb5e30171 + if (method_exists($attachment, 'getName')) { + $attachments[$attachment->getBody()] = $attachment->getName(); + continue; + } + + $header = $attachment->getPreparedHeaders()->get('Content-Type'); + + if (!$header instanceof ParameterizedHeader || !($name = $header->getParameter('name'))) { + continue; + } + + $attachments[$attachment->getBody()] = $name; } $expectedHtml = $parsedTemplateHtml; @@ -90,7 +106,7 @@ static function (Email $email) use ($parsedTemplateHtml, $expectedAttachmentsCon $mailer, ); $container = new Container(); - $container->set(AbstractGateway::SERVICE_NAME_BULKY_ITEM_STORAGE, new BulkyItemStorage($vfsCollection->get('bulky_item'), $this->createMock(RouterInterface::class), $this->createMock(UriSigner::class))); + $container->set(AbstractGateway::SERVICE_NAME_BULKY_ITEM_STORAGE, new BulkyItemStorage($vfsCollection->get('bulky_item'), $this->createMock(RouterInterface::class), $this->mockUriSigner())); $container->set(AbstractGateway::SERVICE_NAME_SIMPLE_TOKEN_PARSER, new SimpleTokenParser(new ExpressionLanguage())); $gateway->setContainer($container); @@ -164,13 +180,36 @@ private function createFrameWorkWithTemplate(string $parsedTemplateHtml): Contao ->willReturn($parsedTemplateHtml) ; - return $this->mockContaoFramework( + $framework = $this->mockContaoFramework( [ Controller::class => $controllerAdapter, ], - [ - FrontendTemplate::class => $templateInstance, - ], ); + + // contao/test-case 4.13 does not support "$instances" on `mockContaoFramework` + $framework + ->method('createInstance') + ->willReturnCallback( + static function (string $key) use ($templateInstance): mixed { + if (FrontendTemplate::class === $key) { + return $templateInstance; + } + + return null; + }, + ) + ; + + return $framework; + } + + /** + * For compatibility with Symfony 5, 6 and 7. + */ + private function mockUriSigner(): HttpFoundationUriSigner|HttpKernelUriSigner|MockObject + { + $class = class_exists(HttpFoundationUriSigner::class) ? HttpFoundationUriSigner::class : HttpKernelUriSigner::class; + + return $this->createMock($class); } }