Closes the two release-prep items called out in the Phase 5 closure
paragraph (a3d35a7).
License: LGPL-3.0-or-later. Chosen to align with Qt 6's LGPLv3, which
keeps the AppImage's relinkability obligation (PLAN.md §12) satisfied
and avoids version-mixing friction with upstream Qt. Two files at the
repo root:
- LICENSE — LGPL-3.0 text (the project license).
- LICENSE.GPL — GPL-3.0 text the LGPL-3.0 explicitly incorporates
("This version of the GNU Lesser General Public
License incorporates the terms and conditions of
version 3 of the GNU General Public License…").
framework/php/composer.json: "license": "proprietary" → SPDX
"LGPL-3.0-or-later". CHANGELOG Notes section updated with the actual
license + LICENSE/LICENSE.GPL pointer.
Repo URL: every `gitea.example/<org|you>/php-qml` (and `<org>/<repo>`
in docs/packaging-linux.md) replaced with the real
`src.bundespruefstelle.ch/magdev/php-qml`. Touched README.md,
CHANGELOG.md (compare + tag links), docs/getting-started.md,
docs/packaging-linux.md (build-appimage --update-info example +
latest.json appcast example).
PLAN.md: status line bumped to "v0.1.0 ready to tag — LGPL-3.0-or-later
license shipped, repo URL fixed". Phase 5 closure paragraph rewritten
to record both items resolved (rather than pending).
Only remaining manual edit at tag time: CHANGELOG `[0.1.0] — TBD` →
`[0.1.0] — YYYY-MM-DD` (per Keep-a-Changelog), and the actual
`git tag v0.1.0 && git push --tags` itself, which triggers
.gitea/workflows/release.yml. Per the branching memory, releases land
on main — merge dev → main first.
Verified: `make quality` from framework/skeleton green (16 tests, 45
assertions; PHPStan + cs-fixer clean; maker snapshots match).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.
Status: Phase 5 / pre-v0.1.0. Phases 0–4a are merged (working framework, real POC, Linux AppImage, auto-update, release CI). macOS and Windows packaging are deferred to 4b/4c. 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
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.
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 models —
ReactiveListModel,ReactiveObject, Mercure dual-publish. - Makers —
make:bridge:resource/command/window. - Dev workflow — hot reload, dev console, editor setup,
bridge:doctor. - Bundled mode — supervisor, per-session secret rotation, first-launch migrations.
- Linux packaging —
make appimage, auto-update, performance budgets. - Configuration reference — env vars, CLI flags.
- QML API reference / PHP API reference — singletons, components, attributes, services.
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 4b/4c ⏳ macOS / Windows packaging.
- Phase 5 🚧 DX polish — dev console, init script, hot-reload docs, v0.1.0 prep.
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 examples/todo && make quality # adds qmllint + integration test
A dedicated CONTRIBUTING.md arrives with Phase 5's wrap-up.
Versioning
Semantic Versioning — MAJOR.MINOR.BUGFIX. Pre-v1.0.0, minor bumps may break public API.
License
To be decided before v0.1.0 is tagged. The framework's own code will be permissively licensed; Qt is shipped under LGPL with relinkability obligations — see PLAN.md §12.