Files
php-qml/docs/dev-workflow.md

201 lines
7.8 KiB
Markdown
Raw Normal View History

# Dev workflow
Day-to-day: hot reload, dev console, editor configs, `bridge:doctor`. The skeleton README has a short version of this; here is the long version with the *why*.
## `make dev`
`make dev` is the canonical entry point. It:
1. Runs `make build` (cmake + make) for the Qt host, since QML files are baked into the binary's resource bundle.
2. Invokes [`scripts/dev.sh`](../framework/skeleton/scripts/dev.sh):
- Starts FrankenPHP under `--watch` in its own process group.
- Starts the Qt host with `BRIDGE_URL=http://127.0.0.1:8765` and `BRIDGE_TOKEN=devtoken`.
- Traps `SIGTERM` / `SIGINT` and tears down both processes (per-PID, not via `kill 0` — that broke under tmux).
Stop with `Ctrl-C`; you'll see two cleanup messages.
## Hot reload — PHP side
FrankenPHP `--watch` does the work. Save any file under `symfony/` and the next request through the host hits the new code. There's no opcache to clear and no service to restart.
Things that *don't* require even a save-reload:
- Editing a controller method body.
- Editing a service's body.
- Editing a Twig template (if you have any).
Things that need a `make:migration` + `migrate`:
- Adding/removing/renaming an entity field.
- Adding/removing an entity.
- Changing column types.
```bash
cd symfony
bin/console make:migration
bin/console doctrine:migrations:migrate -n
```
The Qt host stays up across all of this. The next time you ask Doctrine for the affected entity, the new schema is in effect.
Things that need a host restart:
- Changes to `framework/qml/src/*.cpp` (C++ — needs rebuild + relink).
- Changes to QML files baked into the QML cache (see below).
## Hot reload — QML side
QML resides in a Qt resource bundle baked into the host binary. Saving `Main.qml` does *not* automatically flip the running window. Three workflows that do:
### Qt Creator → File → Reload (`Ctrl+R`)
The lowest-friction option. Works on edits to existing QML files; new files need a rebuild because they have to be added to `QML_FILES` in CMake.
### `qmlls` live preview
[`qmlls`](https://doc.qt.io/qt-6/qtqml-tooling-qmlls.html) is the QML language server bundled with Qt 6.5+. With the *Qt for VSCode* extension or any other LSP-capable editor, you get completion, navigation, and a live preview window driven by `qmlls`. Edits show up instantly in the preview, no rebuild.
`qmlls` writes a `.qmlls.ini` next to your project; that file is git-ignored at the repo root.
### Run from source
Set `QML_IMPORT_TRACE=1` and pass `-DQT_QML_DEBUG` to the host build. Launch the host with Qt Creator's QML/JS Debugger attached. Save QML, the engine reloads in place.
This is more involved than the first two and we don't yet have it gated behind `BRIDGE_DEV=1` — see [PLAN.md §6](../PLAN.md#6-development-mode-toolchain) for the long-term plan.
## Dev console (`Ctrl+\``)
The skeleton's `Main.qml` ships a hidden `DevConsole` component bound to `Ctrl+\``:
```qml
DevConsole {
id: devConsole
visible: false
Layout.fillWidth: true
Layout.preferredHeight: 220
}
Shortcut {
sequences: ["Ctrl+`", "Ctrl+~"]
onActivated: devConsole.visible = !devConsole.visible
}
```
`Ctrl+\`` toggles a 220px-tall panel showing the bundled FrankenPHP child's merged stdout+stderr, scrolling live. The panel reads from a 500-line ring buffer that `BackendConnection` keeps regardless of whether the panel is open, so opening it is free — no IPC ramp-up.
The console only has content in **bundled mode**. Dev mode (`BRIDGE_URL` set) shows an "observe your terminal instead" hint, since the FrankenPHP child is owned by `make dev` and writes to its own controlling terminal.
`Ctrl+~` is provided as an alias because some keyboard layouts report the backtick as a shifted tilde and the original binding doesn't fire.
The other thing the console is good for: triggering it on a deployed AppImage to inspect why something didn't boot. The 500-line ring buffer typically catches the entire stack trace if the child died at startup.
## `bridge:doctor`
A Symfony console command that checks the dev environment is wired up correctly:
```bash
make doctor
# → bridge:doctor
# ✓ Symfony bundle present and registered
# ✓ Mercure URL reachable
# ✓ BRIDGE_TOKEN configured
# ✓ JWT secret length (≥256 bits)
# ✓ SQLite database created
# ✓ Doctrine connection works
# ✓ No pending migrations
```
Add `--connect` to also probe the Qt host's expected `BRIDGE_URL`:
```bash
make doctor-connect
# additionally:
# ✓ BackendConnection probe succeeded
# ✓ Mercure subscribe + publish round-trip
```
Run this after every `php-qml-init` or whenever something looks off. It catches ~80% of the "why doesn't dev mode work?" failures before they hit the terminal.
## Editor configs
Both the skeleton and `examples/todo` ship `.vscode/` and `.idea/runConfigurations/`.
### VSCode
`.vscode/launch.json`:
- **Listen for Xdebug (Symfony / FrankenPHP)** — attaches the debugger on port 9003. Set `XDEBUG_MODE=debug` for the FrankenPHP child:
```bash
XDEBUG_MODE=debug make dev
```
- **Run skeleton (Qt host)** — gdb-launches the host with dev-mode env vars. Use this when FrankenPHP is already running (e.g. `make dev` started in another terminal).
- **Compound: Dev: Xdebug + Qt host** — runs both at once.
`.vscode/tasks.json``make build`, `make dev`, `make doctor`, `make quality` (the todo example also has `make integration`, `make appimage`).
`.vscode/settings.json` — file-explorer excludes for `build/`, `vendor/`, `.qt/`, `.rcc/`, etc. and per-language tab sizes. The skeleton sets `intelephense.environment.phpVersion: "8.4.0"` so PHP IntelliSense doesn't flag 8.4-only syntax.
### PhpStorm / IntelliJ
`.idea/runConfigurations/`:
- `make dev`, `make doctor`, `make quality` (and `make appimage` in the todo example) as Shell run configs that execute in PhpStorm's terminal.
PhpStorm's Xdebug listener is global, not per-project. Toggle it via the toolbar's **Start Listening for PHP Debug Connections** button.
`.idea/.gitignore` is set up so per-IDE state (`workspace.xml`, etc.) is ignored while shared run configs are tracked.
### Why both VSCode and PhpStorm
A php-qml app is multi-language: Symfony controllers (PHP), QML, C++ host code. Different editors win at different parts:
- **VSCode** with Qt+PHP extensions: best PHP↔QML cross-edit experience.
- **PhpStorm**: superior PHP refactoring, debugger, Symfony plugin.
Ship both so people start with a working setup either way; let them remove the one they don't use.
## Snapshot test loop
Tweaking a maker template? The snapshot test in CI catches accidental drift. Locally:
```bash
cd framework/php
vendor/bin/phpunit tests/Maker/
# diff against tests/snapshot/
```
If you intentionally changed the template, regenerate the snapshot and commit it:
```bash
./tests/snapshot/run.sh
git add tests/snapshot/
```
## Integration test loop
`examples/todo/tests/integration.sh` boots the example app in dev mode, fires a real HTTP+SSE round-trip plus a crash-recover, and asserts the output. Run it after touching anything in `BackendConnection`, `MercureClient`, or `ReactiveListModel`:
```bash
cd examples/todo
make integration
```
It takes ~15 seconds and runs in CI on every push to `main`.
## Performance smoke
After packaging:
```bash
cd examples/todo
make appimage
make perf # asserts §11 budgets against build/Todo-x86_64.AppImage
```
Budgets:
- Bundle ≤ 200 MB
- Cold start ≤ 2 s (4 s on shared CI runners)
- Idle RSS ≤ 200 MB
Fail-loud: any breach exits non-zero and CI flags it. See [Linux packaging](packaging-linux.md#performance-smoke).
## See also
- [Getting started](getting-started.md) — first-time setup including PHP/Qt by distro.
- [Bundled mode](bundled-mode.md) — what `make dev` *doesn't* exercise (token rotation, supervisor).
- [Linux packaging](packaging-linux.md) — when to switch from `make dev` to `make appimage`.