magdev a43b440b20 docs: refresh README status + roadmap, add 'What you get' section
The Status note still pointed at the dead Phase-4b/4c framing; the
Roadmap was a mix of legacy phase numbering and version numbers and
didn't reflect that macOS/Windows/Flathub/Snap have been consolidated
into a single v0.9.0 cross-platform packaging push (PLAN.md §13). Drop
phases entirely, list each shipped/upcoming SemVer version, and pull
v0.3.0 (i18n, persistent logs, cache warmup) and v0.9.0 forward so the
roadmap matches what's actually planned.

Add a 'What you get' section between 'What it is' and the 60-second
tour with concrete numbers (bundle size, cold start, idle RSS) and the
shipped capabilities (five makers, reactive models, supervisor
hardening, self-update, DX tooling, CI surface) so the README has more
substance than just an architecture description.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 18:35:54 +02:00
2026-05-03 21:06:20 +02:00
2026-05-03 21:06:20 +02:00

php-qml

A framework for native desktop applications with a Symfony / FrankenPHP backend and a Qt / QML frontend, packaged as a single distributable per OS.

Release License PHP Symfony Qt FrankenPHP CI Platform

Status: v0.2.0 (2026-05-03). Linux AppImage is the only packaged target through the v0.2.0 / v0.3.0 minors; macOS, Windows, Flathub and Snap all land together in v0.9.0 as a single cross-platform packaging push. Pre-v1.0 SemVer permits API breaks on minor bumps — see CHANGELOG.md.


What it is

php-qml lets a PHP developer write a desktop app using ordinary Symfony on the backend and ordinary QML on the frontend. The two halves run as a process pair inside one bundled binary:

  • A Qt/QML host owns the window, input, and rendering.
  • A bundled FrankenPHP child runs a Symfony app in worker mode.
  • They communicate over a local socket — HTTP for commands and queries, Mercure SSE for state push.

It is not a PHP↔Qt language binding — the languages run in separate processes; the bridge is a wire protocol, not an FFI layer. That deliberately avoids the failure mode that left php-gtk and php-qt unmaintained.

What you get

  • One ~150 MB AppImage bundling Qt, the Symfony app, FrankenPHP, and the AppImageUpdate sidecar. Cold start ≤ 2 s on bare metal (≤ 4 s on shared CI runners), idle RSS ≤ 200 MB. Gates enforced in CI on every release tag.
  • Five make:bridge:* makers covering CRUD, non-CRUD commands, domain events, query-only read-models, and second windows. The headline make:bridge:resource generates entity + REST controller + ReactiveListModel-bound QML in one command, with optional --with-dto for #[MapRequestPayload] + RFC 7807 validation.
  • Reactive models out of the box. ReactiveListModel / ReactiveObject do the initial GET, subscribe to Mercure, apply optimistic mutations, and reconcile via Idempotency-KeycorrelationKey round-tripping — no handwritten cross-side glue.
  • Production-grade bundled-mode supervisor. Per-session bearer + JWT secrets (rotated on every restart), pre-migration SQLite auto-backup, runtime-negotiated TCP port (no two installed apps collide), prctl(PR_SET_PDEATHSIG) so a host crash takes the child with it.
  • Self-update via embedded zsync (typical delta 1020 MB). Auto-checks on launch and every 6 h; the install step is always user-driven, never auto-restart.
  • Opt-in DX: bridge:doctor readiness probe, bridge:export database backup, DevConsole QML in-window log viewer (Ctrl+backtick), single-instance lock with launch-arg forwarding (file-association friendly), shipped .vscode/ + .idea/ configs.
  • Quality gate on every push: PHPStan + php-cs-fixer + PHPUnit + qmllint + qmltestrunner + an HTTP/SSE round-trip integration test + a bundled-supervisor smoke test + perfsmoke against the budgets above.

60-second tour

git clone https://src.bundespruefstelle.ch/magdev/php-qml && cd php-qml

# Scaffold a fresh app
./bin/php-qml-init my-app

# Run it
cd my-app
make doctor                 # readiness check
make dev                    # FrankenPHP --watch + Qt host

Add a reactive resource (entity + REST controller + QML snippet) with one maker:

cd my-app/symfony
bin/console make:bridge:resource Todo            # add --with-dto for #[MapRequestPayload] + RFC 7807 errors
bin/console make:migration && bin/console doctrine:migrations:migrate -n

make dev opens the Qt window, connection state flips to Online, and the generated TodoList.qml shows a list whose ReactiveListModel is auto-subscribed to app://model/todo over Mercure. There is no handwritten cross-side glue.

The maker family covers the four common shapes: make:bridge:resource (CRUD), make:bridge:command (non-CRUD action), make:bridge:event (domain event → QML signal), make:bridge:read-model (query-only projection), and make:bridge:window (second window).

