Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions .gitleaksignore
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
/github/workspace/docs/mercure.md:jwt:88
/github/workspace/docs/mercure.md:jwt:90
/github/workspace/docs/mercure.md:jwt:95
Comment thread
dunglas marked this conversation as resolved.
Comment thread
dunglas marked this conversation as resolved.
Comment thread
dunglas marked this conversation as resolved.
Comment thread
dunglas marked this conversation as resolved.
Comment thread
dunglas marked this conversation as resolved.
Comment thread
dunglas marked this conversation as resolved.
2 changes: 2 additions & 0 deletions .markdown-lint.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
---
MD010: false
MD013: false
MD025:
front_matter_title: ""
MD033: false
MD060: false
5 changes: 5 additions & 0 deletions docs/classic.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
---
title: FrankenPHP Classic Mode: Drop-in PHP-FPM Replacement
description: Run FrankenPHP in classic mode as a drop-in replacement for PHP-FPM or Apache mod_php, with a fixed or autoscaling thread pool serving PHP files directly.
---

# Using Classic Mode

Without any additional configuration, FrankenPHP operates in classic mode. In this mode, FrankenPHP functions like a traditional PHP server, directly serving PHP files. This makes it a seamless drop-in replacement for PHP-FPM or Apache with mod_php.
Expand Down
7 changes: 6 additions & 1 deletion docs/compile.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
---
title: Compile FrankenPHP from sources with libphp.so
description: Build FrankenPHP from source on Linux, macOS and FreeBSD, linking PHP as a shared library via xcaddy or go build, and add custom Caddy modules and extensions.
---

# Compile From Sources

This document explains how to create a FrankenPHP binary that will load PHP as a dynamic library.
Expand Down Expand Up @@ -36,7 +41,7 @@ cd php-*/
Then, run the `configure` script with the options needed for your platform.
The following `./configure` flags are mandatory, but you can add others, for example, to compile extensions or additional features.

#### Linux
#### Linux and FreeBSD

```console
./configure \
Expand Down
5 changes: 5 additions & 0 deletions docs/config.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
---
title: Configuring FrankenPHP With Caddyfile, php.ini, and Env Vars
description: Configure FrankenPHP and Caddy via Caddyfile, JSON, or environment variables, including PHP runtime tuning, worker mode, file watching, and module options.
---

# Configuration

FrankenPHP, Caddy as well as the [Mercure](mercure.md) and [Vulcain](https://vulcain.rocks) modules can be configured using [the formats supported by Caddy](https://caddyserver.com/docs/getting-started#your-first-config).
Expand Down
11 changes: 8 additions & 3 deletions docs/docker.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
---
title: FrankenPHP Docker Image: Build, Configure, Extend
description: Build custom FrankenPHP Docker images, install PHP extensions and Caddy modules, run as non-root, harden with distroless, and enable worker mode by default.
---

# Building Custom Docker Image

[FrankenPHP Docker images](https://hub.docker.com/r/dunglas/frankenphp) are based on [official PHP images](https://hub.docker.com/_/php/).
Expand All @@ -13,7 +18,7 @@ The tags follow this pattern: `dunglas/frankenphp:<frankenphp-version>-php<php-v

