Commit Graph

84 Commits

Author SHA1 Message Date
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
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
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
16e2bbb969 tests: improve benchmarks 2025-12-02 17:13:38 +01:00
Kévin Dunglas
b1bdce359b tests: simplify benchmarks code 2025-12-01 15:34:04 +01:00
Kévin Dunglas
911e6d156b fix: crash when a string is passed for the topics parameter of the mercure_publish() function (#2021) 2025-11-23 17:03:43 +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
6225da9c18 refactor: improve ExtensionWorkers API (#1952)
* refactor: improve ExtensionWorkers API

* Update workerextension.go

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

* Update workerextension.go

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

* Update caddy/app.go

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

* Apply suggestion from @Copilot

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

* review

* fix tests

* docs

* errors

* improved error handling

* fix race

* add missing return

* use %q in Errorf

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-10 14:12:14 +01:00
Marc
bf4c9fe986 fix test typo (#1964)
* Fix typo in TestFlushEmptyResponse_worker function

* Fix test function name for flush empty response
2025-11-08 08:28:56 +01:00
Kévin Dunglas
a1ae2692e1 chore: modernize Go code 2025-08-15 00:22:44 +02:00
Alexander Stecher
9e4a6b789b refactor: remove some duplications in tests (#1783)
* Removes test duplications.

* Adds t.Helper().

* Fixes tests.

* UUpdate frankenphp_test.go

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

---------

Co-authored-by: Kévin Dunglas <kevin@dunglas.fr>
2025-08-09 22:45:56 +02:00
Alexander Stecher
a161af26ae fix: allow headers without whitespace after colon (#1741)
* Allows headers without whitespace after colon.

* Makes headers faster.

* Optimizes header splitting.

* Formatting.
2025-07-16 08:57:37 +02:00
Luffy
ac900e0df4 docs: update repository links and sync cn readme (#1711) 2025-07-01 10:29:55 +02:00
Alexandre Daubois
96400a85d0 feat(worker): make maximum consecutive failures configurable (#1692) 2025-06-30 09:38:18 +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
14469d4a0a chore: fix typo in test comment 2025-05-27 10:10:39 +02:00
Alexander Stecher
3741782330 feat: '-r' option for php-cli (#1482) 2025-05-01 02:06:31 +02:00
Indra Gunawan
1ec37f6cc9 feat: replace zap with slog (#1527) 2025-04-26 11:04:46 +02:00
Kévin Dunglas
8092f4a35c chore!: update to golangci-lint-action 7 (#1508) 2025-04-17 20:33:22 +02:00
Indra Gunawan
87315a19ae feat: introduces worker name option, use label on worker metrics instead (#1376)
* add worker name option and use it in logs and metrics, update tests

* fix missing reference for collector

* update tests

* update docs

* fix conflict

* add missing allowedDirectives

* update tests
2025-03-22 12:32:59 +01:00
Alexander Stecher
93266dfcad feat(watcher): log last changed file (#1447)
* logs last changed file.

* Fixes race condition.

---------

Co-authored-by: Alliballibaba <alliballibaba@gmail.com>
2025-03-19 13:10:02 +01:00
Alexander Stecher
a9cf944b62 ci: env test remediation (#1436)
* nbParallell

* trigger build

* Update frankenphp_test.go

---------

Co-authored-by: Alliballibaba <alliballibaba@gmail.com>
Co-authored-by: Kévin Dunglas <kevin@dunglas.fr>
2025-03-10 22:59:18 +01:00
Alexander Stecher
c57f741d83 fix: concurrent env access (#1409) 2025-03-01 14:45:04 +01:00
Kévin Dunglas
80f13f07ea docs: fix typos (#1399) 2025-02-21 13:55:37 +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
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
ece420c569 chore: fix typos (#1328)
* Fix typos

* Fix indentation
2025-01-21 00:32:52 +01:00
Kévin Dunglas
8cf6616ed6 fix: SIGSEGV when an env var is empty (#1271) 2024-12-20 15:37:42 +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
Alliballibaba2
b4748ee110 fix: don’t ignore empty request headers (#1182)
* Fixes empty request headers.

* Formatting
2024-11-21 12:58:01 +01:00
Kévin Dunglas
843d199469 perf: cache computations in WithRequestDocumentRoot (#1154) 2024-11-13 07:10:53 +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
Rob Landers
e812473fe1 implement getenv and putenv in go (#1086)
* implement getenv and putenv in go

* fix typo

* apply formatting

* return a bool

* prevent ENV= from crashing

* optimization

* optimization

* split env workflows and use go_strings

* clean up unused code

* update tests

* remove useless sprintf

* see if this fixes the asan issues

* clean up comments

* check that VAR= works correctly and use actual php to validate the behavior

* move all unpinning to the end of the request

* handle the case where php is not installed

* fix copy-paste

* optimization

* use strings.cut

* fix lint

* override how env is filled

* reuse fullenv

* use corect function
2024-10-18 13:47:11 +02:00
Alexander Stecher
8d9b6e755b feat: restart workers when on source changes (#1013)
* Adds filesystem watcher with tests.

* Refactoring.

* Formatting.

* Formatting.

* Switches to absolute path in tests.

* Fixes race condition from merge conflict.

* Fixes race condition.

* Fixes tests.

* Fixes markdown lint errors.

* Switches back to absolute paths.

* Reverts back to relative file paths.

* Fixes golangci-lint issues.

* Uses github.com/dunglas/go-fswatch instead.

* Stops watcher before stopping workers.

* Updates docs.

* Avoids segfault in tests.

* Fixes watcher segmentation violations on shutdown.

* Adjusts watcher latencies and tests.

* Adds fswatch to dockerfiles

* Fixes fswatch in alpine.

* Fixes segfault (this time for real).

* Allows queueing new reload if file changes while workers are reloading.

* Makes tests more consistent.

* Prevents the watcher from getting stuck if there is an error in the worker file itself.

* Reverts changing the image.

* Puts fswatch version into docker-bake.hcl.

* Asserts instead of panicking.

* Adds notice

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

* Update dev.Dockerfile

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

* Update Dockerfile

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

* Update Dockerfile

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

* Update alpine.Dockerfile

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

* Update alpine.Dockerfile

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

* Update dev-alpine.Dockerfile

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

* Update dev-alpine.Dockerfile

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

* Update dev.Dockerfile

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

* Update docs/config.md

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

* Runs fswatch version.

* Removes .json.

* Replaces ms with s.

* Resets the channel after closing it.

* Update watcher_options.go

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

* Update watcher_test.go

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

* Asserts no error instead.

* Fixes a race condition where events are fired after frankenphp has stopped.

* Updates docs.

* Update watcher_options_test.go

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

* Allows queuing events while watchers are reloading.

* go fmt

* Refactors stopping and draining logic.

* Allows extended watcher configuration with dirs, recursion, symlinks, case-sensitivity, latency, monitor types and regex.

* Updates docs.

* Adds TODOS.

* go fmt.

* Fixes linting errors.

* Also allows wildcards in the longform and adjusts docs.

* Adds debug log.

* Fixes the watcher short form.

* Refactors sessions and options into a struct.

* Fixes an overflow in the 'workersReadyWG' on unexpected terminations.

* Properly logs errors coming from session.Start().

* go fmt.

* Adds --nocache.

* Fixes lint issue.

* Refactors and resolves race condition on worker reload.

* Implements debouncing with a timer as suggested by @withinboredom.

* Starts watcher even if no workers are defined.

* Updates docs with file limit warning.

* Adds watch config unit tests.

* Adjusts debounce timings.

* go fmt.

* Adds fswatch to static builder (test).

* Adds a short grace period between stopping and destroying the watcher sessions.

* Adds caddy test.

* Adjusts sleep time.

* Swap to edant/watcher.

* Fixes watch options and tests.

* go fmt.

* Adds TODO.

* Installs edant/watcher in the bookworm image.

* Fixes linting.

* Refactors the watcher into its own module.

* Adjusts naming.

* ADocker image adjustments and refactoring.

* Testing installation methods.

* Installs via gcc instead.

* Fixes pointer formats.

* Fixes lint issues.

* Fixes arm alpine and updates docs.

* Clang format.

* Fixes dirs.

* Adds watcher version arg.

* Uses static lib version.

* Adds watcher to tests and sanitizers.

* Uses sudo for copying the shared lib.

* Removes unnused func.

* Refactoring.

* Update .github/workflows/sanitizers.yaml

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

* Adds fpic.

* Fixes linting.

* Skips tests in msan.

* Resets op_cache in every worker thread after termination

* Review fixes part 1.

* Test: installing libstc++ instead of gcc.

* Test: using msan ignorelist.

* Test: using msan ignorelist.

* Test: using msan ignorelist.

* Allows '/**/' for global recursion and '**/' for relative recursion.

* Reverts using the ignorelist.

* Calls opcache directly.

* Adds --watch to php-server command

* Properly free CStrings.

* Sorts alphabetically and uses curl instead of git.

* Labeling and formatting.

* Update .github/workflows/sanitizers.yaml

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

* Update .github/workflows/sanitizers.yaml

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

* Update .github/workflows/tests.yaml

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

* Update .github/workflows/tests.yaml

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

* Update caddy/caddy.go

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

* Update docs/config.md

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

* Update frankenphp_with_watcher_test.go

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

* Update watcher/watcher.h

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

* Update frankenphp.c

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

* Update watcher/watcher.go

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

* Update docs/config.md

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

* Update frankenphp_with_watcher_test.go

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

* Update testdata/files/.gitignore

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

* Update watcher/watcher-c.h

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

* Update watcher/watcher.c

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

* Fixes test and Dockerfile.

* Fixes Dockerfiles.

* Resets go versions.

* Replaces unsafe.pointer with uintptr_t

* Prevents worker channels from being destroyed on reload.

* Minimizes the public api by only passing a []string.

* Adds support for directory patterns and multiple '**' globs.

* Adjusts label.

* go fmt.

* go mod tidy.

* Fixes merge conflict.

* Refactoring and formatting.

* Cleans up unused vars and functions.

* Allows dirs with a dot.

* Makes test nicer.

* Add dir tests.

* Moves the watch directive inside the worker directive.

* Adds debug log on special events.

* Removes line about symlinks.

* Hints at multiple possible --watch flags.

* Adds ./**/*.php as default watch configuration.

* Changes error to a warning.

* Changes the default to './**/*.{php,yaml,yml,twig,env}' and supports the {bracket} pattern.

* Fixes linting.

* Fixes merge conflict and adjust values.

* Adjusts values.

---------

Co-authored-by: a.stecher <a.stecher@sportradar.com>
Co-authored-by: Kévin Dunglas <kevin@dunglas.fr>
2024-10-07 13:17:24 +02:00
Rob Landers
aa585f7da0 handle worker failures gracefully (#1038)
* handle failures gracefully

* fix super-subtle race condition

* address feedback: panic instead of fatal log and make vars into consts

* pass the frankenphp context to worker-ready function

* reset backoff and failures on normal restart

* update docs

* add test and fix race condition

* fail sometimes but do not be pathological about it

* Use title case

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

* fix code style in php

* define lifecycle metrics

* ensure we update unregister the metrics and fix tests

* update caddy tests and fix typo

* update docs

* no need for this

---------

Co-authored-by: Kévin Dunglas <kevin@dunglas.fr>
2024-10-03 21:53:12 +02:00
Kévin Dunglas
a16076e082 perf: prevent useless logger allocs 2024-08-27 11:03:01 +02:00
Rob Landers
d532772355 fix: reset sapi response code (#964)
* fix: reset sapi response code
It turns out that the sapi response code is NOT reset between requests by the zend engine. This resets the code for cgi-based requests.
fixes: #960

* update response header test

* fix assertion

* appears to affect workers too
2024-08-11 22:34:50 +02:00
Kévin Dunglas
4537f27f67 fix: prevent crash when worker terminates after a file upload 2024-06-08 13:36:32 +02:00
Rob Landers
e127cf5e1c update test (#688)
This makes the file size 6mb-ish, more than the 2mb batching that PHP does. I verified this fails on 0e163a0 (main prior to #686).
2024-03-24 16:28:15 +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
d1a6e38438 chore: reset stats in benchmarks 2024-02-12 14:07:11 +01:00
Kévin Dunglas
6dee113a01 perf: add $_SERVER creation benchmark 2024-02-01 11:40:36 +01:00
Kévin Dunglas
fe7e9e7c79 fix: crash when using apache_request_headers() (#536)
* fix: potential crash when using apache_request_headers()

* refactor: better apache_request_headers
2024-02-01 10:00:11 +01:00
Kévin Dunglas
5a8e5f9518 feat: add apache_response_headers() function (#530) 2024-01-31 12:34:30 +01:00
Kévin Dunglas
49baf02035 feat: add go_apache_request_headers() 2024-01-23 23:14:24 +01:00
Kévin Dunglas
aa1d968dcf refactor: faster $_SERVER variables creation 2023-11-16 14:40:52 +01:00
Kévin Dunglas
12791636ee chore: add an echo server benchmark 2023-11-10 17:10:16 +01:00
Kévin Dunglas
c615fe0087 feat: add experimental CLI support (#239)
* feat: add CLI support

* updated

* debug

* fix tests

* Caddy php-cli command

* use thread

* $_SERVER and input streams support

* Update frankenphp.c

Co-authored-by: Francis Lavoie <lavofr@gmail.com>

---------

Co-authored-by: Francis Lavoie <lavofr@gmail.com>
2023-10-09 14:38:15 +02:00
Kévin Dunglas
69416cc0d6 test: prove that Fibers work if not calling Go 2023-09-28 19:04:57 +02:00