For a non-trivial app with a multi-window test, crash-recovery test, and AppImage packaging, see examples/todo/.

Documentation

The full developer documentation lives under docs/:

  • Getting started — prerequisites by distro, first project, troubleshooting.
  • Architecture — process pair, transport, dev vs bundled mode.
  • Update semantics — connection state machine, optimistic mutations, idempotency.
  • Reactive modelsReactiveListModel, ReactiveObject, Mercure dual-publish.
  • Makersmake:bridge:resource / command / event / read-model / window.
  • Dev workflow — hot reload, dev console, editor setup, bridge:doctor, make qmltest.
  • Bundled mode — supervisor, per-session secret rotation, port negotiation, pre-migration auto-backup, first-launch migrations.
  • Linux packagingmake appimage, auto-update (launch + 6h poll), performance budgets.
  • Native dialogs — file pickers, message boxes, system tray; the QML/PHP boundary.
  • Configuration reference — env vars, CLI flags.
  • QML API reference / PHP API reference — singletons, components, attributes, services, interfaces.

Design rationale and roadmap live in PLAN.md. User-facing changes per release are in CHANGELOG.md.

Tech stack

PHP 8.4+ · Symfony 8 · Doctrine ORM 3 · FrankenPHP 1.12+ (worker mode) · Mercure · Qt 6.5+ · CMake · Composer · Gitea Actions

Roadmap

The original Phase 05 POC roadmap shipped as v0.1.0 on 2026-05-03. From there on, work is organised by SemVer version (see PLAN.md §13 for the full per-version breakdown).

  • v0.1.0 first public preview — process-pair architecture, reactive models, three headline makers, bundled mode, Linux AppImage, AppImageUpdate, release CI, DX polish (dev console, php-qml-init, editor configs).
  • v0.1.1 shakedown follow-ups — /healthz deep-load canary, bundled-supervisor integration test, skeleton AppImage parity, cache-wipe on bundled launch.
  • v0.1.2 post-shakedown audit — clean child shutdown via aboutToQuit, configurable bridge.qml_path, SessionAuthenticator problem+json on the entry-point path, CorrelationKeyListener sub-request guard.
  • v0.2.0 public-API surface (BridgeOp enum, PublisherInterface / ModelPublisherInterface / CorrelationContextInterface, BridgeBundleInfo), port negotiation, pre-migration auto-backup, bridge:export, periodic auto-update check, make:bridge:event + make:bridge:read-model makers, --with-dto opt-in, qmltestrunner in CI.
  • v0.3.0 later minor — i18n bridge (Symfony Translator + Qt Translator with shared locale switch), persistent log files + rotation, build-time Symfony cache warmup (requires kernel.project_dir virtualisation, hence its own minor).
  • v0.9.0 cross-platform packaging release-candidate milestone — macOS (.app + Sparkle 2 + notarisation), Windows (NSIS + WinSparkle + Authenticode), Flathub + Snap, multi-arch (Linux ARM64, Windows ARM, macOS universal), composer create-project php-qml/skeleton, opt-in telemetry + crash reporting. Held until one push because the cert / runner / notarisation prerequisites overlap.
  • v1.0.0 API stabilisation — auth model finalised, AppImage relinkability documented end-to-end, security model audited. Pre-1.0 minor bumps may still break public API.

Tested platforms

OS Packaging CI
Linux x86_64 AppImage Gitea Actions (every push)
macOS / Windows v0.9.0

Performance gates (tests/perfsmoke.sh) enforced on every release tag: bundle ≤ 200 MB, cold start ≤ 2 s (4 s on shared CI), idle RSS ≤ 200 MB. See docs/packaging-linux.md §performance smoke.

Contributing

Active development happens on the dev branch; main only carries release commits. Pull requests target dev.

cd framework/php && composer quality       # PHPStan + cs-fixer + PHPUnit
cd framework/skeleton && make qmltest      # qmltestrunner unit tests (Quick Test)
cd examples/todo  && make quality          # adds qmllint + integration test

Versioning

Semantic VersioningMAJOR.MINOR.BUGFIX. Pre-v1.0.0, minor bumps may break public API; bugfix bumps don't.

License

LGPL-3.0-or-later — chosen to align with Qt 6's LGPLv3 licensing. The bundled AppImage honours the relinkability obligations (Qt libs are shipped as separate .sos, not statically linked); see PLAN.md §12 for the full rationale.

Description
Bridge to run Symfony applications with native QML interfaces
Readme 503 KiB
v0.1.0 Latest
2026-05-03 10:21:14 +00:00
Languages
PHP 44.4%
C++ 34.4%
Shell 10.3%
QML 8.1%
CMake 1.4%
Other 1.4%