Files
php-qml/README.md
magdev beb4e3ab9d docs: refresh README + docs/ for v0.2.0
The README still framed the project as "Phase 5 / pre-v0.1.0" and the
docs predated the v0.2.0 surface (typed BridgeOp, public service
interfaces, port negotiation, pre-migration auto-backup, bridge:export,
periodic auto-update, two new makers, qmltestrunner). Bring them in line
with what's actually shipped, and add badges (release, license, PHP,
Symfony, Qt, FrankenPHP, CI, platform) to the README so the status is
legible at a glance.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 22:27:52 +02:00

6.7 KiB

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; macOS and Windows packaging are tracked under PLAN.md §13 Phases 4b/4c. 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.

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

  • Phase 0 throwaway transport spike.
  • Phase 1 framework skeleton, dev mode, single-instance lock, CI quality gate.
  • Phase 2 reactive models, update semantics, headline maker.
  • Phase 3 POC todo app, integration + snapshot tests.
  • Phase 4a bundled mode, Linux AppImage, release CI, AppImageUpdate.
  • Phase 5 DX polish — dev console, init script, hot-reload docs (shipped with v0.1.0).
  • v0.2.0 post-v0.1 architecture audit + operations row: typed BridgeOp enum, public service interfaces, port negotiation, pre-migration auto-backup, bridge:export, periodic auto-update check, two new makers (event, read-model), qmltestrunner in CI.
  • Phase 4b/4c macOS / Windows packaging.
  • v1.0.0 API stabilisation; pre-1.0 minor bumps may still break.

Tested platforms

OS Packaging CI
Linux x86_64 AppImage Gitea Actions (every push)
macOS / Windows not yet

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.