diff --git a/composer.json b/composer.json index 78acaa7..9089039 100644 --- a/composer.json +++ b/composer.json @@ -22,8 +22,8 @@ "@composer bin all update --ansi" ], "lint": "find . -name \\*.php -not -path './vendor/*' -not -path './vendor-bin/*' -not -path './build/*' -print0 | xargs -0 -n1 php -l", - "cs:check": "php-cs-fixer fix --dry-run --diff", - "cs:fix": "php-cs-fixer fix", + "cs:check": "vendor-bin/cs-fixer/vendor/php-cs-fixer/shim/php-cs-fixer fix --dry-run --diff", + "cs:fix": "vendor-bin/cs-fixer/vendor/php-cs-fixer/shim/php-cs-fixer fix", "psalm": "psalm --threads=1 --no-cache", "test:unit": "phpunit tests -c tests/phpunit.xml --colors=always --fail-on-warning --fail-on-risky" }, diff --git a/composer.lock b/composer.lock index 23f8795..4f86fc3 100644 --- a/composer.lock +++ b/composer.lock @@ -8,16 +8,16 @@ "packages": [ { "name": "bamarni/composer-bin-plugin", - "version": "1.8.2", + "version": "1.9.1", "source": { "type": "git", "url": "https://github.com/bamarni/composer-bin-plugin.git", - "reference": "92fd7b1e6e9cdae19b0d57369d8ad31a37b6a880" + "reference": "641d0663f5ac270b1aeec4337b7856f76204df47" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/bamarni/composer-bin-plugin/zipball/92fd7b1e6e9cdae19b0d57369d8ad31a37b6a880", - "reference": "92fd7b1e6e9cdae19b0d57369d8ad31a37b6a880", + "url": "https://api.github.com/repos/bamarni/composer-bin-plugin/zipball/641d0663f5ac270b1aeec4337b7856f76204df47", + "reference": "641d0663f5ac270b1aeec4337b7856f76204df47", "shasum": "" }, "require": { @@ -25,12 +25,12 @@ "php": "^7.2.5 || ^8.0" }, "require-dev": { - "composer/composer": "^2.0", + "composer/composer": "^2.2.26", "ext-json": "*", "phpstan/extension-installer": "^1.1", - "phpstan/phpstan": "^1.8", - "phpstan/phpstan-phpunit": "^1.1", - "phpunit/phpunit": "^8.5 || ^9.5", + "phpstan/phpstan": "^1.8 || ^2.0", + "phpstan/phpstan-phpunit": "^1.1 || ^2.0", + "phpunit/phpunit": "^8.5 || ^9.6 || ^10.0", "symfony/console": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0 || ^6.0", "symfony/finder": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0 || ^6.0", "symfony/process": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0 || ^6.0" @@ -59,9 +59,9 @@ ], "support": { "issues": "https://github.com/bamarni/composer-bin-plugin/issues", - "source": "https://github.com/bamarni/composer-bin-plugin/tree/1.8.2" + "source": "https://github.com/bamarni/composer-bin-plugin/tree/1.9.1" }, - "time": "2022-10-31T08:38:03+00:00" + "time": "2026-02-04T10:18:12+00:00" }, { "name": "ionos-productivity/ionos-mail-notification-api-client", @@ -82,16 +82,16 @@ "packages-dev": [ { "name": "guzzlehttp/guzzle", - "version": "7.10.0", + "version": "7.10.6", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "b51ac707cfa420b7bfd4e4d5e510ba8008e822b4" + "reference": "e7412b3180912c01650cc66647f18c1d1cbe9b94" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/b51ac707cfa420b7bfd4e4d5e510ba8008e822b4", - "reference": "b51ac707cfa420b7bfd4e4d5e510ba8008e822b4", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/e7412b3180912c01650cc66647f18c1d1cbe9b94", + "reference": "e7412b3180912c01650cc66647f18c1d1cbe9b94", "shasum": "" }, "require": { @@ -109,8 +109,9 @@ "bamarni/composer-bin-plugin": "^1.8.2", "ext-curl": "*", "guzzle/client-integration-tests": "3.0.2", + "guzzlehttp/test-server": "^0.4", "php-http/message-factory": "^1.1", - "phpunit/phpunit": "^8.5.39 || ^9.6.20", + "phpunit/phpunit": "^8.5.52 || ^9.6.34", "psr/log": "^1.1 || ^2.0 || ^3.0" }, "suggest": { @@ -188,7 +189,7 @@ ], "support": { "issues": "https://github.com/guzzle/guzzle/issues", - "source": "https://github.com/guzzle/guzzle/tree/7.10.0" + "source": "https://github.com/guzzle/guzzle/tree/7.10.6" }, "funding": [ { @@ -204,7 +205,7 @@ "type": "tidelift" } ], - "time": "2025-08-23T22:36:01+00:00" + "time": "2026-06-01T13:06:22+00:00" }, { "name": "guzzlehttp/promises", @@ -460,16 +461,16 @@ }, { "name": "nextcloud/coding-standard", - "version": "v1.4.0", + "version": "v1.5.0", "source": { "type": "git", "url": "https://github.com/nextcloud/coding-standard.git", - "reference": "8e06808c1423e9208d63d1bd205b9a38bd400011" + "reference": "80547a93236fbb9c783e05f0f0899043851b0dba" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nextcloud/coding-standard/zipball/8e06808c1423e9208d63d1bd205b9a38bd400011", - "reference": "8e06808c1423e9208d63d1bd205b9a38bd400011", + "url": "https://api.github.com/repos/nextcloud/coding-standard/zipball/80547a93236fbb9c783e05f0f0899043851b0dba", + "reference": "80547a93236fbb9c783e05f0f0899043851b0dba", "shasum": "" }, "require": { @@ -499,22 +500,22 @@ ], "support": { "issues": "https://github.com/nextcloud/coding-standard/issues", - "source": "https://github.com/nextcloud/coding-standard/tree/v1.4.0" + "source": "https://github.com/nextcloud/coding-standard/tree/v1.5.0" }, - "time": "2025-06-19T12:27:27+00:00" + "time": "2026-05-19T18:30:09+00:00" }, { "name": "nextcloud/ocp", - "version": "v33.0.2", + "version": "v33.0.5", "source": { "type": "git", "url": "https://github.com/nextcloud-deps/ocp.git", - "reference": "f5a898fb7795fa28a135f528105b8628df6d97f5" + "reference": "6f53a7ad7eda8c00f335b38e392cb76494d82422" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nextcloud-deps/ocp/zipball/f5a898fb7795fa28a135f528105b8628df6d97f5", - "reference": "f5a898fb7795fa28a135f528105b8628df6d97f5", + "url": "https://api.github.com/repos/nextcloud-deps/ocp/zipball/6f53a7ad7eda8c00f335b38e392cb76494d82422", + "reference": "6f53a7ad7eda8c00f335b38e392cb76494d82422", "shasum": "" }, "require": { @@ -547,9 +548,9 @@ "description": "Composer package containing Nextcloud's public OCP API and the unstable NCU API", "support": { "issues": "https://github.com/nextcloud-deps/ocp/issues", - "source": "https://github.com/nextcloud-deps/ocp/tree/v33.0.2" + "source": "https://github.com/nextcloud-deps/ocp/tree/v33.0.5" }, - "time": "2026-03-27T01:17:36+00:00" + "time": "2026-05-21T02:07:59+00:00" }, { "name": "php-cs-fixer/shim", diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index 274b113..b2cdc53 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -1,34 +1,19 @@ + * SPDX-FileCopyrightText: 2026 STRATO GmbH * SPDX-License-Identifier: AGPL-3.0-or-later - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * */ -declare(strict_types=1); - namespace OCA\IonosProcesses\AppInfo; -use OCA\IonosProcesses\Listener\ShareCreatedEventListener; +use OCA\IonosProcesses\Listener\BeforeShareMailSentEventListener; use OCP\AppFramework\App; use OCP\AppFramework\Bootstrap\IBootContext; use OCP\AppFramework\Bootstrap\IBootstrap; use OCP\AppFramework\Bootstrap\IRegistrationContext; -use OCP\Share\Events\ShareCreatedEvent; class Application extends App implements IBootstrap { public const APP_ID = 'nc_ionos_processes'; @@ -41,7 +26,10 @@ public function __construct() { public function register(IRegistrationContext $context): void { include_once __DIR__ . '/../../vendor/autoload.php'; - $context->registerEventListener(ShareCreatedEvent::class, ShareCreatedEventListener::class); + $context->registerEventListener( + \OCA\ShareByMail\Event\BeforeShareMailSentEvent::class, + BeforeShareMailSentEventListener::class, + ); } public function boot(IBootContext $context): void { diff --git a/lib/Listener/BeforeShareMailSentEventListener.php b/lib/Listener/BeforeShareMailSentEventListener.php new file mode 100644 index 0000000..b07468e --- /dev/null +++ b/lib/Listener/BeforeShareMailSentEventListener.php @@ -0,0 +1,75 @@ + + */ +class BeforeShareMailSentEventListener implements IEventListener { + public const EVENT_NAME_SHARE_BY_LINK = 'share-by-link'; + + /** @psalm-api */ + public function __construct( + private readonly LoggerInterface $logger, + private readonly IL10N $l10n, + private readonly IonosMailerService $mailer, + ) { + } + + public function handle(Event $event): void { + if (!($event instanceof BeforeShareMailSentEvent)) { + return; + } + + $share = $event->getShare(); + + if ($share->getShareType() !== IShare::TYPE_EMAIL) { + return; + } + + $resolvedEmails = $event->getResolvedEmails(); + if (empty($resolvedEmails)) { + $event->markMailHandled(); + $this->logger->warning("empty recipients for share with token '" . $share->getToken() . "', skipping"); + return; + } + + $data = [ + 'senderUserId' => $event->getSenderUserId(), + 'fileName' => $event->getFileName(), + 'resourceUrl' => $event->getResourceUrl(), + 'note' => $event->getNote(), + 'expirationDate' => $event->getExpiration()?->getTimestamp(), + 'language' => $this->l10n->getLanguageCode(), + 'receiverEmails' => $resolvedEmails, + ]; + + $event->markMailHandled(); + $this->mailer->send(self::EVENT_NAME_SHARE_BY_LINK, $data); + } +} diff --git a/lib/Listener/ShareCreatedEventListener.php b/lib/Listener/ShareCreatedEventListener.php deleted file mode 100644 index 9dcd5ba..0000000 --- a/lib/Listener/ShareCreatedEventListener.php +++ /dev/null @@ -1,89 +0,0 @@ - - */ -class ShareCreatedEventListener implements IEventListener { - public const EVENT_NAME_SHARE_BY_LINK = 'share-by-link'; - - /** @psalm-api */ - public function __construct( - private readonly LoggerInterface $logger, - private readonly IUserManager $userManager, - private readonly IL10N $l10n, - private readonly IURLGenerator $urlGenerator, - private readonly IonosMailerService $mailer, - ) { - } - - public function handle(Event $event): void { - if (!($event instanceof ShareCreatedEvent)) { - return; - } - - $share = $event->getShare(); - $shareType = $share->getShareType(); - - if ($shareType !== IShare::TYPE_EMAIL) { - $this->logger->debug('share type ' . $shareType . ' ignored'); - return; - } - - $user = $this->userManager->get($share->getSharedBy()); - - if ($user === null) { - $this->logger->error("can not find user for share with token '" . $share->getToken() . "'"); - return; - } - - try { - $filename = $share->getNode()->getName(); - } catch (NotFoundException $e) { - $this->logger->error("can not find node for share with token '" . $share->getToken() . "': " . $e->getMessage()); - return; - } - - $language = $this->l10n->getLanguageCode(); - $userId = $user->getUID(); - $note = $share->getNote(); - $expirationDate = $share->getExpirationDate(); - $recipient = $share->getSharedWith(); - $url = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.showShare', [ - 'token' => $share->getToken() - ]); - - $data = [ - 'senderUserId' => $userId, - 'fileName' => $filename, - 'resourceUrl' => $url, - 'note' => $note, - 'expirationDate' => $expirationDate?->getTimestamp(), - 'language' => $language, - 'receiverEmails' => [ - $recipient, - ] - ]; - - $this->mailer->send(ShareCreatedEventListener::EVENT_NAME_SHARE_BY_LINK, $data); - } -} diff --git a/lib/Service/IonosMailerService.php b/lib/Service/IonosMailerService.php index fd73e19..0116421 100644 --- a/lib/Service/IonosMailerService.php +++ b/lib/Service/IonosMailerService.php @@ -10,7 +10,7 @@ use Exception; use IONOS\MailNotificationAPI\Client\Model\ShareMessageByLink; use OCA\IonosProcesses\AppInfo\Application; -use OCA\IonosProcesses\Listener\ShareCreatedEventListener; +use OCA\IonosProcesses\Listener\BeforeShareMailSentEventListener; use OCP\Exceptions\AppConfigException; use OCP\IAppConfig; use Psr\Log\LoggerInterface; @@ -70,7 +70,7 @@ public function send(string $eventName, array $variables): void { $apiInstance = $this->apiClientService->newEventAPIApi($client, $apiBaseUrl); - if ($eventName === ShareCreatedEventListener::EVENT_NAME_SHARE_BY_LINK) { + if ($eventName === BeforeShareMailSentEventListener::EVENT_NAME_SHARE_BY_LINK) { $message = new ShareMessageByLink($variables); try { $this->logger->debug('Send message to mailer service', ['event' => $eventName, 'variables' => $variables]); diff --git a/psalm.xml b/psalm.xml index cead67c..37da3df 100644 --- a/psalm.xml +++ b/psalm.xml @@ -17,6 +17,7 @@ + diff --git a/tests/AppInfo/ApplicationTest.php b/tests/AppInfo/ApplicationTest.php index 4f887f0..2df304a 100644 --- a/tests/AppInfo/ApplicationTest.php +++ b/tests/AppInfo/ApplicationTest.php @@ -3,7 +3,7 @@ declare(strict_types=1); /** - * SPDX-FileLicenseText: 2024 STRATO AG + * SPDX-FileCopyrightText: 2026 STRATO GmbH * SPDX-License-Identifier: AGPL-3.0-or-later */ @@ -11,14 +11,15 @@ use OC\AppFramework\Bootstrap\Coordinator; use OCA\IonosProcesses\AppInfo\Application; -use OCA\IonosProcesses\Listener\ShareCreatedEventListener; +use OCA\IonosProcesses\Listener\BeforeShareMailSentEventListener; +use OCA\ShareByMail\Event\BeforeShareMailSentEvent; use OCP\AppFramework\Bootstrap\IRegistrationContext; -use OCP\Share\Events\ShareCreatedEvent; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; class ApplicationTest extends TestCase { private Application $app; - private IRegistrationContext $context; + private IRegistrationContext&MockObject $context; protected function setUp(): void { parent::setUp(); @@ -30,11 +31,12 @@ protected function setUp(): void { } public function testRegisterCallsRegisterEventListenerOnContext(): void { - $this->context->expects($this->exactly(1)) + $this->context + ->expects($this->exactly(1)) ->method('registerEventListener') ->with( - ShareCreatedEvent::class, - ShareCreatedEventListener::class, + BeforeShareMailSentEvent::class, + BeforeShareMailSentEventListener::class, ); $this->app->register($this->context); diff --git a/tests/Listener/BeforeShareMailSentEventListenerTest.php b/tests/Listener/BeforeShareMailSentEventListenerTest.php new file mode 100644 index 0000000..fe30189 --- /dev/null +++ b/tests/Listener/BeforeShareMailSentEventListenerTest.php @@ -0,0 +1,226 @@ +mockLogger = $this->getMockBuilder(LoggerInterface::class)->getMock(); + + $this->mockMailer = $this->getMockBuilder(IonosMailerService::class) + ->disableOriginalConstructor() + ->onlyMethods(['send']) + ->getMock(); + + $this->mockL10N = $this->getMockBuilder(IL10N::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->mockShare = $this->getMockBuilder(IShare::class)->getMock(); + $this->mockMessage = $this->getMockBuilder(IMessage::class)->getMock(); + + $this->listener = new BeforeShareMailSentEventListener( + $this->mockLogger, + $this->mockL10N, + $this->mockMailer, + ); + } + + private function makeEvent( + array $resolvedEmails = [self::MOCK_RECIPIENT], + array $templateData = [], + ): BeforeShareMailSentEvent { + if (empty($templateData)) { + $templateData = [ + 'senderUserId' => self::MOCK_USER_ID, + 'filename' => self::MOCK_FILENAME, + 'link' => self::MOCK_URL, + 'initiator' => 'Test User', + 'shareWith' => 'other@example.com', + 'note' => self::MOCK_NOTE, + 'expiration' => null, + ]; + } + return new BeforeShareMailSentEvent($this->mockShare, $resolvedEmails, $this->mockMessage, $templateData); + } + + public function testNonShareTypeEmailIsIgnored(): void { + $this->mockShare->method('getShareType')->willReturn(IShare::TYPE_USER); + $this->mockMailer->expects($this->never())->method('send'); + + $event = $this->makeEvent(); + $this->listener->handle($event); + + $this->assertFalse($event->isMailHandled()); + } + + public function testEmptyRecipientsMarkHandledAndLogsWarning(): void { + $this->mockShare->method('getShareType')->willReturn(IShare::TYPE_EMAIL); + $this->mockShare->method('getToken')->willReturn(self::MOCK_SHARE_TOKEN); + + $this->mockMailer->expects($this->never())->method('send'); + $this->mockLogger + ->expects($this->once()) + ->method('warning') + ->with($this->stringContains(self::MOCK_SHARE_TOKEN)); + + $event = $this->makeEvent([]); + $this->listener->handle($event); + + $this->assertTrue($event->isMailHandled()); + } + + public function testSuccessfulSendWithoutExpiration(): void { + $mockLanguageCode = 'lang_LOCALE'; + + $this->mockShare->method('getShareType')->willReturn(IShare::TYPE_EMAIL); + $this->mockShare->method('getToken')->willReturn(self::MOCK_SHARE_TOKEN); + $this->mockL10N->method('getLanguageCode')->willReturn($mockLanguageCode); + + $this->mockMailer + ->expects($this->once()) + ->method('send') + ->with( + BeforeShareMailSentEventListener::EVENT_NAME_SHARE_BY_LINK, + [ + 'senderUserId' => self::MOCK_USER_ID, + 'fileName' => self::MOCK_FILENAME, + 'resourceUrl' => self::MOCK_URL, + 'note' => self::MOCK_NOTE, + 'expirationDate' => null, + 'language' => $mockLanguageCode, + 'receiverEmails' => [self::MOCK_RECIPIENT], + ], + ); + + $event = $this->makeEvent(); + $this->listener->handle($event); + + $this->assertTrue($event->isMailHandled()); + } + + public function testSuccessfulSendWithExpiration(): void { + $mockLanguageCode = 'lang_LOCALE'; + $mockTimestamp = 123456789; + $expiration = $this->getMockBuilder(DateTime::class)->getMock(); + $expiration->method('getTimestamp')->willReturn($mockTimestamp); + + $this->mockShare->method('getShareType')->willReturn(IShare::TYPE_EMAIL); + $this->mockShare->method('getToken')->willReturn(self::MOCK_SHARE_TOKEN); + $this->mockL10N->method('getLanguageCode')->willReturn($mockLanguageCode); + + $templateData = [ + 'senderUserId' => self::MOCK_USER_ID, + 'filename' => self::MOCK_FILENAME, + 'link' => self::MOCK_URL, + 'initiator' => 'Test User', + 'shareWith' => 'other@example.com', + 'note' => self::MOCK_NOTE, + 'expiration' => $expiration, + ]; + + $this->mockMailer + ->expects($this->once()) + ->method('send') + ->with( + BeforeShareMailSentEventListener::EVENT_NAME_SHARE_BY_LINK, + [ + 'senderUserId' => self::MOCK_USER_ID, + 'fileName' => self::MOCK_FILENAME, + 'resourceUrl' => self::MOCK_URL, + 'note' => self::MOCK_NOTE, + 'expirationDate' => $mockTimestamp, + 'language' => $mockLanguageCode, + 'receiverEmails' => [self::MOCK_RECIPIENT], + ], + ); + + $event = $this->makeEvent([self::MOCK_RECIPIENT], $templateData); + $this->listener->handle($event); + + $this->assertTrue($event->isMailHandled()); + } + + public function testUnrelatedEventIsIgnored(): void { + $this->mockMailer->expects($this->never())->method('send'); + + $this->listener->handle(new Event()); + } + + public function testMultipleRecipientsAreAllPassed(): void { + $recipients = ['a@example.com', 'b@example.com', 'c@example.com']; + + $this->mockShare->method('getShareType')->willReturn(IShare::TYPE_EMAIL); + $this->mockShare->method('getToken')->willReturn(self::MOCK_SHARE_TOKEN); + $this->mockL10N->method('getLanguageCode')->willReturn('en'); + + $this->mockMailer + ->expects($this->once()) + ->method('send') + ->with( + BeforeShareMailSentEventListener::EVENT_NAME_SHARE_BY_LINK, + $this->callback(fn ($data) => $data['receiverEmails'] === $recipients), + ); + + $event = $this->makeEvent($recipients); + $this->listener->handle($event); + + $this->assertTrue($event->isMailHandled()); + } + + public function testMailHandledBeforeSendSoNoSmtpFallbackOnMailerException(): void { + $this->mockShare->method('getShareType')->willReturn(IShare::TYPE_EMAIL); + $this->mockShare->method('getToken')->willReturn(self::MOCK_SHARE_TOKEN); + $this->mockL10N->method('getLanguageCode')->willReturn('en'); + + $this->mockMailer + ->expects($this->once()) + ->method('send') + ->willThrowException(new \Exception('IONOS API unreachable')); + + $event = $this->makeEvent(); + + try { + $this->listener->handle($event); + $this->fail('Expected exception was not thrown'); + } catch (\Exception $e) { + $this->assertSame('IONOS API unreachable', $e->getMessage()); + } + + // markMailHandled() is called before send(), so isMailHandled() is true + // even when send() throws — native SMTP is suppressed regardless. + $this->assertTrue($event->isMailHandled()); + } +} diff --git a/tests/Listener/ShareCreatedEventListenerTest.php b/tests/Listener/ShareCreatedEventListenerTest.php deleted file mode 100644 index 2228440..0000000 --- a/tests/Listener/ShareCreatedEventListenerTest.php +++ /dev/null @@ -1,326 +0,0 @@ -mockLogger = $this->getMockBuilder(LoggerInterface::class) - ->getMock(); - - $this->mockUser = $this->getMockBuilder(IUser::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->mockUserManager = $this->getMockBuilder(IUserManager::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->mockMailer = $this->getMockBuilder(IonosMailerService::class) - ->disableOriginalConstructor() - ->onlyMethods([ - 'send', - ]) - ->getMock(); - - $this->mockL10N = $this->getMockBuilder(IL10N::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->mockUrlGenerator = $this->getMockBuilder(IURLGenerator::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->mockNode = $this->getMockBuilder(Node::class) - ->getMock(); - - $this->mockTimestamp = $this->getMockBuilder(\DateTime::class) - ->getMock(); - - $this->mockShare = $this->getMockBuilder(IShare::class) - ->getMock(); - - $this->mockEvent = $this->getMockBuilder(ShareCreatedEvent::class) - ->setConstructorArgs([$this->mockShare]) - ->getMock(); - - $this->mockEvent - ->method('getShare') - ->willReturn($this->mockShare); - - $this->listener = new ShareCreatedEventListener( - $this->mockLogger, - $this->mockUserManager, - $this->mockL10N, - $this->mockUrlGenerator, - $this->mockMailer, - ); - } - - public function testNonShareTypeEmailShallBeIgnored(): void { - $this->expectNotToPerformAssertions(); - - $this->mockShare - ->method('getShareType') - ->willReturnOnConsecutiveCalls( - IShare::TYPE_USER, - IShare::TYPE_GROUP, - IShare::TYPE_USERGROUP, - IShare::TYPE_LINK, - ); - - $this->mockLogger - ->method('debug') - ->will($this->onConsecutiveCalls( - 'share type ' . IShare::TYPE_USER . ' ignored', - 'share type ' . IShare::TYPE_GROUP . ' ignored', - 'share type ' . IShare::TYPE_USERGROUP . ' ignored', - 'share type ' . IShare::TYPE_LINK . ' ignored', - )); - - - // Four calls for the event types - $this->listener->handle($this->mockEvent); - $this->listener->handle($this->mockEvent); - $this->listener->handle($this->mockEvent); - $this->listener->handle($this->mockEvent); - } - - private function configureMocks( - UserIs $userFound, - string $mockLanguageCode, - ?int $mockExpiration, - ): void { - $mockUserId = '123e4567-e89b-12d3-a456-426614174000'; - $mockNodeName = 'mock-name'; - $mockUrl = 'mock-url'; - - $this->mockL10N - ->method('getLanguageCode') - ->willReturn($mockLanguageCode); - - if ($userFound == UserIs::FOUND) { - $this->mockUser - ->method('getUID') - ->willReturn(ShareCreatedEventListenerTest::MOCK_USER_ID); - - $this->mockUserManager - ->method('get') - ->willReturn($this->mockUser); - } else { - $this->mockUserManager - ->method('get') - ->willReturn(null); - } - - $this->mockUserManager - ->method('get') - ->willReturn($mockUserId); - - $this->mockUrlGenerator - ->method('linkToRouteAbsolute') - ->with( - 'files_sharing.sharecontroller.showShare', - [ - 'token' => ShareCreatedEventListenerTest::MOCK_SHARE_TOKEN, - ], - ) - ->willReturn($mockUrl); - - $this->mockNode - ->method('getName') - ->willReturn($mockNodeName); - - $this->mockShare - ->method('getShareType') - ->willReturn(IShare::TYPE_EMAIL); - - $this->mockShare - ->method('getNode') - ->willReturn($this->mockNode); - - $this->mockShare - ->method('getToken') - ->willReturn(ShareCreatedEventListenerTest::MOCK_SHARE_TOKEN); - - $this->mockShare - ->method('getSharedWith') - ->willReturn(ShareCreatedEventListenerTest::MOCK_RECIPIENT); - - $this->mockShare - ->method('getNote') - ->willReturn(ShareCreatedEventListenerTest::MOCK_NOTE); - - if ($mockExpiration !== null) { - $this->mockShare - ->method('getExpirationDate') - ->willReturn($this->mockTimestamp); - - $this->mockTimestamp - ->method('getTimestamp') - ->willReturn($mockExpiration); - } else { - $this->mockShare - ->method('getExpirationDate') - ->willReturn(null); - } - } - - public function testShareTypeEmailNoExpirationDate(): void { - $mockLanguageCode = 'lang_LOCALE'; - $mockNodeName = 'mock-name'; - $mockUrl = 'mock-url'; - $mockExpiration = null; - - $this->configureMocks( - UserIs::FOUND, - $mockLanguageCode, - $mockExpiration, - ); - - $this->mockMailer - ->expects($this->exactly(1)) - ->method('send') - ->with( - ShareCreatedEventListener::EVENT_NAME_SHARE_BY_LINK, - [ - 'senderUserId' => ShareCreatedEventListenerTest::MOCK_USER_ID, - 'fileName' => $mockNodeName, - 'resourceUrl' => $mockUrl, - 'note' => ShareCreatedEventListenerTest::MOCK_NOTE, - 'expirationDate' => $mockExpiration, - 'language' => $mockLanguageCode, - 'receiverEmails' => [ - ShareCreatedEventListenerTest::MOCK_RECIPIENT, - ] - ] - ); - - $this->listener->handle($this->mockEvent); - } - - public function testShareTypeEmailHasExpirationDate(): void { - $mockLanguageCode = 'lang_LOCALE'; - $mockNodeName = 'mock-name'; - $mockUrl = 'mock-url'; - $mockExpiration = 123456789; - - $this->configureMocks( - UserIs::FOUND, - $mockLanguageCode, - $mockExpiration, - ); - - $this->mockMailer - ->expects($this->exactly(1)) - ->method('send') - ->with( - ShareCreatedEventListener::EVENT_NAME_SHARE_BY_LINK, - [ - 'senderUserId' => ShareCreatedEventListenerTest::MOCK_USER_ID, - 'fileName' => $mockNodeName, - 'resourceUrl' => $mockUrl, - 'note' => ShareCreatedEventListenerTest::MOCK_NOTE, - 'expirationDate' => $mockExpiration, - 'language' => $mockLanguageCode, - 'receiverEmails' => [ - ShareCreatedEventListenerTest::MOCK_RECIPIENT, - ] - ] - ); - - $this->listener->handle($this->mockEvent); - } - - public function testShareTypeEmailUserNotFound(): void { - $mockLanguageCode = 'lang_LOCALE'; - $mockExpiration = 123456789; - - $this->configureMocks( - UserIs::NOT_FOUND, - $mockLanguageCode, - $mockExpiration, - ); - - $this->mockMailer - ->expects($this->exactly(0)) - ->method('send'); - - $this->mockLogger - ->method('error') - ->with("can not find user for share with token '" . ShareCreatedEventListenerTest::MOCK_SHARE_TOKEN . "'"); - - $this->listener->handle($this->mockEvent); - } - - public function testShareTypeEmailNodeNotFound(): void { - $mockLanguageCode = 'lang_LOCALE'; - $mockExpiration = 123456789; - - $this->mockShare - ->method('getNode') - ->willThrowException(new NotFoundException('node not found')); - - $this->configureMocks( - UserIs::FOUND, - $mockLanguageCode, - $mockExpiration, - ); - - $this->mockMailer - ->expects($this->exactly(0)) - ->method('send'); - - $this->mockLogger - ->method('error') - ->with("can not find node for share with token '" . ShareCreatedEventListenerTest::MOCK_SHARE_TOKEN . "': node not found"); - - $this->listener->handle($this->mockEvent); - } -} diff --git a/tests/Service/IonosMailerServiceTest.php b/tests/Service/IonosMailerServiceTest.php index 968a11c..cba3c18 100644 --- a/tests/Service/IonosMailerServiceTest.php +++ b/tests/Service/IonosMailerServiceTest.php @@ -12,7 +12,7 @@ use IONOS\MailNotificationAPI\Client\Api\EventAPIApi; use IONOS\MailNotificationAPI\Client\Model\ShareMessageByLink; use OCA\IonosProcesses\AppInfo\Application; -use OCA\IonosProcesses\Listener\ShareCreatedEventListener; +use OCA\IonosProcesses\Listener\BeforeShareMailSentEventListener; use OCA\IonosProcesses\Service\ApiClientService; use OCA\IonosProcesses\Service\IonosMailerService; use OCP\Exceptions\AppConfigException; @@ -80,7 +80,7 @@ public function testShareByMailSendMailsSuccessfully() { ->expects($matcher) ->method('debug'); - $this->service->send(ShareCreatedEventListener::EVENT_NAME_SHARE_BY_LINK, ['key' => 'value']); + $this->service->send(BeforeShareMailSentEventListener::EVENT_NAME_SHARE_BY_LINK, ['key' => 'value']); } public function testThrowExceptionWhenNoMailerServiceUrlConfigured() { @@ -93,7 +93,7 @@ public function testThrowExceptionWhenNoMailerServiceUrlConfigured() { $this->expectException(AppConfigException::class); $this->expectExceptionMessage('No mailer service configured'); - $this->service->send(ShareCreatedEventListener::EVENT_NAME_SHARE_BY_LINK, ['key' => 'value']); + $this->service->send(BeforeShareMailSentEventListener::EVENT_NAME_SHARE_BY_LINK, ['key' => 'value']); } public function testThrowExceptionWhenNoMailerServiceUserConfigured() { @@ -106,7 +106,7 @@ public function testThrowExceptionWhenNoMailerServiceUserConfigured() { $this->expectException(AppConfigException::class); $this->expectExceptionMessage('No mailer user configured'); - $this->service->send(ShareCreatedEventListener::EVENT_NAME_SHARE_BY_LINK, ['key' => 'value']); + $this->service->send(BeforeShareMailSentEventListener::EVENT_NAME_SHARE_BY_LINK, ['key' => 'value']); } public function testThrowExceptionWhenNoMailerServicePassConfigured() { @@ -119,7 +119,7 @@ public function testThrowExceptionWhenNoMailerServicePassConfigured() { $this->expectException(AppConfigException::class); $this->expectExceptionMessage('No mailer service pass configured'); - $this->service->send(ShareCreatedEventListener::EVENT_NAME_SHARE_BY_LINK, ['key' => 'value']); + $this->service->send(BeforeShareMailSentEventListener::EVENT_NAME_SHARE_BY_LINK, ['key' => 'value']); } public function testLogErrorWhenExceptionThrownByApi() { @@ -150,6 +150,6 @@ public function testLogErrorWhenExceptionThrownByApi() { ['exception' => $exception] ); - $this->service->send(ShareCreatedEventListener::EVENT_NAME_SHARE_BY_LINK, ['key' => 'value']); + $this->service->send(BeforeShareMailSentEventListener::EVENT_NAME_SHARE_BY_LINK, ['key' => 'value']); } } diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 11b0339..8c6ed90 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -5,5 +5,9 @@ require_once __DIR__ . '/../../../tests/bootstrap.php'; require_once __DIR__ . '/../vendor/autoload.php'; +if (!class_exists(\OCA\ShareByMail\Event\BeforeShareMailSentEvent::class)) { + require_once __DIR__ . '/stubs/BeforeShareMailSentEvent.php'; +} + \OC_App::loadApp(OCA\IonosProcesses\AppInfo\Application::APP_ID); OC_Hook::clear(); diff --git a/tests/stubs/BeforeShareMailSentEvent.php b/tests/stubs/BeforeShareMailSentEvent.php new file mode 100644 index 0000000..e21eda9 --- /dev/null +++ b/tests/stubs/BeforeShareMailSentEvent.php @@ -0,0 +1,76 @@ +share; + } + + /** @return string[] */ + public function getResolvedEmails(): array { + return $this->resolvedEmails; + } + + public function getMessage(): ?IMessage { + return $this->message; + } + + public function markMailHandled(): void { + $this->mailHandled = true; + } + + public function isMailHandled(): bool { + return $this->mailHandled; + } +} + +class BeforeShareMailSentEvent extends AbstractBeforeShareMailSentEvent { + public function getSenderUserId(): string { + return (string)($this->templateData['senderUserId'] ?? ''); + } + + public function getFileName(): string { + return (string)($this->templateData['filename'] ?? ''); + } + + public function getResourceUrl(): string { + return (string)($this->templateData['link'] ?? ''); + } + + public function getNote(): string { + return (string)($this->templateData['note'] ?? ''); + } + + public function getExpiration(): ?\DateTime { + $v = $this->templateData['expiration'] ?? null; + return $v instanceof \DateTime ? $v : null; + } +} diff --git a/vendor-bin/cs-fixer/composer.lock b/vendor-bin/cs-fixer/composer.lock index 0e4f971..dadc8b4 100644 --- a/vendor-bin/cs-fixer/composer.lock +++ b/vendor-bin/cs-fixer/composer.lock @@ -9,26 +9,26 @@ "packages-dev": [ { "name": "kubawerlos/php-cs-fixer-custom-fixers", - "version": "v3.30.0", + "version": "v3.37.2", "source": { "type": "git", "url": "https://github.com/kubawerlos/php-cs-fixer-custom-fixers.git", - "reference": "0cf921cf9aee50ac3091839325f5cf38ab189b97" + "reference": "678df979ce743466b42ddb6eea46b3f4c9a7bade" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/kubawerlos/php-cs-fixer-custom-fixers/zipball/0cf921cf9aee50ac3091839325f5cf38ab189b97", - "reference": "0cf921cf9aee50ac3091839325f5cf38ab189b97", + "url": "https://api.github.com/repos/kubawerlos/php-cs-fixer-custom-fixers/zipball/678df979ce743466b42ddb6eea46b3f4c9a7bade", + "reference": "678df979ce743466b42ddb6eea46b3f4c9a7bade", "shasum": "" }, "require": { "ext-filter": "*", "ext-tokenizer": "*", - "friendsofphp/php-cs-fixer": "^3.82", + "friendsofphp/php-cs-fixer": "^3.87", "php": "^7.4 || ^8.0" }, "require-dev": { - "phpunit/phpunit": "^9.6.22 || 10.5.45 || ^11.5.7" + "phpunit/phpunit": "^9.6.34 || ^10.5.63 || ^11.5.55" }, "type": "library", "autoload": { @@ -49,7 +49,7 @@ "description": "A set of custom fixers for PHP CS Fixer", "support": { "issues": "https://github.com/kubawerlos/php-cs-fixer-custom-fixers/issues", - "source": "https://github.com/kubawerlos/php-cs-fixer-custom-fixers/tree/v3.30.0" + "source": "https://github.com/kubawerlos/php-cs-fixer-custom-fixers/tree/v3.37.2" }, "funding": [ { @@ -57,20 +57,20 @@ "type": "github" } ], - "time": "2025-07-07T22:51:31+00:00" + "time": "2026-05-12T16:22:19+00:00" }, { "name": "nextcloud/coding-standard", - "version": "v1.4.0", + "version": "v1.5.0", "source": { "type": "git", "url": "https://github.com/nextcloud/coding-standard.git", - "reference": "8e06808c1423e9208d63d1bd205b9a38bd400011" + "reference": "80547a93236fbb9c783e05f0f0899043851b0dba" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nextcloud/coding-standard/zipball/8e06808c1423e9208d63d1bd205b9a38bd400011", - "reference": "8e06808c1423e9208d63d1bd205b9a38bd400011", + "url": "https://api.github.com/repos/nextcloud/coding-standard/zipball/80547a93236fbb9c783e05f0f0899043851b0dba", + "reference": "80547a93236fbb9c783e05f0f0899043851b0dba", "shasum": "" }, "require": { @@ -100,22 +100,22 @@ ], "support": { "issues": "https://github.com/nextcloud/coding-standard/issues", - "source": "https://github.com/nextcloud/coding-standard/tree/v1.4.0" + "source": "https://github.com/nextcloud/coding-standard/tree/v1.5.0" }, - "time": "2025-06-19T12:27:27+00:00" + "time": "2026-05-19T18:30:09+00:00" }, { "name": "php-cs-fixer/shim", - "version": "v3.82.2", + "version": "v3.95.5", "source": { "type": "git", "url": "https://github.com/PHP-CS-Fixer/shim.git", - "reference": "2dbabef3dae30752382cf4b9864780e78f71bc59" + "reference": "f6ce9082af26d7497e8a1c75c5fc77387179b856" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHP-CS-Fixer/shim/zipball/2dbabef3dae30752382cf4b9864780e78f71bc59", - "reference": "2dbabef3dae30752382cf4b9864780e78f71bc59", + "url": "https://api.github.com/repos/PHP-CS-Fixer/shim/zipball/f6ce9082af26d7497e8a1c75c5fc77387179b856", + "reference": "f6ce9082af26d7497e8a1c75c5fc77387179b856", "shasum": "" }, "require": { @@ -152,9 +152,9 @@ "description": "A tool to automatically fix PHP code style", "support": { "issues": "https://github.com/PHP-CS-Fixer/shim/issues", - "source": "https://github.com/PHP-CS-Fixer/shim/tree/v3.82.2" + "source": "https://github.com/PHP-CS-Fixer/shim/tree/v3.95.5" }, - "time": "2025-07-08T21:13:44+00:00" + "time": "2026-06-09T14:55:56+00:00" } ], "aliases": [], diff --git a/vendor-bin/phpunit/composer.lock b/vendor-bin/phpunit/composer.lock index 767a9b9..ade9fcd 100644 --- a/vendor-bin/phpunit/composer.lock +++ b/vendor-bin/phpunit/composer.lock @@ -9,16 +9,16 @@ "packages-dev": [ { "name": "myclabs/deep-copy", - "version": "1.13.3", + "version": "1.13.4", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "faed855a7b5f4d4637717c2b3863e277116beb36" + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/faed855a7b5f4d4637717c2b3863e277116beb36", - "reference": "faed855a7b5f4d4637717c2b3863e277116beb36", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a", + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a", "shasum": "" }, "require": { @@ -57,7 +57,7 @@ ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.13.3" + "source": "https://github.com/myclabs/DeepCopy/tree/1.13.4" }, "funding": [ { @@ -65,20 +65,20 @@ "type": "tidelift" } ], - "time": "2025-07-05T12:25:42+00:00" + "time": "2025-08-01T08:46:24+00:00" }, { "name": "nikic/php-parser", - "version": "v5.5.0", + "version": "v5.7.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "ae59794362fe85e051a58ad36b289443f57be7a9" + "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/ae59794362fe85e051a58ad36b289443f57be7a9", - "reference": "ae59794362fe85e051a58ad36b289443f57be7a9", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/dca41cd15c2ac9d055ad70dbfd011130757d1f82", + "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82", "shasum": "" }, "require": { @@ -97,7 +97,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "5.0-dev" + "dev-master": "5.x-dev" } }, "autoload": { @@ -121,9 +121,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.5.0" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.7.0" }, - "time": "2025-05-31T08:24:38+00:00" + "time": "2025-12-06T11:56:16+00:00" }, { "name": "phar-io/manifest", @@ -566,16 +566,16 @@ }, { "name": "phpunit/phpunit", - "version": "10.5.47", + "version": "10.5.63", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "3637b3e50d32ab3a0d1a33b3b6177169ec3d95a3" + "reference": "33198268dad71e926626b618f3ec3966661e4d90" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/3637b3e50d32ab3a0d1a33b3b6177169ec3d95a3", - "reference": "3637b3e50d32ab3a0d1a33b3b6177169ec3d95a3", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/33198268dad71e926626b618f3ec3966661e4d90", + "reference": "33198268dad71e926626b618f3ec3966661e4d90", "shasum": "" }, "require": { @@ -585,7 +585,7 @@ "ext-mbstring": "*", "ext-xml": "*", "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.13.1", + "myclabs/deep-copy": "^1.13.4", "phar-io/manifest": "^2.0.4", "phar-io/version": "^3.2.1", "php": ">=8.1", @@ -596,13 +596,13 @@ "phpunit/php-timer": "^6.0.0", "sebastian/cli-parser": "^2.0.1", "sebastian/code-unit": "^2.0.0", - "sebastian/comparator": "^5.0.3", + "sebastian/comparator": "^5.0.5", "sebastian/diff": "^5.1.1", "sebastian/environment": "^6.1.0", - "sebastian/exporter": "^5.1.2", + "sebastian/exporter": "^5.1.4", "sebastian/global-state": "^6.0.2", "sebastian/object-enumerator": "^5.0.0", - "sebastian/recursion-context": "^5.0.0", + "sebastian/recursion-context": "^5.0.1", "sebastian/type": "^4.0.0", "sebastian/version": "^4.0.1" }, @@ -647,7 +647,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.47" + "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.63" }, "funding": [ { @@ -671,7 +671,7 @@ "type": "tidelift" } ], - "time": "2025-06-20T11:29:11+00:00" + "time": "2026-01-27T05:48:37+00:00" }, { "name": "sebastian/cli-parser", @@ -843,16 +843,16 @@ }, { "name": "sebastian/comparator", - "version": "5.0.3", + "version": "5.0.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "a18251eb0b7a2dcd2f7aa3d6078b18545ef0558e" + "reference": "55dfef806eb7dfeb6e7a6935601fef866f8ca48d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/a18251eb0b7a2dcd2f7aa3d6078b18545ef0558e", - "reference": "a18251eb0b7a2dcd2f7aa3d6078b18545ef0558e", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/55dfef806eb7dfeb6e7a6935601fef866f8ca48d", + "reference": "55dfef806eb7dfeb6e7a6935601fef866f8ca48d", "shasum": "" }, "require": { @@ -908,15 +908,27 @@ "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", "security": "https://github.com/sebastianbergmann/comparator/security/policy", - "source": "https://github.com/sebastianbergmann/comparator/tree/5.0.3" + "source": "https://github.com/sebastianbergmann/comparator/tree/5.0.5" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/comparator", + "type": "tidelift" } ], - "time": "2024-10-18T14:56:07+00:00" + "time": "2026-01-24T09:25:16+00:00" }, { "name": "sebastian/complexity", @@ -1109,16 +1121,16 @@ }, { "name": "sebastian/exporter", - "version": "5.1.2", + "version": "5.1.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "955288482d97c19a372d3f31006ab3f37da47adf" + "reference": "0735b90f4da94969541dac1da743446e276defa6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/955288482d97c19a372d3f31006ab3f37da47adf", - "reference": "955288482d97c19a372d3f31006ab3f37da47adf", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/0735b90f4da94969541dac1da743446e276defa6", + "reference": "0735b90f4da94969541dac1da743446e276defa6", "shasum": "" }, "require": { @@ -1127,7 +1139,7 @@ "sebastian/recursion-context": "^5.0" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^10.5" }, "type": "library", "extra": { @@ -1175,15 +1187,27 @@ "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", "security": "https://github.com/sebastianbergmann/exporter/security/policy", - "source": "https://github.com/sebastianbergmann/exporter/tree/5.1.2" + "source": "https://github.com/sebastianbergmann/exporter/tree/5.1.4" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/exporter", + "type": "tidelift" } ], - "time": "2024-03-02T07:17:12+00:00" + "time": "2025-09-24T06:09:11+00:00" }, { "name": "sebastian/global-state", @@ -1419,23 +1443,23 @@ }, { "name": "sebastian/recursion-context", - "version": "5.0.0", + "version": "5.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "05909fb5bc7df4c52992396d0116aed689f93712" + "reference": "47e34210757a2f37a97dcd207d032e1b01e64c7a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/05909fb5bc7df4c52992396d0116aed689f93712", - "reference": "05909fb5bc7df4c52992396d0116aed689f93712", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/47e34210757a2f37a97dcd207d032e1b01e64c7a", + "reference": "47e34210757a2f37a97dcd207d032e1b01e64c7a", "shasum": "" }, "require": { "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^10.5" }, "type": "library", "extra": { @@ -1470,15 +1494,28 @@ "homepage": "https://github.com/sebastianbergmann/recursion-context", "support": { "issues": "https://github.com/sebastianbergmann/recursion-context/issues", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/5.0.0" + "security": "https://github.com/sebastianbergmann/recursion-context/security/policy", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/5.0.1" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/recursion-context", + "type": "tidelift" } ], - "time": "2023-02-03T07:05:40+00:00" + "time": "2025-08-10T07:50:56+00:00" }, { "name": "sebastian/type", @@ -1591,16 +1628,16 @@ }, { "name": "theseer/tokenizer", - "version": "1.2.3", + "version": "1.3.1", "source": { "type": "git", "url": "https://github.com/theseer/tokenizer.git", - "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2" + "reference": "b7489ce515e168639d17feec34b8847c326b0b3c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", - "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/b7489ce515e168639d17feec34b8847c326b0b3c", + "reference": "b7489ce515e168639d17feec34b8847c326b0b3c", "shasum": "" }, "require": { @@ -1629,7 +1666,7 @@ "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", "support": { "issues": "https://github.com/theseer/tokenizer/issues", - "source": "https://github.com/theseer/tokenizer/tree/1.2.3" + "source": "https://github.com/theseer/tokenizer/tree/1.3.1" }, "funding": [ { @@ -1637,7 +1674,7 @@ "type": "github" } ], - "time": "2024-03-03T12:36:25+00:00" + "time": "2025-11-17T20:03:58+00:00" } ], "aliases": [], diff --git a/vendor-bin/psalm/composer.lock b/vendor-bin/psalm/composer.lock index 55f8757..28fdef6 100644 --- a/vendor-bin/psalm/composer.lock +++ b/vendor-bin/psalm/composer.lock @@ -9,16 +9,16 @@ "packages-dev": [ { "name": "amphp/amp", - "version": "v2.6.4", + "version": "v2.6.5", "source": { "type": "git", "url": "https://github.com/amphp/amp.git", - "reference": "ded3d9be08f526089eb7ee8d9f16a9768f9dec2d" + "reference": "d7dda98dae26e56f3f6fcfbf1c1f819c9a993207" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/amphp/amp/zipball/ded3d9be08f526089eb7ee8d9f16a9768f9dec2d", - "reference": "ded3d9be08f526089eb7ee8d9f16a9768f9dec2d", + "url": "https://api.github.com/repos/amphp/amp/zipball/d7dda98dae26e56f3f6fcfbf1c1f819c9a993207", + "reference": "d7dda98dae26e56f3f6fcfbf1c1f819c9a993207", "shasum": "" }, "require": { @@ -34,11 +34,6 @@ "vimeo/psalm": "^3.12" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.x-dev" - } - }, "autoload": { "files": [ "lib/functions.php", @@ -86,7 +81,7 @@ "support": { "irc": "irc://irc.freenode.org/amphp", "issues": "https://github.com/amphp/amp/issues", - "source": "https://github.com/amphp/amp/tree/v2.6.4" + "source": "https://github.com/amphp/amp/tree/v2.6.5" }, "funding": [ { @@ -94,7 +89,7 @@ "type": "github" } ], - "time": "2024-03-21T18:52:26+00:00" + "time": "2025-09-03T19:41:28+00:00" }, { "name": "amphp/byte-stream", @@ -169,28 +164,29 @@ }, { "name": "composer/pcre", - "version": "3.3.2", + "version": "3.4.0", "source": { "type": "git", "url": "https://github.com/composer/pcre.git", - "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e" + "reference": "d5a341b3fb61f3001970940afb1d332968a183ed" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/pcre/zipball/b2bed4734f0cc156ee1fe9c0da2550420d99a21e", - "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e", + "url": "https://api.github.com/repos/composer/pcre/zipball/d5a341b3fb61f3001970940afb1d332968a183ed", + "reference": "d5a341b3fb61f3001970940afb1d332968a183ed", "shasum": "" }, "require": { "php": "^7.4 || ^8.0" }, "conflict": { - "phpstan/phpstan": "<1.11.10" + "phpstan/phpstan": "<2.2.2" }, "require-dev": { - "phpstan/phpstan": "^1.12 || ^2", - "phpstan/phpstan-strict-rules": "^1 || ^2", - "phpunit/phpunit": "^8 || ^9" + "phpstan/phpstan": "^2", + "phpstan/phpstan-deprecation-rules": "^2", + "phpstan/phpstan-strict-rules": "^2", + "phpunit/phpunit": "^9" }, "type": "library", "extra": { @@ -228,7 +224,7 @@ ], "support": { "issues": "https://github.com/composer/pcre/issues", - "source": "https://github.com/composer/pcre/tree/3.3.2" + "source": "https://github.com/composer/pcre/tree/3.4.0" }, "funding": [ { @@ -238,26 +234,22 @@ { "url": "https://github.com/composer", "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", - "type": "tidelift" } ], - "time": "2024-11-12T16:29:46+00:00" + "time": "2026-06-07T11:47:49+00:00" }, { "name": "composer/semver", - "version": "3.4.3", + "version": "3.4.4", "source": { "type": "git", "url": "https://github.com/composer/semver.git", - "reference": "4313d26ada5e0c4edfbd1dc481a92ff7bff91f12" + "reference": "198166618906cb2de69b95d7d47e5fa8aa1b2b95" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/4313d26ada5e0c4edfbd1dc481a92ff7bff91f12", - "reference": "4313d26ada5e0c4edfbd1dc481a92ff7bff91f12", + "url": "https://api.github.com/repos/composer/semver/zipball/198166618906cb2de69b95d7d47e5fa8aa1b2b95", + "reference": "198166618906cb2de69b95d7d47e5fa8aa1b2b95", "shasum": "" }, "require": { @@ -309,7 +301,7 @@ "support": { "irc": "ircs://irc.libera.chat:6697/composer", "issues": "https://github.com/composer/semver/issues", - "source": "https://github.com/composer/semver/tree/3.4.3" + "source": "https://github.com/composer/semver/tree/3.4.4" }, "funding": [ { @@ -319,13 +311,9 @@ { "url": "https://github.com/composer", "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", - "type": "tidelift" } ], - "time": "2024-09-19T14:15:21+00:00" + "time": "2025-08-20T19:15:30+00:00" }, { "name": "composer/xdebug-handler", @@ -432,29 +420,29 @@ }, { "name": "doctrine/deprecations", - "version": "1.1.5", + "version": "1.1.6", "source": { "type": "git", "url": "https://github.com/doctrine/deprecations.git", - "reference": "459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38" + "reference": "d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/deprecations/zipball/459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38", - "reference": "459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca", + "reference": "d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca", "shasum": "" }, "require": { "php": "^7.1 || ^8.0" }, "conflict": { - "phpunit/phpunit": "<=7.5 || >=13" + "phpunit/phpunit": "<=7.5 || >=14" }, "require-dev": { - "doctrine/coding-standard": "^9 || ^12 || ^13", - "phpstan/phpstan": "1.4.10 || 2.1.11", + "doctrine/coding-standard": "^9 || ^12 || ^14", + "phpstan/phpstan": "1.4.10 || 2.1.30", "phpstan/phpstan-phpunit": "^1.0 || ^2", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6 || ^10.5 || ^11.5 || ^12", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6 || ^10.5 || ^11.5 || ^12.4 || ^13.0", "psr/log": "^1 || ^2 || ^3" }, "suggest": { @@ -474,9 +462,9 @@ "homepage": "https://www.doctrine-project.org/", "support": { "issues": "https://github.com/doctrine/deprecations/issues", - "source": "https://github.com/doctrine/deprecations/tree/1.1.5" + "source": "https://github.com/doctrine/deprecations/tree/1.1.6" }, - "time": "2025-04-07T20:06:18+00:00" + "time": "2026-02-07T07:09:04+00:00" }, { "name": "felixfbecker/advanced-json-rpc", @@ -581,16 +569,16 @@ }, { "name": "fidry/cpu-core-counter", - "version": "1.2.0", + "version": "1.3.0", "source": { "type": "git", "url": "https://github.com/theofidry/cpu-core-counter.git", - "reference": "8520451a140d3f46ac33042715115e290cf5785f" + "reference": "db9508f7b1474469d9d3c53b86f817e344732678" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/8520451a140d3f46ac33042715115e290cf5785f", - "reference": "8520451a140d3f46ac33042715115e290cf5785f", + "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/db9508f7b1474469d9d3c53b86f817e344732678", + "reference": "db9508f7b1474469d9d3c53b86f817e344732678", "shasum": "" }, "require": { @@ -600,10 +588,10 @@ "fidry/makefile": "^0.2.0", "fidry/php-cs-fixer-config": "^1.1.2", "phpstan/extension-installer": "^1.2.0", - "phpstan/phpstan": "^1.9.2", - "phpstan/phpstan-deprecation-rules": "^1.0.0", - "phpstan/phpstan-phpunit": "^1.2.2", - "phpstan/phpstan-strict-rules": "^1.4.4", + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-deprecation-rules": "^2.0.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", "phpunit/phpunit": "^8.5.31 || ^9.5.26", "webmozarts/strict-phpunit": "^7.5" }, @@ -630,7 +618,7 @@ ], "support": { "issues": "https://github.com/theofidry/cpu-core-counter/issues", - "source": "https://github.com/theofidry/cpu-core-counter/tree/1.2.0" + "source": "https://github.com/theofidry/cpu-core-counter/tree/1.3.0" }, "funding": [ { @@ -638,7 +626,7 @@ "type": "github" } ], - "time": "2024-08-06T10:04:20+00:00" + "time": "2025-08-14T07:29:31+00:00" }, { "name": "netresearch/jsonmapper", @@ -693,16 +681,16 @@ }, { "name": "nikic/php-parser", - "version": "v4.19.4", + "version": "v4.19.5", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "715f4d25e225bc47b293a8b997fe6ce99bf987d2" + "reference": "51bd93cc741b7fc3d63d20b6bdcd99fdaa359837" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/715f4d25e225bc47b293a8b997fe6ce99bf987d2", - "reference": "715f4d25e225bc47b293a8b997fe6ce99bf987d2", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/51bd93cc741b7fc3d63d20b6bdcd99fdaa359837", + "reference": "51bd93cc741b7fc3d63d20b6bdcd99fdaa359837", "shasum": "" }, "require": { @@ -717,11 +705,6 @@ "bin/php-parse" ], "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.9-dev" - } - }, "autoload": { "psr-4": { "PhpParser\\": "lib/PhpParser" @@ -743,9 +726,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.19.4" + "source": "https://github.com/nikic/PHP-Parser/tree/v4.19.5" }, - "time": "2024-09-29T15:01:53+00:00" + "time": "2025-12-06T11:45:25+00:00" }, { "name": "phpdocumentor/reflection-common", @@ -802,16 +785,16 @@ }, { "name": "phpdocumentor/reflection-docblock", - "version": "5.6.2", + "version": "5.6.7", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "92dde6a5919e34835c506ac8c523ef095a95ed62" + "reference": "31a105931bc8ffa3a123383829772e832fd8d903" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/92dde6a5919e34835c506ac8c523ef095a95ed62", - "reference": "92dde6a5919e34835c506ac8c523ef095a95ed62", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/31a105931bc8ffa3a123383829772e832fd8d903", + "reference": "31a105931bc8ffa3a123383829772e832fd8d903", "shasum": "" }, "require": { @@ -821,7 +804,7 @@ "phpdocumentor/reflection-common": "^2.2", "phpdocumentor/type-resolver": "^1.7", "phpstan/phpdoc-parser": "^1.7|^2.0", - "webmozart/assert": "^1.9.1" + "webmozart/assert": "^1.9.1 || ^2" }, "require-dev": { "mockery/mockery": "~1.3.5 || ~1.6.0", @@ -860,22 +843,22 @@ "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", "support": { "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", - "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.6.2" + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.6.7" }, - "time": "2025-04-13T19:20:35+00:00" + "time": "2026-03-18T20:47:46+00:00" }, { "name": "phpdocumentor/type-resolver", - "version": "1.10.0", + "version": "1.12.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "679e3ce485b99e84c775d28e2e96fade9a7fb50a" + "reference": "92a98ada2b93d9b201a613cb5a33584dde25f195" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/679e3ce485b99e84c775d28e2e96fade9a7fb50a", - "reference": "679e3ce485b99e84c775d28e2e96fade9a7fb50a", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/92a98ada2b93d9b201a613cb5a33584dde25f195", + "reference": "92a98ada2b93d9b201a613cb5a33584dde25f195", "shasum": "" }, "require": { @@ -918,22 +901,22 @@ "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", "support": { "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.10.0" + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.12.0" }, - "time": "2024-11-09T15:12:26+00:00" + "time": "2025-11-21T15:09:14+00:00" }, { "name": "phpstan/phpdoc-parser", - "version": "2.1.0", + "version": "2.3.2", "source": { "type": "git", "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "9b30d6fd026b2c132b3985ce6b23bec09ab3aa68" + "reference": "a004701b11273a26cd7955a61d67a7f1e525a45a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/9b30d6fd026b2c132b3985ce6b23bec09ab3aa68", - "reference": "9b30d6fd026b2c132b3985ce6b23bec09ab3aa68", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/a004701b11273a26cd7955a61d67a7f1e525a45a", + "reference": "a004701b11273a26cd7955a61d67a7f1e525a45a", "shasum": "" }, "require": { @@ -965,9 +948,9 @@ "description": "PHPDoc parser with support for nullable, intersection and generic types", "support": { "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/2.1.0" + "source": "https://github.com/phpstan/phpdoc-parser/tree/2.3.2" }, - "time": "2025-02-19T13:28:12+00:00" + "time": "2026-01-25T14:56:51+00:00" }, { "name": "psr/container", @@ -1141,16 +1124,16 @@ }, { "name": "spatie/array-to-xml", - "version": "3.4.0", + "version": "3.4.4", "source": { "type": "git", "url": "https://github.com/spatie/array-to-xml.git", - "reference": "7dcfc67d60b0272926dabad1ec01f6b8a5fb5e67" + "reference": "88b2f3852a922dd73177a68938f8eb2ec70c7224" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/array-to-xml/zipball/7dcfc67d60b0272926dabad1ec01f6b8a5fb5e67", - "reference": "7dcfc67d60b0272926dabad1ec01f6b8a5fb5e67", + "url": "https://api.github.com/repos/spatie/array-to-xml/zipball/88b2f3852a922dd73177a68938f8eb2ec70c7224", + "reference": "88b2f3852a922dd73177a68938f8eb2ec70c7224", "shasum": "" }, "require": { @@ -1193,7 +1176,7 @@ "xml" ], "support": { - "source": "https://github.com/spatie/array-to-xml/tree/3.4.0" + "source": "https://github.com/spatie/array-to-xml/tree/3.4.4" }, "funding": [ { @@ -1205,20 +1188,20 @@ "type": "github" } ], - "time": "2024-12-16T12:45:15+00:00" + "time": "2025-12-15T09:00:41+00:00" }, { "name": "symfony/console", - "version": "v6.4.23", + "version": "v6.4.41", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "9056771b8eca08d026cd3280deeec3cfd99c4d93" + "reference": "d21b17ed158e79180fac3895ff751707970eeb57" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/9056771b8eca08d026cd3280deeec3cfd99c4d93", - "reference": "9056771b8eca08d026cd3280deeec3cfd99c4d93", + "url": "https://api.github.com/repos/symfony/console/zipball/d21b17ed158e79180fac3895ff751707970eeb57", + "reference": "d21b17ed158e79180fac3895ff751707970eeb57", "shasum": "" }, "require": { @@ -1283,7 +1266,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v6.4.23" + "source": "https://github.com/symfony/console/tree/v6.4.41" }, "funding": [ { @@ -1294,25 +1277,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-06-27T19:37:22+00:00" + "time": "2026-05-24T08:48:41+00:00" }, { "name": "symfony/deprecation-contracts", - "version": "v3.6.0", + "version": "v3.7.0", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62" + "reference": "50f59d1f3ca46d41ac911f97a78626b6756af35b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/63afe740e99a13ba87ec199bb07bbdee937a5b62", - "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/50f59d1f3ca46d41ac911f97a78626b6756af35b", + "reference": "50f59d1f3ca46d41ac911f97a78626b6756af35b", "shasum": "" }, "require": { @@ -1325,7 +1312,7 @@ "name": "symfony/contracts" }, "branch-alias": { - "dev-main": "3.6-dev" + "dev-main": "3.7-dev" } }, "autoload": { @@ -1350,7 +1337,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.6.0" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.7.0" }, "funding": [ { @@ -1361,25 +1348,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-25T14:21:43+00:00" + "time": "2026-04-13T15:52:40+00:00" }, { "name": "symfony/filesystem", - "version": "v6.4.13", + "version": "v6.4.39", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "4856c9cf585d5a0313d8d35afd681a526f038dd3" + "reference": "c507b077756b4e3e09adbbe7975fac81cd3722ca" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/4856c9cf585d5a0313d8d35afd681a526f038dd3", - "reference": "4856c9cf585d5a0313d8d35afd681a526f038dd3", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/c507b077756b4e3e09adbbe7975fac81cd3722ca", + "reference": "c507b077756b4e3e09adbbe7975fac81cd3722ca", "shasum": "" }, "require": { @@ -1416,7 +1407,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v6.4.13" + "source": "https://github.com/symfony/filesystem/tree/v6.4.39" }, "funding": [ { @@ -1427,25 +1418,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-10-25T15:07:50+00:00" + "time": "2026-05-07T13:11:42+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.32.0", + "version": "v1.37.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" + "reference": "141046a8f9477948ff284fa65be2095baafb94f2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", - "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/141046a8f9477948ff284fa65be2095baafb94f2", + "reference": "141046a8f9477948ff284fa65be2095baafb94f2", "shasum": "" }, "require": { @@ -1495,7 +1490,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.37.0" }, "funding": [ { @@ -1506,25 +1501,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2026-04-10T16:19:22+00:00" }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.32.0", + "version": "v1.38.1", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe" + "reference": "e9247d281d694a5120554d9afaf54e070e88a603" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", - "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/e9247d281d694a5120554d9afaf54e070e88a603", + "reference": "e9247d281d694a5120554d9afaf54e070e88a603", "shasum": "" }, "require": { @@ -1573,7 +1572,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.38.1" }, "funding": [ { @@ -1584,25 +1583,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2026-05-26T05:58:03+00:00" }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.32.0", + "version": "v1.38.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "3833d7255cc303546435cb650316bff708a1c75c" + "reference": "2d446c214bdbe5b71bde5011b060a05fece3ae6b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c", - "reference": "3833d7255cc303546435cb650316bff708a1c75c", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/2d446c214bdbe5b71bde5011b060a05fece3ae6b", + "reference": "2d446c214bdbe5b71bde5011b060a05fece3ae6b", "shasum": "" }, "require": { @@ -1654,7 +1657,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.38.0" }, "funding": [ { @@ -1665,25 +1668,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2026-05-25T13:48:31+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.32.0", + "version": "v1.38.2", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493" + "reference": "d3d318bad5e7a1bfbd026009c8bfb8d8f99ae6b6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493", - "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/d3d318bad5e7a1bfbd026009c8bfb8d8f99ae6b6", + "reference": "d3d318bad5e7a1bfbd026009c8bfb8d8f99ae6b6", "shasum": "" }, "require": { @@ -1735,7 +1742,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.38.2" }, "funding": [ { @@ -1746,25 +1753,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-12-23T08:48:59+00:00" + "time": "2026-05-27T06:59:30+00:00" }, { "name": "symfony/service-contracts", - "version": "v3.6.0", + "version": "v3.7.0", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4" + "reference": "d25d82433a80eba6aa0e6c24b61d7370d99e444a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/f021b05a130d35510bd6b25fe9053c2a8a15d5d4", - "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/d25d82433a80eba6aa0e6c24b61d7370d99e444a", + "reference": "d25d82433a80eba6aa0e6c24b61d7370d99e444a", "shasum": "" }, "require": { @@ -1782,7 +1793,7 @@ "name": "symfony/contracts" }, "branch-alias": { - "dev-main": "3.6-dev" + "dev-main": "3.7-dev" } }, "autoload": { @@ -1818,7 +1829,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.6.0" + "source": "https://github.com/symfony/service-contracts/tree/v3.7.0" }, "funding": [ { @@ -1829,25 +1840,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-04-25T09:37:31+00:00" + "time": "2026-03-28T09:44:51+00:00" }, { "name": "symfony/string", - "version": "v6.4.21", + "version": "v6.4.39", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "73e2c6966a5aef1d4892873ed5322245295370c6" + "reference": "62e3c927de664edadb5bef260987eb047a17a113" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/73e2c6966a5aef1d4892873ed5322245295370c6", - "reference": "73e2c6966a5aef1d4892873ed5322245295370c6", + "url": "https://api.github.com/repos/symfony/string/zipball/62e3c927de664edadb5bef260987eb047a17a113", + "reference": "62e3c927de664edadb5bef260987eb047a17a113", "shasum": "" }, "require": { @@ -1861,7 +1876,6 @@ "symfony/translation-contracts": "<2.5" }, "require-dev": { - "symfony/error-handler": "^5.4|^6.0|^7.0", "symfony/http-client": "^5.4|^6.0|^7.0", "symfony/intl": "^6.2|^7.0", "symfony/translation-contracts": "^2.5|^3.0", @@ -1904,7 +1918,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v6.4.21" + "source": "https://github.com/symfony/string/tree/v6.4.39" }, "funding": [ { @@ -1915,12 +1929,16 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-04-18T15:23:29+00:00" + "time": "2026-05-12T11:44:19+00:00" }, { "name": "vimeo/psalm", @@ -2034,28 +2052,28 @@ }, { "name": "webmozart/assert", - "version": "1.11.0", + "version": "1.12.1", "source": { "type": "git", "url": "https://github.com/webmozarts/assert.git", - "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" + "reference": "9be6926d8b485f55b9229203f962b51ed377ba68" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", - "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/9be6926d8b485f55b9229203f962b51ed377ba68", + "reference": "9be6926d8b485f55b9229203f962b51ed377ba68", "shasum": "" }, "require": { "ext-ctype": "*", + "ext-date": "*", + "ext-filter": "*", "php": "^7.2 || ^8.0" }, - "conflict": { - "phpstan/phpstan": "<0.12.20", - "vimeo/psalm": "<4.6.1 || 4.6.2" - }, - "require-dev": { - "phpunit/phpunit": "^8.5.13" + "suggest": { + "ext-intl": "", + "ext-simplexml": "", + "ext-spl": "" }, "type": "library", "extra": { @@ -2086,9 +2104,9 @@ ], "support": { "issues": "https://github.com/webmozarts/assert/issues", - "source": "https://github.com/webmozarts/assert/tree/1.11.0" + "source": "https://github.com/webmozarts/assert/tree/1.12.1" }, - "time": "2022-06-03T18:03:27+00:00" + "time": "2025-10-29T15:56:20+00:00" } ], "aliases": [],