magdev ed4db00a62
Some checks failed
CI / Quality (push) Failing after 5m46s
Release / Linux AppImage (push) Successful in 6m15s
bundled: relay SIGTERM/SIGINT into Qt's quit() via self-pipe
The aboutToQuit-based teardown wired in v0.1.2 only fires when something
calls QCoreApplication::quit() — typically a window close. `kill -TERM`
to the host process bypasses Qt entirely (no default SIGTERM handler),
so teardownChild never ran on signal-driven shutdown. Local tests
passed on lucky timing because PR_SET_PDEATHSIG made the kernel SIGTERM
frankenphp once the host died, but the timing was racy and surfaced on
CI as "frankenphp child PID outlived the host (supervisor didn't clean
up)".

Fix: install a SIGTERM/SIGINT handler in BackendConnection that uses
the self-pipe pattern — the C signal handler writes one byte (the only
truly async-signal-safe primitive), a QSocketNotifier on the read end
calls QCoreApplication::quit() in the main thread, and aboutToQuit runs
the existing teardownChild before app.exec() returns. The host now
exits cleanly under `kill -TERM` from service managers, launchers, and
the test harness.

Also bumps the bundled-supervisor.sh first-relaunch grace from 2s to
3s — teardownChild itself waits up to 2s for frankenphp to finish after
SIGTERM, so the host needs ~2.x seconds to exit. The graceful-shutdown
step further down was already at 3s.

No public-API change; production-correctness fix.

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

Status: Phase 5 / pre-v0.1.0. Phases 04a 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/:

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 VersioningMAJOR.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.

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%