mirror of
https://github.com/dunglas/frankenphp.git
synced 2025-12-24 13:38:11 +08:00
Compare commits
207 Commits
feat/memor
...
feat/frank
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
79e3a98a11 | ||
|
|
afc4463ab3 | ||
|
|
6749ddbde5 | ||
|
|
82ba882a4e | ||
|
|
4b1679e70f | ||
|
|
75ce2e22c2 | ||
|
|
5a43e9f4de | ||
|
|
5542044376 | ||
|
|
52b65311c2 | ||
|
|
2dc8048ad2 | ||
|
|
a59b649dac | ||
|
|
68a4548bf4 | ||
|
|
6f049f9a9c | ||
|
|
340b1fd1c2 | ||
|
|
c9329bd717 | ||
|
|
f54a1fa85e | ||
|
|
b4115ca9a2 | ||
|
|
14469d4a0a | ||
|
|
ee394756b1 | ||
|
|
5a260c430a | ||
|
|
b6fcab5a95 | ||
|
|
1e49586b0e | ||
|
|
b27cd1c986 | ||
|
|
c6483088c5 | ||
|
|
5a9785d0d9 | ||
|
|
c522b52804 | ||
|
|
9a8ad979e0 | ||
|
|
663aff7cc4 | ||
|
|
79f2b2347b | ||
|
|
bf5c98410b | ||
|
|
cf7541fde6 | ||
|
|
25491068df | ||
|
|
d72751b9fd | ||
|
|
8820e53819 | ||
|
|
d2b6f9e723 | ||
|
|
13fbe126ea | ||
|
|
afa7dafe1c | ||
|
|
39e22bd5e0 | ||
|
|
bbbfdb31b5 | ||
|
|
0b83602575 | ||
|
|
ecca9dc01d | ||
|
|
eb40c03a21 | ||
|
|
c2390e7c3b | ||
|
|
0d12a5162d | ||
|
|
a48db9422d | ||
|
|
ab0fcd80de | ||
|
|
2f7b987198 | ||
|
|
1d74b2caa8 | ||
|
|
92e92335e3 | ||
|
|
8f5f9e4c8b | ||
|
|
5b7fc5ec52 | ||
|
|
05220de0e3 | ||
|
|
3741782330 | ||
|
|
a6e1d3554d | ||
|
|
6f1b4f3bae | ||
|
|
cd540bda11 | ||
|
|
8125993001 | ||
|
|
8583afd83e | ||
|
|
d10a243f86 | ||
|
|
1ec37f6cc9 | ||
|
|
4ad5e870ec | ||
|
|
49d2e62996 | ||
|
|
8febee71af | ||
|
|
16814581f9 | ||
|
|
ffa52f7c8d | ||
|
|
4550027de4 | ||
|
|
7f8e43fd62 | ||
|
|
254c0a8a55 | ||
|
|
22cf94d556 | ||
|
|
a4dc93f831 | ||
|
|
c276a3f434 | ||
|
|
02a1c92b88 | ||
|
|
8092f4a35c | ||
|
|
b250bd9a07 | ||
|
|
99064ee3e1 | ||
|
|
58a728b790 | ||
|
|
66aa975d47 | ||
|
|
5e1ad5d797 | ||
|
|
96dd739064 | ||
|
|
729cf9bba1 | ||
|
|
c5752f9e3b | ||
|
|
ba36f92a35 | ||
|
|
d3589f9770 | ||
|
|
8e6a183bda | ||
|
|
855b3f93b1 | ||
|
|
f85ca1c2d2 | ||
|
|
a30ed2e9d3 | ||
|
|
565b3a9629 | ||
|
|
3701516e5e | ||
|
|
f36bd51163 | ||
|
|
45bba2101f | ||
|
|
87315a19ae | ||
|
|
3bc426482a | ||
|
|
341b0240c9 | ||
|
|
432824edf1 | ||
|
|
9cca12858b | ||
|
|
cc473ee03e | ||
|
|
93266dfcad | ||
|
|
6203d207fa | ||
|
|
424ca426cb | ||
|
|
a9cf944b62 | ||
|
|
8d9ce15849 | ||
|
|
409c0fdf5f | ||
|
|
f50248a7d2 | ||
|
|
09b8219ad4 | ||
|
|
f2bae25a78 | ||
|
|
3dd90a3071 | ||
|
|
c57f741d83 | ||
|
|
3ba4e257a1 | ||
|
|
619c903386 | ||
|
|
78824107f0 | ||
|
|
f64c0f948e | ||
|
|
db3e1a047c | ||
|
|
80f13f07ea | ||
|
|
072151dfee | ||
|
|
965fa6570c | ||
|
|
251567a617 | ||
|
|
57e7747b9b | ||
|
|
f109f0403b | ||
|
|
d407dbd498 | ||
|
|
d970309544 | ||
|
|
30bf69cbe5 | ||
|
|
f61bc180c4 | ||
|
|
9f5e7a9eaa | ||
|
|
a5ca60da44 | ||
|
|
1c097a6fdf | ||
|
|
233753ca6b | ||
|
|
9dd05b0b1b | ||
|
|
4c92633396 | ||
|
|
be2e4714f5 | ||
|
|
941f218a79 | ||
|
|
7bd6ca89b0 | ||
|
|
5342d34126 | ||
|
|
dd250e3bda | ||
|
|
7e39e0a201 | ||
|
|
05aafc7c44 | ||
|
|
eee1de147e | ||
|
|
ece420c569 | ||
|
|
2f4c8310e2 | ||
|
|
d712aed2a5 | ||
|
|
d0b259df42 | ||
|
|
0681c63bc9 | ||
|
|
92e6b48156 | ||
|
|
e53ba345a1 | ||
|
|
34dfd8789a | ||
|
|
16bb790d52 | ||
|
|
1e56edceb8 | ||
|
|
f05f3b3d13 | ||
|
|
c3031ea07f | ||
|
|
39a88c3e83 | ||
|
|
19344a0dfe | ||
|
|
5b86f2c554 | ||
|
|
fd6cc7148d | ||
|
|
72120d7a2c | ||
|
|
479ba0a063 | ||
|
|
2b7b3d1e4b | ||
|
|
ec8eea0c7d | ||
|
|
c2ca4dbf03 | ||
|
|
2276129c6d | ||
|
|
045ce00c15 | ||
|
|
43c1de2372 | ||
|
|
5a148342b0 | ||
|
|
2f93baf984 | ||
|
|
7aaea72f14 | ||
|
|
028bad3e54 | ||
|
|
851ff9976e | ||
|
|
07622be221 | ||
|
|
d8f393900b | ||
|
|
e2687dbeb9 | ||
|
|
43984c3990 | ||
|
|
e874ea8710 | ||
|
|
92f95342d1 | ||
|
|
0fc6ccc5ce | ||
|
|
13eb9e8534 | ||
|
|
cb37c3d66d | ||
|
|
f288c3688e | ||
|
|
8cf6616ed6 | ||
|
|
a3e5af523c | ||
|
|
1bebb12ad9 | ||
|
|
57bc54864e | ||
|
|
d276032e20 | ||
|
|
20eaecf325 | ||
|
|
b16b60b053 | ||
|
|
85c273543d | ||
|
|
ec99f6a761 | ||
|
|
79ab84dad7 | ||
|
|
c0e0c2d07f | ||
|
|
fbbc129e4d | ||
|
|
f592e0f47b | ||
|
|
2676bffa98 | ||
|
|
047ce0c8b2 | ||
|
|
2f3e4b650b | ||
|
|
61922473ac | ||
|
|
fc6be5d09e | ||
|
|
9e2433e39b | ||
|
|
8a199bb4d7 | ||
|
|
fd49eade1a | ||
|
|
a396e64ad6 | ||
|
|
15ad1b4cb4 | ||
|
|
ccf2af7ab2 | ||
|
|
6d123c7e66 | ||
|
|
a1797c49b0 | ||
|
|
1e279bc348 | ||
|
|
449a0e7191 | ||
|
|
08e99fc85f | ||
|
|
2d6a299dbc | ||
|
|
b4748ee110 |
@@ -1,14 +1,11 @@
|
||||
# ignored
|
||||
**/*
|
||||
|
||||
# authorized
|
||||
!**/Caddyfile
|
||||
!**/*.go
|
||||
!**/go.*
|
||||
!**/*.c
|
||||
!**/*.h
|
||||
!testdata/*.php
|
||||
!testdata/*.txt
|
||||
!build-static.sh
|
||||
!app.tar
|
||||
!app_checksum.txt
|
||||
/caddy/frankenphp/frankenphp
|
||||
/internal/testserver/testserver
|
||||
/internal/testcli/testcli
|
||||
/dist
|
||||
.DS_Store
|
||||
.idea/
|
||||
.vscode/
|
||||
__debug_bin
|
||||
frankenphp.dev.test
|
||||
caddy/frankenphp/Build
|
||||
*.log
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
2
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
@@ -74,7 +74,7 @@ body:
|
||||
attributes:
|
||||
label: PHP configuration
|
||||
description: |
|
||||
Please copy and paste the output of the `phpinfo()` function.
|
||||
Please copy and paste the output of the `phpinfo()` function -- remember to remove **sensitive information** like passwords, API keys, etc.
|
||||
render: shell
|
||||
validations:
|
||||
required: true
|
||||
|
||||
12
.github/actions/watcher/action.yaml
vendored
12
.github/actions/watcher/action.yaml
vendored
@@ -3,22 +3,19 @@ description: Install e-dant/watcher
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
-
|
||||
name: Determine e-dant/watcher version
|
||||
- name: Determine e-dant/watcher version
|
||||
id: determine-watcher-version
|
||||
run: echo version="$(gh release view --repo e-dant/watcher --json tagName --template '{{ .tagName }}')" >> "${GITHUB_OUTPUT}"
|
||||
shell: bash
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
-
|
||||
name: Cache e-dant/watcher
|
||||
- name: Cache e-dant/watcher
|
||||
id: cache-watcher
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: watcher/target
|
||||
key: watcher-${{ runner.os }}-${{ runner.arch }}-${{ steps.determine-watcher-version.outputs.version }}-${{ env.CC && env.CC || 'gcc' }}
|
||||
-
|
||||
if: steps.cache-watcher.outputs.cache-hit != 'true'
|
||||
- if: steps.cache-watcher.outputs.cache-hit != 'true'
|
||||
name: Compile e-dant/watcher
|
||||
run: |
|
||||
mkdir watcher
|
||||
@@ -30,8 +27,7 @@ runs:
|
||||
shell: bash
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
-
|
||||
name: Update LD_LIBRARY_PATH
|
||||
- name: Update LD_LIBRARY_PATH
|
||||
run: |
|
||||
sudo sh -c "echo ${PWD}/watcher/target/lib > /etc/ld.so.conf.d/watcher.conf"
|
||||
sudo ldconfig
|
||||
|
||||
28
.github/dependabot.yaml
vendored
28
.github/dependabot.yaml
vendored
@@ -1,31 +1,33 @@
|
||||
---
|
||||
version: 2
|
||||
updates:
|
||||
-
|
||||
package-ecosystem: gomod
|
||||
- package-ecosystem: gomod
|
||||
directory: /
|
||||
schedule:
|
||||
interval: weekly
|
||||
commit-message:
|
||||
prefix: chore
|
||||
-
|
||||
package-ecosystem: gomod
|
||||
groups:
|
||||
go-modules:
|
||||
patterns:
|
||||
- "*"
|
||||
- package-ecosystem: gomod
|
||||
directory: /caddy
|
||||
schedule:
|
||||
interval: weekly
|
||||
commit-message:
|
||||
prefix: chore(caddy)
|
||||
# These packages must be in sync with versions
|
||||
# used by github.com/caddyserver/caddy/v2
|
||||
ignore:
|
||||
-
|
||||
dependency-name: github.com/google/cel-go
|
||||
-
|
||||
dependency-name: github.com/quic-go/*
|
||||
-
|
||||
package-ecosystem: github-actions
|
||||
groups:
|
||||
go-modules:
|
||||
patterns:
|
||||
- "*"
|
||||
- package-ecosystem: github-actions
|
||||
directory: /
|
||||
schedule:
|
||||
interval: weekly
|
||||
commit-message:
|
||||
prefix: ci
|
||||
groups:
|
||||
github-actions:
|
||||
patterns:
|
||||
- "*"
|
||||
|
||||
124
.github/workflows/docker.yaml
vendored
124
.github/workflows/docker.yaml
vendored
@@ -8,7 +8,7 @@ on:
|
||||
branches:
|
||||
- main
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
- "docs/**"
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
@@ -18,18 +18,18 @@ on:
|
||||
inputs:
|
||||
#checkov:skip=CKV_GHA_7
|
||||
version:
|
||||
description: 'FrankenPHP version'
|
||||
description: "FrankenPHP version"
|
||||
required: false
|
||||
type: string
|
||||
schedule:
|
||||
- cron: '0 4 * * *'
|
||||
- cron: "0 4 * * *"
|
||||
permissions:
|
||||
contents: read
|
||||
env:
|
||||
IMAGE_NAME: ${{ (github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && inputs.version) || startsWith(github.ref, 'refs/tags/')) && 'dunglas/frankenphp' || 'dunglas/frankenphp-dev' }}
|
||||
jobs:
|
||||
prepare:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-24.04
|
||||
outputs:
|
||||
# Push if it's a scheduled job, a tag, or if we're committing to the main branch
|
||||
push: ${{ (github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && inputs.version) || startsWith(github.ref, 'refs/tags/') || (github.ref == 'refs/heads/main' && github.event_name != 'pull_request')) && true || false }}
|
||||
@@ -42,18 +42,19 @@ jobs:
|
||||
skip: ${{ steps.check.outputs.skip }}
|
||||
ref: ${{ steps.check.outputs.ref || (github.event_name == 'workflow_dispatch' && inputs.version) || '' }}
|
||||
steps:
|
||||
-
|
||||
name: Check PHP versions
|
||||
- name: Check PHP versions
|
||||
id: check
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
PHP_82_LATEST=$(skopeo inspect docker://docker.io/library/php:8.2 --override-os linux --override-arch amd64 | jq -r '.Env[] | select(test("^PHP_VERSION=")) | sub("^PHP_VERSION="; "")')
|
||||
PHP_83_LATEST=$(skopeo inspect docker://docker.io/library/php:8.3 --override-os linux --override-arch amd64 | jq -r '.Env[] | select(test("^PHP_VERSION=")) | sub("^PHP_VERSION="; "")')
|
||||
PHP_84_LATEST=$(skopeo inspect docker://docker.io/library/php:8.4 --override-os linux --override-arch amd64 | jq -r '.Env[] | select(test("^PHP_VERSION=")) | sub("^PHP_VERSION="; "")')
|
||||
{
|
||||
echo php_version="${PHP_83_LATEST},${PHP_82_LATEST}"
|
||||
echo php_version="${PHP_82_LATEST},${PHP_83_LATEST},${PHP_84_LATEST}"
|
||||
echo php82_version="${PHP_82_LATEST//./-}"
|
||||
echo php83_version="${PHP_83_LATEST//./-}"
|
||||
echo php84_version="${PHP_84_LATEST//./-}"
|
||||
} >> "${GITHUB_OUTPUT}"
|
||||
|
||||
# Check if the Docker images must be rebuilt
|
||||
@@ -64,8 +65,9 @@ jobs:
|
||||
|
||||
FRANKENPHP_82_LATEST=$(skopeo inspect docker://docker.io/dunglas/frankenphp:php8.2 --override-os linux --override-arch amd64 | jq -r '.Env[] | select(test("^PHP_VERSION=")) | sub("^PHP_VERSION="; "")')
|
||||
FRANKENPHP_83_LATEST=$(skopeo inspect docker://docker.io/dunglas/frankenphp:php8.3 --override-os linux --override-arch amd64 | jq -r '.Env[] | select(test("^PHP_VERSION=")) | sub("^PHP_VERSION="; "")')
|
||||
FRANKENPHP_84_LATEST=$(skopeo inspect docker://docker.io/dunglas/frankenphp:php8.4 --override-os linux --override-arch amd64 | jq -r '.Env[] | select(test("^PHP_VERSION=")) | sub("^PHP_VERSION="; "")')
|
||||
|
||||
if [[ "${FRANKENPHP_82_LATEST}" == "${PHP_82_LATEST}" ]] && [[ "${FRANKENPHP_83_LATEST}" == "${PHP_83_LATEST}" ]]; then
|
||||
if [[ "${FRANKENPHP_82_LATEST}" == "${PHP_82_LATEST}" ]] && [[ "${FRANKENPHP_83_LATEST}" == "${PHP_83_LATEST}" ]] && [[ "${FRANKENPHP_84_LATEST}" == "${PHP_84_LATEST}" ]]; then
|
||||
echo skip=true >> "${GITHUB_OUTPUT}"
|
||||
exit 0
|
||||
fi
|
||||
@@ -74,20 +76,19 @@ jobs:
|
||||
echo ref="$(gh release view --repo dunglas/frankenphp --json tagName --jq '.tagName')"
|
||||
echo skip=false
|
||||
} >> "${GITHUB_OUTPUT}"
|
||||
-
|
||||
uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v4
|
||||
if: ${{ !fromJson(steps.check.outputs.skip) }}
|
||||
with:
|
||||
ref: ${{ steps.check.outputs.ref }}
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
- name: Set up Docker Buildx
|
||||
if: ${{ !fromJson(steps.check.outputs.skip) }}
|
||||
uses: docker/setup-buildx-action@v3
|
||||
-
|
||||
name: Create variants matrix
|
||||
- name: Create variants matrix
|
||||
if: ${{ !fromJson(steps.check.outputs.skip) }}
|
||||
id: matrix
|
||||
shell: bash
|
||||
run: |
|
||||
set -e
|
||||
METADATA="$(docker buildx bake --print | jq -c)"
|
||||
{
|
||||
echo metadata="${METADATA}"
|
||||
@@ -99,7 +100,7 @@ jobs:
|
||||
VERSION: ${{ (github.ref_type == 'tag' && github.ref_name) || steps.check.outputs.ref || 'dev' }}
|
||||
PHP_VERSION: ${{ steps.check.outputs.php_version }}
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ${{ startsWith(matrix.platform, 'linux/arm') && 'ubuntu-24.04-arm' || 'ubuntu-24.04' }}
|
||||
needs:
|
||||
- prepare
|
||||
if: ${{ !fromJson(needs.prepare.outputs.skip) }}
|
||||
@@ -109,51 +110,37 @@ jobs:
|
||||
variant: ${{ fromJson(needs.prepare.outputs.variants) }}
|
||||
platform: ${{ fromJson(needs.prepare.outputs.platforms) }}
|
||||
include:
|
||||
-
|
||||
race: ""
|
||||
qemu: true
|
||||
-
|
||||
platform: linux/amd64
|
||||
qemu: false
|
||||
- race: ""
|
||||
- platform: linux/amd64
|
||||
race: "-race" # The Go race detector is only supported on amd64
|
||||
-
|
||||
platform: linux/386
|
||||
qemu: false
|
||||
exclude:
|
||||
# arm/v6 is only available for Alpine: https://github.com/docker-library/golang/issues/502
|
||||
-
|
||||
variant: php-${{ needs.prepare.outputs.php82_version }}-bookworm
|
||||
- variant: php-${{ needs.prepare.outputs.php82_version }}-bookworm
|
||||
platform: linux/arm/v6
|
||||
-
|
||||
variant: php-${{ needs.prepare.outputs.php83_version }}-bookworm
|
||||
- variant: php-${{ needs.prepare.outputs.php83_version }}-bookworm
|
||||
platform: linux/arm/v6
|
||||
steps:
|
||||
-
|
||||
uses: actions/checkout@v4
|
||||
- name: Prepare
|
||||
id: prepare
|
||||
run: |
|
||||
platform=${{ matrix.platform }}
|
||||
echo "sanitized_platform=${platform//\//-}" >> "${GITHUB_OUTPUT}"
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ needs.prepare.outputs.ref }}
|
||||
-
|
||||
name: Set up QEMU
|
||||
if: matrix.qemu
|
||||
uses: docker/setup-qemu-action@v3
|
||||
with:
|
||||
platforms: ${{ matrix.platform }}
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
with:
|
||||
platforms: ${{ matrix.platform }}
|
||||
-
|
||||
name: Login to DockerHub
|
||||
if: fromJson(needs.prepare.outputs.push)
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v3
|
||||
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
|
||||
with:
|
||||
username: ${{ secrets.REGISTRY_USERNAME }}
|
||||
password: ${{ secrets.REGISTRY_PASSWORD }}
|
||||
-
|
||||
name: Build
|
||||
- name: Build
|
||||
id: build
|
||||
uses: docker/bake-action@v5
|
||||
uses: docker/bake-action@v6
|
||||
with:
|
||||
pull: true
|
||||
load: ${{ !fromJson(needs.prepare.outputs.push) }}
|
||||
@@ -176,8 +163,7 @@ jobs:
|
||||
SHA: ${{ github.sha }}
|
||||
VERSION: ${{ (github.ref_type == 'tag' && github.ref_name) || needs.prepare.outputs.ref || 'dev' }}
|
||||
PHP_VERSION: ${{ needs.prepare.outputs.php_version }}
|
||||
-
|
||||
# Workaround for https://github.com/actions/runner/pull/2477#issuecomment-1501003600
|
||||
- # Workaround for https://github.com/actions/runner/pull/2477#issuecomment-1501003600
|
||||
name: Export metadata
|
||||
if: fromJson(needs.prepare.outputs.push)
|
||||
run: |
|
||||
@@ -190,27 +176,24 @@ jobs:
|
||||
touch "/tmp/metadata/runner/${runnerDigest#sha256:}"
|
||||
env:
|
||||
METADATA: ${{ steps.build.outputs.metadata }}
|
||||
-
|
||||
name: Upload builder metadata
|
||||
- name: Upload builder metadata
|
||||
if: fromJson(needs.prepare.outputs.push)
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: metadata-builder-${{ matrix.variant }}
|
||||
name: metadata-builder-${{ matrix.variant }}-${{ steps.prepare.outputs.sanitized_platform }}
|
||||
path: /tmp/metadata/builder/*
|
||||
if-no-files-found: error
|
||||
retention-days: 1
|
||||
-
|
||||
name: Upload runner metadata
|
||||
- name: Upload runner metadata
|
||||
if: fromJson(needs.prepare.outputs.push)
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: metadata-runner-${{ matrix.variant }}
|
||||
name: metadata-runner-${{ matrix.variant }}-${{ steps.prepare.outputs.sanitized_platform }}
|
||||
path: /tmp/metadata/runner/*
|
||||
if-no-files-found: error
|
||||
retention-days: 1
|
||||
-
|
||||
name: Run tests
|
||||
if: ${{ !matrix.qemu && !fromJson(needs.prepare.outputs.push) }}
|
||||
- name: Run tests
|
||||
if: ${{ !fromJson(needs.prepare.outputs.push) }}
|
||||
run: |
|
||||
docker run --platform=${{ matrix.platform }} --rm \
|
||||
"$(jq -r '."builder-${{ matrix.variant }}"."containerimage.config.digest"' <<< "${METADATA}")" \
|
||||
@@ -219,7 +202,7 @@ jobs:
|
||||
METADATA: ${{ steps.build.outputs.metadata }}
|
||||
# Adapted from https://docs.docker.com/build/ci/github-actions/multi-platform/
|
||||
push:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-24.04
|
||||
needs:
|
||||
- prepare
|
||||
- build
|
||||
@@ -228,25 +211,23 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
variant: ${{ fromJson(needs.prepare.outputs.variants) }}
|
||||
target: ['builder', 'runner']
|
||||
target: ["builder", "runner"]
|
||||
steps:
|
||||
-
|
||||
name: Download metadata
|
||||
uses: actions/download-artifact@v3
|
||||
- name: Download metadata
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: metadata-${{ matrix.target }}-${{ matrix.variant }}
|
||||
pattern: metadata-${{ matrix.target }}-${{ matrix.variant }}-*
|
||||
path: /tmp/metadata
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
merge-multiple: true
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
-
|
||||
name: Login to DockerHub
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v3
|
||||
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
|
||||
with:
|
||||
username: ${{ secrets.REGISTRY_USERNAME }}
|
||||
password: ${{ secrets.REGISTRY_PASSWORD }}
|
||||
-
|
||||
name: Create manifest list and push
|
||||
- name: Create manifest list and push
|
||||
working-directory: /tmp/metadata
|
||||
run: |
|
||||
set -x
|
||||
@@ -255,10 +236,9 @@ jobs:
|
||||
$(printf "${IMAGE_NAME}@sha256:%s " *)
|
||||
env:
|
||||
METADATA: ${{ needs.prepare.outputs.metadata }}
|
||||
-
|
||||
name: Inspect image
|
||||
- name: Inspect image
|
||||
run: |
|
||||
# shellcheck disable=SC2046,SC2086
|
||||
docker buildx imagetools inspect $(jq -cr '.target."${{ matrix.target }}-${{ matrix.variant }}".tags | first' <<< ${METADATA})
|
||||
docker buildx imagetools inspect $(jq -cr '.target."${{ matrix.target }}-${{ matrix.variant }}".tags | first' <<< ${METADATA})
|
||||
env:
|
||||
METADATA: ${{ needs.prepare.outputs.metadata }}
|
||||
|
||||
15
.github/workflows/lint.yaml
vendored
15
.github/workflows/lint.yaml
vendored
@@ -10,23 +10,19 @@ on:
|
||||
permissions:
|
||||
contents: read
|
||||
packages: read
|
||||
statuses: write
|
||||
statuses: write
|
||||
jobs:
|
||||
build:
|
||||
name: Lint Code Base
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
-
|
||||
name: Checkout Code
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
-
|
||||
name: Lint Code Base
|
||||
uses: super-linter/super-linter/slim@v6.8.0
|
||||
- name: Lint Code Base
|
||||
uses: super-linter/super-linter/slim@v7.4.0
|
||||
env:
|
||||
VALIDATE_ALL_CODEBASE: true
|
||||
DEFAULT_BRANCH: main
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
LINTER_RULES_PATH: /
|
||||
MARKDOWN_CONFIG_FILE: .markdown-lint.yaml
|
||||
@@ -38,6 +34,9 @@ jobs:
|
||||
VALIDATE_PHP_PHPSTAN: false
|
||||
VALIDATE_PHP_PSALM: false
|
||||
VALIDATE_TERRAGRUNT: false
|
||||
VALIDATE_DOCKERFILE_HADOLINT: false
|
||||
# Prettier and StandardJS are incompatible
|
||||
VALIDATE_JAVASCRIPT_PRETTIER: false
|
||||
VALIDATE_TYPESCRIPT_PRETTIER: false
|
||||
# Conflicts with MARKDOWN
|
||||
VALIDATE_MARKDOWN_PRETTIER: false
|
||||
|
||||
47
.github/workflows/sanitizers.yaml
vendored
47
.github/workflows/sanitizers.yaml
vendored
@@ -5,14 +5,16 @@ on:
|
||||
branches:
|
||||
- main
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
- "docs/**"
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
- "docs/**"
|
||||
permissions:
|
||||
contents: read
|
||||
env:
|
||||
GOTOOLCHAIN: local
|
||||
jobs:
|
||||
# Adapted from https://github.com/beberlei/hdrhistogram-php
|
||||
sanitizers:
|
||||
@@ -21,7 +23,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
sanitizer: ['asan', 'msan']
|
||||
sanitizer: ["asan", "msan"]
|
||||
env:
|
||||
CFLAGS: -g -O0 -fsanitize=${{ matrix.sanitizer == 'asan' && 'address' || 'memory' }} -DZEND_TRACK_ARENA_ALLOC
|
||||
LDFLAGS: -fsanitize=${{ matrix.sanitizer == 'asan' && 'address' || 'memory' }}
|
||||
@@ -31,34 +33,28 @@ jobs:
|
||||
LIBRARY_PATH: ${{ github.workspace }}/php/target/lib:${{ github.workspace }}/watcher/target/lib
|
||||
LD_LIBRARY_PATH: ${{ github.workspace }}/php/target/lib
|
||||
steps:
|
||||
-
|
||||
name: Remove local PHP
|
||||
- name: Remove local PHP
|
||||
run: sudo apt-get remove --purge --autoremove 'php*' 'libmemcached*'
|
||||
-
|
||||
uses: actions/checkout@v4
|
||||
-
|
||||
uses: actions/setup-go@v5
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.22'
|
||||
go-version: "1.24"
|
||||
cache-dependency-path: |
|
||||
go.sum
|
||||
caddy/go.sum
|
||||
-
|
||||
name: Determine PHP version
|
||||
- name: Determine PHP version
|
||||
id: determine-php-version
|
||||
run: |
|
||||
curl -fsSL 'https://www.php.net/releases/index.php?json&max=1&version=8.3' -o version.json
|
||||
curl -fsSL 'https://www.php.net/releases/index.php?json&max=1&version=8.4' -o version.json
|
||||
echo version="$(jq -r 'keys[0]' version.json)" >> "$GITHUB_OUTPUT"
|
||||
echo archive="$(jq -r '.[] .source[] | select(.filename |endswith(".xz")) | "https://www.php.net/distributions/" + .filename' version.json)" >> "$GITHUB_OUTPUT"
|
||||
-
|
||||
name: Cache PHP
|
||||
- name: Cache PHP
|
||||
id: cache-php
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: php/target
|
||||
key: php-sanitizers-${{ matrix.sanitizer }}-${{ runner.arch }}-${{ steps.determine-php-version.outputs.version }}
|
||||
-
|
||||
if: steps.cache-php.outputs.cache-hit != 'true'
|
||||
- if: steps.cache-php.outputs.cache-hit != 'true'
|
||||
name: Compile PHP
|
||||
run: |
|
||||
mkdir php/
|
||||
@@ -92,22 +88,17 @@ jobs:
|
||||
--prefix="$(pwd)/target/"
|
||||
make -j"$(getconf _NPROCESSORS_ONLN)"
|
||||
make install
|
||||
-
|
||||
name: Add PHP to the PATH
|
||||
- name: Add PHP to the PATH
|
||||
run: echo "$(pwd)/php/target/bin" >> "$GITHUB_PATH"
|
||||
-
|
||||
name: Install e-dant/watcher
|
||||
- name: Install e-dant/watcher
|
||||
uses: ./.github/actions/watcher
|
||||
-
|
||||
name: Set Set CGO flags
|
||||
- name: Set Set CGO flags
|
||||
run: |
|
||||
{
|
||||
echo "CGO_CFLAGS=$CFLAGS -I${PWD}/watcher/target/include $(php-config --includes)"
|
||||
echo "CGO_LDFLAGS=$LDFLAGS $(php-config --ldflags) $(php-config --libs)"
|
||||
} >> "$GITHUB_ENV"
|
||||
-
|
||||
name: Compile tests
|
||||
- name: Compile tests
|
||||
run: go test ${{ matrix.sanitizer == 'msan' && '-tags=nowatcher' || '' }} -${{ matrix.sanitizer }} -v -x -c
|
||||
-
|
||||
name: Run tests
|
||||
run: ./frankenphp.test -test.v
|
||||
- name: Run tests
|
||||
run: ./frankenphp.dev.test -test.v
|
||||
|
||||
372
.github/workflows/static.yaml
vendored
372
.github/workflows/static.yaml
vendored
@@ -8,7 +8,7 @@ on:
|
||||
branches:
|
||||
- main
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
- "docs/**"
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
@@ -18,28 +18,29 @@ on:
|
||||
inputs:
|
||||
#checkov:skip=CKV_GHA_7
|
||||
version:
|
||||
description: 'FrankenPHP version'
|
||||
description: "FrankenPHP version"
|
||||
required: false
|
||||
type: string
|
||||
schedule:
|
||||
- cron: '0 0 * * *'
|
||||
- cron: "0 0 * * *"
|
||||
permissions:
|
||||
contents: write
|
||||
id-token: write
|
||||
attestations: write
|
||||
env:
|
||||
IMAGE_NAME: ${{ (github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && inputs.version) || startsWith(github.ref, 'refs/tags/')) && 'dunglas/frankenphp' || 'dunglas/frankenphp-dev' }}
|
||||
IMAGE_NAME: ${{ (github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && inputs.version) || startsWith(github.ref, 'refs/tags/')) && 'dunglas/frankenphp' || 'dunglas/frankenphp-dev' }}
|
||||
GOTOOLCHAIN: local
|
||||
jobs:
|
||||
prepare:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-24.04
|
||||
outputs:
|
||||
push: ${{ toJson((steps.check.outputs.ref || (github.event_name == 'workflow_dispatch' && inputs.version) || startsWith(github.ref, 'refs/tags/') || (github.ref == 'refs/heads/main' && github.event_name != 'pull_request')) && true || false) }}
|
||||
platforms: ${{ steps.matrix.outputs.platforms }}
|
||||
metadata: ${{ steps.matrix.outputs.metadata }}
|
||||
gnu_metadata: ${{ steps.matrix.outputs.gnu_metadata }}
|
||||
ref: ${{ steps.check.outputs.ref }}
|
||||
steps:
|
||||
-
|
||||
name: Get version
|
||||
- name: Get version
|
||||
id: check
|
||||
if: github.event_name == 'schedule'
|
||||
run: |
|
||||
@@ -51,26 +52,25 @@ jobs:
|
||||
echo "ref=${ref}" >> "${GITHUB_OUTPUT}"
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
-
|
||||
uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ steps.check.outputs.ref }}
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
-
|
||||
name: Create platforms matrix
|
||||
- name: Create platforms matrix
|
||||
id: matrix
|
||||
run: |
|
||||
METADATA="$(docker buildx bake --print static-builder | jq -c)"
|
||||
METADATA="$(docker buildx bake --print static-builder-musl | jq -c)"
|
||||
GNU_METADATA="$(docker buildx bake --print static-builder-gnu | jq -c)"
|
||||
{
|
||||
echo metadata="${METADATA}"
|
||||
echo platforms="$(jq -c 'first(.target[]) | .platforms' <<< "${METADATA}")"
|
||||
echo gnu_metadata="${GNU_METADATA}"
|
||||
} >> "${GITHUB_OUTPUT}"
|
||||
env:
|
||||
SHA: ${{ github.sha }}
|
||||
VERSION: ${{ steps.check.outputs.ref || 'dev' }}
|
||||
build-linux:
|
||||
build-linux-musl:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -78,200 +78,297 @@ jobs:
|
||||
debug: [false]
|
||||
mimalloc: [false]
|
||||
include:
|
||||
-
|
||||
qemu: true
|
||||
-
|
||||
platform: linux/amd64
|
||||
qemu: false
|
||||
-
|
||||
platform: linux/amd64
|
||||
qemu: false
|
||||
- platform: linux/amd64
|
||||
- platform: linux/amd64
|
||||
debug: true
|
||||
-
|
||||
platform: linux/amd64
|
||||
qemu: false
|
||||
- platform: linux/amd64
|
||||
mimalloc: true
|
||||
name: Build ${{ matrix.platform }} static binary${{ matrix.debug && ' (debug)' || '' }}${{ matrix.mimalloc && ' (mimalloc)' || '' }}
|
||||
runs-on: ubuntu-latest
|
||||
needs: [ prepare ]
|
||||
name: Build ${{ matrix.platform }} static musl binary${{ matrix.debug && ' (debug)' || '' }}${{ matrix.mimalloc && ' (mimalloc)' || '' }}
|
||||
runs-on: ${{ startsWith(matrix.platform, 'linux/arm') && 'ubuntu-24.04-arm' || 'ubuntu-24.04' }}
|
||||
needs: [prepare]
|
||||
steps:
|
||||
-
|
||||
uses: actions/checkout@v4
|
||||
- name: Prepare
|
||||
id: prepare
|
||||
run: |
|
||||
platform=${{ matrix.platform }}
|
||||
echo "sanitized_platform=${platform//\//-}" >> "${GITHUB_OUTPUT}"
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ needs.prepare.outputs.ref }}
|
||||
-
|
||||
name: Set up QEMU
|
||||
if: matrix.qemu
|
||||
uses: docker/setup-qemu-action@v3
|
||||
with:
|
||||
platforms: ${{ matrix.platform }}
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
with:
|
||||
platforms: ${{ matrix.platform }}
|
||||
-
|
||||
name: Login to DockerHub
|
||||
if: ${{ fromJson(needs.prepare.outputs.push) && !matrix.debug && !matrix.mimalloc }}
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v3
|
||||
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
|
||||
with:
|
||||
username: ${{ secrets.REGISTRY_USERNAME }}
|
||||
password: ${{ secrets.REGISTRY_PASSWORD }}
|
||||
-
|
||||
name: Build
|
||||
password: ${{ secrets.REGISTRY_PASSWORD }}
|
||||
- name: Build
|
||||
id: build
|
||||
uses: docker/bake-action@v5
|
||||
uses: docker/bake-action@v6
|
||||
with:
|
||||
pull: true
|
||||
load: ${{ !fromJson(needs.prepare.outputs.push) || matrix.debug || matrix.mimalloc }}
|
||||
targets: static-builder
|
||||
targets: static-builder-musl
|
||||
set: |
|
||||
${{ matrix.debug && 'static-builder.args.DEBUG_SYMBOLS=1' || '' }}
|
||||
${{ matrix.mimalloc && 'static-builder.args.MIMALLOC=1' || '' }}
|
||||
${{ (github.event_name == 'pull_request' || matrix.platform == 'linux/arm64') && 'static-builder.args.NO_COMPRESS=1' || '' }}
|
||||
${{ matrix.debug && 'static-builder-musl.args.DEBUG_SYMBOLS=1' || '' }}
|
||||
${{ matrix.mimalloc && 'static-builder-musl.args.MIMALLOC=1' || '' }}
|
||||
${{ (github.event_name == 'pull_request' || matrix.platform == 'linux/arm64') && 'static-builder-musl.args.NO_COMPRESS=1' || '' }}
|
||||
*.tags=
|
||||
*.platform=${{ matrix.platform }}
|
||||
*.cache-from=type=gha,scope=${{ needs.prepare.outputs.ref || github.ref }}-static-builder${{ matrix.debug && '-debug' || '' }}${{ matrix.mimalloc && '-mimalloc' || '' }}
|
||||
*.cache-from=type=gha,scope=refs/heads/main-static-builder${{ matrix.debug && '-debug' || '' }}${{ matrix.mimalloc && '-mimalloc' || '' }}
|
||||
*.cache-to=type=gha,scope=${{ needs.prepare.outputs.ref || github.ref }}-static-builder${{ matrix.debug && '-debug' || '' }}${{ matrix.mimalloc && '-mimalloc' || '' }},ignore-error=true
|
||||
*.cache-from=type=gha,scope=${{ needs.prepare.outputs.ref || github.ref }}-static-builder-musl${{ matrix.debug && '-debug' || '' }}${{ matrix.mimalloc && '-mimalloc' || '' }}
|
||||
*.cache-from=type=gha,scope=refs/heads/main-static-builder-musl${{ matrix.debug && '-debug' || '' }}${{ matrix.mimalloc && '-mimalloc' || '' }}
|
||||
*.cache-to=type=gha,scope=${{ needs.prepare.outputs.ref || github.ref }}-static-builder-musl${{ matrix.debug && '-debug' || '' }}${{ matrix.mimalloc && '-mimalloc' || '' }},ignore-error=true
|
||||
${{ (fromJson(needs.prepare.outputs.push) && !matrix.debug && !matrix.mimalloc) && format('*.output=type=image,name={0},push-by-digest=true,name-canonical=true,push=true', env.IMAGE_NAME) || '' }}
|
||||
env:
|
||||
SHA: ${{ github.sha }}
|
||||
VERSION: ${{ (github.ref_type == 'tag' && github.ref_name) || needs.prepare.outputs.ref || 'dev' }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
-
|
||||
# Workaround for https://github.com/actions/runner/pull/2477#issuecomment-1501003600
|
||||
- # Workaround for https://github.com/actions/runner/pull/2477#issuecomment-1501003600
|
||||
name: Export metadata
|
||||
if: fromJson(needs.prepare.outputs.push) && !matrix.debug && !matrix.mimalloc
|
||||
run: |
|
||||
mkdir -p /tmp/metadata
|
||||
|
||||
# shellcheck disable=SC2086
|
||||
digest=$(jq -r '."static-builder"."containerimage.digest"' <<< ${METADATA})
|
||||
digest=$(jq -r '."static-builder-musl"."containerimage.digest"' <<< ${METADATA})
|
||||
touch "/tmp/metadata/${digest#sha256:}"
|
||||
env:
|
||||
METADATA: ${{ steps.build.outputs.metadata }}
|
||||
-
|
||||
name: Upload metadata
|
||||
- name: Upload metadata
|
||||
if: fromJson(needs.prepare.outputs.push) && !matrix.debug && !matrix.mimalloc
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: metadata-static-builder
|
||||
name: metadata-static-builder-musl-${{ steps.prepare.outputs.sanitized_platform }}
|
||||
path: /tmp/metadata/*
|
||||
if-no-files-found: error
|
||||
retention-days: 1
|
||||
-
|
||||
name: Copy binary
|
||||
if: ${{ !fromJson(needs.prepare.outputs.push) || matrix.debug || matrix.mimalloc }}
|
||||
- name: Copy binary
|
||||
run: |
|
||||
digest=$(jq -r '."static-builder"."containerimage.config.digest"' <<< "${METADATA}")
|
||||
docker create --platform=${{ matrix.platform }} --name static-builder "${digest}"
|
||||
docker cp "static-builder:/go/src/app/dist/${BINARY}" "${BINARY}${{ matrix.debug && '-debug' || '' }}${{ matrix.mimalloc && '-mimalloc' || '' }}"
|
||||
# shellcheck disable=SC2034
|
||||
digest=$(jq -r '."static-builder-musl"."${{ (fromJson(needs.prepare.outputs.push) && !matrix.debug && !matrix.mimalloc) && 'containerimage.digest' || 'containerimage.config.digest' }}"' <<< "${METADATA}")
|
||||
docker create --platform=${{ matrix.platform }} --name static-builder-musl "${{ (fromJson(needs.prepare.outputs.push) && !matrix.debug && !matrix.mimalloc) && '${IMAGE_NAME}@${digest}' || '${digest}' }}"
|
||||
docker cp "static-builder-musl:/go/src/app/dist/${BINARY}" "${BINARY}${{ matrix.debug && '-debug' || '' }}${{ matrix.mimalloc && '-mimalloc' || '' }}"
|
||||
env:
|
||||
METADATA: ${{ steps.build.outputs.metadata }}
|
||||
BINARY: frankenphp-linux-${{ matrix.platform == 'linux/amd64' && 'x86_64' || 'aarch64' }}
|
||||
-
|
||||
name: Upload artifact
|
||||
- name: Upload artifact
|
||||
if: ${{ !fromJson(needs.prepare.outputs.push) }}
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: frankenphp-linux-${{ matrix.platform == 'linux/amd64' && 'x86_64' || 'aarch64' }}${{ matrix.debug && '-debug' || '' }}${{ matrix.mimalloc && '-mimalloc' || '' }}
|
||||
path: frankenphp-linux-${{ matrix.platform == 'linux/amd64' && 'x86_64' || 'aarch64' }}${{ matrix.debug && '-debug' || '' }}${{ matrix.mimalloc && '-mimalloc' || '' }}
|
||||
-
|
||||
name: Upload special assets
|
||||
if: fromJson(needs.prepare.outputs.push) && (matrix.debug || matrix.mimalloc) && (needs.prepare.outputs.ref || github.ref_type == 'tag')
|
||||
run: gh release upload "${{ (github.ref_type == 'tag' && github.ref_name) || needs.prepare.outputs.ref }}" frankenphp-linux-x86_64${{ matrix.debug && '-debug' || '' }}${{ matrix.mimalloc && '-mimalloc' || '' }} --repo dunglas/frankenphp --clobber
|
||||
- name: Upload assets
|
||||
if: fromJson(needs.prepare.outputs.push) && (needs.prepare.outputs.ref || github.ref_type == 'tag')
|
||||
run: gh release upload "${{ (github.ref_type == 'tag' && github.ref_name) || needs.prepare.outputs.ref }}" frankenphp-linux-${{ matrix.platform == 'linux/amd64' && 'x86_64' || 'aarch64' }}${{ matrix.debug && '-debug' || '' }}${{ matrix.mimalloc && '-mimalloc' || '' }} --repo dunglas/frankenphp --clobber
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
-
|
||||
if: fromJson(needs.prepare.outputs.push) && (matrix.debug || matrix.mimalloc) && (needs.prepare.outputs.ref || github.ref_type == 'tag')
|
||||
uses: actions/attest-build-provenance@v1
|
||||
- if: fromJson(needs.prepare.outputs.push) && (needs.prepare.outputs.ref || github.ref_type == 'tag')
|
||||
uses: actions/attest-build-provenance@v2
|
||||
with:
|
||||
subject-path: ${{ github.workspace }}/frankenphp-linux-x86_64${{ matrix.debug && '-debug' || '' }}${{ matrix.mimalloc && '-mimalloc' || '' }}
|
||||
subject-path: ${{ github.workspace }}/frankenphp-linux-*
|
||||
- name: Run sanity checks
|
||||
run: |
|
||||
"${BINARY}" version
|
||||
"${BINARY}" build-info
|
||||
"${BINARY}" list-modules | grep frankenphp
|
||||
"${BINARY}" list-modules | grep http.encoders.br
|
||||
"${BINARY}" list-modules | grep http.handlers.mercure
|
||||
"${BINARY}" list-modules | grep http.handlers.mercure
|
||||
"${BINARY}" list-modules | grep http.handlers.vulcain
|
||||
"${BINARY}" php-cli -r "echo 'Sanity check passed';"
|
||||
env:
|
||||
BINARY: ./frankenphp-linux-${{ matrix.platform == 'linux/amd64' && 'x86_64' || 'aarch64' }}${{ matrix.debug && '-debug' || '' }}${{ matrix.mimalloc && '-mimalloc' || '' }}
|
||||
|
||||
# Adapted from https://docs.docker.com/build/ci/github-actions/multi-platform/
|
||||
push:
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- prepare
|
||||
- build-linux
|
||||
if: fromJson(needs.prepare.outputs.push)
|
||||
build-linux-gnu:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform: ${{ fromJson(needs.prepare.outputs.platforms) }}
|
||||
name: Build ${{ matrix.platform }} static GNU binary
|
||||
runs-on: ${{ startsWith(matrix.platform, 'linux/arm') && 'ubuntu-24.04-arm' || 'ubuntu-24.04' }}
|
||||
needs: [prepare]
|
||||
steps:
|
||||
-
|
||||
name: Download metadata
|
||||
uses: actions/download-artifact@v3
|
||||
- name: Prepare
|
||||
id: prepare
|
||||
run: |
|
||||
platform=${{ matrix.platform }}
|
||||
echo "sanitized_platform=${platform//\//-}" >> "${GITHUB_OUTPUT}"
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
name: metadata-static-builder
|
||||
path: /tmp/metadata
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
ref: ${{ needs.prepare.outputs.ref }}
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
-
|
||||
name: Login to DockerHub
|
||||
with:
|
||||
platforms: ${{ matrix.platform }}
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v3
|
||||
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
|
||||
with:
|
||||
username: ${{ secrets.REGISTRY_USERNAME }}
|
||||
password: ${{ secrets.REGISTRY_PASSWORD }}
|
||||
-
|
||||
name: Create manifest list and push
|
||||
- name: Build
|
||||
id: build
|
||||
uses: docker/bake-action@v6
|
||||
with:
|
||||
pull: true
|
||||
load: ${{ !fromJson(needs.prepare.outputs.push) }}
|
||||
targets: static-builder-gnu
|
||||
set: |
|
||||
${{ (github.event_name == 'pull_request' || matrix.platform == 'linux/arm64') && 'static-builder-gnu.args.NO_COMPRESS=1' || '' }}
|
||||
static-builder-gnu.args.BUILD_PACKAGES=1
|
||||
*.tags=
|
||||
*.platform=${{ matrix.platform }}
|
||||
*.cache-from=type=gha,scope=${{ needs.prepare.outputs.ref || github.ref }}-static-builder-gnu
|
||||
*.cache-from=type=gha,scope=refs/heads/main-static-builder-gnu
|
||||
*.cache-to=type=gha,scope=${{ needs.prepare.outputs.ref || github.ref }}-static-builder-gnu,ignore-error=true
|
||||
${{ fromJson(needs.prepare.outputs.push) && format('*.output=type=image,name={0},push-by-digest=true,name-canonical=true,push=true', env.IMAGE_NAME) || '' }}
|
||||
env:
|
||||
SHA: ${{ github.sha }}
|
||||
VERSION: ${{ (github.ref_type == 'tag' && github.ref_name) || needs.prepare.outputs.ref || 'dev' }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
- # Workaround for https://github.com/actions/runner/pull/2477#issuecomment-1501003600
|
||||
name: Export metadata
|
||||
if: fromJson(needs.prepare.outputs.push)
|
||||
run: |
|
||||
mkdir -p /tmp/metadata-gnu
|
||||
|
||||
# shellcheck disable=SC2086
|
||||
digest=$(jq -r '."static-builder-gnu"."containerimage.digest"' <<< ${METADATA})
|
||||
touch "/tmp/metadata-gnu/${digest#sha256:}"
|
||||
env:
|
||||
METADATA: ${{ steps.build.outputs.metadata }}
|
||||
- name: Upload metadata
|
||||
if: fromJson(needs.prepare.outputs.push)
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: metadata-static-builder-gnu-${{ steps.prepare.outputs.sanitized_platform }}
|
||||
path: /tmp/metadata-gnu/*
|
||||
if-no-files-found: error
|
||||
retention-days: 1
|
||||
- name: Copy all frankenphp* files
|
||||
run: |
|
||||
# shellcheck disable=SC2034
|
||||
digest=$(jq -r '."static-builder-gnu"."${{ fromJson(needs.prepare.outputs.push) && 'containerimage.digest' || 'containerimage.config.digest' }}"' <<< "${METADATA}")
|
||||
container_id=$(docker create --platform=${{ matrix.platform }} "${{ fromJson(needs.prepare.outputs.push) && '${IMAGE_NAME}@${digest}' || '${digest}' }}")
|
||||
mkdir -p gh-output
|
||||
cd gh-output
|
||||
for file in $(docker run --rm "${{ fromJson(needs.prepare.outputs.push) && '${IMAGE_NAME}@${digest}' || '${digest}' }}" sh -c "ls /go/src/app/dist | grep '^frankenphp'"); do
|
||||
docker cp "${container_id}:/go/src/app/dist/${file}" "./${file}"
|
||||
done
|
||||
docker rm "${container_id}"
|
||||
mv "${BINARY}" "${BINARY}-gnu"
|
||||
env:
|
||||
METADATA: ${{ steps.build.outputs.metadata }}
|
||||
BINARY: frankenphp-linux-${{ matrix.platform == 'linux/amd64' && 'x86_64' || 'aarch64' }}
|
||||
- name: Upload artifact
|
||||
if: ${{ !fromJson(needs.prepare.outputs.push) }}
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: frankenphp-linux-${{ matrix.platform == 'linux/amd64' && 'x86_64' || 'aarch64' }}-gnu-files
|
||||
path: gh-output/*
|
||||
- name: Upload assets
|
||||
if: fromJson(needs.prepare.outputs.push) && (needs.prepare.outputs.ref || github.ref_type == 'tag')
|
||||
run: gh release upload "${{ (github.ref_type == 'tag' && github.ref_name) || needs.prepare.outputs.ref }}" gh-output/* --repo dunglas/frankenphp --clobber
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
- if: fromJson(needs.prepare.outputs.push) && (needs.prepare.outputs.ref || github.ref_type == 'tag')
|
||||
uses: actions/attest-build-provenance@v2
|
||||
with:
|
||||
subject-path: ${{ github.workspace }}/gh-output/frankenphp-linux-*-gnu
|
||||
- name: Run sanity checks
|
||||
run: |
|
||||
"${BINARY}" version
|
||||
"${BINARY}" list-modules | grep frankenphp
|
||||
"${BINARY}" list-modules | grep http.encoders.br
|
||||
"${BINARY}" list-modules | grep http.handlers.mercure
|
||||
"${BINARY}" list-modules | grep http.handlers.mercure
|
||||
"${BINARY}" list-modules | grep http.handlers.vulcain
|
||||
"${BINARY}" php-cli -r "echo 'Sanity check passed';"
|
||||
env:
|
||||
BINARY: ./gh-output/frankenphp-linux-${{ matrix.platform == 'linux/amd64' && 'x86_64' || 'aarch64' }}-gnu
|
||||
|
||||
# Adapted from https://docs.docker.com/build/ci/github-actions/multi-platform/
|
||||
push:
|
||||
runs-on: ubuntu-24.04
|
||||
needs:
|
||||
- prepare
|
||||
- build-linux-musl
|
||||
- build-linux-gnu
|
||||
if: fromJson(needs.prepare.outputs.push)
|
||||
steps:
|
||||
- name: Download metadata
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
pattern: metadata-static-builder-musl-*
|
||||
path: /tmp/metadata
|
||||
merge-multiple: true
|
||||
- name: Download GNU metadata
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
pattern: metadata-static-builder-gnu-*
|
||||
path: /tmp/metadata-gnu
|
||||
merge-multiple: true
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v3
|
||||
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
|
||||
with:
|
||||
username: ${{ secrets.REGISTRY_USERNAME }}
|
||||
password: ${{ secrets.REGISTRY_PASSWORD }}
|
||||
- name: Create manifest list and push
|
||||
working-directory: /tmp/metadata
|
||||
run: |
|
||||
# shellcheck disable=SC2046,SC2086
|
||||
docker buildx imagetools create $(jq -cr '.target."static-builder".tags | map("-t " + .) | join(" ")' <<< "${METADATA}") \
|
||||
docker buildx imagetools create $(jq -cr '.target."static-builder-musl".tags | map("-t " + .) | join(" ")' <<< "${METADATA}") \
|
||||
$(printf "${IMAGE_NAME}@sha256:%s " *)
|
||||
env:
|
||||
METADATA: ${{ needs.prepare.outputs.metadata }}
|
||||
-
|
||||
name: Inspect image
|
||||
- name: Create GNU manifest list and push
|
||||
working-directory: /tmp/metadata-gnu
|
||||
run: |
|
||||
# shellcheck disable=SC2046,SC2086
|
||||
docker buildx imagetools inspect "$(jq -cr '.target."static-builder".tags | first' <<< "${METADATA}")"
|
||||
docker buildx imagetools create $(jq -cr '.target."static-builder-gnu".tags | map("-t " + .) | join(" ")' <<< "${GNU_METADATA}") \
|
||||
$(printf "${IMAGE_NAME}@sha256:%s " *)
|
||||
env:
|
||||
METADATA: ${{ needs.prepare.outputs.metadata }}
|
||||
-
|
||||
name: Copy binary
|
||||
GNU_METADATA: ${{ needs.prepare.outputs.gnu_metadata }}
|
||||
- name: Inspect image
|
||||
run: |
|
||||
tag=$(jq -cr '.target."static-builder".tags | first' <<< "${METADATA}")
|
||||
docker cp "$(docker create --platform=linux/amd64 --name static-builder "${tag}"):/go/src/app/dist/frankenphp-linux-x86_64" frankenphp-linux-x86_64 ; docker rm static-builder
|
||||
docker cp "$(docker create --platform=linux/arm64 --name static-builder "${tag}"):/go/src/app/dist/frankenphp-linux-aarch64" frankenphp-linux-aarch64 ; docker rm static-builder
|
||||
# shellcheck disable=SC2046,SC2086
|
||||
docker buildx imagetools inspect "$(jq -cr '.target."static-builder-musl".tags | first' <<< "${METADATA}")"
|
||||
env:
|
||||
METADATA: ${{ needs.prepare.outputs.metadata }}
|
||||
-
|
||||
name: Upload asset
|
||||
if: needs.prepare.outputs.ref || github.ref_type == 'tag'
|
||||
run: gh release upload "${{ (github.ref_type == 'tag' && github.ref_name) || needs.prepare.outputs.ref }}" frankenphp-linux-x86_64 frankenphp-linux-aarch64 --repo dunglas/frankenphp --clobber
|
||||
- name: Inspect GNU image
|
||||
run: |
|
||||
# shellcheck disable=SC2046,SC2086
|
||||
docker buildx imagetools inspect "$(jq -cr '.target."static-builder-gnu".tags | first' <<< "${GNU_METADATA}")-gnu"
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
-
|
||||
if: needs.prepare.outputs.ref || github.ref_type == 'tag'
|
||||
uses: actions/attest-build-provenance@v1
|
||||
with:
|
||||
subject-path: ${{ github.workspace }}/frankenphp-linux-*
|
||||
GNU_METADATA: ${{ needs.prepare.outputs.gnu_metadata }}
|
||||
|
||||
build-mac:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform: ['arm64', 'x86_64']
|
||||
platform: ["arm64", "x86_64"]
|
||||
name: Build macOS ${{ matrix.platform }} binaries
|
||||
runs-on: ${{ matrix.platform == 'arm64' && 'macos-14' || 'macos-13' }}
|
||||
needs: [ prepare ]
|
||||
needs: [prepare]
|
||||
env:
|
||||
HOMEBREW_NO_AUTO_UPDATE: 1
|
||||
steps:
|
||||
-
|
||||
uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ needs.prepare.outputs.ref }}
|
||||
-
|
||||
uses: actions/setup-go@v5
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.22'
|
||||
go-version: "1.24"
|
||||
cache-dependency-path: |
|
||||
go.sum
|
||||
go.sum
|
||||
caddy/go.sum
|
||||
-
|
||||
name: Set FRANKENPHP_VERSION
|
||||
- name: Set FRANKENPHP_VERSION
|
||||
run: |
|
||||
if [ "${GITHUB_REF_TYPE}" == "tag" ]; then
|
||||
export FRANKENPHP_VERSION=${GITHUB_REF_NAME:1}
|
||||
@@ -282,22 +379,31 @@ jobs:
|
||||
fi
|
||||
|
||||
echo "FRANKENPHP_VERSION=${FRANKENPHP_VERSION}" >> "${GITHUB_ENV}"
|
||||
-
|
||||
name: Build FrankenPHP
|
||||
- name: Build FrankenPHP
|
||||
run: ./build-static.sh
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
RELEASE: ${{ (needs.prepare.outputs.ref || github.ref_type == 'tag') && '1' || '' }}
|
||||
NO_COMPRESS: ${{ github.event_name == 'pull_request' && '1' || '' }}
|
||||
-
|
||||
if: needs.prepare.outputs.ref || github.ref_type == 'tag'
|
||||
uses: actions/attest-build-provenance@v1
|
||||
- if: needs.prepare.outputs.ref || github.ref_type == 'tag'
|
||||
uses: actions/attest-build-provenance@v2
|
||||
with:
|
||||
subject-path: ${{ github.workspace }}/dist/frankenphp-mac-*
|
||||
-
|
||||
name: Upload artifact
|
||||
- name: Upload artifact
|
||||
if: github.ref_type == 'branch'
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: frankenphp-mac-${{ matrix.platform }}
|
||||
path: dist/frankenphp-mac-${{ matrix.platform }}
|
||||
- name: Run sanity checks
|
||||
run: |
|
||||
"${BINARY}" version
|
||||
"${BINARY}" build-info
|
||||
"${BINARY}" list-modules | grep frankenphp
|
||||
"${BINARY}" list-modules | grep http.encoders.br
|
||||
"${BINARY}" list-modules | grep http.handlers.mercure
|
||||
"${BINARY}" list-modules | grep http.handlers.mercure
|
||||
"${BINARY}" list-modules | grep http.handlers.vulcain
|
||||
"${BINARY}" php-cli -r "echo 'Sanity check passed';"
|
||||
env:
|
||||
BINARY: dist/frankenphp-mac-${{ matrix.platform }}
|
||||
|
||||
98
.github/workflows/tests.yaml
vendored
98
.github/workflows/tests.yaml
vendored
@@ -5,37 +5,37 @@ on:
|
||||
branches:
|
||||
- main
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
- "docs/**"
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
- "docs/**"
|
||||
permissions:
|
||||
contents: read
|
||||
env:
|
||||
GOTOOLCHAIN: local
|
||||
GOEXPERIMENT: cgocheck2
|
||||
jobs:
|
||||
tests:
|
||||
tests-linux:
|
||||
name: Tests (Linux, PHP ${{ matrix.php-versions }})
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
php-versions: ['8.2', '8.3', '8.4']
|
||||
php-versions: ["8.2", "8.3", "8.4"]
|
||||
env:
|
||||
GOEXPERIMENT: cgocheck2
|
||||
GOMAXPROCS: 10
|
||||
LIBRARY_PATH: ${{ github.workspace }}/watcher/target/lib
|
||||
steps:
|
||||
-
|
||||
uses: actions/checkout@v4
|
||||
-
|
||||
uses: actions/setup-go@v5
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.22'
|
||||
go-version: "1.24"
|
||||
cache-dependency-path: |
|
||||
go.sum
|
||||
caddy/go.sum
|
||||
-
|
||||
uses: shivammathur/setup-php@v2
|
||||
- uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: ${{ matrix.php-versions }}
|
||||
ini-file: development
|
||||
@@ -44,44 +44,68 @@ jobs:
|
||||
env:
|
||||
phpts: ts
|
||||
debug: true
|
||||
-
|
||||
name: Install e-dant/watcher
|
||||
- name: Install e-dant/watcher
|
||||
uses: ./.github/actions/watcher
|
||||
-
|
||||
name: Set Set CGO flags
|
||||
- name: Set CGO flags
|
||||
run: echo "CGO_CFLAGS=-I${PWD}/watcher/target/include $(php-config --includes)" >> "${GITHUB_ENV}"
|
||||
-
|
||||
name: Build
|
||||
run: go build
|
||||
-
|
||||
name: Build testcli binary
|
||||
- name: Build
|
||||
run: go build
|
||||
- name: Build testcli binary
|
||||
working-directory: internal/testcli/
|
||||
run: go build
|
||||
-
|
||||
name: Run library tests
|
||||
- name: Run library tests
|
||||
run: go test -race -v ./...
|
||||
-
|
||||
name: Run Caddy module tests
|
||||
- name: Run Caddy module tests
|
||||
working-directory: caddy/
|
||||
run: go test -tags nobadger,nomysql,nopgx -race -v ./...
|
||||
-
|
||||
name: Run Fuzzing Tests
|
||||
- name: Run Fuzzing Tests
|
||||
working-directory: caddy/
|
||||
run: go test -fuzz FuzzRequest -fuzztime 20s
|
||||
-
|
||||
name: Build the server
|
||||
- name: Build the server
|
||||
working-directory: caddy/frankenphp/
|
||||
run: go build
|
||||
-
|
||||
name: Start the server
|
||||
- name: Start the server
|
||||
working-directory: testdata/
|
||||
run: sudo ../caddy/frankenphp/frankenphp start
|
||||
-
|
||||
name: Run integrations tests
|
||||
- name: Run integrations tests
|
||||
run: ./reload_test.sh
|
||||
-
|
||||
name: Lint Go code
|
||||
uses: golangci/golangci-lint-action@v6
|
||||
if: matrix.php-versions == '8.3'
|
||||
- name: Lint Go code
|
||||
uses: golangci/golangci-lint-action@v8
|
||||
if: matrix.php-versions == '8.4'
|
||||
with:
|
||||
version: latest
|
||||
tests-mac:
|
||||
name: Tests (macOS, PHP 8.4)
|
||||
runs-on: macos-latest
|
||||
env:
|
||||
HOMEBREW_NO_AUTO_UPDATE: 1
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "1.24"
|
||||
cache-dependency-path: |
|
||||
go.sum
|
||||
caddy/go.sum
|
||||
- uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: 8.4
|
||||
ini-file: development
|
||||
coverage: none
|
||||
tools: none
|
||||
env:
|
||||
phpts: ts
|
||||
debug: true
|
||||
- name: Set Set CGO flags
|
||||
run: |
|
||||
{
|
||||
echo "CGO_CFLAGS=-I/opt/homebrew/include/ $(php-config --includes)"
|
||||
echo "CGO_LDFLAGS=-L/opt/homebrew/lib/ $(php-config --ldflags) $(php-config --libs)"
|
||||
} >> "${GITHUB_ENV}"
|
||||
- name: Build
|
||||
run: go build -tags nowatcher
|
||||
- name: Run library tests
|
||||
run: go test -tags nowatcher -race -v ./...
|
||||
- name: Run Caddy module tests
|
||||
working-directory: caddy/
|
||||
run: go test -tags nowatcher,nobadger,nomysql,nopgx -race -v ./...
|
||||
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -2,7 +2,11 @@
|
||||
/internal/testserver/testserver
|
||||
/internal/testcli/testcli
|
||||
/dist
|
||||
.DS_Store
|
||||
.idea/
|
||||
.vscode/
|
||||
__debug_bin
|
||||
frankenphp.test
|
||||
frankenphp.dev.test
|
||||
caddy/frankenphp/Build
|
||||
package/etc/php.ini
|
||||
*.log
|
||||
|
||||
7
.golangci.yaml
Normal file
7
.golangci.yaml
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
version: "2"
|
||||
run:
|
||||
build-tags:
|
||||
- nobadger
|
||||
- nomysql
|
||||
- nopgx
|
||||
111
CONTRIBUTING.md
111
CONTRIBUTING.md
@@ -11,9 +11,13 @@ docker build -t frankenphp-dev -f dev.Dockerfile .
|
||||
docker run --cap-add=SYS_PTRACE --security-opt seccomp=unconfined -p 8080:8080 -p 443:443 -p 443:443/udp -v $PWD:/go/src/app -it frankenphp-dev
|
||||
```
|
||||
|
||||
The image contains the usual development tools (Go, GDB, Valgrind, Neovim...).
|
||||
The image contains the usual development tools (Go, GDB, Valgrind, Neovim...) and uses the following php setting locations
|
||||
|
||||
If docker version is lower than 23.0, build is failed by dockerignore [pattern issue](https://github.com/moby/moby/pull/42676). Add directories to `.dockerignore`.
|
||||
- php.ini: `/etc/frankenphp/php.ini` A php.ini file with development presets is provided by default.
|
||||
- additional configuration files: `/etc/frankenphp/php.d/*.ini`
|
||||
- php extensions: `/usr/lib/frankenphp/modules/`
|
||||
|
||||
If your docker version is lower than 23.0, the build will fail due to dockerignore [pattern issue](https://github.com/moby/moby/pull/42676). Add directories to `.dockerignore`.
|
||||
|
||||
```patch
|
||||
!testdata/*.php
|
||||
@@ -49,10 +53,13 @@ cd testdata/
|
||||
../caddy/frankenphp/frankenphp run
|
||||
```
|
||||
|
||||
The server is listening on `127.0.0.1:8080`:
|
||||
The server is listening on `127.0.0.1:80`:
|
||||
|
||||
> [!NOTE]
|
||||
> if you are using Docker, you will have to either bind container port 80 or execute from inside the container
|
||||
|
||||
```console
|
||||
curl -vk https://localhost/phpinfo.php
|
||||
curl -vk http://127.0.0.1/phpinfo.php
|
||||
```
|
||||
|
||||
## Minimal test server
|
||||
@@ -108,22 +115,22 @@ docker buildx bake -f docker-bake.hcl --pull --no-cache --push
|
||||
|
||||
1. Download the debug version of the FrankenPHP binary from GitHub or create your custom static build including debug symbols:
|
||||
|
||||
```console
|
||||
docker buildx bake \
|
||||
--load \
|
||||
--set static-builder.args.DEBUG_SYMBOLS=1 \
|
||||
--set "static-builder.platform=linux/amd64" \
|
||||
static-builder
|
||||
docker cp $(docker create --name static-builder dunglas/frankenphp:static-builder):/go/src/app/dist/frankenphp-linux-$(uname -m) frankenphp
|
||||
```
|
||||
```console
|
||||
docker buildx bake \
|
||||
--load \
|
||||
--set static-builder.args.DEBUG_SYMBOLS=1 \
|
||||
--set "static-builder.platform=linux/amd64" \
|
||||
static-builder
|
||||
docker cp $(docker create --name static-builder-musl dunglas/frankenphp:static-builder-musl):/go/src/app/dist/frankenphp-linux-$(uname -m) frankenphp
|
||||
```
|
||||
|
||||
2. Replace your current version of `frankenphp` by the debug FrankenPHP executable
|
||||
3. Start FrankenPHP as usual (alternatively, you can directly start FrankenPHP with GDB: `gdb --args frankenphp run`)
|
||||
4. Attach to the process with GDB:
|
||||
|
||||
```console
|
||||
gdb -p `pidof frankenphp`
|
||||
```
|
||||
```console
|
||||
gdb -p `pidof frankenphp`
|
||||
```
|
||||
|
||||
5. If necessary, type `continue` in the GDB shell
|
||||
6. Make FrankenPHP crash
|
||||
@@ -135,64 +142,60 @@ docker buildx bake -f docker-bake.hcl --pull --no-cache --push
|
||||
1. Open `.github/workflows/tests.yml`
|
||||
2. Enable PHP debug symbols
|
||||
|
||||
```patch
|
||||
- uses: shivammathur/setup-php@v2
|
||||
# ...
|
||||
env:
|
||||
phpts: ts
|
||||
+ debug: true
|
||||
```
|
||||
```patch
|
||||
- uses: shivammathur/setup-php@v2
|
||||
# ...
|
||||
env:
|
||||
phpts: ts
|
||||
+ debug: true
|
||||
```
|
||||
|
||||
3. Enable `tmate` to connect to the container
|
||||
|
||||
```patch
|
||||
-
|
||||
name: Set CGO flags
|
||||
run: echo "CGO_CFLAGS=$(php-config --includes)" >> "$GITHUB_ENV"
|
||||
+ -
|
||||
+ run: |
|
||||
+ sudo apt install gdb
|
||||
+ mkdir -p /home/runner/.config/gdb/
|
||||
+ printf "set auto-load safe-path /\nhandle SIG34 nostop noprint pass" > /home/runner/.config/gdb/gdbinit
|
||||
+ -
|
||||
+ uses: mxschmitt/action-tmate@v3
|
||||
```
|
||||
```patch
|
||||
- name: Set CGO flags
|
||||
run: echo "CGO_CFLAGS=$(php-config --includes)" >> "$GITHUB_ENV"
|
||||
+ - run: |
|
||||
+ sudo apt install gdb
|
||||
+ mkdir -p /home/runner/.config/gdb/
|
||||
+ printf "set auto-load safe-path /\nhandle SIG34 nostop noprint pass" > /home/runner/.config/gdb/gdbinit
|
||||
+ - uses: mxschmitt/action-tmate@v3
|
||||
```
|
||||
|
||||
4. Connect to the container
|
||||
5. Open `frankenphp.go`
|
||||
6. Enable `cgosymbolizer`
|
||||
|
||||
```patch
|
||||
- //_ "github.com/ianlancetaylor/cgosymbolizer"
|
||||
+ _ "github.com/ianlancetaylor/cgosymbolizer"
|
||||
```
|
||||
```patch
|
||||
- //_ "github.com/ianlancetaylor/cgosymbolizer"
|
||||
+ _ "github.com/ianlancetaylor/cgosymbolizer"
|
||||
```
|
||||
|
||||
7. Download the module: `go get`
|
||||
8. In the container, you can use GDB and the like:
|
||||
|
||||
```console
|
||||
go test -tags watcher -c -ldflags=-w
|
||||
gdb --args frankenphp.test -test.run ^MyTest$
|
||||
```
|
||||
```console
|
||||
go test -tags watcher -c -ldflags=-w
|
||||
gdb --args frankenphp.dev.test -test.run ^MyTest$
|
||||
```
|
||||
|
||||
9. When the bug is fixed, revert all these changes
|
||||
|
||||
## Misc Dev Resources
|
||||
|
||||
* [PHP embedding in uWSGI](https://github.com/unbit/uwsgi/blob/master/plugins/php/php_plugin.c)
|
||||
* [PHP embedding in NGINX Unit](https://github.com/nginx/unit/blob/master/src/nxt_php_sapi.c)
|
||||
* [PHP embedding in Go (go-php)](https://github.com/deuill/go-php)
|
||||
* [PHP embedding in Go (GoEmPHP)](https://github.com/mikespook/goemphp)
|
||||
* [PHP embedding in C++](https://gist.github.com/paresy/3cbd4c6a469511ac7479aa0e7c42fea7)
|
||||
* [Extending and Embedding PHP by Sara Golemon](https://books.google.fr/books?id=zMbGvK17_tYC&pg=PA254&lpg=PA254#v=onepage&q&f=false)
|
||||
* [What the heck is TSRMLS_CC, anyway?](http://blog.golemon.com/2006/06/what-heck-is-tsrmlscc-anyway.html)
|
||||
* [PHP embedding on Mac](https://gist.github.com/jonnywang/61427ffc0e8dde74fff40f479d147db4)
|
||||
* [SDL bindings](https://pkg.go.dev/github.com/veandco/go-sdl2@v0.4.21/sdl#Main)
|
||||
- [PHP embedding in uWSGI](https://github.com/unbit/uwsgi/blob/master/plugins/php/php_plugin.c)
|
||||
- [PHP embedding in NGINX Unit](https://github.com/nginx/unit/blob/master/src/nxt_php_sapi.c)
|
||||
- [PHP embedding in Go (go-php)](https://github.com/deuill/go-php)
|
||||
- [PHP embedding in Go (GoEmPHP)](https://github.com/mikespook/goemphp)
|
||||
- [PHP embedding in C++](https://gist.github.com/paresy/3cbd4c6a469511ac7479aa0e7c42fea7)
|
||||
- [Extending and Embedding PHP by Sara Golemon](https://books.google.fr/books?id=zMbGvK17_tYC&pg=PA254&lpg=PA254#v=onepage&q&f=false)
|
||||
- [What the heck is TSRMLS_CC, anyway?](http://blog.golemon.com/2006/06/what-heck-is-tsrmlscc-anyway.html)
|
||||
- [SDL bindings](https://pkg.go.dev/github.com/veandco/go-sdl2@v0.4.21/sdl#Main)
|
||||
|
||||
## Docker-Related Resources
|
||||
|
||||
* [Bake file definition](https://docs.docker.com/build/customize/bake/file-definition/)
|
||||
* [docker buildx build](https://docs.docker.com/engine/reference/commandline/buildx_build/)
|
||||
- [Bake file definition](https://docs.docker.com/build/customize/bake/file-definition/)
|
||||
- [docker buildx build](https://docs.docker.com/engine/reference/commandline/buildx_build/)
|
||||
|
||||
## Useful Command
|
||||
|
||||
@@ -211,6 +214,6 @@ follow these steps:
|
||||
3. Copy the `README.md` and `CONTRIBUTING.md` files from the root directory to the new directory
|
||||
4. Translate the content of the files, but don't change the filenames, also don't translate strings starting with `> [!` (it's special markup for GitHub)
|
||||
5. Create a Pull Request with the translations
|
||||
6. In the [site repository](https://github.com/dunglas/frankenphp-website/tree/main), copy and translate the translation files in the `content/`, `data/` and `i18n/` directories
|
||||
6. In the [site repository](https://github.com/dunglas/frankenphp-website), copy and translate the translation files in the `content/`, `data/` and `i18n/` directories
|
||||
7. Translate the values in the created YAML file
|
||||
8. Open a Pull Request on the site repository
|
||||
|
||||
49
Dockerfile
49
Dockerfile
@@ -19,17 +19,19 @@ RUN set -eux; \
|
||||
/app/public \
|
||||
/config/caddy \
|
||||
/data/caddy \
|
||||
/etc/caddy; \
|
||||
/etc/caddy \
|
||||
/etc/frankenphp; \
|
||||
sed -i 's/php/frankenphp run/g' /usr/local/bin/docker-php-entrypoint; \
|
||||
echo '<?php phpinfo();' > /app/public/index.php
|
||||
|
||||
COPY --link caddy/frankenphp/Caddyfile /etc/caddy/Caddyfile
|
||||
RUN curl -sSLf \
|
||||
RUN ln /etc/caddy/Caddyfile /etc/frankenphp/Caddyfile && \
|
||||
curl -sSLf \
|
||||
-o /usr/local/bin/install-php-extensions \
|
||||
https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions && \
|
||||
chmod +x /usr/local/bin/install-php-extensions
|
||||
|
||||
CMD ["--config", "/etc/caddy/Caddyfile", "--adapter", "caddyfile"]
|
||||
CMD ["--config", "/etc/frankenphp/Caddyfile", "--adapter", "caddyfile"]
|
||||
HEALTHCHECK CMD curl -f http://localhost:2019/metrics || exit 1
|
||||
|
||||
# See https://caddyserver.com/docs/conventions#file-locations for details
|
||||
@@ -57,11 +59,13 @@ SHELL ["/bin/bash", "-o", "pipefail", "-c"]
|
||||
COPY --from=golang-base /usr/local/go /usr/local/go
|
||||
|
||||
ENV PATH=/usr/local/go/bin:$PATH
|
||||
ENV GOTOOLCHAIN=local
|
||||
|
||||
# This is required to link the FrankenPHP binary to the PHP binary
|
||||
RUN apt-get update && \
|
||||
apt-get -y --no-install-recommends install \
|
||||
cmake \
|
||||
cmake \
|
||||
git \
|
||||
libargon2-dev \
|
||||
libbrotli-dev \
|
||||
libcurl4-openssl-dev \
|
||||
@@ -75,21 +79,6 @@ RUN apt-get update && \
|
||||
&& \
|
||||
apt-get clean
|
||||
|
||||
WORKDIR /go/src/app
|
||||
|
||||
COPY --link go.mod go.sum ./
|
||||
RUN go mod graph | awk '{if ($1 !~ "@") print $2}' | xargs go get
|
||||
|
||||
WORKDIR /go/src/app/caddy
|
||||
COPY --link caddy/go.mod caddy/go.sum ./
|
||||
RUN go mod graph | awk '{if ($1 !~ "@") print $2}' | xargs go get
|
||||
|
||||
WORKDIR /go/src/app
|
||||
COPY --link *.* ./
|
||||
COPY --link caddy caddy
|
||||
COPY --link internal internal
|
||||
COPY --link testdata testdata
|
||||
|
||||
# Install e-dant/watcher (necessary for file watching)
|
||||
WORKDIR /usr/local/src/watcher
|
||||
RUN curl -s https://api.github.com/repos/e-dant/watcher/releases/latest | \
|
||||
@@ -104,6 +93,18 @@ RUN curl -s https://api.github.com/repos/e-dant/watcher/releases/latest | \
|
||||
cmake --install build && \
|
||||
ldconfig
|
||||
|
||||
WORKDIR /go/src/app
|
||||
|
||||
COPY --link go.mod go.sum ./
|
||||
RUN go mod download
|
||||
|
||||
WORKDIR /go/src/app/caddy
|
||||
COPY --link caddy/go.mod caddy/go.sum ./
|
||||
RUN go mod download
|
||||
|
||||
WORKDIR /go/src/app
|
||||
COPY --link . ./
|
||||
|
||||
# See https://github.com/docker-library/php/blob/master/8.3/bookworm/zts/Dockerfile#L57-L59 for PHP values
|
||||
ENV CGO_CFLAGS="-DFRANKENPHP_VERSION=$FRANKENPHP_VERSION $PHP_CFLAGS"
|
||||
ENV CGO_CPPFLAGS=$PHP_CPPFLAGS
|
||||
@@ -112,10 +113,11 @@ ENV CGO_LDFLAGS="-L/usr/local/lib -lssl -lcrypto -lreadline -largon2 -lcurl -lon
|
||||
RUN echo $CGO_LDFLAGS
|
||||
|
||||
WORKDIR /go/src/app/caddy/frankenphp
|
||||
RUN GOBIN=/usr/local/bin go install -tags 'nobadger,nomysql,nopgx' -ldflags "-w -s -X 'github.com/caddyserver/caddy/v2.CustomVersion=FrankenPHP $FRANKENPHP_VERSION PHP $PHP_VERSION Caddy'" && \
|
||||
RUN GOBIN=/usr/local/bin go install -tags 'nobadger,nomysql,nopgx' -ldflags "-w -s -X 'github.com/caddyserver/caddy/v2.CustomVersion=FrankenPHP $FRANKENPHP_VERSION PHP $PHP_VERSION Caddy'" -buildvcs=true && \
|
||||
setcap cap_net_bind_service=+ep /usr/local/bin/frankenphp && \
|
||||
cp Caddyfile /etc/caddy/Caddyfile && \
|
||||
frankenphp version
|
||||
cp Caddyfile /etc/frankenphp/Caddyfile && \
|
||||
frankenphp version && \
|
||||
frankenphp build-info
|
||||
|
||||
WORKDIR /go/src/app
|
||||
|
||||
@@ -133,4 +135,5 @@ RUN apt-get install -y --no-install-recommends libstdc++6 && \
|
||||
|
||||
COPY --from=builder /usr/local/bin/frankenphp /usr/local/bin/frankenphp
|
||||
RUN setcap cap_net_bind_service=+ep /usr/local/bin/frankenphp && \
|
||||
frankenphp version
|
||||
frankenphp version && \
|
||||
frankenphp build-info
|
||||
|
||||
106
README.md
106
README.md
@@ -4,37 +4,22 @@
|
||||
|
||||
FrankenPHP is a modern application server for PHP built on top of the [Caddy](https://caddyserver.com/) web server.
|
||||
|
||||
FrankenPHP gives superpowers to your PHP apps thanks to its stunning features: [*Early Hints*](https://frankenphp.dev/docs/early-hints/), [worker mode](https://frankenphp.dev/docs/worker/), [real-time capabilities](https://frankenphp.dev/docs/mercure/), automatic HTTPS, HTTP/2, and HTTP/3 support...
|
||||
FrankenPHP gives superpowers to your PHP apps thanks to its stunning features: [_Early Hints_](https://frankenphp.dev/docs/early-hints/), [worker mode](https://frankenphp.dev/docs/worker/), [real-time capabilities](https://frankenphp.dev/docs/mercure/), automatic HTTPS, HTTP/2, and HTTP/3 support...
|
||||
|
||||
FrankenPHP works with any PHP app and makes your Laravel and Symfony projects faster than ever thanks to their official integrations with the worker mode.
|
||||
|
||||
FrankenPHP can also be used as a standalone Go library to embed PHP in any app using `net/http`.
|
||||
|
||||
[**Learn more** on *frankenphp.dev*](https://frankenphp.dev) and in this slide deck:
|
||||
[**Learn more** on _frankenphp.dev_](https://frankenphp.dev) and in this slide deck:
|
||||
|
||||
<a href="https://dunglas.dev/2022/10/frankenphp-the-modern-php-app-server-written-in-go/"><img src="https://dunglas.dev/wp-content/uploads/2022/10/frankenphp.png" alt="Slides" width="600"></a>
|
||||
|
||||
## Getting Started
|
||||
|
||||
### Docker
|
||||
|
||||
```console
|
||||
docker run -v .:/app/public \
|
||||
-p 80:80 -p 443:443 -p 443:443/udp \
|
||||
dunglas/frankenphp
|
||||
```
|
||||
|
||||
Go to `https://localhost`, and enjoy!
|
||||
|
||||
> [!TIP]
|
||||
>
|
||||
> Do not attempt to use `https://127.0.0.1`. Use `https://localhost` and accept the self-signed certificate.
|
||||
> Use the [`SERVER_NAME` environment variable](docs/config.md#environment-variables) to change the domain to use.
|
||||
|
||||
### Standalone Binary
|
||||
|
||||
If you prefer not to use Docker, we provide standalone FrankenPHP binaries for Linux and macOS
|
||||
containing [PHP 8.3](https://www.php.net/releases/8.3/en.php) and most popular PHP extensions.
|
||||
We provide static FrankenPHP binaries for Linux and macOS
|
||||
containing [PHP 8.4](https://www.php.net/releases/8.4/en.php) and most popular PHP extensions.
|
||||
|
||||
On Windows, use [WSL](https://learn.microsoft.com/windows/wsl/) to run FrankenPHP.
|
||||
|
||||
@@ -58,31 +43,68 @@ You can also run command-line scripts with:
|
||||
frankenphp php-cli /path/to/your/script.php
|
||||
```
|
||||
|
||||
### Docker
|
||||
|
||||
Alternatively, [Docker images](https://frankenphp.dev/docs/docker/) are available:
|
||||
|
||||
```console
|
||||
docker run -v .:/app/public \
|
||||
-p 80:80 -p 443:443 -p 443:443/udp \
|
||||
dunglas/frankenphp
|
||||
```
|
||||
|
||||
Go to `https://localhost`, and enjoy!
|
||||
|
||||
> [!TIP]
|
||||
>
|
||||
> Do not attempt to use `https://127.0.0.1`. Use `https://localhost` and accept the self-signed certificate.
|
||||
> Use the [`SERVER_NAME` environment variable](docs/config.md#environment-variables) to change the domain to use.
|
||||
|
||||
### Homebrew
|
||||
|
||||
FrankenPHP is also available as a [Homebrew](https://brew.sh) package for macOS and Linux.
|
||||
|
||||
To install it:
|
||||
|
||||
```console
|
||||
brew install dunglas/frankenphp/frankenphp
|
||||
```
|
||||
|
||||
To serve the content of the current directory, run:
|
||||
|
||||
```console
|
||||
frankenphp php-server
|
||||
```
|
||||
|
||||
## Docs
|
||||
|
||||
* [The worker mode](https://frankenphp.dev/docs/worker/)
|
||||
* [Early Hints support (103 HTTP status code)](https://frankenphp.dev/docs/early-hints/)
|
||||
* [Real-time](https://frankenphp.dev/docs/mercure/)
|
||||
* [Configuration](https://frankenphp.dev/docs/config/)
|
||||
* [Docker images](https://frankenphp.dev/docs/docker/)
|
||||
* [Deploy in production](https://frankenphp.dev/docs/production/)
|
||||
* [Performance optimization](https://frankenphp.dev/docs/performance/)
|
||||
* [Create **standalone**, self-executable PHP apps](https://frankenphp.dev/docs/embed/)
|
||||
* [Create static binaries](https://frankenphp.dev/docs/static/)
|
||||
* [Compile from sources](https://frankenphp.dev/docs/compile/)
|
||||
* [Laravel integration](https://frankenphp.dev/docs/laravel/)
|
||||
* [Known issues](https://frankenphp.dev/docs/known-issues/)
|
||||
* [Demo app (Symfony) and benchmarks](https://github.com/dunglas/frankenphp-demo)
|
||||
* [Go library documentation](https://pkg.go.dev/github.com/dunglas/frankenphp)
|
||||
* [Contributing and debugging](https://frankenphp.dev/docs/contributing/)
|
||||
- [Classic mode](https://frankenphp.dev/docs/classic/)
|
||||
- [Worker mode](https://frankenphp.dev/docs/worker/)
|
||||
- [Early Hints support (103 HTTP status code)](https://frankenphp.dev/docs/early-hints/)
|
||||
- [Real-time](https://frankenphp.dev/docs/mercure/)
|
||||
- [Efficiently Serving Large Static Files](https://frankenphp.dev/docs/x-sendfile/)
|
||||
- [Configuration](https://frankenphp.dev/docs/config/)
|
||||
- [Docker images](https://frankenphp.dev/docs/docker/)
|
||||
- [Deploy in production](https://frankenphp.dev/docs/production/)
|
||||
- [Performance optimization](https://frankenphp.dev/docs/performance/)
|
||||
- [Create **standalone**, self-executable PHP apps](https://frankenphp.dev/docs/embed/)
|
||||
- [Create static binaries](https://frankenphp.dev/docs/static/)
|
||||
- [Compile from sources](https://frankenphp.dev/docs/compile/)
|
||||
- [Monitoring FrankenPHP](https://frankenphp.dev/docs/metrics/)
|
||||
- [Laravel integration](https://frankenphp.dev/docs/laravel/)
|
||||
- [Known issues](https://frankenphp.dev/docs/known-issues/)
|
||||
- [Demo app (Symfony) and benchmarks](https://github.com/dunglas/frankenphp-demo)
|
||||
- [Go library documentation](https://pkg.go.dev/frankenphp.dev)
|
||||
- [Contributing and debugging](https://frankenphp.dev/docs/contributing/)
|
||||
|
||||
## Examples and Skeletons
|
||||
|
||||
* [Symfony](https://github.com/dunglas/symfony-docker)
|
||||
* [API Platform](https://api-platform.com/docs/symfony)
|
||||
* [Laravel](https://frankenphp.dev/docs/laravel/)
|
||||
* [Sulu](https://sulu.io/blog/running-sulu-with-frankenphp)
|
||||
* [WordPress](https://github.com/StephenMiracle/frankenwp)
|
||||
* [Drupal](https://github.com/dunglas/frankenphp-drupal)
|
||||
* [Joomla](https://github.com/alexandreelise/frankenphp-joomla)
|
||||
* [TYPO3](https://github.com/ochorocho/franken-typo3)
|
||||
- [Symfony](https://github.com/dunglas/symfony-docker)
|
||||
- [API Platform](https://api-platform.com/docs/symfony)
|
||||
- [Laravel](https://frankenphp.dev/docs/laravel/)
|
||||
- [Sulu](https://sulu.io/blog/running-sulu-with-frankenphp)
|
||||
- [WordPress](https://github.com/StephenMiracle/frankenwp)
|
||||
- [Drupal](https://github.com/dunglas/frankenphp-drupal)
|
||||
- [Joomla](https://github.com/alexandreelise/frankenphp-joomla)
|
||||
- [TYPO3](https://github.com/ochorocho/franken-typo3)
|
||||
- [Magento2](https://github.com/ekino/frankenphp-magento2)
|
||||
|
||||
@@ -18,17 +18,20 @@ RUN set -eux; \
|
||||
/app/public \
|
||||
/config/caddy \
|
||||
/data/caddy \
|
||||
/etc/caddy; \
|
||||
/etc/caddy \
|
||||
/etc/frankenphp; \
|
||||
sed -i 's/php/frankenphp run/g' /usr/local/bin/docker-php-entrypoint; \
|
||||
echo '<?php phpinfo();' > /app/public/index.php
|
||||
|
||||
COPY --link caddy/frankenphp/Caddyfile /etc/caddy/Caddyfile
|
||||
RUN curl -sSLf \
|
||||
|
||||
RUN ln /etc/caddy/Caddyfile /etc/frankenphp/Caddyfile && \
|
||||
curl -sSLf \
|
||||
-o /usr/local/bin/install-php-extensions \
|
||||
https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions && \
|
||||
chmod +x /usr/local/bin/install-php-extensions
|
||||
|
||||
CMD ["--config", "/etc/caddy/Caddyfile", "--adapter", "caddyfile"]
|
||||
CMD ["--config", "/etc/frankenphp/Caddyfile", "--adapter", "caddyfile"]
|
||||
HEALTHCHECK CMD curl -f http://localhost:2019/metrics || exit 1
|
||||
|
||||
# See https://caddyserver.com/docs/conventions#file-locations for details
|
||||
@@ -57,6 +60,7 @@ SHELL ["/bin/ash", "-eo", "pipefail", "-c"]
|
||||
COPY --link --from=golang-base /usr/local/go /usr/local/go
|
||||
|
||||
ENV PATH=/usr/local/go/bin:$PATH
|
||||
ENV GOTOOLCHAIN=local
|
||||
|
||||
# hadolint ignore=SC2086
|
||||
RUN apk add --no-cache --virtual .build-deps \
|
||||
@@ -82,36 +86,6 @@ RUN apk add --no-cache --virtual .build-deps \
|
||||
sqlite-dev \
|
||||
upx
|
||||
|
||||
# FIXME: temporary workaround for https://github.com/golang/go/issues/68285
|
||||
WORKDIR /
|
||||
RUN git clone https://go.googlesource.com/go goroot
|
||||
WORKDIR /goroot
|
||||
# Revert https://github.com/golang/go/commit/3560cf0afb3c29300a6c88ccd98256949ca7a6f6 to prevent the crash with musl
|
||||
RUN git config --global user.email "build@example.com" && \
|
||||
git config --global user.name "Build" && \
|
||||
git checkout "$(go env GOVERSION)" && \
|
||||
git revert 3560cf0afb3c29300a6c88ccd98256949ca7a6f6
|
||||
WORKDIR /goroot/src
|
||||
ENV GOHOSTARCH="$TARGETARCH"
|
||||
RUN ./make.bash
|
||||
ENV PATH="/goroot/bin:$PATH"
|
||||
RUN go version
|
||||
|
||||
WORKDIR /go/src/app
|
||||
|
||||
COPY --link go.mod go.sum ./
|
||||
RUN go mod graph | awk '{if ($1 !~ "@") print $2}' | xargs go get
|
||||
|
||||
WORKDIR /go/src/app/caddy
|
||||
COPY caddy/go.mod caddy/go.sum ./
|
||||
RUN go mod graph | awk '{if ($1 !~ "@") print $2}' | xargs go get
|
||||
|
||||
WORKDIR /go/src/app
|
||||
COPY --link *.* ./
|
||||
COPY --link caddy caddy
|
||||
COPY --link internal internal
|
||||
COPY --link testdata testdata
|
||||
|
||||
# Install e-dant/watcher (necessary for file watching)
|
||||
WORKDIR /usr/local/src/watcher
|
||||
RUN curl -s https://api.github.com/repos/e-dant/watcher/releases/latest | \
|
||||
@@ -125,16 +99,29 @@ RUN curl -s https://api.github.com/repos/e-dant/watcher/releases/latest | \
|
||||
cmake --build build && \
|
||||
cmake --install build
|
||||
|
||||
WORKDIR /go/src/app
|
||||
|
||||
COPY --link go.mod go.sum ./
|
||||
RUN go mod download
|
||||
|
||||
WORKDIR /go/src/app/caddy
|
||||
COPY caddy/go.mod caddy/go.sum ./
|
||||
RUN go mod download
|
||||
|
||||
WORKDIR /go/src/app
|
||||
COPY --link . ./
|
||||
|
||||
# See https://github.com/docker-library/php/blob/master/8.3/alpine3.20/zts/Dockerfile#L53-L55
|
||||
ENV CGO_CFLAGS="-DFRANKENPHP_VERSION=$FRANKENPHP_VERSION $PHP_CFLAGS"
|
||||
ENV CGO_CPPFLAGS=$PHP_CPPFLAGS
|
||||
ENV CGO_LDFLAGS="-lssl -lcrypto -lreadline -largon2 -lcurl -lonig -lz $PHP_LDFLAGS"
|
||||
|
||||
WORKDIR /go/src/app/caddy/frankenphp
|
||||
RUN GOBIN=/usr/local/bin go install -tags 'nobadger,nomysql,nopgx' -ldflags "-w -s -extldflags '-Wl,-z,stack-size=0x80000' -X 'github.com/caddyserver/caddy/v2.CustomVersion=FrankenPHP $FRANKENPHP_VERSION PHP $PHP_VERSION Caddy'" && \
|
||||
RUN GOBIN=/usr/local/bin go install -tags 'nobadger,nomysql,nopgx' -ldflags "-w -s -extldflags '-Wl,-z,stack-size=0x80000' -X 'github.com/caddyserver/caddy/v2.CustomVersion=FrankenPHP $FRANKENPHP_VERSION PHP $PHP_VERSION Caddy'" -buildvcs=true && \
|
||||
setcap cap_net_bind_service=+ep /usr/local/bin/frankenphp && \
|
||||
([ -z "${NO_COMPRESS}" ] && upx --best /usr/local/bin/frankenphp || true) && \
|
||||
frankenphp version
|
||||
frankenphp version && \
|
||||
frankenphp build-info
|
||||
|
||||
WORKDIR /go/src/app
|
||||
|
||||
@@ -150,4 +137,5 @@ RUN apk add --no-cache libstdc++ && \
|
||||
|
||||
COPY --from=builder /usr/local/bin/frankenphp /usr/local/bin/frankenphp
|
||||
RUN setcap cap_net_bind_service=+ep /usr/local/bin/frankenphp && \
|
||||
frankenphp version
|
||||
frankenphp version && \
|
||||
frankenphp build-info
|
||||
|
||||
51
backoff.go
Normal file
51
backoff.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package frankenphp
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type exponentialBackoff struct {
|
||||
backoff time.Duration
|
||||
failureCount int
|
||||
mu sync.RWMutex
|
||||
maxBackoff time.Duration
|
||||
minBackoff time.Duration
|
||||
maxConsecutiveFailures int
|
||||
}
|
||||
|
||||
// recordSuccess resets the backoff and failureCount
|
||||
func (e *exponentialBackoff) recordSuccess() {
|
||||
e.mu.Lock()
|
||||
e.failureCount = 0
|
||||
e.backoff = e.minBackoff
|
||||
e.mu.Unlock()
|
||||
}
|
||||
|
||||
// recordFailure increments the failure count and increases the backoff, it returns true if maxConsecutiveFailures has been reached
|
||||
func (e *exponentialBackoff) recordFailure() bool {
|
||||
e.mu.Lock()
|
||||
e.failureCount += 1
|
||||
if e.backoff < e.minBackoff {
|
||||
e.backoff = e.minBackoff
|
||||
}
|
||||
|
||||
e.backoff = min(e.backoff*2, e.maxBackoff)
|
||||
|
||||
e.mu.Unlock()
|
||||
return e.failureCount >= e.maxConsecutiveFailures
|
||||
}
|
||||
|
||||
// wait sleeps for the backoff duration if failureCount is non-zero.
|
||||
// NOTE: this is not tested and should be kept 'obviously correct' (i.e., simple)
|
||||
func (e *exponentialBackoff) wait() {
|
||||
e.mu.RLock()
|
||||
if e.failureCount == 0 {
|
||||
e.mu.RUnlock()
|
||||
|
||||
return
|
||||
}
|
||||
e.mu.RUnlock()
|
||||
|
||||
time.Sleep(e.backoff)
|
||||
}
|
||||
41
backoff_test.go
Normal file
41
backoff_test.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package frankenphp
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestExponentialBackoff_Reset(t *testing.T) {
|
||||
e := &exponentialBackoff{
|
||||
maxBackoff: 5 * time.Second,
|
||||
minBackoff: 500 * time.Millisecond,
|
||||
maxConsecutiveFailures: 3,
|
||||
}
|
||||
|
||||
assert.False(t, e.recordFailure())
|
||||
assert.False(t, e.recordFailure())
|
||||
e.recordSuccess()
|
||||
|
||||
e.mu.RLock()
|
||||
defer e.mu.RUnlock()
|
||||
assert.Equal(t, 0, e.failureCount, "expected failureCount to be reset to 0")
|
||||
assert.Equal(t, e.backoff, e.minBackoff, "expected backoff to be reset to minBackoff")
|
||||
}
|
||||
|
||||
func TestExponentialBackoff_Trigger(t *testing.T) {
|
||||
e := &exponentialBackoff{
|
||||
maxBackoff: 500 * 3 * time.Millisecond,
|
||||
minBackoff: 500 * time.Millisecond,
|
||||
maxConsecutiveFailures: 3,
|
||||
}
|
||||
|
||||
assert.False(t, e.recordFailure())
|
||||
assert.False(t, e.recordFailure())
|
||||
assert.True(t, e.recordFailure())
|
||||
|
||||
e.mu.RLock()
|
||||
defer e.mu.RUnlock()
|
||||
assert.Equal(t, e.failureCount, e.maxConsecutiveFailures, "expected failureCount to be maxConsecutiveFailures")
|
||||
assert.Equal(t, e.backoff, e.maxBackoff, "expected backoff to be maxBackoff")
|
||||
}
|
||||
119
build-packages.sh
Executable file
119
build-packages.sh
Executable file
@@ -0,0 +1,119 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -o errexit
|
||||
set -x
|
||||
|
||||
# Ensure required tools are installed
|
||||
if ! command -v rpmbuild &>/dev/null; then
|
||||
echo "Error: rpm-build is required to create RPM packages."
|
||||
echo "Install it with: sudo dnf install rpm-build"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command -v ruby &>/dev/null; then
|
||||
echo "Error: Ruby is required by FPM."
|
||||
echo "Install it with: sudo dnf install ruby"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command -v fpm &>/dev/null; then
|
||||
echo "Error: FPM (rubygem-fpm) is required to create RPM packages."
|
||||
echo "Install it with: sudo gem install fpm"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
arch="$(uname -m)"
|
||||
os="$(uname -s | tr '[:upper:]' '[:lower:]')"
|
||||
bin="frankenphp-${os}-${arch}"
|
||||
|
||||
if [ ! -f "dist/$bin" ]; then
|
||||
echo "Error: dist/$bin not found. Run './build-static.sh' first"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
version_output="$(dist/"$bin" version)"
|
||||
frankenphp_version=$(echo "$version_output" | grep -oP 'FrankenPHP\s+\K[^ ]+' || true)
|
||||
frankenphp_version=${frankenphp_version#v}
|
||||
|
||||
if [[ ! "${frankenphp_version}" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
||||
echo "Warning: frankenphp_version must be set to X.Y.Z (e.g. 1.5.1), got '${frankenphp_version}'"
|
||||
echo "Falling back to non-release version 0.0.0"
|
||||
frankenphp_version=0.0.0
|
||||
fi
|
||||
|
||||
group_preexists=0
|
||||
user_preexists=0
|
||||
|
||||
if getent group frankenphp >/dev/null; then
|
||||
group_preexists=1
|
||||
else
|
||||
sudo groupadd --system frankenphp
|
||||
fi
|
||||
|
||||
if getent passwd frankenphp >/dev/null; then
|
||||
user_preexists=1
|
||||
else
|
||||
sudo useradd --system \
|
||||
--gid frankenphp \
|
||||
--create-home \
|
||||
--home-dir /var/lib/frankenphp \
|
||||
--shell /usr/sbin/nologin \
|
||||
--comment "FrankenPHP web server" \
|
||||
frankenphp
|
||||
fi
|
||||
|
||||
mkdir -p package/empty
|
||||
mkdir -p package/etc
|
||||
[ -f ./dist/static-php-cli/source/php-src/php.ini-production ] && cp -f ./dist/static-php-cli/source/php-src/php.ini-production ./package/etc/php.ini
|
||||
|
||||
cd dist
|
||||
iteration=1
|
||||
glibc_version=$(ldd -v "$bin" | awk '/GLIBC_/ {gsub(/[()]/, "", $2); print $2}' | grep -v GLIBC_PRIVATE | sort -V | tail -n1)
|
||||
cxxabi_version=$(strings "$bin" | grep -oP 'CXXABI_\d+\.\d+(\.\d+)?' | sort -V | tail -n1)
|
||||
|
||||
fpm -s dir -t rpm -n frankenphp -v "${frankenphp_version}" \
|
||||
--config-files /etc/frankenphp/Caddyfile \
|
||||
--config-files /etc/frankenphp/php.ini \
|
||||
--depends "libc.so.6(${glibc_version})(64bit)" \
|
||||
--depends "libstdc++.so.6(${cxxabi_version})(64bit)" \
|
||||
--before-install ../package/rhel/preinstall.sh \
|
||||
--after-install ../package/rhel/postinstall.sh \
|
||||
--before-remove ../package/rhel/preuninstall.sh \
|
||||
--after-remove ../package/rhel/postuninstall.sh \
|
||||
--iteration "${iteration}" \
|
||||
--rpm-user frankenphp --rpm-group frankenphp \
|
||||
"${bin}=/usr/bin/frankenphp" \
|
||||
"../package/rhel/frankenphp.service=/usr/lib/systemd/system/frankenphp.service" \
|
||||
"../package/Caddyfile=/etc/frankenphp/Caddyfile" \
|
||||
"../package/content/=/usr/share/frankenphp" \
|
||||
"../package/etc/php.ini=/etc/frankenphp/php.ini" \
|
||||
"../package/empty/=/etc/frankenphp/php.d" \
|
||||
"../package/empty/=/usr/lib/frankenphp/modules" \
|
||||
"../package/empty/=/var/lib/frankenphp"
|
||||
|
||||
glibc_version=$(ldd -v "$bin" | awk '/GLIBC_/ {gsub(/[()]/, "", $2); print $2}' | grep -v GLIBC_PRIVATE | sed 's/GLIBC_//' | sort -V | tail -n1)
|
||||
cxxabi_version=$(strings "$bin" | grep -oP 'CXXABI_\d+\.\d+(\.\d+)?' | sed 's/CXXABI_//' | sort -V | tail -n1)
|
||||
|
||||
fpm -s dir -t deb -n frankenphp -v "${frankenphp_version}" \
|
||||
--config-files /etc/frankenphp/Caddyfile \
|
||||
--config-files /etc/frankenphp/php.ini \
|
||||
--depends "libc6 (>= ${glibc_version})" \
|
||||
--depends "libstdc++6 (>= ${cxxabi_version})" \
|
||||
--after-install ../package/debian/postinst.sh \
|
||||
--before-remove ../package/debian/prerm.sh \
|
||||
--after-remove ../package/debian/postrm.sh \
|
||||
--iteration "${iteration}" \
|
||||
--deb-user frankenphp --deb-group frankenphp \
|
||||
"${bin}=/usr/bin/frankenphp" \
|
||||
"../package/debian/frankenphp.service=/usr/lib/systemd/system/frankenphp.service" \
|
||||
"../package/Caddyfile=/etc/frankenphp/Caddyfile" \
|
||||
"../package/content/=/usr/share/frankenphp" \
|
||||
"../package/etc/php.ini=/etc/frankenphp/php.ini" \
|
||||
"../package/empty/=/etc/frankenphp/php.d" \
|
||||
"../package/empty/=/usr/lib/frankenphp/modules" \
|
||||
"../package/empty/=/var/lib/frankenphp"
|
||||
|
||||
[ "$user_preexists" -eq 0 ] && sudo userdel frankenphp
|
||||
[ "$group_preexists" -eq 0 ] && (sudo groupdel frankenphp || true)
|
||||
|
||||
cd ..
|
||||
359
build-static.sh
359
build-static.sh
@@ -10,6 +10,75 @@ fi
|
||||
|
||||
arch="$(uname -m)"
|
||||
os="$(uname -s | tr '[:upper:]' '[:lower:]')"
|
||||
|
||||
# Supported variables:
|
||||
# - PHP_VERSION: PHP version to build (default: "8.4")
|
||||
# - PHP_EXTENSIONS: PHP extensions to build (default: ${defaultExtensions} set below)
|
||||
# - PHP_EXTENSION_LIBS: PHP extension libraries to build (default: ${defaultExtensionLibs} set below)
|
||||
# - FRANKENPHP_VERSION: FrankenPHP version (default: current Git commit)
|
||||
# - EMBED: Path to the PHP app to embed (default: none)
|
||||
# - DEBUG_SYMBOLS: Enable debug symbols if set to 1 (default: none)
|
||||
# - MIMALLOC: Use mimalloc as the allocator if set to 1 (default: none)
|
||||
# - XCADDY_ARGS: Additional arguments to pass to xcaddy
|
||||
# - RELEASE: [maintainer only] Create a GitHub release if set to 1 (default: none)
|
||||
|
||||
# - SPC_REL_TYPE: Release type to download (accept "source" and "binary", default: "source")
|
||||
# - SPC_OPT_BUILD_ARGS: Additional arguments to pass to spc build
|
||||
# - SPC_OPT_DOWNLOAD_ARGS: Additional arguments to pass to spc download
|
||||
# - SPC_LIBC: Set to glibc to build with GNU toolchain (default: musl)
|
||||
|
||||
# init spc command, if we use spc binary, just use it instead of fetching source
|
||||
if [ -z "${SPC_REL_TYPE}" ]; then
|
||||
SPC_REL_TYPE="source"
|
||||
fi
|
||||
# init spc libc
|
||||
if [ -z "${SPC_LIBC}" ]; then
|
||||
if [ "${os}" = "linux" ]; then
|
||||
SPC_LIBC="musl"
|
||||
fi
|
||||
fi
|
||||
# init spc build additional args
|
||||
if [ -z "${SPC_OPT_BUILD_ARGS}" ]; then
|
||||
SPC_OPT_BUILD_ARGS=""
|
||||
fi
|
||||
if [ "${SPC_LIBC}" = "musl" ] && [[ "${SPC_OPT_BUILD_ARGS}" != *"--disable-opcache-jit"* ]]; then
|
||||
SPC_OPT_BUILD_ARGS="${SPC_OPT_BUILD_ARGS} --disable-opcache-jit"
|
||||
fi
|
||||
# init spc download additional args
|
||||
if [ -z "${SPC_OPT_DOWNLOAD_ARGS}" ]; then
|
||||
SPC_OPT_DOWNLOAD_ARGS="--ignore-cache-sources=php-src --retry 5"
|
||||
if [ "${SPC_LIBC}" = "musl" ]; then
|
||||
SPC_OPT_DOWNLOAD_ARGS="${SPC_OPT_DOWNLOAD_ARGS} --prefer-pre-built"
|
||||
fi
|
||||
fi
|
||||
# if we need debug symbols, disable strip
|
||||
if [ -n "${DEBUG_SYMBOLS}" ]; then
|
||||
SPC_OPT_BUILD_ARGS="${SPC_OPT_BUILD_ARGS} --no-strip"
|
||||
fi
|
||||
# php version to build
|
||||
if [ -z "${PHP_VERSION}" ]; then
|
||||
get_latest_php_version() {
|
||||
input="$1"
|
||||
json=$(curl -s "https://www.php.net/releases/index.php?json&version=$input")
|
||||
latest=$(echo "$json" | jq -r '.version')
|
||||
|
||||
if [[ "$latest" == "$input"* ]]; then
|
||||
echo "$latest"
|
||||
else
|
||||
echo "$input"
|
||||
fi
|
||||
}
|
||||
|
||||
PHP_VERSION="$(get_latest_php_version "8.4")"
|
||||
export PHP_VERSION
|
||||
fi
|
||||
# default extension set
|
||||
defaultExtensions="apcu,bcmath,bz2,calendar,ctype,curl,dba,dom,exif,fileinfo,filter,ftp,gd,gmp,gettext,iconv,igbinary,imagick,intl,ldap,mbregex,mbstring,mysqli,mysqlnd,opcache,openssl,parallel,pcntl,pdo,pdo_mysql,pdo_pgsql,pdo_sqlite,pgsql,phar,posix,protobuf,readline,redis,session,shmop,simplexml,soap,sockets,sodium,sqlite3,ssh2,sysvmsg,sysvsem,sysvshm,tidy,tokenizer,xlswriter,xml,xmlreader,xmlwriter,zip,zlib,yaml,zstd"
|
||||
# if [ "${os}" != "linux" ] || [ "${SPC_LIBC}" = "glibc" ]; then
|
||||
# defaultExtensions="${defaultExtensions},ffi"
|
||||
# fi
|
||||
defaultExtensionLibs="bzip2,freetype,libavif,libjpeg,liblz4,libwebp,libzip,nghttp2"
|
||||
|
||||
md5binary="md5sum"
|
||||
if [ "${os}" = "darwin" ]; then
|
||||
os="mac"
|
||||
@@ -21,28 +90,16 @@ if [ "${os}" = "linux" ] && ! type "cmake" >/dev/null 2>&1; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "${PHP_EXTENSIONS}" ]; then
|
||||
if [ -n "${EMBED}" ] && [ -f "${EMBED}/composer.json" ]; then
|
||||
cd "${EMBED}"
|
||||
PHP_EXTENSIONS="$(composer check-platform-reqs --no-dev 2>/dev/null | grep ^ext | sed -e 's/^ext-//' -e 's/ .*//' | xargs | tr ' ' ',')"
|
||||
export PHP_EXTENSIONS
|
||||
cd -
|
||||
else
|
||||
export PHP_EXTENSIONS="apcu,bcmath,bz2,calendar,ctype,curl,dba,dom,exif,fileinfo,filter,ftp,gd,gmp,gettext,iconv,igbinary,imagick,intl,ldap,mbregex,mbstring,mysqli,mysqlnd,opcache,openssl,parallel,pcntl,pdo,pdo_mysql,pdo_pgsql,pdo_sqlite,pgsql,phar,posix,protobuf,readline,redis,session,shmop,simplexml,soap,sockets,sodium,sqlite3,ssh2,sysvmsg,sysvsem,sysvshm,tidy,tokenizer,xlswriter,xml,xmlreader,xmlwriter,zip,zlib,yaml,zstd"
|
||||
if [ "${os}" = "linux" ] && { [[ "${arch}" =~ "aarch" ]] || [[ "${arch}" =~ "arm" ]]; }; then
|
||||
fpic="-fPIC"
|
||||
fpie="-fPIE"
|
||||
|
||||
if [ -z "${DEBUG_SYMBOLS}" ]; then
|
||||
export SPC_PHP_DEFAULT_OPTIMIZE_CFLAGS="-g -fstack-protector-strong -fPIC -fPIE -Os -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "${PHP_EXTENSION_LIBS}" ]; then
|
||||
export PHP_EXTENSION_LIBS="bzip2,freetype,libavif,libjpeg,liblz4,libwebp,libzip,nghttp2"
|
||||
fi
|
||||
|
||||
# The Brotli library must always be built as it is required by http://github.com/dunglas/caddy-cbrotli
|
||||
if ! echo "${PHP_EXTENSION_LIBS}" | grep -q "\bbrotli\b"; then
|
||||
export PHP_EXTENSION_LIBS="${PHP_EXTENSION_LIBS},brotli"
|
||||
fi
|
||||
|
||||
if [ -z "${PHP_VERSION}" ]; then
|
||||
export PHP_VERSION="8.3"
|
||||
else
|
||||
fpic="-fpic"
|
||||
fpie="-fpie"
|
||||
fi
|
||||
|
||||
if [ -z "${FRANKENPHP_VERSION}" ]; then
|
||||
@@ -72,52 +129,101 @@ if [ -n "${CLEAN}" ]; then
|
||||
go clean -cache
|
||||
fi
|
||||
|
||||
# Build libphp if necessary
|
||||
if [ -f "dist/static-php-cli/buildroot/lib/libphp.a" ]; then
|
||||
cd dist/static-php-cli
|
||||
else
|
||||
mkdir -p dist/
|
||||
cd dist/
|
||||
mkdir -p dist/
|
||||
cd dist/
|
||||
|
||||
if [ -d "static-php-cli/" ]; then
|
||||
cd static-php-cli/
|
||||
git pull
|
||||
if type "brew" >/dev/null 2>&1; then
|
||||
if ! type "composer" >/dev/null; then
|
||||
packages="composer"
|
||||
fi
|
||||
if ! type "go" >/dev/null 2>&1; then
|
||||
packages="${packages} go"
|
||||
fi
|
||||
if [ -n "${RELEASE}" ] && ! type "gh" >/dev/null 2>&1; then
|
||||
packages="${packages} gh"
|
||||
fi
|
||||
|
||||
if [ -n "${packages}" ]; then
|
||||
# shellcheck disable=SC2086
|
||||
brew install --formula --quiet ${packages}
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "${SPC_REL_TYPE}" = "binary" ]; then
|
||||
mkdir -p static-php-cli/
|
||||
cd static-php-cli/
|
||||
if [[ "${arch}" =~ "arm" ]]; then
|
||||
dl_arch="aarch64"
|
||||
else
|
||||
git clone --depth 1 https://github.com/crazywhalecc/static-php-cli
|
||||
cd static-php-cli/
|
||||
dl_arch="${arch}"
|
||||
fi
|
||||
curl -o spc -fsSL "https://dl.static-php.dev/static-php-cli/spc-bin/nightly/spc-linux-${dl_arch}"
|
||||
chmod +x spc
|
||||
spcCommand="./spc"
|
||||
elif [ -d "static-php-cli/src" ]; then
|
||||
cd static-php-cli/
|
||||
git pull
|
||||
composer install --no-dev -a --no-interaction
|
||||
spcCommand="./bin/spc"
|
||||
else
|
||||
git clone --depth 1 https://github.com/crazywhalecc/static-php-cli --branch main
|
||||
cd static-php-cli/
|
||||
composer install --no-dev -a --no-interaction
|
||||
spcCommand="./bin/spc"
|
||||
fi
|
||||
|
||||
if type "brew" >/dev/null 2>&1; then
|
||||
if ! type "composer" >/dev/null; then
|
||||
packages="composer"
|
||||
fi
|
||||
if ! type "go" >/dev/null; then
|
||||
packages="${packages} go"
|
||||
fi
|
||||
if [ -n "${RELEASE}" ] && ! type "gh" >/dev/null 2>&1; then
|
||||
packages="${packages} gh"
|
||||
fi
|
||||
|
||||
if [ -n "${packages}" ]; then
|
||||
# shellcheck disable=SC2086
|
||||
brew install --formula --quiet ${packages}
|
||||
fi
|
||||
# Extensions to build
|
||||
if [ -z "${PHP_EXTENSIONS}" ]; then
|
||||
# enable EMBED mode, first check if project has dumped extensions
|
||||
if [ -n "${EMBED}" ] && [ -f "${EMBED}/composer.json" ] && [ -f "${EMBED}/composer.lock" ] && [ -f "${EMBED}/vendor/installed.json" ]; then
|
||||
cd "${EMBED}"
|
||||
# read the extensions using spc dump-extensions
|
||||
PHP_EXTENSIONS=$(${spcCommand} dump-extensions "${EMBED}" --format=text --no-dev --no-ext-output="${defaultExtensions}")
|
||||
else
|
||||
PHP_EXTENSIONS="${defaultExtensions}"
|
||||
fi
|
||||
fi
|
||||
|
||||
composer install --no-dev -a
|
||||
# Additional libraries to build
|
||||
if [ -z "${PHP_EXTENSION_LIBS}" ]; then
|
||||
PHP_EXTENSION_LIBS="${defaultExtensionLibs}"
|
||||
fi
|
||||
|
||||
if [ "${os}" = "linux" ]; then
|
||||
extraOpts="--disable-opcache-jit"
|
||||
# The Brotli library must always be built as it is required by http://github.com/dunglas/caddy-cbrotli
|
||||
if ! echo "${PHP_EXTENSION_LIBS}" | grep -q "\bbrotli\b"; then
|
||||
PHP_EXTENSION_LIBS="${PHP_EXTENSION_LIBS},brotli"
|
||||
fi
|
||||
|
||||
# The mimalloc library must be built if MIMALLOC is true
|
||||
if [ -n "${MIMALLOC}" ]; then
|
||||
if ! echo "${PHP_EXTENSION_LIBS}" | grep -q "\bmimalloc\b"; then
|
||||
PHP_EXTENSION_LIBS="${PHP_EXTENSION_LIBS},mimalloc"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -n "${DEBUG_SYMBOLS}" ]; then
|
||||
extraOpts="${extraOpts} --no-strip"
|
||||
fi
|
||||
|
||||
./bin/spc doctor --auto-fix
|
||||
./bin/spc download --with-php="${PHP_VERSION}" --for-extensions="${PHP_EXTENSIONS}" --for-libs="${PHP_EXTENSION_LIBS}" --ignore-cache-sources=php-src --prefer-pre-built
|
||||
# Build libphp if necessary
|
||||
cache_key="${PHP_VERSION}-${PHP_EXTENSIONS}-${PHP_EXTENSION_LIBS}"
|
||||
if [ -f ../cache_key ] && [ "$(cat ../cache_key)" = "${cache_key}" ] && [ -f "buildroot/lib/libphp.a" ]; then
|
||||
echo "Hit cache, skipping libphp build."
|
||||
else
|
||||
${spcCommand} doctor --auto-fix
|
||||
# shellcheck disable=SC2086
|
||||
./bin/spc build --debug --enable-zts --build-embed ${extraOpts} "${PHP_EXTENSIONS}" --with-libs="${PHP_EXTENSION_LIBS}"
|
||||
${spcCommand} download --with-php="${PHP_VERSION}" --for-extensions="${PHP_EXTENSIONS}" --for-libs="${PHP_EXTENSION_LIBS}" ${SPC_OPT_DOWNLOAD_ARGS}
|
||||
# shellcheck disable=SC2086
|
||||
${spcCommand} build --enable-zts --build-embed ${SPC_OPT_BUILD_ARGS} "${PHP_EXTENSIONS}" --with-libs="${PHP_EXTENSION_LIBS}"
|
||||
|
||||
echo -n "${cache_key}" >../cache_key
|
||||
fi
|
||||
|
||||
if ! type "go" >/dev/null 2>&1; then
|
||||
echo "The \"go\" command must be installed."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
XCADDY_COMMAND="xcaddy"
|
||||
if ! type "$XCADDY_COMMAND" >/dev/null 2>&1; then
|
||||
go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest
|
||||
XCADDY_COMMAND="$(go env GOPATH)/bin/xcaddy"
|
||||
fi
|
||||
|
||||
curlGitHubHeaders=(--header "X-GitHub-Api-Version: 2022-11-28")
|
||||
@@ -136,7 +242,12 @@ curl -f --retry 5 "${curlGitHubHeaders[@]}" https://api.github.com/repos/e-dant/
|
||||
xargs curl -fL --retry 5 "${curlGitHubHeaders[@]}" |
|
||||
tar xz --strip-components 1
|
||||
cd watcher-c
|
||||
cc -c -o libwatcher-c.o ./src/watcher-c.cpp -I ./include -I ../include -std=c++17 -Wall -Wextra -fPIC
|
||||
if [ -z "${CC}" ]; then
|
||||
watcherCC=cc
|
||||
else
|
||||
watcherCC="${CC}"
|
||||
fi
|
||||
${watcherCC} -c -o libwatcher-c.o ./src/watcher-c.cpp -I ./include -I ../include -std=c++17 -Wall -Wextra "${fpic}"
|
||||
ar rcs libwatcher-c.a libwatcher-c.o
|
||||
cp libwatcher-c.a ../../buildroot/lib/libwatcher-c.a
|
||||
mkdir -p ../../buildroot/include/wtr
|
||||
@@ -144,11 +255,11 @@ cp -R include/wtr/watcher-c.h ../../buildroot/include/wtr/watcher-c.h
|
||||
cd ../../
|
||||
|
||||
# See https://github.com/docker-library/php/blob/master/8.3/alpine3.20/zts/Dockerfile#L53-L55
|
||||
CGO_CFLAGS="-DFRANKENPHP_VERSION=${FRANKENPHP_VERSION} -I${PWD}/buildroot/include/ $(./buildroot/bin/php-config --includes | sed s#-I/#-I"${PWD}"/buildroot/#g)"
|
||||
CGO_CFLAGS="-DFRANKENPHP_VERSION=${FRANKENPHP_VERSION} -I${PWD}/buildroot/include/ $(${spcCommand} spc-config "${PHP_EXTENSIONS}" --with-libs="${PHP_EXTENSION_LIBS}" --includes)"
|
||||
if [ -n "${DEBUG_SYMBOLS}" ]; then
|
||||
CGO_CFLAGS="-g ${CGO_CFLAGS}"
|
||||
else
|
||||
CGO_CFLAGS="-fstack-protector-strong -fpic -fpie -O2 -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 ${CGO_CFLAGS}"
|
||||
CGO_CFLAGS="-fstack-protector-strong ${fpic} ${fpie} -O2 -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 ${CGO_CFLAGS}"
|
||||
fi
|
||||
export CGO_CFLAGS
|
||||
export CGO_CPPFLAGS="${CGO_CFLAGS}"
|
||||
@@ -158,97 +269,27 @@ if [ "${os}" = "mac" ]; then
|
||||
elif [ "${os}" = "linux" ] && [ -z "${DEBUG_SYMBOLS}" ]; then
|
||||
CGO_LDFLAGS="-Wl,-O1 -pie"
|
||||
fi
|
||||
if [ "${os}" = "linux" ] && [ "${SPC_LIBC}" = "glibc" ]; then
|
||||
CGO_LDFLAGS="${CGO_LDFLAGS} -Wl,--allow-multiple-definition -Wl,--export-dynamic"
|
||||
fi
|
||||
|
||||
CGO_LDFLAGS="${CGO_LDFLAGS} ${PWD}/buildroot/lib/libbrotlicommon.a ${PWD}/buildroot/lib/libbrotlienc.a ${PWD}/buildroot/lib/libbrotlidec.a ${PWD}/buildroot/lib/libwatcher-c.a $(./buildroot/bin/php-config --ldflags || true) $(./buildroot/bin/php-config --libs | sed -e 's/-lgcc_s//g' || true)"
|
||||
if [ "${os}" = "linux" ]; then
|
||||
if echo "${PHP_EXTENSIONS}" | grep -qE "\b(intl|imagick|grpc|v8js|protobuf|mongodb|tbb)\b"; then
|
||||
CGO_LDFLAGS="${CGO_LDFLAGS} -lstdc++"
|
||||
fi
|
||||
CGO_LDFLAGS="${CGO_LDFLAGS} ${PWD}/buildroot/lib/libbrotlicommon.a ${PWD}/buildroot/lib/libbrotlienc.a ${PWD}/buildroot/lib/libbrotlidec.a ${PWD}/buildroot/lib/libwatcher-c.a $(${spcCommand} spc-config "${PHP_EXTENSIONS}" --with-libs="${PHP_EXTENSION_LIBS}" --libs)"
|
||||
if [[ "$CGO_LDFLAGS" == *"${PWD}/buildroot/lib/mimalloc.o"* ]]; then
|
||||
CGO_LDFLAGS=${CGO_LDFLAGS//${PWD}\/buildroot\/lib\/mimalloc.o/}
|
||||
CGO_LDFLAGS="${PWD}/buildroot/lib/libmimalloc.a $CGO_LDFLAGS"
|
||||
fi
|
||||
if [ "${os}" = "linux" ] && [ "${SPC_LIBC}" = "glibc" ]; then
|
||||
CGO_LDFLAGS="${CGO_LDFLAGS//-lphp/-Wl,--whole-archive -lphp -Wl,--no-whole-archive}"
|
||||
# shellcheck disable=SC2046
|
||||
ar d "${PWD}/buildroot/lib/libphp.a" $(ar t "${PWD}/buildroot/lib/libphp.a" | grep '\.a$')
|
||||
fi
|
||||
|
||||
export CGO_LDFLAGS
|
||||
|
||||
LIBPHP_VERSION="$(./buildroot/bin/php-config --version)"
|
||||
export LIBPHP_VERSION
|
||||
|
||||
cd ../
|
||||
|
||||
if [ "${os}" = "linux" ]; then
|
||||
if [ -n "${MIMALLOC}" ]; then
|
||||
# Replace musl's mallocng by mimalloc
|
||||
# The default musl allocator is slow, especially when used by multi-threaded apps,
|
||||
# and triggers weird bugs
|
||||
# Adapted from https://www.tweag.io/blog/2023-08-10-rust-static-link-with-mimalloc/
|
||||
|
||||
echo 'The USE_MIMALLOC environment variable is EXPERIMENTAL.'
|
||||
echo 'This option can be removed or its behavior modified at any time.'
|
||||
|
||||
if [ ! -f "mimalloc/out/libmimalloc.a" ]; then
|
||||
if [ -d "mimalloc" ]; then
|
||||
cd mimalloc/
|
||||
git reset --hard
|
||||
git clean -xdf
|
||||
git fetch --tags
|
||||
else
|
||||
git clone https://github.com/microsoft/mimalloc.git
|
||||
cd mimalloc/
|
||||
fi
|
||||
|
||||
git checkout "$(git describe --tags "$(git rev-list --tags --max-count=1 || true)" || true)"
|
||||
|
||||
curl -fL --retry 5 https://raw.githubusercontent.com/tweag/rust-alpine-mimalloc/b26002b49d466a295ea8b50828cb7520a71a872a/mimalloc.diff -o mimalloc.diff
|
||||
patch -p1 <mimalloc.diff
|
||||
|
||||
mkdir -p out/
|
||||
cd out/
|
||||
if [ -n "${DEBUG_SYMBOLS}" ]; then
|
||||
cmake \
|
||||
-DCMAKE_BUILD_TYPE=Debug \
|
||||
-DMI_BUILD_SHARED=OFF \
|
||||
-DMI_BUILD_OBJECT=OFF \
|
||||
-DMI_BUILD_TESTS=OFF \
|
||||
../
|
||||
else
|
||||
cmake \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DMI_BUILD_SHARED=OFF \
|
||||
-DMI_BUILD_OBJECT=OFF \
|
||||
-DMI_BUILD_TESTS=OFF \
|
||||
../
|
||||
fi
|
||||
make -j"$(nproc || true)"
|
||||
|
||||
cd ../../
|
||||
fi
|
||||
|
||||
if [ -n "${DEBUG_SYMBOLS}" ]; then
|
||||
libmimalloc_path=mimalloc/out/libmimalloc-debug.a
|
||||
else
|
||||
libmimalloc_path=mimalloc/out/libmimalloc.a
|
||||
fi
|
||||
|
||||
# Patch musl library to use mimalloc
|
||||
for libc_path in "/usr/local/musl/lib/libc.a" "/usr/local/musl/$(uname -m)-linux-musl/lib/libc.a" "/usr/lib/libc.a"; do
|
||||
if [ ! -f "${libc_path}" ] || [ -f "${libc_path}.unpatched" ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
{
|
||||
echo "CREATE libc.a"
|
||||
echo "ADDLIB ${libc_path}"
|
||||
echo "DELETE aligned_alloc.lo calloc.lo donate.lo free.lo libc_calloc.lo lite_malloc.lo malloc.lo malloc_usable_size.lo memalign.lo posix_memalign.lo realloc.lo reallocarray.lo valloc.lo"
|
||||
echo "ADDLIB ${libmimalloc_path}"
|
||||
echo "SAVE"
|
||||
} | ar -M
|
||||
mv "${libc_path}" "${libc_path}.unpatched"
|
||||
mv libc.a "${libc_path}"
|
||||
done
|
||||
fi
|
||||
|
||||
# Increase the default stack size to prevents issues with code including many files such as Symfony containers
|
||||
extraExtldflags="-Wl,-z,stack-size=0x80000"
|
||||
fi
|
||||
|
||||
if [ -z "${DEBUG_SYMBOLS}" ]; then
|
||||
extraLdflags="-w -s"
|
||||
fi
|
||||
@@ -261,10 +302,37 @@ if [ -n "${EMBED}" ] && [ -d "${EMBED}" ]; then
|
||||
${md5binary} app.tar | awk '{printf $1}' >app_checksum.txt
|
||||
fi
|
||||
|
||||
cd caddy/frankenphp/
|
||||
if [ -z "${XCADDY_ARGS}" ]; then
|
||||
XCADDY_ARGS="--with github.com/dunglas/caddy-cbrotli --with github.com/dunglas/mercure/caddy --with github.com/dunglas/vulcain/caddy"
|
||||
fi
|
||||
|
||||
XCADDY_DEBUG=0
|
||||
if [ -n "${DEBUG_SYMBOLS}" ]; then
|
||||
XCADDY_DEBUG=1
|
||||
fi
|
||||
|
||||
if [ "${SPC_LIBC}" = "musl" ]; then
|
||||
muslStackSizeFix="-Wl,-z,stack-size=0x80000"
|
||||
fi
|
||||
|
||||
go env
|
||||
go build -buildmode=pie -tags "cgo,netgo,osusergo,static_build,nobadger,nomysql,nopgx" -ldflags "-linkmode=external -extldflags '-static-pie ${extraExtldflags}' ${extraLdflags} -X 'github.com/caddyserver/caddy/v2.CustomVersion=FrankenPHP ${FRANKENPHP_VERSION} PHP ${LIBPHP_VERSION} Caddy'" -o "../../dist/${bin}"
|
||||
cd ../..
|
||||
cd caddy/
|
||||
if [ -z "${SPC_LIBC}" ] || [ "${SPC_LIBC}" = "musl" ]; then
|
||||
xcaddyGoBuildFlags="-buildmode=pie -tags cgo,netgo,osusergo,static_build,nobadger,nomysql,nopgx -ldflags \"-linkmode=external -extldflags '-static-pie ${muslStackSizeFix}' ${extraLdflags} -X 'github.com/caddyserver/caddy/v2.CustomVersion=FrankenPHP ${FRANKENPHP_VERSION} PHP ${LIBPHP_VERSION} Caddy'\""
|
||||
elif [ "${SPC_LIBC}" = "glibc" ]; then
|
||||
xcaddyGoBuildFlags="-buildmode=pie -tags cgo,netgo,osusergo,nobadger,nomysql,nopgx -ldflags \"-linkmode=external -extldflags '-pie' ${extraLdflags} -X 'github.com/caddyserver/caddy/v2.CustomVersion=FrankenPHP ${FRANKENPHP_VERSION} PHP ${LIBPHP_VERSION} Caddy'\""
|
||||
fi
|
||||
|
||||
# shellcheck disable=SC2086
|
||||
CGO_ENABLED=1 \
|
||||
XCADDY_GO_BUILD_FLAGS=${xcaddyGoBuildFlags} \
|
||||
XCADDY_DEBUG="${XCADDY_DEBUG}" \
|
||||
${XCADDY_COMMAND} build \
|
||||
--output "../dist/${bin}" \
|
||||
${XCADDY_ARGS} \
|
||||
--with frankenphp.dev=.. \
|
||||
--with frankenphp.dev/caddy=.
|
||||
cd ..
|
||||
|
||||
if [ -d "${EMBED}" ]; then
|
||||
truncate -s 0 app.tar
|
||||
@@ -276,6 +344,7 @@ if type "upx" >/dev/null 2>&1 && [ -z "${DEBUG_SYMBOLS}" ] && [ -z "${NO_COMPRES
|
||||
fi
|
||||
|
||||
"dist/${bin}" version
|
||||
"dist/${bin}" build-info
|
||||
|
||||
if [ -n "${RELEASE}" ]; then
|
||||
gh release upload "v${FRANKENPHP_VERSION}" "dist/${bin}" --repo dunglas/frankenphp --clobber
|
||||
|
||||
65
caddy/admin.go
Normal file
65
caddy/admin.go
Normal file
@@ -0,0 +1,65 @@
|
||||
package caddy
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/caddyserver/caddy/v2"
|
||||
"frankenphp.dev"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type FrankenPHPAdmin struct{}
|
||||
|
||||
// if the id starts with "admin.api" the module will register AdminRoutes via module.Routes()
|
||||
func (FrankenPHPAdmin) CaddyModule() caddy.ModuleInfo {
|
||||
return caddy.ModuleInfo{
|
||||
ID: "admin.api.frankenphp",
|
||||
New: func() caddy.Module { return new(FrankenPHPAdmin) },
|
||||
}
|
||||
}
|
||||
|
||||
// EXPERIMENTAL: These routes are not yet stable and may change in the future.
|
||||
func (admin FrankenPHPAdmin) Routes() []caddy.AdminRoute {
|
||||
return []caddy.AdminRoute{
|
||||
{
|
||||
Pattern: "/frankenphp/workers/restart",
|
||||
Handler: caddy.AdminHandlerFunc(admin.restartWorkers),
|
||||
},
|
||||
{
|
||||
Pattern: "/frankenphp/threads",
|
||||
Handler: caddy.AdminHandlerFunc(admin.threads),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (admin *FrankenPHPAdmin) restartWorkers(w http.ResponseWriter, r *http.Request) error {
|
||||
if r.Method != http.MethodPost {
|
||||
return admin.error(http.StatusMethodNotAllowed, fmt.Errorf("method not allowed"))
|
||||
}
|
||||
|
||||
frankenphp.RestartWorkers()
|
||||
caddy.Log().Info("workers restarted from admin api")
|
||||
admin.success(w, "workers restarted successfully\n")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (admin *FrankenPHPAdmin) threads(w http.ResponseWriter, _ *http.Request) error {
|
||||
debugState := frankenphp.DebugState()
|
||||
prettyJson, err := json.MarshalIndent(debugState, "", " ")
|
||||
if err != nil {
|
||||
return admin.error(http.StatusInternalServerError, err)
|
||||
}
|
||||
|
||||
return admin.success(w, string(prettyJson))
|
||||
}
|
||||
|
||||
func (admin *FrankenPHPAdmin) success(w http.ResponseWriter, message string) error {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, err := w.Write([]byte(message))
|
||||
return err
|
||||
}
|
||||
|
||||
func (admin *FrankenPHPAdmin) error(statusCode int, err error) error {
|
||||
return caddy.APIError{HTTPStatus: statusCode, Err: err}
|
||||
}
|
||||
293
caddy/admin_test.go
Normal file
293
caddy/admin_test.go
Normal file
@@ -0,0 +1,293 @@
|
||||
package caddy_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"frankenphp.dev"
|
||||
"frankenphp.dev/internal/fastabs"
|
||||
"github.com/caddyserver/caddy/v2/caddytest"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestRestartWorkerViaAdminApi(t *testing.T) {
|
||||
tester := caddytest.NewTester(t)
|
||||
tester.InitServer(`
|
||||
{
|
||||
skip_install_trust
|
||||
admin localhost:2999
|
||||
http_port `+testPort+`
|
||||
|
||||
frankenphp {
|
||||
worker ../testdata/worker-with-counter.php 1
|
||||
}
|
||||
}
|
||||
|
||||
localhost:`+testPort+` {
|
||||
route {
|
||||
root ../testdata
|
||||
rewrite worker-with-counter.php
|
||||
php
|
||||
}
|
||||
}
|
||||
`, "caddyfile")
|
||||
|
||||
tester.AssertGetResponse("http://localhost:"+testPort+"/", http.StatusOK, "requests:1")
|
||||
tester.AssertGetResponse("http://localhost:"+testPort+"/", http.StatusOK, "requests:2")
|
||||
|
||||
assertAdminResponse(t, tester, "POST", "workers/restart", http.StatusOK, "workers restarted successfully\n")
|
||||
|
||||
tester.AssertGetResponse("http://localhost:"+testPort+"/", http.StatusOK, "requests:1")
|
||||
}
|
||||
|
||||
func TestShowTheCorrectThreadDebugStatus(t *testing.T) {
|
||||
tester := caddytest.NewTester(t)
|
||||
tester.InitServer(`
|
||||
{
|
||||
skip_install_trust
|
||||
admin localhost:2999
|
||||
http_port `+testPort+`
|
||||
|
||||
frankenphp {
|
||||
num_threads 3
|
||||
max_threads 6
|
||||
worker ../testdata/worker-with-counter.php 1
|
||||
worker ../testdata/index.php 1
|
||||
}
|
||||
}
|
||||
|
||||
localhost:`+testPort+` {
|
||||
route {
|
||||
root ../testdata
|
||||
rewrite worker-with-counter.php
|
||||
php
|
||||
}
|
||||
}
|
||||
`, "caddyfile")
|
||||
|
||||
debugState := getDebugState(t, tester)
|
||||
|
||||
// assert that the correct threads are present in the thread info
|
||||
assert.Equal(t, debugState.ThreadDebugStates[0].State, "ready")
|
||||
assert.Contains(t, debugState.ThreadDebugStates[1].Name, "worker-with-counter.php")
|
||||
assert.Contains(t, debugState.ThreadDebugStates[2].Name, "index.php")
|
||||
assert.Equal(t, debugState.ReservedThreadCount, 3)
|
||||
assert.Len(t, debugState.ThreadDebugStates, 3)
|
||||
}
|
||||
|
||||
func TestAutoScaleWorkerThreads(t *testing.T) {
|
||||
wg := sync.WaitGroup{}
|
||||
maxTries := 10
|
||||
requestsPerTry := 200
|
||||
tester := caddytest.NewTester(t)
|
||||
tester.InitServer(`
|
||||
{
|
||||
skip_install_trust
|
||||
admin localhost:2999
|
||||
http_port `+testPort+`
|
||||
|
||||
frankenphp {
|
||||
max_threads 10
|
||||
num_threads 2
|
||||
worker ../testdata/sleep.php 1
|
||||
}
|
||||
}
|
||||
|
||||
localhost:`+testPort+` {
|
||||
route {
|
||||
root ../testdata
|
||||
rewrite sleep.php
|
||||
php
|
||||
}
|
||||
}
|
||||
`, "caddyfile")
|
||||
|
||||
// spam an endpoint that simulates IO
|
||||
endpoint := "http://localhost:" + testPort + "/?sleep=2&work=1000"
|
||||
amountOfThreads := len(getDebugState(t, tester).ThreadDebugStates)
|
||||
|
||||
// try to spawn the additional threads by spamming the server
|
||||
for tries := 0; tries < maxTries; tries++ {
|
||||
wg.Add(requestsPerTry)
|
||||
for i := 0; i < requestsPerTry; i++ {
|
||||
go func() {
|
||||
tester.AssertGetResponse(endpoint, http.StatusOK, "slept for 2 ms and worked for 1000 iterations")
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
amountOfThreads = len(getDebugState(t, tester).ThreadDebugStates)
|
||||
if amountOfThreads > 2 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// assert that there are now more threads than before
|
||||
assert.NotEqual(t, amountOfThreads, 2)
|
||||
}
|
||||
|
||||
// Note this test requires at least 2x40MB available memory for the process
|
||||
func TestAutoScaleRegularThreadsOnAutomaticThreadLimit(t *testing.T) {
|
||||
wg := sync.WaitGroup{}
|
||||
maxTries := 10
|
||||
requestsPerTry := 200
|
||||
tester := caddytest.NewTester(t)
|
||||
tester.InitServer(`
|
||||
{
|
||||
skip_install_trust
|
||||
admin localhost:2999
|
||||
http_port `+testPort+`
|
||||
|
||||
frankenphp {
|
||||
max_threads auto
|
||||
num_threads 1
|
||||
php_ini memory_limit 40M # a reasonable limit for the test
|
||||
}
|
||||
}
|
||||
|
||||
localhost:`+testPort+` {
|
||||
route {
|
||||
root ../testdata
|
||||
php
|
||||
}
|
||||
}
|
||||
`, "caddyfile")
|
||||
|
||||
// spam an endpoint that simulates IO
|
||||
endpoint := "http://localhost:" + testPort + "/sleep.php?sleep=2&work=1000"
|
||||
amountOfThreads := len(getDebugState(t, tester).ThreadDebugStates)
|
||||
|
||||
// try to spawn the additional threads by spamming the server
|
||||
for tries := 0; tries < maxTries; tries++ {
|
||||
wg.Add(requestsPerTry)
|
||||
for i := 0; i < requestsPerTry; i++ {
|
||||
go func() {
|
||||
tester.AssertGetResponse(endpoint, http.StatusOK, "slept for 2 ms and worked for 1000 iterations")
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
amountOfThreads = len(getDebugState(t, tester).ThreadDebugStates)
|
||||
if amountOfThreads > 1 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// assert that there are now more threads present
|
||||
assert.NotEqual(t, amountOfThreads, 1)
|
||||
}
|
||||
|
||||
func assertAdminResponse(t *testing.T, tester *caddytest.Tester, method string, path string, expectedStatus int, expectedBody string) {
|
||||
adminUrl := "http://localhost:2999/frankenphp/"
|
||||
r, err := http.NewRequest(method, adminUrl+path, nil)
|
||||
assert.NoError(t, err)
|
||||
if expectedBody == "" {
|
||||
_ = tester.AssertResponseCode(r, expectedStatus)
|
||||
return
|
||||
}
|
||||
_, _ = tester.AssertResponse(r, expectedStatus, expectedBody)
|
||||
}
|
||||
|
||||
func getAdminResponseBody(t *testing.T, tester *caddytest.Tester, method string, path string) string {
|
||||
adminUrl := "http://localhost:2999/frankenphp/"
|
||||
r, err := http.NewRequest(method, adminUrl+path, nil)
|
||||
assert.NoError(t, err)
|
||||
resp := tester.AssertResponseCode(r, http.StatusOK)
|
||||
defer resp.Body.Close()
|
||||
bytes, err := io.ReadAll(resp.Body)
|
||||
assert.NoError(t, err)
|
||||
|
||||
return string(bytes)
|
||||
}
|
||||
|
||||
func getDebugState(t *testing.T, tester *caddytest.Tester) frankenphp.FrankenPHPDebugState {
|
||||
threadStates := getAdminResponseBody(t, tester, "GET", "threads")
|
||||
|
||||
var debugStates frankenphp.FrankenPHPDebugState
|
||||
err := json.Unmarshal([]byte(threadStates), &debugStates)
|
||||
assert.NoError(t, err)
|
||||
|
||||
return debugStates
|
||||
}
|
||||
|
||||
func TestAddModuleWorkerViaAdminApi(t *testing.T) {
|
||||
// Initialize a server with admin API enabled
|
||||
tester := caddytest.NewTester(t)
|
||||
tester.InitServer(`
|
||||
{
|
||||
skip_install_trust
|
||||
admin localhost:2999
|
||||
http_port `+testPort+`
|
||||
}
|
||||
|
||||
localhost:`+testPort+` {
|
||||
route {
|
||||
root ../testdata
|
||||
php
|
||||
}
|
||||
}
|
||||
`, "caddyfile")
|
||||
|
||||
// Get initial debug state to check number of workers
|
||||
initialDebugState := getDebugState(t, tester)
|
||||
initialWorkerCount := 0
|
||||
for _, thread := range initialDebugState.ThreadDebugStates {
|
||||
if thread.Name != "" && thread.Name != "ready" {
|
||||
initialWorkerCount++
|
||||
}
|
||||
}
|
||||
|
||||
// Create a Caddyfile configuration with a module worker
|
||||
workerConfig := `
|
||||
{
|
||||
skip_install_trust
|
||||
admin localhost:2999
|
||||
http_port ` + testPort + `
|
||||
}
|
||||
|
||||
localhost:` + testPort + ` {
|
||||
route {
|
||||
root ../testdata
|
||||
php {
|
||||
worker ../testdata/worker-with-counter.php 1
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
// Send the configuration to the admin API
|
||||
adminUrl := "http://localhost:2999/load"
|
||||
r, err := http.NewRequest("POST", adminUrl, bytes.NewBufferString(workerConfig))
|
||||
assert.NoError(t, err)
|
||||
r.Header.Set("Content-Type", "text/caddyfile")
|
||||
resp := tester.AssertResponseCode(r, http.StatusOK)
|
||||
defer resp.Body.Close()
|
||||
|
||||
// Get the updated debug state to check if the worker was added
|
||||
updatedDebugState := getDebugState(t, tester)
|
||||
updatedWorkerCount := 0
|
||||
workerFound := false
|
||||
filename, _ := fastabs.FastAbs("../testdata/worker-with-counter.php")
|
||||
for _, thread := range updatedDebugState.ThreadDebugStates {
|
||||
if thread.Name != "" && thread.Name != "ready" {
|
||||
updatedWorkerCount++
|
||||
if thread.Name == "Worker PHP Thread - "+filename {
|
||||
workerFound = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Assert that the worker was added
|
||||
assert.Greater(t, updatedWorkerCount, initialWorkerCount, "Worker count should have increased")
|
||||
assert.True(t, workerFound, fmt.Sprintf("Worker with name %q should be found", "Worker PHP Thread - "+filename))
|
||||
|
||||
// Make a request to the worker to verify it's working
|
||||
tester.AssertGetResponse("http://localhost:"+testPort+"/worker-with-counter.php", http.StatusOK, "requests:1")
|
||||
}
|
||||
549
caddy/caddy.go
549
caddy/caddy.go
@@ -7,12 +7,14 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/dunglas/frankenphp/internal/fastabs"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"frankenphp.dev/internal/fastabs"
|
||||
|
||||
"github.com/caddyserver/caddy/v2"
|
||||
"github.com/caddyserver/caddy/v2/caddyconfig"
|
||||
@@ -21,15 +23,20 @@ import (
|
||||
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
|
||||
"github.com/caddyserver/caddy/v2/modules/caddyhttp/fileserver"
|
||||
"github.com/caddyserver/caddy/v2/modules/caddyhttp/rewrite"
|
||||
"github.com/dunglas/frankenphp"
|
||||
"go.uber.org/zap"
|
||||
"frankenphp.dev"
|
||||
)
|
||||
|
||||
const defaultDocumentRoot = "public"
|
||||
const (
|
||||
defaultDocumentRoot = "public"
|
||||
defaultWatchPattern = "./**/*.{php,yaml,yml,twig,env}"
|
||||
)
|
||||
|
||||
var iniError = errors.New("'php_ini' must be in the format: php_ini \"<key>\" \"<value>\"")
|
||||
|
||||
func init() {
|
||||
caddy.RegisterModule(FrankenPHPApp{})
|
||||
caddy.RegisterModule(FrankenPHPModule{})
|
||||
caddy.RegisterModule(FrankenPHPAdmin{})
|
||||
|
||||
httpcaddyfile.RegisterGlobalOption("frankenphp", parseGlobalOption)
|
||||
|
||||
@@ -40,23 +47,9 @@ func init() {
|
||||
httpcaddyfile.RegisterDirectiveOrder("php_server", "before", "file_server")
|
||||
}
|
||||
|
||||
type mainPHPinterpreterKeyType int
|
||||
|
||||
var mainPHPInterpreterKey mainPHPinterpreterKeyType
|
||||
|
||||
var phpInterpreter = caddy.NewUsagePool()
|
||||
|
||||
var metrics = frankenphp.NewPrometheusMetrics(prometheus.DefaultRegisterer)
|
||||
|
||||
type phpInterpreterDestructor struct{}
|
||||
|
||||
func (phpInterpreterDestructor) Destruct() error {
|
||||
frankenphp.Shutdown()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type workerConfig struct {
|
||||
// Name for the worker. Default: the filename for FrankenPHPApp workers, always prefixed with "m#" for FrankenPHPModule workers.
|
||||
Name string `json:"name,omitempty"`
|
||||
// FileName sets the path to the worker script.
|
||||
FileName string `json:"file_name,omitempty"`
|
||||
// Num sets the number of workers to start.
|
||||
@@ -70,8 +63,17 @@ type workerConfig struct {
|
||||
type FrankenPHPApp struct {
|
||||
// NumThreads sets the number of PHP threads to start. Default: 2x the number of available CPUs.
|
||||
NumThreads int `json:"num_threads,omitempty"`
|
||||
// MaxThreads limits how many threads can be started at runtime. Default 2x NumThreads
|
||||
MaxThreads int `json:"max_threads,omitempty"`
|
||||
// Workers configures the worker scripts to start.
|
||||
Workers []workerConfig `json:"workers,omitempty"`
|
||||
// Overwrites the default php ini configuration
|
||||
PhpIni map[string]string `json:"php_ini,omitempty"`
|
||||
// The maximum amount of time a request may be stalled waiting for a thread
|
||||
MaxWaitTime time.Duration `json:"max_wait_time,omitempty"`
|
||||
|
||||
metrics frankenphp.Metrics
|
||||
logger *slog.Logger
|
||||
}
|
||||
|
||||
// CaddyModule returns the Caddy module information.
|
||||
@@ -82,127 +84,287 @@ func (f FrankenPHPApp) CaddyModule() caddy.ModuleInfo {
|
||||
}
|
||||
}
|
||||
|
||||
// Provision sets up the module.
|
||||
func (f *FrankenPHPApp) Provision(ctx caddy.Context) error {
|
||||
f.logger = ctx.Slogger()
|
||||
|
||||
if httpApp, err := ctx.AppIfConfigured("http"); err == nil {
|
||||
if httpApp.(*caddyhttp.App).Metrics != nil {
|
||||
f.metrics = frankenphp.NewPrometheusMetrics(ctx.GetMetricsRegistry())
|
||||
}
|
||||
} else {
|
||||
// if the http module is not configured (this should never happen) then collect the metrics by default
|
||||
f.metrics = frankenphp.NewPrometheusMetrics(ctx.GetMetricsRegistry())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *FrankenPHPApp) generateUniqueModuleWorkerName(filepath string) string {
|
||||
var i uint
|
||||
filepath, _ = fastabs.FastAbs(filepath)
|
||||
name := "m#" + filepath
|
||||
|
||||
retry:
|
||||
for _, wc := range f.Workers {
|
||||
if wc.Name == name {
|
||||
name = fmt.Sprintf("m#%s_%d", filepath, i)
|
||||
i++
|
||||
|
||||
goto retry
|
||||
}
|
||||
}
|
||||
|
||||
return name
|
||||
}
|
||||
|
||||
func (f *FrankenPHPApp) addModuleWorkers(workers ...workerConfig) ([]workerConfig, error) {
|
||||
for i := range workers {
|
||||
w := &workers[i]
|
||||
if frankenphp.EmbeddedAppPath != "" && filepath.IsLocal(w.FileName) {
|
||||
w.FileName = filepath.Join(frankenphp.EmbeddedAppPath, w.FileName)
|
||||
}
|
||||
if w.Name == "" {
|
||||
w.Name = f.generateUniqueModuleWorkerName(w.FileName)
|
||||
} else if !strings.HasPrefix(w.Name, "m#") {
|
||||
w.Name = "m#" + w.Name
|
||||
}
|
||||
f.Workers = append(f.Workers, *w)
|
||||
}
|
||||
return workers, nil
|
||||
}
|
||||
|
||||
func (f *FrankenPHPApp) Start() error {
|
||||
repl := caddy.NewReplacer()
|
||||
logger := caddy.Log()
|
||||
|
||||
opts := []frankenphp.Option{frankenphp.WithNumThreads(f.NumThreads), frankenphp.WithLogger(logger), frankenphp.WithMetrics(metrics)}
|
||||
for _, w := range f.Workers {
|
||||
opts = append(opts, frankenphp.WithWorkers(repl.ReplaceKnown(w.FileName, ""), w.Num, w.Env, w.Watch))
|
||||
opts := []frankenphp.Option{
|
||||
frankenphp.WithNumThreads(f.NumThreads),
|
||||
frankenphp.WithMaxThreads(f.MaxThreads),
|
||||
frankenphp.WithLogger(f.logger),
|
||||
frankenphp.WithMetrics(f.metrics),
|
||||
frankenphp.WithPhpIni(f.PhpIni),
|
||||
frankenphp.WithMaxWaitTime(f.MaxWaitTime),
|
||||
}
|
||||
for _, w := range append(f.Workers) {
|
||||
opts = append(opts, frankenphp.WithWorkers(w.Name, repl.ReplaceKnown(w.FileName, ""), w.Num, w.Env, w.Watch))
|
||||
}
|
||||
|
||||
_, loaded, err := phpInterpreter.LoadOrNew(mainPHPInterpreterKey, func() (caddy.Destructor, error) {
|
||||
if err := frankenphp.Init(opts...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return phpInterpreterDestructor{}, nil
|
||||
})
|
||||
if err != nil {
|
||||
frankenphp.Shutdown()
|
||||
if err := frankenphp.Init(opts...); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if loaded {
|
||||
frankenphp.Shutdown()
|
||||
if err := frankenphp.Init(opts...); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *FrankenPHPApp) Stop() error {
|
||||
caddy.Log().Info("FrankenPHP stopped 🐘")
|
||||
// reset configuration so it doesn't bleed into later tests
|
||||
f.logger.Info("FrankenPHP stopped 🐘")
|
||||
|
||||
// attempt a graceful shutdown if caddy is exiting
|
||||
// note: Exiting() is currently marked as 'experimental'
|
||||
// https://github.com/caddyserver/caddy/blob/e76405d55058b0a3e5ba222b44b5ef00516116aa/caddy.go#L810
|
||||
if caddy.Exiting() {
|
||||
frankenphp.DrainWorkers()
|
||||
}
|
||||
|
||||
// reset the configuration so it doesn't bleed into later tests
|
||||
f.Workers = nil
|
||||
f.NumThreads = 0
|
||||
f.MaxWaitTime = 0
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseWorkerConfig(d *caddyfile.Dispenser) (workerConfig, error) {
|
||||
wc := workerConfig{}
|
||||
if d.NextArg() {
|
||||
wc.FileName = d.Val()
|
||||
}
|
||||
|
||||
if d.NextArg() {
|
||||
if d.Val() == "watch" {
|
||||
wc.Watch = append(wc.Watch, defaultWatchPattern)
|
||||
} else {
|
||||
v, err := strconv.ParseUint(d.Val(), 10, 32)
|
||||
if err != nil {
|
||||
return wc, err
|
||||
}
|
||||
|
||||
wc.Num = int(v)
|
||||
}
|
||||
}
|
||||
|
||||
if d.NextArg() {
|
||||
return wc, errors.New(`FrankenPHP: too many "worker" arguments: ` + d.Val())
|
||||
}
|
||||
|
||||
for d.NextBlock(1) {
|
||||
v := d.Val()
|
||||
switch v {
|
||||
case "name":
|
||||
if !d.NextArg() {
|
||||
return wc, d.ArgErr()
|
||||
}
|
||||
wc.Name = d.Val()
|
||||
case "file":
|
||||
if !d.NextArg() {
|
||||
return wc, d.ArgErr()
|
||||
}
|
||||
wc.FileName = d.Val()
|
||||
case "num":
|
||||
if !d.NextArg() {
|
||||
return wc, d.ArgErr()
|
||||
}
|
||||
|
||||
v, err := strconv.ParseUint(d.Val(), 10, 32)
|
||||
if err != nil {
|
||||
return wc, err
|
||||
}
|
||||
|
||||
wc.Num = int(v)
|
||||
case "env":
|
||||
args := d.RemainingArgs()
|
||||
if len(args) != 2 {
|
||||
return wc, d.ArgErr()
|
||||
}
|
||||
if wc.Env == nil {
|
||||
wc.Env = make(map[string]string)
|
||||
}
|
||||
wc.Env[args[0]] = args[1]
|
||||
case "watch":
|
||||
if !d.NextArg() {
|
||||
// the default if the watch directory is left empty:
|
||||
wc.Watch = append(wc.Watch, defaultWatchPattern)
|
||||
} else {
|
||||
wc.Watch = append(wc.Watch, d.Val())
|
||||
}
|
||||
default:
|
||||
allowedDirectives := "name, file, num, env, watch"
|
||||
return wc, wrongSubDirectiveError("worker", allowedDirectives, v)
|
||||
}
|
||||
}
|
||||
|
||||
if wc.FileName == "" {
|
||||
return wc, errors.New(`the "file" argument must be specified`)
|
||||
}
|
||||
|
||||
if frankenphp.EmbeddedAppPath != "" && filepath.IsLocal(wc.FileName) {
|
||||
wc.FileName = filepath.Join(frankenphp.EmbeddedAppPath, wc.FileName)
|
||||
}
|
||||
|
||||
return wc, nil
|
||||
}
|
||||
|
||||
// UnmarshalCaddyfile implements caddyfile.Unmarshaler.
|
||||
func (f *FrankenPHPApp) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||
for d.Next() {
|
||||
for d.NextBlock(0) {
|
||||
// when adding a new directive, also update the allowedDirectives error message
|
||||
switch d.Val() {
|
||||
case "num_threads":
|
||||
if !d.NextArg() {
|
||||
return d.ArgErr()
|
||||
}
|
||||
|
||||
v, err := strconv.Atoi(d.Val())
|
||||
v, err := strconv.ParseUint(d.Val(), 10, 32)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
f.NumThreads = v
|
||||
case "worker":
|
||||
wc := workerConfig{}
|
||||
if d.NextArg() {
|
||||
wc.FileName = d.Val()
|
||||
f.NumThreads = int(v)
|
||||
case "max_threads":
|
||||
if !d.NextArg() {
|
||||
return d.ArgErr()
|
||||
}
|
||||
|
||||
if d.NextArg() {
|
||||
v, err := strconv.Atoi(d.Val())
|
||||
if d.Val() == "auto" {
|
||||
f.MaxThreads = -1
|
||||
continue
|
||||
}
|
||||
|
||||
v, err := strconv.ParseUint(d.Val(), 10, 32)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
f.MaxThreads = int(v)
|
||||
case "max_wait_time":
|
||||
if !d.NextArg() {
|
||||
return d.ArgErr()
|
||||
}
|
||||
|
||||
v, err := time.ParseDuration(d.Val())
|
||||
if err != nil {
|
||||
return errors.New("max_wait_time must be a valid duration (example: 10s)")
|
||||
}
|
||||
|
||||
f.MaxWaitTime = v
|
||||
case "php_ini":
|
||||
parseIniLine := func(d *caddyfile.Dispenser) error {
|
||||
key := d.Val()
|
||||
if !d.NextArg() {
|
||||
return iniError
|
||||
}
|
||||
if f.PhpIni == nil {
|
||||
f.PhpIni = make(map[string]string)
|
||||
}
|
||||
f.PhpIni[key] = d.Val()
|
||||
if d.NextArg() {
|
||||
return iniError
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
isBlock := false
|
||||
for d.NextBlock(1) {
|
||||
isBlock = true
|
||||
err := parseIniLine(d)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
wc.Num = v
|
||||
}
|
||||
|
||||
for d.NextBlock(1) {
|
||||
v := d.Val()
|
||||
switch v {
|
||||
case "file":
|
||||
if !d.NextArg() {
|
||||
return d.ArgErr()
|
||||
}
|
||||
wc.FileName = d.Val()
|
||||
case "num":
|
||||
if !d.NextArg() {
|
||||
return d.ArgErr()
|
||||
}
|
||||
|
||||
v, err := strconv.Atoi(d.Val())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
wc.Num = v
|
||||
case "env":
|
||||
args := d.RemainingArgs()
|
||||
if len(args) != 2 {
|
||||
return d.ArgErr()
|
||||
}
|
||||
if wc.Env == nil {
|
||||
wc.Env = make(map[string]string)
|
||||
}
|
||||
wc.Env[args[0]] = args[1]
|
||||
case "watch":
|
||||
if !d.NextArg() {
|
||||
// the default if the watch directory is left empty:
|
||||
wc.Watch = append(wc.Watch, "./**/*.{php,yaml,yml,twig,env}")
|
||||
} else {
|
||||
wc.Watch = append(wc.Watch, d.Val())
|
||||
}
|
||||
if !isBlock {
|
||||
if !d.NextArg() {
|
||||
return iniError
|
||||
}
|
||||
|
||||
if wc.FileName == "" {
|
||||
return errors.New(`the "file" argument must be specified`)
|
||||
err := parseIniLine(d)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if frankenphp.EmbeddedAppPath != "" && filepath.IsLocal(wc.FileName) {
|
||||
wc.FileName = filepath.Join(frankenphp.EmbeddedAppPath, wc.FileName)
|
||||
case "worker":
|
||||
wc, err := parseWorkerConfig(d)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if frankenphp.EmbeddedAppPath != "" && filepath.IsLocal(wc.FileName) {
|
||||
wc.FileName = filepath.Join(frankenphp.EmbeddedAppPath, wc.FileName)
|
||||
}
|
||||
if strings.HasPrefix(wc.Name, "m#") {
|
||||
return fmt.Errorf(`global worker names must not start with "m#": %q`, wc.Name)
|
||||
}
|
||||
// check for duplicate workers
|
||||
for _, existingWorker := range f.Workers {
|
||||
if existingWorker.FileName == wc.FileName {
|
||||
return fmt.Errorf("global workers must not have duplicate filenames: %q", wc.FileName)
|
||||
}
|
||||
}
|
||||
|
||||
f.Workers = append(f.Workers, wc)
|
||||
default:
|
||||
allowedDirectives := "num_threads, max_threads, php_ini, worker, max_wait_time"
|
||||
return wrongSubDirectiveError("frankenphp", allowedDirectives, d.Val())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if f.MaxThreads > 0 && f.NumThreads > 0 && f.MaxThreads < f.NumThreads {
|
||||
return errors.New(`"max_threads"" must be greater than or equal to "num_threads"`)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -228,11 +390,13 @@ type FrankenPHPModule struct {
|
||||
ResolveRootSymlink *bool `json:"resolve_root_symlink,omitempty"`
|
||||
// Env sets an extra environment variable to the given value. Can be specified more than once for multiple environment variables.
|
||||
Env map[string]string `json:"env,omitempty"`
|
||||
// Workers configures the worker scripts to start.
|
||||
Workers []workerConfig `json:"workers,omitempty"`
|
||||
|
||||
resolvedDocumentRoot string
|
||||
preparedEnv frankenphp.PreparedEnv
|
||||
preparedEnvNeedsReplacement bool
|
||||
logger *zap.Logger
|
||||
logger *slog.Logger
|
||||
}
|
||||
|
||||
// CaddyModule returns the Caddy module information.
|
||||
@@ -245,7 +409,24 @@ func (FrankenPHPModule) CaddyModule() caddy.ModuleInfo {
|
||||
|
||||
// Provision sets up the module.
|
||||
func (f *FrankenPHPModule) Provision(ctx caddy.Context) error {
|
||||
f.logger = ctx.Logger(f)
|
||||
f.logger = ctx.Slogger()
|
||||
app, err := ctx.App("frankenphp")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fapp, ok := app.(*FrankenPHPApp)
|
||||
if !ok {
|
||||
return fmt.Errorf(`expected ctx.App("frankenphp") to return *FrankenPHPApp, got %T`, app)
|
||||
}
|
||||
if fapp == nil {
|
||||
return fmt.Errorf(`expected ctx.App("frankenphp") to return *FrankenPHPApp, got nil`)
|
||||
}
|
||||
|
||||
workers, err := fapp.addModuleWorkers(f.Workers...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f.Workers = workers
|
||||
|
||||
if f.Root == "" {
|
||||
if frankenphp.EmbeddedAppPath == "" {
|
||||
@@ -304,29 +485,42 @@ func (f *FrankenPHPModule) Provision(ctx caddy.Context) error {
|
||||
|
||||
// needReplacement checks if a string contains placeholders.
|
||||
func needReplacement(s string) bool {
|
||||
return strings.Contains(s, "{") || strings.Contains(s, "}")
|
||||
return strings.ContainsAny(s, "{}")
|
||||
}
|
||||
|
||||
// ServeHTTP implements caddyhttp.MiddlewareHandler.
|
||||
// TODO: Expose TLS versions as env vars, as Apache's mod_ssl: https://github.com/caddyserver/caddy/blob/master/modules/caddyhttp/reverseproxy/fastcgi/fastcgi.go#L298
|
||||
func (f FrankenPHPModule) ServeHTTP(w http.ResponseWriter, r *http.Request, _ caddyhttp.Handler) error {
|
||||
func (f *FrankenPHPModule) ServeHTTP(w http.ResponseWriter, r *http.Request, _ caddyhttp.Handler) error {
|
||||
origReq := r.Context().Value(caddyhttp.OriginalRequestCtxKey).(http.Request)
|
||||
repl := r.Context().Value(caddy.ReplacerCtxKey).(*caddy.Replacer)
|
||||
|
||||
var documentRootOption frankenphp.RequestOption
|
||||
var documentRoot string
|
||||
if f.resolvedDocumentRoot == "" {
|
||||
documentRootOption = frankenphp.WithRequestDocumentRoot(repl.ReplaceKnown(f.Root, ""), *f.ResolveRootSymlink)
|
||||
documentRoot = repl.ReplaceKnown(f.Root, "")
|
||||
if documentRoot == "" && frankenphp.EmbeddedAppPath != "" {
|
||||
documentRoot = frankenphp.EmbeddedAppPath
|
||||
}
|
||||
documentRootOption = frankenphp.WithRequestDocumentRoot(documentRoot, *f.ResolveRootSymlink)
|
||||
} else {
|
||||
documentRootOption = frankenphp.WithRequestResolvedDocumentRoot(f.resolvedDocumentRoot)
|
||||
documentRoot = f.resolvedDocumentRoot
|
||||
documentRootOption = frankenphp.WithRequestResolvedDocumentRoot(documentRoot)
|
||||
}
|
||||
|
||||
env := make(map[string]string, len(f.preparedEnv)+1)
|
||||
env["REQUEST_URI\x00"] = origReq.URL.RequestURI()
|
||||
for k, v := range f.preparedEnv {
|
||||
if f.preparedEnvNeedsReplacement {
|
||||
env := f.preparedEnv
|
||||
if f.preparedEnvNeedsReplacement {
|
||||
env = make(frankenphp.PreparedEnv, len(f.Env))
|
||||
for k, v := range f.preparedEnv {
|
||||
env[k] = repl.ReplaceKnown(v, "")
|
||||
} else {
|
||||
env[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
fullScriptPath, _ := fastabs.FastAbs(documentRoot + "/" + r.URL.Path)
|
||||
|
||||
workerName := ""
|
||||
for _, w := range f.Workers {
|
||||
if p, _ := fastabs.FastAbs(w.FileName); p == fullScriptPath {
|
||||
workerName = w.Name
|
||||
}
|
||||
}
|
||||
|
||||
@@ -335,17 +529,20 @@ func (f FrankenPHPModule) ServeHTTP(w http.ResponseWriter, r *http.Request, _ ca
|
||||
documentRootOption,
|
||||
frankenphp.WithRequestSplitPath(f.SplitPath),
|
||||
frankenphp.WithRequestPreparedEnv(env),
|
||||
frankenphp.WithOriginalRequest(&origReq),
|
||||
frankenphp.WithWorkerName(workerName),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
if err = frankenphp.ServeHTTP(w, fr); err != nil {
|
||||
return caddyhttp.Error(http.StatusInternalServerError, err)
|
||||
}
|
||||
|
||||
return frankenphp.ServeHTTP(w, fr)
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalCaddyfile implements caddyfile.Unmarshaler.
|
||||
func (f *FrankenPHPModule) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||
// First pass: Parse all directives except "worker"
|
||||
for d.Next() {
|
||||
for d.NextBlock(0) {
|
||||
switch d.Val() {
|
||||
@@ -377,7 +574,6 @@ func (f *FrankenPHPModule) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||
if !d.NextArg() {
|
||||
continue
|
||||
}
|
||||
|
||||
v, err := strconv.ParseBool(d.Val())
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -385,8 +581,58 @@ func (f *FrankenPHPModule) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||
if d.NextArg() {
|
||||
return d.ArgErr()
|
||||
}
|
||||
|
||||
f.ResolveRootSymlink = &v
|
||||
|
||||
case "worker":
|
||||
for d.NextBlock(1) {
|
||||
}
|
||||
for d.NextArg() {
|
||||
}
|
||||
// Skip "worker" blocks in the first pass
|
||||
continue
|
||||
|
||||
default:
|
||||
allowedDirectives := "root, split, env, resolve_root_symlink, worker"
|
||||
return wrongSubDirectiveError("php or php_server", allowedDirectives, d.Val())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Second pass: Parse only "worker" blocks
|
||||
d.Reset()
|
||||
for d.Next() {
|
||||
for d.NextBlock(0) {
|
||||
if d.Val() == "worker" {
|
||||
wc, err := parseWorkerConfig(d)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Inherit environment variables from the parent php_server directive
|
||||
if !filepath.IsAbs(wc.FileName) && f.Root != "" {
|
||||
wc.FileName = filepath.Join(f.Root, wc.FileName)
|
||||
}
|
||||
|
||||
if f.Env != nil {
|
||||
if wc.Env == nil {
|
||||
wc.Env = make(map[string]string)
|
||||
}
|
||||
for k, v := range f.Env {
|
||||
// Only set if not already defined in the worker
|
||||
if _, exists := wc.Env[k]; !exists {
|
||||
wc.Env[k] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if a worker with this filename already exists in this module
|
||||
for _, existingWorker := range f.Workers {
|
||||
if existingWorker.FileName == wc.FileName {
|
||||
return fmt.Errorf(`workers in a single "php_server" block must not have duplicate filenames: %q`, wc.FileName)
|
||||
}
|
||||
}
|
||||
|
||||
f.Workers = append(f.Workers, wc)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -396,7 +642,7 @@ func (f *FrankenPHPModule) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||
|
||||
// parseCaddyfile unmarshals tokens from h into a new Middleware.
|
||||
func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error) {
|
||||
m := FrankenPHPModule{}
|
||||
m := &FrankenPHPModule{}
|
||||
err := m.UnmarshalCaddyfile(h.Dispenser)
|
||||
|
||||
return m, err
|
||||
@@ -448,7 +694,7 @@ func parsePhpServer(h httpcaddyfile.Helper) ([]httpcaddyfile.ConfigValue, error)
|
||||
indexFile := "index.php"
|
||||
|
||||
// set up for explicitly overriding try_files
|
||||
tryFiles := []string{}
|
||||
var tryFiles []string
|
||||
|
||||
// if the user specified a matcher token, use that
|
||||
// matcher in a route that wraps both of our routes;
|
||||
@@ -546,37 +792,65 @@ func parsePhpServer(h httpcaddyfile.Helper) ([]httpcaddyfile.ConfigValue, error)
|
||||
|
||||
// if the index is turned off, we skip the redirect and try_files
|
||||
if indexFile != "off" {
|
||||
// route to redirect to canonical path if index PHP file
|
||||
redirMatcherSet := caddy.ModuleMap{
|
||||
"file": h.JSON(fileserver.MatchFile{
|
||||
TryFiles: []string{"{http.request.uri.path}/" + indexFile},
|
||||
}),
|
||||
"not": h.JSON(caddyhttp.MatchNot{
|
||||
MatcherSetsRaw: []caddy.ModuleMap{
|
||||
{
|
||||
"path": h.JSON(caddyhttp.MatchPath{"*/"}),
|
||||
},
|
||||
},
|
||||
}),
|
||||
}
|
||||
redirHandler := caddyhttp.StaticResponse{
|
||||
StatusCode: caddyhttp.WeakString(strconv.Itoa(http.StatusPermanentRedirect)),
|
||||
Headers: http.Header{"Location": []string{"{http.request.orig_uri.path}/"}},
|
||||
}
|
||||
redirRoute := caddyhttp.Route{
|
||||
MatcherSetsRaw: []caddy.ModuleMap{redirMatcherSet},
|
||||
HandlersRaw: []json.RawMessage{caddyconfig.JSONModuleObject(redirHandler, "handler", "static_response", nil)},
|
||||
}
|
||||
dirRedir := false
|
||||
dirIndex := "{http.request.uri.path}/" + indexFile
|
||||
tryPolicy := "first_exist_fallback"
|
||||
|
||||
// if tryFiles wasn't overridden, use a reasonable default
|
||||
if len(tryFiles) == 0 {
|
||||
tryFiles = []string{"{http.request.uri.path}", "{http.request.uri.path}/" + indexFile, indexFile}
|
||||
if disableFsrv {
|
||||
tryFiles = []string{dirIndex, indexFile}
|
||||
} else {
|
||||
tryFiles = []string{"{http.request.uri.path}", dirIndex, indexFile}
|
||||
}
|
||||
|
||||
dirRedir = true
|
||||
} else {
|
||||
if !strings.HasSuffix(tryFiles[len(tryFiles)-1], ".php") {
|
||||
// use first_exist strategy if the last file is not a PHP file
|
||||
tryPolicy = ""
|
||||
}
|
||||
|
||||
for _, tf := range tryFiles {
|
||||
if tf == dirIndex {
|
||||
dirRedir = true
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// route to redirect to canonical path if index PHP file
|
||||
if dirRedir {
|
||||
redirMatcherSet := caddy.ModuleMap{
|
||||
"file": h.JSON(fileserver.MatchFile{
|
||||
TryFiles: []string{dirIndex},
|
||||
}),
|
||||
"not": h.JSON(caddyhttp.MatchNot{
|
||||
MatcherSetsRaw: []caddy.ModuleMap{
|
||||
{
|
||||
"path": h.JSON(caddyhttp.MatchPath{"*/"}),
|
||||
},
|
||||
},
|
||||
}),
|
||||
}
|
||||
redirHandler := caddyhttp.StaticResponse{
|
||||
StatusCode: caddyhttp.WeakString(strconv.Itoa(http.StatusPermanentRedirect)),
|
||||
Headers: http.Header{"Location": []string{"{http.request.orig_uri.path}/"}},
|
||||
}
|
||||
redirRoute := caddyhttp.Route{
|
||||
MatcherSetsRaw: []caddy.ModuleMap{redirMatcherSet},
|
||||
HandlersRaw: []json.RawMessage{caddyconfig.JSONModuleObject(redirHandler, "handler", "static_response", nil)},
|
||||
}
|
||||
|
||||
routes = append(routes, redirRoute)
|
||||
}
|
||||
|
||||
// route to rewrite to PHP index file
|
||||
rewriteMatcherSet := caddy.ModuleMap{
|
||||
"file": h.JSON(fileserver.MatchFile{
|
||||
TryFiles: tryFiles,
|
||||
TryPolicy: tryPolicy,
|
||||
SplitPath: extensions,
|
||||
}),
|
||||
}
|
||||
@@ -588,7 +862,7 @@ func parsePhpServer(h httpcaddyfile.Helper) ([]httpcaddyfile.ConfigValue, error)
|
||||
HandlersRaw: []json.RawMessage{caddyconfig.JSONModuleObject(rewriteHandler, "handler", "rewrite", nil)},
|
||||
}
|
||||
|
||||
routes = append(routes, redirRoute, rewriteRoute)
|
||||
routes = append(routes, rewriteRoute)
|
||||
}
|
||||
|
||||
// route to actually pass requests to PHP files;
|
||||
@@ -605,6 +879,7 @@ func parsePhpServer(h httpcaddyfile.Helper) ([]httpcaddyfile.ConfigValue, error)
|
||||
// using the php directive syntax
|
||||
dispenser.Next() // consume the directive name
|
||||
err = phpsrv.UnmarshalCaddyfile(dispenser)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -656,9 +931,15 @@ func parsePhpServer(h httpcaddyfile.Helper) ([]httpcaddyfile.ConfigValue, error)
|
||||
}, nil
|
||||
}
|
||||
|
||||
// return a nice error message
|
||||
func wrongSubDirectiveError(module string, allowedDriectives string, wrongValue string) error {
|
||||
return fmt.Errorf("unknown '%s' subdirective: '%s' (allowed directives are: %s)", module, wrongValue, allowedDriectives)
|
||||
}
|
||||
|
||||
// Interface guards
|
||||
var (
|
||||
_ caddy.App = (*FrankenPHPApp)(nil)
|
||||
_ caddy.Provisioner = (*FrankenPHPApp)(nil)
|
||||
_ caddy.Provisioner = (*FrankenPHPModule)(nil)
|
||||
_ caddyhttp.MiddlewareHandler = (*FrankenPHPModule)(nil)
|
||||
_ caddyfile.Unmarshaler = (*FrankenPHPModule)(nil)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
222
caddy/config_test.go
Normal file
222
caddy/config_test.go
Normal file
@@ -0,0 +1,222 @@
|
||||
package caddy
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestModuleWorkerDuplicateFilenamesFail(t *testing.T) {
|
||||
// Create a test configuration with duplicate worker filenames
|
||||
configWithDuplicateFilenames := `
|
||||
{
|
||||
php {
|
||||
worker {
|
||||
file worker-with-env.php
|
||||
num 1
|
||||
}
|
||||
worker {
|
||||
file worker-with-env.php
|
||||
num 2
|
||||
}
|
||||
}
|
||||
}`
|
||||
|
||||
// Parse the configuration
|
||||
d := caddyfile.NewTestDispenser(configWithDuplicateFilenames)
|
||||
module := &FrankenPHPModule{}
|
||||
|
||||
// Unmarshal the configuration
|
||||
err := module.UnmarshalCaddyfile(d)
|
||||
|
||||
// Verify that an error was returned
|
||||
require.Error(t, err, "Expected an error when two workers in the same module have the same filename")
|
||||
require.Contains(t, err.Error(), "must not have duplicate filenames", "Error message should mention duplicate filenames")
|
||||
}
|
||||
|
||||
func TestModuleWorkersWithDifferentFilenames(t *testing.T) {
|
||||
// Create a test configuration with different worker filenames
|
||||
configWithDifferentFilenames := `
|
||||
{
|
||||
php {
|
||||
worker ../testdata/worker-with-env.php
|
||||
worker ../testdata/worker-with-counter.php
|
||||
}
|
||||
}`
|
||||
|
||||
// Parse the configuration
|
||||
d := caddyfile.NewTestDispenser(configWithDifferentFilenames)
|
||||
module := &FrankenPHPModule{}
|
||||
|
||||
// Unmarshal the configuration
|
||||
err := module.UnmarshalCaddyfile(d)
|
||||
|
||||
// Verify that no error was returned
|
||||
require.NoError(t, err, "Expected no error when two workers in the same module have different filenames")
|
||||
|
||||
// Verify that both workers were added to the module
|
||||
require.Len(t, module.Workers, 2, "Expected two workers to be added to the module")
|
||||
require.Equal(t, "../testdata/worker-with-env.php", module.Workers[0].FileName, "First worker should have the correct filename")
|
||||
require.Equal(t, "../testdata/worker-with-counter.php", module.Workers[1].FileName, "Second worker should have the correct filename")
|
||||
}
|
||||
|
||||
func TestModuleWorkersDifferentNamesSucceed(t *testing.T) {
|
||||
// Create a test configuration with a worker name
|
||||
configWithWorkerName1 := `
|
||||
{
|
||||
php_server {
|
||||
worker {
|
||||
name test-worker-1
|
||||
file ../testdata/worker-with-env.php
|
||||
num 1
|
||||
}
|
||||
}
|
||||
}`
|
||||
|
||||
// Parse the first configuration
|
||||
d1 := caddyfile.NewTestDispenser(configWithWorkerName1)
|
||||
app := &FrankenPHPApp{}
|
||||
module1 := &FrankenPHPModule{}
|
||||
|
||||
// Unmarshal the first configuration
|
||||
err := module1.UnmarshalCaddyfile(d1)
|
||||
require.NoError(t, err, "First module should be configured without errors")
|
||||
|
||||
// Create a second test configuration with a different worker name
|
||||
configWithWorkerName2 := `
|
||||
{
|
||||
php_server {
|
||||
worker {
|
||||
name test-worker-2
|
||||
file ../testdata/worker-with-env.php
|
||||
num 1
|
||||
}
|
||||
}
|
||||
}`
|
||||
|
||||
// Parse the second configuration
|
||||
d2 := caddyfile.NewTestDispenser(configWithWorkerName2)
|
||||
module2 := &FrankenPHPModule{}
|
||||
|
||||
// Unmarshal the second configuration
|
||||
err = module2.UnmarshalCaddyfile(d2)
|
||||
|
||||
// Verify that no error was returned
|
||||
require.NoError(t, err, "Expected no error when two workers have different names")
|
||||
|
||||
_, err = app.addModuleWorkers(module1.Workers...)
|
||||
require.NoError(t, err, "Expected no error when adding the first module workers")
|
||||
_, err = app.addModuleWorkers(module2.Workers...)
|
||||
require.NoError(t, err, "Expected no error when adding the second module workers")
|
||||
|
||||
// Verify that both workers were added
|
||||
require.Len(t, app.Workers, 2, "Expected two workers in the app")
|
||||
require.Equal(t, "m#test-worker-1", app.Workers[0].Name, "First worker should have the correct name")
|
||||
require.Equal(t, "m#test-worker-2", app.Workers[1].Name, "Second worker should have the correct name")
|
||||
}
|
||||
|
||||
func TestModuleWorkerWithEnvironmentVariables(t *testing.T) {
|
||||
// Create a test configuration with environment variables
|
||||
configWithEnv := `
|
||||
{
|
||||
php {
|
||||
worker {
|
||||
file ../testdata/worker-with-env.php
|
||||
num 1
|
||||
env APP_ENV production
|
||||
env DEBUG true
|
||||
}
|
||||
}
|
||||
}`
|
||||
|
||||
// Parse the configuration
|
||||
d := caddyfile.NewTestDispenser(configWithEnv)
|
||||
module := &FrankenPHPModule{}
|
||||
|
||||
// Unmarshal the configuration
|
||||
err := module.UnmarshalCaddyfile(d)
|
||||
|
||||
// Verify that no error was returned
|
||||
require.NoError(t, err, "Expected no error when configuring a worker with environment variables")
|
||||
|
||||
// Verify that the worker was added to the module
|
||||
require.Len(t, module.Workers, 1, "Expected one worker to be added to the module")
|
||||
require.Equal(t, "../testdata/worker-with-env.php", module.Workers[0].FileName, "Worker should have the correct filename")
|
||||
|
||||
// Verify that the environment variables were set correctly
|
||||
require.Len(t, module.Workers[0].Env, 2, "Expected two environment variables")
|
||||
require.Equal(t, "production", module.Workers[0].Env["APP_ENV"], "APP_ENV should be set to production")
|
||||
require.Equal(t, "true", module.Workers[0].Env["DEBUG"], "DEBUG should be set to true")
|
||||
}
|
||||
|
||||
func TestModuleWorkerWithWatchConfiguration(t *testing.T) {
|
||||
// Create a test configuration with watch directories
|
||||
configWithWatch := `
|
||||
{
|
||||
php {
|
||||
worker {
|
||||
file ../testdata/worker-with-env.php
|
||||
num 1
|
||||
watch
|
||||
watch ./src/**/*.php
|
||||
watch ./config/**/*.yaml
|
||||
}
|
||||
}
|
||||
}`
|
||||
|
||||
// Parse the configuration
|
||||
d := caddyfile.NewTestDispenser(configWithWatch)
|
||||
module := &FrankenPHPModule{}
|
||||
|
||||
// Unmarshal the configuration
|
||||
err := module.UnmarshalCaddyfile(d)
|
||||
|
||||
// Verify that no error was returned
|
||||
require.NoError(t, err, "Expected no error when configuring a worker with watch directories")
|
||||
|
||||
// Verify that the worker was added to the module
|
||||
require.Len(t, module.Workers, 1, "Expected one worker to be added to the module")
|
||||
require.Equal(t, "../testdata/worker-with-env.php", module.Workers[0].FileName, "Worker should have the correct filename")
|
||||
|
||||
// Verify that the watch directories were set correctly
|
||||
require.Len(t, module.Workers[0].Watch, 3, "Expected three watch patterns")
|
||||
require.Equal(t, "./**/*.{php,yaml,yml,twig,env}", module.Workers[0].Watch[0], "First watch pattern should be the default")
|
||||
require.Equal(t, "./src/**/*.php", module.Workers[0].Watch[1], "Second watch pattern should match the configuration")
|
||||
require.Equal(t, "./config/**/*.yaml", module.Workers[0].Watch[2], "Third watch pattern should match the configuration")
|
||||
}
|
||||
|
||||
func TestModuleWorkerWithCustomName(t *testing.T) {
|
||||
// Create a test configuration with a custom worker name
|
||||
configWithCustomName := `
|
||||
{
|
||||
php {
|
||||
worker {
|
||||
file ../testdata/worker-with-env.php
|
||||
num 1
|
||||
name custom-worker-name
|
||||
}
|
||||
}
|
||||
}`
|
||||
|
||||
// Parse the configuration
|
||||
d := caddyfile.NewTestDispenser(configWithCustomName)
|
||||
module := &FrankenPHPModule{}
|
||||
app := &FrankenPHPApp{}
|
||||
|
||||
// Unmarshal the configuration
|
||||
err := module.UnmarshalCaddyfile(d)
|
||||
|
||||
// Verify that no error was returned
|
||||
require.NoError(t, err, "Expected no error when configuring a worker with a custom name")
|
||||
|
||||
// Verify that the worker was added to the module
|
||||
require.Len(t, module.Workers, 1, "Expected one worker to be added to the module")
|
||||
require.Equal(t, "../testdata/worker-with-env.php", module.Workers[0].FileName, "Worker should have the correct filename")
|
||||
|
||||
// Verify that the worker was added to app.Workers with the m# prefix
|
||||
module.Workers, err = app.addModuleWorkers(module.Workers...)
|
||||
require.NoError(t, err, "Expected no error when adding the worker to the app")
|
||||
require.Equal(t, "m#custom-worker-name", module.Workers[0].Name, "Worker should have the custom name, prefixed with m#")
|
||||
require.Equal(t, "m#custom-worker-name", app.Workers[0].Name, "Worker should have the custom name, prefixed with m#")
|
||||
}
|
||||
@@ -1,8 +1,14 @@
|
||||
# The Caddyfile is an easy way to configure FrankenPHP and the Caddy web server.
|
||||
#
|
||||
# https://frankenphp.dev/docs/config
|
||||
# https://caddyserver.com/docs/caddyfile
|
||||
{
|
||||
skip_install_trust
|
||||
|
||||
{$CADDY_GLOBAL_OPTIONS}
|
||||
|
||||
frankenphp {
|
||||
{$FRANKENPHP_CONFIG}
|
||||
#worker /path/to/your/worker.php
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +25,7 @@
|
||||
#}
|
||||
|
||||
root public/
|
||||
root * public/
|
||||
encode zstd br gzip
|
||||
|
||||
# Uncomment the following lines to enable Mercure and Vulcain modules
|
||||
#mercure {
|
||||
@@ -41,5 +47,13 @@
|
||||
{$CADDY_SERVER_EXTRA_DIRECTIVES}
|
||||
|
||||
php_server {
|
||||
php_server
|
||||
#worker /path/to/your/worker.php
|
||||
}
|
||||
}
|
||||
|
||||
# As an alternative to editing the above site block, you can add your own site
|
||||
# block files in the Caddyfile.d directory, and they will be included as long
|
||||
# as they use the .caddyfile extension.
|
||||
|
||||
import Caddyfile.d/*.caddyfile
|
||||
import Caddyfile.d/*.caddyfile
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
// plug in Caddy modules here.
|
||||
_ "github.com/caddyserver/caddy/v2/modules/standard"
|
||||
_ "github.com/dunglas/caddy-cbrotli"
|
||||
_ "github.com/dunglas/frankenphp/caddy"
|
||||
_ "frankenphp.dev/caddy"
|
||||
_ "github.com/dunglas/mercure/caddy"
|
||||
_ "github.com/dunglas/vulcain/caddy"
|
||||
)
|
||||
|
||||
226
caddy/go.mod
226
caddy/go.mod
@@ -1,152 +1,155 @@
|
||||
module github.com/dunglas/frankenphp/caddy
|
||||
module frankenphp.dev/caddy
|
||||
|
||||
go 1.22.7
|
||||
go 1.24.0
|
||||
|
||||
replace github.com/dunglas/frankenphp => ../
|
||||
replace frankenphp.dev => ../
|
||||
|
||||
retract v1.0.0-rc.1 // Human error
|
||||
|
||||
require (
|
||||
github.com/caddyserver/caddy/v2 v2.8.4
|
||||
github.com/caddyserver/certmagic v0.21.4
|
||||
github.com/caddyserver/caddy/v2 v2.10.0
|
||||
github.com/caddyserver/certmagic v0.23.0
|
||||
github.com/dunglas/caddy-cbrotli v1.0.0
|
||||
github.com/dunglas/frankenphp v1.3.1
|
||||
github.com/dunglas/mercure/caddy v0.17.1
|
||||
github.com/dunglas/vulcain/caddy v1.0.5
|
||||
github.com/prometheus/client_golang v1.20.5
|
||||
github.com/spf13/cobra v1.8.1
|
||||
github.com/stretchr/testify v1.9.0
|
||||
go.uber.org/zap v1.27.0
|
||||
frankenphp.dev v1.7.0
|
||||
github.com/dunglas/mercure/caddy v0.19.2
|
||||
github.com/dunglas/vulcain/caddy v1.2.0
|
||||
github.com/prometheus/client_golang v1.22.0
|
||||
github.com/spf13/cobra v1.9.1
|
||||
github.com/stretchr/testify v1.10.0
|
||||
)
|
||||
|
||||
require github.com/smallstep/go-attestation v0.4.4-0.20240109183208-413678f90935 // indirect
|
||||
require github.com/smallstep/go-attestation v0.4.4-0.20241119153605-2306d5b464ca // indirect
|
||||
|
||||
require (
|
||||
cel.dev/expr v0.18.0 // indirect
|
||||
dario.cat/mergo v1.0.1 // indirect
|
||||
cel.dev/expr v0.24.0 // indirect
|
||||
dario.cat/mergo v1.0.2 // indirect
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 // indirect
|
||||
github.com/BurntSushi/toml v1.4.0 // indirect
|
||||
github.com/BurntSushi/toml v1.5.0 // indirect
|
||||
github.com/KimMachineGun/automemlimit v0.7.2 // indirect
|
||||
github.com/Masterminds/goutils v1.1.1 // indirect
|
||||
github.com/Masterminds/semver/v3 v3.3.0 // indirect
|
||||
github.com/Masterminds/semver/v3 v3.3.1 // indirect
|
||||
github.com/Masterminds/sprig/v3 v3.3.0 // indirect
|
||||
github.com/MauriceGit/skiplist v0.0.0-20211105230623-77f5c8d3e145 // indirect
|
||||
github.com/MicahParks/jwkset v0.5.20 // indirect
|
||||
github.com/MicahParks/keyfunc/v3 v3.3.5 // indirect
|
||||
github.com/MicahParks/jwkset v0.9.6 // indirect
|
||||
github.com/MicahParks/keyfunc/v3 v3.4.0 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||
github.com/RoaringBitmap/roaring v1.9.4 // indirect
|
||||
github.com/alecthomas/chroma/v2 v2.14.0 // indirect
|
||||
github.com/alecthomas/chroma/v2 v2.18.0 // indirect
|
||||
github.com/antlr4-go/antlr/v4 v4.13.1 // indirect
|
||||
github.com/aryann/difflib v0.0.0-20210328193216-ff5ff6dc229b // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/bits-and-blooms/bitset v1.15.0 // indirect
|
||||
github.com/bits-and-blooms/bitset v1.22.0 // indirect
|
||||
github.com/caddyserver/zerossl v0.1.3 // indirect
|
||||
github.com/ccoveille/go-safecast v1.6.1 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||
github.com/cespare/xxhash v1.1.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/chzyer/readline v1.5.1 // indirect
|
||||
github.com/coreos/go-oidc/v3 v3.11.0 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect
|
||||
github.com/cloudflare/circl v1.6.1 // indirect
|
||||
github.com/coreos/go-oidc/v3 v3.14.1 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/dgraph-io/badger v1.6.2 // indirect
|
||||
github.com/dgraph-io/badger/v2 v2.2007.4 // indirect
|
||||
github.com/dgraph-io/ristretto v0.2.0 // indirect
|
||||
github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da // indirect
|
||||
github.com/dlclark/regexp2 v1.11.4 // indirect
|
||||
github.com/dlclark/regexp2 v1.11.5 // indirect
|
||||
github.com/dolthub/maphash v0.1.0 // indirect
|
||||
github.com/dunglas/httpsfv v1.0.2 // indirect
|
||||
github.com/dunglas/mercure v0.17.1 // indirect
|
||||
github.com/dunglas/vulcain v1.0.5 // indirect
|
||||
github.com/dunglas/httpsfv v1.1.0 // indirect
|
||||
github.com/dunglas/mercure v0.19.2 // indirect
|
||||
github.com/dunglas/vulcain v1.2.0 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/fsnotify/fsnotify v1.8.0 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
|
||||
github.com/gammazero/deque v0.2.1 // indirect
|
||||
github.com/getkin/kin-openapi v0.128.0 // indirect
|
||||
github.com/go-chi/chi/v5 v5.1.0 // indirect
|
||||
github.com/go-jose/go-jose/v3 v3.0.3 // indirect
|
||||
github.com/go-jose/go-jose/v4 v4.0.4 // indirect
|
||||
github.com/francoispqt/gojay v1.2.13 // indirect
|
||||
github.com/fsnotify/fsnotify v1.9.0 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.8.0 // indirect
|
||||
github.com/gammazero/deque v1.0.0 // indirect
|
||||
github.com/getkin/kin-openapi v0.132.0 // indirect
|
||||
github.com/go-chi/chi/v5 v5.2.1 // indirect
|
||||
github.com/go-jose/go-jose/v3 v3.0.4 // indirect
|
||||
github.com/go-jose/go-jose/v4 v4.1.0 // indirect
|
||||
github.com/go-logr/logr v1.4.2 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.21.0 // indirect
|
||||
github.com/go-openapi/swag v0.23.0 // indirect
|
||||
github.com/go-sql-driver/mysql v1.8.1 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.21.1 // indirect
|
||||
github.com/go-openapi/swag v0.23.1 // indirect
|
||||
github.com/go-sql-driver/mysql v1.9.2 // indirect
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
|
||||
github.com/gofrs/uuid v4.4.0+incompatible // indirect
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
|
||||
github.com/golang-jwt/jwt/v5 v5.2.2 // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/golang/snappy v1.0.0 // indirect
|
||||
github.com/google/brotli/go/cbrotli v0.0.0-20241111155135-4303850b01d6 // indirect
|
||||
github.com/google/cel-go v0.22.0 // indirect
|
||||
github.com/google/certificate-transparency-go v1.2.2 // indirect
|
||||
github.com/google/go-tpm v0.9.1 // indirect
|
||||
github.com/google/cel-go v0.25.0 // indirect
|
||||
github.com/google/certificate-transparency-go v1.3.1 // indirect
|
||||
github.com/google/go-tpm v0.9.5 // indirect
|
||||
github.com/google/go-tspi v0.3.0 // indirect
|
||||
github.com/google/pprof v0.0.0-20241101162523-b92577c0c142 // indirect
|
||||
github.com/google/pprof v0.0.0-20250602020802-c6617b811d0e // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/gorilla/handlers v1.5.2 // indirect
|
||||
github.com/gorilla/mux v1.8.1 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.23.0 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 // indirect
|
||||
github.com/hashicorp/golang-lru v1.0.2 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/huandu/xstrings v1.5.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/invopop/yaml v0.3.1 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
||||
github.com/jackc/pgx/v5 v5.7.1 // indirect
|
||||
github.com/jackc/pgx/v5 v5.7.5 // indirect
|
||||
github.com/jackc/puddle/v2 v2.2.2 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/kevburnsjr/skipfilter v0.0.1 // indirect
|
||||
github.com/klauspost/compress v1.17.11 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.9 // indirect
|
||||
github.com/klauspost/compress v1.18.0 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.10 // indirect
|
||||
github.com/kylelemons/godebug v1.1.0 // indirect
|
||||
github.com/libdns/libdns v0.2.2 // indirect
|
||||
github.com/magiconair/properties v1.8.7 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/libdns/libdns v1.1.0 // indirect
|
||||
github.com/mailru/easyjson v0.9.0 // indirect
|
||||
github.com/manifoldco/promptui v0.9.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/maypok86/otter v1.2.3 // indirect
|
||||
github.com/maypok86/otter v1.2.4 // indirect
|
||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
|
||||
github.com/mholt/acmez/v2 v2.0.3 // indirect
|
||||
github.com/miekg/dns v1.1.62 // indirect
|
||||
github.com/mholt/acmez/v3 v3.1.2 // indirect
|
||||
github.com/miekg/dns v1.1.66 // indirect
|
||||
github.com/mitchellh/copystructure v1.2.0 // indirect
|
||||
github.com/mitchellh/go-ps v1.0.0 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
|
||||
github.com/mschoch/smat v0.2.0 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.21.0 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
|
||||
github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 // indirect
|
||||
github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.23.4 // indirect
|
||||
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
||||
github.com/perimeterx/marshmallow v1.1.5 // indirect
|
||||
github.com/pires/go-proxyproto v0.8.0 // indirect
|
||||
github.com/pires/go-proxyproto v0.8.1 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/prometheus/client_model v0.6.1 // indirect
|
||||
github.com/prometheus/common v0.60.1 // indirect
|
||||
github.com/prometheus/procfs v0.15.1 // indirect
|
||||
github.com/prometheus/client_model v0.6.2 // indirect
|
||||
github.com/prometheus/common v0.64.0 // indirect
|
||||
github.com/prometheus/procfs v0.16.1 // indirect
|
||||
github.com/quic-go/qpack v0.5.1 // indirect
|
||||
github.com/quic-go/quic-go v0.48.1 // indirect
|
||||
github.com/quic-go/quic-go v0.52.0 // indirect
|
||||
github.com/rs/xid v1.6.0 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/sagikazarmark/locafero v0.6.0 // indirect
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
||||
github.com/sagikazarmark/locafero v0.9.0 // indirect
|
||||
github.com/shopspring/decimal v1.4.0 // indirect
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
|
||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||
github.com/slackhq/nebula v1.9.4 // indirect
|
||||
github.com/smallstep/certificates v0.28.0 // indirect
|
||||
github.com/smallstep/cli-utils v0.10.0 // indirect
|
||||
github.com/slackhq/nebula v1.9.5 // indirect
|
||||
github.com/smallstep/certificates v0.28.3 // indirect
|
||||
github.com/smallstep/cli-utils v0.12.1 // indirect
|
||||
github.com/smallstep/linkedca v0.23.0 // indirect
|
||||
github.com/smallstep/nosql v0.7.0 // indirect
|
||||
github.com/smallstep/pkcs7 v0.0.0-20241029111203-fbab67b7673f // indirect
|
||||
github.com/smallstep/scep v0.0.0-20240926084937-8cf1ca453101 // indirect
|
||||
github.com/smallstep/pkcs7 v0.2.1 // indirect
|
||||
github.com/smallstep/scep v0.0.0-20250318231241-a25cabb69492 // indirect
|
||||
github.com/smallstep/truststore v0.13.0 // indirect
|
||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||
github.com/spf13/afero v1.11.0 // indirect
|
||||
github.com/spf13/cast v1.7.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/spf13/viper v1.19.0 // indirect
|
||||
github.com/spf13/afero v1.14.0 // indirect
|
||||
github.com/spf13/cast v1.9.2 // indirect
|
||||
github.com/spf13/pflag v1.0.6 // indirect
|
||||
github.com/spf13/viper v1.20.1 // indirect
|
||||
github.com/stoewer/go-strcase v1.3.0 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
github.com/tailscale/tscert v0.0.0-20240608151842-d3f834017e53 // indirect
|
||||
@@ -158,46 +161,47 @@ require (
|
||||
github.com/urfave/cli v1.22.16 // indirect
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
|
||||
github.com/yuin/goldmark v1.7.8 // indirect
|
||||
github.com/yuin/goldmark v1.7.12 // indirect
|
||||
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc // indirect
|
||||
github.com/zeebo/blake3 v0.2.4 // indirect
|
||||
go.etcd.io/bbolt v1.3.11 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.57.0 // indirect
|
||||
go.opentelemetry.io/contrib/propagators/autoprop v0.57.0 // indirect
|
||||
go.opentelemetry.io/contrib/propagators/aws v1.32.0 // indirect
|
||||
go.opentelemetry.io/contrib/propagators/b3 v1.32.0 // indirect
|
||||
go.opentelemetry.io/contrib/propagators/jaeger v1.32.0 // indirect
|
||||
go.opentelemetry.io/contrib/propagators/ot v1.32.0 // indirect
|
||||
go.opentelemetry.io/otel v1.32.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.32.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.32.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.32.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.32.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.32.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.3.1 // indirect
|
||||
go.step.sm/crypto v0.54.0 // indirect
|
||||
go.step.sm/linkedca v0.22.2 // indirect
|
||||
go.etcd.io/bbolt v1.4.0 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect
|
||||
go.opentelemetry.io/contrib/propagators/autoprop v0.60.0 // indirect
|
||||
go.opentelemetry.io/contrib/propagators/aws v1.35.0 // indirect
|
||||
go.opentelemetry.io/contrib/propagators/b3 v1.35.0 // indirect
|
||||
go.opentelemetry.io/contrib/propagators/jaeger v1.35.0 // indirect
|
||||
go.opentelemetry.io/contrib/propagators/ot v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.35.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.6.0 // indirect
|
||||
go.step.sm/crypto v0.66.0 // indirect
|
||||
go.uber.org/automaxprocs v1.6.0 // indirect
|
||||
go.uber.org/mock v0.5.0 // indirect
|
||||
go.uber.org/mock v0.5.2 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
go.uber.org/zap/exp v0.2.0 // indirect
|
||||
golang.org/x/crypto v0.29.0 // indirect
|
||||
golang.org/x/crypto/x509roots/fallback v0.0.0-20241107225453-6018723c7405 // indirect
|
||||
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect
|
||||
golang.org/x/mod v0.22.0 // indirect
|
||||
golang.org/x/net v0.31.0 // indirect
|
||||
golang.org/x/oauth2 v0.24.0 // indirect
|
||||
golang.org/x/sync v0.9.0 // indirect
|
||||
golang.org/x/sys v0.27.0 // indirect
|
||||
golang.org/x/term v0.26.0 // indirect
|
||||
golang.org/x/text v0.20.0 // indirect
|
||||
golang.org/x/time v0.8.0 // indirect
|
||||
golang.org/x/tools v0.27.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20241104194629-dd2ea8efbc28 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241104194629-dd2ea8efbc28 // indirect
|
||||
google.golang.org/grpc v1.68.0 // indirect
|
||||
google.golang.org/protobuf v1.35.1 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
go.uber.org/zap v1.27.0 // indirect
|
||||
go.uber.org/zap/exp v0.3.0 // indirect
|
||||
golang.org/x/crypto v0.38.0 // indirect
|
||||
golang.org/x/crypto/x509roots/fallback v0.0.0-20250531095911-4f9f0ca9fcfb // indirect
|
||||
golang.org/x/exp v0.0.0-20250531010427-b6e5de432a8b // indirect
|
||||
golang.org/x/mod v0.24.0 // indirect
|
||||
golang.org/x/net v0.40.0 // indirect
|
||||
golang.org/x/oauth2 v0.30.0 // indirect
|
||||
golang.org/x/sync v0.14.0 // indirect
|
||||
golang.org/x/sys v0.33.0 // indirect
|
||||
golang.org/x/term v0.32.0 // indirect
|
||||
golang.org/x/text v0.25.0 // indirect
|
||||
golang.org/x/time v0.11.0 // indirect
|
||||
golang.org/x/tools v0.33.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250528174236-200df99c418a // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a // indirect
|
||||
google.golang.org/grpc v1.72.2 // indirect
|
||||
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1 // indirect
|
||||
google.golang.org/protobuf v1.36.6 // indirect
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
howett.net/plist v1.0.1 // indirect
|
||||
|
||||
722
caddy/go.sum
722
caddy/go.sum
File diff suppressed because it is too large
Load Diff
@@ -6,7 +6,7 @@ import (
|
||||
"path/filepath"
|
||||
|
||||
caddycmd "github.com/caddyserver/caddy/v2/cmd"
|
||||
"github.com/dunglas/frankenphp"
|
||||
"frankenphp.dev"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
@@ -25,7 +25,7 @@ Executes a PHP script similarly to the CLI SAPI.`,
|
||||
})
|
||||
}
|
||||
|
||||
func cmdPHPCLI(caddycmd.Flags) (int, error) {
|
||||
func cmdPHPCLI(fs caddycmd.Flags) (int, error) {
|
||||
args := os.Args[2:]
|
||||
if len(args) < 1 {
|
||||
return 1, errors.New("the path to the PHP script is required")
|
||||
@@ -37,7 +37,13 @@ func cmdPHPCLI(caddycmd.Flags) (int, error) {
|
||||
}
|
||||
}
|
||||
|
||||
status := frankenphp.ExecuteScriptCLI(args[0], args)
|
||||
var status int
|
||||
if len(args) >= 2 && args[0] == "-r" {
|
||||
status = frankenphp.ExecutePHPCode(args[1])
|
||||
} else {
|
||||
status = frankenphp.ExecuteScriptCLI(args[0], args)
|
||||
}
|
||||
|
||||
os.Exit(status)
|
||||
|
||||
return status, nil
|
||||
|
||||
@@ -3,15 +3,14 @@ package caddy
|
||||
import (
|
||||
"encoding/json"
|
||||
"log"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
mercureModule "github.com/dunglas/mercure/caddy"
|
||||
"go.uber.org/zap/zapcore"
|
||||
|
||||
"github.com/caddyserver/caddy/v2"
|
||||
"github.com/caddyserver/caddy/v2/caddyconfig"
|
||||
@@ -21,15 +20,14 @@ import (
|
||||
"github.com/caddyserver/caddy/v2/modules/caddyhttp/fileserver"
|
||||
"github.com/caddyserver/caddy/v2/modules/caddyhttp/rewrite"
|
||||
"github.com/caddyserver/certmagic"
|
||||
"github.com/dunglas/frankenphp"
|
||||
|
||||
"frankenphp.dev"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func init() {
|
||||
caddycmd.RegisterCommand(caddycmd.Command{
|
||||
Name: "php-server",
|
||||
Usage: "[--domain <example.com>] [--root <path>] [--listen <addr>] [--worker /path/to/worker.php<,nb-workers>] [--watch <paths...>] [--access-log] [--debug] [--no-compress] [--mercure]",
|
||||
Usage: "[--domain=<example.com>] [--root=<path>] [--listen=<addr>] [--worker=/path/to/worker.php<,nb-workers>] [--watch[=<glob-pattern>]]... [--access-log] [--debug] [--no-compress] [--mercure]",
|
||||
Short: "Spins up a production-ready PHP server",
|
||||
Long: `
|
||||
A simple but production-ready PHP server. Useful for quick deployments,
|
||||
@@ -48,11 +46,14 @@ For more advanced use cases, see https://github.com/dunglas/frankenphp/blob/main
|
||||
cmd.Flags().StringP("root", "r", "", "The path to the root of the site")
|
||||
cmd.Flags().StringP("listen", "l", "", "The address to which to bind the listener")
|
||||
cmd.Flags().StringArrayP("worker", "w", []string{}, "Worker script")
|
||||
cmd.Flags().StringArrayP("watch", "", []string{}, "Directory to watch for file changes")
|
||||
cmd.Flags().StringArray("watch", []string{}, "Glob pattern of directories and files to watch for changes")
|
||||
cmd.Flags().BoolP("access-log", "a", false, "Enable the access log")
|
||||
cmd.Flags().BoolP("debug", "v", false, "Enable verbose debug logs")
|
||||
cmd.Flags().BoolP("mercure", "m", false, "Enable the built-in Mercure.rocks hub")
|
||||
cmd.Flags().BoolP("no-compress", "", false, "Disable Zstandard, Brotli and Gzip compression")
|
||||
cmd.Flags().Bool("no-compress", false, "Disable Zstandard, Brotli and Gzip compression")
|
||||
|
||||
cmd.Flags().Lookup("watch").NoOptDefVal = defaultWatchPattern
|
||||
|
||||
cmd.RunE = caddycmd.WrapCommandFuncForCobra(cmdPHPServer)
|
||||
},
|
||||
})
|
||||
@@ -79,27 +80,30 @@ func cmdPHPServer(fs caddycmd.Flags) (int, error) {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if frankenphp.EmbeddedAppPath != "" {
|
||||
if err := os.Chdir(frankenphp.EmbeddedAppPath); err != nil {
|
||||
return caddy.ExitCodeFailedStartup, err
|
||||
}
|
||||
}
|
||||
|
||||
var workersOption []workerConfig
|
||||
if len(workers) != 0 {
|
||||
workersOption = make([]workerConfig, 0, len(workers))
|
||||
for _, worker := range workers {
|
||||
parts := strings.SplitN(worker, ",", 2)
|
||||
if frankenphp.EmbeddedAppPath != "" && filepath.IsLocal(parts[0]) {
|
||||
parts[0] = filepath.Join(frankenphp.EmbeddedAppPath, parts[0])
|
||||
}
|
||||
|
||||
var num int
|
||||
var num uint64
|
||||
if len(parts) > 1 {
|
||||
num, _ = strconv.Atoi(parts[1])
|
||||
num, _ = strconv.ParseUint(parts[1], 10, 32)
|
||||
}
|
||||
|
||||
workersOption = append(workersOption, workerConfig{FileName: parts[0], Num: num})
|
||||
workersOption = append(workersOption, workerConfig{FileName: parts[0], Num: int(num)})
|
||||
}
|
||||
workersOption[0].Watch = watch
|
||||
}
|
||||
|
||||
if frankenphp.EmbeddedAppPath != "" {
|
||||
if _, err := os.Stat(filepath.Join(frankenphp.EmbeddedAppPath, "php.ini")); err == nil {
|
||||
if _, err := os.Stat("php.ini"); err == nil {
|
||||
iniScanDir := os.Getenv("PHP_INI_SCAN_DIR")
|
||||
|
||||
if err := os.Setenv("PHP_INI_SCAN_DIR", iniScanDir+":"+frankenphp.EmbeddedAppPath); err != nil {
|
||||
@@ -107,8 +111,8 @@ func cmdPHPServer(fs caddycmd.Flags) (int, error) {
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := os.Stat(filepath.Join(frankenphp.EmbeddedAppPath, "Caddyfile")); err == nil {
|
||||
config, _, err := caddycmd.LoadConfig(filepath.Join(frankenphp.EmbeddedAppPath, "Caddyfile"), "")
|
||||
if _, err := os.Stat("Caddyfile"); err == nil {
|
||||
config, _, err := caddycmd.LoadConfig("Caddyfile", "caddyfile")
|
||||
if err != nil {
|
||||
return caddy.ExitCodeFailedStartup, err
|
||||
}
|
||||
@@ -121,9 +125,7 @@ func cmdPHPServer(fs caddycmd.Flags) (int, error) {
|
||||
}
|
||||
|
||||
if root == "" {
|
||||
root = filepath.Join(frankenphp.EmbeddedAppPath, defaultDocumentRoot)
|
||||
} else if filepath.IsLocal(root) {
|
||||
root = filepath.Join(frankenphp.EmbeddedAppPath, root)
|
||||
root = defaultDocumentRoot
|
||||
}
|
||||
}
|
||||
|
||||
@@ -334,7 +336,7 @@ func cmdPHPServer(fs caddycmd.Flags) (int, error) {
|
||||
cfg.Logging = &caddy.Logging{
|
||||
Logs: map[string]*caddy.CustomLog{
|
||||
"default": {
|
||||
BaseLog: caddy.BaseLog{Level: zapcore.DebugLevel.CapitalString()},
|
||||
BaseLog: caddy.BaseLog{Level: slog.LevelDebug.String()},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -15,24 +15,24 @@ func TestWorkerWithInactiveWatcher(t *testing.T) {
|
||||
{
|
||||
skip_install_trust
|
||||
admin localhost:2999
|
||||
http_port 9080
|
||||
http_port `+testPort+`
|
||||
|
||||
frankenphp {
|
||||
worker {
|
||||
file ../testdata/worker-with-watcher.php
|
||||
file ../testdata/worker-with-counter.php
|
||||
num 1
|
||||
watch ./**/*.php
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
localhost:9080 {
|
||||
localhost:`+testPort+` {
|
||||
root ../testdata
|
||||
rewrite worker-with-watcher.php
|
||||
rewrite worker-with-counter.php
|
||||
php
|
||||
}
|
||||
`, "caddyfile")
|
||||
|
||||
tester.AssertGetResponse("http://localhost:9080", http.StatusOK, "requests:1")
|
||||
tester.AssertGetResponse("http://localhost:9080", http.StatusOK, "requests:2")
|
||||
tester.AssertGetResponse("http://localhost:"+testPort, http.StatusOK, "requests:1")
|
||||
tester.AssertGetResponse("http://localhost:"+testPort, http.StatusOK, "requests:2")
|
||||
}
|
||||
|
||||
260
cgi.go
260
cgi.go
@@ -1,52 +1,63 @@
|
||||
package frankenphp
|
||||
|
||||
// #cgo nocallback frankenphp_register_bulk
|
||||
// #cgo nocallback frankenphp_register_variables_from_request_info
|
||||
// #cgo nocallback frankenphp_register_variable_safe
|
||||
// #cgo nocallback frankenphp_register_single
|
||||
// #cgo noescape frankenphp_register_bulk
|
||||
// #cgo noescape frankenphp_register_variables_from_request_info
|
||||
// #cgo noescape frankenphp_register_variable_safe
|
||||
// #cgo noescape frankenphp_register_single
|
||||
// #include <php_variables.h>
|
||||
// #include "frankenphp.h"
|
||||
import "C"
|
||||
import (
|
||||
"crypto/tls"
|
||||
"net"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"unsafe"
|
||||
|
||||
"frankenphp.dev/internal/phpheaders"
|
||||
)
|
||||
|
||||
var knownServerKeys = map[string]struct{}{
|
||||
"CONTENT_LENGTH\x00": {},
|
||||
"DOCUMENT_ROOT\x00": {},
|
||||
"DOCUMENT_URI\x00": {},
|
||||
"GATEWAY_INTERFACE\x00": {},
|
||||
"HTTP_HOST\x00": {},
|
||||
"HTTPS\x00": {},
|
||||
"PATH_INFO\x00": {},
|
||||
"PHP_SELF\x00": {},
|
||||
"REMOTE_ADDR\x00": {},
|
||||
"REMOTE_HOST\x00": {},
|
||||
"REMOTE_PORT\x00": {},
|
||||
"REQUEST_SCHEME\x00": {},
|
||||
"SCRIPT_FILENAME\x00": {},
|
||||
"SCRIPT_NAME\x00": {},
|
||||
"SERVER_NAME\x00": {},
|
||||
"SERVER_PORT\x00": {},
|
||||
"SERVER_PROTOCOL\x00": {},
|
||||
"SERVER_SOFTWARE\x00": {},
|
||||
"SSL_PROTOCOL\x00": {},
|
||||
"AUTH_TYPE\x00": {},
|
||||
"REMOTE_IDENT\x00": {},
|
||||
"CONTENT_TYPE\x00": {},
|
||||
"PATH_TRANSLATED\x00": {},
|
||||
"QUERY_STRING\x00": {},
|
||||
"REMOTE_USER\x00": {},
|
||||
"REQUEST_METHOD\x00": {},
|
||||
"REQUEST_URI\x00": {},
|
||||
var knownServerKeys = []string{
|
||||
"CONTENT_LENGTH",
|
||||
"DOCUMENT_ROOT",
|
||||
"DOCUMENT_URI",
|
||||
"GATEWAY_INTERFACE",
|
||||
"HTTP_HOST",
|
||||
"HTTPS",
|
||||
"PATH_INFO",
|
||||
"PHP_SELF",
|
||||
"REMOTE_ADDR",
|
||||
"REMOTE_HOST",
|
||||
"REMOTE_PORT",
|
||||
"REQUEST_SCHEME",
|
||||
"SCRIPT_FILENAME",
|
||||
"SCRIPT_NAME",
|
||||
"SERVER_NAME",
|
||||
"SERVER_PORT",
|
||||
"SERVER_PROTOCOL",
|
||||
"SERVER_SOFTWARE",
|
||||
"SSL_PROTOCOL",
|
||||
"AUTH_TYPE",
|
||||
"REMOTE_IDENT",
|
||||
"CONTENT_TYPE",
|
||||
"PATH_TRANSLATED",
|
||||
"QUERY_STRING",
|
||||
"REMOTE_USER",
|
||||
"REQUEST_METHOD",
|
||||
"REQUEST_URI",
|
||||
}
|
||||
|
||||
// computeKnownVariables returns a set of CGI environment variables for the request.
|
||||
//
|
||||
// TODO: handle this case https://github.com/caddyserver/caddy/issues/3718
|
||||
// Inspired by https://github.com/caddyserver/caddy/blob/master/modules/caddyhttp/reverseproxy/fastcgi/fastcgi.go
|
||||
func addKnownVariablesToServer(thread *phpThread, request *http.Request, fc *FrankenPHPContext, trackVarsArray *C.zval) {
|
||||
keys := getKnownVariableKeys(thread)
|
||||
func addKnownVariablesToServer(thread *phpThread, fc *frankenPHPContext, trackVarsArray *C.zval) {
|
||||
request := fc.request
|
||||
keys := mainThread.knownServerKeys
|
||||
// Separate remote IP and port; more lenient than net.SplitHostPort
|
||||
var ip, port string
|
||||
if idx := strings.LastIndex(request.RemoteAddr, ":"); idx > -1 {
|
||||
@@ -60,58 +71,26 @@ func addKnownVariablesToServer(thread *phpThread, request *http.Request, fc *Fra
|
||||
ip = strings.Replace(ip, "[", "", 1)
|
||||
ip = strings.Replace(ip, "]", "", 1)
|
||||
|
||||
ra, raOK := fc.env["REMOTE_ADDR\x00"]
|
||||
if raOK {
|
||||
registerTrustedVar(keys["REMOTE_ADDR\x00"], ra, trackVarsArray, thread)
|
||||
} else {
|
||||
registerTrustedVar(keys["REMOTE_ADDR\x00"], ip, trackVarsArray, thread)
|
||||
}
|
||||
|
||||
if rh, ok := fc.env["REMOTE_HOST\x00"]; ok {
|
||||
registerTrustedVar(keys["REMOTE_HOST\x00"], rh, trackVarsArray, thread) // For speed, remote host lookups disabled
|
||||
} else {
|
||||
if raOK {
|
||||
registerTrustedVar(keys["REMOTE_HOST\x00"], ra, trackVarsArray, thread)
|
||||
} else {
|
||||
registerTrustedVar(keys["REMOTE_HOST\x00"], ip, trackVarsArray, thread)
|
||||
}
|
||||
}
|
||||
|
||||
registerTrustedVar(keys["REMOTE_PORT\x00"], port, trackVarsArray, thread)
|
||||
registerTrustedVar(keys["DOCUMENT_ROOT\x00"], fc.documentRoot, trackVarsArray, thread)
|
||||
registerTrustedVar(keys["PATH_INFO\x00"], fc.pathInfo, trackVarsArray, thread)
|
||||
registerTrustedVar(keys["PHP_SELF\x00"], request.URL.Path, trackVarsArray, thread)
|
||||
registerTrustedVar(keys["DOCUMENT_URI\x00"], fc.docURI, trackVarsArray, thread)
|
||||
registerTrustedVar(keys["SCRIPT_FILENAME\x00"], fc.scriptFilename, trackVarsArray, thread)
|
||||
registerTrustedVar(keys["SCRIPT_NAME\x00"], fc.scriptName, trackVarsArray, thread)
|
||||
|
||||
var https string
|
||||
var sslProtocol string
|
||||
var rs string
|
||||
if request.TLS == nil {
|
||||
rs = "http"
|
||||
registerTrustedVar(keys["HTTPS\x00"], "", trackVarsArray, thread)
|
||||
registerTrustedVar(keys["SSL_PROTOCOL\x00"], "", trackVarsArray, thread)
|
||||
https = ""
|
||||
sslProtocol = ""
|
||||
} else {
|
||||
rs = "https"
|
||||
if h, ok := fc.env["HTTPS\x00"]; ok {
|
||||
registerTrustedVar(keys["HTTPS\x00"], h, trackVarsArray, thread)
|
||||
} else {
|
||||
registerTrustedVar(keys["HTTPS\x00"], "on", trackVarsArray, thread)
|
||||
}
|
||||
https = "on"
|
||||
|
||||
// and pass the protocol details in a manner compatible with apache's mod_ssl
|
||||
// (which is why these have an SSL_ prefix and not TLS_).
|
||||
if pr, ok := fc.env["SSL_PROTOCOL\x00"]; ok {
|
||||
registerTrustedVar(keys["SSL_PROTOCOL\x00"], pr, trackVarsArray, thread)
|
||||
if v, ok := tlsProtocolStrings[request.TLS.Version]; ok {
|
||||
sslProtocol = v
|
||||
} else {
|
||||
if v, ok := tlsProtocolStrings[request.TLS.Version]; ok {
|
||||
registerTrustedVar(keys["SSL_PROTOCOL\x00"], v, trackVarsArray, thread)
|
||||
} else {
|
||||
registerTrustedVar(keys["SSL_PROTOCOL\x00"], "", trackVarsArray, thread)
|
||||
}
|
||||
sslProtocol = ""
|
||||
}
|
||||
}
|
||||
|
||||
registerTrustedVar(keys["REQUEST_SCHEME\x00"], rs, trackVarsArray, thread)
|
||||
reqHost, reqPort, _ := net.SplitHostPort(request.Host)
|
||||
|
||||
if reqHost == "" {
|
||||
@@ -132,104 +111,96 @@ func addKnownVariablesToServer(thread *phpThread, request *http.Request, fc *Fra
|
||||
}
|
||||
}
|
||||
|
||||
registerTrustedVar(keys["SERVER_NAME\x00"], reqHost, trackVarsArray, thread)
|
||||
if reqPort != "" {
|
||||
registerTrustedVar(keys["SERVER_PORT\x00"], reqPort, trackVarsArray, thread)
|
||||
serverPort := reqPort
|
||||
contentLength := request.Header.Get("Content-Length")
|
||||
|
||||
var requestURI string
|
||||
if fc.originalRequest != nil {
|
||||
requestURI = fc.originalRequest.URL.RequestURI()
|
||||
} else {
|
||||
registerTrustedVar(keys["SERVER_PORT\x00"], "", trackVarsArray, thread)
|
||||
requestURI = request.URL.RequestURI()
|
||||
}
|
||||
|
||||
// Variables defined in CGI 1.1 spec
|
||||
// Some variables are unused but cleared explicitly to prevent
|
||||
// the parent environment from interfering.
|
||||
|
||||
// These values can not be overridden
|
||||
registerTrustedVar(keys["CONTENT_LENGTH\x00"], request.Header.Get("Content-Length"), trackVarsArray, thread)
|
||||
registerTrustedVar(keys["GATEWAY_INTERFACE\x00"], "CGI/1.1", trackVarsArray, thread)
|
||||
registerTrustedVar(keys["SERVER_PROTOCOL\x00"], request.Proto, trackVarsArray, thread)
|
||||
registerTrustedVar(keys["SERVER_SOFTWARE\x00"], "FrankenPHP", trackVarsArray, thread)
|
||||
registerTrustedVar(keys["HTTP_HOST\x00"], request.Host, trackVarsArray, thread) // added here, since not always part of headers
|
||||
|
||||
// These values are always empty but must be defined:
|
||||
registerTrustedVar(keys["AUTH_TYPE\x00"], "", trackVarsArray, thread)
|
||||
registerTrustedVar(keys["REMOTE_IDENT\x00"], "", trackVarsArray, thread)
|
||||
C.frankenphp_register_bulk(
|
||||
trackVarsArray,
|
||||
packCgiVariable(keys["REMOTE_ADDR"], ip),
|
||||
packCgiVariable(keys["REMOTE_HOST"], ip),
|
||||
packCgiVariable(keys["REMOTE_PORT"], port),
|
||||
packCgiVariable(keys["DOCUMENT_ROOT"], fc.documentRoot),
|
||||
packCgiVariable(keys["PATH_INFO"], fc.pathInfo),
|
||||
packCgiVariable(keys["PHP_SELF"], request.URL.Path),
|
||||
packCgiVariable(keys["DOCUMENT_URI"], fc.docURI),
|
||||
packCgiVariable(keys["SCRIPT_FILENAME"], fc.scriptFilename),
|
||||
packCgiVariable(keys["SCRIPT_NAME"], fc.scriptName),
|
||||
packCgiVariable(keys["HTTPS"], https),
|
||||
packCgiVariable(keys["SSL_PROTOCOL"], sslProtocol),
|
||||
packCgiVariable(keys["REQUEST_SCHEME"], rs),
|
||||
packCgiVariable(keys["SERVER_NAME"], reqHost),
|
||||
packCgiVariable(keys["SERVER_PORT"], serverPort),
|
||||
// Variables defined in CGI 1.1 spec
|
||||
// Some variables are unused but cleared explicitly to prevent
|
||||
// the parent environment from interfering.
|
||||
// These values can not be overridden
|
||||
packCgiVariable(keys["CONTENT_LENGTH"], contentLength),
|
||||
packCgiVariable(keys["GATEWAY_INTERFACE"], "CGI/1.1"),
|
||||
packCgiVariable(keys["SERVER_PROTOCOL"], request.Proto),
|
||||
packCgiVariable(keys["SERVER_SOFTWARE"], "FrankenPHP"),
|
||||
packCgiVariable(keys["HTTP_HOST"], request.Host),
|
||||
// These values are always empty but must be defined:
|
||||
packCgiVariable(keys["AUTH_TYPE"], ""),
|
||||
packCgiVariable(keys["REMOTE_IDENT"], ""),
|
||||
// Request uri of the original request
|
||||
packCgiVariable(keys["REQUEST_URI"], requestURI),
|
||||
)
|
||||
|
||||
// These values are already present in the SG(request_info), so we'll register them from there
|
||||
C.frankenphp_register_variables_from_request_info(
|
||||
trackVarsArray,
|
||||
keys["CONTENT_TYPE\x00"],
|
||||
keys["PATH_TRANSLATED\x00"],
|
||||
keys["QUERY_STRING\x00"],
|
||||
keys["REMOTE_USER\x00"],
|
||||
keys["REQUEST_METHOD\x00"],
|
||||
keys["REQUEST_URI\x00"],
|
||||
keys["CONTENT_TYPE"],
|
||||
keys["PATH_TRANSLATED"],
|
||||
keys["QUERY_STRING"],
|
||||
keys["REMOTE_USER"],
|
||||
keys["REQUEST_METHOD"],
|
||||
)
|
||||
}
|
||||
|
||||
func registerTrustedVar(key *C.zend_string, value string, trackVarsArray *C.zval, thread *phpThread) {
|
||||
C.frankenphp_register_trusted_var(key, thread.pinString(value), C.int(len(value)), trackVarsArray)
|
||||
func packCgiVariable(key *C.zend_string, value string) C.ht_key_value_pair {
|
||||
return C.ht_key_value_pair{key, toUnsafeChar(value), C.size_t(len(value))}
|
||||
}
|
||||
|
||||
func addHeadersToServer(thread *phpThread, request *http.Request, fc *FrankenPHPContext, trackVarsArray *C.zval) {
|
||||
for field, val := range request.Header {
|
||||
k, ok := headerKeyCache.Get(field)
|
||||
if !ok {
|
||||
k = "HTTP_" + headerNameReplacer.Replace(strings.ToUpper(field)) + "\x00"
|
||||
headerKeyCache.SetIfAbsent(field, k)
|
||||
}
|
||||
|
||||
if _, ok := fc.env[k]; ok {
|
||||
func addHeadersToServer(fc *frankenPHPContext, trackVarsArray *C.zval) {
|
||||
for field, val := range fc.request.Header {
|
||||
if k := mainThread.commonHeaders[field]; k != nil {
|
||||
v := strings.Join(val, ", ")
|
||||
C.frankenphp_register_single(k, toUnsafeChar(v), C.size_t(len(v)), trackVarsArray)
|
||||
continue
|
||||
}
|
||||
|
||||
// if the header name could not be cached, it needs to be registered safely
|
||||
// this is more inefficient but allows additional sanitizing by PHP
|
||||
k := phpheaders.GetUnCommonHeader(field)
|
||||
v := strings.Join(val, ", ")
|
||||
C.frankenphp_register_variable_safe(thread.pinString(k), thread.pinString(v), C.size_t(len(v)), trackVarsArray)
|
||||
C.frankenphp_register_variable_safe(toUnsafeChar(k), toUnsafeChar(v), C.size_t(len(v)), trackVarsArray)
|
||||
}
|
||||
}
|
||||
|
||||
func addPreparedEnvToServer(thread *phpThread, fc *FrankenPHPContext, trackVarsArray *C.zval) {
|
||||
func addPreparedEnvToServer(fc *frankenPHPContext, trackVarsArray *C.zval) {
|
||||
for k, v := range fc.env {
|
||||
C.frankenphp_register_variable_safe(thread.pinString(k), thread.pinString(v), C.size_t(len(v)), trackVarsArray)
|
||||
C.frankenphp_register_variable_safe(toUnsafeChar(k), toUnsafeChar(v), C.size_t(len(v)), trackVarsArray)
|
||||
}
|
||||
fc.env = nil
|
||||
}
|
||||
|
||||
func getKnownVariableKeys(thread *phpThread) map[string]*C.zend_string {
|
||||
if thread.knownVariableKeys != nil {
|
||||
return thread.knownVariableKeys
|
||||
}
|
||||
threadServerKeys := make(map[string]*C.zend_string)
|
||||
for k := range knownServerKeys {
|
||||
keyWithoutNull := strings.Replace(k, "\x00", "", -1)
|
||||
threadServerKeys[k] = C.frankenphp_init_persistent_string(thread.pinString(keyWithoutNull), C.size_t(len(keyWithoutNull)))
|
||||
}
|
||||
thread.knownVariableKeys = threadServerKeys
|
||||
return threadServerKeys
|
||||
}
|
||||
|
||||
//export go_register_variables
|
||||
func go_register_variables(threadIndex C.uintptr_t, trackVarsArray *C.zval) {
|
||||
thread := phpThreads[threadIndex]
|
||||
r := thread.getActiveRequest()
|
||||
fc := r.Context().Value(contextKey).(*FrankenPHPContext)
|
||||
fc := thread.getRequestContext()
|
||||
|
||||
addKnownVariablesToServer(thread, r, fc, trackVarsArray)
|
||||
addHeadersToServer(thread, r, fc, trackVarsArray)
|
||||
addPreparedEnvToServer(thread, fc, trackVarsArray)
|
||||
}
|
||||
addKnownVariablesToServer(thread, fc, trackVarsArray)
|
||||
addHeadersToServer(fc, trackVarsArray)
|
||||
|
||||
//export go_frankenphp_release_known_variable_keys
|
||||
func go_frankenphp_release_known_variable_keys(threadIndex C.uintptr_t) {
|
||||
thread := phpThreads[threadIndex]
|
||||
if thread.knownVariableKeys == nil {
|
||||
return
|
||||
}
|
||||
for _, v := range thread.knownVariableKeys {
|
||||
C.frankenphp_release_zend_string(v)
|
||||
}
|
||||
// release everything that might still be pinned to the thread
|
||||
thread.Unpin()
|
||||
thread.knownVariableKeys = nil
|
||||
// The Prepared Environment is registered last and can overwrite any previous values
|
||||
addPreparedEnvToServer(fc, trackVarsArray)
|
||||
}
|
||||
|
||||
// splitPos returns the index where path should
|
||||
@@ -237,7 +208,7 @@ func go_frankenphp_release_known_variable_keys(threadIndex C.uintptr_t) {
|
||||
//
|
||||
// Adapted from https://github.com/caddyserver/caddy/blob/master/modules/caddyhttp/reverseproxy/fastcgi/fastcgi.go
|
||||
// Copyright 2015 Matthew Holt and The Caddy Authors
|
||||
func splitPos(fc *FrankenPHPContext, path string) int {
|
||||
func splitPos(fc *frankenPHPContext, path string) int {
|
||||
if len(fc.splitPath) == 0 {
|
||||
return 0
|
||||
}
|
||||
@@ -260,8 +231,6 @@ var tlsProtocolStrings = map[uint16]string{
|
||||
tls.VersionTLS13: "TLSv1.3",
|
||||
}
|
||||
|
||||
var headerNameReplacer = strings.NewReplacer(" ", "_", "-", "_")
|
||||
|
||||
// SanitizedPathJoin performs filepath.Join(root, reqPath) that
|
||||
// is safe against directory traversal attacks. It uses logic
|
||||
// similar to that in the Go standard library, specifically
|
||||
@@ -291,3 +260,8 @@ func sanitizedPathJoin(root, reqPath string) string {
|
||||
}
|
||||
|
||||
const separator = string(filepath.Separator)
|
||||
|
||||
func toUnsafeChar(s string) *C.char {
|
||||
sData := unsafe.StringData(s)
|
||||
return (*C.char)(unsafe.Pointer(sData))
|
||||
}
|
||||
|
||||
166
context.go
Normal file
166
context.go
Normal file
@@ -0,0 +1,166 @@
|
||||
package frankenphp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// frankenPHPContext provides contextual information about the Request to handle.
|
||||
type frankenPHPContext struct {
|
||||
documentRoot string
|
||||
splitPath []string
|
||||
env PreparedEnv
|
||||
logger *slog.Logger
|
||||
request *http.Request
|
||||
originalRequest *http.Request
|
||||
|
||||
docURI string
|
||||
pathInfo string
|
||||
scriptName string
|
||||
scriptFilename string
|
||||
workerName string
|
||||
|
||||
// Whether the request is already closed by us
|
||||
isDone bool
|
||||
|
||||
responseWriter http.ResponseWriter
|
||||
|
||||
done chan interface{}
|
||||
startedAt time.Time
|
||||
}
|
||||
|
||||
// fromContext extracts the frankenPHPContext from a context.
|
||||
func fromContext(ctx context.Context) (fctx *frankenPHPContext, ok bool) {
|
||||
fctx, ok = ctx.Value(contextKey).(*frankenPHPContext)
|
||||
return
|
||||
}
|
||||
|
||||
// NewRequestWithContext creates a new FrankenPHP request context.
|
||||
func NewRequestWithContext(r *http.Request, opts ...RequestOption) (*http.Request, error) {
|
||||
fc := &frankenPHPContext{
|
||||
done: make(chan interface{}),
|
||||
startedAt: time.Now(),
|
||||
request: r,
|
||||
}
|
||||
for _, o := range opts {
|
||||
if err := o(fc); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if fc.logger == nil {
|
||||
fc.logger = logger
|
||||
}
|
||||
|
||||
if fc.documentRoot == "" {
|
||||
if EmbeddedAppPath != "" {
|
||||
fc.documentRoot = EmbeddedAppPath
|
||||
} else {
|
||||
var err error
|
||||
if fc.documentRoot, err = os.Getwd(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if fc.splitPath == nil {
|
||||
fc.splitPath = []string{".php"}
|
||||
}
|
||||
|
||||
if fc.env == nil {
|
||||
fc.env = make(map[string]string)
|
||||
}
|
||||
|
||||
if splitPos := splitPos(fc, r.URL.Path); splitPos > -1 {
|
||||
fc.docURI = r.URL.Path[:splitPos]
|
||||
fc.pathInfo = r.URL.Path[splitPos:]
|
||||
|
||||
// Strip PATH_INFO from SCRIPT_NAME
|
||||
fc.scriptName = strings.TrimSuffix(r.URL.Path, fc.pathInfo)
|
||||
|
||||
// Ensure the SCRIPT_NAME has a leading slash for compliance with RFC3875
|
||||
// Info: https://tools.ietf.org/html/rfc3875#section-4.1.13
|
||||
if fc.scriptName != "" && !strings.HasPrefix(fc.scriptName, "/") {
|
||||
fc.scriptName = "/" + fc.scriptName
|
||||
}
|
||||
}
|
||||
|
||||
// SCRIPT_FILENAME is the absolute path of SCRIPT_NAME
|
||||
fc.scriptFilename = sanitizedPathJoin(fc.documentRoot, fc.scriptName)
|
||||
c := context.WithValue(r.Context(), contextKey, fc)
|
||||
|
||||
return r.WithContext(c), nil
|
||||
}
|
||||
|
||||
func newDummyContext(requestPath string, opts ...RequestOption) (*frankenPHPContext, error) {
|
||||
r, err := http.NewRequest(http.MethodGet, requestPath, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fr, err := NewRequestWithContext(r, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fc, _ := fromContext(fr.Context())
|
||||
|
||||
return fc, nil
|
||||
}
|
||||
|
||||
// closeContext sends the response to the client
|
||||
func (fc *frankenPHPContext) closeContext() {
|
||||
if fc.isDone {
|
||||
return
|
||||
}
|
||||
|
||||
close(fc.done)
|
||||
fc.isDone = true
|
||||
}
|
||||
|
||||
// validate checks if the request should be outright rejected
|
||||
func (fc *frankenPHPContext) validate() bool {
|
||||
if !strings.Contains(fc.request.URL.Path, "\x00") {
|
||||
return true
|
||||
}
|
||||
|
||||
fc.rejectBadRequest("Invalid request path")
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (fc *frankenPHPContext) clientHasClosed() bool {
|
||||
select {
|
||||
case <-fc.request.Context().Done():
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// reject sends a response with the given status code and message
|
||||
func (fc *frankenPHPContext) reject(statusCode int, message string) {
|
||||
if fc.isDone {
|
||||
return
|
||||
}
|
||||
|
||||
rw := fc.responseWriter
|
||||
if rw != nil {
|
||||
rw.WriteHeader(statusCode)
|
||||
_, _ = rw.Write([]byte(message))
|
||||
|
||||
if f, ok := rw.(http.Flusher); ok {
|
||||
f.Flush()
|
||||
}
|
||||
}
|
||||
|
||||
fc.closeContext()
|
||||
}
|
||||
|
||||
func (fc *frankenPHPContext) rejectBadRequest(message string) {
|
||||
fc.reject(http.StatusBadRequest, message)
|
||||
}
|
||||
46
debugstate.go
Normal file
46
debugstate.go
Normal file
@@ -0,0 +1,46 @@
|
||||
package frankenphp
|
||||
|
||||
// EXPERIMENTAL: ThreadDebugState prints the state of a single PHP thread - debugging purposes only
|
||||
type ThreadDebugState struct {
|
||||
Index int
|
||||
Name string
|
||||
State string
|
||||
IsWaiting bool
|
||||
IsBusy bool
|
||||
WaitingSinceMilliseconds int64
|
||||
}
|
||||
|
||||
// EXPERIMENTAL: FrankenPHPDebugState prints the state of all PHP threads - debugging purposes only
|
||||
type FrankenPHPDebugState struct {
|
||||
ThreadDebugStates []ThreadDebugState
|
||||
ReservedThreadCount int
|
||||
}
|
||||
|
||||
// EXPERIMENTAL: DebugState prints the state of all PHP threads - debugging purposes only
|
||||
func DebugState() FrankenPHPDebugState {
|
||||
fullState := FrankenPHPDebugState{
|
||||
ThreadDebugStates: make([]ThreadDebugState, 0, len(phpThreads)),
|
||||
ReservedThreadCount: 0,
|
||||
}
|
||||
for _, thread := range phpThreads {
|
||||
if thread.state.is(stateReserved) {
|
||||
fullState.ReservedThreadCount++
|
||||
continue
|
||||
}
|
||||
fullState.ThreadDebugStates = append(fullState.ThreadDebugStates, threadDebugState(thread))
|
||||
}
|
||||
|
||||
return fullState
|
||||
}
|
||||
|
||||
// threadDebugState creates a small jsonable status message for debugging purposes
|
||||
func threadDebugState(thread *phpThread) ThreadDebugState {
|
||||
return ThreadDebugState{
|
||||
Index: thread.threadIndex,
|
||||
Name: thread.name(),
|
||||
State: thread.state.name(),
|
||||
IsWaiting: thread.state.isInWaitingState(),
|
||||
IsBusy: !thread.state.isInWaitingState(),
|
||||
WaitingSinceMilliseconds: thread.state.waitTime(),
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
#checkov:skip=CKV_DOCKER_2
|
||||
#checkov:skip=CKV_DOCKER_3
|
||||
FROM golang:1.22-alpine
|
||||
FROM golang:1.24-alpine
|
||||
|
||||
ENV GOTOOLCHAIN=local
|
||||
ENV CFLAGS="-ggdb3"
|
||||
ENV PHPIZE_DEPS="\
|
||||
autoconf \
|
||||
@@ -47,27 +48,29 @@ RUN apk add --no-cache \
|
||||
echo 'set auto-load safe-path /' > /root/.gdbinit
|
||||
|
||||
WORKDIR /usr/local/src/php
|
||||
RUN git clone --branch=PHP-8.3 https://github.com/php/php-src.git . && \
|
||||
RUN git clone --branch=PHP-8.4 https://github.com/php/php-src.git . && \
|
||||
# --enable-embed is only necessary to generate libphp.so, we don't use this SAPI directly
|
||||
./buildconf --force && \
|
||||
./configure \
|
||||
EXTENSION_DIR=/usr/lib/frankenphp/modules ./configure \
|
||||
--enable-embed \
|
||||
--enable-zts \
|
||||
--disable-zend-signals \
|
||||
--enable-zend-max-execution-timers \
|
||||
--with-config-file-path=/etc/frankenphp/php.ini \
|
||||
--with-config-file-scan-dir=/etc/frankenphp/php.d \
|
||||
--enable-debug && \
|
||||
make -j"$(nproc)" && \
|
||||
make install && \
|
||||
ldconfig /etc/ld.so.conf.d && \
|
||||
cp php.ini-development /usr/local/lib/php.ini && \
|
||||
echo "zend_extension=opcache.so" >> /usr/local/lib/php.ini && \
|
||||
echo "opcache.enable=1" >> /usr/local/lib/php.ini && \
|
||||
mkdir -p /etc/frankenphp/php.d && \
|
||||
cp php.ini-development /etc/frankenphp/php.ini && \
|
||||
echo "zend_extension=opcache.so" >> /etc/frankenphp/php.ini && \
|
||||
echo "opcache.enable=1" >> /etc/frankenphp/php.ini && \
|
||||
php --version
|
||||
|
||||
# Install e-dant/watcher (necessary for file watching)
|
||||
ARG EDANT_WATCHER_VERSION=release
|
||||
WORKDIR /usr/local/src/watcher
|
||||
RUN git clone --branch=$EDANT_WATCHER_VERSION https://github.com/e-dant/watcher . && \
|
||||
RUN git clone https://github.com/e-dant/watcher . && \
|
||||
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release && \
|
||||
cmake --build build/ && \
|
||||
cmake --install build
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
#checkov:skip=CKV_DOCKER_2
|
||||
#checkov:skip=CKV_DOCKER_3
|
||||
FROM golang:1.22
|
||||
FROM golang:1.24
|
||||
|
||||
ENV GOTOOLCHAIN=local
|
||||
ENV CFLAGS="-ggdb3"
|
||||
ENV PHPIZE_DEPS="\
|
||||
autoconf \
|
||||
@@ -46,33 +47,37 @@ RUN apt-get update && \
|
||||
echo 'set auto-load safe-path /' > /root/.gdbinit && \
|
||||
echo '* soft core unlimited' >> /etc/security/limits.conf \
|
||||
&& \
|
||||
apt-get clean
|
||||
apt-get clean
|
||||
|
||||
WORKDIR /usr/local/src/php
|
||||
RUN git clone --branch=PHP-8.3 https://github.com/php/php-src.git . && \
|
||||
RUN git clone --branch=PHP-8.4 https://github.com/php/php-src.git . && \
|
||||
# --enable-embed is only necessary to generate libphp.so, we don't use this SAPI directly
|
||||
./buildconf --force && \
|
||||
./configure \
|
||||
EXTENSION_DIR=/usr/lib/frankenphp/modules ./configure \
|
||||
--enable-embed \
|
||||
--enable-zts \
|
||||
--disable-zend-signals \
|
||||
--enable-zend-max-execution-timers \
|
||||
--with-config-file-path=/etc/frankenphp/php.ini \
|
||||
--with-config-file-scan-dir=/etc/frankenphp/php.d \
|
||||
--enable-debug && \
|
||||
make -j"$(nproc)" && \
|
||||
make install && \
|
||||
ldconfig && \
|
||||
cp php.ini-development /usr/local/lib/php.ini && \
|
||||
echo "zend_extension=opcache.so" >> /usr/local/lib/php.ini && \
|
||||
echo "opcache.enable=1" >> /usr/local/lib/php.ini && \
|
||||
mkdir -p /etc/frankenphp/php.d && \
|
||||
cp php.ini-development /etc/frankenphp/php.ini && \
|
||||
echo "zend_extension=opcache.so" >> /etc/frankenphp/php.ini && \
|
||||
echo "opcache.enable=1" >> /etc/frankenphp/php.ini && \
|
||||
php --version
|
||||
|
||||
# Install e-dant/watcher (necessary for file watching)
|
||||
ARG EDANT_WATCHER_VERSION=release
|
||||
WORKDIR /usr/local/src/watcher
|
||||
RUN git clone --branch=$EDANT_WATCHER_VERSION https://github.com/e-dant/watcher . && \
|
||||
RUN git clone https://github.com/e-dant/watcher . && \
|
||||
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release && \
|
||||
cmake --build build/ && \
|
||||
cmake --install build
|
||||
cmake --install build && \
|
||||
cp build/libwatcher-c.so /usr/local/lib/libwatcher-c.so && \
|
||||
ldconfig
|
||||
|
||||
WORKDIR /go/src/app
|
||||
COPY . .
|
||||
|
||||
@@ -7,15 +7,11 @@ variable "VERSION" {
|
||||
}
|
||||
|
||||
variable "PHP_VERSION" {
|
||||
default = "8.2,8.3"
|
||||
default = "8.2,8.3,8.4"
|
||||
}
|
||||
|
||||
variable "GO_VERSION" {
|
||||
default = "1.22"
|
||||
}
|
||||
|
||||
variable EDANT_WATCHER_VERSION {
|
||||
default = "next"
|
||||
default = "1.24"
|
||||
}
|
||||
|
||||
variable "SHA" {}
|
||||
@@ -29,7 +25,7 @@ variable "CACHE" {
|
||||
}
|
||||
|
||||
variable DEFAULT_PHP_VERSION {
|
||||
default = "8.3"
|
||||
default = "8.4"
|
||||
}
|
||||
|
||||
function "tag" {
|
||||
@@ -119,24 +115,23 @@ target "default" {
|
||||
}
|
||||
args = {
|
||||
FRANKENPHP_VERSION = VERSION
|
||||
EDANT_WATCHER_VERSION = EDANT_WATCHER_VERSION
|
||||
}
|
||||
}
|
||||
|
||||
target "static-builder" {
|
||||
target "static-builder-musl" {
|
||||
contexts = {
|
||||
golang-base = "docker-image://golang:${GO_VERSION}-alpine"
|
||||
}
|
||||
dockerfile = "static-builder.Dockerfile"
|
||||
dockerfile = "static-builder-musl.Dockerfile"
|
||||
context = "./"
|
||||
platforms = [
|
||||
"linux/amd64",
|
||||
"linux/arm64",
|
||||
]
|
||||
tags = distinct(flatten([
|
||||
LATEST ? "${IMAGE_NAME}:static-builder" : "",
|
||||
SHA == "" || VERSION != "dev" ? "" : "${IMAGE_NAME}:static-builder-sha-${substr(SHA, 0, 7)}",
|
||||
VERSION == "dev" ? [] : [for v in semver(VERSION) : "${IMAGE_NAME}:static-builder-${v}"]
|
||||
LATEST ? "${IMAGE_NAME}:static-builder-musl" : "",
|
||||
SHA == "" || VERSION != "dev" ? "" : "${IMAGE_NAME}:static-builder-musl-sha-${substr(SHA, 0, 7)}",
|
||||
VERSION == "dev" ? [] : [for v in semver(VERSION) : "${IMAGE_NAME}:static-builder-musl-${v}"]
|
||||
]))
|
||||
labels = {
|
||||
"org.opencontainers.image.created" = "${timestamp()}"
|
||||
@@ -145,7 +140,30 @@ target "static-builder" {
|
||||
}
|
||||
args = {
|
||||
FRANKENPHP_VERSION = VERSION
|
||||
EDANT_WATCHER_VERSION = EDANT_WATCHER_VERSION
|
||||
}
|
||||
secret = ["id=github-token,env=GITHUB_TOKEN"]
|
||||
}
|
||||
|
||||
target "static-builder-gnu" {
|
||||
dockerfile = "static-builder-gnu.Dockerfile"
|
||||
context = "./"
|
||||
platforms = [
|
||||
"linux/amd64",
|
||||
"linux/arm64"
|
||||
]
|
||||
tags = distinct(flatten([
|
||||
LATEST ? "${IMAGE_NAME}:static-builder-gnu" : "",
|
||||
SHA == "" || VERSION != "dev" ? "" : "${IMAGE_NAME}:static-builder-gnu-sha-${substr(SHA, 0, 7)}",
|
||||
VERSION == "dev" ? [] : [for v in semver(VERSION) : "${IMAGE_NAME}:static-builder-gnu-${v}"]
|
||||
]))
|
||||
labels = {
|
||||
"org.opencontainers.image.created" = "${timestamp()}"
|
||||
"org.opencontainers.image.version" = VERSION
|
||||
"org.opencontainers.image.revision" = SHA
|
||||
}
|
||||
args = {
|
||||
FRANKENPHP_VERSION = VERSION
|
||||
GO_VERSION = GO_VERSION
|
||||
}
|
||||
secret = ["id=github-token,env=GITHUB_TOKEN"]
|
||||
}
|
||||
|
||||
11
docs/classic.md
Normal file
11
docs/classic.md
Normal file
@@ -0,0 +1,11 @@
|
||||
# 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.
|
||||
|
||||
Similar to Caddy, FrankenPHP accepts an unlimited number of connections and uses a [fixed number of threads](config.md#caddyfile-config) to serve them. The number of accepted and queued connections is limited only by the available system resources.
|
||||
The PHP thread pool operates with a fixed number of threads initialized at startup, comparable to the static mode of PHP-FPM. It's also possible to let threads [scale automatically at runtime](performance.md#max_threads), similar to the dynamic mode of PHP-FPM.
|
||||
|
||||
Queued connections will wait indefinitely until a PHP thread is available to serve them. To prevent that, you can use the `max_wait_time` [configuration](config.md#caddyfile-config) to limit how long a request may wait for a free PHP thread before being rejected.
|
||||
Additionally, you can set a reasonable [write timeout in Caddy](https://caddyserver.com/docs/caddyfile/options#timeouts).
|
||||
|
||||
Each Caddy instance will only spin up one FrankenPHP thread pool, which will be shared across all `php_server` blocks.
|
||||
@@ -11,9 +11,13 @@ docker build -t frankenphp-dev -f dev.Dockerfile .
|
||||
docker run --cap-add=SYS_PTRACE --security-opt seccomp=unconfined -p 8080:8080 -p 443:443 -p 443:443/udp -v $PWD:/go/src/app -it frankenphp-dev
|
||||
```
|
||||
|
||||
该镜像包含常用的开发工具(Go、GDB、Valgrind、Neovim等)。
|
||||
该镜像包含常用的开发工具(Go、GDB、Valgrind、Neovim等)并使用以下 php 设置位置
|
||||
|
||||
如果 docker 版本低于 23.0,则会因为 dockerignore [pattern issue](https://github.com/moby/moby/pull/42676) 而导致构建失败。将目录添加到 `.dockerignore`。
|
||||
- php.ini: `/etc/frankenphp/php.ini` 默认提供了一个带有开发预设的 php.ini 文件。
|
||||
- 附加配置文件: `/etc/frankenphp/php.d/*.ini`
|
||||
- php 扩展: `/usr/lib/frankenphp/modules/`
|
||||
|
||||
如果您的 docker 版本低于 23.0,则会因为 dockerignore [pattern issue](https://github.com/moby/moby/pull/42676) 而导致构建失败。将目录添加到 `.dockerignore`。
|
||||
|
||||
```patch
|
||||
!testdata/*.php
|
||||
@@ -114,7 +118,7 @@ docker buildx bake -f docker-bake.hcl --pull --no-cache --push
|
||||
--set static-builder.args.DEBUG_SYMBOLS=1 \
|
||||
--set "static-builder.platform=linux/amd64" \
|
||||
static-builder
|
||||
docker cp $(docker create --name static-builder dunglas/frankenphp:static-builder):/go/src/app/dist/frankenphp-linux-$(uname -m) frankenphp
|
||||
docker cp $(docker create --name static-builder-musl dunglas/frankenphp:static-builder-musl):/go/src/app/dist/frankenphp-linux-$(uname -m) frankenphp
|
||||
```
|
||||
|
||||
2. 将当前版本的 `frankenphp` 替换为 debug FrankenPHP 可执行文件
|
||||
@@ -172,27 +176,27 @@ docker buildx bake -f docker-bake.hcl --pull --no-cache --push
|
||||
|
||||
```console
|
||||
go test -tags watcher -c -ldflags=-w
|
||||
gdb --args frankenphp.test -test.run ^MyTest$
|
||||
gdb --args frankenphp.dev.test -test.run ^MyTest$
|
||||
```
|
||||
|
||||
9. 当错误修复后,恢复所有这些更改
|
||||
|
||||
## 其他开发资源
|
||||
|
||||
* [PHP 嵌入 uWSGI](https://github.com/unbit/uwsgi/blob/master/plugins/php/php_plugin.c)
|
||||
* [PHP 嵌入 NGINX Unit](https://github.com/nginx/unit/blob/master/src/nxt_php_sapi.c)
|
||||
* [PHP 嵌入 Go (go-php)](https://github.com/deuill/go-php)
|
||||
* [PHP 嵌入 Go (GoEmPHP)](https://github.com/mikespook/goemphp)
|
||||
* [PHP 嵌入 C++](https://gist.github.com/paresy/3cbd4c6a469511ac7479aa0e7c42fea7)
|
||||
* [扩展和嵌入 PHP 作者:Sara Golemon](https://books.google.fr/books?id=zMbGvK17_tYC&pg=PA254&lpg=PA254#v=onepage&q&f=false)
|
||||
* [TSRMLS_CC到底是什么?](http://blog.golemon.com/2006/06/what-heck-is-tsrmlscc-anyway.html)
|
||||
* [Mac 上的 PHP 嵌入](https://gist.github.com/jonnywang/61427ffc0e8dde74fff40f479d147db4)
|
||||
* [SDL 绑定](https://pkg.go.dev/github.com/veandco/go-sdl2@v0.4.21/sdl#Main)
|
||||
- [PHP 嵌入 uWSGI](https://github.com/unbit/uwsgi/blob/master/plugins/php/php_plugin.c)
|
||||
- [PHP 嵌入 NGINX Unit](https://github.com/nginx/unit/blob/master/src/nxt_php_sapi.c)
|
||||
- [PHP 嵌入 Go (go-php)](https://github.com/deuill/go-php)
|
||||
- [PHP 嵌入 Go (GoEmPHP)](https://github.com/mikespook/goemphp)
|
||||
- [PHP 嵌入 C++](https://gist.github.com/paresy/3cbd4c6a469511ac7479aa0e7c42fea7)
|
||||
- [扩展和嵌入 PHP 作者:Sara Golemon](https://books.google.fr/books?id=zMbGvK17_tYC&pg=PA254&lpg=PA254#v=onepage&q&f=false)
|
||||
- [TSRMLS_CC到底是什么?](http://blog.golemon.com/2006/06/what-heck-is-tsrmlscc-anyway.html)
|
||||
- [Mac 上的 PHP 嵌入](https://gist.github.com/jonnywang/61427ffc0e8dde74fff40f479d147db4)
|
||||
- [SDL 绑定](https://pkg.go.dev/github.com/veandco/go-sdl2@v0.4.21/sdl#Main)
|
||||
|
||||
## Docker 相关资源
|
||||
|
||||
* [Bake 文件定义](https://docs.docker.com/build/customize/bake/file-definition/)
|
||||
* [docker buildx 构建](https://docs.docker.com/engine/reference/commandline/buildx_build/)
|
||||
- [Bake 文件定义](https://docs.docker.com/build/customize/bake/file-definition/)
|
||||
- [docker buildx 构建](https://docs.docker.com/engine/reference/commandline/buildx_build/)
|
||||
|
||||
## 有用的命令
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ docker run -v $PWD:/app/public \
|
||||
### 独立二进制
|
||||
|
||||
如果您不想使用 Docker,我们为 Linux 和 macOS 提供独立的 FrankenPHP 二进制文件
|
||||
,其中包含 [PHP 8.3](https://www.php.net/releases/8.3/en.php) 和最流行的 PHP 扩展:[下载 FrankenPHP](https://github.com/dunglas/frankenphp/releases)。
|
||||
,其中包含 [PHP 8.4](https://www.php.net/releases/8.4/en.php) 和最流行的 PHP 扩展:[下载 FrankenPHP](https://github.com/dunglas/frankenphp/releases)。
|
||||
|
||||
若要启动当前目录的内容,请运行:
|
||||
|
||||
@@ -75,3 +75,4 @@ docker run -v $PWD:/app/public \
|
||||
* [Drupal](https://github.com/dunglas/frankenphp-drupal)
|
||||
* [Joomla](https://github.com/alexandreelise/frankenphp-joomla)
|
||||
* [TYPO3](https://github.com/ochorocho/franken-typo3)
|
||||
* [Magento2](https://github.com/ekino/frankenphp-magento2)
|
||||
|
||||
@@ -80,7 +80,7 @@ CGO_ENABLED=1 \
|
||||
XCADDY_GO_BUILD_FLAGS="-ldflags '-w -s'" \
|
||||
xcaddy build \
|
||||
--output frankenphp \
|
||||
--with github.com/dunglas/frankenphp/caddy \
|
||||
--with frankenphp.dev/caddy \
|
||||
--with github.com/dunglas/caddy-cbrotli \
|
||||
--with github.com/dunglas/mercure/caddy \
|
||||
--with github.com/dunglas/vulcain/caddy
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
# 配置
|
||||
# 配置
|
||||
|
||||
FrankenPHP,Caddy 以及 Mercure 和 Vulcain 模块可以使用 [Caddy 支持的格式](https://caddyserver.com/docs/getting-started#your-first-config) 进行配置。
|
||||
|
||||
在[Docker 映像](docker.md) 中,`Caddyfile` 位于 `/etc/caddy/Caddyfile`。
|
||||
在[Docker 映像](docker.md) 中,`Caddyfile` 位于 `/etc/frankenphp/Caddyfile`。
|
||||
静态二进制文件会在启动时所在的目录中查找 `Caddyfile`。
|
||||
PHP 本身可以[使用 `php.ini` 文件](https://www.php.net/manual/zh/configuration.file.php)进行配置。
|
||||
默认情况下,随 Docker 映像提供的 PHP 和静态二进制文件中包含的 PHP 将在启动 FrankenPHP 的目录和 `/usr/local/etc/php/` 中查找`php.ini` 文件。它们还会从 `/usr/local/etc/php/conf.d/` 中加载所有以 `.ini` 结尾的文件。
|
||||
默认情况下没有 `php.ini` 文件,因此应复制 PHP 项目提供的官方模板。
|
||||
在 Docker 上,模板在镜像中提供:
|
||||
PHP 解释器将在以下位置查找:
|
||||
|
||||
Docker:
|
||||
|
||||
- php.ini: `/usr/local/etc/php/php.ini` 默认情况下不提供 php.ini。
|
||||
- 附加配置文件: `/usr/local/etc/php/conf.d/*.ini`
|
||||
- php 扩展: `/usr/local/lib/php/extensions/no-debug-zts-<YYYYMMDD>/`
|
||||
- 您应该复制 PHP 项目提供的官方模板:
|
||||
|
||||
```dockerfile
|
||||
FROM dunglas/frankenphp
|
||||
@@ -19,20 +24,26 @@ RUN cp $PHP_INI_DIR/php.ini-production $PHP_INI_DIR/php.ini
|
||||
RUN cp $PHP_INI_DIR/php.ini-development $PHP_INI_DIR/php.ini
|
||||
```
|
||||
|
||||
如果不使用 Docker,请复制[PHP 源代码](https://github.com/php/php-src/)中提供的`php.ini-production`或`php.ini-development`中的一个。
|
||||
FrankenPHP 安装 (.rpm 或 .deb):
|
||||
|
||||
- php.ini: `/etc/frankenphp/php.ini` 默认情况下提供带有生产预设的 php.ini 文件。
|
||||
- 附加配置文件: `/etc/frankenphp/php.d/*.ini`
|
||||
- php 扩展: `/usr/lib/frankenphp/modules/`
|
||||
|
||||
静态二进制:
|
||||
|
||||
- php.ini: 执行 `frankenphp run` 或 `frankenphp php-server` 的目录,然后是 `/etc/frankenphp/php.ini`
|
||||
- 附加配置文件: `/etc/frankenphp/php.d/*.ini`
|
||||
- php 扩展: 无法加载
|
||||
- 复制[PHP 源代码](https://github.com/php/php-src/)中提供的`php.ini-production`或`php.ini-development`中的一个。
|
||||
|
||||
## Caddyfile 配置
|
||||
|
||||
要注册 FrankenPHP 执行器,必须设置 `frankenphp` [全局选项](https://caddyserver.com/docs/caddyfile/concepts#global-options),然后可以在站点块中使用 `php_server` 或 `php` [HTTP 指令](https://caddyserver.com/docs/caddyfile/concepts#directives) 来为您的 PHP 应用程序提供服务。
|
||||
可以在站点块中使用 `php_server` 或 `php` [HTTP 指令](https://caddyserver.com/docs/caddyfile/concepts#directives) 来为您的 PHP 应用程序提供服务。
|
||||
|
||||
最小示例:
|
||||
|
||||
```caddyfile
|
||||
{
|
||||
# 启用 FrankenPHP
|
||||
frankenphp
|
||||
}
|
||||
|
||||
localhost {
|
||||
# 启用压缩(可选)
|
||||
encode zstd br gzip
|
||||
@@ -41,7 +52,8 @@ localhost {
|
||||
}
|
||||
```
|
||||
|
||||
或者,可以在全局选项下指定要创建的线程数和要从服务器启动的 [worker 脚本](worker.md)。
|
||||
您也可以使用全局选项显式配置 FrankenPHP:
|
||||
`frankenphp` [全局选项](https://caddyserver.com/docs/caddyfile/concepts#global-options) 可用于配置 FrankenPHP。
|
||||
|
||||
```caddyfile
|
||||
{
|
||||
@@ -73,21 +85,18 @@ localhost {
|
||||
如果在同一服务器上运行多个应用,还可以定义多个 worker:
|
||||
|
||||
```caddyfile
|
||||
{
|
||||
frankenphp {
|
||||
worker /path/to/app/public/index.php <num>
|
||||
worker /path/to/other/public/index.php <num>
|
||||
app.example.com {
|
||||
php_server {
|
||||
root /path/to/app/public
|
||||
worker index.php <num>
|
||||
}
|
||||
}
|
||||
|
||||
app.example.com {
|
||||
root * /path/to/app/public
|
||||
php_server
|
||||
}
|
||||
|
||||
other.example.com {
|
||||
root * /path/to/other/public
|
||||
php_server
|
||||
php_server {
|
||||
root /path/to/other/public
|
||||
worker index.php <num>
|
||||
}
|
||||
}
|
||||
# ...
|
||||
```
|
||||
@@ -123,9 +132,18 @@ route {
|
||||
```caddyfile
|
||||
php_server [<matcher>] {
|
||||
root <directory> # 设置站点的根目录。默认值:`root` 指令。
|
||||
split_path <delim...> # 设置用于将 URI 拆分为两部分的子字符串。第一个匹配的子字符串将用于从路径中拆分“路径信息”。第一个部分以匹配的子字符串为后缀,并将假定为实际资源(CGI 脚本)名称。第二部分将设置为PATH_INFO,供脚本使用。默认值:`.php`
|
||||
split_path <delim...> # 设置用于将 URI 拆分为两部分的子字符串。第一个匹配的子字符串将用于从路径中拆分"路径信息"。第一个部分以匹配的子字符串为后缀,并将假定为实际资源(CGI 脚本)名称。第二部分将设置为PATH_INFO,供脚本使用。默认值:`.php`
|
||||
resolve_root_symlink false # 禁用将 `root` 目录在符号链接时将其解析为实际值(默认启用)。
|
||||
env <key> <value> # 设置额外的环境变量,可以设置多个环境变量。
|
||||
file_server off # 禁用内置的 file_server 指令。
|
||||
worker { # 创建特定于此服务器的 worker。可以为多个 worker 多次指定。
|
||||
file <path> # 设置 worker 脚本的路径,可以相对于 php_server 根目录
|
||||
num <num> # 设置要启动的 PHP 线程数,默认为可用 CPU 数的 2 倍
|
||||
name <name> # 为 worker 设置名称,用于日志和指标。默认值:worker 文件的绝对路径。在 php_server 块中定义时始终以 m# 开头。
|
||||
watch <path> # 设置要监视文件更改的路径。可以为多个路径多次指定。
|
||||
env <key> <value> # 将额外的环境变量设置为给定值。可以为多个环境变量多次指定。此 worker 的环境变量也从 php_server 父级继承,但可以在此处覆盖。
|
||||
}
|
||||
worker <other_file> <num> # 也可以像在全局 frankenphp 块中一样使用简短形式。
|
||||
}
|
||||
```
|
||||
|
||||
@@ -133,9 +151,9 @@ php_server [<matcher>] {
|
||||
|
||||
以下环境变量可用于在 `Caddyfile` 中注入 Caddy 指令,而无需对其进行修改:
|
||||
|
||||
* `SERVER_NAME`: 更改 [要监听的地址](https://caddyserver.com/docs/caddyfile/concepts#addresses),提供的主机名也将用于生成的 TLS 证书
|
||||
* `CADDY_GLOBAL_OPTIONS`: 注入 [全局选项](https://caddyserver.com/docs/caddyfile/options)
|
||||
* `FRANKENPHP_CONFIG`: 在 `frankenphp` 指令下注入配置
|
||||
- `SERVER_NAME`: 更改 [要监听的地址](https://caddyserver.com/docs/caddyfile/concepts#addresses),提供的主机名也将用于生成的 TLS 证书
|
||||
- `CADDY_GLOBAL_OPTIONS`: 注入 [全局选项](https://caddyserver.com/docs/caddyfile/options)
|
||||
- `FRANKENPHP_CONFIG`: 在 `frankenphp` 指令下注入配置
|
||||
|
||||
## PHP 配置
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# 构建自定义 Docker 镜像
|
||||
|
||||
[FrankenPHP Docker 镜像](https://hub.docker.com/r/dunglas/frankenphp) 基于 [官方 PHP 镜像](https://hub.docker.com/_/php/)。
|
||||
Alpine Linux 和 Debian 衍生版适用于常见的处理器架构,且支持 PHP 8.2 和 PHP 8.3。[查看 Tags](https://hub.docker.com/r/dunglas/frankenphp/tags)。
|
||||
Alpine Linux 和 Debian 衍生版适用于常见的处理器架构,支持 PHP 8.2、8.3 和 8.4。。[查看 Tags](https://hub.docker.com/r/dunglas/frankenphp/tags)。
|
||||
|
||||
## 如何使用镜像
|
||||
|
||||
@@ -57,8 +57,8 @@ RUN CGO_ENABLED=1 \
|
||||
CGO_LDFLAGS="$(php-config --ldflags) $(php-config --libs)" \
|
||||
xcaddy build \
|
||||
--output /usr/local/bin/frankenphp \
|
||||
--with github.com/dunglas/frankenphp=./ \
|
||||
--with github.com/dunglas/frankenphp/caddy=./caddy/ \
|
||||
--with frankenphp.dev=./ \
|
||||
--with frankenphp.dev/caddy=./caddy/ \
|
||||
--with github.com/dunglas/caddy-cbrotli \
|
||||
# Mercure 和 Vulcain 包含在官方版本中,如果不需要你可以删除它们
|
||||
--with github.com/dunglas/mercure/caddy \
|
||||
@@ -141,11 +141,11 @@ FrankenPHP 可以在 Docker 中以非 root 用户身份运行。
|
||||
```dockerfile
|
||||
FROM dunglas/frankenphp
|
||||
|
||||
ARG USER=www-data
|
||||
ARG USER=appuser
|
||||
|
||||
RUN \
|
||||
# 在基于 alpine 的发行版使用 "adduser -D ${USER}"
|
||||
useradd -D ${USER}; \
|
||||
useradd ${USER}; \
|
||||
# 需要开放80和443端口的权限
|
||||
setcap CAP_NET_BIND_SERVICE=+eip /usr/local/bin/frankenphp; \
|
||||
# 需要 /data/caddy 和 /config/caddy 目录的写入权限
|
||||
|
||||
@@ -1,35 +1,5 @@
|
||||
# 已知问题
|
||||
|
||||
## Fibers
|
||||
|
||||
在 [Fibers](https://www.php.net/manual/en/language.fibers.php) 中调用 PHP 的函数和代码等语言结构,这些结构内部再调用 [cgo](https://go.dev/blog/cgo) 会导致崩溃。
|
||||
|
||||
这个问题 [正在由 Go 项目处理](https://github.com/golang/go/issues/62130)。
|
||||
|
||||
一种解决方案是不要使用从 Fibers 内部委托给 Go 的构造(如 `echo`)和函数(如 `header()`)。
|
||||
|
||||
下面的代码可能会崩溃,因为它在 Fiber 中使用了 `echo`:
|
||||
|
||||
```php
|
||||
$fiber = new Fiber(function() {
|
||||
echo 'In the Fiber'.PHP_EOL;
|
||||
echo 'Still inside'.PHP_EOL;
|
||||
});
|
||||
$fiber->start();
|
||||
```
|
||||
|
||||
相反,请从 Fiber 返回值并在外部使用它:
|
||||
|
||||
```php
|
||||
$fiber = new Fiber(function() {
|
||||
Fiber::suspend('In the Fiber'.PHP_EOL));
|
||||
Fiber::suspend('Still inside'.PHP_EOL));
|
||||
});
|
||||
echo $fiber->start();
|
||||
echo $fiber->resume();
|
||||
$fiber->resume();
|
||||
```
|
||||
|
||||
## 不支持的 PHP 扩展
|
||||
|
||||
已知以下扩展与 FrankenPHP 不兼容:
|
||||
|
||||
@@ -27,7 +27,7 @@ docker run -p 80:80 -p 443:443 -p 443:443/udp -v $PWD:/app dunglas/frankenphp
|
||||
# 服务器的域名
|
||||
localhost {
|
||||
# 将 webroot 设置为 public/ 目录
|
||||
root * public/
|
||||
root public/
|
||||
# 启用压缩(可选)
|
||||
encode zstd br gzip
|
||||
# 执行当前目录中的 PHP 文件并提供资产
|
||||
|
||||
@@ -13,7 +13,7 @@ FrankenPHP 还支持 [将 PHP 应用程序嵌入到静态二进制文件中](emb
|
||||
|
||||
```console
|
||||
docker buildx bake --load static-builder
|
||||
docker cp $(docker create --name static-builder dunglas/frankenphp:static-builder):/go/src/app/dist/frankenphp-linux-$(uname -m) frankenphp ; docker rm static-builder
|
||||
docker cp $(docker create --name static-builder-musl dunglas/frankenphp:static-builder-musl):/go/src/app/dist/frankenphp-linux-$(uname -m) frankenphp ; docker rm static-builder
|
||||
```
|
||||
|
||||
生成的静态二进制文件名为 `frankenphp`,可在当前目录中找到。
|
||||
@@ -43,6 +43,24 @@ docker buildx bake \
|
||||
static-builder
|
||||
```
|
||||
|
||||
### 额外的 Caddy 模块
|
||||
|
||||
要向 [xcaddy](https://github.com/caddyserver/xcaddy) 添加额外的 Caddy 模块或传递其他参数,请使用 `XCADDY_ARGS` Docker 参数:
|
||||
|
||||
```console
|
||||
docker buildx bake \
|
||||
--load \
|
||||
--set static-builder.args.XCADDY_ARGS="--with github.com/darkweak/souin/plugins/caddy --with github.com/dunglas/caddy-cbrotli --with github.com/dunglas/mercure/caddy --with github.com/dunglas/vulcain/caddy" \
|
||||
static-builder
|
||||
```
|
||||
|
||||
在本例中,我们为 Caddy 添加了 [Souin](https://souin.io) HTTP 缓存模块,以及 [cbrotli](https://github.com/dunglas/caddy-cbrotli)、[Mercure](https://mercure.rocks) 和 [Vulcain](https://vulcain.rocks) 模块。
|
||||
|
||||
> [!TIP]
|
||||
>
|
||||
> 如果 `XCADDY_ARGS` 为空或未设置,则默认包含 cbrotli、Mercure 和 Vulcain 模块。
|
||||
> 如果自定义了 `XCADDY_ARGS` 的值,则必须显式地包含它们。
|
||||
|
||||
参见:[自定义构建](#自定义构建)
|
||||
|
||||
### GitHub Token
|
||||
@@ -75,6 +93,7 @@ cd frankenphp
|
||||
* `PHP_VERSION`: 要使用的 PHP 版本
|
||||
* `PHP_EXTENSIONS`: 要构建的 PHP 扩展([支持的扩展列表](https://static-php.dev/zh/guide/extensions.html))
|
||||
* `PHP_EXTENSION_LIBS`: 要构建的额外库,为扩展添加额外的功能
|
||||
* `XCADDY_ARGS`:传递给 [xcaddy](https://github.com/caddyserver/xcaddy) 的参数,例如用于添加额外的 Caddy 模块
|
||||
* `EMBED`: 要嵌入二进制文件的 PHP 应用程序的路径
|
||||
* `CLEAN`: 设置后,libphp 及其所有依赖项都是重新构建的(不使用缓存)
|
||||
* `DEBUG_SYMBOLS`: 设置后,调试符号将被保留在二进制文件内
|
||||
|
||||
@@ -3,13 +3,30 @@
|
||||
This document explains how to create a FrankenPHP binary that will load PHP as a dynamic library.
|
||||
This is the recommended method.
|
||||
|
||||
Alternatively, [static builds](static.md) can also be created.
|
||||
Alternatively, [fully and mostly static builds](static.md) can also be created.
|
||||
|
||||
## Install PHP
|
||||
|
||||
FrankenPHP is compatible with PHP 8.2 and superior.
|
||||
|
||||
First, [get the PHP sources](https://www.php.net/downloads.php) and extract them:
|
||||
### With Homebrew (Linux and Mac)
|
||||
|
||||
The easiest way to install a version of libphp compatible with FrankenPHP is to use the ZTS packages provided by [Homebrew PHP](https://github.com/shivammathur/homebrew-php).
|
||||
|
||||
First, if not already done, install [Homebrew](https://brew.sh).
|
||||
|
||||
Then, install the ZTS variant of PHP, Brotli (optional, for compression support) and watcher (optional, for file change detection):
|
||||
|
||||
```console
|
||||
brew install shivammathur/php/php-zts brotli watcher
|
||||
brew link --overwrite --force shivammathur/php/php-zts
|
||||
```
|
||||
|
||||
### By Compiling PHP
|
||||
|
||||
Alternatively, you can compile PHP from sources with the options needed by FrankenPHP by following these steps.
|
||||
~~
|
||||
~~First, [get the PHP sources](https://www.php.net/downloads.php) and extract them:
|
||||
|
||||
```console
|
||||
tar xf php-*
|
||||
@@ -19,7 +36,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
|
||||
|
||||
```console
|
||||
./configure \
|
||||
@@ -29,13 +46,12 @@ The following `./configure` flags are mandatory, but you can add others, for exa
|
||||
--enable-zend-max-execution-timers
|
||||
```
|
||||
|
||||
### Mac
|
||||
#### Mac
|
||||
|
||||
Use the [Homebrew](https://brew.sh/) package manager to install
|
||||
`libiconv`, `bison`, `re2c` and `pkg-config`:
|
||||
Use the [Homebrew](https://brew.sh/) package manager to install the required and optional dependencies:
|
||||
|
||||
```console
|
||||
brew install libiconv bison brotli re2c pkg-config
|
||||
brew install libiconv bison brotli re2c pkg-config watcher
|
||||
echo 'export PATH="/opt/homebrew/opt/bison/bin:$PATH"' >> ~/.zshrc
|
||||
```
|
||||
|
||||
@@ -43,16 +59,13 @@ Then run the configure script:
|
||||
|
||||
```console
|
||||
./configure \
|
||||
--enable-embed=static \
|
||||
--enable-embed \
|
||||
--enable-zts \
|
||||
--disable-zend-signals \
|
||||
--disable-opcache-jit \
|
||||
--enable-static \
|
||||
--enable-shared=no \
|
||||
--with-iconv=/opt/homebrew/opt/libiconv/
|
||||
```
|
||||
|
||||
## Compile PHP
|
||||
#### Compile PHP
|
||||
|
||||
Finally, compile and install PHP:
|
||||
|
||||
@@ -73,17 +86,12 @@ Alternatively, these features can be disabled by passing build tags to the Go co
|
||||
|
||||
## Compile the Go App
|
||||
|
||||
You can now build the final binary:
|
||||
|
||||
```console
|
||||
curl -L https://github.com/dunglas/frankenphp/archive/refs/heads/main.tar.gz | tar xz
|
||||
cd frankenphp-main/caddy/frankenphp
|
||||
CGO_CFLAGS=$(php-config --includes) CGO_LDFLAGS="$(php-config --ldflags) $(php-config --libs)" go build -tags=nobadger,nomysql,nopgx
|
||||
```
|
||||
You can now build the final binary.
|
||||
|
||||
### Using xcaddy
|
||||
|
||||
Alternatively, use [xcaddy](https://github.com/caddyserver/xcaddy) to compile FrankenPHP with [custom Caddy modules](https://caddyserver.com/docs/modules/):
|
||||
The recommended way is to use [xcaddy](https://github.com/caddyserver/xcaddy) to compile FrankenPHP.
|
||||
`xcaddy` also allows to easily add [custom Caddy modules](https://caddyserver.com/docs/modules/) and FrankenPHP extensions:
|
||||
|
||||
```console
|
||||
CGO_ENABLED=1 \
|
||||
@@ -92,10 +100,10 @@ CGO_CFLAGS=$(php-config --includes) \
|
||||
CGO_LDFLAGS="$(php-config --ldflags) $(php-config --libs)" \
|
||||
xcaddy build \
|
||||
--output frankenphp \
|
||||
--with github.com/dunglas/frankenphp/caddy \
|
||||
--with frankenphp.dev/caddy \
|
||||
--with github.com/dunglas/mercure/caddy \
|
||||
--with github.com/dunglas/vulcain/caddy
|
||||
# Add extra Caddy modules here
|
||||
# Add extra Caddy modules and FrankenPHP extensions here
|
||||
```
|
||||
|
||||
> [!TIP]
|
||||
@@ -107,3 +115,13 @@ xcaddy build \
|
||||
> To do so, change the `XCADDY_GO_BUILD_FLAGS` environment variable to something like
|
||||
> `XCADDY_GO_BUILD_FLAGS=$'-ldflags "-w -s -extldflags \'-Wl,-z,stack-size=0x80000\'"'`
|
||||
> (change the stack size value according to your app needs).
|
||||
|
||||
### Without xcaddy
|
||||
|
||||
Alternatively, it's possible to compile FrankenPHP without `xcaddy` by using the `go` command directly:
|
||||
|
||||
```console
|
||||
curl -L https://github.com/dunglas/frankenphp/archive/refs/heads/main.tar.gz | tar xz
|
||||
cd frankenphp-main/caddy/frankenphp
|
||||
CGO_CFLAGS=$(php-config --includes) CGO_LDFLAGS="$(php-config --ldflags) $(php-config --libs)" go build -tags=nobadger,nomysql,nopgx
|
||||
```
|
||||
|
||||
109
docs/config.md
109
docs/config.md
@@ -2,16 +2,20 @@
|
||||
|
||||
FrankenPHP, Caddy as well as the Mercure and Vulcain modules can be configured using [the formats supported by Caddy](https://caddyserver.com/docs/getting-started#your-first-config).
|
||||
|
||||
In [the Docker images](docker.md), the `Caddyfile` is located at `/etc/caddy/Caddyfile`.
|
||||
The static binary will look for the `Caddyfile` in the directory in which it is started.
|
||||
In [the Docker images](docker.md), the `Caddyfile` is located at `/etc/frankenphp/Caddyfile`.
|
||||
The static binary will also look for the `Caddyfile` in the directory where the `frankenphp run` command is executed.
|
||||
You can specify a custom path with the `-c` or `--config` option.
|
||||
|
||||
PHP itself can be configured [using a `php.ini` file](https://www.php.net/manual/en/configuration.file.php).
|
||||
|
||||
By default, PHP supplied with Docker images and the one included in the static binary will look for a `php.ini` file in the directory where FrankenPHP is started and in `/usr/local/etc/php/`. They will also load all files ending in `.ini` from `/usr/local/etc/php/conf.d/`.
|
||||
Depending on your installation method, the PHP interpreter will look for configuration files in locations described above.
|
||||
|
||||
No `php.ini` file is present by default, you should copy an official template provided by the PHP project.
|
||||
## Docker
|
||||
|
||||
On Docker, the templates are provided in the images:
|
||||
- `php.ini`: `/usr/local/etc/php/php.ini` (no `php.ini` is provided by default)
|
||||
- additional configuration files: `/usr/local/etc/php/conf.d/*.ini`
|
||||
- PHP extensions: `/usr/local/lib/php/extensions/no-debug-zts-<YYYYMMDD>/`
|
||||
- You should copy an official template provided by the PHP project:
|
||||
|
||||
```dockerfile
|
||||
FROM dunglas/frankenphp
|
||||
@@ -19,24 +23,30 @@ FROM dunglas/frankenphp
|
||||
# Production:
|
||||
RUN cp $PHP_INI_DIR/php.ini-production $PHP_INI_DIR/php.ini
|
||||
|
||||
# Or developement:
|
||||
# Or development:
|
||||
RUN cp $PHP_INI_DIR/php.ini-development $PHP_INI_DIR/php.ini
|
||||
```
|
||||
|
||||
If you don't use Docker, copy one of `php.ini-production` or `php.ini-development` provided [in the PHP sources](https://github.com/php/php-src/).
|
||||
## RPM and Debian packages
|
||||
|
||||
- `php.ini`: `/etc/frankenphp/php.ini` (a `php.ini` file with production presets is provided by default)
|
||||
- additional configuration files: `/etc/frankenphp/php.d/*.ini`
|
||||
- PHP extensions: `/usr/lib/frankenphp/modules/`
|
||||
|
||||
## Static binary
|
||||
|
||||
- `php.ini`: The directory in which `frankenphp run` or `frankenphp php-server` is executed, then `/etc/frankenphp/php.ini`
|
||||
- additional configuration files: `/etc/frankenphp/php.d/*.ini`
|
||||
- PHP extensions: cannot be loaded, bundle them in the binary itself
|
||||
- copy one of `php.ini-production` or `php.ini-development` provided [in the PHP sources](https://github.com/php/php-src/).
|
||||
|
||||
## Caddyfile Config
|
||||
|
||||
To register the FrankenPHP executor, the `frankenphp` [global option](https://caddyserver.com/docs/caddyfile/concepts#global-options) must be set, then the `php_server` or the `php` [HTTP directives](https://caddyserver.com/docs/caddyfile/concepts#directives) may be used within the site blocks to serve your PHP app.
|
||||
The `php_server` or the `php` [HTTP directives](https://caddyserver.com/docs/caddyfile/concepts#directives) may be used within the site blocks to serve your PHP app.
|
||||
|
||||
Minimal example:
|
||||
|
||||
```caddyfile
|
||||
{
|
||||
# Enable FrankenPHP
|
||||
frankenphp
|
||||
}
|
||||
|
||||
localhost {
|
||||
# Enable compression (optional)
|
||||
encode zstd br gzip
|
||||
@@ -45,17 +55,22 @@ localhost {
|
||||
}
|
||||
```
|
||||
|
||||
Optionally, the number of threads to create and [worker scripts](worker.md) to start with the server can be specified under the global option.
|
||||
You can also explicitly configure FrankenPHP using the global option:
|
||||
The `frankenphp` [global option](https://caddyserver.com/docs/caddyfile/concepts#global-options) can be used to configure FrankenPHP.
|
||||
|
||||
```caddyfile
|
||||
{
|
||||
frankenphp {
|
||||
num_threads <num_threads> # Sets the number of PHP threads to start. Default: 2x the number of available CPUs.
|
||||
max_threads <num_threads> # Limits the number of additional PHP threads that can be started at runtime. Default: num_threads. Can be set to 'auto'.
|
||||
max_wait_time <duration> # Sets the maximum time a request may wait for a free PHP thread before timing out. Default: disabled.
|
||||
php_ini <key> <value> # Set a php.ini directive. Can be used several times to set multiple directives.
|
||||
worker {
|
||||
file <path> # Sets the path to the worker script.
|
||||
num <num> # Sets the number of PHP threads to start, defaults to 2x the number of available CPUs.
|
||||
env <key> <value> # Sets an extra environment variable to the given value. Can be specified more than once for multiple environment variables.
|
||||
watch <path> # Sets the path to watch for file changes. Can be specified more than once for multiple paths.
|
||||
name <name> # Sets the name of the worker, used in logs and metrics. Default: absolute path of worker file
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -78,28 +93,27 @@ Alternatively, you may use the one-line short form of the `worker` option:
|
||||
You can also define multiple workers if you serve multiple apps on the same server:
|
||||
|
||||
```caddyfile
|
||||
{
|
||||
frankenphp {
|
||||
worker /path/to/app/public/index.php <num>
|
||||
worker /path/to/other/public/index.php <num>
|
||||
app.example.com {
|
||||
php_server {
|
||||
root /path/to/app/public
|
||||
worker index.php <num>
|
||||
}
|
||||
}
|
||||
|
||||
app.example.com {
|
||||
root * /path/to/app/public
|
||||
php_server
|
||||
}
|
||||
|
||||
other.example.com {
|
||||
root * /path/to/other/public
|
||||
php_server
|
||||
php_server {
|
||||
root /path/to/other/public
|
||||
worker index.php <num>
|
||||
}
|
||||
}
|
||||
|
||||
# ...
|
||||
```
|
||||
|
||||
Using the `php_server` directive is generally what you need,
|
||||
but if you need full control, you can use the lower level `php` directive:
|
||||
but if you need full control, you can use the lower-level `php` directive.
|
||||
The `php` directive passes all input to PHP, instead of first checking whether
|
||||
it's a PHP file or not. Read more about it in the [performance page](performance.md).
|
||||
|
||||
Using the `php_server` directive is equivalent to this configuration:
|
||||
|
||||
@@ -133,6 +147,14 @@ php_server [<matcher>] {
|
||||
resolve_root_symlink false # Disables resolving the `root` directory to its actual value by evaluating a symbolic link, if one exists (enabled by default).
|
||||
env <key> <value> # Sets an extra environment variable to the given value. Can be specified more than once for multiple environment variables.
|
||||
file_server off # Disables the built-in file_server directive.
|
||||
worker { # Creates a worker specific to this server. Can be specified more than once for multiple workers.
|
||||
file <path> # Sets the path to the worker script, can be relative to the php_server root
|
||||
num <num> # Sets the number of PHP threads to start, defaults to 2x the number of available
|
||||
name <name> # Sets the name for the worker, used in logs and metrics. Default: absolute path of worker file. Always starts with m# when defined in a php_server block.
|
||||
watch <path> # Sets the path to watch for file changes. Can be specified more than once for multiple paths.
|
||||
env <key> <value> # Sets an extra environment variable to the given value. Can be specified more than once for multiple environment variables. Environment variables for this worker are also inherited from the php_server parent, but can be overwritten here.
|
||||
}
|
||||
worker <other_file> <num> # Can also use the short form like in the global frankenphp block.
|
||||
}
|
||||
```
|
||||
|
||||
@@ -174,10 +196,10 @@ where the FrankenPHP process was started. You can instead also specify one or mo
|
||||
}
|
||||
```
|
||||
|
||||
* The `**` pattern signifies recursive watching
|
||||
* Directories can also be relative (to where the FrankenPHP process is started from)
|
||||
* If you have multiple workers defined, all of them will be restarted when a file changes
|
||||
* Be wary about watching files that are created at runtime (like logs) since they might cause unwanted worker restarts.
|
||||
- The `**` pattern signifies recursive watching
|
||||
- Directories can also be relative (to where the FrankenPHP process is started from)
|
||||
- If you have multiple workers defined, all of them will be restarted when a file changes
|
||||
- Be wary about watching files that are created at runtime (like logs) since they might cause unwanted worker restarts.
|
||||
|
||||
The file watcher is based on [e-dant/watcher](https://github.com/e-dant/watcher).
|
||||
|
||||
@@ -202,7 +224,9 @@ This is an opt-in configuration that needs to be added to the global options in
|
||||
> This can also be configured using the `CADDY_GLOBAL_OPTIONS` environment config:
|
||||
|
||||
```sh
|
||||
CADDY_GLOBAL_OPTIONS="servers { enable_full_duplex }"
|
||||
CADDY_GLOBAL_OPTIONS="servers {
|
||||
enable_full_duplex
|
||||
}"
|
||||
```
|
||||
|
||||
You can find more information about this setting in the [Caddy documentation](https://caddyserver.com/docs/caddyfile/options#enable-full-duplex).
|
||||
@@ -211,9 +235,9 @@ You can find more information about this setting in the [Caddy documentation](ht
|
||||
|
||||
The following environment variables can be used to inject Caddy directives in the `Caddyfile` without modifying it:
|
||||
|
||||
* `SERVER_NAME`: change [the addresses on which to listen](https://caddyserver.com/docs/caddyfile/concepts#addresses), the provided hostnames will also be used for the generated TLS certificate
|
||||
* `CADDY_GLOBAL_OPTIONS`: inject [global options](https://caddyserver.com/docs/caddyfile/options)
|
||||
* `FRANKENPHP_CONFIG`: inject config under the `frankenphp` directive
|
||||
- `SERVER_NAME`: change [the addresses on which to listen](https://caddyserver.com/docs/caddyfile/concepts#addresses), the provided hostnames will also be used for the generated TLS certificate
|
||||
- `CADDY_GLOBAL_OPTIONS`: inject [global options](https://caddyserver.com/docs/caddyfile/options)
|
||||
- `FRANKENPHP_CONFIG`: inject config under the `frankenphp` directive
|
||||
|
||||
As for FPM and CLI SAPIs, environment variables are exposed by default in the `$_SERVER` superglobal.
|
||||
|
||||
@@ -225,6 +249,23 @@ To load [additional PHP configuration files](https://www.php.net/manual/en/confi
|
||||
the `PHP_INI_SCAN_DIR` environment variable can be used.
|
||||
When set, PHP will load all the file with the `.ini` extension present in the given directories.
|
||||
|
||||
You can also change the PHP configuration using the `php_ini` directive in the `Caddyfile`:
|
||||
|
||||
```caddyfile
|
||||
{
|
||||
frankenphp {
|
||||
php_ini memory_limit 256M
|
||||
|
||||
# or
|
||||
|
||||
php_ini {
|
||||
memory_limit 256M
|
||||
max_execution_time 15
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Enable the Debug Mode
|
||||
|
||||
When using the Docker image, set the `CADDY_GLOBAL_OPTIONS` environment variable to `debug` to enable the debug mode:
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 126 KiB After Width: | Height: | Size: 73 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 159 KiB After Width: | Height: | Size: 87 KiB |
@@ -2,12 +2,12 @@
|
||||
|
||||
[FrankenPHP Docker images](https://hub.docker.com/r/dunglas/frankenphp) are based on [official PHP images](https://hub.docker.com/_/php/). Debian and Alpine Linux variants are provided for popular architectures. Debian variants are recommended.
|
||||
|
||||
Variants for PHP 8.2 and PHP 8.3 are provided.
|
||||
Variants for PHP 8.2, 8.3 and 8.4 are provided.
|
||||
|
||||
The tags follow this pattern: `dunglas/frankenphp:<frankenphp-version>-php<php-version>-<os>`
|
||||
|
||||
* `<frankenphp-version>` and `<php-version>` are version numbers of FrankenPHP and PHP respectively, with specifities ranging from major (e.g. `1`), minor (e.g. `1.2`) to patch versions (e.g. `1.2.3`).
|
||||
* `<os>` is either `bookworm` (for Debian Bookworm) or `alpine` (for the latest stable version of Alpine).
|
||||
- `<frankenphp-version>` and `<php-version>` are version numbers of FrankenPHP and PHP respectively, ranging from major (e.g. `1`), minor (e.g. `1.2`) to patch versions (e.g. `1.2.3`).
|
||||
- `<os>` is either `bookworm` (for Debian Bookworm) or `alpine` (for the latest stable version of Alpine).
|
||||
|
||||
[Browse tags](https://hub.docker.com/r/dunglas/frankenphp/tags).
|
||||
|
||||
@@ -65,8 +65,8 @@ RUN CGO_ENABLED=1 \
|
||||
CGO_LDFLAGS="$(php-config --ldflags) $(php-config --libs)" \
|
||||
xcaddy build \
|
||||
--output /usr/local/bin/frankenphp \
|
||||
--with github.com/dunglas/frankenphp=./ \
|
||||
--with github.com/dunglas/frankenphp/caddy=./caddy/ \
|
||||
--with frankenphp.dev=./ \
|
||||
--with frankenphp.dev/caddy=./caddy/ \
|
||||
--with github.com/dunglas/caddy-cbrotli \
|
||||
# Mercure and Vulcain are included in the official build, but feel free to remove them
|
||||
--with github.com/dunglas/mercure/caddy \
|
||||
@@ -149,11 +149,11 @@ Here is a sample `Dockerfile` doing this:
|
||||
```dockerfile
|
||||
FROM dunglas/frankenphp
|
||||
|
||||
ARG USER=www-data
|
||||
ARG USER=appuser
|
||||
|
||||
RUN \
|
||||
# Use "adduser -D ${USER}" for alpine based distros
|
||||
useradd -D ${USER}; \
|
||||
useradd ${USER}; \
|
||||
# Add additional capability to bind to port 80 and 443
|
||||
setcap CAP_NET_BIND_SERVICE=+eip /usr/local/bin/frankenphp; \
|
||||
# Give write access to /data/caddy and /config/caddy
|
||||
@@ -173,11 +173,11 @@ the webserver as a non-root user, and without the need for any capability:
|
||||
```dockerfile
|
||||
FROM dunglas/frankenphp
|
||||
|
||||
ARG USER=www-data
|
||||
ARG USER=appuser
|
||||
|
||||
RUN \
|
||||
# Use "adduser -D ${USER}" for alpine based distros
|
||||
useradd -D ${USER}; \
|
||||
useradd ${USER}; \
|
||||
# Remove default capability
|
||||
setcap -r /usr/local/bin/frankenphp; \
|
||||
# Give write access to /data/caddy and /config/caddy
|
||||
@@ -193,8 +193,8 @@ Example: `:8000`
|
||||
|
||||
The Docker images are built:
|
||||
|
||||
* when a new release is tagged
|
||||
* daily at 4 am UTC, if new versions of the official PHP images are available
|
||||
- when a new release is tagged
|
||||
- daily at 4 am UTC, if new versions of the official PHP images are available
|
||||
|
||||
## Development Versions
|
||||
|
||||
|
||||
@@ -14,10 +14,10 @@ Before creating the self-contained binary be sure that your app is ready for emb
|
||||
|
||||
For instance, you likely want to:
|
||||
|
||||
* Install the production dependencies of the app
|
||||
* Dump the autoloader
|
||||
* Enable the production mode of your application (if any)
|
||||
* Strip unneeded files such as `.git` or tests to reduce the size of your final binary
|
||||
- Install the production dependencies of the app
|
||||
- Dump the autoloader
|
||||
- Enable the production mode of your application (if any)
|
||||
- Strip unneeded files such as `.git` or tests to reduce the size of your final binary
|
||||
|
||||
For instance, for a Symfony app, you can use the following commands:
|
||||
|
||||
@@ -53,34 +53,34 @@ 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
|
||||
FROM --platform=linux/amd64 dunglas/frankenphp:static-builder
|
||||
```dockerfile
|
||||
FROM --platform=linux/amd64 dunglas/frankenphp:static-builder
|
||||
|
||||
# Copy your app
|
||||
WORKDIR /go/src/app/dist/app
|
||||
COPY . .
|
||||
# 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
|
||||
```
|
||||
# Build the static binary
|
||||
WORKDIR /go/src/app/
|
||||
RUN EMBED=dist/app/ ./build-static.sh
|
||||
```
|
||||
|
||||
> [!CAUTION]
|
||||
>
|
||||
> Some `.dockerignore` files (e.g. default [Symfony Docker `.dockerignore`](https://github.com/dunglas/symfony-docker/blob/main/.dockerignore))
|
||||
> will ignore the `vendor/` directory and `.env` files. Be sure to adjust or remove the `.dockerignore` file before the build.
|
||||
> [!CAUTION]
|
||||
>
|
||||
> Some `.dockerignore` files (e.g. default [Symfony Docker `.dockerignore`](https://github.com/dunglas/symfony-docker/blob/main/.dockerignore))
|
||||
> will ignore the `vendor/` directory and `.env` files. Be sure to adjust or remove the `.dockerignore` file before the build.
|
||||
|
||||
2. Build:
|
||||
|
||||
```console
|
||||
docker build -t static-app -f static-build.Dockerfile .
|
||||
```
|
||||
```console
|
||||
docker build -t static-app -f static-build.Dockerfile .
|
||||
```
|
||||
|
||||
3. Extract the binary:
|
||||
|
||||
```console
|
||||
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
|
||||
```
|
||||
```console
|
||||
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
|
||||
```
|
||||
|
||||
The resulting binary is the file named `my-app` in the current directory.
|
||||
|
||||
|
||||
@@ -11,9 +11,13 @@ docker build -t frankenphp-dev -f dev.Dockerfile .
|
||||
docker run --cap-add=SYS_PTRACE --security-opt seccomp=unconfined -p 8080:8080 -p 443:443 -p 443:443/udp -v $PWD:/go/src/app -it frankenphp-dev
|
||||
```
|
||||
|
||||
L'image contient les outils de développement habituels (Go, GDB, Valgrind, Neovim...).
|
||||
L'image contient les outils de développement habituels (Go, GDB, Valgrind, Neovim...) et utilise les emplacements de configuration PHP suivants
|
||||
|
||||
Si la version de Docker est inférieure à 23.0, la construction échoue à cause d'un [problème de pattern](https://github.com/moby/moby/pull/42676) dans `.dockerignore`. Ajoutez les répertoires à `.dockerignore`.
|
||||
- php.ini: `/etc/frankenphp/php.ini` Un fichier php.ini avec des préréglages de développement est fourni par défaut.
|
||||
- fichiers de configuration supplémentaires: `/etc/frankenphp/php.d/*.ini`
|
||||
- extensions php: `/usr/lib/frankenphp/modules/`
|
||||
|
||||
Si votre version de Docker est inférieure à 23.0, la construction échouera à cause d'un [problème de pattern](https://github.com/moby/moby/pull/42676) dans `.dockerignore`. Ajoutez les répertoires à `.dockerignore`.
|
||||
|
||||
```patch
|
||||
!testdata/*.php
|
||||
@@ -38,7 +42,7 @@ Construire Caddy avec le module FrankenPHP :
|
||||
|
||||
```console
|
||||
cd caddy/frankenphp/
|
||||
go build
|
||||
go build -tags watcher,brotli,nobadger,nomysql,nopgx
|
||||
cd ../../
|
||||
```
|
||||
|
||||
@@ -49,10 +53,14 @@ cd testdata/
|
||||
../caddy/frankenphp/frankenphp run
|
||||
```
|
||||
|
||||
Le serveur est configuré pour écouter à l'adresse `127.0.0.1:8080`:
|
||||
Le serveur est configuré pour écouter à l'adresse `127.0.0.1:80`:
|
||||
|
||||
> [!NOTE]
|
||||
>
|
||||
> Si vous utilisez Docker, vous devrez soit lier le port 80 du conteneur, soit exécuter depuis l'intérieur du conteneur.
|
||||
|
||||
```console
|
||||
curl -vk https://localhost/phpinfo.php
|
||||
curl -vk http://127.0.0.1/phpinfo.php
|
||||
```
|
||||
|
||||
## Serveur de test minimal
|
||||
@@ -104,69 +112,92 @@ Construire à partir de zéro les images FrankenPHP pour arm64 & amd64 et les po
|
||||
docker buildx bake -f docker-bake.hcl --pull --no-cache --push
|
||||
```
|
||||
|
||||
## Déboguer les erreurs de segmentation avec les builds statiques
|
||||
|
||||
1. Téléchargez la version de débogage du binaire FrankenPHP depuis GitHub ou créez votre propre build statique incluant des symboles de débogage :
|
||||
|
||||
```console
|
||||
docker buildx bake \
|
||||
--load \
|
||||
--set static-builder.args.DEBUG_SYMBOLS=1 \
|
||||
--set "static-builder.platform=linux/amd64" \
|
||||
static-builder
|
||||
docker cp $(docker create --name static-builder-musl dunglas/frankenphp:static-builder-musl):/go/src/app/dist/frankenphp-linux-$(uname -m) frankenphp
|
||||
```
|
||||
|
||||
2. Remplacez votre version actuelle de `frankenphp` par l'exécutable de débogage de FrankenPHP.
|
||||
3. Démarrez FrankenPHP comme d'habitude (alternativement, vous pouvez directement démarrer FrankenPHP avec GDB : `gdb --args frankenphp run`).
|
||||
4. Attachez-vous au processus avec GDB :
|
||||
|
||||
```console
|
||||
gdb -p `pidof frankenphp`
|
||||
```
|
||||
|
||||
5. Si nécessaire, tapez `continue` dans le shell GDB
|
||||
6. Faites planter FrankenPHP.
|
||||
7. Tapez `bt` dans le shell GDB
|
||||
8. Copiez la sortie
|
||||
|
||||
## Déboguer les erreurs de segmentation dans GitHub Actions
|
||||
|
||||
1. Ouvrir `.github/workflows/tests.yml`
|
||||
2. Activer les symboles de débogage de la bibliothèque PHP
|
||||
|
||||
```patch
|
||||
- uses: shivammathur/setup-php@v2
|
||||
# ...
|
||||
env:
|
||||
phpts: ts
|
||||
+ debug: true
|
||||
```
|
||||
```patch
|
||||
- uses: shivammathur/setup-php@v2
|
||||
# ...
|
||||
env:
|
||||
phpts: ts
|
||||
+ debug: true
|
||||
```
|
||||
|
||||
3. Activer `tmate` pour se connecter au conteneur
|
||||
|
||||
```patch
|
||||
-
|
||||
name: Set CGO flags
|
||||
run: echo "CGO_CFLAGS=$(php-config --includes)" >> "$GITHUB_ENV"
|
||||
+ -
|
||||
+ run: |
|
||||
+ sudo apt install gdb
|
||||
+ mkdir -p /home/runner/.config/gdb/
|
||||
+ printf "set auto-load safe-path /\nhandle SIG34 nostop noprint pass" > /home/runner/.config/gdb/gdbinit
|
||||
+ -
|
||||
+ uses: mxschmitt/action-tmate@v3
|
||||
```
|
||||
```patch
|
||||
- name: Set CGO flags
|
||||
run: echo "CGO_CFLAGS=$(php-config --includes)" >> "$GITHUB_ENV"
|
||||
+ - run: |
|
||||
+ sudo apt install gdb
|
||||
+ mkdir -p /home/runner/.config/gdb/
|
||||
+ printf "set auto-load safe-path /\nhandle SIG34 nostop noprint pass" > /home/runner/.config/gdb/gdbinit
|
||||
+ - uses: mxschmitt/action-tmate@v3
|
||||
```
|
||||
|
||||
4. Se connecter au conteneur
|
||||
5. Ouvrir `frankenphp.go`
|
||||
6. Activer `cgosymbolizer`
|
||||
|
||||
```patch
|
||||
- //_ "github.com/ianlancetaylor/cgosymbolizer"
|
||||
+ _ "github.com/ianlancetaylor/cgosymbolizer"
|
||||
```
|
||||
```patch
|
||||
- //_ "github.com/ianlancetaylor/cgosymbolizer"
|
||||
+ _ "github.com/ianlancetaylor/cgosymbolizer"
|
||||
```
|
||||
|
||||
7. Télécharger le module : `go get`
|
||||
8. Dans le conteneur, vous pouvez utiliser GDB et similaires :
|
||||
|
||||
```console
|
||||
go test -tags watcher -c -ldflags=-w
|
||||
gdb --args frankenphp.test -test.run ^MyTest$
|
||||
```
|
||||
```console
|
||||
go test -tags watcher -c -ldflags=-w
|
||||
gdb --args frankenphp.dev.test -test.run ^MyTest$
|
||||
```
|
||||
|
||||
9. Quand le bug est corrigé, annulez tous les changements
|
||||
9. Quand le bug est corrigé, annulez tous les changements.
|
||||
|
||||
## Ressources Diverses pour le Développement
|
||||
|
||||
* [Intégration de PHP dans uWSGI](https://github.com/unbit/uwsgi/blob/master/plugins/php/php_plugin.c)
|
||||
* [Intégration de PHP dans NGINX Unit](https://github.com/nginx/unit/blob/master/src/nxt_php_sapi.c)
|
||||
* [Intégration de PHP dans Go (go-php)](https://github.com/deuill/go-php)
|
||||
* [Intégration de PHP dans Go (GoEmPHP)](https://github.com/mikespook/goemphp)
|
||||
* [Intégration de PHP dans C++](https://gist.github.com/paresy/3cbd4c6a469511ac7479aa0e7c42fea7)
|
||||
* [Extending and Embedding PHP par Sara Golemon](https://books.google.fr/books?id=zMbGvK17_tYC&pg=PA254&lpg=PA254#v=onepage&q&f=false)
|
||||
* [Qu'est-ce que TSRMLS_CC, au juste ?](http://blog.golemon.com/2006/06/what-heck-is-tsrmlscc-anyway.html)
|
||||
* [Intégration de PHP sur Mac](https://gist.github.com/jonnywang/61427ffc0e8dde74fff40f479d147db4)
|
||||
* [Bindings SDL](https://pkg.go.dev/github.com/veandco/go-sdl2@v0.4.21/sdl#Main)
|
||||
- [Intégration de PHP dans uWSGI](https://github.com/unbit/uwsgi/blob/master/plugins/php/php_plugin.c)
|
||||
- [Intégration de PHP dans NGINX Unit](https://github.com/nginx/unit/blob/master/src/nxt_php_sapi.c)
|
||||
- [Intégration de PHP dans Go (go-php)](https://github.com/deuill/go-php)
|
||||
- [Intégration de PHP dans Go (GoEmPHP)](https://github.com/mikespook/goemphp)
|
||||
- [Intégration de PHP dans C++](https://gist.github.com/paresy/3cbd4c6a469511ac7479aa0e7c42fea7)
|
||||
- [Extending and Embedding PHP par Sara Golemon](https://books.google.fr/books?id=zMbGvK17_tYC&pg=PA254&lpg=PA254#v=onepage&q&f=false)
|
||||
- [Qu'est-ce que TSRMLS_CC, au juste ?](http://blog.golemon.com/2006/06/what-heck-is-tsrmlscc-anyway.html)
|
||||
- [Intégration de PHP sur Mac](https://gist.github.com/jonnywang/61427ffc0e8dde74fff40f479d147db4)
|
||||
- [Bindings SDL](https://pkg.go.dev/github.com/veandco/go-sdl2@v0.4.21/sdl#Main)
|
||||
|
||||
## Ressources Liées à Docker
|
||||
|
||||
* [Définition du fichier Bake](https://docs.docker.com/build/customize/bake/file-definition/)
|
||||
* [docker buildx build](https://docs.docker.com/engine/reference/commandline/buildx_build/)
|
||||
- [Définition du fichier Bake](https://docs.docker.com/build/customize/bake/file-definition/)
|
||||
- [docker buildx build](https://docs.docker.com/engine/reference/commandline/buildx_build/)
|
||||
|
||||
## Commande utile
|
||||
|
||||
@@ -174,3 +205,16 @@ docker buildx bake -f docker-bake.hcl --pull --no-cache --push
|
||||
apk add strace util-linux gdb
|
||||
strace -e 'trace=!futex,epoll_ctl,epoll_pwait,tgkill,rt_sigreturn' -p 1
|
||||
```
|
||||
|
||||
## Traduire la documentation
|
||||
|
||||
Pour traduire la documentation et le site dans une nouvelle langue, procédez comme suit :
|
||||
|
||||
1. Créez un nouveau répertoire nommé avec le code ISO à 2 caractères de la langue dans le répertoire `docs/` de ce dépôt
|
||||
2. Copiez tous les fichiers `.md` à la racine du répertoire `docs/` dans le nouveau répertoire (utilisez toujours la version anglaise comme source de traduction, car elle est toujours à jour).
|
||||
3. Copiez les fichiers `README.md` et `CONTRIBUTING.md` du répertoire racine vers le nouveau répertoire.
|
||||
4. Traduisez le contenu des fichiers, mais ne changez pas les noms de fichiers, ne traduisez pas non plus les chaînes commençant par `> [!` (c'est un balisage spécial pour GitHub).
|
||||
5. Créez une Pull Request avec les traductions
|
||||
6. Dans le [référentiel du site](https://github.com/dunglas/frankenphp-website/tree/main), copiez et traduisez les fichiers de traduction dans les répertoires `content/`, `data/` et `i18n/`.
|
||||
7. Traduire les valeurs dans le fichier YAML créé.
|
||||
8. Ouvrir une Pull Request sur le dépôt du site.
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
FrankenPHP est un serveur d'applications moderne pour PHP construit à partir du serveur web [Caddy](https://caddyserver.com/).
|
||||
|
||||
FrankenPHP donne des super-pouvoirs à vos applications PHP grâce à ses fonctionnalités à la pointe : [*Early Hints*](early-hints.md), [mode worker](worker.md), [fonctionnalités en temps réel](mercure.md), HTTPS automatique, prise en charge de HTTP/2 et HTTP/3...
|
||||
FrankenPHP donne des super-pouvoirs à vos applications PHP grâce à ses fonctionnalités à la pointe : [_Early Hints_](early-hints.md), [mode worker](worker.md), [fonctionnalités en temps réel](mercure.md), HTTPS automatique, prise en charge de HTTP/2 et HTTP/3...
|
||||
|
||||
FrankenPHP fonctionne avec n'importe quelle application PHP et rend vos projets Laravel et Symfony plus rapides que jamais grâce à leurs intégrations officielles avec le mode worker.
|
||||
|
||||
@@ -16,25 +16,10 @@ Découvrez plus de détails sur ce serveur d’application dans le replay de cet
|
||||
|
||||
## Pour Commencer
|
||||
|
||||
### Docker
|
||||
|
||||
```console
|
||||
docker run -v $PWD:/app/public \
|
||||
-p 80:80 -p 443:443 -p 443:443/udp \
|
||||
dunglas/frankenphp
|
||||
```
|
||||
|
||||
Rendez-vous sur `https://localhost`, c'est parti !
|
||||
|
||||
> [!TIP]
|
||||
>
|
||||
> Ne tentez pas d'utiliser `https://127.0.0.1`. Utilisez `https://localhost` et acceptez le certificat auto-signé.
|
||||
> Utilisez [la variable d'environnement `SERVER_NAME`](config.md#variables-denvironnement) pour changer le domaine à utiliser.
|
||||
|
||||
### Binaire autonome
|
||||
|
||||
Si vous préférez ne pas utiliser Docker, nous fournissons des binaires autonomes de FrankenPHP pour Linux et macOS
|
||||
contenant [PHP 8.3](https://www.php.net/releases/8.3/fr.php) et la plupart des extensions PHP populaires.
|
||||
contenant [PHP 8.4](https://www.php.net/releases/8.4/fr.php) et la plupart des extensions PHP populaires.
|
||||
|
||||
Sous Windows, utilisez [WSL](https://learn.microsoft.com/windows/wsl/) pour exécuter FrankenPHP.
|
||||
|
||||
@@ -57,31 +42,68 @@ Vous pouvez également exécuter des scripts en ligne de commande avec :
|
||||
frankenphp php-cli /path/to/your/script.php
|
||||
```
|
||||
|
||||
### Docker
|
||||
|
||||
Des [images Docker](https://frankenphp.dev/docs/fr/docker/) sont également disponibles :
|
||||
|
||||
```console
|
||||
docker run -v .:/app/public \
|
||||
-p 80:80 -p 443:443 -p 443:443/udp \
|
||||
dunglas/frankenphp
|
||||
```
|
||||
|
||||
Rendez-vous sur `https://localhost`, c'est parti !
|
||||
|
||||
> [!TIP]
|
||||
>
|
||||
> Ne tentez pas d'utiliser `https://127.0.0.1`. Utilisez `https://localhost` et acceptez le certificat auto-signé.
|
||||
> Utilisez [la variable d'environnement `SERVER_NAME`](config.md#variables-denvironnement) pour changer le domaine à utiliser.
|
||||
|
||||
### Homebrew
|
||||
|
||||
FrankenPHP est également disponible sous forme de paquet [Homebrew](https://brew.sh) pour macOS et Linux.
|
||||
|
||||
Pour l'installer :
|
||||
|
||||
```console
|
||||
brew install dunglas/frankenphp/frankenphp
|
||||
```
|
||||
|
||||
Pour servir le contenu du répertoire courant, exécutez :
|
||||
|
||||
```console
|
||||
frankenphp php-server
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
* [Le mode worker](worker.md)
|
||||
* [Le support des Early Hints (code de statut HTTP 103)](early-hints.md)
|
||||
* [Temps réel](mercure.md)
|
||||
* [Configuration](config.md)
|
||||
* [Images Docker](docker.md)
|
||||
* [Déploiement en production](production.md)
|
||||
* [Optimisation des performances](performance.md)
|
||||
* [Créer des applications PHP **standalone**, auto-exécutables](embed.md)
|
||||
* [Créer un build statique](static.md)
|
||||
* [Compiler depuis les sources](compile.md)
|
||||
* [Intégration Laravel](laravel.md)
|
||||
* [Problèmes connus](known-issues.md)
|
||||
* [Application de démo (Symfony) et benchmarks](https://github.com/dunglas/frankenphp-demo)
|
||||
* [Documentation de la bibliothèque Go](https://pkg.go.dev/github.com/dunglas/frankenphp)
|
||||
* [Contribuer et débugger](CONTRIBUTING.md)
|
||||
- [Le mode classique](classic.md)
|
||||
- [Le mode worker](worker.md)
|
||||
- [Le support des Early Hints (code de statut HTTP 103)](early-hints.md)
|
||||
- [Temps réel](mercure.md)
|
||||
- [Servir efficacement les fichiers statiques volumineux](x-sendfile.md)
|
||||
- [Configuration](config.md)
|
||||
- [Images Docker](docker.md)
|
||||
- [Déploiement en production](production.md)
|
||||
- [Optimisation des performances](performance.md)
|
||||
- [Créer des applications PHP **standalone**, auto-exécutables](embed.md)
|
||||
- [Créer un build statique](static.md)
|
||||
- [Compiler depuis les sources](compile.md)
|
||||
- [Surveillance de FrankenPHP](metrics.md)
|
||||
- [Intégration Laravel](laravel.md)
|
||||
- [Problèmes connus](known-issues.md)
|
||||
- [Application de démo (Symfony) et benchmarks](https://github.com/dunglas/frankenphp-demo)
|
||||
- [Documentation de la bibliothèque Go](https://pkg.go.dev/github.com/dunglas/frankenphp)
|
||||
- [Contribuer et débugger](CONTRIBUTING.md)
|
||||
|
||||
## Exemples et squelettes
|
||||
|
||||
* [Symfony](https://github.com/dunglas/symfony-docker)
|
||||
* [API Platform](https://api-platform.com/docs/distribution/)
|
||||
* [Laravel](laravel.md)
|
||||
* [Sulu](https://sulu.io/blog/running-sulu-with-frankenphp)
|
||||
* [WordPress](https://github.com/StephenMiracle/frankenwp)
|
||||
* [Drupal](https://github.com/dunglas/frankenphp-drupal)
|
||||
* [Joomla](https://github.com/alexandreelise/frankenphp-joomla)
|
||||
* [TYPO3](https://github.com/ochorocho/franken-typo3)
|
||||
- [Symfony](https://github.com/dunglas/symfony-docker)
|
||||
- [API Platform](https://api-platform.com/docs/distribution/)
|
||||
- [Laravel](laravel.md)
|
||||
- [Sulu](https://sulu.io/blog/running-sulu-with-frankenphp)
|
||||
- [WordPress](https://github.com/StephenMiracle/frankenwp)
|
||||
- [Drupal](https://github.com/dunglas/frankenphp-drupal)
|
||||
- [Joomla](https://github.com/alexandreelise/frankenphp-joomla)
|
||||
- [TYPO3](https://github.com/ochorocho/franken-typo3)
|
||||
- [Magento2](https://github.com/ekino/frankenphp-magento2)
|
||||
|
||||
11
docs/fr/classic.md
Normal file
11
docs/fr/classic.md
Normal file
@@ -0,0 +1,11 @@
|
||||
# Utilisation du mode classique
|
||||
|
||||
Sans aucune configuration additionnelle, FrankenPHP fonctionne en mode classique. Dans ce mode, FrankenPHP fonctionne comme un serveur PHP traditionnel, en servant directement les fichiers PHP. Cela en fait un remplaçant parfait à PHP-FPM ou Apache avec mod_php.
|
||||
|
||||
Comme Caddy, FrankenPHP accepte un nombre illimité de connexions et utilise un [nombre fixe de threads](config.md#configuration-du-caddyfile) pour les servir. Le nombre de connexions acceptées et en attente n'est limité que par les ressources système disponibles.
|
||||
Le pool de threads PHP fonctionne avec un nombre fixe de threads initialisés au démarrage, comparable au mode statique de PHP-FPM. Il est également possible de laisser les threads [s'adapter automatiquement à l'exécution](performance.md#max_threads), comme dans le mode dynamique de PHP-FPM.
|
||||
|
||||
Les connexions en file d'attente attendront indéfiniment jusqu'à ce qu'un thread PHP soit disponible pour les servir. Pour éviter cela, vous pouvez utiliser la [configuration](config.md#configuration-du-caddyfile) `max_wait_time` pour limiter la durée pendant laquelle une requête peut attendre un thread PHP libre avant d'être rejetée.
|
||||
En outre, vous pouvez définir un [délai d'écriture dans Caddy](https://caddyserver.com/docs/caddyfile/options#timeouts) raisonnable.
|
||||
|
||||
Chaque instance de Caddy n'utilisera qu'un seul pool de threads FrankenPHP, qui sera partagé par tous les blocs `php_server`.
|
||||
@@ -9,6 +9,23 @@ Alternativement, il est aussi possible de [créer des builds statiques](static.m
|
||||
|
||||
FrankenPHP est compatible avec PHP 8.2 et versions ultérieures.
|
||||
|
||||
### Avec Homebrew (Linux et Mac)
|
||||
|
||||
La manière la plus simple d'installer une version de libphp compatible avec FrankenPHP est d'utiliser les paquets ZTS fournis par [Homebrew PHP](https://github.com/shivammathur/homebrew-php).
|
||||
|
||||
Tout d'abord, si ce n'est déjà fait, installez [Homebrew](https://brew.sh).
|
||||
|
||||
Ensuite, installez la variante ZTS de PHP, Brotli (facultatif, pour la prise en charge de la compression) et watcher (facultatif, pour la détection des modifications de fichiers) :
|
||||
|
||||
```console
|
||||
brew install shivammathur/php/php-zts brotli watcher
|
||||
brew link --overwrite --force shivammathur/php/php-zts
|
||||
```
|
||||
|
||||
### En compilant PHP
|
||||
|
||||
Vous pouvez également compiler PHP à partir des sources avec les options requises par FrankenPHP en suivant ces étapes.
|
||||
|
||||
Tout d'abord, [téléchargez les sources de PHP](https://www.php.net/downloads.php) et extrayez-les :
|
||||
|
||||
```console
|
||||
@@ -32,10 +49,10 @@ Les options de configuration suivantes sont nécessaires pour la compilation, ma
|
||||
|
||||
### Mac
|
||||
|
||||
Utilisez le gestionnaire de paquets [Homebrew](https://brew.sh/) pour installer `libiconv`, `bison`, `re2c` et `pkg-config` :
|
||||
Utilisez le gestionnaire de paquets [Homebrew](https://brew.sh/) pour installer les dépendances obligatoires et optionnelles :
|
||||
|
||||
```console
|
||||
brew install libiconv bison re2c pkg-config
|
||||
brew install libiconv bison brotli re2c pkg-config watcher
|
||||
echo 'export PATH="/opt/homebrew/opt/bison/bin:$PATH"' >> ~/.zshrc
|
||||
```
|
||||
|
||||
@@ -43,16 +60,14 @@ Puis exécutez le script de configuration :
|
||||
|
||||
```console
|
||||
./configure \
|
||||
--enable-embed=static \
|
||||
--enable-embed \
|
||||
--enable-zts \
|
||||
--disable-zend-signals \
|
||||
--disable-opcache-jit \
|
||||
--enable-static \
|
||||
--enable-shared=no \
|
||||
--with-iconv=/opt/homebrew/opt/libiconv/
|
||||
```
|
||||
|
||||
## Compilez PHP
|
||||
### Compilez PHP
|
||||
|
||||
Finalement, compilez et installez PHP :
|
||||
|
||||
@@ -61,30 +76,35 @@ make -j"$(getconf _NPROCESSORS_ONLN)"
|
||||
sudo make install
|
||||
```
|
||||
|
||||
## Installez les dépendances optionnelles
|
||||
|
||||
Certaines fonctionnalités de FrankenPHP nécessitent des dépendances optionnelles qui doivent être installées.
|
||||
Ces fonctionnalités peuvent également être désactivées en passant des tags de compilation au compilateur Go.
|
||||
|
||||
| Fonctionnalité | Dépendance | Tag de compilation pour la désactiver |
|
||||
|---------------------------------------------------------|-----------------------------------------------------------------------|---------------------------------------|
|
||||
| Compression Brotli | [Brotli](https://github.com/google/brotli) | nobrotli |
|
||||
| Redémarrage des workers en cas de changement de fichier | [Watcher C](https://github.com/e-dant/watcher/tree/release/watcher-c) | nowatcher |
|
||||
|
||||
## Compiler l'application Go
|
||||
|
||||
Vous pouvez maintenant compilez FrankenPHP :
|
||||
|
||||
```console
|
||||
curl -L https://github.com/dunglas/frankenphp/archive/refs/heads/main.tar.gz | tar xz
|
||||
cd frankenphp-main/caddy/frankenphp
|
||||
CGO_CFLAGS=$(php-config --includes) CGO_LDFLAGS="$(php-config --ldflags) $(php-config --libs)" go build
|
||||
```
|
||||
|
||||
### Utiliser xcaddy
|
||||
|
||||
Alternativement, Vous pouvez utiliser [xcaddy](https://github.com/caddyserver/xcaddy) pour compiler FrankenPHP avec [des modules Caddy additionnels](https://caddyserver.com/docs/modules/):
|
||||
La méthode recommandée consiste à utiliser [xcaddy](https://github.com/caddyserver/xcaddy) pour compiler FrankenPHP.
|
||||
`xcaddy` permet également d'ajouter facilement des [modules Caddy personnalisés](https://caddyserver.com/docs/modules/) et des extensions FrankenPHP :
|
||||
|
||||
```console
|
||||
CGO_ENABLED=1 \
|
||||
XCADDY_GO_BUILD_FLAGS="-ldflags '-w -s'" \
|
||||
XCADDY_GO_BUILD_FLAGS="-ldflags='-w -s' -tags=nobadger,nomysql,nopgx" \
|
||||
CGO_CFLAGS=$(php-config --includes) \
|
||||
CGO_LDFLAGS="$(php-config --ldflags) $(php-config --libs)" \
|
||||
xcaddy build \
|
||||
--output frankenphp \
|
||||
--with github.com/dunglas/frankenphp/caddy \
|
||||
--with frankenphp.dev/caddy \
|
||||
--with github.com/dunglas/caddy-cbrotli \
|
||||
--with github.com/dunglas/mercure/caddy \
|
||||
--with github.com/dunglas/vulcain/caddy
|
||||
# Add extra Caddy modules here
|
||||
# Ajoutez les modules Caddy supplémentaires et les extensions FrankenPHP ici
|
||||
```
|
||||
|
||||
> [!TIP]
|
||||
@@ -96,3 +116,13 @@ xcaddy build \
|
||||
> Pour ce faire, modifiez la variable d'environnement `XCADDY_GO_BUILD_FLAGS` en quelque chose comme
|
||||
> `XCADDY_GO_BUILD_FLAGS=$'-ldflags "-w -s -extldflags \'-Wl,-z,stack-size=0x80000\'"'`
|
||||
> (modifiez la valeur de la taille de la pile selon les besoins de votre application).
|
||||
|
||||
### Sans xcaddy
|
||||
|
||||
Il est également possible de compiler FrankenPHP sans `xcaddy` en utilisant directement la commande `go` :
|
||||
|
||||
```console
|
||||
curl -L https://github.com/dunglas/frankenphp/archive/refs/heads/main.tar.gz | tar xz
|
||||
cd frankenphp-main/caddy/frankenphp
|
||||
CGO_CFLAGS=$(php-config --includes) CGO_LDFLAGS="$(php-config --ldflags) $(php-config --libs)" go build -tags=nobadger,nomysql,nopgx
|
||||
```
|
||||
|
||||
@@ -1,15 +1,20 @@
|
||||
# Configuration
|
||||
# Configuration
|
||||
|
||||
FrankenPHP, Caddy ainsi que les modules Mercure et Vulcain peuvent être configurés en utilisant [les formats pris en charge par Caddy](https://caddyserver.com/docs/getting-started#your-first-config).
|
||||
|
||||
Dans [les images Docker](docker.md), le `Caddyfile` est situé dans `/etc/caddy/Caddyfile`.
|
||||
Dans [les images Docker](docker.md), le `Caddyfile` est situé dans `/etc/frankenphp/Caddyfile`.
|
||||
Le binaire statique cherchera le `Caddyfile` dans le répertoire dans lequel il est démarré.
|
||||
|
||||
PHP lui-même peut être configuré [en utilisant un fichier `php.ini`](https://www.php.net/manual/fr/configuration.file.php).
|
||||
|
||||
Par défaut, le PHP fourni avec les images Docker et celui inclus dans le binaire statique cherchera un fichier `php.ini` dans le répertoire où FrankenPHP est démarré et dans `/usr/local/etc/php/`. Ils chargeront également tous les fichiers se terminant par `.ini` dans `/usr/local/etc/php/conf.d/`.
|
||||
L'interpréteur PHP cherchera dans les emplacements suivants :
|
||||
|
||||
Aucun fichier `php.ini` n'est présent par défaut, vous devriez copier un modèle officiel fourni par le projet PHP.
|
||||
Sur Docker, les modèles sont fournis dans les images :
|
||||
Docker :
|
||||
|
||||
- php.ini : `/usr/local/etc/php/php.ini` Aucun php.ini n'est fourni par défaut.
|
||||
- fichiers de configuration supplémentaires : `/usr/local/etc/php/conf.d/*.ini`
|
||||
- extensions php : `/usr/local/lib/php/extensions/no-debug-zts-<YYYYMMDD>/`
|
||||
- Vous devriez copier un modèle officiel fourni par le projet PHP :
|
||||
|
||||
```dockerfile
|
||||
FROM dunglas/frankenphp
|
||||
@@ -21,40 +26,52 @@ RUN cp $PHP_INI_DIR/php.ini-production $PHP_INI_DIR/php.ini
|
||||
RUN cp $PHP_INI_DIR/php.ini-development $PHP_INI_DIR/php.ini
|
||||
```
|
||||
|
||||
Si vous n'utilisez pas Docker, copiez l'un des fichiers `php.ini-production` ou `php.ini-development` fournis [dans les sources de PHP](https://github.com/php/php-src/).
|
||||
Installation de FrankenPHP (.rpm ou .deb) :
|
||||
|
||||
- php.ini : `/etc/frankenphp/php.ini` Un fichier php.ini avec des préréglages de production est fourni par défaut.
|
||||
- fichiers de configuration supplémentaires : `/etc/frankenphp/php.d/*.ini`
|
||||
- extensions php : `/usr/lib/frankenphp/modules/`
|
||||
|
||||
Binaire statique :
|
||||
|
||||
- php.ini : Le répertoire dans lequel `frankenphp run` ou `frankenphp php-server` est exécuté, puis `/etc/frankenphp/php.ini`
|
||||
- fichiers de configuration supplémentaires : `/etc/frankenphp/php.d/*.ini`
|
||||
- extensions php : ne peuvent pas être chargées
|
||||
- copiez l'un des fichiers `php.ini-production` ou `php.ini-development` fournis [dans les sources de PHP](https://github.com/php/php-src/).
|
||||
|
||||
## Configuration du Caddyfile
|
||||
|
||||
Pour enregistrer l'exécutable de FrankenPHP, l'[option globale](https://caddyserver.com/docs/caddyfile/concepts#global-options) `frankenphp` doit être définie, puis les [directives HTTP](https://caddyserver.com/docs/caddyfile/concepts#directives) `php_server` ou `php` peuvent être utilisées dans les blocs de site pour servir votre application PHP.
|
||||
Les [directives HTTP](https://caddyserver.com/docs/caddyfile/concepts#directives) `php_server` ou `php` peuvent être utilisées dans les blocs de site pour servir votre application PHP.
|
||||
|
||||
Exemple minimal :
|
||||
|
||||
```caddyfile
|
||||
{
|
||||
# Activer FrankenPHP
|
||||
frankenphp
|
||||
}
|
||||
|
||||
localhost {
|
||||
# Activer la compression (optionnel)
|
||||
encode zstd br gzip
|
||||
# Exécuter les fichiers PHP dans le répertoire courant et servir les assets
|
||||
php_server
|
||||
# Activer la compression (optionnel)
|
||||
encode zstd br gzip
|
||||
# Exécuter les fichiers PHP dans le répertoire courant et servir les assets
|
||||
php_server
|
||||
}
|
||||
```
|
||||
|
||||
En option, le nombre de threads à créer et les [workers](worker.md) à démarrer avec le serveur peuvent être spécifiés sous l'option globale.
|
||||
Vous pouvez également configurer explicitement FrankenPHP en utilisant l'option globale :
|
||||
L'[option globale](https://caddyserver.com/docs/caddyfile/concepts#global-options) `frankenphp` peut être utilisée pour configurer FrankenPHP.
|
||||
|
||||
```caddyfile
|
||||
{
|
||||
frankenphp {
|
||||
num_threads <num_threads> # Définit le nombre de threads PHP à démarrer. Par défaut : 2x le nombre de CPUs disponibles.
|
||||
worker {
|
||||
file <path> # Définit le chemin vers le script worker.
|
||||
num <num> # Définit le nombre de threads PHP à démarrer, par défaut 2x le nombre de CPUs disponibles.
|
||||
env <key> <value> # Définit une variable d'environnement supplémentaire avec la valeur donnée. Peut être spécifié plusieurs fois pour régler plusieurs variables d'environnement.
|
||||
}
|
||||
}
|
||||
frankenphp {
|
||||
num_threads <num_threads> # Définit le nombre de threads PHP à démarrer. Par défaut : 2x le nombre de CPUs disponibles.
|
||||
max_threads <num_threads> # Limite le nombre de threads PHP supplémentaires qui peuvent être démarrés au moment de l'exécution. Valeur par défaut : num_threads. Peut être mis à 'auto'.
|
||||
max_wait_time <duration> # Définit le temps maximum pendant lequel une requête peut attendre un thread PHP libre avant d'être interrompue. Valeur par défaut : désactivé.
|
||||
php_ini <key> <value> Définit une directive php.ini. Peut être utilisé plusieurs fois pour définir plusieurs directives.
|
||||
worker {
|
||||
file <path> # Définit le chemin vers le script worker.
|
||||
num <num> # Définit le nombre de threads PHP à démarrer, par défaut 2x le nombre de CPUs disponibles.
|
||||
env <key> <value> # Définit une variable d'environnement supplémentaire avec la valeur donnée. Peut être spécifié plusieurs fois pour régler plusieurs variables d'environnement.
|
||||
watch <path> # Définit le chemin d'accès à surveiller pour les modifications de fichiers. Peut être spécifié plusieurs fois pour plusieurs chemins.
|
||||
name <name> # Définit le nom du worker, utilisé dans les journaux et les métriques. Défaut : chemin absolu du fichier du worker
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# ...
|
||||
@@ -64,9 +81,9 @@ Vous pouvez également utiliser la forme courte de l'option worker en une seule
|
||||
|
||||
```caddyfile
|
||||
{
|
||||
frankenphp {
|
||||
worker <file> <num>
|
||||
}
|
||||
frankenphp {
|
||||
worker <file> <num>
|
||||
}
|
||||
}
|
||||
|
||||
# ...
|
||||
@@ -75,21 +92,18 @@ Vous pouvez également utiliser la forme courte de l'option worker en une seule
|
||||
Vous pouvez aussi définir plusieurs workers si vous servez plusieurs applications sur le même serveur :
|
||||
|
||||
```caddyfile
|
||||
{
|
||||
frankenphp {
|
||||
worker /path/to/app/public/index.php <num>
|
||||
worker /path/to/other/public/index.php <num>
|
||||
}
|
||||
}
|
||||
|
||||
app.example.com {
|
||||
root * /path/to/app/public
|
||||
php_server
|
||||
php_server {
|
||||
root /path/to/app/public
|
||||
worker index.php <num>
|
||||
}
|
||||
}
|
||||
|
||||
other.example.com {
|
||||
root * /path/to/other/public
|
||||
php_server
|
||||
php_server {
|
||||
root /path/to/other/public
|
||||
worker index.php <num>
|
||||
}
|
||||
}
|
||||
|
||||
# ...
|
||||
@@ -97,27 +111,29 @@ other.example.com {
|
||||
|
||||
L'utilisation de la directive `php_server` est généralement suffisante,
|
||||
mais si vous avez besoin d'un contrôle total, vous pouvez utiliser la directive `php`, qui permet un plus grand niveau de finesse dans la configuration.
|
||||
La directive `php` transmet toutes les entrées à PHP, au lieu de vérifier d'abord si
|
||||
c'est un fichier PHP ou pas. En savoir plus à ce sujet dans la [page performances](performance.md).
|
||||
|
||||
Utiliser la directive `php_server` est équivalent à cette configuration :
|
||||
|
||||
```caddyfile
|
||||
route {
|
||||
# Ajoute un slash final pour les requêtes de répertoire
|
||||
@canonicalPath {
|
||||
file {path}/index.php
|
||||
not path */
|
||||
}
|
||||
redir @canonicalPath {path}/ 308
|
||||
# Si le fichier demandé n'existe pas, essayer les fichiers index
|
||||
@indexFiles file {
|
||||
try_files {path} {path}/index.php index.php
|
||||
split_path .php
|
||||
}
|
||||
rewrite @indexFiles {http.matchers.file.relative}
|
||||
# FrankenPHP!
|
||||
@phpFiles path *.php
|
||||
php @phpFiles
|
||||
file_server
|
||||
# Ajoute un slash final pour les requêtes de répertoire
|
||||
@canonicalPath {
|
||||
file {path}/index.php
|
||||
not path */
|
||||
}
|
||||
redir @canonicalPath {path}/ 308
|
||||
# Si le fichier demandé n'existe pas, essayer les fichiers index
|
||||
@indexFiles file {
|
||||
try_files {path} {path}/index.php index.php
|
||||
split_path .php
|
||||
}
|
||||
rewrite @indexFiles {http.matchers.file.relative}
|
||||
# FrankenPHP!
|
||||
@phpFiles path *.php
|
||||
php @phpFiles
|
||||
file_server
|
||||
}
|
||||
```
|
||||
|
||||
@@ -125,20 +141,102 @@ Les directives `php_server` et `php` disposent des options suivantes :
|
||||
|
||||
```caddyfile
|
||||
php_server [<matcher>] {
|
||||
root <directory> # Définit le dossier racine du le site. Par défaut : valeur de la directive `root` parente.
|
||||
split_path <delim...> # Définit les sous-chaînes pour diviser l'URI en deux parties. La première sous-chaîne correspondante sera utilisée pour séparer le "path info" du chemin. La première partie est suffixée avec la sous-chaîne correspondante et sera considérée comme le nom réel de la ressource (script CGI). La seconde partie sera définie comme PATH_INFO pour utilisation par le script. Par défaut : `.php`
|
||||
resolve_root_symlink false # Désactive la résolution du répertoire `root` vers sa valeur réelle en évaluant un lien symbolique, s'il existe (activé par défaut).
|
||||
env <key> <value> # Définit une variable d'environnement supplémentaire avec la valeur donnée. Peut être spécifié plusieurs fois pour plusieurs variables d'environnement.
|
||||
root <directory> # Définit le dossier racine du le site. Par défaut : valeur de la directive `root` parente.
|
||||
split_path <delim...> # Définit les sous-chaînes pour diviser l'URI en deux parties. La première sous-chaîne correspondante sera utilisée pour séparer le "path info" du chemin. La première partie est suffixée avec la sous-chaîne correspondante et sera considérée comme le nom réel de la ressource (script CGI). La seconde partie sera définie comme PATH_INFO pour utilisation par le script. Par défaut : `.php`
|
||||
resolve_root_symlink false # Désactive la résolution du répertoire `root` vers sa valeur réelle en évaluant un lien symbolique, s'il existe (activé par défaut).
|
||||
env <key> <value> # Définit une variable d'environnement supplémentaire avec la valeur donnée. Peut être spécifié plusieurs fois pour plusieurs variables d'environnement.
|
||||
file_server off # Désactive la directive file_server intégrée.
|
||||
worker { # Crée un worker spécifique à ce serveur. Peut être spécifié plusieurs fois pour plusieurs workers.
|
||||
file <path> # Définit le chemin vers le script worker, peut être relatif à la racine du php_server
|
||||
num <num> # Définit le nombre de threads PHP à démarrer, par défaut 2x le nombre de CPUs disponibles
|
||||
name <name> # Définit le nom du worker, utilisé dans les journaux et les métriques. Défaut : chemin absolu du fichier du worker. Commence toujours par m# lorsqu'il est défini dans un bloc php_server.
|
||||
watch <path> # Définit le chemin d'accès à surveiller pour les modifications de fichiers. Peut être spécifié plusieurs fois pour plusieurs chemins.
|
||||
env <key> <value> # Définit une variable d'environnement supplémentaire avec la valeur donnée. Peut être spécifié plusieurs fois pour plusieurs variables d'environnement. Les variables d'environnement pour ce worker sont également héritées du parent php_server, mais peuvent être écrasées ici.
|
||||
}
|
||||
worker <other_file> <num> # Peut également utiliser la forme courte comme dans le bloc frankenphp global.
|
||||
}
|
||||
```
|
||||
|
||||
### Surveillance des modifications de fichier
|
||||
|
||||
Vu que les workers ne démarrent votre application qu'une seule fois et la gardent en mémoire, toute modification
|
||||
apportée à vos fichiers PHP ne sera pas répercutée immédiatement.
|
||||
|
||||
Les workers peuvent être redémarrés en cas de changement de fichier via la directive `watch`.
|
||||
Ceci est utile pour les environnements de développement.
|
||||
|
||||
```caddyfile
|
||||
{
|
||||
frankenphp {
|
||||
worker {
|
||||
file /path/to/app/public/worker.php
|
||||
watch
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Si le répertoire `watch` n'est pas précisé, il se rabattra sur `./**/*.{php,yaml,yml,twig,env}`,
|
||||
qui surveille tous les fichiers `.php`, `.yaml`, `.yml`, `.twig` et `.env` dans le répertoire et les sous-répertoires
|
||||
où le processus FrankenPHP a été lancé. Vous pouvez également spécifier un ou plusieurs répertoires via une commande
|
||||
[motif de nom de fichier shell](https://pkg.go.dev/path/filepath#Match) :
|
||||
|
||||
```caddyfile
|
||||
{
|
||||
frankenphp {
|
||||
worker {
|
||||
file /path/to/app/public/worker.php
|
||||
watch /path/to/app # surveille tous les fichiers dans tous les sous-répertoires de /path/to/app
|
||||
watch /path/to/app/*.php # surveille les fichiers se terminant par .php dans /path/to/app
|
||||
watch /path/to/app/**/*.php # surveille les fichiers PHP dans /path/to/app et les sous-répertoires
|
||||
watch /path/to/app/**/*.{php,twig} # surveille les fichiers PHP et Twig dans /path/to/app et les sous-répertoires
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- Le motif `**` signifie une surveillance récursive.
|
||||
- Les répertoires peuvent également être relatifs (depuis l'endroit où le processus FrankenPHP est démarré).
|
||||
- Si vous avez défini plusieurs workers, ils seront tous redémarrés lorsqu'un fichier est modifié.
|
||||
- Méfiez-vous des fichiers créés au moment de l'exécution (comme les logs) car ils peuvent provoquer des redémarrages intempestifs du worker.
|
||||
|
||||
La surveillance des fichiers est basé sur [e-dant/watcher](https://github.com/e-dant/watcher).
|
||||
|
||||
### Full Duplex (HTTP/1)
|
||||
|
||||
Lors de l'utilisation de HTTP/1.x, il peut être souhaitable d'activer le mode full-duplex pour permettre l'écriture d'une réponse avant que le corps entier
|
||||
n'ait été lu. (par exemple : WebSocket, événements envoyés par le serveur, etc.)
|
||||
|
||||
Il s'agit d'une configuration optionnelle qui doit être ajoutée aux options globales dans le fichier `Caddyfile` :
|
||||
|
||||
```caddyfile
|
||||
{
|
||||
servers {
|
||||
enable_full_duplex
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> [!CAUTION]
|
||||
>
|
||||
> L'activation de cette option peut entraîner un blocage (deadlock) des anciens clients HTTP/1.x qui ne supportent pas le full-duplex.
|
||||
> Cela peut aussi être configuré en utilisant la variable d'environnement `CADDY_GLOBAL_OPTIONS` :
|
||||
|
||||
```sh
|
||||
CADDY_GLOBAL_OPTIONS="servers {
|
||||
enable_full_duplex
|
||||
}"
|
||||
```
|
||||
|
||||
Vous trouverez plus d'informations sur ce paramètre dans la [documentation Caddy](https://caddyserver.com/docs/caddyfile/options#enable-full-duplex).
|
||||
|
||||
## Variables d'environnement
|
||||
|
||||
Les variables d'environnement suivantes peuvent être utilisées pour insérer des directives Caddy dans le `Caddyfile` sans le modifier :
|
||||
|
||||
* `SERVER_NAME` : change [les adresses sur lesquelles écouter](https://caddyserver.com/docs/caddyfile/concepts#addresses), les noms d'hôte fournis seront également utilisés pour le certificat TLS généré
|
||||
* `CADDY_GLOBAL_OPTIONS` : injecte [des options globales](https://caddyserver.com/docs/caddyfile/options)
|
||||
* `FRANKENPHP_CONFIG` : insère la configuration sous la directive `frankenphp`
|
||||
- `SERVER_NAME` : change [les adresses sur lesquelles écouter](https://caddyserver.com/docs/caddyfile/concepts#addresses), les noms d'hôte fournis seront également utilisés pour le certificat TLS généré
|
||||
- `CADDY_GLOBAL_OPTIONS` : injecte [des options globales](https://caddyserver.com/docs/caddyfile/options)
|
||||
- `FRANKENPHP_CONFIG` : insère la configuration sous la directive `frankenphp`
|
||||
|
||||
Comme pour les SAPI FPM et CLI, les variables d'environnement ne sont exposées par défaut dans la superglobale `$_SERVER`.
|
||||
|
||||
@@ -146,9 +244,27 @@ La valeur `S` de [la directive `variables_order` de PHP](https://www.php.net/man
|
||||
|
||||
## Configuration PHP
|
||||
|
||||
Pour charger [des fichiers de configuration PHP supplémentaires](https://www.php.net/manual/fr/configuration.file.php#configuration.file.scan), la variable d'environnement `PHP_INI_SCAN_DIR` peut être utilisée.
|
||||
Pour charger [des fichiers de configuration PHP supplémentaires](https://www.php.net/manual/fr/configuration.file.php#configuration.file.scan),
|
||||
la variable d'environnement `PHP_INI_SCAN_DIR` peut être utilisée.
|
||||
Lorsqu'elle est définie, PHP chargera tous les fichiers avec l'extension `.ini` présents dans les répertoires donnés.
|
||||
|
||||
Vous pouvez également modifier la configuration de PHP en utilisant la directive `php_ini` dans le fichier `Caddyfile` :
|
||||
|
||||
```caddyfile
|
||||
{
|
||||
frankenphp {
|
||||
php_ini memory_limit 256M
|
||||
|
||||
# or
|
||||
|
||||
php_ini {
|
||||
memory_limit 256M
|
||||
max_execution_time 15
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Activer le mode debug
|
||||
|
||||
Lors de l'utilisation de l'image Docker, définissez la variable d'environnement `CADDY_GLOBAL_OPTIONS` sur `debug` pour activer le mode debug :
|
||||
|
||||
@@ -2,7 +2,14 @@
|
||||
|
||||
Les images Docker de [FrankenPHP](https://hub.docker.com/r/dunglas/frankenphp) sont basées sur les [images PHP officielles](https://hub.docker.com/_/php/). Des variantes Debian et Alpine Linux sont fournies pour les architectures populaires. Les variantes Debian sont recommandées.
|
||||
|
||||
Des variantes pour PHP 8.2 et PHP 8.3 sont disponibles. [Parcourir les tags](https://hub.docker.com/r/dunglas/frankenphp/tags).
|
||||
Des variantes pour PHP 8.2, 8.3 et 8.4 sont disponibles. [Parcourir les tags](https://hub.docker.com/r/dunglas/frankenphp/tags).
|
||||
|
||||
Les tags suivent le pattern suivant: `dunglas/frankenphp:<frankenphp-version>-php<php-version>-<os>`
|
||||
|
||||
- `<frankenphp-version>` et `<php-version>` sont repsectivement les numéros de version de FrankenPHP et PHP, allant de majeur (e.g. `1`), mineur (e.g. `1.2`) à des versions correctives (e.g. `1.2.3`).
|
||||
- `<os>` est soit `bookworm` (pour Debian Bookworm) ou `alpine` (pour la dernière version stable d'Alpine).
|
||||
|
||||
[Parcourir les tags](https://hub.docker.com/r/dunglas/frankenphp/tags).
|
||||
|
||||
## Comment utiliser les images
|
||||
|
||||
@@ -42,7 +49,7 @@ RUN install-php-extensions \
|
||||
|
||||
FrankenPHP est construit sur Caddy, et tous les [modules Caddy](https://caddyserver.com/docs/modules/) peuvent être utilisés avec FrankenPHP.
|
||||
|
||||
La manière la plus simple d'installer des modules Caddy personnalisés est d'utiliser [xcaddy](https://github.com/caddyserver/xcaddy):
|
||||
La manière la plus simple d'installer des modules Caddy personnalisés est d'utiliser [xcaddy](https://github.com/caddyserver/xcaddy) :
|
||||
|
||||
```dockerfile
|
||||
FROM dunglas/frankenphp:builder AS builder
|
||||
@@ -58,8 +65,8 @@ RUN CGO_ENABLED=1 \
|
||||
CGO_LDFLAGS="$(php-config --ldflags) $(php-config --libs)" \
|
||||
xcaddy build \
|
||||
--output /usr/local/bin/frankenphp \
|
||||
--with github.com/dunglas/frankenphp=./ \
|
||||
--with github.com/dunglas/frankenphp/caddy=./caddy/ \
|
||||
--with frankenphp.dev=./ \
|
||||
--with frankenphp.dev/caddy=./caddy/ \
|
||||
--with github.com/dunglas/caddy-cbrotli \
|
||||
# Mercure et Vulcain sont inclus dans la construction officielle, mais n'hésitez pas à les retirer
|
||||
--with github.com/dunglas/mercure/caddy \
|
||||
@@ -142,11 +149,11 @@ Voici un exemple de `Dockerfile` le permettant :
|
||||
```dockerfile
|
||||
FROM dunglas/frankenphp
|
||||
|
||||
ARG USER=www-data
|
||||
ARG USER=appuser
|
||||
|
||||
RUN \
|
||||
# Utilisez "adduser -D ${USER}" pour les distributions basées sur Alpine
|
||||
useradd -D ${USER}; \
|
||||
useradd ${USER}; \
|
||||
# Ajouter la capacité supplémentaire de se lier aux ports 80 et 443
|
||||
setcap CAP_NET_BIND_SERVICE=+eip /usr/local/bin/frankenphp; \
|
||||
# Donner l'accès en écriture à /data/caddy et /config/caddy
|
||||
@@ -165,14 +172,14 @@ Si vous exposez FrankenPHP sur un port non privilégié (à partir de 1024), il
|
||||
```dockerfile
|
||||
FROM dunglas/frankenphp
|
||||
|
||||
ARG USER=www-data
|
||||
ARG USER=appuser
|
||||
|
||||
RUN
|
||||
RUN \
|
||||
# Utiliser "adduser -D ${USER}" pour les distros basées sur Alpine
|
||||
useradd -D ${USER};
|
||||
# Supprimer la capacité par défaut
|
||||
useradd ${USER}; \
|
||||
# Supprimer la capacité par défaut \
|
||||
setcap -r /usr/local/bin/frankenphp; \
|
||||
# Donner un accès en écriture à /data/caddy et /config/caddy
|
||||
# Donner un accès en écriture à /data/caddy et /config/caddy \
|
||||
chown -R ${USER}:${USER} /data/caddy && chown -R ${USER}:${USER} /config/caddy
|
||||
|
||||
USER ${USER}
|
||||
@@ -185,8 +192,8 @@ Exemple `:8000`
|
||||
|
||||
Les images Docker sont construites :
|
||||
|
||||
* lorsqu'une nouvelle version est taguée
|
||||
* tous les jours à 4h UTC, si de nouvelles versions des images officielles PHP sont disponibles
|
||||
- lorsqu'une nouvelle version est taguée
|
||||
- tous les jours à 4h UTC, si de nouvelles versions des images officielles PHP sont disponibles
|
||||
|
||||
## Versions de développement
|
||||
|
||||
|
||||
@@ -6,16 +6,18 @@ Grâce à cette fonctionnalité, les applications PHP peuvent être distribuées
|
||||
|
||||
Pour en savoir plus sur cette fonctionnalité, consultez [la présentation faite par Kévin à la SymfonyCon 2023](https://dunglas.dev/2023/12/php-and-symfony-apps-as-standalone-binaries/).
|
||||
|
||||
Pour embarquer des applications Laravel, [lisez ce point spécifique de la documentation](laravel.md#les-applications-laravel-en-tant-que-binaires-autonomes).
|
||||
|
||||
## Préparer votre application
|
||||
|
||||
Avant de créer le binaire autonome, assurez-vous que votre application est prête à être intégrée.
|
||||
|
||||
Vous devrez probablement :
|
||||
|
||||
* Installer les dépendances de production de l'application
|
||||
* Dumper l'autoloader
|
||||
* Activer le mode production de votre application (si disponible)
|
||||
* Supprimer les fichiers inutiles tels que `.git` ou les tests pour réduire la taille de votre binaire final
|
||||
- Installer les dépendances de production de l'application
|
||||
- Dumper l'autoloader
|
||||
- Activer le mode production de votre application (si disponible)
|
||||
- Supprimer les fichiers inutiles tels que `.git` ou les tests pour réduire la taille de votre binaire final
|
||||
|
||||
Par exemple, pour une application Symfony, lancez les commandes suivantes :
|
||||
|
||||
@@ -29,7 +31,8 @@ cd $TMPDIR/my-prepared-app
|
||||
echo APP_ENV=prod > .env.local
|
||||
echo APP_DEBUG=0 >> .env.local
|
||||
|
||||
# Supprimer les tests
|
||||
# Supprimer les tests et autres fichiers inutiles pour économiser de l'espace
|
||||
# Alternativement, ajoutez ces fichiers avec l'attribut export-ignore dans votre fichier .gitattributes
|
||||
rm -Rf tests/
|
||||
|
||||
# Installer les dépendances
|
||||
@@ -52,36 +55,34 @@ La manière la plus simple de créer un binaire Linux est d'utiliser le builder
|
||||
|
||||
1. Créez un fichier nommé `static-build.Dockerfile` dans le répertoire de votre application préparée :
|
||||
|
||||
```dockerfile
|
||||
FROM --platform=linux/amd64 dunglas/frankenphp:static-builder
|
||||
```dockerfile
|
||||
FROM --platform=linux/amd64 dunglas/frankenphp:static-builder
|
||||
|
||||
# Copy your app
|
||||
WORKDIR /go/src/app/dist/app
|
||||
COPY . .
|
||||
# Copy your app
|
||||
WORKDIR /go/src/app/dist/app
|
||||
COPY . .
|
||||
|
||||
# Build the static binary, be sure to select only the PHP extensions you want
|
||||
WORKDIR /go/src/app/
|
||||
RUN EMBED=dist/app/ \
|
||||
PHP_EXTENSIONS=ctype,iconv,pdo_sqlite \
|
||||
./build-static.sh
|
||||
```
|
||||
# Build the static binary, be sure to select only the PHP extensions you want
|
||||
WORKDIR /go/src/app/
|
||||
RUN EMBED=dist/app/ ./build-static.sh
|
||||
```
|
||||
|
||||
> [!CAUTION]
|
||||
>
|
||||
> Certains fichiers `.dockerignore` (par exemple celui fourni par défaut par [Symfony Docker](https://github.com/dunglas/symfony-docker/blob/main/.dockerignore))
|
||||
> empêchent la copie du dossier `vendor/` et des fichiers `.env`. Assurez-vous d'ajuster ou de supprimer le fichier `.dockerignore` avant le build.
|
||||
> [!CAUTION]
|
||||
>
|
||||
> Certains fichiers `.dockerignore` (par exemple celui fourni par défaut par [Symfony Docker](https://github.com/dunglas/symfony-docker/blob/main/.dockerignore))
|
||||
> empêchent la copie du dossier `vendor/` et des fichiers `.env`. Assurez-vous d'ajuster ou de supprimer le fichier `.dockerignore` avant le build.
|
||||
|
||||
2. Construisez:
|
||||
|
||||
```console
|
||||
docker build -t static-app -f static-build.Dockerfile .
|
||||
```
|
||||
```console
|
||||
docker build -t static-app -f static-build.Dockerfile .
|
||||
```
|
||||
|
||||
3. Extrayez le binaire :
|
||||
|
||||
```console
|
||||
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
|
||||
```
|
||||
```console
|
||||
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
|
||||
```
|
||||
|
||||
Le binaire généré sera nommé `my-app` dans le répertoire courant.
|
||||
|
||||
@@ -92,9 +93,7 @@ Si vous ne souhaitez pas utiliser Docker, ou souhaitez construire un binaire mac
|
||||
```console
|
||||
git clone https://github.com/dunglas/frankenphp
|
||||
cd frankenphp
|
||||
EMBED=/path/to/your/app \
|
||||
PHP_EXTENSIONS=ctype,iconv,pdo_sqlite \
|
||||
./build-static.sh
|
||||
EMBED=/path/to/your/app ./build-static.sh
|
||||
```
|
||||
|
||||
Le binaire obtenu est le fichier nommé `frankenphp-<os>-<arch>` dans le répertoire `dist/`.
|
||||
@@ -127,6 +126,19 @@ Vous pouvez également exécuter les scripts CLI PHP incorporés dans votre bina
|
||||
./my-app php-cli bin/console
|
||||
```
|
||||
|
||||
## Extensions PHP
|
||||
|
||||
Par défaut, le script construira les extensions requises par le fichier `composer.json` de votre projet, s'il y en a.
|
||||
Si le fichier `composer.json` n'existe pas, les extensions par défaut sont construites, comme documenté dans [Créer un binaire statique](static.md).
|
||||
|
||||
Pour personnaliser les extensions, utilisez la variable d'environnement `PHP_EXTENSIONS`.
|
||||
|
||||
```console
|
||||
EMBED=/path/to/your/app \
|
||||
PHP_EXTENSIONS=ctype,iconv,pdo_sqlite \
|
||||
./build-static.sh
|
||||
```
|
||||
|
||||
## Personnaliser la compilation
|
||||
|
||||
[Consultez la documentation sur la compilation statique](static.md) pour voir comment personnaliser le binaire (extensions, version PHP...).
|
||||
|
||||
@@ -1,41 +1,11 @@
|
||||
# Problèmes Connus
|
||||
|
||||
## Fibres
|
||||
|
||||
Appeller de fonctions et mots clefs PHP qui eux-mêmes appellent [cgo](https://go.dev/blog/cgo) dans des [Fibres](https://www.php.net/manual/fr/language.fibers.php) est connu pour provoquer des plantages.
|
||||
|
||||
Ce problème est [en cours de correction par le projet Go](https://github.com/golang/go/issues/62130).
|
||||
|
||||
En attendant, une solution consiste à ne pas utiliser de mots clefs (comme `echo`) et de fonctions (comme `header()`) qui délèguent à Go depuis l'intérieur de fibres.
|
||||
|
||||
Ce code risque de planter car il utilise `echo` dans une fibre :
|
||||
|
||||
```php
|
||||
$fiber = new Fiber(function() {
|
||||
echo 'Dans la fibre'.PHP_EOL;
|
||||
echo 'Toujours dedans'.PHP_EOL;
|
||||
});
|
||||
$fiber->start();
|
||||
```
|
||||
|
||||
A la place, retournez la valeur de la Fibre et utilisez-la à l'extérieur :
|
||||
|
||||
```php
|
||||
$fiber = new Fiber(function() {
|
||||
Fiber::suspend('Dans la fibre'.PHP_EOL));
|
||||
Fiber::suspend('Toujours dedans'.PHP_EOL));
|
||||
});
|
||||
echo $fiber->start();
|
||||
echo $fiber->resume();
|
||||
$fiber->resume();
|
||||
```
|
||||
|
||||
## Extensions PHP non prises en charge
|
||||
|
||||
Les extensions suivantes sont connues pour ne pas être compatibles avec FrankenPHP :
|
||||
|
||||
| Nom | Raison | Alternatives |
|
||||
|-------------------------------------------------------------------------------------------------------------|-----------------|----------------------------------------------------------------------------------------------------------------------|
|
||||
| ----------------------------------------------------------------------------------------------------------- | --------------- | -------------------------------------------------------------------------------------------------------------------- |
|
||||
| [imap](https://www.php.net/manual/en/imap.installation.php) | Non thread-safe | [javanile/php-imap2](https://github.com/javanile/php-imap2), [webklex/php-imap](https://github.com/Webklex/php-imap) |
|
||||
| [newrelic](https://docs.newrelic.com/docs/apm/agents/php-agent/getting-started/introduction-new-relic-php/) | Non thread-safe | - |
|
||||
|
||||
@@ -44,7 +14,7 @@ Les extensions suivantes sont connues pour ne pas être compatibles avec Franken
|
||||
Les extensions suivantes ont des bugs connus ou des comportements inattendus lorsqu'elles sont utilisées avec FrankenPHP :
|
||||
|
||||
| Nom | Problème |
|
||||
|---------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| ------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| [ext-openssl](https://www.php.net/manual/fr/book.openssl.php) | Lors de l'utilisation d'une version statique de FrankenPHP (construite avec la libc musl), l'extension OpenSSL peut planter sous de fortes charges. Une solution consiste à utiliser une version liée dynamiquement (comme celle utilisée dans les images Docker). Ce bogue est [suivi par PHP](https://github.com/php/php-src/issues/13648). |
|
||||
|
||||
## get_browser
|
||||
@@ -58,9 +28,9 @@ Le binaire autonome et les images docker basées sur Alpine (`dunglas/frankenphp
|
||||
## Utilisation de `https://127.0.0.1` avec Docker
|
||||
|
||||
Par défaut, FrankenPHP génère un certificat TLS pour `localhost`.
|
||||
C'est l'option est la plus simple et est recommandée pour le développement local.
|
||||
C'est l'option la plus simple et recommandée pour le développement local.
|
||||
|
||||
Si vous voulez vraiment utiliser `127.0.0.1` comme hôte, il est possible de configure FrankenPHP pour générer un certificat pour cela en définissant le nom du serveur à `127.0.0.1`.
|
||||
Si vous voulez vraiment utiliser `127.0.0.1` comme hôte, il est possible de configurer FrankenPHP pour générer un certificat pour cela en définissant le nom du serveur à `127.0.0.1`.
|
||||
|
||||
Malheureusement, cela ne suffit pas lors de l'utilisation de Docker à cause de [son système de gestion des réseaux](https://docs.docker.com/network/).
|
||||
Vous obtiendrez une erreur TLS similaire à `curl: (35) LibreSSL/3.3.6: error:1404B438:SSL routines:ST_CONNECT:tlsv1 alert internal error`.
|
||||
@@ -77,7 +47,7 @@ docker run \
|
||||
|
||||
Le pilote de réseau "hôte" n'est pas pris en charge sur Mac et Windows. Sur ces plateformes, vous devrez deviner l'adresse IP du conteneur et l'inclure dans les noms de serveur.
|
||||
|
||||
Exécutez la commande `docker network inspect bridge` et inpectez la clef `Containers` pour identifier la dernière adresse IP attribuée sous la clef `IPv4Address`, puis incrémentez-la de un. Si aucun conteneur n'est en cours d'exécution, la première adresse IP attribuée est généralement `172.17.0.2`.
|
||||
Exécutez la commande `docker network inspect bridge` et inpectez la clef `Containers` pour identifier la dernière adresse IP attribuée sous la clef `IPv4Address`, puis incrémentez-la d'un. Si aucun conteneur n'est en cours d'exécution, la première adresse IP attribuée est généralement `172.17.0.2`.
|
||||
|
||||
Ensuite, incluez ceci dans la variable d'environnement `SERVER_NAME` :
|
||||
|
||||
@@ -105,3 +75,69 @@ docker run \
|
||||
-p 80:80 -p 443:443 -p 443:443/udp \
|
||||
dunglas/frankenphp
|
||||
```
|
||||
|
||||
## Scripts Composer Faisant Références à `@php`
|
||||
|
||||
Les [scripts Composer](https://getcomposer.org/doc/articles/scripts.md) peuvent vouloir exécuter un binaire PHP pour certaines tâches, par exemple dans [un projet Laravel](laravel.md) pour exécuter `@php artisan package:discover --ansi`. Cela [echoue actuellement](https://github.com/dunglas/frankenphp/issues/483#issuecomment-1899890915) pour deux raisons :
|
||||
|
||||
- Composer ne sait pas comment appeler le binaire FrankenPHP ;
|
||||
- Composer peut ajouter des paramètres PHP en utilisant le paramètre `-d` dans la commande, ce que FrankenPHP ne supporte pas encore.
|
||||
|
||||
Comme solution de contournement, nous pouvons créer un script shell dans `/usr/local/bin/php` qui supprime les paramètres non supportés et appelle ensuite FrankenPHP :
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
args=("$@")
|
||||
index=0
|
||||
for i in "$@"
|
||||
do
|
||||
if [ "$i" == "-d" ]; then
|
||||
unset 'args[$index]'
|
||||
unset 'args[$index+1]'
|
||||
fi
|
||||
index=$((index+1))
|
||||
done
|
||||
|
||||
/usr/local/bin/frankenphp php-cli ${args[@]}
|
||||
```
|
||||
|
||||
Ensuite, mettez la variable d'environnement `PHP_BINARY` au chemin de notre script `php` et lancez Composer :
|
||||
|
||||
```console
|
||||
export PHP_BINARY=/usr/local/bin/php
|
||||
composer install
|
||||
```
|
||||
|
||||
## Résolution des problèmes TLS/SSL avec les binaires statiques
|
||||
|
||||
Lorsque vous utilisez les binaires statiques, vous pouvez rencontrer les erreurs suivantes liées à TLS, par exemple lors de l'envoi de courriels utilisant STARTTLS :
|
||||
|
||||
```text
|
||||
Unable to connect with STARTTLS: stream_socket_enable_crypto(): SSL operation failed with code 5. OpenSSL Error messages:
|
||||
error:80000002:system library::No such file or directory
|
||||
error:80000002:system library::No such file or directory
|
||||
error:80000002:system library::No such file or directory
|
||||
error:0A000086:SSL routines::certificate verify failed
|
||||
```
|
||||
|
||||
Comme le binaire statique ne contient pas de certificats TLS, vous devez indiquer à OpenSSL l'installation de vos certificats CA locaux.
|
||||
|
||||
Inspectez la sortie de [`openssl_get_cert_locations()`](https://www.php.net/manual/en/function.openssl-get-cert-locations.php),
|
||||
pour trouver l'endroit où les certificats CA doivent être installés et stockez-les à cet endroit.
|
||||
|
||||
> [!WARNING]
|
||||
>
|
||||
> Les contextes Web et CLI peuvent avoir des paramètres différents.
|
||||
> Assurez-vous d'exécuter `openssl_get_cert_locations()` dans le bon contexte.
|
||||
|
||||
[Les certificats CA extraits de Mozilla peuvent être téléchargés sur le site curl](https://curl.se/docs/caextract.html).
|
||||
|
||||
Alternativement, de nombreuses distributions, y compris Debian, Ubuntu, et Alpine fournissent des paquets nommés `ca-certificates` qui contiennent ces certificats.
|
||||
|
||||
Il est également possible d'utiliser `SSL_CERT_FILE` et `SSL_CERT_DIR` pour indiquer à OpenSSL où chercher les certificats CA :
|
||||
|
||||
```console
|
||||
# Définir les variables d'environnement des certificats TLS
|
||||
export SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt
|
||||
export SSL_CERT_DIR=/etc/ssl/certs
|
||||
```
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
|
||||
## Docker
|
||||
|
||||
Déployer une application web [Laravel](https://laravel.com) avec FrankenPHP est très facile.
|
||||
Il suffit de monter le projet dans le répertoire `/app` de l'image Docker officielle.
|
||||
Déployer une application web [Laravel](https://laravel.com) avec FrankenPHP est très facile. Il suffit de monter le projet dans le répertoire `/app` de l'image Docker officielle.
|
||||
|
||||
Exécutez cette commande depuis le répertoire principal de votre application Laravel :
|
||||
|
||||
@@ -20,21 +19,23 @@ Vous pouvez également exécuter vos projets Laravel avec FrankenPHP depuis votr
|
||||
1. [Téléchargez le binaire correspondant à votre système](README.md#binaire-autonome)
|
||||
2. Ajoutez la configuration suivante dans un fichier nommé `Caddyfile` placé dans le répertoire racine de votre projet Laravel :
|
||||
|
||||
```caddyfile
|
||||
{
|
||||
frankenphp
|
||||
}
|
||||
```caddyfile
|
||||
{
|
||||
frankenphp
|
||||
}
|
||||
|
||||
# Le nom de domaine de votre serveur
|
||||
localhost {
|
||||
# Définir le répertoire racine sur le dossier public/
|
||||
root * public/
|
||||
# Autoriser la compression (optionnel)
|
||||
encode zstd br gzip
|
||||
# Exécuter les scripts PHP du dossier public/ et servir les assets
|
||||
php_server
|
||||
}
|
||||
```
|
||||
# Le nom de domaine de votre serveur
|
||||
localhost {
|
||||
# Définir le répertoire racine sur le dossier public/
|
||||
root public/
|
||||
# Autoriser la compression (optionnel)
|
||||
encode zstd br gzip
|
||||
# Exécuter les scripts PHP du dossier public/ et servir les assets
|
||||
php_server {
|
||||
try_files {path} index.php
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
3. Démarrez FrankenPHP depuis le répertoire racine de votre projet Laravel : `frankenphp run`
|
||||
|
||||
@@ -60,19 +61,124 @@ php artisan octane:frankenphp
|
||||
|
||||
La commande `octane:frankenphp` peut prendre les options suivantes :
|
||||
|
||||
* `--host` : L'adresse IP à laquelle le serveur doit se lier (par défaut : `127.0.0.1`)
|
||||
* `--port` : Le port sur lequel le serveur doit être disponible (par défaut : `8000`)
|
||||
* `--admin-port` : Le port sur lequel le serveur administratif doit être disponible (par défaut : `2019`)
|
||||
* `--workers` : Le nombre de workers qui doivent être disponibles pour traiter les requêtes (par défaut : `auto`)
|
||||
* `--max-requests` : Le nombre de requêtes à traiter avant de recharger le serveur (par défaut : `500`)
|
||||
* `--caddyfile` : Le chemin vers le fichier `Caddyfile` de FrankenPHP
|
||||
* `--https` : Activer HTTPS, HTTP/2, et HTTP/3, et générer automatiquement et renouveler les certificats
|
||||
* `--http-redirect` : Activer la redirection HTTP vers HTTPS (uniquement activé si --https est passé)
|
||||
* `--watch` : Recharger automatiquement le serveur lorsque l'application est modifiée
|
||||
* `--poll` : Utiliser le sondage du système de fichiers pendant la surveillance pour surveiller les fichiers sur un réseau
|
||||
* `--log-level` : Enregistrer les messages au niveau de journalisation spécifié ou au-dessus, en utilisant le logger natif de Caddy
|
||||
- `--host` : L'adresse IP à laquelle le serveur doit se lier (par défaut : `127.0.0.1`)
|
||||
- `--port` : Le port sur lequel le serveur doit être disponible (par défaut : `8000`)
|
||||
- `--admin-port` : Le port sur lequel le serveur administratif doit être disponible (par défaut : `2019`)
|
||||
- `--workers` : Le nombre de workers qui doivent être disponibles pour traiter les requêtes (par défaut : `auto`)
|
||||
- `--max-requests` : Le nombre de requêtes à traiter avant de recharger le serveur (par défaut : `500`)
|
||||
- `--caddyfile` : Le chemin vers le fichier `Caddyfile` de FrankenPHP
|
||||
- `--https` : Activer HTTPS, HTTP/2, et HTTP/3, et générer automatiquement et renouveler les certificats
|
||||
- `--http-redirect` : Activer la redirection HTTP vers HTTPS (uniquement activé si --https est passé)
|
||||
- `--watch` : Recharger automatiquement le serveur lorsque l'application est modifiée
|
||||
- `--poll` : Utiliser le sondage du système de fichiers pendant la surveillance pour surveiller les fichiers sur un réseau
|
||||
- `--log-level` : Enregistrer les messages au niveau de journalisation spécifié ou au-dessus, en utilisant le logger natif de Caddy
|
||||
|
||||
> [!TIP]
|
||||
> Pour obtenir des logs structurés en JSON logs (utile quand vous utilisez des solutions d'analyse de logs), passez explicitement l'otpion `--log-level`.
|
||||
> Pour obtenir des logs structurés en JSON logs (utile quand vous utilisez des solutions d'analyse de logs), passez explicitement l'option `--log-level`.
|
||||
|
||||
En savoir plus sur Laravel Octane [dans sa documentation officielle](https://laravel.com/docs/octane).
|
||||
|
||||
## Les Applications Laravel En Tant Que Binaires Autonomes
|
||||
|
||||
En utilisant la [fonctionnalité d'intégration d'applications de FrankenPHP](embed.md), il est possible de distribuer
|
||||
les applications Laravel sous forme de binaires autonomes.
|
||||
|
||||
Suivez ces étapes pour empaqueter votre application Laravel en tant que binaire autonome pour Linux :
|
||||
|
||||
1. Créez un fichier nommé `static-build.Dockerfile` dans le dépôt de votre application :
|
||||
|
||||
```dockerfile
|
||||
FROM --platform=linux/amd64 dunglas/frankenphp:static-builder
|
||||
|
||||
# Copiez votre application
|
||||
WORKDIR /go/src/app/dist/app
|
||||
COPY . .
|
||||
|
||||
# Supprimez les tests et autres fichiers inutiles pour gagner de la place
|
||||
# Alternativement, ajoutez ces fichiers à un fichier .dockerignore
|
||||
RUN rm -Rf tests/
|
||||
|
||||
# Copiez le fichier .env
|
||||
RUN cp .env.example .env
|
||||
# Modifier APP_ENV et APP_DEBUG pour qu'ils soient prêts pour la production
|
||||
RUN sed -i'' -e 's/^APP_ENV=.*/APP_ENV=production/' -e 's/^APP_DEBUG=.*/APP_DEBUG=false/' .env
|
||||
|
||||
# Apportez d'autres modifications à votre fichier .env si nécessaire
|
||||
|
||||
# Installez les dépendances
|
||||
RUN composer install --ignore-platform-reqs --no-dev -a
|
||||
|
||||
# Construire le binaire statique
|
||||
WORKDIR /go/src/app/
|
||||
RUN EMBED=dist/app/ ./build-static.sh
|
||||
```
|
||||
|
||||
> [!CAUTION]
|
||||
>
|
||||
> Certains fichiers `.dockerignore` ignoreront le répertoire `vendor/`
|
||||
> et les fichiers `.env`. Assurez-vous d'ajuster ou de supprimer le fichier `.dockerignore` avant la construction.
|
||||
|
||||
2. Build:
|
||||
|
||||
```console
|
||||
docker build -t static-laravel-app -f static-build.Dockerfile .
|
||||
```
|
||||
|
||||
3. Extraire le binaire
|
||||
|
||||
```console
|
||||
docker cp $(docker create --name static-laravel-app-tmp static-laravel-app):/go/src/app/dist/frankenphp-linux-x86_64 frankenphp ; docker rm static-laravel-app-tmp
|
||||
```
|
||||
|
||||
4. Remplir les caches :
|
||||
|
||||
```console
|
||||
frankenphp php-cli artisan optimize
|
||||
```
|
||||
|
||||
5. Exécutez les migrations de base de données (s'il y en a) :
|
||||
|
||||
```console
|
||||
frankenphp php-cli artisan migrate
|
||||
```
|
||||
|
||||
6. Générer la clé secrète de l'application :
|
||||
|
||||
```console
|
||||
frankenphp php-cli artisan key:generate
|
||||
```
|
||||
|
||||
7. Démarrez le serveur:
|
||||
|
||||
```console
|
||||
frankenphp php-server
|
||||
```
|
||||
|
||||
Votre application est maintenant prête !
|
||||
|
||||
Pour en savoir plus sur les options disponibles et sur la construction de binaires pour d'autres systèmes d'exploitation,
|
||||
consultez la documentation [Applications PHP en tant que binaires autonomes](embed.md).
|
||||
|
||||
### Changer le chemin de stockage
|
||||
|
||||
Par défaut, Laravel stocke les fichiers téléchargés, les caches, les logs, etc. dans le répertoire `storage/` de l'application.
|
||||
Ceci n'est pas adapté aux applications embarquées, car chaque nouvelle version sera extraite dans un répertoire temporaire différent.
|
||||
|
||||
Définissez la variable d'environnement `LARAVEL_STORAGE_PATH` (par exemple, dans votre fichier `.env`) ou appelez la méthode `Illuminate\Foundation\Application::useStoragePath()` pour utiliser un répertoire en dehors du répertoire temporaire.
|
||||
|
||||
### Exécuter Octane avec des binaires autonomes
|
||||
|
||||
Il est même possible d'empaqueter les applications Laravel Octane en tant que binaires autonomes !
|
||||
|
||||
Pour ce faire, [installez Octane correctement](#laravel-octane) et suivez les étapes décrites dans [la section précédente](#les-applications-laravel-en-tant-que-binaires-autonomes).
|
||||
|
||||
Ensuite, pour démarrer FrankenPHP en mode worker via Octane, exécutez :
|
||||
|
||||
```console
|
||||
PATH="$PWD:$PATH" frankenphp php-cli artisan octane:frankenphp
|
||||
```
|
||||
|
||||
> [!CAUTION]
|
||||
>
|
||||
> Pour que la commande fonctionne, le binaire autonome **doit** être nommé `frankenphp`
|
||||
> car Octane a besoin d'un programme nommé `frankenphp` disponible dans le chemin
|
||||
|
||||
17
docs/fr/metrics.md
Normal file
17
docs/fr/metrics.md
Normal file
@@ -0,0 +1,17 @@
|
||||
# Métriques
|
||||
|
||||
Lorsque les [métriques Caddy](https://caddyserver.com/docs/metrics) sont activées, FrankenPHP expose les métriques suivantes :
|
||||
|
||||
- `frankenphp_total_threads` : Le nombre total de threads PHP.
|
||||
- `frankenphp_busy_threads` : Le nombre de threads PHP en cours de traitement d'une requête (les workers en cours d'exécution consomment toujours un thread).
|
||||
- `frankenphp_queue_depth` : Le nombre de requêtes régulières en file d'attente
|
||||
- `frankenphp_total_workers{worker=« [nom_du_worker] »}` : Le nombre total de workers.
|
||||
- `frankenphp_busy_workers{worker=« [nom_du_worker] »}` : Le nombre de workers qui traitent actuellement une requête.
|
||||
- `frankenphp_worker_request_time{worker=« [nom_du_worker] »}` : Le temps passé à traiter les requêtes par tous les workers.
|
||||
- `frankenphp_worker_request_count{worker=« [nom_du_worker] »}` : Le nombre de requêtes traitées par tous les workers.
|
||||
- `frankenphp_ready_workers{worker=« [nom_du_worker] »}` : Le nombre de workers qui ont appelé `frankenphp_handle_request` au moins une fois.
|
||||
- `frankenphp_worker_crashes{worker=« [nom_du_worker] »}` : Le nombre de fois où un worker s'est arrêté de manière inattendue.
|
||||
- `frankenphp_worker_restarts{worker=« [nom_du_worker] »}` : Le nombre de fois où un worker a été délibérément redémarré.
|
||||
- `frankenphp_worker_queue_depth{worker=« [nom_du_worker] »}` : Le nombre de requêtes en file d'attente.
|
||||
|
||||
Pour les métriques de worker, le placeholder `[nom_du_worker]` est remplacé par le nom du worker dans le Caddyfile, sinon le chemin absolu du fichier du worker sera utilisé.
|
||||
@@ -5,7 +5,7 @@ Cependant, il est possible d'améliorer considérablement les performances en ut
|
||||
|
||||
## Nombre de threads et de workers
|
||||
|
||||
Par défaut, FrankenPHP démarre 2 fois plus de threads et de workers (en mode worker) que le nombre de CPU disponibles.
|
||||
Par défaut, FrankenPHP démarre deux fois plus de threads et de workers (en mode worker) que le nombre de CPU disponibles.
|
||||
|
||||
Les valeurs appropriées dépendent fortement de la manière dont votre application est écrite, de ce qu'elle fait et de votre matériel.
|
||||
Nous recommandons vivement de modifier ces valeurs.
|
||||
@@ -16,6 +16,16 @@ Pour trouver les bonnes valeurs, il est souhaitable d'effectuer des tests de cha
|
||||
Pour configurer le nombre de threads, utilisez l'option `num_threads` des directives `php_server` et `php`.
|
||||
Pour changer le nombre de travailleurs, utilisez l'option `num` de la section `worker` de la directive `frankenphp`.
|
||||
|
||||
### `max_threads`
|
||||
|
||||
Bien qu'il soit toujours préférable de savoir exactement à quoi ressemblera votre trafic, les applications réelles
|
||||
ont tendance à être plus imprévisibles. Le paramètre `max_threads` permet à FrankenPHP de créer automatiquement des threads supplémentaires au moment de l'exécution, jusqu'à la limite spécifiée.
|
||||
`max_threads` peut vous aider à déterminer le nombre de threads dont vous avez besoin pour gérer votre trafic et peut rendre le serveur plus résistant aux pics de latence.
|
||||
Si elle est fixée à `auto`, la limite sera estimée en fonction de la valeur de `memory_limit` dans votre `php.ini`. Si ce n'est pas possible,
|
||||
`auto` prendra par défaut 2x `num_threads`. Gardez à l'esprit que `auto` peut fortement sous-estimer le nombre de threads nécessaires.
|
||||
`max_threads` est similaire à [pm.max_children](https://www.php.net/manual/en/install.fpm.configuration.php#pm.max-children) de PHP FPM. La principale différence est que FrankenPHP utilise des threads au lieu de
|
||||
processus et les délègue automatiquement à différents scripts de travail et au `mode classique` selon les besoins.
|
||||
|
||||
## Mode worker
|
||||
|
||||
Activer [le mode worker](worker.md) améliore considérablement les performances,
|
||||
@@ -26,10 +36,10 @@ vous devez créer un script worker et vous assurer que l'application n'a pas de
|
||||
|
||||
Les binaires statiques que nous fournissons, ainsi que la variante Alpine Linux des images Docker officielles, utilisent [la bibliothèque musl](https://musl.libc.org).
|
||||
|
||||
PHP est connu pour être significativement plus lent lorsqu'il utilise cette bibliothèque C alternative au lieu de la bibliothèque GNU traditionnelle,
|
||||
surtout lorsqu'il est compilé en mode ZTS (thread-safe), ce qui est nécessaire pour FrankenPHP.
|
||||
PHP est connu pour être [significativement plus lent](https://gitlab.alpinelinux.org/alpine/aports/-/issues/14381) lorsqu'il utilise cette bibliothèque C alternative au lieu de la bibliothèque GNU traditionnelle,
|
||||
surtout lorsqu'il est compilé en mode ZTS (_thread-safe_), ce qui est nécessaire pour FrankenPHP.
|
||||
|
||||
En outre, certains bogues ne se produisent que lors de l'utilisation de musl.
|
||||
En outre, [certains bogues ne se produisent que lors de l'utilisation de musl](https://github.com/php/php-src/issues?q=sort%3Aupdated-desc+is%3Aissue+is%3Aopen+label%3ABug+musl).
|
||||
|
||||
Dans les environnements de production, nous recommandons fortement d'utiliser la glibc.
|
||||
|
||||
@@ -53,7 +63,7 @@ Pour plus de détails, [la page de documentation Go dédiée à ce sujet](https:
|
||||
|
||||
## `file_server`
|
||||
|
||||
Par défaut, la directive `php_server` met automatiquement en place un serveur de fichiers pour
|
||||
Par défaut, la directive `php_server` met automatiquement en place un serveur de fichiers
|
||||
pour servir les fichiers statiques (assets) stockés dans le répertoire racine.
|
||||
|
||||
Cette fonctionnalité est pratique, mais a un coût.
|
||||
@@ -65,16 +75,54 @@ php_server {
|
||||
}
|
||||
```
|
||||
|
||||
## *Placeholders*
|
||||
## `try_files`
|
||||
|
||||
Vous pouvez utiliser des [*placeholders*](https://caddyserver.com/docs/conventions#placeholders) dans les directives `root` et `env`.
|
||||
En plus des fichiers statiques et des fichiers PHP, `php_server` essaiera aussi de servir les fichiers d'index
|
||||
et d'index de répertoire de votre application (`/path/` -> `/path/index.php`). Si vous n'avez pas besoin des index de répertoires,
|
||||
vous pouvez les désactiver en définissant explicitement `try_files` comme ceci :
|
||||
|
||||
```caddyfile
|
||||
php_server {
|
||||
try_files {path} index.php
|
||||
root /root/to/your/app # l'ajout explicite de la racine ici permet une meilleure mise en cache
|
||||
}
|
||||
```
|
||||
|
||||
Cela permet de réduire considérablement le nombre d'opérations inutiles sur les fichiers.
|
||||
|
||||
Une approche alternative avec 0 opérations inutiles sur le système de fichiers serait d'utiliser la directive `php`
|
||||
et de diviser les fichiers de PHP par chemin. Cette approche fonctionne bien si votre application entière est servie par un seul fichier d'entrée.
|
||||
Un exemple de [configuration](config.md#configuration-du-caddyfile) qui sert des fichiers statiques derrière un dossier `/assets` pourrait ressembler à ceci :
|
||||
|
||||
```caddyfile
|
||||
route {
|
||||
@assets {
|
||||
path /assets/*
|
||||
}
|
||||
|
||||
# tout ce qui se trouve derrière /assets est géré par le serveur de fichiers
|
||||
file_server @assets {
|
||||
root /root/to/your/app
|
||||
}
|
||||
|
||||
# tout ce qui n'est pas dans /assets est géré par votre index ou votre fichier PHP worker
|
||||
rewrite index.php
|
||||
php {
|
||||
root /root/to/your/app # l'ajout explicite de la racine ici permet une meilleure mise en cache
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## _Placeholders_
|
||||
|
||||
Vous pouvez utiliser des [_placeholders_](https://caddyserver.com/docs/conventions#placeholders) dans les directives `root` et `env`.
|
||||
Cependant, cela empêche la mise en cache de ces valeurs et a un coût important en termes de performances.
|
||||
|
||||
Si possible, évitez les *placeholders* dans ces directives.
|
||||
Si possible, évitez les _placeholders_ dans ces directives.
|
||||
|
||||
## `resolve_root_symlink`
|
||||
|
||||
Par défaut, si le *document root* est un lien symbolique, il est automatiquement résolu par FrankenPHP (c'est nécessaire pour le bon fonctionnement de PHP).
|
||||
Par défaut, si le _document root_ est un lien symbolique, il est automatiquement résolu par FrankenPHP (c'est nécessaire pour le bon fonctionnement de PHP).
|
||||
Si la racine du document n'est pas un lien symbolique, vous pouvez désactiver cette fonctionnalité.
|
||||
|
||||
```caddyfile
|
||||
@@ -83,12 +131,12 @@ php_server {
|
||||
}
|
||||
```
|
||||
|
||||
Cela améliorera les performances si la directive `root` contient des [*placeholders*](https://caddyserver.com/docs/conventions#placeholders).
|
||||
Cela améliorera les performances si la directive `root` contient des [_placeholders_](https://caddyserver.com/docs/conventions#placeholders).
|
||||
Le gain sera négligeable dans les autres cas.
|
||||
|
||||
## Journaux
|
||||
|
||||
La journalisation est évidemment très utile, mais, par définition, elle nécessite des opérations d'*I/O* et des allocations de mémoire,
|
||||
La journalisation est évidemment très utile, mais, par définition, elle nécessite des opérations d'_I/O_ et des allocations de mémoire,
|
||||
ce qui réduit considérablement les performances.
|
||||
Assurez-vous de [définir le niveau de journalisation](https://caddyserver.com/docs/caddyfile/options#log) correctement,
|
||||
et de ne journaliser que ce qui est nécessaire.
|
||||
@@ -100,10 +148,10 @@ Toutes les optimisations de performances habituelles liées à PHP s'appliquent
|
||||
|
||||
En particulier :
|
||||
|
||||
* vérifiez que [OPcache](https://www.php.net/manual/en/book.opcache.php) est installé, activé et correctement configuré
|
||||
* activez [les optimisations de l'autoloader de Composer](https://getcomposer.org/doc/articles/autoloader-optimization.md)
|
||||
* assurez-vous que le cache `realpath` est suffisamment grand pour les besoins de votre application
|
||||
* utilisez le [pré-chargement](https://www.php.net/manual/en/opcache.preloading.php)
|
||||
- vérifiez que [OPcache](https://www.php.net/manual/en/book.opcache.php) est installé, activé et correctement configuré
|
||||
- activez [les optimisations de l'autoloader de Composer](https://getcomposer.org/doc/articles/autoloader-optimization.md)
|
||||
- assurez-vous que le cache `realpath` est suffisamment grand pour les besoins de votre application
|
||||
- utilisez le [pré-chargement](https://www.php.net/manual/en/opcache.preloading.php)
|
||||
|
||||
Pour plus de détails, lisez [l'entrée de la documentation dédiée de Symfony](https://symfony.com/doc/current/performance.html)
|
||||
(la plupart des conseils sont utiles même si vous n'utilisez pas Symfony).
|
||||
|
||||
@@ -4,20 +4,49 @@ Au lieu d'utiliser une installation locale de la bibliothèque PHP, il est possi
|
||||
|
||||
Avec cette méthode, un binaire portable unique contiendra l'interpréteur PHP, le serveur web Caddy et FrankenPHP !
|
||||
|
||||
Les exécutables natifs entièrement statiques ne nécessitent aucune dépendance et peuvent même être exécutés sur une [image Docker `scratch`](https://docs.docker.com/build/building/base-images/#create-a-minimal-base-image-using-scratch).
|
||||
Cependant, ils ne peuvent pas charger les extensions dynamiques de PHP (comme Xdebug) et ont quelques limitations parce qu'ils utilisent la librairie musl.
|
||||
|
||||
La plupart des binaires statiques ne nécessitent que la `glibc` et peuvent charger des extensions dynamiques.
|
||||
|
||||
Lorsque c'est possible, nous recommandons d'utiliser des binaires statiques basés sur la glibc.
|
||||
|
||||
FrankenPHP permet également [d'embarquer l'application PHP dans le binaire statique](embed.md).
|
||||
|
||||
## Linux
|
||||
|
||||
Nous fournissons une image Docker pour créer un binaire statique pour Linux :
|
||||
Nous fournissons des images Docker pour créer des binaires statiques pour Linux :
|
||||
|
||||
### Build entièrement statique, basé sur musl
|
||||
|
||||
Pour un binaire entièrement statique qui fonctionne sur n'importe quelle distribution Linux sans dépendances,
|
||||
mais qui ne prend pas en charge le chargement dynamique des extensions :
|
||||
|
||||
```console
|
||||
docker buildx bake --load static-builder
|
||||
docker cp $(docker create --name static-builder dunglas/frankenphp:static-builder):/go/src/app/dist/frankenphp-linux-$(uname -m) frankenphp ; docker rm static-builder
|
||||
docker buildx bake --load static-builder-musl
|
||||
docker cp $(docker create --name static-builder-musl dunglas/frankenphp:static-builder-musl):/go/src/app/dist/frankenphp-linux-$(uname -m) frankenphp ; docker rm static-builder-musl
|
||||
```
|
||||
|
||||
Le binaire statique résultant est nommé `frankenphp`, et il est disponible dans le répertoire courant.
|
||||
Pour améliorer les performances dans les scénarios fortement concurrents, envisagez d'utiliser l'allocateur [mimalloc](https://github.com/microsoft/mimalloc).
|
||||
|
||||
Si vous souhaitez construire le binaire statique sans Docker, regardez les instructions pour macOS, qui fonctionnent également pour Linux.
|
||||
```console
|
||||
docker buildx bake --load --set static-builder-musl.args.MIMALLOC=1 static-builder-musl
|
||||
```
|
||||
|
||||
### Construction principalement statique (avec prise en charge des extensions dynamiques), basé sur la glibc
|
||||
|
||||
Pour un binaire qui supporte le chargement dynamique des extensions PHP tout en ayant les extensions sélectionnées compilées statiquement :
|
||||
|
||||
```console
|
||||
docker buildx bake --load static-builder-gnu
|
||||
docker cp $(docker create --name static-builder-gnu dunglas/frankenphp:static-builder-gnu):/go/src/app/dist/frankenphp-linux-$(uname -m) frankenphp ; docker rm static-builder-gnu
|
||||
```
|
||||
|
||||
Ce binaire supporte toutes les versions 2.17 et supérieures de la glibc mais ne fonctionne pas sur les systèmes basés sur musl (comme Alpine Linux).
|
||||
|
||||
Le binaire résultant, principalement statique (à l'exception de `glibc`), est nommé `frankenphp` et est disponible dans le répertoire courant.
|
||||
|
||||
Si vous voulez construire le binaire statique sans Docker, jetez un coup d'œil aux instructions pour macOS, qui fonctionnent aussi pour Linux.
|
||||
|
||||
### Extensions personnalisées
|
||||
|
||||
@@ -28,7 +57,7 @@ Pour réduire la taille du binaire et diminuer la surface d'attaque, vous pouvez
|
||||
Par exemple, exécutez la commande suivante pour ne construire que l'extension `opcache` :
|
||||
|
||||
```console
|
||||
docker buildx bake --load --set static-builder.args.PHP_EXTENSIONS=opcache,pdo_sqlite static-builder
|
||||
docker buildx bake --load --set static-builder-musl.args.PHP_EXTENSIONS=opcache,pdo_sqlite static-builder-musl
|
||||
# ...
|
||||
```
|
||||
|
||||
@@ -37,11 +66,29 @@ Pour ajouter des bibliothèques permettant des fonctionnalités supplémentaires
|
||||
```console
|
||||
docker buildx bake \
|
||||
--load \
|
||||
--set static-builder.args.PHP_EXTENSIONS=gd \
|
||||
--set static-builder.args.PHP_EXTENSION_LIBS=libjpeg,libwebp \
|
||||
static-builder
|
||||
--set static-builder-musl.args.PHP_EXTENSIONS=gd \
|
||||
--set static-builder-musl.args.PHP_EXTENSION_LIBS=libjpeg,libwebp \
|
||||
static-builder-musl
|
||||
```
|
||||
|
||||
### Modules supplémentaires de Caddy
|
||||
|
||||
Pour ajouter des modules Caddy supplémentaires ou passer d'autres arguments à [xcaddy](https://github.com/caddyserver/xcaddy), utilisez l'argument Docker `XCADDY_ARGS` :
|
||||
|
||||
```console
|
||||
docker buildx bake \
|
||||
--load \
|
||||
--set static-builder-musl.args.XCADDY_ARGS="--with github.com/darkweak/souin/plugins/caddy --with github.com/dunglas/caddy-cbrotli --with github.com/dunglas/mercure/caddy --with github.com/dunglas/vulcain/caddy" \
|
||||
static-builder-musl
|
||||
```
|
||||
|
||||
Dans cet exemple, nous ajoutons le module de cache HTTP [Souin](https://souin.io) pour Caddy ainsi que les modules [cbrotli](https://github.com/dunglas/caddy-cbrotli), [Mercure](https://mercure.rocks) et [Vulcain](https://vulcain.rocks).
|
||||
|
||||
> [!TIP]
|
||||
>
|
||||
> Les modules cbrotli, Mercure et Vulcain sont inclus par défaut si `XCADDY_ARGS` est vide ou n'est pas défini.
|
||||
> Si vous personnalisez la valeur de `XCADDY_ARGS`, vous devez les inclure explicitement si vous voulez qu'ils soient inclus.
|
||||
|
||||
Voir aussi comment [personnaliser la construction](#personnalisation-de-la-construction)
|
||||
|
||||
### Jeton GitHub
|
||||
@@ -49,7 +96,7 @@ Voir aussi comment [personnaliser la construction](#personnalisation-de-la-const
|
||||
Si vous atteignez la limite de taux d'appels de l'API GitHub, définissez un jeton d'accès personnel GitHub dans une variable d'environnement nommée `GITHUB_TOKEN` :
|
||||
|
||||
```console
|
||||
GITHUB_TOKEN="xxx" docker --load buildx bake static-builder
|
||||
GITHUB_TOKEN="xxx" docker --load buildx bake static-builder-musl
|
||||
# ...
|
||||
```
|
||||
|
||||
@@ -69,13 +116,46 @@ Note : ce script fonctionne également sur Linux (et probablement sur d'autres U
|
||||
|
||||
Les variables d'environnement suivantes peuvent être transmises à `docker build` et au script `build-static.sh` pour personnaliser la construction statique :
|
||||
|
||||
* `FRANKENPHP_VERSION` : la version de FrankenPHP à utiliser
|
||||
* `PHP_VERSION` : la version de PHP à utiliser
|
||||
* `PHP_EXTENSIONS` : les extensions PHP à construire ([liste des extensions prises en charge](https://static-php.dev/en/guide/extensions.html))
|
||||
* `PHP_EXTENSION_LIBS` : bibliothèques supplémentaires à construire qui ajoutent des fonctionnalités aux extensions
|
||||
* `EMBED` : chemin de l'application PHP à intégrer dans le binaire
|
||||
* `CLEAN` : lorsque défini, `libphp` et toutes ses dépendances sont construites à partir de zéro (pas de cache)
|
||||
* `DEBUG_SYMBOLS` : lorsque défini, les symboles de débogage ne seront pas supprimés et seront ajoutés dans le binaire
|
||||
* `NO_COMPRESS`: ne pas compresser le binaire avec UPX
|
||||
* `MIMALLOC`: (expérimental, Linux seulement) remplace l'allocateur mallocng de musl par [mimalloc](https://github.com/microsoft/mimalloc) pour des performances améliorées
|
||||
* `RELEASE` : (uniquement pour les mainteneurs) lorsque défini, le binaire résultant sera uploadé sur GitHub
|
||||
- `FRANKENPHP_VERSION` : la version de FrankenPHP à utiliser
|
||||
- `PHP_VERSION` : la version de PHP à utiliser
|
||||
- `PHP_EXTENSIONS` : les extensions PHP à construire ([liste des extensions prises en charge](https://static-php.dev/en/guide/extensions.html))
|
||||
- `PHP_EXTENSION_LIBS` : bibliothèques supplémentaires à construire qui ajoutent des fonctionnalités aux extensions
|
||||
- `XCADDY_ARGS` : arguments à passer à [xcaddy](https://github.com/caddyserver/xcaddy), par exemple pour ajouter des modules Caddy supplémentaires
|
||||
- `EMBED` : chemin de l'application PHP à intégrer dans le binaire
|
||||
- `CLEAN` : lorsque défini, `libphp` et toutes ses dépendances sont construites à partir de zéro (pas de cache)
|
||||
- `DEBUG_SYMBOLS` : lorsque défini, les symboles de débogage ne seront pas supprimés et seront ajoutés dans le binaire
|
||||
- `NO_COMPRESS`: ne pas compresser le binaire avec UPX
|
||||
- `MIMALLOC`: (expérimental, Linux seulement) remplace l'allocateur mallocng de musl par [mimalloc](https://github.com/microsoft/mimalloc) pour des performances améliorées
|
||||
- `RELEASE` : (uniquement pour les mainteneurs) lorsque défini, le binaire résultant sera uploadé sur GitHub
|
||||
|
||||
## Extensions
|
||||
|
||||
Avec la glibc ou les binaires basés sur macOS, vous pouvez charger des extensions PHP dynamiquement. Cependant, ces extensions devront être compilées avec le support ZTS.
|
||||
Comme la plupart des gestionnaires de paquets ne proposent pas de versions ZTS de leurs extensions, vous devrez les compiler vous-même.
|
||||
|
||||
Pour cela, vous pouvez construire et exécuter le conteneur Docker `static-builder-gnu`, vous y connecter à distance et compiler les extensions avec `./configure --with-php-config=/go/src/app/dist/static-php-cli/buildroot/bin/php-config`.
|
||||
|
||||
Exemple d'étapes pour [l'extension Xdebug](https://xdebug.org) :
|
||||
|
||||
```console
|
||||
docker build -t gnu-ext -f static-builder-gnu.Dockerfile --build-arg FRANKENPHP_VERSION=1.0 .
|
||||
docker create --name static-builder-gnu -it gnu-ext /bin/sh
|
||||
docker start static-builder-gnu
|
||||
docker exec -it static-builder-gnu /bin/sh
|
||||
cd /go/src/app/dist/static-php-cli/buildroot/bin
|
||||
git clone https://github.com/xdebug/xdebug.git && cd xdebug
|
||||
source scl_source enable devtoolset-10
|
||||
../phpize
|
||||
./configure --with-php-config=/go/src/app/dist/static-php-cli/buildroot/bin/php-config
|
||||
make
|
||||
exit
|
||||
docker cp static-builder-gnu:/go/src/app/dist/static-php-cli/buildroot/bin/xdebug/modules/xdebug.so xdebug-zts.so
|
||||
docker cp static-builder-gnu:/go/src/app/dist/frankenphp-linux-$(uname -m) ./frankenphp
|
||||
docker stop static-builder-gnu
|
||||
docker rm static-builder-gnu
|
||||
docker rmi gnu-ext
|
||||
```
|
||||
|
||||
Cela aura créé `frankenphp` et `xdebug-zts.so` dans le répertoire courant.
|
||||
Si vous déplacez `xdebug-zts.so` dans votre répertoire d'extension, ajoutez `zend_extension=xdebug-zts.so` à votre php.ini
|
||||
et lancez FrankenPHP, il chargera Xdebug.
|
||||
|
||||
@@ -28,6 +28,13 @@ frankenphp php-server --worker /path/to/your/worker/script.php
|
||||
Si votre application PHP est [intégrée dans le binaire](embed.md), vous pouvez également ajouter un `Caddyfile` personnalisé dans le répertoire racine de l'application.
|
||||
Il sera utilisé automatiquement.
|
||||
|
||||
Il est également possible de [redémarrer le worker en cas de changement de fichier](config.md#surveillance-des-modifications-de-fichier) avec l'option `--watch`.
|
||||
La commande suivante déclenchera un redémarrage si un fichier se terminant par `.php` dans le répertoire `/path/to/your/app/` ou ses sous-répertoires est modifié :
|
||||
|
||||
```console
|
||||
frankenphp php-server --worker /path/to/your/worker/script.php --watch="/path/to/your/app/**/*.php"
|
||||
```
|
||||
|
||||
## Runtime Symfony
|
||||
|
||||
Le mode worker de FrankenPHP est pris en charge par le [Composant Runtime de Symfony](https://symfony.com/doc/current/components/runtime.html).
|
||||
@@ -76,14 +83,17 @@ $handler = static function () use ($myApp) {
|
||||
echo $myApp->handle($_GET, $_POST, $_COOKIE, $_FILES, $_SERVER);
|
||||
};
|
||||
|
||||
for ($nbRequests = 0, $running = true; isset($_SERVER['MAX_REQUESTS']) && ($nbRequests < ((int)$_SERVER['MAX_REQUESTS'])) && $running; ++$nbRequests) {
|
||||
$running = \frankenphp_handle_request($handler);
|
||||
$maxRequests = (int)($_SERVER['MAX_REQUESTS'] ?? 0);
|
||||
for ($nbRequests = 0; !$maxRequests || $nbRequests < $maxRequests; ++$nbRequests) {
|
||||
$keepRunning = \frankenphp_handle_request($handler);
|
||||
|
||||
// Faire quelque chose après l'envoi de la réponse HTTP
|
||||
$myApp->terminate();
|
||||
|
||||
// Exécuter le ramasse-miettes pour réduire les chances qu'il soit déclenché au milieu de la génération d'une page
|
||||
gc_collect_cycles();
|
||||
|
||||
if (!$keepRunning) break;
|
||||
}
|
||||
|
||||
// Nettoyage
|
||||
@@ -118,13 +128,35 @@ Une solution pour utiliser ce type de code en mode worker est de redémarrer le
|
||||
|
||||
Le code du worker précédent permet de configurer un nombre maximal de requêtes à traiter en définissant une variable d'environnement nommée `MAX_REQUESTS`.
|
||||
|
||||
### Redémarrer les workers manuellement
|
||||
|
||||
Bien qu'il soit possible de redémarrer les workers [en cas de changement de fichier](config.md#surveillance-des-modifications-de-fichier),
|
||||
il est également possible de redémarrer tous les workers de manière élégante via l'[API Admin de Caddy](https://caddyserver.com/docs/api).
|
||||
Si l'administration est activée dans votre [Caddyfile](config.md#configuration-du-caddyfile), vous pouvez envoyer un ping
|
||||
à l'endpoint de redémarrage avec une simple requête POST comme celle-ci :
|
||||
|
||||
```console
|
||||
curl -X POST http://localhost:2019/frankenphp/workers/restart
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
>
|
||||
> C'est une fonctionnalité expérimentale et peut être modifiée ou supprimée dans le futur.
|
||||
|
||||
### Worker Failures
|
||||
|
||||
Si un script de worker se plante avec un code de sortie non nul, FrankenPHP le redémarre avec une stratégie de backoff exponentielle.
|
||||
Si le script worker reste en place plus longtemps que le dernier backoff \* 2, FrankenPHP ne pénalisera pas le script et le redémarrera à nouveau.
|
||||
Toutefois, si le script de worker continue d'échouer avec un code de sortie non nul dans un court laps de temps
|
||||
(par exemple, une faute de frappe dans un script), FrankenPHP plantera avec l'erreur : `too many consecutive failures` (trop d'échecs consécutifs).
|
||||
|
||||
## Comportement des superglobales
|
||||
|
||||
[Les superglobales PHP](https://www.php.net/manual/fr/language.variables.superglobals.php) (`$_SERVER`, `$_ENV`, `$_GET`...)
|
||||
se comportent comme suit :
|
||||
|
||||
* avant le premier appel à `frankenphp_handle_request()`, les superglobales contiennent des valeurs liées au script worker lui-même
|
||||
* pendant et après l'appel à `frankenphp_handle_request()`, les superglobales contiennent des valeurs générées à partir de la requête HTTP traitée, chaque appel à `frankenphp_handle_request()` change les valeurs des superglobales
|
||||
- avant le premier appel à `frankenphp_handle_request()`, les superglobales contiennent des valeurs liées au script worker lui-même
|
||||
- pendant et après l'appel à `frankenphp_handle_request()`, les superglobales contiennent des valeurs générées à partir de la requête HTTP traitée, chaque appel à `frankenphp_handle_request()` change les valeurs des superglobales
|
||||
|
||||
Pour accéder aux superglobales du script worker à l'intérieur de la fonction de rappel, vous devez les copier et importer la copie dans le scope de la fonction :
|
||||
|
||||
|
||||
71
docs/fr/x-sendfile.md
Normal file
71
docs/fr/x-sendfile.md
Normal file
@@ -0,0 +1,71 @@
|
||||
# Servir efficacement les gros fichiers statiques (`X-Sendfile`/`X-Accel-Redirect`)
|
||||
|
||||
Habituellement, les fichiers statiques peuvent être servis directement par le serveur web,
|
||||
mais parfois, il est nécessaire d'exécuter du code PHP avant de les envoyer :
|
||||
contrôle d'accès, statistiques, en-têtes HTTP personnalisés...
|
||||
|
||||
Malheureusement, utiliser PHP pour servir de gros fichiers statiques est inefficace comparé à
|
||||
à l'utilisation directe du serveur web (surcharge mémoire, diminution des performances...).
|
||||
|
||||
FrankenPHP permet de déléguer l'envoi des fichiers statiques au serveur web
|
||||
**après** avoir exécuté du code PHP personnalisé.
|
||||
|
||||
Pour ce faire, votre application PHP n'a qu'à définir un en-tête HTTP personnalisé
|
||||
contenant le chemin du fichier à servir. FrankenPHP se chargera du reste.
|
||||
|
||||
Cette fonctionnalité est connue sous le nom de **`X-Sendfile`** pour Apache, et **`X-Accel-Redirect`** pour NGINX.
|
||||
|
||||
Dans les exemples suivants, nous supposons que le "document root" du projet est le répertoire `public/`
|
||||
et que nous voulons utiliser PHP pour servir des fichiers stockés en dehors du dossier `public/`,
|
||||
depuis un répertoire nommé `private-files/`.
|
||||
|
||||
## Configuration
|
||||
|
||||
Tout d'abord, ajoutez la configuration suivante à votre `Caddyfile` pour activer cette fonctionnalité :
|
||||
|
||||
```patch
|
||||
root public/
|
||||
# ...
|
||||
|
||||
+ # Needed for Symfony, Laravel and other projects using the Symfony HttpFoundation component
|
||||
+ request_header X-Sendfile-Type x-accel-redirect
|
||||
+ request_header X-Accel-Mapping ../private-files=/private-files
|
||||
+
|
||||
+ intercept {
|
||||
+ @accel header X-Accel-Redirect *
|
||||
+ handle_response @accel {
|
||||
+ root private-files/
|
||||
+ rewrite * {resp.header.X-Accel-Redirect}
|
||||
+ method * GET
|
||||
+
|
||||
+ # Remove the X-Accel-Redirect header set by PHP for increased security
|
||||
+ header -X-Accel-Redirect
|
||||
+
|
||||
+ file_server
|
||||
+ }
|
||||
+ }
|
||||
|
||||
php_server
|
||||
```
|
||||
|
||||
## PHP simple
|
||||
|
||||
Définissez le chemin relatif du fichier (à partir de `private-files/`) comme valeur de l'en-tête `X-Accel-Redirect` :
|
||||
|
||||
```php
|
||||
header('X-Accel-Redirect: file.txt') ;
|
||||
```
|
||||
|
||||
## Projets utilisant le composant Symfony HttpFoundation (Symfony, Laravel, Drupal...)
|
||||
|
||||
Symfony HttpFoundation [supporte nativement cette fonctionnalité](https://symfony.com/doc/current/components/http_foundation.html#serving-files).
|
||||
Il va automatiquement déterminer la bonne valeur pour l'en-tête `X-Accel-Redirect` et l'ajoutera à la réponse.
|
||||
|
||||
```php
|
||||
use Symfony\Component\HttpFoundation\BinaryFileResponse;
|
||||
|
||||
BinaryFileResponse::trustXSendfileTypeHeader();
|
||||
$response = new BinaryFileResponse(__DIR__.'/../private-files/file.txt');
|
||||
|
||||
// ...
|
||||
```
|
||||
@@ -1,41 +1,11 @@
|
||||
# Known Issues
|
||||
|
||||
## Fibers
|
||||
|
||||
Calling PHP functions and language constructs that themselves call [cgo](https://go.dev/blog/cgo) in [Fibers](https://www.php.net/manual/en/language.fibers.php) is known to cause crashes.
|
||||
|
||||
This issue [is being worked on by the Go project](https://github.com/golang/go/issues/62130).
|
||||
|
||||
In the meantime, one solution is not to use constructs (like `echo`) and functions (like `header()`) that delegate to Go from inside Fibers.
|
||||
|
||||
This code will likely crash because it uses `echo` in the Fiber:
|
||||
|
||||
```php
|
||||
$fiber = new Fiber(function() {
|
||||
echo 'In the Fiber'.PHP_EOL;
|
||||
echo 'Still inside'.PHP_EOL;
|
||||
});
|
||||
$fiber->start();
|
||||
```
|
||||
|
||||
Instead, return the value from the Fiber and use it outside:
|
||||
|
||||
```php
|
||||
$fiber = new Fiber(function() {
|
||||
Fiber::suspend('In the Fiber'.PHP_EOL));
|
||||
Fiber::suspend('Still inside'.PHP_EOL));
|
||||
});
|
||||
echo $fiber->start();
|
||||
echo $fiber->resume();
|
||||
$fiber->resume();
|
||||
```
|
||||
|
||||
## Unsupported PHP Extensions
|
||||
|
||||
The following extensions are known not to be compatible with FrankenPHP:
|
||||
|
||||
| Name | Reason | Alternatives |
|
||||
|-------------------------------------------------------------------------------------------------------------|-----------------|----------------------------------------------------------------------------------------------------------------------|
|
||||
| ----------------------------------------------------------------------------------------------------------- | --------------- | -------------------------------------------------------------------------------------------------------------------- |
|
||||
| [imap](https://www.php.net/manual/en/imap.installation.php) | Not thread-safe | [javanile/php-imap2](https://github.com/javanile/php-imap2), [webklex/php-imap](https://github.com/Webklex/php-imap) |
|
||||
| [newrelic](https://docs.newrelic.com/docs/apm/agents/php-agent/getting-started/introduction-new-relic-php/) | Not thread-safe | - |
|
||||
|
||||
@@ -44,7 +14,7 @@ The following extensions are known not to be compatible with FrankenPHP:
|
||||
The following extensions have known bugs and unexpected behaviors when used with FrankenPHP:
|
||||
|
||||
| Name | Problem |
|
||||
|---------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| ------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| [ext-openssl](https://www.php.net/manual/en/book.openssl.php) | When using a static build of FrankenPHP (built with the musl libc), the OpenSSL extension may crash under heavy loads. A workaround is to use a dynamically linked build (like the one used in Docker images). This bug is [being tracked by PHP](https://github.com/php/php-src/issues/13648). |
|
||||
|
||||
## get_browser
|
||||
@@ -110,8 +80,8 @@ docker run \
|
||||
|
||||
[Composer scripts](https://getcomposer.org/doc/articles/scripts.md) may want to execute a PHP binary for some tasks, e.g. in [a Laravel project](laravel.md) to run `@php artisan package:discover --ansi`. This [currently fails](https://github.com/dunglas/frankenphp/issues/483#issuecomment-1899890915) for two reasons:
|
||||
|
||||
* Composer does not know how to call the FrankenPHP binary;
|
||||
* Composer may add PHP settings using the `-d` flag in the command, which FrankenPHP does not yet support.
|
||||
- Composer does not know how to call the FrankenPHP binary;
|
||||
- Composer may add PHP settings using the `-d` flag in the command, which FrankenPHP does not yet support.
|
||||
|
||||
As a workaround, we can create a shell script in `/usr/local/bin/php` which strips the unsupported parameters and then calls FrankenPHP:
|
||||
|
||||
|
||||
136
docs/laravel.md
136
docs/laravel.md
@@ -16,24 +16,26 @@ And enjoy!
|
||||
|
||||
Alternatively, you can run your Laravel projects with FrankenPHP from your local machine:
|
||||
|
||||
1. [Download the binary corresponding to your system](../README.md#standalone-binary)
|
||||
1. [Download the binary corresponding to your system](../#standalone-binary)
|
||||
2. Add the following configuration to a file named `Caddyfile` in the root directory of your Laravel project:
|
||||
|
||||
```caddyfile
|
||||
{
|
||||
frankenphp
|
||||
}
|
||||
```caddyfile
|
||||
{
|
||||
frankenphp
|
||||
}
|
||||
|
||||
# The domain name of your server
|
||||
localhost {
|
||||
# Set the webroot to the public/ directory
|
||||
root * public/
|
||||
# Enable compression (optional)
|
||||
encode zstd br gzip
|
||||
# Execute PHP files from the public/ directory and serve assets
|
||||
php_server
|
||||
}
|
||||
```
|
||||
# The domain name of your server
|
||||
localhost {
|
||||
# Set the webroot to the public/ directory
|
||||
root public/
|
||||
# Enable compression (optional)
|
||||
encode zstd br gzip
|
||||
# Execute PHP files from the public/ directory and serve assets
|
||||
php_server {
|
||||
try_files {path} index.php
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
3. Start FrankenPHP from the root directory of your Laravel project: `frankenphp run`
|
||||
|
||||
@@ -59,17 +61,17 @@ php artisan octane:frankenphp
|
||||
|
||||
The `octane:frankenphp` command can take the following options:
|
||||
|
||||
* `--host`: The IP address the server should bind to (default: `127.0.0.1`)
|
||||
* `--port`: The port the server should be available on (default: `8000`)
|
||||
* `--admin-port`: The port the admin server should be available on (default: `2019`)
|
||||
* `--workers`: The number of workers that should be available to handle requests (default: `auto`)
|
||||
* `--max-requests`: The number of requests to process before reloading the server (default: `500`)
|
||||
* `--caddyfile`: The path to the FrankenPHP `Caddyfile` file (default: [stubbed `Caddyfile` in Laravel Octane](https://github.com/laravel/octane/blob/2.x/src/Commands/stubs/Caddyfile))
|
||||
* `--https`: Enable HTTPS, HTTP/2, and HTTP/3, and automatically generate and renew certificates
|
||||
* `--http-redirect`: Enable HTTP to HTTPS redirection (only enabled if --https is passed)
|
||||
* `--watch`: Automatically reload the server when the application is modified
|
||||
* `--poll`: Use file system polling while watching in order to watch files over a network
|
||||
* `--log-level`: Log messages at or above the specified log level, using the native Caddy logger
|
||||
- `--host`: The IP address the server should bind to (default: `127.0.0.1`)
|
||||
- `--port`: The port the server should be available on (default: `8000`)
|
||||
- `--admin-port`: The port the admin server should be available on (default: `2019`)
|
||||
- `--workers`: The number of workers that should be available to handle requests (default: `auto`)
|
||||
- `--max-requests`: The number of requests to process before reloading the server (default: `500`)
|
||||
- `--caddyfile`: The path to the FrankenPHP `Caddyfile` file (default: [stubbed `Caddyfile` in Laravel Octane](https://github.com/laravel/octane/blob/2.x/src/Commands/stubs/Caddyfile))
|
||||
- `--https`: Enable HTTPS, HTTP/2, and HTTP/3, and automatically generate and renew certificates
|
||||
- `--http-redirect`: Enable HTTP to HTTPS redirection (only enabled if --https is passed)
|
||||
- `--watch`: Automatically reload the server when the application is modified
|
||||
- `--poll`: Use file system polling while watching in order to watch files over a network
|
||||
- `--log-level`: Log messages at or above the specified log level, using the native Caddy logger
|
||||
|
||||
> [!TIP]
|
||||
> To get structured JSON logs (useful when using log analytics solutions), explicitly the pass `--log-level` option.
|
||||
@@ -85,72 +87,72 @@ Follow these steps to package your Laravel app as a standalone binary for Linux:
|
||||
|
||||
1. Create a file named `static-build.Dockerfile` in the repository of your app:
|
||||
|
||||
```dockerfile
|
||||
FROM --platform=linux/amd64 dunglas/frankenphp:static-builder
|
||||
```dockerfile
|
||||
FROM --platform=linux/amd64 dunglas/frankenphp:static-builder
|
||||
|
||||
# Copy your app
|
||||
WORKDIR /go/src/app/dist/app
|
||||
COPY . .
|
||||
# Copy your app
|
||||
WORKDIR /go/src/app/dist/app
|
||||
COPY . .
|
||||
|
||||
# Remove the tests and other unneeded files to save space
|
||||
# Alternatively, add these files to a .dockerignore file
|
||||
RUN rm -Rf tests/
|
||||
# Remove the tests and other unneeded files to save space
|
||||
# Alternatively, add these files to a .dockerignore file
|
||||
RUN rm -Rf tests/
|
||||
|
||||
# Copy .env file
|
||||
RUN cp .env.example .env
|
||||
# Change APP_ENV and APP_DEBUG to be production ready
|
||||
RUN sed -i'' -e 's/^APP_ENV=.*/APP_ENV=production/' -e 's/^APP_DEBUG=.*/APP_DEBUG=false/' .env
|
||||
# Copy .env file
|
||||
RUN cp .env.example .env
|
||||
# Change APP_ENV and APP_DEBUG to be production ready
|
||||
RUN sed -i'' -e 's/^APP_ENV=.*/APP_ENV=production/' -e 's/^APP_DEBUG=.*/APP_DEBUG=false/' .env
|
||||
|
||||
# Make other changes to your .env file if needed
|
||||
# Make other changes to your .env file if needed
|
||||
|
||||
# Install the dependencies
|
||||
RUN composer install --ignore-platform-reqs --no-dev -a
|
||||
# Install the dependencies
|
||||
RUN composer install --ignore-platform-reqs --no-dev -a
|
||||
|
||||
# Build the static binary
|
||||
WORKDIR /go/src/app/
|
||||
RUN EMBED=dist/app/ ./build-static.sh
|
||||
```
|
||||
# Build the static binary
|
||||
WORKDIR /go/src/app/
|
||||
RUN EMBED=dist/app/ ./build-static.sh
|
||||
```
|
||||
|
||||
> [!CAUTION]
|
||||
>
|
||||
> Some `.dockerignore` files
|
||||
> will ignore the `vendor/` directory and `.env` files. Be sure to adjust or remove the `.dockerignore` file before the build.
|
||||
> [!CAUTION]
|
||||
>
|
||||
> Some `.dockerignore` files
|
||||
> will ignore the `vendor/` directory and `.env` files. Be sure to adjust or remove the `.dockerignore` file before the build.
|
||||
|
||||
2. Build:
|
||||
|
||||
```console
|
||||
docker build -t static-laravel-app -f static-build.Dockerfile .
|
||||
```
|
||||
```console
|
||||
docker build -t static-laravel-app -f static-build.Dockerfile .
|
||||
```
|
||||
|
||||
3. Extract the binary:
|
||||
|
||||
```console
|
||||
docker cp $(docker create --name static-laravel-app-tmp static-laravel-app):/go/src/app/dist/frankenphp-linux-x86_64 frankenphp ; docker rm static-laravel-app-tmp
|
||||
```
|
||||
```console
|
||||
docker cp $(docker create --name static-laravel-app-tmp static-laravel-app):/go/src/app/dist/frankenphp-linux-x86_64 frankenphp ; docker rm static-laravel-app-tmp
|
||||
```
|
||||
|
||||
4. Populate caches:
|
||||
|
||||
```console
|
||||
frankenphp php-cli artisan optimize
|
||||
```
|
||||
```console
|
||||
frankenphp php-cli artisan optimize
|
||||
```
|
||||
|
||||
5. Run database migrations (if any):
|
||||
|
||||
```console
|
||||
frankenphp php-cli artisan migrate
|
||||
````
|
||||
```console
|
||||
frankenphp php-cli artisan migrate
|
||||
```
|
||||
|
||||
6. Generate app's secret key:
|
||||
|
||||
```console
|
||||
frankenphp php-cli artisan key:generate
|
||||
```
|
||||
```console
|
||||
frankenphp php-cli artisan key:generate
|
||||
```
|
||||
|
||||
7. Start the server:
|
||||
|
||||
```console
|
||||
frankenphp php-server
|
||||
```
|
||||
```console
|
||||
frankenphp php-server
|
||||
```
|
||||
|
||||
Your app is now ready!
|
||||
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.5 MiB After Width: | Height: | Size: 1009 KiB |
@@ -1,12 +1,15 @@
|
||||
# Real-time
|
||||
|
||||
FrankenPHP comes with a built-in [Mercure](https://mercure.rocks) hub!
|
||||
Mercure allows to push events in real-time to all the connected devices: they will receive a JavaScript event instantly.
|
||||
Mercure allows you to push real-time events to all the connected devices: they will receive a JavaScript event instantly.
|
||||
|
||||
No JS library or SDK required!
|
||||
No JS library or SDK is required!
|
||||
|
||||

|
||||
|
||||
To enable the Mercure hub, update the `Caddyfile` as described [on Mercure's site](https://mercure.rocks/docs/hub/config).
|
||||
|
||||
To push Mercure updates from your code, we recommend the [Symfony Mercure Component](https://symfony.com/components/Mercure) (you don't need the Symfony full stack framework to use it).
|
||||
The path of the Mercure hub is `/.well-known/mercure`.
|
||||
When running FrankenPHP inside Docker, the full send URL would look like `http://php/.well-known/mercure` (with `php` being the container's name running FrankenPHP).
|
||||
|
||||
To push Mercure updates from your code, we recommend the [Symfony Mercure Component](https://symfony.com/components/Mercure) (you don't need the Symfony full-stack framework to use it).
|
||||
|
||||
@@ -2,14 +2,16 @@
|
||||
|
||||
When [Caddy metrics](https://caddyserver.com/docs/metrics) are enabled, FrankenPHP exposes the following metrics:
|
||||
|
||||
* `frankenphp_[worker]_total_workers`: The total number of workers.
|
||||
* `frankenphp_[worker]_busy_workers`: The number of workers currently processing a request.
|
||||
* `frankenphp_[worker]_worker_request_time`: The time spent processing requests by all workers.
|
||||
* `frankenphp_[worker]_worker_request_count`: The number of requests processed by all workers.
|
||||
* `frankenphp_[worker]_ready_workers`: The number of workers that have called `frankenphp_handle_request` at least once.
|
||||
* `frankenphp_[worker]_worker_crashes`: The number of times a worker has unexpectedly terminated.
|
||||
* `frankenphp_[worker]_worker_restarts`: The number of times a worker has been deliberately restarted.
|
||||
* `frankenphp_total_threads`: The total number of PHP threads.
|
||||
* `frankenphp_busy_threads`: The number of PHP threads currently processing a request (running workers always consume a thread).
|
||||
- `frankenphp_total_threads`: The total number of PHP threads.
|
||||
- `frankenphp_busy_threads`: The number of PHP threads currently processing a request (running workers always consume a thread).
|
||||
- `frankenphp_queue_depth`: The number of regular queued requests
|
||||
- `frankenphp_total_workers{worker="[worker_name]"}`: The total number of workers.
|
||||
- `frankenphp_busy_workers{worker="[worker_name]"}`: The number of workers currently processing a request.
|
||||
- `frankenphp_worker_request_time{worker="[worker_name]"}`: The time spent processing requests by all workers.
|
||||
- `frankenphp_worker_request_count{worker="[worker_name]"}`: The number of requests processed by all workers.
|
||||
- `frankenphp_ready_workers{worker="[worker_name]"}`: The number of workers that have called `frankenphp_handle_request` at least once.
|
||||
- `frankenphp_worker_crashes{worker="[worker_name]"}`: The number of times a worker has unexpectedly terminated.
|
||||
- `frankenphp_worker_restarts{worker="[worker_name]"}`: The number of times a worker has been deliberately restarted.
|
||||
- `frankenphp_worker_queue_depth{worker="[worker_name]"}`: The number of queued requests.
|
||||
|
||||
For worker metrics, the `[worker]` placeholder is replaced by the worker script path in the Caddyfile.
|
||||
For worker metrics, the `[worker_name]` placeholder is replaced by the worker name in the Caddyfile, otherwise absolute path of worker file will be used.
|
||||
|
||||
@@ -8,7 +8,7 @@ However, it is possible to substantially improve performance using an appropriat
|
||||
By default, FrankenPHP starts 2 times more threads and workers (in worker mode) than the available numbers of CPU.
|
||||
|
||||
The appropriate values depend heavily on how your application is written, what it does and your hardware.
|
||||
We strongly recommend changing these values.
|
||||
We strongly recommend changing these values. For best system stability, it is recommended to have `num_threads` x `memory_limit` < `available_memory`.
|
||||
|
||||
To find the right values, it's best to run load tests simulating real traffic.
|
||||
[k6](https://k6.io) and [Gatling](https://gatling.io) are good tools for this.
|
||||
@@ -16,6 +16,16 @@ To find the right values, it's best to run load tests simulating real traffic.
|
||||
To configure the number of threads, use the `num_threads` option of the `php_server` and `php` directives.
|
||||
To change the number of workers, use the `num` option of the `worker` section of the `frankenphp` directive.
|
||||
|
||||
### `max_threads`
|
||||
|
||||
While it's always better to know exactly what your traffic will look like, real-life applications tend to be more
|
||||
unpredictable. The `max_threads` [configuration](config.md#caddyfile-config) allows FrankenPHP to automatically spawn additional threads at runtime up to the specified limit.
|
||||
`max_threads` can help you figure out how many threads you need to handle your traffic and can make the server more resilient to latency spikes.
|
||||
If set to `auto`, the limit will be estimated based on the `memory_limit` in your `php.ini`. If not able to do so,
|
||||
`auto` will instead default to 2x `num_threads`. Keep in mind that `auto` might strongly underestimate the number of threads needed.
|
||||
`max_threads` is similar to PHP FPM's [pm.max_children](https://www.php.net/manual/en/install.fpm.configuration.php#pm.max-children). The main difference is that FrankenPHP uses threads instead of
|
||||
processes and automatically delegates them across different worker scripts and 'classic mode' as needed.
|
||||
|
||||
## Worker Mode
|
||||
|
||||
Enabling [the worker mode](worker.md) dramatically improves performance,
|
||||
@@ -24,19 +34,18 @@ you need to create a worker script and to be sure that the app is not leaking me
|
||||
|
||||
## Don't Use musl
|
||||
|
||||
The static binaries we provide and the Alpine Linux variant of the official Docker images
|
||||
are using [the musl libc](https://musl.libc.org).
|
||||
The Alpine Linux variant of the official Docker images and the default binaries we provide are using [the musl libc](https://musl.libc.org).
|
||||
|
||||
PHP is known to be significantly slower when using this alternative C library instead of the traditional GNU library,
|
||||
especially when compiled in ZTS mode (thread-safe), which is required for FrankenPHP.
|
||||
PHP is known to be [slower](https://gitlab.alpinelinux.org/alpine/aports/-/issues/14381) when using this alternative C library instead of the traditional GNU library,
|
||||
especially when compiled in ZTS mode (thread-safe), which is required for FrankenPHP. The difference can be significant in a heavily threaded environment.
|
||||
|
||||
Also, some bugs also only happen when using musl.
|
||||
Also, [some bugs only happen when using musl](https://github.com/php/php-src/issues?q=sort%3Aupdated-desc+is%3Aissue+is%3Aopen+label%3ABug+musl).
|
||||
|
||||
In production environements, we strongly recommend to use the glibc.
|
||||
In production environments, we recommend using FrankenPHP linked against glibc.
|
||||
|
||||
This can be achieved by using the Debian Docker images (the default) and [by compiling FrankenPHP from sources](compile.md).
|
||||
This can be achieved by using the Debian Docker images (the default), downloading the -gnu suffix binary from our [Releases](https://github.com/dunglas/frankenphp/releases), or by [compiling FrankenPHP from sources](compile.md).
|
||||
|
||||
Alternatively, we provide static binaries compiled with [the mimalloc allocator](https://github.com/microsoft/mimalloc), which makes FrankenPHP+musl faster (but still slower than FrankenPHP+glibc).
|
||||
Alternatively, we provide static musl binaries compiled with [the mimalloc allocator](https://github.com/microsoft/mimalloc), which alleviates the problems in threaded scenarios.
|
||||
|
||||
## Go Runtime Configuration
|
||||
|
||||
@@ -66,6 +75,44 @@ php_server {
|
||||
}
|
||||
```
|
||||
|
||||
## `try_files`
|
||||
|
||||
Besides static files and PHP files, `php_server` will also try to serve your application's index
|
||||
and directory index files (`/path/` -> `/path/index.php`). If you don't need directory indices,
|
||||
you can disable them by explicitly defining `try_files` like this:
|
||||
|
||||
```caddyfile
|
||||
php_server {
|
||||
try_files {path} index.php
|
||||
root /root/to/your/app # explicitly adding the root here allows for better caching
|
||||
}
|
||||
```
|
||||
|
||||
This can significantly reduce the number of unnecessary file operations.
|
||||
|
||||
An alternate approach with 0 unnecessary file system operations would be to instead use the `php` directive and split
|
||||
files from PHP by path. This approach works well if your entire application is served by one entry file.
|
||||
An example [configuration](config.md#caddyfile-config) that serves static files behind an `/assets` folder could look like this:
|
||||
|
||||
```caddyfile
|
||||
route {
|
||||
@assets {
|
||||
path /assets/*
|
||||
}
|
||||
|
||||
# everything behind /assets is handled by the file server
|
||||
file_server @assets {
|
||||
root /root/to/your/app
|
||||
}
|
||||
|
||||
# everything that is not in /assets is handled by your index or worker PHP file
|
||||
rewrite index.php
|
||||
php {
|
||||
root /root/to/your/app # explicitly adding the root here allows for better caching
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Placeholders
|
||||
|
||||
You can use [placeholders](https://caddyserver.com/docs/conventions#placeholders) in the `root` and `env` directives.
|
||||
@@ -101,10 +148,10 @@ All usual PHP-related performance optimizations apply with FrankenPHP.
|
||||
|
||||
In particular:
|
||||
|
||||
* check that [OPcache](https://www.php.net/manual/en/book.opcache.php) is installed, enabled and properly configured
|
||||
* enable [Composer autoloader optimizations](https://getcomposer.org/doc/articles/autoloader-optimization.md)
|
||||
* ensure that the `realpath` cache is big enough for the needs of your application
|
||||
* use [preloading](https://www.php.net/manual/en/opcache.preloading.php)
|
||||
- check that [OPcache](https://www.php.net/manual/en/book.opcache.php) is installed, enabled and properly configured
|
||||
- enable [Composer autoloader optimizations](https://getcomposer.org/doc/articles/autoloader-optimization.md)
|
||||
- ensure that the `realpath` cache is big enough for the needs of your application
|
||||
- use [preloading](https://www.php.net/manual/en/opcache.preloading.php)
|
||||
|
||||
For more details, read [the dedicated Symfony documentation entry](https://symfony.com/doc/current/performance.html)
|
||||
(most tips are useful even if you don't use Symfony).
|
||||
|
||||
@@ -31,7 +31,7 @@ Refer to "[Building Custom Docker Image](docker.md)" for more details and option
|
||||
and to learn how to customize the configuration, install PHP extensions and Caddy modules.
|
||||
|
||||
If your project uses Composer,
|
||||
be sure to include it in the Docker image and to install your depedencies.
|
||||
be sure to include it in the Docker image and to install your dependencies.
|
||||
|
||||
Then, add a `compose.yaml` file:
|
||||
|
||||
@@ -124,7 +124,7 @@ git clone git@github.com:<username>/<project-name>.git
|
||||
Go into the directory containing your project (`<project-name>`), and start the app in production mode:
|
||||
|
||||
```console
|
||||
docker compose up -d --wait
|
||||
docker compose up --wait
|
||||
```
|
||||
|
||||
Your server is up and running, and an HTTPS certificate has been automatically generated for you.
|
||||
|
||||
218
docs/ru/CONTRIBUTING.md
Normal file
218
docs/ru/CONTRIBUTING.md
Normal file
@@ -0,0 +1,218 @@
|
||||
# Участие в проекте
|
||||
|
||||
## Компиляция PHP
|
||||
|
||||
### С помощью Docker (Linux)
|
||||
|
||||
Создайте образ Docker для разработки:
|
||||
|
||||
```console
|
||||
docker build -t frankenphp-dev -f dev.Dockerfile .
|
||||
docker run --cap-add=SYS_PTRACE --security-opt seccomp=unconfined -p 8080:8080 -p 443:443 -p 443:443/udp -v $PWD:/go/src/app -it frankenphp-dev
|
||||
```
|
||||
|
||||
Образ содержит стандартные инструменты для разработки (Go, GDB, Valgrind, Neovim и др.) и использует следующие пути для настроек PHP
|
||||
|
||||
- php.ini: `/etc/frankenphp/php.ini` По умолчанию предоставляется файл php.ini с настройками для разработки.
|
||||
- дополнительные файлы конфигурации: `/etc/frankenphp/php.d/*.ini`
|
||||
- расширения php: `/usr/lib/frankenphp/modules/`
|
||||
|
||||
Если ваша версия Docker ниже 23.0, сборка может завершиться ошибкой из-за [проблемы с шаблонами dockerignore](https://github.com/moby/moby/pull/42676). Добавьте в `.dockerignore` следующие директории:
|
||||
|
||||
```patch
|
||||
!testdata/*.php
|
||||
!testdata/*.txt
|
||||
+!caddy
|
||||
+!internal
|
||||
```
|
||||
|
||||
### Без Docker (Linux и macOS)
|
||||
|
||||
[Следуйте инструкциям по компиляции из исходников](https://frankenphp.dev/docs/compile/) и укажите флаг конфигурации `--debug`.
|
||||
|
||||
## Запуск тестов
|
||||
|
||||
```console
|
||||
go test -tags watcher -race -v ./...
|
||||
```
|
||||
|
||||
## Модуль Caddy
|
||||
|
||||
Соберите Caddy с модулем FrankenPHP:
|
||||
|
||||
```console
|
||||
cd caddy/frankenphp/
|
||||
go build -tags watcher,brotli,nobadger,nomysql,nopgx
|
||||
cd ../../
|
||||
```
|
||||
|
||||
Запустите Caddy с модулем FrankenPHP:
|
||||
|
||||
```console
|
||||
cd testdata/
|
||||
../caddy/frankenphp/frankenphp run
|
||||
```
|
||||
|
||||
Сервер будет доступен по адресу `127.0.0.1:8080`:
|
||||
|
||||
```console
|
||||
curl -vk https://localhost/phpinfo.php
|
||||
```
|
||||
|
||||
## Минимальный тестовый сервер
|
||||
|
||||
Соберите минимальный тестовый сервер:
|
||||
|
||||
```console
|
||||
cd internal/testserver/
|
||||
go build
|
||||
cd ../../
|
||||
```
|
||||
|
||||
Запустите тестовый сервер:
|
||||
|
||||
```console
|
||||
cd testdata/
|
||||
../internal/testserver/testserver
|
||||
```
|
||||
|
||||
Сервер будет доступен по адресу `127.0.0.1:8080`:
|
||||
|
||||
```console
|
||||
curl -v http://127.0.0.1:8080/phpinfo.php
|
||||
```
|
||||
|
||||
## Локальная сборка Docker-образов
|
||||
|
||||
Выведите план bake:
|
||||
|
||||
```console
|
||||
docker buildx bake -f docker-bake.hcl --print
|
||||
```
|
||||
|
||||
Соберите образы FrankenPHP для amd64 локально:
|
||||
|
||||
```console
|
||||
docker buildx bake -f docker-bake.hcl --pull --load --set "*.platform=linux/amd64"
|
||||
```
|
||||
|
||||
Соберите образы FrankenPHP для arm64 локально:
|
||||
|
||||
```console
|
||||
docker buildx bake -f docker-bake.hcl --pull --load --set "*.platform=linux/arm64"
|
||||
```
|
||||
|
||||
Соберите образы FrankenPHP с нуля для arm64 и amd64 и отправьте их в Docker Hub:
|
||||
|
||||
```console
|
||||
docker buildx bake -f docker-bake.hcl --pull --no-cache --push
|
||||
```
|
||||
|
||||
## Отладка ошибок сегментации с использованием статических сборок
|
||||
|
||||
1. Скачайте отладочную версию бинарного файла FrankenPHP с GitHub или создайте собственную статическую сборку с включённым отладочным режимом:
|
||||
|
||||
```console
|
||||
docker buildx bake \
|
||||
--load \
|
||||
--set static-builder.args.DEBUG_SYMBOLS=1 \
|
||||
--set "static-builder.platform=linux/amd64" \
|
||||
static-builder
|
||||
docker cp $(docker create --name static-builder-musl dunglas/frankenphp:static-builder-musl):/go/src/app/dist/frankenphp-linux-$(uname -m) frankenphp
|
||||
```
|
||||
|
||||
2. Замените текущую версию `frankenphp` на бинарный файл с включенным отладочным режимом.
|
||||
3. Запустите FrankenPHP как обычно (или сразу запустите FrankenPHP с GDB: `gdb --args frankenphp run`).
|
||||
4. Подключитесь к процессу через GDB:
|
||||
|
||||
```console
|
||||
gdb -p `pidof frankenphp`
|
||||
```
|
||||
|
||||
5. При необходимости введите `continue` в консоли GDB.
|
||||
6. Вызовите сбой FrankenPHP.
|
||||
7. Введите `bt` в консоли GDB.
|
||||
8. Скопируйте вывод.
|
||||
|
||||
## Отладка ошибок сегментации в GitHub Actions
|
||||
|
||||
1. Откройте файл `.github/workflows/tests.yml`.
|
||||
2. Включите режим отладки PHP:
|
||||
|
||||
```patch
|
||||
- uses: shivammathur/setup-php@v2
|
||||
# ...
|
||||
env:
|
||||
phpts: ts
|
||||
+ debug: true
|
||||
```
|
||||
|
||||
3. Настройте `tmate` для удалённого подключения к контейнеру:
|
||||
|
||||
```patch
|
||||
-
|
||||
name: Set CGO flags
|
||||
run: echo "CGO_CFLAGS=$(php-config --includes)" >> "$GITHUB_ENV"
|
||||
+ -
|
||||
+ run: |
|
||||
+ sudo apt install gdb
|
||||
+ mkdir -p /home/runner/.config/gdb/
|
||||
+ printf "set auto-load safe-path /\nhandle SIG34 nostop noprint pass" > /home/runner/.config/gdb/gdbinit
|
||||
+ -
|
||||
+ uses: mxschmitt/action-tmate@v3
|
||||
```
|
||||
|
||||
4. Подключитесь к контейнеру.
|
||||
5. Откройте файл `frankenphp.go`.
|
||||
6. Включите `cgosymbolizer`:
|
||||
|
||||
```patch
|
||||
- //_ "github.com/ianlancetaylor/cgosymbolizer"
|
||||
+ _ "github.com/ianlancetaylor/cgosymbolizer"
|
||||
```
|
||||
|
||||
7. Загрузите модуль: `go get`.
|
||||
8. В контейнере используйте GDB и другие инструменты:
|
||||
|
||||
```console
|
||||
go test -tags watcher -c -ldflags=-w
|
||||
gdb --args frankenphp.dev.test -test.run ^MyTest$
|
||||
```
|
||||
|
||||
9. После исправления ошибки откатите все внесенные изменения.
|
||||
|
||||
## Дополнительные ресурсы для разработки
|
||||
|
||||
- [Встраивание PHP в uWSGI](https://github.com/unbit/uwsgi/blob/master/plugins/php/php_plugin.c)
|
||||
- [Встраивание PHP в NGINX Unit](https://github.com/nginx/unit/blob/master/src/nxt_php_sapi.c)
|
||||
- [Встраивание PHP в Go (go-php)](https://github.com/deuill/go-php)
|
||||
- [Встраивание PHP в Go (GoEmPHP)](https://github.com/mikespook/goemphp)
|
||||
- [Встраивание PHP в C++](https://gist.github.com/paresy/3cbd4c6a469511ac7479aa0e7c42fea7)
|
||||
- [Книга "Extending and Embedding PHP" Сары Големан](https://books.google.fr/books?id=zMbGvK17_tYC&pg=PA254&lpg=PA254#v=onepage&q&f=false)
|
||||
- [Статья: Что такое TSRMLS_CC?](http://blog.golemon.com/2006/06/what-heck-is-tsrmlscc-anyway.html)
|
||||
- [SDL bindings](https://pkg.go.dev/github.com/veandco/go-sdl2@v0.4.21/sdl#Main)
|
||||
|
||||
## Docker-ресурсы
|
||||
|
||||
- [Определение файлов bake](https://docs.docker.com/build/customize/bake/file-definition/)
|
||||
- [Документация по команде `docker buildx build`](https://docs.docker.com/engine/reference/commandline/buildx_build/)
|
||||
|
||||
## Полезные команды
|
||||
|
||||
```console
|
||||
apk add strace util-linux gdb
|
||||
strace -e 'trace=!futex,epoll_ctl,epoll_pwait,tgkill,rt_sigreturn' -p 1
|
||||
```
|
||||
|
||||
## Перевод документации
|
||||
|
||||
Чтобы перевести документацию и сайт на новый язык, выполните следующие шаги:
|
||||
|
||||
1. Создайте новую директорию с 2-буквенным ISO-кодом языка в папке `docs/`.
|
||||
2. Скопируйте все `.md` файлы из корня папки `docs/` в новую директорию (используйте английскую версию как основу для перевода).
|
||||
3. Скопируйте файлы `README.md` и `CONTRIBUTING.md` из корневой директории в новую папку.
|
||||
4. Переведите содержимое файлов, но не изменяйте имена файлов. Не переводите строки, начинающиеся с `> [!`, это специальная разметка GitHub.
|
||||
5. Создайте Pull Request с переводом.
|
||||
6. В [репозитории сайта](https://github.com/dunglas/frankenphp-website/tree/main) скопируйте и переведите файлы в папках `content/`, `data/` и `i18n/`.
|
||||
7. Переведите значения в созданных YAML-файлах.
|
||||
8. Откройте Pull Request в репозитории сайта.
|
||||
86
docs/ru/README.md
Normal file
86
docs/ru/README.md
Normal file
@@ -0,0 +1,86 @@
|
||||
# FrankenPHP: Современный сервер приложений для PHP
|
||||
|
||||
<h1 align="center"><a href="https://frankenphp.dev"><img src="../../frankenphp.png" alt="FrankenPHP" width="600"></a></h1>
|
||||
|
||||
**FrankenPHP** — это современный сервер приложений для PHP, построенный на базе веб-сервера [Caddy](https://caddyserver.com/).
|
||||
|
||||
FrankenPHP добавляет новые возможности вашим PHP-приложениям благодаря следующим функциям: [_Early Hints_](https://frankenphp.dev/docs/early-hints/), [Worker режим](https://frankenphp.dev/docs/worker/), [Real-time режим](https://frankenphp.dev/docs/mercure/), автоматическая поддержка HTTPS, HTTP/2 и HTTP/3.
|
||||
|
||||
FrankenPHP совместим с любыми PHP-приложениями и значительно ускоряет ваши проекты на Laravel и Symfony благодаря их официальной поддержке в worker режиме.
|
||||
|
||||
FrankenPHP также может использоваться как автономная Go-библиотека для встраивания PHP в любое приложение с использованием `net/http`.
|
||||
|
||||
[**Узнайте больше** на сайте _frankenphp.dev_](https://frankenphp.dev) или из этой презентации:
|
||||
|
||||
<a href="https://dunglas.dev/2022/10/frankenphp-the-modern-php-app-server-written-in-go/"><img src="https://dunglas.dev/wp-content/uploads/2022/10/frankenphp.png" alt="Slides" width="600"></a>
|
||||
|
||||
## Начало работы
|
||||
|
||||
### Docker
|
||||
|
||||
```console
|
||||
docker run -v .:/app/public \
|
||||
-p 80:80 -p 443:443 -p 443:443/udp \
|
||||
dunglas/frankenphp
|
||||
```
|
||||
|
||||
Перейдите по адресу `https://localhost` и наслаждайтесь!
|
||||
|
||||
> [!TIP]
|
||||
>
|
||||
> Не используйте `https://127.0.0.1`. Используйте `https://localhost` и настройте самоподписанный сертификат.
|
||||
> Чтобы изменить используемый домен, настройте переменную окружения [`SERVER_NAME`](config.md#переменные-окружения).
|
||||
|
||||
### Автономный бинарный файл
|
||||
|
||||
Если вы предпочитаете не использовать Docker, мы предоставляем автономный бинарный файл FrankenPHP для Linux и macOS, включающий [PHP 8.4](https://www.php.net/releases/8.4/en.php) и большинство популярных PHP-расширений.
|
||||
|
||||
Для Windows используйте [WSL](https://learn.microsoft.com/windows/wsl/) для запуска FrankenPHP.
|
||||
|
||||
[Скачать FrankenPHP](https://github.com/dunglas/frankenphp/releases) или выполните следующую команду для автоматической установки подходящей версии:
|
||||
|
||||
```console
|
||||
curl https://frankenphp.dev/install.sh | sh
|
||||
mv frankenphp /usr/local/bin/
|
||||
```
|
||||
|
||||
Для запуска содержимого текущей директории выполните:
|
||||
|
||||
```console
|
||||
frankenphp php-server
|
||||
```
|
||||
|
||||
Вы также можете запускать CLI-скрипты:
|
||||
|
||||
```console
|
||||
frankenphp php-cli /path/to/your/script.php
|
||||
```
|
||||
|
||||
## Документация
|
||||
|
||||
- [Worker режим](https://frankenphp.dev/docs/worker/)
|
||||
- [Поддержка Early Hints (103 HTTP статус код)](https://frankenphp.dev/docs/early-hints/)
|
||||
- [Real-time режим](https://frankenphp.dev/docs/mercure/)
|
||||
- [Конфигурация](https://frankenphp.dev/docs/config/)
|
||||
- [Docker-образы](https://frankenphp.dev/docs/docker/)
|
||||
- [Деплой в продакшен](https://frankenphp.dev/docs/production/)
|
||||
- [Оптимизация производительности](https://frankenphp.dev/docs/performance/)
|
||||
- [Создание автономного PHP-приложений](https://frankenphp.dev/docs/embed/)
|
||||
- [Создание статических бинарных файлов](https://frankenphp.dev/docs/static/)
|
||||
- [Компиляция из исходников](https://frankenphp.dev/docs/compile/)
|
||||
- [Интеграция с Laravel](https://frankenphp.dev/docs/laravel/)
|
||||
- [Известные проблемы](https://frankenphp.dev/docs/known-issues/)
|
||||
- [Демо-приложение (Symfony) и бенчмарки](https://github.com/dunglas/frankenphp-demo)
|
||||
- [Документация Go-библиотеки](https://pkg.go.dev/github.com/dunglas/frankenphp)
|
||||
- [Участие в проекте и отладка](https://frankenphp.dev/docs/contributing/)
|
||||
|
||||
## Примеры и шаблоны
|
||||
|
||||
- [Symfony](https://github.com/dunglas/symfony-docker)
|
||||
- [API Platform](https://api-platform.com/docs/symfony)
|
||||
- [Laravel](https://frankenphp.dev/docs/laravel/)
|
||||
- [Sulu](https://sulu.io/blog/running-sulu-with-frankenphp)
|
||||
- [WordPress](https://github.com/StephenMiracle/frankenwp)
|
||||
- [Drupal](https://github.com/dunglas/frankenphp-drupal)
|
||||
- [Joomla](https://github.com/alexandreelise/frankenphp-joomla)
|
||||
- [TYPO3](https://github.com/ochorocho/franken-typo3)
|
||||
110
docs/ru/compile.md
Normal file
110
docs/ru/compile.md
Normal file
@@ -0,0 +1,110 @@
|
||||
# Компиляция из исходников
|
||||
|
||||
Этот документ объясняет, как создать бинарный файл FrankenPHP, который будет загружать PHP как динамическую библиотеку.
|
||||
Это рекомендуемый способ.
|
||||
|
||||
Альтернативно можно создать [статическую сборку](static.md).
|
||||
|
||||
## Установка PHP
|
||||
|
||||
FrankenPHP совместим с PHP версии 8.2 и выше.
|
||||
|
||||
Сначала [загрузите исходники PHP](https://www.php.net/downloads.php) и распакуйте их:
|
||||
|
||||
```console
|
||||
tar xf php-*
|
||||
cd php-*/
|
||||
```
|
||||
|
||||
Далее выполните скрипт `configure` с параметрами, необходимыми для вашей платформы.
|
||||
Следующие флаги `./configure` обязательны, но вы можете добавить и другие, например, для компиляции расширений или дополнительных функций.
|
||||
|
||||
### Linux
|
||||
|
||||
```console
|
||||
./configure \
|
||||
--enable-embed \
|
||||
--enable-zts \
|
||||
--disable-zend-signals \
|
||||
--enable-zend-max-execution-timers
|
||||
```
|
||||
|
||||
### Mac
|
||||
|
||||
Используйте пакетный менеджер [Homebrew](https://brew.sh/) для установки
|
||||
`libiconv`, `bison`, `re2c` и `pkg-config`:
|
||||
|
||||
```console
|
||||
brew install libiconv bison brotli re2c pkg-config
|
||||
echo 'export PATH="/opt/homebrew/opt/bison/bin:$PATH"' >> ~/.zshrc
|
||||
```
|
||||
|
||||
Затем выполните скрипт configure:
|
||||
|
||||
```console
|
||||
./configure \
|
||||
--enable-embed=static \
|
||||
--enable-zts \
|
||||
--disable-zend-signals \
|
||||
--disable-opcache-jit \
|
||||
--enable-static \
|
||||
--enable-shared=no \
|
||||
--with-iconv=/opt/homebrew/opt/libiconv/
|
||||
```
|
||||
|
||||
## Компиляция PHP
|
||||
|
||||
Наконец, скомпилируйте и установите PHP:
|
||||
|
||||
```console
|
||||
make -j"$(getconf _NPROCESSORS_ONLN)"
|
||||
sudo make install
|
||||
```
|
||||
|
||||
## Установка дополнительных зависимостей
|
||||
|
||||
Некоторые функции FrankenPHP зависят от опциональных системных зависимостей.
|
||||
Альтернативно, эти функции можно отключить, передав соответствующие теги сборки компилятору Go.
|
||||
|
||||
| Функция | Зависимость | Тег сборки для отключения |
|
||||
| ----------------------------------------------- | --------------------------------------------------------------------- | ------------------------- |
|
||||
| Сжатие Brotli | [Brotli](https://github.com/google/brotli) | nobrotli |
|
||||
| Перезапуск worker-скриптов при изменении файлов | [Watcher C](https://github.com/e-dant/watcher/tree/release/watcher-c) | nowatcher |
|
||||
|
||||
## Компиляция Go-приложения
|
||||
|
||||
Теперь можно собрать итоговый бинарный файл:
|
||||
|
||||
```console
|
||||
curl -L https://github.com/dunglas/frankenphp/archive/refs/heads/main.tar.gz | tar xz
|
||||
cd frankenphp-main/caddy/frankenphp
|
||||
CGO_CFLAGS=$(php-config --includes) CGO_LDFLAGS="$(php-config --ldflags) $(php-config --libs)" go build -tags=nobadger,nomysql,nopgx
|
||||
```
|
||||
|
||||
### Использование xcaddy
|
||||
|
||||
Альтернативно, используйте [xcaddy](https://github.com/caddyserver/xcaddy) для компиляции FrankenPHP с [пользовательскими модулями Caddy](https://caddyserver.com/docs/modules/):
|
||||
|
||||
```console
|
||||
CGO_ENABLED=1 \
|
||||
XCADDY_GO_BUILD_FLAGS="-ldflags='-w -s' -tags=nobadger,nomysql,nopgx" \
|
||||
CGO_CFLAGS=$(php-config --includes) \
|
||||
CGO_LDFLAGS="$(php-config --ldflags) $(php-config --libs)" \
|
||||
xcaddy build \
|
||||
--output frankenphp \
|
||||
--with frankenphp.dev/caddy \
|
||||
--with github.com/dunglas/mercure/caddy \
|
||||
--with github.com/dunglas/vulcain/caddy
|
||||
# Добавьте дополнительные модули Caddy здесь
|
||||
```
|
||||
|
||||
> [!TIP]
|
||||
>
|
||||
> Если вы используете musl libc (по умолчанию в Alpine Linux) и Symfony,
|
||||
> возможно, потребуется увеличить размер стека.
|
||||
> В противном случае вы можете столкнуться с ошибками вроде
|
||||
> `PHP Fatal error: Maximum call stack size of 83360 bytes reached during compilation. Try splitting expression`.
|
||||
>
|
||||
> Для этого измените значение переменной окружения `XCADDY_GO_BUILD_FLAGS`, например:
|
||||
> `XCADDY_GO_BUILD_FLAGS=$'-ldflags "-w -s -extldflags \'-Wl,-z,stack-size=0x80000\'"'`
|
||||
> (измените значение размера стека в зависимости от требований вашего приложения).
|
||||
247
docs/ru/config.md
Normal file
247
docs/ru/config.md
Normal file
@@ -0,0 +1,247 @@
|
||||
# Конфигурация
|
||||
|
||||
FrankenPHP, Caddy, а также модули Mercure и Vulcain могут быть настроены с использованием [конфигурационных форматов, поддерживаемых Caddy](https://caddyserver.com/docs/getting-started#your-first-config).
|
||||
|
||||
В [Docker-образах](docker.md) файл `Caddyfile` находится по пути `/etc/frankenphp/Caddyfile`.
|
||||
Статический бинарный файл будет искать `Caddyfile` в директории запуска.
|
||||
|
||||
PHP можно настроить [с помощью файла `php.ini`](https://www.php.net/manual/en/configuration.file.php).
|
||||
|
||||
PHP-интерпретатор будет искать в следующих местах:
|
||||
|
||||
Docker:
|
||||
|
||||
- php.ini: `/usr/local/etc/php/php.ini` По умолчанию php.ini не предоставляется.
|
||||
- дополнительные файлы конфигурации: `/usr/local/etc/php/conf.d/*.ini`
|
||||
- расширения php: `/usr/local/lib/php/extensions/no-debug-zts-<YYYYMMDD>/`
|
||||
- Вы должны скопировать официальный шаблон, предоставляемый проектом PHP:
|
||||
|
||||
```dockerfile
|
||||
FROM dunglas/frankenphp
|
||||
|
||||
# Для production:
|
||||
RUN cp $PHP_INI_DIR/php.ini-production $PHP_INI_DIR/php.ini
|
||||
|
||||
# Или для development:
|
||||
RUN cp $PHP_INI_DIR/php.ini-development $PHP_INI_DIR/php.ini
|
||||
```
|
||||
|
||||
Установка FrankenPHP (.rpm или .deb):
|
||||
|
||||
- php.ini: `/etc/frankenphp/php.ini` По умолчанию предоставляется файл php.ini с производственными настройками.
|
||||
- дополнительные файлы конфигурации: `/etc/frankenphp/php.d/*.ini`
|
||||
- расширения php: `/usr/lib/frankenphp/modules/`
|
||||
|
||||
Статический бинарный файл:
|
||||
|
||||
- php.ini: Директория, в которой выполняется `frankenphp run` или `frankenphp php-server`, затем `/etc/frankenphp/php.ini`
|
||||
- дополнительные файлы конфигурации: `/etc/frankenphp/php.d/*.ini`
|
||||
- расширения php: не могут быть загружены
|
||||
- скопируйте один из шаблонов `php.ini-production` или `php.ini-development`, предоставленных [в исходниках PHP](https://github.com/php/php-src/).
|
||||
|
||||
## Конфигурация Caddyfile
|
||||
|
||||
[HTTP-директивы](https://caddyserver.com/docs/caddyfile/concepts#directives) `php_server` или `php` могут быть использованы в блоках сайта для обработки вашего PHP-приложения.
|
||||
|
||||
Минимальный пример:
|
||||
|
||||
```caddyfile
|
||||
localhost {
|
||||
# Включить сжатие (опционально)
|
||||
encode zstd br gzip
|
||||
# Выполнять PHP-файлы в текущей директории и обслуживать ресурсы
|
||||
php_server
|
||||
}
|
||||
```
|
||||
|
||||
Вы также можете явно настроить FrankenPHP с помощью глобальной опции:
|
||||
[Глобальная опция](https://caddyserver.com/docs/caddyfile/concepts#global-options) `frankenphp` может быть использована для настройки FrankenPHP.
|
||||
|
||||
```caddyfile
|
||||
{
|
||||
frankenphp {
|
||||
num_threads <num_threads> # Указывает количество потоков PHP. По умолчанию: 2x от числа доступных CPU.
|
||||
worker {
|
||||
file <path> # Указывает путь к worker-скрипту.
|
||||
num <num> # Указывает количество потоков PHP. По умолчанию: 2x от числа доступных CPU.
|
||||
env <key> <value> # Устанавливает дополнительную переменную окружения. Можно указать несколько раз для разных переменных.
|
||||
watch <path> # Указывает путь для отслеживания изменений файлов.Можно указать несколько раз для разных путей.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# ...
|
||||
```
|
||||
|
||||
В качестве альтернативы можно использовать однострочную краткую форму для опции `worker`:
|
||||
|
||||
```caddyfile
|
||||
{
|
||||
frankenphp {
|
||||
worker <file> <num>
|
||||
}
|
||||
}
|
||||
|
||||
# ...
|
||||
```
|
||||
|
||||
Вы также можете определить несколько workers, если обслуживаете несколько приложений на одном сервере:
|
||||
|
||||
```caddyfile
|
||||
app.example.com {
|
||||
php_server {
|
||||
root /path/to/app/public
|
||||
worker index.php <num>
|
||||
}
|
||||
}
|
||||
|
||||
other.example.com {
|
||||
php_server {
|
||||
root /path/to/other/public
|
||||
worker index.php <num>
|
||||
}
|
||||
}
|
||||
|
||||
# ...
|
||||
```
|
||||
|
||||
Использование директивы `php_server` — это то, что нужно в большинстве случаев. Однако если требуется полный контроль, вы можете использовать более низкоуровневую директиву `php`:
|
||||
|
||||
Использование директивы `php_server` эквивалентно следующей конфигурации:
|
||||
|
||||
```caddyfile
|
||||
route {
|
||||
# Добавить слэш в конец запросов к директориям
|
||||
@canonicalPath {
|
||||
file {path}/index.php
|
||||
not path */
|
||||
}
|
||||
redir @canonicalPath {path}/ 308
|
||||
# Если запрошенный файл не существует, попытаться использовать файлы index
|
||||
@indexFiles file {
|
||||
try_files {path} {path}/index.php index.php
|
||||
split_path .php
|
||||
}
|
||||
rewrite @indexFiles {http.matchers.file.relative}
|
||||
# FrankenPHP!
|
||||
@phpFiles path *.php
|
||||
php @phpFiles
|
||||
file_server
|
||||
}
|
||||
```
|
||||
|
||||
Директивы `php_server` и `php` имеют следующие опции:
|
||||
|
||||
```caddyfile
|
||||
php_server [<matcher>] {
|
||||
root <directory> # Указывает корневую директорию сайта. По умолчанию: директива `root`.
|
||||
split_path <delim...> # Устанавливает подстроки для разделения URI на две части. Первая часть будет использована как имя ресурса (CGI-скрипта), вторая часть — как PATH_INFO. По умолчанию: `.php`.
|
||||
resolve_root_symlink false # Отключает разрешение символьных ссылок для `root` (включено по умолчанию).
|
||||
env <key> <value> # Устанавливает дополнительные переменные окружения. Можно указать несколько раз для разных переменных.
|
||||
file_server off # Отключает встроенную директиву file_server.
|
||||
worker { # Создает worker, специфичный для этого сервера. Можно указать несколько раз для разных workers.
|
||||
file <path> # Указывает путь к worker-скрипту, может быть относительным к корню php_server
|
||||
num <num> # Указывает количество потоков PHP. По умолчанию: 2x от числа доступных CPU.
|
||||
name <name> # Устанавливает имя для worker, используемое в логах и метриках. По умолчанию: абсолютный путь к файлу worker. Всегда начинается с m# при определении в блоке php_server.
|
||||
watch <path> # Указывает путь для отслеживания изменений файлов. Можно указать несколько раз для разных путей.
|
||||
env <key> <value> # Устанавливает дополнительную переменную окружения. Можно указать несколько раз для разных переменных. Переменные окружения для этого worker также наследуются от родительского php_server, но могут быть переопределены здесь.
|
||||
}
|
||||
worker <other_file> <num> # Также можно использовать краткую форму как в глобальном блоке frankenphp.
|
||||
}
|
||||
```
|
||||
|
||||
### Отслеживание изменений файлов
|
||||
|
||||
Поскольку workers запускают ваше приложение только один раз и держат его в памяти, изменения в PHP-файлах не будут применяться сразу.
|
||||
|
||||
Для разработки можно настроить перезапуск workers при изменении файлов с помощью директивы `watch`:
|
||||
|
||||
```caddyfile
|
||||
{
|
||||
frankenphp {
|
||||
worker {
|
||||
file /path/to/app/public/worker.php
|
||||
watch
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Если директория для `watch` не указана, по умолчанию будет использоваться путь `./**/*.{php,yaml,yml,twig,env}`,
|
||||
который отслеживает все файлы с расширениями `.php`, `.yaml`, `.yml`, `.twig` и `.env` в директории, где был запущен процесс FrankenPHP, и во всех её поддиректориях. Вы также можете указать одну или несколько директорий с использованием [шаблона имён файлов](https://pkg.go.dev/path/filepath#Match):
|
||||
|
||||
```caddyfile
|
||||
{
|
||||
frankenphp {
|
||||
worker {
|
||||
file /path/to/app/public/worker.php
|
||||
watch /path/to/app # отслеживает все файлы во всех поддиректориях /path/to/app
|
||||
watch /path/to/app/*.php # отслеживает файлы с расширением .php в /path/to/app
|
||||
watch /path/to/app/**/*.php # отслеживает PHP-файлы в /path/to/app и поддиректориях
|
||||
watch /path/to/app/**/*.{php,twig} # отслеживает PHP и Twig-файлы в /path/to/app и поддиректориях
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- Шаблон `**` указывает на рекурсивное отслеживание.
|
||||
- Директории могут быть указаны относительно директории запуска FrankenPHP.
|
||||
- Если у вас определено несколько workers, все они будут перезапущены при изменении файлов.
|
||||
- Избегайте отслеживания файлов, создаваемых во время выполнения (например, логов), так как это может вызвать нежелательные перезапуски.
|
||||
|
||||
Механизм отслеживания файлов основан на [e-dant/watcher](https://github.com/e-dant/watcher).
|
||||
|
||||
### Полный дуплекс (HTTP/1)
|
||||
|
||||
При использовании HTTP/1.x можно включить режим полного дуплекса, чтобы разрешить запись ответа до завершения чтения тела запроса (например, для WebSocket, Server-Sent Events и т.д.).
|
||||
|
||||
Эта опция включается вручную и должна быть добавлена в глобальные настройки `Caddyfile`:
|
||||
|
||||
```caddyfile
|
||||
{
|
||||
servers {
|
||||
enable_full_duplex
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> [!CAUTION]
|
||||
>
|
||||
> Включение этой опции может привести к зависанию устаревших HTTP/1.x клиентов, которые не поддерживают полный дуплекс.
|
||||
> Настройка также доступна через переменную окружения `CADDY_GLOBAL_OPTIONS`:
|
||||
|
||||
```sh
|
||||
CADDY_GLOBAL_OPTIONS="servers {
|
||||
enable_full_duplex
|
||||
}"
|
||||
```
|
||||
|
||||
Дополнительную информацию об этой настройке можно найти в [документации Caddy](https://caddyserver.com/docs/caddyfile/options#enable-full-duplex).
|
||||
|
||||
## Переменные окружения
|
||||
|
||||
Следующие переменные окружения могут быть использованы для добавления директив в `Caddyfile` без его изменения:
|
||||
|
||||
- `SERVER_NAME`: изменение [адресов для прослушивания](https://caddyserver.com/docs/caddyfile/concepts#addresses); предоставленные хостнеймы также будут использованы для генерации TLS-сертификата.
|
||||
- `CADDY_GLOBAL_OPTIONS`: добавление [глобальных опций](https://caddyserver.com/docs/caddyfile/options).
|
||||
- `FRANKENPHP_CONFIG`: добавление конфигурации в директиву `frankenphp`.
|
||||
|
||||
Как и для FPM и CLI SAPIs, переменные окружения по умолчанию доступны в суперглобальной переменной `$_SERVER`.
|
||||
|
||||
Значение `S` в [директиве PHP `variables_order`](https://www.php.net/manual/en/ini.core.php#ini.variables-order) всегда эквивалентно `ES`, независимо от того, где расположена `E` в этой директиве.
|
||||
|
||||
## Конфигурация PHP
|
||||
|
||||
Для загрузки [дополнительных конфигурационных файлов PHP](https://www.php.net/manual/en/configuration.file.php#configuration.file.scan) можно использовать переменную окружения `PHP_INI_SCAN_DIR`.
|
||||
Если она установлена, PHP загрузит все файлы с расширением `.ini`, находящиеся в указанных директориях.
|
||||
|
||||
## Включение режима отладки
|
||||
|
||||
При использовании Docker-образа установите переменную окружения `CADDY_GLOBAL_OPTIONS` в `debug`, чтобы включить режим отладки:
|
||||
|
||||
```console
|
||||
docker run -v $PWD:/app/public \
|
||||
-e CADDY_GLOBAL_OPTIONS=debug \
|
||||
-p 80:80 -p 443:443 -p 443:443/udp \
|
||||
dunglas/frankenphp
|
||||
```
|
||||
201
docs/ru/docker.md
Normal file
201
docs/ru/docker.md
Normal file
@@ -0,0 +1,201 @@
|
||||
# Создание кастомных Docker-образов
|
||||
|
||||
[Docker-образы FrankenPHP](https://hub.docker.com/r/dunglas/frankenphp) основаны на [официальных PHP-образах](https://hub.docker.com/_/php/). Доступны варианты для Debian и Alpine Linux для популярных архитектур. Рекомендуется использовать Debian-варианты.
|
||||
|
||||
Доступны версии для PHP 8.2, 8.3 и 8.4.
|
||||
|
||||
Теги следуют следующему шаблону: `dunglas/frankenphp:<frankenphp-version>-php<php-version>-<os>`.
|
||||
|
||||
- `<frankenphp-version>` и `<php-version>` — версии FrankenPHP и PHP соответственно: от основных (например, `1`) до минорных (например, `1.2`) и патч-версий (например, `1.2.3`).
|
||||
- `<os>` может быть `bookworm` (для Debian Bookworm) или `alpine` (для последней стабильной версии Alpine).
|
||||
|
||||
[Просмотреть доступные теги](https://hub.docker.com/r/dunglas/frankenphp/tags).
|
||||
|
||||
## Как использовать образы
|
||||
|
||||
Создайте `Dockerfile` в вашем проекте:
|
||||
|
||||
```dockerfile
|
||||
FROM dunglas/frankenphp
|
||||
|
||||
COPY . /app/public
|
||||
```
|
||||
|
||||
Затем выполните следующие команды для сборки и запуска Docker-образа:
|
||||
|
||||
```console
|
||||
docker build -t my-php-app .
|
||||
docker run -it --rm --name my-running-app my-php-app
|
||||
```
|
||||
|
||||
## Как установить дополнительные PHP-расширения
|
||||
|
||||
Скрипт [`docker-php-extension-installer`](https://github.com/mlocati/docker-php-extension-installer) включён в базовый образ. Установка дополнительных PHP-расширений осуществляется просто:
|
||||
|
||||
```dockerfile
|
||||
FROM dunglas/frankenphp
|
||||
|
||||
# Добавьте дополнительные расширения здесь:
|
||||
RUN install-php-extensions \
|
||||
pdo_mysql \
|
||||
gd \
|
||||
intl \
|
||||
zip \
|
||||
opcache
|
||||
```
|
||||
|
||||
## Как установить дополнительные модули Caddy
|
||||
|
||||
FrankenPHP построен на базе Caddy, и все [модули Caddy](https://caddyserver.com/docs/modules/) можно использовать с FrankenPHP.
|
||||
|
||||
Самый простой способ установить пользовательские модули Caddy — использовать [xcaddy](https://github.com/caddyserver/xcaddy):
|
||||
|
||||
```dockerfile
|
||||
FROM dunglas/frankenphp:builder AS builder
|
||||
|
||||
# Копируем xcaddy в образ сборки
|
||||
COPY --from=caddy:builder /usr/bin/xcaddy /usr/bin/xcaddy
|
||||
|
||||
# Для сборки FrankenPHP необходимо включить CGO
|
||||
RUN CGO_ENABLED=1 \
|
||||
XCADDY_SETCAP=1 \
|
||||
XCADDY_GO_BUILD_FLAGS="-ldflags='-w -s' -tags=nobadger,nomysql,nopgx" \
|
||||
CGO_CFLAGS=$(php-config --includes) \
|
||||
CGO_LDFLAGS="$(php-config --ldflags) $(php-config --libs)" \
|
||||
xcaddy build \
|
||||
--output /usr/local/bin/frankenphp \
|
||||
--with frankenphp.dev=./ \
|
||||
--with frankenphp.dev/caddy=./caddy/ \
|
||||
--with github.com/dunglas/caddy-cbrotli \
|
||||
# Mercure и Vulcain включены в официальный билд, но вы можете их удалить
|
||||
--with github.com/dunglas/mercure/caddy \
|
||||
--with github.com/dunglas/vulcain/caddy
|
||||
# Добавьте дополнительные модули Caddy здесь
|
||||
|
||||
FROM dunglas/frankenphp AS runner
|
||||
|
||||
# Заменяем официальный бинарный файл на пользовательский с добавленными модулями
|
||||
COPY --from=builder /usr/local/bin/frankenphp /usr/local/bin/frankenphp
|
||||
```
|
||||
|
||||
Образ `builder`, предоставляемый FrankenPHP, содержит скомпилированную версию `libphp`.
|
||||
[Образы builder](https://hub.docker.com/r/dunglas/frankenphp/tags?name=builder) доступны для всех версий FrankenPHP и PHP, как для Debian, так и для Alpine.
|
||||
|
||||
> [!TIP]
|
||||
>
|
||||
> Если вы используете Alpine Linux и Symfony, возможно, потребуется [увеличить размер стека](compile.md#использование-xcaddy).
|
||||
|
||||
## Активировать worker режим по умолчанию
|
||||
|
||||
Установите переменную окружения `FRANKENPHP_CONFIG`, чтобы запускать FrankenPHP с Worker-скриптом:
|
||||
|
||||
```dockerfile
|
||||
FROM dunglas/frankenphp
|
||||
|
||||
# ...
|
||||
|
||||
ENV FRANKENPHP_CONFIG="worker ./public/index.php"
|
||||
```
|
||||
|
||||
## Использование тома в разработке
|
||||
|
||||
Для удобной разработки с FrankenPHP смонтируйте директорию с исходным кодом приложения на хосте как том в Docker-контейнере:
|
||||
|
||||
```console
|
||||
docker run -v $PWD:/app/public -p 80:80 -p 443:443 -p 443:443/udp --tty my-php-app
|
||||
```
|
||||
|
||||
> [!TIP]
|
||||
>
|
||||
> Опция `--tty` позволяет видеть удобочитаемые логи вместо JSON-формата.
|
||||
|
||||
С использованием Docker Compose:
|
||||
|
||||
```yaml
|
||||
# compose.yaml
|
||||
|
||||
services:
|
||||
php:
|
||||
image: dunglas/frankenphp
|
||||
# раскомментируйте следующую строку, если хотите использовать собственный Dockerfile
|
||||
#build: .
|
||||
# раскомментируйте следующую строку, если вы запускаете это в продакшн среде
|
||||
# restart: always
|
||||
ports:
|
||||
- "80:80" # HTTP
|
||||
- "443:443" # HTTPS
|
||||
- "443:443/udp" # HTTP/3
|
||||
volumes:
|
||||
- ./:/app/public
|
||||
- caddy_data:/data
|
||||
- caddy_config:/config
|
||||
# закомментируйте следующую строку в продакшн среде, она позволяет получать удобочитаемые логи в режиме разработки
|
||||
tty: true
|
||||
|
||||
# Томы, необходимые для сертификатов и конфигурации Caddy
|
||||
volumes:
|
||||
caddy_data:
|
||||
caddy_config:
|
||||
```
|
||||
|
||||
## Запуск под обычным пользователем
|
||||
|
||||
FrankenPHP поддерживает запуск под обычным пользователем в Docker.
|
||||
|
||||
Пример `Dockerfile` для этого:
|
||||
|
||||
```dockerfile
|
||||
FROM dunglas/frankenphp
|
||||
|
||||
ARG USER=appuser
|
||||
|
||||
RUN \
|
||||
# Для дистрибутивов на основе Alpine используйте "adduser -D ${USER}"
|
||||
useradd ${USER}; \
|
||||
# Добавьте возможность привязываться к портам 80 и 443
|
||||
setcap CAP_NET_BIND_SERVICE=+eip /usr/local/bin/frankenphp; \
|
||||
# Дайте права на запись для /data/caddy и /config/caddy
|
||||
chown -R ${USER}:${USER} /data/caddy && chown -R ${USER}:${USER} /config/caddy
|
||||
|
||||
USER ${USER}
|
||||
```
|
||||
|
||||
### Запуск без дополнительных прав
|
||||
|
||||
Даже при запуске без root-прав, FrankenPHP требуется возможность `CAP_NET_BIND_SERVICE` для привязки веб-сервера к зарезервированным портам (80 и 443).
|
||||
|
||||
Если вы открываете доступ к FrankenPHP на непривилегированном порту (1024 и выше), можно запустить веб-сервер от имени обычного пользователя без необходимости предоставления дополнительных возможностей:
|
||||
|
||||
```dockerfile
|
||||
FROM dunglas/frankenphp
|
||||
|
||||
ARG USER=appuser
|
||||
|
||||
RUN \
|
||||
# Для Alpine-дистрибутивов используйте команду "adduser -D ${USER}"
|
||||
useradd ${USER}; \
|
||||
# Удалите стандартные возможности
|
||||
setcap -r /usr/local/bin/frankenphp; \
|
||||
# Дайте права на запись для /data/caddy и /config/caddy
|
||||
chown -R ${USER}:${USER} /data/caddy && chown -R ${USER}:${USER} /config/caddy
|
||||
|
||||
USER ${USER}
|
||||
```
|
||||
|
||||
Затем установите переменную окружения `SERVER_NAME`, чтобы использовать непривилегированный порт.
|
||||
Пример: `:8000`.
|
||||
|
||||
## Обновления
|
||||
|
||||
Docker-образы обновляются:
|
||||
|
||||
- при выпуске новой версии;
|
||||
- ежедневно в 4 утра UTC, если доступны новые версии официальных PHP-образов.
|
||||
|
||||
## Версии для разработки
|
||||
|
||||
Версии для разработки доступны в Docker-репозитории [`dunglas/frankenphp-dev`](https://hub.docker.com/repository/docker/dunglas/frankenphp-dev).
|
||||
Сборка запускается автоматически при каждом коммите в основную ветку GitHub-репозитория
|
||||
|
||||
Теги с префиксом `latest*` указывают на актуальное состояние ветки `main`.
|
||||
Также доступны теги в формате `sha-<git-commit-hash>`.
|
||||
21
docs/ru/early-hints.md
Normal file
21
docs/ru/early-hints.md
Normal file
@@ -0,0 +1,21 @@
|
||||
# Early Hints
|
||||
|
||||
FrankenPHP изначально поддерживает [Early Hints (103 HTTP статус код)](https://developer.chrome.com/blog/early-hints/).
|
||||
Использование Early Hints может улучшить время загрузки ваших веб-страниц на 30%.
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
header('Link: </style.css>; rel=preload; as=style');
|
||||
headers_send(103);
|
||||
|
||||
// ваши медленные алгоритмы и SQL-запросы 🤪
|
||||
|
||||
echo <<<'HTML'
|
||||
<!DOCTYPE html>
|
||||
<title>Hello FrankenPHP</title>
|
||||
<link rel="stylesheet" href="style.css">
|
||||
HTML;
|
||||
```
|
||||
|
||||
Early Hints поддерживается как в обычном, так и в [worker режиме](worker.md).
|
||||
140
docs/ru/embed.md
Normal file
140
docs/ru/embed.md
Normal file
@@ -0,0 +1,140 @@
|
||||
# PHP-приложения как автономные бинарные файлы
|
||||
|
||||
FrankenPHP позволяет встраивать исходный код и ресурсы PHP-приложений в статический автономный бинарный файл.
|
||||
|
||||
Благодаря этой функции PHP-приложения могут распространяться как автономные бинарные файлы, которые содержат само приложение, интерпретатор PHP и Caddy — веб-сервер уровня продакшн.
|
||||
|
||||
Подробнее об этой функции [в презентации Кевина на SymfonyCon 2023](https://dunglas.dev/2023/12/php-and-symfony-apps-as-standalone-binaries/).
|
||||
|
||||
Для встраивания Laravel-приложений ознакомьтесь с [документацией](laravel.md#laravel-приложения-как-автономные-бинарные-файлы).
|
||||
|
||||
## Подготовка приложения
|
||||
|
||||
Перед созданием автономного бинарного файла убедитесь, что ваше приложение готово для встраивания.
|
||||
|
||||
Например, вам может понадобиться:
|
||||
|
||||
- Установить продакшн-зависимости приложения.
|
||||
- Сгенерировать автозагрузчик.
|
||||
- Включить продакшн-режим приложения (если он есть).
|
||||
- Удалить ненужные файлы, такие как `.git` или тесты, чтобы уменьшить размер итогового бинарного файла.
|
||||
|
||||
Для приложения на Symfony это может выглядеть так:
|
||||
|
||||
```console
|
||||
# Экспорт проекта, чтобы избавиться от .git/ и других ненужных файлов
|
||||
mkdir $TMPDIR/my-prepared-app
|
||||
git archive HEAD | tar -x -C $TMPDIR/my-prepared-app
|
||||
cd $TMPDIR/my-prepared-app
|
||||
|
||||
# Установить соответствующие переменные окружения
|
||||
echo APP_ENV=prod > .env.local
|
||||
echo APP_DEBUG=0 >> .env.local
|
||||
|
||||
# Удалить тесты и другие ненужные файлы
|
||||
rm -Rf tests/
|
||||
|
||||
# Установить зависимости
|
||||
composer install --ignore-platform-reqs --no-dev -a
|
||||
|
||||
# Оптимизировать .env
|
||||
composer dump-env prod
|
||||
```
|
||||
|
||||
### Настройка конфигурации
|
||||
|
||||
Чтобы настроить [конфигурацию](config.md), вы можете разместить файлы `Caddyfile` и `php.ini` в основной директории приложения (`$TMPDIR/my-prepared-app` в примере выше).
|
||||
|
||||
## Создание бинарного файла для Linux
|
||||
|
||||
Самый простой способ создать бинарный файл для Linux — использовать предоставленный Docker-билдер.
|
||||
|
||||
1. Создайте файл `static-build.Dockerfile` в репозитории вашего приложения:
|
||||
|
||||
```dockerfile
|
||||
FROM --platform=linux/amd64 dunglas/frankenphp:static-builder
|
||||
|
||||
# Скопировать приложение
|
||||
WORKDIR /go/src/app/dist/app
|
||||
COPY . .
|
||||
|
||||
# Сборка статического бинарного файла
|
||||
WORKDIR /go/src/app/
|
||||
RUN EMBED=dist/app/ ./build-static.sh
|
||||
```
|
||||
|
||||
> [!CAUTION]
|
||||
>
|
||||
> Некоторые `.dockerignore` файлы (например, [Symfony Docker `.dockerignore`](https://github.com/dunglas/symfony-docker/blob/main/.dockerignore))
|
||||
> игнорируют директорию `vendor/` и файлы `.env`. Перед сборкой убедитесь, что `.dockerignore` файл настроен корректно или удалён.
|
||||
|
||||
2. Соберите образ:
|
||||
|
||||
```console
|
||||
docker build -t static-app -f static-build.Dockerfile .
|
||||
```
|
||||
|
||||
3. Извлеките бинарный файл:
|
||||
|
||||
```console
|
||||
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
|
||||
```
|
||||
|
||||
Созданный бинарный файл сохранится в текущей директории под именем `my-app`.
|
||||
|
||||
## Создание бинарного файла для других ОС
|
||||
|
||||
Если вы не хотите использовать Docker или хотите собрать бинарный файл для macOS, используйте предоставленный скрипт:
|
||||
|
||||
```console
|
||||
git clone https://github.com/dunglas/frankenphp
|
||||
cd frankenphp
|
||||
EMBED=/path/to/your/app ./build-static.sh
|
||||
```
|
||||
|
||||
Итоговый бинарный файл будет находиться в директории `dist/` под именем `frankenphp-<os>-<arch>`.
|
||||
|
||||
## Использование бинарного файла
|
||||
|
||||
Готово! Файл `my-app` (или `dist/frankenphp-<os>-<arch>` для других ОС) содержит ваше автономное приложение.
|
||||
|
||||
Для запуска веб-приложения выполните:
|
||||
|
||||
```console
|
||||
./my-app php-server
|
||||
```
|
||||
|
||||
Если ваше приложение содержит [worker-скрипт](worker.md), запустите его следующим образом:
|
||||
|
||||
```console
|
||||
./my-app php-server --worker public/index.php
|
||||
```
|
||||
|
||||
Чтобы включить HTTPS (Let's Encrypt автоматически создаст сертификат), HTTP/2 и HTTP/3, укажите доменное имя:
|
||||
|
||||
```console
|
||||
./my-app php-server --domain localhost
|
||||
```
|
||||
|
||||
Вы также можете запускать PHP-скрипты CLI, встроенные в бинарный файл:
|
||||
|
||||
```console
|
||||
./my-app php-cli bin/console
|
||||
```
|
||||
|
||||
## PHP-расширения
|
||||
|
||||
По умолчанию скрипт собирает расширения, указанные в `composer.json` вашего проекта.
|
||||
Если файла `composer.json` нет, собираются стандартные расширения, как указано в [документации по статической сборке](static.md).
|
||||
|
||||
Чтобы настроить список расширений, используйте переменную окружения `PHP_EXTENSIONS`.
|
||||
|
||||
## Настройка сборки
|
||||
|
||||
[Ознакомьтесь с документацией по статической сборке](static.md), чтобы узнать, как настроить бинарный файл (расширения, версию PHP и т.д.).
|
||||
|
||||
## Распространение бинарного файла
|
||||
|
||||
На Linux созданный бинарный файл сжимается с помощью [UPX](https://upx.github.io).
|
||||
|
||||
На Mac для уменьшения размера файла перед отправкой его можно сжать. Рекомендуется использовать `xz`.
|
||||
30
docs/ru/github-actions.md
Normal file
30
docs/ru/github-actions.md
Normal file
@@ -0,0 +1,30 @@
|
||||
# Использование GitHub Actions
|
||||
|
||||
Этот репозиторий автоматически собирает и публикует Docker-образы в [Docker Hub](https://hub.docker.com/r/dunglas/frankenphp) для каждого одобренного pull request или вашего собственного форка после настройки.
|
||||
|
||||
## Настройка GitHub Actions
|
||||
|
||||
В настройках репозитория, в разделе "Secrets", добавьте следующие секреты:
|
||||
|
||||
- `REGISTRY_LOGIN_SERVER`: Docker-реестр, который будет использоваться (например, `docker.io`).
|
||||
- `REGISTRY_USERNAME`: Имя пользователя для входа в реестр (например, `dunglas`).
|
||||
- `REGISTRY_PASSWORD`: Пароль для входа в реестр (например, токен доступа).
|
||||
- `IMAGE_NAME`: Имя образа (например, `dunglas/frankenphp`).
|
||||
|
||||
## Сборка и загрузка образа
|
||||
|
||||
1. Создайте Pull Request или выполните push в ваш форк.
|
||||
2. GitHub Actions соберёт образ и выполнит тесты.
|
||||
3. Если сборка пройдёт успешно, образ будет отправлен в реестр с тегом `pr-x`, где `x` — номер PR.
|
||||
|
||||
## Развёртывание образа
|
||||
|
||||
1. После слияния Pull Request GitHub Actions выполнит повторные тесты и соберёт новый образ.
|
||||
2. Если сборка пройдёт успешно, тег `main` будет обновлён в Docker-реестре.
|
||||
|
||||
## Релизы
|
||||
|
||||
1. Создайте новый тег в репозитории.
|
||||
2. GitHub Actions соберёт образ и выполнит тесты.
|
||||
3. Если сборка пройдёт успешно, образ будет отправлен в реестр с именем тега (например, `v1.2.3` и `v1.2` будут созданы).
|
||||
4. Также будет обновлён тег `latest`.
|
||||
140
docs/ru/known-issues.md
Normal file
140
docs/ru/known-issues.md
Normal file
@@ -0,0 +1,140 @@
|
||||
# Известные проблемы
|
||||
|
||||
## Неподдерживаемые расширения PHP
|
||||
|
||||
Следующие расширения не совместимы с FrankenPHP:
|
||||
|
||||
| Название | Причина | Альтернативы |
|
||||
| ----------------------------------------------------------------------------------------------------------- | ---------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
|
||||
| [imap](https://www.php.net/manual/en/imap.installation.php) | Не поддерживает потокобезопасность | [javanile/php-imap2](https://github.com/javanile/php-imap2), [webklex/php-imap](https://github.com/Webklex/php-imap) |
|
||||
| [newrelic](https://docs.newrelic.com/docs/apm/agents/php-agent/getting-started/introduction-new-relic-php/) | Не поддерживает потокобезопасность | - |
|
||||
|
||||
## Проблемные расширения PHP
|
||||
|
||||
Следующие расширения имеют известные ошибки или могут вести себя непредсказуемо при использовании с FrankenPHP:
|
||||
|
||||
| Название | Проблема |
|
||||
| ------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| [ext-openssl](https://www.php.net/manual/en/book.openssl.php) | При использовании статической сборки FrankenPHP (на базе musl libc) расширение OpenSSL может аварийно завершаться при высокой нагрузке. Решение — использовать динамически связанную сборку (например, ту, что используется в Docker-образах). Ошибка [отслеживается сообществом PHP](https://github.com/php/php-src/issues/13648). |
|
||||
|
||||
## `get_browser`
|
||||
|
||||
Функция [get_browser()](https://www.php.net/manual/en/function.get-browser.php) начинает работать медленно через некоторое время. Решение — кэшировать результаты для каждого User-Agent, например, с помощью [APCu](https://www.php.net/manual/en/book.apcu.php), так как они статичны.
|
||||
|
||||
## Автономные бинарные файлы и образы на базе Alpine
|
||||
|
||||
Автономные бинарные файлы и образы на базе Alpine (`dunglas/frankenphp:*-alpine`) используют [musl libc](https://musl.libc.org/) вместо [glibc](https://www.etalabs.net/compare_libcs.html) для уменьшения размера бинарных файлов. Это может вызвать проблемы совместимости. В частности, флаг `GLOB_BRACE` в функции glob [не поддерживается](https://www.php.net/manual/en/function.glob.php).
|
||||
|
||||
## Использование `https://127.0.0.1` с Docker
|
||||
|
||||
По умолчанию FrankenPHP генерирует TLS-сертификат для `localhost`, что является самым простым и рекомендуемым вариантом для локальной разработки.
|
||||
|
||||
Если вы всё же хотите использовать `127.0.0.1`, настройте генерацию сертификата, указав в переменной окружения `SERVER_NAME` значение `127.0.0.1`.
|
||||
|
||||
Однако этого может не хватить при использовании Docker из-за [особенностей его сетевой системы](https://docs.docker.com/network/). Возможна ошибка TLS вида:
|
||||
`curl: (35) LibreSSL/3.3.6: error:1404B438:SSL routines:ST_CONNECT:tlsv1 alert internal error`.
|
||||
|
||||
Если вы используете Linux, можно воспользоваться [host-драйвером](https://docs.docker.com/network/network-tutorial-host/):
|
||||
|
||||
```console
|
||||
docker run \
|
||||
-e SERVER_NAME="127.0.0.1" \
|
||||
-v $PWD:/app/public \
|
||||
--network host \
|
||||
dunglas/frankenphp
|
||||
```
|
||||
|
||||
Host-драйвер не поддерживается на Mac и Windows. На этих платформах нужно определить IP-адрес контейнера и включить его в `SERVER_NAME`.
|
||||
|
||||
Выполните команду `docker network inspect bridge`, найдите ключ `Containers` и определите последний присвоенный IP из `IPv4Address`. Увеличьте его на единицу. Если контейнеров нет, первый IP обычно `172.17.0.2`.
|
||||
|
||||
Включите этот IP в переменную окружения `SERVER_NAME`:
|
||||
|
||||
```console
|
||||
docker run \
|
||||
-e SERVER_NAME="127.0.0.1, 172.17.0.3" \
|
||||
-v $PWD:/app/public \
|
||||
-p 80:80 -p 443:443 -p 443:443/udp \
|
||||
dunglas/frankenphp
|
||||
```
|
||||
|
||||
> [!CAUTION]
|
||||
> Обязательно замените `172.17.0.3` на IP, который будет присвоен вашему контейнеру.
|
||||
|
||||
Теперь вы должны иметь доступ к `https://127.0.0.1`.
|
||||
|
||||
Если это не так, запустите FrankenPHP в режиме отладки:
|
||||
|
||||
```console
|
||||
docker run \
|
||||
-e CADDY_GLOBAL_OPTIONS="debug" \
|
||||
-e SERVER_NAME="127.0.0.1" \
|
||||
-v $PWD:/app/public \
|
||||
-p 80:80 -p 443:443 -p 443:443/udp \
|
||||
dunglas/frankenphp
|
||||
```
|
||||
|
||||
## Скрипты Composer с использованием `@php`
|
||||
|
||||
[Скрипты Composer](https://getcomposer.org/doc/articles/scripts.md) могут вызывать PHP для выполнения задач, например, в [проекте Laravel](laravel.md) для команды `@php artisan package:discover --ansi`.
|
||||
Это [на данный момент не поддерживается](https://github.com/dunglas/frankenphp/issues/483#issuecomment-1899890915) по двум причинам:
|
||||
|
||||
- Composer не знает, как вызывать бинарный файл FrankenPHP;
|
||||
- Composer может добавлять настройки PHP через флаг `-d`, который FrankenPHP пока не поддерживает.
|
||||
|
||||
Решение — создать shell-скрипт в `/usr/local/bin/php`, который удаляет неподдерживаемые параметры и вызывает FrankenPHP:
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
args=("$@")
|
||||
index=0
|
||||
for i in "$@"
|
||||
do
|
||||
if [ "$i" == "-d" ]; then
|
||||
unset 'args[$index]'
|
||||
unset 'args[$index+1]'
|
||||
fi
|
||||
index=$((index+1))
|
||||
done
|
||||
|
||||
/usr/local/bin/frankenphp php-cli ${args[@]}
|
||||
```
|
||||
|
||||
Затем установите переменную окружения `PHP_BINARY` на путь к нашему скрипту `php` и запустите Composer:
|
||||
|
||||
```console
|
||||
export PHP_BINARY=/usr/local/bin/php
|
||||
composer install
|
||||
```
|
||||
|
||||
## TLS/SSL: проблемы со статическими бинарными файлами
|
||||
|
||||
При использовании статических бинарных файлов могут возникать следующие ошибки TLS, например, при отправке писем через STARTTLS:
|
||||
|
||||
```text
|
||||
Unable to connect with STARTTLS: stream_socket_enable_crypto(): SSL operation failed with code 5. OpenSSL Error messages:
|
||||
error:80000002:system library::No such file or directory
|
||||
error:80000002:system library::No such file or directory
|
||||
error:80000002:system library::No such file or directory
|
||||
error:0A000086:SSL routines::certificate verify failed
|
||||
```
|
||||
|
||||
Статический бинарный файл не включает TLS-сертификаты, поэтому необходимо указать OpenSSL местоположение локальных сертификатов CA.
|
||||
|
||||
Выполните [`openssl_get_cert_locations()`](https://www.php.net/manual/en/function.openssl-get-cert-locations.php), чтобы определить, где должны находиться сертификаты CA, и поместите их туда.
|
||||
|
||||
> [!WARNING]
|
||||
> Веб и CLI контексты могут иметь разные настройки.
|
||||
> Запустите `openssl_get_cert_locations()` в нужном контексте.
|
||||
|
||||
[Сертификаты CA, извлечённые из Mozilla, можно скачать с сайта curl](https://curl.se/docs/caextract.html).
|
||||
|
||||
Кроме того, многие дистрибутивы, такие как Debian, Ubuntu и Alpine, предоставляют пакеты `ca-certificates`, содержащие эти сертификаты.
|
||||
|
||||
Также можно использовать переменные `SSL_CERT_FILE` и `SSL_CERT_DIR`, чтобы указать OpenSSL, где искать сертификаты CA:
|
||||
|
||||
```console
|
||||
# Установите переменные окружения для TLS-сертификатов
|
||||
export SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt
|
||||
export SSL_CERT_DIR=/etc/ssl/certs
|
||||
```
|
||||
176
docs/ru/laravel.md
Normal file
176
docs/ru/laravel.md
Normal file
@@ -0,0 +1,176 @@
|
||||
# Laravel
|
||||
|
||||
## Docker
|
||||
|
||||
Запустить [Laravel](https://laravel.com) веб-приложение с FrankenPHP очень просто: достаточно смонтировать проект в директорию `/app` официального Docker-образа.
|
||||
|
||||
Выполните эту команду из корневой директории вашего Laravel-приложения:
|
||||
|
||||
```console
|
||||
docker run -p 80:80 -p 443:443 -p 443:443/udp -v $PWD:/app dunglas/frankenphp
|
||||
```
|
||||
|
||||
И наслаждайтесь!
|
||||
|
||||
## Локальная установка
|
||||
|
||||
Вы также можете запустить ваши Laravel-проекты с FrankenPHP на локальной машине:
|
||||
|
||||
1. [Скачайте бинарный файл для вашей системы](README.md#автономный-бинарный-файл)
|
||||
2. Добавьте следующую конфигурацию в файл с именем `Caddyfile` в корневой директории вашего Laravel-проекта:
|
||||
|
||||
```caddyfile
|
||||
{
|
||||
frankenphp
|
||||
}
|
||||
|
||||
# Доменное имя вашего сервера
|
||||
localhost {
|
||||
# Укажите веб-корень как директорию public/
|
||||
root public/
|
||||
# Включите сжатие (опционально)
|
||||
encode zstd br gzip
|
||||
# Выполняйте PHP-файлы из директории public/ и обслуживайте статические файлы
|
||||
php_server
|
||||
}
|
||||
```
|
||||
|
||||
3. Запустите FrankenPHP из корневой директории вашего Laravel-проекта: `frankenphp run`
|
||||
|
||||
## Laravel Octane
|
||||
|
||||
Octane можно установить с помощью менеджера пакетов Composer:
|
||||
|
||||
```console
|
||||
composer require laravel/octane
|
||||
```
|
||||
|
||||
После установки Octane выполните Artisan-команду `octane:install`, которая создаст конфигурационный файл Octane в вашем приложении:
|
||||
|
||||
```console
|
||||
php artisan octane:install --server=frankenphp
|
||||
```
|
||||
|
||||
Сервер Octane можно запустить с помощью Artisan-команды `octane:frankenphp`:
|
||||
|
||||
```console
|
||||
php artisan octane:frankenphp
|
||||
```
|
||||
|
||||
Команда `octane:frankenphp` поддерживает следующие опции:
|
||||
|
||||
- `--host`: IP-адрес, к которому должен привязаться сервер (по умолчанию: `127.0.0.1`)
|
||||
- `--port`: Порт, на котором сервер будет доступен (по умолчанию: `8000`)
|
||||
- `--admin-port`: Порт, на котором будет доступен административный сервер (по умолчанию: `2019`)
|
||||
- `--workers`: Количество worker-скриптов для обработки запросов (по умолчанию: `auto`)
|
||||
- `--max-requests`: Количество запросов, обрабатываемых перед перезагрузкой сервера (по умолчанию: `500`)
|
||||
- `--caddyfile`: Путь к файлу `Caddyfile` FrankenPHP (по умолчанию: [stubbed `Caddyfile` в Laravel Octane](https://github.com/laravel/octane/blob/2.x/src/Commands/stubs/Caddyfile))
|
||||
- `--https`: Включить HTTPS, HTTP/2 и HTTP/3, а также автоматически генерировать и обновлять сертификаты
|
||||
- `--http-redirect`: Включить редирект с HTTP на HTTPS (включается только при передаче --https)
|
||||
- `--watch`: Автоматически перезагружать сервер при изменении приложения
|
||||
- `--poll`: Использовать опрос файловой системы для отслеживания изменений в файлах через сеть
|
||||
- `--log-level`: Установить уровень логирования, используя встроенный логгер Caddy
|
||||
|
||||
> [!TIP]
|
||||
> Чтобы получить структурированные JSON-логи (полезно при использовании решений для анализа логов), явно укажите опцию `--log-level`.
|
||||
|
||||
Подробнее о [Laravel Octane читайте в официальной документации](https://laravel.com/docs/octane).
|
||||
|
||||
## Laravel-приложения как автономные бинарные файлы
|
||||
|
||||
Используя [возможность встраивания приложений в FrankenPHP](embed.md), можно распространять Laravel-приложения как автономные бинарные файлы.
|
||||
|
||||
Следуйте этим шагам, чтобы упаковать ваше Laravel-приложение в автономный бинарный файл для Linux:
|
||||
|
||||
1. Создайте файл с именем `static-build.Dockerfile` в репозитории вашего приложения:
|
||||
|
||||
```dockerfile
|
||||
FROM --platform=linux/amd64 dunglas/frankenphp:static-builder
|
||||
|
||||
# Скопируйте ваше приложение
|
||||
WORKDIR /go/src/app/dist/app
|
||||
COPY . .
|
||||
|
||||
# Удалите тесты и другие ненужные файлы, чтобы сэкономить место
|
||||
# В качестве альтернативы добавьте эти файлы в .dockerignore
|
||||
RUN rm -Rf tests/
|
||||
|
||||
# Скопируйте файл .env
|
||||
RUN cp .env.example .env
|
||||
# Измените APP_ENV и APP_DEBUG для продакшна
|
||||
RUN sed -i'' -e 's/^APP_ENV=.*/APP_ENV=production/' -e 's/^APP_DEBUG=.*/APP_DEBUG=false/' .env
|
||||
|
||||
# Внесите другие изменения в файл .env, если необходимо
|
||||
|
||||
# Установите зависимости
|
||||
RUN composer install --ignore-platform-reqs --no-dev -a
|
||||
|
||||
# Соберите статический бинарный файл
|
||||
WORKDIR /go/src/app/
|
||||
RUN EMBED=dist/app/ ./build-static.sh
|
||||
```
|
||||
|
||||
> [!CAUTION]
|
||||
>
|
||||
> Некоторые `.dockerignore` файлы могут игнорировать директорию `vendor/` и файлы `.env`. Убедитесь, что вы скорректировали или удалили `.dockerignore` перед сборкой.
|
||||
|
||||
2. Соберите:
|
||||
|
||||
```console
|
||||
docker build -t static-laravel-app -f static-build.Dockerfile .
|
||||
```
|
||||
|
||||
3. Извлеките бинарный файл:
|
||||
|
||||
```console
|
||||
docker cp $(docker create --name static-laravel-app-tmp static-laravel-app):/go/src/app/dist/frankenphp-linux-x86_64 frankenphp ; docker rm static-laravel-app-tmp
|
||||
```
|
||||
|
||||
4. Заполните кеши:
|
||||
|
||||
```console
|
||||
frankenphp php-cli artisan optimize
|
||||
```
|
||||
|
||||
5. Запустите миграции базы данных (если есть):
|
||||
|
||||
```console
|
||||
frankenphp php-cli artisan migrate
|
||||
```
|
||||
|
||||
6. Сгенерируйте секретный ключ приложения:
|
||||
|
||||
```console
|
||||
frankenphp php-cli artisan key:generate
|
||||
```
|
||||
|
||||
7. Запустите сервер:
|
||||
|
||||
```console
|
||||
frankenphp php-server
|
||||
```
|
||||
|
||||
Ваше приложение готово!
|
||||
|
||||
Узнайте больше о доступных опциях и о том, как собирать бинарные файлы для других ОС в [документации по встраиванию приложений](embed.md).
|
||||
|
||||
### Изменение пути хранения
|
||||
|
||||
По умолчанию Laravel сохраняет загруженные файлы, кеши, логи и другие данные в директории `storage/` приложения. Это неудобно для встроенных приложений, так как каждая новая версия будет извлекаться в другую временную директорию.
|
||||
|
||||
Установите переменную окружения `LARAVEL_STORAGE_PATH` (например, в вашем `.env` файле) или вызовите метод `Illuminate\Foundation\Application::useStoragePath()`, чтобы использовать директорию за пределами временной директории.
|
||||
|
||||
### Запуск Octane как автономный бинарный файл
|
||||
|
||||
Можно даже упаковать приложения Laravel Octane как автономный бинарный файл!
|
||||
|
||||
Для этого [установите Octane правильно](#laravel-octane) и следуйте шагам, описанным в [предыдущем разделе](#laravel-приложения-как-автономные-бинарные-файлы).
|
||||
|
||||
Затем, чтобы запустить FrankenPHP в worker-режиме через Octane, выполните:
|
||||
|
||||
```console
|
||||
PATH="$PWD:$PATH" frankenphp php-cli artisan octane:frankenphp
|
||||
```
|
||||
|
||||
> [!CAUTION]
|
||||
> Для работы команды автономный бинарник **обязательно** должен быть назван `frankenphp`, так как Octane требует наличия программы с именем `frankenphp` в PATH.
|
||||
12
docs/ru/mercure.md
Normal file
12
docs/ru/mercure.md
Normal file
@@ -0,0 +1,12 @@
|
||||
# Real-time режим
|
||||
|
||||
FrankenPHP поставляется с встроенным хабом [Mercure](https://mercure.rocks)!
|
||||
Mercure позволяет отправлять события в режиме реального времени на все подключённые устройства: они мгновенно получат JavaScript-событие.
|
||||
|
||||
Не требуются JS-библиотеки или SDK!
|
||||
|
||||

|
||||
|
||||
Чтобы включить хаб Mercure, обновите `Caddyfile` в соответствии с инструкциями [на сайте Mercure](https://mercure.rocks/docs/hub/config).
|
||||
|
||||
Для отправки обновлений Mercure из вашего кода мы рекомендуем использовать [Symfony Mercure Component](https://symfony.com/components/Mercure) (для его использования не требуется полный стек Symfony).
|
||||
15
docs/ru/metrics.md
Normal file
15
docs/ru/metrics.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# Метрики
|
||||
|
||||
При включении [метрик Caddy](https://caddyserver.com/docs/metrics) FrankenPHP предоставляет следующие метрики:
|
||||
|
||||
- `frankenphp_[worker]_total_workers`: Общее количество worker-скриптов.
|
||||
- `frankenphp_[worker]_busy_workers`: Количество worker-скриптов, которые в данный момент обрабатывают запрос.
|
||||
- `frankenphp_[worker]_worker_request_time`: Время, затраченное всеми worker-скриптами на обработку запросов.
|
||||
- `frankenphp_[worker]_worker_request_count`: Количество запросов, обработанных всеми worker-скриптами.
|
||||
- `frankenphp_[worker]_ready_workers`: Количество worker-скриптов, которые вызвали `frankenphp_handle_request` хотя бы один раз.
|
||||
- `frankenphp_[worker]_worker_crashes`: Количество случаев неожиданного завершения worker-скриптов.
|
||||
- `frankenphp_[worker]_worker_restarts`: Количество случаев, когда worker-скрипт был перезапущен целенаправленно.
|
||||
- `frankenphp_total_threads`: Общее количество потоков PHP.
|
||||
- `frankenphp_busy_threads`: Количество потоков PHP, которые в данный момент обрабатывают запрос (работающие worker-скрипты всегда используют поток).
|
||||
|
||||
Для метрик worker-скриптов плейсхолдер `[worker]` заменяется на путь к Worker-скрипту, указанному в Caddyfile.
|
||||
101
docs/ru/performance.md
Normal file
101
docs/ru/performance.md
Normal file
@@ -0,0 +1,101 @@
|
||||
# Производительность
|
||||
|
||||
По умолчанию FrankenPHP предлагает хороший баланс между производительностью и удобством использования.
|
||||
Однако, используя подходящую конфигурацию, можно существенно улучшить производительность.
|
||||
|
||||
## Количество потоков и worker-скриптов
|
||||
|
||||
По умолчанию FrankenPHP запускает потоков и worker-скриптов (в worker режиме) вдвое больше, чем количество доступных процессорных ядер.
|
||||
|
||||
Оптимальные значения зависят от структуры вашего приложения, его функциональности и аппаратного обеспечения.
|
||||
Мы настоятельно рекомендуем изменить эти значения.
|
||||
|
||||
Чтобы найти подходящие параметры, лучше всего провести нагрузочные тесты, имитирующие реальный трафик.
|
||||
Хорошими инструментами для этого являются [k6](https://k6.io) и [Gatling](https://gatling.io).
|
||||
|
||||
Чтобы настроить количество потоков, используйте опцию `num_threads` в директивах `php_server` и `php`.
|
||||
Для изменения количества worker-скриптов используйте опцию `num` в секции `worker` директивы `frankenphp`.
|
||||
|
||||
## Worker режим
|
||||
|
||||
Включение [Worker режима](worker.md) значительно улучшает производительность,
|
||||
но ваше приложение должно быть адаптировано для совместимости с этим режимом:
|
||||
необходимо создать worker-скрипт и убедиться, что приложение не имеет утечек памяти.
|
||||
|
||||
## Избегайте использования musl
|
||||
|
||||
Статические бинарники, которые мы предоставляем, а также Alpine Linux-вариант официальных Docker-образов используют [библиотеку musl libc](https://musl.libc.org).
|
||||
|
||||
Известно, что PHP [значительно медленнее работает](https://gitlab.alpinelinux.org/alpine/aports/-/issues/14381) с этой библиотекой по сравнению с традиционной GNU libc, особенно при компиляции в ZTS режиме (потокобезопасный режим), который требуется для FrankenPHP.
|
||||
|
||||
Кроме того, [некоторые ошибки проявляются исключительно при использовании musl](https://github.com/php/php-src/issues?q=sort%3Aupdated-desc+is%3Aissue+is%3Aopen+label%3ABug+musl).
|
||||
|
||||
В производственной среде настоятельно рекомендуется использовать glibc.
|
||||
|
||||
Это можно сделать, используя Debian Docker-образы (по умолчанию) и [компилируя FrankenPHP из исходников](compile.md).
|
||||
|
||||
В качестве альтернативы мы предоставляем статические бинарники, скомпилированные с [аллокатором mimalloc](https://github.com/microsoft/mimalloc), что делает FrankenPHP+musl быстрее (но всё же медленнее, чем FrankenPHP+glibc).
|
||||
|
||||
## Настройка среды выполнения Go
|
||||
|
||||
FrankenPHP написан на языке Go.
|
||||
|
||||
В большинстве случаев среда выполнения Go не требует особой настройки, но в некоторых ситуациях специфическая конфигурация может улучшить производительность.
|
||||
|
||||
Рекомендуется установить переменную окружения `GODEBUG` в значение `cgocheck=0` (по умолчанию в Docker-образах FrankenPHP).
|
||||
|
||||
Если вы запускаете FrankenPHP в контейнерах (Docker, Kubernetes, LXC и т.д.) и ограничиваете доступную память, установите переменную окружения `GOMEMLIMIT` в значение доступного объёма памяти.
|
||||
|
||||
Для более детальной информации ознакомьтесь с [документацией Go по этой теме](https://pkg.go.dev/runtime#hdr-Environment_Variables).
|
||||
|
||||
## `file_server`
|
||||
|
||||
По умолчанию директива `php_server` автоматически настраивает файловый сервер для обслуживания статических файлов (ресурсов), хранящихся в корневой директории.
|
||||
|
||||
Эта функция удобна, но имеет издержки. Чтобы отключить её, используйте следующую конфигурацию:
|
||||
|
||||
```caddyfile
|
||||
php_server {
|
||||
file_server off
|
||||
}
|
||||
```
|
||||
|
||||
## Плейсхолдеры
|
||||
|
||||
Вы можете использовать [плейсхолдеры](https://caddyserver.com/docs/conventions#placeholders) в директивах `root` и `env`.
|
||||
Однако это предотвращает кеширование значений и существенно снижает производительность.
|
||||
|
||||
По возможности избегайте использования плейсхолдеров в этих директивах.
|
||||
|
||||
## `resolve_root_symlink`
|
||||
|
||||
По умолчанию, если корневая директория документа является символьной ссылкой, она автоматически разрешается FrankenPHP (это необходимо для корректной работы PHP).
|
||||
Если корневая директория документа не является символьной ссылкой, вы можете отключить эту функцию.
|
||||
|
||||
```caddyfile
|
||||
php_server {
|
||||
resolve_root_symlink false
|
||||
}
|
||||
```
|
||||
|
||||
Это улучшит производительность, если директива `root` содержит [плейсхолдеры](https://caddyserver.com/docs/conventions#placeholders).
|
||||
В остальных случаях прирост производительности будет минимальным.
|
||||
|
||||
## Логи
|
||||
|
||||
Логирование, безусловно, полезно, но требует операций ввода-вывода и выделения памяти, что значительно снижает производительность.
|
||||
Убедитесь, что вы [правильно настроили уровень логирования](https://caddyserver.com/docs/caddyfile/options#log) и логируете только необходимое.
|
||||
|
||||
## Производительность PHP
|
||||
|
||||
FrankenPHP использует официальный интерпретатор PHP.
|
||||
Все стандартные оптимизации производительности PHP применимы к FrankenPHP.
|
||||
|
||||
В частности:
|
||||
|
||||
- убедитесь, что [OPcache](https://www.php.net/manual/en/book.opcache.php) установлен, включён и настроен должным образом;
|
||||
- включите [оптимизацию автозагрузки Composer](https://getcomposer.org/doc/articles/autoloader-optimization.md);
|
||||
- убедитесь, что кеш `realpath` достаточно велик для нужд вашего приложения;
|
||||
- используйте [предварительную загрузку](https://www.php.net/manual/en/opcache.preloading.php).
|
||||
|
||||
Для более детальной информации ознакомьтесь с [документацией Symfony о производительности](https://symfony.com/doc/current/performance.html) (большинство советов полезны даже если вы не используете Symfony).
|
||||
125
docs/ru/production.md
Normal file
125
docs/ru/production.md
Normal file
@@ -0,0 +1,125 @@
|
||||
# Деплой в продакшен
|
||||
|
||||
В этом руководстве мы рассмотрим, как развернуть PHP-приложение на одном сервере с использованием Docker Compose.
|
||||
|
||||
Если вы используете Symfony, рекомендуется прочитать раздел "[Deploy in production](https://github.com/dunglas/symfony-docker/blob/main/docs/production.md)" документации проекта Symfony Docker (в котором используется FrankenPHP).
|
||||
|
||||
Если вы используете API Platform (который также работает с FrankenPHP), ознакомьтесь с [документацией по деплою этого фреймворка](https://api-platform.com/docs/deployment/).
|
||||
|
||||
## Подготовка приложения
|
||||
|
||||
Сначала создайте файл `Dockerfile` в корневой директории вашего PHP-проекта:
|
||||
|
||||
```dockerfile
|
||||
FROM dunglas/frankenphp
|
||||
|
||||
# Замените "your-domain-name.example.com" на ваш домен
|
||||
ENV SERVER_NAME=your-domain-name.example.com
|
||||
# Если вы хотите отключить HTTPS, используйте вместо этого:
|
||||
#ENV SERVER_NAME=:80
|
||||
|
||||
# Включите настройки PHP для продакшн
|
||||
RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"
|
||||
|
||||
# Скопируйте файлы PHP вашего проекта в публичную директорию
|
||||
COPY . /app/public
|
||||
# Если вы используете Symfony или Laravel, необходимо скопировать весь проект:
|
||||
#COPY . /app
|
||||
```
|
||||
|
||||
Ознакомьтесь с разделом "[Создание кастомных Docker-образов](docker.md)" для получения дополнительных подробностей и настроек, а также для установки PHP-расширений и модулей Caddy.
|
||||
|
||||
Если ваш проект использует Composer, убедитесь, что он включён в Docker-образ, и установите все зависимости.
|
||||
|
||||
Затем добавьте файл `compose.yaml`:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
php:
|
||||
image: dunglas/frankenphp
|
||||
restart: always
|
||||
ports:
|
||||
- "80:80" # HTTP
|
||||
- "443:443" # HTTPS
|
||||
- "443:443/udp" # HTTP/3
|
||||
volumes:
|
||||
- caddy_data:/data
|
||||
- caddy_config:/config
|
||||
|
||||
# Томы, необходимые для сертификатов и конфигурации Caddy
|
||||
volumes:
|
||||
caddy_data:
|
||||
caddy_config:
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
>
|
||||
> Примеры выше предназначены для использования в продакшне.
|
||||
> В процессе разработки вы можете использовать том для монтирования, другую конфигурацию PHP и другое значение для переменной окружения `SERVER_NAME`.
|
||||
>
|
||||
> Посмотрите проект [Symfony Docker](https://github.com/dunglas/symfony-docker) (который использует FrankenPHP) для более сложного примера с использованием мультистейдж-образов, Composer, дополнительных PHP-расширений и т.д.
|
||||
|
||||
Наконец, если вы используете Git, закоммитьте эти файлы и отправьте их в репозиторий.
|
||||
|
||||
## Подготовка сервера
|
||||
|
||||
Для деплоя приложения в продакшн требуется сервер. В этом руководстве мы будем использовать виртуальную машину, предоставляемую DigitalOcean, но подойдёт любой Linux-сервер.
|
||||
Если у вас уже есть Linux-сервер с установленным Docker, вы можете сразу перейти к [следующему разделу](#настройка-доменного-имени).
|
||||
|
||||
В противном случае, используйте [эту ссылку](https://m.do.co/c/5d8aabe3ab80), чтобы получить $200 на баланс, создайте аккаунт, затем нажмите "Create a Droplet".
|
||||
Перейдите во вкладку "Marketplace" в разделе "Choose an image" и найдите приложение "Docker". Это создаст сервер на Ubuntu с установленными Docker и Docker Compose.
|
||||
|
||||
Для тестов подойдут самые дешёвые тарифы. Для реального продакшна выберите тариф из раздела "general purpose" в зависимости от ваших потребностей.
|
||||
|
||||

|
||||
|
||||
После этого подключитесь к серверу через SSH:
|
||||
|
||||
```console
|
||||
ssh root@<droplet-ip>
|
||||
```
|
||||
|
||||
## Настройка доменного имени
|
||||
|
||||
В большинстве случаев вам потребуется связать доменное имя с вашим сайтом.
|
||||
Создайте запись DNS типа `A`, указывающую на IP вашего сервера:
|
||||
|
||||
```dns
|
||||
your-domain-name.example.com. IN A 207.154.233.113
|
||||
```
|
||||
|
||||
Пример настройки через DigitalOcean ("Networking" > "Domains"):
|
||||
|
||||

|
||||
|
||||
> [!NOTE]
|
||||
>
|
||||
> Let's Encrypt, сервис, используемый FrankenPHP для автоматической генерации TLS-сертификатов, не поддерживает использование IP-адресов. Для работы необходим домен.
|
||||
|
||||
## Деплой
|
||||
|
||||
Скопируйте ваш проект на сервер с помощью `git clone`, `scp` или любого другого инструмента.
|
||||
Если вы используете GitHub, настройте [ключи развёртывания](https://docs.github.com/en/free-pro-team@latest/developers/overview/managing-deploy-keys#deploy-keys).
|
||||
|
||||
Пример с использованием Git:
|
||||
|
||||
```console
|
||||
git clone git@github.com:<username>/<project-name>.git
|
||||
```
|
||||
|
||||
Перейдите в директорию проекта и запустите приложение в режиме продакшн:
|
||||
|
||||
```console
|
||||
docker compose up -d --wait
|
||||
```
|
||||
|
||||
Сервер готов, а HTTPS-сертификат был автоматически сгенерирован. Перейдите на `https://your-domain-name.example.com` и наслаждайтесь!
|
||||
|
||||
> [!CAUTION]
|
||||
>
|
||||
> Docker может кэшировать слои. Убедитесь, что вы используете актуальную сборку, или используйте опцию `--no-cache` для предотвращения проблем с кэшем.
|
||||
|
||||
## Деплой на несколько узлов
|
||||
|
||||
Если вам нужно развернуть приложение на кластер машин, используйте [Docker Swarm](https://docs.docker.com/engine/swarm/stack-deploy/), который совместим с предоставленными файлами Compose.
|
||||
Для деплоя на Kubernetes ознакомьтесь с [Helm-чартом API Platform](https://api-platform.com/docs/deployment/kubernetes/), который использует FrankenPHP.
|
||||
100
docs/ru/static.md
Normal file
100
docs/ru/static.md
Normal file
@@ -0,0 +1,100 @@
|
||||
# Создание статических бинарных файлов
|
||||
|
||||
Вместо использования локальной установки библиотеки PHP, можно создать статическую сборку FrankenPHP благодаря проекту [static-php-cli](https://github.com/crazywhalecc/static-php-cli) (несмотря на название, проект поддерживает все SAPI, а не только CLI).
|
||||
|
||||
С помощью этого метода создаётся единый переносимый бинарник, который включает PHP-интерпретатор, веб-сервер Caddy и FrankenPHP!
|
||||
|
||||
FrankenPHP также поддерживает [встраивание PHP-приложений в статический бинарный файл](embed.md).
|
||||
|
||||
## Linux
|
||||
|
||||
Мы предоставляем Docker-образ для сборки статического бинарника для Linux:
|
||||
|
||||
```console
|
||||
docker buildx bake --load static-builder
|
||||
docker cp $(docker create --name static-builder-musl dunglas/frankenphp:static-builder-musl):/go/src/app/dist/frankenphp-linux-$(uname -m) frankenphp ; docker rm static-builder
|
||||
```
|
||||
|
||||
Созданный статический бинарный файл называется `frankenphp` и будет доступен в текущей директории.
|
||||
|
||||
Чтобы собрать статический бинарный файл без Docker, используйте инструкции для macOS — они подходят и для Linux.
|
||||
|
||||
### Дополнительные расширения
|
||||
|
||||
По умолчанию компилируются самые популярные PHP-расширения.
|
||||
|
||||
Чтобы уменьшить размер бинарного файла и сократить возможные векторы атак, можно указать список расширений, которые следует включить в сборку, используя Docker-аргумент `PHP_EXTENSIONS`.
|
||||
|
||||
Например, выполните следующую команду, чтобы собрать только расширение `opcache`:
|
||||
|
||||
```console
|
||||
docker buildx bake --load --set static-builder.args.PHP_EXTENSIONS=opcache,pdo_sqlite static-builder
|
||||
# ...
|
||||
```
|
||||
|
||||
Чтобы добавить библиотеки, расширяющие функциональность включённых расширений, используйте Docker-аргумент `PHP_EXTENSION_LIBS`:
|
||||
|
||||
```console
|
||||
docker buildx bake \
|
||||
--load \
|
||||
--set static-builder.args.PHP_EXTENSIONS=gd \
|
||||
--set static-builder.args.PHP_EXTENSION_LIBS=libjpeg,libwebp \
|
||||
static-builder
|
||||
```
|
||||
|
||||
### Дополнительные модули Caddy
|
||||
|
||||
Чтобы добавить дополнительные модули Caddy или передать аргументы в [xcaddy](https://github.com/caddyserver/xcaddy), используйте Docker-аргумент `XCADDY_ARGS`:
|
||||
|
||||
```console
|
||||
docker buildx bake \
|
||||
--load \
|
||||
--set static-builder.args.XCADDY_ARGS="--with github.com/darkweak/souin/plugins/caddy --with github.com/dunglas/caddy-cbrotli --with github.com/dunglas/mercure/caddy --with github.com/dunglas/vulcain/caddy" \
|
||||
static-builder
|
||||
```
|
||||
|
||||
В этом примере добавляются модуль HTTP-кэширования [Souin](https://souin.io) для Caddy, а также модули [cbrotli](https://github.com/dunglas/caddy-cbrotli), [Mercure](https://mercure.rocks) и [Vulcain](https://vulcain.rocks).
|
||||
|
||||
> [!TIP]
|
||||
>
|
||||
> Модули cbrotli, Mercure и Vulcain включены по умолчанию, если `XCADDY_ARGS` пуст или не установлен.
|
||||
> Если вы изменяете значение `XCADDY_ARGS`, добавьте их явно, если хотите включить их в сборку.
|
||||
|
||||
См. также, как [настроить сборку](#настройка-сборки).
|
||||
|
||||
### Токен GitHub
|
||||
|
||||
Если вы достигли лимита запросов к API GitHub, задайте личный токен доступа GitHub в переменной окружения `GITHUB_TOKEN`:
|
||||
|
||||
```console
|
||||
GITHUB_TOKEN="xxx" docker --load buildx bake static-builder
|
||||
# ...
|
||||
```
|
||||
|
||||
## macOS
|
||||
|
||||
Запустите следующий скрипт, чтобы создать статический бинарный файл для macOS (должен быть установлен [Homebrew](https://brew.sh/)):
|
||||
|
||||
```console
|
||||
git clone https://github.com/dunglas/frankenphp
|
||||
cd frankenphp
|
||||
./build-static.sh
|
||||
```
|
||||
|
||||
Примечание: этот скрипт также работает на Linux (и, вероятно, на других Unix-системах) и используется внутри предоставленного Docker-образа для статической сборки.
|
||||
|
||||
## Настройка сборки
|
||||
|
||||
Следующие переменные окружения можно передать в `docker build` и скрипт `build-static.sh`, чтобы настроить статическую сборку:
|
||||
|
||||
- `FRANKENPHP_VERSION`: версия FrankenPHP
|
||||
- `PHP_VERSION`: версия PHP
|
||||
- `PHP_EXTENSIONS`: PHP-расширения для сборки ([список поддерживаемых расширений](https://static-php.dev/en/guide/extensions.html))
|
||||
- `PHP_EXTENSION_LIBS`: дополнительные библиотеки, добавляющие функциональность расширениям
|
||||
- `XCADDY_ARGS`: аргументы для [xcaddy](https://github.com/caddyserver/xcaddy), например, для добавления модулей Caddy
|
||||
- `EMBED`: путь к PHP-приложению для встраивания в бинарник
|
||||
- `CLEAN`: если задано, libphp и все его зависимости будут пересобраны с нуля (без кэша)
|
||||
- `NO_COMPRESS`: отключает сжатие результирующего бинарника с помощью UPX
|
||||
- `DEBUG_SYMBOLS`: если задано, отладочные символы не будут удалены и будут добавлены в бинарник
|
||||
- `MIMALLOC`: (экспериментально, только для Linux) заменяет musl's mallocng на [mimalloc](https://github.com/microsoft/mimalloc) для повышения производительности
|
||||
- `RELEASE`: (только для мейнтейнеров) если задано, бинарник будет загружен на GitHub
|
||||
159
docs/ru/worker.md
Normal file
159
docs/ru/worker.md
Normal file
@@ -0,0 +1,159 @@
|
||||
# Worker режим в FrankenPHP
|
||||
|
||||
Загрузите приложение один раз и держите его в памяти.
|
||||
FrankenPHP обрабатывает входящие запросы за несколько миллисекунд.
|
||||
|
||||
## Запуск worker-скриптов
|
||||
|
||||
### Docker
|
||||
|
||||
Установите значение переменной окружения `FRANKENPHP_CONFIG` на `worker /path/to/your/worker/script.php`:
|
||||
|
||||
```console
|
||||
docker run \
|
||||
-e FRANKENPHP_CONFIG="worker /app/path/to/your/worker/script.php" \
|
||||
-v $PWD:/app \
|
||||
-p 80:80 -p 443:443 -p 443:443/udp \
|
||||
dunglas/frankenphp
|
||||
```
|
||||
|
||||
### Автономный бинарный файл
|
||||
|
||||
Используйте опцию `--worker` команды `php-server`, чтобы обслуживать содержимое текущей директории через worker-скрипт:
|
||||
|
||||
```console
|
||||
frankenphp php-server --worker /path/to/your/worker/script.php
|
||||
```
|
||||
|
||||
Если ваше PHP-приложение [встроено в бинарник](embed.md), вы можете добавить пользовательский `Caddyfile` в корневую директорию приложения.
|
||||
Он будет использоваться автоматически.
|
||||
|
||||
Также можно настроить [автоматический перезапуск worker-скрипта при изменении файлов](config.md#отслеживание-изменений-файлов) с помощью опции `--watch`.
|
||||
Следующая команда выполнит перезапуск, если будет изменён любой файл с расширением `.php` в директории `/path/to/your/app/` или её поддиректориях:
|
||||
|
||||
```console
|
||||
frankenphp php-server --worker /path/to/your/worker/script.php --watch="/path/to/your/app/**/*.php"
|
||||
```
|
||||
|
||||
## Symfony Runtime
|
||||
|
||||
Worker режим FrankenPHP поддерживается компонентом [Symfony Runtime](https://symfony.com/doc/current/components/runtime.html).
|
||||
Чтобы запустить любое Symfony-приложение в worker режиме, установите пакет FrankenPHP для [PHP Runtime](https://github.com/php-runtime/runtime):
|
||||
|
||||
```console
|
||||
composer require runtime/frankenphp-symfony
|
||||
```
|
||||
|
||||
Запустите сервер приложения, задав переменную окружения `APP_RUNTIME` для использования FrankenPHP Symfony Runtime:
|
||||
|
||||
```console
|
||||
docker run \
|
||||
-e FRANKENPHP_CONFIG="worker ./public/index.php" \
|
||||
-e APP_RUNTIME=Runtime\\FrankenPhpSymfony\\Runtime \
|
||||
-v $PWD:/app \
|
||||
-p 80:80 -p 443:443 -p 443:443/udp \
|
||||
dunglas/frankenphp
|
||||
```
|
||||
|
||||
## Laravel Octane
|
||||
|
||||
Подробнее см. в [документации](laravel.md#laravel-octane).
|
||||
|
||||
## Пользовательские приложения
|
||||
|
||||
Следующий пример показывает, как создать собственный worker-скрипт без использования сторонних библиотек:
|
||||
|
||||
```php
|
||||
<?php
|
||||
// public/index.php
|
||||
|
||||
// Предотвращает завершение worker-скрипта при разрыве соединения клиента
|
||||
ignore_user_abort(true);
|
||||
|
||||
// Инициализация приложения
|
||||
require __DIR__.'/vendor/autoload.php';
|
||||
|
||||
$myApp = new \App\Kernel();
|
||||
$myApp->boot();
|
||||
|
||||
// Обработчик запросов за пределами цикла для повышения производительности
|
||||
$handler = static function () use ($myApp) {
|
||||
// Выполняется при обработке запроса.
|
||||
// Суперглобальные переменные, php://input и другие данные обновляются для каждого запроса.
|
||||
echo $myApp->handle($_GET, $_POST, $_COOKIE, $_FILES, $_SERVER);
|
||||
};
|
||||
|
||||
$maxRequests = (int)($_SERVER['MAX_REQUESTS'] ?? 0);
|
||||
for ($nbRequests = 0; !$maxRequests || $nbRequests < $maxRequests; ++$nbRequests) {
|
||||
$keepRunning = \frankenphp_handle_request($handler);
|
||||
|
||||
// Действия после отправки HTTP-ответа
|
||||
$myApp->terminate();
|
||||
|
||||
// Вызов сборщика мусора, чтобы снизить вероятность его запуска в процессе генерации страницы
|
||||
gc_collect_cycles();
|
||||
|
||||
if (!$keepRunning) break;
|
||||
}
|
||||
|
||||
// Завершение
|
||||
$myApp->shutdown();
|
||||
```
|
||||
|
||||
Запустите приложение, настроив worker-скрипт с помощью переменной окружения `FRANKENPHP_CONFIG`:
|
||||
|
||||
```console
|
||||
docker run \
|
||||
-e FRANKENPHP_CONFIG="worker ./public/index.php" \
|
||||
-v $PWD:/app \
|
||||
-p 80:80 -p 443:443 -p 443:443/udp \
|
||||
dunglas/frankenphp
|
||||
```
|
||||
|
||||
## Настройка количества worker-скриптов
|
||||
|
||||
По умолчанию запускается по 2 worker-скрипта на каждый CPU.
|
||||
Вы можете задать своё значение:
|
||||
|
||||
```console
|
||||
docker run \
|
||||
-e FRANKENPHP_CONFIG="worker ./public/index.php 42" \
|
||||
-v $PWD:/app \
|
||||
-p 80:80 -p 443:443 -p 443:443/udp \
|
||||
dunglas/frankenphp
|
||||
```
|
||||
|
||||
### Перезапуск worker-скрипта после определённого количества запросов
|
||||
|
||||
PHP изначально не предназначался для долгоживущих процессов, поэтому некоторые библиотеки и устаревший код могут приводить к утечкам памяти.
|
||||
Для этого можно настроить автоматический перезапуск worker-скрипта после обработки определённого количества запросов.
|
||||
|
||||
В предыдущем примере максимальное количество запросов задаётся с помощью переменной окружения `MAX_REQUESTS`.
|
||||
|
||||
### Сбои worker-скрипта
|
||||
|
||||
Если worker-скрипт завершится с ненулевым кодом выхода, FrankenPHP перезапустит его с использованием экспоненциальной задержки.
|
||||
Если worker-скрипт проработает дольше, чем время последней задержки \* 2, он будет считаться стабильным, и задержка сбросится.
|
||||
Однако, если worker-скрипт продолжает завершаться с ненулевым кодом выхода в течение короткого промежутка времени (например, из-за опечатки в коде), FrankenPHP завершит работу с ошибкой: `too many consecutive failures`.
|
||||
|
||||
## Поведение суперглобальных переменных
|
||||
|
||||
[PHP суперглобальные переменные](https://www.php.net/manual/en/language.variables.superglobals.php) (`$_SERVER`, `$_ENV`, `$_GET` и т.д.) ведут себя следующим образом:
|
||||
|
||||
- до первого вызова `frankenphp_handle_request()` суперглобальные переменные содержат значения, связанные с самим worker-скриптом
|
||||
- во время и после вызова `frankenphp_handle_request()` суперглобальные переменные содержат значения, сгенерированные на основе обработанного HTTP-запроса, каждый вызов изменяет значения суперглобальных переменных
|
||||
|
||||
Чтобы получить доступ к суперглобальным переменным worker-скрипта внутри колбэка, необходимо скопировать их и импортировать копию в область видимости колбэка:
|
||||
|
||||
```php
|
||||
<?php
|
||||
// Копирование $_SERVER worker-скрипта перед первым вызовом frankenphp_handle_request()
|
||||
$workerServer = $_SERVER;
|
||||
|
||||
$handler = static function () use ($workerServer) {
|
||||
var_dump($_SERVER); // $_SERVER для запроса
|
||||
var_dump($workerServer); // $_SERVER worker-скрипта
|
||||
};
|
||||
|
||||
// ...
|
||||
```
|
||||
128
docs/static.md
128
docs/static.md
@@ -1,48 +1,94 @@
|
||||
# Create a Static Build
|
||||
|
||||
Instead of using a local installation of the PHP library,
|
||||
it's possible to create a static build of FrankenPHP thanks to the great [static-php-cli project](https://github.com/crazywhalecc/static-php-cli) (despite its name, this project support all SAPIs, not only CLI).
|
||||
it's possible to create a static or mostly static build of FrankenPHP thanks to the great [static-php-cli project](https://github.com/crazywhalecc/static-php-cli) (despite its name, this project supports all SAPIs, not only CLI).
|
||||
|
||||
With this method, a single, portable, binary will contain the PHP interpreter, the Caddy web server and FrankenPHP!
|
||||
With this method, a single, portable, binary will contain the PHP interpreter, the Caddy web server, and FrankenPHP!
|
||||
|
||||
Fully static native executables require no dependencies at all and can even be run on [`scratch` Docker image](https://docs.docker.com/build/building/base-images/#create-a-minimal-base-image-using-scratch).
|
||||
However, they can't load dynamic PHP extensions (such as Xdebug) and have some limitations because they are using the musl libc.
|
||||
|
||||
Mostly static binaries only require `glibc` and can load dynamic extensions.
|
||||
|
||||
When possible, we recommend using glibc-based, mostly static builds.
|
||||
|
||||
FrankenPHP also supports [embedding the PHP app in the static binary](embed.md).
|
||||
|
||||
## Linux
|
||||
|
||||
We provide a Docker image to build a Linux static binary:
|
||||
We provide Docker images to build static Linux binaries:
|
||||
|
||||
### musl-Based, Fully Static Build
|
||||
|
||||
For a fully-static binary that runs on any Linux distribution without dependencies but doesn't support dynamic loading of extensions:
|
||||
|
||||
```console
|
||||
docker buildx bake --load static-builder
|
||||
docker cp $(docker create --name static-builder dunglas/frankenphp:static-builder):/go/src/app/dist/frankenphp-linux-$(uname -m) frankenphp ; docker rm static-builder
|
||||
docker buildx bake --load static-builder-musl
|
||||
docker cp $(docker create --name static-builder-musl dunglas/frankenphp:static-builder-musl):/go/src/app/dist/frankenphp-linux-$(uname -m) frankenphp ; docker rm static-builder-musl
|
||||
```
|
||||
|
||||
The resulting static binary is named `frankenphp` and is available in the current directory.
|
||||
For better performance in heavily concurrent scenarios, consider using the [mimalloc](https://github.com/microsoft/mimalloc) allocator.
|
||||
|
||||
If you want to build the static binary without Docker, take a look at the macOS instructions, which also works for Linux.
|
||||
```console
|
||||
docker buildx bake --load --set static-builder-musl.args.MIMALLOC=1 static-builder-musl
|
||||
```
|
||||
|
||||
### glibc-Based, Mostly Static Build (With Dynamic Extension Support)
|
||||
|
||||
For a binary that supports loading PHP extensions dynamically while still having the selected extensions compiled statically:
|
||||
|
||||
```console
|
||||
docker buildx bake --load static-builder-gnu
|
||||
docker cp $(docker create --name static-builder-gnu dunglas/frankenphp:static-builder-gnu):/go/src/app/dist/frankenphp-linux-$(uname -m) frankenphp ; docker rm static-builder-gnu
|
||||
```
|
||||
|
||||
This binary supports all glibc versions 2.17 and superior but does not run on musl-based systems (like Alpine Linux).
|
||||
|
||||
The resulting mostly static (except `glibc`) binary is named `frankenphp` and is available in the current directory.
|
||||
|
||||
If you want to build the static binary without Docker, take a look at the macOS instructions, which also work for Linux.
|
||||
|
||||
### Custom Extensions
|
||||
|
||||
By default, most popular PHP extensions are compiled.
|
||||
By default, the most popular PHP extensions are compiled.
|
||||
|
||||
To reduce the size of the binary and to reduce the attack surface, you can choose the list of extensions to build using the `PHP_EXTENSIONS` Docker ARG.
|
||||
|
||||
For instance, run the following command to only build the `opcache` extension:
|
||||
|
||||
```console
|
||||
docker buildx bake --load --set static-builder.args.PHP_EXTENSIONS=opcache,pdo_sqlite static-builder
|
||||
docker buildx bake --load --set static-builder-musl.args.PHP_EXTENSIONS=opcache,pdo_sqlite static-builder-musl
|
||||
# ...
|
||||
```
|
||||
|
||||
To add libraries enabling additional functionality to the extensions you've enabled, you can pass use the `PHP_EXTENSION_LIBS` Docker ARG:
|
||||
To add libraries enabling additional functionality to the extensions you've enabled, you can pass the `PHP_EXTENSION_LIBS` Docker ARG:
|
||||
|
||||
```console
|
||||
docker buildx bake \
|
||||
--load \
|
||||
--set static-builder.args.PHP_EXTENSIONS=gd \
|
||||
--set static-builder.args.PHP_EXTENSION_LIBS=libjpeg,libwebp \
|
||||
static-builder
|
||||
--set static-builder-musl.args.PHP_EXTENSIONS=gd \
|
||||
--set static-builder-musl.args.PHP_EXTENSION_LIBS=libjpeg,libwebp \
|
||||
static-builder-musl
|
||||
```
|
||||
|
||||
### Extra Caddy Modules
|
||||
|
||||
To add extra Caddy modules or pass other arguments to [xcaddy](https://github.com/caddyserver/xcaddy), use the `XCADDY_ARGS` Docker ARG:
|
||||
|
||||
```console
|
||||
docker buildx bake \
|
||||
--load \
|
||||
--set static-builder-musl.args.XCADDY_ARGS="--with github.com/darkweak/souin/plugins/caddy --with github.com/dunglas/caddy-cbrotli --with github.com/dunglas/mercure/caddy --with github.com/dunglas/vulcain/caddy" \
|
||||
static-builder-musl
|
||||
```
|
||||
|
||||
In this example, we add the [Souin](https://souin.io) HTTP cache module for Caddy as well as the [cbrotli](https://github.com/dunglas/caddy-cbrotli), [Mercure](https://mercure.rocks) and [Vulcain](https://vulcain.rocks) modules.
|
||||
|
||||
> [!TIP]
|
||||
>
|
||||
> The cbrotli, Mercure, and Vulcain modules are included by default if `XCADDY_ARGS` is empty or not set.
|
||||
> If you customize the value of `XCADDY_ARGS`, you must include them explicitly if you want them to be included.
|
||||
|
||||
See also how to [customize the build](#customizing-the-build)
|
||||
|
||||
### GitHub Token
|
||||
@@ -50,7 +96,7 @@ See also how to [customize the build](#customizing-the-build)
|
||||
If you hit the GitHub API rate limit, set a GitHub Personal Access Token in an environment variable named `GITHUB_TOKEN`:
|
||||
|
||||
```console
|
||||
GITHUB_TOKEN="xxx" docker --load buildx bake static-builder
|
||||
GITHUB_TOKEN="xxx" docker --load buildx bake static-builder-musl
|
||||
# ...
|
||||
```
|
||||
|
||||
@@ -64,20 +110,52 @@ cd frankenphp
|
||||
./build-static.sh
|
||||
```
|
||||
|
||||
Note: this script also works on Linux (and probably on other Unixes), and is used internally by the Docker based static builder we provide.
|
||||
Note: this script also works on Linux (and probably on other Unixes), and is used internally by the Docker images we provide.
|
||||
|
||||
## Customizing The Build
|
||||
|
||||
The following environment variables can be passed to `docker build` and to the `build-static.sh`
|
||||
script to customize the static build:
|
||||
|
||||
* `FRANKENPHP_VERSION`: the version of FrankenPHP to use
|
||||
* `PHP_VERSION`: the version of PHP to use
|
||||
* `PHP_EXTENSIONS`: the PHP extensions to build ([list of supported extensions](https://static-php.dev/en/guide/extensions.html))
|
||||
* `PHP_EXTENSION_LIBS`: extra libraries to build that add features to the extensions
|
||||
* `EMBED`: path of the PHP application to embed in the binary
|
||||
* `CLEAN`: when set, libphp and all its dependencies are built from scratch (no cache)
|
||||
* `NO_COMPRESS`: don't compress the resulting binary using UPX
|
||||
* `DEBUG_SYMBOLS`: when set, debug-symbols will not be stripped and will be added within the binary
|
||||
* `MIMALLOC`: (experimental, Linux-only) replace musl's mallocng by [mimalloc](https://github.com/microsoft/mimalloc) for improved performance
|
||||
* `RELEASE`: (maintainers only) when set, the resulting binary will be uploaded on GitHub
|
||||
- `FRANKENPHP_VERSION`: the version of FrankenPHP to use
|
||||
- `PHP_VERSION`: the version of PHP to use
|
||||
- `PHP_EXTENSIONS`: the PHP extensions to build ([list of supported extensions](https://static-php.dev/en/guide/extensions.html))
|
||||
- `PHP_EXTENSION_LIBS`: extra libraries to build that add features to the extensions
|
||||
- `XCADDY_ARGS`: arguments to pass to [xcaddy](https://github.com/caddyserver/xcaddy), for instance to add extra Caddy modules
|
||||
- `EMBED`: path of the PHP application to embed in the binary
|
||||
- `CLEAN`: when set, libphp and all its dependencies are built from scratch (no cache)
|
||||
- `NO_COMPRESS`: don't compress the resulting binary using UPX
|
||||
- `DEBUG_SYMBOLS`: when set, debug-symbols will not be stripped and will be added to the binary
|
||||
- `MIMALLOC`: (experimental, Linux-only) replace musl's mallocng by [mimalloc](https://github.com/microsoft/mimalloc) for improved performance. We only recommend using this for musl targeting builds, for glibc prefer disabling this option and using [`LD_PRELOAD`](https://microsoft.github.io/mimalloc/overrides.html) when you run your binary instead.
|
||||
- `RELEASE`: (maintainers only) when set, the resulting binary will be uploaded on GitHub
|
||||
|
||||
## Extensions
|
||||
|
||||
With the glibc or macOS-based binaries, you can load PHP extensions dynamically. However, these extensions will have to be compiled with ZTS support.
|
||||
Since most package managers do not currently offer ZTS versions of their extensions, you will have to compile them yourself.
|
||||
|
||||
For this, you can build and run the `static-builder-gnu` Docker container, remote into it, and compile the extensions with `./configure --with-php-config=/go/src/app/dist/static-php-cli/buildroot/bin/php-config`.
|
||||
|
||||
Example steps for [the Xdebug extension](https://xdebug.org):
|
||||
|
||||
```console
|
||||
docker build -t gnu-ext -f static-builder-gnu.Dockerfile --build-arg FRANKENPHP_VERSION=1.0 .
|
||||
docker create --name static-builder-gnu -it gnu-ext /bin/sh
|
||||
docker start static-builder-gnu
|
||||
docker exec -it static-builder-gnu /bin/sh
|
||||
cd /go/src/app/dist/static-php-cli/buildroot/bin
|
||||
git clone https://github.com/xdebug/xdebug.git && cd xdebug
|
||||
source scl_source enable devtoolset-10
|
||||
../phpize
|
||||
./configure --with-php-config=/go/src/app/dist/static-php-cli/buildroot/bin/php-config
|
||||
make
|
||||
exit
|
||||
docker cp static-builder-gnu:/go/src/app/dist/static-php-cli/buildroot/bin/xdebug/modules/xdebug.so xdebug-zts.so
|
||||
docker cp static-builder-gnu:/go/src/app/dist/frankenphp-linux-$(uname -m) ./frankenphp
|
||||
docker stop static-builder-gnu
|
||||
docker rm static-builder-gnu
|
||||
docker rmi gnu-ext
|
||||
```
|
||||
|
||||
This will have created `frankenphp` and `xdebug-zts.so` in the current directory.
|
||||
If you move the `xdebug-zts.so` into your extension directory, add `zend_extension=xdebug-zts.so` to your php.ini and run FrankenPHP, it will load Xdebug.
|
||||
|
||||
@@ -1,202 +1,206 @@
|
||||
# Katkıda Bulunmak
|
||||
|
||||
## PHP Derleme
|
||||
|
||||
### Docker ile (Linux)
|
||||
|
||||
Geliştirme Ortamı için Docker İmajını Oluşturun:
|
||||
|
||||
```console
|
||||
docker build -t frankenphp-dev -f dev.Dockerfile .
|
||||
docker run --cap-add=SYS_PTRACE --security-opt seccomp=unconfined -p 8080:8080 -p 443:443 -p 443:443/udp -v $PWD:/go/src/app -it frankenphp-dev
|
||||
```
|
||||
|
||||
İmaj genel geliştirme araçlarını (Go, GDB, Valgrind, Neovim...) içerir.
|
||||
|
||||
Docker sürümü 23.0'dan düşükse, derleme dockerignore [pattern issue](https://github.com/moby/moby/pull/42676) tarafından başarısız olur. Dizinleri `.dockerignore` dosyasına ekleyin.
|
||||
|
||||
```patch
|
||||
!testdata/*.php
|
||||
!testdata/*.txt
|
||||
+!caddy
|
||||
+!internal
|
||||
```
|
||||
|
||||
### Docker olmadan (Linux ve macOS)
|
||||
|
||||
[Kaynaklardan derlemek için talimatları izleyin](https://frankenphp.dev/docs/compile/) ve `--debug` yapılandırma seçeneğini geçirin.
|
||||
|
||||
## Test senaryolarını çalıştırma
|
||||
|
||||
```console
|
||||
go test -tags watcher -race -v ./...
|
||||
```
|
||||
|
||||
## Caddy modülü
|
||||
|
||||
FrankenPHP Caddy modülü ile Caddy'yi oluşturun:
|
||||
|
||||
```console
|
||||
cd caddy/frankenphp/
|
||||
go build
|
||||
cd ../../
|
||||
```
|
||||
|
||||
Caddy'yi FrankenPHP Caddy modülü ile çalıştırın:
|
||||
|
||||
```console
|
||||
cd testdata/
|
||||
../caddy/frankenphp/frankenphp run
|
||||
```
|
||||
|
||||
Sunucu `127.0.0.1:8080` adresini dinliyor:
|
||||
|
||||
```console
|
||||
curl -vk https://localhost/phpinfo.php
|
||||
```
|
||||
|
||||
## Minimal test sunucusu
|
||||
|
||||
Minimal test sunucusunu oluşturun:
|
||||
|
||||
```console
|
||||
cd internal/testserver/
|
||||
go build
|
||||
cd ../../
|
||||
```
|
||||
|
||||
Test sunucusunu çalıştırın:
|
||||
|
||||
```console
|
||||
cd testdata/
|
||||
../internal/testserver/testserver
|
||||
```
|
||||
|
||||
Sunucu `127.0.0.1:8080` adresini dinliyor:
|
||||
|
||||
```console
|
||||
curl -v http://127.0.0.1:8080/phpinfo.php
|
||||
```
|
||||
|
||||
## Docker İmajlarını Yerel Olarak Oluşturma
|
||||
|
||||
Bake (pişirme) planını yazdırın:
|
||||
|
||||
```console
|
||||
docker buildx bake -f docker-bake.hcl --print
|
||||
```
|
||||
|
||||
Yerel olarak amd64 için FrankenPHP görüntüleri oluşturun:
|
||||
|
||||
```console
|
||||
docker buildx bake -f docker-bake.hcl --pull --load --set "*.platform=linux/amd64"
|
||||
```
|
||||
|
||||
Yerel olarak arm64 için FrankenPHP görüntüleri oluşturun:
|
||||
|
||||
```console
|
||||
docker buildx bake -f docker-bake.hcl --pull --load --set "*.platform=linux/arm64"
|
||||
```
|
||||
|
||||
FrankenPHP imajlarını arm64 ve amd64 için sıfırdan oluşturun ve Docker Hub'a gönderin:
|
||||
|
||||
```console
|
||||
docker buildx bake -f docker-bake.hcl --pull --no-cache --push
|
||||
```
|
||||
|
||||
## Statik Derlemelerle Segmentasyon Hatalarında Hata Ayıklama
|
||||
|
||||
1. FrankenPHP binary dosyasının hata ayıklama sürümünü GitHub'dan indirin veya hata ayıklama seçeneklerini kullanarak özel statik derlemenizi oluşturun:
|
||||
|
||||
```console
|
||||
docker buildx bake \
|
||||
--load \
|
||||
--set static-builder.args.DEBUG_SYMBOLS=1 \
|
||||
--set "static-builder.platform=linux/amd64" \
|
||||
static-builder
|
||||
docker cp $(docker create --name static-builder dunglas/frankenphp:static-builder):/go/src/app/dist/frankenphp-linux-$(uname -m) frankenphp
|
||||
```
|
||||
|
||||
2. Mevcut `frankenphp` sürümünüzü hata ayıklama FrankenPHP çalıştırılabilir dosyasıyla değiştirin
|
||||
3. FrankenPHP'yi her zamanki gibi başlatın (alternatif olarak FrankenPHP'yi doğrudan GDB ile başlatabilirsiniz: `gdb --args frankenphp run`)
|
||||
4. GDB ile sürece bağlanın:
|
||||
|
||||
```console
|
||||
gdb -p `pidof frankenphp`
|
||||
```
|
||||
|
||||
5. Gerekirse, GDB kabuğuna `continue` yazın
|
||||
6. FrankenPHP'nin çökmesini sağlayın
|
||||
7. GDB kabuğuna `bt` yazın
|
||||
8. Çıktıyı kopyalayın
|
||||
|
||||
## GitHub Eylemlerinde Segmentasyon Hatalarında Hata Ayıklama
|
||||
|
||||
1. `.github/workflows/tests.yml` dosyasını açın
|
||||
2. PHP hata ayıklama seçeneklerini etkinleştirin
|
||||
|
||||
```patch
|
||||
- uses: shivammathur/setup-php@v2
|
||||
# ...
|
||||
env:
|
||||
phpts: ts
|
||||
+ debug: true
|
||||
```
|
||||
|
||||
3. Konteynere bağlanmak için `tmate`i etkinleştirin
|
||||
|
||||
```patch
|
||||
-
|
||||
name: Set CGO flags
|
||||
run: echo "CGO_CFLAGS=$(php-config --includes)" >> "$GITHUB_ENV"
|
||||
+ -
|
||||
+ run: |
|
||||
+ sudo apt install gdb
|
||||
+ mkdir -p /home/runner/.config/gdb/
|
||||
+ printf "set auto-load safe-path /\nhandle SIG34 nostop noprint pass" > /home/runner/.config/gdb/gdbinit
|
||||
+ -
|
||||
+ uses: mxschmitt/action-tmate@v3
|
||||
```
|
||||
|
||||
4. Konteynere bağlanın
|
||||
5. `frankenphp.go` dosyasını açın
|
||||
6. `cgosymbolizer`'ı etkinleştirin
|
||||
|
||||
```patch
|
||||
- //_ "github.com/ianlancetaylor/cgosymbolizer"
|
||||
+ _ "github.com/ianlancetaylor/cgosymbolizer"
|
||||
```
|
||||
|
||||
7. Modülü indirin: `go get`
|
||||
8. Konteynerde GDB ve benzerlerini kullanabilirsiniz:
|
||||
|
||||
```console
|
||||
go test -tags watcher -c -ldflags=-w
|
||||
gdb --args frankenphp.test -test.run ^MyTest$
|
||||
```
|
||||
|
||||
9. Hata düzeltildiğinde, tüm bu değişiklikleri geri alın
|
||||
|
||||
## Misc Dev Resources
|
||||
|
||||
* [uWSGI içine PHP gömme](https://github.com/unbit/uwsgi/blob/master/plugins/php/php_plugin.c)
|
||||
* [NGINX Unit'te PHP gömme](https://github.com/nginx/unit/blob/master/src/nxt_php_sapi.c)
|
||||
* [Go (go-php) içinde PHP gömme](https://github.com/deuill/go-php)
|
||||
* [Go'da PHP gömme (GoEmPHP)](https://github.com/mikespook/goemphp)
|
||||
* [C++'da PHP gömme](https://gist.github.com/paresy/3cbd4c6a469511ac7479aa0e7c42fea7)
|
||||
* [Sara Golemon tarafından PHP'yi Genişletme ve Yerleştirme](https://books.google.fr/books?id=zMbGvK17_tYC&pg=PA254&lpg=PA254#v=onepage&q&f=false)
|
||||
* [TSRMLS_CC de neyin nesi?](http://blog.golemon.com/2006/06/what-heck-is-tsrmlscc-anyway.html)
|
||||
* [Mac'te PHP gömme](https://gist.github.com/jonnywang/61427ffc0e8dde74fff40f479d147db4)
|
||||
* [SDL bağları](https://pkg.go.dev/github.com/veandco/go-sdl2@v0.4.21/sdl#Main)
|
||||
|
||||
## Docker ile İlgili Kaynaklar
|
||||
|
||||
* [Pişirme (bake) dosya tanımı](https://docs.docker.com/build/customize/bake/file-definition/)
|
||||
* [docker buildx build](https://docs.docker.com/engine/reference/commandline/buildx_build/)
|
||||
|
||||
## Faydalı Komut
|
||||
|
||||
```console
|
||||
apk add strace util-linux gdb
|
||||
strace -e 'trace=!futex,epoll_ctl,epoll_pwait,tgkill,rt_sigreturn' -p 1
|
||||
```
|
||||
# Katkıda Bulunmak
|
||||
|
||||
## PHP Derleme
|
||||
|
||||
### Docker ile (Linux)
|
||||
|
||||
Geliştirme Ortamı için Docker İmajını Oluşturun:
|
||||
|
||||
```console
|
||||
docker build -t frankenphp-dev -f dev.Dockerfile .
|
||||
docker run --cap-add=SYS_PTRACE --security-opt seccomp=unconfined -p 8080:8080 -p 443:443 -p 443:443/udp -v $PWD:/go/src/app -it frankenphp-dev
|
||||
```
|
||||
|
||||
İmaj genel geliştirme araçlarını (Go, GDB, Valgrind, Neovim...) içerir ve aşağıdaki php ayar konumlarını kullanır
|
||||
|
||||
- php.ini: `/etc/frankenphp/php.ini` Varsayılan olarak geliştirme ön ayarlarına sahip bir php.ini dosyası sağlanır.
|
||||
- ek yapılandırma dosyaları: `/etc/frankenphp/php.d/*.ini`
|
||||
- php uzantıları: `/usr/lib/frankenphp/modules/`
|
||||
|
||||
Docker sürümünüz 23.0'dan düşükse, derleme dockerignore [pattern issue](https://github.com/moby/moby/pull/42676) nedeniyle başarısız olacaktır. Dizinleri `.dockerignore` dosyasına ekleyin.
|
||||
|
||||
```patch
|
||||
!testdata/*.php
|
||||
!testdata/*.txt
|
||||
+!caddy
|
||||
+!internal
|
||||
```
|
||||
|
||||
### Docker olmadan (Linux ve macOS)
|
||||
|
||||
[Kaynaklardan derlemek için talimatları izleyin](https://frankenphp.dev/docs/compile/) ve `--debug` yapılandırma seçeneğini geçirin.
|
||||
|
||||
## Test senaryolarını çalıştırma
|
||||
|
||||
```console
|
||||
go test -tags watcher -race -v ./...
|
||||
```
|
||||
|
||||
## Caddy modülü
|
||||
|
||||
FrankenPHP Caddy modülü ile Caddy'yi oluşturun:
|
||||
|
||||
```console
|
||||
cd caddy/frankenphp/
|
||||
go build
|
||||
cd ../../
|
||||
```
|
||||
|
||||
Caddy'yi FrankenPHP Caddy modülü ile çalıştırın:
|
||||
|
||||
```console
|
||||
cd testdata/
|
||||
../caddy/frankenphp/frankenphp run
|
||||
```
|
||||
|
||||
Sunucu `127.0.0.1:8080` adresini dinliyor:
|
||||
|
||||
```console
|
||||
curl -vk https://localhost/phpinfo.php
|
||||
```
|
||||
|
||||
## Minimal test sunucusu
|
||||
|
||||
Minimal test sunucusunu oluşturun:
|
||||
|
||||
```console
|
||||
cd internal/testserver/
|
||||
go build
|
||||
cd ../../
|
||||
```
|
||||
|
||||
Test sunucusunu çalıştırın:
|
||||
|
||||
```console
|
||||
cd testdata/
|
||||
../internal/testserver/testserver
|
||||
```
|
||||
|
||||
Sunucu `127.0.0.1:8080` adresini dinliyor:
|
||||
|
||||
```console
|
||||
curl -v http://127.0.0.1:8080/phpinfo.php
|
||||
```
|
||||
|
||||
## Docker İmajlarını Yerel Olarak Oluşturma
|
||||
|
||||
Bake (pişirme) planını yazdırın:
|
||||
|
||||
```console
|
||||
docker buildx bake -f docker-bake.hcl --print
|
||||
```
|
||||
|
||||
Yerel olarak amd64 için FrankenPHP görüntüleri oluşturun:
|
||||
|
||||
```console
|
||||
docker buildx bake -f docker-bake.hcl --pull --load --set "*.platform=linux/amd64"
|
||||
```
|
||||
|
||||
Yerel olarak arm64 için FrankenPHP görüntüleri oluşturun:
|
||||
|
||||
```console
|
||||
docker buildx bake -f docker-bake.hcl --pull --load --set "*.platform=linux/arm64"
|
||||
```
|
||||
|
||||
FrankenPHP imajlarını arm64 ve amd64 için sıfırdan oluşturun ve Docker Hub'a gönderin:
|
||||
|
||||
```console
|
||||
docker buildx bake -f docker-bake.hcl --pull --no-cache --push
|
||||
```
|
||||
|
||||
## Statik Derlemelerle Segmentasyon Hatalarında Hata Ayıklama
|
||||
|
||||
1. FrankenPHP binary dosyasının hata ayıklama sürümünü GitHub'dan indirin veya hata ayıklama seçeneklerini kullanarak özel statik derlemenizi oluşturun:
|
||||
|
||||
```console
|
||||
docker buildx bake \
|
||||
--load \
|
||||
--set static-builder.args.DEBUG_SYMBOLS=1 \
|
||||
--set "static-builder.platform=linux/amd64" \
|
||||
static-builder
|
||||
docker cp $(docker create --name static-builder-musl dunglas/frankenphp:static-builder-musl):/go/src/app/dist/frankenphp-linux-$(uname -m) frankenphp
|
||||
```
|
||||
|
||||
2. Mevcut `frankenphp` sürümünüzü hata ayıklama FrankenPHP çalıştırılabilir dosyasıyla değiştirin
|
||||
3. FrankenPHP'yi her zamanki gibi başlatın (alternatif olarak FrankenPHP'yi doğrudan GDB ile başlatabilirsiniz: `gdb --args frankenphp run`)
|
||||
4. GDB ile sürece bağlanın:
|
||||
|
||||
```console
|
||||
gdb -p `pidof frankenphp`
|
||||
```
|
||||
|
||||
5. Gerekirse, GDB kabuğuna `continue` yazın
|
||||
6. FrankenPHP'nin çökmesini sağlayın
|
||||
7. GDB kabuğuna `bt` yazın
|
||||
8. Çıktıyı kopyalayın
|
||||
|
||||
## GitHub Eylemlerinde Segmentasyon Hatalarında Hata Ayıklama
|
||||
|
||||
1. `.github/workflows/tests.yml` dosyasını açın
|
||||
2. PHP hata ayıklama seçeneklerini etkinleştirin
|
||||
|
||||
```patch
|
||||
- uses: shivammathur/setup-php@v2
|
||||
# ...
|
||||
env:
|
||||
phpts: ts
|
||||
+ debug: true
|
||||
```
|
||||
|
||||
3. Konteynere bağlanmak için `tmate`i etkinleştirin
|
||||
|
||||
```patch
|
||||
-
|
||||
name: Set CGO flags
|
||||
run: echo "CGO_CFLAGS=$(php-config --includes)" >> "$GITHUB_ENV"
|
||||
+ -
|
||||
+ run: |
|
||||
+ sudo apt install gdb
|
||||
+ mkdir -p /home/runner/.config/gdb/
|
||||
+ printf "set auto-load safe-path /\nhandle SIG34 nostop noprint pass" > /home/runner/.config/gdb/gdbinit
|
||||
+ -
|
||||
+ uses: mxschmitt/action-tmate@v3
|
||||
```
|
||||
|
||||
4. Konteynere bağlanın
|
||||
5. `frankenphp.go` dosyasını açın
|
||||
6. `cgosymbolizer`'ı etkinleştirin
|
||||
|
||||
```patch
|
||||
- //_ "github.com/ianlancetaylor/cgosymbolizer"
|
||||
+ _ "github.com/ianlancetaylor/cgosymbolizer"
|
||||
```
|
||||
|
||||
7. Modülü indirin: `go get`
|
||||
8. Konteynerde GDB ve benzerlerini kullanabilirsiniz:
|
||||
|
||||
```console
|
||||
go test -tags watcher -c -ldflags=-w
|
||||
gdb --args frankenphp.dev.test -test.run ^MyTest$
|
||||
```
|
||||
|
||||
9. Hata düzeltildiğinde, tüm bu değişiklikleri geri alın
|
||||
|
||||
## Misc Dev Resources
|
||||
|
||||
- [uWSGI içine PHP gömme](https://github.com/unbit/uwsgi/blob/master/plugins/php/php_plugin.c)
|
||||
- [NGINX Unit'te PHP gömme](https://github.com/nginx/unit/blob/master/src/nxt_php_sapi.c)
|
||||
- [Go (go-php) içinde PHP gömme](https://github.com/deuill/go-php)
|
||||
- [Go'da PHP gömme (GoEmPHP)](https://github.com/mikespook/goemphp)
|
||||
- [C++'da PHP gömme](https://gist.github.com/paresy/3cbd4c6a469511ac7479aa0e7c42fea7)
|
||||
- [Sara Golemon tarafından PHP'yi Genişletme ve Yerleştirme](https://books.google.fr/books?id=zMbGvK17_tYC&pg=PA254&lpg=PA254#v=onepage&q&f=false)
|
||||
- [TSRMLS_CC de neyin nesi?](http://blog.golemon.com/2006/06/what-heck-is-tsrmlscc-anyway.html)
|
||||
- [Mac'te PHP gömme](https://gist.github.com/jonnywang/61427ffc0e8dde74fff40f479d147db4)
|
||||
- [SDL bağları](https://pkg.go.dev/github.com/veandco/go-sdl2@v0.4.21/sdl#Main)
|
||||
|
||||
## Docker ile İlgili Kaynaklar
|
||||
|
||||
- [Pişirme (bake) dosya tanımı](https://docs.docker.com/build/customize/bake/file-definition/)
|
||||
- [docker buildx build](https://docs.docker.com/engine/reference/commandline/buildx_build/)
|
||||
|
||||
## Faydalı Komut
|
||||
|
||||
```console
|
||||
apk add strace util-linux gdb
|
||||
strace -e 'trace=!futex,epoll_ctl,epoll_pwait,tgkill,rt_sigreturn' -p 1
|
||||
```
|
||||
|
||||
@@ -1,77 +1,78 @@
|
||||
# FrankenPHP: PHP için Modern Uygulama Sunucusu
|
||||
|
||||
<h1 align="center"><a href="https://frankenphp.dev"><img src="../../frankenphp.png" alt="FrankenPHP" width="600"></a></h1>
|
||||
|
||||
FrankenPHP, [Caddy](https://caddyserver.com/) web sunucusunun üzerine inşa edilmiş PHP için modern bir uygulama sunucusudur.
|
||||
|
||||
FrankenPHP, çarpıcı özellikleri sayesinde PHP uygulamalarınıza süper güçler kazandırır: [Early Hints*](https://frankenphp.dev/docs/early-hints/), [worker modu](https://frankenphp.dev/docs/worker/), [real-time yetenekleri](https://frankenphp.dev/docs/mercure/), otomatik HTTPS, HTTP/2 ve HTTP/3 desteği...
|
||||
|
||||
FrankenPHP herhangi bir PHP uygulaması ile çalışır ve worker modu ile resmi entegrasyonları sayesinde Laravel ve Symfony projelerinizi her zamankinden daha performanslı hale getirir.
|
||||
|
||||
FrankenPHP, PHP'yi `net/http` kullanarak herhangi bir uygulamaya yerleştirmek için bağımsız bir Go kütüphanesi olarak da kullanılabilir.
|
||||
|
||||
[*Frankenphp.dev*](https://frankenphp.dev) adresinden ve bu slayt üzerinden daha fazlasını öğrenin:
|
||||
|
||||
<a href="https://dunglas.dev/2022/10/frankenphp-the-modern-php-app-server-written-in-go/"><img src="https://dunglas.dev/wp-content/uploads/2022/10/frankenphp.png" alt="Slides" width="600"></a>
|
||||
|
||||
## Başlarken
|
||||
|
||||
### Docker
|
||||
|
||||
```console
|
||||
docker run -v $PWD:/app/public \
|
||||
-p 80:80 -p 443:443 -p 443:443/udp \
|
||||
dunglas/frankenphp
|
||||
```
|
||||
|
||||
`https://localhost` adresine gidin ve keyfini çıkarın!
|
||||
|
||||
> [!TIP]
|
||||
>
|
||||
> `https://127.0.0.1` kullanmaya çalışmayın. `https://localhost` kullanın ve kendinden imzalı sertifikayı kabul edin.
|
||||
> Kullanılacak alan adını değiştirmek için [`SERVER_NAME` ortam değişkenini](https://frankenphp.dev/tr/docs/config#ortam-değişkenleri) kullanın.
|
||||
|
||||
### Binary Çıktısı
|
||||
|
||||
Docker kullanmayı tercih etmiyorsanız, Linux ve macOS için bağımsız FrankenPHP binary dosyası sağlıyoruz
|
||||
[PHP 8.3](https://www.php.net/releases/8.3/en.php) ve en popüler PHP eklentilerini de içermekte: [FrankenPHP](https://github.com/dunglas/frankenphp/releases) indirin
|
||||
|
||||
Geçerli dizinin içeriğini başlatmak için çalıştırın:
|
||||
|
||||
```console
|
||||
./frankenphp php-server
|
||||
```
|
||||
|
||||
Ayrıca aşağıdaki tek komut satırı ile de çalıştırabilirsiniz:
|
||||
|
||||
```console
|
||||
./frankenphp php-cli /path/to/your/script.php
|
||||
```
|
||||
|
||||
## Docs
|
||||
|
||||
* [Worker modu](worker.md)
|
||||
* [Early Hints desteği (103 HTTP durum kodu)](early-hints.md)
|
||||
* [Real-time](mercure.md)
|
||||
* [Konfigürasyon](config.md)
|
||||
* [Docker imajları](docker.md)
|
||||
* [Production'a dağıtım](production.md)
|
||||
* [**Bağımsız** kendiliğinden çalıştırılabilir PHP uygulamaları oluşturma](embed.md)
|
||||
* [Statik binary'leri oluşturma](static.md)
|
||||
* [Kaynak dosyalarından derleme](config.md)
|
||||
* [Laravel entegrasyonu](laravel.md)
|
||||
* [Bilinen sorunlar](known-issues.md)
|
||||
* [Demo uygulama (Symfony) ve kıyaslamalar](https://github.com/dunglas/frankenphp-demo)
|
||||
* [Go kütüphane dokümantasonu](https://pkg.go.dev/github.com/dunglas/frankenphp)
|
||||
* [Katkıda bulunma ve hata ayıklama](CONTRIBUTING.md)
|
||||
|
||||
## Örnekler ve İskeletler
|
||||
|
||||
* [Symfony](https://github.com/dunglas/symfony-docker)
|
||||
* [API Platform](https://api-platform.com/docs/distribution/)
|
||||
* [Laravel](https://frankenphp.dev/docs/laravel/)
|
||||
* [Sulu](https://sulu.io/blog/running-sulu-with-frankenphp)
|
||||
* [WordPress](https://github.com/StephenMiracle/frankenwp)
|
||||
* [Drupal](https://github.com/dunglas/frankenphp-drupal)
|
||||
* [Joomla](https://github.com/alexandreelise/frankenphp-joomla)
|
||||
* [TYPO3](https://github.com/ochorocho/franken-typo3)
|
||||
# FrankenPHP: PHP için Modern Uygulama Sunucusu
|
||||
|
||||
<h1 align="center"><a href="https://frankenphp.dev"><img src="../../frankenphp.png" alt="FrankenPHP" width="600"></a></h1>
|
||||
|
||||
FrankenPHP, [Caddy](https://caddyserver.com/) web sunucusunun üzerine inşa edilmiş PHP için modern bir uygulama sunucusudur.
|
||||
|
||||
FrankenPHP, çarpıcı özellikleri sayesinde PHP uygulamalarınıza süper güçler kazandırır: [Early Hints\*](https://frankenphp.dev/docs/early-hints/), [worker modu](https://frankenphp.dev/docs/worker/), [real-time yetenekleri](https://frankenphp.dev/docs/mercure/), otomatik HTTPS, HTTP/2 ve HTTP/3 desteği...
|
||||
|
||||
FrankenPHP herhangi bir PHP uygulaması ile çalışır ve worker modu ile resmi entegrasyonları sayesinde Laravel ve Symfony projelerinizi her zamankinden daha performanslı hale getirir.
|
||||
|
||||
FrankenPHP, PHP'yi `net/http` kullanarak herhangi bir uygulamaya yerleştirmek için bağımsız bir Go kütüphanesi olarak da kullanılabilir.
|
||||
|
||||
[_Frankenphp.dev_](https://frankenphp.dev) adresinden ve bu slayt üzerinden daha fazlasını öğrenin:
|
||||
|
||||
<a href="https://dunglas.dev/2022/10/frankenphp-the-modern-php-app-server-written-in-go/"><img src="https://dunglas.dev/wp-content/uploads/2022/10/frankenphp.png" alt="Slides" width="600"></a>
|
||||
|
||||
## Başlarken
|
||||
|
||||
### Docker
|
||||
|
||||
```console
|
||||
docker run -v $PWD:/app/public \
|
||||
-p 80:80 -p 443:443 -p 443:443/udp \
|
||||
dunglas/frankenphp
|
||||
```
|
||||
|
||||
`https://localhost` adresine gidin ve keyfini çıkarın!
|
||||
|
||||
> [!TIP]
|
||||
>
|
||||
> `https://127.0.0.1` kullanmaya çalışmayın. `https://localhost` kullanın ve kendinden imzalı sertifikayı kabul edin.
|
||||
> Kullanılacak alan adını değiştirmek için [`SERVER_NAME` ortam değişkenini](https://frankenphp.dev/tr/docs/config#ortam-değişkenleri) kullanın.
|
||||
|
||||
### Binary Çıktısı
|
||||
|
||||
Docker kullanmayı tercih etmiyorsanız, Linux ve macOS için bağımsız FrankenPHP binary dosyası sağlıyoruz
|
||||
[PHP 8.4](https://www.php.net/releases/8.4/en.php) ve en popüler PHP eklentilerini de içermekte: [FrankenPHP](https://github.com/dunglas/frankenphp/releases) indirin
|
||||
|
||||
Geçerli dizinin içeriğini başlatmak için çalıştırın:
|
||||
|
||||
```console
|
||||
./frankenphp php-server
|
||||
```
|
||||
|
||||
Ayrıca aşağıdaki tek komut satırı ile de çalıştırabilirsiniz:
|
||||
|
||||
```console
|
||||
./frankenphp php-cli /path/to/your/script.php
|
||||
```
|
||||
|
||||
## Docs
|
||||
|
||||
- [Worker modu](worker.md)
|
||||
- [Early Hints desteği (103 HTTP durum kodu)](early-hints.md)
|
||||
- [Real-time](mercure.md)
|
||||
- [Konfigürasyon](config.md)
|
||||
- [Docker imajları](docker.md)
|
||||
- [Production'a dağıtım](production.md)
|
||||
- [**Bağımsız** kendiliğinden çalıştırılabilir PHP uygulamaları oluşturma](embed.md)
|
||||
- [Statik binary'leri oluşturma](static.md)
|
||||
- [Kaynak dosyalarından derleme](config.md)
|
||||
- [Laravel entegrasyonu](laravel.md)
|
||||
- [Bilinen sorunlar](known-issues.md)
|
||||
- [Demo uygulama (Symfony) ve kıyaslamalar](https://github.com/dunglas/frankenphp-demo)
|
||||
- [Go kütüphane dokümantasonu](https://pkg.go.dev/github.com/dunglas/frankenphp)
|
||||
- [Katkıda bulunma ve hata ayıklama](CONTRIBUTING.md)
|
||||
|
||||
## Örnekler ve İskeletler
|
||||
|
||||
- [Symfony](https://github.com/dunglas/symfony-docker)
|
||||
- [API Platform](https://api-platform.com/docs/distribution/)
|
||||
- [Laravel](https://frankenphp.dev/docs/laravel/)
|
||||
- [Sulu](https://sulu.io/blog/running-sulu-with-frankenphp)
|
||||
- [WordPress](https://github.com/StephenMiracle/frankenwp)
|
||||
- [Drupal](https://github.com/dunglas/frankenphp-drupal)
|
||||
- [Joomla](https://github.com/alexandreelise/frankenphp-joomla)
|
||||
- [TYPO3](https://github.com/ochorocho/franken-typo3)
|
||||
- [Magento2](https://github.com/ekino/frankenphp-magento2)
|
||||
|
||||
@@ -1,100 +1,100 @@
|
||||
# Kaynak Kodlardan Derleme
|
||||
|
||||
Bu doküman, PHP'yi dinamik bir kütüphane olarak yükleyecek bir FrankenPHP yapısının nasıl oluşturulacağını açıklamaktadır.
|
||||
Önerilen yöntem bu şekildedir.
|
||||
|
||||
Alternatif olarak, [statik yapılar oluşturma](static.md) da mümkündür.
|
||||
|
||||
## PHP'yi yükleyin
|
||||
|
||||
FrankenPHP, PHP 8.2 ve üstü ile uyumludur.
|
||||
|
||||
İlk olarak, [PHP'nin kaynaklarını edinin](https://www.php.net/downloads.php) ve bunları çıkarın:
|
||||
|
||||
```console
|
||||
tar xf php-*
|
||||
cd php-*/
|
||||
```
|
||||
|
||||
Ardından, PHP'yi platformunuz için yapılandırın.
|
||||
|
||||
Bu şekilde yapılandırma gereklidir, ancak başka opsiyonlar da ekleyebilirsiniz (örn. ekstra uzantılar)
|
||||
İhtiyaç halinde.
|
||||
|
||||
### Linux
|
||||
|
||||
```console
|
||||
./configure \
|
||||
--enable-embed \
|
||||
--enable-zts \
|
||||
--disable-zend-signals \
|
||||
--enable-zend-max-execution-timers
|
||||
```
|
||||
|
||||
### Mac
|
||||
|
||||
Yüklemek için [Homebrew](https://brew.sh/) paket yöneticisini kullanın
|
||||
`libiconv`, `bison`, `re2c` ve `pkg-config`:
|
||||
|
||||
```console
|
||||
brew install libiconv bison re2c pkg-config
|
||||
echo 'export PATH="/opt/homebrew/opt/bison/bin:$PATH"' >> ~/.zshrc
|
||||
```
|
||||
|
||||
Ardından yapılandırma betiğini çalıştırın:
|
||||
|
||||
```console
|
||||
./configure \
|
||||
--enable-embed=static \
|
||||
--enable-zts \
|
||||
--disable-zend-signals \
|
||||
--disable-opcache-jit \
|
||||
--enable-static \
|
||||
--enable-shared=no \
|
||||
--with-iconv=/opt/homebrew/opt/libiconv/
|
||||
```
|
||||
|
||||
## PHP Derleyin
|
||||
|
||||
Son olarak, PHP'yi derleyin ve kurun:
|
||||
|
||||
```console
|
||||
make -j"$(getconf _NPROCESSORS_ONLN)"
|
||||
sudo make install
|
||||
```
|
||||
|
||||
## Go Uygulamasını Derleyin
|
||||
|
||||
Artık Go kütüphanesini kullanabilir ve Caddy yapımızı derleyebilirsiniz:
|
||||
|
||||
```console
|
||||
curl -L https://github.com/dunglas/frankenphp/archive/refs/heads/main.tar.gz | tar xz
|
||||
cd frankenphp-main/caddy/frankenphp
|
||||
CGO_CFLAGS=$(php-config --includes) CGO_LDFLAGS="$(php-config --ldflags) $(php-config --libs)" go build
|
||||
```
|
||||
|
||||
### Xcaddy kullanımı
|
||||
|
||||
Alternatif olarak, FrankenPHP'yi [özel Caddy modülleri](https://caddyserver.com/docs/modules/) ile derlemek için [xcaddy](https://github.com/caddyserver/xcaddy) kullanın:
|
||||
|
||||
```console
|
||||
CGO_ENABLED=1 \
|
||||
XCADDY_GO_BUILD_FLAGS="-ldflags '-w -s'" \
|
||||
xcaddy build \
|
||||
--output frankenphp \
|
||||
--with github.com/dunglas/frankenphp/caddy \
|
||||
--with github.com/dunglas/caddy-cbrotli \
|
||||
--with github.com/dunglas/mercure/caddy \
|
||||
--with github.com/dunglas/vulcain/caddy
|
||||
# Add extra Caddy modules here
|
||||
```
|
||||
|
||||
> [!TIP]
|
||||
>
|
||||
> Eğer musl libc (Alpine Linux'ta varsayılan) ve Symfony kullanıyorsanız,
|
||||
> varsayılan yığın boyutunu artırmanız gerekebilir.
|
||||
> Aksi takdirde, şu tarz hatalar alabilirsiniz `PHP Fatal error: Maximum call stack size of 83360 bytes reached during compilation. Try splitting expression`
|
||||
>
|
||||
> Bunu yapmak için, `XCADDY_GO_BUILD_FLAGS` ortam değişkenini bu şekilde değiştirin
|
||||
> `XCADDY_GO_BUILD_FLAGS=$'-ldflags "-w -s -extldflags \'-Wl,-z,stack-size=0x80000\'"'`
|
||||
> (yığın boyutunun değerini uygulamanızın ihtiyaçlarına göre değiştirin).
|
||||
# Kaynak Kodlardan Derleme
|
||||
|
||||
Bu doküman, PHP'yi dinamik bir kütüphane olarak yükleyecek bir FrankenPHP yapısının nasıl oluşturulacağını açıklamaktadır.
|
||||
Önerilen yöntem bu şekildedir.
|
||||
|
||||
Alternatif olarak, [statik yapılar oluşturma](static.md) da mümkündür.
|
||||
|
||||
## PHP'yi yükleyin
|
||||
|
||||
FrankenPHP, PHP 8.2 ve üstü ile uyumludur.
|
||||
|
||||
İlk olarak, [PHP'nin kaynaklarını edinin](https://www.php.net/downloads.php) ve bunları çıkarın:
|
||||
|
||||
```console
|
||||
tar xf php-*
|
||||
cd php-*/
|
||||
```
|
||||
|
||||
Ardından, PHP'yi platformunuz için yapılandırın.
|
||||
|
||||
Bu şekilde yapılandırma gereklidir, ancak başka opsiyonlar da ekleyebilirsiniz (örn. ekstra uzantılar)
|
||||
İhtiyaç halinde.
|
||||
|
||||
### Linux
|
||||
|
||||
```console
|
||||
./configure \
|
||||
--enable-embed \
|
||||
--enable-zts \
|
||||
--disable-zend-signals \
|
||||
--enable-zend-max-execution-timers
|
||||
```
|
||||
|
||||
### Mac
|
||||
|
||||
Yüklemek için [Homebrew](https://brew.sh/) paket yöneticisini kullanın
|
||||
`libiconv`, `bison`, `re2c` ve `pkg-config`:
|
||||
|
||||
```console
|
||||
brew install libiconv bison re2c pkg-config
|
||||
echo 'export PATH="/opt/homebrew/opt/bison/bin:$PATH"' >> ~/.zshrc
|
||||
```
|
||||
|
||||
Ardından yapılandırma betiğini çalıştırın:
|
||||
|
||||
```console
|
||||
./configure \
|
||||
--enable-embed=static \
|
||||
--enable-zts \
|
||||
--disable-zend-signals \
|
||||
--disable-opcache-jit \
|
||||
--enable-static \
|
||||
--enable-shared=no \
|
||||
--with-iconv=/opt/homebrew/opt/libiconv/
|
||||
```
|
||||
|
||||
## PHP Derleyin
|
||||
|
||||
Son olarak, PHP'yi derleyin ve kurun:
|
||||
|
||||
```console
|
||||
make -j"$(getconf _NPROCESSORS_ONLN)"
|
||||
sudo make install
|
||||
```
|
||||
|
||||
## Go Uygulamasını Derleyin
|
||||
|
||||
Artık Go kütüphanesini kullanabilir ve Caddy yapımızı derleyebilirsiniz:
|
||||
|
||||
```console
|
||||
curl -L https://github.com/dunglas/frankenphp/archive/refs/heads/main.tar.gz | tar xz
|
||||
cd frankenphp-main/caddy/frankenphp
|
||||
CGO_CFLAGS=$(php-config --includes) CGO_LDFLAGS="$(php-config --ldflags) $(php-config --libs)" go build
|
||||
```
|
||||
|
||||
### Xcaddy kullanımı
|
||||
|
||||
Alternatif olarak, FrankenPHP'yi [özel Caddy modülleri](https://caddyserver.com/docs/modules/) ile derlemek için [xcaddy](https://github.com/caddyserver/xcaddy) kullanın:
|
||||
|
||||
```console
|
||||
CGO_ENABLED=1 \
|
||||
XCADDY_GO_BUILD_FLAGS="-ldflags '-w -s'" \
|
||||
xcaddy build \
|
||||
--output frankenphp \
|
||||
--with frankenphp.dev/caddy \
|
||||
--with github.com/dunglas/caddy-cbrotli \
|
||||
--with github.com/dunglas/mercure/caddy \
|
||||
--with github.com/dunglas/vulcain/caddy
|
||||
# Add extra Caddy modules here
|
||||
```
|
||||
|
||||
> [!TIP]
|
||||
>
|
||||
> Eğer musl libc (Alpine Linux'ta varsayılan) ve Symfony kullanıyorsanız,
|
||||
> varsayılan yığın boyutunu artırmanız gerekebilir.
|
||||
> Aksi takdirde, şu tarz hatalar alabilirsiniz `PHP Fatal error: Maximum call stack size of 83360 bytes reached during compilation. Try splitting expression`
|
||||
>
|
||||
> Bunu yapmak için, `XCADDY_GO_BUILD_FLAGS` ortam değişkenini bu şekilde değiştirin
|
||||
> `XCADDY_GO_BUILD_FLAGS=$'-ldflags "-w -s -extldflags \'-Wl,-z,stack-size=0x80000\'"'`
|
||||
> (yığın boyutunun değerini uygulamanızın ihtiyaçlarına göre değiştirin).
|
||||
|
||||
@@ -1,163 +1,180 @@
|
||||
# Konfigürasyon
|
||||
|
||||
FrankenPHP, Caddy'nin yanı sıra Mercure ve Vulcain modülleri [Caddy tarafından desteklenen formatlar](https://caddyserver.com/docs/getting-started#your-first-config) kullanılarak yapılandırılabilir.
|
||||
|
||||
Docker imajlarında] (docker.md), `Caddyfile` `/etc/caddy/Caddyfile` adresinde bulunur.
|
||||
Statik ikili, başlatıldığı dizinde `Caddyfile` dosyasını arayacaktır.
|
||||
|
||||
PHP'nin kendisi [bir `php.ini` dosyası kullanılarak yapılandırılabilir](https://www.php.net/manual/tr/configuration.file.php).
|
||||
|
||||
Varsayılan olarak, Docker imajlarıyla birlikte verilen PHP ve statik ikili dosyada bulunan PHP, FrankenPHP'nin başlatıldığı dizinde ve `/usr/local/etc/php/` içinde bir `php.ini` dosyası arayacaktır. Ayrıca `.ini` ile biten tüm dosyaları `/usr/local/etc/php/conf.d/` dizininden yükleyecektir.
|
||||
|
||||
Öntanımlı olarak `php.ini` dosyası yoktur, PHP projesi tarafından sağlanan resmi bir şablonu kopyalamanız gerekir.
|
||||
Docker'da şablonlar imajlar içinde sağlanır:
|
||||
|
||||
```dockerfile
|
||||
FROM dunglas/frankenphp
|
||||
|
||||
# Developement:
|
||||
RUN cp $PHP_INI_DIR/php.ini-development $PHP_INI_DIR/php.ini
|
||||
|
||||
# Veya production:
|
||||
RUN cp $PHP_INI_DIR/php.ini-production $PHP_INI_DIR/php.ini
|
||||
```
|
||||
|
||||
Docker kullanmıyorsanız, [PHP kaynak kodu](https://github.com/php/php-src/) ile birlikte verilen `php.ini-production` veya `php.ini-development` dosyalarından birini kopyalayın.
|
||||
|
||||
## Caddyfile Konfigürasyonu
|
||||
|
||||
FrankenPHP yürütücüsünü kaydetmek için `frankenphp` [global seçenek](https://caddyserver.com/docs/caddyfile/concepts#global-options) ayarlanmalıdır, ardından PHP uygulamanızı sunmak için site blokları içinde `php_server` veya `php` [HTTP yönergeleri](https://caddyserver.com/docs/caddyfile/concepts#directives) kullanılabilir.
|
||||
|
||||
Minimal örnek:
|
||||
|
||||
```caddyfile
|
||||
{
|
||||
# FrankenPHP'yi aktif et
|
||||
frankenphp
|
||||
}
|
||||
|
||||
localhost {
|
||||
# Sıkıştırmayı etkinleştir (isteğe bağlı)
|
||||
encode zstd br gzip
|
||||
# Geçerli dizindeki PHP dosyalarını çalıştırın ve varlıkları sunun
|
||||
php_server
|
||||
}
|
||||
```
|
||||
|
||||
İsteğe bağlı olarak, oluşturulacak iş parçacığı sayısı ve sunucuyla birlikte başlatılacak [işçi betikleri] (worker.md) global seçenek altında belirtilebilir.
|
||||
|
||||
```caddyfile
|
||||
{
|
||||
frankenphp {
|
||||
num_threads <num_threads> # Başlatılacak PHP iş parçacığı sayısını ayarlar. Varsayılan: Mevcut CPU çekirdek sayısının 2 katı.
|
||||
worker {
|
||||
file <path> # Çalışan komut dosyasının yolunu ayarlar.
|
||||
num <num> # Başlatılacak PHP iş parçacığı sayısını ayarlar, varsayılan değer mevcut CPU çekirdek sayısının 2 katıdır.
|
||||
env <key> <value> # Ek bir ortam değişkenini verilen değere ayarlar. Birden fazla ortam değişkeni için birden fazla kez belirtilebilir.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# ...
|
||||
```
|
||||
|
||||
Alternatif olarak, `worker` seçeneğinin tek satırlık kısa formunu kullanabilirsiniz:
|
||||
|
||||
```caddyfile
|
||||
{
|
||||
frankenphp {
|
||||
worker <file> <num>
|
||||
}
|
||||
}
|
||||
|
||||
# ...
|
||||
```
|
||||
|
||||
Aynı sunucuda birden fazla uygulamaya hizmet veriyorsanız birden fazla işçi de tanımlayabilirsiniz:
|
||||
|
||||
```caddyfile
|
||||
{
|
||||
frankenphp {
|
||||
worker /path/to/app/public/index.php <num>
|
||||
worker /path/to/other/public/index.php <num>
|
||||
}
|
||||
}
|
||||
|
||||
app.example.com {
|
||||
root * /path/to/app/public
|
||||
php_server
|
||||
}
|
||||
|
||||
other.example.com {
|
||||
root * /path/to/other/public
|
||||
php_server
|
||||
}
|
||||
|
||||
# ...
|
||||
```
|
||||
|
||||
Genellikle ihtiyacınız olan şey `php_server` yönergesini kullanmaktır,
|
||||
ancak tam kontrole ihtiyacınız varsa, daha düşük seviyeli `php` yönergesini kullanabilirsiniz:
|
||||
|
||||
php_server` yönergesini kullanmak bu yapılandırmay ile aynıdır:
|
||||
|
||||
```caddyfile
|
||||
route {
|
||||
# Dizin istekleri için sondaki eğik çizgiyi, diğer adıyla taksim işaretini ekleyin
|
||||
@canonicalPath {
|
||||
file {path}/index.php
|
||||
not path */
|
||||
}
|
||||
redir @canonicalPath {path}/ 308
|
||||
# İstenen dosya mevcut değilse, dizin dosyalarını deneyin
|
||||
@indexFiles file {
|
||||
try_files {path} {path}/index.php index.php
|
||||
split_path .php
|
||||
}
|
||||
rewrite @indexFiles {http.matchers.file.relative}
|
||||
# FrankenPHP!
|
||||
@phpFiles path *.php
|
||||
php @phpFiles
|
||||
file_server
|
||||
}
|
||||
```
|
||||
|
||||
php_server` ve `php` yönergeleri aşağıdaki seçeneklere sahiptir:
|
||||
|
||||
```caddyfile
|
||||
php_server [<matcher>] {
|
||||
root <directory> # Sitenin kök klasörünü ayarlar. Öntanımlı: `root` yönergesi.
|
||||
split_path <delim...> # URI'yi iki parçaya bölmek için alt dizgeleri ayarlar. İlk eşleşen alt dizge "yol bilgisini" yoldan ayırmak için kullanılır. İlk parça eşleşen alt dizeyle sonlandırılır ve gerçek kaynak (CGI betiği) adı olarak kabul edilir. İkinci parça betiğin kullanması için PATH_INFO olarak ayarlanacaktır. Varsayılan: `.php`
|
||||
resolve_root_symlink false # Varsa, sembolik bir bağlantıyı değerlendirerek `root` dizininin gerçek değerine çözümlenmesini devre dışı bırakır (varsayılan olarak etkindir).
|
||||
env <key> <value> # Ek bir ortam değişkenini verilen değere ayarlar. Birden fazla ortam değişkeni için birden fazla kez belirtilebilir.
|
||||
}
|
||||
```
|
||||
|
||||
## Ortam Değişkenleri
|
||||
|
||||
Aşağıdaki ortam değişkenleri `Caddyfile` içinde değişiklik yapmadan Caddy yönergelerini entegre etmek için kullanılabilir:
|
||||
|
||||
* `SERVER_NAME`: değiştirin [dinlenecek adresleri](https://caddyserver.com/docs/caddyfile/concepts#addresses), sağlanan ana bilgisayar adları oluşturulan TLS sertifikası için de kullanılacaktır
|
||||
* `CADDY_GLOBAL_OPTIONS`: entegre edin [global seçenekler](https://caddyserver.com/docs/caddyfile/options)
|
||||
* `FRANKENPHP_CONFIG`: `frankenphp` yönergesi altına yapılandırma entegre edin
|
||||
|
||||
FPM ve CLI SAPI'lerinde olduğu gibi, ortam değişkenleri varsayılan olarak `$_SERVER` süper globalinde gösterilir.
|
||||
|
||||
[`variables_order`'a ait PHP yönergesinin](https://www.php.net/manual/en/ini.core.php#ini.variables-order) `S` değeri bu yönergede `E`'nin başka bir yere yerleştirilmesinden bağımsız olarak her zaman `ES` ile eş değerdir.
|
||||
|
||||
## PHP konfigürasyonu
|
||||
|
||||
Ek olarak [PHP yapılandırma dosyalarını](https://www.php.net/manual/en/configuration.file.php#configuration.file.scan) yüklemek için
|
||||
`PHP_INI_SCAN_DIR` ortam değişkeni kullanılabilir.
|
||||
Ayarlandığında, PHP verilen dizinlerde bulunan `.ini` uzantılı tüm dosyaları yükleyecektir.
|
||||
|
||||
## Hata Ayıklama Modunu Etkinleştirin
|
||||
|
||||
Docker imajını kullanırken, hata ayıklama modunu etkinleştirmek için `CADDY_GLOBAL_OPTIONS` ortam değişkenini `debug` olarak ayarlayın:
|
||||
|
||||
```console
|
||||
docker run -v $PWD:/app/public \
|
||||
-e CADDY_GLOBAL_OPTIONS=debug \
|
||||
-p 80:80 -p 443:443 -p 443:443/udp \
|
||||
dunglas/frankenphp
|
||||
```
|
||||
# Konfigürasyon
|
||||
|
||||
FrankenPHP, Caddy'nin yanı sıra Mercure ve Vulcain modülleri [Caddy tarafından desteklenen formatlar](https://caddyserver.com/docs/getting-started#your-first-config) kullanılarak yapılandırılabilir.
|
||||
|
||||
Docker imajlarında] (docker.md), `Caddyfile` `/etc/frankenphp/Caddyfile` adresinde bulunur.
|
||||
Statik ikili, başlatıldığı dizinde `Caddyfile` dosyasını arayacaktır.
|
||||
|
||||
PHP'nin kendisi [bir `php.ini` dosyası kullanılarak yapılandırılabilir](https://www.php.net/manual/tr/configuration.file.php).
|
||||
|
||||
PHP yorumlayıcısı aşağıdaki konumlarda arama yapacaktır:
|
||||
|
||||
Docker:
|
||||
|
||||
- php.ini: `/usr/local/etc/php/php.ini` Varsayılan olarak php.ini sağlanmaz.
|
||||
- ek yapılandırma dosyaları: `/usr/local/etc/php/conf.d/*.ini`
|
||||
- php uzantıları: `/usr/local/lib/php/extensions/no-debug-zts-<YYYYMMDD>/`
|
||||
- PHP projesi tarafından sağlanan resmi bir şablonu kopyalamalısınız:
|
||||
|
||||
```dockerfile
|
||||
FROM dunglas/frankenphp
|
||||
|
||||
# Developement:
|
||||
RUN cp $PHP_INI_DIR/php.ini-development $PHP_INI_DIR/php.ini
|
||||
|
||||
# Veya production:
|
||||
RUN cp $PHP_INI_DIR/php.ini-production $PHP_INI_DIR/php.ini
|
||||
```
|
||||
|
||||
FrankenPHP kurulumu (.rpm veya .deb):
|
||||
|
||||
- php.ini: `/etc/frankenphp/php.ini` Varsayılan olarak üretim ön ayarlarına sahip bir php.ini dosyası sağlanır.
|
||||
- ek yapılandırma dosyaları: `/etc/frankenphp/php.d/*.ini`
|
||||
- php uzantıları: `/usr/lib/frankenphp/modules/`
|
||||
|
||||
Statik ikili:
|
||||
|
||||
- php.ini: `frankenphp run` veya `frankenphp php-server` komutunun çalıştırıldığı dizin, ardından `/etc/frankenphp/php.ini`
|
||||
- ek yapılandırma dosyaları: `/etc/frankenphp/php.d/*.ini`
|
||||
- php uzantıları: yüklenemez
|
||||
- [PHP kaynak kodu](https://github.com/php/php-src/) ile birlikte verilen `php.ini-production` veya `php.ini-development` dosyalarından birini kopyalayın.
|
||||
|
||||
## Caddyfile Konfigürasyonu
|
||||
|
||||
PHP uygulamanızı sunmak için site blokları içinde `php_server` veya `php` [HTTP yönergeleri](https://caddyserver.com/docs/caddyfile/concepts#directives) kullanılabilir.
|
||||
|
||||
Minimal örnek:
|
||||
|
||||
```caddyfile
|
||||
localhost {
|
||||
# Sıkıştırmayı etkinleştir (isteğe bağlı)
|
||||
encode zstd br gzip
|
||||
# Geçerli dizindeki PHP dosyalarını çalıştırın ve varlıkları sunun
|
||||
php_server
|
||||
}
|
||||
```
|
||||
|
||||
FrankenPHP'yi global seçenek kullanarak açıkça yapılandırabilirsiniz:
|
||||
`frankenphp` [global seçenek](https://caddyserver.com/docs/caddyfile/concepts#global-options) FrankenPHP'yi yapılandırmak için kullanılabilir.
|
||||
|
||||
```caddyfile
|
||||
{
|
||||
frankenphp {
|
||||
num_threads <num_threads> # Başlatılacak PHP iş parçacığı sayısını ayarlar. Varsayılan: Mevcut CPU çekirdek sayısının 2 katı.
|
||||
worker {
|
||||
file <path> # Çalışan komut dosyasının yolunu ayarlar.
|
||||
num <num> # Başlatılacak PHP iş parçacığı sayısını ayarlar, varsayılan değer mevcut CPU çekirdek sayısının 2 katıdır.
|
||||
env <key> <value> # Ek bir ortam değişkenini verilen değere ayarlar. Birden fazla ortam değişkeni için birden fazla kez belirtilebilir.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# ...
|
||||
```
|
||||
|
||||
Alternatif olarak, `worker` seçeneğinin tek satırlık kısa formunu kullanabilirsiniz:
|
||||
|
||||
```caddyfile
|
||||
{
|
||||
frankenphp {
|
||||
worker <file> <num>
|
||||
}
|
||||
}
|
||||
|
||||
# ...
|
||||
```
|
||||
|
||||
Aynı sunucuda birden fazla uygulamaya hizmet veriyorsanız birden fazla işçi de tanımlayabilirsiniz:
|
||||
|
||||
```caddyfile
|
||||
app.example.com {
|
||||
php_server {
|
||||
root /path/to/app/public
|
||||
worker index.php <num>
|
||||
}
|
||||
}
|
||||
|
||||
other.example.com {
|
||||
php_server {
|
||||
root /path/to/other/public
|
||||
worker index.php <num>
|
||||
}
|
||||
}
|
||||
|
||||
# ...
|
||||
```
|
||||
|
||||
Genellikle ihtiyacınız olan şey `php_server` yönergesini kullanmaktır,
|
||||
ancak tam kontrole ihtiyacınız varsa, daha düşük seviyeli `php` yönergesini kullanabilirsiniz:
|
||||
|
||||
php_server` yönergesini kullanmak bu yapılandırmay ile aynıdır:
|
||||
|
||||
```caddyfile
|
||||
route {
|
||||
# Dizin istekleri için sondaki eğik çizgiyi, diğer adıyla taksim işaretini ekleyin
|
||||
@canonicalPath {
|
||||
file {path}/index.php
|
||||
not path */
|
||||
}
|
||||
redir @canonicalPath {path}/ 308
|
||||
# İstenen dosya mevcut değilse, dizin dosyalarını deneyin
|
||||
@indexFiles file {
|
||||
try_files {path} {path}/index.php index.php
|
||||
split_path .php
|
||||
}
|
||||
rewrite @indexFiles {http.matchers.file.relative}
|
||||
# FrankenPHP!
|
||||
@phpFiles path *.php
|
||||
php @phpFiles
|
||||
file_server
|
||||
}
|
||||
```
|
||||
|
||||
php_server`ve`php` yönergeleri aşağıdaki seçeneklere sahiptir:
|
||||
|
||||
```caddyfile
|
||||
php_server [<matcher>] {
|
||||
root <directory> # Sitenin kök klasörünü ayarlar. Öntanımlı: `root` yönergesi.
|
||||
split_path <delim...> # URI'yi iki parçaya bölmek için alt dizgeleri ayarlar. İlk eşleşen alt dizge "yol bilgisini" yoldan ayırmak için kullanılır. İlk parça eşleşen alt dizeyle sonlandırılır ve gerçek kaynak (CGI betiği) adı olarak kabul edilir. İkinci parça betiğin kullanması için PATH_INFO olarak ayarlanacaktır. Varsayılan: `.php`
|
||||
resolve_root_symlink false # Varsa, sembolik bir bağlantıyı değerlendirerek `root` dizininin gerçek değerine çözümlenmesini devre dışı bırakır (varsayılan olarak etkindir).
|
||||
env <key> <value> # Ek bir ortam değişkenini verilen değere ayarlar. Birden fazla ortam değişkeni için birden fazla kez belirtilebilir.
|
||||
file_server off # Yerleşik file_server yönergesini devre dışı bırakır.
|
||||
worker { # Bu sunucuya özgü bir worker oluşturur. Birden fazla worker için birden fazla kez belirtilebilir.
|
||||
file <path> # Worker betiğinin yolunu ayarlar, php_server köküne göre göreceli olabilir
|
||||
num <num> # Başlatılacak PHP iş parçacığı sayısını ayarlar, varsayılan değer mevcut CPU çekirdek sayısının 2 katıdır
|
||||
name <name> # Worker için günlüklerde ve metriklerde kullanılan bir ad ayarlar. Varsayılan: worker dosyasının mutlak yolu. Bir php_server bloğunda tanımlandığında her zaman m# ile başlar.
|
||||
watch <path> # Dosya değişikliklerini izlemek için yolu ayarlar. Birden fazla yol için birden fazla kez belirtilebilir.
|
||||
env <key> <value> # Ek bir ortam değişkenini verilen değere ayarlar. Birden fazla ortam değişkeni için birden fazla kez belirtilebilir. Bu worker için ortam değişkenleri ayrıca php_server üst öğesinden devralınır, ancak burada geçersiz kılınabilir.
|
||||
}
|
||||
worker <other_file> <num> # Global frankenphp bloğundaki gibi kısa formu da kullanabilirsiniz.
|
||||
}
|
||||
```
|
||||
|
||||
## Ortam Değişkenleri
|
||||
|
||||
Aşağıdaki ortam değişkenleri `Caddyfile` içinde değişiklik yapmadan Caddy yönergelerini entegre etmek için kullanılabilir:
|
||||
|
||||
- `SERVER_NAME`: değiştirin [dinlenecek adresleri](https://caddyserver.com/docs/caddyfile/concepts#addresses), sağlanan ana bilgisayar adları oluşturulan TLS sertifikası için de kullanılacaktır
|
||||
- `CADDY_GLOBAL_OPTIONS`: entegre edin [global seçenekler](https://caddyserver.com/docs/caddyfile/options)
|
||||
- `FRANKENPHP_CONFIG`: `frankenphp` yönergesi altına yapılandırma entegre edin
|
||||
|
||||
FPM ve CLI SAPI'lerinde olduğu gibi, ortam değişkenleri varsayılan olarak `$_SERVER` süper globalinde gösterilir.
|
||||
|
||||
[`variables_order`'a ait PHP yönergesinin](https://www.php.net/manual/en/ini.core.php#ini.variables-order) `S` değeri bu yönergede `E`'nin başka bir yere yerleştirilmesinden bağımsız olarak her zaman `ES` ile eş değerdir.
|
||||
|
||||
## PHP konfigürasyonu
|
||||
|
||||
Ek olarak [PHP yapılandırma dosyalarını](https://www.php.net/manual/en/configuration.file.php#configuration.file.scan) yüklemek için
|
||||
`PHP_INI_SCAN_DIR` ortam değişkeni kullanılabilir.
|
||||
Ayarlandığında, PHP verilen dizinlerde bulunan `.ini` uzantılı tüm dosyaları yükleyecektir.
|
||||
|
||||
## Hata Ayıklama Modunu Etkinleştirin
|
||||
|
||||
Docker imajını kullanırken, hata ayıklama modunu etkinleştirmek için `CADDY_GLOBAL_OPTIONS` ortam değişkenini `debug` olarak ayarlayın:
|
||||
|
||||
```console
|
||||
docker run -v $PWD:/app/public \
|
||||
-e CADDY_GLOBAL_OPTIONS=debug \
|
||||
-p 80:80 -p 443:443 -p 443:443/udp \
|
||||
dunglas/frankenphp
|
||||
```
|
||||
|
||||
@@ -1,171 +1,171 @@
|
||||
# Özel Docker İmajı Oluşturma
|
||||
|
||||
[Resmi PHP imajları](https://hub.docker.com/_/php/) temel alınarak [FrankenPHP Docker imajları](https://hub.docker.com/r/dunglas/frankenphp) hazırlanmıştır. Popüler mimariler için Debian ve Alpine Linux varyantları sağlanmıştır. Debian dağıtımı tavsiye edilir.
|
||||
|
||||
PHP 8.2 ve PHP 8.3 için varyantlar sağlanmıştır. [Etiketlere göz atın](https://hub.docker.com/r/dunglas/frankenphp/tags).
|
||||
|
||||
## İmajlar Nasıl Kullanılır
|
||||
|
||||
Projenizde bir `Dockerfile` oluşturun:
|
||||
|
||||
```dockerfile
|
||||
FROM dunglas/frankenphp
|
||||
|
||||
COPY . /app/public
|
||||
```
|
||||
|
||||
Ardından, Docker imajını oluşturmak ve çalıştırmak için bu komutları çalıştırın:
|
||||
|
||||
```console
|
||||
docker build -t my-php-app .
|
||||
docker run -it --rm --name my-running-app my-php-app
|
||||
```
|
||||
|
||||
## Daha Fazla PHP Eklentisi Nasıl Kurulur
|
||||
|
||||
[Docker-php-extension-installer`](https://github.com/mlocati/docker-php-extension-installer) betiği temel imajda sağlanmıştır.
|
||||
Ek PHP eklentileri eklemek ise gerçekten kolaydır:
|
||||
|
||||
```dockerfile
|
||||
FROM dunglas/frankenphp
|
||||
|
||||
# buraya istenilen eklentileri ekleyin:
|
||||
RUN install-php-extensions \
|
||||
pdo_mysql \
|
||||
gd \
|
||||
intl \
|
||||
zip \
|
||||
opcache
|
||||
```
|
||||
|
||||
## Daha Fazla Caddy Modülü Nasıl Kurulur
|
||||
|
||||
FrankenPHP, Caddy'nin üzerine inşa edilmiştir ve tüm [Caddy modülleri](https://caddyserver.com/docs/modules/) FrankenPHP ile kullanılabilir.
|
||||
|
||||
Özel Caddy modüllerini kurmanın en kolay yolu [xcaddy](https://github.com/caddyserver/xcaddy) kullanmaktır:
|
||||
|
||||
```dockerfile
|
||||
FROM dunglas/frankenphp:builder AS builder
|
||||
|
||||
# xcaddy'yi derleyen imaja kopyalayın
|
||||
COPY --from=caddy:builder /usr/bin/xcaddy /usr/bin/xcaddy
|
||||
|
||||
# FrankenPHP oluşturmak için CGO etkinleştirilmelidir
|
||||
RUN CGO_ENABLED=1 \
|
||||
XCADDY_SETCAP=1 \
|
||||
XCADDY_GO_BUILD_FLAGS="-ldflags='-w -s' -tags=nobadger,nomysql,nopgx" \
|
||||
CGO_CFLAGS=$(php-config --includes) \
|
||||
CGO_LDFLAGS="$(php-config --ldflags) $(php-config --libs)" \
|
||||
xcaddy build \
|
||||
--output /usr/local/bin/frankenphp \
|
||||
--with github.com/dunglas/frankenphp=./ \
|
||||
--with github.com/dunglas/frankenphp/caddy=./caddy/ \
|
||||
--with github.com/dunglas/caddy-cbrotli \
|
||||
# Mercure ve Vulcain resmi yapıya dahil edilmiştir, ancak bunları kaldırmaktan çekinmeyin
|
||||
--with github.com/dunglas/mercure/caddy \
|
||||
--with github.com/dunglas/vulcain/caddy
|
||||
# Buraya ekstra Caddy modülleri ekleyin
|
||||
|
||||
FROM dunglas/frankenphp AS runner
|
||||
|
||||
# Resmi binary dosyayı özel modüllerinizi içeren binary dosyayla değiştirin
|
||||
COPY --from=builder /usr/local/bin/frankenphp /usr/local/bin/frankenphp
|
||||
```
|
||||
|
||||
FrankenPHP tarafından sağlanan `builder` imajı `libphp`'nin derlenmiş bir sürümünü içerir.
|
||||
[Derleyici imajları](https://hub.docker.com/r/dunglas/frankenphp/tags?name=builder) hem Debian hem de Alpine için FrankenPHP ve PHP'nin tüm sürümleri için sağlanmıştır.
|
||||
|
||||
> [!TIP]
|
||||
>
|
||||
> Eğer Alpine Linux ve Symfony kullanıyorsanız,
|
||||
> [varsayılan yığın boyutunu artırmanız](compile.md#xcaddy-kullanımı) gerekebilir.
|
||||
|
||||
## Varsayılan Olarak Worker Modunun Etkinleştirilmesi
|
||||
|
||||
FrankenPHP'yi bir worker betiği ile başlatmak için `FRANKENPHP_CONFIG` ortam değişkenini ayarlayın:
|
||||
|
||||
```dockerfile
|
||||
FROM dunglas/frankenphp
|
||||
|
||||
# ...
|
||||
|
||||
ENV FRANKENPHP_CONFIG="worker ./public/index.php"
|
||||
```
|
||||
|
||||
## Geliştirme Sürecinde Yığın (Volume) Kullanma
|
||||
|
||||
FrankenPHP ile kolayca geliştirme yapmak için, uygulamanın kaynak kodunu içeren dizini ana bilgisayarınızdan Docker konteynerine bir yığın (volume) olarak bağlayın:
|
||||
|
||||
```console
|
||||
docker run -v $PWD:/app/public -p 80:80 -p 443:443 -p 443:443/udp --tty my-php-app
|
||||
```
|
||||
|
||||
> [!TIP]
|
||||
>
|
||||
> `--tty` seçeneği JSON günlükleri yerine insan tarafından okunabilir güzel günlüklere sahip olmayı sağlar.
|
||||
|
||||
Docker Compose ile:
|
||||
|
||||
```yaml
|
||||
# compose.yaml
|
||||
|
||||
services:
|
||||
php:
|
||||
image: dunglas/frankenphp
|
||||
# özel bir Dockerfile kullanmak istiyorsanız aşağıdaki yorum satırını kaldırın
|
||||
#build: .
|
||||
# bunu bir production ortamında çalıştırmak istiyorsanız aşağıdaki yorum satırını kaldırın
|
||||
# restart: always
|
||||
ports:
|
||||
- "80:80" # HTTP
|
||||
- "443:443" # HTTPS
|
||||
- "443:443/udp" # HTTP/3
|
||||
volumes:
|
||||
- ./:/app/public
|
||||
- caddy_data:/data
|
||||
- caddy_config:/config
|
||||
# production ortamda aşağıdaki satırı yorum satırı yapın, geliştirme ortamında insan tarafından okunabilir güzel günlüklere sahip olmanızı sağlar
|
||||
tty: true
|
||||
|
||||
# Caddy sertifikaları ve yapılandırması için gereken yığınlar (volumes)
|
||||
volumes:
|
||||
caddy_data:
|
||||
caddy_config:
|
||||
```
|
||||
|
||||
## Root Olmayan Kullanıcı Olarak Çalıştırma
|
||||
|
||||
FrankenPHP, Docker'da root olmayan kullanıcı olarak çalışabilir.
|
||||
|
||||
İşte bunu yapan örnek bir `Dockerfile`:
|
||||
|
||||
```dockerfile
|
||||
FROM dunglas/frankenphp
|
||||
|
||||
ARG USER=www-data
|
||||
|
||||
RUN \
|
||||
# Alpine tabanlı dağıtımlar için "adduser -D ${USER}" kullanın
|
||||
useradd -D ${USER}; \
|
||||
# 80 ve 443 numaralı bağlantı noktalarına bağlanmak için ek özellik ekleyin
|
||||
setcap CAP_NET_BIND_SERVICE=+eip /usr/local/bin/frankenphp; \
|
||||
# /data/caddy ve /config/caddy dosyalarına yazma erişimi verin
|
||||
chown -R ${USER}:${USER} /data/caddy && chown -R ${USER}:${USER} /config/caddy;
|
||||
|
||||
USER ${USER}
|
||||
```
|
||||
|
||||
## Güncellemeler
|
||||
|
||||
Docker imajları oluşturulur:
|
||||
|
||||
* Yeni bir sürüm etiketlendiğinde
|
||||
* Her gün UTC ile saat 4'te Resmi PHP imajlarının yeni sürümleri mevcutsa
|
||||
|
||||
## Geliştirme Sürümleri
|
||||
|
||||
Geliştirme sürümleri [`dunglas/frankenphp-dev`](https://hub.docker.com/repository/docker/dunglas/frankenphp-dev) Docker deposunda mevcuttur.
|
||||
GitHub deposunun ana dalına her commit yapıldığında yeni bir derleme tetiklenir.
|
||||
|
||||
`latest*` etiketleri `main` dalının başına işaret eder.
|
||||
`sha-<hash-du-commit-git>` biçimindeki etiketler de kullanılabilir.
|
||||
# Özel Docker İmajı Oluşturma
|
||||
|
||||
[Resmi PHP imajları](https://hub.docker.com/_/php/) temel alınarak [FrankenPHP Docker imajları](https://hub.docker.com/r/dunglas/frankenphp) hazırlanmıştır. Popüler mimariler için Debian ve Alpine Linux varyantları sağlanmıştır. Debian dağıtımı tavsiye edilir.
|
||||
|
||||
PHP 8.2, 8.3 ve 8.4 için varyantlar sağlanmıştır. [Etiketlere göz atın](https://hub.docker.com/r/dunglas/frankenphp/tags).
|
||||
|
||||
## İmajlar Nasıl Kullanılır
|
||||
|
||||
Projenizde bir `Dockerfile` oluşturun:
|
||||
|
||||
```dockerfile
|
||||
FROM dunglas/frankenphp
|
||||
|
||||
COPY . /app/public
|
||||
```
|
||||
|
||||
Ardından, Docker imajını oluşturmak ve çalıştırmak için bu komutları çalıştırın:
|
||||
|
||||
```console
|
||||
docker build -t my-php-app .
|
||||
docker run -it --rm --name my-running-app my-php-app
|
||||
```
|
||||
|
||||
## Daha Fazla PHP Eklentisi Nasıl Kurulur
|
||||
|
||||
[Docker-php-extension-installer`](https://github.com/mlocati/docker-php-extension-installer) betiği temel imajda sağlanmıştır.
|
||||
Ek PHP eklentileri eklemek ise gerçekten kolaydır:
|
||||
|
||||
```dockerfile
|
||||
FROM dunglas/frankenphp
|
||||
|
||||
# buraya istenilen eklentileri ekleyin:
|
||||
RUN install-php-extensions \
|
||||
pdo_mysql \
|
||||
gd \
|
||||
intl \
|
||||
zip \
|
||||
opcache
|
||||
```
|
||||
|
||||
## Daha Fazla Caddy Modülü Nasıl Kurulur
|
||||
|
||||
FrankenPHP, Caddy'nin üzerine inşa edilmiştir ve tüm [Caddy modülleri](https://caddyserver.com/docs/modules/) FrankenPHP ile kullanılabilir.
|
||||
|
||||
Özel Caddy modüllerini kurmanın en kolay yolu [xcaddy](https://github.com/caddyserver/xcaddy) kullanmaktır:
|
||||
|
||||
```dockerfile
|
||||
FROM dunglas/frankenphp:builder AS builder
|
||||
|
||||
# xcaddy'yi derleyen imaja kopyalayın
|
||||
COPY --from=caddy:builder /usr/bin/xcaddy /usr/bin/xcaddy
|
||||
|
||||
# FrankenPHP oluşturmak için CGO etkinleştirilmelidir
|
||||
RUN CGO_ENABLED=1 \
|
||||
XCADDY_SETCAP=1 \
|
||||
XCADDY_GO_BUILD_FLAGS="-ldflags='-w -s' -tags=nobadger,nomysql,nopgx" \
|
||||
CGO_CFLAGS=$(php-config --includes) \
|
||||
CGO_LDFLAGS="$(php-config --ldflags) $(php-config --libs)" \
|
||||
xcaddy build \
|
||||
--output /usr/local/bin/frankenphp \
|
||||
--with frankenphp.dev=./ \
|
||||
--with frankenphp.dev/caddy=./caddy/ \
|
||||
--with github.com/dunglas/caddy-cbrotli \
|
||||
# Mercure ve Vulcain resmi yapıya dahil edilmiştir, ancak bunları kaldırmaktan çekinmeyin
|
||||
--with github.com/dunglas/mercure/caddy \
|
||||
--with github.com/dunglas/vulcain/caddy
|
||||
# Buraya ekstra Caddy modülleri ekleyin
|
||||
|
||||
FROM dunglas/frankenphp AS runner
|
||||
|
||||
# Resmi binary dosyayı özel modüllerinizi içeren binary dosyayla değiştirin
|
||||
COPY --from=builder /usr/local/bin/frankenphp /usr/local/bin/frankenphp
|
||||
```
|
||||
|
||||
FrankenPHP tarafından sağlanan `builder` imajı `libphp`'nin derlenmiş bir sürümünü içerir.
|
||||
[Derleyici imajları](https://hub.docker.com/r/dunglas/frankenphp/tags?name=builder) hem Debian hem de Alpine için FrankenPHP ve PHP'nin tüm sürümleri için sağlanmıştır.
|
||||
|
||||
> [!TIP]
|
||||
>
|
||||
> Eğer Alpine Linux ve Symfony kullanıyorsanız,
|
||||
> [varsayılan yığın boyutunu artırmanız](compile.md#xcaddy-kullanımı) gerekebilir.
|
||||
|
||||
## Varsayılan Olarak Worker Modunun Etkinleştirilmesi
|
||||
|
||||
FrankenPHP'yi bir worker betiği ile başlatmak için `FRANKENPHP_CONFIG` ortam değişkenini ayarlayın:
|
||||
|
||||
```dockerfile
|
||||
FROM dunglas/frankenphp
|
||||
|
||||
# ...
|
||||
|
||||
ENV FRANKENPHP_CONFIG="worker ./public/index.php"
|
||||
```
|
||||
|
||||
## Geliştirme Sürecinde Yığın (Volume) Kullanma
|
||||
|
||||
FrankenPHP ile kolayca geliştirme yapmak için, uygulamanın kaynak kodunu içeren dizini ana bilgisayarınızdan Docker konteynerine bir yığın (volume) olarak bağlayın:
|
||||
|
||||
```console
|
||||
docker run -v $PWD:/app/public -p 80:80 -p 443:443 -p 443:443/udp --tty my-php-app
|
||||
```
|
||||
|
||||
> [!TIP]
|
||||
>
|
||||
> `--tty` seçeneği JSON günlükleri yerine insan tarafından okunabilir güzel günlüklere sahip olmayı sağlar.
|
||||
|
||||
Docker Compose ile:
|
||||
|
||||
```yaml
|
||||
# compose.yaml
|
||||
|
||||
services:
|
||||
php:
|
||||
image: dunglas/frankenphp
|
||||
# özel bir Dockerfile kullanmak istiyorsanız aşağıdaki yorum satırını kaldırın
|
||||
#build: .
|
||||
# bunu bir production ortamında çalıştırmak istiyorsanız aşağıdaki yorum satırını kaldırın
|
||||
# restart: always
|
||||
ports:
|
||||
- "80:80" # HTTP
|
||||
- "443:443" # HTTPS
|
||||
- "443:443/udp" # HTTP/3
|
||||
volumes:
|
||||
- ./:/app/public
|
||||
- caddy_data:/data
|
||||
- caddy_config:/config
|
||||
# production ortamda aşağıdaki satırı yorum satırı yapın, geliştirme ortamında insan tarafından okunabilir güzel günlüklere sahip olmanızı sağlar
|
||||
tty: true
|
||||
|
||||
# Caddy sertifikaları ve yapılandırması için gereken yığınlar (volumes)
|
||||
volumes:
|
||||
caddy_data:
|
||||
caddy_config:
|
||||
```
|
||||
|
||||
## Root Olmayan Kullanıcı Olarak Çalıştırma
|
||||
|
||||
FrankenPHP, Docker'da root olmayan kullanıcı olarak çalışabilir.
|
||||
|
||||
İşte bunu yapan örnek bir `Dockerfile`:
|
||||
|
||||
```dockerfile
|
||||
FROM dunglas/frankenphp
|
||||
|
||||
ARG USER=appuser
|
||||
|
||||
RUN \
|
||||
# Alpine tabanlı dağıtımlar için "adduser -D ${USER}" kullanın
|
||||
useradd ${USER}; \
|
||||
# 80 ve 443 numaralı bağlantı noktalarına bağlanmak için ek özellik ekleyin
|
||||
setcap CAP_NET_BIND_SERVICE=+eip /usr/local/bin/frankenphp; \
|
||||
# /data/caddy ve /config/caddy dosyalarına yazma erişimi verin
|
||||
chown -R ${USER}:${USER} /data/caddy && chown -R ${USER}:${USER} /config/caddy;
|
||||
|
||||
USER ${USER}
|
||||
```
|
||||
|
||||
## Güncellemeler
|
||||
|
||||
Docker imajları oluşturulur:
|
||||
|
||||
- Yeni bir sürüm etiketlendiğinde
|
||||
- Her gün UTC ile saat 4'te Resmi PHP imajlarının yeni sürümleri mevcutsa
|
||||
|
||||
## Geliştirme Sürümleri
|
||||
|
||||
Geliştirme sürümleri [`dunglas/frankenphp-dev`](https://hub.docker.com/repository/docker/dunglas/frankenphp-dev) Docker deposunda mevcuttur.
|
||||
GitHub deposunun ana dalına her commit yapıldığında yeni bir derleme tetiklenir.
|
||||
|
||||
`latest*` etiketleri `main` dalının başına işaret eder.
|
||||
`sha-<hash-du-commit-git>` biçimindeki etiketler de kullanılabilir.
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
# Early Hints
|
||||
|
||||
FrankenPHP [103 Early Hints durum kodunu](https://developer.chrome.com/blog/early-hints/) yerel olarak destekler.
|
||||
Early Hints kullanmak web sayfalarınızın yüklenme süresini %30 oranında artırabilir.
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
header('Link: </style.css>; rel=preload; as=style');
|
||||
headers_send(103);
|
||||
|
||||
// yavaş algoritmalarınız ve SQL sorgularınız 🤪
|
||||
|
||||
echo <<<'HTML'
|
||||
<!DOCTYPE html>
|
||||
<title>Hello FrankenPHP</title>
|
||||
<link rel="stylesheet" href="style.css">
|
||||
HTML;
|
||||
```
|
||||
|
||||
Early Hints hem normal hem de [worker](worker.md) modları tarafından desteklenir.
|
||||
# Early Hints
|
||||
|
||||
FrankenPHP [103 Early Hints durum kodunu](https://developer.chrome.com/blog/early-hints/) yerel olarak destekler.
|
||||
Early Hints kullanmak web sayfalarınızın yüklenme süresini %30 oranında artırabilir.
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
header('Link: </style.css>; rel=preload; as=style');
|
||||
headers_send(103);
|
||||
|
||||
// yavaş algoritmalarınız ve SQL sorgularınız 🤪
|
||||
|
||||
echo <<<'HTML'
|
||||
<!DOCTYPE html>
|
||||
<title>Hello FrankenPHP</title>
|
||||
<link rel="stylesheet" href="style.css">
|
||||
HTML;
|
||||
```
|
||||
|
||||
Early Hints hem normal hem de [worker](worker.md) modları tarafından desteklenir.
|
||||
|
||||
264
docs/tr/embed.md
264
docs/tr/embed.md
@@ -1,132 +1,132 @@
|
||||
# Binary Dosyası Olarak PHP Uygulamaları
|
||||
|
||||
FrankenPHP, PHP uygulamalarının kaynak kodunu ve varlıklarını statik, kendi kendine yeten bir binary dosyaya yerleştirme yeteneğine sahiptir.
|
||||
|
||||
Bu özellik sayesinde PHP uygulamaları, uygulamanın kendisini, PHP yorumlayıcısını ve üretim düzeyinde bir web sunucusu olan Caddy'yi içeren bağımsız bir binary dosyalar olarak çıktısı alınabilir ve dağıtılabilir.
|
||||
|
||||
Bu özellik hakkında daha fazla bilgi almak için [Kévin tarafından SymfonyCon 2023'te yapılan sunuma](https://dunglas.dev/2023/12/php-and-symfony-apps-as-standalone-binaries/) göz atabilirsiniz.
|
||||
|
||||
## Preparing Your App
|
||||
|
||||
Bağımsız binary dosyayı oluşturmadan önce uygulamanızın gömülmeye hazır olduğundan emin olun.
|
||||
|
||||
Örneğin muhtemelen şunları yapmak istersiniz:
|
||||
|
||||
* Uygulamanın üretim bağımlılıklarını yükleyin
|
||||
* Otomatik yükleyiciyi boşaltın
|
||||
* Uygulamanızın üretim modunu etkinleştirin (varsa)
|
||||
* Nihai binary dosyanızın boyutunu küçültmek için `.git` veya testler gibi gerekli olmayan dosyaları çıkarın
|
||||
|
||||
Örneğin, bir Symfony uygulaması için aşağıdaki komutları kullanabilirsiniz:
|
||||
|
||||
```console
|
||||
# .git/, vb. dosyalarından kurtulmak için projeyi dışa aktarın
|
||||
mkdir $TMPDIR/my-prepared-app
|
||||
git archive HEAD | tar -x -C $TMPDIR/my-prepared-app
|
||||
cd $TMPDIR/my-prepared-app
|
||||
|
||||
# Uygun ortam değişkenlerini ayarlayın
|
||||
echo APP_ENV=prod > .env.local
|
||||
echo APP_DEBUG=0 >> .env.local
|
||||
|
||||
# Testleri kaldırın
|
||||
rm -Rf tests/
|
||||
|
||||
# Bağımlılıkları yükleyin
|
||||
composer install --ignore-platform-reqs --no-dev -a
|
||||
|
||||
# .env'yi optimize edin
|
||||
composer dump-env prod
|
||||
```
|
||||
|
||||
## Linux Binary'si Oluşturma
|
||||
|
||||
Bir Linux binary çıktısı almanın en kolay yolu, sağladığımız Docker tabanlı derleyiciyi kullanmaktır.
|
||||
|
||||
1. Hazırladığınız uygulamanın deposunda `static-build.Dockerfile` adlı bir dosya oluşturun:
|
||||
|
||||
```dockerfile
|
||||
FROM --platform=linux/amd64 dunglas/frankenphp:static-builder
|
||||
|
||||
# Uygulamanızı kopyalayın
|
||||
WORKDIR /go/src/app/dist/app
|
||||
COPY . .
|
||||
|
||||
# Statik binary dosyasını oluşturun, yalnızca istediğiniz PHP eklentilerini seçtiğinizden emin olun
|
||||
WORKDIR /go/src/app/
|
||||
RUN EMBED=dist/app/ \
|
||||
PHP_EXTENSIONS=ctype,iconv,pdo_sqlite \
|
||||
./build-static.sh
|
||||
```
|
||||
|
||||
> [!CAUTION]
|
||||
>
|
||||
> Bazı `.dockerignore` dosyaları (örneğin varsayılan [Symfony Docker `.dockerignore`](https://github.com/dunglas/symfony-docker/blob/main/.dockerignore))
|
||||
> `vendor/` dizinini ve `.env` dosyalarını yok sayacaktır. Derlemeden önce `.dockerignore` dosyasını ayarladığınızdan veya kaldırdığınızdan emin olun.
|
||||
|
||||
2. Derleyin:
|
||||
|
||||
```console
|
||||
docker build -t static-app -f static-build.Dockerfile .
|
||||
```
|
||||
|
||||
3. Binary dosyasını çıkarın:
|
||||
|
||||
```console
|
||||
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
|
||||
```
|
||||
|
||||
Elde edilen binary dosyası, geçerli dizindeki `my-app` adlı dosyadır.
|
||||
|
||||
## Diğer İşletim Sistemleri için Binary Çıktısı Alma
|
||||
|
||||
Docker kullanmak istemiyorsanız veya bir macOS binary dosyası oluşturmak istiyorsanız, sağladığımız kabuk betiğini kullanın:
|
||||
|
||||
```console
|
||||
git clone https://github.com/dunglas/frankenphp
|
||||
cd frankenphp
|
||||
EMBED=/path/to/your/app \
|
||||
PHP_EXTENSIONS=ctype,iconv,pdo_sqlite \
|
||||
./build-static.sh
|
||||
```
|
||||
|
||||
Elde edilen binary dosyası `dist/` dizinindeki `frankenphp-<os>-<arch>` adlı dosyadır.
|
||||
|
||||
## Binary Dosyasını Kullanma
|
||||
|
||||
İşte bu kadar! `my-app` dosyası (veya diğer işletim sistemlerinde `dist/frankenphp-<os>-<arch>`) bağımsız uygulamanızı içerir!
|
||||
|
||||
Web uygulamasını başlatmak için çalıştırın:
|
||||
|
||||
```console
|
||||
./my-app php-server
|
||||
```
|
||||
|
||||
Uygulamanız bir [worker betiği](worker.md) içeriyorsa, worker'ı aşağıdaki gibi bir şeyle başlatın:
|
||||
|
||||
```console
|
||||
./my-app php-server --worker public/index.php
|
||||
```
|
||||
|
||||
HTTPS (Let's Encrypt sertifikası otomatik olarak oluşturulur), HTTP/2 ve HTTP/3'ü etkinleştirmek için kullanılacak alan adını belirtin:
|
||||
|
||||
```console
|
||||
./my-app php-server --domain localhost
|
||||
```
|
||||
|
||||
Ayrıca binary dosyanıza gömülü PHP CLI betiklerini de çalıştırabilirsiniz:
|
||||
|
||||
```console
|
||||
./my-app php-cli bin/console
|
||||
```
|
||||
|
||||
## Yapıyı Özelleştirme
|
||||
|
||||
Binary dosyasının nasıl özelleştirileceğini (uzantılar, PHP sürümü...) görmek için [Statik derleme dokümanını okuyun](static.md).
|
||||
|
||||
## Binary Dosyasının Dağıtılması
|
||||
|
||||
Linux'ta, oluşturulan ikili dosya [UPX](https://upx.github.io) kullanılarak sıkıştırılır.
|
||||
|
||||
Mac'te, göndermeden önce dosyanın boyutunu küçültmek için sıkıştırabilirsiniz.
|
||||
Biz `xz` öneririz.
|
||||
# Binary Dosyası Olarak PHP Uygulamaları
|
||||
|
||||
FrankenPHP, PHP uygulamalarının kaynak kodunu ve varlıklarını statik, kendi kendine yeten bir binary dosyaya yerleştirme yeteneğine sahiptir.
|
||||
|
||||
Bu özellik sayesinde PHP uygulamaları, uygulamanın kendisini, PHP yorumlayıcısını ve üretim düzeyinde bir web sunucusu olan Caddy'yi içeren bağımsız bir binary dosyalar olarak çıktısı alınabilir ve dağıtılabilir.
|
||||
|
||||
Bu özellik hakkında daha fazla bilgi almak için [Kévin tarafından SymfonyCon 2023'te yapılan sunuma](https://dunglas.dev/2023/12/php-and-symfony-apps-as-standalone-binaries/) göz atabilirsiniz.
|
||||
|
||||
## Preparing Your App
|
||||
|
||||
Bağımsız binary dosyayı oluşturmadan önce uygulamanızın gömülmeye hazır olduğundan emin olun.
|
||||
|
||||
Örneğin muhtemelen şunları yapmak istersiniz:
|
||||
|
||||
- Uygulamanın üretim bağımlılıklarını yükleyin
|
||||
- Otomatik yükleyiciyi boşaltın
|
||||
- Uygulamanızın üretim modunu etkinleştirin (varsa)
|
||||
- Nihai binary dosyanızın boyutunu küçültmek için `.git` veya testler gibi gerekli olmayan dosyaları çıkarın
|
||||
|
||||
Örneğin, bir Symfony uygulaması için aşağıdaki komutları kullanabilirsiniz:
|
||||
|
||||
```console
|
||||
# .git/, vb. dosyalarından kurtulmak için projeyi dışa aktarın
|
||||
mkdir $TMPDIR/my-prepared-app
|
||||
git archive HEAD | tar -x -C $TMPDIR/my-prepared-app
|
||||
cd $TMPDIR/my-prepared-app
|
||||
|
||||
# Uygun ortam değişkenlerini ayarlayın
|
||||
echo APP_ENV=prod > .env.local
|
||||
echo APP_DEBUG=0 >> .env.local
|
||||
|
||||
# Testleri kaldırın
|
||||
rm -Rf tests/
|
||||
|
||||
# Bağımlılıkları yükleyin
|
||||
composer install --ignore-platform-reqs --no-dev -a
|
||||
|
||||
# .env'yi optimize edin
|
||||
composer dump-env prod
|
||||
```
|
||||
|
||||
## Linux Binary'si Oluşturma
|
||||
|
||||
Bir Linux binary çıktısı almanın en kolay yolu, sağladığımız Docker tabanlı derleyiciyi kullanmaktır.
|
||||
|
||||
1. Hazırladığınız uygulamanın deposunda `static-build.Dockerfile` adlı bir dosya oluşturun:
|
||||
|
||||
```dockerfile
|
||||
FROM --platform=linux/amd64 dunglas/frankenphp:static-builder
|
||||
|
||||
# Uygulamanızı kopyalayın
|
||||
WORKDIR /go/src/app/dist/app
|
||||
COPY . .
|
||||
|
||||
# Statik binary dosyasını oluşturun, yalnızca istediğiniz PHP eklentilerini seçtiğinizden emin olun
|
||||
WORKDIR /go/src/app/
|
||||
RUN EMBED=dist/app/ \
|
||||
PHP_EXTENSIONS=ctype,iconv,pdo_sqlite \
|
||||
./build-static.sh
|
||||
```
|
||||
|
||||
> [!CAUTION]
|
||||
>
|
||||
> Bazı `.dockerignore` dosyaları (örneğin varsayılan [Symfony Docker `.dockerignore`](https://github.com/dunglas/symfony-docker/blob/main/.dockerignore))
|
||||
> `vendor/` dizinini ve `.env` dosyalarını yok sayacaktır. Derlemeden önce `.dockerignore` dosyasını ayarladığınızdan veya kaldırdığınızdan emin olun.
|
||||
|
||||
2. Derleyin:
|
||||
|
||||
```console
|
||||
docker build -t static-app -f static-build.Dockerfile .
|
||||
```
|
||||
|
||||
3. Binary dosyasını çıkarın:
|
||||
|
||||
```console
|
||||
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
|
||||
```
|
||||
|
||||
Elde edilen binary dosyası, geçerli dizindeki `my-app` adlı dosyadır.
|
||||
|
||||
## Diğer İşletim Sistemleri için Binary Çıktısı Alma
|
||||
|
||||
Docker kullanmak istemiyorsanız veya bir macOS binary dosyası oluşturmak istiyorsanız, sağladığımız kabuk betiğini kullanın:
|
||||
|
||||
```console
|
||||
git clone https://github.com/dunglas/frankenphp
|
||||
cd frankenphp
|
||||
EMBED=/path/to/your/app \
|
||||
PHP_EXTENSIONS=ctype,iconv,pdo_sqlite \
|
||||
./build-static.sh
|
||||
```
|
||||
|
||||
Elde edilen binary dosyası `dist/` dizinindeki `frankenphp-<os>-<arch>` adlı dosyadır.
|
||||
|
||||
## Binary Dosyasını Kullanma
|
||||
|
||||
İşte bu kadar! `my-app` dosyası (veya diğer işletim sistemlerinde `dist/frankenphp-<os>-<arch>`) bağımsız uygulamanızı içerir!
|
||||
|
||||
Web uygulamasını başlatmak için çalıştırın:
|
||||
|
||||
```console
|
||||
./my-app php-server
|
||||
```
|
||||
|
||||
Uygulamanız bir [worker betiği](worker.md) içeriyorsa, worker'ı aşağıdaki gibi bir şeyle başlatın:
|
||||
|
||||
```console
|
||||
./my-app php-server --worker public/index.php
|
||||
```
|
||||
|
||||
HTTPS (Let's Encrypt sertifikası otomatik olarak oluşturulur), HTTP/2 ve HTTP/3'ü etkinleştirmek için kullanılacak alan adını belirtin:
|
||||
|
||||
```console
|
||||
./my-app php-server --domain localhost
|
||||
```
|
||||
|
||||
Ayrıca binary dosyanıza gömülü PHP CLI betiklerini de çalıştırabilirsiniz:
|
||||
|
||||
```console
|
||||
./my-app php-cli bin/console
|
||||
```
|
||||
|
||||
## Yapıyı Özelleştirme
|
||||
|
||||
Binary dosyasının nasıl özelleştirileceğini (uzantılar, PHP sürümü...) görmek için [Statik derleme dokümanını okuyun](static.md).
|
||||
|
||||
## Binary Dosyasının Dağıtılması
|
||||
|
||||
Linux'ta, oluşturulan ikili dosya [UPX](https://upx.github.io) kullanılarak sıkıştırılır.
|
||||
|
||||
Mac'te, göndermeden önce dosyanın boyutunu küçültmek için sıkıştırabilirsiniz.
|
||||
Biz `xz` öneririz.
|
||||
|
||||
@@ -1,31 +1,31 @@
|
||||
# GitHub Actions Kullanma
|
||||
|
||||
Bu depo Docker imajını [Docker Hub](https://hub.docker.com/r/dunglas/frankenphp) üzerinde derler ve dağıtır.
|
||||
Bu durum onaylanan her çekme (pull) isteğinde veya çatallandıktan (fork) sonra gerçekleşir.
|
||||
|
||||
## GitHub Eylemlerini Ayarlama
|
||||
|
||||
Depo ayarlarında, gizli değerler altında aşağıdaki gizli değerleri ekleyin:
|
||||
|
||||
- `REGISTRY_LOGIN_SERVER`: Kullanılacak Docker Registry bilgisi (örneğin `docker.io`).
|
||||
- `REGISTRY_USERNAME`: Giriş yapmak için kullanılacak kullanıcı adı (örn. `dunglas`).
|
||||
- `REGISTRY_PASSWORD`: Oturum açmak için kullanılacak parola (örn. bir erişim anahtarı).
|
||||
- `IMAGE_NAME`: İmajın adı (örn. `dunglas/frankenphp`).
|
||||
|
||||
## İmajı Oluşturma ve Dağıtma
|
||||
|
||||
1. Bir Çekme (pull) İsteği oluşturun veya çatala (forka) dağıtın.
|
||||
2. GitHub Actions imajı oluşturacak ve tüm testleri çalıştıracaktır.
|
||||
3. Derleme başarılı olursa, görüntü `pr-x` (burada `x` PR numarasıdır) etiketi kullanılarak ilgili saklanan yere (registry'e) gönderilir.
|
||||
|
||||
## İmajı Dağıtma
|
||||
|
||||
1. Çekme (pull) isteği birleştirildikten sonra, GitHub Actions testleri tekrar çalıştıracak ve yeni bir imaj oluşturacaktır.
|
||||
2. Derleme başarılı olursa, `main` etiketi Docker Registry'de güncellenecektir.
|
||||
|
||||
## Bültenler
|
||||
|
||||
1. Depoda yeni bir etiket oluşturun.
|
||||
2. GitHub Actions imajı oluşturacak ve tüm testleri çalıştıracaktır.
|
||||
3. Derleme başarılı olursa, etiket adı etiket olarak kullanılarak imaj saklanan yere (registry'e) gönderilir (örneğin `v1.2.3` ve `v1.2` oluşturulur).
|
||||
4. `latest` etiketi de güncellenecektir.
|
||||
# GitHub Actions Kullanma
|
||||
|
||||
Bu depo Docker imajını [Docker Hub](https://hub.docker.com/r/dunglas/frankenphp) üzerinde derler ve dağıtır.
|
||||
Bu durum onaylanan her çekme (pull) isteğinde veya çatallandıktan (fork) sonra gerçekleşir.
|
||||
|
||||
## GitHub Eylemlerini Ayarlama
|
||||
|
||||
Depo ayarlarında, gizli değerler altında aşağıdaki gizli değerleri ekleyin:
|
||||
|
||||
- `REGISTRY_LOGIN_SERVER`: Kullanılacak Docker Registry bilgisi (örneğin `docker.io`).
|
||||
- `REGISTRY_USERNAME`: Giriş yapmak için kullanılacak kullanıcı adı (örn. `dunglas`).
|
||||
- `REGISTRY_PASSWORD`: Oturum açmak için kullanılacak parola (örn. bir erişim anahtarı).
|
||||
- `IMAGE_NAME`: İmajın adı (örn. `dunglas/frankenphp`).
|
||||
|
||||
## İmajı Oluşturma ve Dağıtma
|
||||
|
||||
1. Bir Çekme (pull) İsteği oluşturun veya çatala (forka) dağıtın.
|
||||
2. GitHub Actions imajı oluşturacak ve tüm testleri çalıştıracaktır.
|
||||
3. Derleme başarılı olursa, görüntü `pr-x` (burada `x` PR numarasıdır) etiketi kullanılarak ilgili saklanan yere (registry'e) gönderilir.
|
||||
|
||||
## İmajı Dağıtma
|
||||
|
||||
1. Çekme (pull) isteği birleştirildikten sonra, GitHub Actions testleri tekrar çalıştıracak ve yeni bir imaj oluşturacaktır.
|
||||
2. Derleme başarılı olursa, `main` etiketi Docker Registry'de güncellenecektir.
|
||||
|
||||
## Bültenler
|
||||
|
||||
1. Depoda yeni bir etiket oluşturun.
|
||||
2. GitHub Actions imajı oluşturacak ve tüm testleri çalıştıracaktır.
|
||||
3. Derleme başarılı olursa, etiket adı etiket olarak kullanılarak imaj saklanan yere (registry'e) gönderilir (örneğin `v1.2.3` ve `v1.2` oluşturulur).
|
||||
4. `latest` etiketi de güncellenecektir.
|
||||
|
||||
@@ -1,137 +1,107 @@
|
||||
# Bilinen Sorunlar
|
||||
|
||||
## Fibers
|
||||
|
||||
[Fibers](https://www.php.net/manual/en/language.fibers.php) içinde [cgo](https://go.dev/blog/cgo) çağrısı yapan PHP fonksiyonlarının ve dil yapılarının çağrılmasının çökmelere neden olduğu bilinmektedir.
|
||||
|
||||
Bu sorun [Go projesi tarafından üzerinde çalışılmaktadır](https://github.com/golang/go/issues/62130).
|
||||
|
||||
Bu arada, bir çözüm Fibers içinden Go'ya temsilci atayan yapıları (`echo` gibi) ve fonksiyonları (`header()` gibi) kullanmamaktır.
|
||||
|
||||
Bu kod, Fiber içinde `echo` kullandığı için büyük olasılıkla çökecektir:
|
||||
|
||||
```php
|
||||
$fiber = new Fiber(function() {
|
||||
echo 'In the Fiber'.PHP_EOL;
|
||||
echo 'Still inside'.PHP_EOL;
|
||||
});
|
||||
$fiber->start();
|
||||
```
|
||||
|
||||
Bunun yerine, değeri Fiber'den döndürün ve dışarıda kullanın:
|
||||
|
||||
```php
|
||||
$fiber = new Fiber(function() {
|
||||
Fiber::suspend('In the Fiber'.PHP_EOL));
|
||||
Fiber::suspend('Still inside'.PHP_EOL));
|
||||
});
|
||||
echo $fiber->start();
|
||||
echo $fiber->resume();
|
||||
$fiber->resume();
|
||||
```
|
||||
|
||||
## Desteklenmeyen PHP Eklentileri
|
||||
|
||||
Aşağıdaki eklentilerin FrankenPHP ile uyumlu olmadığı bilinmektedir:
|
||||
|
||||
| Adı | Nedeni | Alternatifleri |
|
||||
|-------------------------------------------------------------|----------------------------|----------------------------------------------------------------------------------------------------------------------|
|
||||
| [imap](https://www.php.net/manual/en/imap.installation.php) | İş parçacığı güvenli değil | [javanile/php-imap2](https://github.com/javanile/php-imap2), [webklex/php-imap](https://github.com/Webklex/php-imap) |
|
||||
|
||||
## Sorunlu PHP Eklentileri
|
||||
|
||||
Aşağıdaki eklentiler FrankenPHP ile kullanıldığında bilinen hatalara ve beklenmeyen davranışlara sahiptir:
|
||||
|
||||
| Adı | Problem |
|
||||
|-----------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
|
||||
## get_browser
|
||||
|
||||
[get_browser()](https://www.php.net/manual/en/function.get-browser.php) fonksiyonu bir süre sonra kötü performans gösteriyor gibi görünüyor. Geçici bir çözüm, statik oldukları için User-Agent başına sonuçları önbelleğe almaktır (örneğin [APCu](https://www.php.net/manual/en/book.apcu.php) ile).
|
||||
|
||||
## Binary Çıktısı ve Alpine Tabanlı Docker İmajları
|
||||
|
||||
Binary çıktısı ve Alpine tabanlı Docker imajları (dunglas/frankenphp:*-alpine), daha küçük bir binary boyutu korumak için glibc ve arkadaşları yerine musl libc kullanır. Bu durum bazı uyumluluk sorunlarına yol açabilir. Özellikle, glob seçeneği GLOB_BRACE mevcut değildir.
|
||||
|
||||
## Docker ile `https://127.0.0.1` Kullanımı
|
||||
|
||||
FrankenPHP varsayılan olarak `localhost` için bir TLS sertifikası oluşturur.
|
||||
Bu, yerel geliştirme için en kolay ve önerilen seçenektir.
|
||||
|
||||
Bunun yerine ana bilgisayar olarak `127.0.0.1` kullanmak istiyorsanız, sunucu adını `127.0.0.1` şeklinde ayarlayarak bunun için bir sertifika oluşturacak yapılandırma yapmak mümkündür.
|
||||
|
||||
Ne yazık ki, [ağ sistemi](https://docs.docker.com/network/) nedeniyle Docker kullanırken bu yeterli değildir.
|
||||
`Curl: (35) LibreSSL/3.3.6: error:1404B438:SSL routines:ST_CONNECT:tlsv1 alert internal error`'a benzer bir TLS hatası alırsınız.
|
||||
|
||||
Linux kullanıyorsanız, [ana bilgisayar ağ sürücüsünü](https://docs.docker.com/network/network-tutorial-host/) kullanmak bir çözümdür:
|
||||
|
||||
```console
|
||||
docker run \
|
||||
-e SERVER_NAME="127.0.0.1" \
|
||||
-v $PWD:/app/public \
|
||||
--network host \
|
||||
dunglas/frankenphp
|
||||
```
|
||||
|
||||
Ana bilgisayar ağ sürücüsü Mac ve Windows'ta desteklenmez. Bu platformlarda, konteynerin IP adresini tahmin etmeniz ve bunu sunucu adlarına dahil etmeniz gerekecektir.
|
||||
|
||||
`docker network inspect bridge`'i çalıştırın ve `IPv4Address` anahtarının altındaki son atanmış IP adresini belirlemek için `Containers` anahtarına bakın ve bir artırın. Eğer hiçbir konteyner çalışmıyorsa, ilk atanan IP adresi genellikle `172.17.0.2`dir.
|
||||
|
||||
Ardından, bunu `SERVER_NAME` ortam değişkenine ekleyin:
|
||||
|
||||
```console
|
||||
docker run \
|
||||
-e SERVER_NAME="127.0.0.1, 172.17.0.3" \
|
||||
-v $PWD:/app/public \
|
||||
-p 80:80 -p 443:443 -p 443:443/udp \
|
||||
dunglas/frankenphp
|
||||
```
|
||||
|
||||
> [!CAUTION]
|
||||
>
|
||||
> 172.17.0.3`ü konteynerinize atanacak IP ile değiştirdiğinizden emin olun.
|
||||
|
||||
Artık ana makineden `https://127.0.0.1` adresine erişebilmeniz gerekir.
|
||||
|
||||
Eğer durum böyle değilse, sorunu anlamaya çalışmak için FrankenPHP'yi hata ayıklama modunda başlatın:
|
||||
|
||||
```console
|
||||
docker run \
|
||||
-e CADDY_GLOBAL_OPTIONS="debug" \
|
||||
-e SERVER_NAME="127.0.0.1" \
|
||||
-v $PWD:/app/public \
|
||||
-p 80:80 -p 443:443 -p 443:443/udp \
|
||||
dunglas/frankenphp
|
||||
```
|
||||
|
||||
## `@php` Referanslı Composer Betikler
|
||||
|
||||
[Composer betikleri](https://getcomposer.org/doc/articles/scripts.md) bazı görevler için bir PHP binary çalıştırmak isteyebilir, örneğin [bir Laravel projesinde](laravel.md) `@php artisan package:discover --ansi` çalıştırmak. Bu [şu anda mümkün değil](https://github.com/dunglas/frankenphp/issues/483#issuecomment-1899890915) ve 2 nedeni var:
|
||||
|
||||
* Composer FrankenPHP binary dosyasını nasıl çağıracağını bilmiyor;
|
||||
* Composer, FrankenPHP'nin henüz desteklemediği `-d` bayrağını kullanarak PHP ayarlarını komuta ekleyebilir.
|
||||
|
||||
Geçici bir çözüm olarak, `/usr/local/bin/php` içinde desteklenmeyen parametreleri silen ve ardından FrankenPHP'yi çağıran bir kabuk betiği oluşturabiliriz:
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
args=("$@")
|
||||
index=0
|
||||
for i in "$@"
|
||||
do
|
||||
if [ "$i" == "-d" ]; then
|
||||
unset 'args[$index]'
|
||||
unset 'args[$index+1]'
|
||||
fi
|
||||
index=$((index+1))
|
||||
done
|
||||
|
||||
/usr/local/bin/frankenphp php-cli ${args[@]}
|
||||
```
|
||||
|
||||
Ardından `PHP_BINARY` ortam değişkenini PHP betiğimizin yoluna ayarlayın ve Composer bu yolla çalışacaktır:
|
||||
|
||||
```bash
|
||||
export PHP_BINARY=/usr/local/bin/php
|
||||
composer install
|
||||
```
|
||||
# Bilinen Sorunlar
|
||||
|
||||
## Desteklenmeyen PHP Eklentileri
|
||||
|
||||
Aşağıdaki eklentilerin FrankenPHP ile uyumlu olmadığı bilinmektedir:
|
||||
|
||||
| Adı | Nedeni | Alternatifleri |
|
||||
| ----------------------------------------------------------- | -------------------------- | -------------------------------------------------------------------------------------------------------------------- |
|
||||
| [imap](https://www.php.net/manual/en/imap.installation.php) | İş parçacığı güvenli değil | [javanile/php-imap2](https://github.com/javanile/php-imap2), [webklex/php-imap](https://github.com/Webklex/php-imap) |
|
||||
|
||||
## Sorunlu PHP Eklentileri
|
||||
|
||||
Aşağıdaki eklentiler FrankenPHP ile kullanıldığında bilinen hatalara ve beklenmeyen davranışlara sahiptir:
|
||||
|
||||
| Adı | Problem |
|
||||
| --- | ------- |
|
||||
|
||||
## get_browser
|
||||
|
||||
[get_browser()](https://www.php.net/manual/en/function.get-browser.php) fonksiyonu bir süre sonra kötü performans gösteriyor gibi görünüyor. Geçici bir çözüm, statik oldukları için User-Agent başına sonuçları önbelleğe almaktır (örneğin [APCu](https://www.php.net/manual/en/book.apcu.php) ile).
|
||||
|
||||
## Binary Çıktısı ve Alpine Tabanlı Docker İmajları
|
||||
|
||||
Binary çıktısı ve Alpine tabanlı Docker imajları (dunglas/frankenphp:\*-alpine), daha küçük bir binary boyutu korumak için glibc ve arkadaşları yerine musl libc kullanır. Bu durum bazı uyumluluk sorunlarına yol açabilir. Özellikle, glob seçeneği GLOB_BRACE mevcut değildir.
|
||||
|
||||
## Docker ile `https://127.0.0.1` Kullanımı
|
||||
|
||||
FrankenPHP varsayılan olarak `localhost` için bir TLS sertifikası oluşturur.
|
||||
Bu, yerel geliştirme için en kolay ve önerilen seçenektir.
|
||||
|
||||
Bunun yerine ana bilgisayar olarak `127.0.0.1` kullanmak istiyorsanız, sunucu adını `127.0.0.1` şeklinde ayarlayarak bunun için bir sertifika oluşturacak yapılandırma yapmak mümkündür.
|
||||
|
||||
Ne yazık ki, [ağ sistemi](https://docs.docker.com/network/) nedeniyle Docker kullanırken bu yeterli değildir.
|
||||
`Curl: (35) LibreSSL/3.3.6: error:1404B438:SSL routines:ST_CONNECT:tlsv1 alert internal error`'a benzer bir TLS hatası alırsınız.
|
||||
|
||||
Linux kullanıyorsanız, [ana bilgisayar ağ sürücüsünü](https://docs.docker.com/network/network-tutorial-host/) kullanmak bir çözümdür:
|
||||
|
||||
```console
|
||||
docker run \
|
||||
-e SERVER_NAME="127.0.0.1" \
|
||||
-v $PWD:/app/public \
|
||||
--network host \
|
||||
dunglas/frankenphp
|
||||
```
|
||||
|
||||
Ana bilgisayar ağ sürücüsü Mac ve Windows'ta desteklenmez. Bu platformlarda, konteynerin IP adresini tahmin etmeniz ve bunu sunucu adlarına dahil etmeniz gerekecektir.
|
||||
|
||||
`docker network inspect bridge`'i çalıştırın ve `IPv4Address` anahtarının altındaki son atanmış IP adresini belirlemek için `Containers` anahtarına bakın ve bir artırın. Eğer hiçbir konteyner çalışmıyorsa, ilk atanan IP adresi genellikle `172.17.0.2`dir.
|
||||
|
||||
Ardından, bunu `SERVER_NAME` ortam değişkenine ekleyin:
|
||||
|
||||
```console
|
||||
docker run \
|
||||
-e SERVER_NAME="127.0.0.1, 172.17.0.3" \
|
||||
-v $PWD:/app/public \
|
||||
-p 80:80 -p 443:443 -p 443:443/udp \
|
||||
dunglas/frankenphp
|
||||
```
|
||||
|
||||
> [!CAUTION]
|
||||
>
|
||||
> 172.17.0.3`ü konteynerinize atanacak IP ile değiştirdiğinizden emin olun.
|
||||
|
||||
Artık ana makineden `https://127.0.0.1` adresine erişebilmeniz gerekir.
|
||||
|
||||
Eğer durum böyle değilse, sorunu anlamaya çalışmak için FrankenPHP'yi hata ayıklama modunda başlatın:
|
||||
|
||||
```console
|
||||
docker run \
|
||||
-e CADDY_GLOBAL_OPTIONS="debug" \
|
||||
-e SERVER_NAME="127.0.0.1" \
|
||||
-v $PWD:/app/public \
|
||||
-p 80:80 -p 443:443 -p 443:443/udp \
|
||||
dunglas/frankenphp
|
||||
```
|
||||
|
||||
## `@php` Referanslı Composer Betikler
|
||||
|
||||
[Composer betikleri](https://getcomposer.org/doc/articles/scripts.md) bazı görevler için bir PHP binary çalıştırmak isteyebilir, örneğin [bir Laravel projesinde](laravel.md) `@php artisan package:discover --ansi` çalıştırmak. Bu [şu anda mümkün değil](https://github.com/dunglas/frankenphp/issues/483#issuecomment-1899890915) ve 2 nedeni var:
|
||||
|
||||
- Composer FrankenPHP binary dosyasını nasıl çağıracağını bilmiyor;
|
||||
- Composer, FrankenPHP'nin henüz desteklemediği `-d` bayrağını kullanarak PHP ayarlarını komuta ekleyebilir.
|
||||
|
||||
Geçici bir çözüm olarak, `/usr/local/bin/php` içinde desteklenmeyen parametreleri silen ve ardından FrankenPHP'yi çağıran bir kabuk betiği oluşturabiliriz:
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
args=("$@")
|
||||
index=0
|
||||
for i in "$@"
|
||||
do
|
||||
if [ "$i" == "-d" ]; then
|
||||
unset 'args[$index]'
|
||||
unset 'args[$index+1]'
|
||||
fi
|
||||
index=$((index+1))
|
||||
done
|
||||
|
||||
/usr/local/bin/frankenphp php-cli ${args[@]}
|
||||
```
|
||||
|
||||
Ardından `PHP_BINARY` ortam değişkenini PHP betiğimizin yoluna ayarlayın ve Composer bu yolla çalışacaktır:
|
||||
|
||||
```bash
|
||||
export PHP_BINARY=/usr/local/bin/php
|
||||
composer install
|
||||
```
|
||||
|
||||
@@ -1,74 +1,74 @@
|
||||
# Laravel
|
||||
|
||||
## Docker
|
||||
|
||||
Bir [Laravel](https://laravel.com) web uygulamasını FrankenPHP ile çalıştırmak, projeyi resmi Docker imajının `/app` dizinine monte etmek kadar kolaydır.
|
||||
|
||||
Bu komutu Laravel uygulamanızın ana dizininden çalıştırın:
|
||||
|
||||
```console
|
||||
docker run -p 80:80 -p 443:443 -p 443:443/udp -v $PWD:/app dunglas/frankenphp
|
||||
```
|
||||
|
||||
And tadını çıkarın!
|
||||
|
||||
## Yerel Kurulum
|
||||
|
||||
Alternatif olarak, Laravel projelerinizi FrankenPHP ile yerel makinenizden çalıştırabilirsiniz:
|
||||
|
||||
1. [Sisteminize karşılık gelen binary dosyayı indirin](https://github.com/dunglas/frankenphp/releases)
|
||||
2. Aşağıdaki yapılandırmayı Laravel projenizin kök dizinindeki `Caddyfile` adlı bir dosyaya ekleyin:
|
||||
|
||||
```caddyfile
|
||||
{
|
||||
frankenphp
|
||||
}
|
||||
|
||||
# Sunucunuzun alan adı
|
||||
localhost {
|
||||
# Webroot'u public/ dizinine ayarlayın
|
||||
root * public/
|
||||
# Sıkıştırmayı etkinleştir (isteğe bağlı)
|
||||
encode zstd br gzip
|
||||
# PHP dosyalarını public/ dizininden çalıştırın ve varlıkları sunun
|
||||
php_server
|
||||
}
|
||||
```
|
||||
|
||||
3. FrankenPHP'yi Laravel projenizin kök dizininden başlatın: `frankenphp run`
|
||||
|
||||
## Laravel Octane
|
||||
|
||||
Octane, Composer paket yöneticisi aracılığıyla kurulabilir:
|
||||
|
||||
```console
|
||||
composer require laravel/octane
|
||||
```
|
||||
|
||||
Octane'ı kurduktan sonra, Octane'ın yapılandırma dosyasını uygulamanıza yükleyecek olan `octane:install` Artisan komutunu çalıştırabilirsiniz:
|
||||
|
||||
```console
|
||||
php artisan octane:install --server=frankenphp
|
||||
```
|
||||
|
||||
Octane sunucusu `octane:frankenphp` Artisan komutu aracılığıyla başlatılabilir.
|
||||
|
||||
```console
|
||||
php artisan octane:frankenphp
|
||||
```
|
||||
|
||||
`octane:frankenphp` komutu aşağıdaki seçenekleri alabilir:
|
||||
|
||||
* `--host`: Sunucunun bağlanması gereken IP adresi (varsayılan: `127.0.0.1`)
|
||||
* `--port`: Sunucunun erişilebilir olması gereken port (varsayılan: `8000`)
|
||||
* `--admin-port`: Yönetici sunucusunun erişilebilir olması gereken port (varsayılan: `2019`)
|
||||
* `--workers`: İstekleri işlemek için hazır olması gereken worker sayısı (varsayılan: `auto`)
|
||||
* `--max-requests`: Sunucu yeniden yüklenmeden önce işlenecek istek sayısı (varsayılan: `500`)
|
||||
* `--caddyfile`: FrankenPHP `Caddyfile` dosyasının yolu
|
||||
* `--https`: HTTPS, HTTP/2 ve HTTP/3'ü etkinleştirin ve sertifikaları otomatik olarak oluşturup yenileyin
|
||||
* `--http-redirect`: HTTP'den HTTPS'ye yeniden yönlendirmeyi etkinleştir (yalnızca --https geçilirse etkinleştirilir)
|
||||
* `--watch`: Uygulamada kod değişikliği olduğunda sunucuyu otomatik olarak yeniden yükle
|
||||
* `--poll`: Dosyaları bir ağ üzerinden izlemek için izleme sırasında dosya sistemi yoklamasını kullanın
|
||||
* `--log-level`: Belirtilen günlük seviyesinde veya üzerinde günlük mesajları
|
||||
|
||||
Laravel Octane hakkında daha fazla bilgi edinmek için [Laravel Octane resmi belgelerine](https://laravel.com/docs/octane) göz atın.
|
||||
# Laravel
|
||||
|
||||
## Docker
|
||||
|
||||
Bir [Laravel](https://laravel.com) web uygulamasını FrankenPHP ile çalıştırmak, projeyi resmi Docker imajının `/app` dizinine monte etmek kadar kolaydır.
|
||||
|
||||
Bu komutu Laravel uygulamanızın ana dizininden çalıştırın:
|
||||
|
||||
```console
|
||||
docker run -p 80:80 -p 443:443 -p 443:443/udp -v $PWD:/app dunglas/frankenphp
|
||||
```
|
||||
|
||||
And tadını çıkarın!
|
||||
|
||||
## Yerel Kurulum
|
||||
|
||||
Alternatif olarak, Laravel projelerinizi FrankenPHP ile yerel makinenizden çalıştırabilirsiniz:
|
||||
|
||||
1. [Sisteminize karşılık gelen binary dosyayı indirin](https://github.com/dunglas/frankenphp/releases)
|
||||
2. Aşağıdaki yapılandırmayı Laravel projenizin kök dizinindeki `Caddyfile` adlı bir dosyaya ekleyin:
|
||||
|
||||
```caddyfile
|
||||
{
|
||||
frankenphp
|
||||
}
|
||||
|
||||
# Sunucunuzun alan adı
|
||||
localhost {
|
||||
# Webroot'u public/ dizinine ayarlayın
|
||||
root public/
|
||||
# Sıkıştırmayı etkinleştir (isteğe bağlı)
|
||||
encode zstd br gzip
|
||||
# PHP dosyalarını public/ dizininden çalıştırın ve varlıkları sunun
|
||||
php_server
|
||||
}
|
||||
```
|
||||
|
||||
3. FrankenPHP'yi Laravel projenizin kök dizininden başlatın: `frankenphp run`
|
||||
|
||||
## Laravel Octane
|
||||
|
||||
Octane, Composer paket yöneticisi aracılığıyla kurulabilir:
|
||||
|
||||
```console
|
||||
composer require laravel/octane
|
||||
```
|
||||
|
||||
Octane'ı kurduktan sonra, Octane'ın yapılandırma dosyasını uygulamanıza yükleyecek olan `octane:install` Artisan komutunu çalıştırabilirsiniz:
|
||||
|
||||
```console
|
||||
php artisan octane:install --server=frankenphp
|
||||
```
|
||||
|
||||
Octane sunucusu `octane:frankenphp` Artisan komutu aracılığıyla başlatılabilir.
|
||||
|
||||
```console
|
||||
php artisan octane:frankenphp
|
||||
```
|
||||
|
||||
`octane:frankenphp` komutu aşağıdaki seçenekleri alabilir:
|
||||
|
||||
- `--host`: Sunucunun bağlanması gereken IP adresi (varsayılan: `127.0.0.1`)
|
||||
- `--port`: Sunucunun erişilebilir olması gereken port (varsayılan: `8000`)
|
||||
- `--admin-port`: Yönetici sunucusunun erişilebilir olması gereken port (varsayılan: `2019`)
|
||||
- `--workers`: İstekleri işlemek için hazır olması gereken worker sayısı (varsayılan: `auto`)
|
||||
- `--max-requests`: Sunucu yeniden yüklenmeden önce işlenecek istek sayısı (varsayılan: `500`)
|
||||
- `--caddyfile`: FrankenPHP `Caddyfile` dosyasının yolu
|
||||
- `--https`: HTTPS, HTTP/2 ve HTTP/3'ü etkinleştirin ve sertifikaları otomatik olarak oluşturup yenileyin
|
||||
- `--http-redirect`: HTTP'den HTTPS'ye yeniden yönlendirmeyi etkinleştir (yalnızca --https geçilirse etkinleştirilir)
|
||||
- `--watch`: Uygulamada kod değişikliği olduğunda sunucuyu otomatik olarak yeniden yükle
|
||||
- `--poll`: Dosyaları bir ağ üzerinden izlemek için izleme sırasında dosya sistemi yoklamasını kullanın
|
||||
- `--log-level`: Belirtilen günlük seviyesinde veya üzerinde günlük mesajları
|
||||
|
||||
Laravel Octane hakkında daha fazla bilgi edinmek için [Laravel Octane resmi belgelerine](https://laravel.com/docs/octane) göz atın.
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user