This patch brings hot reloading capabilities to PHP apps: in
development, the browser will automatically refresh the page when any
source file changes!
It's similar to HMR in JavaScript.
It is built on top of [the watcher
mechanism](https://frankenphp.dev/docs/config/#watching-for-file-changes)
and of the [Mercure](https://frankenphp.dev/docs/mercure/) integration.
Each time a watched file is modified, a Mercure update is sent, giving
the ability to the client to reload the page, or part of the page
(assets, images...).
Here is an example implementation:
```caddyfile
root ./public
mercure {
subscriber_jwt {env.MERCURE_SUBSCRIBER_JWT_KEY}
anonymous
}
php_server {
hot_reload
}
```
```php
<?php
header('Content-Type: text/html');
?>
<!DOCTYPE html>
<html lang="en">
<head>
<title>Test</title>
<script>
const es = new EventSource('<?=$_SERVER['FRANKENPHP_HOT_RELOAD']?>');
es.onmessage = () => location.reload();
</script>
</head>
<body>
Hello
```
I plan to create a helper JS library to handle more advanced cases
(reloading CSS, JS, etc), similar to [HotWire
Spark](https://github.com/hotwired/spark). Be sure to attend my
SymfonyCon to learn more!
There is still room for improvement:
- Provide an option to only trigger the update without reloading the
worker for some files (ex, images, JS, CSS...)
- Support classic mode (currently, only the worker mode is supported)
- Don't reload all workers when only the files used by one change
However, this PR is working as-is and can be merged as a first step.
This patch heavily refactors the watcher module. Maybe it will be
possible to extract it as a standalone library at some point (would be
useful to add a similar feature but not tight to PHP as a Caddy module).
---------
Signed-off-by: Kévin Dunglas <kevin@dunglas.fr>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* add module (php_server directive) based workers
* refactor moduleID to uintptr for faster comparisons
* let workers inherit environment variables and root from php_server
* caddy can shift FrankenPHPModules in memory for some godforsaken reason, can't rely on them staying the same
* remove debugging statement
* fix tests
* refactor moduleID to uint64 for faster comparisons
* actually allow multiple workers per script filename
* remove logging
* utility function
* reuse existing worker with same filename and environment when calling newWorker with a filepath that already has a suitable worker, simply add number of threads
* no cleanup happens between tests, so restore old global worker overwriting logic
* add test, use getWorker(ForContext) function in frankenphp.go as well
* bring error on second global worker with the same filename again
* refactor to using name instead of moduleID
* nicer name
* nicer name
* add more tests
* remove test case already covered by previous test
* revert back to single variable, moduleIDs no longer relevant
* update comment
* figure out the worker to use in FrankenPHPModule::ServeHTTP
* add caddy/config_tests, add --retry 5 to download
* add caddy/config_tests
* sum up logic a bit, put worker thread addition into moduleWorkers parsing, before workers are actually created
* implement suggestions as far as possible
* fixup
* remove tags
* feat: download the mostly static binary when possible (#1467)
* feat: download the mostly static binary when possible
* cs
* docs: remove wildcard matcher from root directive (#1513)
* docs: update README with additional documentation links
Add link to classic mode, efficiently serving large static files and monitoring FrankenPHP
Signed-off-by: Romain Bastide <romain.bastide@orange.com>
* ci: combine dependabot updates for one group to 1 pull-request
* feat: compatibility with libphp.dylib on macOS
* feat: upgrade to Caddy 2.10
* feat: upgrade to Caddy 2.10
* chore: run prettier
* fix: build-static.sh consecutive builds (#1496)
* fix consecutive builds
* use minor version in PHP_VERSION
* install jq in centos container
* fix "arm64" download arch for spc binary
* jq is not available as a rpm download
* linter
* specify php 8.4 default
specify 8.4 so we manually switch to 8.5 when we make sure it works
allows to run without jq installed
* Apply suggestions from code review
Co-authored-by: Kévin Dunglas <kevin@dunglas.fr>
---------
Co-authored-by: Kévin Dunglas <kevin@dunglas.fr>
* chore: update Go and toolchain version (#1526)
* apply suggestions one be one - scriptpath only
* generate unique worker names by filename and number
* support worker config from embedded apps
* rename back to make sure we don't accidentally add FrankenPHPApp workers to the slice
* fix test after changing error message
* use 🧩 for module workers
* use 🌍 for global workers :)
* revert 1c414cebbc
* revert 4cc8893ced
* apply suggestions
* add dynamic config loading test of module worker
* fix test
* minor changes
---------
Signed-off-by: Romain Bastide <romain.bastide@orange.com>
Co-authored-by: Kévin Dunglas <kevin@dunglas.fr>
Co-authored-by: Indra Gunawan <hello@indra.my.id>
Co-authored-by: Romain Bastide <romain.bastide@orange.com>
* add metrics
* change how counting works
* also replace dots
* check that metrics exist
* rename NullMetrics to nullMetrics
* update go.sum
* register collectors only once
* add tests
* add tests for metrics and fix bugs
* keep old metrics around for test
* properly reset during shutdown
* use the same method as frankenphp
* Revert "keep old metrics around for test"
This reverts commit 1f0df6f6bdaebf32aec346f068d6f42a0b5f4007.
* change to require.NoError
* compile regex only once
* remove name sanitizer
* use require
* parameterize host port because security software sucks
* remove need for renaming workers
* increase number of threads and add tests
* fix where frankenphp configuration was bleeding into later tests
* adds basic docs for metrics
* Add caddy metrics link
Co-authored-by: Kévin Dunglas <kevin@dunglas.fr>
* Fix typos
Co-authored-by: Kévin Dunglas <kevin@dunglas.fr>
* address feedback
* change comment to be much more "dangerous"
---------
Co-authored-by: Kévin Dunglas <kevin@dunglas.fr>