-
Notifications
You must be signed in to change notification settings - Fork 467
docs: document using FrankenPHP as a Go library #2531
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
AlliBalliBaba
merged 1 commit into
php:refactor/phpserver
from
nicolas-grekas:library-docs
Jul 20, 2026
+100
−0
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,98 @@ | ||
| --- | ||
| title: Use FrankenPHP as a Go library | ||
| description: Embed PHP in any Go program with the FrankenPHP library, serve PHP scripts through net/http, and scope workers to server instances. | ||
| --- | ||
|
|
||
| # Use FrankenPHP as a Go library | ||
|
|
||
| FrankenPHP is not only a Caddy module: it can be embedded as a library in any Go program to execute PHP scripts with `net/http`. | ||
|
|
||
| The compilation requirements are the same as for [building FrankenPHP from source](compile.md): a PHP built with the embed SAPI and ZTS enabled, and the matching CGO flags. | ||
|
|
||
| ## Getting started | ||
|
|
||
| Create a `Server`, register it while initializing FrankenPHP, and use it as an `http.Handler`: | ||
|
|
||
| ```go | ||
| // Minimal FrankenPHP library usage | ||
|
alexandre-daubois marked this conversation as resolved.
|
||
| package main | ||
|
|
||
| import ( | ||
| "log" | ||
| "net/http" | ||
|
|
||
| "github.com/dunglas/frankenphp" | ||
| ) | ||
|
|
||
| func main() { | ||
| server, err := frankenphp.NewServer("public/", nil, nil, nil) | ||
| if err != nil { | ||
| log.Fatal(err) | ||
| } | ||
|
|
||
| if err := frankenphp.Init(frankenphp.WithServer(server)); err != nil { | ||
| log.Fatal(err) | ||
| } | ||
| defer frankenphp.Shutdown() | ||
|
|
||
| http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { | ||
| if err := server.ServeHTTP(w, r); err != nil { | ||
| http.Error(w, err.Error(), http.StatusInternalServerError) | ||
| } | ||
| }) | ||
| log.Fatal(http.ListenAndServe(":8080", nil)) | ||
| } | ||
| ``` | ||
|
|
||
| `NewServer()` takes the document root, the split path suffixes (defaults to `[".php"]`), environment variables made available to every request, and a `*slog.Logger` (defaults to the global logger). | ||
|
|
||
| `Init()` starts the PHP runtime and must be called exactly once before serving requests; `Shutdown()` stops it. Calling `Server.ServeHTTP()` before `Init()` or after `Shutdown()` returns `ErrNotRunning`. | ||
|
|
||
| ## Multiple servers | ||
|
|
||
| Several servers can be registered at once, each with its own document root, environment and logger. This mirrors what multiple `php_server` blocks do in a Caddyfile: | ||
|
|
||
| ```go | ||
| // Registering two servers with separate document roots | ||
|
alexandre-daubois marked this conversation as resolved.
|
||
| api, _ := frankenphp.NewServer("api/public/", nil, nil, nil) | ||
| admin, _ := frankenphp.NewServer("admin/public/", nil, nil, nil) | ||
|
|
||
| err := frankenphp.Init( | ||
| frankenphp.WithServer(api), | ||
| frankenphp.WithServer(admin), | ||
| ) | ||
| ``` | ||
|
|
||
| Requests served through `api.ServeHTTP()` only see the configuration (and, see below, the workers) of that server. | ||
|
|
||
| ## Workers | ||
|
|
||
| [Worker scripts](worker.md) are declared with `WithWorkers()`. A worker can be scoped to a server with `WithWorkerServerScope()`: only requests handled by this server instance will reach the worker. Requests are matched by script path, or by a custom matcher registered with `WithWorkerMatcher()`: | ||
|
|
||
| ```go | ||
| // Scoping workers to a server | ||
|
alexandre-daubois marked this conversation as resolved.
|
||
| server, _ := frankenphp.NewServer("public/", nil, nil, nil) | ||
|
|
||
| err := frankenphp.Init( | ||
| frankenphp.WithServer(server), | ||
| frankenphp.WithWorkers("app", "public/index.php", 4, | ||
| frankenphp.WithWorkerServerScope(server), | ||
| ), | ||
| frankenphp.WithWorkers("api", "public/api.php", 2, | ||
| frankenphp.WithWorkerServerScope(server), | ||
| frankenphp.WithWorkerMatcher(func(r *http.Request) bool { | ||
| return strings.HasPrefix(r.URL.Path, "/api/") | ||
| }), | ||
| ), | ||
| ) | ||
| ``` | ||
|
|
||
| Workers declared without a server scope are global: they match by file path on any server. | ||
|
|
||
| ## Per-request options | ||
|
|
||
| `Server.ServeHTTP()` accepts `RequestOption`s to override the server configuration for a single request, e.g. `WithRequestDocumentRoot()`, `WithRequestSplitPath()`, `WithRequestEnv()` or `WithRequestLogger()`. | ||
|
|
||
| ## Compatibility with the pre-Server API | ||
|
|
||
| The package-level `frankenphp.ServeHTTP()` function keeps working without registering any server: requests prepared with `frankenphp.NewRequestWithContext()` are executed on an internal fallback server carrying the global configuration. New code should prefer explicit `Server` instances. | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.