From 70ea73e24dc5e5470dbb05e41887cf9a5cb3fd1b Mon Sep 17 00:00:00 2001 From: Andres Contreras Date: Wed, 21 Oct 2026 10:50:36 -0700 Subject: [PATCH] fix(security): require PHP 8.5.12 and Laravel 13.38 PHP 8.5.12 is the latest release that fixes CVE-2026-109618 and CVE-2026-109619. The previous ^8.3 constraint still allowed unpatched interpreters. --- .github/actions/build-docs/action.yml | 2 +- .github/workflows/ci.yml | 8 +- .github/workflows/release.yml | 4 +- CHANGELOG.md | 15 ++ README.md | 10 +- book/src-es/00-front/00-copyright.md | 2 +- book/src-es/00-front/00-preface.md | 2 +- book/src-es/00-quickstart.md | 6 +- book/src/00-front/00-copyright.md | 2 +- book/src/00-front/00-preface.md | 2 +- book/src/00-quickstart.md | 6 +- composer.json | 50 +++--- docs/architecture.md | 2 +- docs/getting-started.md | 6 +- docs/installation.md | 4 +- docs/publishing.md | 6 +- docs/tutorial.es.md | 2 +- docs/tutorial.md | 2 +- docs/versioning.md | 2 +- packages/actuator/composer.json | 18 +- packages/admin/composer.json | 12 +- packages/admin/src/Web/AdminAction.php | 3 +- packages/autoconfigure/composer.json | 8 +- packages/callbacks/composer.json | 14 +- packages/cli/composer.json | 10 +- packages/config/composer.json | 10 +- packages/container/composer.json | 6 +- packages/context/composer.json | 10 +- packages/cqrs/composer.json | 8 +- packages/data/composer.json | 8 +- .../src/Repository/EloquentRepository.php | 5 +- .../TransactionSynchronizationRegistry.php | 4 + packages/domain/composer.json | 2 +- packages/eda-database/composer.json | 10 +- packages/eda-kafka/composer.json | 8 +- packages/eda-postgres/composer.json | 10 +- packages/eda-rabbitmq/composer.json | 8 +- packages/eda/composer.json | 16 +- packages/eventsourcing/composer.json | 14 +- .../src/Scanner/EventSourcingScanner.php | 2 +- packages/feature-flags/composer.json | 20 +-- .../src/Gating/FeatureFlagRouteGatingPass.php | 4 - .../src/Source/FileFlagSource.php | 5 +- .../tests/Source/FileFlagSourceTest.php | 16 +- packages/firefly/composer.json | 2 +- packages/installer/composer.json | 2 +- packages/kernel/composer.json | 2 +- packages/kernel/src/Version.php | 2 +- packages/messaging/composer.json | 16 +- packages/observability/composer.json | 12 +- packages/openapi/composer.json | 14 +- packages/orchestration/composer.json | 22 +-- packages/resilience/composer.json | 8 +- packages/scheduling-database/composer.json | 8 +- packages/scheduling-postgres/composer.json | 8 +- packages/scheduling/composer.json | 12 +- packages/security-oauth2-client/composer.json | 14 +- packages/security-oauth2-server/composer.json | 14 +- ...EloquentOAuth2AuthorizationServiceTest.php | 2 +- packages/security/composer.json | 18 +- packages/testing/composer.json | 26 +-- packages/validation/composer.json | 8 +- .../src/Constraint/ContainerElementType.php | 12 +- packages/web/composer.json | 16 +- packages/web/src/Dispatch/ResponseFactory.php | 5 +- .../Fixtures/View/RecordingViewFactory.php | 5 +- packages/webhooks/composer.json | 18 +- phpstan.neon.dist | 8 +- samples/lumen/composer.json | 10 +- skeleton/composer.json | 4 +- tests/MinimumPhpSyntaxTest.php | 164 ++---------------- 71 files changed, 345 insertions(+), 451 deletions(-) diff --git a/.github/actions/build-docs/action.yml b/.github/actions/build-docs/action.yml index beb4449f..ff49ba3b 100644 --- a/.github/actions/build-docs/action.yml +++ b/.github/actions/build-docs/action.yml @@ -8,7 +8,7 @@ runs: python-version: '3.12' - uses: shivammathur/setup-php@v2 with: - php-version: '8.4' + php-version: '8.5.12' coverage: none - name: Install rendering dependencies shell: bash diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c5cc3f69..cc8d6eac 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,7 +16,7 @@ jobs: strategy: fail-fast: false matrix: - php: [ '8.3', '8.4', '8.5' ] + php: [ '8.5.12' ] steps: - uses: actions/checkout@v4 - name: Setup PHP @@ -50,7 +50,7 @@ jobs: - name: Setup PHP uses: shivammathur/setup-php@v2 with: - php-version: '8.4' + php-version: '8.5.12' coverage: none tools: composer:v2 - name: Install dependencies @@ -141,7 +141,7 @@ jobs: - name: Setup PHP uses: shivammathur/setup-php@v2 with: - php-version: '8.4' + php-version: '8.5.12' coverage: none tools: composer:v2 extensions: ${{ matrix.extensions }} @@ -215,7 +215,7 @@ jobs: - name: Setup PHP uses: shivammathur/setup-php@v2 with: - php-version: '8.4' + php-version: '8.5.12' coverage: none tools: composer:v2 extensions: rdkafka diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9635a8b5..3128b169 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -17,7 +17,7 @@ jobs: strategy: fail-fast: false matrix: - php: [ '8.3', '8.4', '8.5' ] + php: [ '8.5.12' ] steps: - uses: actions/checkout@v4 - uses: shivammathur/setup-php@v2 @@ -46,7 +46,7 @@ jobs: - uses: actions/checkout@v4 - uses: shivammathur/setup-php@v2 with: - php-version: '8.4' + php-version: '8.5.12' coverage: none tools: composer:v2 - run: composer install --no-interaction --prefer-dist diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e81ffb4..bb248009 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,21 @@ All notable changes to LaraFly are documented here. This project uses CalVer (`Y ## [Unreleased] +## [26.10.4] - 2026-10-21 + +### Security + +- **PHP 8.5.12 is the required runtime.** The constraint moves from `^8.3` to `^8.5.12`, and CI runs on that + release. PHP 8.5.12 is the latest release that fixes CVE-2026-109618 and CVE-2026-109619 (PHP2Shell, + unauthenticated remote code execution). Advisories: https://www.php.net/archive/2026.php and + https://calif.io/php2shell/. + +### Changed + +- **Laravel 13.38 is the minimum.** `illuminate/*` and the skeleton's `laravel/framework` move from `^13.0` + to `^13.38`. Larastan moves from `~3.11.0` to `^3.13.0`, Orchestra Testbench from `^11.1` to `^11.3`, + Laravel Octane from `^2.0` to `^2.22`, and Laravel Pint from `^1.30` to `^1.32`. + ## [26.10.3] - 2026-10-13 ### Changed diff --git a/README.md b/README.md index 4522dfbf..367c3637 100644 --- a/README.md +++ b/README.md @@ -11,10 +11,10 @@

CI Firefly Framework - PHP 8.3+ + PHP 8.5.12+ Laravel 13 License: Apache 2.0 - Version: 26.10.3 + Version: 26.10.4 PHPStan: max Code Style: Pint

@@ -1011,7 +1011,7 @@ set, no CDN request, no npm step, and it still renders in an air-gapped or stric ## Installation -**Requirements:** PHP 8.3+ (8.4 recommended), Composer 2, and an existing (or new) Laravel 13 application — +**Requirements:** PHP 8.5.12+, Composer 2, and an existing (or new) Laravel 13.38 application — LaraFly layers onto Laravel, it is not a standalone runtime. Full details in [Installation](docs/installation.md). @@ -1216,9 +1216,9 @@ model across every runtime: | Requirement | Version | |---|---| -| PHP | 8.3+ (8.4 recommended) | +| PHP | 8.5.12+ | | Composer | 2.x | -| Laravel | 13 | +| Laravel | 13.38+ | | Git | for cloning and for `firefly new`'s optional `git init` | --- diff --git a/book/src-es/00-front/00-copyright.md b/book/src-es/00-front/00-copyright.md index 154e4c77..23bc903e 100644 --- a/book/src-es/00-front/00-copyright.md +++ b/book/src-es/00-front/00-copyright.md @@ -8,7 +8,7 @@ Salvo que lo exija la ley aplicable o se acuerde por escrito, el software y la d **Primera edición.** -Cada listado de código de este libro procede del propio código fuente del framework LaraFly o de `samples/lumen`, la aplicación de monedero digital y libro mayor que se distribuye junto al monorepo del framework como acompañante, y se ha verificado que compila de forma limpia con un compilador PHP 8.3+ real: nada aquí es pseudocódigo ilustrativo. +Cada listado de código de este libro procede del propio código fuente del framework LaraFly o de `samples/lumen`, la aplicación de monedero digital y libro mayor que se distribuye junto al monorepo del framework como acompañante, y se ha verificado que compila de forma limpia con un compilador PHP 8.5.12+ real: nada aquí es pseudocódigo ilustrativo. "LaraFly" es el nombre de trabajo del port a PHP-sobre-Laravel del Firefly Framework. "Laravel" es una marca registrada de Taylor Otwell; esta es una obra independiente y no está afiliada ni respaldada por el proyecto Laravel. diff --git a/book/src-es/00-front/00-preface.md b/book/src-es/00-front/00-preface.md index 231713b6..6ec457af 100644 --- a/book/src-es/00-front/00-preface.md +++ b/book/src-es/00-front/00-preface.md @@ -2,7 +2,7 @@ El PHP empresarial ha significado durante mucho tiempo ensamblar un contenedor de servicios de un paquete, un enrutador de otro, un validador de un tercero y un despachador de eventos de un cuarto — cada uno con su propio idioma de configuración, sus propias convenciones, su propia manera de decir "inyecta esto". Laravel resolvió gran parte de esa fragmentación para las aplicaciones web, pero el PHP hexagonal y orientado a servicios — inyección de dependencias basada en atributos, transacciones declarativas, CQRS, arquitectura orientada a eventos, seguridad a nivel de método, observabilidad de producción — ha seguido siendo cuestión de gustos, convenciones de nombres de carpetas y pegamento hecho a mano. -**LaraFly** cambia eso. Lleva a PHP 8.3+ y Laravel 13 la experiencia cohesionada de convención sobre configuración que Spring Boot dio al mundo Java, construida *sobre* Laravel en lugar de en su lugar: el mismo Eloquent, el mismo contenedor de servicios, el mismo `artisan`, ahora organizados mediante atributos de PHP 8 en componentes con estereotipo, un grafo de dependencias compilado y una tubería de arranque determinista. +**LaraFly** cambia eso. Lleva a PHP 8.5.12+ y Laravel 13.38 la experiencia cohesionada de convención sobre configuración que Spring Boot dio al mundo Java, construida *sobre* Laravel en lugar de en su lugar: el mismo Eloquent, el mismo contenedor de servicios, el mismo `artisan`, ahora organizados mediante atributos de PHP 8 en componentes con estereotipo, un grafo de dependencias compilado y una tubería de arranque determinista. Este libro enseña LaraFly **con ejemplos**. Cada listado de estas páginas no es pseudocódigo ilustrativo: está tomado del propio código fuente distribuido del framework o de `samples/lumen`, una aplicación real y en funcionamiento de monedero digital y libro mayor que se distribuye en el monorepo del framework, que compila, arranca y supera su propia batería de pruebas. Lo que lees es lo que realmente funciona. diff --git a/book/src-es/00-quickstart.md b/book/src-es/00-quickstart.md index 414baa2d..5f59fe0f 100644 --- a/book/src-es/00-quickstart.md +++ b/book/src-es/00-quickstart.md @@ -15,7 +15,7 @@ Esto es un *recorrido*, no la inmersión profunda. El Capítulo 1 argumenta el e LaraFly es un framework PHP construido sobre Laravel 13. Necesitas: -* **PHP 8.3 o más reciente**, con las extensiones que el propio Laravel requiere (`mbstring`, `openssl`, `pdo`, `tokenizer`, `xml`, `ctype`, `json`). +* **PHP 8.5.12 o más reciente**, con las extensiones que el propio Laravel requiere (`mbstring`, `openssl`, `pdo`, `tokenizer`, `xml`, `ctype`, `json`). * **Composer 2**, el gestor de dependencias de PHP. Confirma que ambos están disponibles: @@ -59,10 +59,10 @@ El `composer.json` del andamiaje requiere la biblioteca principal y declara un r ```json "require": { - "php": "^8.3", + "php": "^8.5.12", "firefly/cli": "*@dev", "fireflyframework/larafly": "*@dev", - "laravel/framework": "^13.0" + "laravel/framework": "^13.38" } ``` diff --git a/book/src/00-front/00-copyright.md b/book/src/00-front/00-copyright.md index 14809b5f..f3b6b5e6 100644 --- a/book/src/00-front/00-copyright.md +++ b/book/src/00-front/00-copyright.md @@ -8,7 +8,7 @@ Unless required by applicable law or agreed to in writing, software and document **First edition.** -Every code listing in this book is drawn from the LaraFly framework's own source or from `samples/lumen`, the companion digital-wallet-and-ledger application shipped in the framework's monorepo, and is verified to lint cleanly against a real PHP 8.3+ toolchain — nothing here is illustrative pseudocode. +Every code listing in this book is drawn from the LaraFly framework's own source or from `samples/lumen`, the companion digital-wallet-and-ledger application shipped in the framework's monorepo, and is verified to lint cleanly against a real PHP 8.5.12+ toolchain — nothing here is illustrative pseudocode. "LaraFly" is the working name for the Firefly Framework's PHP-on-Laravel port. "Laravel" is a trademark of Taylor Otwell; this is an independent work and is not affiliated with or endorsed by the Laravel project. diff --git a/book/src/00-front/00-preface.md b/book/src/00-front/00-preface.md index 42167bea..6c02778d 100644 --- a/book/src/00-front/00-preface.md +++ b/book/src/00-front/00-preface.md @@ -2,7 +2,7 @@ Enterprise PHP has long meant assembling a service container from one package, a router from another, a validator from a third, and an event dispatcher from a fourth — each with its own configuration idiom, its own conventions, its own way of saying "inject this." Laravel solved a great deal of that fragmentation for web applications, but hexagonal, service-oriented PHP — attribute-driven dependency injection, declarative transactions, CQRS, event-driven architecture, method-level security, production observability — has stayed a matter of taste, folder-naming conventions, and hand-rolled glue. -**LaraFly** changes that. It brings the cohesive, convention-over-configuration experience Spring Boot gave the Java world to PHP 8.3+ and Laravel 13, built *on* Laravel rather than instead of it: the same Eloquent, the same service container, the same `artisan`, now organized by PHP 8 attributes into stereotyped components, a compiled dependency graph, and a deterministic boot pipeline. +**LaraFly** changes that. It brings the cohesive, convention-over-configuration experience Spring Boot gave the Java world to PHP 8.5.12+ and Laravel 13.38, built *on* Laravel rather than instead of it: the same Eloquent, the same service container, the same `artisan`, now organized by PHP 8 attributes into stereotyped components, a compiled dependency graph, and a deterministic boot pipeline. This book teaches LaraFly **by example**. Every listing in these pages is not illustrative pseudocode — it is lifted from the framework's own shipped source or from `samples/lumen`, a real, running digital-wallet-and-ledger application that ships in the framework's monorepo, compiles, boots, and passes its own test suite. What you read is what actually works. diff --git a/book/src/00-quickstart.md b/book/src/00-quickstart.md index 97478433..345e78e4 100644 --- a/book/src/00-quickstart.md +++ b/book/src/00-quickstart.md @@ -15,7 +15,7 @@ This is a *tour*, not the deep dive. Chapter 1 makes the case for the whole appr LaraFly is a PHP framework built on Laravel 13. You need: -* **PHP 8.3 or newer**, with the extensions Laravel itself requires (`mbstring`, `openssl`, `pdo`, `tokenizer`, `xml`, `ctype`, `json`). +* **PHP 8.5.12 or newer**, with the extensions Laravel itself requires (`mbstring`, `openssl`, `pdo`, `tokenizer`, `xml`, `ctype`, `json`). * **Composer 2**, PHP's dependency manager. Confirm both are available: @@ -59,10 +59,10 @@ The skeleton's `composer.json` requires the root library and records a CLI compo ```json "require": { - "php": "^8.3", + "php": "^8.5.12", "firefly/cli": "*@dev", "fireflyframework/larafly": "*@dev", - "laravel/framework": "^13.0" + "laravel/framework": "^13.38" } ``` diff --git a/composer.json b/composer.json index c408b6da..4878ba10 100644 --- a/composer.json +++ b/composer.json @@ -4,28 +4,28 @@ "type": "library", "license": "Apache-2.0", "require": { - "php": "^8.3", + "php": "^8.5.12", "ext-openssl": "*", "firebase/php-jwt": "^7.0", "guzzlehttp/promises": "^2.0 || ^3.0", - "illuminate/auth": "^13.0", - "illuminate/bus": "^13.0", - "illuminate/cache": "^13.0", - "illuminate/config": "^13.0", - "illuminate/console": "^13.0", - "illuminate/container": "^13.0", - "illuminate/contracts": "^13.0", - "illuminate/cookie": "^13.0", - "illuminate/database": "^13.0", - "illuminate/events": "^13.0", - "illuminate/http": "^13.0", - "illuminate/log": "^13.0", - "illuminate/queue": "^13.0", - "illuminate/routing": "^13.0", - "illuminate/session": "^13.0", - "illuminate/support": "^13.0", - "illuminate/translation": "^13.0", - "illuminate/validation": "^13.0", + "illuminate/auth": "^13.38", + "illuminate/bus": "^13.38", + "illuminate/cache": "^13.38", + "illuminate/config": "^13.38", + "illuminate/console": "^13.38", + "illuminate/container": "^13.38", + "illuminate/contracts": "^13.38", + "illuminate/cookie": "^13.38", + "illuminate/database": "^13.38", + "illuminate/events": "^13.38", + "illuminate/http": "^13.38", + "illuminate/log": "^13.38", + "illuminate/queue": "^13.38", + "illuminate/routing": "^13.38", + "illuminate/session": "^13.38", + "illuminate/support": "^13.38", + "illuminate/translation": "^13.38", + "illuminate/validation": "^13.38", "monolog/monolog": "^3.0", "open-feature/sdk": "^2.3", "php-amqplib/php-amqplib": "^3.7", @@ -40,13 +40,13 @@ "require-dev": { "deptrac/deptrac": "^4.6", "kwn/php-rdkafka-stubs": "^2.2", - "larastan/larastan": "~3.11.0", - "laravel/octane": "^2.0", - "laravel/pint": "^1.30", + "larastan/larastan": "^3.13.0", + "laravel/octane": "^2.22", + "laravel/pint": "^1.32", "open-telemetry/api": "^1.3", "open-telemetry/exporter-otlp": "^1.3", "open-telemetry/sdk": "^1.6", - "orchestra/testbench": "^11.1", + "orchestra/testbench": "^11.3", "pestphp/pest": "^4.5", "pestphp/pest-plugin-browser": "^4.3", "phpstan/phpstan": "^2.1", @@ -228,12 +228,12 @@ "suggest": { "ext-pdo_pgsql": "Required to use the PostgreSQL event transport (firefly.eda.provider=postgres).", "ext-rdkafka": "Required ONLY to activate the Kafka adapter (firefly.eda.provider=kafka). Needs librdkafka >= 1.5.3. Absent = the adapter is inert; the default gate + non-Kafka consumers never need it.", - "illuminate/testing": "^13.0 for the Firefly testing helpers; install with --dev.", + "illuminate/testing": "^13.38 for the Firefly testing helpers; install with --dev.", "open-telemetry/api": "The OpenTelemetry API — required with open-telemetry/sdk for the OpenTelemetryTracer adapter (firefly.observability.tracing.enabled).", "open-telemetry/exporter-otlp": "OTLP span export over http/protobuf or http/json (firefly.observability.tracing.exporter=otlp).", "open-telemetry/sdk": "The OpenTelemetry SDK (TracerProvider, samplers, console exporter) behind OpenTelemetryTracer.", "open-telemetry/transport-grpc": "OTLP export over gRPC (firefly.observability.tracing.otlp.protocol=grpc; needs ext-grpc).", - "orchestra/testbench": "^11.1 for FireflyTestCase and Laravel test applications; install with --dev.", + "orchestra/testbench": "^11.3 for FireflyTestCase and Laravel test applications; install with --dev.", "symfony/var-dumper": "What dd() and dump() are built on; the package registers a caster so a dumped ClientRegistration shows its client secret masked (laravel/framework already requires it).", "testcontainers/testcontainers": "Ephemeral Docker backends for @group integration tests (fireflyConfigFor())." }, diff --git a/docs/architecture.md b/docs/architecture.md index ff285d67..538ca525 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -53,7 +53,7 @@ table, your own bean wins regardless of which provider Laravel instantiated firs `tests/VersionConsistencyTest.php` checks it against the CHANGELOG's latest heading and the README's version badge, so the three cannot drift apart. -Its `composer.json` requires nothing but `php: ^8.3`, and no file under `packages/kernel/src` imports +Its `composer.json` requires nothing but `php: ^8.5.12`, and no file under `packages/kernel/src` imports `Illuminate\*` or `Symfony\*`. That is what makes it safe for a domain package to reuse the exception taxonomy without acquiring a framework dependency — and it is the reason `deptrac.yaml` can write the `Kernel` layer's rule as `~`. diff --git a/docs/getting-started.md b/docs/getting-started.md index f02dd7da..21a94337 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -59,16 +59,16 @@ php artisan firefly:serve ## The library and component requirement `firefly/skeleton` is itself a `type: project` create-project template rather than something you require, and -its `composer.json` asks for the library and a CLI component requirement beside `php: ^8.3` and `laravel/framework: ^13.0`: +its `composer.json` asks for the library and a CLI component requirement beside `php: ^8.5.12` and `laravel/framework: ^13.38`: ```json "require": { - "php": "^8.3", + "php": "^8.5.12", "firefly/cli": "*@dev", "fireflyframework/larafly": "*@dev", - "laravel/framework": "^13.0" + "laravel/framework": "^13.38" }, ``` diff --git a/docs/installation.md b/docs/installation.md index dba27024..474cb914 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -2,9 +2,9 @@ ## Requirements -- **PHP 8.3+** (8.4 recommended). +- **PHP 8.5.12+**. - **Composer 2.** -- **Laravel 13** — LaraFly is a Composer library that layers onto a Laravel application; it is not a +- **Laravel 13.38+** — LaraFly is a Composer library that layers onto a Laravel application; it is not a standalone runtime. ## Quick install diff --git a/docs/publishing.md b/docs/publishing.md index 0d214dae..fe376c86 100644 --- a/docs/publishing.md +++ b/docs/publishing.md @@ -55,7 +55,7 @@ application, requires every component name, boots and compiles a real route, run installs the Lumen consumer through a copied root path repository, and rejects an incompatible component version. Its package repositories exclude `firefly/*` from Packagist so a mirror cannot mask a missing replacement. The consumer directories are retained under the system temporary directory for inspection. -CI runs these package checks on PHP 8.3, 8.4 and 8.5 for PRs to `main` and pushes to `main`. +CI runs these package checks on PHP 8.5.12 for PRs to `main` and pushes to `main`. ## First publication and later releases @@ -67,7 +67,7 @@ CI runs these package checks on PHP 8.3, 8.4 and 8.5 for PRs to `main` and pushe `https://github.com/fireflyframework/fireflyframework-php` and enable the repository webhook. This is a one-time publisher action; subsequent releases use tags from this same repository. 5. Push the new release tag. **Release (single package)** validates the tagged distribution on PHP - 8.3, 8.4 and 8.5, waits for Packagist's Composer metadata (`repo.packagist.org/p2/…`) to list that exact + 8.5.12, waits for Packagist's Composer metadata (`repo.packagist.org/p2/…`) to list that exact commit for the tag, then installs it in a fresh stable consumer without custom repositories and exercises the bundled installer. Only after those checks pass does its final job build both books and create the GitHub release from the changelog with the @@ -81,7 +81,7 @@ CI runs these package checks on PHP 8.3, 8.4 and 8.5 for PRs to `main` and pushe To repeat the public install check locally: ```bash -RELEASE_TAG=v26.10.3 RELEASE_SHA="$(git rev-parse 'v26.10.3^{commit}')" php scripts/check-package-install.php --published +RELEASE_TAG=v26.10.4 RELEASE_SHA="$(git rev-parse 'v26.10.4^{commit}')" php scripts/check-package-install.php --published ``` An unmerged branch or a local consumer check is not a published release. diff --git a/docs/tutorial.es.md b/docs/tutorial.es.md index d41345ea..1f36b214 100644 --- a/docs/tutorial.es.md +++ b/docs/tutorial.es.md @@ -36,7 +36,7 @@ modelo de lectura y un arranque cacheado sin reflexión — la misma arquitectur ## Requisitos previos -- **PHP 8.3+** (se recomienda 8.4) y **Composer 2**. +- **PHP 8.5.12+** y **Composer 2**. - Un **terminal** y un editor de código. - `curl` (o cualquier cliente HTTP) para probar los endpoints a medida que los construyes. diff --git a/docs/tutorial.md b/docs/tutorial.md index 66c7deef..87b600a5 100644 --- a/docs/tutorial.md +++ b/docs/tutorial.md @@ -34,7 +34,7 @@ projected into a read model, and a zero-reflection cached boot — the same arch ## Prerequisites -- **PHP 8.3+** (8.4 recommended) and **Composer 2**. +- **PHP 8.5.12+** and **Composer 2**. - A **terminal** and a code editor. - `curl` (or any HTTP client) to exercise the endpoints as you build them. diff --git a/docs/versioning.md b/docs/versioning.md index c778941f..c663cd06 100644 --- a/docs/versioning.md +++ b/docs/versioning.md @@ -21,7 +21,7 @@ The single place the current version *is* asserted in code is: ```php final class Version { - public const string VERSION = '26.10.3'; + public const string VERSION = '26.10.4'; } ``` diff --git a/packages/actuator/composer.json b/packages/actuator/composer.json index 2a1ad34f..71865b1c 100644 --- a/packages/actuator/composer.json +++ b/packages/actuator/composer.json @@ -20,7 +20,7 @@ "source": "https://github.com/fireflyframework/fireflyframework-php/tree/main/packages/actuator" }, "require": { - "php": "^8.3", + "php": "^8.5.12", "firefly/autoconfigure": "*@dev", "firefly/config": "*@dev", "firefly/container": "*@dev", @@ -28,14 +28,14 @@ "firefly/kernel": "*@dev", "firefly/scheduling": "*@dev", "firefly/web": "*@dev", - "illuminate/console": "^13.0", - "illuminate/container": "^13.0", - "illuminate/contracts": "^13.0", - "illuminate/database": "^13.0", - "illuminate/http": "^13.0", - "illuminate/log": "^13.0", - "illuminate/routing": "^13.0", - "illuminate/support": "^13.0" + "illuminate/console": "^13.38", + "illuminate/container": "^13.38", + "illuminate/contracts": "^13.38", + "illuminate/database": "^13.38", + "illuminate/http": "^13.38", + "illuminate/log": "^13.38", + "illuminate/routing": "^13.38", + "illuminate/support": "^13.38" }, "extra": { "laravel": { diff --git a/packages/admin/composer.json b/packages/admin/composer.json index 82740da6..699f2203 100644 --- a/packages/admin/composer.json +++ b/packages/admin/composer.json @@ -22,7 +22,7 @@ "source": "https://github.com/fireflyframework/fireflyframework-php/tree/main/packages/admin" }, "require": { - "php": "^8.3", + "php": "^8.5.12", "firefly/actuator": "*@dev", "firefly/autoconfigure": "*@dev", "firefly/config": "*@dev", @@ -31,11 +31,11 @@ "firefly/data": "*@dev", "firefly/kernel": "*@dev", "firefly/web": "*@dev", - "illuminate/contracts": "^13.0", - "illuminate/database": "^13.0", - "illuminate/http": "^13.0", - "illuminate/routing": "^13.0", - "illuminate/support": "^13.0" + "illuminate/contracts": "^13.38", + "illuminate/database": "^13.38", + "illuminate/http": "^13.38", + "illuminate/routing": "^13.38", + "illuminate/support": "^13.38" }, "extra": { "laravel": { diff --git a/packages/admin/src/Web/AdminAction.php b/packages/admin/src/Web/AdminAction.php index b6621a35..8c2363a4 100644 --- a/packages/admin/src/Web/AdminAction.php +++ b/packages/admin/src/Web/AdminAction.php @@ -2011,7 +2011,8 @@ private function scalar(mixed $value): string /** @param array $data */ private function render(string $view, array $data, ?AdminPage $page = null): string { - return $this->views->make('firefly-admin::'.$view, [ + // The name is a package view chosen at runtime, which Larastan types as view-string. + return $this->views->make('firefly-admin::'.$view, [ // @phpstan-ignore argument.type ...$data, 'settings' => $this->settings, 'nav' => $this->nav(), diff --git a/packages/autoconfigure/composer.json b/packages/autoconfigure/composer.json index b3840c65..da4f131d 100644 --- a/packages/autoconfigure/composer.json +++ b/packages/autoconfigure/composer.json @@ -20,14 +20,14 @@ "source": "https://github.com/fireflyframework/fireflyframework-php/tree/main/packages/autoconfigure" }, "require": { - "php": "^8.3", + "php": "^8.5.12", "firefly/config": "*@dev", "firefly/container": "*@dev", "firefly/context": "*@dev", "firefly/kernel": "*@dev", - "illuminate/container": "^13.0", - "illuminate/contracts": "^13.0", - "illuminate/support": "^13.0" + "illuminate/container": "^13.38", + "illuminate/contracts": "^13.38", + "illuminate/support": "^13.38" }, "extra": { "laravel": { diff --git a/packages/callbacks/composer.json b/packages/callbacks/composer.json index 0e6fdbd0..1776b8ec 100644 --- a/packages/callbacks/composer.json +++ b/packages/callbacks/composer.json @@ -21,7 +21,7 @@ "source": "https://github.com/fireflyframework/fireflyframework-php/tree/main/packages/callbacks" }, "require": { - "php": "^8.3", + "php": "^8.5.12", "firefly/autoconfigure": "*@dev", "firefly/config": "*@dev", "firefly/container": "*@dev", @@ -29,12 +29,12 @@ "firefly/eda": "*@dev", "firefly/kernel": "*@dev", "firefly/resilience": "*@dev", - "illuminate/console": "^13.0", - "illuminate/container": "^13.0", - "illuminate/contracts": "^13.0", - "illuminate/database": "^13.0", - "illuminate/http": "^13.0", - "illuminate/support": "^13.0", + "illuminate/console": "^13.38", + "illuminate/container": "^13.38", + "illuminate/contracts": "^13.38", + "illuminate/database": "^13.38", + "illuminate/http": "^13.38", + "illuminate/support": "^13.38", "psr/log": "^3.0" }, "extra": { diff --git a/packages/cli/composer.json b/packages/cli/composer.json index 395d4170..c7524592 100644 --- a/packages/cli/composer.json +++ b/packages/cli/composer.json @@ -20,7 +20,7 @@ "source": "https://github.com/fireflyframework/fireflyframework-php/tree/main/packages/cli" }, "require": { - "php": "^8.3", + "php": "^8.5.12", "firefly/actuator": "*@dev", "firefly/autoconfigure": "*@dev", "firefly/config": "*@dev", @@ -41,10 +41,10 @@ "firefly/security-oauth2-server": "*@dev", "firefly/validation": "*@dev", "firefly/web": "*@dev", - "illuminate/console": "^13.0", - "illuminate/container": "^13.0", - "illuminate/contracts": "^13.0", - "illuminate/support": "^13.0" + "illuminate/console": "^13.38", + "illuminate/container": "^13.38", + "illuminate/contracts": "^13.38", + "illuminate/support": "^13.38" }, "extra": { "laravel": { diff --git a/packages/config/composer.json b/packages/config/composer.json index 47983c39..8de38962 100644 --- a/packages/config/composer.json +++ b/packages/config/composer.json @@ -20,13 +20,13 @@ "source": "https://github.com/fireflyframework/fireflyframework-php/tree/main/packages/config" }, "require": { - "php": "^8.3", + "php": "^8.5.12", "firefly/kernel": "*@dev", "firefly/container": "*@dev", - "illuminate/config": "^13.0", - "illuminate/container": "^13.0", - "illuminate/contracts": "^13.0", - "illuminate/support": "^13.0", + "illuminate/config": "^13.38", + "illuminate/container": "^13.38", + "illuminate/contracts": "^13.38", + "illuminate/support": "^13.38", "symfony/expression-language": "^7.4|^8.0" }, "extra": { diff --git a/packages/container/composer.json b/packages/container/composer.json index 177389be..8f256001 100644 --- a/packages/container/composer.json +++ b/packages/container/composer.json @@ -20,10 +20,10 @@ "source": "https://github.com/fireflyframework/fireflyframework-php/tree/main/packages/container" }, "require": { - "php": "^8.3", + "php": "^8.5.12", "firefly/kernel": "*@dev", - "illuminate/container": "^13.0", - "illuminate/contracts": "^13.0", + "illuminate/container": "^13.38", + "illuminate/contracts": "^13.38", "symfony/expression-language": "^7.4|^8.0" }, "extra": { diff --git a/packages/context/composer.json b/packages/context/composer.json index 0d096883..06e9b437 100644 --- a/packages/context/composer.json +++ b/packages/context/composer.json @@ -20,14 +20,14 @@ "source": "https://github.com/fireflyframework/fireflyframework-php/tree/main/packages/context" }, "require": { - "php": "^8.3", + "php": "^8.5.12", "firefly/config": "*@dev", "firefly/container": "*@dev", "firefly/kernel": "*@dev", - "illuminate/container": "^13.0", - "illuminate/contracts": "^13.0", - "illuminate/events": "^13.0", - "illuminate/support": "^13.0" + "illuminate/container": "^13.38", + "illuminate/contracts": "^13.38", + "illuminate/events": "^13.38", + "illuminate/support": "^13.38" }, "extra": { "branch-alias": { diff --git a/packages/cqrs/composer.json b/packages/cqrs/composer.json index a5699ef1..589bc551 100644 --- a/packages/cqrs/composer.json +++ b/packages/cqrs/composer.json @@ -20,7 +20,7 @@ "source": "https://github.com/fireflyframework/fireflyframework-php/tree/main/packages/cqrs" }, "require": { - "php": "^8.3", + "php": "^8.5.12", "firefly/autoconfigure": "*@dev", "firefly/config": "*@dev", "firefly/container": "*@dev", @@ -29,9 +29,9 @@ "firefly/eda": "*@dev", "firefly/kernel": "*@dev", "firefly/validation": "*@dev", - "illuminate/container": "^13.0", - "illuminate/contracts": "^13.0", - "illuminate/support": "^13.0" + "illuminate/container": "^13.38", + "illuminate/contracts": "^13.38", + "illuminate/support": "^13.38" }, "extra": { "laravel": { diff --git a/packages/data/composer.json b/packages/data/composer.json index 9ba756c3..d045eed6 100644 --- a/packages/data/composer.json +++ b/packages/data/composer.json @@ -21,16 +21,16 @@ "source": "https://github.com/fireflyframework/fireflyframework-php/tree/main/packages/data" }, "require": { - "php": "^8.3", + "php": "^8.5.12", "firefly/autoconfigure": "*@dev", "firefly/config": "*@dev", "firefly/container": "*@dev", "firefly/context": "*@dev", "firefly/domain": "*@dev", "firefly/kernel": "*@dev", - "illuminate/contracts": "^13.0", - "illuminate/database": "^13.0", - "illuminate/support": "^13.0" + "illuminate/contracts": "^13.38", + "illuminate/database": "^13.38", + "illuminate/support": "^13.38" }, "extra": { "laravel": { diff --git a/packages/data/src/Repository/EloquentRepository.php b/packages/data/src/Repository/EloquentRepository.php index 2ce8134d..573a54a5 100644 --- a/packages/data/src/Repository/EloquentRepository.php +++ b/packages/data/src/Repository/EloquentRepository.php @@ -989,7 +989,10 @@ protected function repositoryClass(): string */ protected function query(): Builder { - return ($this->model)::query(); + /** @var Builder $query */ + $query = ($this->model)::query(); + + return $query; } /** diff --git a/packages/data/src/Transaction/TransactionSynchronizationRegistry.php b/packages/data/src/Transaction/TransactionSynchronizationRegistry.php index fa933513..d0b38f69 100644 --- a/packages/data/src/Transaction/TransactionSynchronizationRegistry.php +++ b/packages/data/src/Transaction/TransactionSynchronizationRegistry.php @@ -119,6 +119,10 @@ public function register(TransactionPhase $phase, Closure $callback): void private function queueBeforeCommit(Connection $connection, Closure $callback): void { $name = $connection->getName(); + if ($name === null) { + throw new ConfigurationException('A BEFORE_COMMIT listener needs a named database connection.'); + } + $this->beforeCommit[$name][] = [$connection->transactionLevel(), $callback]; if (isset($this->subscribed[$name])) { diff --git a/packages/domain/composer.json b/packages/domain/composer.json index c2abbc13..6bd794df 100644 --- a/packages/domain/composer.json +++ b/packages/domain/composer.json @@ -20,7 +20,7 @@ "source": "https://github.com/fireflyframework/fireflyframework-php/tree/main/packages/domain" }, "require": { - "php": "^8.3" + "php": "^8.5.12" }, "extra": { "branch-alias": { diff --git a/packages/eda-database/composer.json b/packages/eda-database/composer.json index 3f22da00..350b21ad 100644 --- a/packages/eda-database/composer.json +++ b/packages/eda-database/composer.json @@ -21,7 +21,7 @@ "source": "https://github.com/fireflyframework/fireflyframework-php/tree/main/packages/eda-database" }, "require": { - "php": "^8.3", + "php": "^8.5.12", "firefly/actuator": "*@dev", "firefly/autoconfigure": "*@dev", "firefly/config": "*@dev", @@ -32,10 +32,10 @@ "firefly/domain": "*@dev", "firefly/eda": "*@dev", "firefly/kernel": "*@dev", - "illuminate/console": "^13.0", - "illuminate/contracts": "^13.0", - "illuminate/database": "^13.0", - "illuminate/support": "^13.0" + "illuminate/console": "^13.38", + "illuminate/contracts": "^13.38", + "illuminate/database": "^13.38", + "illuminate/support": "^13.38" }, "extra": { "laravel": { diff --git a/packages/eda-kafka/composer.json b/packages/eda-kafka/composer.json index 3dc80157..089c84c7 100644 --- a/packages/eda-kafka/composer.json +++ b/packages/eda-kafka/composer.json @@ -20,7 +20,7 @@ "source": "https://github.com/fireflyframework/fireflyframework-php/tree/main/packages/eda-kafka" }, "require": { - "php": "^8.3", + "php": "^8.5.12", "firefly/actuator": "*@dev", "firefly/autoconfigure": "*@dev", "firefly/config": "*@dev", @@ -29,9 +29,9 @@ "firefly/eda": "*@dev", "firefly/kernel": "*@dev", "firefly/messaging": "*@dev", - "illuminate/console": "^13.0", - "illuminate/contracts": "^13.0", - "illuminate/support": "^13.0", + "illuminate/console": "^13.38", + "illuminate/contracts": "^13.38", + "illuminate/support": "^13.38", "psr/log": "^3.0" }, "suggest": { diff --git a/packages/eda-postgres/composer.json b/packages/eda-postgres/composer.json index ffa76fd2..7280a65c 100644 --- a/packages/eda-postgres/composer.json +++ b/packages/eda-postgres/composer.json @@ -21,7 +21,7 @@ "source": "https://github.com/fireflyframework/fireflyframework-php/tree/main/packages/eda-postgres" }, "require": { - "php": "^8.3", + "php": "^8.5.12", "ext-pdo_pgsql": "*", "firefly/actuator": "*@dev", "firefly/autoconfigure": "*@dev", @@ -34,10 +34,10 @@ "firefly/eda": "*@dev", "firefly/eda-database": "*@dev", "firefly/kernel": "*@dev", - "illuminate/console": "^13.0", - "illuminate/contracts": "^13.0", - "illuminate/database": "^13.0", - "illuminate/support": "^13.0" + "illuminate/console": "^13.38", + "illuminate/contracts": "^13.38", + "illuminate/database": "^13.38", + "illuminate/support": "^13.38" }, "extra": { "laravel": { diff --git a/packages/eda-rabbitmq/composer.json b/packages/eda-rabbitmq/composer.json index 6b4291f7..a28b6b6b 100644 --- a/packages/eda-rabbitmq/composer.json +++ b/packages/eda-rabbitmq/composer.json @@ -20,7 +20,7 @@ "source": "https://github.com/fireflyframework/fireflyframework-php/tree/main/packages/eda-rabbitmq" }, "require": { - "php": "^8.3", + "php": "^8.5.12", "firefly/actuator": "*@dev", "firefly/autoconfigure": "*@dev", "firefly/config": "*@dev", @@ -29,9 +29,9 @@ "firefly/eda": "*@dev", "firefly/kernel": "*@dev", "firefly/messaging": "*@dev", - "illuminate/console": "^13.0", - "illuminate/contracts": "^13.0", - "illuminate/support": "^13.0", + "illuminate/console": "^13.38", + "illuminate/contracts": "^13.38", + "illuminate/support": "^13.38", "php-amqplib/php-amqplib": "^3.7", "psr/log": "^3.0" }, diff --git a/packages/eda/composer.json b/packages/eda/composer.json index f936c1bc..0f9da469 100644 --- a/packages/eda/composer.json +++ b/packages/eda/composer.json @@ -20,19 +20,19 @@ "source": "https://github.com/fireflyframework/fireflyframework-php/tree/main/packages/eda" }, "require": { - "php": "^8.3", + "php": "^8.5.12", "firefly/autoconfigure": "*@dev", "firefly/config": "*@dev", "firefly/container": "*@dev", "firefly/context": "*@dev", "firefly/kernel": "*@dev", - "illuminate/bus": "^13.0", - "illuminate/console": "^13.0", - "illuminate/container": "^13.0", - "illuminate/contracts": "^13.0", - "illuminate/database": "^13.0", - "illuminate/queue": "^13.0", - "illuminate/support": "^13.0" + "illuminate/bus": "^13.38", + "illuminate/console": "^13.38", + "illuminate/container": "^13.38", + "illuminate/contracts": "^13.38", + "illuminate/database": "^13.38", + "illuminate/queue": "^13.38", + "illuminate/support": "^13.38" }, "extra": { "laravel": { diff --git a/packages/eventsourcing/composer.json b/packages/eventsourcing/composer.json index 949cd6de..1ab3d9e4 100644 --- a/packages/eventsourcing/composer.json +++ b/packages/eventsourcing/composer.json @@ -23,7 +23,7 @@ "source": "https://github.com/fireflyframework/fireflyframework-php/tree/main/packages/eventsourcing" }, "require": { - "php": "^8.3", + "php": "^8.5.12", "firefly/actuator": "*@dev", "firefly/autoconfigure": "*@dev", "firefly/config": "*@dev", @@ -37,12 +37,12 @@ "firefly/orchestration": "*@dev", "firefly/resilience": "*@dev", "firefly/scheduling": "*@dev", - "illuminate/console": "^13.0", - "illuminate/container": "^13.0", - "illuminate/contracts": "^13.0", - "illuminate/database": "^13.0", - "illuminate/log": "^13.0", - "illuminate/support": "^13.0", + "illuminate/console": "^13.38", + "illuminate/container": "^13.38", + "illuminate/contracts": "^13.38", + "illuminate/database": "^13.38", + "illuminate/log": "^13.38", + "illuminate/support": "^13.38", "psr/log": "^3.0" }, "extra": { diff --git a/packages/eventsourcing/src/Scanner/EventSourcingScanner.php b/packages/eventsourcing/src/Scanner/EventSourcingScanner.php index adb24ad3..d7425f8c 100644 --- a/packages/eventsourcing/src/Scanner/EventSourcingScanner.php +++ b/packages/eventsourcing/src/Scanner/EventSourcingScanner.php @@ -88,7 +88,7 @@ public function scan(array $psr4): EventSourcingManifest * A convention-mapped event stores its public instance properties and is rebuilt from its constructor parameters, * so both sets must name the same fields, or what is appended cannot be read back as it was written. * - * @param ReflectionClass $reflection + * @param ReflectionClass $reflection */ private function assertRoundTrips(ReflectionClass $reflection): void { diff --git a/packages/feature-flags/composer.json b/packages/feature-flags/composer.json index 4bc031f8..93c64141 100644 --- a/packages/feature-flags/composer.json +++ b/packages/feature-flags/composer.json @@ -22,7 +22,7 @@ "source": "https://github.com/fireflyframework/fireflyframework-php/tree/main/packages/feature-flags" }, "require": { - "php": "^8.3", + "php": "^8.5.12", "firefly/actuator": "*@dev", "firefly/autoconfigure": "*@dev", "firefly/config": "*@dev", @@ -32,15 +32,15 @@ "firefly/kernel": "*@dev", "firefly/resilience": "*@dev", "firefly/web": "*@dev", - "illuminate/cache": "^13.0", - "illuminate/console": "^13.0", - "illuminate/container": "^13.0", - "illuminate/contracts": "^13.0", - "illuminate/database": "^13.0", - "illuminate/events": "^13.0", - "illuminate/http": "^13.0", - "illuminate/routing": "^13.0", - "illuminate/support": "^13.0", + "illuminate/cache": "^13.38", + "illuminate/console": "^13.38", + "illuminate/container": "^13.38", + "illuminate/contracts": "^13.38", + "illuminate/database": "^13.38", + "illuminate/events": "^13.38", + "illuminate/http": "^13.38", + "illuminate/routing": "^13.38", + "illuminate/support": "^13.38", "open-feature/sdk": "^2.3", "psr/log": "^3.0", "symfony/yaml": "^7.4|^8.0" diff --git a/packages/feature-flags/src/Gating/FeatureFlagRouteGatingPass.php b/packages/feature-flags/src/Gating/FeatureFlagRouteGatingPass.php index 63ff8cdf..43ea5673 100644 --- a/packages/feature-flags/src/Gating/FeatureFlagRouteGatingPass.php +++ b/packages/feature-flags/src/Gating/FeatureFlagRouteGatingPass.php @@ -62,10 +62,6 @@ public function run(BootContext $context): void continue; } foreach ($route->methods() as $verb) { - if (! is_string($verb)) { - continue; - } - $routes[$verb.' '.$route->uri()] = $route; } } diff --git a/packages/feature-flags/src/Source/FileFlagSource.php b/packages/feature-flags/src/Source/FileFlagSource.php index 90882380..567ecab3 100644 --- a/packages/feature-flags/src/Source/FileFlagSource.php +++ b/packages/feature-flags/src/Source/FileFlagSource.php @@ -129,8 +129,9 @@ private static function decode(string $path, string $extension, string $contents try { $parsed = Yaml::parse($contents, self::YAML_FLAGS); } catch (Throwable $failure) { - // Not only the documented ParseException: Symfony Yaml stores a block-mapping key as a stdClass property, - // and a key starting with "\0" throws an Error. Anything parsing throws means the file is not valid YAML. + // Not only the documented ParseException: a mapping key starting with "\0" is refused + // (a block key throws an Error; a flow key is rejected under PARSE_OBJECT_FOR_MAP). + // Anything parsing throws means the file is not valid YAML. throw self::unparsable($path, 'YAML', $failure); } diff --git a/packages/feature-flags/tests/Source/FileFlagSourceTest.php b/packages/feature-flags/tests/Source/FileFlagSourceTest.php index 2b1854a1..652a1d0c 100644 --- a/packages/feature-flags/tests/Source/FileFlagSourceTest.php +++ b/packages/feature-flags/tests/Source/FileFlagSourceTest.php @@ -137,6 +137,7 @@ function featureFlagsFileRefusal(string $path): ?InvalidFlagDefinition 'a PHP constant in YAML' => ['yaml', "metadata:\n limit: !php/const PHP_INT_MAX\n", 'YAML'], 'a date as a flow-mapping key in YAML' => ['yaml', "metadata: {2024-01-01: launch}\n", 'YAML'], 'a YAML block key starting with NUL, which Symfony Yaml fails on with an Error' => ['yaml', "metadata:\n \"\\0a\": x\n", 'YAML'], + 'a YAML flow key starting with NUL, which Symfony Yaml rejects under PARSE_OBJECT_FOR_MAP' => ['yaml', "metadata: {\"\\0a\": x}\n", 'YAML'], ]); it('reads an absent document as no flags: JSON null, an empty or comment-only YAML file', function (string $extension, string $contents): void { @@ -221,13 +222,7 @@ function featureFlagsFileRefusal(string $path): ?InvalidFlagDefinition ->and($reloaded?->revision)->toEndWith('-'.strlen($after)); }); -it('keeps a YAML flow-mapping key starting with NUL as Symfony Yaml reads it', function (): void { - $path = TemporaryFlagFiles::write('yaml', "metadata: {\"\\0a\": x}\n"); - - expect(featureFlagsFileSource($path)->load(null)?->document->metadata)->toBe(["\0a" => 'x']); -}); - -it('retains a good YAML cache across invalid binary refresh and fresh worker boot then accepts NUL names', function (): void { +it('retains a good YAML cache across an invalid binary refresh and a flow key starting with NUL', function (): void { $good = "flags:\n a:\n state: ENABLED\n variants: {on: true, off: false}\n defaultVariant: on\n"; $path = TemporaryFlagFiles::write('yaml', $good); $cache = new Illuminate\Cache\Repository(new ArrayStore); @@ -250,7 +245,8 @@ function featureFlagsFileRefusal(string $path): ?InvalidFlagDefinition ->and($events->events)->toHaveCount(1); TemporaryFlagFiles::rewrite($path, $good." metadata: {\"\\0name\": value}\n", TemporaryFlagFiles::MTIME + 20); $fresh->refresh(force: true); - expect($fresh->document()->flag('a')?->metadata())->toBe(["\0name" => 'value']) - ->and($fresh->states()[0]->status())->toBe('UP') - ->and($events->events)->toHaveCount(2); + expect($fresh->document()->flag('a')?->metadata())->toBe([]) + ->and($fresh->states()[0]->status())->toBe('STALE') + ->and($fresh->states()[0]->revision)->toBe($revision) + ->and($events->events)->toHaveCount(1); }); diff --git a/packages/firefly/composer.json b/packages/firefly/composer.json index 95cee722..80970a05 100644 --- a/packages/firefly/composer.json +++ b/packages/firefly/composer.json @@ -21,7 +21,7 @@ "source": "https://github.com/fireflyframework/fireflyframework-php/tree/main/packages/firefly" }, "require": { - "php": "^8.3", + "php": "^8.5.12", "firefly/actuator": "*@dev", "firefly/admin": "*@dev", "firefly/autoconfigure": "*@dev", diff --git a/packages/installer/composer.json b/packages/installer/composer.json index d7679b82..d79a1c38 100644 --- a/packages/installer/composer.json +++ b/packages/installer/composer.json @@ -25,7 +25,7 @@ }, "require": { "firefly/kernel": "*@dev", - "php": "^8.3", + "php": "^8.5.12", "symfony/console": "^7.4|^8.0", "symfony/process": "^7.4|^8.0" }, diff --git a/packages/kernel/composer.json b/packages/kernel/composer.json index 22779b9b..f08a6d7f 100644 --- a/packages/kernel/composer.json +++ b/packages/kernel/composer.json @@ -20,7 +20,7 @@ "source": "https://github.com/fireflyframework/fireflyframework-php/tree/main/packages/kernel" }, "require": { - "php": "^8.3" + "php": "^8.5.12" }, "extra": { "branch-alias": { diff --git a/packages/kernel/src/Version.php b/packages/kernel/src/Version.php index 8788ccc0..0174a050 100644 --- a/packages/kernel/src/Version.php +++ b/packages/kernel/src/Version.php @@ -15,5 +15,5 @@ */ final class Version { - public const string VERSION = '26.10.3'; + public const string VERSION = '26.10.4'; } diff --git a/packages/messaging/composer.json b/packages/messaging/composer.json index 11aa6cac..a854a0fd 100644 --- a/packages/messaging/composer.json +++ b/packages/messaging/composer.json @@ -20,19 +20,19 @@ "source": "https://github.com/fireflyframework/fireflyframework-php/tree/main/packages/messaging" }, "require": { - "php": "^8.3", + "php": "^8.5.12", "firefly/autoconfigure": "*@dev", "firefly/config": "*@dev", "firefly/container": "*@dev", "firefly/context": "*@dev", "firefly/kernel": "*@dev", - "illuminate/bus": "^13.0", - "illuminate/console": "^13.0", - "illuminate/container": "^13.0", - "illuminate/contracts": "^13.0", - "illuminate/database": "^13.0", - "illuminate/queue": "^13.0", - "illuminate/support": "^13.0" + "illuminate/bus": "^13.38", + "illuminate/console": "^13.38", + "illuminate/container": "^13.38", + "illuminate/contracts": "^13.38", + "illuminate/database": "^13.38", + "illuminate/queue": "^13.38", + "illuminate/support": "^13.38" }, "extra": { "laravel": { diff --git a/packages/observability/composer.json b/packages/observability/composer.json index 33e3612e..50dcaa5b 100644 --- a/packages/observability/composer.json +++ b/packages/observability/composer.json @@ -21,7 +21,7 @@ "source": "https://github.com/fireflyframework/fireflyframework-php/tree/main/packages/observability" }, "require": { - "php": "^8.3", + "php": "^8.5.12", "firefly/actuator": "*@dev", "firefly/autoconfigure": "*@dev", "firefly/config": "*@dev", @@ -36,11 +36,11 @@ "firefly/resilience": "*@dev", "firefly/web": "*@dev", "guzzlehttp/promises": "^2.0 || ^3.0", - "illuminate/container": "^13.0", - "illuminate/contracts": "^13.0", - "illuminate/http": "^13.0", - "illuminate/log": "^13.0", - "illuminate/support": "^13.0", + "illuminate/container": "^13.38", + "illuminate/contracts": "^13.38", + "illuminate/http": "^13.38", + "illuminate/log": "^13.38", + "illuminate/support": "^13.38", "monolog/monolog": "^3.0", "psr/http-message": "^1.0 || ^2.0" }, diff --git a/packages/openapi/composer.json b/packages/openapi/composer.json index afbe37b4..d90601ca 100644 --- a/packages/openapi/composer.json +++ b/packages/openapi/composer.json @@ -21,7 +21,7 @@ "source": "https://github.com/fireflyframework/fireflyframework-php/tree/main/packages/openapi" }, "require": { - "php": "^8.3", + "php": "^8.5.12", "firefly/autoconfigure": "*@dev", "firefly/config": "*@dev", "firefly/container": "*@dev", @@ -29,12 +29,12 @@ "firefly/kernel": "*@dev", "firefly/validation": "*@dev", "firefly/web": "*@dev", - "illuminate/console": "^13.0", - "illuminate/container": "^13.0", - "illuminate/contracts": "^13.0", - "illuminate/http": "^13.0", - "illuminate/routing": "^13.0", - "illuminate/support": "^13.0", + "illuminate/console": "^13.38", + "illuminate/container": "^13.38", + "illuminate/contracts": "^13.38", + "illuminate/http": "^13.38", + "illuminate/routing": "^13.38", + "illuminate/support": "^13.38", "swagger-api/swagger-ui": "^5.17" }, "extra": { diff --git a/packages/orchestration/composer.json b/packages/orchestration/composer.json index 8c08bbc3..0f485b03 100644 --- a/packages/orchestration/composer.json +++ b/packages/orchestration/composer.json @@ -23,7 +23,7 @@ "source": "https://github.com/fireflyframework/fireflyframework-php/tree/main/packages/orchestration" }, "require": { - "php": "^8.3", + "php": "^8.5.12", "firefly/actuator": "*@dev", "firefly/autoconfigure": "*@dev", "firefly/config": "*@dev", @@ -33,16 +33,16 @@ "firefly/kernel": "*@dev", "firefly/resilience": "*@dev", "firefly/scheduling": "*@dev", - "illuminate/bus": "^13.0", - "illuminate/cache": "^13.0", - "illuminate/console": "^13.0", - "illuminate/container": "^13.0", - "illuminate/contracts": "^13.0", - "illuminate/database": "^13.0", - "illuminate/http": "^13.0", - "illuminate/queue": "^13.0", - "illuminate/routing": "^13.0", - "illuminate/support": "^13.0", + "illuminate/bus": "^13.38", + "illuminate/cache": "^13.38", + "illuminate/console": "^13.38", + "illuminate/container": "^13.38", + "illuminate/contracts": "^13.38", + "illuminate/database": "^13.38", + "illuminate/http": "^13.38", + "illuminate/queue": "^13.38", + "illuminate/routing": "^13.38", + "illuminate/support": "^13.38", "psr/log": "^3.0", "symfony/expression-language": "^7.4|^8.0" }, diff --git a/packages/resilience/composer.json b/packages/resilience/composer.json index 60376032..4c5fc6eb 100644 --- a/packages/resilience/composer.json +++ b/packages/resilience/composer.json @@ -20,16 +20,16 @@ "source": "https://github.com/fireflyframework/fireflyframework-php/tree/main/packages/resilience" }, "require": { - "php": "^8.3", + "php": "^8.5.12", "firefly/autoconfigure": "*@dev", "firefly/config": "*@dev", "firefly/container": "*@dev", "firefly/context": "*@dev", "firefly/data": "*@dev", "firefly/kernel": "*@dev", - "illuminate/cache": "^13.0", - "illuminate/contracts": "^13.0", - "illuminate/support": "^13.0" + "illuminate/cache": "^13.38", + "illuminate/contracts": "^13.38", + "illuminate/support": "^13.38" }, "extra": { "laravel": { diff --git a/packages/scheduling-database/composer.json b/packages/scheduling-database/composer.json index 029607a3..2ace963f 100644 --- a/packages/scheduling-database/composer.json +++ b/packages/scheduling-database/composer.json @@ -21,16 +21,16 @@ "source": "https://github.com/fireflyframework/fireflyframework-php/tree/main/packages/scheduling-database" }, "require": { - "php": "^8.3", + "php": "^8.5.12", "firefly/autoconfigure": "*@dev", "firefly/config": "*@dev", "firefly/container": "*@dev", "firefly/context": "*@dev", "firefly/kernel": "*@dev", "firefly/scheduling": "*@dev", - "illuminate/contracts": "^13.0", - "illuminate/database": "^13.0", - "illuminate/support": "^13.0" + "illuminate/contracts": "^13.38", + "illuminate/database": "^13.38", + "illuminate/support": "^13.38" }, "extra": { "laravel": { diff --git a/packages/scheduling-postgres/composer.json b/packages/scheduling-postgres/composer.json index 2c1c5149..f7ee880f 100644 --- a/packages/scheduling-postgres/composer.json +++ b/packages/scheduling-postgres/composer.json @@ -21,16 +21,16 @@ "source": "https://github.com/fireflyframework/fireflyframework-php/tree/main/packages/scheduling-postgres" }, "require": { - "php": "^8.3", + "php": "^8.5.12", "firefly/autoconfigure": "*@dev", "firefly/config": "*@dev", "firefly/container": "*@dev", "firefly/context": "*@dev", "firefly/kernel": "*@dev", "firefly/scheduling": "*@dev", - "illuminate/contracts": "^13.0", - "illuminate/database": "^13.0", - "illuminate/support": "^13.0" + "illuminate/contracts": "^13.38", + "illuminate/database": "^13.38", + "illuminate/support": "^13.38" }, "extra": { "laravel": { diff --git a/packages/scheduling/composer.json b/packages/scheduling/composer.json index 7b37612a..faf20593 100644 --- a/packages/scheduling/composer.json +++ b/packages/scheduling/composer.json @@ -20,18 +20,18 @@ "source": "https://github.com/fireflyframework/fireflyframework-php/tree/main/packages/scheduling" }, "require": { - "php": "^8.3", + "php": "^8.5.12", "firefly/autoconfigure": "*@dev", "firefly/config": "*@dev", "firefly/container": "*@dev", "firefly/context": "*@dev", "firefly/kernel": "*@dev", "firefly/resilience": "*@dev", - "illuminate/cache": "^13.0", - "illuminate/console": "^13.0", - "illuminate/container": "^13.0", - "illuminate/contracts": "^13.0", - "illuminate/support": "^13.0" + "illuminate/cache": "^13.38", + "illuminate/console": "^13.38", + "illuminate/container": "^13.38", + "illuminate/contracts": "^13.38", + "illuminate/support": "^13.38" }, "extra": { "laravel": { diff --git a/packages/security-oauth2-client/composer.json b/packages/security-oauth2-client/composer.json index a73f5b34..70f7a42d 100644 --- a/packages/security-oauth2-client/composer.json +++ b/packages/security-oauth2-client/composer.json @@ -22,7 +22,7 @@ "source": "https://github.com/fireflyframework/fireflyframework-php/tree/main/packages/security-oauth2-client" }, "require": { - "php": "^8.3", + "php": "^8.5.12", "firebase/php-jwt": "^7.0", "firefly/autoconfigure": "*@dev", "firefly/config": "*@dev", @@ -31,12 +31,12 @@ "firefly/kernel": "*@dev", "firefly/security": "*@dev", "firefly/web": "*@dev", - "illuminate/container": "^13.0", - "illuminate/contracts": "^13.0", - "illuminate/http": "^13.0", - "illuminate/routing": "^13.0", - "illuminate/session": "^13.0", - "illuminate/support": "^13.0" + "illuminate/container": "^13.38", + "illuminate/contracts": "^13.38", + "illuminate/http": "^13.38", + "illuminate/routing": "^13.38", + "illuminate/session": "^13.38", + "illuminate/support": "^13.38" }, "suggest": { "symfony/var-dumper": "What dd() and dump() are built on; the package registers a caster so a dumped ClientRegistration shows its client secret masked (laravel/framework already requires it)." diff --git a/packages/security-oauth2-server/composer.json b/packages/security-oauth2-server/composer.json index 08cdb0f7..e19a6cc9 100644 --- a/packages/security-oauth2-server/composer.json +++ b/packages/security-oauth2-server/composer.json @@ -22,7 +22,7 @@ "source": "https://github.com/fireflyframework/fireflyframework-php/tree/main/packages/security-oauth2-server" }, "require": { - "php": "^8.3", + "php": "^8.5.12", "ext-openssl": "*", "firebase/php-jwt": "^7.0", "firefly/actuator": "*@dev", @@ -36,12 +36,12 @@ "firefly/scheduling": "*@dev", "firefly/security": "*@dev", "firefly/web": "*@dev", - "illuminate/container": "^13.0", - "illuminate/contracts": "^13.0", - "illuminate/database": "^13.0", - "illuminate/http": "^13.0", - "illuminate/session": "^13.0", - "illuminate/support": "^13.0", + "illuminate/container": "^13.38", + "illuminate/contracts": "^13.38", + "illuminate/database": "^13.38", + "illuminate/http": "^13.38", + "illuminate/session": "^13.38", + "illuminate/support": "^13.38", "psr/log": "^3.0" }, "require-dev": { diff --git a/packages/security-oauth2-server/tests/Eloquent/EloquentOAuth2AuthorizationServiceTest.php b/packages/security-oauth2-server/tests/Eloquent/EloquentOAuth2AuthorizationServiceTest.php index 40fc7616..680185db 100644 --- a/packages/security-oauth2-server/tests/Eloquent/EloquentOAuth2AuthorizationServiceTest.php +++ b/packages/security-oauth2-server/tests/Eloquent/EloquentOAuth2AuthorizationServiceTest.php @@ -95,7 +95,7 @@ function oauth2StatementsRun(callable $work): array try { $work(); - return array_values(array_map(static fn (array $entry): string => strtolower((string) $entry['query']), DB::getQueryLog())); + return array_map(static fn (array $entry): string => strtolower((string) $entry['query']), DB::getQueryLog()); } finally { DB::disableQueryLog(); } diff --git a/packages/security/composer.json b/packages/security/composer.json index e183b188..2c0445a1 100644 --- a/packages/security/composer.json +++ b/packages/security/composer.json @@ -20,7 +20,7 @@ "source": "https://github.com/fireflyframework/fireflyframework-php/tree/main/packages/security" }, "require": { - "php": "^8.3", + "php": "^8.5.12", "firebase/php-jwt": "^7.0", "firefly/autoconfigure": "*@dev", "firefly/config": "*@dev", @@ -31,14 +31,14 @@ "firefly/kernel": "*@dev", "firefly/validation": "*@dev", "firefly/web": "*@dev", - "illuminate/container": "^13.0", - "illuminate/contracts": "^13.0", - "illuminate/cookie": "^13.0", - "illuminate/database": "^13.0", - "illuminate/http": "^13.0", - "illuminate/routing": "^13.0", - "illuminate/session": "^13.0", - "illuminate/support": "^13.0" + "illuminate/container": "^13.38", + "illuminate/contracts": "^13.38", + "illuminate/cookie": "^13.38", + "illuminate/database": "^13.38", + "illuminate/http": "^13.38", + "illuminate/routing": "^13.38", + "illuminate/session": "^13.38", + "illuminate/support": "^13.38" }, "require-dev": { "firefly/actuator": "*@dev", diff --git a/packages/testing/composer.json b/packages/testing/composer.json index f62e6ccb..f3cc8ab8 100644 --- a/packages/testing/composer.json +++ b/packages/testing/composer.json @@ -20,7 +20,7 @@ "source": "https://github.com/fireflyframework/fireflyframework-php/tree/main/packages/testing" }, "require": { - "php": "^8.3", + "php": "^8.5.12", "firebase/php-jwt": "^7.0", "firefly/actuator": "*@dev", "firefly/autoconfigure": "*@dev", @@ -42,18 +42,18 @@ "firefly/validation": "*@dev", "firefly/web": "*@dev", "guzzlehttp/promises": "^2.0 || ^3.0", - "illuminate/cache": "^13.0", - "illuminate/config": "^13.0", - "illuminate/container": "^13.0", - "illuminate/contracts": "^13.0", - "illuminate/database": "^13.0", - "illuminate/http": "^13.0", - "illuminate/routing": "^13.0", - "illuminate/support": "^13.0", - "illuminate/testing": "^13.0", - "illuminate/translation": "^13.0", - "illuminate/validation": "^13.0", - "orchestra/testbench": "^11.1" + "illuminate/cache": "^13.38", + "illuminate/config": "^13.38", + "illuminate/container": "^13.38", + "illuminate/contracts": "^13.38", + "illuminate/database": "^13.38", + "illuminate/http": "^13.38", + "illuminate/routing": "^13.38", + "illuminate/support": "^13.38", + "illuminate/testing": "^13.38", + "illuminate/translation": "^13.38", + "illuminate/validation": "^13.38", + "orchestra/testbench": "^11.3" }, "suggest": { "testcontainers/testcontainers": "Ephemeral Docker backends for @group integration tests (fireflyConfigFor())." diff --git a/packages/validation/composer.json b/packages/validation/composer.json index 957f4211..9a09b14a 100644 --- a/packages/validation/composer.json +++ b/packages/validation/composer.json @@ -20,15 +20,15 @@ "source": "https://github.com/fireflyframework/fireflyframework-php/tree/main/packages/validation" }, "require": { - "php": "^8.3", + "php": "^8.5.12", "firefly/autoconfigure": "*@dev", "firefly/config": "*@dev", "firefly/container": "*@dev", "firefly/context": "*@dev", "firefly/kernel": "*@dev", - "illuminate/contracts": "^13.0", - "illuminate/support": "^13.0", - "illuminate/validation": "^13.0" + "illuminate/contracts": "^13.38", + "illuminate/support": "^13.38", + "illuminate/validation": "^13.38" }, "extra": { "laravel": { diff --git a/packages/validation/src/Constraint/ContainerElementType.php b/packages/validation/src/Constraint/ContainerElementType.php index ba0d06b1..42d6a5ec 100644 --- a/packages/validation/src/Constraint/ContainerElementType.php +++ b/packages/validation/src/Constraint/ContainerElementType.php @@ -90,7 +90,9 @@ public static function of(ReflectionParameter|ReflectionProperty $member): ?stri * Element classes read out of a constructor docblock: `@param list $lines`, `@param Line[] $lines` * and `@param array $lines` all mean the same thing. A name that does not resolve is left out. * - * @param ReflectionClass $declaring + * @template T of object + * + * @param ReflectionClass $declaring * @return array parameter name => element class */ public static function fromParams(string $docComment, ReflectionClass $declaring): array @@ -119,7 +121,9 @@ public static function fromParams(string $docComment, ReflectionClass $declaring /** * The element class a member's own `@var` tag states, or null. * - * @param ReflectionClass $declaring + * @template T of object + * + * @param ReflectionClass $declaring */ public static function fromVar(string $docComment, ReflectionClass $declaring): ?string { @@ -149,7 +153,9 @@ private static function promoted(ReflectionParameter $parameter, ReflectionClass } /** - * @param ReflectionClass $declaring + * @template T of object + * + * @param ReflectionClass $declaring */ private static function resolve(string $name, ReflectionClass $declaring): ?string { diff --git a/packages/web/composer.json b/packages/web/composer.json index 2686382e..ffb9d030 100644 --- a/packages/web/composer.json +++ b/packages/web/composer.json @@ -21,20 +21,20 @@ "source": "https://github.com/fireflyframework/fireflyframework-php/tree/main/packages/web" }, "require": { - "php": "^8.3", + "php": "^8.5.12", "firefly/autoconfigure": "*@dev", "firefly/config": "*@dev", "firefly/container": "*@dev", "firefly/context": "*@dev", "firefly/kernel": "*@dev", "firefly/validation": "*@dev", - "illuminate/auth": "^13.0", - "illuminate/contracts": "^13.0", - "illuminate/http": "^13.0", - "illuminate/log": "^13.0", - "illuminate/routing": "^13.0", - "illuminate/support": "^13.0", - "illuminate/validation": "^13.0" + "illuminate/auth": "^13.38", + "illuminate/contracts": "^13.38", + "illuminate/http": "^13.38", + "illuminate/log": "^13.38", + "illuminate/routing": "^13.38", + "illuminate/support": "^13.38", + "illuminate/validation": "^13.38" }, "extra": { "laravel": { diff --git a/packages/web/src/Dispatch/ResponseFactory.php b/packages/web/src/Dispatch/ResponseFactory.php index 5c66f9aa..a2a268dd 100644 --- a/packages/web/src/Dispatch/ResponseFactory.php +++ b/packages/web/src/Dispatch/ResponseFactory.php @@ -109,8 +109,11 @@ private function renderModelAndView(ModelAndView $result, ?int $status): Symfony ); } + /** @var view-string $view */ + $view = $result->view; + return $this->html( - $this->views->make($result->view, $result->model)->render(), + $this->views->make($view, $result->model)->render(), $status ?? $result->status, $result->headers, ); diff --git a/packages/web/tests/Fixtures/View/RecordingViewFactory.php b/packages/web/tests/Fixtures/View/RecordingViewFactory.php index e5aa89c4..94c948be 100644 --- a/packages/web/tests/Fixtures/View/RecordingViewFactory.php +++ b/packages/web/tests/Fixtures/View/RecordingViewFactory.php @@ -24,7 +24,10 @@ public function exists($view): bool */ public function file($path, $data = [], $mergeData = []): ViewContract { - return $this->make($path, $data, $mergeData); + /** @var view-string $view */ + $view = $path; + + return $this->make($view, $data, $mergeData); } /** diff --git a/packages/webhooks/composer.json b/packages/webhooks/composer.json index 046ca412..316517b7 100644 --- a/packages/webhooks/composer.json +++ b/packages/webhooks/composer.json @@ -20,7 +20,7 @@ "source": "https://github.com/fireflyframework/fireflyframework-php/tree/main/packages/webhooks" }, "require": { - "php": "^8.3", + "php": "^8.5.12", "firefly/autoconfigure": "*@dev", "firefly/config": "*@dev", "firefly/container": "*@dev", @@ -29,14 +29,14 @@ "firefly/kernel": "*@dev", "firefly/resilience": "*@dev", "firefly/web": "*@dev", - "illuminate/cache": "^13.0", - "illuminate/console": "^13.0", - "illuminate/container": "^13.0", - "illuminate/contracts": "^13.0", - "illuminate/database": "^13.0", - "illuminate/http": "^13.0", - "illuminate/routing": "^13.0", - "illuminate/support": "^13.0", + "illuminate/cache": "^13.38", + "illuminate/console": "^13.38", + "illuminate/container": "^13.38", + "illuminate/contracts": "^13.38", + "illuminate/database": "^13.38", + "illuminate/http": "^13.38", + "illuminate/routing": "^13.38", + "illuminate/support": "^13.38", "psr/log": "^3.0" }, "extra": { diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 3611d813..d9016430 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -4,12 +4,8 @@ includes: parameters: level: max - # PARSE AT THE MINIMUM SUPPORTED VERSION, not at whatever the developer happens to run. Every package - # declares `php: ^8.3`, and PHP 8.4's `new Foo()->bar()` (no parentheses) is a PARSE ERROR on 8.3 — so a - # file using it is broken for the framework's own floor while analysing and testing perfectly on 8.5. - # That is exactly what happened: ten call sites across two test files shipped green locally and turned - # the 8.3 CI job red with "Parse error", which is the least actionable failure a contributor can get. - phpVersion: 80300 + # Analyse at the composer floor, PHP 8.5.12. + phpVersion: 80512 universalObjectCratesClasses: - Pest\Mixins\Expectation paths: diff --git a/samples/lumen/composer.json b/samples/lumen/composer.json index 8e3a37f2..131a2d0a 100644 --- a/samples/lumen/composer.json +++ b/samples/lumen/composer.json @@ -19,7 +19,7 @@ "cqrs" ], "require": { - "php": "^8.3", + "php": "^8.5.12", "firefly/actuator": "*@dev", "firefly/callbacks": "*@dev", "firefly/config": "*@dev", @@ -40,15 +40,15 @@ "firefly/web": "*@dev", "firefly/webhooks": "*@dev", "fireflyframework/larafly": "*@dev", - "illuminate/database": "^13.0", - "illuminate/support": "^13.0" + "illuminate/database": "^13.38", + "illuminate/support": "^13.38" }, "require-dev": { "firefly/eda-database": "*@dev", "firefly/eda-postgres": "*@dev", "firefly/testing": "*@dev", - "orchestra/testbench": "^11.1", - "illuminate/testing": "^13.0" + "orchestra/testbench": "^11.3", + "illuminate/testing": "^13.38" }, "autoload": { "psr-4": { diff --git a/skeleton/composer.json b/skeleton/composer.json index d99a0090..faf0e567 100644 --- a/skeleton/composer.json +++ b/skeleton/composer.json @@ -21,10 +21,10 @@ "source": "https://github.com/fireflyframework/fireflyframework-php/tree/main/skeleton" }, "require": { - "php": "^8.3", + "php": "^8.5.12", "firefly/cli": "*@dev", "fireflyframework/larafly": "*@dev", - "laravel/framework": "^13.0" + "laravel/framework": "^13.38" }, "extra": { "branch-alias": { diff --git a/tests/MinimumPhpSyntaxTest.php b/tests/MinimumPhpSyntaxTest.php index a9cfc57e..b32c9a0a 100644 --- a/tests/MinimumPhpSyntaxTest.php +++ b/tests/MinimumPhpSyntaxTest.php @@ -3,160 +3,30 @@ declare(strict_types=1); /** - * No source file may use syntax that PHP 8.3 — the framework's own floor — cannot parse. - * - * Every package declares `php: ^8.3`, but a contributor runs whatever their machine has. PHP 8.4 allows - * `new Foo()->bar()` without parentheses around the constructor, and on 8.3 that is a **parse error**, not a - * deprecation. So a file using it analyses clean, tests clean and lints clean on 8.5 while being unloadable - * for a third of the supported range. - * - * That is not hypothetical: ten call sites across two test files shipped green locally and turned the 8.3 CI - * job red with `Parse error: syntax error`, which is the least actionable failure a contributor can be - * handed — it names a file and a column, and nothing about why the same file is fine on their machine. - * - * PHPSTAN CANNOT DO THIS. Its `phpVersion` parameter governs semantic analysis — which functions and - * behaviours exist — and not the parser, which always reads the newest grammar. `php -l` cannot either, - * unless the CI runner happens to be on the oldest version, which is exactly the coincidence that let this - * through. A scan is the only check that runs on every developer's machine regardless of what they have - * installed. + * The runtime floor is PHP 8.5.12, the latest release that contains the + * October 2026 fixes for CVE-2026-109618 and CVE-2026-109619. Every manifest + * has to say so. A floating `^8.3` would still install on an unpatched + * interpreter. */ -it('uses no syntax newer than the minimum supported PHP version', function () { +it('requires the patched PHP 8.5.12 release in every composer manifest', function () { $root = dirname(__DIR__); - $offenders = []; - - foreach (['packages', 'skeleton', 'samples', 'tests'] as $directory) { - $path = $root.'/'.$directory; - if (! is_dir($path)) { - continue; - } + $files = array_merge( + [$root.'/composer.json', $root.'/skeleton/composer.json'], + glob($root.'/packages/*/composer.json') ?: [], + glob($root.'/samples/*/composer.json') ?: [], + ); - /** @var iterable $files */ - $files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::SKIP_DOTS)); + $offenders = []; - foreach ($files as $file) { - if (! $file->isFile() || $file->getExtension() !== 'php' || str_contains($file->getPathname(), '/vendor/')) { - continue; - } + foreach ($files as $file) { + /** @var array{require?: array} $json */ + $json = json_decode((string) file_get_contents($file), true, flags: JSON_THROW_ON_ERROR); + $php = $json['require']['php'] ?? null; - foreach (newWithoutParentheses((string) file_get_contents($file->getPathname())) as $line) { - $offenders[] = substr($file->getPathname(), strlen($root) + 1).':'.$line; - } + if ($php !== '^8.5.12') { + $offenders[] = str_replace($root.'/', '', $file).' requires '.var_export($php, true); } } - sort($offenders); - expect($offenders)->toBe([]); }); - -/** - * Lines holding `new Foo(…)->` — the arrow attached directly to the constructor's own closing parenthesis. - * - * TOKENISED, NOT PATTERN-MATCHED, and the first version of this function proved why: a regex over the raw - * text matched the example inside this very docblock and reported the guard as its own first offender. - * `token_get_all()` sees code and never comments or string literals, so the scan cannot be fooled by prose - * that happens to describe the thing it is looking for. - * - * Parentheses are brace-matched because a constructor argument may contain its own — `new Money(max(1, $n))` - * — and counting to the first `)` would report the wrong sites. An already-correct `(new Foo(…))->` is - * skipped by checking the token before the `new`. - * - * @return list - */ -function newWithoutParentheses(string $source): array -{ - $tokens = token_get_all($source); - $lines = []; - - /** @var list $tokens */ - foreach ($tokens as $index => $token) { - if (! is_array($token) || $token[0] !== T_NEW) { - continue; - } - - // Already wrapped: the token before `new` is the opening parenthesis of `(new Foo(…))->`. - $previous = previousCode($tokens, $index); - if ($previous === '(') { - continue; - } - - $cursor = openingParenthesis($tokens, $index); - if ($cursor === null) { - continue; - } - - $depth = 0; - for ($i = $cursor; $i < count($tokens); $i++) { - $current = $tokens[$i]; - if ($current === '(') { - $depth++; - } elseif ($current === ')') { - $depth--; - if ($depth === 0) { - $after = nextCode($tokens, $i); - if (is_array($after) && $after[0] === T_OBJECT_OPERATOR) { - $lines[] = $token[2]; - } - break; - } - } - } - } - - return $lines; -} - -/** - * The `(` that opens the constructor's argument list, or null when the `new` is followed by something this - * scan does not model (an anonymous class, a variable class name with no call). - * - * @param list $tokens - */ -function openingParenthesis(array $tokens, int $from): ?int -{ - for ($i = $from + 1; $i < count($tokens); $i++) { - $token = $tokens[$i]; - - if (is_array($token) && in_array($token[0], [T_WHITESPACE, T_STRING, T_NAME_QUALIFIED, T_NAME_FULLY_QUALIFIED, T_NS_SEPARATOR], true)) { - continue; - } - - return $token === '(' ? $i : null; - } - - return null; -} - -/** - * @param list $tokens - * @return array{0: int, 1: string, 2: int}|string|null - */ -function previousCode(array $tokens, int $from): array|string|null -{ - for ($i = $from - 1; $i >= 0; $i--) { - if (is_array($tokens[$i]) && in_array($tokens[$i][0], [T_WHITESPACE, T_COMMENT, T_DOC_COMMENT], true)) { - continue; - } - - return $tokens[$i]; - } - - return null; -} - -/** - * @param list $tokens - * @return array{0: int, 1: string, 2: int}|string|null - */ -function nextCode(array $tokens, int $from): array|string|null -{ - for ($i = $from + 1; $i < count($tokens); $i++) { - if (is_array($tokens[$i]) && in_array($tokens[$i][0], [T_WHITESPACE, T_COMMENT, T_DOC_COMMENT], true)) { - continue; - } - - return $tokens[$i]; - } - - return null; -}