From 5a98a217d5345cc485e92959ef05a3062c90499f Mon Sep 17 00:00:00 2001 From: Brent Roose Date: Wed, 3 Dec 2025 14:10:00 +0100 Subject: [PATCH 01/17] feat(core): support php 8.5 (#1733) --- .github/workflows/coding-conventions.yml | 9 ++++----- .github/workflows/integration-tests-windows.yml | 6 +++--- .github/workflows/integration-tests.yml | 6 +++--- .github/workflows/isolated-tests.yml | 6 +++--- .github/workflows/subsplit-packages.yml | 2 +- .github/workflows/validate-packages.yml | 2 +- composer.json | 6 +++--- packages/auth/composer.json | 2 +- packages/cache/composer.json | 2 +- packages/clock/composer.json | 2 +- packages/command-bus/composer.json | 2 +- packages/console/composer.json | 2 +- packages/container/composer.json | 2 +- packages/core/composer.json | 2 +- packages/cryptography/composer.json | 2 +- packages/database/composer.json | 2 +- packages/datetime/composer.json | 2 +- packages/debug/composer.json | 2 +- packages/discovery/composer.json | 2 +- packages/event-bus/composer.json | 2 +- packages/generation/composer.json | 2 +- packages/http-client/composer.json | 2 +- packages/http/composer.json | 2 +- packages/icon/composer.json | 2 +- packages/intl/composer.json | 2 +- packages/kv-store/composer.json | 2 +- packages/log/composer.json | 2 +- packages/mail/composer.json | 2 +- packages/mapper/composer.json | 2 +- packages/process/composer.json | 2 +- packages/reflection/composer.json | 2 +- packages/router/composer.json | 2 +- packages/storage/composer.json | 2 +- packages/support/composer.json | 2 +- packages/upgrade/composer.json | 2 +- packages/validation/composer.json | 2 +- packages/view/composer.json | 2 +- packages/vite/composer.json | 2 +- 38 files changed, 49 insertions(+), 50 deletions(-) diff --git a/.github/workflows/coding-conventions.yml b/.github/workflows/coding-conventions.yml index 0192386fc..7f66d9f3f 100644 --- a/.github/workflows/coding-conventions.yml +++ b/.github/workflows/coding-conventions.yml @@ -4,7 +4,6 @@ on: pull_request: workflow_dispatch: -# CSFixer and Rector are temporarily disabled until they have proper PHP 8.4 support jobs: check-style: name: Run style check @@ -15,12 +14,12 @@ jobs: - name: Setup PHP uses: shivammathur/setup-php@v2 with: - php-version: 8.4 + php-version: 8.5 coverage: none - name: Install dependencies run: | - composer update --prefer-dist --no-interaction + composer update --prefer-dist --no-interaction --ignore-platform-reqs composer mago:install-binary - name: Run Mago @@ -46,7 +45,7 @@ jobs: - name: Setup PHP uses: shivammathur/setup-php@v2 with: - php-version: 8.4 + php-version: 8.5 coverage: none - name: Install composer dependencies @@ -65,7 +64,7 @@ jobs: # - name: Setup PHP # uses: shivammathur/setup-php@v2 # with: -# php-version: 8.4 +# php-version: 8.5 # coverage: none # # - name: Install composer dependencies diff --git a/.github/workflows/integration-tests-windows.yml b/.github/workflows/integration-tests-windows.yml index aea44c2f1..c2875b06d 100644 --- a/.github/workflows/integration-tests-windows.yml +++ b/.github/workflows/integration-tests-windows.yml @@ -16,7 +16,7 @@ jobs: fail-fast: false matrix: php: - - 8.4 + - 8.5 os: - windows-latest env: @@ -55,7 +55,7 @@ jobs: os: - windows-latest php: - - 8.4 + - 8.5 database: - sqlite - mysql @@ -84,7 +84,7 @@ jobs: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json" - name: Install dependencies - run: composer update --${{ matrix.stability }} --prefer-dist --no-interaction + run: composer update --${{ matrix.stability }} --prefer-dist --no-interaction --ignore-platform-reqs - name: "Setup Redis" if: ${{ matrix.os != 'windows-latest' }} diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 48f2862f0..d9e3e67ff 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -20,7 +20,7 @@ jobs: fail-fast: false matrix: php: - - 8.4 + - 8.5 os: - ubuntu-latest # - windows-latest @@ -59,7 +59,7 @@ jobs: - ubuntu-latest # - windows-latest php: - - 8.4 + - 8.5 database: - sqlite - mysql @@ -90,7 +90,7 @@ jobs: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json" - name: Install dependencies - run: composer update --${{ matrix.stability }} --prefer-dist --no-interaction + run: composer update --${{ matrix.stability }} --prefer-dist --no-interaction --ignore-platform-reqs - name: "Setup Redis" if: ${{ matrix.os != 'windows-latest' }} diff --git a/.github/workflows/isolated-tests.yml b/.github/workflows/isolated-tests.yml index e296c065d..3cf644085 100644 --- a/.github/workflows/isolated-tests.yml +++ b/.github/workflows/isolated-tests.yml @@ -17,7 +17,7 @@ jobs: - name: Set up PHP uses: shivammathur/setup-php@v2 with: - php-version: 8.4 + php-version: 8.5 coverage: none - uses: actions/checkout@v4 @@ -43,7 +43,7 @@ jobs: - ubuntu-latest package: ${{ fromJson(needs.get_packages.outputs.matrix) }} php: - - 8.4 + - 8.5 stability: - prefer-stable - prefer-lowest @@ -77,7 +77,7 @@ jobs: run: | ./bin/build-changed-packages cd "packages/${{ matrix.package.basename }}" - composer update --${{ matrix.stability }} --prefer-dist --no-interaction + composer update --${{ matrix.stability }} --prefer-dist --no-interaction --ignore-platform-reqs - name: Execute tests run: phpunit -c "packages/${{ matrix.package.basename }}/phpunit.xml" diff --git a/.github/workflows/subsplit-packages.yml b/.github/workflows/subsplit-packages.yml index b534d3a30..278300491 100644 --- a/.github/workflows/subsplit-packages.yml +++ b/.github/workflows/subsplit-packages.yml @@ -17,7 +17,7 @@ jobs: - name: Set Up PHP uses: shivammathur/setup-php@v2 with: - php-version: 8.4 + php-version: 8.5 coverage: none - uses: actions/checkout@v4 diff --git a/.github/workflows/validate-packages.yml b/.github/workflows/validate-packages.yml index 7a62fd23c..dc7643f31 100644 --- a/.github/workflows/validate-packages.yml +++ b/.github/workflows/validate-packages.yml @@ -18,7 +18,7 @@ jobs: - name: Setup PHP uses: shivammathur/setup-php@v2 with: - php-version: 8.4 + php-version: 8.5 extensions: dom, curl, libxml, mbstring, pcntl, fileinfo, intl coverage: none diff --git a/composer.json b/composer.json index fef33c3c1..907d7aa43 100644 --- a/composer.json +++ b/composer.json @@ -24,7 +24,7 @@ "monolog/monolog": "^3.7.0", "nette/php-generator": "^4.1.6", "nikic/php-parser": "^5.3", - "php": "^8.4", + "php": "^8.5", "psr-discovery/http-client-implementations": "^1.4", "psr-discovery/http-factory-implementations": "^1.2", "psr/cache": "^3.0", @@ -49,6 +49,7 @@ "adam-paterson/oauth2-slack": "^1.1", "aws/aws-sdk-php": "^3.338.0", "azure-oss/storage-blob-flysystem": "^1.2", + "brianium/paratest": "^7.14", "carthage-software/mago": "1.0.0-beta.28", "depotwarehouse/oauth2-twitch": "^1.3", "guzzlehttp/psr7": "^2.6.1", @@ -87,8 +88,7 @@ "tempest/blade": "dev-main", "thenetworg/oauth2-azure": "^2.2", "twig/twig": "^3.16", - "wohali/oauth2-discord-new": "^1.2", - "brianium/paratest": "^7.14" + "wohali/oauth2-discord-new": "^1.2" }, "replace": { "tempest/auth": "self.version", diff --git a/packages/auth/composer.json b/packages/auth/composer.json index f530ffa19..687412af4 100644 --- a/packages/auth/composer.json +++ b/packages/auth/composer.json @@ -2,7 +2,7 @@ "name": "tempest/auth", "description": "A flexible authentication package for Tempest, providing authentication and authorization.", "require": { - "php": "^8.4", + "php": "^8.5", "tempest/core": "dev-main", "tempest/router": "dev-main", "tempest/database": "dev-main", diff --git a/packages/cache/composer.json b/packages/cache/composer.json index 60ae7d905..3cbc5b997 100644 --- a/packages/cache/composer.json +++ b/packages/cache/composer.json @@ -2,7 +2,7 @@ "name": "tempest/cache", "description": "The PHP framework that gets out of your way.", "require": { - "php": "^8.4", + "php": "^8.5", "psr/cache": "^3.0", "symfony/cache": "^7.3", "tempest/core": "dev-main", diff --git a/packages/clock/composer.json b/packages/clock/composer.json index f2272e073..d3411aa19 100644 --- a/packages/clock/composer.json +++ b/packages/clock/composer.json @@ -2,7 +2,7 @@ "name": "tempest/clock", "description": "A clock component that handle few simple clock operations.", "require": { - "php": "^8.4", + "php": "^8.5", "psr/clock": "^1.0.0", "tempest/datetime": "dev-main" }, diff --git a/packages/command-bus/composer.json b/packages/command-bus/composer.json index 5834601a0..f34ec675d 100644 --- a/packages/command-bus/composer.json +++ b/packages/command-bus/composer.json @@ -2,7 +2,7 @@ "name": "tempest/command-bus", "description": "A command bus component designed to dispatch commands to their respective handlers.", "require": { - "php": "^8.4", + "php": "^8.5", "tempest/core": "dev-main", "tempest/console": "dev-main", "tempest/container": "dev-main" diff --git a/packages/console/composer.json b/packages/console/composer.json index ce53de888..44552dfaa 100644 --- a/packages/console/composer.json +++ b/packages/console/composer.json @@ -4,7 +4,7 @@ "license": "MIT", "minimum-stability": "dev", "require": { - "php": "^8.4", + "php": "^8.5", "tempest/cache": "dev-main", "tempest/core": "dev-main", "tempest/container": "dev-main", diff --git a/packages/container/composer.json b/packages/container/composer.json index 3897d3cce..b60a28517 100644 --- a/packages/container/composer.json +++ b/packages/container/composer.json @@ -4,7 +4,7 @@ "license": "MIT", "minimum-stability": "dev", "require": { - "php": "^8.4", + "php": "^8.5", "tempest/reflection": "dev-main" }, "autoload": { diff --git a/packages/core/composer.json b/packages/core/composer.json index 226b393e2..61182e788 100644 --- a/packages/core/composer.json +++ b/packages/core/composer.json @@ -4,7 +4,7 @@ "license": "MIT", "minimum-stability": "dev", "require": { - "php": "^8.4", + "php": "^8.5", "tempest/container": "dev-main", "tempest/discovery": "dev-main", "tempest/reflection": "dev-main", diff --git a/packages/cryptography/composer.json b/packages/cryptography/composer.json index 40b072222..a60e22ff5 100644 --- a/packages/cryptography/composer.json +++ b/packages/cryptography/composer.json @@ -4,7 +4,7 @@ "license": "MIT", "minimum-stability": "dev", "require": { - "php": "^8.4", + "php": "^8.5", "tempest/container": "dev-main", "tempest/support": "dev-main", "tempest/clock": "dev-main" diff --git a/packages/database/composer.json b/packages/database/composer.json index 3fe7e0ba3..74792cf84 100644 --- a/packages/database/composer.json +++ b/packages/database/composer.json @@ -4,7 +4,7 @@ "license": "MIT", "minimum-stability": "dev", "require": { - "php": "^8.4", + "php": "^8.5", "ext-pdo": "*", "tempest/container": "dev-main", "tempest/event-bus": "dev-main", diff --git a/packages/datetime/composer.json b/packages/datetime/composer.json index 56661c111..fe6751a1e 100644 --- a/packages/datetime/composer.json +++ b/packages/datetime/composer.json @@ -4,7 +4,7 @@ "license": "MIT", "minimum-stability": "dev", "require": { - "php": "^8.4", + "php": "^8.5", "tempest/intl": "dev-main", "tempest/support": "dev-main" }, diff --git a/packages/debug/composer.json b/packages/debug/composer.json index d5eae9289..fa5052fd0 100644 --- a/packages/debug/composer.json +++ b/packages/debug/composer.json @@ -4,7 +4,7 @@ "license": "MIT", "minimum-stability": "dev", "require": { - "php": "^8.4", + "php": "^8.5", "tempest/highlight": "^2.11.4", "symfony/var-dumper": "^7.1" }, diff --git a/packages/discovery/composer.json b/packages/discovery/composer.json index a8d380860..cb2849333 100644 --- a/packages/discovery/composer.json +++ b/packages/discovery/composer.json @@ -4,7 +4,7 @@ "license": "MIT", "minimum-stability": "dev", "require": { - "php": "^8.4", + "php": "^8.5", "tempest/reflection": "dev-main", "tempest/support": "dev-main" }, diff --git a/packages/event-bus/composer.json b/packages/event-bus/composer.json index b80a1d824..794948ba5 100644 --- a/packages/event-bus/composer.json +++ b/packages/event-bus/composer.json @@ -2,7 +2,7 @@ "name": "tempest/event-bus", "description": "A lightweight event bus component designed to facilitate event-driven architecture and asynchronous message handling.", "require": { - "php": "^8.4", + "php": "^8.5", "tempest/core": "dev-main", "tempest/container": "dev-main", "tempest/reflection": "dev-main" diff --git a/packages/generation/composer.json b/packages/generation/composer.json index 549472d21..75d400c7c 100644 --- a/packages/generation/composer.json +++ b/packages/generation/composer.json @@ -4,7 +4,7 @@ "license": "MIT", "minimum-stability": "dev", "require": { - "php": "^8.4", + "php": "^8.5", "nette/php-generator": "^4.1.6", "nikic/php-parser": "^5.3", "tempest/support": "dev-main" diff --git a/packages/http-client/composer.json b/packages/http-client/composer.json index 578ce523e..62016d3a7 100644 --- a/packages/http-client/composer.json +++ b/packages/http-client/composer.json @@ -4,7 +4,7 @@ "license": "MIT", "minimum-stability": "dev", "require": { - "php": "^8.4", + "php": "^8.5", "psr/http-client": "^1.0.0", "psr/http-message": "^1.0|^2.0", "tempest/container": "dev-main", diff --git a/packages/http/composer.json b/packages/http/composer.json index 17700a16e..cdb45d1b0 100644 --- a/packages/http/composer.json +++ b/packages/http/composer.json @@ -4,7 +4,7 @@ "license": "MIT", "minimum-stability": "dev", "require": { - "php": "^8.4", + "php": "^8.5", "tempest/core": "dev-main", "tempest/clock": "dev-main", "tempest/console": "dev-main", diff --git a/packages/icon/composer.json b/packages/icon/composer.json index 1bf22c13a..78fad985e 100644 --- a/packages/icon/composer.json +++ b/packages/icon/composer.json @@ -4,7 +4,7 @@ "license": "MIT", "minimum-stability": "dev", "require": { - "php": "^8.4", + "php": "^8.5", "tempest/container": "dev-main", "tempest/http-client": "dev-main", "tempest/support": "dev-main", diff --git a/packages/intl/composer.json b/packages/intl/composer.json index 4ff8ef769..8d8e833aa 100644 --- a/packages/intl/composer.json +++ b/packages/intl/composer.json @@ -4,7 +4,7 @@ "license": "MIT", "minimum-stability": "dev", "require": { - "php": "^8.4", + "php": "^8.5", "doctrine/inflector": "^2.0", "symfony/yaml": "^7.3", "tempest/core": "dev-main", diff --git a/packages/kv-store/composer.json b/packages/kv-store/composer.json index 629555509..768fd6dc5 100644 --- a/packages/kv-store/composer.json +++ b/packages/kv-store/composer.json @@ -4,7 +4,7 @@ "license": "MIT", "minimum-stability": "dev", "require": { - "php": "^8.4", + "php": "^8.5", "tempest/support": "dev-main", "tempest/datetime": "dev-main", "tempest/event-bus": "dev-main" diff --git a/packages/log/composer.json b/packages/log/composer.json index bc0a48a29..8453b6e44 100644 --- a/packages/log/composer.json +++ b/packages/log/composer.json @@ -4,7 +4,7 @@ "license": "MIT", "minimum-stability": "dev", "require": { - "php": "^8.4", + "php": "^8.5", "monolog/monolog": "^3.7.0", "psr/log": "^3.0.0", "tempest/container": "dev-main" diff --git a/packages/mail/composer.json b/packages/mail/composer.json index b67757b3d..5ef478c8f 100644 --- a/packages/mail/composer.json +++ b/packages/mail/composer.json @@ -4,7 +4,7 @@ "license": "MIT", "minimum-stability": "dev", "require": { - "php": "^8.4", + "php": "^8.5", "tempest/reflection": "dev-main", "tempest/support": "dev-main", "tempest/event-bus": "dev-main", diff --git a/packages/mapper/composer.json b/packages/mapper/composer.json index 679e0d251..88cde6285 100644 --- a/packages/mapper/composer.json +++ b/packages/mapper/composer.json @@ -4,7 +4,7 @@ "license": "MIT", "minimum-stability": "dev", "require": { - "php": "^8.4", + "php": "^8.5", "tempest/validation": "dev-main", "tempest/core": "dev-main" }, diff --git a/packages/process/composer.json b/packages/process/composer.json index 97e073f3c..da0cef944 100644 --- a/packages/process/composer.json +++ b/packages/process/composer.json @@ -4,7 +4,7 @@ "license": "MIT", "minimum-stability": "dev", "require": { - "php": "^8.4", + "php": "^8.5", "symfony/process": "^7.3", "tempest/container": "dev-main", "tempest/support": "dev-main", diff --git a/packages/reflection/composer.json b/packages/reflection/composer.json index 896595215..2e5525bb8 100644 --- a/packages/reflection/composer.json +++ b/packages/reflection/composer.json @@ -4,7 +4,7 @@ "license": "MIT", "minimum-stability": "dev", "require": { - "php": "^8.4" + "php": "^8.5" }, "autoload": { "psr-4": { diff --git a/packages/router/composer.json b/packages/router/composer.json index cab9eb741..91f5aa170 100644 --- a/packages/router/composer.json +++ b/packages/router/composer.json @@ -4,7 +4,7 @@ "license": "MIT", "minimum-stability": "dev", "require": { - "php": "^8.4", + "php": "^8.5", "tempest/http": "dev-main", "tempest/view": "dev-main", "tempest/highlight": "^2.11.4", diff --git a/packages/storage/composer.json b/packages/storage/composer.json index 5fc1f61ac..9156514f2 100644 --- a/packages/storage/composer.json +++ b/packages/storage/composer.json @@ -4,7 +4,7 @@ "license": "MIT", "minimum-stability": "dev", "require": { - "php": "^8.4", + "php": "^8.5", "league/flysystem": "^3.29.1", "tempest/container": "dev-main" }, diff --git a/packages/support/composer.json b/packages/support/composer.json index 1c377e3e1..3c52fe5d2 100644 --- a/packages/support/composer.json +++ b/packages/support/composer.json @@ -4,7 +4,7 @@ "license": "MIT", "minimum-stability": "dev", "require": { - "php": "^8.4", + "php": "^8.5", "tempest/container": "dev-main", "voku/portable-ascii": "^2.0.3", "symfony/uid": "^7.1" diff --git a/packages/upgrade/composer.json b/packages/upgrade/composer.json index 7bebcfc54..7ee63987c 100644 --- a/packages/upgrade/composer.json +++ b/packages/upgrade/composer.json @@ -4,7 +4,7 @@ "license": "MIT", "minimum-stability": "dev", "require": { - "php": "^8.4", + "php": "^8.5", "rector/rector": "^2.2.5" }, "autoload": { diff --git a/packages/validation/composer.json b/packages/validation/composer.json index a04f34d07..5f0a5f2f4 100644 --- a/packages/validation/composer.json +++ b/packages/validation/composer.json @@ -4,7 +4,7 @@ "license": "MIT", "minimum-stability": "dev", "require": { - "php": "^8.4", + "php": "^8.5", "egulias/email-validator": "^4.0.2", "giggsey/libphonenumber-for-php-lite": "^9.0", "tempest/reflection": "dev-main", diff --git a/packages/view/composer.json b/packages/view/composer.json index e43cbe96f..342e8ed26 100644 --- a/packages/view/composer.json +++ b/packages/view/composer.json @@ -4,7 +4,7 @@ "license": "MIT", "minimum-stability": "dev", "require": { - "php": "^8.4", + "php": "^8.5", "tempest/core": "dev-main", "tempest/container": "dev-main", "tempest/validation": "dev-main", diff --git a/packages/vite/composer.json b/packages/vite/composer.json index 37a039ec7..844454c4c 100644 --- a/packages/vite/composer.json +++ b/packages/vite/composer.json @@ -4,7 +4,7 @@ "license": "MIT", "minimum-stability": "dev", "require": { - "php": "^8.4" + "php": "^8.5" }, "autoload": { "psr-4": { From 0f631879b8e06668d0330f350d7c9fd99f40f8d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rk=20Magyar?= Date: Wed, 3 Dec 2025 13:51:30 +0100 Subject: [PATCH 02/17] fix(core): gracefully handle missing seeders when using `db:seed` (#1759) --- src/Tempest/Framework/Commands/DatabaseSeedCommand.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Tempest/Framework/Commands/DatabaseSeedCommand.php b/src/Tempest/Framework/Commands/DatabaseSeedCommand.php index 033a83492..1c79e48f3 100644 --- a/src/Tempest/Framework/Commands/DatabaseSeedCommand.php +++ b/src/Tempest/Framework/Commands/DatabaseSeedCommand.php @@ -36,6 +36,12 @@ public function __invoke( return; } + if ($this->seederConfig->seeders === []) { + $this->console->info('No seeders are configured.'); + + return; + } + if (count($this->seederConfig->seeders) === 1) { $this->runSeeder($this->seederConfig->seeders[0], $database); return; From 6dcb56f6f6ef464c9bf123ff3042e506bf1faa78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rk=20Magyar?= Date: Wed, 3 Dec 2025 13:51:58 +0100 Subject: [PATCH 03/17] fix(auth): pass scopes/options to auth URL builder (#1750) --- packages/auth/src/OAuth/GenericOAuthClient.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/auth/src/OAuth/GenericOAuthClient.php b/packages/auth/src/OAuth/GenericOAuthClient.php index e3eba0b2e..302f3277b 100644 --- a/packages/auth/src/OAuth/GenericOAuthClient.php +++ b/packages/auth/src/OAuth/GenericOAuthClient.php @@ -62,7 +62,7 @@ public function buildAuthorizationUrl(array $scopes = [], array $options = []): public function createRedirect(array $scopes = [], array $options = []): Redirect { - $to = $this->buildAuthorizationUrl(); + $to = $this->buildAuthorizationUrl($scopes, $options); $this->session->set($this->sessionKey, $this->provider->getState()); From cebe571690cdcdbb8b54bfce11037b9757b6857e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rk=20Magyar?= Date: Wed, 3 Dec 2025 13:52:39 +0100 Subject: [PATCH 04/17] fix(auth): update outdated authenticatable import (#1752) --- packages/auth/src/Exceptions/AuthenticatableModelWasInvalid.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/auth/src/Exceptions/AuthenticatableModelWasInvalid.php b/packages/auth/src/Exceptions/AuthenticatableModelWasInvalid.php index dcabe5ac0..fc8387ba9 100644 --- a/packages/auth/src/Exceptions/AuthenticatableModelWasInvalid.php +++ b/packages/auth/src/Exceptions/AuthenticatableModelWasInvalid.php @@ -3,7 +3,7 @@ namespace Tempest\Auth\Exceptions; use Exception; -use Tempest\Auth\Authenticatable; +use Tempest\Auth\Authentication\Authenticatable; final class AuthenticatableModelWasInvalid extends Exception implements AuthenticationException { From d99ded04b23ef30f71f8e301a77830513b96edbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rk=20Magyar?= Date: Wed, 3 Dec 2025 13:53:11 +0100 Subject: [PATCH 05/17] refactor(auth): remove double base url call (#1753) --- packages/auth/src/OAuth/Testing/TestingOAuthClient.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/auth/src/OAuth/Testing/TestingOAuthClient.php b/packages/auth/src/OAuth/Testing/TestingOAuthClient.php index 5e00ec4b4..620f5914c 100644 --- a/packages/auth/src/OAuth/Testing/TestingOAuthClient.php +++ b/packages/auth/src/OAuth/Testing/TestingOAuthClient.php @@ -64,11 +64,11 @@ public function buildAuthorizationUrl(array $scopes = [], array $options = []): $this->state = Random\secure_string(16); $provider = $this->config->createProvider(); - $provider->getBaseAuthorizationUrl(); + $baseAuthorizationUrl = $provider->getBaseAuthorizationUrl(); $url = sprintf( '%s/oauth/authorize?redirect_uri=%s&client_id=%s&state=%s', - $this->baseUrl ?? $provider->getBaseAuthorizationUrl(), + $this->baseUrl ?? $baseAuthorizationUrl, $this->redirectUri, $this->clientId, $this->state, From b80308ef9702ab9f08bd6df6ccc151ad45dcec50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rk=20Magyar?= Date: Wed, 3 Dec 2025 13:55:40 +0100 Subject: [PATCH 06/17] feat(cache): make `assertLocked` ensure that the checked lock has an expiration (#1758) --- packages/cache/src/Testing/TestingLock.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/cache/src/Testing/TestingLock.php b/packages/cache/src/Testing/TestingLock.php index 9dcaf26ce..e1c0a873e 100644 --- a/packages/cache/src/Testing/TestingLock.php +++ b/packages/cache/src/Testing/TestingLock.php @@ -66,6 +66,11 @@ public function assertLocked(null|Stringable|string $by = null, null|DateTimeInt $until = DateTime::now()->plus($until); } + Assert::assertNotNull( + actual: $this->expiration, + message: "Expected lock `{$this->key}` to have an expiration, but it has none.", + ); + Assert::assertTrue( condition: $this->expiration->afterOrAtTheSameTime($until), message: "Expected lock `{$this->key}` to expire at or after `{$until}`, but it expires at `{$this->expiration}`.", From 6877cd28b2e685014e79cc4c8a4ce12e8caa8b82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rk=20Magyar?= Date: Wed, 3 Dec 2025 13:56:30 +0100 Subject: [PATCH 07/17] fix(cache): add descriptions to `cache:clear` arguments (#1755) --- packages/cache/src/Commands/CacheClearCommand.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/cache/src/Commands/CacheClearCommand.php b/packages/cache/src/Commands/CacheClearCommand.php index bcf2434a5..e4022e28a 100644 --- a/packages/cache/src/Commands/CacheClearCommand.php +++ b/packages/cache/src/Commands/CacheClearCommand.php @@ -38,9 +38,9 @@ public function __construct( public function __invoke( #[ConsoleArgument(description: 'Name of the tagged cache to clear')] ?string $tag = null, - #[ConsoleCommand(description: 'Whether to clear all caches')] + #[ConsoleArgument(description: 'Whether to clear all caches')] bool $all = false, - #[ConsoleCommand(description: 'Whether to clear internal caches')] + #[ConsoleArgument(description: 'Whether to clear internal caches')] bool $internal = false, ): void { if (! $this->container instanceof GenericContainer) { From 974e7bfa9fb2a938ef60ea9457e1efe0458f952c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rk=20Magyar?= Date: Wed, 3 Dec 2025 13:56:50 +0100 Subject: [PATCH 08/17] refactor(cache): several fixes and improvements for the cache package (#1760) Co-authored-by: Enzo Innocenzi --- .../cache/src/Commands/CacheStatusCommand.php | 2 +- packages/cache/src/GenericCache.php | 7 +++++ .../src/InternalCacheInsightsProvider.php | 7 ++++- .../cache/src/Testing/RestrictedCache.php | 30 +++++++++++-------- 4 files changed, 32 insertions(+), 14 deletions(-) diff --git a/packages/cache/src/Commands/CacheStatusCommand.php b/packages/cache/src/Commands/CacheStatusCommand.php index c7b93c0ac..61e45da51 100644 --- a/packages/cache/src/Commands/CacheStatusCommand.php +++ b/packages/cache/src/Commands/CacheStatusCommand.php @@ -38,7 +38,7 @@ public function __construct( public function __invoke(bool $internal = true): void { if (! $this->container instanceof GenericContainer) { - $this->console->error('Clearing caches is only available when using the default container.'); + $this->console->error('Checking cache status is only available when using the default container.'); return; } diff --git a/packages/cache/src/GenericCache.php b/packages/cache/src/GenericCache.php index df378a6e6..1d59a34f0 100644 --- a/packages/cache/src/GenericCache.php +++ b/packages/cache/src/GenericCache.php @@ -130,6 +130,13 @@ public function get(Stringable|string $key): mixed public function getMany(iterable $key): array { + if (! $this->enabled) { + return Arr\map_with_keys( + array: $key, + map: fn (string|Stringable $key) => yield (string) $key => null, + ); + } + return Arr\map_with_keys( array: $key, map: fn (string|Stringable $key) => yield (string) $key => $this->adapter->getItem((string) $key)->get(), diff --git a/packages/cache/src/InternalCacheInsightsProvider.php b/packages/cache/src/InternalCacheInsightsProvider.php index f6a3fe812..5e8cd9b78 100644 --- a/packages/cache/src/InternalCacheInsightsProvider.php +++ b/packages/cache/src/InternalCacheInsightsProvider.php @@ -4,6 +4,7 @@ use Tempest\Core\ConfigCache; use Tempest\Core\DiscoveryCache; +use Tempest\Core\DiscoveryCacheStrategy; use Tempest\Core\Insight; use Tempest\Core\InsightsProvider; use Tempest\Icon\IconCache; @@ -26,7 +27,11 @@ public function getInsights(): array 'Discovery' => match ($this->discoveryCache->valid) { false => new Insight('Invalid', Insight::ERROR), true => match ($this->discoveryCache->enabled) { - true => new Insight('Enabled', Insight::ERROR), + true => match ($this->discoveryCache->strategy) { + DiscoveryCacheStrategy::FULL => new Insight('Enabled', Insight::SUCCESS), + DiscoveryCacheStrategy::PARTIAL => new Insight('Enabled (partial)', Insight::SUCCESS), + default => null, // INVALID and NONE are handled + }, false => new Insight('Disabled', Insight::WARNING), }, }, diff --git a/packages/cache/src/Testing/RestrictedCache.php b/packages/cache/src/Testing/RestrictedCache.php index 71ca12fc5..eb7e5d465 100644 --- a/packages/cache/src/Testing/RestrictedCache.php +++ b/packages/cache/src/Testing/RestrictedCache.php @@ -10,67 +10,73 @@ use Tempest\Cache\Lock; use Tempest\DateTime\DateTimeInterface; use Tempest\DateTime\Duration; +use UnitEnum; final class RestrictedCache implements Cache { public bool $enabled; public function __construct( - private ?string $tag = null, + private null|string|UnitEnum $tag = null, ) {} + private function resolveTag(): ?string + { + return $this->tag instanceof UnitEnum ? $this->tag->name : $this->tag; + } + public function lock(Stringable|string $key, null|Duration|DateTimeInterface $expiration = null, null|Stringable|string $owner = null): Lock { - throw new CacheUsageWasForbidden($this->tag); + throw new CacheUsageWasForbidden($this->resolveTag()); } public function has(Stringable|string $key): bool { - throw new CacheUsageWasForbidden($this->tag); + throw new CacheUsageWasForbidden($this->resolveTag()); } public function put(Stringable|string $key, mixed $value, null|Duration|DateTimeInterface $expiration = null): CacheItemInterface { - throw new CacheUsageWasForbidden($this->tag); + throw new CacheUsageWasForbidden($this->resolveTag()); } public function putMany(iterable $values, null|Duration|DateTimeInterface $expiration = null): array { - throw new CacheUsageWasForbidden($this->tag); + throw new CacheUsageWasForbidden($this->resolveTag()); } public function increment(Stringable|string $key, int $by = 1): int { - throw new CacheUsageWasForbidden($this->tag); + throw new CacheUsageWasForbidden($this->resolveTag()); } public function decrement(Stringable|string $key, int $by = 1): int { - throw new CacheUsageWasForbidden($this->tag); + throw new CacheUsageWasForbidden($this->resolveTag()); } public function get(Stringable|string $key): mixed { - throw new CacheUsageWasForbidden($this->tag); + throw new CacheUsageWasForbidden($this->resolveTag()); } public function getMany(iterable $key): array { - throw new CacheUsageWasForbidden($this->tag); + throw new CacheUsageWasForbidden($this->resolveTag()); } public function resolve(Stringable|string $key, Closure $callback, null|Duration|DateTimeInterface $expiration = null, ?Duration $stale = null): mixed { - throw new CacheUsageWasForbidden($this->tag); + throw new CacheUsageWasForbidden($this->resolveTag()); } public function remove(Stringable|string $key): void { - throw new CacheUsageWasForbidden($this->tag); + throw new CacheUsageWasForbidden($this->resolveTag()); } public function clear(): void { - throw new CacheUsageWasForbidden($this->tag); + throw new CacheUsageWasForbidden($this->resolveTag()); } } From 3771463ab2353041a94acb03dc9f96c73af1c6ca Mon Sep 17 00:00:00 2001 From: Mark Date: Wed, 3 Dec 2025 21:30:57 +0100 Subject: [PATCH 09/17] fix(view): correct ArrayAccess::offsetExists in TokenCollection --- packages/view/src/Parser/TokenCollection.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/view/src/Parser/TokenCollection.php b/packages/view/src/Parser/TokenCollection.php index a571fb396..b2f63a95a 100644 --- a/packages/view/src/Parser/TokenCollection.php +++ b/packages/view/src/Parser/TokenCollection.php @@ -40,7 +40,7 @@ public function __debugInfo(): array public function offsetExists(mixed $offset): bool { - return $this->tokens[$offset] ?? false; + return isset($this->tokens[$offset]); } public function offsetGet(mixed $offset): mixed From a3300e21b53e753c84028203446564a0e699c4f3 Mon Sep 17 00:00:00 2001 From: Mark Date: Wed, 3 Dec 2025 21:35:23 +0100 Subject: [PATCH 10/17] fix(view): handle invalid base64 in Slot content getter --- packages/view/src/Slot.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/view/src/Slot.php b/packages/view/src/Slot.php index 86a5b0b8b..d0e086cd6 100644 --- a/packages/view/src/Slot.php +++ b/packages/view/src/Slot.php @@ -22,7 +22,7 @@ public function __construct( } public string $content { - get => base64_decode($this->content, true); + get => base64_decode($this->content, strict: true) ?: ''; } public ImmutableArray $exportData { From 9e594a84e64981189aa85f7badc883501499b34d Mon Sep 17 00:00:00 2001 From: Mark Date: Wed, 3 Dec 2025 21:36:20 +0100 Subject: [PATCH 11/17] fix(view): add missing sprintf placeholder for scoped variables --- packages/view/src/Elements/ViewComponentElement.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/view/src/Elements/ViewComponentElement.php b/packages/view/src/Elements/ViewComponentElement.php index 85f97735e..dd49004db 100644 --- a/packages/view/src/Elements/ViewComponentElement.php +++ b/packages/view/src/Elements/ViewComponentElement.php @@ -141,7 +141,7 @@ public function compile(): string ->prepend( // Open the current scope sprintf( - '', + '', $this->dataAttributes->isNotEmpty() ? ', ' . $this->dataAttributes->map(fn (string $_value, string $key) => "\${$key}")->implode(', ') : '', $this->expressionAttributes->isNotEmpty() ? ', ' . $this->expressionAttributes->map(fn (string $_value, string $key) => "\${$key}")->implode(', ') : '', $this->scopedVariables->isNotEmpty() ? ', ' . $this->scopedVariables->map(fn (string $name) => "\${$name}")->implode(', ') : '', @@ -150,7 +150,7 @@ public function compile(): string ->append( // Close and call the current scope sprintf( - 'currentView?->data ?? []) %s %s) ?>', + 'currentView?->data ?? []) %s %s %s) ?>', ViewObjectExporter::export($this->viewComponentAttributes), ViewObjectExporter::export($slots), $this->scopedVariables->isNotEmpty() @@ -162,6 +162,9 @@ public function compile(): string $this->expressionAttributes->isNotEmpty() ? ', ' . $this->expressionAttributes->map(fn (mixed $value, string $key) => "{$key}: " . $value)->implode(', ') : '', + $this->scopedVariables->isNotEmpty() + ? ', ' . $this->scopedVariables->map(fn (string $name) => "{$name}: \${$name}")->implode(', ') + : '', ), ); From 27cc963c292629136614fa3607ffb66ca30358e5 Mon Sep 17 00:00:00 2001 From: Mark Date: Wed, 3 Dec 2025 21:37:30 +0100 Subject: [PATCH 12/17] fix(view): use ENT_QUOTES flag in escape method --- packages/view/src/Renderers/TempestViewRenderer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/view/src/Renderers/TempestViewRenderer.php b/packages/view/src/Renderers/TempestViewRenderer.php index 01c239a1e..f62ee8ff9 100644 --- a/packages/view/src/Renderers/TempestViewRenderer.php +++ b/packages/view/src/Renderers/TempestViewRenderer.php @@ -135,7 +135,7 @@ public function escape(null|string|HtmlString|Stringable $value): string return (string) $value; } - return htmlentities((string) $value); + return htmlentities((string) $value, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); } private function validateView(View $view): void From 30ed8eb619046f653c659929b0edb4e7d6032f0f Mon Sep 17 00:00:00 2001 From: Mark Date: Wed, 3 Dec 2025 21:39:29 +0100 Subject: [PATCH 13/17] fix(view): correct null handling in parser scope property --- packages/view/src/Parser/TempestViewParser.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/view/src/Parser/TempestViewParser.php b/packages/view/src/Parser/TempestViewParser.php index af0452aeb..e88df3c2b 100644 --- a/packages/view/src/Parser/TempestViewParser.php +++ b/packages/view/src/Parser/TempestViewParser.php @@ -9,7 +9,10 @@ final class TempestViewParser private array $scope = []; private ?Token $currentScope { - get => $this->scope[array_key_last($this->scope) ?? ''] ?? null; + get { + $lastKey = array_key_last($this->scope); + return $lastKey !== null ? $this->scope[$lastKey] : null; + } } public function __construct( From f999b2496a856f8f948ed0b7faa8344e860028c5 Mon Sep 17 00:00:00 2001 From: Mark Date: Wed, 3 Dec 2025 22:41:34 +0100 Subject: [PATCH 14/17] refactor(view): use named parameters --- packages/view/src/Renderers/TempestViewRenderer.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/view/src/Renderers/TempestViewRenderer.php b/packages/view/src/Renderers/TempestViewRenderer.php index f62ee8ff9..a71f35461 100644 --- a/packages/view/src/Renderers/TempestViewRenderer.php +++ b/packages/view/src/Renderers/TempestViewRenderer.php @@ -135,7 +135,11 @@ public function escape(null|string|HtmlString|Stringable $value): string return (string) $value; } - return htmlentities((string) $value, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); + return htmlentities( + string: (string) $value, + flags: ENT_QUOTES | ENT_SUBSTITUTE, + encoding: 'UTF-8', + ); } private function validateView(View $view): void From b91d4365668696c5c5e6240f1234d52704d58ee5 Mon Sep 17 00:00:00 2001 From: Mark Date: Fri, 5 Dec 2025 13:13:54 +0100 Subject: [PATCH 15/17] refactor(view): uses array_last to keep oneliner --- packages/view/src/Parser/TempestViewParser.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/view/src/Parser/TempestViewParser.php b/packages/view/src/Parser/TempestViewParser.php index e88df3c2b..77309107b 100644 --- a/packages/view/src/Parser/TempestViewParser.php +++ b/packages/view/src/Parser/TempestViewParser.php @@ -9,10 +9,7 @@ final class TempestViewParser private array $scope = []; private ?Token $currentScope { - get { - $lastKey = array_key_last($this->scope); - return $lastKey !== null ? $this->scope[$lastKey] : null; - } + get => array_last($this->scope); } public function __construct( From eed3a45afb354775f275d2b6da1e288bd0f8f0f4 Mon Sep 17 00:00:00 2001 From: Mark Date: Sun, 14 Dec 2025 03:39:37 +0100 Subject: [PATCH 16/17] trigger workflows From 35b25c3222580efaea4910b406f7bcefaee59139 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rk=20Magyar?= Date: Wed, 17 Dec 2025 08:11:37 +0100 Subject: [PATCH 17/17] fix(view): handle falsy values --- packages/view/src/Slot.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/view/src/Slot.php b/packages/view/src/Slot.php index d0e086cd6..6d65f56f8 100644 --- a/packages/view/src/Slot.php +++ b/packages/view/src/Slot.php @@ -22,7 +22,7 @@ public function __construct( } public string $content { - get => base64_decode($this->content, strict: true) ?: ''; + get => ($d = base64_decode($this->content, strict: true)) === false ? '' : $d; } public ImmutableArray $exportData {