279 Commits

Author SHA1 Message Date
Marc
0818f8f8c1 chore: bump go mod (#2297)
closes https://github.com/php/frankenphp/pull/2296
closes https://github.com/php/frankenphp/issues/2295
2026-03-23 19:30:03 +07:00
Kévin Dunglas
2728df98c9 chore: prepare release 1.12.1 2026-03-10 10:49:02 +01:00
Kévin Dunglas
ff70f7e02b chore: bump deps 2026-03-10 10:33:03 +01:00
Marc
d1fcf0656c fix sigsev on bind permissions denied (#2250?) (#2251)
Previous behaviour was bugged from somewhere between 1.11.3 and 1.12.0:

```bash
❯❯ frankenphp git:(main) 10:46 ./frankenphp php-server
2026/03/08 03:46:36.541 WARN    admin   admin endpoint disabled
2026/03/08 03:46:36.541 WARN    http.auto_https server is listening only on the HTTP port, so no automatic HTTPS will be applied to this server {"server_name": "php", "http_port": 80}
2026/03/08 03:46:36.542 INFO    tls.cache.maintenance   started background certificate maintenance      {"cache": "0x182aaec16500"}
2026/03/08 03:46:36.582 INFO    frankenphp      FrankenPHP started 🐘   {"php_version": "8.6.0-dev", "num_threads": 32, "max_threads": 32}
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x30 pc=0xb436bf]

goroutine 1 [running]:
github.com/caddyserver/caddy/v2.(*Context).Value(0x182aaedbe078?, {0x1e2d340?, 0x25e0c90?})
        <autogenerated>:1 +0x1f
github.com/caddyserver/caddy/v2/modules/caddyhttp.(*extraFieldsSlogHandler).Handle(_, {_, _}, {{0xc2635a2724e776c5, 0x67b02fd, 0x3ca5000}, {0x22f7b13, 0x17}, 0x0, 0x1b7943e, ...})
        /home/m/go/pkg/mod/github.com/caddyserver/caddy/v2@v2.11.2/modules/caddyhttp/logging.go:308 +0x4c
log/slog.(*Logger).logAttrs(0x182aaea2bc30, {0x260a170?, 0x182aae96ad20?}, 0x0, {0x22f7b13, 0x17}, {0x0, 0x0, 0x0})
        /home/m/static-php-cli/pkgroot/x86_64-linux/go-xcaddy/src/log/slog/logger.go:276 +0x277
log/slog.(*Logger).LogAttrs(...)
        /home/m/static-php-cli/pkgroot/x86_64-linux/go-xcaddy/src/log/slog/logger.go:194
github.com/dunglas/frankenphp/caddy.(*FrankenPHPApp).Stop(0x182aae844300)
        /home/m/frankenphp/caddy/app.go:182 +0x14b
github.com/caddyserver/caddy/v2.run.func2(...)
        /home/m/go/pkg/mod/github.com/caddyserver/caddy/v2@v2.11.2/caddy.go:460
github.com/caddyserver/caddy/v2.run(0x25d1e48?, 0x1)
        /home/m/go/pkg/mod/github.com/caddyserver/caddy/v2@v2.11.2/caddy.go:471 +0x977
github.com/caddyserver/caddy/v2.unsyncedDecodeAndRun({0x182aaee76400, 0x3ad, 0x400}, 0x1)
        /home/m/go/pkg/mod/github.com/caddyserver/caddy/v2@v2.11.2/caddy.go:364 +0x17a
github.com/caddyserver/caddy/v2.changeConfig({0x22c19cb, 0x4}, {0x22c7b94, 0x7}, {0x182aaee76000, 0x3ad, 0x400}, {0x0, 0x0}, 0x1)
        /home/m/go/pkg/mod/github.com/caddyserver/caddy/v2@v2.11.2/caddy.go:248 +0x959
github.com/caddyserver/caddy/v2.Load({0x182aaee76000, 0x3ad, 0x400}, 0x1)
        /home/m/go/pkg/mod/github.com/caddyserver/caddy/v2@v2.11.2/caddy.go:137 +0x225
github.com/caddyserver/caddy/v2.Run(0x1fdb7a0?)
        /home/m/go/pkg/mod/github.com/caddyserver/caddy/v2@v2.11.2/caddy.go:109 +0x3b
github.com/dunglas/frankenphp/caddy.cmdPHPServer({0x0?})
        /home/m/frankenphp/caddy/php-server.go:320 +0x27b6
github.com/dunglas/frankenphp/caddy.init.4.func1.WrapCommandFuncForCobra.1(0x182aaee53808, {0x22c1a77?, 0x4?, 0x22c1a03?})
        /home/m/go/pkg/mod/github.com/caddyserver/caddy/v2@v2.11.2/cmd/cobra.go:151 +0x2f
github.com/spf13/cobra.(*Command).execute(0x182aaee53808, {0x3cc96a0, 0x0, 0x0})
        /home/m/go/pkg/mod/github.com/spf13/cobra@v1.10.2/command.go:1015 +0xb14
github.com/spf13/cobra.(*Command).ExecuteC(0x182aae7fbb08)
        /home/m/go/pkg/mod/github.com/spf13/cobra@v1.10.2/command.go:1148 +0x465
github.com/spf13/cobra.(*Command).Execute(...)
        /home/m/go/pkg/mod/github.com/spf13/cobra@v1.10.2/command.go:1071
github.com/caddyserver/caddy/v2/cmd.Main()
        /home/m/go/pkg/mod/github.com/caddyserver/caddy/v2@v2.11.2/cmd/main.go:72 +0x65
main.main()
        /home/m/frankenphp/caddy/frankenphp/main.go:14 +0xf
```

This restores it to exit cleanly again:

```bash
        /home/m/frankenphp/caddy/frankenphp/main.go:14 +0xf
❯❯ frankenphp git:(main)  10:46 go build 
❯❯ frankenphp git:(main) 10:58 ./frankenphp php-server
2026/03/08 03:58:01.533 WARN    admin   admin endpoint disabled
2026/03/08 03:58:01.533 WARN    http.auto_https server is listening only on the HTTP port, so no automatic HTTPS will be applied to this server {"server_name": "php", "http_port": 80}
2026/03/08 03:58:01.533 INFO    tls.cache.maintenance   started background certificate maintenance      {"cache": "0x850ebf79280"}
2026/03/08 03:58:01.556 INFO    tls.cache.maintenance   stopped background certificate maintenance      {"cache": "0x850ebf79280"}
2026/03/08 03:58:01.556 INFO    http    servers shutting down with eternal grace period
Error: loading new config: http app module: start: listening on :80: listen tcp :80: bind: permission denied
```
2026-03-09 18:32:22 +07:00
Kévin Dunglas
74e8195dc8 chore: prepare release 1.12.0 2026-03-06 16:48:45 +01:00
Kévin Dunglas
c2eb7a2538 chore: bump deps 2026-03-06 16:48:09 +01:00
Kévin Dunglas
87556c473e tests: fix race condition on macOS (#2242) 2026-03-06 16:47:49 +01:00
Mads Jon Nielsen
c099d665a2 feat(caddy): configurable max_idle_time for autoscaled threads (#2225)
Add configurable max_idle_time for autoscaled threads

The idle timeout for autoscaled threads is currently hardcoded to 5
seconds. With bursty traffic patterns, this causes threads to be
deactivated too quickly, leading to repeated cold-start overhead when
the next burst arrives.

This PR replaces the hardcoded constant with a configurable
max_idle_time directive, allowing users to tune how long idle
autoscaled threads stay alive before deactivation. The default remains 5
seconds, preserving existing behavior.

  Usage:

  Caddyfile:
````
  frankenphp {
      max_idle_time 30s
  }
````
  JSON config:
```
  {
      "frankenphp": {
          "max_idle_time": "30s"
      }
  }
````

  Changes:
  - New max_idle_time Caddyfile directive and JSON config option
  - New WithMaxIdleTime functional option
- Replaced hardcoded maxThreadIdleTime constant with configurable
maxIdleTime variable
  - Added tests for custom and default idle time behavior
  - Updated docs
2026-03-06 14:43:37 +01:00
Alexander Stecher
8f4412cbbf perf: move sandboxed environment to the C side (#2058)
This PR uses `zend_array_dup` to simplify and optimize the environment sandboxing
logic. It also guarantees no environment leakage on FrankenPHP restarts.
2026-02-26 22:34:54 +01:00
Kévin Dunglas
25ed020036 feat: Windows support (#2119)
Closes #83 #880 #1286.

Working patch for Windows support.

Supports linking to the [official PHP release (TS
version)](https://www.php.net/downloads.php).
Includes some work from #1286 (thanks @TenHian!!)

This patch allows using Visual Studio to compile the cgo code. To do so,
it must be compiled with Go 1.26 (RC) with the following setup:

```powershell
winget install -e --id Microsoft.VisualStudio.2022.Community --override "--passive --wait --add Microsoft.VisualStudio.Workload.NativeDesktop --add Microsoft.VisualStudio.Component.VC.Llvm.Clang --includeRecommended"
winget install -e --id GoLang.Go

$env:PATH += ';C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\Llvm\bin'

cd c:\
gh repo clone microsoft/vcpkg
.\vcpkg\bootstrap-vcpkg.bat
.\vcpkg\vcpkg install pthreads brotli

# build watcher
Invoke-WebRequest -Uri "https://github.com/e-dant/watcher/releases/download/0.14.3/x86_64-pc-windows-msvc.tar" -OutFile "$env:TEMP\watcher.tar"
tar -xf "$env:TEMP\watcher.tar" -C C:\
Rename-Item -Path "C:\x86_64-pc-windows-msvc" -NewName "watcher-x86_64-pc-windows-msvc"
Remove-Item "$env:TEMP\watcher.tar"

# download php
Invoke-WebRequest -Uri "https://downloads.php.net/~windows/releases/archives/php-8.5.1-Win32-vs17-x64.zip" -OutFile "$env:TEMP\php.zip"
Expand-Archive -Path "$env:TEMP\php.zip" -DestinationPath "C:\"
Remove-Item "$env:TEMP\php.zip"

# download php development package
Invoke-WebRequest -Uri "https://downloads.php.net/~windows/releases/archives/php-devel-pack-8.5.1-Win32-vs17-x64.zip" -OutFile "$env:TEMP\php-devel.zip"
Expand-Archive -Path "$env:TEMP\php-devel.zip" -DestinationPath "C:\"
Remove-Item "$env:TEMP\php-devel.zip"

$env:GOTOOLCHAIN = 'go1.26rc1'
$env:CC = 'clang'
$env:CXX = 'clang++'
$env:CGO_CFLAGS = "-I$env:C:\vcpkg\installed\x64-windows\include -IC:\watcher-x86_64-pc-windows-msvc -IC:\php-8.5.1-devel-vs17-x64\include -IC:\php-8.5.1-devel-vs17-x64\include\main -IC:\php-8.5.1-devel-vs17-x64\include\TSRM -IC:\php-8.5.1-devel-vs17-x64\include\Zend -IC:\php-8.5.1-devel-vs17-x64\include\ext"
$env:CGO_LDFLAGS = '-LC:\vcpkg\installed\x64-windows\lib -lbrotlienc -LC:\watcher-x86_64-pc-windows-msvc -llibwatcher-c -LC:\php-8.5.1-Win32-vs17-x64 -LC:\php-8.5.1-devel-vs17-x64\lib -lphp8ts -lphp8embed'

# clone frankenphp and build
git clone -b windows https://github.com/php/frankenphp.git
cd frankenphp\caddy\frankenphp
go build -ldflags '-extldflags="-fuse-ld=lld"' -tags nowatcher,nobadger,nomysql,nopgx

# Tests

$env:PATH += ";$env:VCPKG_ROOT\installed\x64-windows\bin;C:\watcher-x86_64-pc-windows-msvc";C:\php-8.5.1-Win32-vs17-x64"
"opcache.enable=0`r`nopcache.enable_cli=0" | Out-File -Encoding ascii php.ini
$env:PHPRC = Get-Location
go test -ldflags '-extldflags="-fuse-ld=lld"' -tags nowatcher,nobadger,nomysql,nopgx .
```

TODO:

- [x] Fix remaining skipped tests (scaling and watcher)
- [x] Test if the watcher mode works as expected
- [x] Automate the build with GitHub Actions

---------

Signed-off-by: Marc <m@pyc.ac>
Co-authored-by: Kévin Dunglas <kevin@dunglas.dev>
Co-authored-by: DubbleClick <m@pyc.ac>
2026-02-26 12:38:14 +01:00
Kévin Dunglas
0ae33863d0 chore: use ldflags to set Server header 2026-02-24 13:28:52 +01:00
Kévin Dunglas
bf30297104 chore: prepare release 1.11.3 2026-02-23 16:47:48 +01:00
Kévin Dunglas
f434f5ea37 chore: bump deps 2026-02-23 16:46:51 +01:00
tehmaestro
c99b6db5ae fix: nomercure build tag (#2212)
- Fixes nomercure tag build
- Conditionally addds the mercure route and mercure project import

Fixes https://github.com/php/frankenphp/issues/2209

I am by no means a go developer, feel free to use this or not :)

---------

Signed-off-by: tehmaestro <alexnegrila89@gmail.com>
Co-authored-by: Alexander Stecher <45872305+AlliBalliBaba@users.noreply.github.com>
2026-02-22 18:46:39 +01:00
Marc
2bdf85866c Upgrade to Cady v2.11.1 (#2214)
fixes failing integration tests and closes
https://github.com/php/frankenphp/issues/2213
2026-02-21 23:29:47 +07:00
Kévin Dunglas
7c563d2567 chore: minor cleanup in Caddy module tests (#2202) 2026-02-19 10:15:45 +01:00
Kévin Dunglas
151ea1c87d test(caddy): fix flaky tests on macOS 2026-02-18 16:29:36 +01:00
Kévin Dunglas
3aa71fd428 chore: prepare release 1.11.2 2026-02-12 10:58:02 +01:00
Kévin Dunglas
e2062af083 chore: cs improvements 2026-02-11 15:49:39 +01:00
Kévin Dunglas
49d738012a chore(caddy): modernize for Go 1.26 (#2183) 2026-02-11 15:36:18 +01:00
Kévin Dunglas
040ce55e17 perf: various optimizations (#2175) 2026-02-11 15:21:55 +01:00
Alexandre Daubois
d704e60bb0 chore: bump to Go 1.26 (#2178)
Bump and free perf boost with Green tea GC 🍵
2026-02-11 14:55:57 +01:00
Kévin Dunglas
471c5af2df fix: race condition introduced in 04fdc0c (#2180)
Fix issue introduced in 04fdc0c1e8
2026-02-11 13:07:09 +01:00
Kévin Dunglas
04fdc0c1e8 fix: path confusion via unicode casing in CGI path splitting 2026-02-11 11:59:16 +01:00
Kévin Dunglas
6eef0d30aa chore: bump deps 2026-02-09 14:31:16 +01:00
Kévin Dunglas
709ba29bef chore: bump deps 2026-01-29 16:51:29 +01:00
dependabot[bot]
d2b941833d chore(caddy): bump the go-modules group across 1 directory with 3 updates (#2144)
Bumps the go-modules group with 3 updates in the /caddy directory:
[github.com/caddyserver/certmagic](https://github.com/caddyserver/certmagic),
[github.com/dunglas/mercure](https://github.com/dunglas/mercure) and
[github.com/dunglas/mercure/caddy](https://github.com/dunglas/mercure).

Updates `github.com/caddyserver/certmagic` from 0.25.0 to 0.25.1
<details>
<summary>Commits</summary>
<ul>
<li><a
href="d2a72863e6"><code>d2a7286</code></a>
Upgrade dependencies, esp. zerossl</li>
<li><a
href="20b57b0b0d"><code>20b57b0</code></a>
Bump golang.org/x/crypto from 0.41.0 to 0.45.0 (<a
href="https://redirect.github.com/caddyserver/certmagic/issues/358">#358</a>)</li>
<li><a
href="80e9a59765"><code>80e9a59</code></a>
Explicitly allow small RSA key sizes for testing</li>
<li><a
href="d66689d310"><code>d66689d</code></a>
Add TryLock for use with optional tasks like ARI updates to reduce lock
conte...</li>
<li><a
href="aba1313fdf"><code>aba1313</code></a>
Fix edge case panic in case of repeated account recreation failure (fix
<a
href="https://redirect.github.com/caddyserver/certmagic/issues/354">#354</a>)</li>
<li><a
href="14972fd692"><code>14972fd</code></a>
Don't log about OCSP when disabled (Fixes <a
href="https://redirect.github.com/caddyserver/certmagic/issues/353">#353</a>)</li>
<li>See full diff in <a
href="https://github.com/caddyserver/certmagic/compare/v0.25.0...v0.25.1">compare
view</a></li>
</ul>
</details>
<br />

Updates `github.com/dunglas/mercure` from 0.21.4 to 0.21.5
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/dunglas/mercure/releases">github.com/dunglas/mercure's
releases</a>.</em></p>
<blockquote>
<h2>helm-chart-0.21.5</h2>
<p>A Helm chart to install a Mercure Hub in a Kubernetes cluster.
Mercure is a protocol to push data updates to web browsers and other
HTTP clients in a convenient, fast, reliable and battery-efficient
way.</p>
<h2>v0.21.5</h2>
<h2>What's Changed</h2>
<ul>
<li>fix: prevent context cancellation for critical write by <a
href="https://github.com/dunglas"><code>@​dunglas</code></a> in <a
href="https://redirect.github.com/dunglas/mercure/pull/1151">dunglas/mercure#1151</a></li>
<li>chore: bump deps by <a
href="https://github.com/dunglas"><code>@​dunglas</code></a> in <a
href="https://redirect.github.com/dunglas/mercure/pull/1152">dunglas/mercure#1152</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/dunglas/mercure/compare/v0.21.4...v0.21.5">https://github.com/dunglas/mercure/compare/v0.21.4...v0.21.5</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="142944de73"><code>142944d</code></a>
chore: prepare release 0.21.5</li>
<li><a
href="124f2f86c4"><code>124f2f8</code></a>
ci: bump io.gatling:gatling-maven-plugin in /gatling</li>
<li><a
href="e444581233"><code>e444581</code></a>
chore: bump deps (<a
href="https://redirect.github.com/dunglas/mercure/issues/1152">#1152</a>)</li>
<li><a
href="aa6539d075"><code>aa6539d</code></a>
fix: prevent context cancellation for critical write (<a
href="https://redirect.github.com/dunglas/mercure/issues/1151">#1151</a>)</li>
<li><a
href="19b3850840"><code>19b3850</code></a>
ci: bump actions/upload-artifact from 5 to 6</li>
<li><a
href="4c684fef3f"><code>4c684fe</code></a>
ci: bump net.alchim31.maven:scala-maven-plugin in /gatling</li>
<li>See full diff in <a
href="https://github.com/dunglas/mercure/compare/v0.21.4...v0.21.5">compare
view</a></li>
</ul>
</details>
<br />

Updates `github.com/dunglas/mercure/caddy` from 0.21.4 to 0.21.5
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/dunglas/mercure/releases">github.com/dunglas/mercure/caddy's
releases</a>.</em></p>
<blockquote>
<h2>helm-chart-0.21.5</h2>
<p>A Helm chart to install a Mercure Hub in a Kubernetes cluster.
Mercure is a protocol to push data updates to web browsers and other
HTTP clients in a convenient, fast, reliable and battery-efficient
way.</p>
<h2>v0.21.5</h2>
<h2>What's Changed</h2>
<ul>
<li>fix: prevent context cancellation for critical write by <a
href="https://github.com/dunglas"><code>@​dunglas</code></a> in <a
href="https://redirect.github.com/dunglas/mercure/pull/1151">dunglas/mercure#1151</a></li>
<li>chore: bump deps by <a
href="https://github.com/dunglas"><code>@​dunglas</code></a> in <a
href="https://redirect.github.com/dunglas/mercure/pull/1152">dunglas/mercure#1152</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/dunglas/mercure/compare/v0.21.4...v0.21.5">https://github.com/dunglas/mercure/compare/v0.21.4...v0.21.5</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="142944de73"><code>142944d</code></a>
chore: prepare release 0.21.5</li>
<li><a
href="124f2f86c4"><code>124f2f8</code></a>
ci: bump io.gatling:gatling-maven-plugin in /gatling</li>
<li><a
href="e444581233"><code>e444581</code></a>
chore: bump deps (<a
href="https://redirect.github.com/dunglas/mercure/issues/1152">#1152</a>)</li>
<li><a
href="aa6539d075"><code>aa6539d</code></a>
fix: prevent context cancellation for critical write (<a
href="https://redirect.github.com/dunglas/mercure/issues/1151">#1151</a>)</li>
<li><a
href="19b3850840"><code>19b3850</code></a>
ci: bump actions/upload-artifact from 5 to 6</li>
<li><a
href="4c684fef3f"><code>4c684fe</code></a>
ci: bump net.alchim31.maven:scala-maven-plugin in /gatling</li>
<li>See full diff in <a
href="https://github.com/dunglas/mercure/compare/v0.21.4...v0.21.5">compare
view</a></li>
</ul>
</details>
<br />


Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore <dependency name> major version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's major version (unless you unignore this specific
dependency's major version or upgrade to it yourself)
- `@dependabot ignore <dependency name> minor version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's minor version (unless you unignore this specific
dependency's minor version or upgrade to it yourself)
- `@dependabot ignore <dependency name>` will close this group update PR
and stop Dependabot creating any more for the specific dependency
(unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore <dependency name>` will remove all of the ignore
conditions of the specified dependency
- `@dependabot unignore <dependency name> <ignore condition>` will
remove the ignore condition of the specified dependency and ignore
conditions


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-26 14:35:01 +01:00
Rob Landers
e0f01d12d6 Handle symlinking edge cases (#1660)
This one is interesting — though I’m not sure the best way to provide a
test. I will have to look into maybe an integration test because it is a
careful dance between how we resolve paths in the Caddy module vs.
workers. I looked into making a proper change (literally using the same
logic everywhere), but I think it is best to wait until #1646 is merged.

But anyway, this change deals with some interesting edge cases. I will
use gherkin to describe them:

```gherkin
Feature: FrankenPHP symlinked edge cases
  Background: 
    Given a `test` folder
    And a `public` folder linked to `test`
    And a worker script located at `test/index.php`
    And a `test/nested` folder
    And a worker script located at `test/nested/index.php`
  Scenario: neighboring worker script
    Given frankenphp located in the test folder
    When I execute `frankenphp php-server --listen localhost:8080 -w index.php` from `public`
    Then I expect to see the worker script executed successfully
  Scenario: nested worker script
    Given frankenphp located in the test folder
    When I execute `frankenphp --listen localhost:8080 -w nested/index.php` from `public`
    Then I expect to see the worker script executed successfully
  Scenario: outside the symlinked folder
    Given frankenphp located in the root folder
    When I execute `frankenphp --listen localhost:8080 -w public/index.php` from the root folder
    Then I expect to see the worker script executed successfully
  Scenario: specified root directory
    Given frankenphp located in the root folder
    When I execute `frankenphp --listen localhost:8080 -w public/index.php -r public` from the root folder
    Then I expect to see the worker script executed successfully    
```

Trying to write that out in regular English would be more complex IMHO.

These scenarios should all pass now with this PR.

---------

Signed-off-by: Marc <m@pyc.ac>
Co-authored-by: henderkes <m@pyc.ac>
Co-authored-by: Kévin Dunglas <kevin@dunglas.fr>
2026-01-02 10:23:16 +01:00
Kévin Dunglas
57c58faf1c chore: prepare release 1.11.1 2025-12-20 09:16:23 +01:00
Kévin Dunglas
4092ecb5b5 fix: frankenphp_log() level parameter must be optional 2025-12-19 16:25:32 +01:00
Kévin Dunglas
75ccccf1b2 fix(caddy): use default patterns when hot_reload is alone 2025-12-19 09:38:05 +01:00
Kévin Dunglas
6231bf4a1c chore: prepare release 1.11.0 2025-12-18 16:51:41 +01:00
Kévin Dunglas
e01e40fd97 chore: bump deps (#2078) 2025-12-17 11:47:14 +01:00
dependabot[bot]
3599299cde chore(caddy): bump github.com/spf13/cobra
Bumps the go-modules group in /caddy with 1 update: [github.com/spf13/cobra](https://github.com/spf13/cobra).


Updates `github.com/spf13/cobra` from 1.10.1 to 1.10.2
- [Release notes](https://github.com/spf13/cobra/releases)
- [Commits](https://github.com/spf13/cobra/compare/v1.10.1...v1.10.2)

---
updated-dependencies:
- dependency-name: github.com/spf13/cobra
  dependency-version: 1.10.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: go-modules
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-15 13:41:56 +01:00
Alexandre Daubois
41da660088 fix(hot-reload): fix import (#2069) 2025-12-12 15:23:55 +01:00
Kévin Dunglas
225ca409d3 feat: hot reload (#2031)
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>
2025-12-12 14:29:18 +01:00
Kévin Dunglas
4ac024a1d0 fix: remove deprecated Mercure "transport_url" directive from Caddyfile 2025-12-10 15:40:21 +01:00
Kacper Rowiński
e0dcf42852 chore: bump github.com/smallstep/certificates/ from 0.28.4 to 0.29.0 2025-12-09 11:40:58 +01:00
Max
c9ad9fc55a headerKeyCache: use otter v2 (#2040)
Benchmarks show that version 1, while extremely fast with hot keys,
becomes several times slower than no‑cache under frequent misses.
Version 2 delivers consistently better performance across all scenarios,
with no allocations and stable latency.

```
BenchmarkGetUnCommonHeaderNoCacheSequential-12                           7545640               169.4 ns/op            72 B/op          4 allocs/op
BenchmarkGetUnCommonHeaderV2Sequential-12                               14471982                85.98 ns/op            0 B/op          0 allocs/op
BenchmarkGetUnCommonHeaderV1Sequential-12                               19748048                59.63 ns/op            0 B/op          0 allocs/op

BenchmarkGetUnCommonHeaderNoCacheParallelOneKey-12                      24352088                44.47 ns/op           72 B/op          4 allocs/op
BenchmarkGetUnCommonHeaderV2ParallelOneKey-12                           91024160                11.76 ns/op            0 B/op          0 allocs/op
BenchmarkGetUnCommonHeaderV1ParallelOneKey-12                           192048842                6.186 ns/op           0 B/op          0 allocs/op

BenchmarkGetUnCommonHeaderNoCacheParallelRandomMaximumSize-12           26261611                43.07 ns/op           62 B/op          3 allocs/op
BenchmarkGetUnCommonHeaderV2ParallelRandomMaximumSize-12                100000000               14.43 ns/op            0 B/op          0 allocs/op
BenchmarkGetUnCommonHeaderV1ParallelRandomMaximumSize-12                137813384                8.965 ns/op           0 B/op          0 allocs/op

BenchmarkGetUnCommonHeaderNoCacheParallelRandomLenKeys-12               24224664                46.57 ns/op           71 B/op          3 allocs/op
BenchmarkGetUnCommonHeaderV2ParallelRandomLenKeys-12                    69002575                17.42 ns/op            0 B/op          0 allocs/op
BenchmarkGetUnCommonHeaderV1ParallelRandomLenKeys-12                     8498404               253.1 ns/op            42 B/op          1 allocs/op
```

---------

Co-authored-by: Kévin Dunglas <kevin@dunglas.fr>
2025-12-01 11:37:40 +01:00
Alexander Stecher
dadeb5a628 perf: tail latency with goSched (#2033)
Alternate implementation to #2016 that doesn't reduce RPS with lower
amounts of threads
2025-11-26 18:33:07 +01:00
Kévin Dunglas
fc5f6ef092 chore: prepare release 1.10.1 2025-11-25 10:54:34 +01:00
Kévin Dunglas
6c764ad9c5 fix: correctly set the Mercure hub for the main worker request 2025-11-24 11:21:45 +01:00
Kévin Dunglas
e6b3f70d91 chore: bump deps 2025-11-23 23:13:12 +01:00
Kévin Dunglas
c6cadf3bf6 chore: prepare release 1.10.0 2025-11-21 16:16:01 +01:00
Marc
49e98cc8d6 delete source/downloads after building in script, add .editorconfig (#2000)
* 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
2025-11-20 11:49:09 +01:00
Alexander Stecher
0b2d3c913f feat: per worker max threads (#1962)
* adds worker max_threads

* Adds tests for all calculation cases.

* Adds max_threads limitation to test.

* Removes the test sleep.

* Adds max_threads to error message.

* correctly uses continue.

* Fixes logic with only worker max_threads set.

* Adjust comments.

* Removes unnecessary check.

* Fixes comment.

* suggestions by @dunlgas.

* copilot suggestions.

* Renames logger.

* review

---------

Co-authored-by: Kévin Dunglas <kevin@dunglas.fr>
2025-11-18 11:55:29 +01:00
Kévin Dunglas
75a48e81a7 chore: bump deps 2025-11-18 11:48:59 +01:00
Kévin Dunglas
41cb2bbeaa feat: mercure_publish() PHP function to dispatch Mercure updates (#1927)
* feat: mercure_publish() PHP function to dispatch Mercure updates

* fix stubs for old versions

* review

* cleanup and fixes
2025-11-18 09:59:53 +01:00
Kévin Dunglas
8341cc98c6 refactor: rely on context.Context for log/slog and others (#1969)
* refactor: rely on context.Context for log/slog and others

* optimize

* refactor

* Apply suggestion from @Copilot

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* fix watcher-skip

* better globals handling

* fix

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-17 16:32:23 +01:00
Alexandre Daubois
40cb42aace chore: bump net 2025-11-17 15:09:30 +01:00