A Webserver (Apache, NGINX, …) using PHP via mod_php
or php-fpm
.
PHP and a Webserver in a single binary
Caddyfile:
{
# Enable FrankenPHP
frankenphp
order php_server before file_server
}
localhost {
# Enable compression (optional)
encode zstd br gzip
# Execute PHP files in the current directory and serve assets
php_server
}
Configure worker mode:
In this example FrankenPHP starts 2 workers per CPU
{
# ...
frankenphp {
worker ./public/index.php 2
}
# ...
}
Allow the browser to download resources or preconnect to a site before the final response was sent.
header('Link: </style.css>; rel=preload; as=style');
headers_send(103);
// Do sluggish stuff 🐌
Caddyfile example configuration:
localhost {
# ...
# Enable Mercure hub
mercure {
# Transport to use (default to Bolt)
transport_url {$MERCURE_TRANSPORT_URL:bolt:///data/mercure.db}
# Publisher JWT key
publisher_jwt {env.MERCURE_PUBLISHER_JWT_KEY} {env.MERCURE_PUBLISHER_JWT_ALG}
# Subscriber JWT key
subscriber_jwt {env.MERCURE_SUBSCRIBER_JWT_KEY} {env.MERCURE_SUBSCRIBER_JWT_ALG}
# Allow anonymous subscribers (double-check that it's what you want)
anonymous
# Enable the subscription API (double-check that it's what you want)
subscriptions
# Extra directives
{$MERCURE_EXTRA_DIRECTIVES}
# Development only, enables the mercure UI
# demo
}
}
Publish a message to all clients
$hubUrl = 'https://localhost:8788/.well-known/mercure';
$jwt = '<JWT>';
$defaults = HttpClientInterface::OPTIONS_DEFAULTS;
$client = HttpClient::create($defaults);
$hub = new Hub($hubUrl, new StaticTokenProvider($jwt), null, null, $client);
// The topic you want to publish to
$topic = '/chat/messages';
$update = new Update($topic, 'MESSAGE TO ALL CLIENTS');
$hub->publish($update);
Receive the message:
let endpoint = "https://localhost:8788/.well-known/mercure"
let topic = "/chat/messages";
let jwtToken = "<JWT>"
let eventSource = new EventSource(`${endpoint}?topic=${encodeURIComponent(topic)}`, {
headers: {
Authorization: `Bearer ${jwtToken}`,
},
});
let eventSource.onmessage = (event) => {
console.log("Received event:", event.data);
};
Enable it in the Caddyfile:
{
# ...
servers {
metrics
}
# ...
}
The exporter is available under http://localhost:2019/metrics
Example metric:
# HELP caddy_http_request_duration_seconds Histogram of round-trip request durations.
# TYPE caddy_http_request_duration_seconds histogram
caddy_http_request_duration_seconds_bucket{code="200",handler="subroute",method="GET",server="srv0",le="0.005"} 564
...
Scrape the metrics provided by FrankenPHP:
prometheus.yml:
scrape_configs:
- job_name: 'franky'
metrics_path: '/metrics'
static_configs:
- targets: [ "localhost:2019" ]
Restart without interrupting current users connection.
frankenphp reload -c Caddyfile
Wrap your PHP application into a self-contained binary.
FROM --platform=linux/amd64 dunglas/frankenphp:static-builder
# Copy your app
WORKDIR /go/src/app/dist/app
COPY . .
# Build the static binary
WORKDIR /go/src/app/
RUN EMBED=dist/app/ ./build-static.sh
Create the self-contained binary using Docker:
docker build -t static-app -f static-build.Dockerfile . --load
Extract the binary (frankenphp-mac-arm64)
docker cp $(docker create --name static-app-tmp static-app):/go/src/app/dist/frankenphp-linux-x86_64 my-app ; docker rm static-app-tmp
Docker:
docker run -v $PWD:/app/public \
-p 80:80 -p 443:443 -p 443:443/udp \
dunglas/frankenphp
Standalone Binary
./frankenphp run -c Caddyfile
Standalone Binary - CLI
Does not require additional PHP to be installed!
./frankenphp php-cli /path/to/your/script.php
Feature idea:
Using GoRoutines to run e.g. the TYPO3 MessageBus (symfony/messenger) and the Scheduler
Join the Chat
Demo: https://franky.knallimall.org/