diff --git a/docs/cn/README.md b/docs/cn/README.md new file mode 100644 index 00000000..2282e4e7 --- /dev/null +++ b/docs/cn/README.md @@ -0,0 +1,77 @@ +# FrankenPHP: 适用于 PHP 的现代应用服务器 + +

FrankenPHP

+ +FrankenPHP 是建立在 [Caddy](https://caddyserver.com/) Web 服务器之上的现代 PHP 应用程序服务器。 + +FrankenPHP 凭借其令人惊叹的功能为您的 PHP 应用程序提供了超能力:[早期提示](early-hints.md)、[worker 模式](worker.md)、[实时功能](mercure.md)、自动 HTTPS、HTTP/2 和 HTTP/3 支持...... + +FrankenPHP 可与任何 PHP 应用程序一起使用,并且由于提供了与 worker 模式的集成,使您的 Symfony 和 Laravel 项目比以往任何时候都更快。 + +FrankenPHP 也可以用作独立的 Go 库,将 PHP 嵌入到任何使用 net/http 的应用程序中。 + +[**了解更多** *frankenphp.dev*](https://frankenphp.dev/cn) 以及在以下地址中: + +Slides + +## 开始 + +### Docker + +```console +docker run -v $PWD:/app/public \ + -p 80:80 -p 443:443 -p 443:443/udp \ + dunglas/frankenphp +``` + +访问 `https://localhost`, 并享受吧! + +> [!提示] +> +> 不要尝试使用 `https://127.0.0.1`。使用 `localhost` 并接受自签名证书。 +> 使用 [`SERVER_NAME` 环境变量](config.md#环境变量) 更改要使用的域。 + +### 独立二进制 + +如果您不想使用 Docker,我们为 Linux 和 macOS 提供独立的 FrankenPHP 二进制文件 +包含 [PHP 8.3](https://www.php.net/releases/8.3/en.php) 和最流行的 PHP 扩展:[下载 FrankenPHP](https://github.com/dunglas/frankenphp/releases) + +若要启动当前目录的内容,请运行: + +```console +./frankenphp php-server +``` + +您还可以使用以下命令运行命令行脚本: + +```console +./frankenphp php-cli /path/to/your/script.php +``` + +## 文档 + +* [worker 模式](worker.md) +* [早期提示支持(103 HTTP status code)](early-hints.md) +* [实时功能](mercure.md) +* [配置](config.md) +* [Docker 镜像](docker.md) +* [在生产环境中部署](production.md) +* [创建独立、可自行执行的 PHP 应用程序](embed.md) +* [创建静态二进制文件](static.md) +* [从源代码编译](compile.md) +* [Laravel 集成](laravel.md) +* [已知问题](known-issues.md) +* [演示应用程序 (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/distribution/) +* [Laravel](laravel.md) +* [Sulu](https://sulu.io/blog/running-sulu-with-frankenphp) +* [WordPress](https://github.com/dunglas/frankenphp-wordpress) +* [Drupal](https://github.com/dunglas/frankenphp-drupal) +* [Joomla](https://github.com/alexandreelise/frankenphp-joomla) +* [TYPO3](https://github.com/ochorocho/franken-typo3) diff --git a/docs/cn/compile.md b/docs/cn/compile.md new file mode 100644 index 00000000..cd22ba9a --- /dev/null +++ b/docs/cn/compile.md @@ -0,0 +1,104 @@ +# 从源代码编译 + +本文档解释了如何创建一个 FrankenPHP 构建,它将 PHP 加载为一个动态库。 +这是推荐的方法。 + +或者,[创建静态构建](static.md)也是可能的。 + +## 安装 PHP + +FrankenPHP 与 PHP 8.2 及更高版本兼容。 + +首先,[获取PHP的源代码](https://www.php.net/downloads.php)并提取它们: + +```console +tar xf php-* +cd php-*/ +``` + +然后,为您的平台配置 PHP: + +### Linux + +```console +./configure \ + --enable-embed \ + --enable-zts \ + --disable-zend-signals \ + --enable-zend-max-execution-timers +``` + +最后,编译并安装 PHP: + +```console +make -j$(nproc) +sudo make install +``` + +### Mac + +使用 [Homebrew](https://brew.sh/) 包管理器安装 +`libiconv`, `bison`, `re2c` 和 `pkg-config`: + +```console +brew install libiconv bison re2c pkg-config +echo 'export PATH="/opt/homebrew/opt/bison/bin:$PATH"' >> ~/.zshrc +``` + +然后运行配置脚本: + +```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: + +```console +make -j$(sysctl -n hw.logicalcpu) +sudo make install +``` + +## 编译 Go 应用 + +您现在可以使用 Go 库并编译我们的 Caddy 构建: + +```console +curl -L https://github.com/dunglas/frankenphp/archive/refs/heads/main.tar.gz | tar x +cd frankenphp-main/caddy/frankenphp +CGO_CFLAGS=$(php-config --includes) CGO_LDFLAGS="$(php-config --ldflags) $(php-config --libs)" go build +``` + +### 使用 xcaddy + +或者,使用 [xcaddy](https://github.com/caddyserver/xcaddy) 用 [自定义 Caddy 模块](https://caddyserver.com/docs/modules/) 编译 FrankenPHP: + +```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/mercure/caddy \ + --with github.com/dunglas/vulcain/caddy + # Add extra Caddy modules here +``` + +> [!提示] +> +> 如果您使用的是 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\'"'` +> (根据您的应用需求更改堆栈大小的值)。 diff --git a/docs/cn/config.md b/docs/cn/config.md new file mode 100644 index 00000000..5309bd74 --- /dev/null +++ b/docs/cn/config.md @@ -0,0 +1,154 @@ +# 配置 + +FrankenPHP,Caddy 以及 Mercure 和 Vulcain 模块可以使用 [Caddy 支持的格式](https://caddyserver.com/docs/getting-started#your-first-config) 进行配置。 + +在 Docker 镜像中,`Caddyfile` 位于 `/etc/caddy/Caddyfile`。 + +您也可以像往常一样使用 `php.ini` 配置 PHP。 + +在 Docker 镜像中,`php.ini` 文件不存在,您可以手动创建它或 `复制`。 + +```dockerfile +FROM dunglas/frankenphp + +# 开发: +RUN cp $PHP_INI_DIR/php.ini-development $PHP_INI_DIR/php.ini + +# 还是生产: +RUN cp $PHP_INI_DIR/php.ini-production $PHP_INI_DIR/php.ini +``` + +## Caddyfile 配置 + +要注册 FrankenPHP 执行器,必须设置 `frankenphp` [全局选项](https://caddyserver.com/docs/caddyfile/concepts#global-options),然后可以在站点块中使用 `php_server` 或 `php` [HTTP 指令](https://caddyserver.com/docs/caddyfile/concepts#directives)来为您的 PHP 应用程序提供服务。 + +极小示例: + +```caddyfile +{ + # 启用 FrankenPHP + frankenphp + # 配置何时必须执行指令 + order php_server before file_server +} + +localhost { + # 启用压缩(可选) + encode zstd br gzip + # 执行当前目录中的 PHP 文件并提供资产 + php_server +} +``` + +或者,可以在全局选项下指定要创建的线程数和要从服务器启动的 [worker scripts](worker.md)。 + +```caddyfile +{ + frankenphp { + num_threads # 设置要启动的 PHP 线程数。默认值:可用 CPU 数量的 2 倍。 + worker { + file # 设置 worker 脚本的路径。 + num # 设置要启动的 PHP 线程数,默认为可用 CPU 数的 2 倍。 + env # 将额外的环境变量设置为给定值。可以为多个环境变量多次指定。 + } + } +} + +# ... +``` + +或者,您可以使用 `worker` 选项的一行缩写形式: + +```caddyfile +{ + frankenphp { + worker + } +} + +# ... +``` + +如果在同一服务器上提供多个应用,还可以定义多个 worker: + +```caddyfile +{ + frankenphp { + worker /path/to/app/public/index.php + worker /path/to/other/public/index.php + } +} + +app.example.com { + root * /path/to/app/public + php_server +} + +other.example.com { + root * /path/to/other/public + php_server +} +... +``` + +使用 `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 [] { + root # 设置站点的根文件夹。默认值:`root` 指令。 + split_path # 设置用于将 URI 拆分为两部分的子字符串。第一个匹配的子字符串将用于从路径中拆分“路径信息”。第一个部分以匹配的子字符串为后缀,并将假定为实际资源(CGI 脚本)名称。第二部分将设置为PATH_INFO,供 脚本使用。默认值:`.php` + resolve_root_symlink # 允许通过计算符号链接(如果存在)将 `根` 目录解析为其实际值。 + env # 将额外的环境变量设置为给定值。可以为多个环境变量多次指定。 +} +``` + +## 环境变量 + +以下环境变量可用于在 `Caddyfile` 中注入 Caddy 指令,而无需对其进行修改: + +* `SERVER_NAME`: 更改 [要监听的地址](https://caddyserver.com/docs/caddyfile/concepts#addresses),提供的主机名也将用于生成的 TLS 证书 +* `CADDY_GLOBAL_OPTIONS`: 注入 [全局选项](https://caddyserver.com/docs/caddyfile/options) +* `FRANKENPHP_CONFIG`: 在 `frankenphp` 指令下注入配置 + +## 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 +``` diff --git a/docs/cn/docker.md b/docs/cn/docker.md new file mode 100644 index 00000000..5de046d7 --- /dev/null +++ b/docs/cn/docker.md @@ -0,0 +1,164 @@ +# 构建自定义 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 的变体。[浏览标签](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:latest-builder AS builder + +# 在构建器镜像中复制 xcaddy +COPY --from=caddy:builder /usr/bin/xcaddy /usr/bin/xcaddy + +# 必须启用 CGO 才能构建 FrankenPHP +ENV CGO_ENABLED=1 XCADDY_SETCAP=1 XCADDY_GO_BUILD_FLAGS="-ldflags '-w -s'" +RUN xcaddy build \ + --output /usr/local/bin/frankenphp \ + --with github.com/dunglas/frankenphp=./ \ + --with github.com/dunglas/frankenphp/caddy=./caddy/ \ + # 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 +``` + +FrankenPHP 提供的 `builder` 镜像包含 libphp 的编译版本。 +[构建器图像](https://hub.docker.com/r/dunglas/frankenphp/tags?name=builder) 适用于所有版本的 FrankenPHP 和 PHP,包括 Alpine 和 Debian。 + +> [!提示] +> +> 如果您使用的是 Alpine Linux 和 Symfony, +> 您可能需要 [增加默认堆栈大小](compile.md#使用-xcaddy)。 + +## 默认启用 worker 模式 + +设置 `FRANKENPHP_CONFIG` 环境变量以使用 worker 脚本启动 FrankenPHP: + +```dockerfile +FROM dunglas/frankenphp + +# ... + +ENV FRANKENPHP_CONFIG="worker ./public/index.php" +``` + +## 在开发中使用 Volume + +要使用 FrankenPHP 轻松开发,请从包含应用程序源代码的主机挂载目录作为 Docker 容器中的 volume: + +```console +docker run -v $PWD:/app/public -p 80:80 -p 443:443 -p 443:443/udp --tty my-php-app +``` + +> [!提示] +> +> `--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 + # 在生产环境中注释以下行,它允许在 dev 中使用清晰可读日志 + tty: true + +# Caddy 证书和配置所需的 volumes +volumes: + caddy_data: + caddy_config: +``` + +## 以非 root 用户身份运行 + +FrankenPHP 可以在 Docker 中以非 root 用户身份运行。 + +下面是一个示例 Dockerfile: + +```dockerfile +FROM dunglas/frankenphp + +ARG USER=www-data + +RUN \ + # 在基于 alpine 的发行版使用 "adduser -D ${USER}" + useradd -D ${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} +``` + +## 更新 + +Docker 镜像会按照以下条件更新: + +* 发布新的版本后 +* 每天的 4am UTC 检查如果有新的PHP镜像可用 + +## 开发版本 + +可在此docker[`dunglas/frankenphp-dev`](https://hub.docker.com/repository/docker/dunglas/frankenphp-dev)仓库路径获取开发版本。 +每次在GitHub仓库的主分支有了新的commit都会触发一个新的build。 + +'latest*' tag 指向最新的`main`分支。 +也支持 'sha-' 的tag格式。 diff --git a/docs/cn/early-hints.md b/docs/cn/early-hints.md new file mode 100644 index 00000000..5fad9bdc --- /dev/null +++ b/docs/cn/early-hints.md @@ -0,0 +1,21 @@ +# 早期提示 + +FrankenPHP 原生支持 [103 Early Hints 状态代码](https://developer.chrome.com/blog/early-hints/)。 +使用早期提示可以将网页的加载时间缩短 30%。 + +```php +; rel=preload; as=style'); +headers_send(103); + +// 慢速算法和 SQL 查询 + +echo <<<'HTML' + +Hello FrankenPHP + +HTML; +``` + +早期提示由普通模式和 [worker](worker.md) 模式支持。 diff --git a/docs/cn/embed.md b/docs/cn/embed.md new file mode 100644 index 00000000..72b959a1 --- /dev/null +++ b/docs/cn/embed.md @@ -0,0 +1,127 @@ +# PHP 应用程序作为独立二进制文件 + +FrankenPHP 能够将 PHP 应用程序的源代码和资产嵌入到静态的、独立的二进制文件中。 + +由于这个特性,PHP应用程序可以作为独立的二进制文件分发,包括应用程序本身、PHP解释器和生产级Web服务器Caddy。 + +了解有关此功能的更多信息 [Kévin 在 SymfonyCon 上的演讲中](https://dunglas.dev/2023/12/php-and-symfony-apps-as-standalone-binaries/)。 + +## 准备应用 + +在创建独立二进制文件之前,请确保您的应用已准备好进行嵌入。 + +例如,您可能希望: + +* 安装应用的生产依赖项 +* 转储自动加载机 +* 启用应用程序的生产模式(如果有) +* 剥离不需要的文件,例如 `.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 +``` + +## 创建 Linux 二进制文件 + +创建 Linux 二进制文件的最简单方法是使用我们提供的基于 Docker 的构建器。 + +1. 在准备好的应用的存储库中创建一个名为 `static-build.Dockerfile` 的文件。 + + ```dockerfile + FROM --platform=linux/amd64 dunglas/frankenphp:static-builder + + # 复制应用 + WORKDIR /go/src/app/dist/app + COPY . . + + # 构建静态二进制文件,确保只选择你想要的PHP扩展 + WORKDIR /go/src/app/ + RUN EMBED=dist/app/ \ + PHP_EXTENSIONS=ctype,iconv,pdo_sqlite \ + ./build-static.sh + ``` + +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 二进制文件,请使用我们提供的 shell 脚本: + +```console +git clone https://github.com/dunglas/frankenphp +cd frankenphp +EMBED=/path/to/your/app \ + PHP_EXTENSIONS=ctype,iconv,pdo_sqlite \ + ./build-static.sh +``` + +在 `dist/` 目录中生成的二进制文件名为 `frankenphp--`。 + +## 使用二进制文件 + +就是这样!`my-app` 文件(或其他操作系统上的 `dist/frankenphp--`)包含您的独立应用程序! + +若要启动 Web 应用,请运行: + +```console +./my-app php-server +``` + +如果您的应用包含 [worker 脚本](worker.md),请使用如下内容启动 worker: + +```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 +``` + +## 自定义构建 + +[阅读静态构建文档](static.md) 查看如何自定义二进制文件(扩展、PHP 版本......)。 + +## 分发二进制文件 + +创建的二进制文件不会被压缩。 +若要在发送文件之前减小文件的大小,可以对其进行压缩。 + +我们推荐使用 `xz`。 diff --git a/docs/cn/github-actions.md b/docs/cn/github-actions.md new file mode 100644 index 00000000..43f43584 --- /dev/null +++ b/docs/cn/github-actions.md @@ -0,0 +1,31 @@ +# 使用 GitHub Actions + +此存储库构建 Docker 镜像并将其部署到 [Docker Hub](https://hub.docker.com/r/dunglas/frankenphp) 上 +每个批准的拉取请求或设置后在您自己的分支上。 + +## 设置 GitHub Actions + +在存储库设置中的机密下,添加以下机密: + +- `REGISTRY_LOGIN_SERVER`: 要使用的 docker 注册表 (例如 `docker.io`). +- `REGISTRY_USERNAME`: 用于登录注册表的用户名 (例如 `dunglas`). +- `REGISTRY_PASSWORD`: 用于登录注册表的密码 (例如 访问密钥). +- `IMAGE_NAME`: 镜像的名称 (例如 `dunglas/frankenphp`). + +## 构建和推送镜像 + +1. 创建拉取请求或推送到分支。 +2. GitHub Actions 将生成镜像并运行任何测试。 +3. 如果生成成功,则将使用 `pr-x`,其中 `x` 是 PR 编号,作为标记将镜像推送到注册表。 + +## 部署镜像 + +1. 合并拉取请求后,GitHub Actions 将再次运行测试并生成新镜像。 +2. 如果构建成功,则 Docker 注册表中的 `main` 标记将更新。 + +## 释放 + +1. 在存储库中创建新标签。 +2. GitHub Actions 将生成镜像并运行任何测试。 +3. 如果构建成功,镜像将使用标记名称作为标记推送到注册表(例如,将创建 `v1.2.3` 和 `v1.2`)。 +4. `latest` 标签也将更新。 diff --git a/docs/cn/known-issues.md b/docs/cn/known-issues.md new file mode 100644 index 00000000..b01e29e1 --- /dev/null +++ b/docs/cn/known-issues.md @@ -0,0 +1,98 @@ +# 已知问题 + +## Fibers + +众所周知,调用本身在 [Fibers](https://www.php.net/manual/en/language.fibers.php) 中调用 [cgo](https://go.dev/blog/cgo) 的 PHP 函数和语言结构会导致崩溃。 + +这个问题[正在由 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 不兼容: + +| 名称 | 原因 | 选择 | +| ----------------------------------------------------------- | --------------- | -------------------------------------------------------------------------------------------------------------------- | +| [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) | + +## get_browser + +[get_browser()](https://www.php.net/manual/en/function.get-browser.php) 函数在一段时间后似乎表现不佳。解决方法是缓存(例如使用 APCU)每个用户代理的结果,因为它们是静态的。 + +## 独立的二进制和基于 Alpine 的 Docker 镜像 + +独立的二进制文件和基于 Alpine 的 docker 镜像 (`dunglas/frankenphp:*-alpine`) 使用 [musl libc](https://musl.libc.org/) 而不是 [glibc and friends](https://www.etalabs.net/compare_libcs.html),以保持较小的二进制大小。这可能会导致一些兼容性问题。特别是,glob 标志 `GLOB_BRACE` [不可用](https://www.php.net/manual/en/function.glob.php) + +## 在 Docker 中使用 `https://127.0.0.1` + +默认情况下,FrankenPHP 会为 `localhost` 生成一个 TLS 证书。 +这是本地开发最简单且推荐的选项。 + +如果确实想使用 `127.0.0.1` 作为主机,可以通过将服务器名称设置为 `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,解决方案是使用 [主机网络驱动程序](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 +``` + +Mac 和 Windows 不支持主机网络驱动程序。在这些平台上,您必须猜测容器的 IP 地址并将其包含在服务器名称中。 + +运行 `docker network inspect bridge` 并查看 `Containers` 键,以识别 `IPv4Address` 键下当前分配的最后一个 IP 地址,并将其递增 1。如果没有容器正在运行,则第一个分配的 IP 地址通常为 `172.17.0.2`。 + +然后,将其包含在 `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 +``` + +> ![谨慎] +> +> 请务必将 `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 +``` diff --git a/docs/cn/laravel.md b/docs/cn/laravel.md new file mode 100644 index 00000000..a1010732 --- /dev/null +++ b/docs/cn/laravel.md @@ -0,0 +1,75 @@ +# Laravel + +## Docker + +使用 FrankenPHP 为 [Laravel](https://laravel.com) Web 应用程序提供服务就像将项目挂载到官方 Docker 镜像的 `/app` 目录中一样简单。 + +从 Laravel 应用程序的主目录运行以下命令: + +```console +docker run -p 80:80 -p 443:443 -p 443:443/udp -v $PWD:/app dunglas/frankenphp +``` + +尽情享受吧! + +## 本地安装 + +或者,你可以从本地机器上使用 FrankenPHP 运行 Laravel 项目: + +1. [下载与您的系统相对应的二进制文件](https://github.com/dunglas/frankenphp/releases) +2. 将以下配置添加到Laravel项目根目录中名为 `Caddyfile` 的文件中: + + ```caddyfile + { + frankenphp + order php_server before file_server + } + + # 服务器的域名 + localhost { + # 将 webroot 设置为 public/ 目录 + root * public/ + # 启用压缩(可选) + encode zstd br gzip + # 执行当前目录中的 PHP 文件并提供资产 + php_server + } + ``` + +3. 从 Laravel 项目的根目录启动 FrankenPHP: `./frankenphp run` + +## Laravel Octane + +Octane 可以通过 Composer 包管理器安装: + +```console +composer require laravel/octane +``` + +安装 Octane 后,您可以执行 `octane:install` Artisan 命令,该命令会将 Octane 的配置文件安装到您的应用程序中: + +```console +php artisan octane:install --server=frankenphp +``` + +Octane 服务可以通过 `octane:start` Artisan 命令启动。 + +```console +php artisan octane:start +``` + +`octane:start` 命令可以采用以下选项: + +* `--host`: 服务器应绑定到的 IP 地址 (默认值: `127.0.0.1`) +* `--port`: 服务器应可用的端口 (默认值: `8000`) +* `--admin-port`: 管理服务器应可用的端口 (默认值: `2019`) +* `--workers`: 应可用于处理请求的 worker 数 (默认值: `auto`) +* `--max-requests`: 在重新加载服务之前要处理的请求数 (默认值: `500`) +* `--caddyfile`: FrankenPHP `Caddyfile` 文件的路径 +* `--https`: 开启 HTTPS、HTTP/2 和 HTTP/3,自动生成和续订证书 +* `--http-redirect`: 启用 HTTP 到 HTTPS 重定向(仅在通过 --https 时启用) +* `--watch`: 修改应用程序时自动重新加载服务器 +* `--poll`: 在监视时使用文件系统轮询,以便通过网络监视文件 +* `--log-level`: 在指定日志级别或高于指定日志级别的日志消息 + +了解更多关于 [Laravel Octane 在其官方文档](https://laravel.com/docs/octane)。 diff --git a/docs/cn/mercure.md b/docs/cn/mercure.md new file mode 100644 index 00000000..855e4520 --- /dev/null +++ b/docs/cn/mercure.md @@ -0,0 +1,12 @@ +# 实时 + +FrankenPHP 带有一个内置的 Mercure 集线器! +Mercure 允许将事件实时推送到所有连接的设备:它们将立即收到 JavaScript 事件。 + +无需JS库或SDK! + +![Mercure](https://mercure.rocks/static/main.png) + +要启用 Mercure 中心,请按照 [Mercure 网站](https://mercure.rocks/docs/hub/config)中的说明更新`Caddyfile`。 + +要从您的代码中推送 Mercure 更新,我们推荐 [Symfony Mercure Component](https://symfony.com/components/Mercure)(您不需要 Symfony 全栈框架来使用它)。 diff --git a/docs/cn/production.md b/docs/cn/production.md new file mode 100644 index 00000000..144df0c7 --- /dev/null +++ b/docs/cn/production.md @@ -0,0 +1,138 @@ +# 在生产环境中部署 + +在本教程中,我们将学习如何使用 Docker Compose 在单个服务器上部署 PHP 应用程序。 + +如果您使用的是 Symfony,请阅读 Symfony Docker 项目(使用 FrankenPHP)的“[在生产环境中部署](https://github.com/dunglas/symfony-docker/blob/main/docs/production.md)”文档条目。 + +如果您使用的是 API Platform(也使用 FrankenPHP),请参阅 [框架的部署文档](https://api-platform.com/docs/deployment/)。 + +## 准备应用 + +首先,在 PHP 项目的根目录中创建一个 `Dockerfile`: + +```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 文件复制到 public 目录中 +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 +volumes: + caddy_data: + caddy_config: +``` + +> [!注意] +> 前面的示例适用于生产用途。 +> 在开发中,您可能希望使用 volume,不同的 PHP 配置和不同的 `SERVER_NAME` 环境变量值。 +> +> 看看 [Symfony Docker](https://github.com/dunglas/symfony-docker) 项目 +> (使用 FrankenPHP)作为使用多阶段镜像的更高级示例, +> Composer、额外的 PHP 扩展等。 + +最后,如果您使用 Git,请提交这些文件并推送。 + +## 准备服务器 + +若要在生产环境中部署应用程序,需要一台服务器。 +在本教程中,我们将使用 DigitalOcean 提供的虚拟机,但任何 Linux 服务器都可以工作。 +如果您已经有安装了 Docker 的 Linux 服务器,您可以直接跳到 [下一节](#配置域名)。 + +否则,请使用 [此会员链接](https://m.do.co/c/5d8aabe3ab80) 获得 200 美元的免费信用额度,创建一个帐户,然后单击“创建 Droplet”。 +然后,单击“选择镜像”部分下的“市场”选项卡,然后搜索名为“Docker”的应用程序。 +这将配置已安装最新版本的 Docker 和 Docker Compose 的 Ubuntu 服务器! + +出于测试目的,最便宜的计划就足够了。 +对于实际的生产用途,您可能需要在“常规用途”部分中选择一个计划来满足您的需求。 + +![使用 Docker 在 DigitalOcean 上部署 FrankenPHP](../digitalocean-droplet.png) + +您可以保留其他设置的默认值,也可以根据需要进行调整。 +不要忘记添加您的SSH密钥或创建密码,然后按“完成并创建”按钮。 + +然后,在 Droplet 预配时等待几秒钟。 +Droplet 准备就绪后,使用 SSH 进行连接: + +```console +ssh root@ +``` + +## 配置域名 + +在大多数情况下,您需要将域名与您的网站相关联。 +如果您还没有域名,则必须通过注册商购买。 + +然后为您的域名创建类型为 `A` 的 DNS 记录,指向服务器的 IP 地址: + +```dns +your-domain-name.example.com. IN A 207.154.233.113 +``` + +DigitalOcean 域服务示例(“网络” > “域”): + +![在 DigitalOcean 上配置 DNS](../digitalocean-dns.png) + +> [!注意] +> Let's Encrypt 是 FrankenPHP 默认用于自动生成 TLS 证书的服务,不支持使用裸 IP 地址。使用域名是使用 Let's Encrypt 的必要条件。 + +## 部署 + +使用 `git clone`、`scp` 或任何其他可能适合您需要的工具在服务器上复制您的项目。 +如果使用 GitHub,则可能需要使用 [部署密钥](https://docs.github.com/en/free-pro-team@latest/developers/overview/managing-deploy-keys#deploy-keys)。 +部署密钥也[由 GitLab 支持](https://docs.gitlab.com/ee/user/project/deploy_keys/)。 + +Git 示例: + +```console +git clone git@github.com:/.git +``` + +进入包含项目 (``) 的目录,并在生产模式下启动应用: + +```console +docker compose up -d --wait +``` + +您的服务器已启动并运行,并且已自动为您生成 HTTPS 证书。 +去 `https://your-domain-name.example.com` 享受吧! + +> [!谨慎] +> Docker 有一个缓存层,请确保每个部署都有正确的构建,或者使用 --no-cache 选项重新构建项目以避免缓存问题。 + +## 在多个节点上部署 + +如果要在计算机集群上部署应用程序,可以使用 [Docker Swarm](https://docs.docker.com/engine/swarm/stack-deploy/), +它与提供的 Compose 文件兼容。 +要在 Kubernetes 上部署,请查看 [API 平台提供的 Helm 图表](https://api-platform.com/docs/deployment/kubernetes/),它可以很容易地适应 Symfony Docker 的使用。 diff --git a/docs/cn/static.md b/docs/cn/static.md new file mode 100644 index 00000000..9770c75f --- /dev/null +++ b/docs/cn/static.md @@ -0,0 +1,81 @@ +# 创建静态构建 + +由于伟大的 [static-php-cli 项目](https://github.com/crazywhalecc/static-php-cli),创建 FrankenPHP 的静态构建是可能的(尽管它的名字,这个项目支持所有 SAPI,而不仅仅是 CLI), +而不是使用 PHP 库的本地安装。 + +使用这种方法,一个可移植的二进制文件将包含 PHP 解释器、Caddy Web 服务器和 FrankenPHP! + +FrankenPHP 还支持 [将 PHP 应用程序嵌入到静态二进制文件中](embed.md)。 + +## Linux + +我们提供了一个 Docker 镜像来构建 Linux 静态二进制文件: + +```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 +``` + +生成的静态二进制文件名为 `frankenphp` ,可在当前目录中找到。 + +如果您想在没有 Docker 的情况下构建静态二进制文件,请查看 macOS 说明,它也适用于 Linux。 + +### 自定义扩展 + +默认情况下,大多数流行的 PHP 扩展都会被编译。 + +若要减小二进制文件的大小并减少攻击面,可以选择使用 `PHP_EXTENSIONS` Docker ARG 构建的扩展列表。 + +例如,运行以下命令以仅生成 `opcache` 扩展: + +```console +docker buildx bake --load --set static-builder.args.PHP_EXTENSIONS=opcache,pdo_sqlite static-builder +# ... +``` + +若要将启用其他功能的库添加到已启用的扩展中,可以使用 `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 +``` + +参见:[自定义构建](#自定义构建) + +### GitHub Token + +如果达到 GitHub API 速率限制,请在名为 `GITHUB_TOKEN` 的环境变量中设置 GitHub Personal Access 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`: 要构建的额外库,为扩展添加额外的功能 +* `EMBED`: 要嵌入二进制文件的 PHP 应用程序的路径 +* `CLEAN`: 设置后,libphp 及其所有依赖项都是从头开始构建的(无缓存) +* `DEBUG_SYMBOLS`: 设置后,调试符号不会被剥离,而是将添加到二进制文件中 +* `RELEASE`: (仅限维护者)设置后,生成的二进制文件将上传到 GitHub 上 diff --git a/docs/cn/worker.md b/docs/cn/worker.md new file mode 100644 index 00000000..42269e4b --- /dev/null +++ b/docs/cn/worker.md @@ -0,0 +1,114 @@ +# 使用 FrankenPHP Workers + +启动应用程序一次并将其保存在内存中。 +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 +``` + +### 独立二进制 + +使用 `php-server` 命令的 `--worker` 选项, 执行命令使当前目录的内容使用 worker: + +```console +./frankenphp php-server --worker /path/to/your/worker/script.php +``` + +## Symfony Runtime + +FrankenPHP 的 worker 模式由 [Symfony Runtime 组件](https://symfony.com/doc/current/components/runtime.html) 支持。 +要在 worker 中启动任何 Symfony 应用程序,请安装[PHP Runtime](https://github.com/php-runtime/runtime)的 FrankenPHP 软件包: + +```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 +boot(); + +// 循环外的处理程序以获得更好的性能(减少工作量) +$handler = static function () use ($myApp) { + // 收到请求时调用, + // 超全局变量, php://input + 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); + + // 发送 HTTP 响应后执行某些操作 + $myApp->terminate(); + + // 调用垃圾回收器以减少在页面生成过程中触发垃圾回收器的几率 + gc_collect_cycles(); +} +// 清理 +$myApp->shutdown(); +``` + +然后,启动应用并使用 `FRANKENPHP_CONFIG` 环境变量来配置你的 worker: + +```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 +``` + +默认情况下,每个 CPU 启动一个 worker 线程。 +您还可以配置要启动的 worker 线程数: + +```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 模式下使用此类代码的解决方法是在处理一定数量的请求后重新启动 worker 程序脚本: + +前面的 worker 线程代码段允许通过设置名为 `MAX_REQUESTS` 的环境变量来配置要处理的最大请求数。