28 Commits

Author SHA1 Message Date
Alexander Stecher
356d2e1745 refactor: cleaner cgi string handling
Introduces C-side interned string registry (frankenphp_strings) and a frankenphp_server_vars struct to bulk-register known $_SERVER variables with pre-sized hashtable capacity.
2026-03-04 17:20:24 +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
040ce55e17 perf: various optimizations (#2175) 2026-02-11 15:21:55 +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
c30fef09d3 fix: ensure $SERVER["PHP_SELF"] always starts with a slash (#2172)
Closes #2166.
2026-02-09 13:51:55 +01:00
Alexander Stecher
0c2a0105b5 fix: let PHP handle basic auth. (#2142)
I noticed that PHP likes to handle and free basic auth parameters
internally (see
[here](9f774e3a85/main/main.c (L2739))
and
[here](9f774e3a85/main/SAPI.c (L514-L525))).
This PR changes it so the basic auth header is forwarded to PHP instead
of resolving it in go.

I suspect that this might fix some crashes in shutdown functions (like
#2121 and #1841) since it allows us freeing the `request_info` after
shutdown is finished. I haven't been able to reproduce these crashes yet
though.
2026-01-26 10:42:32 +01:00
Alexander Stecher
98573ed7c0 refactor: extract the state module and make the backoff error instead of panic
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
2025-12-02 23:10:12 +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
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
Kévin Dunglas
c42d287138 refactor: extension worker (#1910)
* refactor: extension worker

* feat: optional HTTP request

* allow passing unsafe.Pointer to the extension callback

* lint

* simplify
2025-10-09 14:10:09 +02:00
Alexander Stecher
c10e85b905 refactor: cleanup context (#1816)
* Removes NewRequestWithContext.

* Moves cgi logic to cgi.go

* Calls 'update_request_info' from the C side.

* Calls 'update_request_info' from the C side.

* clang-format

* Removes unnecessary export.

* Adds TODO.

* Adds TODO.

* Removes 'is_worker_thread'

* Shortens return statement.

* Removes the context refactor.

* adjusts comment.

* Skips parsing cgi path variables on explicitly assigned worker.

* suggesions by @dunglas.

* Re-introduces 'is_worker_thread'.

* More formatting.
2025-08-25 16:18:20 +02:00
Alexandre Daubois
d2a1b619a5 feat: expose SSL_CIPHER env var (#1693) 2025-06-27 14:27:20 +02:00
Kévin Dunglas
8092f4a35c chore!: update to golangci-lint-action 7 (#1508) 2025-04-17 20:33:22 +02:00
Alexander Stecher
f50248a7d2 refactor: removes context on the C side (#1404) 2025-03-10 08:44:03 +01:00
Alexander Stecher
619c903386 perf: nocallback and noescape cgo flags (#1406) 2025-02-28 12:08:08 +01:00
Alexander Stecher
dd250e3bda perf: optimized request headers (#1335)
* Optimizes header registration.

* Adds malformed cookie tests.

* Sets key to NULL (releasing them is unnecessary)

* Adjusts test.

* Sanitizes null bytes anyways.

* Sorts headers.

* trigger

* clang-format

* More clang-format.

* Updates headers and tests.

* Adds header test.

* Adds more headers.

* Updates headers again.

* ?Removes comments.

* ?Reformats headers

* ?Reformats headers

* renames header files.

* ?Renames test.

* ?Fixes assertion.

* test

* test

* test

* Moves headers test to main package.

* Properly capitalizes headers.

* Allows and tests multiple cookie headers.

* Fixes comment.

* Adds otter back in.

* Verifies correct capitalization.

* Resets package version.

* Removes debug log.

* Makes persistent strings also interned and saves them once on the main thread.

---------

Co-authored-by: Alliballibaba <alliballibaba@gmail.com>
2025-01-27 21:48:20 +01:00
Alexander Stecher
2b7b3d1e4b perf: put all $_SERVER vars into one function call. (#1303)
* Puts everything into one function call.

* Clang-format off.

* Cleans up.

* Changes function name.

* Removes unnecessary check.

* Passes hash table directly.

* Also tests that the original request path is passed.

* Puts vars into a struct.

* clang-format

---------

Co-authored-by: Alliballibaba <alliballibaba@gmail.com>
2025-01-08 08:23:23 +01:00
Alexander Stecher
045ce00c15 perf: remove some useless string pinning (#1295)
* Removes pinning.

* trigger build

* Cleans up function params.

---------

Co-authored-by: Alliballibaba <alliballibaba@gmail.com>
2025-01-05 10:07:45 +01:00
Alliballibaba2
f592e0f47b refactor: decouple worker threads from non-worker threads (#1137)
* Decouple workers.

* Moves code to separate file.

* Cleans up the exponential backoff.

* Initial working implementation.

* Refactors php threads to take callbacks.

* Cleanup.

* Cleanup.

* Cleanup.

* Cleanup.

* Adjusts watcher logic.

* Adjusts the watcher logic.

* Fix opcache_reset race condition.

* Fixing merge conflicts and formatting.

* Prevents overlapping of TSRM reservation and script execution.

* Adjustments as suggested by @dunglas.

* Adds error assertions.

* Adds comments.

* Removes logs and explicitly compares to C.false.

* Resets check.

* Adds cast for safety.

* Fixes waitgroup overflow.

* Resolves waitgroup race condition on startup.

* Moves worker request logic to worker.go.

* Removes defer.

* Removes call from go to c.

* Fixes merge conflict.

* Adds fibers test back in.

* Refactors new thread loop approach.

* Removes redundant check.

* Adds compareAndSwap.

* Refactor: removes global waitgroups and uses a 'thread state' abstraction instead.

* Removes unnecessary method.

* Updates comment.

* Removes unnecessary booleans.

* test

* First state machine steps.

* Splits threads.

* Minimal working implementation with broken tests.

* Fixes tests.

* Refactoring.

* Fixes merge conflicts.

* Formatting

* C formatting.

* More cleanup.

* Allows for clean state transitions.

* Adds state tests.

* Adds support for thread transitioning.

* Fixes the testdata path.

* Formatting.

* Allows transitioning back to inactive state.

* Fixes go linting.

* Formatting.

* Removes duplication.

* Applies suggestions by @dunglas

* Removes redundant check.

* Locks the handler on restart.

* Removes unnecessary log.

* Changes Unpin() logic as suggested by @withinboredom

* Adds suggestions by @dunglas and resolves TODO.

* Makes restarts fully safe.

* Will make the initial startup fail even if the watcher is enabled (as is currently the case)

* Also adds compareAndSwap to the test.

* Adds comment.

* Prevents panic on initial watcher startup.
2024-12-17 11:28:51 +01:00
Kévin Dunglas
75dab8f33d chore: bump deps and misc improvements (#1135) 2024-11-04 16:42:15 +01:00
Alexander Stecher
e5ca97308e perf: optimize $_SERVER import (#1106)
Co-authored-by: Kévin Dunglas <kevin@dunglas.fr>
Co-authored-by: a.stecher <a.stecher@sportradar.com>
Co-authored-by: Alliballibaba <alliballibaba@gmail.com>
2024-11-04 15:34:00 +01:00
Kévin Dunglas
07a74e5c5a perf: reduce allocs when creating $_SERVER (#540)
* perf: reduce allocs when creating $_SERVER

* improve

* refactor: prevent C allocs when populating $_SERVER

* cs

* remove append()

* simplify

* wip

* cleanup

* add cache

* cleanup otter init

* some fixes

* cleanup

* test with a leak

* remove const?

* add const

* wip

* wip

* allocate dynamic variables in Go memory

* cleanup

* typo

* bump otter

* chore: bump deps
2024-03-12 18:31:30 +01:00
Kévin Dunglas
aa1d968dcf refactor: faster $_SERVER variables creation 2023-11-16 14:40:52 +01:00
Kennedy Tedesco
e7bd54cc00 Chore: remove duplicated code in populateEnv() 2023-11-01 15:51:00 +01:00
Kévin Dunglas
48908f599d fix: ensure that SERVER_PORT is always defined 2023-09-17 13:20:16 +02:00
Kévin Dunglas
91f6620151 fix: ensure SERVER_PORT is always set 2023-07-18 14:58:22 +02:00
Dylan Blokhuis
18940108d1 feat: add support for $_SERVER['PHP_SELF'] (#71)
* PHP_SELF

* remove accidental formatting

* test: add test for PHP_SELF

Co-authored-by: Kévin Dunglas <kevin@dunglas.fr>
2022-10-28 17:25:02 +02:00
Kévin Dunglas
7d81fa51fe feat: add a woker mode (#1)
* refactor: better memory management

* wip

* tmp

* introduce a go-like api

* upgraded to PHP 8.2

* Fix thread safety issues

* fix tests

* wip

* refactor worker

* worker prototype

* fix populate env

* session

* improve tests

* fix Caddy tests

* refactor
2022-05-18 11:52:24 +02:00