129 Commits

Author SHA1 Message Date
Alexander Stecher
9cfa7b3f65 fix: opcache_preload in PHP 8.2 (#2284)
Fixes the Docker image tests currently failing in CI.
2026-03-17 11:14:45 +07:00
Marc
2895273476 perf: extend table on env startup instead of letting zend_hash_copy do it (#2272)
this can potentially save us a few internal calls to zend_hash_do_resize
while it loops over the source table (main_thread_env)

`8 -> 16 -> 32 -> 64 -> 128` becomes `8 -> target_size (rounded to power
of 2) 128`.
2026-03-12 22:23:39 +07:00
Marc
5979b4dac7 fix php startup errors when ini files contain environment variables (#2252)
closes #2250

---------

Signed-off-by: Marc <m@pyc.ac>
Co-authored-by: Kévin Dunglas <kevin@dunglas.fr>
2026-03-08 22:28:14 +07:00
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
cda58d224b fix(windows): ensure DLLs can always be located by PHP (#2227)
Prevent crashes when `php.ini` references PHP extensions using relative
paths, and FrankenPHP is started from a different working directory than
the one containing extensions, or with `caddy start` (instead of `caddy
run`).

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 18:27:30 +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
681aae60a6 fix(worker): revert ini reset, keep session fixes (#2139)
Revert the INI snapshot/restore mechanism from #2139 which caused
issues with frameworks that lazily set ini values like session.save_path
(#2185). Replace the session handler snapshot/restore with a simpler
direct session state reset from #2193, which preserves mod_user_names
across requests without requiring session module reload.

Co-Authored-By: Xavier Leune <xavier.leune@gmail.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 14:02:20 +01:00
Alexander Stecher
b02d99ae8a feat: always ignore user abort (#2189)
Automatically sets `ignore_user_abort` to true in worker mode as
mentioned in #2186, removing the requirement to change it via ini.

Would also be possible to expose something like an explicit
`frankenphp_client_has_closed()` function for in-between critical
sections.

---------

Co-authored-by: Marc <m@pyc.ac>
2026-02-19 11:55:39 +01:00
Xavier Leune
24d6c991a7 fix(worker): session leak between requests 2026-02-11 12:12:52 +01:00
Kévin Dunglas
db59edb590 ci: fix shellcheck errors and improve consistency (#2165) 2026-02-05 12:48:25 +01:00
Xavier Leune
ad7e4f146d fix(worker): reset ini settinfs and session if changed during worker request 2026-02-03 16:27:35 +01:00
Xavier Leune
0e8de8f56f fix(worker): initialize $_RESUEST (#2136)
Hi,

This PR fixes #1931, it handles $_REQUEST in worker mode correctly when
`auto_globals_jit` is enabled (default configuration for PHP).
Some concerns were raised in the comments of the issue regarding
performance. This implementation should make sure that request is
created only if used.

However if a previous execution plan already used `_REQUEST`, all
subsequent requests will create it. So the concern is "kindof"
mitigated.

Let me know if you have any suggestion to improve this.

---------

Signed-off-by: Xavier Leune <xavier.leune@gmail.com>
Co-authored-by: Alexander Stecher <45872305+AlliBalliBaba@users.noreply.github.com>
2026-01-29 06:56:45 +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
YL
11160fb7b3 fix: segmentation fault when registering multiple extensions (#2112)
This PR fixes a segmentation fault when using
frankenphp.RegisterExtension with more than one extension.
The issue was a type mismatch between Go and C: Go passes a slice of
pointers, but the C code was treating it as a contiguous array of
structs. This caused invalid memory access when iterating past the first
element.
I created a minimal reproduction repo here:
[https://github.com/y-l-g/frankenphp-extensions-segfault-repro](https://www.google.com/url?sa=E&q=https%3A%2F%2Fgithub.com%2Fy-l-g%2Ffrankenphp-extensions-segfault-repro)
2026-01-07 09:21:03 +01:00
Kévin Dunglas
4092ecb5b5 fix: frankenphp_log() level parameter must be optional 2025-12-19 16:25:32 +01:00
Raphael Coeffic
91c553f3d9 feat: add support for structured logging with the frankenphp_log() PHP function (#1979)
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>
2025-12-15 16:10:35 +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
Kévin Dunglas
41e0713a1b fix: allow null for mercure_publish() retry parameter 2025-11-21 13:52:55 +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
Alexander Stecher
bf6e6534f6 fix: exit() and dd() support in worker mode (#1946)
* Verifies exit behavior.

* formatting

* Checks for actual exit.

* Fixes test.

* Fixes test.

* Update testdata/dd.php

Co-authored-by: Kévin Dunglas <kevin@dunglas.fr>

---------

Co-authored-by: Kévin Dunglas <kevin@dunglas.fr>
2025-10-28 10:57:50 +01:00
SpencerMalone
1f6f768c97 fix: release but don't free CLI streams when executing cli scripts (#1906)
* Bring upstream commit 0a4a55fd44 into cli_register_file_handles to release but not free stdout/in/err.

Fixes being unable to log to stdout or error after using frankenphp.ExecutePHPCode

* chore: clang-format

---------

Co-authored-by: Kévin Dunglas <kevin@dunglas.fr>
2025-10-08 17:07:54 +02:00
Rob Landers
52df300f86 feat: custom workers initial support (#1795)
* create a simple thread framework

Signed-off-by: Robert Landers <landers.robert@gmail.com>

* add tests

Signed-off-by: Robert Landers <landers.robert@gmail.com>

* fix comment

Signed-off-by: Robert Landers <landers.robert@gmail.com>

* remove mention of an old function that no longer exists

Signed-off-by: Robert Landers <landers.robert@gmail.com>

* simplify providing a request

Signed-off-by: Robert Landers <landers.robert@gmail.com>

* satisfy linter

Signed-off-by: Robert Landers <landers.robert@gmail.com>

* add error handling and handle shutdowns

Signed-off-by: Robert Landers <landers.robert@gmail.com>

* add tests

Signed-off-by: Robert Landers <landers.robert@gmail.com>

* pipes are tied to workers, not threads

Signed-off-by: Robert Landers <landers.robert@gmail.com>

* fix test

Signed-off-by: Robert Landers <landers.robert@gmail.com>

* add a way to detect when a request is completed

Signed-off-by: Robert Landers <landers.robert@gmail.com>

* we never shutdown workers or remove them, so we do not need this

Signed-off-by: Robert Landers <landers.robert@gmail.com>

* add more comments

Signed-off-by: Robert Landers <landers.robert@gmail.com>

* Simplify modular threads (#1874)

* Simplify

* remove unused variable

* log thread index

* feat: allow passing parameters to the PHP callback and accessing its return value (#1881)

* fix formatting

Signed-off-by: Robert Landers <landers.robert@gmail.com>

* fix test compilation

Signed-off-by: Robert Landers <landers.robert@gmail.com>

* fix segfaults

Signed-off-by: Robert Landers <landers.robert@gmail.com>

* Update frankenphp.c

Co-authored-by: Kévin Dunglas <kevin@dunglas.fr>

---------

Signed-off-by: Robert Landers <landers.robert@gmail.com>
Co-authored-by: Kévin Dunglas <kevin@dunglas.fr>
2025-09-18 09:21:49 +02:00
Alexander Stecher
78bc5c87d8 fix: free request context if php_request_startup() errors (#1842) 2025-08-28 17:29:10 +02:00
Alexander Stecher
952754db27 fix: don't flush env between requests (#1814) 2025-08-27 08:30:40 +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
8175ae7e8c chore: miscellaneous fix in C code (#1766) 2025-07-24 10:24:38 +02:00
Alexandre Daubois
b80cb6cdea chore: cleanup duplication in sapi_cli_register_variables() (#1716) 2025-07-05 19:05:38 +02:00
Alexandre Daubois
29c88c0fec feat: use modern ZEND_PARSE_PARAMETERS_NONE() macro (#1704) 2025-06-30 14:50:11 +02:00
Alexandre Daubois
8d88c13795 chore: remove TODO items not relevant anymore (#1694) 2025-06-27 14:36:09 +02:00
Alexandre Daubois
d2a1b619a5 feat: expose SSL_CIPHER env var (#1693) 2025-06-27 14:27:20 +02:00
Kévin Dunglas
abfd893d88 feat: FrankenPHP extensions (#1651)
* feat: add helpers to create PHP extensions (#1644)

* feat: add helpers to create PHP extensions

* cs

* feat: GoString

* test

* add test for RegisterExtension

* cs

* optimize includes

* fix

* feat(extensions): add the PHP extension generator (#1649)

* feat(extensions): add the PHP extension generator

* unexport many types

* unexport more symbols

* cleanup some tests

* unexport more symbols

* fix

* revert types files

* revert

* add better validation and fix templates

* remove GoStringCopy

* small fixes

---------

Co-authored-by: Kévin Dunglas <kevin@dunglas.fr>

* try to fix tests

* fix CS

* try some workarounds

* try some workarounds

* ingore TestRegisterExtension

* exclude cgo tests in Docker images

* fix

* workaround...

* race detector

* simplify tests and code

* make linter happy

* feat(gofile): use templates to generate the Go file (#1666)

---------

Co-authored-by: Alexandre Daubois <2144837+alexandre-daubois@users.noreply.github.com>
2025-06-25 10:18:22 +02:00
Rob Landers
a59b649dac fix: headers before flushing (#1622)
* add tests

* fix test

* attempt to send headers when flushing

* Update testdata/only-headers.php

Co-authored-by: Kévin Dunglas <kevin@dunglas.fr>

---------

Co-authored-by: Kévin Dunglas <kevin@dunglas.fr>
2025-06-01 14:58:36 +02:00
Kévin Dunglas
c522b52804 fix: exit(), die() and uncaught exceptions must stop the worker 2025-05-21 01:19:22 +02:00
Alexander Stecher
3741782330 feat: '-r' option for php-cli (#1482) 2025-05-01 02:06:31 +02:00
Pierre Tondereau
729cf9bba1 fix: module reload on request startup (#1476) 2025-04-01 20:54:24 +02:00
Gina Peter Banyard
3701516e5e refactor: call opcache_reset PHP function directly (#1401)
* Call opcache_reset PHP function directly

* prevent warning

* cleanup

* remove frankenphp_execute_php_function

* cs

---------

Co-authored-by: Kévin Dunglas <kevin@dunglas.fr>
2025-03-24 11:29:13 +01:00
Alexander Stecher
432824edf1 fix: ensure env is not in an invalid state on shutdown (#1442) 2025-03-19 13:22:06 +01:00
Kévin Dunglas
424ca426cb fix: timeouts handling on macOS (#1435)
* ci: run tests on macOS

* debug

* debug

* fix

* nobrotli

* install brotli

* fix

* fix

* Also registers php.ini if ZEND_MAX_EXECUTION_TIMERS is disabled.

* Removes max_execution_time from tests (it gets overwritten on mac)

* tiny refacto

* fix free

* cs

---------

Co-authored-by: Alliballibaba <alliballibaba@gmail.com>
2025-03-11 17:34:49 +01:00
Alexander Stecher
f50248a7d2 refactor: removes context on the C side (#1404) 2025-03-10 08:44:03 +01:00
Alexander Stecher
c57f741d83 fix: concurrent env access (#1409) 2025-03-01 14:45:04 +01:00
Alliballibaba2
072151dfee feat: Adds automatic thread scaling at runtime and php_ini configuration in Caddyfile (#1266)
Adds option to scale threads at runtime

Adds php_ini configuration in Caddyfile
2025-02-19 20:39:33 +01:00
Kévin Dunglas
251567a617 fix: Mercure duplicate metrics panic (#1393)
* fix: Mercure duplicate metrics panic

* tidy

* ci: clang-format
2025-02-19 12:40:59 +01:00
Niels Dossche
f109f0403b perf: avoid redundant work in frankenphp_release_temporary_streams()
Persistent streams are of type le_pstream, not le_stream. Therefore, the
persistent check will always be false. We can thus replace that check
with an assertion.

`zend_list_delete` removes the entry from the regular_list table, and
calls `zend_resource_dtor` via the table destructor.
When the refcount is 1, `zend_list_close` calls `zend_resource_dtor`,
but keeps the entry in the table.
Multiple calls to `zend_resource_dtor` have no effect: the destructor is
only called once.
Therefore, the `zend_list_close` operation is redundant because it is
fully included in the work done by `zend_list_delete`.
2025-02-19 00:16:00 +01:00
Niels Dossche
30bf69cbe5 perf: avoid extra string allocation in get_full_env() (#1382)
* Avoid extra string allocation in get_full_env()

We can pass the key directly to add_assoc_str().

* Use add_assoc_str_ex
2025-02-18 09:11:23 +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
7e39e0a201 Fix: only flush temporary unreferenced streams (#1351)
* Only flush temporary streams.
---------

Co-authored-by: Alliballibaba <alliballibaba@gmail.com>
2025-01-27 00:25:12 +01:00
Rob Landers
05aafc7c44 fix memory leaks (#1350)
* fix a memory leak on thread shutdown

* clean up unused resources at end of request

* try the obvious

* Test

* clang-format

* Also ignores persistent streams.

* Adds stream test.

* Moves clean up function to frankenphp_worker_request_shutdown.

* Fixes test on -nowatcher

* Fixes test on -nowatcher

* Update testdata/file-stream.txt

Co-authored-by: Kévin Dunglas <kevin@dunglas.fr>

* Update frankenphp_test.go

Co-authored-by: Kévin Dunglas <kevin@dunglas.fr>

---------

Co-authored-by: Alliballibaba <alliballibaba@gmail.com>
Co-authored-by: Kévin Dunglas <kevin@dunglas.fr>
2025-01-25 22:54:04 +01:00
Viktor Szépe
eee1de147e chore: fix CS (#1345) 2025-01-21 11:27:46 +01:00