diff --git a/.github/workflows/build-push-test-docker-image.yml b/.github/workflows/build-push-test-docker-image.yml index 1e72a4058..2ef6bdf53 100644 --- a/.github/workflows/build-push-test-docker-image.yml +++ b/.github/workflows/build-push-test-docker-image.yml @@ -16,7 +16,7 @@ jobs: uses: actions/checkout@v6 - name: Build the app - uses: openconext/build-and-publish-test-container/php82-node20@main + uses: openconext/build-and-publish-test-container/php85-node24@main with: use_yarn: false diff --git a/.github/workflows/test-integration.yml b/.github/workflows/test-integration.yml index 293cbfec2..8ddbc10f9 100644 --- a/.github/workflows/test-integration.yml +++ b/.github/workflows/test-integration.yml @@ -10,7 +10,7 @@ jobs: run: working-directory: /var/www/html/ container: - image: ghcr.io/openconext/openconext-basecontainers/php82-apache2-node20-composer2:latest + image: ghcr.io/openconext/openconext-basecontainers/php85-apache2-node24:latest volumes: - .:/var/www/html diff --git a/.scrutinizer.yml b/.scrutinizer.yml index c3e1cf469..53e5b0b46 100644 --- a/.scrutinizer.yml +++ b/.scrutinizer.yml @@ -1,8 +1,8 @@ build: image: default-jammy environment: - php: 8.2 - node: 'v20' + php: 8.5 + node: 'v24' filter: excluded_paths: diff --git a/ci/qa/patch-mockery.php b/ci/qa/patch-mockery.php new file mode 100644 index 000000000..5819487aa --- /dev/null +++ b/ci/qa/patch-mockery.php @@ -0,0 +1,19 @@ + 0) { + file_put_contents($file, $patched); + echo "Patched Mockery: fixed $count implicit nullable param(s) for PHP 8.4+ compatibility\n"; +} diff --git a/ci/qa/phpunit.xml b/ci/qa/phpunit.xml index 91193bae7..8d2e72538 100644 --- a/ci/qa/phpunit.xml +++ b/ci/qa/phpunit.xml @@ -2,20 +2,29 @@ - + + + + + ../../src + + + ../../vendor + + ../../src diff --git a/ci/qa/rector.php b/ci/qa/rector.php index 2dcd41602..e188f4f8f 100644 --- a/ci/qa/rector.php +++ b/ci/qa/rector.php @@ -7,6 +7,7 @@ use Rector\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromotionRector; use Rector\Php81\Rector\Property\ReadOnlyPropertyRector; use Rector\Php82\Rector\Class_\ReadOnlyClassRector; +use Rector\Php84\Rector\Param\ExplicitNullableParamTypeRector; return RectorConfig::configure() ->withPaths([ @@ -21,6 +22,9 @@ ->withTypeCoverageLevel(10) ->withDeadCodeLevel(10) ->withCodeQualityLevel(10) + ->withRules([ + ExplicitNullableParamTypeRector::class, + ]) ->withSkip([ ReadOnlyClassRector::class, ReadOnlyPropertyRector::class, diff --git a/composer.json b/composer.json index aa25c08ba..57f0e18e7 100644 --- a/composer.json +++ b/composer.json @@ -18,7 +18,7 @@ }, "minimum-stability": "stable", "require": { - "php": "^8.2", + "php": "^8.5", "ext-gmp": "*", "ext-json": "*", "ext-openssl": "*", @@ -108,11 +108,13 @@ "post-install-cmd": [ "Incenteev\\ParameterHandler\\ScriptHandler::buildParameters", - "@auto-scripts" + "@auto-scripts", + "php ci/qa/patch-mockery.php" ], "post-update-cmd": [ "Incenteev\\ParameterHandler\\ScriptHandler::buildParameters", - "@auto-scripts" + "@auto-scripts", + "php ci/qa/patch-mockery.php" ], "frontend-install": "echo 'Skipping task: no frontend assets available'" @@ -141,7 +143,7 @@ "symfony/runtime": true }, "platform": { - "php": "8.2" + "php": "8.5" }, "optimize-autoloader": true, "sort-packages": true diff --git a/composer.lock b/composer.lock index 0c18f4215..62f058944 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "e6f4c60a781be36f8c5e5a5cff4133e7", + "content-hash": "a70e3cac55ce6aa80b5440fe9a420a51", "packages": [ { "name": "beberlei/assert", @@ -12262,7 +12262,7 @@ "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": "^8.2", + "php": "^8.5", "ext-gmp": "*", "ext-json": "*", "ext-openssl": "*", @@ -12270,7 +12270,7 @@ }, "platform-dev": {}, "platform-overrides": { - "php": "8.2" + "php": "8.5" }, "plugin-api-version": "2.9.0" } diff --git a/docker/Dockerfile.prod b/docker/Dockerfile.prod index 3b0a04a30..7ffaf303d 100644 --- a/docker/Dockerfile.prod +++ b/docker/Dockerfile.prod @@ -1,4 +1,4 @@ -FROM ghcr.io/openconext/openconext-basecontainers/php82-apache2:latest AS php-build +FROM ghcr.io/openconext/openconext-basecontainers/php85-apache2-node24:latest AS php-build ARG APP_VERSION ARG GIT_SHA ARG GIT_COMMIT_TIME diff --git a/docker/Dockerfile.test b/docker/Dockerfile.test index 6fcc12639..744ee5b0a 100644 --- a/docker/Dockerfile.test +++ b/docker/Dockerfile.test @@ -3,7 +3,7 @@ WORKDIR /unpack COPY output.zip /unpack RUN unzip /unpack/output.zip -FROM ghcr.io/openconext/openconext-basecontainers/php82-apache2:latest +FROM ghcr.io/openconext/openconext-basecontainers/php85-apache2-node24:latest # Set the default workdir WORKDIR /var/www/html COPY --from=unpack /unpack/ /var/www/html/ diff --git a/src/Surfnet/Stepup/DateTime/DateTime.php b/src/Surfnet/Stepup/DateTime/DateTime.php index 7ecab8e0f..c8a66f908 100644 --- a/src/Surfnet/Stepup/DateTime/DateTime.php +++ b/src/Surfnet/Stepup/DateTime/DateTime.php @@ -73,7 +73,7 @@ public static function fromString(string $string): self /** * @param CoreDateTime|null $dateTime */ - public function __construct(CoreDateTime $dateTime = null) + public function __construct(?CoreDateTime $dateTime = null) { $this->dateTime = $dateTime ?: new CoreDateTime(); } diff --git a/src/Surfnet/Stepup/Tests/Identity/Value/EmailVerificationWindowTest.php b/src/Surfnet/Stepup/Tests/Identity/Value/EmailVerificationWindowTest.php index 3e1b31630..45cc19594 100644 --- a/src/Surfnet/Stepup/Tests/Identity/Value/EmailVerificationWindowTest.php +++ b/src/Surfnet/Stepup/Tests/Identity/Value/EmailVerificationWindowTest.php @@ -116,7 +116,7 @@ public function the_window_correctly_calculates_the_end_datetime(): void * @throws Exception * @throws Exception */ - private function newEmailVerificationWindow(int $timeFrameSeconds, string $startTimeOffset = null): EmailVerificationWindow + private function newEmailVerificationWindow(int $timeFrameSeconds, ?string $startTimeOffset = null): EmailVerificationWindow { $start = DateTime::now(); if ($startTimeOffset) { diff --git a/src/Surfnet/StepupMiddleware/ApiBundle/Authorization/Service/CommandAuthorizationService.php b/src/Surfnet/StepupMiddleware/ApiBundle/Authorization/Service/CommandAuthorizationService.php index 38a21bcd5..cd5a0a38c 100644 --- a/src/Surfnet/StepupMiddleware/ApiBundle/Authorization/Service/CommandAuthorizationService.php +++ b/src/Surfnet/StepupMiddleware/ApiBundle/Authorization/Service/CommandAuthorizationService.php @@ -69,7 +69,7 @@ public function __construct( * @param IdentityId|null $actorId * @return bool */ - public function isInstitutionWhitelisted(Institution $institution, IdentityId $actorId = null): bool + public function isInstitutionWhitelisted(Institution $institution, ?IdentityId $actorId = null): bool { // If the actor is SRAA all actions should be allowed if (!is_null($actorId) && $this->isSraa($actorId)) { @@ -78,7 +78,7 @@ public function isInstitutionWhitelisted(Institution $institution, IdentityId $a return $this->whitelistService->isWhitelisted($institution->getInstitution()); } - public function maySelfServiceCommandBeExecutedOnBehalfOf(Command $command, IdentityId $actorId = null): bool + public function maySelfServiceCommandBeExecutedOnBehalfOf(Command $command, ?IdentityId $actorId = null): bool { $commandName = $command::class; $identityId = $actorId instanceof IdentityId ? $actorId->getIdentityId() : null; @@ -140,8 +140,8 @@ public function maySelfServiceCommandBeExecutedOnBehalfOf(Command $command, Iden */ public function mayRaCommandBeExecutedOnBehalfOf( Command $command, - IdentityId $actorId = null, - Institution $actorInstitution = null, + ?IdentityId $actorId = null, + ?Institution $actorInstitution = null, ): bool { $commandName = $command::class; $identityId = $actorId instanceof IdentityId ? $actorId->getIdentityId() : null; @@ -256,7 +256,7 @@ public function mayRaCommandBeExecutedOnBehalfOf( return true; } - private function isSraa(IdentityId $actorId = null): bool + private function isSraa(?IdentityId $actorId = null): bool { if (is_null($actorId)) { return false; diff --git a/src/Surfnet/StepupMiddleware/ApiBundle/Identity/Projector/RaSecondFactorProjector.php b/src/Surfnet/StepupMiddleware/ApiBundle/Identity/Projector/RaSecondFactorProjector.php index 59541282d..2f04a1089 100644 --- a/src/Surfnet/StepupMiddleware/ApiBundle/Identity/Projector/RaSecondFactorProjector.php +++ b/src/Surfnet/StepupMiddleware/ApiBundle/Identity/Projector/RaSecondFactorProjector.php @@ -229,8 +229,8 @@ private function saveRaSecondFactor( string $secondFactorIdentifier, CommonName $commonName, Email $email, - SecondFactorStatus $status = null, - DocumentNumber $documentNumber = null, + ?SecondFactorStatus $status = null, + ?DocumentNumber $documentNumber = null, ): void { $identity = $this->identityRepository->find($identityId); diff --git a/src/Surfnet/StepupMiddleware/ApiBundle/Security/Http/EntryPoint/JsonBasicAuthenticationEntryPoint.php b/src/Surfnet/StepupMiddleware/ApiBundle/Security/Http/EntryPoint/JsonBasicAuthenticationEntryPoint.php index 692fd8063..3f56607dc 100644 --- a/src/Surfnet/StepupMiddleware/ApiBundle/Security/Http/EntryPoint/JsonBasicAuthenticationEntryPoint.php +++ b/src/Surfnet/StepupMiddleware/ApiBundle/Security/Http/EntryPoint/JsonBasicAuthenticationEntryPoint.php @@ -36,7 +36,7 @@ public function __construct(private string $realmName) /** * {@inheritdoc} */ - public function start(Request $request, AuthenticationException $authException = null): Response + public function start(Request $request, ?AuthenticationException $authException = null): Response { $authExceptionMessage = $authException instanceof AuthenticationException ? $authException->getMessage() : ''; $error = sprintf('You are required to authorise before accessing this API (%s).', $authExceptionMessage); diff --git a/src/Surfnet/StepupMiddleware/ApiBundle/Tests/Identity/Projector/AuditLogProjectorTest.php b/src/Surfnet/StepupMiddleware/ApiBundle/Tests/Identity/Projector/AuditLogProjectorTest.php index f82c14e97..fb3d8f2ac 100644 --- a/src/Surfnet/StepupMiddleware/ApiBundle/Tests/Identity/Projector/AuditLogProjectorTest.php +++ b/src/Surfnet/StepupMiddleware/ApiBundle/Tests/Identity/Projector/AuditLogProjectorTest.php @@ -170,9 +170,9 @@ public function it_creates_entries_for_auditable_events(DomainMessage $message, private function createAuditLogMetadata( IdentityId $identityId, Institution $institution, - SecondFactorId $secondFactorId = null, - SecondFactorType $secondFactorType = null, - SecondFactorIdentifier $secondFactorIdentifier = null, + ?SecondFactorId $secondFactorId = null, + ?SecondFactorType $secondFactorType = null, + ?SecondFactorIdentifier $secondFactorIdentifier = null, ): Metadata { $metadata = new Metadata(); $metadata->identityId = $identityId; @@ -189,10 +189,10 @@ private function createExpectedAuditLogEntry( Institution $identityInstitution, string $event, StepupDateTime $recordedOn, - IdentityId $actorId = null, - Institution $actorInstitution = null, - SecondFactorId $secondFactorId = null, - SecondFactorType $secondFactorType = null, + ?IdentityId $actorId = null, + ?Institution $actorInstitution = null, + ?SecondFactorId $secondFactorId = null, + ?SecondFactorType $secondFactorType = null, ?YubikeyPublicId $secondFactorIdentifier = null, ?CommonName $actorCommonName = null, ): AuditLogEntry { diff --git a/src/Surfnet/StepupMiddleware/CommandHandlingBundle/EventSourcing/MetadataEnricher.php b/src/Surfnet/StepupMiddleware/CommandHandlingBundle/EventSourcing/MetadataEnricher.php index 555c9b882..6997ce6fe 100644 --- a/src/Surfnet/StepupMiddleware/CommandHandlingBundle/EventSourcing/MetadataEnricher.php +++ b/src/Surfnet/StepupMiddleware/CommandHandlingBundle/EventSourcing/MetadataEnricher.php @@ -26,5 +26,5 @@ interface MetadataEnricher * @param Metadata|null $metadata * @return void */ - public function setMetadata(Metadata $metadata = null): void; + public function setMetadata(?Metadata $metadata = null): void; } diff --git a/src/Surfnet/StepupMiddleware/CommandHandlingBundle/EventSourcing/MetadataEnrichingEventStreamDecorator.php b/src/Surfnet/StepupMiddleware/CommandHandlingBundle/EventSourcing/MetadataEnrichingEventStreamDecorator.php index e3e1848e0..e1b6d2041 100644 --- a/src/Surfnet/StepupMiddleware/CommandHandlingBundle/EventSourcing/MetadataEnrichingEventStreamDecorator.php +++ b/src/Surfnet/StepupMiddleware/CommandHandlingBundle/EventSourcing/MetadataEnrichingEventStreamDecorator.php @@ -28,7 +28,7 @@ final class MetadataEnrichingEventStreamDecorator implements EventStreamDecorato { private ?Metadata $metadata = null; - public function setMetadata(Metadata $metadata = null): void + public function setMetadata(?Metadata $metadata = null): void { $this->metadata = $metadata; } diff --git a/src/Surfnet/StepupMiddleware/CommandHandlingBundle/Exception/SecondFactorNotAllowedException.php b/src/Surfnet/StepupMiddleware/CommandHandlingBundle/Exception/SecondFactorNotAllowedException.php index 2b2564231..8a7e86ad3 100644 --- a/src/Surfnet/StepupMiddleware/CommandHandlingBundle/Exception/SecondFactorNotAllowedException.php +++ b/src/Surfnet/StepupMiddleware/CommandHandlingBundle/Exception/SecondFactorNotAllowedException.php @@ -36,7 +36,7 @@ public function getErrors(): array return $this->errors; } - public function __construct(string $message = "", int $code = 0, Throwable $previous = null) + public function __construct(string $message = "", int $code = 0, ?Throwable $previous = null) { parent::__construct($message, $code, $previous); diff --git a/src/Surfnet/StepupMiddleware/CommandHandlingBundle/Identity/CommandHandler/Exception/DuplicateIdentityException.php b/src/Surfnet/StepupMiddleware/CommandHandlingBundle/Identity/CommandHandler/Exception/DuplicateIdentityException.php index 6521d1ba5..9a4f8dedf 100644 --- a/src/Surfnet/StepupMiddleware/CommandHandlingBundle/Identity/CommandHandler/Exception/DuplicateIdentityException.php +++ b/src/Surfnet/StepupMiddleware/CommandHandlingBundle/Identity/CommandHandler/Exception/DuplicateIdentityException.php @@ -24,7 +24,7 @@ final class DuplicateIdentityException extends RuntimeException { - public function __construct(string $message = "", int $code = 0, Throwable $previous = null) + public function __construct(string $message = "", int $code = 0, ?Throwable $previous = null) { parent::__construct($message, $code, $previous); } diff --git a/src/Surfnet/StepupMiddleware/CommandHandlingBundle/SensitiveData/EventSourcing/SensitiveDataMessageStream.php b/src/Surfnet/StepupMiddleware/CommandHandlingBundle/SensitiveData/EventSourcing/SensitiveDataMessageStream.php index d5a21794b..99817d931 100644 --- a/src/Surfnet/StepupMiddleware/CommandHandlingBundle/SensitiveData/EventSourcing/SensitiveDataMessageStream.php +++ b/src/Surfnet/StepupMiddleware/CommandHandlingBundle/SensitiveData/EventSourcing/SensitiveDataMessageStream.php @@ -74,7 +74,7 @@ public function getIterator(): Iterator private function setSensitiveData( DomainMessage $domainMessage, - SensitiveDataMessage $sensitiveDataMessage = null, + ?SensitiveDataMessage $sensitiveDataMessage = null, ): void { $event = $domainMessage->getPayload(); $eventIsForgettable = $event instanceof Forgettable; diff --git a/src/Surfnet/StepupMiddleware/CommandHandlingBundle/Tests/Configuration/CommandHandler/ConfigurationCommandHandlerTest.php b/src/Surfnet/StepupMiddleware/CommandHandlingBundle/Tests/Configuration/CommandHandler/ConfigurationCommandHandlerTest.php index 3ffa73cad..4d427cf3b 100644 --- a/src/Surfnet/StepupMiddleware/CommandHandlingBundle/Tests/Configuration/CommandHandler/ConfigurationCommandHandlerTest.php +++ b/src/Surfnet/StepupMiddleware/CommandHandlingBundle/Tests/Configuration/CommandHandler/ConfigurationCommandHandlerTest.php @@ -167,7 +167,7 @@ private function createNewConfigurationCreatedEvent(): NewConfigurationCreatedEv /** * @return array */ - private function createConfigurationUpdatedEvents(array $newConfiguration, array $oldConfiguration = null): array + private function createConfigurationUpdatedEvents(array $newConfiguration, ?array $oldConfiguration = null): array { return [ new ConfigurationUpdatedEvent(self::CID, $newConfiguration, $oldConfiguration), diff --git a/src/Surfnet/StepupMiddleware/CommandHandlingBundle/Tests/DateTimeHelper.php b/src/Surfnet/StepupMiddleware/CommandHandlingBundle/Tests/DateTimeHelper.php index c25036e59..4c28d3260 100644 --- a/src/Surfnet/StepupMiddleware/CommandHandlingBundle/Tests/DateTimeHelper.php +++ b/src/Surfnet/StepupMiddleware/CommandHandlingBundle/Tests/DateTimeHelper.php @@ -28,7 +28,7 @@ class DateTimeHelper * * @param DateTime|null $now */ - public static function setCurrentTime(DateTime $now = null): void + public static function setCurrentTime(?DateTime $now = null): void { $nowProperty = new ReflectionProperty(DateTime::class, 'now'); $nowProperty->setValue($now); diff --git a/src/Surfnet/StepupMiddleware/CommandHandlingBundle/Twig/BackwardsCompatibleExtension.php b/src/Surfnet/StepupMiddleware/CommandHandlingBundle/Twig/BackwardsCompatibleExtension.php index 2d9757bbe..a42f4cd48 100644 --- a/src/Surfnet/StepupMiddleware/CommandHandlingBundle/Twig/BackwardsCompatibleExtension.php +++ b/src/Surfnet/StepupMiddleware/CommandHandlingBundle/Twig/BackwardsCompatibleExtension.php @@ -45,7 +45,7 @@ public function localizedDate( DateTimeInterface|string|null $date, ?string $dateFormat = 'medium', ?string $timeFormat = 'medium', - string $locale = null + ?string $locale = null ): string { return $this->intlExtension->formatDateTime($env, $date, $dateFormat, $timeFormat, locale: $locale); } diff --git a/symfony.lock b/symfony.lock index 862a56e38..76144d2e0 100644 --- a/symfony.lock +++ b/symfony.lock @@ -135,9 +135,6 @@ "guzzlehttp/psr7": { "version": "1.6.1" }, - "hamcrest/hamcrest-php": { - "version": "v2.0.0" - }, "incenteev/composer-parameter-handler": { "version": "v2.1.4" }, diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 8a716bd9e..9b343e247 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -16,6 +16,10 @@ * limitations under the License. */ +// Suppress vendor E_DEPRECATED notices (e.g. Mockery implicit nullable params in PHP 8.4+) +// before autoloading to avoid noise in RunInSeparateProcess subprocess output. +error_reporting(E_ALL & ~E_DEPRECATED & ~E_USER_DEPRECATED); + use Symfony\Component\Dotenv\Dotenv; require dirname(__DIR__).'/vendor/autoload.php';