magdev 28af802e9c gitignore: ignore framework/qml/build-tests/
The make qmltest target writes its CMake build tree to build-tests/
(deliberately distinct from the regular build/ tree so a configured-with
-DBUILD_TESTING=ON tree doesn't shadow production builds). The existing
build/ patterns don't match it, so it kept showing up as untracked.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 22:29:49 +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; 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.

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%