Files
php-qml/README.md

114 lines
6.7 KiB
Markdown
Raw Normal View History

# 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](https://img.shields.io/badge/release-v0.2.0-blue)](https://src.bundespruefstelle.ch/magdev/php-qml/releases/tag/v0.2.0)
[![License](https://img.shields.io/badge/license-LGPL--3.0--or--later-blue)](LICENSE)
[![PHP](https://img.shields.io/badge/PHP-8.4%2B-777bb4)](https://www.php.net/)
[![Symfony](https://img.shields.io/badge/Symfony-8-000000)](https://symfony.com/)
[![Qt](https://img.shields.io/badge/Qt-6.5%2B-41cd52)](https://www.qt.io/)
[![FrankenPHP](https://img.shields.io/badge/FrankenPHP-1.12%2B-ff7e1d)](https://frankenphp.dev/)
[![CI](https://src.bundespruefstelle.ch/magdev/php-qml/actions/workflows/ci.yml/badge.svg?branch=main)](https://src.bundespruefstelle.ch/magdev/php-qml/actions/workflows/ci.yml)
[![Platform](https://img.shields.io/badge/platform-Linux-yellow)](docs/packaging-linux.md)
> **Status:** v0.2.0 (2026-05-03). Linux AppImage is the only packaged target; macOS and Windows packaging are tracked under [PLAN.md §13](PLAN.md#13-roadmap-to-poc) Phases 4b/4c. Pre-v1.0 SemVer permits API breaks on minor bumps — see [CHANGELOG.md](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
```bash
Release prep v0.1.0: LGPL-3.0-or-later + real Gitea host URL 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>
2026-05-03 09:50:15 +02:00
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:
```bash
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`](docs/makers.md#makebridgeresource) (CRUD), [`make:bridge:command`](docs/makers.md#makebridgecommand) (non-CRUD action), [`make:bridge:event`](docs/makers.md#makebridgeevent) (domain event → QML signal), [`make:bridge:read-model`](docs/makers.md#makebridgeread-model) (query-only projection), and [`make:bridge:window`](docs/makers.md#makebridgewindow) (second window).
For a non-trivial app with a multi-window test, crash-recovery test, and AppImage packaging, see [`examples/todo/`](examples/todo/README.md).
## Documentation
The full developer documentation lives under [`docs/`](docs/README.md):
- **[Getting started](docs/getting-started.md)** — prerequisites by distro, first project, troubleshooting.
- **[Architecture](docs/architecture.md)** — process pair, transport, dev vs bundled mode.
- **[Update semantics](docs/update-semantics.md)** — connection state machine, optimistic mutations, idempotency.
- **[Reactive models](docs/reactive-models.md)** — `ReactiveListModel`, `ReactiveObject`, Mercure dual-publish.
- **[Makers](docs/makers.md)** — `make:bridge:resource` / `command` / `event` / `read-model` / `window`.
- **[Dev workflow](docs/dev-workflow.md)** — hot reload, dev console, editor setup, `bridge:doctor`, `make qmltest`.
- **[Bundled mode](docs/bundled-mode.md)** — supervisor, per-session secret rotation, port negotiation, pre-migration auto-backup, first-launch migrations.
- **[Linux packaging](docs/packaging-linux.md)** — `make appimage`, auto-update (launch + 6h poll), performance budgets.
- **[Native dialogs](docs/native-dialogs.md)** — file pickers, message boxes, system tray; the QML/PHP boundary.
- **[Configuration reference](docs/configuration.md)** — env vars, CLI flags.
- **[QML API reference](docs/qml-api.md)** / **[PHP API reference](docs/php-api.md)** — singletons, components, attributes, services, interfaces.
Design rationale and roadmap live in [PLAN.md](PLAN.md). User-facing changes per release are in [CHANGELOG.md](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`.
```bash
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 Versioning](https://semver.org/) — `MAJOR.MINOR.BUGFIX`. Pre-v1.0.0, minor bumps may break public API; bugfix bumps don't.
## License
[**LGPL-3.0-or-later**](LICENSE) — chosen to align with Qt 6's LGPLv3 licensing. The bundled AppImage honours the relinkability obligations (Qt libs are shipped as separate `.so`s, not statically linked); see [PLAN.md §12](PLAN.md#12-open-questions-and-risks) for the full rationale.