v0.35.0

2 November 2026

A view can now keep its full-page render for anonymous visitors, and caches follow the query a render read.

Added

  • A view's cache in _index.json keeps the full-page render of its pages for anonymous visitors: "cache": 300 or {"ttl": 300, "scope": "page"}. scope site (the default) is cleared by any write, page only by a write to the page, above it or below it. Kept pages are sent with Cache-Control: no-cache and an ETag, and answer 304 to a matching If-None-Match.
  • A render is not kept when it reads the session, sets a cookie or an hx- header, or is not a 200. Inline {cache} regions shorten the page's life to their own.
  • Cache::getVaried() and Cache::setVaried() key an entry on the query parameters the render read through Request::query(). Request::queryKeys() lists them.
  • Cache::flushTree($url) empties the caches of a page, every page above it and everything below it.
  • A Cache rejects a set() from a render that began before a flush, so a save is never overtaken by a stale render.
  • "cache": false in config turns every cache off: the stores, kept pages and the headers that let a browser keep a response.
  • Events page.cache:hit, page.cache:skip, cache.get:after, cache.set:after and cache.flush:after, and a Clockwork Cache panel that shows them.
  • $app->users('role=admin,sort=-created') takes a find query.
  • Res::status(), Res::headerNames(), Ses::reads() and App::cacheDisabled().
  • Reference takes an optional Cache and keeps the reflected API entries in it.
  • Served page files answer If-Modified-Since with 304, and a request carrying the file's current mtime token is immutable.

Changed

  • A view's cache is seconds, not a Cache-Control string. Write "cache": 300 where it was "public, max-age=300". A kept page skips the router and controllers, so opt in only views whose GET handling has no side effects.
  • debug no longer turns the caches off. Set "cache": false while editing templates.
  • A write to a page clears the caches of every page below it as well as those above.
  • The fragment and {cache} keys no longer include the whole query string. Code that reads $_GET directly is not seen: read it through Request::query().
  • The Cache table is rebuilt on first use (schema 2), so existing entries are dropped.
  • SessionRoot::user() checks the session cookie once per request.

Removed

  • phlat/Dockerfile, phlat/Dockerfile.dist, phlat/Caddyfile and .dockerignore: the FrankenPHP image and the reusable phlat/page image are gone. The PHP-FPM setup (phlat/Dockerfile.fpm, phlat/Caddyfile.fpm) remains.
  • The docker:publish, docker:publish-dev and server:* tasks in phlat/taskfile.yml.

Bug fix releases