magdev 1c5a5761f6
Some checks failed
CI / Quality (push) Failing after 1m54s
Phase 2 sub-commit 2: ModelPublisher + #[BridgeResource] + Doctrine listener
Bundle gains the model layer that bridges Doctrine entities to Mercure
without per-resource glue. Three new pieces:

- `#[BridgeResource(name: ?string)]` attribute marks an entity as a
  reactive bridge model. Topic name defaults to the lowercased class
  basename and can be overridden per resource.

- `ModelPublisher` translates entity changes into PLAN.md §4 envelopes
  ({op, id, data, version, ?correlationKey}) and dual-publishes them
  on `app://model/{name}` (collection topic) and `app://model/{name}/{id}`
  (entity topic). Entity normalisation goes through Symfony's Serializer
  (ObjectNormalizer + DateTime + BackedEnum) for predictable JSON. The
  envelope `version` field is a per-process monotonic counter — fine for
  single-instance dev mode; production should back this with a Postgres
  SEQUENCE or equivalent (noted for Phase 4).

- `DoctrineBridgeListener` registers `postPersist`/`postUpdate`/
  `postRemove` via `#[AsDoctrineListener]` and routes events through
  ModelPublisher. Entities without `#[BridgeResource]` are silently
  skipped.

Plus the correlation-key plumbing the §5 Update Semantics layer needs:

- `CorrelationContext` is a per-request holder for the originating
  request's `Idempotency-Key`.
- `CorrelationKeyListener` reads the header on `KernelEvents::REQUEST`
  and clears the context on `KernelEvents::TERMINATE` (worker mode
  hygiene). CLI mutations see no key, which is correct.

Bundle composer.json picks up `doctrine/dbal`, `doctrine/orm`,
`doctrine/doctrine-bundle`, `symfony/serializer`, `symfony/property-*`,
`symfony/uid`. PHPStan extension `phpstan-doctrine` added so the listener's
event-args types resolve. Skeleton's framework.yaml enables `serializer`
and `property_info`.

Tests: 5 new for ModelPublisher (dual publish, correlation echo, delete
op omits data, untagged entities ignored, version increments). Total:
16 tests, 45 assertions, PHPStan clean, cs-fixer clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 02:32:51 +02:00
2026-05-01 23:58:26 +02:00

php-qml

A framework for building native desktop applications with a Symfony / FrankenPHP backend and a Qt/QML frontend, packaged as a single distributable per OS.

Status

Planning stage. The architectural design lives in PLAN.md. No implementation exists yet — first code lands in Phase 0 (a throwaway transport spike). See the roadmap.

What it is

php-qml lets a PHP developer write a desktop application 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 process owns the window, input, and rendering.
  • A bundled FrankenPHP child runs a Symfony application in worker mode.
  • They communicate over a local socket — HTTP for commands and queries, Mercure SSE for state push.

The framework provides the lifecycle, transport, reactive models, and scaffolding so application code stays idiomatic on both sides.

What it is not

Not a PHP↔Qt language binding. It does not embed PHP into a Qt event loop and it does not generate Qt classes from PHP. The two languages run in separate processes; the bridge is a wire protocol, not an FFI layer.

If you've watched php-gtk and php-qt go quiet, that is the failure mode this project deliberately avoids — the framework owns the boring parts (lifecycle, transport, conventions) so it doesn't depend on a single maintainer keeping a language binding alive.

Tech stack

  • Backend: PHP 8.x, Symfony, Doctrine ORM, FrankenPHP (worker mode), Mercure
  • Frontend: Qt 6 LTS, QML, C++ plugin where required
  • Build: CMake, Composer
  • CI: Gitea Actions, Gitea Releases
  • Targets: Linux (AppImage), macOS (.app + .dmg), Windows (NSIS / MSIX)

Getting started

Nothing to run yet. Once Phase 0 lands:

  1. Clone the repository and check out the dev branch.
  2. Install Qt 6 and PHP 8.x; the FrankenPHP runtime is fetched at build time.
  3. task dev — starts FrankenPHP in watch mode and launches the Qt host pointed at it.

Detailed setup will be documented alongside the framework skeleton in Phase 1.

Project structure

See PLAN.md §9 for the intended layout. The repo currently contains only PLAN.md and this README.

Roadmap

Six phases, each ending with something runnable. Detail in PLAN.md §13.

  • Phase 0 — throwaway spike, prove transport on Linux.
  • Phase 1 — framework skeleton, dev mode, single-instance lock, CI quality gate.
  • Phase 2 — reactive models, update semantics, headline maker (make:bridge:resource).
  • Phase 3 — POC todo application generated via the makers; testing infrastructure.
  • Phase 4 — bundled mode, per-OS packaging, release CI, auto-update.
  • Phase 5 — DX polish.

Contributing

Active development happens on the dev branch; main only carries release commits. Pull requests target dev.

A CONTRIBUTING.md will be added with the framework skeleton in Phase 1.

Versioning

Semantic Versioning — MAJOR.MINOR.BUGFIX. MAJOR for breaking changes, MINOR for backwards-compatible features, BUGFIX for backwards-compatible fixes.

License

To be decided before the first release. The framework's own code will be permissively licensed; note that Qt is shipped under LGPL and that carries obligations for distributors — see PLAN.md §12 (Qt LGPL relinkability).

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%