Releases: fireflyframework/fireflyframework-php
Release list
LaraFly 26.09.11
Fixed
- Scheduling: every duration of a
#[Scheduled]method —fixedRate,fixedDelayandlockTtl, as
initialDelayalready was — is parsed at boot, and one that does not parse refuses the boot naming its method.
lockTtlused to be parsed only when its task ran, so an unparseable value failed that task on every tick and
the task never ran while the application booted and looked healthy. - Durations:
Firefly\Resilience\Durationalso reads ISO-8601 durations (PT14M,PT1H30M,P1D,
PT0.25S), the form Spring's@Scheduledandjava.time.Durationuse. Years, months and weeks are refused
because their length is not fixed. Resilience settings and every scheduling duration accept it.
LaraFly 26.09.10
Fixed
-
OpenAPI contracts: derive path-pattern
404responses and restrict inferred validation422responses
to typed request bodies. Included page controllers now preserve their actual JSON, HTML or redirect return
contract. Browser coverage checks every generated operation, tag, response and component, including nested
schemas and keyboard access on phones. -
OpenAPI readability: improve local Swagger text contrast for method and version badges, links, actions,
code examples, schema controls and constraints; verify expanded schemas with either system color preference.
Normalize native schema buttons and wrap operation controls to prevent phone overflow in WebKit. -
Error pages: shorten paths in symlinked deployments and test harnesses, bound the debug stack before
rendering, and group dependency frames behind a native disclosure. Frame summaries stay on one line on desktop and give calls a second line on phones;
text contrast meets 4.5:1 in both themes. The production facts grid has no empty colored cells and shows
the reference once, with selectable text and an optional clipboard enhancement. -
Error URL safety: validate configured home, sign-in, support and problem-type URLs at construction;
reject unsafe schemes, authority-relative paths and interior URL control characters. -
Problem documents: substitute invalid UTF-8 and return a degraded document if encoding or a serialization
callback fails. Wildcard, absent and unsupported Accept types receive problem+json while error-page
fallback is enabled. Laravel's own validation, authentication and carried-response exceptions retain
their native handling. -
Migration: problem
instancevalues now begin with/, with unsafe path characters percent-encoded.
Clients comparing the old relative path must account for the leading slash. -
Admin listings: replace competing column rules with typed columns, fixed table layout and explicit
colgroups. Route paths no longer collapse into stacks of characters beside unused space. Rigid column
widths include cell padding and allow for Linux header-font metrics, and a bounded table scrollport makes
sticky headers work. -
Stable paging: append an ascending identity tiebreak so tied rows cannot move between pages. Clamp
stale out-of-range pages to the last page; a SQL-backed data listing may need one additional query.
Added
-
Route detail: permalinked, server-rendered route contracts with ordered caller/injected bindings,
resolver claims, binding-specific failures, bounded DTO trees, sibling comparison and duplicate warnings.
Gated wiring/configuration/API/traffic links, default responses, exception handlers, registered route
metadata and collapsed advice provenance make the compiled contract inspectable without running it.
firefly.admin.routes.detailandfirefly.admin.routes.advicecontrol the new surface. -
Bean explorer: server-rendered landing/search/focus/module states, native keyboard links, bounded hop
columns, exact overflow links, complete paginated catalogue and relations, module coupling metrics,
conditions and shortest entry-point chains. Iterative SCC analysis handles deep graphs and self-cycles. -
Bean graph truthfulness: stable competing factory identities across configurations, explicit unresolved
ambiguity instead of an arbitrary target, unknown factory scope and exclusion of unbound config DTOs. -
Explorer settings: focus depth/row/node/path/page budgets, starter/module budgets and catalogue page size.
The legacyfirefly.admin.graph.max-nodesis still parsed but no longer controls drawing; 0 no longer
forces a list. Use the catalogue or relation tables for tabular exploration. -
Error navigation: configured sign-in on 401, retry on GET/HEAD 5xx, and home/support links where
configured. Production ledes retain safe authored details and 405 pages name the allowed methods. -
Error configuration: documented
max-frames,home,sign-in,support,actions,copy-button,
authored-detail,problem-fallbackandproblem.type-urisettings in the reference and module guide. -
RFC 9457 type:
about:blankby default, a code-derived URI when an HTTP(S) base is configured, or an
omitted member when the setting is empty. Omittingtypedoes not revert the correctedinstancepath. -
Shared listing controls: server-side paging, sorting and searching with validated, bookmarkable URL
state across routes, beans, conditions, scheduled tasks, configuration, runtime and data listings. Paired
listings preserve each other's state, and row-count controls have submit buttons for use without JavaScript. -
Table settings:
firefly.admin.table.page-size,page-sizes,max-page-size,max-height,density
andremember-scroll. The offered size set is closed; the data browser applies its own bounds in series.
Auto-refresh keeps URL state; optional per-URL scroll restoration applies on reload and back/forward.
Changed
packages/admin— the data browser's rows-per-page control offers the dashboard's set, and a?size=
outside it is refused rather than lowered./firefly/dataused to draw its own<select>with10
among the literal options and cap whatever arrived; its listing query is now parsed against the shared
firefly.admin.table.page-sizesnarrowed byfirefly.admin.data.max-page-size, and that set is closed
— a size that is not a member falls back tofirefly.admin.data.page-sizeinstead of being clamped to the
nearest permitted one. So?size=300renders 25 rows rather than 200, and a bookmark holding?size=10
renders 25 rather than 10, because ten is no longer offered unless a deployment says so
(FIREFLY_ADMIN_TABLE_PAGE_SIZES=10,25,50,100,200, orFIREFLY_ADMIN_DATA_PAGE_SIZE=10, which forces its
own default into the set).firefly.admin.data.max-page-sizeis a plain cap only for a direct
Firefly\Admin\Data\DataBrowser::list()call, which is parsed against no query string.
LaraFly 26.09.9
Changed
- Publish the repository root as the
fireflyframework/laraflylibrary, replacing component names at the same
version. Runtime dependencies, autoloading and Laravel discovery now belong to that package; no split
mirrors or cross-repository release credential are needed. The public package uses the
fireflyframeworknamespace because Packagist reservesfireflyfor another publisher. - Gate GitHub releases on PHP 8.3–8.5 validation and a clean installation of the exact tagged commit
from Packagist, including the bundled application installer. - Exclude local development dependencies from both release archives and copied path installations.
- Keep Lumen and component test support in development autoloading, and validate exported and copied
consumer installations in CI. The installer uses the bundled skeleton and adds Testbench explicitly
when the testing capability is requested. PostgreSQL outbox migrations are registered only for that
configured transport.
Documentation downloads
English and Spanish editions of LaraFly by Example are attached as PDF and EPUB, updated for the single-package installation and bundled installer. The same downloads are available on the book page.
These documentation assets were rebuilt after the package release from documentation commit ac03345, after its complete CI and Pages deployment passed. build-info.json records that source and SHA256SUMS verifies all four books. The v26.09.9 tag and Composer package are unchanged.
v26.09.8 — the consumer close() that stranded its client, and a main that could not pass its own gate
The leak
RdKafkaConsumerClient::close() called KafkaConsumer::close() and then released the property. Releasing the property is exactly right and was always there; it did nothing, because by the time it ran the handle was already gone.
ext-rdkafka 6.0.5, kafka_consumer.c:531-542, is the whole of that method:
rd_kafka_consumer_close(intern->rk);
intern->rk = NULL;
No rd_kafka_destroy(), and the free handler at kafka_consumer.c:53-64 destroys the handle only if (intern->rk) — which close() has just nulled. The PHP object is freed and the rd_kafka_t is not.
Measured on PHP 8.5.8 / ext-rdkafka 6.0.5 / librdkafka 2.15.1, no broker: four OS threads stranded per closed consumer, still there after five seconds of polling; zero when the reference is dropped. Unconditional, not a race. It now calls unsubscribe(), which leaves the group and lets the drop destroy the client.
The regression test counts rd_kafka_thread_cnt() rather than asserting a WeakReference goes null, because the natural test is green on this bug: the PHP object really is freed and the leak is underneath it, in C.
main could not pass composer check
Three reasons, which is why CI had been red on every PHP version since 26.09.6:
- The version is carried in four places and 26.09.6 moved one.
Version::VERSIONsaid26.09.6, the CHANGELOG heading26.09.7, the README badge26.09.5, and the verbatim listing indocs/versioning.md26.09.5— so the code inside the tagv26.09.7reported itself as26.09.6. All four now say26.09.8. pint --testwas red in three files from the 26.09.7 commit.phpstanhad three errors inInMemoryJwksProviderTest, from one missing@return.
composer check now passes: pint, phpstan clean, 3808 tests, deptrac 0/0.
Known: the split mirrors are still unpublished
The Release workflow refused again at its preflight, as it has since 26.09.5, and it is right to: the split action exits 0 even when its push fails, so the gate stops a matrix of green jobs that published nothing.
ACCESS_TOKEN is unset and the fireflyframework/firefly-* repositories do not exist. No split package has ever been published for any version. Fixing that needs an org PAT and the repositories — docs/publishing.md steps 6-7 — and is the one part of a release this project cannot do for itself.
v26.09.4 — the native gaps, closed in the framework
Everything here began as a place where an application running on 26.09.3 had to write framework-shaped code of its own — a metric wrapper around a method, a retry loop, a publisher per broker, a security rule that silently matched nothing. Each one is now an attribute the compiled manifest carries and a firefly.* key that configures it.
Highlights
Micrometer's method attributes. #[Timed], #[Counted] and #[Observed] ride the same interceptor chain #[Transactional] does. The scanner refuses the shapes that would compile into nothing rather than dropping them in silence — a metric on a class no post-processor reaches, on a final class or method, and the two shapes PHP does not inherit: a class-level attribute on a base whose stereotyped child returns [] for it, and a method an unannotated override hides. The drop decision is per method and an intersection over subclasses, so one child that merely inherits an annotated method cannot vouch for a sibling that overrides it.
The six resilience patterns as attributes, on that same chain, instead of wrappers — with an idle TTL for breaker and limiter records.
The EDA brokers reach the tracing seam. RabbitMqEventPublisher, KafkaEventPublisher and PostgresEventPublisher route publish() through EdaTracing::tracePublish(), so a broker record carries a traceparent of the framework's own making — gated by firefly.eda.tracing.brokers.enabled. The tracing figure and its Known-latent caveat moved with the code.
Spring Data continues. A #[Projection] and a trailing Pageable combine, paging in the database. initialDelay is applied rather than carried.
Breaking
Each of these is migratable without guessing; CHANGELOG.md carries the full migration note for every one.
when-authorizedhealth details are real, instead of quietly degrading tonever. An application already running that value with security on starts disclosing what it used to withhold. Decide, do not inherit: setnever, list roles infirefly.management.endpoint.health.roles, or bind your ownHealthDetailsAuthorizer.HealthEndpointgained a required fourth constructor parameter.- problem+json's
traceIdis the W3C trace id, andcorrelationIdis its own member beside it; both travel as response headers. With tracing off, every byte is what it was. - A URL rule written
/api/*stops being a dead rule, so every/-prefixed rule now matches what its author meant. - The OpenAPI document publishes the security the server actually has, and what the dispatcher enforces rather than only what the URL rules say.
Documentation
The provenance guard that shipped in 26.09.3 audits every Markdown file this repository ships, and wave N branched before it existed. Eighteen listings quoting code the wave had changed were re-quoted verbatim from the files they name, five new listings gained a marker, and four claims that live outside any fenced block — where no guard reads them — were corrected, including the figure's alt text.
Verification
All 21 findings wave N's review loop left unconfirmed were verified against the tree before this tag: 21 already fixed, 0 still broken, each proven with git merge-base --is-ancestor.
Gate at 72db787: Pint, PHPStan max, 3779 tests (15,144 assertions), Deptrac 0 violations, 75 browser tests, and CI green on PHP 8.3, 8.4 and 8.5.
Installing
This release is not published to Packagist — the org's mirror repositories and release credentials are not configured, by design. Install from the monorepo or a git VCS repository entry; docs/publishing.md documents what a future Packagist release would need.
Full changelog: v26.09.3...v26.09.4
v26.09.3 — Spring Security parity, two OAuth2 packages, Spring Data, tracing, and documentation held to the code
Seven waves of framework work and the documentation set that describes them truthfully.
Spring-Security-grade authentication and authorization: session-persisted context, form login
with the framework's own page, HTTP Basic, logout, remember-me, an entry point that negotiates
a login redirect against a 401 problem document, method security on any stereotyped bean through
one compiled proxy chain that runs security before transaction, principal injection, an Eloquent
user store, the authentication event family, and test support.
Two new packages: firefly/security-oauth2-client (OpenID Connect login with provider presets and
discovery, PKCE, id-token validation, RP-initiated logout, client credentials, Http::oauth2Client())
and firefly/security-oauth2-server (a Spring-Authorization-Server-shaped OAuth 2.1 / OIDC provider:
registered clients, /oauth2/authorize with a consent page, three grants, introspection, revocation,
userinfo, JWKS, both .well-known documents, RS256/ES256 keys with rotation).
Spring-Data-grade data: a translated DataAccessException family, query by example, #[Modifying],
OpenTelemetry-shaped tracing with W3C propagation across the web filter, the Http client, both CQRS
buses and EDA envelopes; structured logging (json, ECS, logstash); histogram buckets on timers.
Spring-shaped validation messages and #[Valid] cascading into list elements.
A browser end-to-end suite: Pest 4 and Playwright driving real Chromium over the shipped skeleton,
with its own CI job.
And the documentation held to the code: every fenced listing in the README, the guides and both
book editions is now proved against the file it came from, or marked as the reader's own code.
Nine diagrams, a redesigned site, and a bilingual book caught up with all of it.
Gate: 3,340 tests · PHPStan level max · Pint · Deptrac 0 violations · 68 browser scenarios in real Chromium · 14 book tests · 294 verified code listings per manuscript · mkdocs build --strict. CI green on PHP 8.3, 8.4 and 8.5.
Documentation: https://fireflyframework.github.io/fireflyframework-php/
Note on installation: the per-package mirrors are not published for this tag — the release workflow's credential preflight refused, by design, because the org token is not configured. Install from this monorepo until the mirrors are set up; see docs/publishing.md.
v26.09.1 — response schemas, an error page, a data browser, and five security fixes
A correctness release that also grew two browser surfaces — then had its own 606-file diff reviewed
adversarially and fixed everything that found.
Added
firefly/openapidocuments what an endpoint RETURNS. Every success response used to be
{"type": "object"}— a blank panel in a viewer,anyin a generated client.DocTypecompiles a PHPDoc
type expression into a JSON Schema fragment (array shapes,list<T>,array<K,V>told apart as
array-vs-object, tuples, literal unions, PHPStan pseudo-types), andResponseSchemaFactorybuilds a
returned class from its wire shape rather than its constructor. Three input collections were fixed with
it too:array<string, int>was published as an array, which is the wrong JSON type.- An HTML error page in the framework's own design, with the exception, its
previouschain, the source
around the throwing line, and a stack trace that separates your frames from your dependencies'. Overridable
per status;json-paths(defaultapi/*) forces problem+json on your API space whatever the caller asks. - Four new dashboard pages — a datasource page (connections, PDO persistence, the compiled
#[Transactional]contract), a drawn entity map, a feature-switch console, and a data browser with
filtering, real pagination, full CRUD and relations you can walk in both directions. - The skeleton ships what it advertises.
firefly/adminandfirefly/openapiwere required by nothing,
socreate-projectproduced a project with neither. Its sample REST resource did not persist while its
docblock claimed it did; it is now anEloquentRepositoryover two tables, which earns it its first
#[Transactional].
Security
Five defects, each reproduced before it was fixed and each pinned by a test that fails when the fix is reverted.
| CSRF | Dashboard routes were mounted with no middleware at all — every @csrf in the views was decorative, and a tokenless curl -X POST changed the log level. |
| Extraction oracle | A filter on a masked column recovered correct horse battery in 21 requests while the page displayed ******. |
| Production leak | problem+json published an unhandled QueryException's SQL and its bindings in production, ungated, while the HTML page beside it withheld everything. |
| Write primitive | The connection wizard, documented as "never writes anything", could create a file anywhere the worker could write via sqlite's database path. |
| Silent wrong answer | contains/starts escaped LIKE wildcards with no ESCAPE clause, so a search for ada_love returned zero rows against a table containing ada_lovelace@example.test. |
Laravel's CSRF middleware skips itself under tests, which is how that hole survived being written — so its
regression test asserts the middleware is attached, and the behaviour was verified over real HTTP.
The book
LaraFly by Example — fourteen chapters plus appendices, bilingual — is attached below as PDF and EPUB in
both languages, rebuilt from this tag. Chapter 4A gained "The success body: what an endpoint actually
returns" and Chapter 11 gained the new dashboard surfaces; both editions were edited in parallel.
Gates
2078 passed, 1 skipped · PHPStan max, 0 errors · deptrac 0 violations · Pint clean · book listings
223/223 · CI green on PHP 8.3, 8.4 and 8.5.
Not yet published to Packagist
release.yml splits 28 packages to fireflyframework/firefly-<pkg> mirrors, and those repositories do not
exist and no ACCESS_TOKEN secret is set — so this tag published nothing to Packagist.
Worth knowing if you are watching the Actions tab: the split run for this tag reported success on all 28
jobs anyway. symplify/monorepo-split-github-action exits 0 even when its push fails, so the run went
green while no mirror existed and nothing was published. That false green is fixed on main — the workflow
now refuses to start without ACCESS_TOKEN, and reads each tag back from its mirror before passing — but
this tag's run predates the fix.
Creating the mirrors and registering on Packagist need the org owner; see docs/publishing.md steps 6–7.
Full changelog: CHANGELOG.md
v26.07.18 — Docs-parity milestone
The documentation-parity milestone: a runnable Lumen wallet/ledger sample, a professional README, a docs table of contents, a bilingual step-by-step tutorial, and the complete bilingual book — all adversarially reviewed.
📘 LaraFly by Example — the book
Attached below as PDF + EPUB in both languages (a quick start, 13 chapters across four parts, plus a Laravel→LaraFly cheat-sheet and glossary — every listing verified against the real samples/lumen sample):
| EPUB | ||
|---|---|---|
| English | larafly-by-example.pdf |
larafly-by-example.epub |
| Español | larafly-by-example-es.pdf |
larafly-by-example-es.epub |
The book sources live in book/ and rebuild via book/build/run.sh.
See CHANGELOG.md for the full milestone notes.