[Browse tags](https://hub.docker.com/r/dunglas/frankenphp/tags).

## How to Use The Images
## How to Use the FrankenPHP Docker Images

Create a `Dockerfile` in your project:

Expand All @@ -30,7 +35,7 @@ docker build -t my-php-app .
docker run -it --rm --name my-running-app my-php-app
```

## How to Tweak the Configuration
## How to Tweak the FrankenPHP Docker Configuration

For convenience, [a default `Caddyfile`](https://github.com/php/frankenphp/blob/main/caddy/frankenphp/Caddyfile) containing
useful environment variables is provided in the image.
Expand Down Expand Up @@ -198,7 +203,7 @@ USER ${USER}
Next, set the `SERVER_NAME` environment variable to use an unprivileged port.
Example: `:8000`

## Updates
## FrankenPHP Docker Image Updates

The Docker images are built:

Expand Down
5 changes: 5 additions & 0 deletions docs/early-hints.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
---
title: Sending HTTP 103 Early Hints from PHP with FrankenPHP
description: FrankenPHP natively supports the HTTP 103 Early Hints status code, letting PHP applications preload assets before the final response is ready.
---

# Early Hints

FrankenPHP natively supports the [103 Early Hints status code](https://developer.chrome.com/blog/early-hints/).
Expand Down
14 changes: 10 additions & 4 deletions docs/embed.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
---
title: Embedding PHP Apps as Standalone Binaries with FrankenPHP
description: How to package a PHP application (including Symfony or Laravel) as a self-contained static binary with FrankenPHP, the PHP interpreter, PHP extensions and Caddy.
---

# PHP Apps As Standalone Binaries

FrankenPHP has the ability to embed the source code and assets of PHP applications in a static, self-contained binary.
Expand Down Expand Up @@ -54,6 +59,7 @@ The easiest way to create a Linux binary is to use the Docker-based builder we p
1. Create a file named `static-build.Dockerfile` in the repository of your app:

```dockerfile
# static-build.Dockerfile
FROM --platform=linux/amd64 dunglas/frankenphp:static-builder-gnu
# If you intend to run the binary on musl-libc systems, use static-builder-musl instead

Expand Down Expand Up @@ -97,7 +103,7 @@ EMBED=/path/to/your/app ./build-static.sh

The resulting binary is the file named `frankenphp-<os>-<arch>` in the `dist/` directory.

## Using The Binary
## Using the Embedded Binary

This is it! The `my-app` file (or `dist/frankenphp-<os>-<arch>` on other OSes) contains your self-contained app!

Expand Down Expand Up @@ -125,18 +131,18 @@ You can also run the PHP CLI scripts embedded in your binary:
./my-app php-cli bin/console
```

## PHP Extensions
## PHP Extensions in the Embedded Binary
Comment thread
dunglas marked this conversation as resolved.
Outdated

By default, the script will build extensions required by the `composer.json` file of your project, if any.
If the `composer.json` file doesn't exist, the default extensions are built, as documented in [the static builds entry](static.md).

To customize the extensions, use the `PHP_EXTENSIONS` environment variable.

## Customizing The Build
## Customizing the Embedded Binary Build

[Read the static build documentation](static.md) to see how to customize the binary (extensions, PHP version...).

## Distributing The Binary
## Distributing the Embedded Binary

On Linux, the created binary is compressed using [UPX](https://upx.github.io).

Expand Down
29 changes: 19 additions & 10 deletions docs/extension-workers.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
---
title: FrankenPHP Extension Workers: Background PHP Thread Pools
description: Use FrankenPHP Extension Workers to run a dedicated PHP thread pool from a Go extension for queues, schedulers, event listeners, and custom protocols.
---

# Extension Workers

Extension Workers enable your [FrankenPHP extension](https://frankenphp.dev/docs/extensions/) to manage a dedicated pool of PHP threads for executing background tasks, handling asynchronous events, or implementing custom protocols. Useful for queue systems, event listeners, schedulers, etc.

## Registering the Worker
## Registering a FrankenPHP Extension Worker
Comment thread
dunglas marked this conversation as resolved.
Outdated

### Static Registration
### Static Worker Registration
Comment thread
dunglas marked this conversation as resolved.
Outdated

If you don't need to make the worker configurable by the user (fixed script path, fixed number of threads), you can simply register the worker in the `init()` function.

```go
// FrankenPHP extension worker static registration
package myextension

import (
Expand All @@ -35,23 +41,24 @@ func init() {

### In a Caddy Module (Configurable by the user)

If you plan to share your extension (like a generic queue or event listener), you should wrap it in a Caddy module. This allows users to configure the script path and thread count via their `Caddyfile`. This requires implementing the `caddy.Provisioner` interface and parsing the Caddyfile ([see an example](https://github.com/dunglas/frankenphp-queue/blob/989120d394d66dd6c8e2101cac73dd622fade334/caddy.go)).
If you plan to share your extension (like a generic queue or event listener), you should wrap it in a Caddy module. This allows users to configure the script path and thread count via their `Caddyfile`. This requires implementing the `caddy.Provisioner` interface and parsing the Caddyfile ([see the frankenphp-queue Caddy module example](https://github.com/dunglas/frankenphp-queue/blob/main/caddy.go)).

### In a Pure Go Application (Embedding)

If you are [embedding FrankenPHP in a standard Go application without caddy](https://pkg.go.dev/github.com/dunglas/frankenphp#example-ServeHTTP), you can register extension workers using `frankenphp.WithExtensionWorkers` when initializing options.

## Interacting with Workers
## Dispatching Tasks to FrankenPHP Extension Workers

Once the worker pool is active, you can dispatch tasks to it. This can be done inside [native functions exported to PHP](https://frankenphp.dev/docs/extensions/#writing-the-extension), or from any Go logic such as a cron scheduler, an event listener (MQTT, Kafka), or a any other goroutine.
Once the worker pool is active, you can dispatch tasks to it. This can be done inside [native functions exported to PHP](https://frankenphp.dev/docs/extensions/#writing-the-extension), or from any Go logic such as a cron scheduler, an event listener (MQTT, Kafka), or any other goroutine.

### Headless Mode : `SendMessage`

Use `SendMessage` to pass raw data directly to your worker script. This is ideal for queues or simple commands.

#### Example: An Async Queue Extension
#### Async Queue Extension Example

```go
// FrankenPHP extension: dispatch raw messages to a worker via SendMessage
// #include <Zend/zend_types.h>
import "C"
import (
Expand Down Expand Up @@ -83,6 +90,7 @@ func my_queue_push(data *C.zval) bool {
Use `SendRequest` if your extension needs to invoke a PHP script that expects a standard web environment (populating `$_SERVER`, `$_GET`, etc.).

```go
// FrankenPHP extension: invoke a worker PHP script via SendRequest (HTTP emulation)
// #include <Zend/zend_types.h>
import "C"
import (
Expand All @@ -109,13 +117,13 @@ func my_worker_http_request(path *C.zend_string) unsafe.Pointer {
}
```

## Worker Script
## FrankenPHP Extension Worker PHP Script

The PHP worker script runs in a loop and can handle both raw messages and HTTP requests.

```php
<?php
// Handle both raw messages and HTTP requests in the same loop
// FrankenPHP extension worker script: handles raw messages and HTTP requests
$handler = function ($payload = null) {
// Case 1: Message Mode
if ($payload !== null) {
Expand All @@ -131,7 +139,7 @@ while (frankenphp_handle_request($handler)) {
}
```

## Lifecycle Hooks
## FrankenPHP Extension Worker Lifecycle Hooks

FrankenPHP provides hooks to execute Go code at specific points in the lifecycle.

Expand All @@ -142,9 +150,10 @@ FrankenPHP provides hooks to execute Go code at specific points in the lifecycle
| **Thread** | `WithWorkerOnReady` | `func(threadID int)` | Per-thread setup. Called when a thread starts. Receives the Thread ID. |
| **Thread** | `WithWorkerOnShutdown` | `func(threadID int)` | Per-thread cleanup. Receives the Thread ID. |

### Example
### Lifecycle Hooks Example

```go
// FrankenPHP extension worker with lifecycle hooks
package myextension

import (
Expand Down
23 changes: 23 additions & 0 deletions docs/extensions.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
---
title: Writing PHP Extensions in Go with FrankenPHP
description: Build PHP extensions in Go using FrankenPHP, with an extension generator for boilerplate, a types API for PHP/Go conversion, and support for goroutines.
---

# Writing PHP Extensions in Go

With FrankenPHP, you can **write PHP extensions in Go**, which allows you to create **high-performance native functions** that can be called directly from PHP. Your applications can leverage any existing or new Go library, as well as the famous concurrency model of **goroutines right from your PHP code**.
Expand Down Expand Up @@ -47,6 +52,7 @@ tar xf php-*
Everything is now set up to write your native function in Go. Create a new file named `stringext.go`. Our first function will take a string as an argument, the number of times to repeat it, a boolean to indicate whether to reverse the string, and return the resulting string. This should look like this:

```go
// stringext.go
package example

// #include <Zend/zend_types.h>
Expand Down Expand Up @@ -180,6 +186,7 @@ If order or association are not needed, it's also possible to directly convert t
**Creating and manipulating arrays in Go:**

```go
// Converting between PHP arrays and Go maps/slices
package example

// #include <Zend/zend_types.h>
Expand Down Expand Up @@ -279,6 +286,7 @@ FrankenPHP provides a way to work with PHP callables using the `frankenphp.CallP
To showcase this, let's create our own `array_map()` function that takes a callable and an array, applies the callable to each element of the array, and returns a new array with the results:

```go
// Calling a PHP callable from a Go-defined extension function
// export_php:function my_array_map(array $data, callable $callback): array
func my_array_map(arr *C.zend_array, callback *C.zval) unsafe.Pointer {
goSlice, err := frankenphp.GoPackedArray[any](unsafe.Pointer(arr))
Expand Down Expand Up @@ -313,6 +321,7 @@ $result = my_array_map(['hello', 'world'], 'strtoupper');
The generator supports declaring **opaque classes** as Go structs, which can be used to create PHP objects. You can use the `//export_php:class` directive comment to define a PHP class. For example:

```go
// Declaring a PHP class backed by a Go struct
package example

//export_php:class User
Expand All @@ -339,6 +348,7 @@ This approach provides better encapsulation and prevents PHP code from accidenta
Since properties are not directly accessible, you **must define methods** to interact with your opaque classes. Use the `//export_php:method` directive to define behavior:

```go
// Defining methods on a Go-backed PHP class
package example

// #include <Zend/zend_types.h>
Expand Down Expand Up @@ -381,6 +391,7 @@ func (us *UserStruct) SetNamePrefix(prefix *C.zend_string) {
The generator supports nullable parameters using the `?` prefix in PHP signatures. When a parameter is nullable, it becomes a pointer in your Go function, allowing you to check if the value was `null` in PHP:

```go
// Handling nullable PHP parameters in a Go method
package example

// #include <Zend/zend_types.h>
Expand Down Expand Up @@ -455,6 +466,7 @@ The generator supports exporting Go constants to PHP using two directives: `//ex
Use the `//export_php:const` directive to create global PHP constants:

```go
// Exporting global PHP constants from Go
package example

//export_php:const
Expand All @@ -479,6 +491,7 @@ const (
Use the `//export_php:classconst ClassName` directive to create constants that belong to a specific PHP class:

```go
// Exporting PHP class constants from Go
package example

//export_php:classconst User
Expand Down Expand Up @@ -522,6 +535,7 @@ The directive supports various value types, including strings, integers, boolean
You can use constants just like you are used to in the Go code. For example, let's take the `repeat_this()` function we declared earlier and change the last argument to an integer:

```go
// Combining functions, classes, methods, and constants in one extension
package example

// #include <Zend/zend_types.h>
Expand Down Expand Up @@ -590,6 +604,7 @@ The generator supports organizing your PHP extension's functions, classes, and c
Use the `//export_php:namespace` directive at the top of your Go file to place all exported symbols under a specific namespace:

```go
// Placing exported symbols under a PHP namespace
//export_php:namespace My\Extension
package example

Expand Down Expand Up @@ -653,6 +668,7 @@ We'll see how to write a simple PHP extension in Go that defines a new native fu
In your module, you need to define a new native function that will be called from PHP. To do this, create a file with the name you want, for example, `extension.go`, and add the following code:

```go
// extension.go
package example

// #include "extension.h"
Expand Down Expand Up @@ -686,6 +702,7 @@ To allow PHP to call our function, we need to define a corresponding PHP functio

```php
<?php
// extension.stub.php

/** @generate-class-entries */

Expand All @@ -707,6 +724,7 @@ This script will generate a file named `extension_arginfo.h` that contains the n
Now, we need to write the bridge between Go and C. Create a file named `extension.h` in your module directory with the following content:

```c
// extension.h
#ifndef _EXTENSION_H
#define _EXTENSION_H

Expand All @@ -726,6 +744,7 @@ Next, create a file named `extension.c` that will perform the following steps:
Let's start by including the required headers:

```c
// extension.c
#include <php.h>
#include "extension.h"
#include "extension_arginfo.h"
Expand Down Expand Up @@ -774,6 +793,7 @@ To define the new PHP function, we will modify our `extension.stub.php` file to

```php
<?php
// extension.stub.php

/** @generate-class-entries */

Expand All @@ -792,6 +812,7 @@ function go_upper(string $string): string {}
By regenerating the stub file with the `gen_stub.php` script, the `extension_arginfo.h` file should look like this:

```c
// extension_arginfo.h (generated)
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_go_upper, 0, 1, IS_STRING, 0)
ZEND_ARG_TYPE_INFO(0, string, IS_STRING, 0)
ZEND_END_ARG_INFO()
Expand All @@ -813,6 +834,7 @@ Your Go function cannot directly accept a PHP string as a parameter. You need to
The header file remains simple:

```c
// extension.h
#ifndef _EXTENSION_H
#define _EXTENSION_H

Expand Down Expand Up @@ -848,6 +870,7 @@ There's only one thing left to do: implement the `go_upper` function in Go.
Our Go function will take a `*C.zend_string` as a parameter, convert it to a Go string using FrankenPHP's helper function, process it, and return the result as a new `*C.zend_string`. The helper functions handle all the memory management and conversion complexity for us.

```go
// extension.go
package example

// #include <Zend/zend_types.h>
Expand Down
5 changes: 5 additions & 0 deletions docs/github-actions.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
---
title: Building FrankenPHP Docker Images with GitHub Actions
description: Use GitHub Actions to automatically build and publish FrankenPHP Docker images to a registry on pull requests, merges to main, and tagged releases.
---

# Using GitHub Actions

This repository builds and deploys the Docker image to [Docker Hub](https://hub.docker.com/r/dunglas/frankenphp) on
Expand Down
Loading
Loading