Allows doing something like this:
```caddyfile
watch "/app/{config,src}/*.{php,js}"
```
In the long term it would be nice to have pattern matching in the
watcher repo itself
As discussed in https://github.com/php/frankenphp/discussions/1961,
there is no real way to pass a severity/level to any log handler offered
by PHP that would make it to the FrankenPHP layer. This new function
allows applications embedding FrankenPHP to integrate PHP logging into
the application itself, thus offering a more streamlined experience.
---------
Co-authored-by: Quentin Burgess <qutn.burgess@gmail.com>
Co-authored-by: Kévin Dunglas <kevin@dunglas.fr>
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>
This PR:
- moves state.go to its own module
- moves the phpheaders test the phpheaders module
- simplifies backoff.go
- makes the backoff error instead of panic (so it can be tested)
- removes some unused C structs
The current configuration is not able to start FrankenPHP when mercure
and SELinux are used with a Caddyfile like this:
```Caddyfile
mercure {
transport bolt {
path mercure.db
}
}
```
closes https://github.com/php/frankenphp/issues/2035
Exact error:
```
SELinux is preventing /usr/bin/frankenphp from map access on the file /var/lib/frankenphp/mercure.db.
***** Plugin catchall_boolean (89.3 confidence) suggests ******************
If you want to allow domain to can mmap files
Then you must tell SELinux about this by enabling the 'domain_can_mmap_files' boolean.
Do
setsebool -P domain_can_mmap_files 1
***** Plugin catchall (11.6 confidence) suggests **************************
If you believe that frankenphp should be allowed map access on the mercure.db file by default.
Then you should report this as a bug.
You can generate a local policy module to allow this access.
Do
allow this access for now by executing:
# ausearch -c 'frankenphp' --raw | audit2allow -M my-frankenphp
# semodule -X 300 -i my-frankenphp.pp
Additional Information:
Source Context system_u:system_r:httpd_t:s0
Target Context system_u:object_r:httpd_var_lib_t:s0
Target Objects /var/lib/frankenphp/mercure.db [ file ]
Source frankenphp
Source Path /usr/bin/frankenphp
Port <Unknown>
Host localhost
Source RPM Packages frankenphp-1.10.0_84-1.x86_64
Target RPM Packages
SELinux Policy RPM selinux-policy-targeted-3.14.3-139.el8_10.1.noarch
Local Policy RPM selinux-policy-targeted-3.14.3-139.el8_10.1.noarch
Selinux Enabled True
Policy Type targeted
Enforcing Mode Enforcing
Host Name localhost
Platform Linux localhost
4.18.0-553.81.1.el8_10.x86_64 #1 SMP Mon Oct 27
11:29:19 EDT 2025 x86_64 x86_64
Alert Count 12
First Seen 2025-10-29 17:25:26 CET
Last Seen 2025-11-25 17:18:19 CET
Local ID c4e79504-117e-4e9f-ad8c-f0bcc4856697
Raw Audit Messages
type=AVC msg=audit(1764087499.320:475517): avc: denied { map } for pid=322613 comm="frankenphp" path="/var/lib/frankenphp/mercure.db" dev="md3" ino=93716492 scontext=system_u:system_r:httpd_t:s0 tcontext=system_u:object_r:httpd_var_lib_t:s0 tclass=file permissive=0
type=SYSCALL msg=audit(1764087499.320:475517): arch=x86_64 syscall=mmap success=no exit=EACCES a0=0 a1=8000 a2=1 a3=1 items=0 ppid=1 pid=322613 auid=4294967295 uid=991 gid=988 euid=991 suid=991 fsuid=991 egid=988 sgid=988 fsgid=988 tty=(none) ses=4294967295 comm=frankenphp exe=/usr/bin/frankenphp subj=system_u:system_r:httpd_t:s0 key=(null)
Hash: frankenphp,httpd_t,httpd_var_lib_t,file,map
```
* delete source/downloads after building in script, not in dockerfile
* add editorconfig
* eol
* cs fix
* added \n there
* we expect Hello\n
* Change tab width for shell scripts to 4 spaces
* bring back embed comment