From 14202307a8a0b9fa21630aa009164cd0f0cd9565 Mon Sep 17 00:00:00 2001 From: Alliballibaba Date: Wed, 7 Jan 2026 21:36:21 +0100 Subject: [PATCH 1/9] LLM translation. --- dev.Dockerfile | 2 + docs/cn/classic.md | 6 +- docs/cn/compile.md | 18 +- docs/cn/config.md | 136 ++++-- docs/cn/docker.md | 48 +- docs/cn/early-hints.md | 2 +- docs/cn/embed.md | 33 +- docs/cn/extensions.md | 371 ++++++++++----- docs/cn/github-actions.md | 31 +- docs/cn/hot-reload.md | 139 ++++++ docs/cn/known-issues.md | 40 +- docs/cn/laravel.md | 46 +- docs/cn/logging.md | 66 +++ docs/cn/mercure.md | 149 +++++- docs/cn/performance.md | 35 +- docs/cn/production.md | 23 +- docs/cn/static.md | 62 +-- docs/cn/wordpress.md | 59 +++ docs/cn/worker.md | 20 +- docs/fr/classic.md | 12 +- docs/fr/compile.md | 39 +- docs/fr/config.md | 282 ++++++----- docs/fr/docker.md | 56 ++- docs/fr/embed.md | 19 +- docs/fr/extensions.md | 225 ++++----- docs/fr/github-actions.md | 2 +- docs/fr/hot-reload.md | 139 ++++++ docs/fr/known-issues.md | 16 +- docs/fr/laravel.md | 54 ++- docs/fr/logging.md | 67 +++ docs/fr/mercure.md | 147 +++++- docs/fr/metrics.md | 18 +- docs/fr/performance.md | 90 ++-- docs/fr/production.md | 20 +- docs/fr/static.md | 19 +- docs/fr/wordpress.md | 59 +++ docs/fr/worker.md | 37 +- docs/fr/x-sendfile.md | 7 +- docs/ja/classic.md | 2 +- docs/ja/compile.md | 34 +- docs/ja/config.md | 148 +++--- docs/ja/docker.md | 19 +- docs/ja/extensions.md | 429 +++++++++++++---- docs/ja/hot-reload.md | 138 ++++++ docs/ja/known-issues.md | 15 +- docs/ja/laravel.md | 36 +- docs/ja/logging.md | 71 +++ docs/ja/mercure.md | 140 +++++- docs/ja/performance.md | 44 +- docs/ja/production.md | 4 +- docs/ja/static.md | 8 +- docs/ja/wordpress.md | 59 +++ docs/ja/worker.md | 29 +- docs/ja/x-sendfile.md | 1 - docs/pt-br/classic.md | 29 +- docs/pt-br/compile.md | 65 +-- docs/pt-br/config.md | 381 +++++++-------- docs/pt-br/docker.md | 78 ++- docs/pt-br/embed.md | 18 +- docs/pt-br/extensions.md | 452 +++++++++++------- docs/pt-br/github-actions.md | 25 +- docs/pt-br/hot-reload.md | 139 ++++++ docs/pt-br/known-issues.md | 100 ++-- docs/pt-br/laravel.md | 145 +++--- docs/pt-br/logging.md | 73 +++ docs/pt-br/mercure.md | 152 +++++- docs/pt-br/metrics.md | 34 +- docs/pt-br/performance.md | 104 ++-- docs/pt-br/production.md | 15 +- docs/pt-br/static.md | 6 +- docs/pt-br/wordpress.md | 59 +++ docs/pt-br/worker.md | 28 +- docs/pt-br/x-sendfile.md | 19 +- docs/ru/classic.md | 11 + docs/ru/compile.md | 95 ++-- docs/ru/config.md | 250 +++++++--- docs/ru/docker.md | 54 ++- docs/ru/early-hints.md | 2 +- docs/ru/embed.md | 47 +- docs/ru/extensions.md | 893 +++++++++++++++++++++++++++++++++++ docs/ru/github-actions.md | 4 +- docs/ru/hot-reload.md | 139 ++++++ docs/ru/known-issues.md | 62 +-- docs/ru/laravel.md | 55 ++- docs/ru/logging.md | 71 +++ docs/ru/mercure.md | 149 +++++- docs/ru/metrics.md | 20 +- docs/ru/performance.md | 155 ++++-- docs/ru/production.md | 78 +-- docs/ru/static.md | 114 +++-- docs/ru/wordpress.md | 59 +++ docs/ru/worker.md | 107 +++-- docs/ru/x-sendfile.md | 71 +++ docs/tr/classic.md | 11 + docs/tr/compile.md | 103 ++-- docs/tr/config.md | 238 ++++++++-- docs/tr/docker.md | 76 ++- docs/tr/early-hints.md | 2 +- docs/tr/embed.md | 59 ++- docs/tr/extensions.md | 893 +++++++++++++++++++++++++++++++++++ docs/tr/github-actions.md | 31 +- docs/tr/hot-reload.md | 139 ++++++ docs/tr/known-issues.md | 79 +++- docs/tr/laravel.md | 50 +- docs/tr/logging.md | 71 +++ docs/tr/mercure.md | 146 +++++- docs/tr/metrics.md | 17 + docs/tr/production.md | 37 +- docs/tr/static.md | 110 ++++- docs/tr/wordpress.md | 59 +++ docs/tr/worker.md | 101 +++- docs/tr/x-sendfile.md | 69 +++ docs/translate.php | 120 +++++ 113 files changed, 8322 insertions(+), 2218 deletions(-) create mode 100644 docs/cn/hot-reload.md create mode 100644 docs/cn/logging.md create mode 100644 docs/cn/wordpress.md create mode 100644 docs/fr/hot-reload.md create mode 100644 docs/fr/logging.md create mode 100644 docs/fr/wordpress.md create mode 100644 docs/ja/hot-reload.md create mode 100644 docs/ja/logging.md create mode 100644 docs/ja/wordpress.md create mode 100644 docs/pt-br/hot-reload.md create mode 100644 docs/pt-br/logging.md create mode 100644 docs/pt-br/wordpress.md create mode 100644 docs/ru/classic.md create mode 100644 docs/ru/extensions.md create mode 100644 docs/ru/hot-reload.md create mode 100644 docs/ru/logging.md create mode 100644 docs/ru/wordpress.md create mode 100644 docs/ru/x-sendfile.md create mode 100644 docs/tr/classic.md create mode 100644 docs/tr/extensions.md create mode 100644 docs/tr/hot-reload.md create mode 100644 docs/tr/logging.md create mode 100644 docs/tr/metrics.md create mode 100644 docs/tr/wordpress.md create mode 100644 docs/tr/x-sendfile.md create mode 100644 docs/translate.php diff --git a/dev.Dockerfile b/dev.Dockerfile index 62e83e3821..5fbbc5732b 100644 --- a/dev.Dockerfile +++ b/dev.Dockerfile @@ -37,6 +37,7 @@ RUN apt-get update && \ # Dev tools \ git \ clang \ + clang-format \ cmake \ llvm \ gdb \ @@ -56,6 +57,7 @@ RUN git clone --branch=PHP-8.5 https://github.com/php/php-src.git . && \ EXTENSION_DIR=/usr/lib/frankenphp/modules ./configure \ --enable-embed \ --enable-zts \ + --with-openssl \ --disable-zend-signals \ --enable-zend-max-execution-timers \ --with-config-file-path=/etc/frankenphp/php.ini \ diff --git a/docs/cn/classic.md b/docs/cn/classic.md index 5fbe797060..4b05cc31e5 100644 --- a/docs/cn/classic.md +++ b/docs/cn/classic.md @@ -2,10 +2,10 @@ 在没有任何额外配置的情况下,FrankenPHP 以经典模式运行。在此模式下,FrankenPHP 的功能类似于传统的 PHP 服务器,直接提供 PHP 文件服务。这使其成为 PHP-FPM 或 Apache with mod_php 的无缝替代品。 -与 Caddy 类似,FrankenPHP 接受无限数量的连接,并使用[固定数量的线程](config.md#caddyfile-配置)来为它们提供服务。接受和排队的连接数量仅受可用系统资源的限制。 -PHP 线程池使用在启动时初始化的固定数量的线程运行,类似于 PHP-FPM 的静态模式。也可以让线程在[运行时自动扩展](performance.md#max_threads),类似于 PHP-FPM 的动态模式。 +与 Caddy 类似,FrankenPHP 接受无限数量的连接,并使用[固定数量的线程](config.md#caddyfile-config)来为它们提供服务。接受和排队的连接数量仅受可用系统资源的限制。 +PHP 线程池以启动时初始化的固定数量的线程运行,这类似于 PHP-FPM 的静态模式。线程也可以在[运行时自动扩展](performance.md#max_threads),类似于 PHP-FPM 的动态模式。 -排队的连接将无限期等待,直到有 PHP 线程可以为它们提供服务。为了避免这种情况,你可以在 FrankenPHP 的全局配置中使用 max_wait_time [配置](config.md#caddyfile-配置)来限制请求可以等待空闲的 PHP 线程的时间,超时后将被拒绝。 +排队的连接将无限期等待,直到有 PHP 线程可以为它们提供服务。为了避免这种情况,你可以在 FrankenPHP 的全局配置中使用 `max_wait_time` [配置](config.md#caddyfile-config)来限制请求在被拒绝之前等待空闲 PHP 线程的时间。 此外,你还可以在 Caddy 中设置合理的[写超时](https://caddyserver.com/docs/caddyfile/options#timeouts)。 每个 Caddy 实例只会启动一个 FrankenPHP 线程池,该线程池将在所有 `php_server` 块之间共享。 diff --git a/docs/cn/compile.md b/docs/cn/compile.md index 5432ab5ea6..3553849ea6 100644 --- a/docs/cn/compile.md +++ b/docs/cn/compile.md @@ -1,13 +1,13 @@ # 从源代码编译 -本文档解释了如何创建一个 FrankenPHP 构建,它将 PHP 加载为一个动态库。 +本文档解释了如何创建一个 FrankenPHP 二进制文件,它将 PHP 加载为一个动态库。 这是推荐的方法。 -或者,你也可以 [编译静态版本](static.md)。 +或者,也可以创建 [完全静态和半静态构建](static.md)。 ## 安装 PHP -FrankenPHP 支持 PHP 8.2 及更高版本。 +FrankenPHP 兼容 PHP 8.2 及更高版本。 ### 使用 Homebrew (Linux 和 Mac) @@ -55,7 +55,7 @@ brew install libiconv bison brotli re2c pkg-config watcher echo 'export PATH="/opt/homebrew/opt/bison/bin:$PATH"' >> ~/.zshrc ``` -然后运行 `./configure` 脚本: +然后运行 configure 脚本: ```console ./configure \ @@ -80,9 +80,10 @@ sudo make install 或者,可以通过向 Go 编译器传递构建标签来禁用这些功能。 | 功能 | 依赖项 | 用于禁用的构建标签 | -| --------------------- | --------------------------------------------------------------------- | ------------------ | +| :-------------------- | :-------------------------------------------------------------------- | :----------------- | | Brotli 压缩 | [Brotli](https://github.com/google/brotli) | nobrotli | | 文件更改时重启 worker | [Watcher C](https://github.com/e-dant/watcher/tree/release/watcher-c) | nowatcher | +| [Mercure](mercure.md) | [Mercure Go 库](https://pkg.go.dev/github.com/dunglas/mercure)(自动安装,AGPL 许可) | nomercure | ## 编译 Go 应用 @@ -102,8 +103,12 @@ xcaddy build \ --output frankenphp \ --with github.com/dunglas/frankenphp/caddy \ --with github.com/dunglas/mercure/caddy \ - --with github.com/dunglas/vulcain/caddy + --with github.com/dunglas/vulcain/caddy \ + --with github.com/dunglas/caddy-cbrotli # 在这里添加额外的 Caddy 模块和 FrankenPHP 扩展 + # (可选)如果你想从 FrankenPHP 源代码编译: + # --with github.com/dunglas/frankenphp=$(pwd) \ + # --with github.com/dunglas/frankenphp/caddy=$(pwd)/caddy ``` > [!TIP] @@ -124,4 +129,3 @@ xcaddy build \ curl -L https://github.com/php/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 -``` diff --git a/docs/cn/config.md b/docs/cn/config.md index 3b46811251..c278aefa5f 100644 --- a/docs/cn/config.md +++ b/docs/cn/config.md @@ -1,17 +1,35 @@ # 配置 -FrankenPHP、Caddy 以及 Mercure 和 Vulcain 模块可以使用 [Caddy 支持的格式](https://caddyserver.com/docs/getting-started#your-first-config) 进行配置。 +FrankenPHP、Caddy 以及 [Mercure](mercure.md) 和 [Vulcain](https://vulcain.rocks) 模块可以使用 [Caddy 支持的格式](https://caddyserver.com/docs/getting-started#your-first-config) 进行配置。 -在 [Docker 镜像](docker.md) 中,`Caddyfile` 位于 `/etc/frankenphp/Caddyfile`。 -静态二进制文件也会在执行 `frankenphp run` 命令的目录中查找 `Caddyfile`。 -你可以使用 `-c` 或 `--config` 选项指定自定义路径。 +最常见的格式是 `Caddyfile`,它是一种简单、人类可读的文本格式。默认情况下,FrankenPHP 会在当前目录中查找 `Caddyfile`。你可以使用 `-c` 或 `--config` 选项指定自定义路径。 + +以下显示了一个用于服务 PHP 应用程序的最小 `Caddyfile`: + +```caddyfile +# 响应的主机名 +localhost + +# (可选)提供文件服务的目录,否则默认为当前目录 +#root public/ +php_server +``` + +FrankenPHP 仓库中提供了 [一个更高级的 `Caddyfile`](https://github.com/php/frankenphp/blob/main/caddy/frankenphp/Caddyfile),该文件支持更多功能并提供方便的环境变量,Docker 镜像也附带了该文件。 PHP 本身可以[使用 `php.ini` 文件](https://www.php.net/manual/zh/configuration.file.php)进行配置。 -根据你的安装方法,PHP 解释器将在上述位置查找配置文件。 +根据你的安装方法,FrankenPHP 和 PHP 解释器将在下面描述的位置查找配置文件。 ## Docker +FrankenPHP: + +- `/etc/frankenphp/Caddyfile`:主配置文件 +- `/etc/frankenphp/Caddyfile.d/*.caddyfile`:自动加载的附加配置文件 + +PHP: + - `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-/` @@ -29,12 +47,24 @@ RUN cp $PHP_INI_DIR/php.ini-development $PHP_INI_DIR/php.ini ## RPM 和 Debian 包 -- `php.ini`: `/etc/frankenphp/php.ini`(默认情况下提供带有生产预设的 `php.ini` 文件) -- 附加配置文件: `/etc/frankenphp/php.d/*.ini` -- PHP 扩展: `/usr/lib/frankenphp/modules/` +FrankenPHP: + +- `/etc/frankenphp/Caddyfile`:主配置文件 +- `/etc/frankenphp/Caddyfile.d/*.caddyfile`:自动加载的附加配置文件 + +PHP: + +- `php.ini`: `/etc/php-zts/php.ini`(默认情况下提供带有生产预设的 `php.ini` 文件) +- 附加配置文件: `/etc/php-zts/conf.d/*.ini` ## 静态二进制文件 +FrankenPHP: + +- 在当前工作目录中:`Caddyfile` + +PHP: + - `php.ini`: 执行 `frankenphp run` 或 `frankenphp php-server` 的目录,然后是 `/etc/frankenphp/php.ini` - 附加配置文件: `/etc/frankenphp/php.d/*.ini` - PHP 扩展: 无法加载,将它们打包在二进制文件本身中 @@ -55,14 +85,13 @@ localhost { } ``` -你还可以使用全局选项显式配置 FrankenPHP: -`frankenphp` [全局选项](https://caddyserver.com/docs/caddyfile/concepts#global-options) 可用于配置 FrankenPHP。 +你还可以使用 `frankenphp` [全局选项](https://caddyserver.com/docs/caddyfile/concepts#global-options) 显式配置 FrankenPHP: ```caddyfile { frankenphp { num_threads # 设置要启动的 PHP 线程数量。默认:可用 CPU 数量的 2 倍。 - max_threads # 限制可以在运行时启动的额外 PHP 线程的数量。默认值:num_threads。可以设置为 'auto'。 + max_threads # 限制可以在运行时启动的额外 PHP 线程的数量。默认值:num_threads。可以设置为 'auto'。 max_wait_time # 设置请求在超时之前可以等待的最大时间,直到找到一个空闲的 PHP 线程。 默认:禁用。 php_ini # 设置一个 php.ini 指令。可以多次使用以设置多个指令。 worker { @@ -79,7 +108,7 @@ localhost { # ... ``` -或者,您可以使用 `worker` 选项的一行简短形式。 +或者,您可以使用 `worker` 选项的一行简短形式: ```caddyfile { @@ -91,7 +120,7 @@ localhost { # ... ``` -如果您在同一服务器上服务多个应用程序,您还可以定义多个工作线程: +如果您在同一服务器上服务多个应用程序,您还可以定义多个工作线程: ```caddyfile app.example.com { @@ -118,7 +147,7 @@ other.example.com { `php` 指令将所有输入传递给 PHP,而不是先检查是否 是一个PHP文件。在[性能页面](performance.md#try_files)中了解更多关于它的信息。 -使用 `php_server` 指令等同于以下配置: +使用 `php_server` 指令等同于以下配置: ```caddyfile route { @@ -152,7 +181,7 @@ php_server [] { file_server off # 禁用内置的 file_server 指令。 worker { # 为此服务器创建特定的worker。可以多次指定以创建多个workers。 file # 设置工作脚本的路径,可以相对于 php_server 根目录 - num # 设置要启动的 PHP 线程数,默认为可用数量的 2 倍 + num # 设置要启动的 PHP 线程数,默认为可用 CPU 数量的 2 倍 name # 为worker设置名称,用于日志和指标。默认值:worker文件的绝对路径。定义在 php_server 块中时,始终以 m# 开头。 watch # 设置要监视文件更改的路径。可以为多个路径多次指定。 env # 设置一个额外的环境变量为给定值。可以多次指定以设置多个环境变量。此工作进程的环境变量也从 php_server 父进程继承,但可以在此处覆盖。 @@ -167,7 +196,7 @@ php_server [] { 由于 workers 只会启动您的应用程序一次并将其保留在内存中, 因此对您的 PHP 文件的任何更改不会立即反映出来。 -Wworkers 可以通过 `watch` 指令在文件更改时重新启动。 +工作线程可以通过 `watch` 指令在文件更改时重新启动。 这对开发环境很有用。 ```caddyfile @@ -181,8 +210,10 @@ Wworkers 可以通过 `watch` 指令在文件更改时重新启动。 } ``` -如果没有指定 `watch` 目录,它将回退到 `./**/*.{php,yaml,yml,twig,env}`, -这将监视启动 FrankenPHP 进程的目录及其子目录中的所有 `.php`、`.yaml`、`.yml`、`.twig` 和 `.env` 文件。 +此功能通常与 [热重载](hot-reload.md) 结合使用。 + +如果没有指定 `watch` 目录,它将回退到 `./**/*.{env,php,twig,yaml,yml}`, +这将监视启动 FrankenPHP 进程的目录及其子目录中的所有 `.env`、`.php`、`.twig`、`.yaml` 和 `.yml` 文件。 你也可以通过 [shell 文件名模式](https://pkg.go.dev/path/filepath#Match) 指定一个或多个目录: ```caddyfile @@ -229,34 +260,6 @@ Wworkers 可以通过 `watch` 指令在文件更改时重新启动。 } ``` -### 全双工 (HTTP/1) - -在使用HTTP/1.x时,可能希望启用全双工模式,以便在完整主体之前写入响应。 -已被阅读。(例如:WebSocket、服务器发送事件等。) - -这是一个可选配置,需要添加到 `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` 的情况下注入 Caddy 指令: @@ -268,12 +271,12 @@ CADDY_GLOBAL_OPTIONS="servers { 至于 FPM 和 CLI SAPIs,环境变量默认在 `$_SERVER` 超全局中暴露。 -[the `variables_order` PHP 指令](https://www.php.net/manual/en/ini.core.php#ini.variables-order) 的 `S` 值始终等于 `ES`,无论 `E` 在该指令中的其他位置如何。 +[the `variables_order` PHP 指令](https://www.php.net/manual/zh/ini.core.php#ini.variables-order) 的 `S` 值始终等于 `ES`,无论 `E` 在该指令中的其他位置如何。 ## PHP 配置 -加载[附加的 PHP 配置文件](https://www.php.net/manual/en/configuration.file.php#configuration.file.scan), -`PHP_INI_SCAN_DIR`环境变量可以被使用。 +要加载[附加的 PHP 配置文件](https://www.php.net/manual/zh/configuration.file.php#configuration.file.scan), +可以使用 `PHP_INI_SCAN_DIR` 环境变量。 设置后,PHP 将加载给定目录中所有带有 `.ini` 扩展名的文件。 您还可以通过在 `Caddyfile` 中使用 `php_ini` 指令来更改 PHP 配置: @@ -293,6 +296,41 @@ CADDY_GLOBAL_OPTIONS="servers { } ``` +### 禁用 HTTPS + +默认情况下,FrankenPHP 会为所有主机名(包括 `localhost`)自动启用 HTTPS。如果你想禁用 HTTPS(例如在开发环境中),你可以将 `SERVER_NAME` 环境变量设置为 `http://` 或 `:80`: + +另外,你可以使用 [Caddy 文档](https://caddyserver.com/docs/automatic-https#activation) 中描述的所有其他方法。 + +如果你想使用 `127.0.0.1` IP 地址而不是 `localhost` 主机名来使用 HTTPS,请阅读 [已知问题](known-issues.md#using-https127001-with-docker) 部分。 + +### 全双工 (HTTP/1) + +在使用 HTTP/1.x 时,可能希望启用全双工模式,以允许在读取整个请求体之前写入响应。(例如:[Mercure](mercure.md)、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)中找到有关此设置的更多信息。 + ## 启用调试模式 使用Docker镜像时,将`CADDY_GLOBAL_OPTIONS`环境变量设置为`debug`以启用调试模式: diff --git a/docs/cn/docker.md b/docs/cn/docker.md index 7d7402221f..eba151b7e2 100644 --- a/docs/cn/docker.md +++ b/docs/cn/docker.md @@ -7,7 +7,7 @@ 标签遵循此模式:`dunglas/frankenphp:-php-` - `` 和 `` 分别是 FrankenPHP 和 PHP 的版本号,范围从主版本(例如 `1`)、次版本(例如 `1.2`)到补丁版本(例如 `1.2.3`)。 -- `` 要么是 `bookworm`(用于 Debian Bookworm)要么是 `alpine`(用于 Alpine 的最新稳定版本)。 +- `` 可以是 `trixie`(用于 Debian Trixie)、`bookworm`(用于 Debian Bookworm),也可以是 `alpine`(用于 Alpine 的最新稳定版本)。 [浏览标签](https://hub.docker.com/r/dunglas/frankenphp/tags)。 @@ -28,6 +28,10 @@ docker build -t my-php-app . docker run -it --rm --name my-running-app my-php-app ``` +## 如何调整配置 + +为方便起见,镜像中提供了一个包含有用环境变量的[默认 Caddyfile](https://github.com/php/frankenphp/blob/main/caddy/frankenphp/Caddyfile)。 + ## 如何安装更多 PHP 扩展 [`docker-php-extension-installer`](https://github.com/mlocati/docker-php-extension-installer) 脚本在基础镜像中提供。 @@ -80,14 +84,13 @@ 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。 +[用于构建的镜像](https://hub.docker.com/r/dunglas/frankenphp/tags?name=builder) 适用于所有版本的 FrankenPHP 和 PHP,包括 Debian 和 Alpine。 > [!TIP] > -> 如果你的系统基于 musl libc(Alpine Linux 上默认使用)并搭配 Symfony 使用, -> 你可能需要 [增加默认堆栈大小](compile.md#using-xcaddy)。 +> 如果你正在使用 Alpine Linux 和 Symfony,你可能需要 [增加默认堆栈大小](compile.md#using-xcaddy)。 -## 默认启用 worker 模式 +## 默认启用 Worker 模式 设置 `FRANKENPHP_CONFIG` 环境变量以使用 worker 脚本启动 FrankenPHP: @@ -99,9 +102,9 @@ FROM dunglas/frankenphp ENV FRANKENPHP_CONFIG="worker ./public/index.php" ``` -## 开发挂载宿主机目录 +## 开发时使用卷 -要使用 FrankenPHP 轻松开发,请从包含应用程序源代码的主机挂载目录作为 Docker 容器中的 volume: +要使用 FrankenPHP 轻松开发,请将包含应用程序源代码的主机目录作为卷挂载到 Docker 容器中: ```console docker run -v $PWD:/app/public -p 80:80 -p 443:443 -p 443:443/udp --tty my-php-app @@ -131,16 +134,16 @@ services: - ./:/app/public - caddy_data:/data - caddy_config:/config - # 在生产环境中注释以下行,它允许在 dev 中使用清晰可读日志 + # 在生产环境中注释以下行,它允许在开发中输出清晰可读的日志 tty: true -# Caddy 证书和配置所需的挂载目录 +# Caddy 证书和配置所需的卷 volumes: caddy_data: caddy_config: ``` -## 以非 root 用户身份运行 +## 以非 Root 用户身份运行 FrankenPHP 可以在 Docker 中以非 root 用户身份运行。 @@ -154,21 +157,21 @@ ARG USER=appuser RUN \ # 在基于 alpine 的发行版使用 "adduser -D ${USER}" useradd ${USER}; \ - # 需要开放80和443端口的权限 + # 添加绑定到 80 和 443 端口的额外能力 setcap CAP_NET_BIND_SERVICE=+eip /usr/local/bin/frankenphp; \ - # 需要 /config/caddy 和 /data/caddy 目录的写入权限 + # 授予 /config/caddy 和 /data/caddy 写入权限 chown -R ${USER}:${USER} /config/caddy /data/caddy USER ${USER} ``` -### 无权限运行 +### 无能力运行 -即使在无根运行时,FrankenPHP 也需要 `CAP_NET_BIND_SERVICE` 权限来将 +即使在无根运行时,FrankenPHP 也需要 `CAP_NET_BIND_SERVICE` 能力来将 Web 服务器绑定到特权端口(80 和 443)。 如果你在非特权端口(1024 及以上)上公开 FrankenPHP,则可以以非 root 用户身份运行 -Web 服务器,并且不需要任何权限: +Web 服务器,并且不需要任何能力: ```dockerfile FROM dunglas/frankenphp @@ -178,7 +181,7 @@ ARG USER=appuser RUN \ # 在基于 alpine 的发行版使用 "adduser -D ${USER}" useradd ${USER}; \ - # 移除默认权限 + # 移除默认能力 setcap -r /usr/local/bin/frankenphp; \ # 给予 /config/caddy 和 /data/caddy 写入权限 chown -R ${USER}:${USER} /config/caddy /data/caddy @@ -191,14 +194,15 @@ USER ${USER} ## 更新 -Docker 镜像会按照以下条件更新: +Docker 镜像会在以下情况下构建: -- 发布新的版本后 -- 每日 4:00(UTC 时间)检查新的 PHP 镜像 +- 发布新的版本时 +- 每日 UTC 时间 4 点,如果官方 PHP 镜像有新版本可用时 ## 开发版本 -可在此 [`dunglas/frankenphp-dev`](https://hub.docker.com/repository/docker/dunglas/frankenphp-dev) 仓库获取开发版本。 -每次在 GitHub 仓库的主分支有新的 commit 都会触发一次新的 build。 +开发版本可在 [`dunglas/frankenphp-dev`](https://hub.docker.com/repository/docker/dunglas/frankenphp-dev) Docker 仓库中提供。 +每次有新的提交推送到 GitHub 仓库的主分支时,都会触发一次新的构建。 -`latest*` tag 指向最新的 `main` 分支,且同样支持 `sha-` 的 tag。 +`latest*` 标签指向 `main` 分支的头部。 +同时,`sha-` 形式的标签也可用。 \ No newline at end of file diff --git a/docs/cn/early-hints.md b/docs/cn/early-hints.md index a45eb4cc41..5a58880816 100644 --- a/docs/cn/early-hints.md +++ b/docs/cn/early-hints.md @@ -9,7 +9,7 @@ FrankenPHP 原生支持 [103 Early Hints 状态码](https://developer.chrome.com header('Link: ; rel=preload; as=style'); headers_send(103); -// 慢速算法和 SQL 查询 +// 你的慢速算法和 SQL 查询 🤪 echo <<<'HTML' diff --git a/docs/cn/embed.md b/docs/cn/embed.md index 2ef8b936e3..2aab5c2e65 100644 --- a/docs/cn/embed.md +++ b/docs/cn/embed.md @@ -4,25 +4,25 @@ FrankenPHP 能够将 PHP 应用程序的源代码和资源文件嵌入到静态 由于这个特性,PHP 应用程序可以作为独立的二进制文件分发,包括应用程序本身、PHP 解释器和生产级 Web 服务器 Caddy。 -了解有关此功能的更多信息 [Kévin 在 SymfonyCon 上的演讲](https://dunglas.dev/2023/12/php-and-symfony-apps-as-standalone-binaries/)。 +了解有关此功能的更多信息 [Kévin 在 SymfonyCon 2023 上的演讲](https://dunglas.dev/2023/12/php-and-symfony-apps-as-standalone-binaries/)。 -有关嵌入 Laravel 应用程序,请[阅读此特定文档条目](laravel.md#laravel-apps-as-standalone-binaries)。 +要嵌入 Laravel 应用程序,请[阅读此特定文档条目](laravel.md#laravel-apps-as-standalone-binaries)。 ## 准备你的应用 -在创建独立二进制文件之前,请确保应用已准备好进行打包。 +在创建独立二进制文件之前,请确保应用已准备好进行嵌入。 例如,你可能希望: - 给应用安装生产环境的依赖 -- 导出 autoloader -- 如果可能,为应用启用生产模式 -- 丢弃不需要的文件,例如 `.git` 或测试文件,以减小最终二进制文件的大小 +- 生成自动加载器 +- 为应用程序启用生产模式(如果有的话) +- 剔除不需要的文件,例如 `.git` 或测试文件,以减小最终二进制文件的大小 例如,对于 Symfony 应用程序,你可以使用以下命令: ```console -# 导出项目以避免 .git/ 等目录 +# 导出项目以清除 .git/ 等 mkdir $TMPDIR/my-prepared-app git archive HEAD | tar -x -C $TMPDIR/my-prepared-app cd $TMPDIR/my-prepared-app @@ -32,7 +32,7 @@ echo APP_ENV=prod > .env.local echo APP_DEBUG=0 >> .env.local # 删除测试和其他不需要的文件以节省空间 -# 或者,将这些文件添加到您的 .gitattributes 文件中,并设置 export-ignore 属性 +# 或者,在 .gitattributes 文件中使用 export-ignore 属性添加这些文件 rm -Rf tests/ # 安装依赖项 @@ -44,20 +44,19 @@ composer dump-env prod ### 自定义配置 -要自定义[配置](config.md),您可以放置一个 `Caddyfile` 以及一个 `php.ini` 文件 -在应用程序的主目录中嵌入(在之前的示例中是`$TMPDIR/my-prepared-app`)。 +要自定义[配置](config.md),您可以将 `Caddyfile` 和 `php.ini` 文件放置在要嵌入的应用程序的主目录中(在之前的示例中是 `$TMPDIR/my-prepared-app`)。 ## 创建 Linux 二进制文件 创建 Linux 二进制文件的最简单方法是使用我们提供的基于 Docker 的构建器。 -1. 在准备好的应用的存储库中创建一个名为 `static-build.Dockerfile` 的文件。 +1. 在你的应用程序的存储库中创建一个名为 `static-build.Dockerfile` 的文件: ```dockerfile FROM --platform=linux/amd64 dunglas/frankenphp:static-builder-gnu - # 如果你打算在 glibc 系统上运行该二进制文件,请使用 static-builder-gnu + # 如果你打算在 musl-libc 系统上运行该二进制文件,请改用 static-builder-musl - # 复制应用代码 + # 复制你的应用程序 WORKDIR /go/src/app/dist/app COPY . . @@ -77,7 +76,7 @@ composer dump-env prod docker build -t static-app -f static-build.Dockerfile . ``` -3. 提取二进制文件 +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 @@ -95,7 +94,7 @@ cd frankenphp EMBED=/path/to/your/app ./build-static.sh ``` -在 `dist/` 目录中生成的二进制文件名称为 `frankenphp--`。 +生成的二进制文件是 `dist/` 目录中名为 `frankenphp--` 的文件。 ## 使用二进制文件 @@ -138,7 +137,7 @@ EMBED=/path/to/your/app ./build-static.sh ## 分发二进制文件 -在Linux上,创建的二进制文件使用[UPX](https://upx.github.io)进行压缩。 +在 Linux 上,创建的二进制文件使用 [UPX](https://upx.github.io) 进行压缩。 -在Mac上,您可以在发送文件之前压缩它以减小文件大小。 +在 Mac 上,您可以在发送文件之前压缩它以减小文件大小。 我们推荐使用 `xz`。 diff --git a/docs/cn/extensions.md b/docs/cn/extensions.md index 4e2c120f17..d10f06dc89 100644 --- a/docs/cn/extensions.md +++ b/docs/cn/extensions.md @@ -1,6 +1,6 @@ # 使用 Go 编写 PHP 扩展 -使用 FrankenPHP,你可以**使用 Go 编写 PHP 扩展**,这允许你创建**高性能的原生函数**,可以直接从 PHP 调用。你的应用程序可以利用任何现有或新的 Go 库,以及直接从你的 PHP 代码中使用**协程(goroutines)的并发模型**。 +使用 FrankenPHP,你可以**使用 Go 编写 PHP 扩展**,这允许你创建**高性能的原生函数**,可以直接从 PHP 调用。你的应用程序可以利用任何现有或新的 Go 库,以及直接从你的 PHP 代码中使用**臭名昭著的协程(goroutines)并发模型**。 编写 PHP 扩展通常使用 C 语言完成,但通过一些额外的工作,也可以使用其他语言编写。PHP 扩展允许你利用底层语言的强大功能来扩展 PHP 的功能,例如,通过添加原生函数或优化特定操作。 @@ -8,10 +8,10 @@ ## 两种方法 -FrankenPHP 提供两种方式来创建 Go 语言的 PHP 扩展: +FrankenPHP 提供两种方式来**用 Go 创建 PHP 扩展**: -1. **使用扩展生成器** - 推荐的方法,为大多数用例生成所有必要的样板代码,让你专注于编写 Go 代码 -2. **手动实现** - 对于高级用例,完全控制扩展结构 +1. **使用扩展生成器** - 推荐的方法,为大多数用例生成所有必要的样板代码,让你专注于编写 Go 代码 +2. **手动实现** - 对于高级用例,完全控制扩展结构 我们将从生成器方法开始,因为这是最简单的入门方式,然后为那些需要完全控制的人展示手动实现。 @@ -33,7 +33,7 @@ FrankenPHP 捆绑了一个工具,允许你**仅使用 Go 创建 PHP 扩展** 在 Go 中编写 PHP 扩展的第一步是创建一个新的 Go 模块。你可以使用以下命令: ```console -go mod init github.com/my-account/my-module +go mod init example.com/example ``` 第二步是为后续步骤[获取 PHP 源代码](https://www.php.net/downloads.php)。获取后,将它们解压到你选择的目录中,不要放在你的 Go 模块内: @@ -47,10 +47,15 @@ tar xf php-* 现在一切都设置好了,可以在 Go 中编写你的原生函数。创建一个名为 `stringext.go` 的新文件。我们的第一个函数将接受一个字符串作为参数,重复次数,一个布尔值来指示是否反转字符串,并返回结果字符串。这应该看起来像这样: ```go +package example + +// #include +import "C" import ( - "C" - "github.com/dunglas/frankenphp" "strings" + "unsafe" + + "github.com/dunglas/frankenphp" ) //export_php:function repeat_this(string $str, int $count, bool $reverse): string @@ -72,8 +77,8 @@ func repeat_this(s *C.zend_string, count int64, reverse bool) unsafe.Pointer { 这里有两个重要的事情要注意: -- 指令注释 `//export_php:function` 定义了 PHP 中的函数签名。这是生成器知道如何使用正确的参数和返回类型生成 PHP 函数的方式; -- 函数必须返回 `unsafe.Pointer`。FrankenPHP 提供了一个 API 来帮助你在 C 和 Go 之间进行类型转换。 +- 指令注释 `//export_php:function` 定义了 PHP 中的函数签名。这是生成器知道如何使用正确的参数和返回类型生成 PHP 函数的方式; +- 函数必须返回 `unsafe.Pointer`。FrankenPHP 提供了一个 API 来帮助你在 C 和 Go 之间进行类型转换。 虽然第一点不言自明,但第二点可能更难理解。让我们在下一节中深入了解类型转换。 @@ -81,20 +86,24 @@ func repeat_this(s *C.zend_string, count int64, reverse bool) unsafe.Pointer { 虽然一些变量类型在 C/PHP 和 Go 之间具有相同的内存表示,但某些类型需要更多逻辑才能直接使用。这可能是编写扩展时最困难的部分,因为它需要了解 Zend 引擎的内部结构以及变量在 PHP 中的内部存储方式。此表总结了你需要知道的内容: -| PHP 类型 | Go 类型 | 直接转换 | C 到 Go 助手 | Go 到 C 助手 | 类方法支持 | -| ------------------ | ------------------- | -------- | --------------------- | ---------------------- | ---------- | -| `int` | `int64` | ✅ | - | - | ✅ | -| `?int` | `*int64` | ✅ | - | - | ✅ | -| `float` | `float64` | ✅ | - | - | ✅ | -| `?float` | `*float64` | ✅ | - | - | ✅ | -| `bool` | `bool` | ✅ | - | - | ✅ | -| `?bool` | `*bool` | ✅ | - | - | ✅ | -| `string`/`?string` | `*C.zend_string` | ❌ | frankenphp.GoString() | frankenphp.PHPString() | ✅ | -| `array` | `*frankenphp.Array` | ❌ | frankenphp.GoArray() | frankenphp.PHPArray() | ✅ | -| `mixed` | `any` | ❌ | `GoValue()` | `PHPValue()` | ❌ | -| `object` | `struct` | ❌ | _尚未实现_ | _尚未实现_ | ❌ | +| PHP 类型 | Go 类型 | 直接转换 | C 到 Go 助手 | Go 到 C 助手 | 类方法支持 | +| :----------------- | :---------------------------- | :------- | :-------------------------------- | :--------------------------------- | :--------- | +| `int` | `int64` | ✅ | - | - | ✅ | +| `?int` | `*int64` | ✅ | - | - | ✅ | +| `float` | `float64` | ✅ | - | - | ✅ | +| `?float` | `*float64` | ✅ | - | - | ✅ | +| `bool` | `bool` | ✅ | - | - | ✅ | +| `?bool` | `*bool` | ✅ | - | - | ✅ | +| `string`/`?string` | `*C.zend_string` | ❌ | `frankenphp.GoString()` | `frankenphp.PHPString()` | ✅ | +| `array` | `frankenphp.AssociativeArray` | ❌ | `frankenphp.GoAssociativeArray()` | `frankenphp.PHPAssociativeArray()` | ✅ | +| `array` | `map[string]any` | ❌ | `frankenphp.GoMap()` | `frankenphp.PHPMap()` | ✅ | +| `array` | `[]any` | ❌ | `frankenphp.GoPackedArray()` | `frankenphp.PHPPackedArray()` | ✅ | +| `mixed` | `any` | ❌ | `GoValue()` | `PHPValue()` | ❌ | +| `callable` | `*C.zval` | ❌ | - | `frankenphp.CallPHPCallable()` | ❌ | +| `object` | `struct` | ❌ | _尚未实现_ | _尚未实现_ | ❌ | > [!NOTE] +> > 此表尚不详尽,将随着 FrankenPHP 类型 API 变得更加完整而完善。 > > 特别是对于类方法,目前支持原始类型和数组。对象尚不能用作方法参数或返回类型。 @@ -103,65 +112,155 @@ func repeat_this(s *C.zend_string, count int64, reverse bool) unsafe.Pointer { #### 处理数组 -FrankenPHP 通过 `frankenphp.Array` 类型为 PHP 数组提供原生支持。此类型表示 PHP 索引数组(列表)和关联数组(哈希映射),具有有序的键值对。 +FrankenPHP 通过 `frankenphp.AssociativeArray` 或直接转换为 map 或 slice 来为 PHP 数组提供原生支持。 + +`AssociativeArray` 表示一个 [哈希映射](https://en.wikipedia.org/wiki/Hash_table),由一个 `Map: map[string]any` 字段和一个可选的 `Order: []string` 字段组成(与 PHP 的“关联数组”不同,Go map 是无序的)。 + +如果不需要顺序或关联,也可以直接转换为 slice `[]any` 或无序 map `map[string]any`。 **在 Go 中创建和操作数组:** ```go -//export_php:function process_data(array $input): array -func process_data(arr *C.zval) unsafe.Pointer { - // 将 PHP 数组转换为 Go - goArray := frankenphp.GoArray(unsafe.Pointer(arr)) - - result := &frankenphp.Array{} - - result.SetInt(0, "first") - result.SetInt(1, "second") - result.Append("third") // 自动分配下一个整数键 - - result.SetString("name", "John") - result.SetString("age", int64(30)) - - for i := uint32(0); i < goArray.Len(); i++ { - key, value := goArray.At(i) - if key.Type == frankenphp.PHPStringKey { - result.SetString("processed_"+key.Str, value) - } else { - result.SetInt(key.Int+100, value) - } +package example + +// #include +import "C" +import ( + "unsafe" + + "github.com/dunglas/frankenphp" +) + +// export_php:function process_data_ordered(array $input): array +func process_data_ordered_map(arr *C.zend_array) unsafe.Pointer { + // 将 PHP 关联数组转换为 Go,同时保留顺序 + associativeArray, err := frankenphp.GoAssociativeArray[any](unsafe.Pointer(arr)) + if err != nil { + // 处理错误 } - // 转换回 PHP 数组 - return frankenphp.PHPArray(result) + // 按顺序遍历条目 + for _, key := range associativeArray.Order { + value := associativeArray.Map[key] + // 处理键和值 + } + + // 返回一个有序数组 + // 如果 'Order' 不为空,将只尊重 'Order' 中的键值对 + return frankenphp.PHPAssociativeArray[string](frankenphp.AssociativeArray[string]{ + Map: map[string]string{ + "key1": "value1", + "key2": "value2", + }, + Order: []string{"key1", "key2"}, + }) +} + +// export_php:function process_data_unordered(array $input): array +func process_data_unordered_map(arr *C.zend_array) unsafe.Pointer { + // 将 PHP 关联数组转换为 Go map,不保留顺序 + // 忽略顺序会获得更好的性能 + goMap, err := frankenphp.GoMap[any](unsafe.Pointer(arr)) + if err != nil { + // 处理错误 + } + + // 以无特定顺序遍历条目 + for key, value := range goMap { + // 处理键和值 + } + + // 返回一个无序数组 + return frankenphp.PHPMap(map[string]string { + "key1": "value1", + "key2": "value2", + }) +} + +// export_php:function process_data_packed(array $input): array +func process_data_packed(arr *C.zend_array) unsafe.Pointer { + // 将 PHP 打包数组转换为 Go + goSlice, err := frankenphp.GoPackedArray(unsafe.Pointer(arr)) + if err != nil { + // 处理错误 + } + + // 按顺序遍历切片 + for index, value := range goSlice { + // 处理索引和值 + } + + // 返回一个打包数组 + return frankenphp.PHPPackedArray([]string{"value1", "value2", "value3"}) +} +``` + +**数组转换的关键特性:** + +- **有序键值对** - 可选择保留关联数组的顺序 +- **针对多种情况优化** - 可选择放弃顺序以获得更好的性能,或直接转换为切片 +- **自动列表检测** - 转换为 PHP 时,自动检测数组应该是打包列表还是哈希映射 +- **嵌套数组** - 数组可以嵌套,并将自动转换所有支持的类型(`int64`、`float64`、`string`、`bool`、`nil`、`AssociativeArray`、`map[string]any`、`[]any`) +- **不支持对象** - 目前,只有标量类型和数组可以用作值。提供对象将导致 PHP 数组中的 `null` 值。 + +##### 可用方法:打包和关联 + +- `frankenphp.PHPAssociativeArray(arr frankenphp.AssociativeArray) unsafe.Pointer` - 转换为带有键值对的有序 PHP 数组 +- `frankenphp.PHPMap(arr map[string]any) unsafe.Pointer` - 将 map 转换为带有键值对的无序 PHP 数组 +- `frankenphp.PHPPackedArray(slice []any) unsafe.Pointer` - 将 slice 转换为仅带有索引值的 PHP 打包数组 +- `frankenphp.GoAssociativeArray(arr unsafe.Pointer, ordered bool) frankenphp.AssociativeArray` - 将 PHP 数组转换为有序的 Go `AssociativeArray`(带有顺序的 map) +- `frankenphp.GoMap(arr unsafe.Pointer) map[string]any` - 将 PHP 数组转换为无序的 Go map +- `frankenphp.GoPackedArray(arr unsafe.Pointer) []any` - 将 PHP 数组转换为 Go slice +- `frankenphp.IsPacked(zval *C.zend_array) bool` - 检查 PHP 数组是打包(仅索引)还是关联(键值对) + +### 处理可调用对象 + +FrankenPHP 提供了一种使用 `frankenphp.CallPHPCallable` 助手来处理 PHP 可调用对象的方法。这允许你从 Go 代码中调用 PHP 函数或方法。 + +为了展示这一点,让我们创建自己的 `array_map()` 函数,它接受一个可调用对象和一个数组,将可调用对象应用于数组的每个元素,并返回一个包含结果的新数组: + +```go +// export_php:function my_array_map(array $data, callable $callback): array +func my_array_map(arr *C.zend_array, callback *C.zval) unsafe.Pointer { + goSlice, err := frankenphp.GoPackedArray[any](unsafe.Pointer(arr)) + if err != nil { + panic(err) + } + + result := make([]any, len(goSlice)) + + for index, value := range goSlice { + result[index] = frankenphp.CallPHPCallable(unsafe.Pointer(callback), []interface{}{value}) + } + + return frankenphp.PHPPackedArray(result) } ``` -**`frankenphp.Array` 的关键特性:** +请注意我们如何使用 `frankenphp.CallPHPCallable()` 来调用作为参数传递的 PHP 可调用对象。此函数接受一个指向可调用对象的指针和一个参数数组,并返回可调用对象执行的结果。你可以使用你习惯的可调用语法: -- **有序键值对** - 像 PHP 数组一样维护插入顺序 -- **混合键类型** - 在同一数组中支持整数和字符串键 -- **类型安全** - `PHPKey` 类型确保正确的键处理 -- **自动列表检测** - 转换为 PHP 时,自动检测数组应该是打包列表还是哈希映射 -- **不支持对象** - 目前,只有标量类型和数组可以用作值。提供对象将导致 PHP 数组中的 `null` 值。 +```php +name` 不起作用) -- **仅方法接口** - 所有交互必须通过你定义的方法进行 -- **更好的封装** - 内部数据结构完全由 Go 代码控制 -- **类型安全** - 没有 PHP 代码使用错误类型破坏内部状态的风险 -- **更清晰的 API** - 强制设计适当的公共接口 +- **无直接属性访问**:你不能直接从 PHP 读取或写入属性(`$user->name` 不起作用) +- **仅方法接口** - 所有交互必须通过你定义的方法进行 +- **更好的封装** - 内部数据结构完全由 Go 代码控制 +- **类型安全** - 没有 PHP 代码使用错误类型破坏内部状态的风险 +- **更清晰的 API** - 强制设计适当的公共接口 这种方法提供了更好的封装,并防止 PHP 代码意外破坏 Go 对象的内部状态。与对象的所有交互都必须通过你明确定义的方法进行。 @@ -182,10 +281,21 @@ type UserStruct struct { 由于属性不能直接访问,你**必须定义方法**来与不透明类交互。使用 `//export_php:method` 指令来定义行为: ```go +package example + +// #include +import "C" +import ( + "unsafe" + + "github.com/dunglas/frankenphp" +) + //export_php:class User type UserStruct struct { - Name string - Age int + Name string + Age int + Active bool // Added for the UpdateInfo example } //export_php:method User::getName(): string @@ -214,6 +324,16 @@ func (us *UserStruct) SetNamePrefix(prefix *C.zend_string) { 生成器支持在 PHP 签名中使用 `?` 前缀的可空参数。当参数可空时,它在你的 Go 函数中变成指针,允许你检查值在 PHP 中是否为 `null`: ```go +package example + +// #include +import "C" +import ( + "unsafe" + + "github.com/dunglas/frankenphp" +) + //export_php:method User::updateInfo(?string $name, ?int $age, ?bool $active): void func (us *UserStruct) UpdateInfo(name *C.zend_string, age *int64, active *bool) { // 检查是否提供了 name(不为 null) @@ -235,12 +355,13 @@ func (us *UserStruct) UpdateInfo(name *C.zend_string, age *int64, active *bool) **关于可空参数的要点:** -- **可空原始类型**(`?int`、`?float`、`?bool`)在 Go 中变成指针(`*int64`、`*float64`、`*bool`) -- **可空字符串**(`?string`)仍然是 `*C.zend_string`,但可以是 `nil` -- **在解引用指针值之前检查 `nil`** -- **PHP `null` 变成 Go `nil`** - 当 PHP 传递 `null` 时,你的 Go 函数接收 `nil` 指针 +- **可空原始类型**(`?int`、`?float`、`?bool`)在 Go 中变成指针(`*int64`、`*float64`、`*bool`) +- **可空字符串**(`?string`)仍然是 `*C.zend_string`,但可以是 `nil` +- **在解引用指针值之前检查 `nil`** +- **PHP `null` 变成 Go `nil`** - 当 PHP 传递 `null` 时,你的 Go 函数接收 `nil` 指针 > [!WARNING] +> > 目前,类方法有以下限制。**不支持对象**作为参数类型或返回类型。**完全支持数组**作为参数和返回类型。支持的类型:`string`、`int`、`float`、`bool`、`array` 和 `void`(用于返回类型)。**完全支持可空参数类型**,适用于所有标量类型(`?string`、`?int`、`?float`、`?bool`)。 生成扩展后,你将被允许在 PHP 中使用类及其方法。请注意,你**不能直接访问属性**: @@ -277,6 +398,8 @@ $user->updateInfo(null, 25, null); // Name 和 active 为 null 使用 `//export_php:const` 指令创建全局 PHP 常量: ```go +package example + //export_php:const const MAX_CONNECTIONS = 100 @@ -295,6 +418,8 @@ const STATUS_ERROR = iota 使用 `//export_php:classconst ClassName` 指令创建属于特定 PHP 类的常量: ```go +package example + //export_php:classconst User const STATUS_ACTIVE = 1 @@ -329,15 +454,20 @@ echo User::ROLE_ADMIN; // "admin" echo Order::STATE_PENDING; // 0 ``` -该指令支持各种值类型,包括字符串、整数、布尔值、浮点数和 iota 常量。使用 `iota` 时,生成器自动分配顺序值(0、1、2 等)。全局常量在你的 PHP 代码中作为全局常量可用,而类常量使用公共可见性限定在各自的类中。使用整数时,支持不同的可能记法(二进制、十六进制、八进制)并在 PHP 存根文件中按原样转储。 +该指令支持各种值类型,包括字符串、整数、布尔值、浮点数和 iota 常量。使用 `iota` 时,生成器自动分配顺序值(0、1、2 等)。全局常量在你的 PHP 代码中作为全局常量可用,而类常量使用公共可见性限定在各自的类中。使用整数时,不同的可能记法(二进制、十六进制、八进制)都受支持,并在 PHP 存根文件中按原样转储。 你可以像在 Go 代码中习惯的那样使用常量。例如,让我们采用我们之前声明的 `repeat_this()` 函数,并将最后一个参数更改为整数: ```go +package example + +// #include +import "C" import ( - "C" - "github.com/dunglas/frankenphp" - "strings" + "strings" + "unsafe" + + "github.com/dunglas/frankenphp" ) //export_php:const @@ -354,37 +484,37 @@ const MODE_UPPERCASE = 2 //export_php:function repeat_this(string $str, int $count, int $mode): string func repeat_this(s *C.zend_string, count int64, mode int) unsafe.Pointer { - str := frankenphp.GoString(unsafe.Pointer(s)) + str := frankenphp.GoString(unsafe.Pointer(s)) - result := strings.Repeat(str, int(count)) - if mode == STR_REVERSE { - // 反转字符串 - } + result := strings.Repeat(str, int(count)) + if mode == STR_REVERSE { + // 反转字符串 + } - if mode == STR_NORMAL { - // 无操作,只是为了展示常量 - } + if mode == STR_NORMAL { + // 无操作,只是为了展示常量 + } - return frankenphp.PHPString(result, false) + return frankenphp.PHPString(result, false) } //export_php:class StringProcessor type StringProcessorStruct struct { - // 内部字段 + // internal fields } //export_php:method StringProcessor::process(string $input, int $mode): string func (sp *StringProcessorStruct) Process(input *C.zend_string, mode int64) unsafe.Pointer { - str := frankenphp.GoString(unsafe.Pointer(input)) + str := frankenphp.GoString(unsafe.Pointer(input)) - switch mode { - case MODE_LOWERCASE: - str = strings.ToLower(str) - case MODE_UPPERCASE: - str = strings.ToUpper(str) - } + switch mode { + case MODE_LOWERCASE: + str = strings.ToLower(str) + case MODE_UPPERCASE: + str = strings.ToUpper(str) + } - return frankenphp.PHPString(str, false) + return frankenphp.PHPString(str, false) } ``` @@ -398,9 +528,13 @@ func (sp *StringProcessorStruct) Process(input *C.zend_string, mode int64) unsaf ```go //export_php:namespace My\Extension -package main +package example -import "C" +import ( + "unsafe" + + "github.com/dunglas/frankenphp" +) //export_php:function hello(): string func hello() string { @@ -409,7 +543,7 @@ func hello() string { //export_php:class User type UserStruct struct { - // 内部字段 + // internal fields } //export_php:method User::getName(): string @@ -438,10 +572,10 @@ echo My\Extension\STATUS_ACTIVE; // 1 #### 重要说明 -- 每个文件只允许**一个**命名空间指令。如果找到多个命名空间指令,生成器将返回错误。 -- 命名空间适用于文件中的**所有**导出符号:函数、类、方法和常量。 -- 命名空间名称遵循 PHP 命名空间约定,使用反斜杠(`\`)作为分隔符。 -- 如果没有声明命名空间,符号将照常导出到全局命名空间。 +- 每个文件只允许**一个**命名空间指令。如果找到多个命名空间指令,生成器将返回错误。 +- 命名空间适用于文件中的**所有**导出符号:函数、类、方法和常量。 +- 命名空间名称遵循 PHP 命名空间约定,使用反斜杠(`\`)作为分隔符。 +- 如果没有声明命名空间,符号将照常导出到全局命名空间。 ### 生成扩展 @@ -503,25 +637,26 @@ echo $processor->process('Hello World', StringProcessor::MODE_UPPERCASE); // "H 在你的模块中,你需要定义一个新的原生函数,该函数将从 PHP 调用。为此,创建一个你想要的名称的文件,例如 `extension.go`,并添加以下代码: ```go -package ext_go +package example -//#include "extension.h" +// #include "extension.h" import "C" import ( - "unsafe" - "github.com/caddyserver/caddy/v2" - "github.com/dunglas/frankenphp" + "log/slog" + "unsafe" + + "github.com/dunglas/frankenphp" ) func init() { - frankenphp.RegisterExtension(unsafe.Pointer(&C.ext_module_entry)) + frankenphp.RegisterExtension(unsafe.Pointer(&C.ext_module_entry)) } //export go_print_something func go_print_something() { - go func() { - caddy.Log().Info("Hello from a goroutine!") - }() + go func() { + slog.Info("Hello from a goroutine!") + }() } ``` @@ -568,9 +703,9 @@ extern zend_module_entry ext_module_entry; 接下来,创建一个名为 `extension.c` 的文件,该文件将执行以下步骤: -- 包含 PHP 头文件; -- 声明我们的新原生 PHP 函数 `go_print()`; -- 声明扩展元数据。 +- 包含 PHP 头文件; +- 声明我们的新原生 PHP 函数 `go_print()`; +- 声明扩展元数据。 让我们首先包含所需的头文件: @@ -697,7 +832,16 @@ PHP_FUNCTION(go_upper) 我们的 Go 函数将接受 `*C.zend_string` 作为参数,使用 FrankenPHP 的助手函数将其转换为 Go 字符串,处理它,并将结果作为新的 `*C.zend_string` 返回。助手函数为我们处理所有内存管理和转换复杂性。 ```go -import "strings" +package example + +// #include +import "C" +import ( + "unsafe" + "strings" + + "github.com/dunglas/frankenphp" +) //export go_upper func go_upper(s *C.zend_string) *C.zend_string { @@ -712,6 +856,7 @@ func go_upper(s *C.zend_string) *C.zend_string { 这种方法比手动内存管理更清洁、更安全。FrankenPHP 的助手函数自动处理 PHP 的 `zend_string` 格式和 Go 字符串之间的转换。`PHPString()` 中的 `false` 参数表示我们想要创建一个新的非持久字符串(在请求结束时释放)。 > [!TIP] +> > 在此示例中,我们不执行任何错误处理,但你应该始终检查指针不是 `nil` 并且数据在 Go 函数中使用之前是有效的。 ### 将扩展集成到 FrankenPHP 中 diff --git a/docs/cn/github-actions.md b/docs/cn/github-actions.md index 04f5cbbd61..8a411b9a77 100644 --- a/docs/cn/github-actions.md +++ b/docs/cn/github-actions.md @@ -1,31 +1,30 @@ # 使用 GitHub Actions -此存储库构建 Docker 镜像并将其部署到 [Docker Hub](https://hub.docker.com/r/dunglas/frankenphp) 上 -每个批准的拉取请求或设置后在你自己的分支上。 +此存储库会在每次获得批准的拉取请求或在您自己的 fork 配置完成后,构建 Docker 镜像并将其部署到 [Docker Hub](https://hub.docker.com/r/dunglas/frankenphp)。 ## 设置 GitHub Actions -在存储库设置中的 `secrets` 下,添加以下字段: +在存储库设置的 `Secrets` (密钥) 下,添加以下密钥: -- `REGISTRY_LOGIN_SERVER`: 要使用的 Docker registry(如 `docker.io`)。 -- `REGISTRY_USERNAME`: 用于登录 registry 的用户名(如 `dunglas`)。 -- `REGISTRY_PASSWORD`: 用于登录 registry 的密码(如 `access key`)。 -- `IMAGE_NAME`: 镜像的名称(如 `dunglas/frankenphp`)。 +- `REGISTRY_LOGIN_SERVER`: 要使用的 Docker registry(例如 `docker.io`)。 +- `REGISTRY_USERNAME`: 用于登录 registry 的用户名(例如 `dunglas`)。 +- `REGISTRY_PASSWORD`: 用于登录 registry 的密码(例如,访问密钥)。 +- `IMAGE_NAME`: 镜像的名称(例如 `dunglas/frankenphp`)。 ## 构建和推送镜像 -1. 创建 Pull Request 或推送到你的 Fork 分支。 -2. GitHub Actions 将生成镜像并运行每项测试。 -3. 如果生成成功,则将使用 `pr-x` 推送 registry,其中 `x` 是 PR 编号,作为标记将镜像推送到注册表。 +1. 创建 Pull Request 或推送到您的 Fork 分支。 +2. GitHub Actions 将构建镜像并运行所有测试。 +3. 如果构建成功,镜像将使用 `pr-x`(其中 `x` 是 PR 编号)作为标签推送到注册表。 ## 部署镜像 -1. 合并 Pull Request 后,GitHub Actions 将再次运行测试并生成新镜像。 -2. 如果构建成功,则 Docker 注册表中的 `main` tag 将更新。 +1. Pull Request 合并后,GitHub Actions 将再次运行测试并构建新的镜像。 +2. 如果构建成功,Docker 注册表中的 `main` 标签将被更新。 ## 发布 -1. 在项目仓库中创建新 Tag。 -2. GitHub Actions 将生成镜像并运行每项测试。 -3. 如果构建成功,镜像将使用标记名称作为标记推送到 registry(例如,将创建 `v1.2.3` 和 `v1.2`)。 -4. `latest` 标签也将更新。 +1. 在仓库中创建新标签。 +2. GitHub Actions 将构建镜像并运行所有测试。 +3. 如果构建成功,镜像将使用标签名称作为标签推送到注册表(例如,将创建 `v1.2.3` 和 `v1.2`)。 +4. `latest` 标签也将被更新。 diff --git a/docs/cn/hot-reload.md b/docs/cn/hot-reload.md new file mode 100644 index 0000000000..371685758b --- /dev/null +++ b/docs/cn/hot-reload.md @@ -0,0 +1,139 @@ +# 热重载 + +FrankenPHP 内置的**热重载**功能旨在极大改善开发者体验。 + +![Mercure](hot-reload.png) + +此功能提供类似于现代 JavaScript 工具(如 Vite 或 webpack)中**模块热替换 (HMR)** 的工作流。 +开发者无需在每次文件更改(PHP 代码、模板、JavaScript 和 CSS 文件...)后手动刷新浏览器, +FrankenPHP 会实时更新内容。 + +热重载原生支持 WordPress、Laravel、Symfony 以及任何其他 PHP 应用或框架。 + +启用后,FrankenPHP 会监视当前工作目录的文件系统更改。 +当文件被修改时,它会将 [Mercure](mercure.md) 更新推送到浏览器。 + +根据您的设置,浏览器将: + +- 如果加载了 [Idiomorph](https://github.com/bigskysoftware/idiomorph),则**修改 DOM**(保留滚动位置和输入状态)。 +- 如果没有 Idiomorph,则**重新加载页面**(标准实时重载)。 + +## 配置 + +要启用热重载,请先启用 Mercure,然后在 `Caddyfile` 中的 `php_server` 指令中添加 `hot_reload` 子指令。 + +> [!WARNING] +> 此功能仅适用于**开发环境**。 +> 请勿在生产环境中启用 `hot_reload`,因为监视文件系统会带来性能开销并暴露内部端点。 + +```caddyfile +localhost + +mercure { + anonymous +} + +root public/ +php_server { + hot_reload +} +``` + +默认情况下,FrankenPHP 将监视当前工作目录中匹配以下全局模式的所有文件:`./**/*.{css,env,gif,htm,html,jpg,jpeg,js,mjs,php,png,svg,twig,webp,xml,yaml,yml}` + +可以通过全局语法明确设置要监视的文件: + +```caddyfile +localhost + +mercure { + anonymous +} + +root public/ +php_server { + hot_reload src/**/*{.php,.js} config/**/*.yaml +} +``` + +使用长格式来指定要使用的 Mercure 主题以及通过向 `hot_reload` 选项提供路径来监视哪些目录或文件: + +```caddyfile +localhost + +mercure { + anonymous +} + +root public/ +php_server { + hot_reload { + topic hot-reload-topic + watch src/**/*.php + watch assets/**/*.{ts,json} + watch templates/ + watch public/css/ + } +} +``` + +## 客户端集成 + +虽然服务器检测到更改,但浏览器需要订阅这些事件才能更新页面。 +FrankenPHP 通过 `$_SERVER['FRANKENPHP_HOT_RELOAD']` 环境变量暴露 Mercure Hub URL,用于订阅文件更改。 + +还提供了一个便利的 JavaScript 库 [frankenphp-hot-reload](https://www.npmjs.com/package/frankenphp-hot-reload) 来处理客户端逻辑。 +要使用它,请将以下内容添加到您的主布局中: + +```php + +FrankenPHP Hot Reload + + + + + +``` + +该库将自动订阅 Mercure hub,当检测到文件更改时在后台获取当前 URL 并修改 DOM。 +它作为一个 [npm](https://www.npmjs.com/package/frankenphp-hot-reload) 包和在 [GitHub](https://github.com/dunglas/frankenphp-hot-reload) 上可用。 + +或者,您可以通过使用 `EventSource` 原生 JavaScript 类直接订阅 Mercure hub 来实现自己的客户端逻辑。 + +### Worker 模式 + +如果您在 [Worker 模式](https://frankenphp.dev/docs/worker/)下运行您的应用程序,您的应用程序脚本会保留在内存中。 +这意味着即使浏览器重新加载,您对 PHP 代码的更改也不会立即反映。 + +为了获得最佳的开发体验,您应该将 `hot_reload` 与 [worker 指令中的 `watch` 子指令](config.md#watching-for-file-changes)结合使用。 + +- `hot_reload`:当文件更改时刷新**浏览器** +- `worker.watch`:当文件更改时重启 worker + +```caddy +localhost + +mercure { + anonymous +} + +root public/ +php_server { + hot_reload + worker { + file /path/to/my_worker.php + watch + } +} +``` + +### 工作原理 + +1. **监视**:FrankenPHP 使用底层 [e-dant/watcher 库](https://github.com/e-dant/watcher)监视文件系统修改(我们贡献了 Go 绑定)。 +2. **重启 (Worker 模式)**:如果在 worker 配置中启用了 `watch`,PHP worker 将重新启动以加载新代码。 +3. **推送**:包含更改文件列表的 JSON 有效载荷被发送到内置的 [Mercure hub](https://mercure.rocks)。 +4. **接收**:浏览器通过 JavaScript 库监听,接收 Mercure 事件。 +5. **更新**: + + - 如果检测到 **Idiomorph**,它会获取更新的内容并修改当前的 HTML 以匹配新状态,即时应用更改而不会丢失状态。 + - 否则,将调用 `window.location.reload()` 以刷新页面。 diff --git a/docs/cn/known-issues.md b/docs/cn/known-issues.md index a2bc0f8fb6..3105b51152 100644 --- a/docs/cn/known-issues.md +++ b/docs/cn/known-issues.md @@ -4,26 +4,28 @@ 已知以下扩展与 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/) | 不安全的线程 | - | +| 名称 | 原因 | 替代方案 | +| ----------------------------------------------------------------------------------------------------------- | -------------- | -------------------------------------------------------------------------------------------------------------------- | +| [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)。 | +| 名称 | 问题 | +| ------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| [ext-openssl](https://www.php.net/manual/en/book.openssl.php) | 在使用 musl libc 时,OpenSSL 扩展在重负载下可能会崩溃。在使用更流行的 GNU libc 时,不会出现此问题。此错误正在由 PHP 跟踪。[查看问题](https://github.com/php/php-src/issues/13648)。 | ## get_browser -[get_browser()](https://www.php.net/manual/en/function.get-browser.php) 函数在一段时间后似乎表现不佳。解决方法是缓存(例如使用 [APCu](https://www.php.net/manual/zh/book.apcu.php))每个 User-Agent,因为它们是不变的。 +[get_browser()](https://www.php.net/manual/en/function.get-browser.php) 函数在一段时间后似乎表现不佳。解决方法是缓存(例如使用 [APCu](https://www.php.net/manual/en/book.apcu.php))每个 User Agent 的结果,因为它们是静态的。 -## 独立的二进制和基于 Alpine 的 Docker 镜像 +## 独立二进制和基于 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)。 +独立二进制和基于 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)。 + +如果遇到问题,请优先使用 GNU 变体的静态二进制文件和基于 Debian 的 Docker 镜像。 ## 在 Docker 中使用 `https://127.0.0.1` @@ -32,10 +34,10 @@ 如果确实想使用 `127.0.0.1` 作为主机,可以通过将服务器名称设置为 `127.0.0.1` 来配置它以为其生成证书。 -如果你使用 Docker,因为 [Docker 网络](https://docs.docker.com/network/) 问题,只做这些是不够的。 +不幸的是,在使用 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/): +如果你使用的是 Linux,解决方案是使用[主机网络驱动](https://docs.docker.com/network/network-tutorial-host/): ```console docker run \ @@ -45,9 +47,9 @@ docker run \ dunglas/frankenphp ``` -Mac 和 Windows 不支持 Docker 使用宿主机网络。在这些平台上,你必须猜测容器的 IP 地址并将其包含在服务器名称中。 +主机网络驱动程序在 Mac 和 Windows 上不受支持。在这些平台上,你必须猜测容器的 IP 地址并将其包含在服务器名称中。 -运行 `docker network inspect bridge` 并查看 `Containers`,找到 `IPv4Address` 当前分配的最后一个 IP 地址,并增加 1。如果没有容器正在运行,则第一个分配的 IP 地址通常为 `172.17.0.2`。 +运行 `docker network inspect bridge` 并查看 `Containers` 键,以找到 `IPv4Address` 键下当前分配的最后一个 IP 地址,并将其增加一。如果没有容器正在运行,则第一个分配的 IP 地址通常为 `172.17.0.2`。 然后将其包含在 `SERVER_NAME` 环境变量中: @@ -108,9 +110,9 @@ export PHP_BINARY=/usr/local/bin/php composer install ``` -## 使用静态二进制文件排查 TLS/SSL 问题 +## 排查静态二进制文件的 TLS/SSL 问题 -在使用静态二进制文件时,您可能会遇到以下与TLS相关的错误,例如在使用STARTTLS发送电子邮件时: +在使用静态二进制文件时,您可能会遇到以下与 TLS 相关的错误,例如在使用 STARTTLS 发送电子邮件时: ```text Unable to connect with STARTTLS: stream_socket_enable_crypto(): SSL operation failed with code 5. OpenSSL Error messages: @@ -127,10 +129,10 @@ error:0A000086:SSL routines::certificate verify failed > [!WARNING] > -> Web 和命令行界面可能有不同的设置。 -> 确保在适当的上下文中运行 `openssl_get_cert_locations()`。 +> Web 和 CLI 上下文可能有不同的设置。 +> 请务必在适当的上下文中运行 `openssl_get_cert_locations()`。 -[从Mozilla提取的CA证书可以在curl网站上下载](https://curl.se/docs/caextract.html)。 +[从 Mozilla 提取的 CA 证书可以在 cURL 网站上下载](https://curl.se/docs/caextract.html)。 或者,许多发行版,包括 Debian、Ubuntu 和 Alpine,提供名为 `ca-certificates` 的软件包,其中包含这些证书。 diff --git a/docs/cn/laravel.md b/docs/cn/laravel.md index 07fa646ab5..7d98b6b6f2 100644 --- a/docs/cn/laravel.md +++ b/docs/cn/laravel.md @@ -16,7 +16,7 @@ docker run -p 80:80 -p 443:443 -p 443:443/udp -v $PWD:/app dunglas/frankenphp 或者,你可以从本地机器上使用 FrankenPHP 运行 Laravel 项目: -1. [下载与你的系统相对应的二进制文件](https://github.com/php/frankenphp/releases) +1. [下载与您的系统相对应的二进制文件](../#standalone-binary) 2. 将以下配置添加到 Laravel 项目根目录中名为 `Caddyfile` 的文件中: ```caddyfile @@ -30,7 +30,7 @@ docker run -p 80:80 -p 443:443 -p 443:443/udp -v $PWD:/app dunglas/frankenphp root public/ # 启用压缩(可选) encode zstd br gzip - # 执行当前目录中的 PHP 文件并提供资源 + # 从 public/ 目录执行 PHP 文件并提供静态文件 php_server { try_files {path} index.php } @@ -65,20 +65,22 @@ php artisan octane:frankenphp - `--port`: 服务器应可用的端口(默认值: `8000`) - `--admin-port`: 管理服务器应可用的端口(默认值: `2019`) - `--workers`: 应可用于处理请求的 worker 数(默认值: `auto`) -- `--max-requests`: 在 worker 重启之前要处理的请求数(默认值: `500`) +- `--max-requests`: 在服务器重新加载前处理的请求数量(默认值: `500`) - `--caddyfile`:FrankenPHP `Caddyfile` 文件的路径(默认: [Laravel Octane 中的存根 `Caddyfile`](https://github.com/laravel/octane/blob/2.x/src/Commands/stubs/Caddyfile)) -- `--https`: 开启 HTTPS、HTTP/2 和 HTTP/3,自动生成和延长证书 +- `--https`: 开启 HTTPS、HTTP/2 和 HTTP/3,自动生成和续订证书 - `--http-redirect`: 启用 HTTP 到 HTTPS 重定向(仅在使用 `--https` 时启用) - `--watch`: 修改应用程序时自动重新加载服务器 - `--poll`: 在监视时使用文件系统轮询,以便通过网络监视文件 -- `--log-level`: 在指定日志级别或高于指定日志级别的日志消息 +- `--log-level`: 使用原生 Caddy 日志记录器,记录指定日志级别或更高级别的消息 > [!TIP] > 要获取结构化的 JSON 日志(在使用日志分析解决方案时非常有用),请明确传递 `--log-level` 选项。 -你可以了解更多关于 [Laravel Octane 官方文档](https://laravel.com/docs/octane)。 +另请参阅 [如何在 Octane 中使用 Mercure](#mercure-support)。 -## Laravel 应用程序作为独立的可执行文件 +在 [Laravel Octane 官方文档](https://laravel.com/docs/octane) 中了解更多信息。 + +## Laravel 应用程序作为独立二进制文件 使用[FrankenPHP 的应用嵌入功能](embed.md),可以将 Laravel 应用程序作为 独立的二进制文件分发。 @@ -167,11 +169,39 @@ php artisan octane:frankenphp 设置 `LARAVEL_STORAGE_PATH` 环境变量(例如,在 `.env` 文件中)或调用 `Illuminate\Foundation\Application::useStoragePath()` 方法以使用临时目录之外的目录。 +### Mercure 支持 + +[Mercure](https://mercure.rocks) 是为您的 Laravel 应用程序添加实时功能的绝佳方式。 +FrankenPHP [开箱即用地支持 Mercure](mercure.md)。 + +如果您未使用 [Octane](#laravel-octane),请参阅 [Mercure 文档条目](mercure.md)。 + +如果您正在使用 Octane,可以通过在您的 `config/octane.php` 文件中添加以下行来启用 Mercure 支持: + +```php +// ... + +return [ + // ... + + 'mercure' => [ + 'anonymous' => true, + 'publisher_jwt' => '!ChangeThisMercureHubJWTSecretKey!', + 'subscriber_jwt' => '!ChangeThisMercureHubJWTSecretKey!', + ], +]; +``` + +您可以在此数组中使用 [Mercure 支持的所有指令](https://mercure.rocks/docs/hub/config#directives)。 + +要发布和订阅更新,我们推荐使用 [Laravel Mercure Broadcaster](https://github.com/mvanduijker/laravel-mercure-broadcaster) 库。 +或者,请参阅 [Mercure 文档](mercure.md) 以纯 PHP 和 JavaScript 实现。 + ### 使用独立二进制文件运行 Octane 甚至可以将 Laravel Octane 应用打包为独立的二进制文件! -为此,[正确安装 Octane](#laravel-octane) 并遵循 [前一部分](#laravel-应用程序作为独立的可执行文件) 中描述的步骤。 +为此,[正确安装 Octane](#laravel-octane) 并遵循 [独立二进制文件形式的 Laravel 应用程序](#laravel-apps-as-standalone-binaries) 中描述的步骤。 然后,通过 Octane 在工作模式下启动 FrankenPHP,运行: diff --git a/docs/cn/logging.md b/docs/cn/logging.md new file mode 100644 index 0000000000..989d93ddae --- /dev/null +++ b/docs/cn/logging.md @@ -0,0 +1,66 @@ +# 日志 + +FrankenPHP 与 [Caddy 的日志系统](https://caddyserver.com/docs/logging)无缝集成。 +您可以使用标准的 PHP 函数记录消息,也可以利用专用的 `frankenphp_log()` 函数进行高级结构化日志记录。 + +## `frankenphp_log()` + +`frankenphp_log()` 函数允许您直接从 PHP 应用程序发出结构化日志,从而更容易地将其摄取到 Datadog、Grafana Loki 或 Elastic 等平台中,并支持 OpenTelemetry。 + +在底层,`frankenphp_log()` 封装了 [Go 的 `log/slog` 包](https://pkg.go.dev/log/slog)以提供丰富的日志功能。 + +这些日志包括严重性级别和可选的上下文数据。 + +```php +function frankenphp_log(string $message, int $level = FRANKENPHP_LOG_LEVEL_INFO, array $context = []): void +``` + +### 参数 + +- **`message`**: 日志消息字符串。 +- **`level`**: 日志的严重性级别。可以是任意整数。提供了用于常见级别的便捷常量:`FRANKENPHP_LOG_LEVEL_DEBUG` (`-4`)、`FRANKENPHP_LOG_LEVEL_INFO` (`0`)、`FRANKENPHP_LOG_LEVEL_WARN` (`4`) 和 `FRANKENPHP_LOG_LEVEL_ERROR` (`8`)。默认值为 `FRANKENPHP_LOG_LEVEL_INFO`。 +- **`context`**: 一个关联数组,包含要包含在日志条目中的附加数据。 + +### 示例 + +```php + memory_get_usage(), + 'peak_usage' => memory_get_peak_usage(), + ], +); + +``` + +当查看日志时(例如,通过 `docker compose logs`),输出将显示为结构化 JSON: + +```json +{"level":"info","ts":1704067200,"logger":"frankenphp","msg":"Hello from FrankenPHP!"} +{"level":"warn","ts":1704067200,"logger":"frankenphp","msg":"Memory usage high","current_usage":10485760,"peak_usage":12582912} +``` + +## `error_log()` + +FrankenPHP 也允许使用标准的 `error_log()` 函数进行日志记录。如果 `$message_type` 参数为 `4` (SAPI),这些消息将被路由到 Caddy 日志记录器。 + +默认情况下,通过 `error_log()` 发送的消息被视为非结构化文本。它们对于与依赖标准 PHP 库的现有应用程序或库的兼容性非常有用。 + +### `error_log()` 示例 + +```php +error_log("Database connection failed", 4); +``` + +这将在 Caddy 日志中显示,通常带有前缀以表明它源自 PHP。 + +> [!TIP] +> 为了在生产环境中获得更好的可观测性,建议优先使用 `frankenphp_log()`,因为它允许您按级别(调试、错误等)过滤日志并在您的日志基础设施中查询特定字段。 diff --git a/docs/cn/mercure.md b/docs/cn/mercure.md index d51c9eb818..76575efd3b 100644 --- a/docs/cn/mercure.md +++ b/docs/cn/mercure.md @@ -1,15 +1,148 @@ # 实时 -FrankenPHP 配备了内置的 [Mercure](https://mercure.rocks) 中心! -Mercure 允许将事件实时推送到所有连接的设备:它们将立即收到 JavaScript 事件。 +FrankenPHP 内置了 [Mercure](https://mercure.rocks) 中心! +Mercure 允许你将实时事件推送到所有连接的设备:它们将立即收到 JavaScript 事件。 -无需 JS 库或 SDK! +它是 WebSockets 的便捷替代方案,使用简单,并原生支持所有现代网络浏览器! -![Mercure](../mercure-hub.png) +![Mercure](mercure-hub.png) -要启用 Mercure Hub,请按照 [Mercure 网站](https://mercure.rocks/docs/hub/config) 中的说明更新 `Caddyfile`。 +## 启用 Mercure -Mercure hub 的路径是`/.well-known/mercure`. -在 Docker 中运行 FrankenPHP 时,完整的发送 URL 将类似于 `http://php/.well-known/mercure` (其中 `php` 是运行 FrankenPHP 的容器名称)。 +Mercure 支持默认是禁用的。 +这是一个启用 FrankenPHP 和 Mercure 中心的 `Caddyfile` 最小示例: -要从你的代码中推送 Mercure 更新,我们推荐 [Symfony Mercure Component](https://symfony.com/components/Mercure)(不需要 Symfony 框架来使用)。 +```caddyfile +# 要响应的主机名 +localhost + +mercure { + # 用于签署发布者 JWT 令牌的密钥 + publisher_jwt !ChangeThisMercureHubJWTSecretKey! + # 允许匿名订阅者(无需 JWT) + anonymous +} + +root public/ +php_server +``` + +> [!TIP] +> +> [Docker 镜像](docker.md) 提供的 [示例 `Caddyfile`](https://github.com/php/frankenphp/blob/main/caddy/frankenphp/Caddyfile) +> 已经包含一个被注释掉的 Mercure 配置,其中带有方便的环境变量来配置它。 +> +> 取消注释 `/etc/frankenphp/Caddyfile` 中的 Mercure 部分即可启用它。 + +## 订阅更新 + +默认情况下,Mercure 中心在你的 FrankenPHP 服务器的 `/.well-known/mercure` 路径上可用。 +要订阅更新,请使用原生的 [`EventSource`](https://developer.mozilla.org/docs/Web/API/EventSource) JavaScript 类: + +```html + + +Mercure 示例 + +``` + +## 发布更新 + +### 使用 `mercure_publish()` + +FrankenPHP 提供了一个方便的 `mercure_publish()` 函数来将更新发布到内置的 Mercure 中心: + +```php + 'value'])); + +// 写入 FrankenPHP 的日志 +error_log("update $updateID published", 4); +``` + +完整的函数签名是: + +```php +/** + * @param string|string[] $topics + */ +function mercure_publish(string|array $topics, string $data = '', bool $private = false, ?string $id = null, ?string $type = null, ?int $retry = null): string {} +``` + +### 使用 `file_get_contents()` + +要将更新分发给连接的订阅者,请向 Mercure 中心发送一个带 `topic` 和 `data` 参数的认证 POST 请求: + +```php + [ + 'method' => 'POST', + 'header' => "Content-type: application/x-www-form-urlencoded\r\nAuthorization: Bearer " . JWT, + 'content' => http_build_query([ + 'topic' => 'my-topic', + 'data' => json_encode(['key' => 'value']), + ]), +]])); + +// 写入 FrankenPHP 的日志 +error_log("update $updateID published", 4); +``` + +在 `Caddyfile` 中作为 `mercure.publisher_jwt` 选项参数传入的密钥必须用于签署 `Authorization` 头中使用的 JWT 令牌。 + +JWT 必须包含一个 `mercure` 声明,其中包含你希望发布到的 topic 的 `publish` 权限。 +有关授权,请参阅 [Mercure 文档](https://mercure.rocks/spec#publishers)。 + +要生成自己的令牌,你可以使用 [这个 jwt.io 链接](https://www.jwt.io/#token=eyJhbGciOiJIUzI1NiJ9.eyJtZXJjdXJlIjp7InB1Ymxpc2giOlsiKiJdfX0.PXwpfIGng6KObfZlcOXvcnWCJOWTFLtswGI5DZuWSK4), +但对于生产应用程序,建议使用受信任的 [JWT 库](https://www.jwt.io/libraries?programming_language=php) 动态生成的短期令牌。 + +### 使用 Symfony Mercure + +或者,你可以使用 [Symfony Mercure Component](https://symfony.com/components/Mercure),这是一个独立的 PHP 库。 + +该库处理 JWT 生成、更新发布以及订阅者的基于 cookie 的授权。 + +首先,使用 Composer 安装该库: + +```console +composer require symfony/mercure lcobucci/jwt +``` + +然后,你可以像这样使用它: + +```php +publish(new \Symfony\Component\Mercure\Update('my-topic', json_encode(['key' => 'value']))); + +// 写入 FrankenPHP 的日志 +error_log("update $updateID published", 4); +``` + +Mercure 也被以下框架原生支持: + +- [Laravel](laravel.md#mercure-support) +- [Symfony](https://symfony.com/doc/current/mercure.html) +- [API Platform](https://api-platform.com/docs/core/mercure/) diff --git a/docs/cn/performance.md b/docs/cn/performance.md index ee11bb07e9..64628a6e98 100644 --- a/docs/cn/performance.md +++ b/docs/cn/performance.md @@ -41,11 +41,9 @@ 另外,[一些错误只在使用 musl 时发生](https://github.com/php/php-src/issues?q=sort%3Aupdated-desc+is%3Aissue+is%3Aopen+label%3ABug+musl)。 -在生产环境中,我们建议使用链接到 glibc 的 FrankenPHP。 +在生产环境中,我们建议使用链接到 glibc,并以适当优化级别编译的 FrankenPHP。 -这可以通过使用 Debian Docker 镜像(默认)、从我们的 [Releases](https://github.com/php/frankenphp/releases) 下载 -gnu 后缀二进制文件,或通过[从源代码编译 FrankenPHP](compile.md) 来实现。 - -或者,我们提供使用 [mimalloc 分配器](https://github.com/microsoft/mimalloc) 编译的静态 musl 二进制文件,这缓解了线程场景中的问题。 +这可以通过使用 Debian Docker 镜像、使用我们的维护者提供的 [.deb](https://debs.henderkes.com) 或 [.rpm](https://rpms.henderkes.com) 包,或通过[从源代码编译 FrankenPHP](compile.md) 来实现。 ## Go 运行时配置 @@ -155,3 +153,32 @@ FrankenPHP 使用官方 PHP 解释器。 有关更多详细信息,请阅读[专门的 Symfony 文档条目](https://symfony.com/doc/current/performance.html) (即使你不使用 Symfony,大多数提示也很有用)。 + +## 分割线程池 + +应用程序通常会与缓慢的外部服务交互,例如在高负载下容易变得不可靠或持续需要 10 秒以上才能响应的 API。 +在这种情况下,将线程池拆分以拥有专用的“慢速”池可能会很有益。这可以防止慢速端点耗尽所有服务器资源/线程,并限制指向慢速端点的请求并发性,类似于连接池。 + +```caddyfile +{ + frankenphp { + max_threads 100 # 所有 worker 共享最多 100 个线程 + } +} + +example.com { + php_server { + root /app/public # 你的应用程序的根目录 + worker index.php { + match /slow-endpoint/* # 所有路径为 /slow-endpoint/* 的请求都由这个线程池处理 + num 10 # 匹配 /slow-endpoint/* 的请求至少有 10 个线程 + } + worker index.php { + match * # 所有其他请求单独处理 + num 20 # 其他请求至少有 20 个线程,即使慢速端点开始挂起 + } + } +} +``` + +通常,也建议通过使用消息队列等相关机制,异步处理非常慢的端点。 diff --git a/docs/cn/production.md b/docs/cn/production.md index 01777f932c..ac971c33e8 100644 --- a/docs/cn/production.md +++ b/docs/cn/production.md @@ -30,8 +30,7 @@ COPY . /app/public #COPY . /app ``` -有关更多详细信息和选项,请参阅 [构建自定义 Docker 镜像](docker.md)。 -要了解如何自定义配置,请安装 PHP 扩展和 Caddy 模块。 +有关更多详细信息和选项,以及如何自定义配置、安装 PHP 扩展和 Caddy 模块,请参阅“[构建自定义 Docker 镜像](docker.md)”。 如果你的项目使用 Composer, 请务必将其包含在 Docker 镜像中并安装你的依赖。 @@ -51,7 +50,7 @@ services: - caddy_data:/data - caddy_config:/config -# Caddy 证书和配置所需的挂载目录 +# Caddy 证书和配置所需的数据卷 volumes: caddy_data: caddy_config: @@ -60,11 +59,11 @@ volumes: > [!NOTE] > > 前面的示例适用于生产用途。 -> 在开发中,你可能希望使用挂载目录,不同的 PHP 配置和不同的 `SERVER_NAME` 环境变量值。 +> 在开发中,你可能希望使用一个数据卷,一个不同的 PHP 配置和不同的 `SERVER_NAME` 环境变量值。 > -> 见 [Symfony Docker](https://github.com/dunglas/symfony-docker) 项目 -> (使用 FrankenPHP)作为使用多阶段镜像的更高级示例, -> Composer、额外的 PHP 扩展等。 +> 请查看 [Symfony Docker](https://github.com/dunglas/symfony-docker) 项目 +> (它也使用了 FrankenPHP),以获取使用多阶段镜像、 +> Composer、额外 PHP 扩展等更高级的示例。 最后,如果你使用 Git,请提交这些文件并推送。 @@ -78,13 +77,13 @@ volumes: 然后,单击“Choose an image”部分下的“Marketplace”选项卡,然后搜索名为“Docker”的应用程序。 这将配置已安装最新版本的 Docker 和 Docker Compose 的 Ubuntu 服务器! -出于测试目的,最便宜的就足够了。 +出于测试目的,最廉价的套餐就足够了。 对于实际的生产用途,你可能需要在“general purpose”部分中选择一个计划来满足你的需求。 -![使用 Docker 在 DigitalOcean 上部署 FrankenPHP](../digitalocean-droplet.png) +![使用 Docker 在 DigitalOcean 上部署 FrankenPHP](digitalocean-droplet.png) 你可以保留其他设置的默认值,也可以根据需要进行调整。 -不要忘记添加你的 SSH 密钥或创建密码,然后点击“完成并创建”按钮。 +不要忘记添加你的 SSH 密钥或创建密码,然后点击“Finalize and create”按钮。 然后,在 Droplet 预配时等待几秒钟。 Droplet 准备就绪后,使用 SSH 进行连接: @@ -106,7 +105,7 @@ your-domain-name.example.com. IN A 207.154.233.113 DigitalOcean 域服务示例(“Networking” > “Domains”): -![在 DigitalOcean 上配置 DNS](../digitalocean-dns.png) +![在 DigitalOcean 上配置 DNS](digitalocean-dns.png) > [!NOTE] > @@ -131,7 +130,7 @@ docker compose up --wait ``` 你的服务器已启动并运行,并且已自动为你生成 HTTPS 证书。 -去 `https://your-domain-name.example.com` 享受吧! +访问 `https://your-domain-name.example.com` 即可体验! > [!CAUTION] > diff --git a/docs/cn/static.md b/docs/cn/static.md index 5680a4f7ca..5ebb9f44f8 100644 --- a/docs/cn/static.md +++ b/docs/cn/static.md @@ -1,26 +1,26 @@ # 创建静态构建 -与其使用本地安装的PHP库, -由于伟大的 [static-php-cli 项目](https://github.com/crazywhalecc/static-php-cli),创建一个静态或基本静态的 FrankenPHP 构建是可能的(尽管它的名字,这个项目支持所有的 SAPI,而不仅仅是 CLI)。 +与使用本地安装的 PHP 库不同, +借助于优秀的 [static-php-cli 项目](https://github.com/crazywhalecc/static-php-cli)(尽管其名称,该项目支持所有 SAPI,而不仅仅是 CLI),可以创建一个静态或基本静态的 FrankenPHP 构建。 -使用这种方法,我们可构建一个包含 PHP 解释器、Caddy Web 服务器和 FrankenPHP 的可移植二进制文件! +通过这种方法,一个单一的、可移植的二进制文件将包含 PHP 解释器、Caddy Web 服务器和 FrankenPHP! -完全静态的本地可执行文件不需要任何依赖,并且可以在 [`scratch` Docker 镜像](https://docs.docker.com/build/building/base-images/#create-a-minimal-base-image-using-scratch) 上运行。 -然而,它们无法加载动态 PHP 扩展(例如 Xdebug),并且由于使用了 musl libc,有一些限制。 +完全静态的本地可执行文件完全不需要任何依赖,甚至可以在 [`scratch` Docker 镜像](https://docs.docker.com/build/building/base-images/#create-a-minimal-base-image-using-scratch) 上运行。 +然而,它们无法加载动态 PHP 扩展(例如 Xdebug),并且由于使用了 musl libc,存在一些限制。 -大多数静态二进制文件只需要 `glibc` 并且可以加载动态扩展。 +基本静态的二进制文件只需要 `glibc` 即可加载动态扩展。 -在可能的情况下,我们建议使用基于glibc的、主要是静态构建的版本。 +在可能的情况下,我们建议使用基于 glibc 的基本静态构建。 FrankenPHP 还支持 [将 PHP 应用程序嵌入到静态二进制文件中](embed.md)。 ## Linux -我们提供了一个 Docker 镜像来构建 Linux 静态二进制文件: +我们提供了 Docker 镜像来构建 Linux 静态二进制文件: -### 基于musl的完全静态构建 +### 基于 musl 的完全静态构建 -对于一个在任何Linux发行版上运行且不需要依赖项的完全静态二进制文件,但不支持动态加载扩展: +对于一个在任何 Linux 发行版上运行且无需依赖项,但不支持动态加载扩展的完全静态二进制文件: ```console docker buildx bake --load static-builder-musl @@ -33,18 +33,18 @@ docker cp $(docker create --name static-builder-musl dunglas/frankenphp:static-b docker buildx bake --load --set static-builder-musl.args.MIMALLOC=1 static-builder-musl ``` -### 基于glibc的,主要静态构建(支持动态扩展) +### 基于 glibc 的基本静态构建(支持动态扩展) -对于一个支持动态加载 PHP 扩展的二进制文件,同时又将所选扩展静态编译: +对于一个支持动态加载 PHP 扩展,同时将所选扩展静态编译的二进制文件: ```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 ``` -该二进制文件支持所有glibc版本2.17及以上,但不支持基于musl的系统(如Alpine Linux)。 +该二进制文件支持所有 glibc 2.17 及以上版本,但不支持基于 musl 的系统(如 Alpine Linux)。 -生成的主要是静态的(除了 `glibc`)二进制文件名为 `frankenphp`,并且可以在当前目录中找到。 +生成的除了 `glibc` 外基本静态的二进制文件名为 `frankenphp`,并且可以在当前目录中找到。 如果你想在没有 Docker 的情况下构建静态二进制文件,请查看 macOS 说明,它也适用于 Linux。 @@ -52,7 +52,7 @@ docker cp $(docker create --name static-builder-gnu dunglas/frankenphp:static-bu 默认情况下,大多数流行的 PHP 扩展都会被编译。 -为了减少二进制文件的大小和减少攻击面,您可以选择使用 `PHP_EXTENSIONS` Docker ARG 构建的扩展列表。 +为了减少二进制文件的大小和减少攻击面,您可以选择使用 `PHP_EXTENSIONS` Docker 参数来构建扩展列表。 例如,运行以下命令仅构建 `opcache` 扩展: @@ -89,11 +89,11 @@ docker buildx bake \ > 如果 `XCADDY_ARGS` 为空或未设置,则默认包含 cbrotli、Mercure 和 Vulcain 模块。 > 如果自定义了 `XCADDY_ARGS` 的值,则必须显式地包含它们。 -参见:[自定义构建](#自定义构建) +另请参阅[如何自定义构建](#customizing-the-build)。 ### GitHub Token -如果遇到了 GitHub API 速率限制,请在 `GITHUB_TOKEN` 的环境变量中设置 GitHub Personal Access Token: +如果遇到了 GitHub API 速率限制,请在名为 `GITHUB_TOKEN` 的环境变量中设置 GitHub 个人访问令牌: ```console GITHUB_TOKEN="xxx" docker --load buildx bake static-builder-musl @@ -102,7 +102,7 @@ GITHUB_TOKEN="xxx" docker --load buildx bake static-builder-musl ## macOS -运行以下脚本以创建适用于 macOS 的静态二进制文件(需要先安装 [Homebrew](https://brew.sh/)): +运行以下脚本以创建适用于 macOS 的静态二进制文件(您必须已安装 [Homebrew](https://brew.sh/)): ```console git clone https://github.com/php/frankenphp @@ -110,7 +110,7 @@ cd frankenphp ./build-static.sh ``` -注意:此脚本也适用于 Linux(可能也适用于其他 Unix 系统),我们提供的用于构建静态二进制的 Docker 镜像也在内部使用这个脚本。 +注意:此脚本也适用于 Linux(以及可能在其他类 Unix 系统上),并且我们提供的 Docker 镜像在内部也使用了此脚本。 ## 自定义构建 @@ -119,24 +119,24 @@ cd frankenphp - `FRANKENPHP_VERSION`: 要使用的 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 模块 +- `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) 用[mimalloc](https://github.com/microsoft/mimalloc)替换musl的mallocng,以提高性能。我们仅建议在musl目标构建中使用此选项,对于glibc,建议禁用此选项,并在运行二进制文件时使用[`LD_PRELOAD`](https://microsoft.github.io/mimalloc/overrides.html)。 -- `RELEASE`: (仅限维护者)设置后,生成的二进制文件将上传到 GitHub 上 +- `CLEAN`: 设置后,libphp 及其所有依赖项都将从头开始构建(不使用缓存) +- `NO_COMPRESS`: 不使用 UPX 压缩生成的二进制文件 +- `DEBUG_SYMBOLS`: 设置后,调试符号将不会被剥离,并会被添加到二进制文件中 +- `MIMALLOC`: (实验性,仅限 Linux) 使用 [mimalloc](https://github.com/microsoft/mimalloc) 替换 musl 的 mallocng,以提高性能。我们仅建议将其用于面向 musl 的构建;对于 glibc,建议禁用此选项,并在运行二进制文件时改用 [`LD_PRELOAD`](https://microsoft.github.io/mimalloc/overrides.html)。 +- `RELEASE`: (仅限维护者)设置后,生成的二进制文件将上传到 GitHub ## 扩展 -使用glibc或基于macOS的二进制文件,您可以动态加载PHP扩展。然而,这些扩展必须使用ZTS支持进行编译。 +使用基于 glibc 或 macOS 的二进制文件,您可以动态加载 PHP 扩展。然而,这些扩展必须使用 ZTS 支持进行编译。 由于大多数软件包管理器目前不提供其扩展的 ZTS 版本,因此您必须自己编译它们。 -为此,您可以构建并运行 `static-builder-gnu` Docker 容器,远程进入它,并使用 `./configure --with-php-config=/go/src/app/dist/static-php-cli/buildroot/bin/php-config` 编译扩展。 +为此,您可以构建并运行 `static-builder-gnu` Docker 容器,连接到它,并使用 `./configure --with-php-config=/go/src/app/dist/static-php-cli/buildroot/bin/php-config` 编译扩展。 -关于 [Xdebug 扩展](https://xdebug.org) 的示例步骤: +以下是针对 [Xdebug 扩展](https://xdebug.org) 的示例步骤: ```console docker build -t gnu-ext -f static-builder-gnu.Dockerfile --build-arg FRANKENPHP_VERSION=1.0 . @@ -158,4 +158,4 @@ docker rmi gnu-ext ``` 这将在当前目录中创建 `frankenphp` 和 `xdebug-zts.so`。 -如果你将 `xdebug-zts.so` 移动到你的扩展目录中,添加 `zend_extension=xdebug-zts.so` 到你的 php.ini 并运行 FrankenPHP,它将加载 Xdebug。 +如果您将 `xdebug-zts.so` 移动到您的扩展目录中,并在您的 `php.ini` 中添加 `zend_extension=xdebug-zts.so`,然后运行 FrankenPHP,它将加载 Xdebug。 diff --git a/docs/cn/wordpress.md b/docs/cn/wordpress.md new file mode 100644 index 0000000000..f2584ab397 --- /dev/null +++ b/docs/cn/wordpress.md @@ -0,0 +1,59 @@ +# WordPress + +使用 FrankenPHP 运行 [WordPress](https://wordpress.org/),享受现代、高性能的堆栈,具备自动 HTTPS、HTTP/3 和 Zstandard 压缩功能。 + +## 最小安装 + +1. [下载 WordPress](https://wordpress.org/download/) +2. 解压 ZIP 存档并在解压后的目录中打开终端 +3. 运行: + + ```console + frankenphp php-server + ``` + +4. 访问 `http://localhost/wp-admin/` 并按照安装说明进行操作 +5. 享受吧! + +对于生产环境就绪的设置,请优先使用 `frankenphp run` 配合如下 `Caddyfile`: + +```caddyfile +example.com + +php_server +encode zstd br gzip +log +``` + +## 热重载 + +要将 [热重载](hot-reload.md) 功能与 WordPress 配合使用,请启用 [Mercure](mercure.md) 并在您的 `Caddyfile` 中为 `php_server` 指令添加 `hot_reload` 子指令: + +```caddyfile +localhost + +mercure { + anonymous +} + +php_server { + hot_reload +} +``` + +然后,在您的 WordPress 主题的 `functions.php` 文件中添加加载 JavaScript 库所需的代码: + +```php +function hot_reload() { + ?> + + + + + + [!TIP] +> 以下部分仅在 Symfony 7.4 之前是必需的,Symfony 7.4 引入了对 FrankenPHP worker 模式的原生支持。 + FrankenPHP 的 worker 模式由 [Symfony Runtime Component](https://symfony.com/doc/current/components/runtime.html) 支持。 要在 worker 中启动任何 Symfony 应用程序,请安装 [PHP Runtime](https://github.com/php-runtime/runtime) 的 FrankenPHP 包: @@ -78,9 +83,15 @@ $myApp->boot(); // 在循环外的处理器以获得更好的性能(减少工作量) $handler = static function () use ($myApp) { - // 当收到请求时调用, - // 超全局变量、php://input 等都会被重置 - echo $myApp->handle($_GET, $_POST, $_COOKIE, $_FILES, $_SERVER); + try { + // 当收到请求时调用, + // 超全局变量、php://input 等都会被重置 + echo $myApp->handle($_GET, $_POST, $_COOKIE, $_FILES, $_SERVER); + } catch (\Throwable $exception) { + // `set_exception_handler` 仅在 worker 脚本结束时调用, + // 这可能不是你期望的,因此在此处捕获并处理异常 + (new \MyCustomExceptionHandler)->handleException($exception); + } }; $maxRequests = (int)($_SERVER['MAX_REQUESTS'] ?? 0); @@ -144,7 +155,7 @@ curl -X POST http://localhost:2019/frankenphp/workers/restart 但是,如果 worker 脚本在短时间内继续以非零退出代码失败 (例如,脚本中有拼写错误),FrankenPHP 将崩溃并出现错误:`too many consecutive failures`。 -可以在你的 [Caddyfile](config.md#caddyfile-配置) 中使用 `max_consecutive_failures` 选项配置连续失败的次数: +可以在你的 [Caddyfile](config.md#caddyfile-config) 中使用 `max_consecutive_failures` 选项配置连续失败的次数: ```caddyfile frankenphp { @@ -176,4 +187,3 @@ $handler = static function () use ($workerServer) { }; // ... -``` diff --git a/docs/fr/classic.md b/docs/fr/classic.md index 68de11c2b7..ef558e43d3 100644 --- a/docs/fr/classic.md +++ b/docs/fr/classic.md @@ -1,11 +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. +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 transparent pour 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. +Comme Caddy, FrankenPHP accepte un nombre illimité de connexions et utilise un [nombre fixe de threads](config.md#caddyfile-config) pour les servir. Le nombre de connexions acceptées et mises en file d'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), à l'instar du 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` dans la configuration globale de FrankenPHP 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. +Les connexions mises 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#caddyfile-config) `max_wait_time` dans la configuration globale de FrankenPHP afin de limiter la durée pendant laquelle une requête peut attendre un thread PHP libre avant d'être rejetée. +De plus, vous pouvez définir un [délai d'écriture raisonnable dans Caddy](https://caddyserver.com/docs/caddyfile/options#timeouts). -Chaque instance de Caddy n'utilisera qu'un seul pool de threads FrankenPHP, qui sera partagé par tous les blocs `php_server`. +Chaque instance de Caddy ne lancera qu'un seul pool de threads FrankenPHP, qui sera partagé entre tous les blocs `php_server`. diff --git a/docs/fr/compile.md b/docs/fr/compile.md index 5a3d1f139a..7e7b4a44d5 100644 --- a/docs/fr/compile.md +++ b/docs/fr/compile.md @@ -1,9 +1,9 @@ # Compiler depuis les sources -Ce document explique comment créer un build FrankenPHP qui chargera PHP en tant que bibliothèque dynamique. +Ce document explique comment créer un binaire FrankenPHP qui chargera PHP en tant que bibliothèque dynamique. C'est la méthode recommandée. -Alternativement, il est aussi possible de [créer des builds statiques](static.md). +Alternativement, des [versions entièrement ou partiellement statiques](static.md) peuvent également être créées. ## Installer PHP @@ -33,11 +33,10 @@ tar xf php-* cd php-*/ ``` -Ensuite, configurez PHP pour votre système d'exploitation. +Ensuite, exécutez le script `configure` avec les options nécessaires pour votre plateforme. +Les `flags` `./configure` suivants sont obligatoires, mais vous pouvez en ajouter d'autres, par exemple, pour compiler des extensions ou des fonctionnalités supplémentaires. -Les options de configuration suivantes sont nécessaires pour la compilation, mais vous pouvez également inclure d'autres options selon vos besoins, par exemple pour ajouter des extensions et fonctionnalités supplémentaires. - -### Linux +#### Linux ```console ./configure \ @@ -47,7 +46,7 @@ Les options de configuration suivantes sont nécessaires pour la compilation, ma --enable-zend-max-execution-timers ``` -### Mac +#### Mac Utilisez le gestionnaire de paquets [Homebrew](https://brew.sh/) pour installer les dépendances obligatoires et optionnelles : @@ -63,11 +62,10 @@ Puis exécutez le script de configuration : --enable-embed \ --enable-zts \ --disable-zend-signals \ - --disable-opcache-jit \ --with-iconv=/opt/homebrew/opt/libiconv/ ``` -### Compilez PHP +#### Compiler PHP Finalement, compilez et installez PHP : @@ -76,18 +74,21 @@ make -j"$(getconf _NPROCESSORS_ONLN)" sudo make install ``` -## Installez les dépendances optionnelles +## Installer les dépendances optionnelles -Certaines fonctionnalités de FrankenPHP nécessitent des dépendances optionnelles qui doivent être installées. +Certaines fonctionnalités de FrankenPHP dépendent de dépendances système 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 | +| 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 | +| [Mercure](mercure.md) | [Bibliothèque Mercure Go](https://pkg.go.dev/github.com/dunglas/mercure) (installée automatiquement, licence AGPL) | nomercure | ## Compiler l'application Go +Vous pouvez maintenant construire le binaire final. + ### Utiliser xcaddy La méthode recommandée consiste à utiliser [xcaddy](https://github.com/caddyserver/xcaddy) pour compiler FrankenPHP. @@ -101,10 +102,13 @@ CGO_LDFLAGS="$(php-config --ldflags) $(php-config --libs)" \ 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 + --with github.com/dunglas/vulcain/caddy \ + --with github.com/dunglas/caddy-cbrotli # Ajoutez les modules Caddy supplémentaires et les extensions FrankenPHP ici + # Facultativement, si vous souhaitez compiler à partir de vos sources frankenphp : + # --with github.com/dunglas/frankenphp=$(pwd) \ + # --with github.com/dunglas/frankenphp/caddy=$(pwd)/caddy ``` > [!TIP] @@ -125,4 +129,3 @@ Il est également possible de compiler FrankenPHP sans `xcaddy` en utilisant dir curl -L https://github.com/php/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 -``` diff --git a/docs/fr/config.md b/docs/fr/config.md index 7c52cea7cb..be14fa9994 100644 --- a/docs/fr/config.md +++ b/docs/fr/config.md @@ -1,19 +1,41 @@ # 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). +FrankenPHP, Caddy ainsi que les modules [Mercure](mercure.md) et [Vulcain](https://vulcain.rocks) 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/frankenphp/Caddyfile`. -Le binaire statique cherchera le `Caddyfile` dans le répertoire dans lequel il est démarré. +Le format le plus courant est le `Caddyfile`, un format texte simple et lisible par l'homme. +Par défaut, FrankenPHP cherchera un `Caddyfile` dans le répertoire actuel. +Vous pouvez spécifier un chemin personnalisé avec l'option `-c` ou `--config`. + +Un `Caddyfile` minimal pour servir une application PHP est présenté ci-dessous : + +```caddyfile +# The hostname to respond to +localhost + +# Optionaly, the directory to serve files from, otherwise defaults to the current directory +#root public/ +php_server +``` + +Un `Caddyfile` plus avancé, activant davantage de fonctionnalités et fournissant des variables d'environnement pratiques, est disponible [dans le dépôt FrankenPHP](https://github.com/php/frankenphp/blob/main/caddy/frankenphp/Caddyfile), +ainsi qu'avec les images Docker. PHP lui-même peut être configuré [en utilisant un fichier `php.ini`](https://www.php.net/manual/fr/configuration.file.php). -L'interpréteur PHP cherchera dans les emplacements suivants : +Selon votre méthode d'installation, FrankenPHP et l'interpréteur PHP chercheront les fichiers de configuration aux emplacements décrits ci-dessous. -Docker : +## Docker -- php.ini : `/usr/local/etc/php/php.ini` Aucun php.ini n'est fourni par défaut. +FrankenPHP : + +- `/etc/frankenphp/Caddyfile` : le fichier de configuration principal +- `/etc/frankenphp/Caddyfile.d/*.caddyfile` : fichiers de configuration additionnels chargés automatiquement + +PHP : + +- `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-/` +- extensions PHP : `/usr/local/lib/php/extensions/no-debug-zts-/` - Vous devriez copier un modèle officiel fourni par le projet PHP : ```dockerfile @@ -26,17 +48,29 @@ 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 ``` -Installation de FrankenPHP (.rpm ou .deb) : +## Paquets RPM et Debian -- 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/` +FrankenPHP : + +- `/etc/frankenphp/Caddyfile` : le fichier de configuration principal +- `/etc/frankenphp/Caddyfile.d/*.caddyfile` : fichiers de configuration additionnels chargés automatiquement -Binaire statique : +PHP : -- php.ini : Le répertoire dans lequel `frankenphp run` ou `frankenphp php-server` est exécuté, puis `/etc/frankenphp/php.ini` +- `php.ini` : `/etc/php-zts/php.ini` (un fichier `php.ini` avec des préréglages de production est fourni par défaut) +- fichiers de configuration supplémentaires : `/etc/php-zts/conf.d/*.ini` + +## Binaire statique + +FrankenPHP : + +- Dans le répertoire de travail actuel : `Caddyfile` + +PHP : + +- `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 +- extensions PHP : ne peuvent pas être chargées, intégrez-les dans le binaire lui-même - 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 @@ -47,44 +81,43 @@ Exemple minimal : ```caddyfile 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 } ``` -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. +Vous pouvez également configurer explicitement FrankenPHP en utilisant l'[option globale](https://caddyserver.com/docs/caddyfile/concepts#global-options) `frankenphp` : ```caddyfile { - frankenphp { - num_threads # Définit le nombre de threads PHP à démarrer. Par défaut : 2x le nombre de CPUs disponibles. - max_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 # 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 Définit une directive php.ini. Peut être utilisé plusieurs fois pour définir plusieurs directives. - worker { - file # Définit le chemin vers le script worker. - num # Définit le nombre de threads PHP à démarrer, par défaut 2x le nombre de CPUs disponibles. - env # 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 # Définit le chemin d'accès à surveiller pour les modifications de fichiers. Peut être spécifié plusieurs fois pour plusieurs chemins. - name # Définit le nom du worker, utilisé dans les journaux et les métriques. Défaut : chemin absolu du fichier du worker - max_consecutive_failures # Définit le nombre maximum d'échecs consécutifs avant que le worker ne soit considéré comme défaillant, -1 signifie que le worker redémarre toujours. Par défaut : 6. - } - } + frankenphp { + num_threads # Définit le nombre de threads PHP à démarrer. Par défaut : 2x le nombre de CPUs disponibles. + max_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 # 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 # Définit une directive php.ini. Peut être utilisé plusieurs fois pour définir plusieurs directives. + worker { + file # Définit le chemin vers le script worker. + num # Définit le nombre de threads PHP à démarrer, par défaut 2x le nombre de CPUs disponibles. + env # 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 # Définit le chemin d'accès à surveiller pour les modifications de fichiers. Peut être spécifié plusieurs fois pour plusieurs chemins. + name # Définit le nom du worker, utilisé dans les journaux et les métriques. Défaut : chemin absolu du fichier worker + max_consecutive_failures # Définit le nombre maximum d'échecs consécutifs avant que le worker ne soit considéré comme défaillant, -1 signifie que le worker redémarre toujours. Par défaut : 6. + } + } } # ... ``` -Vous pouvez également utiliser la forme courte de l'option worker en une seule ligne : +Alternativement, vous pouvez utiliser la forme courte en une seule ligne de l'option `worker` : ```caddyfile { - frankenphp { - worker - } + frankenphp { + worker + } } # ... @@ -95,48 +128,48 @@ Vous pouvez aussi définir plusieurs workers si vous servez plusieurs applicatio ```caddyfile app.example.com { root /path/to/app/public - php_server { - root /path/to/app/public # permet une meilleure mise en cache - worker index.php - } + php_server { + root /path/to/app/public # permet une meilleure mise en cache + worker index.php + } } other.example.com { root /path/to/other/public - php_server { - root /path/to/other/public - worker index.php - } + php_server { + root /path/to/other/public + worker index.php + } } # ... ``` -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. +L'utilisation de la directive `php_server` est généralement ce dont vous avez besoin, +mais si vous avez besoin d'un contrôle total, vous pouvez utiliser la directive `php` de niveau inférieur. 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#try_files). +c'est un fichier PHP ou non. En savoir plus à ce sujet dans la [page performances](performance.md#try_files). 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 } ``` @@ -144,25 +177,26 @@ Les directives `php_server` et `php` disposent des options suivantes : ```caddyfile php_server [] { - root # Définit le dossier racine du le site. Par défaut : valeur de la directive `root` parente. - split_path # 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 # 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 # Définit le chemin vers le script worker, peut être relatif à la racine du php_server - num # Définit le nombre de threads PHP à démarrer, par défaut 2x le nombre de CPUs disponibles - 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 # Définit le chemin d'accès à surveiller pour les modifications de fichiers. Peut être spécifié plusieurs fois pour plusieurs chemins. - env # 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 # Peut également utiliser la forme courte comme dans le bloc frankenphp global. + root # Définit le dossier racine du site. Par défaut : la directive `root`. + split_path # 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 # 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 # Définit le chemin vers le script worker, peut être relatif à la racine du php_server + num # Définit le nombre de threads PHP à démarrer, par défaut 2x le nombre de CPUs disponibles. + name # Définit le nom du worker, utilisé dans les journaux et les métriques. Défaut : chemin absolu du fichier worker. Commence toujours par m# lorsqu'il est défini dans un bloc php_server. + watch # Définit le chemin d'accès à surveiller pour les modifications de fichiers. Peut être spécifié plusieurs fois pour plusieurs chemins. + env # 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. + match # fait correspondre le worker à un motif de chemin. Remplace try_files et ne peut être utilisé que dans la directive php_server. + } + worker # 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 +Étant donné 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`. @@ -179,9 +213,11 @@ Ceci est utile pour les environnements de développement. } ``` -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 +Cette fonctionnalité est souvent utilisée en combinaison avec le [rechargement à chaud (hot reload)](hot-reload.md). + +Si le répertoire `watch` n'est pas spécifié, il se rabattra sur `./**/*.{env,php,twig,yaml,yml}`, +qui surveille tous les fichiers `.env`, `.php`, `.twig`, `.yaml` et `.yml` dans le répertoire et les sous-répertoires +où le processus FrankenPHP a été démarré. Vous pouvez également spécifier un ou plusieurs répertoires via un [motif de nom de fichier shell](https://pkg.go.dev/path/filepath#Match) : ```caddyfile @@ -199,40 +235,35 @@ où le processus FrankenPHP a été lancé. Vous pouvez également spécifier un ``` - Le motif `**` signifie une surveillance récursive. -- Les répertoires peuvent également être relatifs (depuis l'endroit où le processus FrankenPHP est démarré). +- Les répertoires peuvent également être relatifs (par rapport à l'emplacement de démarrage du processus FrankenPHP). - 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. +- Méfiez-vous des fichiers créés au moment de l'exécution (comme les journaux) 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). +Le système de surveillance des fichiers est basé sur [e-dant/watcher](https://github.com/e-dant/watcher). -### Full Duplex (HTTP/1) +## Faire correspondre le worker à un chemin -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.) +Dans les applications PHP traditionnelles, les scripts sont toujours placés dans le répertoire public. +Ceci est également vrai pour les scripts worker, qui sont traités comme tout autre script PHP. +Si vous souhaitez plutôt placer le script worker en dehors du répertoire public, vous pouvez le faire via la directive `match`. -Il s'agit d'une configuration optionnelle qui doit être ajoutée aux options globales dans le fichier `Caddyfile` : +La directive `match` est une alternative optimisée à `try_files`, disponible uniquement au sein de `php_server` et `php`. +L'exemple suivant servira toujours un fichier dans le répertoire public s'il est présent, +et transmettra sinon la requête au worker correspondant au motif de chemin. ```caddyfile { - servers { - enable_full_duplex - } + frankenphp { + php_server { + worker { + file /path/to/worker.php # le fichier peut être en dehors du chemin public + match /api/* # toutes les requêtes commençant par /api/ seront gérées par ce worker + } + } + } } ``` -> [!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 : @@ -242,9 +273,9 @@ Les variables d'environnement suivantes peuvent être utilisées pour insérer d - `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`. +Comme pour les SAPI FPM et CLI, les variables d'environnement sont exposées par défaut dans la superglobale `$_SERVER`. -La valeur `S` de [la directive `variables_order` de PHP](https://www.php.net/manual/fr/ini.core.php#ini.variables-order) est toujours équivalente à `ES`, que `E` soit défini ailleurs dans cette directive ou non. +La valeur `S` de [la directive `variables_order` de PHP](https://www.php.net/manual/fr/ini.core.php#ini.variables-order) est toujours équivalente à `ES` quelle que soit la position de `E` ailleurs dans cette directive. ## Configuration PHP @@ -269,6 +300,43 @@ Vous pouvez également modifier la configuration de PHP en utilisant la directiv } ``` +### Désactivation de HTTPS + +Par défaut, FrankenPHP activera automatiquement HTTPS pour tous les noms d'hôte, y compris `localhost`. +Si vous souhaitez désactiver HTTPS (par exemple dans un environnement de développement), vous pouvez définir la variable d'environnement `SERVER_NAME` à `http://` ou `:80` : + +Alternativement, vous pouvez utiliser toutes les autres méthodes décrites dans la [documentation de Caddy](https://caddyserver.com/docs/automatic-https#activation). + +Si vous souhaitez utiliser HTTPS avec l'adresse IP `127.0.0.1` au lieu du nom d'hôte `localhost`, veuillez consulter la section [problèmes connus](known-issues.md#using-https127001-with-docker). + +### 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 : [Mercure](mercure.md), WebSocket, événements envoyés par le serveur, etc.) + +Il s'agit d'une configuration à activer explicitement qui doit être ajoutée aux options globales dans le `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). + ## 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 : diff --git a/docs/fr/docker.md b/docs/fr/docker.md index 7f97bcc938..d9c8a1e328 100644 --- a/docs/fr/docker.md +++ b/docs/fr/docker.md @@ -1,13 +1,14 @@ # Création d'une image Docker personnalisée -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. +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, 8.3, 8.4 et 8.5 sont disponibles. [Parcourir les tags](https://hub.docker.com/r/dunglas/frankenphp/tags). +Des variantes pour PHP 8.2, 8.3, 8.4 et 8.5 sont disponibles. -Les tags suivent le pattern suivant: `dunglas/frankenphp:-php-` +Les tags suivent ce modèle : `dunglas/frankenphp:-php-` -- `` et `` 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`). -- `` est soit `trixie` (pour Debian Trixie), `bookworm` (pour Debian Bookworm) ou `alpine` (pour la dernière version stable d'Alpine). +- `` et `` sont respectivement 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`). +- `` est soit `trixie` (pour Debian Trixie), `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). @@ -28,10 +29,15 @@ docker build -t my-php-app . docker run -it --rm --name my-running-app my-php-app ``` +## Comment ajuster la configuration + +Pour des raisons de commodité, [un `Caddyfile` par défaut](https://github.com/php/frankenphp/blob/main/caddy/frankenphp/Caddyfile) contenant +des variables d'environnement utiles est fourni dans l'image. + ## Comment installer plus d'extensions PHP Le script [`docker-php-extension-installer`](https://github.com/mlocati/docker-php-extension-installer) est fourni dans l'image de base. -Il est facile d'ajouter des extensions PHP supplémentaires : +L'ajout d'extensions PHP supplémentaires est simple : ```dockerfile FROM dunglas/frankenphp @@ -54,7 +60,7 @@ La manière la plus simple d'installer des modules Caddy personnalisés est d'ut ```dockerfile FROM dunglas/frankenphp:builder AS builder -# Copier xcaddy dans l'image du constructeur +# Copier xcaddy dans l'image builder COPY --from=caddy:builder /usr/bin/xcaddy /usr/bin/xcaddy # CGO doit être activé pour construire FrankenPHP @@ -75,12 +81,12 @@ RUN CGO_ENABLED=1 \ FROM dunglas/frankenphp AS runner -# Remplacer le binaire officiel par celui contenant vos modules personnalisés +# Remplacer le binaire officiel par celui qui contient vos modules personnalisés COPY --from=builder /usr/local/bin/frankenphp /usr/local/bin/frankenphp ``` -L'image builder fournie par FrankenPHP contient une version compilée de `libphp`. -[Les images builder](https://hub.docker.com/r/dunglas/frankenphp/tags?name=builder) sont fournies pour toutes les versions de FrankenPHP et PHP, à la fois pour Debian et Alpine. +L'image `builder` fournie par FrankenPHP contient une version compilée de `libphp`. +[Les images `builder`](https://hub.docker.com/r/dunglas/frankenphp/tags?name=builder) sont fournies pour toutes les versions de FrankenPHP et PHP, à la fois pour Debian et Alpine. > [!TIP] > @@ -109,7 +115,7 @@ docker run -v $PWD:/app/public -p 80:80 -p 443:443 -p 443:443/udp --tty my-php-a > [!TIP] > -> L'option --tty permet d'avoir des logs lisibles par un humain au lieu de logs JSON. +> L'option `--tty` permet d'avoir des logs lisibles par l'homme au lieu de logs JSON. Avec Docker Compose : @@ -131,7 +137,7 @@ services: - ./:/app/public - caddy_data:/data - caddy_config:/config - # commentez la ligne suivante en production, elle permet d'avoir de beaux logs lisibles en dev + # commentez la ligne suivante en production, elle permet d'avoir des logs lisibles par l'homme en dev tty: true # Volumes nécessaires pour les certificats et la configuration de Caddy @@ -156,18 +162,19 @@ RUN \ 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 - chown -R ${USER}:${USER} /data/caddy && chown -R ${USER}:${USER} /config/caddy + # Donner l'accès en écriture à /config/caddy et /data/caddy + chown -R ${USER}:${USER} /config/caddy /data/caddy USER ${USER} ``` ### Exécution sans capacité -Même lorsqu'il s'exécute en tant qu'utilisateur autre que root, FrankenPHP a besoin de la capacité `CAP_NET_BIND_SERVICE` -pour que son serveur utilise les ports privilégiés (80 et 443). +Même lorsqu'il s'exécute sans les privilèges root, FrankenPHP a besoin de la capacité `CAP_NET_BIND_SERVICE` +pour lier le serveur web aux ports privilégiés (80 et 443). -Si vous exposez FrankenPHP sur un port non privilégié (à partir de 1024), il est possible de faire fonctionner le serveur web avec un utilisateur qui n'est pas root, et sans avoir besoin d'aucune capacité. +Si vous exposez FrankenPHP sur un port non privilégié (1024 et au-delà), il est possible +d'exécuter le serveur web en tant qu'utilisateur non-root, et sans avoir besoin d'aucune capacité : ```dockerfile FROM dunglas/frankenphp @@ -175,18 +182,18 @@ FROM dunglas/frankenphp ARG USER=appuser RUN \ - # Utiliser "adduser -D ${USER}" pour les distros basées sur Alpine + # Utilisez "adduser -D ${USER}" pour les distributions basées sur Alpine useradd ${USER}; \ - # Supprimer la capacité par défaut \ + # Supprimer la capacité par défaut setcap -r /usr/local/bin/frankenphp; \ - # Donner un accès en écriture à /data/caddy et /config/caddy \ - chown -R ${USER}:${USER} /data/caddy && chown -R ${USER}:${USER} /config/caddy + # Donner un accès en écriture à /config/caddy et /data/caddy + chown -R ${USER}:${USER} /config/caddy /data/caddy USER ${USER} ``` Ensuite, définissez la variable d'environnement `SERVER_NAME` pour utiliser un port non privilégié. -Exemple `:8000` +Exemple : `:8000` ## Mises à jour @@ -197,7 +204,8 @@ Les images Docker sont construites : ## Versions de développement -Les versions de développement sont disponibles dans le dépôt Docker [`dunglas/frankenphp-dev`](https://hub.docker.com/repository/docker/dunglas/frankenphp-dev). Un nouveau build est déclenché chaque fois qu'un commit est poussé sur la branche principale du dépôt GitHub. +Les versions de développement sont disponibles dans le dépôt Docker [`dunglas/frankenphp-dev`](https://hub.docker.com/repository/docker/dunglas/frankenphp-dev). +Un nouveau build est déclenché chaque fois qu'un commit est poussé sur la branche principale du dépôt GitHub. Les tags `latest*` pointent vers la tête de la branche `main`. -Les tags sous la forme `sha-` sont également disponibles. +Les tags sous la forme `sha-` sont également disponibles. \ No newline at end of file diff --git a/docs/fr/embed.md b/docs/fr/embed.md index 1523a392be..77fb5dbacc 100644 --- a/docs/fr/embed.md +++ b/docs/fr/embed.md @@ -6,20 +6,20 @@ 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). +Pour embarquer des applications Laravel, [lisez ce point spécifique de la documentation](laravel.md#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 : +Par exemple, vous voudrez 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 -Par exemple, pour une application Symfony, lancez les commandes suivantes : +Par exemple, pour une application Symfony, vous pouvez utiliser les commandes suivantes : ```console # Exporter le projet pour se débarrasser de .git/, etc. @@ -53,16 +53,17 @@ dans le répertoire principal de l'application à intégrer La manière la plus simple de créer un binaire Linux est d'utiliser le builder basé sur Docker que nous fournissons. -1. Créez un fichier nommé `static-build.Dockerfile` dans le répertoire de votre application préparée : +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-gnu # Si vous envisagez d'exécuter le binaire sur des systèmes musl-libc, utilisez plutôt static-builder-musl - # Copy your app + # Copiez votre application WORKDIR /go/src/app/dist/app COPY . . + # Construisez le binaire statique WORKDIR /go/src/app/ RUN EMBED=dist/app/ ./build-static.sh ``` @@ -84,7 +85,7 @@ La manière la plus simple de créer un binaire Linux est d'utiliser le builder 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. +Le binaire résultant est le fichier nommé `my-app` dans le répertoire courant. ## Créer un binaire pour d'autres systèmes d'exploitation @@ -96,7 +97,7 @@ cd frankenphp EMBED=/path/to/your/app ./build-static.sh ``` -Le binaire obtenu est le fichier nommé `frankenphp--` dans le répertoire `dist/`. +Le binaire résultant est le fichier nommé `frankenphp--` dans le répertoire `dist/`. ## Utiliser le binaire @@ -129,7 +130,7 @@ Vous pouvez également exécuter les scripts CLI PHP incorporés dans votre bina ## 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). +Si le fichier `composer.json` n'existe pas, les extensions par défaut sont construites, comme documenté dans [la documentation sur la compilation statique](static.md). Pour personnaliser les extensions, utilisez la variable d'environnement `PHP_EXTENSIONS`. @@ -145,7 +146,7 @@ PHP_EXTENSIONS=ctype,iconv,pdo_sqlite \ ## Distribuer le binaire -Sous Linux, le binaire est compressé par défaut à l'aide de [UPX](https://upx.github.io). +Sous Linux, le binaire créé est compressé à l'aide de [UPX](https://upx.github.io). Sous Mac, pour réduire la taille du fichier avant de l'envoyer, vous pouvez le compresser. Nous recommandons `xz`. diff --git a/docs/fr/extensions.md b/docs/fr/extensions.md index caf50e9ca7..e7519bacd1 100644 --- a/docs/fr/extensions.md +++ b/docs/fr/extensions.md @@ -10,8 +10,8 @@ Grâce aux modules Caddy, vous pouvez écrire des extensions PHP en Go et les in FrankenPHP offre deux façons de créer des extensions PHP en Go : -1. **Utilisation du Générateur d'Extensions** - L'approche recommandée qui génère tout le code standard nécessaire pour la plupart des cas d'usage, vous permettant de vous concentrer sur l'écriture de votre code Go -2. **Implémentation Manuelle** - Contrôle total sur la structure de l'extension pour les cas d'usage avancés +1. **Utilisation du Générateur d'Extensions** - L'approche recommandée qui génère tout le code standard nécessaire pour la plupart des cas d'usage, vous permettant de vous concentrer sur l'écriture de votre code Go +2. **Implémentation Manuelle** - Contrôle total sur la structure de l'extension pour les cas d'usage avancés Nous commencerons par l'approche du générateur, car c'est le moyen le plus facile de commencer, puis nous montrerons l'implémentation manuelle pour ceux qui ont besoin d'un contrôle complet. @@ -26,14 +26,14 @@ Gardez à l'esprit que cet outil n'est **pas un générateur d'extensions comple ### Prérequis -Comme aussi couvert dans la section d'implémentation manuelle ci-dessous, vous devez [obtenir les sources PHP](https://www.php.net/downloads.php) et créer un nouveau module Go. +Comme indiqué également dans la section d'implémentation manuelle ci-dessous, vous devez [obtenir les sources PHP](https://www.php.net/downloads.php) et créer un nouveau module Go. #### Créer un Nouveau Module et Obtenir les Sources PHP La première étape pour écrire une extension PHP en Go est de créer un nouveau module Go. Vous pouvez utiliser la commande suivante pour cela : ```console -go mod init github.com/my-account/my-module +go mod init example.com/example ``` La seconde étape est [l'obtention des sources PHP](https://www.php.net/downloads.php) pour les étapes suivantes. Une fois que vous les avez, décompressez-les dans le répertoire de votre choix, mais pas à l'intérieur de votre module Go : @@ -53,9 +53,9 @@ package example import "C" import ( "strings" - "unsafe" + "unsafe" - "github.com/dunglas/frankenphp" + "github.com/dunglas/frankenphp" ) //export_php:function repeat_this(string $str, int $count, bool $reverse): string @@ -77,8 +77,8 @@ func repeat_this(s *C.zend_string, count int64, reverse bool) unsafe.Pointer { Il y a deux choses importantes à noter ici : -- Une directive `//export_php:function` définit la signature de la fonction en PHP. C'est ainsi que le générateur sait comment générer la fonction PHP avec les bons paramètres et le bon type de retour ; -- La fonction doit retourner un `unsafe.Pointer`. FrankenPHP fournit une API pour vous aider avec le jonglage de types entre C et Go. +- Une directive `//export_php:function` définit la signature de la fonction en PHP. C'est ainsi que le générateur sait comment générer la fonction PHP avec les bons paramètres et le bon type de retour ; +- La fonction doit retourner un `unsafe.Pointer`. FrankenPHP fournit une API pour vous aider avec le jonglage de types entre C et Go. Alors que le premier point parle de lui-même, le second peut être plus difficile à appréhender. Plongeons plus profondément dans le jonglage de types dans la section suivante. @@ -87,18 +87,19 @@ Alors que le premier point parle de lui-même, le second peut être plus diffici Bien que certains types de variables aient la même représentation mémoire entre C/PHP et Go, certains types nécessitent plus de logique pour être directement utilisés. C'est peut-être la partie la plus difficile quand il s'agit d'écrire des extensions car cela nécessite de comprendre les fonctionnements internes du moteur Zend et comment les variables sont stockées dans le moteur de PHP. Ce tableau résume ce que vous devez savoir : | Type PHP | Type Go | Conversion directe | Assistant C vers Go | Assistant Go vers C | Support des Méthodes de Classe | -| ------------------ | ----------------------------- | ------------------ | --------------------------------- | ---------------------------------- | ------------------------------ | +| :----------------- | :---------------------------- | :----------------- | :-------------------------------- | :--------------------------------- | :----------------------------- | | `int` | `int64` | ✅ | - | - | ✅ | | `?int` | `*int64` | ✅ | - | - | ✅ | | `float` | `float64` | ✅ | - | - | ✅ | | `?float` | `*float64` | ✅ | - | - | ✅ | | `bool` | `bool` | ✅ | - | - | ✅ | | `?bool` | `*bool` | ✅ | - | - | ✅ | -| `string`/`?string` | `*C.zend_string` | ❌ | frankenphp.GoString() | frankenphp.PHPString() | ✅ | +| `string`/`?string` | `*C.zend_string` | ❌ | `frankenphp.GoString()` | `frankenphp.PHPString()` | ✅ | | `array` | `frankenphp.AssociativeArray` | ❌ | `frankenphp.GoAssociativeArray()` | `frankenphp.PHPAssociativeArray()` | ✅ | | `array` | `map[string]any` | ❌ | `frankenphp.GoMap()` | `frankenphp.PHPMap()` | ✅ | | `array` | `[]any` | ❌ | `frankenphp.GoPackedArray()` | `frankenphp.PHPPackedArray()` | ✅ | | `mixed` | `any` | ❌ | `GoValue()` | `PHPValue()` | ❌ | +| `callable` | `*C.zval` | ❌ | - | `frankenphp.CallPHPCallable()` | ❌ | | `object` | `struct` | ❌ | _Pas encore implémenté_ | _Pas encore implémenté_ | ❌ | > [!NOTE] @@ -112,7 +113,7 @@ Si vous vous référez à l'extrait de code de la section précédente, vous pou FrankenPHP fournit un support natif pour les tableaux PHP à travers `frankenphp.AssociativeArray` ou une conversion directe vers une map ou un slice. -`AssociativeArray` représente une [hash map](https://fr.wikipedia.org/wiki/Table_de_hachage) composée d'un champ `Map: map[string]any` et d'un champ optionnel `Order: []string` (contrairement aux "tableaux associatifs" PHP, les maps Go ne sont pas ordonnées). +`AssociativeArray` représente une [table de hachage](https://fr.wikipedia.org/wiki/Table_de_hachage) composée d'un champ `Map: map[string]any` et d'un champ optionnel `Order: []string` (contrairement aux "tableaux associatifs" PHP, les maps Go ne sont pas ordonnées). Si l'ordre ou l'association ne sont pas nécessaires, il est également possible de convertir directement vers un slice `[]any` ou une map non ordonnée `map[string]any`. @@ -130,90 +131,90 @@ import ( ) // export_php:function process_data_ordered(array $input): array -func process_data_ordered_map(arr *C.zval) unsafe.Pointer { - // Convertir le tableau associatif PHP vers Go en conservant l'ordre - associativeArray, err := frankenphp.GoAssociativeArray[any](unsafe.Pointer(arr)) +func process_data_ordered_map(arr *C.zend_array) unsafe.Pointer { + // Convertir le tableau associatif PHP vers Go en conservant l'ordre + associativeArray, err := frankenphp.GoAssociativeArray[any](unsafe.Pointer(arr)) if err != nil { // gérer l'erreur } - // parcourir les entrées dans l'ordre - for _, key := range associativeArray.Order { - value, _ = associativeArray.Map[key] - // faire quelque chose avec key et value - } + // parcourir les entrées dans l'ordre + for _, key := range associativeArray.Order { + // value, _ = associativeArray.Map[key] // 'value' is not declared + // faire quelque chose avec key et value + } - // retourner un tableau ordonné - // si 'Order' n'est pas vide, seules les paires clé-valeur dans 'Order' seront respectées - return frankenphp.PHPAssociativeArray[string](frankenphp.AssociativeArray[string]{ - Map: map[string]string{ - "key1": "value1", - "key2": "value2", - }, - Order: []string{"key1", "key2"}, - }) + // retourner un tableau ordonné + // si 'Order' n'est pas vide, seules les paires clé-valeur dans 'Order' seront respectées + return frankenphp.PHPAssociativeArray[string](frankenphp.AssociativeArray[string]{ + Map: map[string]string{ + "key1": "value1", + "key2": "value2", + }, + Order: []string{"key1", "key2"}, + }) } // export_php:function process_data_unordered(array $input): array -func process_data_unordered_map(arr *C.zval) unsafe.Pointer { - // Convertir le tableau associatif PHP vers une map Go sans conserver l'ordre - // ignorer l'ordre sera plus performant - goMap, err := frankenphp.GoMap[any](unsafe.Pointer(arr)) +func process_data_unordered_map(arr *C.zend_array) unsafe.Pointer { + // Convertir le tableau associatif PHP vers une map Go sans conserver l'ordre + // ignorer l'ordre sera plus performant + goMap, err := frankenphp.GoMap[any](unsafe.Pointer(arr)) if err != nil { // gérer l'erreur } - // parcourir les entrées sans ordre spécifique - for key, value := range goMap { - // faire quelque chose avec key et value - } + // parcourir les entrées sans ordre spécifique + for key, value := range goMap { + // faire quelque chose avec key et value + } - // retourner un tableau non ordonné - return frankenphp.PHPMap(map[string]string { - "key1": "value1", - "key2": "value2", - }) + // retourner un tableau non ordonné + return frankenphp.PHPMap(map[string]string { + "key1": "value1", + "key2": "value2", + }) } // export_php:function process_data_packed(array $input): array -func process_data_packed(arr *C.zval) unsafe.Pointer { - // Convertir le tableau packed PHP vers Go - goSlice, err := frankenphp.GoPackedArray(unsafe.Pointer(arr)) +func process_data_packed(arr *C.zend_array) unsafe.Pointer { + // Convertir le tableau packed PHP vers Go + goSlice, err := frankenphp.GoPackedArray(unsafe.Pointer(arr)) if err != nil { // gérer l'erreur } - // parcourir le slice dans l'ordre - for index, value := range goSlice { - // faire quelque chose avec index et value - } + // parcourir le slice dans l'ordre + for index, value := range goSlice { + // faire quelque chose avec index et value + } - // retourner un tableau packed - return frankenphp.PHPPackedArray([]string{"value1", "value2", "value3"}) + // retourner un tableau packed + return frankenphp.PHPPackedArray([]string{"value1", "value2", "value3"}) } ``` **Fonctionnalités clés de la conversion de tableaux :** -- **Paires clé-valeur ordonnées** - Option pour conserver l'ordre du tableau associatif -- **Optimisé pour plusieurs cas** - Option de ne pas conserver l'ordre pour de meilleures performances ou conversion directe vers un slice -- **Détection automatique de liste** - Lors de la conversion vers PHP, détecte automatiquement si le tableau doit être une liste packed ou un hashmap -- **Tableaux imbriqués** - Les tableaux peuvent être imbriqués et convertiront automatiquement tous les types supportés (`int64`, `float64`, `string`, `bool`, `nil`, `AssociativeArray`, `map[string]any`, `[]any`) -- **Les objets ne sont pas supportés** - Actuellement, seuls les types scalaires et les tableaux peuvent être utilisés comme valeurs. Fournir un objet résultera en une valeur `null` dans le tableau PHP. +- **Paires clé-valeur ordonnées** - Option pour conserver l'ordre du tableau associatif +- **Optimisé pour plusieurs cas** - Option de ne pas conserver l'ordre pour de meilleures performances ou conversion directe vers un slice +- **Détection automatique de liste** - Lors de la conversion vers PHP, détecte automatiquement si le tableau doit être une liste packed ou un hashmap +- **Tableaux imbriqués** - Les tableaux peuvent être imbriqués et convertiront automatiquement tous les types supportés (`int64`,`float64`,`string`,`bool`,`nil`,`AssociativeArray`,`map[string]any`,`[]any`) +- **Les objets ne sont pas supportés** - Actuellement, seuls les types scalaires et les tableaux peuvent être utilisés comme valeurs. Fournir un objet résultera en une valeur `null` dans le tableau PHP. ##### Méthodes disponibles : Packed et Associatif -- `frankenphp.PHPAssociativeArray(arr frankenphp.AssociativeArray) unsafe.Pointer` - Convertir vers un tableau PHP ordonné avec des paires clé-valeur -- `frankenphp.PHPMap(arr map[string]any) unsafe.Pointer` - Convertir une map vers un tableau PHP non ordonné avec des paires clé-valeur -- `frankenphp.PHPPackedArray(slice []any) unsafe.Pointer` - Convertir un slice vers un tableau PHP packed avec uniquement des valeurs indexées -- `frankenphp.GoAssociativeArray(arr unsafe.Pointer, ordered bool) frankenphp.AssociativeArray` - Convertir un tableau PHP vers un `AssociativeArray` Go ordonné (map avec ordre) -- `frankenphp.GoMap(arr unsafe.Pointer) map[string]any` - Convertir un tableau PHP vers une map Go non ordonnée -- `frankenphp.GoPackedArray(arr unsafe.Pointer) []any` - Convertir un tableau PHP vers un slice Go -- `frankenphp.IsPacked(zval *C.zend_array) bool` - Vérifie si le tableau PHP est une liste ou un tableau associatif +- `frankenphp.PHPAssociativeArray(arr frankenphp.AssociativeArray) unsafe.Pointer` - Convertir vers un tableau PHP ordonné avec des paires clé-valeur +- `frankenphp.PHPMap(arr map[string]any) unsafe.Pointer` - Convertir une map vers un tableau PHP non ordonné avec des paires clé-valeur +- `frankenphp.PHPPackedArray(slice []any) unsafe.Pointer` - Convertir un slice vers un tableau PHP packed avec uniquement des valeurs indexées +- `frankenphp.GoAssociativeArray(arr unsafe.Pointer, ordered bool) frankenphp.AssociativeArray` - Convertir un tableau PHP vers un `AssociativeArray` Go ordonné (map avec ordre) +- `frankenphp.GoMap(arr unsafe.Pointer) map[string]any` - Convertir un tableau PHP vers une map Go non ordonnée +- `frankenphp.GoPackedArray(arr unsafe.Pointer) []any` - Convertir un tableau PHP vers un slice Go +- `frankenphp.IsPacked(zval *C.zend_array) bool` - Vérifie si le tableau PHP est une liste ou un tableau associatif ### Travailler avec des Callables -FrankenPHP propose un moyen de travailler avec les _callables_ PHP grâce au helper `frankenphp.CallPHPCallable()`. Cela permet d'appeler des fonctions ou des méthodes PHP depuis du code Go. +FrankenPHP propose un moyen de travailler avec les _callables_ PHP grâce au helper `frankenphp.CallPHPCallable`. Cela permet d'appeler des fonctions ou des méthodes PHP depuis du code Go. Pour illustrer cela, créons notre propre fonction `array_map()` qui prend un _callable_ et un tableau, applique le _callable_ à chaque élément du tableau, et retourne un nouveau tableau avec les résultats : @@ -263,13 +264,13 @@ type UserStruct struct { #### Que sont les Classes Opaques ? -Les **classes opaques** sont des classes avec lesquelles la structure interne (comprendre : les propriétés) est cachée du code PHP. Cela signifie : +Les **classes opaques** sont des classes où la structure interne est cachée du code PHP. Cela signifie : -- **Pas d'accès direct aux propriétés** : Vous ne pouvez pas lire ou écrire des propriétés directement depuis PHP (`$user->name` ne fonctionnera pas) -- **Interface uniquement par méthodes** - Toutes les interactions doivent passer par les méthodes que vous définissez -- **Meilleure encapsulation** - La structure de données interne est complètement contrôlée par le code Go -- **Sécurité de type** - Aucun risque que le code PHP corrompe l'état interne avec de mauvais types -- **API plus propre** - Force à concevoir une interface publique appropriée +- **Pas d'accès direct aux propriétés** : Vous ne pouvez pas lire ou écrire des propriétés directement depuis PHP (`$user->name` ne fonctionnera pas) +- **Interface uniquement par méthodes** - Toutes les interactions doivent passer par les méthodes que vous définissez +- **Meilleure encapsulation** - La structure de données interne est complètement contrôlée par le code Go +- **Sécurité de type** - Aucun risque que le code PHP corrompe l'état interne avec de mauvais types +- **API plus propre** - Force à concevoir une interface publique appropriée Cette approche fournit une meilleure encapsulation et empêche le code PHP de corrompre accidentellement l'état interne de vos objets Go. Toutes les interactions avec l'objet doivent passer par les méthodes que vous définissez explicitement. @@ -325,9 +326,9 @@ package example // #include import "C" import ( - "unsafe" + "unsafe" - "github.com/dunglas/frankenphp" + "github.com/dunglas/frankenphp" ) //export_php:method User::updateInfo(?string $name, ?int $age, ?bool $active): void @@ -351,10 +352,10 @@ func (us *UserStruct) UpdateInfo(name *C.zend_string, age *int64, active *bool) **Points clés sur les paramètres nullables :** -- **Types primitifs nullables** (`?int`, `?float`, `?bool`) deviennent des pointeurs (`*int64`, `*float64`, `*bool`) en Go -- **Chaînes nullables** (`?string`) restent comme `*C.zend_string` mais peuvent être `nil` -- **Vérifiez `nil`** avant de déréférencer les valeurs de pointeur -- **PHP `null` devient Go `nil`** - quand PHP passe `null`, votre fonction Go reçoit un pointeur `nil` +- **Types primitifs nullables** (`?int`, `?float`, `?bool`) deviennent des pointeurs (`*int64`, `*float64`, `*bool`) en Go +- **Chaînes nullables** (`?string`) restent comme `*C.zend_string` mais peuvent être `nil` +- **Vérifiez `nil`** avant de déréférencer les valeurs de pointeur +- **PHP `null` devient Go `nil`** - quand PHP passe `null`, votre fonction Go reçoit un pointeur `nil` > [!WARNING] > Actuellement, les méthodes de classe ont les limitations suivantes. **Les objets ne sont pas supportés** comme types de paramètres ou types de retour. **Les tableaux sont entièrement supportés** pour les paramètres et types de retour. Types supportés : `string`, `int`, `float`, `bool`, `array`, et `void` (pour le type de retour). **Les types de paramètres nullables sont entièrement supportés** pour tous les types scalaires (`?string`, `?int`, `?float`, `?bool`). @@ -459,10 +460,10 @@ package example // #include import "C" import ( - "strings" - "unsafe" + "strings" + "unsafe" - "github.com/dunglas/frankenphp" + "github.com/dunglas/frankenphp" ) //export_php:const @@ -479,43 +480,43 @@ const MODE_UPPERCASE = 2 //export_php:function repeat_this(string $str, int $count, int $mode): string func repeat_this(s *C.zend_string, count int64, mode int) unsafe.Pointer { - str := frankenphp.GoString(unsafe.Pointer(s)) + str := frankenphp.GoString(unsafe.Pointer(s)) - result := strings.Repeat(str, int(count)) - if mode == STR_REVERSE { - // inverser la chaîne - } + result := strings.Repeat(str, int(count)) + if mode == STR_REVERSE { + // inverser la chaîne + } - if mode == STR_NORMAL { - // no-op, juste pour montrer la constante - } + if mode == STR_NORMAL { + // no-op, juste pour montrer la constante + } - return frankenphp.PHPString(result, false) + return frankenphp.PHPString(result, false) } //export_php:class StringProcessor type StringProcessorStruct struct { - // champs internes + // champs internes } //export_php:method StringProcessor::process(string $input, int $mode): string func (sp *StringProcessorStruct) Process(input *C.zend_string, mode int64) unsafe.Pointer { - str := frankenphp.GoString(unsafe.Pointer(input)) + str := frankenphp.GoString(unsafe.Pointer(input)) - switch mode { - case MODE_LOWERCASE: - str = strings.ToLower(str) - case MODE_UPPERCASE: - str = strings.ToUpper(str) - } + switch mode { + case MODE_LOWERCASE: + str = strings.ToLower(str) + case MODE_UPPERCASE: + str = strings.ToUpper(str) + } - return frankenphp.PHPString(str, false) + return frankenphp.PHPString(str, false) } ``` ### Utilisation des Espaces de Noms -Le générateur prend en charge l'organisation des fonctions, classes et constantes de votre extension PHP sous un espace de noms (namespace) en utilisant la directive `//export_php:namespace`. Cela aide à éviter les conflits de noms et fournit une meilleure organisation pour l'API de votre extension. +Le générateur prend en charge l'organisation des fonctions, classes et constantes de votre extension PHP sous un espace de noms en utilisant la directive `//export_php:namespace`. Cela aide à éviter les conflits de noms et fournit une meilleure organisation pour l'API de votre extension. #### Déclarer un Espace de Noms @@ -567,10 +568,10 @@ echo My\Extension\STATUS_ACTIVE; // 1 #### Notes Importantes -- Seule **une** directive d'espace de noms est autorisée par fichier. Si plusieurs directives d'espace de noms sont trouvées, le générateur retournera une erreur. -- L'espace de noms s'applique à **tous** les symboles exportés dans le fichier : fonctions, classes, méthodes et constantes. -- Les noms d'espaces de noms suivent les conventions des espaces de noms PHP en utilisant les barres obliques inverses (`\`) comme séparateurs. -- Si aucun espace de noms n'est déclaré, les symboles sont exportés vers l'espace de noms global comme d'habitude. +- Seule **une** directive d'espace de noms est autorisée par fichier. Si plusieurs directives d'espace de noms sont trouvées, le générateur retournera une erreur. +- L'espace de noms s'applique à **tous** les symboles exportés dans le fichier : fonctions, classes, méthodes et constantes. +- Les noms d'espaces de noms suivent les conventions des espaces de noms PHP en utilisant les barres obliques inverses (`\`) comme séparateurs. +- Si aucun espace de noms n'est déclaré, les symboles sont exportés vers l'espace de noms global comme d'habitude. ### Générer l'Extension @@ -621,7 +622,7 @@ Une fois que vous avez intégré votre extension dans FrankenPHP comme indiqué ## Implémentation Manuelle -Si vous voulez comprendre comment les extensions fonctionnent ou avez besoin d'un contrôle total sur votre extension, vous pouvez les écrire manuellement. Cette approche vous donne un contrôle complet mais nécessite plus de code intermédiaire. +Si vous voulez comprendre comment les extensions fonctionnent ou avez besoin d'un contrôle total sur votre extension, vous pouvez les écrire manuellement. Cette approche vous donne un contrôle complet mais nécessite plus de code standard. ### Fonction de Base @@ -637,21 +638,21 @@ package example // #include "extension.h" import "C" import ( - "log/slog" - "unsafe" + "log/slog" + "unsafe" - "github.com/dunglas/frankenphp" + "github.com/dunglas/frankenphp" ) func init() { - frankenphp.RegisterExtension(unsafe.Pointer(&C.ext_module_entry)) + frankenphp.RegisterExtension(unsafe.Pointer(&C.ext_module_entry)) } //export go_print_something func go_print_something() { - go func() { - slog.Info("Hello from a goroutine!") - }() + go func() { + slog.Info("Hello from a goroutine!") + }() } ``` @@ -698,9 +699,9 @@ extern zend_module_entry ext_module_entry; Ensuite, créez un fichier nommé `extension.c` qui effectuera les étapes suivantes : -- Inclure les en-têtes PHP ; -- Déclarer notre nouvelle fonction PHP native `go_print()` ; -- Déclarer les métadonnées de l'extension. +- Inclure les en-têtes PHP ; +- Déclarer notre nouvelle fonction PHP native `go_print()` ; +- Déclarer les métadonnées de l'extension. Commençons par inclure les en-têtes requis : diff --git a/docs/fr/github-actions.md b/docs/fr/github-actions.md index 2a151aab6b..c701548e41 100644 --- a/docs/fr/github-actions.md +++ b/docs/fr/github-actions.md @@ -27,5 +27,5 @@ Dans les paramètres du dépôt, sous "secrets", ajoutez les secrets suivants : 1. Créez un nouveau tag dans le dépôt. 2. GitHub Actions va construire l'image et exécuter tous les tests. -3. Si la compilation est réussie, l'image sera poussée vers le registre en utilisant le nom du tag comme tag (par exemple, `v1.2.3` et `v1.2` seront créés). +3. Si la construction est réussie, l'image sera poussée vers le registre en utilisant le nom du tag comme tag (par exemple, `v1.2.3` et `v1.2` seront créés). 4. Le tag `latest` sera également mis à jour. diff --git a/docs/fr/hot-reload.md b/docs/fr/hot-reload.md new file mode 100644 index 0000000000..2bf5fc5243 --- /dev/null +++ b/docs/fr/hot-reload.md @@ -0,0 +1,139 @@ +# Rechargement à chaud + +FrankenPHP inclut une fonctionnalité de **rechargement à chaud** intégrée, conçue pour améliorer considérablement l'expérience développeur. + +![Mercure](hot-reload.png) + +Cette fonctionnalité offre un flux de travail similaire au **Remplacement de Module à Chaud (HMR)** que l'on trouve dans les outils JavaScript modernes (comme Vite ou webpack). +Au lieu de rafraîchir manuellement le navigateur après chaque modification de fichier (code PHP, templates, fichiers JavaScript et CSS...), +FrankenPHP met à jour le contenu en temps réel. + +Le rechargement à chaud fonctionne nativement avec WordPress, Laravel, Symfony, et toute autre application ou framework PHP. + +Lorsqu'il est activé, FrankenPHP surveille votre répertoire de travail actuel pour les modifications du système de fichiers. +Lorsqu'un fichier est modifié, il envoie une mise à jour [Mercure](mercure.md) au navigateur. + +Selon votre configuration, le navigateur : + +- **Transformera le DOM** (en préservant la position de défilement et l'état des entrées) si [Idiomorph](https://github.com/bigskysoftware/idiomorph) est chargé. +- **Rechargera la page** (rechargement en direct standard) si Idiomorph n'est pas présent. + +## Configuration + +Pour activer le rechargement à chaud, activez Mercure, puis ajoutez la sous-directive `hot_reload` à la directive `php_server` dans votre `Caddyfile`. + +> [!WARNING] +> Cette fonctionnalité est destinée aux **environnements de développement uniquement**. +> N'activez pas `hot_reload` en production, car la surveillance du système de fichiers entraîne une surcharge de performance et expose des points d'accès internes. + +```caddyfile +localhost + +mercure { + anonymous +} + +root public/ +php_server { + hot_reload +} +``` + +Par défaut, FrankenPHP surveillera tous les fichiers dans le répertoire de travail actuel correspondant à ce modèle glob : `./**/*.{css,env,gif,htm,html,jpg,jpeg,js,mjs,php,png,svg,twig,webp,xml,yaml,yml}` + +Il est possible de définir explicitement les fichiers à surveiller en utilisant la syntaxe glob : + +```caddyfile +localhost + +mercure { + anonymous +} + +root public/ +php_server { + hot_reload src/**/*{.php,.js} config/**/*.yaml +} +``` + +Utilisez la forme longue pour spécifier le sujet Mercure à utiliser ainsi que les répertoires ou fichiers à surveiller en fournissant des chemins à l'option `hot_reload` : + +```caddyfile +localhost + +mercure { + anonymous +} + +root public/ +php_server { + hot_reload { + topic hot-reload-topic + watch src/**/*.php + watch assets/**/*.{ts,json} + watch templates/ + watch public/css/ + } +} +``` + +## Intégration côté client + +Alors que le serveur détecte les changements, le navigateur doit s'abonner à ces événements pour mettre à jour la page. +FrankenPHP expose l'URL du Hub Mercure à utiliser pour s'abonner aux changements de fichiers via la variable d'environnement `$_SERVER['FRANKENPHP_HOT_RELOAD']`. + +Une bibliothèque JavaScript pratique, [frankenphp-hot-reload](https://www.npmjs.com/package/frankenphp-hot-reload), est également disponible pour gérer la logique côté client. +Pour l'utiliser, ajoutez ce qui suit à votre mise en page principale : + +```php + +Rechargement à chaud FrankenPHP + + + + + +``` + +La bibliothèque s'abonnera automatiquement au hub Mercure, récupérera l'URL actuelle en arrière-plan lorsqu'un changement de fichier est détecté et transformera le DOM. +Elle est disponible en tant que package [npm](https://www.npmjs.com/package/frankenphp-hot-reload) et sur [GitHub](https://github.com/dunglas/frankenphp-hot-reload). + +Alternativement, vous pouvez implémenter votre propre logique côté client en vous abonnant directement au hub Mercure en utilisant la classe JavaScript native `EventSource`. + +### Mode Worker + +Si vous exécutez votre application en [Mode Worker](https://frankenphp.dev/docs/worker/), le script de votre application reste en mémoire. +Cela signifie que les modifications apportées à votre code PHP ne seront pas reflétées immédiatement, même si le navigateur se recharge. + +Pour la meilleure expérience développeur, vous devez combiner `hot_reload` avec [la sous-directive `watch` dans la directive `worker`](config.md#watching-for-file-changes). + +- `hot_reload` : rafraîchit le **navigateur** lorsque les fichiers changent +- `worker.watch` : redémarre le worker lorsque les fichiers changent + +```caddy +localhost + +mercure { + anonymous +} + +root public/ +php_server { + hot_reload + worker { + file /path/to/my_worker.php + watch + } +} +``` + +### Comment ça marche + +1. **Surveillance** : FrankenPHP surveille le système de fichiers pour les modifications en utilisant la bibliothèque [`e-dant/watcher`](https://github.com/e-dant/watcher) en coulisses (nous avons contribué au binding Go). +2. **Redémarrage (Mode Worker)** : si `watch` est activé dans la configuration du worker, le worker PHP est redémarré pour charger le nouveau code. +3. **Poussée** : une charge utile JSON contenant la liste des fichiers modifiés est envoyée au [hub Mercure](https://mercure.rocks) intégré. +4. **Réception** : le navigateur, écoutant via la bibliothèque JavaScript, reçoit l'événement Mercure. +5. **Mise à jour** : + +- Si **Idiomorph** est détecté, il récupère le contenu mis à jour et transforme le HTML actuel pour correspondre au nouvel état, appliquant les changements instantanément sans perdre l'état. +- Sinon, `window.location.reload()` est appelé pour rafraîchir la page. diff --git a/docs/fr/known-issues.md b/docs/fr/known-issues.md index 8387e38811..5d97e5544b 100644 --- a/docs/fr/known-issues.md +++ b/docs/fr/known-issues.md @@ -11,11 +11,11 @@ Les extensions suivantes sont connues pour ne pas être compatibles avec Franken ## Extensions PHP boguées -Les extensions suivantes ont des bugs connus ou des comportements inattendus lorsqu'elles sont utilisées avec FrankenPHP : +Les extensions suivantes ont des bugs connus et 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). | +| Nom | Problème | +| ------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| [ext-openssl](https://www.php.net/manual/fr/book.openssl.php) | Lors de l'utilisation de musl libc, l'extension OpenSSL peut planter sous de fortes charges. Le problème ne se produit pas lors de l'utilisation de la plus populaire GNU libc. Ce bogue est [suivi par PHP](https://github.com/php/php-src/issues/13648). | ## get_browser @@ -25,6 +25,8 @@ La fonction [get_browser()](https://www.php.net/manual/fr/function.get-browser.p Le binaire autonome et les images Docker basées sur Alpine (`dunglas/frankenphp:*-alpine`) utilisent [musl libc](https://musl.libc.org/) au lieu de [glibc et ses amis](https://www.etalabs.net/compare_libcs.html), pour garder une taille de binaire plus petite. Cela peut entraîner des problèmes de compatibilité. En particulier, le drapeau glob `GLOB_BRACE` n'est [pas disponible](https://www.php.net/manual/fr/function.glob.php). +Préférez utiliser la variante GNU du binaire statique et les images Docker basées sur Debian si vous rencontrez des problèmes. + ## Utilisation de `https://127.0.0.1` avec Docker Par défaut, FrankenPHP génère un certificat TLS pour `localhost`. @@ -78,7 +80,7 @@ docker run \ ## 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/php/frankenphp/issues/483#issuecomment-1899890915) pour deux raisons : +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 [échoue actuellement](https://github.com/php/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. @@ -122,7 +124,7 @@ 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), +Inspectez la sortie de [`openssl_get_cert_locations()`](https://www.php.net/manual/fr/function.openssl-get-cert-locations.php), pour trouver l'endroit où les certificats CA doivent être installés et stockez-les à cet endroit. > [!WARNING] @@ -134,7 +136,7 @@ pour trouver l'endroit où les certificats CA doivent être installés et stocke 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 : +Il est également possible d'utiliser les variables `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 diff --git a/docs/fr/laravel.md b/docs/fr/laravel.md index 82677aef02..adda906196 100644 --- a/docs/fr/laravel.md +++ b/docs/fr/laravel.md @@ -2,7 +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 aussi simple que 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 : @@ -16,7 +16,7 @@ Et profitez ! Vous pouvez également exécuter vos projets Laravel avec FrankenPHP depuis votre machine locale : -1. [Téléchargez le binaire correspondant à votre système](README.md#binaire-autonome) +1. [Téléchargez le binaire correspondant à votre système](../#binaire-autonome) 2. Ajoutez la configuration suivante dans un fichier nommé `Caddyfile` placé dans le répertoire racine de votre projet Laravel : ```caddyfile @@ -66,7 +66,7 @@ La commande `octane:frankenphp` peut prendre les options suivantes : - `--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 +- `--caddyfile` : Le chemin vers le fichier `Caddyfile` de FrankenPHP (par défaut : [fichier `Caddyfile` pré-configuré dans Laravel Octane](https://github.com/laravel/octane/blob/2.x/src/Commands/stubs/Caddyfile)) - `--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 @@ -74,9 +74,11 @@ La commande `octane:frankenphp` peut prendre les options suivantes : - `--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'option `--log-level`. +> Pour obtenir des logs JSON structurés (utile lorsque 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). +Voir aussi [comment utiliser Mercure avec Octane](#mercure-support). + +En savoir plus sur [Laravel Octane dans sa documentation officielle](https://laravel.com/docs/octane). ## Les Applications Laravel En Tant Que Binaires Autonomes @@ -101,7 +103,7 @@ Suivez ces étapes pour empaqueter votre application Laravel en tant que binaire # Copiez le fichier .env RUN cp .env.example .env - # Modifier APP_ENV et APP_DEBUG pour qu'ils soient prêts pour la production + # Modifiez 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 @@ -109,7 +111,7 @@ Suivez ces étapes pour empaqueter votre application Laravel en tant que binaire # Installez les dépendances RUN composer install --ignore-platform-reqs --no-dev -a - # Construire le binaire statique + # Construisez le binaire statique WORKDIR /go/src/app/ RUN EMBED=dist/app/ ./build-static.sh ``` @@ -119,19 +121,19 @@ Suivez ces étapes pour empaqueter votre application Laravel en tant que binaire > 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: +2. Construction : ```console docker build -t static-laravel-app -f static-build.Dockerfile . ``` -3. Extraire le binaire +3. Extraction du 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 : +4. Population des caches : ```console frankenphp php-cli artisan optimize @@ -143,7 +145,7 @@ Suivez ces étapes pour empaqueter votre application Laravel en tant que binaire frankenphp php-cli artisan migrate ``` -6. Générer la clé secrète de l'application : +6. Génération de la clé secrète de l'application : ```console frankenphp php-cli artisan key:generate @@ -167,6 +169,34 @@ Ceci n'est pas adapté aux applications embarquées, car chaque nouvelle version 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. +### Mercure Support + +[Mercure](https://mercure.rocks) est un excellent moyen d'ajouter des capacités en temps réel à vos applications Laravel. +FrankenPHP inclut le [support de Mercure nativement](mercure.md). + +Si vous n'utilisez pas [Octane](#laravel-octane), consultez l'[entrée de la documentation Mercure](mercure.md). + +Si vous utilisez Octane, vous pouvez activer le support de Mercure en ajoutant les lignes suivantes à votre fichier `config/octane.php` : + +```php +// ... + +return [ + // ... + + 'mercure' => [ + 'anonymous' => true, + 'publisher_jwt' => '!ChangeThisMercureHubJWTSecretKey!', + 'subscriber_jwt' => '!ChangeThisMercureHubJWTSecretKey!', + ], +]; +``` + +Vous pouvez utiliser [toutes les directives supportées par Mercure](https://mercure.rocks/docs/hub/config#directives) dans ce tableau. + +Pour publier et s'abonner aux mises à jour, nous recommandons d'utiliser la bibliothèque [Laravel Mercure Broadcaster](https://github.com/mvanduijker/laravel-mercure-broadcaster). +Alternativement, consultez [la documentation Mercure](mercure.md) pour le faire en PHP pur et JavaScript. + ### Exécuter Octane avec des binaires autonomes Il est même possible d'empaqueter les applications Laravel Octane en tant que binaires autonomes ! @@ -182,4 +212,4 @@ 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 +> car Octane a besoin d'un programme nommé `frankenphp` disponible dans le chemin. diff --git a/docs/fr/logging.md b/docs/fr/logging.md new file mode 100644 index 0000000000..40c3183c13 --- /dev/null +++ b/docs/fr/logging.md @@ -0,0 +1,67 @@ +# Journalisation + +FrankenPHP s'intègre parfaitement au [système de journalisation de Caddy](https://caddyserver.com/docs/logging). Vous pouvez journaliser des messages en utilisant les fonctions PHP standard ou exploiter la fonction dédiée `frankenphp_log()` pour des capacités de journalisation structurée avancées. + +## `frankenphp_log()` + +La fonction `frankenphp_log()` vous permet d'émettre des journaux structurés directement depuis votre application PHP, facilitant grandement leur ingestion dans des plateformes comme Datadog, Grafana Loki ou Elastic, ainsi que le support d'OpenTelemetry. + +En interne, `frankenphp_log()` enveloppe le [package `log/slog` de Go](https://pkg.go.dev/log/slog) pour offrir des fonctionnalités de journalisation riches. + +Ces journaux incluent le niveau de sévérité et des données de contexte optionnelles. + +```php +function frankenphp_log(string $message, int $level = FRANKENPHP_LOG_LEVEL_INFO, array $context = []): void +``` + +### Paramètres + +- **`message`** : La chaîne de caractères du message de journal. +- **`level`** : Le niveau de sévérité du journal. Peut être n'importe quel entier arbitraire. Des constantes de commodité sont fournies pour les niveaux courants : `FRANKENPHP_LOG_LEVEL_DEBUG` (`-4`), `FRANKENPHP_LOG_LEVEL_INFO` (`0`), `FRANKENPHP_LOG_LEVEL_WARN` (`4`) et `FRANKENPHP_LOG_LEVEL_ERROR` (`8`)). La valeur par défaut est `FRANKENPHP_LOG_LEVEL_INFO`. +- **`context`** : Un tableau associatif de données additionnelles à inclure dans l'entrée du journal. + +### Exemple + +```php + memory_get_usage(), + 'peak_usage' => memory_get_peak_usage(), + ], +); + +``` + +Lorsque vous consultez les journaux (par exemple, via `docker compose logs`), la sortie apparaîtra sous forme de JSON structuré : + +```json +{"level":"info","ts":1704067200,"logger":"frankenphp","msg":"Hello from FrankenPHP!"} +{"level":"warn","ts":1704067200,"logger":"frankenphp","msg":"Memory usage high","current_usage":10485760,"peak_usage":12582912} +``` + +## `error_log()` + +FrankenPHP permet également la journalisation en utilisant la fonction standard `error_log()`. Si le paramètre `$message_type` est `4` (SAPI), ces messages sont acheminés vers le journaliseur de Caddy. + +Par défaut, les messages envoyés via `error_log()` sont traités comme du texte non structuré. Ils sont utiles pour la compatibilité avec les applications ou bibliothèques existantes qui s'appuient sur la bibliothèque PHP standard. + +### Exemple avec error_log() + +```php +error_log("Database connection failed", 4); +``` + +Ceci apparaîtra dans les journaux de Caddy, souvent préfixé pour indiquer qu'il provient de PHP. + +> [!TIP] +> Pour une meilleure observabilité dans les environnements de production, préférez `frankenphp_log()` +> car cela vous permet de filtrer les journaux par niveau (Débogage, Erreur, etc.) +> et d'interroger des champs spécifiques dans votre infrastructure de journalisation. diff --git a/docs/fr/mercure.md b/docs/fr/mercure.md index 4a2af8a506..908f6ce278 100644 --- a/docs/fr/mercure.md +++ b/docs/fr/mercure.md @@ -1,12 +1,149 @@ # Temps Réel -FrankenPHP est livré avec un hub [Mercure](https://mercure.rocks) intégré. +FrankenPHP est livré avec un hub [Mercure](https://mercure.rocks) intégré ! Mercure permet de pousser des événements en temps réel vers tous les appareils connectés : ils recevront un événement JavaScript instantanément. -Aucune bibliothèque JS ou SDK requis ! +C'est une alternative pratique aux WebSockets, simple à utiliser et nativement prise en charge par tous les navigateurs web modernes ! -![Mercure](../mercure-hub.png) +![Mercure](mercure-hub.png) -Pour activer le hub Mercure, mettez à jour le `Caddyfile` comme décrit [sur le site de Mercure](https://mercure.rocks/docs/hub/config). +## Activer Mercure -Pour pousser des mises à jour Mercure depuis votre code, nous recommandons le [Composant Mercure de Symfony](https://symfony.com/components/Mercure) (vous n'avez pas besoin du framework full stack Symfony pour l'utiliser). +Le support de Mercure est désactivé par défaut. +Voici un exemple minimal de `Caddyfile` activant à la fois FrankenPHP et le hub Mercure : + +```caddyfile +# L'hostname auquel répondre +localhost + +mercure { + # La clé secrète utilisée pour signer les jetons JWT pour les éditeurs + publisher_jwt !ChangeThisMercureHubJWTSecretKey! + # Autorise les abonnés anonymes (sans JWT) + anonymous +} + +root public/ +php_server +``` + +> [!TIP] +> +> Le [fichier Caddyfile d'exemple](https://github.com/php/frankenphp/blob/main/caddy/frankenphp/Caddyfile) +> fourni par [les images Docker](docker.md) inclut déjà une configuration Mercure commentée +> avec des variables d'environnement pratiques pour le configurer. +> +> Décommentez la section Mercure dans `/etc/frankenphp/Caddyfile` pour l'activer. + +## S'abonner aux mises à jour + +Par défaut, le hub Mercure est disponible sur le chemin `/.well-known/mercure` de votre serveur FrankenPHP. +Pour vous abonner aux mises à jour, utilisez la classe JavaScript native [`EventSource`](https://developer.mozilla.org/docs/Web/API/EventSource) : + +```html + + +Exemple Mercure + +``` + +## Publier des mises à jour + +### Utilisation de `mercure_publish()` + +FrankenPHP fournit une fonction pratique `mercure_publish()` pour publier des mises à jour vers le hub Mercure intégré : + +```php + 'value'])); + +// Écrit dans les logs de FrankenPHP +error_log("update $updateID published", 4); +``` + +La signature complète de la fonction est : + +```php +/** + * @param string|string[] $topics + */ +function mercure_publish(string|array $topics, string $data = '', bool $private = false, ?string $id = null, ?string $type = null, ?int $retry = null): string {} +``` + +### Utilisation de `file_get_contents()` + +Pour distribuer une mise à jour aux abonnés connectés, envoyez une requête POST authentifiée au hub Mercure avec les paramètres `topic` et `data` : + +```php + [ + 'method' => 'POST', + 'header' => "Content-type: application/x-www-form-urlencoded\r\nAuthorization: Bearer " . JWT, + 'content' => http_build_query([ + 'topic' => 'my-topic', + 'data' => json_encode(['key' => 'value']), + ]), +]])); + +// Écrit dans les logs de FrankenPHP +error_log("update $updateID published", 4); +``` + +La clé passée en paramètre de l'option `mercure.publisher_jwt` dans le `Caddyfile` doit être utilisée pour signer le jeton JWT utilisé dans l'en-tête `Authorization`. + +Le JWT doit inclure une revendication `mercure` avec une permission `publish` pour les sujets auxquels vous souhaitez publier. +Consultez [la documentation Mercure](https://mercure.rocks/spec#publishers) concernant l'autorisation. + +Pour générer vos propres jetons, vous pouvez utiliser [ce lien jwt.io](https://www.jwt.io/#token=eyJhbGciOiJIUzI1NiJ9.eyJtZXJjdXJlIjp7InB1Ymxpc2giOlsiKiJdfX0.PXwpfIGng6KObfZlcOXvcnWCJOWTFLtswGI5DZuWSK4), +mais pour les applications en production, il est recommandé d'utiliser des jetons de courte durée générés dynamiquement à l'aide d'une [bibliothèque JWT](https://www.jwt.io/libraries?programming_language=php) fiable. + +### Utilisation de Symfony Mercure + +Alternativement, vous pouvez utiliser le [Composant Mercure de Symfony](https://symfony.com/components/Mercure), une bibliothèque PHP autonome. + +Cette bibliothèque gère la génération de JWT, la publication de mises à jour ainsi que l'autorisation basée sur les cookies pour les abonnés. + +Tout d'abord, installez la bibliothèque à l'aide de Composer : + +```console +composer require symfony/mercure lcobucci/jwt +``` + +Ensuite, vous pouvez l'utiliser comme ceci : + +```php +publish(new \Symfony\Component\Mercure\Update('my-topic', json_encode(['key' => 'value']))); + +// Écrit dans les logs de FrankenPHP +error_log("update $updateID published", 4); +``` + +Mercure est également pris en charge nativement par : + +- [Laravel](laravel.md#mercure-support) +- [Symfony](https://symfony.com/doc/current/mercure.html) +- [API Platform](https://api-platform.com/docs/core/mercure/) diff --git a/docs/fr/metrics.md b/docs/fr/metrics.md index 697d2eebf1..e0445a553d 100644 --- a/docs/fr/metrics.md +++ b/docs/fr/metrics.md @@ -5,13 +5,13 @@ Lorsque les [métriques Caddy](https://caddyserver.com/docs/metrics) sont activ - `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. +- `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é. +Pour les métriques de worker, l'espace réservé `[nom_du_worker]` est remplacé par le nom du worker dans le Caddyfile, sinon le chemin absolu du fichier du worker sera utilisé. diff --git a/docs/fr/performance.md b/docs/fr/performance.md index e1531624a3..a065ca48a0 100644 --- a/docs/fr/performance.md +++ b/docs/fr/performance.md @@ -8,23 +8,23 @@ Cependant, il est possible d'améliorer considérablement les performances en ut 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. +Nous recommandons vivement de modifier ces valeurs. Pour une stabilité optimale du système, il est recommandé d'avoir `num_threads` x `memory_limit` < `available_memory`. Pour trouver les bonnes valeurs, il est souhaitable d'effectuer des tests de charge simulant le trafic réel. [k6](https://k6.io) et [Gatling](https://gatling.io) sont de bons outils pour cela. 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`. +Pour changer le nombre de workers, 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. +ont tendance à être plus imprévisibles. La [configuration](config.md#caddyfile-config) `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. +processus et les délègue automatiquement à différents scripts de worker et au 'mode classique' selon les besoins. ## Mode worker @@ -34,18 +34,16 @@ vous devez créer un script worker et vous assurer que l'application n'a pas de ## Ne pas utiliser musl -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). +La variante Alpine Linux des images Docker officielles et les binaires par défaut que nous fournissons utilisent [la bibliothèque musl](https://musl.libc.org). -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. +PHP est connu pour être [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. La différence peut être significative dans un environnement fortement threadé. -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). +De plus, [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. +Dans les environnements de production, nous recommandons d'utiliser FrankenPHP lié à glibc, compilé avec un niveau d'optimisation approprié. -Cela peut être réalisé en utilisant les images Docker Debian (par défaut) et [en compilant FrankenPHP à partir des sources](compile.md). - -Alternativement, nous fournissons des binaires statiques compilés avec [l'allocateur mimalloc](https://github.com/microsoft/mimalloc), ce qui rend FrankenPHP+musl plus rapide (mais toujours plus lent que FrankenPHP+glibc). +Cela peut être réalisé en utilisant les images Docker Debian, en utilisant nos paquets [.deb](https://debs.henderkes.com) ou [.rpm](https://rpms.henderkes.com) fournis par nos mainteneurs, ou en [compilant FrankenPHP à partir des sources](compile.md). ## Configuration du runtime Go @@ -54,12 +52,12 @@ FrankenPHP est écrit en Go. En général, le runtime Go ne nécessite pas de configuration particulière, mais dans certaines circonstances, une configuration spécifique améliore les performances. -Vous voudrez probablement mettre la variable d'environnement `GODEBUG` à `cgocheck=0` (la valeur par défaut dans les images Docker de FrankenPHP). +Vous voudrez probablement définir la variable d'environnement `GODEBUG` à `cgocheck=0` (la valeur par défaut dans les images Docker de FrankenPHP). Si vous exécutez FrankenPHP dans des conteneurs (Docker, Kubernetes, LXC...) et que vous limitez la mémoire disponible pour les conteneurs, -mettez la variable d'environnement `GOMEMLIMIT` à la quantité de mémoire disponible. +définissez la variable d'environnement `GOMEMLIMIT` à la quantité de mémoire disponible. -Pour plus de détails, [la page de documentation Go dédiée à ce sujet](https://pkg.go.dev/runtime#hdr-Environment_Variables) est à lire absolument pour tirer le meilleur parti du runtime. +Pour plus de détails, [la page de documentation Go dédiée à ce sujet](https://pkg.go.dev/runtime#hdr-Environment_Variables) est une lecture indispensable pour tirer le meilleur parti du runtime. ## `file_server` @@ -77,22 +75,22 @@ php_server { ## `try_files` -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, +Outre les fichiers statiques et les fichiers PHP, `php_server` essaiera également 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 d'indices de répertoire, 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 + 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 : +Une approche alternative avec 0 opération inutile sur le système de fichiers serait d'utiliser la directive `php` +et de séparer 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#caddyfile-config) qui sert des fichiers statiques derrière un dossier `/assets` pourrait ressembler à ceci : ```caddyfile route { @@ -105,25 +103,25 @@ route { root /root/to/your/app } - # tout ce qui n'est pas dans /assets est géré par votre index ou votre fichier PHP worker + # 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 + root /root/to/your/app # l'ajout explicite de la racine ici permet une meilleure mise en cache } } ``` -## _Placeholders_ +## Placeholders -Vous pouvez utiliser des [_placeholders_](https://caddyserver.com/docs/conventions#placeholders) dans les directives `root` et `env`. +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). -Si la racine du document n'est pas un lien symbolique, vous pouvez désactiver cette fonctionnalité. +Par défaut, si le document root est un lien symbolique, il est automatiquement résolu par FrankenPHP (c'est nécessaire pour que PHP fonctionne correctement). +Si le document root n'est pas un lien symbolique, vous pouvez désactiver cette fonctionnalité. ```caddyfile php_server { @@ -131,12 +129,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. @@ -155,3 +153,35 @@ En particulier : 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). + +## Division du pool de threads + +Il est courant que les applications interagissent avec des services externes lents, comme une +API qui a tendance à être peu fiable sous une forte charge ou qui met constamment plus de 10 secondes à répondre. +Dans de tels cas, il peut être bénéfique de diviser le pool de threads pour avoir des pools "lents" dédiés. +Cela empêche les points d'accès lents de consommer toutes les ressources/threads du serveur et +limite la concurrence des requêtes allant vers le point d'accès lent, à l'instar d'un pool de connexions. + +```caddyfile +{ + frankenphp { + max_threads 100 # 100 threads maximum partagés par tous les workers + } +} + +example.com { + php_server { + root /app/public # la racine de votre application + worker index.php { + match /slow-endpoint/* # toutes les requêtes avec le chemin /slow-endpoint/* sont gérées par ce pool de threads + num 10 # minimum 10 threads pour les requêtes correspondant à /slow-endpoint/* + } + worker index.php { + match * # toutes les autres requêtes sont gérées séparément + num 20 # minimum 20 threads pour les autres requêtes, même si les points d'accès lents commencent à bloquer + } + } +} +``` + +De manière générale, il est également conseillé de gérer les points d'accès très lents de manière asynchrone, en utilisant des mécanismes pertinents tels que les files de messages. diff --git a/docs/fr/production.md b/docs/fr/production.md index 7e1606c50e..50278b3140 100644 --- a/docs/fr/production.md +++ b/docs/fr/production.md @@ -2,7 +2,7 @@ Dans ce tutoriel, nous apprendrons comment déployer une application PHP sur un serveur unique en utilisant Docker Compose. -Si vous utilisez Symfony, lisez plutôt la page de documentation "[Déployer en production](https://github.com/dunglas/symfony-docker/blob/main/docs/production.md)" du projet Symfony Docker (qui utilise FrankenPHP). +Si vous utilisez Symfony, préférez lire l'entrée de documentation "[Déployer en production](https://github.com/dunglas/symfony-docker/blob/main/docs/production.md)" du projet Symfony Docker (qui utilise FrankenPHP). Si vous utilisez API Platform (qui utilise également FrankenPHP), référez-vous à [la documentation de déploiement du framework](https://api-platform.com/docs/deployment/). @@ -65,7 +65,7 @@ volumes: > (qui utilise FrankenPHP) pour un exemple plus avancé utilisant des images multi-étapes, > Composer, des extensions PHP supplémentaires, etc. -Pour finir, si vous utilisez Git, commitez ces fichiers et poussez-les. +Enfin, si vous utilisez Git, commitez ces fichiers et poussez-les. ## Préparer un serveur @@ -73,14 +73,14 @@ Pour déployer votre application en production, vous avez besoin d'un serveur. Dans ce tutoriel, nous utiliserons une machine virtuelle fournie par DigitalOcean, mais n'importe quel serveur Linux peut fonctionner. Si vous avez déjà un serveur Linux avec Docker installé, vous pouvez passer directement à [la section suivante](#configurer-un-nom-de-domaine). -Sinon, utilisez [ce lien affilié](https://m.do.co/c/5d8aabe3ab80) pour obtenir 200$ de crédit gratuit, créez un compte, puis cliquez sur "Créer un Droplet". -Ensuite, cliquez sur l'onglet "Marketplace" sous la section "Choisir une image" et recherchez l'application nommée "Docker". +Sinon, utilisez [ce lien affilié](https://m.do.co/c/5d8aabe3ab80) pour obtenir 200 $ de crédit gratuit, créez un compte, puis cliquez sur "Create a Droplet". +Ensuite, cliquez sur l'onglet "Marketplace" sous la section "Choose an image" et recherchez l'application nommée "Docker". Cela provisionnera un serveur Ubuntu avec les dernières versions de Docker et Docker Compose déjà installées ! Pour des fins de test, les plans les moins chers seront suffisants. -Pour une utilisation en production réelle, vous voudrez probablement choisir un plan dans la section "General Usage" pour répondre à vos besoins. +Pour une utilisation en production réelle, vous voudrez probablement choisir un plan dans la section "general purpose" pour répondre à vos besoins. -![Déployer FrankenPHP sur DigitalOcean avec Docker](../digitalocean-droplet.png) +![Déployer FrankenPHP sur DigitalOcean avec Docker](digitalocean-droplet.png) Vous pouvez conserver les paramètres par défaut pour les autres paramètres, ou les ajuster selon vos besoins. N'oubliez pas d'ajouter votre clé SSH ou de créer un mot de passe puis appuyez sur le bouton "Finalize and create". @@ -105,7 +105,7 @@ your-domain-name.example.com. IN A 207.154.233.113 Exemple avec le service DigitalOcean Domains ("Networking" > "Domains") : -![Configurer les DNS sur DigitalOcean](../digitalocean-dns.png) +![Configurer les DNS sur DigitalOcean](digitalocean-dns.png) > [!NOTE] > @@ -114,7 +114,7 @@ Exemple avec le service DigitalOcean Domains ("Networking" > "Domains") : ## Déploiement Copiez votre projet sur le serveur en utilisant `git clone`, `scp`, ou tout autre outil qui pourrait répondre à votre besoin. -Si vous utilisez GitHub, vous voudrez peut-être utiliser [une clef de déploiement](https://docs.github.com/en/free-pro-team@latest/developers/overview/managing-deploy-keys#deploy-keys). +Si vous utilisez GitHub, vous voudrez peut-être utiliser [une clé de déploiement](https://docs.github.com/en/free-pro-team@latest/developers/overview/managing-deploy-keys#deploy-keys). Les clés de déploiement sont également [prises en charge par GitLab](https://docs.gitlab.com/ee/user/project/deploy_keys/). Exemple avec Git : @@ -126,11 +126,11 @@ git clone git@github.com:/.git Accédez au répertoire contenant votre projet (``), et démarrez l'application en mode production : ```console -docker compose up -d --wait +docker compose up --wait ``` Votre serveur est opérationnel, et un certificat HTTPS a été automatiquement généré pour vous. -Rendez-vous sur `https://your-domain-name.example.com` ! +Rendez-vous sur `https://your-domain-name.example.com` et profitez-en ! > [!CAUTION] > diff --git a/docs/fr/static.md b/docs/fr/static.md index 1efebb18df..76db2698a9 100644 --- a/docs/fr/static.md +++ b/docs/fr/static.md @@ -1,15 +1,16 @@ # Créer un binaire statique -Au lieu d'utiliser une installation locale de la bibliothèque PHP, il est possible de créer un build statique de FrankenPHP grâce à l'excellent projet [static-php-cli](https://github.com/crazywhalecc/static-php-cli) (malgré son nom, ce projet prend en charge tous les SAPIs, pas seulement CLI). +Au lieu d'utiliser une installation locale de la bibliothèque PHP, +il est possible de créer un build statique ou principalement statique de FrankenPHP grâce à l'excellent projet [static-php-cli](https://github.com/crazywhalecc/static-php-cli) (malgré son nom, ce projet prend en charge tous les SAPIs, pas seulement CLI). 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. +Cependant, ils ne peuvent pas charger les extensions dynamiques de PHP (comme Xdebug) et ont quelques limitations parce qu'ils utilisent la libc musl. -La plupart des binaires statiques ne nécessitent que la `glibc` et peuvent charger des extensions dynamiques. +Les binaires principalement 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. +Lorsque c'est possible, nous recommandons d'utiliser des constructions principalement statiques basées sur glibc. FrankenPHP permet également [d'embarquer l'application PHP dans le binaire statique](embed.md). @@ -61,7 +62,7 @@ docker buildx bake --load --set static-builder-musl.args.PHP_EXTENSIONS=opcache, # ... ``` -Pour ajouter des bibliothèques permettant des fonctionnalités supplémentaires aux extensions que vous avez activées, vous pouvez utiliser l'argument Docker `PHP_EXTENSION_LIBS` : +Pour ajouter des bibliothèques permettant des fonctionnalités supplémentaires aux extensions que vous avez activées, vous pouvez passer l'argument Docker `PHP_EXTENSION_LIBS` : ```console docker buildx bake \ @@ -110,7 +111,7 @@ cd frankenphp ./build-static.sh ``` -Note : ce script fonctionne également sur Linux (et probablement sur d'autres Unix) et est utilisé en interne par le builder statique basé sur Docker que nous fournissons. +Note : ce script fonctionne également sur Linux (et probablement sur d'autres Unix) et est utilisé en interne par les images Docker que nous fournissons. ## Personnalisation de la construction @@ -123,9 +124,9 @@ Les variables d'environnement suivantes peuvent être transmises à `docker buil - `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 +- `NO_COMPRESS` : ne pas compresser le binaire résultant avec UPX +- `DEBUG_SYMBOLS` : lorsque défini, les symboles de débogage ne seront pas supprimés et seront ajoutés au binaire +- `MIMALLOC` : (expérimental, Linux seulement) remplace l'allocateur mallocng de musl par [mimalloc](https://github.com/microsoft/mimalloc) pour des performances améliorées. Nous recommandons d'utiliser cette option uniquement pour les constructions ciblant musl ; pour glibc, il est préférable de la désactiver et d'utiliser [`LD_PRELOAD`](https://microsoft.github.io/mimalloc/overrides.html) lors de l'exécution de votre binaire. - `RELEASE` : (uniquement pour les mainteneurs) lorsque défini, le binaire résultant sera uploadé sur GitHub ## Extensions diff --git a/docs/fr/wordpress.md b/docs/fr/wordpress.md new file mode 100644 index 0000000000..741ce5be7d --- /dev/null +++ b/docs/fr/wordpress.md @@ -0,0 +1,59 @@ +# WordPress + +Exécutez [WordPress](https://wordpress.org/) avec FrankenPHP pour profiter d'une pile moderne et performante avec HTTPS automatique, HTTP/3 et la compression Zstandard. + +## Installation Minimale + +1. [Téléchargez WordPress](https://wordpress.org/download/) +2. Extrayez l'archive ZIP et ouvrez un terminal dans le répertoire extrait +3. Exécutez : + + ```console + frankenphp php-server + ``` + +4. Allez sur `http://localhost/wp-admin/` et suivez les instructions d'installation +5. Profitez-en ! + +Pour une configuration prête pour la production, préférez utiliser `frankenphp run` avec un `Caddyfile` comme celui-ci : + +```caddyfile +example.com + +php_server +encode zstd br gzip +log +``` + +## Rechargement à chaud + +Pour utiliser la fonctionnalité de [rechargement à chaud](hot-reload.md) avec WordPress, activez [Mercure](mercure.md) et ajoutez la sous-directive `hot_reload` à la directive `php_server` dans votre `Caddyfile` : + +```caddyfile +localhost + +mercure { + anonymous +} + +php_server { + hot_reload +} +``` + +Ensuite, ajoutez le code nécessaire pour charger les bibliothèques JavaScript dans le fichier `functions.php` de votre thème WordPress : + +```php +function hot_reload() { + ?> + + + + + + [!TIP] @@ -79,11 +81,17 @@ require __DIR__.'/vendor/autoload.php'; $myApp = new \App\Kernel(); $myApp->boot(); -// En dehors de la boucle pour de meilleures performances (moins de travail effectué) +// Gestionnaire en dehors de la boucle pour de meilleures performances (moins de travail effectué) $handler = static function () use ($myApp) { - // Appelé lorsqu'une requête est reçue, - // les superglobales, php://input, etc., sont réinitialisés - echo $myApp->handle($_GET, $_POST, $_COOKIE, $_FILES, $_SERVER); + try { + // Appelé lorsqu'une requête est reçue, + // les superglobales, php://input, etc., sont réinitialisés + echo $myApp->handle($_GET, $_POST, $_COOKIE, $_FILES, $_SERVER); + } catch (\Throwable $exception) { + // `set_exception_handler` n'est appelé que lorsque le script worker se termine, + // ce qui n'est peut-être pas ce à quoi vous vous attendez, alors attrapez et gérez les exceptions ici + (new \MyCustomExceptionHandler)->handleException($exception); + } }; $maxRequests = (int)($_SERVER['MAX_REQUESTS'] ?? 0); @@ -133,27 +141,23 @@ Le code du worker précédent permet de configurer un nombre maximal de requête ### 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), +Bien qu'il soit possible de redémarrer les workers [en cas de changement de fichier](config.md#watching-for-file-changes), 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 +Si l'administration est activée dans votre [Caddyfile](config.md#caddyfile-config), 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 +### Échecs des workers 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). +(par exemple, une faute de frappe dans un script), FrankenPHP plantera avec l'erreur : `too many consecutive failures`. -Le nombre d'échecs consécutifs peut être configuré dans votre [Caddyfile](config.md#configuration-du-caddyfile) avec l'option `max_consecutive_failures` : +Le nombre d'échecs consécutifs peut être configuré dans votre [Caddyfile](config.md#caddyfile-config) avec l'option `max_consecutive_failures` : ```caddyfile frankenphp { @@ -185,4 +189,3 @@ $handler = static function () use ($workerServer) { }; // ... -``` diff --git a/docs/fr/x-sendfile.md b/docs/fr/x-sendfile.md index 1269436a89..449b643f9a 100644 --- a/docs/fr/x-sendfile.md +++ b/docs/fr/x-sendfile.md @@ -27,7 +27,7 @@ Tout d'abord, ajoutez la configuration suivante à votre `Caddyfile` pour active root public/ # ... -+ # Needed for Symfony, Laravel and other projects using the Symfony HttpFoundation component ++ # Nécessaire pour Symfony, Laravel et d'autres projets utilisant le composant Symfony HttpFoundation + request_header X-Sendfile-Type x-accel-redirect + request_header X-Accel-Mapping ../private-files=/private-files + @@ -38,7 +38,7 @@ Tout d'abord, ajoutez la configuration suivante à votre `Caddyfile` pour active + rewrite * {resp.header.X-Accel-Redirect} + method * GET + -+ # Remove the X-Accel-Redirect header set by PHP for increased security ++ # Supprime l'en-tête X-Accel-Redirect défini par PHP pour une sécurité accrue + header -X-Accel-Redirect + + file_server @@ -53,7 +53,7 @@ Tout d'abord, ajoutez la configuration suivante à votre `Caddyfile` pour active 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') ; +header('X-Accel-Redirect: file.txt'); ``` ## Projets utilisant le composant Symfony HttpFoundation (Symfony, Laravel, Drupal...) @@ -68,4 +68,3 @@ BinaryFileResponse::trustXSendfileTypeHeader(); $response = new BinaryFileResponse(__DIR__.'/../private-files/file.txt'); // ... -``` diff --git a/docs/ja/classic.md b/docs/ja/classic.md index 17e99641ad..2fec116e17 100644 --- a/docs/ja/classic.md +++ b/docs/ja/classic.md @@ -5,7 +5,7 @@ Caddyと同様に、FrankenPHPは無制限の接続を受け付け、[固定数のスレッド](config.md#caddyfile-config)でそれらを処理します。受け入れられキューに入れられる接続の数は、利用可能なシステムリソースによってのみ制限されます。 PHPスレッドプールは、起動時に初期化された固定数のスレッドで動作し、これはPHP-FPMの静的モードに相当します。また、PHP-FPMの動的モードと同様に、[実行時にスレッドを自動的にスケール](performance.md#max_threads)させることも可能です。 -キューに入った接続は、PHPスレッドが空くまで無期限に待機します。これを避けるために、FrankenPHP のグローバル設定内の `max_wait_time` [設定](config.md#caddyfile-config)を使って、リクエストが空きスレッドを待てる最大時間を制限し、それを超えるとリクエストが拒否されるようにできます。 +キューに入った接続は、PHPスレッドが空くまで無期限に待機します。これを避けるために、FrankenPHP のグローバル設定内の `max_wait_time` [設定](config.md#caddyfile-config)を使って、リクエストが空きPHPスレッドを待てる最大時間を制限し、それを超えるとリクエストが拒否されるようにできます。 加えて、[Caddy側で適切な書き込みタイムアウト](https://caddyserver.com/docs/caddyfile/options#timeouts)を設定することも可能です。 各Caddyインスタンスは、1つのFrankenPHPスレッドプールのみを起動し、すべての`php_server`ブロック間でこのプールを共有します。 diff --git a/docs/ja/compile.md b/docs/ja/compile.md index 03e7d6217a..40f42b23db 100644 --- a/docs/ja/compile.md +++ b/docs/ja/compile.md @@ -15,7 +15,7 @@ FrankenPHPと互換性のあるlibphpのバージョンをインストールす まず、まだインストールしていない場合は[Homebrew](https://brew.sh)をインストールしてください。 -次に、PHPのZTSバリアント、Brotli(オプション、圧縮サポート用)、watcher(オプション、ファイル変更検出用)をインストールします: +次に、PHPのZTSバリアント、Brotli(オプション、圧縮サポート用)、およびwatcher(オプション、ファイル変更検出用)をインストールします: ```console brew install shivammathur/php/php-zts brotli watcher @@ -24,7 +24,7 @@ brew link --overwrite --force shivammathur/php/php-zts ### PHPをコンパイルする場合 -別の方法として、FrankenPHPに必要なオプションを指定してPHPをソースからコンパイルすることもできます。 +別の方法として、FrankenPHPに必要なオプションを指定してPHPをソースからコンパイルすることもできます。以下の手順に従ってください。 まず、[PHPのソース](https://www.php.net/downloads.php)を取得して展開します: @@ -34,7 +34,7 @@ cd php-*/ ``` 次に、プラットフォームに応じて必要なオプションを指定して`configure`スクリプトを実行します。 -以下の`./configure`フラグは必須ですが、例えば拡張機能モジュールや追加機能をコンパイルするために他のフラグを追加することもできます。 +以下の`./configure`フラグは必須ですが、例えば拡張機能や追加機能をコンパイルするために他のフラグを追加することもできます。 #### Linux @@ -55,7 +55,7 @@ brew install libiconv bison brotli re2c pkg-config watcher echo 'export PATH="/opt/homebrew/opt/bison/bin:$PATH"' >> ~/.zshrc ``` -その後、以下のようにconfigureスクリプトを実行します: +その後、`configure`スクリプトを実行します: ```console ./configure \ @@ -76,19 +76,20 @@ sudo make install ## オプション依存関係のインストール -FrankenPHPの一部の機能は、システムにインストールされているオプションの依存パッケージに依存しています。 +FrankenPHPの一部の機能は、システムにインストールされているオプションの依存関係に依存しています。 または、Goコンパイラにビルドタグを渡すことで、これらの機能を無効にできます。 -| 機能 | 依存関係 | 無効にするためのビルドタグ | -| ------------------------------ | --------------------------------------------------------------------- | -------------------------- | -| Brotli圧縮 | [Brotli](https://github.com/google/brotli) | nobrotli | -| ファイル変更時のワーカー再起動 | [Watcher C](https://github.com/e-dant/watcher/tree/release/watcher-c) | nowatcher | +| 機能 | 依存関係 | 無効にするためのビルドタグ | +| :----------------------------- | :----------------------------------------------------------------------------------------------------------- | :------------------------- | +| Brotli圧縮 | [Brotli](https://github.com/google/brotli) | `nobrotli` | +| ファイル変更時のワーカー再起動 | [Watcher C](https://github.com/e-dant/watcher/tree/release/watcher-c) | `nowatcher` | +| [Mercure](mercure.md) | [Mercure Goライブラリ](https://pkg.go.dev/github.com/dunglas/mercure)(自動インストール、AGPLライセンス) | `nomercure` | ## Goアプリのコンパイル -いよいよ最終的なバイナリをビルドできるようになりました。 +これで最終的なバイナリをビルドできます。 -### xcaddyを使う場合 +### `xcaddy`を使う場合 推奨される方法は、[xcaddy](https://github.com/caddyserver/xcaddy)を使用してFrankenPHPをコンパイルする方法です。 `xcaddy`を使うと、[Caddyのカスタムモジュール](https://caddyserver.com/docs/modules/)やFrankenPHP拡張を簡単に追加できます: @@ -102,13 +103,18 @@ xcaddy build \ --output frankenphp \ --with github.com/dunglas/frankenphp/caddy \ --with github.com/dunglas/mercure/caddy \ - --with github.com/dunglas/vulcain/caddy + --with github.com/dunglas/vulcain/caddy \ + --with github.com/dunglas/caddy-cbrotli # 追加のCaddyモジュールとFrankenPHP拡張をここに追加 + # オプションで、FrankenPHPのソースからコンパイルしたい場合: + # --with github.com/dunglas/frankenphp=$(pwd) \ + # --with github.com/dunglas/frankenphp/caddy=$(pwd)/caddy + ``` > [!TIP] > -> musl libc(Alpine Linuxのデフォルト)とSymfonyを使用している場合、 +> `musl libc`(Alpine Linuxのデフォルト)とSymfonyを使用している場合、 > デフォルトのスタックサイズを増やす必要がある場合があります。 > そうしないと、`PHP Fatal error: Maximum call stack size of 83360 bytes reached during compilation. Try splitting expression`のようなエラーが発生する可能性があります。 > @@ -116,7 +122,7 @@ xcaddy build \ > `XCADDY_GO_BUILD_FLAGS=$'-ldflags "-w -s -extldflags \'-Wl,-z,stack-size=0x80000\'"'`のようなものに変更してください > (アプリの要件に応じてスタックサイズの値を変更してください)。 -### xcaddyを使用しない場合 +### `xcaddy`を使用しない場合 代替として、`xcaddy`を使わずに`go`コマンドを直接使ってFrankenPHPをコンパイルすることも可能です: diff --git a/docs/ja/config.md b/docs/ja/config.md index f11e117a5f..1de0a942f5 100644 --- a/docs/ja/config.md +++ b/docs/ja/config.md @@ -1,44 +1,76 @@ # 設定 -FrankenPHP、Caddy、そしてMercureやVulcainモジュールは、[Caddyでサポートされる形式](https://caddyserver.com/docs/getting-started#your-first-config)を使用して設定できます。 +FrankenPHP、Caddy、そして[Mercure](mercure.md)や[Vulcain](https://vulcain.rocks)モジュールは、[Caddyでサポートされる形式](https://caddyserver.com/docs/getting-started#your-first-config)を使用して設定できます。 -[Dockerイメージ](docker.md)では、`Caddyfile`は`/etc/frankenphp/Caddyfile`に配置されています。 -静的バイナリは、`frankenphp run`コマンドを実行したディレクトリ内の`Caddyfile`を参照します。 -また、`-c`または`--config`オプションでカスタムのパスを指定できます。 +最も一般的な形式は`Caddyfile`で、これはシンプルで人間が読みやすいテキスト形式です。 +デフォルトでは、FrankenPHPは現在のディレクトリで`Caddyfile`を探します。 +`-c`または`--config`オプションでカスタムパスを指定できます。 -PHP自体の設定は[`php.ini` ファイルを使用](https://www.php.net/manual/en/configuration.file.php)して行えます。 +PHPアプリケーションを配信するための最小限の`Caddyfile`を以下に示します: -インストール方法に応じて、PHPインタープリターは上記いずれかの場所にある設定ファイルを参照します。 +```caddyfile +# The hostname to respond to +localhost + +# Optionaly, the directory to serve files from, otherwise defaults to the current directory +#root public/ +php_server +``` + +より多くの機能を有効にし、便利な環境変数を提供する、より高度な`Caddyfile`は、[FrankenPHPリポジトリ](https://github.com/php/frankenphp/blob/main/caddy/frankenphp/Caddyfile)およびDockerイメージに含まれています。 + +PHP自体は、[`php.ini`ファイルを使用](https://www.php.net/manual/en/configuration.file.php)して設定できます。 + +インストール方法に応じて、FrankenPHPとPHPインタープリターは、以下に説明する場所で設定ファイルを探します。 ## Docker +FrankenPHP: + +- `/etc/frankenphp/Caddyfile`: メイン設定ファイル +- `/etc/frankenphp/Caddyfile.d/*.caddyfile`: 自動的に読み込まれる追加の設定ファイル + +PHP: + - `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-/` +- PHP拡張: `/usr/local/lib/php/extensions/no-debug-zts-/` - PHPプロジェクトが提供する公式テンプレートをコピーすることを推奨します: ```dockerfile FROM dunglas/frankenphp -# 本番環境: +# Production: RUN cp $PHP_INI_DIR/php.ini-production $PHP_INI_DIR/php.ini -# または開発環境: +# Or development: RUN cp $PHP_INI_DIR/php.ini-development $PHP_INI_DIR/php.ini ``` ## RPMおよびDebianパッケージ -- `php.ini`: `/etc/frankenphp/php.ini`(本番環境向けのプリセットの`php.ini`ファイルがデフォルトで提供されます) -- 追加の設定ファイル: `/etc/frankenphp/php.d/*.ini` -- PHP拡張モジュール: `/usr/lib/frankenphp/modules/` +FrankenPHP: + +- `/etc/frankenphp/Caddyfile`: メイン設定ファイル +- `/etc/frankenphp/Caddyfile.d/*.caddyfile`: 自動的に読み込まれる追加の設定ファイル + +PHP: + +- `php.ini`: `/etc/php-zts/php.ini`(本番環境向けのプリセットの`php.ini`ファイルがデフォルトで提供されます) +- 追加の設定ファイル: `/etc/php-zts/conf.d/*.ini` ## 静的バイナリ -- `php.ini`: `frankenphp run`または`frankenphp php-server`を実行したディレクトリ内、なければ`/etc/frankenphp/php.ini`を参照 +FrankenPHP: + +- 現在の作業ディレクトリ: `Caddyfile` + +PHP: + +- `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`のいずれかをコピーしてください +- PHP拡張: ロードできません、バイナリ自体にバンドルする必要があります +- [PHPソース](https://github.com/php/php-src/)で提供される`php.ini-production`または`php.ini-development`のいずれかをコピーしてください。 ## Caddyfileの設定 @@ -48,15 +80,14 @@ RUN cp $PHP_INI_DIR/php.ini-development $PHP_INI_DIR/php.ini ```caddyfile localhost { - # 圧縮を有効化(オプション) + # Enable compression (optional) encode zstd br gzip - # 現在のディレクトリ内のPHPファイルを実行し、アセットを配信 + # Execute PHP files in the current directory and serve assets php_server } ``` -グローバルオプションを使用してFrankenPHPを明示的に設定することもできます: -`frankenphp`の[グローバルオプション](https://caddyserver.com/docs/caddyfile/concepts#global-options)を使用してFrankenPHPを構成できます。 +FrankenPHPを明示的に設定するには、`frankenphp`の[グローバルオプション](https://caddyserver.com/docs/caddyfile/concepts#global-options)を使用します: ```caddyfile { @@ -122,13 +153,13 @@ other.example.com { ```caddyfile route { - # ディレクトリへのリクエストに末尾スラッシュを追加 + # Add trailing slash for directory requests @canonicalPath { file {path}/index.php not path */ } redir @canonicalPath {path}/ 308 - # 要求されたファイルが存在しない場合は、indexファイルを試行 + # If the requested file does not exist, try index files @indexFiles file { try_files {path} {path}/index.php index.php split_path .php @@ -152,7 +183,7 @@ php_server [] { file_server off # 組み込みのfile_serverディレクティブを無効にします。 worker { # このサーバー固有のワーカーを作成します。複数のワーカーに対して複数回指定できます。 file # ワーカースクリプトへのパスを設定します。php_serverのルートからの相対パスとなります。 - num # 起動するPHPスレッド数を設定します。デフォルトは利用可能なスレッド数の 2 倍です。 + num # 起動するPHPスレッド数を設定します。デフォルトは利用可能なCPU数の 2 倍です。 name # ログとメトリクスで使用されるワーカーの名前を設定します。デフォルト: ワーカーファイルの絶対パス。php_server ブロックで定義されている場合は、常にm#で始まります。 watch # ファイルの変更を監視するパスを設定する。複数のパスに対して複数回指定することができる。 env # 追加の環境変数を指定された値に設定する。複数の環境変数を指定する場合は、複数回指定することができます。このワーカーの環境変数もphp_serverの親から継承されますが、 ここで上書きすることもできます。 @@ -181,8 +212,10 @@ PHPファイルに変更を加えても即座には反映されません。 } ``` -`watch`ディレクトリが指定されていない場合、`./**/*.{php,yaml,yml,twig,env}`にフォールバックします。 -これは、FrankenPHPプロセスが開始されたディレクトリおよびそのサブディレクトリ内のすべての`.php`、`.yaml`、`.yml`、`.twig`、`.env`ファイルすべてを監視します。 +この機能は、[ホットリロード](hot-reload.md)と組み合わせてよく使用されます。 + +`watch`ディレクトリが指定されていない場合、`./**/*.{env,php,twig,yaml,yml}`にフォールバックします。 +これは、FrankenPHPプロセスが開始されたディレクトリおよびそのサブディレクトリ内のすべての`.env`、`.php`、`.twig`、`.yaml`、`.yml`ファイルを監視します。 代わりに、[シェルのファイル名パターン](https://pkg.go.dev/path/filepath#Match)を使用して 1つ以上のディレクトリを指定することもできます: @@ -230,34 +263,6 @@ PHPファイルに変更を加えても即座には反映されません。 } ``` -### フルデュプレックス(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`を直接変更せずにCaddyディレクティブを注入できます: @@ -284,7 +289,7 @@ FPM や CLI SAPI と同様に、環境変数はデフォルトで`$_SERVER`ス frankenphp { php_ini memory_limit 256M - # または + # or php_ini { memory_limit 256M @@ -294,6 +299,43 @@ FPM や CLI SAPI と同様に、環境変数はデフォルトで`$_SERVER`ス } ``` +### HTTPSの無効化 + +デフォルトでは、FrankenPHPは`localhost`を含むすべてのホスト名に対してHTTPSを自動的に有効にします。 +HTTPSを無効にしたい場合(例えば開発環境で)、`SERVER_NAME`環境変数を`http://`または`:80`に設定できます: + +あるいは、[Caddyドキュメント](https://caddyserver.com/docs/automatic-https#activation)に記載されている他のすべての方法を使用することもできます。 + +`localhost`ホスト名ではなく`127.0.0.1`IPアドレスでHTTPSを使用したい場合は、[既知の問題](known-issues.md#using-https127001-with-docker)セクションをご覧ください。 + +### フルデュプレックス(HTTP/1) + +HTTP/1.xを使用する場合、全体のボディが読み取られる前にレスポンスを書き込めるようにするため、 +フルデュプレックスモードを有効にすることが望ましい場合があります(例:[Mercure](mercure.md)、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)をご覧ください。 + ## デバッグモードの有効化 Dockerイメージを使用する場合、`CADDY_GLOBAL_OPTIONS`環境変数に`debug`を設定するとデバッグモードが有効になります: diff --git a/docs/ja/docker.md b/docs/ja/docker.md index 0cf141bfc4..1d8ff22721 100644 --- a/docs/ja/docker.md +++ b/docs/ja/docker.md @@ -1,13 +1,14 @@ # カスタムDockerイメージのビルド -[FrankenPHPのDockerイメージ](https://hub.docker.com/r/dunglas/frankenphp)は、[公式PHPイメージ](https://hub.docker.com/_/php/)をベースにしています。主要なアーキテクチャに対してDebianとAlpine Linuxのバリアントを提供しており、Debianバリアントの使用を推奨しています。 +[FrankenPHPのDockerイメージ](https://hub.docker.com/r/dunglas/frankenphp)は、[公式PHPイメージ](https://hub.docker.com/_/php/)をベースにしています。 +主要なアーキテクチャに対してDebianとAlpine Linuxのバリアントを提供しており、Debianバリアントの使用を推奨しています。 PHP 8.2、8.3、8.4、8.5向けのバリアントが提供されています。 タグは次のパターンに従います:`dunglas/frankenphp:-php-` - ``および``は、それぞれFrankenPHPおよびPHPのバージョン番号で、メジャー(例:`1`)、マイナー(例:`1.2`)からパッチバージョン(例:`1.2.3`)まであります。 -- ``は`bookworm`(Debian Bookworm用)または`alpine`(Alpine最新安定版用)のいずれかです。 +- ``は`trixie`(Debian Trixie用)、`bookworm`(Debian Bookworm用)、または`alpine`(Alpine最新安定版用)のいずれかです。 [タグを閲覧](https://hub.docker.com/r/dunglas/frankenphp/tags)。 @@ -28,6 +29,10 @@ docker build -t my-php-app . docker run -it --rm --name my-running-app my-php-app ``` +## 設定を微調整する方法 + +利便性のため、有用な環境変数を含む[デフォルトの`Caddyfile`](https://github.com/php/frankenphp/blob/main/caddy/frankenphp/Caddyfile)がイメージに含まれています。 + ## PHP拡張モジュールの追加インストール方法 ベースイメージには[`docker-php-extension-installer`](https://github.com/mlocati/docker-php-extension-installer)スクリプトが含まれており、 @@ -156,8 +161,8 @@ RUN \ 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 + # /config/caddy および /data/caddy への書き込み権限を付与 + chown -R ${USER}:${USER} /config/caddy /data/caddy USER ${USER} ``` @@ -180,8 +185,8 @@ RUN \ useradd ${USER}; \ # デフォルトのケーパビリティを削除 setcap -r /usr/local/bin/frankenphp; \ - # /data/caddy と /config/caddy への書き込み権限を付与 - chown -R ${USER}:${USER} /data/caddy && chown -R ${USER}:${USER} /config/caddy + # /config/caddy と /data/caddy への書き込み権限を付与 + chown -R ${USER}:${USER} /config/caddy /data/caddy USER ${USER} ``` @@ -202,4 +207,4 @@ Dockerイメージは以下のタイミングでビルドされます: GitHubリポジトリのmainブランチにコミットがpushされるたびに新しいビルドが実行されます。 `latest*`タグは`main`ブランチのヘッドを指しており、 -`sha-` 形式のタグも利用可能です。 +`sha-` 形式のタグも利用可能です。 \ No newline at end of file diff --git a/docs/ja/extensions.md b/docs/ja/extensions.md index 9c140a5be6..ec5c4a0153 100644 --- a/docs/ja/extensions.md +++ b/docs/ja/extensions.md @@ -10,8 +10,8 @@ Caddyモジュールのおかげで、GoでPHP拡張モジュールを書いてF FrankenPHPでは、GoでPHP拡張モジュールを作成する2つの方法を提供します: -1. **拡張モジュールジェネレーターを使用** - ほとんどのユースケースに必要なボイラープレートを自動生成する推奨アプローチで、Goコードの記述に集中できます -2. **手動実装** - 拡張モジュール構造を細かく制御したい高度なユースケース +1. **拡張モジュールジェネレーターを使用** - ほとんどのユースケースに必要なボイラープレートを自動生成する推奨アプローチで、Goコードの記述に集中できます +2. **手動実装** - 拡張モジュール構造を細かく制御したい高度なユースケース 最初に始めやすいジェネレーター方式を紹介し、その後で完全な制御が必要な場合の手動実装方式を説明します。 @@ -33,7 +33,7 @@ FrankenPHPにはGoのみを使用して**PHP拡張モジュールを作成する GoでPHP拡張モジュールを書く最初のステップは、新しいGoモジュールの作成です。以下のコマンドを使用できます: ```console -go mod init github.com/my-account/my-module +go mod init example.com/example ``` 2番目のステップは、次のステップのために[PHPのソースを取得](https://www.php.net/downloads.php)することです。取得したら、Goモジュールのディレクトリ内ではなく、任意のディレクトリに展開します: @@ -47,10 +47,15 @@ tar xf php-* これでGoでネイティブ関数を書く準備が整いました。`stringext.go`という名前の新しいファイルを作成します。最初の関数は文字列を引数として取り、それを指定された回数だけ繰り返し、文字列を逆転するかどうかを示すブール値を受け取り、結果の文字列を返します。これは以下のようになります: ```go +package example + +// #include +import "C" import ( - "C" - "github.com/dunglas/frankenphp" "strings" + "unsafe" + + "github.com/dunglas/frankenphp" ) //export_php:function repeat_this(string $str, int $count, bool $reverse): string @@ -72,8 +77,8 @@ func repeat_this(s *C.zend_string, count int64, reverse bool) unsafe.Pointer { ここで重要なポイントが2つあります: -- ディレクティブコメント`//export_php:function`はPHPでの関数シグネチャを定義します。これにより、ジェネレーターは適切なパラメータと戻り値の型でPHP関数を生成する方法を知ることができます。 -- 関数は`unsafe.Pointer`を返さなければなりません。FrankenPHPはCとGo間の型変換を支援するAPIを提供しています。 +- ディレクティブコメント`//export_php:function`はPHPでの関数シグネチャを定義します。これにより、ジェネレーターは適切なパラメータと戻り値の型でPHP関数を生成する方法を知ることができます。 +- 関数は`unsafe.Pointer`を返さなければなりません。FrankenPHPはCとGo間の型変換を支援するAPIを提供しています。 前者は理解しやすいですが、後者は少し複雑かもしれません。次のセクションで型変換について詳しく説明します。 @@ -81,31 +86,190 @@ func repeat_this(s *C.zend_string, count int64, reverse bool) unsafe.Pointer { C/PHPとGoの間でメモリ表現が同じ変数型もありますが、直接使用するにはより多くのロジックが必要な型もあります。これは拡張モジュールを書く際の最も挑戦的な部分かもしれません。Zendエンジンの内部仕組みや、変数がPHP内でどのように格納されているかを理解する必要があるためです。以下の表は、知っておくべき重要な情報をまとめています: -| PHP型 | Go型 | 直接変換 | CからGoヘルパー | GoからCヘルパー | クラスメソッドサポート | -| ------------------ | ---------------- | -------- | --------------------- | ---------------------- | ---------------------- | -| `int` | `int64` | ✅ | - | - | ✅ | -| `?int` | `*int64` | ✅ | - | - | ✅ | -| `float` | `float64` | ✅ | - | - | ✅ | -| `?float` | `*float64` | ✅ | - | - | ✅ | -| `bool` | `bool` | ✅ | - | - | ✅ | -| `?bool` | `*bool` | ✅ | - | - | ✅ | -| `string`/`?string` | `*C.zend_string` | ❌ | frankenphp.GoString() | frankenphp.PHPString() | ✅ | -| `array` | `slice`/`map` | ❌ | _未実装_ | _未実装_ | ❌ | -| `mixed` | `any` | ❌ | `GoValue()` | `PHPValue()` | ❌ | -| `object` | `struct` | ❌ | _未実装_ | _未実装_ | ❌ | +| PHP型 | Go型 | 直接変換 | CからGoヘルパー | GoからCヘルパー | クラスメソッドサポート | +| :----------------- | :---------------------------- | :------- | :-------------------------------- | :--------------------------------- | :--------------------- | +| `int` | `int64` | ✅ | - | - | ✅ | +| `?int` | `*int64` | ✅ | - | - | ✅ | +| `float` | `float64` | ✅ | - | - | ✅ | +| `?float` | `*float64` | ✅ | - | - | ✅ | +| `bool` | `bool` | ✅ | - | - | ✅ | +| `?bool` | `*bool` | ✅ | - | - | ✅ | +| `string`/`?string` | `*C.zend_string` | ❌ | `frankenphp.GoString()` | `frankenphp.PHPString()` | ✅ | +| `array` | `frankenphp.AssociativeArray` | ❌ | `frankenphp.GoAssociativeArray()` | `frankenphp.PHPAssociativeArray()` | ✅ | +| `array` | `map[string]any` | ❌ | `frankenphp.GoMap()` | `frankenphp.PHPMap()` | ✅ | +| `array` | `[]any` | ❌ | `frankenphp.GoPackedArray()` | `frankenphp.PHPPackedArray()` | ✅ | +| `mixed` | `any` | ❌ | `GoValue()` | `PHPValue()` | ❌ | +| `callable` | `*C.zval` | ❌ | - | `frankenphp.CallPHPCallable()` | ❌ | +| `object` | `struct` | ❌ | _未実装_ | _未実装_ | ❌ | > [!NOTE] > この表はまだ完全ではなく、FrankenPHPの型APIがより完全になるにつれて完成されます。 > -> クラスメソッドについては、現在プリミティブ型のみがサポートされています。配列とオブジェクトはまだメソッドパラメータや戻り値の型として使用できません。 +> クラスメソッドについては、現在プリミティブ型と配列がサポートされています。オブジェクトはまだメソッドパラメータや戻り値の型として使用できません。 前のセクションのコードスニペットを参照すると、最初のパラメータと戻り値の変換にヘルパーが使用されていることがわかります。 `repeat_this()`関数の2番目と3番目の引数は、基礎となる型のメモリ表現がCとGoで同じであるため、変換する必要がありません。 +#### Working with Arrays + +FrankenPHPは`frankenphp.AssociativeArray`またはマップやスライスへの直接変換を通じて、PHP配列のネイティブサポートを提供します。 + +`AssociativeArray`は、`Map: map[string]any`フィールドと、オプションの`Order: []string`フィールド(PHPの「連想配列」とは異なり、Goのマップは順序付けされていません)で構成される[ハッシュマップ](https://en.wikipedia.org/wiki/Hash_table)を表します。 + +順序や関連付けが必要ない場合は、スライス`[]any`または順序なしマップ`map[string]any`に直接変換することも可能です。 + +**Goで配列を作成および操作する:** + +```go +package example + +// #include +import "C" +import ( + "unsafe" + + "github.com/dunglas/frankenphp" +) + +// export_php:function process_data_ordered(array $input): array +func process_data_ordered_map(arr *C.zend_array) unsafe.Pointer { + // Convert PHP associative array to Go while keeping the order + associativeArray, err := frankenphp.GoAssociativeArray[any](unsafe.Pointer(arr)) + if err != nil { + // handle error + } + + // loop over the entries in order + for _, key := range associativeArray.Order { + value, _ := associativeArray.Map[key] // Original has 'value, _ =' but 'value' is not declared, fixed to 'value, _ :='. It should be 'value, _ = associativeArray.Map[key]' + // do something with key and value + } + + // return an ordered array + // if 'Order' is not empty, only the key-value pairs in 'Order' will be respected + return frankenphp.PHPAssociativeArray[string](frankenphp.AssociativeArray[string]{ + Map: map[string]string{ + "key1": "value1", + "key2": "value2", + }, + Order: []string{"key1", "key2"}, + }) +} + +// export_php:function process_data_unordered(array $input): array +func process_data_unordered_map(arr *C.zend_array) unsafe.Pointer { + // Convert PHP associative array to a Go map without keeping the order + // ignoring the order will be more performant + goMap, err := frankenphp.GoMap[any](unsafe.Pointer(arr)) + if err != nil { + // handle error + } + + // loop over the entries in no specific order + for key, value := range goMap { + // do something with key and value + _ = key // Added to prevent unused variable error, not in original + _ = value // Added to prevent unused variable error, not in original + } + + // return an unordered array + return frankenphp.PHPMap(map[string]string { + "key1": "value1", + "key2": "value2", + }) +} + +// export_php:function process_data_packed(array $input): array +func process_data_packed(arr *C.zend_array) unsafe.Pointer { + // Convert PHP packed array to Go + goSlice, err := frankenphp.GoPackedArray(unsafe.Pointer(arr)) + if err != nil { + // handle error + } + + // loop over the slice in order + for index, value := range goSlice { + // do something with index and value + _ = index // Added to prevent unused variable error, not in original + _ = value // Added to prevent unused variable error, not in original + } + + // return a packed array + return frankenphp.PHPPackedArray([]string{"value1", "value2", "value3"}) +} +``` + +**配列変換の主な機能:** + +- **順序付けされたキーと値のペア** - 連想配列の順序を保持するオプション +- **複数のケースに最適化** - パフォーマンス向上のために順序を破棄したり、直接スライスに変換したりするオプション +- **自動リスト検出** - PHPに変換する際、配列がパックされたリストになるべきかハッシュマップになるべきかを自動的に検出 +- **ネストされた配列** - 配列はネストでき、すべてのサポートされる型(`int64`、`float64`、`string`、`bool`、`nil`、`AssociativeArray`、`map[string]any`、`[]any`)を自動的に変換します +- **オブジェクトはサポートされていません** - 現在、スカラー型と配列のみが値として使用できます。オブジェクトを提供するとPHP配列内で`null`値になります。 + +##### 利用可能なメソッド: パックおよび連想 + +- `frankenphp.PHPAssociativeArray(arr frankenphp.AssociativeArray) unsafe.Pointer` - キーと値のペアを持つ順序付きPHP配列に変換 +- `frankenphp.PHPMap(arr map[string]any) unsafe.Pointer` - マップをキーと値のペアを持つ順序なしPHP配列に変換 +- `frankenphp.PHPPackedArray(slice []any) unsafe.Pointer` - スライスをインデックス付き値のみのPHPパックされた配列に変換 +- `frankenphp.GoAssociativeArray(arr unsafe.Pointer, ordered bool) frankenphp.AssociativeArray` - PHP配列を順序付きGo `AssociativeArray` (順序付きマップ) に変換 +- `frankenphp.GoMap(arr unsafe.Pointer) map[string]any` - PHP配列を順序なしGoマップに変換 +- `frankenphp.GoPackedArray(arr unsafe.Pointer) []any` - PHP配列をGoスライスに変換 +- `frankenphp.IsPacked(zval *C.zend_array) bool` - PHP配列がパックされている(インデックスのみ)か連想配列(キーと値のペア)かを確認 + +### Working with Callables + +FrankenPHPは`frankenphp.CallPHPCallable`ヘルパーを使用してPHPコール可能オブジェクトを扱う方法を提供します。これにより、GoコードからPHP関数やメソッドを呼び出すことができます。 + +これを示すために、独自の`array_map()`関数を作成してみましょう。この関数はコール可能オブジェクトと配列を受け取り、配列の各要素にコール可能オブジェクトを適用し、結果を含む新しい配列を返します: + +```go +package example + +// #include +import "C" +import ( + "unsafe" + + "github.com/dunglas/frankenphp" +) + +// export_php:function my_array_map(array $data, callable $callback): array +func my_array_map(arr *C.zend_array, callback *C.zval) unsafe.Pointer { + goSlice, err := frankenphp.GoPackedArray[any](unsafe.Pointer(arr)) + if err != nil { + panic(err) + } + + result := make([]any, len(goSlice)) + + for index, value := range goSlice { + _ = index // Added to prevent unused variable error, not in original + result[index] = frankenphp.CallPHPCallable(unsafe.Pointer(callback), []interface{}{value}) + } + + return frankenphp.PHPPackedArray(result) +} +``` + +パラメータとして渡されたPHPコール可能オブジェクトを呼び出すために`frankenphp.CallPHPCallable()`を使用していることに注目してください。この関数はコール可能オブジェクトへのポインタと引数の配列を受け取り、コール可能オブジェクトの実行結果を返します。使い慣れたコール可能構文を使用できます: + +```php +name`は機能しません) -- **メソッド経由のみで操作** - すべてのやりとりはGoで定義したメソッドを通じて行う必要があります -- **より良いカプセル化** - 内部データ構造は完全にGoコードによって制御されます -- **型安全性** - PHP側から誤った型で内部状態が破壊されるリスクがありません -- **よりクリーンなAPI** - 適切な公開インターフェースを設計することを強制します +- **プロパティへの直接アクセス不可** :PHPから直接プロパティを読み書きできません(`$user->name`は機能しません) +- **メソッド経由のみで操作** - すべてのやりとりはGoで定義したメソッドを通じて行う必要があります +- **より良いカプセル化** - 内部データ構造は完全にGoコードによって制御されます +- **型安全性** - PHP側から誤った型で内部状態が破壊されるリスクがありません +- **よりクリーンなAPI** - 適切な公開インターフェースを設計することを強制します このアプローチは優れたカプセル化を実現し、PHPコードがGoオブジェクトの内部状態を意図せずに破壊してしまうことを防ぎます。オブジェクトとのすべてのやりとりは、明示的に定義したメソッドを通じて行う必要があります。 @@ -130,6 +294,16 @@ type UserStruct struct { プロパティは直接アクセスできないため、不透明クラスとやりとりするには **メソッドを定義する必要があります** 。`//export_php:method`ディレクティブを使用して動作を定義します: ```go +package example + +// #include +import "C" +import ( + "unsafe" + + "github.com/dunglas/frankenphp" +) + //export_php:class User type UserStruct struct { Name string @@ -162,19 +336,29 @@ func (us *UserStruct) SetNamePrefix(prefix *C.zend_string) { ジェネレーターは、PHPシグネチャにおける`?`プレフィックスを使用ったnullableパラメータをサポートしています。パラメータがnullableの場合、Go関数内ではポインタとして扱われ、PHP側で値が`null`だったかどうかを確認できます: ```go +package example + +// #include +import "C" +import ( + "unsafe" + + "github.com/dunglas/frankenphp" +) + //export_php:method User::updateInfo(?string $name, ?int $age, ?bool $active): void func (us *UserStruct) UpdateInfo(name *C.zend_string, age *int64, active *bool) { - // nameが渡された(nullではない)かチェック + // Check if name was provided (not null) if name != nil { us.Name = frankenphp.GoString(unsafe.Pointer(name)) } - // ageが渡された(nullではない)かチェック + // Check if age was provided (not null) if age != nil { us.Age = int(*age) } - // activeが渡された(nullではない)かチェック + // Check if active was provided (not null) if active != nil { us.Active = *active } @@ -183,13 +367,14 @@ func (us *UserStruct) UpdateInfo(name *C.zend_string, age *int64, active *bool) **Nullableパラメータの重要なポイント:** -- **プリミティブ型のnullable** (`?int`, `?float`, `?bool`) はGoではそれぞれポインタ (`*int64`, `*float64`, `*bool`) になります -- **nullable文字列** (`?string`) は `*C.zend_string` のままですが、`nil` になることがあります -- ポインタ値を逆参照する前に **`nil`をチェック** してください -- **PHPの`null`はGoの`nil`になります** - PHPが`null`を渡すと、Go関数は`nil`ポインタを受け取ります +- **プリミティブ型のnullable** (`?int`, `?float`, `?bool`) はGoではそれぞれポインタ (`*int64`, `*float64`, `*bool`) になります +- **nullable文字列** (`?string`) は `*C.zend_string` のままですが、`nil` になることがあります +- ポインタ値を逆参照する前に **`nil`をチェック** してください +- **PHPの`null`はGoの`nil`になります** - PHPが`null`を渡すと、Go関数は`nil`ポインタを受け取ります > [!WARNING] -> 現在、クラスメソッドには次の制限があります。**配列とオブジェクトはパラメータ型や戻り値の型としてサポートされていません**。サポートされるのは`string`、`int`、`float`、`bool`、`void`(戻り値の型)といったスカラー型のみです。**nullableなスカラー型はすべてサポートされています** (`?string`、`?int`、`?float`、`?bool`)。 +> +> 現在、クラスメソッドには次の制限があります。オブジェクトはパラメータ型や戻り値の型としてサポートされていません。配列はパラメータと戻り値の型の両方で完全にサポートされています。サポートされる型: `string`、`int`、`float`、`bool`、`array`、および `void`(戻り値の型)。Nullableパラメータ型は、すべてのスカラー型(`?string`、`?int`、`?float`、`?bool`)で完全にサポートされています。 拡張を生成した後、PHP側でクラスとそのメソッドを使用できるようになります。ただし**プロパティに直接アクセスできない**ことに注意してください: @@ -198,20 +383,20 @@ func (us *UserStruct) UpdateInfo(name *C.zend_string, age *int64, active *bool) $user = new User(); -// ✅ これは動作します - メソッドの使用 +// ✅ This works - using methods $user->setAge(25); -echo $user->getName(); // 出力: (empty、デフォルト値) -echo $user->getAge(); // 出力: 25 +echo $user->getName(); // Output: (empty, default value) +echo $user->getAge(); // Output: 25 $user->setNamePrefix("Employee"); -// ✅ これも動作します - nullableパラメータ -$user->updateInfo("John", 30, true); // すべて指定 -$user->updateInfo("Jane", null, false); // Ageがnull -$user->updateInfo(null, 25, null); // Nameとactiveがnull +// ✅ This also works - nullable parameters +$user->updateInfo("John", 30, true); // All parameters provided +$user->updateInfo("Jane", null, false); // Age is null +$user->updateInfo(null, 25, null); // Name and active are null -// ❌ これは動作しません - プロパティへの直接アクセス -// echo $user->name; // エラー: privateプロパティにアクセスできません -// $user->age = 30; // エラー: privateプロパティにアクセスできません +// ❌ This will NOT work - direct property access +// echo $user->name; // Error: Cannot access private property +// $user->age = 30; // Error: Cannot access private property ``` この設計により、Goコードがオブジェクトの状態へのアクセスと変更方法を完全に制御でき、より良いカプセル化と型安全性を提供します。 @@ -225,6 +410,8 @@ $user->updateInfo(null, 25, null); // Nameとactiveがnull `//export_php:const`ディレクティブを使用してグローバルなPHP定数を作成できます: ```go +package example + //export_php:const const MAX_CONNECTIONS = 100 @@ -243,6 +430,8 @@ const STATUS_ERROR = iota `//export_php:classconst ClassName`ディレクティブを使用して、特定のPHPクラスに属する定数を作成できます: ```go +package example + //export_php:classconst User const STATUS_ACTIVE = 1 @@ -267,11 +456,11 @@ const STATE_COMPLETED = iota ```php +import "C" import ( - "C" - "github.com/dunglas/frankenphp" - "strings" + "strings" + "unsafe" + + "github.com/dunglas/frankenphp" ) //export_php:const @@ -302,40 +496,99 @@ const MODE_UPPERCASE = 2 //export_php:function repeat_this(string $str, int $count, int $mode): string func repeat_this(s *C.zend_string, count int64, mode int) unsafe.Pointer { - str := frankenphp.GoString(unsafe.Pointer(s)) + str := frankenphp.GoString(unsafe.Pointer(s)) - result := strings.Repeat(str, int(count)) - if mode == STR_REVERSE { - // 文字列を逆転 - } + result := strings.Repeat(str, int(count)) + if mode == STR_REVERSE { + // reverse the string + } - if mode == STR_NORMAL { - // 何もしない、定数を示すためのみ記載 - } + if mode == STR_NORMAL { + // no-op, just to showcase the constant + } - return frankenphp.PHPString(result, false) + return frankenphp.PHPString(result, false) } //export_php:class StringProcessor type StringProcessorStruct struct { - // 内部フィールド + // internal fields } //export_php:method StringProcessor::process(string $input, int $mode): string func (sp *StringProcessorStruct) Process(input *C.zend_string, mode int64) unsafe.Pointer { - str := frankenphp.GoString(unsafe.Pointer(input)) + str := frankenphp.GoString(unsafe.Pointer(input)) - switch mode { - case MODE_LOWERCASE: - str = strings.ToLower(str) - case MODE_UPPERCASE: - str = strings.ToUpper(str) - } + switch mode { + case MODE_LOWERCASE: + str = strings.ToLower(str) + case MODE_UPPERCASE: + str = strings.ToUpper(str) + } - return frankenphp.PHPString(str, false) + return frankenphp.PHPString(str, false) } ``` +### Using Namespaces + +ジェネレーターは、`//export_php:namespace`ディレクティブを使用してPHP拡張モジュールの関数、クラス、定数を名前空間の下に整理することをサポートしています。これにより、命名衝突を避け、拡張モジュールのAPIの整理が向上します。 + +#### Declaring a Namespace + +Goファイルの先頭で`//export_php:namespace`ディレクティブを使用し、すべてのエクスポートされたシンボルを特定の名前空間の下に配置します: + +```go +//export_php:namespace My\Extension +package example + +import ( + "unsafe" + + "github.com/dunglas/frankenphp" +) + +//export_php:function hello(): string +func hello() string { + return "Hello from My\\Extension namespace!" +} + +//export_php:class User +type UserStruct struct { + // internal fields +} + +//export_php:method User::getName(): string +func (u *UserStruct) GetName() unsafe.Pointer { + return frankenphp.PHPString("John Doe", false) +} + +//export_php:const +const STATUS_ACTIVE = 1 +``` + +#### Using Namespaced Extension in PHP + +名前空間が宣言されると、すべての関数、クラス、および定数はPHPでその名前空間の下に配置されます: + +```php +getName(); // "John Doe" + +echo My\Extension\STATUS_ACTIVE; // 1 +``` + +#### Important Notes + +- 1つのファイルにつき**1つ**の名前空間ディレクティブのみが許可されます。複数の名前空間ディレクティブが見つかった場合、ジェネレーターはエラーを返します。 +- 名前空間はファイル内の**すべて**のエクスポートされたシンボル(関数、クラス、メソッド、定数)に適用されます。 +- 名前空間名は、バックスラッシュ(`\`)を区切り文字とするPHPの名前空間の慣習に従います。 +- 名前空間が宣言されていない場合、シンボルは通常通りグローバル名前空間にエクスポートされます。 + ### 拡張モジュールの生成 ここでいよいよ、拡張モジュールを生成できるようになります。以下のコマンドでジェネレーターを実行できます: @@ -344,7 +597,8 @@ func (sp *StringProcessorStruct) Process(input *C.zend_string, mode int64) unsaf GEN_STUB_SCRIPT=php-src/build/gen_stub.php frankenphp extension-init my_extension.go ``` -> [!NOTE] > `GEN_STUB_SCRIPT`環境変数に、先ほどダウンロードしたPHPソースの`gen_stub.php`ファイルのパスを設定するのを忘れないでください。これは手動実装セクションで言及されているのと同じ`gen_stub.php`スクリプトです。 +> [!NOTE] +> `GEN_STUB_SCRIPT`環境変数に、先ほどダウンロードしたPHPソースの`gen_stub.php`ファイルのパスを設定するのを忘れないでください。これは手動実装セクションで言及されているのと同じ`gen_stub.php`スクリプトです。 すべてがうまくいけば、`build`という名前の新しいディレクトリが作成されているはずです。このディレクトリには、生成されたPHP関数スタブを含む`my_extension.go`ファイルなど、拡張用の生成されたファイルが含まれています。 @@ -395,31 +649,32 @@ echo $processor->process('Hello World', StringProcessor::MODE_UPPERCASE); // "H モジュール内で、PHPから呼び出される新しいネイティブ関数を定義する必要があります。これを行うには、例えば`extension.go`のように任意の名前でファイルを作成し、以下のコードを追加します: ```go -package ext_go +package example -//#include "extension.h" +// #include "extension.h" import "C" import ( - "unsafe" - "github.com/caddyserver/caddy/v2" - "github.com/dunglas/frankenphp" + "log/slog" + "unsafe" + + "github.com/dunglas/frankenphp" ) func init() { - frankenphp.RegisterExtension(unsafe.Pointer(&C.ext_module_entry)) + frankenphp.RegisterExtension(unsafe.Pointer(&C.ext_module_entry)) } //export go_print_something func go_print_something() { - go func() { - caddy.Log().Info("Hello from a goroutine!") - }() + go func() { + slog.Info("Hello from a goroutine!") + }() } ``` `frankenphp.RegisterExtension()`関数は、内部のPHP登録ロジックを処理することで拡張登録プロセスを簡素化します。`go_print_something`関数は`//export`ディレクティブを使用して、CGOのおかげで、これから書くCコードでアクセスできるようになることを示しています。 -この例では、新しい関数がCaddyのログにメッセージ出力するgoroutineをトリガーします。 +この例では、新しい関数がgoroutineをトリガーし、メッセージをCaddyのログに出力します。 #### PHP関数の定義 @@ -460,9 +715,9 @@ extern zend_module_entry ext_module_entry; 次に、以下のステップを実行する`extension.c`という名前のファイルを作成します: -- PHPヘッダーをインクルードする -- 新しいネイティブPHP関数`go_print()`を宣言する -- 拡張モジュールのメタデータを宣言する +- PHPヘッダーをインクルードする +- 新しいネイティブPHP関数`go_print()`を宣言する +- 拡張モジュールのメタデータを宣言する まずは必要なヘッダーのインクルードから始めましょう: @@ -545,7 +800,7 @@ static const zend_function_entry ext_functions[] = { }; ``` -この出力から、`go_upper`関数が`string`型の引数を1つ受け取り、`string`型の戻り値を返すことが定義されていのがわかります。 +この出力から、`go_upper`関数が`string`型の引数を1つ受け取り、`string`型の戻り値を返すことが定義されてのがわかります。 #### GoとPHP/C間の型変換(Type Juggling) @@ -589,7 +844,16 @@ PHP_FUNCTION(go_upper) Go側の関数では`*C.zend_string`を引数として受け取り、FrankenPHPのヘルパー関数を使用してGoの文字列に変換し、処理を行ったうえで、結果を新たな`*C.zend_string`として返します。メモリ管理と変換の複雑さは、ヘルパー関数がすべて対応してくれます。 ```go -import "strings" +package example + +// #include +import "C" +import ( + "unsafe" + "strings" + + "github.com/dunglas/frankenphp" +) //export go_upper func go_upper(s *C.zend_string) *C.zend_string { @@ -604,6 +868,7 @@ func go_upper(s *C.zend_string) *C.zend_string { このアプローチは、手動メモリ管理よりもはるかにクリーンで安全です。FrankenPHPのヘルパー関数は、PHPの`zend_string`形式とGoの文字列間の変換を自動的に処理してくれます。`PHPString()`に`false`引数を指定していることで、新しい非永続文字列(リクエストの終了時に解放される)を作成したいことを示しています。 > [!TIP] +> > この例ではエラーハンドリングを省略していますが、Go関数内でポインタが`nil`ではないこと、渡されたデータが有効であることを常に確認するべきです。 ### 拡張モジュールのFrankenPHPへの統合 diff --git a/docs/ja/hot-reload.md b/docs/ja/hot-reload.md new file mode 100644 index 0000000000..ad2d9b6863 --- /dev/null +++ b/docs/ja/hot-reload.md @@ -0,0 +1,138 @@ +# ホットリロード + +FrankenPHPには、開発者の体験を大幅に向上させるために設計された組み込みの**ホットリロード**機能が含まれています。 + +![Mercure](hot-reload.png) + +この機能は、最新のJavaScriptツール(Viteやwebpackなど)に見られる**ホットモジュールリプレイスメント(HMR)**に似たワークフローを提供します。 +ファイル変更(PHPコード、テンプレート、JavaScript、CSSファイルなど)のたびに手動でブラウザを更新する代わりに、FrankenPHPはコンテンツをリアルタイムで更新します。 + +ホットリロードは、WordPress、Laravel、Symfony、およびその他のあらゆるPHPアプリケーションやフレームワークでネイティブに動作します。 + +有効にすると、FrankenPHPは現在の作業ディレクトリのファイルシステム変更を監視します。 +ファイルが変更されると、[Mercure](mercure.md)の更新をブラウザにプッシュします。 + +設定によっては、ブラウザは次のいずれかを行います。 + +- [Idiomorph](https://github.com/bigskysoftware/idiomorph)がロードされている場合、**DOMをモーフィング**します(スクロール位置と入力状態を保持)。 +- Idiomorphが存在しない場合、**ページをリロード**します(標準のライブリロード)。 + +## 設定 + +ホットリロードを有効にするには、Mercureを有効にし、`Caddyfile`の`php_server`ディレクティブに`hot_reload`サブディレクティブを追加します。 + +> [!WARNING] +> この機能は**開発環境でのみ**使用することを目的としています。 +> ファイルシステムの監視はパフォーマンスのオーバーヘッドを引き起こし、内部エンドポイントを公開するため、本番環境で`hot_reload`を有効にしないでください。 + +```caddyfile +localhost + +mercure { + anonymous +} + +root public/ +php_server { + hot_reload +} +``` + +デフォルトでは、FrankenPHPは現在の作業ディレクトリ内の以下のglobパターンに一致するすべてのファイルを監視します: `./**/*.{css,env,gif,htm,html,jpg,jpeg,js,mjs,php,png,svg,twig,webp,xml,yaml,yml}` + +glob構文を使用して、監視するファイルを明示的に設定することもできます。 + +```caddyfile +localhost + +mercure { + anonymous +} + +root public/ +php_server { + hot_reload src/**/*{.php,.js} config/**/*.yaml +} +``` + +Mercureのトピックと監視するディレクトリやファイルを指定するには、`hot_reload`オプションにパスを指定する長い形式を使用します。 + +```caddyfile +localhost + +mercure { + anonymous +} + +root public/ +php_server { + hot_reload { + topic hot-reload-topic + watch src/**/*.php + watch assets/**/*.{ts,json} + watch templates/ + watch public/css/ + } +} +``` + +## クライアントサイドの統合 + +サーバーが変更を検出する一方で、ブラウザはページを更新するためにこれらのイベントを購読する必要があります。 +FrankenPHPは、ファイル変更を購読するために使用するMercureハブのURLを、`$_SERVER['FRANKENPHP_HOT_RELOAD']`環境変数を通じて公開します。 + +クライアントサイドのロジックを処理するための便利なJavaScriptライブラリ、[frankenphp-hot-reload](https://www.npmjs.com/package/frankenphp-hot-reload)も利用可能です。 +これを使用するには、メインレイアウトに以下を追加します。 + +```php + +FrankenPHP ホットリロード + + + + + +``` + +このライブラリは自動的にMercureハブを購読し、ファイル変更が検出されるとバックグラウンドで現在のURLを取得し、DOMをモーフィングします。 +これは[npm](https://www.npmjs.com/package/frankenphp-hot-reload)パッケージとして、また[GitHub](https://github.com/dunglas/frankenphp-hot-reload)で利用可能です。 + +または、`EventSource`ネイティブJavaScriptクラスを使用してMercureハブを直接購読することで、独自のクライアントサイドロジックを実装することもできます。 + +### ワーカーモード + +アプリケーションを[ワーカーモード](https://frankenphp.dev/docs/worker/)で実行している場合、アプリケーションスクリプトはメモリに残ります。 +これは、ブラウザがリロードされても、PHPコードの変更がすぐに反映されないことを意味します。 + +最高の開発者体験を得るには、`hot_reload`を[ワーカーディレクティブ内の`watch`サブディレクティブ](config.md#watching-for-file-changes)と組み合わせる必要があります。 + +- `hot_reload`: ファイル変更時に**ブラウザ**を更新します +- `worker.watch`: ファイル変更時にワーカーを再起動します + +```caddy +localhost + +mercure { + anonymous +} + +root public/ +php_server { + hot_reload + worker { + file /path/to/my_worker.php + watch + } +} +``` + +### 仕組み + +1. **監視**: FrankenPHPは、内部で[e-dant/watcherライブラリ](https://github.com/e-dant/watcher)を使用してファイルシステムの変更を監視します(我々はGoバインディングに貢献しました)。 +2. **再起動(ワーカーモード)**: ワーカー設定で`watch`が有効になっている場合、PHPワーカーは新しいコードをロードするために再起動されます。 +3. **プッシュ**: 変更されたファイルのリストを含むJSONペイロードが、組み込みの[Mercureハブ](https://mercure.rocks)に送信されます。 +4. **受信**: JavaScriptライブラリを介してリッスンしているブラウザは、Mercureイベントを受信します。 +5. **更新**: + +- **Idiomorph**が検出された場合、更新されたコンテンツを取得し、現在のHTMLを新しい状態にモーフィングして、状態を失うことなく即座に変更を適用します。 +- それ以外の場合、ページを更新するために`window.location.reload()`が呼び出されます。 diff --git a/docs/ja/known-issues.md b/docs/ja/known-issues.md index 19e6330698..80460a7883 100644 --- a/docs/ja/known-issues.md +++ b/docs/ja/known-issues.md @@ -13,9 +13,9 @@ 以下の拡張モジュールは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)です。 | +| 名前 | 問題 | +| ------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| [ext-openssl](https://www.php.net/manual/en/book.openssl.php) | musl libcを使用している場合、OpenSSL拡張モジュールは高負荷時にクラッシュすることがあります。この問題は、より普及しているGNU libcを使用する場合には発生しません。このバグは[PHPによって追跡されています](https://github.com/php/php-src/issues/13648)。 | ## get_browser @@ -23,7 +23,9 @@ ## スタンドアロンバイナリおよびAlpineベースのDockerイメージ -スタンドアロンバイナリおよびAlpineベースのDockerイメージ(`dunglas/frankenphp:*-alpine`)は、バイナリサイズを小さく保つために[glibc and friends](https://www.etalabs.net/compare_libcs.html)ではなく[musl libc](https://musl.libc.org/)を使用しています。これによりいくつかの互換性問題が発生する可能性があります。特に、globフラグ`GLOB_BRACE`は [サポートされていません](https://www.php.net/manual/en/function.glob.php) 。 +スタンドアロンバイナリおよびAlpineベースのDockerイメージ(`dunglas/frankenphp:*-alpine`)は、バイナリサイズを小さく保つために[glibc and friends](https://www.etalabs.net/compare_libcs.html)ではなく[musl libc](https://musl.libc.org/)を使用しています。これによりいくつかの互換性問題が発生する可能性があります。特に、globフラグ`GLOB_BRACE`は [サポートされていません](https://www.php.net/manual/en/function.glob.php)。 + +問題が発生した場合は、静的バイナリのGNUバリアントおよびDebianベースのDockerイメージを使用することをお勧めします。 ## Dockerで`https://127.0.0.1`を使用する @@ -78,7 +80,7 @@ docker run \ ## `@php` を参照するComposerスクリプト -[Composerスクリプト](https://getcomposer.org/doc/articles/scripts.md)では、いくつかのタスクでPHPバイナリを実行したい場合があります。例えば、[Laravelプロジェクト](laravel.md)で`@php artisan package:discover --ansi`を実行する場合です。しかし現在これは以下の2つの理由で[失敗します](https://github.com/dunglas/frankenphp/issues/483#issuecomment-1899890915): +[Composerスクリプト](https://getcomposer.org/doc/articles/scripts.md)では、いくつかのタスクでPHPバイナリを実行したい場合があります。例えば、[Laravelプロジェクト](laravel.md)で`@php artisan package:discover --ansi`を実行する場合です。しかし現在これは以下の2つの理由で[失敗します](https://github.com/php/frankenphp/issues/483#issuecomment-1899890915): - ComposerはFrankenPHPバイナリを呼び出す方法を知りません - Composerはコマンドで`-d`フラグを使用してPHP設定を追加する場合があり、FrankenPHPはまだサポートしていません @@ -130,7 +132,7 @@ CA証明書をどこにインストールすべきか確認し、その場所に > WebとCLIコンテキストでは設定が異なる場合があります。 > 適切なコンテキストで`openssl_get_cert_locations()`を実行してください。 -[Mozillaから抽出されたCA証明書はcurlのサイトでダウンロードできます](https://curl.se/docs/caextract.html)。 +[Mozillaから抽出されたCA証明書はcURLのサイトでダウンロードできます](https://curl.se/docs/caextract.html)。 または、Debian、Ubuntu、Alpineなどのディストリビューションでも、これらの証明書を含む`ca-certificates`というパッケージを提供しています。 @@ -140,4 +142,3 @@ CA証明書をどこにインストールすべきか確認し、その場所に # TLS 証明書の環境変数を設定 export SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt export SSL_CERT_DIR=/etc/ssl/certs -``` diff --git a/docs/ja/laravel.md b/docs/ja/laravel.md index 18e01e95af..adc0064215 100644 --- a/docs/ja/laravel.md +++ b/docs/ja/laravel.md @@ -2,7 +2,7 @@ ## Docker -FrankenPHPを使用して[Laravel](https://laravel.com)のWebアプリケーションを配信するのは簡単で、公式Dockerイメージの`/app`ディレクトリにプロジェクトをマウントするだけです。 +FrankenPHPを使用して[Laravel](https://laravel.com)のWebアプリケーションを提供するのは簡単で、公式Dockerイメージの`/app`ディレクトリにプロジェクトをマウントするだけです。 Laravelアプリのメインディレクトリからこのコマンドを実行してください: @@ -76,6 +76,8 @@ php artisan octane:frankenphp > [!TIP] > 構造化されたJSONログ(ログ分析ソリューションを使用する際に便利)を取得するには、明示的に`--log-level`オプションを指定してください。 +[OctaneでMercureを使用する方法](#mercure-support)も参照してください。 + 詳しくは[Laravel Octaneの公式ドキュメント](https://laravel.com/docs/octane)をご覧ください。 ## Laravelアプリのスタンドアロンバイナリ化 @@ -164,13 +166,41 @@ LaravelアプリをLinux用のスタンドアロンバイナリとしてパッ Laravelはアップロードされたファイルやキャッシュ、ログなどをデフォルトでアプリケーションの`storage/`ディレクトリに保存します。 しかし、これは埋め込みアプリケーションには適していません。なぜなら、アプリの新しいバージョンごとに異なる一時ディレクトリに展開されるためです。 -この問題を回避するには、`LARAVEL_STORAGE_PATH`環境変数を設定(例:`.env`ファイル内)するか、 `Illuminate\Foundation\Application::useStoragePath()`メソッドを呼び出して、一時ディレクトリの外にある任意のディレクトリを使用してください。 +`LARAVEL_STORAGE_PATH`環境変数を設定(例:`.env`ファイル内)するか、 `Illuminate\Foundation\Application::useStoragePath()`メソッドを呼び出して、一時ディレクトリの外にある任意のディレクトリを使用してください。 + +### Mercureサポート + +[Mercure](https://mercure.rocks)は、Laravelアプリにリアルタイム機能を追加する優れた方法です。 +FrankenPHPは、[Mercureのサポートをすぐに利用できる](mercure.md)ように組み込んでいます。 + +[Octane](#laravel-octane)を使用していない場合は、[Mercureのドキュメント](mercure.md)を参照してください。 + +Octaneを使用している場合は、`config/octane.php`ファイルに以下の行を追加することでMercureサポートを有効にできます: + +```php +// ... + +return [ + // ... + + 'mercure' => [ + 'anonymous' => true, + 'publisher_jwt' => '!ChangeThisMercureHubJWTSecretKey!', + 'subscriber_jwt' => '!ChangeThisMercureHubJWTSecretKey!', + ], +]; +``` + +この配列では、[Mercureがサポートするすべてのディレクティブ](https://mercure.rocks/docs/hub/config#directives)を使用できます。 + +更新の公開と購読には、[Laravel Mercure Broadcaster](https://github.com/mvanduijker/laravel-mercure-broadcaster)ライブラリの使用をお勧めします。 +または、純粋なPHPとJavaScriptで実装する方法については、[Mercureのドキュメント](mercure.md)を参照してください。 ### スタンドアロンバイナリでOctaneを実行する Laravel Octaneアプリもスタンドアロンバイナリとしてパッケージ化することが可能です! -そのためには、[Octaneを正しくインストール](#laravel-octane)し、[前のセクション](#laravelアプリのスタンドアロンバイナリ化)で説明した手順に従ってください。 +そのためには、[Octaneを正しくインストール](#laravel-octane)し、[前のセクション](#laravel-apps-as-standalone-binaries)で説明した手順に従ってください。 次に、Octaneを通じてワーカーモードでFrankenPHPを起動するには、以下を実行してください: diff --git a/docs/ja/logging.md b/docs/ja/logging.md new file mode 100644 index 0000000000..45f75737c1 --- /dev/null +++ b/docs/ja/logging.md @@ -0,0 +1,71 @@ +# ロギング + +FrankenPHP は、[Caddy のロギングシステム](https://caddyserver.com/docs/logging)とシームレスに統合されています。 +標準の PHP 関数を使用してメッセージをログに記録するか、高度な構造化ロギング機能のために専用の `frankenphp_log()` 関数を利用できます。 + +## `frankenphp_log()` + +`frankenphp_log()` 関数を使用すると、PHP アプリケーションから直接構造化ログを出力でき、 +Datadog、Grafana Loki、Elastic などのプラットフォームへの取り込みや OpenTelemetry のサポートがはるかに容易になります。 + +内部的に、`frankenphp_log()` は [Go の `log/slog` パッケージ](https://pkg.go.dev/log/slog)をラップして、豊富なロギング機能を提供します。 + +これらのログには、深刻度レベルとオプションのコンテキストデータが含まれます。 + +```php +function frankenphp_log(string $message, int $level = FRANKENPHP_LOG_LEVEL_INFO, array $context = []): void +``` + +### パラメータ + +- **`message`**: ログメッセージ文字列。 +- **`level`**: ログの深刻度レベル。任意の整数値を指定できます。一般的なレベルには便利な定数が用意されています: `FRANKENPHP_LOG_LEVEL_DEBUG` (`-4`)、`FRANKENPHP_LOG_LEVEL_INFO` (`0`)、`FRANKENPHP_LOG_LEVEL_WARN` (`4`)、`FRANKENPHP_LOG_LEVEL_ERROR` (`8`)。デフォルトは `FRANKENPHP_LOG_LEVEL_INFO` です。 +- **`context`**: ログエントリに含める追加データの連想配列。 + +### 例 + +```php + memory_get_usage(), + 'peak_usage' => memory_get_peak_usage(), + ], +); + +``` + +ログを表示すると (例: `docker compose logs` 経由)、出力は構造化された JSON として表示されます。 + +```json +{"level":"info","ts":1704067200,"logger":"frankenphp","msg":"Hello from FrankenPHP!"} +{"level":"warn","ts":1704067200,"logger":"frankenphp","msg":"Memory usage high","current_usage":10485760,"peak_usage":12582912} +``` + +## `error_log()` + +FrankenPHP は、標準の `error_log()` 関数を使用したロギングも可能です。`$message_type` パラメータが `4` (SAPI) の場合、 +これらのメッセージは Caddy ロガーにルーティングされます。 + +デフォルトでは、`error_log()` を介して送信されるメッセージは非構造化テキストとして扱われます。 +これらは、標準の PHP ライブラリに依存する既存のアプリケーションやライブラリとの互換性のために有用です。 + +### `error_log()` の例 + +```php +error_log("Database connection failed", 4); +``` + +これは Caddy ログに表示され、多くの場合、PHP から発信されたことを示すプレフィックスが付けられます。 + +> [!TIP] +> 本番環境での可観測性を高めるには、`frankenphp_log()` を使用することをお勧めします。 +> これは、レベル (デバッグ、エラーなど) でログをフィルタリングしたり、 +> ロギングインフラストラクチャ内の特定のフィールドをクエリしたりできるためです。 diff --git a/docs/ja/mercure.md b/docs/ja/mercure.md index 0aae6d3055..559e9d06f1 100644 --- a/docs/ja/mercure.md +++ b/docs/ja/mercure.md @@ -3,13 +3,143 @@ FrankenPHPには組み込みの[Mercure](https://mercure.rocks)ハブが付属しています! Mercureを使用すると、接続されているすべてのデバイスにリアルタイムイベントをプッシュでき、各デバイスは即座にJavaScriptイベントを受信します。 -JSライブラリやSDKは必要ありません! +これはWebSocketsに代わる便利な方法で、使い方が簡単で、すべてのモダンなウェブブラウザでネイティブにサポートされています! ![Mercure](mercure-hub.png) -Mercureハブを有効にするには、[Mercureのサイト](https://mercure.rocks/docs/hub/config)で説明されているように`Caddyfile`を更新してください。 +## Mercureを有効にする -Mercureハブのパスは`/.well-known/mercure`です。 -FrankenPHPをDocker内で実行している場合、完全な送信URLは`http://php/.well-known/mercure`のようになります。ここでの`php`はFrankenPHPを実行するコンテナの名前です。 +Mercureのサポートはデフォルトで無効になっています。 +以下は、FrankenPHPとMercureハブの両方を有効にする`Caddyfile`の最小限の例です。 -コードからMercureの更新をプッシュするには、[Symfony Mercure Component](https://symfony.com/components/Mercure)をお勧めします。なお、Symfonyのフルスタックフレームワークは必要ありません。 +```caddyfile +# 応答するホスト名 +localhost + +mercure { + # パブリッシャー用のJWTトークンを署名するために使用される秘密鍵 + publisher_jwt !ChangeThisMercureHubJWTSecretKey! + # 匿名購読者(JWTなし)を許可する + anonymous +} + +root public/ +php_server +``` + +> [!ヒント] +> +> [Dockerイメージ](docker.md)によって提供される[サンプル `Caddyfile`](https://github.com/php/frankenphp/blob/main/caddy/frankenphp/Caddyfile)には、便利な環境変数で設定できる、コメントアウトされたMercure設定がすでに含まれています。 +> +> `/etc/frankenphp/Caddyfile`のMercureセクションのコメントを解除して有効にしてください。 + +## 更新を購読する + +デフォルトでは、MercureハブはFrankenPHPサーバーの`/.well-known/mercure`パスで利用可能です。 +更新を購読するには、ネイティブの[`EventSource`](https://developer.mozilla.org/docs/Web/API/EventSource) JavaScriptクラスを使用します。 + +```html + + +Mercureの例 + +``` + +## 更新を公開する + +### `mercure_publish()`を使用する + +FrankenPHPは、組み込みのMercureハブに更新を公開するための便利な`mercure_publish()`関数を提供します。 + +```php + 'value'])); + +// FrankenPHPのログに書き込む +error_log("update $updateID published", 4); +``` + +関数の完全なシグネチャは次のとおりです。 + +```php +/** + * @param string|string[] $topics + */ +function mercure_publish(string|array $topics, string $data = '', bool $private = false, ?string $id = null, ?string $type = null, ?int $retry = null): string {} +``` + +### `file_get_contents()`を使用する + +接続された購読者に更新をディスパッチするには、`topic`および`data`パラメーターを含む認証済みPOSTリクエストをMercureハブに送信します。 + +```php + [ + 'method' => 'POST', + 'header' => "Content-type: application/x-www-form-urlencoded\r\nAuthorization: Bearer " . JWT, + 'content' => http_build_query([ + 'topic' => 'my-topic', + 'data' => json_encode(['key' => 'value']), + ]), +]])); + +// FrankenPHPのログに書き込む +error_log("update $updateID published", 4); +``` + +`Caddyfile`の`mercure.publisher_jwt`オプションのパラメーターとして渡されたキーは、`Authorization`ヘッダーで使用されるJWTトークンの署名に用いられなければなりません。 + +JWTには、公開したいトピックに対する`publish`権限を持つ`mercure`クレームを含める必要があります。認可については、[Mercureのドキュメント](https://mercure.rocks/spec#publishers)を参照してください。 + +独自のトークンを生成するには、[このjwt.ioリンク](https://www.jwt.io/#token=eyJhbGciOiJIUzI1NiJ9.eyJtZXJjdXJlIjp7InB1Ymxpc2giOlsiKiJdfX0.PXwpfIGng6KObfZlcOXvcnWCJOWTFLtswGI5DZuWSK4)を使用できますが、本番環境のアプリケーションでは、信頼できる[JWTライブラリ](https://www.jwt.io/libraries?programming_language=php)を使用して動的に生成される短命のトークンを使用することをお勧めします。 + +### Symfony Mercureを使用する + +あるいは、スタンドアロンのPHPライブラリである[Symfony Mercureコンポーネント](https://symfony.com/components/Mercure)を使用することもできます。 + +このライブラリは、JWTの生成、更新の公開、および購読者向けのクッキーベースの認可を処理します。 + +まず、Composerを使用してライブラリをインストールします。 + +```console +composer require symfony/mercure lcobucci/jwt +``` + +その後、次のように使用できます。 + +```php +publish(new \Symfony\Component\Mercure\Update('my-topic', json_encode(['key' => 'value']))); + +// FrankenPHPのログに書き込む +error_log("update $updateID published", 4); +``` + +Mercureは、以下によってもネイティブにサポートされています。 + +- [Laravel](laravel.md#mercure-support) +- [Symfony](https://symfony.com/doc/current/mercure.html) +- [API Platform](https://api-platform.com/docs/core/mercure/) diff --git a/docs/ja/performance.md b/docs/ja/performance.md index 5139a0347e..b26bc73f7f 100644 --- a/docs/ja/performance.md +++ b/docs/ja/performance.md @@ -1,6 +1,6 @@ # パフォーマンス -デフォルトでは、FrankenPHPはパフォーマンスと使いやすさのバランスが取れた構成を提供するよう設計されています。 +デフォルトでは、FrankenPHPはパフォーマンスと使いやすさの良い妥協点を提供するよう設計されています。 ただし、適切な設定により、パフォーマンスを大幅に向上させることが可能です。 ## スレッド数とワーカー数 @@ -11,10 +11,10 @@ これらの値を調整することを強く推奨します。最適なシステムの安定性のためには、`num_threads` x `memory_limit` < `available_memory`とすることをお勧めします。 適切な値を見つけるには、実際のトラフィックをシミュレートした負荷テストを実行するのが最も効果的です。 -そのためのツールとして、[k6](https://k6.io)や[Gatling](https://gatling.io)が有用です。 +[k6](https://k6.io)や[Gatling](https://gatling.io)がそのための有用なツールです。 -スレッド数を設定するには、`php_server`や`php`ディレクティブ内の`num_threads`オプションを使用してください。 -ワーカー数を変更するには、`frankenphp`ディレクティブ内の`worker`セクションにある`num`オプションを使用してください。 +スレッド数を設定するには、`php_server`や`php`ディレクティブの`num_threads`オプションを使用してください。 +ワーカー数を変更するには、`frankenphp`ディレクティブの`worker`セクションにある`num`オプションを使用してください。 ### `max_threads` @@ -30,7 +30,7 @@ [ワーカーモード](worker.md)を有効にするとパフォーマンスが劇的に向上しますが、 アプリがこのモードと互換性があるように適応する必要があります: -ワーカースクリプトを作成し、アプリがメモリリークしていないことを確認する必要があります。 +ワーカースクリプトを作成し、アプリがメモリリークしていないことを確認してください。 ## muslを使用しない @@ -41,11 +41,9 @@ PHPは、従来のGNUライブラリの代わりにこの代替Cライブラリ また、[一部のバグはmuslを使用した場合にのみ発生します](https://github.com/php/php-src/issues?q=sort%3Aupdated-desc+is%3Aissue+is%3Aopen+label%3ABug+musl)。 -本番環境では、glibcにリンクされたFrankenPHPを使用することをお勧めします。 +本番環境では、glibcにリンクされ、適切な最適化レベルでコンパイルされたFrankenPHPを使用することをお勧めします。 -これは、Debian Dockerイメージ(デフォルト)を使用するか、[リリースページ](https://github.com/php/frankenphp/releases)から -gnu サフィックス付きバイナリをダウンロードするか、あるいは[FrankenPHPをソースからコンパイル](compile.md)することで実現できます。 - -または、[mimalloc allocator](https://github.com/microsoft/mimalloc)でコンパイルされた静的muslバイナリも提供しており、これによりスレッド環境での問題を軽減できます。 +これは、Debian Dockerイメージを使用するか、メンテナーの[.deb](https://debs.henderkes.com)または[.rpm](https://rpms.henderkes.com)パッケージを使用するか、または[FrankenPHPをソースからコンパイル](compile.md)することで実現できます。 ## Go Runtime設定 @@ -155,3 +153,31 @@ FrankenPHPは公式のPHPインタープリターを使用しています。 詳細については、[Symfonyの専用ドキュメントエントリ](https://symfony.com/doc/current/performance.html)をお読みください (Symfonyを使用していなくても、多くのヒントが役立ちます)。 + +## スレッドプールの分割 + +アプリケーションが、高負荷時に不安定になったり、常に10秒以上応答に時間がかかったりするAPIのような、低速な外部サービスとやり取りすることはよくあります。このような場合、専用の「低速」プールを持つようにスレッドプールを分割すると有益です。これにより、低速なエンドポイントがサーバーのリソース/スレッドをすべて消費してしまうのを防ぎ、コネクションプールのように低速なエンドポイントへのリクエストの同時実行数を制限できます。 + +```caddyfile +{ + frankenphp { + max_threads 100 # すべてのワーカーで共有される最大100スレッド + } +} + +example.com { + php_server { + root /app/public # アプリケーションのルート + worker index.php { + match /slow-endpoint/* # パスが /slow-endpoint/* のすべてリクエストはこのスレッドプールによって処理される + num 10 # /slow-endpoint/* に一致するリクエストの最小スレッド数10 + } + worker index.php { + match * # その他すべてのリクエストは個別に処理される + num 20 # 低速なエンドポイントがハングし始めた場合でも、その他のリクエストに最小20スレッド + } + } +} +``` + +一般的に、メッセージキューなどの適切なメカニズムを使用して、非常に低速なエンドポイントを非同期に処理することも推奨されます。 diff --git a/docs/ja/production.md b/docs/ja/production.md index cd5f821728..f028185071 100644 --- a/docs/ja/production.md +++ b/docs/ja/production.md @@ -2,9 +2,9 @@ このチュートリアルでは、Docker Composeを使用して単一サーバーにPHPアプリケーションをデプロイする方法を学びます。 -Symfonyを使用している場合は、Symfony Dockerプロジェクトの「[本番環境へのデプロイ](https://github.com/dunglas/symfony-docker/blob/main/docs/production.md)」ドキュメントを参照してください。 +Symfonyを使用している場合は、Symfony Dockerプロジェクトの「[本番環境へのデプロイ](https://github.com/dunglas/symfony-docker/blob/main/docs/production.md)」ドキュメントエントリー(FrankenPHPを使用しています)を参照してください。 -API Platformを使用している場合は、[フレームワークのデプロイドキュメント](https://api-platform.com/docs/deployment/)を参照してください。 +API Platform(こちらもFrankenPHPを使用しています)を使用している場合は、[フレームワークのデプロイドキュメント](https://api-platform.com/docs/deployment/)を参照してください。 ## アプリの準備 diff --git a/docs/ja/static.md b/docs/ja/static.md index 1887201221..27991085b4 100644 --- a/docs/ja/static.md +++ b/docs/ja/static.md @@ -1,7 +1,7 @@ # 静的ビルドの作成 PHPライブラリのローカルインストールを使用する代わりに、 -[static-php-cli プロジェクト](https://github.com/crazywhalecc/static-php-cli)を利用して、FrankenPHPの静的またはほぼ静的なビルドを作成することが可能です(プロジェクト名に「CLI」とありますが、CLIだけでなく全てのSAPIをサポートしています)。 +素晴らしい[static-php-cli プロジェクト](https://github.com/crazywhalecc/static-php-cli)を利用して、FrankenPHPの静的またはほぼ静的なビルドを作成することが可能です(プロジェクト名に「CLI」とありますが、CLIだけでなく全てのSAPIをサポートしています)。 この方法を使えば、PHPインタープリター、Caddy Webサーバー、FrankenPHPをすべて含んだ単一でポータブルなバイナリを作成できます! @@ -73,7 +73,7 @@ docker buildx bake \ ### 追加のCaddyモジュール -Caddyの拡張モジュールを追加したい場合は、`XCADDY_ARGS`というDocker引数を使用して、[xcaddy](https://github.com/caddyserver/xcaddy)に渡す引数を以下のように指定できます: +Caddyの追加モジュールを追加したり、[xcaddy](https://github.com/caddyserver/xcaddy)に他の引数を渡したりするには、`XCADDY_ARGS`というDocker ARGを使用します: ```console docker buildx bake \ @@ -87,7 +87,7 @@ docker buildx bake \ > [!TIP] > > cbrotli、Mercure、Vulcainモジュールは、`XCADDY_ARGS`が空または設定されていない場合はデフォルトで含まれます。 -> `XCADDY_ARGS`の値をカスタマイズする場合、デフォルトのモジュールは含まれなくなるため、必要なものは明示的に記述してください。 +> `XCADDY_ARGS`の値をカスタマイズする場合、含めたいのであれば明示的にそれらを含める必要があります。 [ビルドのカスタマイズ](#ビルドのカスタマイズ)も参照してください @@ -121,7 +121,7 @@ cd frankenphp - `PHP_VERSION`: 使用するPHPのバージョン - `PHP_EXTENSIONS`: ビルドするPHP拡張([サポートされる拡張のリスト](https://static-php.dev/en/guide/extensions.html)) - `PHP_EXTENSION_LIBS`: 拡張モジュールに追加機能を持たせるためにビルドする追加ライブラリ -- `XCADDY_ARGS`: 追加のCaddyモジュールを導入するなど[xcaddy](https://github.com/caddyserver/xcaddy)に渡す引数 +- `XCADDY_ARGS`: Caddyの追加モジュールを追加したり、[xcaddy](https://github.com/caddyserver/xcaddy)に他の引数を渡したりするための引数 - `EMBED`: バイナリに埋め込むPHPアプリケーションのパス - `CLEAN`: 指定するとlibphpおよびそのすべての依存関係がスクラッチからビルドされます(キャッシュなし) - `NO_COMPRESS`: UPXを使用して結果のバイナリを圧縮しない diff --git a/docs/ja/wordpress.md b/docs/ja/wordpress.md new file mode 100644 index 0000000000..6749ef51bd --- /dev/null +++ b/docs/ja/wordpress.md @@ -0,0 +1,59 @@ +# WordPress + +自動HTTPS、HTTP/3、Zstandard圧縮を備えたモダンで高性能なスタックを楽しむために、FrankenPHPで[WordPress](https://wordpress.org/)を実行してください。 + +## 最小限のインストール + +1. [WordPressをダウンロード](https://wordpress.org/download/) +2. ZIPアーカイブを解凍し、解凍したディレクトリでターミナルを開きます +3. 実行: + + ```console + frankenphp php-server + ``` + +4. `http://localhost/wp-admin/`にアクセスし、インストール手順に従います +5. お楽しみください! + +本番環境向けのセットアップには、次のような`Caddyfile`を使用して`frankenphp run`を実行することをお勧めします: + +```caddyfile +example.com + +php_server +encode zstd br gzip +log +``` + +## ホットリロード + +WordPressで[ホットリロード](hot-reload.md)機能を使用するには、[Mercure](mercure.md)を有効にし、`hot_reload`サブディレクティブを`Caddyfile`の`php_server`ディレクティブに追加します: + +```caddyfile +localhost + +mercure { + anonymous +} + +php_server { + hot_reload +} +``` + +次に、WordPressテーマの`functions.php`ファイルにJavaScriptライブラリをロードするために必要なコードを追加します: + +```php +function hot_reload() { + ?> + + + + + + [!TIP] +> このセクションは、FrankenPHPワーカーモードのネイティブサポートが導入されたSymfony 7.4より前のバージョンでのみ必要です。 + FrankenPHPのワーカーモードは[Symfony Runtime Component](https://symfony.com/doc/current/components/runtime.html)によってサポートされています。 ワーカーでSymfonyアプリケーションを開始するには、FrankenPHP用の[PHP Runtime](https://github.com/php-runtime/runtime)パッケージをインストールします: @@ -67,7 +71,7 @@ docker run \ boot(); -// ループの外側にハンドラーを配置してパフォーマンスを向上(処理量を減らす) +// パフォーマンス向上のため、ループの外でハンドラーを定義(処理量を削減) $handler = static function () use ($myApp) { - // リクエストを受信した際に呼び出され、 - // スーパーグローバルや php://input などがリセットされます。 - echo $myApp->handle($_GET, $_POST, $_COOKIE, $_FILES, $_SERVER); + try { + // リクエストを受信すると呼び出され、 + // スーパーグローバル、php://input などがリセットされます + echo $myApp->handle($_GET, $_POST, $_COOKIE, $_FILES, $_SERVER); + } catch (\Throwable $exception) { + // `set_exception_handler` はワーカースクリプトが終了するときにのみ呼び出されるため、 + // 期待する動作と異なる場合があります。ここで例外をキャッチして処理します + (new \MyCustomExceptionHandler)->handleException($exception); + } }; $maxRequests = (int)($_SERVER['MAX_REQUESTS'] ?? 0); for ($nbRequests = 0; !$maxRequests || $nbRequests < $maxRequests; ++$nbRequests) { $keepRunning = \frankenphp_handle_request($handler); - // HTTPレスポンスの送信後に何か処理を行います + // HTTPレスポンス送信後に何らかの処理を実行 $myApp->terminate(); - // ページ生成の途中でガベージコレクタが起動する可能性を減らすために、ここでガベージコレクタを明示的に呼び出す。 + // ページ生成中にガベージコレクタがトリガーされる可能性を減らすため、ここでガベージコレクタを呼び出します gc_collect_cycles(); if (!$keepRunning) break; @@ -178,4 +188,3 @@ $handler = static function () use ($workerServer) { }; // ... -``` diff --git a/docs/ja/x-sendfile.md b/docs/ja/x-sendfile.md index 2e5ec256f5..f4d4694dc4 100644 --- a/docs/ja/x-sendfile.md +++ b/docs/ja/x-sendfile.md @@ -68,4 +68,3 @@ BinaryFileResponse::trustXSendfileTypeHeader(); $response = new BinaryFileResponse(__DIR__.'/../private-files/file.txt'); // ... -``` diff --git a/docs/pt-br/classic.md b/docs/pt-br/classic.md index e41c27d27a..42ee827df8 100644 --- a/docs/pt-br/classic.md +++ b/docs/pt-br/classic.md @@ -1,28 +1,9 @@ -# Usando o modo clássico +# Usando o Modo Clássico -Sem nenhuma configuração adicional, o FrankenPHP opera no modo clássico. -Neste modo, o FrankenPHP funciona como um servidor PHP tradicional, servindo -diretamente arquivos PHP. -Isso o torna um substituto perfeito para PHP-FPM ou Apache com mod_php. +Sem nenhuma configuração adicional, o FrankenPHP opera no modo clássico. Neste modo, o FrankenPHP funciona como um servidor PHP tradicional, servindo diretamente arquivos PHP. Isso o torna um substituto direto e sem falhas para PHP-FPM ou Apache com mod_php. -Semelhante ao Caddy, o FrankenPHP aceita um número ilimitado de conexões e usa -um [número fixo de threads](config.md#configuracao-do-caddyfile) para servi-las. -O número de conexões aceitas e enfileiradas é limitado apenas pelos recursos -disponíveis no sistema. -O pool de threads do PHP opera com um número fixo de threads inicializadas na -inicialização, comparável ao modo estático do PHP-FPM. -Também é possível permitir que as threads -[escalem automaticamente em tempo de execução](performance.md#max_threads), -semelhante ao modo dinâmico do PHP-FPM. +Assim como o Caddy, o FrankenPHP aceita um número ilimitado de conexões e usa um [número fixo de threads](config.md#caddyfile-config) para servi-las. O número de conexões aceitas e enfileiradas é limitado apenas pelos recursos disponíveis do sistema. O pool de threads do PHP opera com um número fixo de threads inicializadas na inicialização, comparável ao modo estático do PHP-FPM. Também é possível permitir que as threads [escalem automaticamente em tempo de execução](performance.md#max_threads), similar ao modo dinâmico do PHP-FPM. -As conexões enfileiradas aguardarão indefinidamente até que uma thread PHP -esteja disponível para servi-las. -Para evitar isso, você pode usar a -[configuração](config.md#configuracao-do-caddyfile) `max_wait_time` na -configuração global do FrankenPHP para limitar o tempo que uma requisição pode -esperar por uma thread PHP livre antes de ser rejeitada. -Além disso, você pode definir um -[tempo limite de escrita razoável no Caddy](https://caddyserver.com/docs/caddyfile/options#timeouts). +As conexões enfileiradas aguardarão indefinidamente até que uma thread PHP esteja disponível para servi-las. Para evitar isso, você pode usar a `max_wait_time` [configuração](config.md#caddyfile-config) na configuração global do FrankenPHP para limitar a duração que uma requisição pode esperar por uma thread PHP livre antes de ser rejeitada. Além disso, você pode definir um [tempo limite de escrita razoável no Caddy](https://caddyserver.com/docs/caddyfile/options#timeouts). -Cada instância do Caddy ativará apenas um pool de threads do FrankenPHP, que -será compartilhado entre todos os blocos `php_server`. +Cada instância do Caddy ativará apenas um pool de threads do FrankenPHP, que será compartilhado entre todos os blocos `php_server`. diff --git a/docs/pt-br/compile.md b/docs/pt-br/compile.md index a2df4221ea..76f3dd4e5c 100644 --- a/docs/pt-br/compile.md +++ b/docs/pt-br/compile.md @@ -1,11 +1,9 @@ # Compilar a partir do código-fonte -Este documento explica como criar um binário FrankenPHP que carregará o PHP como -uma biblioteca dinâmica. +Este documento explica como criar um binário FrankenPHP que carregará o PHP como uma biblioteca dinâmica. Este é o método recomendado. -Como alternativa, [compilações totalmente e principalmente estáticas](static.md) -também podem ser criadas. +Como alternativa, [compilações totalmente e principalmente estáticas](static.md) também podem ser criadas. ## Instalar o PHP @@ -13,14 +11,11 @@ O FrankenPHP é compatível com PHP 8.2 e versões superiores. ### Com o Homebrew (Linux e Mac) -A maneira mais fácil de instalar uma versão da `libphp` compatível com o -FrankenPHP é usar os pacotes ZTS fornecidos pelo -[Homebrew PHP](https://github.com/shivammathur/homebrew-php). +A maneira mais fácil de instalar uma versão da `libphp` compatível com o FrankenPHP é usar os pacotes ZTS fornecidos pelo [Homebrew PHP](https://github.com/shivammathur/homebrew-php). Primeiro, se ainda não o fez, instale o [Homebrew](https://brew.sh). -Em seguida, instale a variante ZTS do PHP, o Brotli (opcional, para suporte à -compressão) e o watcher (opcional, para detecção de alterações em arquivos): +Em seguida, instale a variante ZTS do PHP, o Brotli (opcional, para suporte à compressão) e o watcher (opcional, para detecção de alterações em arquivos): ```console brew install shivammathur/php/php-zts brotli watcher @@ -29,21 +24,17 @@ brew link --overwrite --force shivammathur/php/php-zts ### Compilando o PHP -Alternativamente, você pode compilar o PHP a partir do código-fonte com as -opções necessárias para o FrankenPHP seguindo estes passos. +Alternativamente, você pode compilar o PHP a partir do código-fonte com as opções necessárias para o FrankenPHP seguindo estes passos. -Primeiro, [obtenha o código-fonte do PHP](https://www.php.net/downloads.php) e -extraia-os: +Primeiro, [obtenha o código-fonte do PHP](https://www.php.net/downloads.php) e extraia-os: ```console tar xf php-* cd php-*/ ``` -Em seguida, execute o script `configure` com as opções necessárias para sua -plataforma. -As seguintes flags `./configure` são obrigatórias, mas você pode adicionar -outras, por exemplo, para compilar extensões ou recursos adicionais. +Em seguida, execute o script `configure` com as opções necessárias para sua plataforma. +As seguintes flags `./configure` são obrigatórias, mas você pode adicionar outras, por exemplo, para compilar extensões ou recursos adicionais. #### Linux @@ -57,8 +48,7 @@ outras, por exemplo, para compilar extensões ou recursos adicionais. #### Mac -Use o gerenciador de pacotes [Homebrew](https://brew.sh/) para instalar as -dependências necessárias e opcionais: +Use o gerenciador de pacotes [Homebrew](https://brew.sh/) para instalar as dependências necessárias e opcionais: ```console brew install libiconv bison brotli re2c pkg-config watcher @@ -86,15 +76,14 @@ sudo make install ## Instalar dependências opcionais -Alguns recursos do FrankenPHP dependem de dependências opcionais do sistema que -devem ser instaladas. -Alternativamente, esses recursos podem ser desabilitados passando as tags de -compilação para o compilador Go. +Alguns recursos do FrankenPHP dependem de dependências opcionais do sistema que devem ser instaladas. +Alternativamente, esses recursos podem ser desabilitados passando as tags de compilação para o compilador Go. -| Recurso | Dependência | Tag de compilação para desabilitá-lo | -| ------------------------------------- | --------------------------------------------------------------------- | ------------------------------------ | -| Compressão Brotli | [Brotli](https://github.com/google/brotli) | `nobrotli` | -| Reiniciar workers ao alterar arquivos | [Watcher C](https://github.com/e-dant/watcher/tree/release/watcher-c) | `nowatcher` | +| Recurso | Dependência | Tag de compilação para desabilitá-lo | +| ------------------------------------- | ------------------------------------------------------------------------------------------------------------ | ------------------------------------ | +| Compressão Brotli | [Brotli](https://github.com/google/brotli) | nobrotli | +| Reiniciar workers ao alterar arquivos | [Watcher C](https://github.com/e-dant/watcher/tree/release/watcher-c) | nowatcher | +| [Mercure](mercure.md) | [Biblioteca Go do Mercure](https://pkg.go.dev/github.com/dunglas/mercure) (instalada automaticamente, licenciada sob AGPL) | nomercure | ## Compilando a aplicação Go @@ -102,11 +91,8 @@ Agora você pode compilar o binário final. ### Usando o `xcaddy` -A maneira recomendada é usar o [`xcaddy`](https://github.com/caddyserver/xcaddy) -para compilar o FrankenPHP. -O `xcaddy` também permite adicionar facilmente -[módulos Caddy personalizados](https://caddyserver.com/docs/modules/) e -extensões FrankenPHP: +A maneira recomendada é usar o [`xcaddy`](https://github.com/caddyserver/xcaddy) para compilar o FrankenPHP. +O `xcaddy` também permite adicionar facilmente [módulos Caddy personalizados](https://caddyserver.com/docs/modules/) e extensões FrankenPHP: ```console CGO_ENABLED=1 \ @@ -115,19 +101,21 @@ CGO_CFLAGS=$(php-config --includes) \ CGO_LDFLAGS="$(php-config --ldflags) $(php-config --libs)" \ xcaddy build \ --output frankenphp \ - --with github.com/php/frankenphp/caddy \ + --with github.com/dunglas/frankenphp/caddy \ --with github.com/dunglas/mercure/caddy \ - --with github.com/dunglas/vulcain/caddy + --with github.com/dunglas/vulcain/caddy \ + --with github.com/dunglas/caddy-cbrotli # Adicione módulos Caddy e extensões FrankenPHP extras aqui + # Opcionalmente, se você quiser compilar a partir do seu código-fonte frankenphp: + # --with github.com/dunglas/frankenphp=$(pwd) \ + # --with github.com/dunglas/frankenphp/caddy=$(pwd)/caddy ``` > [!TIP] > > Se você estiver usando a `libc` `musl` (o padrão no Alpine Linux) e Symfony, > pode ser necessário aumentar o tamanho da pilha padrão. -> Caso contrário, você poderá receber erros como `PHP Fatal error: Maximum call -stack size of 83360 bytes reached during compilation. -Try splitting expression`. +> Caso contrário, você poderá receber erros como `PHP Fatal error: Maximum call stack size of 83360 bytes reached during compilation. Try splitting expression`. > > Para fazer isso, altere a variável de ambiente `XCADDY_GO_BUILD_FLAGS` para > algo como @@ -137,8 +125,7 @@ Try splitting expression`. ### Sem o `xcaddy` -Alternativamente, é possível compilar o FrankenPHP sem o `xcaddy` usando o -comando `go` diretamente: +Alternativamente, é possível compilar o FrankenPHP sem o `xcaddy` usando o comando `go` diretamente: ```console curl -L https://github.com/php/frankenphp/archive/refs/heads/main.tar.gz | tar xz diff --git a/docs/pt-br/config.md b/docs/pt-br/config.md index e36a144d75..68c0691c0a 100644 --- a/docs/pt-br/config.md +++ b/docs/pt-br/config.md @@ -1,27 +1,40 @@ # Configuração -FrankenPHP, Caddy, bem como os módulos Mercure e Vulcain, podem ser configurados -usando -[os formatos suportados pelo Caddy](https://caddyserver.com/docs/getting-started#your-first-config). - -Nas [imagens Docker](docker.md), o `Caddyfile` está localizado em -`/etc/frankenphp/Caddyfile`. -O binário estático também procurará pelo `Caddyfile` no diretório onde o comando -`frankenphp run` é executado. +FrankenPHP, Caddy, bem como os módulos [Mercure](mercure.md) e [Vulcain](https://vulcain.rocks), podem ser configurados usando [os formatos suportados pelo Caddy](https://caddyserver.com/docs/getting-started#your-first-config). + +O formato mais comum é o `Caddyfile`, que é um formato de texto simples e legível por humanos. +Por padrão, o FrankenPHP procurará por um `Caddyfile` no diretório atual. Você pode especificar um caminho personalizado com a opção `-c` ou `--config`. -O próprio PHP pode ser configurado -[usando um arquivo `php.ini`](https://www.php.net/manual/pt_BR/configuration.file.php). +Um `Caddyfile` mínimo para servir uma aplicação PHP é mostrado abaixo: + +```caddyfile +# O nome do host para responder +localhost + +# Opcionalmente, o diretório para servir arquivos, caso contrário, o padrão é o diretório atual +#root public/ +php_server +``` + +Um `Caddyfile` mais avançado, que habilita mais recursos e fornece variáveis de ambiente convenientes, é disponibilizado [no repositório do FrankenPHP](https://github.com/php/frankenphp/blob/main/caddy/frankenphp/Caddyfile) e com as imagens Docker. + +O próprio PHP pode ser configurado [usando um arquivo `php.ini`](https://www.php.net/manual/en/configuration.file.php). -Dependendo do seu método de instalação, o interpretador PHP procurará por -arquivos de configuração nos locais descritos acima. +Dependendo do seu método de instalação, o FrankenPHP e o interpretador PHP procurarão por arquivos de configuração nos locais descritos abaixo. ## Docker -- `php.ini`: `/usr/local/etc/php/php.ini` (nenhum `php.ini` é fornecido por - padrão); -- Arquivos de configuração adicionais: `/usr/local/etc/php/conf.d/*.ini`; -- Extensões PHP: `/usr/local/lib/php/extensions/no-debug-zts-/`; +FrankenPHP: + +- `/etc/frankenphp/Caddyfile`: o arquivo de configuração principal +- `/etc/frankenphp/Caddyfile.d/*.caddyfile`: arquivos de configuração adicionais que são carregados automaticamente + +PHP: + +- `php.ini`: `/usr/local/etc/php/php.ini` (nenhum `php.ini` é fornecido por padrão) +- Arquivos de configuração adicionais: `/usr/local/etc/php/conf.d/*.ini` +- Extensões PHP: `/usr/local/lib/php/extensions/no-debug-zts-/` - Você deve copiar um template oficial fornecido pelo projeto PHP: ```dockerfile @@ -36,125 +49,126 @@ RUN cp $PHP_INI_DIR/php.ini-development $PHP_INI_DIR/php.ini ## Pacotes RPM e Debian -- `php.ini`: `/etc/frankenphp/php.ini` (um arquivo `php.ini` com configurações - de produção é fornecido por padrão); -- Arquivos de configuração adicionais: `/etc/frankenphp/php.d/*.ini`; -- Extensões PHP: `/usr/lib/frankenphp/modules/`. +FrankenPHP: + +- `/etc/frankenphp/Caddyfile`: o arquivo de configuração principal +- `/etc/frankenphp/Caddyfile.d/*.caddyfile`: arquivos de configuração adicionais que são carregados automaticamente + +PHP: + +- `php.ini`: `/etc/php-zts/php.ini` (um arquivo `php.ini` com predefinições de produção é fornecido por padrão) +- Arquivos de configuração adicionais: `/etc/php-zts/conf.d/*.ini` ## Binário estático -- `php.ini`: O diretório no qual `frankenphp run` ou `frankenphp php-server` é - executado e, em seguida, `/etc/frankenphp/php.ini`; -- Arquivos de configuração adicionais: `/etc/frankenphp/php.d/*.ini`; -- Extensões PHP: não podem ser carregadas, empacote-as no próprio binário; -- Copie um dos arquivos `php.ini-production` ou `php.ini-development` fornecidos - [no código-fonte do PHP](https://github.com/php/php-src/). +FrankenPHP: + +- No diretório de trabalho atual: `Caddyfile` + +PHP: + +- `php.ini`: O diretório no qual `frankenphp run` ou `frankenphp php-server` é executado, depois `/etc/frankenphp/php.ini` +- Arquivos de configuração adicionais: `/etc/frankenphp/php.d/*.ini` +- Extensões PHP: não podem ser carregadas, empacote-as no próprio binário +- Copie um dos `php.ini-production` ou `php.ini-development` fornecidos [nas fontes do PHP](https://github.com/php/php-src/). ## Configuração do Caddyfile -As [diretivas HTTP](https://caddyserver.com/docs/caddyfile/concepts#directives) -`php_server` ou `php` podem ser usadas dentro dos blocos de site para servir sua -aplicação PHP. +As diretivas HTTP `php_server` ou `php` [diretivas HTTP](https://caddyserver.com/docs/caddyfile/concepts#directives) podem ser usadas dentro dos blocos de site para servir sua aplicação PHP. Exemplo mínimo: ```caddyfile localhost { - # Habilita compressão (opcional) - encode zstd br gzip - # Executa arquivos PHP no diretório atual e serve assets - php_server + # Habilita compressão (opcional) + encode zstd br gzip + # Executa arquivos PHP no diretório atual e serve assets + php_server } ``` -Você também pode configurar explicitamente o FrankenPHP usando a opção global: -A [opção global](https://caddyserver.com/docs/caddyfile/concepts#global-options) -`frankenphp` pode ser usada para configurar o FrankenPHP. +Você também pode configurar explicitamente o FrankenPHP usando a [opção global](https://caddyserver.com/docs/caddyfile/concepts#global-options) `frankenphp`: ```caddyfile { - frankenphp { - num_threads # Define o número de threads PHP a serem iniciadas. Padrão: 2x o número de CPUs disponíveis. - max_threads # Limita o número de threads PHP adicionais que podem ser iniciadas em tempo de execução. Padrão: num_threads. Pode ser definido como 'auto'. - max_wait_time # Define o tempo máximo que uma requisição pode esperar por uma thread PHP livre antes de atingir o tempo limite. Padrão: disabled. - php_ini # Define uma diretiva php.ini. Pode ser usada várias vezes para definir múltiplas diretivas. - worker { - file # Define o caminho para o worker script. - num # Define o número de threads PHP a serem iniciadas, o padrão é 2x o número de CPUs disponíveis. - env # Define uma variável de ambiente extra para o valor fornecido. Pode ser especificada mais de uma vez para múltiplas variáveis de ambiente. - watch # Define o caminho para monitorar alterações em arquivos. Pode ser especificada mais de uma vez para múltiplos caminhos. - name # Define o nome do worker, usado em logs e métricas. Padrão: caminho absoluto do arquivo do worker. - max_consecutive_failures # Define o número máximo de falhas consecutivas antes do worker ser considerado inoperante. -1 significa que o worker sempre reiniciará. Padrão: 6. - } - } + frankenphp { + num_threads # Define o número de threads PHP a serem iniciadas. Padrão: 2x o número de CPUs disponíveis. + max_threads # Limita o número de threads PHP adicionais que podem ser iniciadas em tempo de execução. Padrão: num_threads. Pode ser definido como 'auto'. + max_wait_time # Define o tempo máximo que uma requisição pode esperar por uma thread PHP livre antes de atingir o tempo limite. Padrão: desabilitado. + php_ini # Define uma diretiva php.ini. Pode ser usada várias vezes para definir múltiplas diretivas. + worker { + file # Define o caminho para o worker script. + num # Define o número de threads PHP a serem iniciadas, o padrão é 2x o número de CPUs disponíveis. + env # Define uma variável de ambiente extra para o valor fornecido. Pode ser especificada mais de uma vez para múltiplas variáveis de ambiente. + watch # Define o caminho para monitorar alterações em arquivos. Pode ser especificada mais de uma vez para múltiplos caminhos. + name # Define o nome do worker, usado em logs e métricas. Padrão: caminho absoluto do arquivo do worker. + max_consecutive_failures # Define o número máximo de falhas consecutivas antes do worker ser considerado inoperante. -1 significa que o worker sempre reiniciará. Padrão: 6. + } + } } # ... ``` -Alternativamente, você pode usar a forma abreviada de uma linha da opção -`worker`: +Alternativamente, você pode usar a forma abreviada de uma linha da opção `worker`: ```caddyfile { - frankenphp { - worker - } + frankenphp { + worker + } } # ... ``` -Você também pode definir vários workers se servir várias aplicações no mesmo -servidor: +Você também pode definir vários workers se servir várias aplicações no mesmo servidor: ```caddyfile app.example.com { root /caminho/para/aplicacao/public - php_server { - root /caminho/para/aplicacao/public # permite melhor armazenamento em cache - worker index.php - } + php_server { + root /caminho/para/aplicacao/public # permite melhor armazenamento em cache + worker index.php + } } outra.example.com { root /caminho/para/outra/aplicacao/public - php_server { - root /caminho/para/outra/aplicacao/public - worker index.php - } + php_server { + root /caminho/para/outra/aplicacao/public + worker index.php + } } # ... ``` -Usar a diretiva `php_server` geralmente é o que você precisa, mas se precisar de -controle total, você pode usar a diretiva `php` de mais baixo nível. +Usar a diretiva `php_server` geralmente é o que você precisa, +mas se precisar de controle total, você pode usar a diretiva `php` de mais baixo nível. A diretiva `php` passa toda a entrada para o PHP, em vez de primeiro verificar -se é um arquivo PHP ou não. -Leia mais sobre isso na [página de desempenho](performance.md#try_files). +se é um arquivo PHP ou não. Leia mais sobre isso na [página de desempenho](performance.md#try_files). Usar a diretiva `php_server` é equivalente a esta configuração: ```caddyfile route { - # Adiciona barra final para requisições de diretório - @canonicalPath { - file {path}/index.php - not path */ - } - redir @canonicalPath {path}/ 308 - # Se o arquivo requisitado não existir, tenta os arquivos 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 + # Adiciona barra final para requisições de diretório + @canonicalPath { + file {path}/index.php + not path */ + } + redir @canonicalPath {path}/ 308 + # Se o arquivo requisitado não existir, tenta os arquivos 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 } ``` @@ -162,20 +176,20 @@ As diretivas `php_server` e `php` têm as seguintes opções: ```caddyfile php_server [] { - root # Define a pasta raiz para o site. Padrão: diretiva `root`. - split_path # Define as substrings para dividir o URI em duas partes. A primeira substring correspondente será usada para separar as "informações de caminho" do caminho. A primeira parte é sufixada com a substring correspondente e será assumida como o nome real do recurso (script CGI). A segunda parte será definida como PATH_INFO para o script usar. Padrão: `.php` - resolve_root_symlink false # Desabilita a resolução do diretório `root` para seu valor real avaliando um link simbólico, se houver (habilitado por padrão). - env # Define uma variável de ambiente extra para o valor fornecido. Pode ser especificada mais de uma vez para múltiplas variáveis de ambiente. - file_server off # Desabilita a diretiva interna file_server. - worker { # Cria um worker específico para este servidor. Pode ser especificada mais de uma vez para múltiplos workers. - file # Define o caminho para o worker script, pode ser relativo à raiz do php_server. - num # Define o número de threads PHP a serem iniciadas, o padrão é 2x o número de threads disponíveis. - name # Define o nome do worker, usado em logs e métricas. Padrão: caminho absoluto do arquivo do worker. Sempre começa com m# quando definido em um bloco php_server. - watch # Define o caminho para monitorar alterações em arquivos. Pode ser especificada mais de uma vez para múltiplos caminhos. - env # Define uma variável de ambiente extra para o valor fornecido. Pode ser especificada mais de uma vez para múltiplas variáveis de ambiente. As variáveis de ambiente para este worker também são herdadas do pai do php_server, mas podem ser sobrescritas aqui. - match # Corresponde o worker a um padrão de caminho. Substitui try_files e só pode ser usada na diretiva php_server. - } - worker # Também pode usar a forma abreviada, como no bloco global frankenphp. + root # Define a pasta raiz para o site. Padrão: diretiva `root`. + split_path # Define as substrings para dividir o URI em duas partes. A primeira substring correspondente será usada para separar as "informações de caminho" do caminho. A primeira parte é sufixada com a substring correspondente e será assumida como o nome real do recurso (script CGI). A segunda parte será definida como PATH_INFO para o script usar. Padrão: `.php` + resolve_root_symlink false # Desabilita a resolução do diretório `root` para seu valor real avaliando um link simbólico, se houver (habilitado por padrão). + env # Define uma variável de ambiente extra para o valor fornecido. Pode ser especificada mais de uma vez para múltiplas variáveis de ambiente. + file_server off # Desabilita a diretiva interna file_server. + worker { # Cria um worker específico para este servidor. Pode ser especificada mais de uma vez para múltiplos workers. + file # Define o caminho para o worker script, pode ser relativo à raiz do php_server. + num # Define o número de threads PHP a serem iniciadas, o padrão é 2x o número de threads disponíveis. + name # Define o nome para o worker, usado em logs e métricas. Padrão: caminho absoluto do arquivo do worker. Sempre começa com m# quando definido em um bloco php_server. + watch # Define o caminho para monitorar alterações em arquivos. Pode ser especificada mais de uma vez para múltiplos caminhos. + env # Define uma variável de ambiente extra para o valor fornecido. Pode ser especificada mais de uma vez para múltiplas variáveis de ambiente. As variáveis de ambiente para este worker também são herdadas do pai do php_server, mas podem ser sobrescritas aqui. + match # Corresponde o worker a um padrão de caminho. Substitui try_files e só pode ser usada na diretiva php_server. + } + worker # Também pode usar a forma abreviada, como no bloco global frankenphp. } ``` @@ -190,48 +204,46 @@ Isso é útil para ambientes de desenvolvimento. ```caddyfile { - frankenphp { - worker { - file /caminho/para/aplicacao/public/worker.php - watch - } - } + frankenphp { + worker { + file /caminho/para/aplicacao/public/worker.php + watch + } + } } ``` +Este recurso é frequentemente usado em combinação com [recarregamento a quente](hot-reload.md). + Se o diretório `watch` não for especificado, ele usará o valor padrão -`./**/*.{php,yaml,yml,twig,env}`, -que monitora todos os arquivos `.php`, `.yaml`, `.yml`, `.twig` e `.env` no -diretório e subdiretórios onde o processo FrankenPHP foi iniciado. -Você também pode especificar um ou mais diretórios por meio de um +`./**/*.{env,php,twig,yaml,yml}`, +que monitora todos os arquivos `.env`, `.php`, `.twig`, `.yaml` e `.yml` no +diretório e subdiretórios onde o processo FrankenPHP foi iniciado. Você pode, em vez disso, também especificar um ou mais diretórios por meio de um [padrão de nome de arquivo shell](https://pkg.go.dev/path/filepath#Match): ```caddyfile { - frankenphp { - worker { - file /caminho/para/aplicacao/public/worker.php - watch /caminho/para/aplicacao # monitora todos os arquivos em todos os subdiretórios de /caminho/para/aplicacao - watch /caminho/para/aplicacao/*.php # monitora arquivos terminados em .php em /caminho/para/aplicacao - watch /caminho/para/aplicacao/**/*.php # monitora arquivos PHP em /caminho/para/aplicacao e subdiretórios - watch /caminho/para/aplicacao/**/*.{php,twig} # monitora arquivos PHP e Twig em /caminho/para/aplicacao e subdiretórios - } - } + frankenphp { + worker { + file /caminho/para/aplicacao/public/worker.php + watch /caminho/para/aplicacao # monitora todos os arquivos em todos os subdiretórios de /caminho/para/aplicacao + watch /caminho/para/aplicacao/*.php # monitora arquivos terminados em .php em /caminho/para/aplicacao + watch /caminho/para/aplicacao/**/*.php # monitora arquivos PHP em /caminho/para/aplicacao e subdiretórios + watch /caminho/para/aplicacao/**/*.{php,twig} # monitora arquivos PHP e Twig em /caminho/para/aplicacao e subdiretórios + } + } } ``` - O padrão `**` significa monitoramento recursivo -- Diretórios também podem ser relativos (ao local de início do processo - FrankenPHP) -- Se você tiver vários workers definidos, todos eles serão reiniciados quando um - arquivo for alterado -- Tenha cuidado ao monitorar arquivos criados em tempo de execução (como logs), - pois eles podem causar reinicializações indesejadas de workers. +- Diretórios também podem ser relativos (ao local de início do processo FrankenPHP) +- Se você tiver vários workers definidos, todos eles serão reiniciados quando um arquivo for alterado +- Tenha cuidado ao monitorar arquivos criados em tempo de execução (como logs), pois eles podem causar reinicializações indesejadas de workers. O monitor de arquivos é baseado no [e-dant/watcher](https://github.com/e-dant/watcher). -## Correspondendo o worker a um caminho +## Correspondendo o Worker a um caminho Em aplicações PHP tradicionais, os scripts são sempre colocados no diretório público. @@ -248,81 +260,37 @@ padrão de caminho. ```caddyfile { - frankenphp { - php_server { - worker { - file /caminho/para/worker.php # arquivo pode estar fora do caminho público - match /api/* # todas as requisições que começam com /api/ serão tratadas por este worker - } - } - } -} -``` - -### Full Duplex (HTTP/1) - -Ao usar HTTP/1.x, pode ser desejável habilitar o modo full-duplex para permitir -a gravação de uma resposta antes que todo o corpo tenha sido lido. -(por exemplo: WebSocket, Server-Sent Events, etc.) - -Esta é uma configuração opcional que precisa ser adicionada às opções globais no -`Caddyfile`: - -```caddyfile -{ - servers { - enable_full_duplex - } + frankenphp { + php_server { + worker { + file /caminho/para/worker.php # arquivo pode estar fora do caminho público + match /api/* # todas as requisições que começam com /api/ serão tratadas por este worker + } + } + } } ``` -> [!CAUTION] -> -> Habilitar esta opção pode causar deadlock em clientes HTTP/1.x antigos que não -> suportam full-duplex. -> Isso também pode ser configurado usando a configuração de ambiente -> `CADDY_GLOBAL_OPTIONS`: - -```sh -CADDY_GLOBAL_OPTIONS="servers { - enable_full_duplex -}" -``` - -Você pode encontrar mais informações sobre esta configuração na -[documentação do Caddy](https://caddyserver.com/docs/caddyfile/options#enable-full-duplex). - ## Variáveis de ambiente -As seguintes variáveis de ambiente podem ser usadas para injetar diretivas Caddy -no `Caddyfile` sem modificá-lo: +As seguintes variáveis de ambiente podem ser usadas para injetar diretivas Caddy no `Caddyfile` sem modificá-lo: -- `SERVER_NAME`: altera - [os endereços nos quais escutar](https://caddyserver.com/docs/caddyfile/concepts#addresses), - os nomes de host fornecidos também serão usados para o certificado TLS gerado; -- `SERVER_ROOT`: altera o diretório raiz do site, o padrão é `public/`; -- `CADDY_GLOBAL_OPTIONS`: injeta - [opções globais](https://caddyserver.com/docs/caddyfile/options); +- `SERVER_NAME`: altera [os endereços nos quais escutar](https://caddyserver.com/docs/caddyfile/concepts#addresses), os nomes de host fornecidos também serão usados para o certificado TLS gerado. +- `SERVER_ROOT`: altera o diretório raiz do site, o padrão é `public/`. +- `CADDY_GLOBAL_OPTIONS`: injeta [opções globais](https://caddyserver.com/docs/caddyfile/options). - `FRANKENPHP_CONFIG`: injeta a configuração sob a diretiva `frankenphp`. -Quanto às SAPIs FPM e CLI, as variáveis de ambiente são expostas por padrão na -superglobal `$_SERVER`. +Quanto às SAPIs FPM e CLI, as variáveis de ambiente são expostas por padrão na superglobal `$_SERVER`. -O valor `S` da -[diretiva `variables_order` do PHP](https://www.php.net/manual/pt_BR/ini.core.php#ini.variables-order) -é sempre equivalente a `ES`, independentemente da colocação de `E` em outra -parte desta diretiva. +O valor `S` da [diretiva `variables_order` do PHP](https://www.php.net/manual/en/ini.core.php#ini.variables-order) é sempre equivalente a `ES`, independentemente da colocação de `E` em outra parte desta diretiva. ## Configuração do PHP -Para carregar -[arquivos de configuração adicionais do PHP](https://www.php.net/manual/pt_BR/configuration.file.php#configuration.file.scan), +Para carregar [arquivos de configuração adicionais do PHP](https://www.php.net/manual/en/configuration.file.php#configuration.file.scan), a variável de ambiente `PHP_INI_SCAN_DIR` pode ser usada. -Quando definida, o PHP carregará todos os arquivos com a extensão `.ini` -presentes nos diretórios fornecidos. +Quando definida, o PHP carregará todos os arquivos com a extensão `.ini` presentes nos diretórios fornecidos. -Você também pode alterar a configuração do PHP usando a diretiva `php_ini` no -`Caddyfile`: +Você também pode alterar a configuração do PHP usando a diretiva `php_ini` no `Caddyfile`: ```caddyfile { @@ -339,10 +307,45 @@ Você também pode alterar a configuração do PHP usando a diretiva `php_ini` n } ``` +### Desabilitando HTTPS + +Por padrão, o FrankenPHP habilitará automaticamente HTTPS para todos os nomes de host, incluindo `localhost`. +Se você deseja desabilitar o HTTPS (por exemplo, em um ambiente de desenvolvimento), você pode definir a variável de ambiente `SERVER_NAME` para `http://` ou `:80`: + +Alternativamente, você pode usar todos os outros métodos descritos na [documentação do Caddy](https://caddyserver.com/docs/automatic-https#activation). + +Se você deseja usar HTTPS com o endereço IP `127.0.0.1` em vez do nome de host `localhost`, por favor, leia a seção de [problemas conhecidos](known-issues.md#using-https127001-with-docker). + +### Full Duplex (HTTP/1) + +Ao usar `HTTP/1.x`, pode ser desejável habilitar o modo `full-duplex` para permitir a gravação de uma resposta antes que todo o corpo tenha sido lido. (por exemplo: [Mercure](mercure.md), WebSocket, Server-Sent Events, etc.) + +Esta é uma configuração opcional que precisa ser adicionada às opções globais no `Caddyfile`: + +```caddyfile +{ + servers { + enable_full_duplex + } +} +``` + +> [!CAUTION] +> +> Habilitar esta opção pode causar deadlock em clientes `HTTP/1.x` antigos que não suportam `full-duplex`. +> Isso também pode ser configurado usando a configuração de ambiente `CADDY_GLOBAL_OPTIONS`: + +```sh +CADDY_GLOBAL_OPTIONS="servers { + enable_full_duplex +}" +``` + +Você pode encontrar mais informações sobre esta configuração na [documentação do Caddy](https://caddyserver.com/docs/caddyfile/options#enable-full-duplex). + ## Habilitar o modo de depuração -Ao usar a imagem Docker, defina a variável de ambiente `CADDY_GLOBAL_OPTIONS` -como `debug` para habilitar o modo de depuração: +Ao usar a imagem Docker, defina a variável de ambiente `CADDY_GLOBAL_OPTIONS` como `debug` para habilitar o modo de depuração: ```console docker run -v $PWD:/app/public \ diff --git a/docs/pt-br/docker.md b/docs/pt-br/docker.md index a6ad90dc40..e544b7689c 100644 --- a/docs/pt-br/docker.md +++ b/docs/pt-br/docker.md @@ -1,21 +1,15 @@ # Construindo uma imagem Docker personalizada -[As imagens Docker do FrankenPHP](https://hub.docker.com/r/dunglas/frankenphp) -são baseadas em [imagens oficiais do PHP](https://hub.docker.com/_/php/). -Variantes do Debian e do Alpine Linux são fornecidas para arquiteturas -populares. +[As imagens Docker do FrankenPHP](https://hub.docker.com/r/dunglas/frankenphp) são baseadas em [imagens oficiais do PHP](https://hub.docker.com/_/php/). +Variantes do Debian e do Alpine Linux são fornecidas para arquiteturas populares. Variantes do Debian são recomendadas. Variantes para PHP 8.2, 8.3, 8.4 e 8.5 são fornecidas. -As tags seguem este padrão: -`dunglas/frankenphp:-php-`. +As tags seguem este padrão: `dunglas/frankenphp:-php-` -- `` e `` são números de versão do - FrankenPHP e do PHP, respectivamente, variando de maior (ex.: `1`), menor - (ex.: `1.2`) a versões de patch (ex.: `1.2.3`). -- `` é `bookworm` (para Debian Bookworm) ou `alpine` (para a versão estável - mais recente do Alpine). +- `` e `` são números de versão do FrankenPHP e do PHP, respectivamente, variando de maior (ex.: `1`), menor (ex.: `1.2`) a versões de patch (ex.: `1.2.3`). +- `` é `trixie` (para Debian Trixie), `bookworm` (para Debian Bookworm) ou `alpine` (para a versão estável mais recente do Alpine). [Navegue pelas tags](https://hub.docker.com/r/dunglas/frankenphp/tags). @@ -36,11 +30,13 @@ docker build -t minha-app-php . docker run -it --rm --name minha-app-rodando minha-app-php ``` +## Como ajustar a configuração + +Para sua conveniência, [um `Caddyfile` padrão](https://github.com/php/frankenphp/blob/main/caddy/frankenphp/Caddyfile) contendo variáveis de ambiente úteis é fornecido na imagem. + ## Como instalar mais extensões PHP -O script -[`docker-php-extension-installer`](https://github.com/mlocati/docker-php-extension-installer) -é fornecido na imagem base. +O script [`docker-php-extension-installer`](https://github.com/mlocati/docker-php-extension-installer) é fornecido na imagem base. Adicionar extensões PHP adicionais é simples: ```dockerfile @@ -57,12 +53,9 @@ RUN install-php-extensions \ ## Como instalar mais módulos Caddy -O FrankenPHP é construído sobre o Caddy, e todos os -[módulos Caddy](https://caddyserver.com/docs/modules/) podem ser usados com o -FrankenPHP. +O FrankenPHP é construído sobre o Caddy, e todos os [módulos Caddy](https://caddyserver.com/docs/modules/) podem ser usados com o FrankenPHP. -A maneira mais fácil de instalar módulos Caddy personalizados é usar o -[xcaddy](https://github.com/caddyserver/xcaddy): +A maneira mais fácil de instalar módulos Caddy personalizados é usar o [xcaddy](https://github.com/caddyserver/xcaddy): ```dockerfile FROM dunglas/frankenphp:builder AS builder @@ -78,8 +71,8 @@ RUN CGO_ENABLED=1 \ CGO_LDFLAGS="$(php-config --ldflags) $(php-config --libs)" \ xcaddy build \ --output /usr/local/bin/frankenphp \ - --with github.com/php/frankenphp=./ \ - --with github.com/php/frankenphp/caddy=./caddy/ \ + --with github.com/dunglas/frankenphp=./ \ + --with github.com/dunglas/frankenphp/caddy=./caddy/ \ --with github.com/dunglas/caddy-cbrotli \ # Mercure e Vulcain estão incluídos na compilação oficial, mas sinta-se # à vontade para removê-los @@ -93,11 +86,8 @@ FROM dunglas/frankenphp AS runner COPY --from=builder /usr/local/bin/frankenphp /usr/local/bin/frankenphp ``` -A imagem `builder` fornecida pelo FrankenPHP contém uma versão compilada da -`libphp`. -[Imagens de builder](https://hub.docker.com/r/dunglas/frankenphp/tags?name=builder) -são fornecidas para todas as versões do FrankenPHP e do PHP, tanto para Debian -quanto para Alpine. +A imagem `builder` fornecida pelo FrankenPHP contém uma versão compilada da `libphp`. +[Imagens de builder](https://hub.docker.com/r/dunglas/frankenphp/tags?name=builder) são fornecidas para todas as versões do FrankenPHP e do PHP, tanto para Debian quanto para Alpine. > [!TIP] > @@ -106,8 +96,7 @@ quanto para Alpine. ## Habilitando o modo worker por padrão -Defina a variável de ambiente `FRANKENPHP_CONFIG` para iniciar o FrankenPHP com -um worker script: +Defina a variável de ambiente `FRANKENPHP_CONFIG` para iniciar o FrankenPHP com um worker script: ```dockerfile FROM dunglas/frankenphp @@ -119,8 +108,7 @@ ENV FRANKENPHP_CONFIG="worker ./public/index.php" ## Usando um volume em desenvolvimento -Para desenvolver facilmente com o FrankenPHP, monte o diretório do seu host que -contém o código-fonte da aplicação como um volume no contêiner Docker: +Para desenvolver facilmente com o FrankenPHP, monte o diretório do seu host que contém o código-fonte da aplicação como um volume no contêiner Docker: ```console docker run -v $PWD:/app/public -p 80:80 -p 443:443 -p 443:443/udp --tty minha-app-php @@ -138,9 +126,9 @@ Com o Docker Compose: services: php: image: dunglas/frankenphp - # Descomente a linha a seguir se quiser usar um Dockerfile personalizado + # descomente a linha a seguir se quiser usar um Dockerfile personalizado #build: . - # Descomente a linha a seguir se quiser executar isso em um ambiente de + # descomente a linha a seguir se quiser executar isso em um ambiente de # produção # restart: always ports: @@ -151,7 +139,7 @@ services: - ./:/app/public - caddy_data:/data - caddy_config:/config - # Comente a linha a seguir em produção, isso permite ter logs legíveis em + # comente a linha a seguir em produção, isso permite ter logs legíveis em # desenvolvimento tty: true @@ -185,13 +173,9 @@ USER ${USER} ### Executando sem capacidades adicionais -Mesmo executando sem root, o FrankenPHP precisa do recurso -`CAP_NET_BIND_SERVICE` para vincular o servidor web em portas privilegiadas (80 -e 443). +Mesmo executando sem root, o FrankenPHP precisa da capacidade `CAP_NET_BIND_SERVICE` para vincular o servidor web em portas privilegiadas (80 e 443). -Se você expor o FrankenPHP em uma porta não privilegiada (1024 e acima), é -possível executar o servidor web como um usuário não root e sem a necessidade de -nenhuma capacidade adicional: +Se você expor o FrankenPHP em uma porta não privilegiada (1024 e acima), é possível executar o servidor web como um usuário não root e sem a necessidade de nenhuma capacidade adicional: ```dockerfile FROM dunglas/frankenphp @@ -209,24 +193,20 @@ RUN \ USER ${USER} ``` -Em seguida, defina a variável de ambiente `SERVER_NAME` para usar uma porta sem -privilégios. +Em seguida, defina a variável de ambiente `SERVER_NAME` para usar uma porta sem privilégios. Exemplo: `:8000` ## Atualizações As imagens Docker são construídas: -- Quando uma tag de uma nova versão é criada; -- Diariamente às 4h UTC, se novas versões das imagens oficiais do PHP estiverem - disponíveis. +- quando uma nova versão é marcada com uma tag; +- Diariamente às 4h UTC, se novas versões das imagens oficiais do PHP estiverem disponíveis. ## Versões de desenvolvimento -As versões de desenvolvimento estão disponíveis no repositório Docker -[`dunglas/frankenphp-dev`](https://hub.docker.com/repository/docker/dunglas/frankenphp-dev). -Uma nova construção é acionada sempre que um commit é enviado para o branch -principal do repositório do GitHub. +As versões de desenvolvimento estão disponíveis no repositório Docker [`dunglas/frankenphp-dev`](https://hub.docker.com/repository/docker/dunglas/frankenphp-dev). +Uma nova construção é acionada sempre que um commit é enviado para o branch principal do repositório do GitHub. As tags `latest*` apontam para o HEAD do branch `main`. -Tags no formato `sha-` também estão disponíveis. +Tags no formato `sha-` também estão disponíveis. \ No newline at end of file diff --git a/docs/pt-br/embed.md b/docs/pt-br/embed.md index 36002f6c65..105ab416c2 100644 --- a/docs/pt-br/embed.md +++ b/docs/pt-br/embed.md @@ -67,7 +67,7 @@ Docker que fornecemos. ```dockerfile FROM --platform=linux/amd64 dunglas/frankenphp:static-builder-gnu - # Se você pretende executar o binário em sistemas musl-libc, use o static-builder-musl + # Se você pretende executar o binário em sistemas musl-libc, use static-builder-musl # Copia sua aplicação WORKDIR /go/src/app/dist/app @@ -89,16 +89,16 @@ Docker que fornecemos. 2. Construa: ```console - docker build -t aplicacao-estatica -f static-build.Dockerfile . + docker build -t static-app -f static-build.Dockerfile . ``` 3. Extraia o binário: ```console - docker cp $(docker create --name aplicacao-estatica-tmp aplicacao-estatica):/go/src/app/dist/frankenphp-linux-x86_64 minha-aplicacao ; docker rm aplicacao-estatica-tmp + 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 ``` -O binário resultante é o arquivo `minha-aplicacao` no diretório atual. +O binário resultante é o arquivo chamado `my-app` no diretório atual. ## Criando um binário para outros sistemas operacionais @@ -115,33 +115,33 @@ O binário resultante é o arquivo `frankenphp--` no diretório `dist/ ## Usando o binário -É isso! O arquivo `minha-aplicacao` (ou `dist/frankenphp--` em outros +É isso! O arquivo `my-app` (ou `dist/frankenphp--` em outros sistemas operacionais) contém sua aplicação independente! Para iniciar a aplicação web, execute: ```console -./minha-aplicacao php-server +./my-app php-server ``` Se a sua aplicação contiver um [worker script](worker.md), inicie o worker com algo como: ```console -./minha-aplicacao php-server --worker public/index.php +./my-app php-server --worker public/index.php ``` Para habilitar HTTPS (um certificado Let's Encrypt é criado automaticamente), HTTP/2 e HTTP/3, especifique o nome de domínio a ser usado: ```console -./minha-aplicacao php-server --domain localhost +./my-app php-server --domain localhost ``` Você também pode executar os scripts PHP CLI incorporados ao seu binário: ```console -./minha-aplicacao php-cli bin/console +./my-app php-cli bin/console ``` ## Extensões PHP diff --git a/docs/pt-br/extensions.md b/docs/pt-br/extensions.md index d7c2c76669..fe23aefe8e 100644 --- a/docs/pt-br/extensions.md +++ b/docs/pt-br/extensions.md @@ -2,8 +2,7 @@ Com o FrankenPHP, você pode **escrever extensões PHP em Go**, o que permite criar **funções nativas de alto desempenho** que podem ser chamadas diretamente -do PHP. -Suas aplicações podem aproveitar qualquer biblioteca Go existente ou nova, bem +do PHP. Suas aplicações podem aproveitar qualquer biblioteca Go existente ou nova, bem como o famoso modelo de concorrência de **goroutines diretamente do seu código PHP**. @@ -20,11 +19,11 @@ rapidamente ao FrankenPHP. O FrankenPHP oferece duas maneiras de criar extensões PHP em Go: -1. **Usando o gerador de extensões** - A abordagem recomendada que gera todo o - código boilerplate necessário para a maioria dos casos de uso, permitindo que - você se concentre em escrever seu código em Go. -2. **Implementação manual** - Controle total sobre a estrutura da extensão para - casos de uso avançados. +1. **Usando o gerador de extensões** - A abordagem recomendada que gera todo o + código boilerplate necessário para a maioria dos casos de uso, permitindo que + você se concentre em escrever seu código em Go. +2. **Implementação manual** - Controle total sobre a estrutura da extensão para + casos de uso avançados. Começaremos com a abordagem do gerador, pois é a maneira mais fácil de começar, e, em seguida, mostraremos a implementação manual para aqueles que precisam de @@ -62,7 +61,7 @@ O primeiro passo para escrever uma extensão PHP em Go é criar um novo módulo Você pode usar o seguinte comando para isso: ```console -go mod init github.com// +go mod init example.com/example ``` O segundo passo é @@ -85,10 +84,15 @@ retornará a string resultante. Deve ficar assim: ```go +package example + +// #include +import "C" import ( - "C" - "github.com/dunglas/frankenphp" "strings" + "unsafe" + + "github.com/dunglas/frankenphp" ) //export_php:function repeat_this(string $str, int $count, bool $reverse): string @@ -110,13 +114,13 @@ func repeat_this(s *C.zend_string, count int64, reverse bool) unsafe.Pointer { Há duas coisas importantes a serem observadas aqui: -- Um comentário de diretiva `//export_php:function` define a assinatura da - função no PHP. - É assim que o gerador sabe como gerar a função PHP com os parâmetros e o tipo - de retorno corretos; -- A função deve retornar um `unsafe.Pointer`. - O FrankenPHP fornece uma API para ajudar você com o malabarismo de tipos entre - C e Go. +- Um comentário de diretiva `//export_php:function` define a assinatura da + função no PHP. + É assim que o gerador sabe como gerar a função PHP com os parâmetros e o tipo + de retorno corretos; +- A função deve retornar um `unsafe.Pointer`. + O FrankenPHP fornece uma API para ajudar você com o malabarismo de tipos entre + C e Go. Embora o primeiro ponto fale por si, o segundo pode ser mais difícil de entender. @@ -132,27 +136,27 @@ variáveis são armazenadas internamente no PHP. Esta tabela resume o que você precisa saber: | Tipo PHP | Tipo Go | Conversão direta | Auxiliar de C para Go | Auxiliar de Go para C | Suporte a métodos de classe | -| ------------------ | ----------------------------- | ---------------- | --------------------------------- | ---------------------------------- | --------------------------- | +| :----------------- | :---------------------------- | :--------------- | :-------------------------------- | :--------------------------------- | :-------------------------- | | `int` | `int64` | ✅ | - | - | ✅ | | `?int` | `*int64` | ✅ | - | - | ✅ | | `float` | `float64` | ✅ | - | - | ✅ | | `?float` | `*float64` | ✅ | - | - | ✅ | | `bool` | `bool` | ✅ | - | - | ✅ | | `?bool` | `*bool` | ✅ | - | - | ✅ | -| `?bool` | `*bool` | ✅ | - | - | ✅ | | `string`/`?string` | `*C.zend_string` | ❌ | `frankenphp.GoString()` | `frankenphp.PHPString()` | ✅ | | `array` | `frankenphp.AssociativeArray` | ❌ | `frankenphp.GoAssociativeArray()` | `frankenphp.PHPAssociativeArray()` | ✅ | | `array` | `map[string]any` | ❌ | `frankenphp.GoMap()` | `frankenphp.PHPMap()` | ✅ | | `array` | `[]any` | ❌ | `frankenphp.GoPackedArray()` | `frankenphp.PHPPackedArray()` | ✅ | | `mixed` | `any` | ❌ | `GoValue()` | `PHPValue()` | ❌ | +| `callable` | `*C.zval` | ❌ | - | `frankenphp.CallPHPCallable()` | ❌ | | `object` | `struct` | ❌ | _Ainda não implementado_ | _Ainda não implementado_ | ❌ | > [!NOTE] > Esta tabela ainda não é exaustiva e será completada à medida que a API de > tipos do FrankenPHP se tornar mais completa. > -> Tipos primitivos e arrays são suportados atualmente, especificamente para -> métodos de classe. +> Para métodos de classe especificamente, tipos primitivos e arrays são +> suportados atualmente. > Objetos ainda não podem ser usados como parâmetros de métodos ou tipos de > retorno. @@ -178,91 +182,168 @@ diretamente para um slice `[]any` ou um mapa não ordenado `map[string]any`. **Criando e manipulando arrays em Go:** ```go -//export_php:function process_data_ordered(array $input): array -func process_data_ordered_map(arr *C.zval) unsafe.Pointer { - // Converte um array associativo PHP para Go, mantendo a ordem - associativeArray := frankenphp.GoAssociativeArray(unsafe.Pointer(arr)) - - // percorre as entradas em ordem - for _, key := range associativeArray.Order { - value, _ = associativeArray.Map[key] - // faz algo com a chave e o valor - } +package example - // retorna um array ordenado - // se 'Order' não estiver vazio, apenas os pares chave-valor em 'Order' - // serão respeitados - return frankenphp.PHPAssociativeArray(AssociativeArray{ - Map: map[string]any{ - "chave1": "valor1", - "chave2": "valor2", - }, - Order: []string{"chave1", "chave2"}, - }) -} +// #include +import "C" +import ( + "unsafe" -//export_php:function process_data_unordered(array $input): array -func process_data_unordered_map(arr *C.zval) unsafe.Pointer { - // Converte um array associativo PHP em um mapa Go sem manter a ordem - // Ignorar a ordem terá melhor desempenho - goMap := frankenphp.GoMap(unsafe.Pointer(arr)) + "github.com/dunglas/frankenphp" +) - // percorre as entradas sem nenhuma ordem específica - for key, value := range goMap { - // faz algo com a chave e o valor +// export_php:function process_data_ordered(array $input): array +func process_data_ordered_map(arr *C.zend_array) unsafe.Pointer { + // Converte um array associativo PHP para Go, mantendo a ordem + associativeArray, err := frankenphp.GoAssociativeArray[any](unsafe.Pointer(arr)) + if err != nil { + // handle error } - // retorna um array não ordenado - return frankenphp.PHPMap(map[string]any{ - "chave1": "valor1", - "chave2": "valor2", - }) + // percorre as entradas em ordem + for _, key := range associativeArray.Order { + value, _ := associativeArray.Map[key] + // faz algo com a chave e o valor + _ = value // Usar 'value' para evitar erro de variável não utilizada + } + + // retorna um array ordenado + // se 'Order' não estiver vazio, apenas os pares chave-valor em 'Order' + // serão respeitados + return frankenphp.PHPAssociativeArray[string](frankenphp.AssociativeArray[string]{ + Map: map[string]string{ + "key1": "value1", + "key2": "value2", + }, + Order: []string{"key1", "key2"}, + }) } -//export_php:function process_data_packed(array $input): array -func process_data_packed(arr *C.zval) unsafe.Pointer { - // Converte um array compactado PHP para Go - goSlice := frankenphp.GoPackedArray(unsafe.Pointer(arr), false) +// export_php:function process_data_unordered(array $input): array +func process_data_unordered_map(arr *C.zend_array) unsafe.Pointer { + // Converte um array associativo PHP em um mapa Go sem manter a ordem + // Ignorar a ordem terá melhor desempenho + goMap, err := frankenphp.GoMap[any](unsafe.Pointer(arr)) + if err != nil { + // handle error + } - // percorre o slice em ordem - for index, value := range goSlice { - // faz algo com a chave e o valor + // percorre as entradas sem nenhuma ordem específica + for key, value := range goMap { + // faz algo com a chave e o valor + _, _ = key, value // Usar 'key' e 'value' para evitar erro de variável não utilizada + } + + // retorna um array não ordenado + return frankenphp.PHPMap(map[string]string { + "key1": "value1", + "key2": "value2", + }) +} + +// export_php:function process_data_packed(array $input): array +func process_data_packed(arr *C.zend_array) unsafe.Pointer { + // Converte um array compactado PHP para Go + goSlice, err := frankenphp.GoPackedArray(unsafe.Pointer(arr)) + if err != nil { + // handle error } - // retorna um array compactado - return frankenphp.PHPackedArray([]any{"valor1", "valor2", "value3"}) + // percorre o slice em ordem + for index, value := range goSlice { + // faz algo com o índice e o valor + _, _ = index, value // Usar 'index' e 'value' para evitar erro de variável não utilizada + } + + // retorna um array compactado + return frankenphp.PHPPackedArray([]string{"value1", "value2", "value3"}) } ``` **Principais recursos da conversão de arrays:** -- **Pares chave-valor ordenados** - Opção para manter a ordem do array - associativo; -- **Otimizado para múltiplos casos** - Opção para ignorar a ordem para melhor - desempenho ou converter diretamente para um slice; -- **Detecção automática de listas** - Ao converter para PHP, detecta - automaticamente se o array deve ser uma lista compactada ou um hashmap; -- **Arrays aninhados** - Os arrays podem ser aninhados e converterão todos os - tipos suportados automaticamente (`int64`, `float64`, `string`, `bool`, `nil`, - `AssociativeArray`, `map[string]any`, `[]any`); -- **Objetos não são suportados** - Atualmente, apenas tipos escalares e arrays - podem ser usados como valores. - Fornecer um objeto resultará em um valor `null` no array PHP. +- **Pares chave-valor ordenados** - Opção para manter a ordem do array + associativo; +- **Otimizado para múltiplos casos** - Opção para ignorar a ordem para melhor + desempenho ou converter diretamente para um slice; +- **Detecção automática de listas** - Ao converter para PHP, detecta + automaticamente se o array deve ser uma lista compactada ou um hashmap; +- **Arrays aninhados** - Os arrays podem ser aninhados e converterão todos os + tipos suportados automaticamente (`int64`, `float64`, `string`, `bool`, `nil`, + `AssociativeArray`, `map[string]any`, `[]any`); +- **Objetos não são suportados** - Atualmente, apenas tipos escalares e arrays + podem ser usados como valores. + Fornecer um objeto resultará em um valor `null` no array PHP. ##### Métodos disponíveis: empacotado e associativo -- `frankenphp.PHPAssociativeArray(arr frankenphp.AssociativeArray) unsafe.Pointer` - \- Converte para um array PHP ordenado com pares chave-valor; -- `frankenphp.PHPMap(arr map[string]any) unsafe.Pointer` - Converte um mapa em - um array PHP não ordenado com pares chave-valor; -- `frankenphp.PHPPackedArray(slice []any) unsafe.Pointer` - Converte um slice - em um array PHP compactado apenas com valores indexados; -- `frankenphp.GoAssociativeArray(arr unsafe.Pointer, ordered bool) frankenphp.AssociativeArray` - \- Converte um array PHP em um `AssociativeArray` Go ordenado (mapa com ordem); -- `frankenphp.GoMap(arr unsafe.Pointer) map[string]any` - Converte um array PHP - em um mapa Go não ordenado; -- `frankenphp.GoPackedArray(arr unsafe.Pointer) []any` - Converte um array PHP - em um slice Go. +- `frankenphp.PHPAssociativeArray(arr frankenphp.AssociativeArray) unsafe.Pointer` + \- Converte para um array PHP ordenado com pares chave-valor; +- `frankenphp.PHPMap(arr map[string]any) unsafe.Pointer` - Converte um mapa em + um array PHP não ordenado com pares chave-valor; +- `frankenphp.PHPPackedArray(slice []any) unsafe.Pointer` - Converte um slice + em um array PHP compactado apenas com valores indexados; +- `frankenphp.GoAssociativeArray(arr unsafe.Pointer, ordered bool) frankenphp.AssociativeArray` + \- Converte um array PHP em um `AssociativeArray` Go ordenado (mapa com ordem); +- `frankenphp.GoMap(arr unsafe.Pointer) map[string]any` - Converte um array PHP + em um mapa Go não ordenado; +- `frankenphp.GoPackedArray(arr unsafe.Pointer) []any` - Converte um array PHP + em um slice Go; +- `frankenphp.IsPacked(zval *C.zend_array) bool` - Verifica se um array PHP é + compactado (apenas indexado) ou associativo (pares chave-valor). + +### Trabalhando com Callables + +O FrankenPHP fornece uma maneira de trabalhar com callables PHP usando o helper +`frankenphp.CallPHPCallable`. Isso permite que você chame funções ou métodos PHP +a partir do código Go. + +Para demonstrar isso, vamos criar nossa própria função `array_map()` que recebe +um callable e um array, aplica o callable a cada elemento do array e retorna um +novo array com os resultados: + +```go +package example + +// #include +import "C" +import ( + "unsafe" + + "github.com/dunglas/frankenphp" +) + +// export_php:function my_array_map(array $data, callable $callback): array +func my_array_map(arr *C.zend_array, callback *C.zval) unsafe.Pointer { + goSlice, err := frankenphp.GoPackedArray[any](unsafe.Pointer(arr)) + if err != nil { + panic(err) + } + + result := make([]any, len(goSlice)) + + for index, value := range goSlice { + result[index] = frankenphp.CallPHPCallable(unsafe.Pointer(callback), []interface{}{value}) + } + + return frankenphp.PHPPackedArray(result) +} +``` + +Observe como usamos `frankenphp.CallPHPCallable()` para chamar o callable PHP +passado como parâmetro. Esta função recebe um ponteiro para o callable e um +array de argumentos, e retorna o resultado da execução do callable. Você pode +usar a sintaxe de callable a que está acostumado: + +```php +name` não funcionará); -- **Interface somente para métodos** - Todas as interações devem passar pelos - métodos que você definir; -- **Melhor encapsulamento** - A estrutura interna de dados é completamente - controlada pelo código Go; -- **Segurança de tipos** - Sem risco do código PHP corromper o estado interno - com tipos incorretos; -- **API mais limpa** - Força o design de uma interface pública adequada. +- **Sem acesso direto às propriedades**: Você não pode ler ou escrever + propriedades diretamente do PHP (`$user->name` não funcionará); +- **Interface somente para métodos** - Todas as interações devem passar pelos + métodos que você definir; +- **Melhor encapsulamento** - A estrutura interna de dados é completamente + controlada pelo código Go; +- **Segurança de tipos** - Sem risco do código PHP corromper o estado interno + com tipos incorretos; +- **API mais limpa** - Força o design de uma interface pública adequada. Essa abordagem fornece melhor encapsulamento e evita que o código PHP corrompa acidentalmente o estado interno dos seus objetos Go. @@ -308,6 +391,16 @@ métodos** para interagir com suas classes opacas. Use a diretiva `//export_php:method` para definir o comportamento: ```go +package example + +// #include +import "C" +import ( + "unsafe" + + "github.com/dunglas/frankenphp" +) + //export_php:class User type UserStruct struct { Name string @@ -342,6 +435,16 @@ Quando um parâmetro é anulável, ele se torna um ponteiro na sua função Go, permitindo que você verifique se o valor era `null` no PHP: ```go +package example + +// #include +import "C" +import ( + "unsafe" + + "github.com/dunglas/frankenphp" +) + //export_php:method User::updateInfo(?string $name, ?int $age, ?bool $active): void func (us *UserStruct) UpdateInfo(name *C.zend_string, age *int64, active *bool) { // Verifica se o parâmetro name foi fornecido (não nulo) @@ -354,7 +457,7 @@ func (us *UserStruct) UpdateInfo(name *C.zend_string, age *int64, active *bool) us.Age = int(*age) } - // Verifique se o parâmetro active foi fornecido (não nulo) + // Verifica se o parâmetro active foi fornecido (não nulo) if active != nil { us.Active = *active } @@ -363,13 +466,13 @@ func (us *UserStruct) UpdateInfo(name *C.zend_string, age *int64, active *bool) **Pontos-chave sobre parâmetros anuláveis:** -- **Tipos primitivos anuláveis** (`?int`, `?float`, `?bool`) tornam-se ponteiros - (`*int64`, `*float64`, `*bool`) em Go; -- **Strings anuláveis** (`?string`) permanecem como `*C.zend_string`, mas podem - ser `*nil`; -- **Verifique `nil`** antes de dereferenciar valores de ponteiro; -- **`null` do PHP torna-se `nil` do Go** - quando o PHP passa `null`, sua função - em Go recebe um ponteiro `nil`. +- **Tipos primitivos anuláveis** (`?int`, `?float`, `?bool`) tornam-se ponteiros + (`*int64`, `*float64`, `*bool`) em Go; +- **Strings anuláveis** (`?string`) permanecem como `*C.zend_string`, mas podem + ser `nil`; +- **Verifique `nil`** antes de dereferenciar valores de ponteiro; +- **`null` do PHP torna-se `nil` do Go** - quando o PHP passa `null`, sua função + em Go recebe um ponteiro `nil`. > [!WARNING] > Atualmente, os métodos de classe têm as seguintes limitações. @@ -393,11 +496,11 @@ $user = new User(); $user->setAge(25); echo $user->getName(); // Saída: (vazio, valor padrão) echo $user->getAge(); // Saída: 25 -$user->setNamePrefix("Funcionária"); +$user->setNamePrefix("Employee"); // ✅ Isso também funciona - parâmetros anuláveis -$user->updateInfo("João", 30, true); // Todos os parâmetros fornecidos -$user->updateInfo("Joana", null, false); // Age é nulo +$user->updateInfo("John", 30, true); // Todos os parâmetros fornecidos +$user->updateInfo("Jane", null, false); // Age é nulo $user->updateInfo(null, 25, null); // Name e active são nulos // ❌ Isso NÃO funcionará - acesso direto à propriedade @@ -422,6 +525,8 @@ outras constantes entre código Go e PHP. Use a diretiva `//export_php:const` para criar constantes PHP globais: ```go +package example + //export_php:const const MAX_CONNECTIONS = 100 @@ -441,6 +546,8 @@ Use a diretiva `//export_php:classconst ClassName` para criar constantes que pertencem a uma classe PHP específica: ```go +package example + //export_php:classconst User const STATUS_ACTIVE = 1 @@ -490,10 +597,15 @@ Por exemplo, vamos pegar a função `repeat_this()` que declaramos anteriormente alterar o último argumento para um inteiro: ```go +package example + +// #include +import "C" import ( - "C" - "github.com/dunglas/frankenphp" - "strings" + "strings" + "unsafe" + + "github.com/dunglas/frankenphp" ) //export_php:const @@ -510,37 +622,37 @@ const MODE_UPPERCASE = 2 //export_php:function repeat_this(string $str, int $count, int $mode): string func repeat_this(s *C.zend_string, count int64, mode int) unsafe.Pointer { - str := frankenphp.GoString(unsafe.Pointer(s)) + str := frankenphp.GoString(unsafe.Pointer(s)) - result := strings.Repeat(str, int(count)) - if mode == STR_REVERSE { - // inverte a string - } + result := strings.Repeat(str, int(count)) + if mode == STR_REVERSE { + // inverte a string + } - if mode == STR_NORMAL { - // sem operação, apenas para mostrar a constante - } + if mode == STR_NORMAL { + // sem operação, apenas para mostrar a constante + } - return frankenphp.PHPString(result, false) + return frankenphp.PHPString(result, false) } //export_php:class StringProcessor type StringProcessorStruct struct { - // campos internos + // campos internos } //export_php:method StringProcessor::process(string $input, int $mode): string func (sp *StringProcessorStruct) Process(input *C.zend_string, mode int64) unsafe.Pointer { - str := frankenphp.GoString(unsafe.Pointer(input)) + str := frankenphp.GoString(unsafe.Pointer(input)) - switch mode { - case MODE_LOWERCASE: - str = strings.ToLower(str) - case MODE_UPPERCASE: - str = strings.ToUpper(str) - } + switch mode { + case MODE_LOWERCASE: + str = strings.ToLower(str) + case MODE_UPPERCASE: + str = strings.ToUpper(str) + } - return frankenphp.PHPString(str, false) + return frankenphp.PHPString(str, false) } ``` @@ -558,13 +670,17 @@ todos os símbolos exportados em um namespace específico: ```go //export_php:namespace My\Extension -package main +package example -import "C" +import ( + "unsafe" + + "github.com/dunglas/frankenphp" +) //export_php:function hello(): string func hello() string { - return "Olá do namespace My\\Extension!" + return "Hello from My\\Extension namespace!" } //export_php:class User @@ -574,7 +690,7 @@ type UserStruct struct { //export_php:method User::getName(): string func (u *UserStruct) GetName() unsafe.Pointer { - return frankenphp.PHPString("João Ninguém", false) + return frankenphp.PHPString("John Doe", false) } //export_php:const @@ -589,25 +705,25 @@ colocadas sob esse namespace no PHP: ```php getName(); // "João Ninguém" +echo $user->getName(); // "John Doe" echo My\Extension\STATUS_ACTIVE; // 1 ``` #### Notas importantes -- Apenas **uma** diretiva de namespace é permitida por arquivo. - Se várias diretivas de namespace forem encontradas, o gerador retornará um - erro; -- O namespace se aplica a **todos** os símbolos exportados no arquivo: funções, - classes, métodos e constantes; -- Os nomes de namespace seguem as convenções de namespace do PHP, usando barras - invertidas (`\`) como separadores; -- Se nenhum namespace for declarado, os símbolos serão exportados para o - namespace global como de costume. +- Apenas **uma** diretiva de namespace é permitida por arquivo. + Se várias diretivas de namespace forem encontradas, o gerador retornará um + erro; +- O namespace se aplica a **todos** os símbolos exportados no arquivo: funções, + classes, métodos e constantes; +- Os nomes de namespace seguem as convenções de namespace do PHP, usando barras + invertidas (`\`) como separadores; +- Se nenhum namespace for declarado, os símbolos serão exportados para o + namespace global como de costume. ### Gerando a extensão @@ -644,7 +760,7 @@ CGO_CFLAGS=$(php-config --includes) \ CGO_LDFLAGS="$(php-config --ldflags) $(php-config --libs)" \ xcaddy build \ --output frankenphp \ - --with github.com///build + --with example.com/example/build ``` Observe que você aponta para o subdiretório `/build` que foi criado durante a @@ -661,12 +777,12 @@ Por exemplo, crie um arquivo `index.php` com o seguinte conteúdo: process('Olá mundo', StringProcessor::MODE_LOWERCASE); // "olá mundo" -echo $processor->process('Olá mundo', StringProcessor::MODE_UPPERCASE); // "OLÁ MUNDO" +echo $processor->process('Hello World', StringProcessor::MODE_LOWERCASE); // "hello world" +echo $processor->process('Hello World', StringProcessor::MODE_UPPERCASE); // "HELLO WORLD" ``` Depois de integrar sua extensão ao FrankenPHP, como demonstrado na seção @@ -695,25 +811,26 @@ Para fazer isso, crie um arquivo com o nome desejado, por exemplo, `extension.go`, e adicione o seguinte código: ```go -package ext_go +package example -//#include "extension.h" +// #include "extension.h" import "C" import ( - "unsafe" - "github.com/caddyserver/caddy/v2" - "github.com/dunglas/frankenphp" + "log/slog" + "unsafe" + + "github.com/dunglas/frankenphp" ) func init() { - frankenphp.RegisterExtension(unsafe.Pointer(&C.ext_module_entry)) + frankenphp.RegisterExtension(unsafe.Pointer(&C.ext_module_entry)) } //export go_print_something func go_print_something() { - go func() { - caddy.Log().Info("Olá de uma goroutine!") - }() + go func() { + slog.Info("Hello from a goroutine!") + }() } ``` @@ -776,9 +893,9 @@ extern zend_module_entry ext_module_entry; Em seguida, crie um arquivo chamado `extension.c` que executará as seguintes etapas: -- Incluir cabeçalhos PHP; -- Declarar nossa nova função nativa PHP `go_print()`; -- Declarar os metadados da extensão. +- Incluir cabeçalhos PHP; +- Declarar nossa nova função nativa PHP `go_print()`; +- Declarar os metadados da extensão. Vamos começar incluindo os cabeçalhos necessários: @@ -937,7 +1054,16 @@ As funções auxiliares cuidam de todo o gerenciamento de memória e da complexidade da conversão para nós. ```go -import "strings" +package example + +// #include +import "C" +import ( + "unsafe" + "strings" + + "github.com/dunglas/frankenphp" +) //export go_upper func go_upper(s *C.zend_string) *C.zend_string { @@ -976,7 +1102,7 @@ CGO_CFLAGS=$(php-config --includes) \ CGO_LDFLAGS="$(php-config --ldflags) $(php-config --libs)" \ xcaddy build \ --output frankenphp \ - --with github.com// + --with example.com/example ``` Pronto! @@ -995,7 +1121,7 @@ com exemplos para as funções que você implementou: go_print(); // Testa a função avançada -echo go_upper("olá mundo") . "\n"; +echo go_upper("hello world") . "\n"; ``` Agora você pode executar o FrankenPHP com este arquivo usando diff --git a/docs/pt-br/github-actions.md b/docs/pt-br/github-actions.md index 8ce398cf1b..b3c2af653b 100644 --- a/docs/pt-br/github-actions.md +++ b/docs/pt-br/github-actions.md @@ -1,39 +1,30 @@ # Usando GitHub Actions -Este repositório constrói e implanta a imagem Docker no -[Docker Hub](https://hub.docker.com/r/dunglas/frankenphp) a cada pull request -aprovado ou em seu próprio fork após a configuração. +Este repositório constrói e implanta a imagem Docker no [Docker Hub](https://hub.docker.com/r/dunglas/frankenphp) a cada pull request aprovado ou em seu próprio fork após a configuração. ## Configurando GitHub Actions Nas configurações do repositório, em "Secrets", adicione os seguintes segredos: -- `REGISTRY_LOGIN_SERVER`: O registro do Docker a ser usado (por exemplo, - `docker.io`). -- `REGISTRY_USERNAME`: O nome de usuário a ser usado para fazer login no - registro (por exemplo, `dunglas`). -- `REGISTRY_PASSWORD`: A senha a ser usada para fazer login no registro (por - exemplo, uma chave de acesso). +- `REGISTRY_LOGIN_SERVER`: O registro do Docker a ser usado (por exemplo, `docker.io`). +- `REGISTRY_USERNAME`: O nome de usuário a ser usado para fazer login no registro (por exemplo, `dunglas`). +- `REGISTRY_PASSWORD`: A senha a ser usada para fazer login no registro (por exemplo, uma chave de acesso). - `IMAGE_NAME`: O nome da imagem (por exemplo, `dunglas/frankenphp`). ## Construindo e enviando a imagem 1. Crie um pull request ou faça o push para o seu fork. 2. O GitHub Actions construirá a imagem e executará os testes. -3. Se a construção for bem-sucedida, a imagem será enviada para o registro - usando a tag `pr-x`, onde `x` é o número do PR. +3. Se a construção for bem-sucedida, a imagem será enviada para o registro usando a tag `pr-x`, onde `x` é o número do PR. ## Implantando a imagem -1. Após o merge do pull request, o GitHub Actions executará os testes novamente - e criará uma nova imagem. -2. Se a construção for bem-sucedida, a tag `main` será atualizada no registro do - Docker. +1. Após o merge do pull request, o GitHub Actions executará os testes novamente e criará uma nova imagem. +2. Se a construção for bem-sucedida, a tag `main` será atualizada no registro do Docker. ## Versões 1. Crie uma nova tag no repositório. 2. O GitHub Actions construirá a imagem e executará os testes. -3. Se a construção for bem-sucedida, a imagem será enviada para o registro - usando o nome da tag como tag (por exemplo, `v1.2.3` e `v1.2` serão criadas). +3. Se a construção for bem-sucedida, a imagem será enviada para o registro usando o nome da tag como tag (por exemplo, `v1.2.3` e `v1.2` serão criadas). 4. A tag `latest` também será atualizada. diff --git a/docs/pt-br/hot-reload.md b/docs/pt-br/hot-reload.md new file mode 100644 index 0000000000..299dd249f0 --- /dev/null +++ b/docs/pt-br/hot-reload.md @@ -0,0 +1,139 @@ +# Hot Reload + +FrankenPHP inclui um recurso de **hot reload** integrado projetado para melhorar significativamente a experiência do desenvolvedor. + +![Mercure](hot-reload.png) + +Este recurso oferece um fluxo de trabalho semelhante ao **Hot Module Replacement (HMR)** encontrado em ferramentas JavaScript modernas (como Vite ou webpack). +Em vez de atualizar manualmente o navegador após cada alteração de arquivo (código PHP, templates, arquivos JavaScript e CSS...), +FrankenPHP atualiza o conteúdo em tempo real. + +O Hot Reload funciona nativamente com WordPress, Laravel, Symfony e qualquer outra aplicação ou framework PHP. + +Quando ativado, o FrankenPHP monitora seu diretório de trabalho atual em busca de alterações no sistema de arquivos. +Quando um arquivo é modificado, ele envia uma atualização [Mercure](mercure.md) para o navegador. + +Dependendo da sua configuração, o navegador irá: + +- **Transformar o DOM** (preservando a posição de rolagem e o estado dos inputs) se o [Idiomorph](https://github.com/bigskysoftware/idiomorph) for carregado. +- **Recarregar a página** (recarregamento ao vivo padrão) se o Idiomorph não estiver presente. + +## Configuração + +Para habilitar o hot reload, ative o Mercure e adicione a subdiretiva `hot_reload` à diretiva `php_server` no seu `Caddyfile`. + +> [!AVISO] +> Este recurso é destinado **apenas a ambientes de desenvolvimento**. +> Não habilite `hot_reload` em produção, pois o monitoramento do sistema de arquivos acarreta sobrecarga de desempenho e expõe endpoints internos. + +```caddyfile +localhost + +mercure { + anonymous +} + +root public/ +php_server { + hot_reload +} +``` + +Por padrão, o FrankenPHP irá monitorar todos os arquivos no diretório de trabalho atual que correspondem a este padrão glob: `./**/*.{css,env,gif,htm,html,jpg,jpeg,js,mjs,php,png,svg,twig,webp,xml,yaml,yml}` + +É possível definir explicitamente os arquivos a serem monitorados usando a sintaxe glob: + +```caddyfile +localhost + +mercure { + anonymous +} + +root public/ +php_server { + hot_reload src/**/*{.php,.js} config/**/*.yaml +} +``` + +Use a forma longa para especificar o tópico Mercure a ser usado, bem como quais diretórios ou arquivos monitorar, fornecendo caminhos para a opção `hot_reload`: + +```caddyfile +localhost + +mercure { + anonymous +} + +root public/ +php_server { + hot_reload { + topic hot-reload-topic + watch src/**/*.php + watch assets/**/*.{ts,json} + watch templates/ + watch public/css/ + } +} +``` + +## Integração no Lado do Cliente + +Enquanto o servidor detecta as alterações, o navegador precisa se inscrever nesses eventos para atualizar a página. +FrankenPHP expõe a URL do Mercure Hub a ser usada para se inscrever em alterações de arquivo através da variável de ambiente `$_SERVER['FRANKENPHP_HOT_RELOAD']`. + +Uma biblioteca JavaScript conveniente, [frankenphp-hot-reload](https://www.npmjs.com/package/frankenphp-hot-reload), também está disponível para lidar com a lógica do lado do cliente. +Para usá-la, adicione o seguinte ao seu layout principal: + +```php + +FrankenPHP Hot Reload + + + + + +``` + +A biblioteca irá se inscrever automaticamente no hub Mercure, buscar a URL atual em segundo plano quando uma alteração de arquivo for detectada e transformar o DOM. +Ela está disponível como um pacote [npm](https://www.npmjs.com/package/frankenphp-hot-reload) e no [GitHub](https://github.com/dunglas/frankenphp-hot-reload). + +Alternativamente, você pode implementar sua própria lógica do lado do cliente inscrevendo-se diretamente no hub Mercure usando a classe nativa JavaScript `EventSource`. + +### Modo Worker + +Se você estiver executando sua aplicação no [Modo Worker](https://frankenphp.dev/docs/worker/), seu script de aplicação permanece na memória. +Isso significa que as alterações no seu código PHP não serão refletidas imediatamente, mesmo que o navegador recarregue. + +Para a melhor experiência do desenvolvedor, você deve combinar `hot_reload` com [a subdiretiva `watch` na diretiva `worker`](config.md#watching-for-file-changes). + +- `hot_reload`: atualiza o **navegador** quando arquivos mudam +- `worker.watch`: reinicia o worker quando arquivos mudam + +```caddy +localhost + +mercure { + anonymous +} + +root public/ +php_server { + hot_reload + worker { + file /path/to/my_worker.php + watch + } +} +``` + +### Como funciona + +1. **Monitoramento**: FrankenPHP monitora o sistema de arquivos em busca de modificações usando a [biblioteca `e-dant/watcher`](https://github.com/e-dant/watcher) por baixo dos panos (contribuímos com o binding Go). +2. **Reinício (Modo Worker)**: se `watch` estiver ativado na configuração do worker, o worker PHP é reiniciado para carregar o novo código. +3. **Envio**: um payload JSON contendo a lista de arquivos alterados é enviado para o [hub Mercure](https://mercure.rocks) integrado. +4. **Recebimento**: O navegador, escutando via a biblioteca JavaScript, recebe o evento Mercure. +5. **Atualização**: + +- Se **Idiomorph** for detectado, ele busca o conteúdo atualizado e transforma o HTML atual para corresponder ao novo estado, aplicando as alterações instantaneamente sem perder o estado. +- Caso contrário, `window.location.reload()` é chamado para recarregar a página. diff --git a/docs/pt-br/known-issues.md b/docs/pt-br/known-issues.md index cdc209c793..ad51865fb4 100644 --- a/docs/pt-br/known-issues.md +++ b/docs/pt-br/known-issues.md @@ -2,57 +2,44 @@ ## Extensões PHP não suportadas -As seguintes extensões são conhecidas por não serem compatíveis com o -FrankenPHP: +As seguintes extensões são conhecidas por não serem compatíveis com o FrankenPHP: | Nome | Motivo | Alternativas | | ----------------------------------------------------------------------------------------------------------- | ----------------- | -------------------------------------------------------------------------------------------------------------------- | -| [imap](https://www.php.net/manual/pt_BR/imap.installation.php) | Não é thread-safe | [javanile/php-imap2](https://github.com/javanile/php-imap2), [webklex/php-imap](https://github.com/Webklex/php-imap) | +| [imap](https://www.php.net/manual/en/imap.installation.php) | Não é 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/) | Não é thread-safe | - | ## Extensões PHP com falhas -As seguintes extensões apresentam falhas conhecidas e comportamentos inesperados -quando usadas com o FrankenPHP: +As seguintes extensões apresentam falhas conhecidas e comportamentos inesperados quando usadas com o FrankenPHP: -| Nome | Problema | -| ---------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| [ext-openssl](https://www.php.net/manual/pt_BR/book.openssl.php) | Ao usar uma versão estática do FrankenPHP (compilada com a `libc` `musl`), a extensão OpenSSL pode quebrar sob cargas pesadas. Uma solução alternativa é usar uma versão vinculada dinamicamente (como a usada em imagens Docker). Esta falha está [sendo monitorada pelo PHP](https://github.com/php/php-src/issues/13648) | +| Nome | Problema | +| ------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| [ext-openssl](https://www.php.net/manual/en/book.openssl.php) | Ao usar a musl libc, a extensão OpenSSL pode falhar sob cargas pesadas. O problema não ocorre ao usar a GNU libc, que é mais popular. Este bug está [sendo monitorado pelo PHP](https://github.com/php/php-src/issues/13648). | -## `get_browser` +## get_browser -A função -[`get_browser()`](https://www.php.net/manual/pt_BR/function.get-browser.php) -parece apresentar mau desempenho após algum tempo. -Uma solução alternativa é armazenar em cache (por exemplo, com -[APCu](https://www.php.net/manual/pt_BR/book.apcu.php)) os resultados por Agente -de Usuário, pois são estáticos. +A função [`get_browser()`](https://www.php.net/manual/en/function.get-browser.php) parece apresentar mau desempenho após algum tempo. Uma solução alternativa é armazenar em cache (por exemplo, com [APCu](https://www.php.net/manual/en/book.apcu.php)) os resultados por Agente de Usuário, pois são estáticos. -## Imagens Docker binárias independentes e baseadas em Alpine +## Imagens Docker binárias standalone e baseadas em Alpine -As imagens Docker binárias independentes e baseadas em Alpine -(`dunglas/frankenphp:*-alpine`) usam a [`libc` `musl`](https://musl.libc.org/) -em vez de [`glibc` e similares](https://www.etalabs.net/compare_libcs.html) para -manter um tamanho binário menor. +As imagens Docker binárias standalone e baseadas em Alpine (`dunglas/frankenphp:*-alpine`) usam [musl libc](https://musl.libc.org/) em vez de [glibc e similares](https://www.etalabs.net/compare_libcs.html), para manter um tamanho binário menor. Isso pode levar a alguns problemas de compatibilidade. -Em particular, o sinalizador glob `GLOB_BRACE` -[não está disponível](https://www.php.net/manual/pt_BR/function.glob.php) +Em particular, o sinalizador glob `GLOB_BRACE` [não está disponível](https://www.php.net/manual/en/function.glob.php) + +Prefira usar a variante GNU do binário estático e as imagens Docker baseadas em Debian se você encontrar problemas. ## Usando `https://127.0.0.1` com o Docker Por padrão, o FrankenPHP gera um certificado TLS para `localhost`. É a opção mais fácil e recomendada para desenvolvimento local. -Se você realmente deseja usar `127.0.0.1` como host, é possível configurá-lo -para gerar um certificado definindo o nome do servidor como `127.0.0.1`. +Se você realmente deseja usar `127.0.0.1` como host, é possível configurá-lo para gerar um certificado definindo o nome do servidor como `127.0.0.1`. -Infelizmente, isso não é suficiente ao usar o Docker devido ao -[seu sistema de rede](https://docs.docker.com/network/). -Você receberá um erro TLS semelhante a -`curl: (35) LibreSSL/3.3.6: erro:1404B438:SSL routines:ST_CONNECT:tlsv1 alert internal error`. +Infelizmente, isso não é suficiente ao usar o Docker devido ao [seu sistema de rede](https://docs.docker.com/network/). +Você receberá um erro TLS semelhante a `curl: (35) LibreSSL/3.3.6: erro:1404B438:SSL routines:ST_CONNECT:tlsv1 alert internal error`. -Se você estiver usando Linux, uma solução é usar -[o driver de rede do host](https://docs.docker.com/network/network-tutorial-host/): +Se você estiver usando Linux, uma solução é usar [o driver de rede do host](https://docs.docker.com/network/network-tutorial-host/): ```console docker run \ @@ -63,14 +50,9 @@ docker run \ ``` O driver de rede do host não é compatível com Mac e Windows. -Nessas plataformas, você terá que descobrir o endereço IP do contêiner e -incluí-lo nos nomes dos servidores. +Nessas plataformas, você terá que descobrir o endereço IP do contêiner e incluí-lo nos nomes dos servidores. -Execute o comando `docker network inspect bridge` e verifique a chave -`Containers` para identificar o último endereço IP atribuído atualmente sob a -chave `IPv4Address` e incremente-o em um. -Se nenhum contêiner estiver em execução, o primeiro endereço IP atribuído -geralmente é `172.17.0.2`. +Execute o comando `docker network inspect bridge` e verifique a chave `Containers` para identificar o último endereço IP atribuído atualmente sob a chave `IPv4Address` e incremente-o em um. Se nenhum contêiner estiver em execução, o primeiro endereço IP atribuído geralmente é `172.17.0.2`. Em seguida, inclua isso na variável de ambiente `SERVER_NAME`: @@ -84,13 +66,11 @@ docker run \ > [!CAUTION] > -> Certifique-se de substituir `172.17.0.3` pelo IP que será atribuído ao seu -> contêiner. +> Certifique-se de substituir `172.17.0.3` pelo IP que será atribuído ao seu contêiner. Agora você deve conseguir acessar `https://127.0.0.1` a partir da máquina host. -Se este não for o caso, inicie o FrankenPHP em modo de depuração para tentar -descobrir o problema: +Se este não for o caso, inicie o FrankenPHP em modo de depuração para tentar descobrir o problema: ```console docker run \ @@ -103,21 +83,12 @@ docker run \ ## Scripts do Composer que referenciam `@php` -[Scripts do Composer](https://getcomposer.org/doc/articles/scripts.md) podem -querer executar um binário PHP para algumas tarefas, por exemplo, em -[um projeto Laravel](laravel.md) para executar -`@php artisan package:discover --ansi`. -Isso -[atualmente falha](https://github.com/php/frankenphp/issues/483#issuecomment-1899890915) -por dois motivos: +[Scripts do Composer](https://getcomposer.org/doc/articles/scripts.md) podem querer executar um binário PHP para algumas tarefas, por exemplo, em [um projeto Laravel](laravel.md) para executar `@php artisan package:discover --ansi`. Isso [atualmente falha](https://github.com/php/frankenphp/issues/483#issuecomment-1899890915) por dois motivos: - O Composer não sabe como chamar o binário do FrankenPHP; -- O Composer pode adicionar configurações do PHP usando a flag `-d` no comando, - que o FrankenPHP ainda não suporta. +- O Composer pode adicionar configurações do PHP usando a flag `-d` no comando, que o FrankenPHP ainda não suporta. -Como solução alternativa, podemos criar um script de shell em -`/usr/local/bin/php` que remove os parâmetros não suportados e, em seguida, -chama o FrankenPHP: +Como solução alternativa, podemos criar um script de shell em `/usr/local/bin/php` que remove os parâmetros não suportados e, em seguida, chama o FrankenPHP: ```bash #!/usr/bin/env bash @@ -135,8 +106,7 @@ done /usr/local/bin/frankenphp php-cli ${args[@]} ``` -Em seguida, defina a variável de ambiente `PHP_BINARY` para o caminho do nosso -script `php` e execute o Composer: +Em seguida, defina a variável de ambiente `PHP_BINARY` para o caminho do nosso script `php` e execute o Composer: ```console export PHP_BINARY=/usr/local/bin/php @@ -145,8 +115,7 @@ composer install ## Solução de problemas de TLS/SSL com binários estáticos -Ao usar binários estáticos, você pode encontrar os seguintes erros relacionados -a TLS, por exemplo, ao enviar emails usando STARTTLS: +Ao usar binários estáticos, você pode encontrar os seguintes erros relacionados a TLS, por exemplo, ao enviar emails usando STARTTLS: ```text Unable to connect with STARTTLS: stream_socket_enable_crypto(): SSL operation failed with code 5. OpenSSL Error messages: @@ -156,27 +125,20 @@ error:80000002:system library::No such file or directory error:0A000086:SSL routines::certificate verify failed ``` -Como o binário estático não empacota certificados TLS, você precisa apontar o -OpenSSL para a instalação local de certificados de CA. +Como o binário estático não empacota certificados TLS, você precisa apontar o OpenSSL para a instalação local de certificados de CA. -Inspecione a saída de -[`openssl_get_cert_locations()`](https://www.php.net/manual/pt_BR/function.openssl-get-cert-locations.php), -para descobrir onde os certificados de CA devem ser instalados e armazene-os -neste local. +Inspecione a saída de [`openssl_get_cert_locations()`](https://www.php.net/manual/en/function.openssl-get-cert-locations.php), para descobrir onde os certificados de CA devem ser instalados e armazene-os neste local. > [!WARNING] > > Contextos web e CLI podem ter configurações diferentes. -> Certifique-se de executar `openssl_get_cert_locations()` no contexto -> apropriado. +> Certifique-se de executar `openssl_get_cert_locations()` no contexto apropriado. [Certificados CA extraídos do Mozilla podem ser baixados no site do cURL](https://curl.se/docs/caextract.html). -Como alternativa, muitas distribuições, incluindo Debian, Ubuntu e Alpine, -fornecem pacotes chamados `ca-certificates` que contêm esses certificados. +Como alternativa, muitas distribuições, incluindo Debian, Ubuntu e Alpine, fornecem pacotes chamados `ca-certificates` que contêm esses certificados. -Também é possível usar `SSL_CERT_FILE` e `SSL_CERT_DIR` para indicar à OpenSSL -onde procurar certificados CA: +Também é possível usar `SSL_CERT_FILE` e `SSL_CERT_DIR` para indicar à OpenSSL onde procurar certificados CA: ```console # Define variáveis de ambiente para certificados TLS diff --git a/docs/pt-br/laravel.md b/docs/pt-br/laravel.md index 05205c53a4..4a2df61dd2 100644 --- a/docs/pt-br/laravel.md +++ b/docs/pt-br/laravel.md @@ -2,8 +2,7 @@ ## Docker -Servir uma aplicação web [Laravel](https://laravel.com) com FrankenPHP é tão -fácil quanto montar o projeto no diretório `/app` da imagem Docker oficial. +Servir uma aplicação web [Laravel](https://laravel.com) com FrankenPHP é tão fácil quanto montar o projeto no diretório `/app` da imagem Docker oficial. Execute este comando a partir do diretório principal da sua aplicação Laravel: @@ -15,33 +14,30 @@ E divirta-se! ## Instalação local -Alternativamente, você pode executar seus projetos Laravel com FrankenPHP a -partir da sua máquina local: +Alternativamente, você pode executar seus projetos Laravel com FrankenPHP a partir da sua máquina local: 1. [Baixe o binário correspondente ao seu sistema](../#standalone-binary). -2. Adicione a seguinte configuração a um arquivo chamado `Caddyfile` no - diretório raiz do seu projeto Laravel: +2. Adicione a seguinte configuração a um arquivo chamado `Caddyfile` no diretório raiz do seu projeto Laravel: ```caddyfile { - frankenphp + frankenphp } # O nome de domínio do seu servidor localhost { - # Define o diretório raiz como public/ - root public/ - # Habilita a compressão (opcional) - encode zstd br gzip - # Executa os arquivos PHP a partir do diretório public/ e serve os assets - php_server { - try_files {path} index.php - } + # Define o diretório raiz como public/ + root public/ + # Habilita a compressão (opcional) + encode zstd br gzip + # Executa os arquivos PHP a partir do diretório public/ e serve os assets + php_server { + try_files {path} index.php + } } ``` -3. Inicie o FrankenPHP a partir do diretório raiz do seu projeto Laravel: - `frankenphp run`. +3. Inicie o FrankenPHP a partir do diretório raiz do seu projeto Laravel: `frankenphp run`. ## Laravel Octane @@ -51,15 +47,13 @@ O Octane pode ser instalado através do gerenciador de pacotes Composer: composer require laravel/octane ``` -Após instalar o Octane, você pode executar o comando `octane:install` do -Artisan, que instalará o arquivo de configuração do Octane em sua aplicação: +Após instalar o Octane, você pode executar o comando `octane:install` do Artisan, que instalará o arquivo de configuração do Octane em sua aplicação: ```console php artisan octane:install --server=frankenphp ``` -O servidor Octane pode ser iniciado por meio do comando `octane:frankenphp` do -Artisan. +O servidor Octane pode ser iniciado por meio do comando `octane:frankenphp` do Artisan. ```console php artisan octane:frankenphp @@ -67,49 +61,36 @@ php artisan octane:frankenphp O comando `octane:frankenphp` pode receber as seguintes opções: -- `--host`: O endereço IP ao qual o servidor deve se vincular (padrão: - `127.0.0.1`); +- `--host`: O endereço IP ao qual o servidor deve se vincular (padrão: `127.0.0.1`); - `--port`: A porta na qual o servidor deve estar disponível (padrão: `8000`); -- `--admin-port`: A porta na qual o servidor de administração deve estar - disponível (padrão: `2019`); -- `--workers`: O número de workers que devem estar disponíveis para processar - requisições (padrão: `auto`); -- `--max-requests`: O número de requisições a serem processadas antes de - recarregar o servidor (padrão: `500`); -- `--caddyfile`: O caminho para o arquivo `Caddyfile` do FrankenPHP (padrão: - [stub do `Caddyfile` no Laravel Octane](https://github.com/laravel/octane/blob/2.x/src/Commands/stubs/Caddyfile)); -- `--https`: Habilita HTTPS, HTTP/2 e HTTP/3 e gera e renova certificados - automaticamente; -- `--http-redirect`: Habilita o redirecionamento de HTTP para HTTPS (somente -- habilitado se `--https` for passada); -- `--watch`: Recarrega o servidor automaticamente quando a aplicação é - modificada; -- `--poll`: Usa o polling do sistema de arquivos durante a verificação para - monitorar arquivos em uma rede; -- `--log-level`: Registra mensagens de log no nível de log especificado ou acima - dele, usando o logger nativo do Caddy. +- `--admin-port`: A porta na qual o servidor de administração deve estar disponível (padrão: `2019`); +- `--workers`: O número de workers que devem estar disponíveis para processar requisições (padrão: `auto`); +- `--max-requests`: O número de requisições a serem processadas antes de recarregar o servidor (padrão: `500`); +- `--caddyfile`: O caminho para o arquivo `Caddyfile` do FrankenPHP (padrão: [stub do `Caddyfile` no Laravel Octane](https://github.com/laravel/octane/blob/2.x/src/Commands/stubs/Caddyfile)); +- `--https`: Habilita HTTPS, HTTP/2 e HTTP/3, e gera e renova certificados automaticamente; +- `--http-redirect`: Habilita o redirecionamento de HTTP para HTTPS (somente habilitado se `--https` for passada); +- `--watch`: Recarrega o servidor automaticamente quando a aplicação é modificada; +- `--poll`: Usa o polling do sistema de arquivos durante a verificação para monitorar arquivos em uma rede; +- `--log-level`: Registra mensagens de log no nível de log especificado ou acima dele, usando o logger nativo do Caddy. > [!TIP] -> Para obter logs JSON estruturados (útil ao usar soluções de análise de logs), -> passe explicitamente a opção `--log-level`. +> Para obter logs JSON estruturados (útil ao usar soluções de análise de logs), passe explicitamente a opção `--log-level`. -Saiba mais sobre o -[Laravel Octane em sua documentação oficial](https://laravel.com/docs/octane). +Veja também [como usar Mercure com Octane](#mercure-support). + +Saiba mais sobre o [Laravel Octane em sua documentação oficial](https://laravel.com/docs/octane). ## Aplicações Laravel como binários independentes -Usando o [recurso de incorporação de aplicações do FrankenPHP](embed.md), é -possível distribuir aplicações Laravel como binários independentes. +Usando o [recurso de incorporação de aplicações do FrankenPHP](embed.md), é possível distribuir aplicações Laravel como binários independentes. -Siga estes passos para empacotar sua aplicação Laravel como um binário -independente para Linux: +Siga estes passos para empacotar sua aplicação Laravel como um binário independente para Linux: -1. Crie um arquivo chamado `static-build.Dockerfile` no repositório da sua - aplicação: +1. Crie um arquivo chamado `static-build.Dockerfile` no repositório da sua aplicação: ```dockerfile FROM --platform=linux/amd64 dunglas/frankenphp:static-builder-gnu - # Se você pretende executar o binário em sistemas musl-libc, use o static-builder-musl + # Se você pretende executar o binário em sistemas musl-libc, use o static-builder-musl em vez disso # Copia sua aplicação WORKDIR /go/src/app/dist/app @@ -136,10 +117,8 @@ independente para Linux: > [!CAUTION] > - > Alguns arquivos `.dockerignore` ignorarão o diretório `vendor/` e os - > arquivos `.env`. - > Certifique-se de ajustar ou remover o arquivo `.dockerignore` antes da - > compilação. + > Alguns arquivos `.dockerignore` ignorarão o diretório `vendor/` e os arquivos `.env`. + > Certifique-se de ajustar ou remover o arquivo `.dockerignore` antes da compilação. 2. Construa: @@ -179,30 +158,48 @@ independente para Linux: Agora sua aplicação está pronta! -Saiba mais sobre as opções disponíveis e como compilar binários para outros -sistemas operacionais na documentação de -[incorporação de aplicações](embed.md). +Saiba mais sobre as opções disponíveis e como compilar binários para outros sistemas operacionais na documentação de [incorporação de aplicações](embed.md). ### Alterando o caminho do armazenamento -Por padrão, o Laravel armazena arquivos enviados, caches, logs, etc., no -diretório `storage/` da aplicação. -Isso não é adequado para aplicações embarcadas, pois cada nova versão será -extraída para um diretório temporário diferente. +Por padrão, o Laravel armazena arquivos enviados, caches, logs, etc., no diretório `storage/` da aplicação. +Isso não é adequado para aplicações embarcadas, pois cada nova versão será extraída para um diretório temporário diferente. + +Defina a variável de ambiente `LARAVEL_STORAGE_PATH` (por exemplo, no seu arquivo `.env`) ou chame o método `Illuminate\Foundation\Application::useStoragePath()` para usar um diretório fora do diretório temporário. + +### Suporte a Mercure + +[Mercure](https://mercure.rocks) é uma ótima maneira de adicionar recursos em tempo real às suas aplicações Laravel. +FrankenPHP inclui [suporte a Mercure nativamente](mercure.md). + +Se você não estiver usando [Octane](#laravel-octane), consulte [a documentação do Mercure](mercure.md). + +Se você estiver usando Octane, pode habilitar o suporte a Mercure adicionando as seguintes linhas ao seu arquivo `config/octane.php`: + +```php +// ... + +return [ + // ... + + 'mercure' => [ + 'anonymous' => true, + 'publisher_jwt' => '!ChangeThisMercureHubJWTSecretKey!', + 'subscriber_jwt' => '!ChangeThisMercureHubJWTSecretKey!', + ], +]; +``` + +Você pode usar [todas as diretivas suportadas pelo Mercure](https://mercure.rocks/docs/hub/config#directives) neste array. -Defina a variável de ambiente `LARAVEL_STORAGE_PATH` (por exemplo, no seu -arquivo `.env`) ou chame o método -`Illuminate\Foundation\Application::useStoragePath()` para usar um diretório -fora do diretório temporário. +Para publicar e assinar atualizações, recomendamos usar a biblioteca [Laravel Mercure Broadcaster](https://github.com/mvanduijker/laravel-mercure-broadcaster). +Alternativamente, consulte [a documentação do Mercure](mercure.md) para fazer isso em PHP puro e JavaScript. ### Executando o Octane com binários independentes -É possível até empacotar aplicações Octane do Laravel como binários -independentes! +É possível até empacotar aplicações Octane do Laravel como binários independentes! -Para fazer isso, [instale o Octane corretamente](#laravel-octane) e siga os -passos descritos na -[seção anterior](#aplicações-laravel-como-binários-independentes). +Para fazer isso, [instale o Octane corretamente](#laravel-octane) e siga os passos descritos na [seção anterior](#aplicações-laravel-como-binários-independentes). Em seguida, para iniciar o FrankenPHP no modo worker através do Octane, execute: @@ -212,6 +209,4 @@ PATH="$PWD:$PATH" frankenphp php-cli artisan octane:frankenphp > [!CAUTION] > -> Para que o comando funcione, o binário independente **deve** ser nomeado -> `frankenphp` porque o Octane precisa de um programa chamado `frankenphp` -> disponível no caminho. +> Para que o comando funcione, o binário independente **deve** ser nomeado `frankenphp` porque o Octane precisa de um programa chamado `frankenphp` disponível no caminho. diff --git a/docs/pt-br/logging.md b/docs/pt-br/logging.md new file mode 100644 index 0000000000..6bd73049f7 --- /dev/null +++ b/docs/pt-br/logging.md @@ -0,0 +1,73 @@ +# Registro (Logging) + +FrankenPHP se integra perfeitamente com o [sistema de registro do Caddy](https://caddyserver.com/docs/logging). +Você pode registrar mensagens usando funções PHP padrão ou aproveitar a função dedicada `frankenphp_log()` para recursos avançados +de registro estruturado. + +## `frankenphp_log()` + +A função `frankenphp_log()` permite que você emita logs estruturados diretamente de sua aplicação PHP, +facilitando muito a ingestão em plataformas como Datadog, Grafana Loki ou Elastic, bem como o suporte a OpenTelemetry. + +Internamente, `frankenphp_log()` envolve o [pacote `log/slog` do Go](https://pkg.go.dev/log/slog) para fornecer recursos de registro +ricos. + +Esses logs incluem o nível de severidade e dados de contexto opcionais. + +```php +function frankenphp_log(string $message, int $level = FRANKENPHP_LOG_LEVEL_INFO, array $context = []): void +``` + +### Parâmetros + +- **`message`**: A string da mensagem de log. +- **`level`**: O nível de severidade do log. Pode ser qualquer número inteiro arbitrário. Constantes de conveniência são fornecidas para níveis comuns: `FRANKENPHP_LOG_LEVEL_DEBUG` (`-4`), `FRANKENPHP_LOG_LEVEL_INFO` (`0`), `FRANKENPHP_LOG_LEVEL_WARN` (`4`) e `FRANKENPHP_LOG_LEVEL_ERROR` (`8`)). O padrão é `FRANKENPHP_LOG_LEVEL_INFO`. +- **`context`**: Um array associativo de dados adicionais a serem incluídos na entrada de log. + +### Exemplo + +```php + memory_get_usage(), + 'peak_usage' => memory_get_peak_usage(), + ], +); + +``` + +Ao visualizar os logs (por exemplo, via `docker compose logs`), a saída aparecerá como JSON estruturado: + +```json +{"level":"info","ts":1704067200,"logger":"frankenphp","msg":"Hello from FrankenPHP!"} +{"level":"warn","ts":1704067200,"logger":"frankenphp","msg":"Memory usage high","current_usage":10485760,"peak_usage":12582912} +``` + +## `error_log()` + +FrankenPHP também permite o registro usando a função padrão `error_log()`. Se o parâmetro `$message_type` for `4` (SAPI), +essas mensagens são roteadas para o logger do Caddy. + +Por padrão, as mensagens enviadas via `error_log()` são tratadas como texto não estruturado. +Elas são úteis para compatibilidade com aplicações ou bibliotecas existentes que dependem da biblioteca PHP padrão. + +### Exemplo com error_log() + +```php +error_log("Falha na conexão com o banco de dados", 4); +``` + +Isso aparecerá nos logs do Caddy, muitas vezes prefixado para indicar que se originou do PHP. + +> [!TIP] +> Para melhor observabilidade em ambientes de produção, prefira `frankenphp_log()` +> pois ele permite filtrar logs por nível (Debug, Error, etc.) +> e consultar campos específicos em sua infraestrutura de registro. diff --git a/docs/pt-br/mercure.md b/docs/pt-br/mercure.md index 8c10e8d7cb..376d6491f3 100644 --- a/docs/pt-br/mercure.md +++ b/docs/pt-br/mercure.md @@ -1,21 +1,149 @@ # Tempo real O FrankenPHP vem com um hub [Mercure](https://mercure.rocks) integrado! -O Mercure permite que você envie eventos em tempo real para todos os -dispositivos conectados: eles receberão um evento JavaScript instantaneamente. +O Mercure permite que você envie eventos em tempo real para todos os dispositivos conectados: eles receberão um evento JavaScript instantaneamente. -Não é necessária nenhuma biblioteca JS ou SDK! +É uma alternativa conveniente aos WebSockets, simples de usar e com suporte nativo em todos os navegadores web modernos! ![Mercure](mercure-hub.png) -Para habilitar o hub Mercure, atualize o `Caddyfile` conforme descrito -[no site do Mercure](https://mercure.rocks/docs/hub/config). +## Habilitando o Mercure -O caminho do hub Mercure é `/.well-known/mercure`. -Ao executar o FrankenPHP dentro do Docker, a URL de envio completa seria -`http://php/.well-known/mercure` (com `php` sendo o nome do contêiner que -executa o FrankenPHP). +O suporte ao Mercure é desativado por padrão. +Aqui está um exemplo mínimo de um `Caddyfile` habilitando tanto o FrankenPHP quanto o hub Mercure: -Para enviar atualizações do Mercure a partir do seu código, recomendamos o -[Componente Symfony Mercure](https://symfony.com/components/Mercure) (você não -precisa do framework full-stack do Symfony para usá-lo). +```caddyfile +# O nome do host para responder +localhost + +mercure { + # A chave secreta usada para assinar os tokens JWT para publicadores + publisher_jwt !ChangeThisMercureHubJWTSecretKey! + # Permite assinantes anônimos (sem JWT) + anonymous +} + +root public/ +php_server +``` + +> [!TIP] +> +> O [exemplo de `Caddyfile`](https://github.com/php/frankenphp/blob/main/caddy/frankenphp/Caddyfile) +> fornecido pelas [imagens Docker](docker.md) já inclui uma configuração Mercure comentada +> com variáveis de ambiente convenientes para configurá-lo. +> +> Descomente a seção Mercure em `/etc/frankenphp/Caddyfile` para habilitá-lo. + +## Assinando Atualizações + +Por padrão, o hub Mercure está disponível no caminho `/.well-known/mercure` do seu servidor FrankenPHP. +Para assinar atualizações, use a classe JavaScript nativa [`EventSource`](https://developer.mozilla.org/docs/Web/API/EventSource): + +```html + + +Exemplo Mercure + +``` + +## Publicando Atualizações + +### Usando `mercure_publish()` + +FrankenPHP fornece uma função conveniente `mercure_publish()` para publicar atualizações no hub Mercure integrado: + +```php + 'value'])); + +// Escreve nos logs do FrankenPHP +error_log("update $updateID published", 4); +``` + +A assinatura completa da função é: + +```php +/** + * @param string|string[] $topics + */ +function mercure_publish(string|array $topics, string $data = '', bool $private = false, ?string $id = null, ?string $type = null, ?int $retry = null): string {} +``` + +### Usando `file_get_contents()` + +Para despachar uma atualização para os assinantes conectados, envie uma requisição POST autenticada para o hub Mercure com os parâmetros `topic` e `data`: + +```php + [ + 'method' => 'POST', + 'header' => "Content-type: application/x-www-form-urlencoded\r\nAuthorization: Bearer " . JWT, + 'content' => http_build_query([ + 'topic' => 'my-topic', + 'data' => json_encode(['key' => 'value']), + ]), +]])); + +// Escreve nos logs do FrankenPHP +error_log("update $updateID published", 4); +``` + +A chave passada como parâmetro da opção `mercure.publisher_jwt` no `Caddyfile` deve ser usada para assinar o token JWT usado no cabeçalho `Authorization`. + +O JWT deve incluir uma reivindicação `mercure` com permissão `publish` para os tópicos nos quais você deseja publicar. +Consulte [a documentação do Mercure](https://mercure.rocks/spec#publishers) sobre autorização. + +Para gerar seus próprios tokens, você pode usar [este link do jwt.io](https://www.jwt.io/#token=eyJhbGciOiJIUzI1NiJ9.eyJtZXJjdXJlIjp7InB1Ymxpc2giOlsiKiJdfX0.PXwpfIGng6KObfZlcOXvcnWCJOWTFLtswGI5DZuWSK4), +mas para aplicações de produção, é recomendado usar tokens de curta duração gerados dinamicamente com uma [biblioteca JWT](https://www.jwt.io/libraries?programming_language=php) confiável. + +### Usando o Symfony Mercure + +Alternativamente, você pode usar o [Componente Symfony Mercure](https://symfony.com/components/Mercure), uma biblioteca PHP autônoma. + +Esta biblioteca lida com a geração de JWT, publicação de atualizações, bem como autorização baseada em cookies para assinantes. + +Primeiro, instale a biblioteca usando o Composer: + +```console +composer require symfony/mercure lcobucci/jwt +``` + +Então, você pode usá-lo assim: + +```php +publish(new \Symfony\Component\Mercure\Update('my-topic', json_encode(['key' => 'value']))); + +// Escreve nos logs do FrankenPHP +error_log("update $updateID published", 4); +``` + +O Mercure também é nativamente suportado por: + +- [Laravel](laravel.md#mercure-support) +- [Symfony](https://symfony.com/doc/current/mercure.html) +- [API Platform](https://api-platform.com/docs/core/mercure/) diff --git a/docs/pt-br/metrics.md b/docs/pt-br/metrics.md index 95c918fb13..8b6c79f869 100644 --- a/docs/pt-br/metrics.md +++ b/docs/pt-br/metrics.md @@ -1,29 +1,17 @@ # Métricas -Quando as [métricas do Caddy](https://caddyserver.com/docs/metrics) estão -habilitadas, o FrankenPHP expõe as seguintes métricas: +Quando as [métricas do Caddy](https://caddyserver.com/docs/metrics) estão habilitadas, o FrankenPHP expõe as seguintes métricas: - `frankenphp_total_threads`: O número total de threads PHP. -- `frankenphp_busy_threads`: O número de threads PHP processando uma requisição - no momento (workers em execução sempre consomem uma thread). +- `frankenphp_busy_threads`: O número de threads PHP processando uma requisição no momento (workers em execução sempre consomem uma thread). - `frankenphp_queue_depth`: O número de requisições regulares na fila. -- `frankenphp_total_workers{worker="[nome_do_worker]"}`: O número total de - workers. -- `frankenphp_busy_workers{worker="[nome_do_worker]"}`: O número de workers - processando uma requisição no momento. -- `frankenphp_worker_request_time{worker="[nome_do_worker]"}`: O tempo gasto no - processamento de requisições por todos os workers. -- `frankenphp_worker_request_count{worker="[nome_do_worker]"}`: O número de - requisições processadas por todos os workers. -- `frankenphp_ready_workers{worker="[nome_do_worker]"}`: O número de workers que - chamaram `frankenphp_handle_request` pelo menos uma vez. -- `frankenphp_worker_crashes{worker="[nome_do_worker]"}`: O número de vezes que - um worker foi encerrado inesperadamente. -- `frankenphp_worker_restarts{worker="[nome_do_worker]"}`: O número de vezes que - um worker foi reiniciado deliberadamente. -- `frankenphp_worker_queue_depth{worker="[nome_do_worker]"}`: O número de - requisições na fila. +- `frankenphp_total_workers{worker="[nome_do_worker]"}`: O número total de workers. +- `frankenphp_busy_workers{worker="[nome_do_worker]"}`: O número de workers processando uma requisição no momento. +- `frankenphp_worker_request_time{worker="[nome_do_worker]"}`: O tempo gasto no processamento de requisições por todos os workers. +- `frankenphp_worker_request_count{worker="[nome_do_worker]"}`: O número de requisições processadas por todos os workers. +- `frankenphp_ready_workers{worker="[nome_do_worker]"}`: O número de workers que chamaram `frankenphp_handle_request` pelo menos uma vez. +- `frankenphp_worker_crashes{worker="[nome_do_worker]"}`: O número de vezes que um worker foi encerrado inesperadamente. +- `frankenphp_worker_restarts{worker="[nome_do_worker]"}`: O número de vezes que um worker foi reiniciado deliberadamente. +- `frankenphp_worker_queue_depth{worker="[nome_do_worker]"}`: O número de requisições na fila. -Para métricas de worker, o placeholder `[nome_do_worker]` é substituído pelo -nome do worker no Caddyfile; caso contrário, o caminho absoluto do arquivo do -worker será usado. +Para métricas de worker, o placeholder `[nome_do_worker]` é substituído pelo nome do worker no Caddyfile; caso contrário, o caminho absoluto do arquivo do worker será usado. diff --git a/docs/pt-br/performance.md b/docs/pt-br/performance.md index 8b825be99e..0dd824efd5 100644 --- a/docs/pt-br/performance.md +++ b/docs/pt-br/performance.md @@ -5,10 +5,10 @@ facilidade de uso. No entanto, é possível melhorar substancialmente o desempenho usando uma configuração apropriada. -## Número de threads e workers +## Número de Threads e Workers Por padrão, o FrankenPHP inicia 2 vezes mais threads e workers (no modo worker) -do que a quantidade de CPU disponível. +do que o número de CPUs disponíveis. Os valores apropriados dependem muito de como sua aplicação foi escrita, do que ela faz e do seu hardware. @@ -28,9 +28,9 @@ diretiva `frankenphp`. ### `max_threads` -Embora seja sempre melhor saber exatamente como será o seu tráfego, aplicações +Embora seja sempre melhor saber exatamente como será o seu tráfego, aplicativos reais tendem a ser mais imprevisíveis. -A [configuração](config.md#configuracao-do-caddyfile) `max_threads` permite que +A [configuração](config.md#caddyfile-config) `max_threads` permite que o FrankenPHP crie threads adicionais automaticamente em tempo de execução até o limite especificado. `max_threads` pode ajudar você a descobrir quantas threads são necessárias para @@ -38,24 +38,25 @@ lidar com seu tráfego e pode tornar o servidor mais resiliente a picos de latência. Se definido como `auto`, o limite será estimado com base no `memory_limit` em seu `php.ini`. -Caso contrário, `auto` assumirá como padrão o valor 2x `num_threads`. +Se não for possível fazer isso, `auto` assumirá como padrão o valor 2x +`num_threads`. Lembre-se de que `auto` pode subestimar bastante o número de threads necessárias. `max_threads` é semelhante ao -[pm.max_children](https://www.php.net/manual/pt_BR/install.fpm.configuration.php#pm.max-children) +[pm.max_children](https://www.php.net/manual/en/install.fpm.configuration.php#pm.max-children) do PHP FPM. A principal diferença é que o FrankenPHP usa threads em vez de processos e as -delega automaticamente entre diferentes worker scripts e o modo clássico, +delega automaticamente entre diferentes scripts worker e o 'modo clássico', conforme necessário. -## Modo worker +## Modo Worker Habilitar [o modo worker](worker.md) melhora drasticamente o desempenho, mas sua aplicação precisa ser adaptada para ser compatível com este modo: você precisa -criar um worker script e garantir que a aplicação não esteja com vazamento de +criar um script worker e garantir que a aplicação não esteja com vazamento de memória. -## Não use `musl` +## Não use musl A variante Alpine Linux das imagens oficiais do Docker e os binários padrão que fornecemos usam [a biblioteca C `musl`](https://musl.libc.org). @@ -63,23 +64,19 @@ fornecemos usam [a biblioteca C `musl`](https://musl.libc.org). O PHP é conhecido por ser [mais lento](https://gitlab.alpinelinux.org/alpine/aports/-/issues/14381) ao usar esta biblioteca C alternativa em vez da biblioteca GNU tradicional, -especialmente quando compilado no modo ZTS (thread-safe), necessário para o -FrankenPHP. +especialmente quando compilado no modo ZTS (thread-safe), que é necessário para +o FrankenPHP. A diferença pode ser significativa em um ambiente com muitas threads. Além disso, -[alguns bugs só acontecem ao usar `musl`](https://github.com/php/php-src/issues?q=sort%3Aupdated-desc+is%3Aissue+is%3Aopen+label%3ABug+musl). - -Em ambientes de produção, recomendamos o uso do FrankenPHP vinculado à `glibc`. +[alguns bugs só acontecem ao usar musl](https://github.com/php/php-src/issues?q=sort%3Aupdated-desc+is%3Aissue+is%3Aopen+label%3ABug+musl). -Isso pode ser feito usando as imagens Docker do Debian (o padrão), baixando o -binário com sufixo -gnu de nossos -[Lançamentos](https://github.com/php/frankenphp/releases) ou -[compilando o FrankenPHP a partir do código-fonte](compile.md). +Em ambientes de produção, recomendamos o uso do FrankenPHP vinculado à glibc, +compilado com um nível de otimização apropriado. -Como alternativa, fornecemos binários `musl` estáticos compilados com -[o alocador `mimalloc`](https://github.com/microsoft/mimalloc), o que alivia os -problemas em cenários com threads. +Isso pode ser alcançado usando as imagens Docker do Debian, usando nossos +pacotes [.deb](https://debs.henderkes.com) ou [.rpm](https://rpms.henderkes.com) +dos mantenedores, ou [compilando o FrankenPHP a partir do código-fonte](compile.md). ## Configuração do runtime do Go @@ -124,7 +121,7 @@ explicitamente `try_files` assim: ```caddyfile php_server { try_files {path} index.php - root /raiz/da/sua/aplicacao # adicionar explicitamente a raiz aqui permite um melhor armazenamento em cache + root /root/to/your/app # adicionar explicitamente o root aqui permite um melhor cache } ``` @@ -132,11 +129,10 @@ Isso pode reduzir significativamente o número de operações desnecessárias co arquivos. Uma abordagem alternativa com 0 operações desnecessárias no sistema de arquivos -seria usar a diretiva `php` e separar os arquivos estáticos do PHP usando -caminhos. +seria usar a diretiva `php` e separar os arquivos estáticos do PHP por caminho. Essa abordagem funciona bem se toda a sua aplicação for servida por um arquivo de entrada. -Um exemplo de [configuração](config.md#configuracao-do-caddyfile) que serve +Um exemplo de [configuração](config.md#caddyfile-config) que serve arquivos estáticos a partir de uma pasta `/assets` poderia ser assim: ```caddyfile @@ -145,15 +141,15 @@ route { path /assets/* } - # tudo a partir de /assets é gerenciado pelo servidor de arquivos + # tudo em /assets é gerenciado pelo servidor de arquivos file_server @assets { - root /raiz/da/sua/aplicacao + root /root/to/your/app } - # tudo o que não está em /assets é gerenciado pelo seu arquivo index ou worker PHP + # tudo o que não está em /assets é gerenciado pelo seu arquivo PHP `index` ou `worker` rewrite index.php php { - root /raiz/da/sua/aplicacao # adicionar explicitamente a raiz aqui permite um melhor armazenamento em cache + root /root/to/your/app # adicionar explicitamente o root aqui permite um melhor cache } } ``` @@ -201,15 +197,51 @@ FrankenPHP. Em particular: -- Verifique se o [OPcache](https://www.php.net/manual/pt_BR/book.opcache.php) +- Verificar se o [OPcache](https://www.php.net/manual/en/book.opcache.php) está instalado, habilitado e configurado corretamente; -- Habilite as +- Habilitar as [otimizações do carregador automático do Composer](https://getcomposer.org/doc/articles/autoloader-optimization.md); -- Certifique-se de que o cache do `realpath` seja grande o suficiente para as +- Garantir que o cache do `realpath` seja grande o suficiente para as necessidades da sua aplicação; -- Use - [pré-carregamento](https://www.php.net/manual/pt_BR/opcache.preloading.php). +- Usar + [pré-carregamento](https://www.php.net/manual/en/opcache.preloading.php). Para mais detalhes, leia [a entrada dedicada na documentação do Symfony](https://symfony.com/doc/current/performance.html) -(a maioria das dicas é útil mesmo se você não usa o Symfony). +(a maioria das dicas é útil mesmo que você não utilize o Symfony). + +## Dividindo o Pool de Threads + +É comum que aplicativos interajam com serviços externos lentos, como uma +API que tende a ser não confiável sob alta carga ou consistentemente leva mais +de 10 segundos para responder. +Nesses casos, pode ser benéfico dividir o pool de threads para ter pools +"lentos" dedicados. +Isso evita que os endpoints lentos consumam todos os recursos/threads do servidor +e limita a concorrência de requisições indo em direção ao endpoint lento, +semelhante a um pool de conexões. + +```caddyfile +{ + frankenphp { + max_threads 100 # máximo de 100 threads compartilhadas por todos os workers + } +} + +example.com { + php_server { + root /app/public # o diretório raiz da sua aplicação + worker index.php { + match /slow-endpoint/* # todas as requisições com o caminho /slow-endpoint/* são tratadas por este pool de threads + num 10 # mínimo de 10 threads para requisições que correspondem a /slow-endpoint/* + } + worker index.php { + match * # todas as outras requisições são tratadas separadamente + num 20 # mínimo de 20 threads para outras requisições, mesmo que os endpoints lentos comecem a travar + } + } +} +``` + +Geralmente, também é aconselhável lidar com endpoints muito lentos de forma +assíncrona, usando mecanismos relevantes, como filas de mensagens. diff --git a/docs/pt-br/production.md b/docs/pt-br/production.md index 2a51ecb64b..35ec62e308 100644 --- a/docs/pt-br/production.md +++ b/docs/pt-br/production.md @@ -1,9 +1,8 @@ # Implantando em produção -Neste tutorial, aprenderemos como implantar uma aplicação PHP em um único -servidor usando o Docker Compose. +Neste tutorial, aprenderemos como implantar uma aplicação PHP em um único servidor usando o Docker Compose. -Se você estiver usando o Symfony, leia a documentação +Se você estiver usando o Symfony, prefira ler a documentação [Implantar em produção](https://github.com/dunglas/symfony-docker/blob/main/docs/production.md) do projeto Docker do Symfony (que usa FrankenPHP). @@ -17,7 +16,7 @@ Primeiro, crie um `Dockerfile` no diretório raiz do seu projeto PHP: ```dockerfile FROM dunglas/frankenphp -# Certifique-se de substituir "seu-nome-de-dominio.example.com" pelo seu nome de +# Certifique-se de substituir "your-domain-name.example.com" pelo seu nome de # domínio ENV SERVER_NAME=seu-nome-de-dominio.example.com # Se quiser desabilitar o HTTPS, use este valor: @@ -70,7 +69,7 @@ volumes: > Em desenvolvimento, você pode querer usar um volume, uma configuração PHP > diferente e um valor diferente para a variável de ambiente `SERVER_NAME`. > -> Consulte o projeto [Symfony Docker](https://github.com/dunglas/symfony-docker) +> Confira o projeto [Symfony Docker](https://github.com/dunglas/symfony-docker) > (que usa FrankenPHP) para um exemplo mais avançado usando imagens > multiestágio, Composer, extensões PHP extras, etc. @@ -120,7 +119,7 @@ Em seguida, crie um registro DNS do tipo `A` para o seu nome de domínio, apontando para o endereço IP do seu servidor: ```dns -seu-nome-de-dominio.example.com. IN A +seu-nome-de-dominio.example.com. IN A 207.154.233.113 ``` Exemplo com o serviço DigitalOcean Domains ("Networking" > "Domains"): @@ -144,10 +143,10 @@ Chaves de implantação também são [suportadas pelo GitLab](https://docs.gitla Exemplo com Git: ```console -git clone git@github.com:/.git +git clone git@github.com:/.git ``` -Acesse o diretório que contém seu projeto (``) e inicie a +Acesse o diretório que contém seu projeto (``) e inicie a aplicação em modo de produção: ```console diff --git a/docs/pt-br/static.md b/docs/pt-br/static.md index 3836099fc9..795e67d0c5 100644 --- a/docs/pt-br/static.md +++ b/docs/pt-br/static.md @@ -14,8 +14,8 @@ ser executados na No entanto, eles não podem carregar extensões PHP dinâmicas (como o Xdebug) e têm algumas limitações por usarem a `libc` `musl`. -A maioria dos binários estáticos requer apenas `glibc` e pode carregar extensões -dinâmicas. +Binários principalmente estáticos requerem apenas `glibc` e podem carregar +extensões dinâmicas. Sempre que possível, recomendamos o uso de compilações principalmente estáticas baseadas na `glibc`. @@ -202,7 +202,7 @@ docker rm static-builder-gnu docker rmi gnu-ext ``` -Isso criará `frankenphp` e `xdebug-zts.so` no diretório atual. +Isso terá criado `frankenphp` e `xdebug-zts.so` no diretório atual. Se você mover `xdebug-zts.so` para o diretório de extensões, adicione `zend_extension=xdebug-zts.so` ao seu `php.ini` e execute o FrankenPHP, ele carregará o Xdebug. diff --git a/docs/pt-br/wordpress.md b/docs/pt-br/wordpress.md new file mode 100644 index 0000000000..a8db381a71 --- /dev/null +++ b/docs/pt-br/wordpress.md @@ -0,0 +1,59 @@ +# WordPress + +Execute [WordPress](https://wordpress.org/) com FrankenPHP para desfrutar de uma pilha moderna e de alta performance com HTTPS automático, HTTP/3 e compressão Zstandard. + +## Instalação Mínima + +1. [Baixe o WordPress](https://wordpress.org/download/) +2. Extraia o arquivo ZIP e abra um terminal no diretório extraído +3. Execute: + + ```console + frankenphp php-server + ``` + +4. Acesse `http://localhost/wp-admin/` e siga as instruções de instalação +5. Aproveite! + +Para uma configuração pronta para produção, prefira usar `frankenphp run` com um `Caddyfile` como este: + +```caddyfile +example.com + +php_server +encode zstd br gzip +log +``` + +## Recarregamento Instantâneo + +Para usar o recurso de [recarregamento instantâneo](hot-reload.md) com WordPress, habilite o [Mercure](mercure.md) e adicione a sub-diretiva `hot_reload` à diretiva `php_server` no seu `Caddyfile`: + +```caddyfile +localhost + +mercure { + anonymous +} + +php_server { + hot_reload +} +``` + +Em seguida, adicione o código necessário para carregar as bibliotecas JavaScript no arquivo `functions.php` do seu tema WordPress: + +```php +function hot_reload() { + ?> + + + + + + [!TIP] +> A seção a seguir é necessária apenas antes do Symfony 7.4, onde o suporte nativo para o modo worker do FrankenPHP foi introduzido. + O modo worker do FrankenPHP é suportado pelo [Componente Symfony Runtime](https://symfony.com/doc/current/components/runtime.html). Para iniciar qualquer aplicação Symfony em um worker, instale o pacote @@ -78,8 +83,7 @@ uma biblioteca de terceiros: boot(); // Manipulador fora do loop para melhor desempenho (fazendo menos trabalho) $handler = static function () use ($myApp) { - // Chamado quando uma requisição é recebida, - // superglobals, php://input e similares são redefinidos - echo $myApp->handle($_GET, $_POST, $_COOKIE, $_FILES, $_SERVER); + try { + // Chamado quando uma requisição é recebida, + // superglobals, php://input e similares são redefinidos + echo $myApp->handle($_GET, $_POST, $_COOKIE, $_FILES, $_SERVER); + } catch (\Throwable $exception) { + // `set_exception_handler` é chamado apenas quando o script do worker termina, + // o que pode não ser o que você espera, então capture e trate as exceções aqui + (new \MyCustomExceptionHandler)->handleException($exception); + } }; $maxRequests = (int)($_SERVER['MAX_REQUESTS'] ?? 0); @@ -149,12 +159,11 @@ requisições a serem processadas, definindo uma variável de ambiente chamada ### Reiniciar os workers manualmente Embora seja possível reiniciar os workers -[em alterações de arquivo](config.md#monitorando-alteracoes-em-arquivos), também +[em alterações de arquivo](config.md#watching-for-file-changes), também é possível reiniciar todos os workers graciosamente por meio da [API de administração do Caddy](https://caddyserver.com/docs/api). Se o administrador estiver habilitado no seu -[Caddyfile](config.md#configuracao-do-caddyfile), você pode executar ping no -endpoint de reinicialização com uma simples requisição POST como esta: +[Caddyfile](config.md#caddyfile-config), você pode acessar o endpoint de reinicialização com uma simples requisição POST como esta: ```console curl -X POST http://localhost:2019/frankenphp/workers/restart @@ -211,4 +220,3 @@ $handler = static function () use ($workerServer) { }; // ... -``` diff --git a/docs/pt-br/x-sendfile.md b/docs/pt-br/x-sendfile.md index 6ad940434f..c8a6c2231a 100644 --- a/docs/pt-br/x-sendfile.md +++ b/docs/pt-br/x-sendfile.md @@ -20,7 +20,7 @@ Esse recurso é conhecido como **`X-Sendfile`** para Apache e Nos exemplos a seguir, assumimos que o diretório raiz do projeto é o diretório `public/` e que queremos usar PHP para servir arquivos armazenados fora do -diretório `public/`, de um diretório chamado `arquivos-privados/`. +diretório `public/`, de um diretório chamado `private-files/`. ## Configuração @@ -31,20 +31,18 @@ este recurso: root public/ # ... -+ # Necessário para Symfony, Laravel e outros projetos que usam o componente -+ # Symfony HttpFoundation ++ # Necessário para Symfony, Laravel e outros projetos que usam o componente Symfony HttpFoundation + request_header X-Sendfile-Type x-accel-redirect -+ request_header X-Accel-Mapping ../arquivos-privados=/arquivos-privados ++ request_header X-Accel-Mapping ../private-files=/private-files + + intercept { + @accel header X-Accel-Redirect * + handle_response @accel { -+ root arquivos-privados/ ++ root private-files/ + rewrite * {resp.header.X-Accel-Redirect} + method * GET + -+ # Remove o cabeçalho X-Accel-Redirect definido pelo PHP para maior -+ # segurança ++ # Remove o cabeçalho X-Accel-Redirect definido pelo PHP para maior segurança + header -X-Accel-Redirect + + file_server @@ -56,11 +54,11 @@ este recurso: ## PHP simples -Defina o caminho relativo do arquivo (de `arquivos-privados/`) como o valor do +Defina o caminho relativo do arquivo (do diretório `private-files/`) como o valor do cabeçalho `X-Accel-Redirect`: ```php -header('X-Accel-Redirect: arquivo.txt'); +header('X-Accel-Redirect: file.txt'); ``` ## Projetos que utilizam o componente Symfony HttpFoundation (Symfony, Laravel, Drupal...) @@ -74,7 +72,6 @@ Ele determinará automaticamente o valor correto para o cabeçalho use Symfony\Component\HttpFoundation\BinaryFileResponse; BinaryFileResponse::trustXSendfileTypeHeader(); -$response = new BinaryFileResponse(__DIR__.'/../arquivos-privados/arquivo.txt'); +$response = new BinaryFileResponse(__DIR__.'/../private-files/file.txt'); // ... -``` diff --git a/docs/ru/classic.md b/docs/ru/classic.md new file mode 100644 index 0000000000..bce296646c --- /dev/null +++ b/docs/ru/classic.md @@ -0,0 +1,11 @@ +# Использование классического режима + +Без какой-либо дополнительной конфигурации FrankenPHP работает в классическом режиме. В этом режиме FrankenPHP функционирует как традиционный PHP-сервер, напрямую обслуживая PHP-файлы. Это делает его бесшовной заменой для PHP-FPM или Apache с mod_php. + +Подобно Caddy, FrankenPHP принимает неограниченное количество соединений и использует [фиксированное количество потоков](config.md#caddyfile-config) для их обслуживания. Количество принятых и поставленных в очередь соединений ограничено только доступными системными ресурсами. +Пул потоков PHP работает с фиксированным количеством потоков, инициализируемых при запуске, что сравнимо со статическим режимом PHP-FPM. Также возможно позволить потокам [автоматически масштабироваться во время выполнения](performance.md#max_threads), аналогично динамическому режиму PHP-FPM. + +Соединения, поставленные в очередь, будут ждать неопределенное время, пока не станет доступен поток PHP для их обслуживания. Чтобы избежать этого, вы можете использовать [конфигурацию](config.md#caddyfile-config) `max_wait_time` в глобальной конфигурации FrankenPHP, чтобы ограничить время ожидания запроса свободного потока PHP до его отклонения. +Кроме того, вы можете установить разумный [таймаут записи в Caddy](https://caddyserver.com/docs/caddyfile/options#timeouts). + +Каждый экземпляр Caddy запускает только один пул потоков FrankenPHP, который будет использоваться всеми блоками `php_server`. diff --git a/docs/ru/compile.md b/docs/ru/compile.md index 549f03d0d1..6161a720db 100644 --- a/docs/ru/compile.md +++ b/docs/ru/compile.md @@ -1,13 +1,30 @@ # Компиляция из исходников -Этот документ объясняет, как создать бинарный файл FrankenPHP, который будет загружать PHP как динамическую библиотеку. +Этот документ объясняет, как создать бинарный файл FrankenPHP, который будет загружать PHP как динамическую библиотеку. Это рекомендуемый способ. -Альтернативно можно создать [статическую сборку](static.md). +Альтернативно, могут быть созданы [полностью и преимущественно статические сборки](static.md). ## Установка PHP -FrankenPHP совместим с PHP версии 8.2 и выше. +FrankenPHP совместим с PHP 8.2 и выше. + +### С помощью Homebrew (Linux и Mac) + +Самый простой способ установить версию `libphp`, совместимую с FrankenPHP, — использовать ZTS-пакеты, предоставляемые [Homebrew PHP](https://github.com/shivammathur/homebrew-php). + +Сначала, если вы еще не сделали этого, установите [Homebrew](https://brew.sh). + +Затем установите ZTS-вариант PHP, Brotli (опционально, для поддержки сжатия) и watcher (опционально, для обнаружения изменений файлов): + +```console +brew install shivammathur/php/php-zts brotli watcher +brew link --overwrite --force shivammathur/php/php-zts +``` + +### Путем компиляции PHP + +Альтернативно, вы можете скомпилировать PHP из исходников с опциями, необходимыми для FrankenPHP, выполнив следующие шаги. Сначала [загрузите исходники PHP](https://www.php.net/downloads.php) и распакуйте их: @@ -16,10 +33,10 @@ tar xf php-* cd php-*/ ``` -Далее выполните скрипт `configure` с параметрами, необходимыми для вашей платформы. +Далее выполните скрипт `configure` с параметрами, необходимыми для вашей платформы. Следующие флаги `./configure` обязательны, но вы можете добавить и другие, например, для компиляции расширений или дополнительных функций. -### Linux +#### Linux ```console ./configure \ @@ -29,13 +46,12 @@ cd php-*/ --enable-zend-max-execution-timers ``` -### Mac +#### Mac -Используйте пакетный менеджер [Homebrew](https://brew.sh/) для установки -`libiconv`, `bison`, `re2c` и `pkg-config`: +Используйте пакетный менеджер [Homebrew](https://brew.sh/) для установки необходимых и опциональных зависимостей: ```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 @@ echo 'export PATH="/opt/homebrew/opt/bison/bin:$PATH"' >> ~/.zshrc ```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/ ``` -## Компиляция PHP +#### Компиляция PHP Наконец, скомпилируйте и установите PHP: @@ -63,27 +76,23 @@ sudo make install ## Установка дополнительных зависимостей -Некоторые функции FrankenPHP зависят от опциональных системных зависимостей. -Альтернативно, эти функции можно отключить, передав соответствующие теги сборки компилятору Go. +Некоторые функции FrankenPHP зависят от опциональных системных зависимостей, которые должны быть установлены. +Альтернативно, эти функции можно отключить, передав теги сборки компилятору Go. -| Функция | Зависимость | Тег сборки для отключения | -| ----------------------------------------------- | --------------------------------------------------------------------- | ------------------------- | -| Сжатие Brotli | [Brotli](https://github.com/google/brotli) | nobrotli | -| Перезапуск worker-скриптов при изменении файлов | [Watcher C](https://github.com/e-dant/watcher/tree/release/watcher-c) | nowatcher | +| Функция | Зависимость | Тег сборки для отключения | +| :---------------------------------------------- | :------------------------------------------------------------------------------------------------------------ | :------------------------ | +| Сжатие Brotli | [Brotli](https://github.com/google/brotli) | nobrotli | +| Перезапуск worker-скриптов при изменении файлов | [Watcher C](https://github.com/e-dant/watcher/tree/release/watcher-c) | nowatcher | +| [Mercure](mercure.md) | [Библиотека Mercure Go](https://pkg.go.dev/github.com/dunglas/mercure) (устанавливается автоматически, лицензия AGPL) | nomercure | ## Компиляция Go-приложения -Теперь можно собрать итоговый бинарный файл: - -```console -curl -L https://github.com/php/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/): +Рекомендуемый способ — использовать [xcaddy](https://github.com/caddyserver/xcaddy) для компиляции FrankenPHP. +`xcaddy` также позволяет легко добавлять [пользовательские модули Caddy](https://caddyserver.com/docs/modules/) и расширения FrankenPHP: ```console CGO_ENABLED=1 \ @@ -94,17 +103,31 @@ xcaddy build \ --output frankenphp \ --with github.com/dunglas/frankenphp/caddy \ --with github.com/dunglas/mercure/caddy \ - --with github.com/dunglas/vulcain/caddy - # Добавьте дополнительные модули Caddy здесь + --with github.com/dunglas/vulcain/caddy \ + --with github.com/dunglas/caddy-cbrotli + # Добавьте дополнительные модули Caddy и расширения FrankenPHP здесь + # опционально, если вы хотите скомпилировать из ваших исходников frankenphp: + # --with github.com/dunglas/frankenphp=$(pwd) \ + # --with github.com/dunglas/frankenphp/caddy=$(pwd)/caddy + ``` > [!TIP] > -> Если вы используете musl libc (по умолчанию в Alpine Linux) и Symfony, -> возможно, потребуется увеличить размер стека. -> В противном случае вы можете столкнуться с ошибками вроде -> `PHP Fatal error: Maximum call stack size of 83360 bytes reached during compilation. Try splitting expression`. +> Если вы используете 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\'"'` +> Для этого измените переменную окружения `XCADDY_GO_BUILD_FLAGS`, например, на +> `XCADDY_GO_BUILD_FLAGS=$'-ldflags "-w -s -extldflags \'-Wl,-z,stack-size=0x80000\'"'` > (измените значение размера стека в зависимости от требований вашего приложения). + +### Без xcaddy + +Альтернативно, можно скомпилировать FrankenPHP без `xcaddy`, используя команду `go` напрямую: + +```console +curl -L https://github.com/php/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 +``` diff --git a/docs/ru/config.md b/docs/ru/config.md index 7a2c25ba66..6b6e821ca7 100644 --- a/docs/ru/config.md +++ b/docs/ru/config.md @@ -1,47 +1,81 @@ -# Конфигурация +# Конфигурация -FrankenPHP, Caddy, а также модули Mercure и Vulcain могут быть настроены с использованием [конфигурационных форматов, поддерживаемых Caddy](https://caddyserver.com/docs/getting-started#your-first-config). +FrankenPHP, Caddy, а также модули [Mercure](mercure.md) и [Vulcain](https://vulcain.rocks) могут быть настроены с использованием [форматов, поддерживаемых Caddy](https://caddyserver.com/docs/getting-started#your-first-config). -В [Docker-образах](docker.md) файл `Caddyfile` находится по пути `/etc/frankenphp/Caddyfile`. -Статический бинарный файл будет искать `Caddyfile` в директории запуска. +Наиболее распространенным форматом является `Caddyfile` — простой, человекочитаемый текстовый формат. +По умолчанию FrankenPHP будет искать `Caddyfile` в текущей директории. +Вы можете указать собственный путь с помощью опции `-c` или `--config`. -PHP можно настроить [с помощью файла `php.ini`](https://www.php.net/manual/en/configuration.file.php). +Минимальный `Caddyfile` для обслуживания PHP-приложения показан ниже: -PHP-интерпретатор будет искать в следующих местах: +```caddyfile +# The hostname to respond to +localhost + +# Optionaly, the directory to serve files from, otherwise defaults to the current directory +#root public/ +php_server +``` + +Более продвинутый `Caddyfile`, включающий больше возможностей и предоставляющий удобные переменные окружения, находится [в репозитории FrankenPHP](https://github.com/php/frankenphp/blob/main/caddy/frankenphp/Caddyfile), +а также поставляется с Docker-образами. + +Сам PHP может быть настроен [с помощью файла `php.ini`](https://www.php.net/manual/en/configuration.file.php). + +В зависимости от метода установки, FrankenPHP и PHP-интерпретатор будут искать конфигурационные файлы в местах, описанных ниже. + +## Docker + +FrankenPHP: -Docker: +- `/etc/frankenphp/Caddyfile`: основной конфигурационный файл +- `/etc/frankenphp/Caddyfile.d/*.caddyfile`: дополнительные конфигурационные файлы, загружаемые автоматически -- 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-/` -- Вы должны скопировать официальный шаблон, предоставляемый проектом PHP: +PHP: + +- `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-/` +- Вам следует скопировать официальный шаблон, предоставляемый проектом PHP: ```dockerfile FROM dunglas/frankenphp -# Для production: +# Production: RUN cp $PHP_INI_DIR/php.ini-production $PHP_INI_DIR/php.ini -# Или для development: +# Or development: RUN cp $PHP_INI_DIR/php.ini-development $PHP_INI_DIR/php.ini ``` -Установка FrankenPHP (.rpm или .deb): +## RPM и Debian пакеты + +FrankenPHP: + +- `/etc/frankenphp/Caddyfile`: основной конфигурационный файл +- `/etc/frankenphp/Caddyfile.d/*.caddyfile`: дополнительные конфигурационные файлы, загружаемые автоматически + +PHP: + +- `php.ini`: `/etc/php-zts/php.ini` (файл `php.ini` с производственными настройками предоставляется по умолчанию) +- дополнительные конфигурационные файлы: `/etc/php-zts/conf.d/*.ini` + +## Статический бинарный файл + +FrankenPHP: -- php.ini: `/etc/frankenphp/php.ini` По умолчанию предоставляется файл php.ini с производственными настройками. -- дополнительные файлы конфигурации: `/etc/frankenphp/php.d/*.ini` -- расширения php: `/usr/lib/frankenphp/modules/` +- В текущей рабочей директории: `Caddyfile` -Статический бинарный файл: +PHP: -- 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/). +- `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-приложения. +[HTTP-директивы](https://caddyserver.com/docs/caddyfile/concepts#directives) `php_server` или `php` могут быть использованы в блоках сайта для обслуживания вашего PHP-приложения. Минимальный пример: @@ -54,18 +88,22 @@ localhost { } ``` -Вы также можете явно настроить FrankenPHP с помощью глобальной опции: -[Глобальная опция](https://caddyserver.com/docs/caddyfile/concepts#global-options) `frankenphp` может быть использована для настройки FrankenPHP. +Вы также можете явно настроить FrankenPHP с помощью [глобальной опции](https://caddyserver.com/docs/caddyfile/concepts#global-options) `frankenphp`: ```caddyfile { frankenphp { - num_threads # Указывает количество потоков PHP. По умолчанию: 2x от числа доступных CPU. + num_threads # Устанавливает количество PHP-потоков для запуска. По умолчанию: 2x от числа доступных CPU. + max_threads # Ограничивает количество дополнительных PHP-потоков, которые могут быть запущены во время выполнения. По умолчанию: num_threads. Может быть установлено в 'auto'. + max_wait_time # Устанавливает максимальное время, в течение которого запрос может ожидать свободный PHP-поток до тайм-аута. По умолчанию: отключено. + php_ini # Устанавливает директиву php.ini. Может быть использовано несколько раз для установки нескольких директив. worker { - file # Указывает путь к worker-скрипту. - num # Указывает количество потоков PHP. По умолчанию: 2x от числа доступных CPU. - env # Устанавливает дополнительную переменную окружения. Можно указать несколько раз для разных переменных. - watch # Указывает путь для отслеживания изменений файлов.Можно указать несколько раз для разных путей. + file # Устанавливает путь к worker-скрипту. + num # Устанавливает количество PHP-потоков для запуска, по умолчанию 2x от числа доступных CPU. + env # Устанавливает дополнительную переменную окружения с указанным значением. Может быть указано несколько раз для нескольких переменных окружения. + watch # Устанавливает путь для отслеживания изменений файлов. Может быть указано несколько раз для нескольких путей. + name # Устанавливает имя worker, используемое в логах и метриках. По умолчанию: абсолютный путь к файлу worker. + max_consecutive_failures # Устанавливает максимальное количество последовательных сбоев, после которых worker считается неработоспособным; -1 означает, что worker будет всегда перезапускаться. По умолчанию: 6. } } } @@ -89,7 +127,7 @@ localhost { ```caddyfile app.example.com { - root /path/to/app/public + root /path/to/app/public php_server { root /path/to/app/public # позволяет лучше кэшировать worker index.php @@ -97,7 +135,7 @@ app.example.com { } other.example.com { - root /path/to/other/public + root /path/to/other/public php_server { root /path/to/other/public worker index.php @@ -107,19 +145,21 @@ other.example.com { # ... ``` -Использование директивы `php_server` — это то, что нужно в большинстве случаев. Однако если требуется полный контроль, вы можете использовать более низкоуровневую директиву `php`: +Использование директивы `php_server` — это то, что нужно в большинстве случаев, +но если вам нужен полный контроль, вы можете использовать более низкоуровневую директиву `php`. +Директива `php` передает все входные данные в PHP, вместо того чтобы сначала проверять, является ли это PHP-файлом или нет. Подробнее об этом читайте на [странице производительности](performance.md#try_files). Использование директивы `php_server` эквивалентно следующей конфигурации: ```caddyfile route { - # Добавить слэш в конец запросов к директориям + # Add trailing slash for directory requests @canonicalPath { file {path}/index.php not path */ } redir @canonicalPath {path}/ 308 - # Если запрошенный файл не существует, попытаться использовать файлы index + # If the requested file does not exist, try index files @indexFiles file { try_files {path} {path}/index.php index.php split_path .php @@ -136,17 +176,18 @@ route { ```caddyfile php_server [] { - root # Указывает корневую директорию сайта. По умолчанию: директива `root`. - split_path # Устанавливает подстроки для разделения URI на две части. Первая часть будет использована как имя ресурса (CGI-скрипта), вторая часть — как PATH_INFO. По умолчанию: `.php`. - resolve_root_symlink false # Отключает разрешение символьных ссылок для `root` (включено по умолчанию). - env # Устанавливает дополнительные переменные окружения. Можно указать несколько раз для разных переменных. + root # Устанавливает корневую папку сайта. По умолчанию: директива `root`. + split_path # Устанавливает подстроки для разделения URI на две части. Первая соответствующая подстрока будет использована для разделения "path info" от пути. Первая часть будет дополнена соответствующей подстрокой и будет считаться именем фактического ресурса (CGI-скрипта). Вторая часть будет установлена как PATH_INFO для использования скриптом. По умолчанию: `.php`. + resolve_root_symlink false # Отключает разрешение корневой директории до ее фактического значения путем оценки символической ссылки, если таковая существует (включено по умолчанию). + env # Устанавливает дополнительную переменную окружения с указанным значением. Может быть указано несколько раз для нескольких переменных окружения. file_server off # Отключает встроенную директиву file_server. - worker { # Создает worker, специфичный для этого сервера. Можно указать несколько раз для разных workers. - file # Указывает путь к worker-скрипту, может быть относительным к корню php_server - num # Указывает количество потоков PHP. По умолчанию: 2x от числа доступных CPU. + worker { # Создает worker, специфичный для этого сервера. Может быть указано несколько раз для нескольких workers. + file # Устанавливает путь к worker-скрипту, может быть относительным к корню php_server. + num # Устанавливает количество PHP-потоков для запуска, по умолчанию 2x от числа доступных CPU. name # Устанавливает имя для worker, используемое в логах и метриках. По умолчанию: абсолютный путь к файлу worker. Всегда начинается с m# при определении в блоке php_server. - watch # Указывает путь для отслеживания изменений файлов. Можно указать несколько раз для разных путей. - env # Устанавливает дополнительную переменную окружения. Можно указать несколько раз для разных переменных. Переменные окружения для этого worker также наследуются от родительского php_server, но могут быть переопределены здесь. + watch # Устанавливает путь для отслеживания изменений файлов. Может быть указано несколько раз для нескольких путей. + env # Устанавливает дополнительную переменную окружения с указанным значением. Может быть указано несколько раз для нескольких переменных окружения. Переменные окружения для этого worker также наследуются от родительского php_server, но могут быть переопределены здесь. + match # сопоставляет worker с шаблоном пути. Переопределяет try_files и может быть использовано только в директиве php_server. } worker # Также можно использовать краткую форму как в глобальном блоке frankenphp. } @@ -154,9 +195,11 @@ php_server [] { ### Отслеживание изменений файлов -Поскольку workers запускают ваше приложение только один раз и держат его в памяти, изменения в PHP-файлах не будут применяться сразу. +Поскольку workers запускают ваше приложение только один раз и держат его в памяти, любые изменения +в ваших PHP-файлах не будут отражены немедленно. -Для разработки можно настроить перезапуск workers при изменении файлов с помощью директивы `watch`: +Workers могут быть перезапущены при изменении файлов с помощью директивы `watch`. +Это полезно для сред разработки. ```caddyfile { @@ -169,8 +212,12 @@ php_server [] { } ``` -Если директория для `watch` не указана, по умолчанию будет использоваться путь `./**/*.{php,yaml,yml,twig,env}`, -который отслеживает все файлы с расширениями `.php`, `.yaml`, `.yml`, `.twig` и `.env` в директории, где был запущен процесс FrankenPHP, и во всех её поддиректориях. Вы также можете указать одну или несколько директорий с использованием [шаблона имён файлов](https://pkg.go.dev/path/filepath#Match): +Эта функция часто используется в сочетании с [горячей перезагрузкой](hot-reload.md). + +Если директория `watch` не указана, по умолчанию будет использоваться `./**/*.{env,php,twig,yaml,yml}`, +который отслеживает все файлы `.env`, `.php`, `.twig`, `.yaml` и `.yml` в директории и поддиректориях, +где был запущен процесс FrankenPHP. Вы также можете указать одну или несколько директорий с помощью +[шаблона имени файла оболочки](https://pkg.go.dev/path/filepath#Match): ```caddyfile { @@ -178,26 +225,94 @@ php_server [] { 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 # отслеживает PHP-файлы в /path/to/app и поддиректориях - watch /path/to/app/**/*.{php,twig} # отслеживает PHP и Twig-файлы в /path/to/app и поддиректориях + watch /path/to/app/**/*.{php,twig} # отслеживает PHP- и Twig-файлы в /path/to/app и поддиректориях } } } ``` -- Шаблон `**` указывает на рекурсивное отслеживание. -- Директории могут быть указаны относительно директории запуска FrankenPHP. -- Если у вас определено несколько workers, все они будут перезапущены при изменении файлов. -- Избегайте отслеживания файлов, создаваемых во время выполнения (например, логов), так как это может вызвать нежелательные перезапуски. +- Шаблон `**` означает рекурсивное отслеживание. +- Директории также могут быть относительными (относительно места запуска процесса FrankenPHP). +- Если у вас определено несколько workers, все они будут перезапущены при изменении файла. +- Будьте осторожны с отслеживанием файлов, которые создаются во время выполнения (например, логов), так как это может вызвать нежелательные перезапуски worker. + +Наблюдатель за файлами основан на [e-dant/watcher](https://github.com/e-dant/watcher). + +## Сопоставление Worker с путем + +В традиционных PHP-приложениях скрипты всегда размещаются в публичной директории. +Это также относится и к worker-скриптам, которые обрабатываются как любые другие PHP-скрипты. +Если вы хотите разместить worker-скрипт вне публичной директории, вы можете сделать это с помощью директивы `match`. + +Директива `match` — это оптимизированная альтернатива `try_files`, доступная только внутри `php_server` и `php`. +Следующий пример всегда будет отдавать файл из публичной директории, если он присутствует, +и в противном случае перенаправлять запрос worker, соответствующему шаблону пути. + +```caddyfile +{ + frankenphp { + php_server { + worker { + file /path/to/worker.php # файл может находиться за пределами публичного пути + match /api/* # все запросы, начинающиеся с /api/, будут обрабатываться этим worker + } + } + } +} +``` + +## Переменные окружения -Механизм отслеживания файлов основан на [e-dant/watcher](https://github.com/e-dant/watcher). +Следующие переменные окружения могут быть использованы для внедрения директив Caddy в `Caddyfile` без его изменения: + +- `SERVER_NAME`: изменение [адресов для прослушивания](https://caddyserver.com/docs/caddyfile/concepts#addresses); предоставленные хостнеймы также будут использованы для генерации TLS-сертификата +- `SERVER_ROOT`: изменение корневой директории сайта, по умолчанию `public/` +- `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`, присутствующие в указанных директориях. + +Вы также можете изменить конфигурацию PHP с помощью директивы `php_ini` в `Caddyfile`: + +```caddyfile +{ + frankenphp { + php_ini memory_limit 256M + + # or + + php_ini { + memory_limit 256M + max_execution_time 15 + } + } +} +``` + +### Отключение HTTPS + +По умолчанию FrankenPHP автоматически включает HTTPS для всех хостнеймов, включая `localhost`. +Если вы хотите отключить HTTPS (например, в среде разработки), вы можете установить переменную окружения `SERVER_NAME` в `http://` или `:80`: + +В качестве альтернативы вы можете использовать все другие методы, описанные в [документации Caddy](https://caddyserver.com/docs/automatic-https#activation). + +Если вы хотите использовать HTTPS с IP-адресом `127.0.0.1` вместо хостнейма `localhost`, пожалуйста, прочтите раздел [известных проблем](known-issues.md#using-https127001-with-docker). ### Полный дуплекс (HTTP/1) -При использовании HTTP/1.x можно включить режим полного дуплекса, чтобы разрешить запись ответа до завершения чтения тела запроса (например, для WebSocket, Server-Sent Events и т.д.). +При использовании HTTP/1.x может быть желательно включить режим полного дуплекса, чтобы разрешить запись ответа до того, как будет прочитано все тело запроса. (например: [Mercure](mercure.md), WebSocket, Server-Sent Events и т.д.) -Эта опция включается вручную и должна быть добавлена в глобальные настройки `Caddyfile`: +Это опциональная конфигурация, которую необходимо добавить в глобальные опции в `Caddyfile`: ```caddyfile { @@ -209,8 +324,8 @@ php_server [] { > [!CAUTION] > -> Включение этой опции может привести к зависанию устаревших HTTP/1.x клиентов, которые не поддерживают полный дуплекс. -> Настройка также доступна через переменную окружения `CADDY_GLOBAL_OPTIONS`: +> Включение этой опции может привести к зависанию старых HTTP/1.x клиентов, которые не поддерживают полный дуплекс. +> Это также можно настроить с помощью переменной окружения `CADDY_GLOBAL_OPTIONS`: ```sh CADDY_GLOBAL_OPTIONS="servers { @@ -220,23 +335,6 @@ CADDY_GLOBAL_OPTIONS="servers { Дополнительную информацию об этой настройке можно найти в [документации 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`, чтобы включить режим отладки: diff --git a/docs/ru/docker.md b/docs/ru/docker.md index 4376dc7ee9..0967868837 100644 --- a/docs/ru/docker.md +++ b/docs/ru/docker.md @@ -2,14 +2,14 @@ [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 и 8.5. +Доступны варианты для PHP 8.2, 8.3, 8.4 и 8.5. Теги следуют следующему шаблону: `dunglas/frankenphp:-php-`. -- `` и `` — версии FrankenPHP и PHP соответственно: от основных (например, `1`) до минорных (например, `1.2`) и патч-версий (например, `1.2.3`). +- `` и `` — номера версий FrankenPHP и PHP соответственно, от мажорных (например, `1`), минорных (например, `1.2`) до патч-версий (например, `1.2.3`). - `` может быть `trixie` (для Debian Trixie), `bookworm` (для Debian Bookworm) или `alpine` (для последней стабильной версии Alpine). -[Просмотреть доступные теги](https://hub.docker.com/r/dunglas/frankenphp/tags). +[Просмотреть теги](https://hub.docker.com/r/dunglas/frankenphp/tags). ## Как использовать образы @@ -28,9 +28,13 @@ docker build -t my-php-app . docker run -it --rm --name my-running-app my-php-app ``` +## Как настроить конфигурацию + +Для удобства в образ включен [Caddyfile по умолчанию](https://github.com/php/frankenphp/blob/main/caddy/frankenphp/Caddyfile), содержащий полезные переменные окружения. + ## Как установить дополнительные PHP-расширения -Скрипт [`docker-php-extension-installer`](https://github.com/mlocati/docker-php-extension-installer) включён в базовый образ. Установка дополнительных PHP-расширений осуществляется просто: +Скрипт [`docker-php-extension-installer`](https://github.com/mlocati/docker-php-extension-installer) предоставляется в базовом образе. Установка дополнительных PHP-расширений осуществляется просто: ```dockerfile FROM dunglas/frankenphp @@ -78,14 +82,14 @@ FROM dunglas/frankenphp AS runner COPY --from=builder /usr/local/bin/frankenphp /usr/local/bin/frankenphp ``` -Образ `builder`, предоставляемый FrankenPHP, содержит скомпилированную версию `libphp`. +Образ `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 режим по умолчанию +## Активация Worker-режима по умолчанию Установите переменную окружения `FRANKENPHP_CONFIG`, чтобы запускать FrankenPHP с Worker-скриптом: @@ -119,7 +123,7 @@ services: image: dunglas/frankenphp # раскомментируйте следующую строку, если хотите использовать собственный Dockerfile #build: . - # раскомментируйте следующую строку, если вы запускаете это в продакшн среде + # раскомментируйте следующую строку, если вы запускаете это в производственной среде # restart: always ports: - "80:80" # HTTP @@ -129,7 +133,7 @@ services: - ./:/app/public - caddy_data:/data - caddy_config:/config - # закомментируйте следующую строку в продакшн среде, она позволяет получать удобочитаемые логи в режиме разработки + # закомментируйте следующую строку в производственной среде, она позволяет получать удобочитаемые логи в режиме разработки tty: true # Томы, необходимые для сертификатов и конфигурации Caddy @@ -138,11 +142,11 @@ volumes: caddy_config: ``` -## Запуск под обычным пользователем +## Запуск от имени пользователя без прав root -FrankenPHP поддерживает запуск под обычным пользователем в Docker. +FrankenPHP может работать от имени пользователя без прав root в Docker. -Пример `Dockerfile` для этого: +Вот пример `Dockerfile` для этого: ```dockerfile FROM dunglas/frankenphp @@ -152,19 +156,19 @@ ARG USER=appuser RUN \ # Для дистрибутивов на основе Alpine используйте "adduser -D ${USER}" useradd ${USER}; \ - # Добавьте возможность привязываться к портам 80 и 443 + # Добавьте дополнительную возможность привязываться к портам 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 + # Дайте права на запись для /config/caddy и /data/caddy + chown -R ${USER}:${USER} /config/caddy /data/caddy USER ${USER} ``` -### Запуск без дополнительных прав +### Запуск без дополнительных возможностей -Даже при запуске без root-прав, FrankenPHP требуется возможность `CAP_NET_BIND_SERVICE` для привязки веб-сервера к зарезервированным портам (80 и 443). +Даже при запуске без прав root, FrankenPHP требуется возможность `CAP_NET_BIND_SERVICE` для привязки веб-сервера к привилегированным портам (80 и 443). -Если вы открываете доступ к FrankenPHP на непривилегированном порту (1024 и выше), можно запустить веб-сервер от имени обычного пользователя без необходимости предоставления дополнительных возможностей: +Если вы открываете доступ к FrankenPHP на непривилегированном порту (1024 и выше), можно запустить веб-сервер от имени пользователя без прав root, и без необходимости предоставления дополнительных возможностей: ```dockerfile FROM dunglas/frankenphp @@ -176,26 +180,26 @@ RUN \ useradd ${USER}; \ # Удалите стандартные возможности setcap -r /usr/local/bin/frankenphp; \ - # Дайте права на запись для /data/caddy и /config/caddy - chown -R ${USER}:${USER} /data/caddy && chown -R ${USER}:${USER} /config/caddy + # Дайте права на запись для /config/caddy и /data/caddy + chown -R ${USER}:${USER} /config/caddy /data/caddy USER ${USER} ``` -Затем установите переменную окружения `SERVER_NAME`, чтобы использовать непривилегированный порт. +Затем установите переменную окружения `SERVER_NAME`, чтобы использовать непривилегированный порт. Пример: `:8000`. ## Обновления -Docker-образы обновляются: +Docker-образы собираются: - при выпуске новой версии; - ежедневно в 4 утра UTC, если доступны новые версии официальных PHP-образов. ## Версии для разработки -Версии для разработки доступны в Docker-репозитории [`dunglas/frankenphp-dev`](https://hub.docker.com/repository/docker/dunglas/frankenphp-dev). -Сборка запускается автоматически при каждом коммите в основную ветку GitHub-репозитория +Версии для разработки доступны в Docker-репозитории [`dunglas/frankenphp-dev`](https://hub.docker.com/repository/docker/dunglas/frankenphp-dev). +Новая сборка запускается каждый раз, когда коммит отправляется в основную ветку GitHub-репозитория. -Теги с префиксом `latest*` указывают на актуальное состояние ветки `main`. -Также доступны теги в формате `sha-`. +Теги с префиксом `latest*` указывают на актуальное состояние ветки `main`. +Также доступны теги в формате `sha-`. \ No newline at end of file diff --git a/docs/ru/early-hints.md b/docs/ru/early-hints.md index e27c3b1945..d907939203 100644 --- a/docs/ru/early-hints.md +++ b/docs/ru/early-hints.md @@ -1,6 +1,6 @@ # Early Hints -FrankenPHP изначально поддерживает [Early Hints (103 HTTP статус код)](https://developer.chrome.com/blog/early-hints/). +FrankenPHP изначально поддерживает [код состояния 103 Early Hints](https://developer.chrome.com/blog/early-hints/). Использование Early Hints может улучшить время загрузки ваших веб-страниц на 30%. ```php diff --git a/docs/ru/embed.md b/docs/ru/embed.md index 2ac91307b5..3c9c61793b 100644 --- a/docs/ru/embed.md +++ b/docs/ru/embed.md @@ -6,7 +6,7 @@ FrankenPHP позволяет встраивать исходный код и р Подробнее об этой функции [в презентации Кевина на SymfonyCon 2023](https://dunglas.dev/2023/12/php-and-symfony-apps-as-standalone-binaries/). -Для встраивания Laravel-приложений ознакомьтесь с [документацией](laravel.md#laravel-приложения-как-автономные-бинарные-файлы). +Для встраивания Laravel-приложений ознакомьтесь с [этой специальной записью в документации](laravel.md#laravel-apps-as-standalone-binaries). ## Подготовка приложения @@ -19,19 +19,20 @@ FrankenPHP позволяет встраивать исходный код и р - Включить продакшн-режим приложения (если он есть). - Удалить ненужные файлы, такие как `.git` или тесты, чтобы уменьшить размер итогового бинарного файла. -Для приложения на Symfony это может выглядеть так: +Например, для приложения на Symfony вы можете использовать следующие команды: ```console -# Экспорт проекта, чтобы избавиться от .git/ и других ненужных файлов +# Экспорт проекта, чтобы избавиться от .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 -# Удалить тесты и другие ненужные файлы +# Удалить тесты и другие ненужные файлы, чтобы сэкономить место +# В качестве альтернативы, добавьте эти файлы с атрибутом export-ignore в ваш файл .gitattributes rm -Rf tests/ # Установить зависимости @@ -43,7 +44,8 @@ composer dump-env prod ### Настройка конфигурации -Чтобы настроить [конфигурацию](config.md), вы можете разместить файлы `Caddyfile` и `php.ini` в основной директории приложения (`$TMPDIR/my-prepared-app` в примере выше). +Чтобы настроить [конфигурацию](config.md), вы можете поместить файл `Caddyfile`, а также файл `php.ini` +в основную директорию приложения для встраивания (`$TMPDIR/my-prepared-app` в предыдущем примере). ## Создание бинарного файла для Linux @@ -53,23 +55,23 @@ composer dump-env prod ```dockerfile FROM --platform=linux/amd64 dunglas/frankenphp:static-builder-gnu - # Если вы планируете запускать бинарный файл на системах с musl-libc, используйте static-builder-musl + # Если вы планируете запускать бинарный файл на системах с musl-libc, используйте static-builder-musl вместо него - # Скопировать приложение + # Скопировать ваше приложение 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` файл настроен корректно или удалён. + > Некоторые `.dockerignore` файлы (например, стандартные [Symfony Docker `.dockerignore`](https://github.com/dunglas/symfony-docker/blob/main/.dockerignore)) + > будут игнорировать директорию `vendor/` и файлы `.env`. Убедитесь, что вы скорректировали или удалили файл `.dockerignore` перед сборкой. -2. Соберите образ: +2. Соберите: ```console docker build -t static-app -f static-build.Dockerfile . @@ -81,7 +83,7 @@ composer dump-env prod 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`. +Итоговым бинарным файлом является файл с именем `my-app` в текущей директории. ## Создание бинарного файла для других ОС @@ -93,11 +95,11 @@ cd frankenphp EMBED=/path/to/your/app ./build-static.sh ``` -Итоговый бинарный файл будет находиться в директории `dist/` под именем `frankenphp--`. +Итоговым бинарным файлом является файл с именем `frankenphp--` в директории `dist/`. ## Использование бинарного файла -Готово! Файл `my-app` (или `dist/frankenphp--` для других ОС) содержит ваше автономное приложение. +Готово! Файл `my-app` (или `dist/frankenphp--` на других ОС) содержит ваше автономное приложение! Для запуска веб-приложения выполните: @@ -105,19 +107,19 @@ EMBED=/path/to/your/app ./build-static.sh ./my-app php-server ``` -Если ваше приложение содержит [worker-скрипт](worker.md), запустите его следующим образом: +Если ваше приложение содержит [worker-скрипт](worker.md), запустите воркер следующим образом: ```console ./my-app php-server --worker public/index.php ``` -Чтобы включить HTTPS (Let's Encrypt автоматически создаст сертификат), HTTP/2 и HTTP/3, укажите доменное имя: +Чтобы включить HTTPS (сертификат Let's Encrypt создается автоматически), HTTP/2 и HTTP/3, укажите доменное имя: ```console ./my-app php-server --domain localhost ``` -Вы также можете запускать PHP-скрипты CLI, встроенные в бинарный файл: +Вы также можете запускать PHP CLI-скрипты, встроенные в бинарный файл: ```console ./my-app php-cli bin/console @@ -125,10 +127,10 @@ EMBED=/path/to/your/app ./build-static.sh ## PHP-расширения -По умолчанию скрипт собирает расширения, указанные в `composer.json` вашего проекта. -Если файла `composer.json` нет, собираются стандартные расширения, как указано в [документации по статической сборке](static.md). +По умолчанию скрипт собирает расширения, требуемые файлом `composer.json` вашего проекта, если таковой имеется. +Если файла `composer.json` не существует, собираются стандартные расширения, как указано в [записи о статических сборках](static.md). -Чтобы настроить список расширений, используйте переменную окружения `PHP_EXTENSIONS`. +Чтобы настроить расширения, используйте переменную окружения `PHP_EXTENSIONS`. ## Настройка сборки @@ -138,4 +140,5 @@ EMBED=/path/to/your/app ./build-static.sh На Linux созданный бинарный файл сжимается с помощью [UPX](https://upx.github.io). -На Mac для уменьшения размера файла перед отправкой его можно сжать. Рекомендуется использовать `xz`. +На Mac для уменьшения размера файла перед отправкой его можно сжать. +Мы рекомендуем `xz`. diff --git a/docs/ru/extensions.md b/docs/ru/extensions.md new file mode 100644 index 0000000000..6090014aeb --- /dev/null +++ b/docs/ru/extensions.md @@ -0,0 +1,893 @@ +# Написание расширений PHP на Go + +С FrankenPHP вы можете **писать расширения PHP на Go**, что позволяет создавать **высокопроизводительные нативные функции**, которые можно вызывать непосредственно из PHP. Ваши приложения могут использовать любые существующие или новые Go библиотеки, а также знаменитую модель параллелизма **горутин прямо из вашего PHP-кода**. + +Написание расширений PHP обычно осуществляется на C, но их также можно писать на других языках с небольшими дополнительными усилиями. Расширения PHP позволяют использовать мощь низкоуровневых языков для расширения функциональности PHP, например, путем добавления нативных функций или оптимизации конкретных операций. + +Благодаря модулям Caddy, вы можете писать расширения PHP на Go и очень быстро интегрировать их в FrankenPHP. + +## Два подхода + +FrankenPHP предоставляет два способа создания расширений PHP на Go: + +1. **Использование генератора расширений** – Рекомендуемый подход, который генерирует весь необходимый шаблонный код для большинства случаев использования, позволяя вам сосредоточиться на написании кода на Go. +2. **Ручная реализация** – Полный контроль над структурой расширения для продвинутых случаев использования. + +Мы начнем с подхода с генератором, так как это самый простой способ начать работу, а затем покажем ручную реализацию для тех, кому нужен полный контроль. + +## Использование генератора расширений + +FrankenPHP поставляется с инструментом, который позволяет **создавать расширения PHP**, используя только Go. **Не нужно писать код на C** или напрямую использовать CGO: FrankenPHP также включает **публичный API типов**, который поможет вам писать расширения на Go, не беспокоясь о **согласовании типов между PHP/C и Go**. + +> [!TIP] +> Если вы хотите понять, как расширения могут быть написаны на Go с нуля, вы можете прочитать раздел "Ручная реализация" ниже, демонстрирующий, как написать расширение PHP на Go без использования генератора. + +Имейте в виду, что этот инструмент **не является полноценным генератором расширений**. Он предназначен для помощи в написании простых расширений на Go, но не предоставляет самых продвинутых функций расширений PHP. Если вам нужно написать более **сложное и оптимизированное** расширение, вам может потребоваться написать некоторый код на C или напрямую использовать CGO. + +### Предварительные требования + +Как описано также в разделе "Ручная реализация" ниже, вам необходимо [получить исходники PHP](https://www.php.net/downloads.php) и создать новый модуль Go. + +#### Создайте новый модуль и получите исходники PHP + +Первым шагом к написанию расширения PHP на Go является создание нового модуля Go. Вы можете использовать следующую команду для этого: + +```console +go mod init example.com/example +``` + +Вторым шагом является [получение исходников PHP](https://www.php.net/downloads.php) для следующих шагов. Как только вы их получите, распакуйте их в каталог по вашему выбору, но не внутрь вашего модуля Go: + +```console +tar xf php-* +``` + +### Написание расширения + +Теперь все настроено для написания вашей нативной функции на Go. Создайте новый файл с именем `stringext.go`. Наша первая функция будет принимать строку в качестве аргумента, количество раз для ее повторения, булево значение, указывающее, нужно ли инвертировать строку, и возвращать результирующую строку. Это должно выглядеть так: + +```go +package example + +// #include +import "C" +import ( + "strings" + "unsafe" + + "github.com/dunglas/frankenphp" +) + +//export_php:function repeat_this(string $str, int $count, bool $reverse): string +func repeat_this(s *C.zend_string, count int64, reverse bool) unsafe.Pointer { + str := frankenphp.GoString(unsafe.Pointer(s)) + + result := strings.Repeat(str, int(count)) + if reverse { + runes := []rune(result) + for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 { + runes[i], runes[j] = runes[j], runes[i] + } + result = string(runes) + } + + return frankenphp.PHPString(result, false) +} +``` + +Здесь следует отметить две важные вещи: + +- Директива-комментарий `//export_php:function` определяет сигнатуру функции в PHP. Таким образом генератор знает, как создать PHP-функцию с правильными параметрами и типом возвращаемого значения; +- Функция должна возвращать `unsafe.Pointer`. FrankenPHP предоставляет API, чтобы помочь вам с согласованием типов между C и Go. + +В то время как первый пункт говорит сам за себя, второй может быть сложнее для понимания. Давайте углубимся в согласование типов в следующем разделе. + +### Согласование типов + +В то время как некоторые типы переменных имеют одно и то же представление в памяти между C/PHP и Go, некоторые типы требуют большей логики для прямого использования. Это, возможно, самая сложная часть при написании расширений, поскольку она требует понимания внутренних механизмов движка Zend и того, как переменные хранятся внутри PHP. +Эта таблица суммирует то, что вам нужно знать: + +| Тип PHP | Тип Go | Прямое преобразование | Помощник C в Go | Помощник Go в C | Поддержка методов класса | +| :----------------- | :---------------------------- | :------------------ | :-------------------------------- | :--------------------------------- | :--------------------- | +| `int` | `int64` | ✅ | - | - | ✅ | +| `?int` | `*int64` | ✅ | - | - | ✅ | +| `float` | `float64` | ✅ | - | - | ✅ | +| `?float` | `*float64` | ✅ | - | - | ✅ | +| `bool` | `bool` | ✅ | - | - | ✅ | +| `?bool` | `*bool` | ✅ | - | - | ✅ | +| `string`/`?string` | `*C.zend_string` | ❌ | `frankenphp.GoString()` | `frankenphp.PHPString()` | ✅ | +| `array` | `frankenphp.AssociativeArray` | ❌ | `frankenphp.GoAssociativeArray()` | `frankenphp.PHPAssociativeArray()` | ✅ | +| `array` | `map[string]any` | ❌ | `frankenphp.GoMap()` | `frankenphp.PHPMap()` | ✅ | +| `array` | `[]any` | ❌ | `frankenphp.GoPackedArray()` | `frankenphp.PHPPackedArray()` | ✅ | +| `mixed` | `any` | ❌ | `GoValue()` | `PHPValue()` | ❌ | +| `callable` | `*C.zval` | ❌ | - | frankenphp.CallPHPCallable() | ❌ | +| `object` | `struct` | ❌ | _Еще не реализовано_ | _Еще не реализовано_ | ❌ | + +> [!NOTE] +> +> Эта таблица еще не исчерпывающая и будет дополнена по мере того, как API типов FrankenPHP станет более полным. +> +> Для методов классов, в частности, в настоящее время поддерживаются примитивные типы и массивы. Объекты пока не могут использоваться в качестве параметров методов или возвращаемых типов. + +Если вы обратитесь к фрагменту кода из предыдущего раздела, вы увидите, что для преобразования первого параметра и возвращаемого значения используются вспомогательные функции. Второй и третий параметры нашей функции `repeat_this()` не требуют преобразования, так как представление в памяти базовых типов одинаково как для C, так и для Go. + +#### Работа с массивами + +FrankenPHP обеспечивает нативную поддержку массивов PHP через `frankenphp.AssociativeArray` или прямое преобразование в карту (map) или срез (slice). + +`AssociativeArray` представляет собой [хеш-таблицу](https://en.wikipedia.org/wiki/Hash_table), состоящую из поля `Map: map[string]any` и необязательного поля `Order: []string` (в отличие от "ассоциативных массивов" PHP, Go-карты не упорядочены). + +Если порядок или ассоциация не требуются, также можно напрямую преобразовать в срез `[]any` или неупорядоченную карту `map[string]any`. + +**Создание и манипулирование массивами в Go:** + +```go +package example + +// #include +import "C" +import ( + "unsafe" + + "github.com/dunglas/frankenphp" +) + +// export_php:function process_data_ordered(array $input): array +func process_data_ordered_map(arr *C.zend_array) unsafe.Pointer { + // Преобразование ассоциативного массива PHP в Go с сохранением порядка + associativeArray, err := frankenphp.GoAssociativeArray[any](unsafe.Pointer(arr)) + if err != nil { + // обработка ошибки + } + + // итерация по элементам в порядке + for _, key := range associativeArray.Order { + value, _ = associativeArray.Map[key] + // что-то делаем с ключом и значением + } + + // возвращаем упорядоченный массив + // если 'Order' не пуст, будут учитываться только пары ключ-значение, указанные в 'Order' + return frankenphp.PHPAssociativeArray[string](frankenphp.AssociativeArray[string]{ + Map: map[string]string{ + "key1": "value1", + "key2": "value2", + }, + Order: []string{"key1", "key2"}, + }) +} + +// export_php:function process_data_unordered(array $input): array +func process_data_unordered_map(arr *C.zend_array) unsafe.Pointer { + // Преобразование ассоциативного массива PHP в Go-карту без сохранения порядка + // игнорирование порядка будет более производительным + goMap, err := frankenphp.GoMap[any](unsafe.Pointer(arr)) + if err != nil { + // обработка ошибки + } + + // итерация по элементам без определенного порядка + for key, value := range goMap { + // что-то делаем с ключом и значением + } + + // возвращаем неупорядоченный массив + return frankenphp.PHPMap(map[string]string { + "key1": "value1", + "key2": "value2", + }) +} + +// export_php:function process_data_packed(array $input): array +func process_data_packed(arr *C.zend_array) unsafe.Pointer { + // Преобразование упакованного массива PHP в Go + goSlice, err := frankenphp.GoPackedArray(unsafe.Pointer(arr)) + if err != nil { + // обработка ошибки + } + + // итерация по срезу в порядке + for index, value := range goSlice { + // что-то делаем с индексом и значением + } + + // возвращаем упакованный массив + return frankenphp.PHPPackedArray([]string{"value1", "value2", "value3"}) +} +``` + +**Ключевые особенности преобразования массивов:** + +- **Упорядоченные пары ключ-значение** — Возможность сохранять порядок ассоциативного массива +- **Оптимизировано для различных случаев** — Возможность отказаться от порядка для лучшей производительности или преобразовать напрямую в срез +- **Автоматическое обнаружение списка** — При преобразовании в PHP автоматически определяет, должен ли массив быть упакованным списком или хеш-картой +- **Вложенные массивы** — Массивы могут быть вложенными и автоматически преобразуют все поддерживаемые типы (`int64`, `float64`, `string`, `bool`, `nil`, `AssociativeArray`, `map[string]any`, `[]any`) +- **Объекты не поддерживаются** — В настоящее время в качестве значений могут использоваться только скалярные типы и массивы. Предоставление объекта приведет к значению `null` в массиве PHP. + +##### Доступные методы: Packed и Associative + +- `frankenphp.PHPAssociativeArray(arr frankenphp.AssociativeArray) unsafe.Pointer` — Преобразует в упорядоченный массив PHP с парами ключ-значение +- `frankenphp.PHPMap(arr map[string]any) unsafe.Pointer` — Преобразует карту в неупорядоченный массив PHP с парами ключ-значение +- `frankenphp.PHPPackedArray(slice []any) unsafe.Pointer` — Преобразует срез в упакованный массив PHP только с индексированными значениями +- `frankenphp.GoAssociativeArray(arr unsafe.Pointer, ordered bool) frankenphp.AssociativeArray` — Преобразует массив PHP в упорядоченный `AssociativeArray` Go (карту с порядком) +- `frankenphp.GoMap(arr unsafe.Pointer) map[string]any` — Преобразует массив PHP в неупорядоченную карту Go +- `frankenphp.GoPackedArray(arr unsafe.Pointer) []any` — Преобразует массив PHP в срез Go +- `frankenphp.IsPacked(zval *C.zend_array) bool` — Проверяет, является ли массив PHP упакованным (только индексированным) или ассоциативным (пары ключ-значение) + +### Работа с вызываемыми объектами (Callables) + +FrankenPHP предоставляет способ работы с вызываемыми объектами PHP с помощью вспомогательной функции `frankenphp.CallPHPCallable`. Это позволяет вызывать функции или методы PHP из кода Go. + +Чтобы продемонстрировать это, давайте создадим нашу собственную функцию `array_map()`, которая принимает вызываемый объект (callable) и массив, применяет вызываемый объект к каждому элементу массива и возвращает новый массив с результатами: + +```go +// export_php:function my_array_map(array $data, callable $callback): array +func my_array_map(arr *C.zend_array, callback *C.zval) unsafe.Pointer { + goSlice, err := frankenphp.GoPackedArray[any](unsafe.Pointer(arr)) + if err != nil { + panic(err) + } + + result := make([]any, len(goSlice)) + + for index, value := range goSlice { + result[index] = frankenphp.CallPHPCallable(unsafe.Pointer(callback), []interface{}{value}) + } + + return frankenphp.PHPPackedArray(result) +} +``` + +Обратите внимание, как мы используем `frankenphp.CallPHPCallable()` для вызова PHP-вызываемого объекта, переданного в качестве параметра. Эта функция принимает указатель на вызываемый объект и массив аргументов, а затем возвращает результат выполнения вызываемого объекта. Вы можете использовать привычный синтаксис вызываемых объектов: + +```php +name` не будет работать) +- **Только интерфейс методов** — Все взаимодействия должны происходить через методы, которые вы определяете +- **Лучшая инкапсуляция** — Внутренняя структура данных полностью контролируется кодом Go +- **Типобезопасность** — Нет риска, что PHP-код повредит внутреннее состояние неправильными типами +- **Более чистый API** — Принуждает к разработке правильного публичного интерфейса + +Этот подход обеспечивает лучшую инкапсуляцию и предотвращает случайное повреждение PHP-кодом внутреннего состояния ваших Go-объектов. Все взаимодействия с объектом должны проходить через явно определенные вами методы. + +#### Добавление методов к классам + +Поскольку свойства недоступны напрямую, вы **должны определить методы** для взаимодействия с вашими непрозрачными классами. Используйте директиву `//export_php:method` для определения поведения: + +```go +package example + +// #include +import "C" +import ( + "unsafe" + + "github.com/dunglas/frankenphp" +) + +//export_php:class User +type UserStruct struct { + Name string + Age int +} + +//export_php:method User::getName(): string +func (us *UserStruct) GetUserName() unsafe.Pointer { + return frankenphp.PHPString(us.Name, false) +} + +//export_php:method User::setAge(int $age): void +func (us *UserStruct) SetUserAge(age int64) { + us.Age = int(age) +} + +//export_php:method User::getAge(): int +func (us *UserStruct) GetUserAge() int64 { + return int64(us.Age) +} + +//export_php:method User::setNamePrefix(string $prefix = "User"): void +func (us *UserStruct) SetNamePrefix(prefix *C.zend_string) { + us.Name = frankenphp.GoString(unsafe.Pointer(prefix)) + ": " + us.Name +} +``` + +#### Обнуляемые параметры + +Генератор поддерживает обнуляемые параметры с использованием префикса `?` в сигнатурах PHP. Когда параметр является обнуляемым, он становится указателем в вашей Go-функции, что позволяет вам проверить, было ли значение `null` в PHP: + +```go +package example + +// #include +import "C" +import ( + "unsafe" + + "github.com/dunglas/frankenphp" +) + +//export_php:method User::updateInfo(?string $name, ?int $age, ?bool $active): void +func (us *UserStruct) UpdateInfo(name *C.zend_string, age *int64, active *bool) { + // Проверить, было ли предоставлено имя (не null) + if name != nil { + us.Name = frankenphp.GoString(unsafe.Pointer(name)) + } + + // Проверить, был ли предоставлен возраст (не null) + if age != nil { + us.Age = int(*age) + } + + // Проверить, был ли предоставлен статус активности (не null) + if active != nil { + us.Active = *active + } +} +``` + +**Ключевые моменты об обнуляемых параметрах:** + +- **Обнуляемые примитивные типы** (`?int`, `?float`, `?bool`) становятся указателями (`*int64`, `*float64`, `*bool`) в Go +- **Обнуляемые строки** (`?string`) остаются `*C.zend_string`, но могут быть `nil` +- **Проверяйте на `nil`** перед разыменованием значений указателей +- **PHP `null` становится Go `nil`** — когда PHP передает `null`, ваша Go-функция получает `nil`-указатель + +> [!WARNING] +> +> В настоящее время методы классов имеют следующие ограничения. **Объекты не поддерживаются** в качестве типов параметров или возвращаемых типов. **Массивы полностью поддерживаются** как для параметров, так и для возвращаемых типов. Поддерживаемые типы: `string`, `int`, `float`, `bool`, `array` и `void` (для возвращаемого типа). **Обнуляемые типы параметров полностью поддерживаются** для всех скалярных типов (`?string`, `?int`, `?float`, `?bool`). + +После генерации расширения вы сможете использовать класс и его методы в PHP. Обратите внимание, что вы **не можете получать доступ к свойствам напрямую**: + +```php +setAge(25); +echo $user->getName(); // Вывод: (пусто, значение по умолчанию) +echo $user->getAge(); // Вывод: 25 +$user->setNamePrefix("Employee"); + +// ✅ Это тоже работает - обнуляемые параметры +$user->updateInfo("John", 30, true); // Все параметры предоставлены +$user->updateInfo("Jane", null, false); // Возраст null +$user->updateInfo(null, 25, null); // Имя и активность null + +// ❌ Это НЕ будет работать - прямой доступ к свойствам +// echo $user->name; // Ошибка: Невозможно получить доступ к приватному свойству +// $user->age = 30; // Ошибка: Невозможно получить доступ к приватному свойству +``` + +Этот дизайн гарантирует, что ваш Go-код полностью контролирует то, как состояние объекта доступно и изменяется, обеспечивая лучшую инкапсуляцию и типобезопасность. + +### Объявление констант + +Генератор поддерживает экспорт Go-констант в PHP с использованием двух директив: `//export_php:const` для глобальных констант и `//export_php:classconst` для констант класса. Это позволяет вам обмениваться значениями конфигурации, кодами состояния и другими константами между Go- и PHP-кодом. + +#### Глобальные константы + +Используйте директиву `//export_php:const` для создания глобальных PHP-констант: + +```go +package example + +//export_php:const +const MAX_CONNECTIONS = 100 + +//export_php:const +const API_VERSION = "1.2.3" + +//export_php:const +const STATUS_OK = iota + +//export_php:const +const STATUS_ERROR = iota +``` + +#### Константы класса + +Используйте директиву `//export_php:classconst ClassName` для создания констант, принадлежащих определенному PHP-классу: + +```go +package example + +//export_php:classconst User +const STATUS_ACTIVE = 1 + +//export_php:classconst User +const STATUS_INACTIVE = 0 + +//export_php:classconst User +const ROLE_ADMIN = "admin" + +//export_php:classconst Order +const STATE_PENDING = iota + +//export_php:classconst Order +const STATE_PROCESSING = iota + +//export_php:classconst Order +const STATE_COMPLETED = iota +``` + +Константы класса доступны с использованием области видимости имени класса в PHP: + +```php + +import "C" +import ( + "strings" + "unsafe" + + "github.com/dunglas/frankenphp" +) + +//export_php:const +const STR_REVERSE = iota + +//export_php:const +const STR_NORMAL = iota + +//export_php:classconst StringProcessor +const MODE_LOWERCASE = 1 + +//export_php:classconst StringProcessor +const MODE_UPPERCASE = 2 + +//export_php:function repeat_this(string $str, int $count, int $mode): string +func repeat_this(s *C.zend_string, count int64, mode int) unsafe.Pointer { + str := frankenphp.GoString(unsafe.Pointer(s)) + + result := strings.Repeat(str, int(count)) + if mode == STR_REVERSE { + // инвертировать строку + } + + if mode == STR_NORMAL { + // ничего не делать, просто для демонстрации константы + } + + return frankenphp.PHPString(result, false) +} + +//export_php:class StringProcessor +type StringProcessorStruct struct { + // внутренние поля +} + +//export_php:method StringProcessor::process(string $input, int $mode): string +func (sp *StringProcessorStruct) Process(input *C.zend_string, mode int64) unsafe.Pointer { + str := frankenphp.GoString(unsafe.Pointer(input)) + + switch mode { + case MODE_LOWERCASE: + str = strings.ToLower(str) + case MODE_UPPERCASE: + str = strings.ToUpper(str) + } + + return frankenphp.PHPString(str, false) +} +``` + +### Использование пространств имен + +Генератор поддерживает организацию функций, классов и констант вашего PHP-расширения в пространстве имен с использованием директивы `//export_php:namespace`. Это помогает избежать конфликтов имен и обеспечивает лучшую организацию API вашего расширения. + +#### Объявление пространства имен + +Используйте директиву `//export_php:namespace` в начале вашего Go-файла, чтобы разместить все экспортируемые символы в определенном пространстве имен: + +```go +//export_php:namespace My\Extension +package example + +import ( + "unsafe" + + "github.com/dunglas/frankenphp" +) + +//export_php:function hello(): string +func hello() string { + return "Hello from My\\Extension namespace!" +} + +//export_php:class User +type UserStruct struct { + // внутренние поля +} + +//export_php:method User::getName(): string +func (u *UserStruct) GetName() unsafe.Pointer { + return frankenphp.PHPString("John Doe", false) +} + +//export_php:const +const STATUS_ACTIVE = 1 +``` + +#### Использование расширения с пространством имен в PHP + +Когда объявляется пространство имен, все функции, классы и константы помещаются в это пространство имен в PHP: + +```php +getName(); // "John Doe" + +echo My\Extension\STATUS_ACTIVE; // 1 +``` + +#### Важные замечания + +- Разрешена только **одна** директива пространства имен на файл. Если найдено несколько директив пространства имен, генератор вернет ошибку. +- Пространство имен применяется ко **всем** экспортируемым символам в файле: функциям, классам, методам и константам. +- Имена пространств имен следуют соглашениям PHP, используя обратные слеши (`\`) в качестве разделителей. +- Если пространство имен не объявлено, символы экспортируются в глобальное пространство имен как обычно. + +### Генерация расширения + +Здесь происходит волшебство, и ваше расширение теперь может быть сгенерировано. Вы можете запустить генератор следующей командой: + +```console +GEN_STUB_SCRIPT=php-src/build/gen_stub.php frankenphp extension-init my_extension.go +``` + +> [!NOTE] +> Не забудьте установить переменную окружения `GEN_STUB_SCRIPT` на путь к файлу `gen_stub.php` в исходниках PHP, которые вы скачали ранее. Это тот же скрипт `gen_stub.php`, упомянутый в разделе "Ручная реализация". + +Если все прошло хорошо, должна быть создана новая директория с именем `build`. Эта директория содержит сгенерированные файлы для вашего расширения, включая файл `my_extension.go` с сгенерированными заглушками функций PHP. + +### Интеграция сгенерированного расширения в FrankenPHP + +Наше расширение готово к компиляции и интеграции в FrankenPHP. Для этого обратитесь к [документации по компиляции](compile.md) FrankenPHP, чтобы узнать, как скомпилировать FrankenPHP. Добавьте модуль, используя флаг `--with`, указывающий путь к вашему модулю: + +```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 github.com/my-account/my-module/build +``` + +Обратите внимание, что вы указываете на подкаталог `/build`, который был создан на этапе генерации. Однако это не является обязательным: вы также можете скопировать сгенерированные файлы в каталог вашего модуля и указать на него напрямую. + +### Тестирование сгенерированного расширения + +Вы можете создать PHP-файл для тестирования созданных вами функций и классов. Например, создайте файл `index.php` со следующим содержимым: + +```php +process('Hello World', StringProcessor::MODE_LOWERCASE); // "hello world" +echo $processor->process('Hello World', StringProcessor::MODE_UPPERCASE); // "HELLO WORLD" +``` + +После того как вы интегрировали ваше расширение в FrankenPHP, как показано в предыдущем разделе, вы можете запустить этот тестовый файл, используя `./frankenphp php-server`, и вы должны увидеть работу вашего расширения. + +## Ручная реализация + +Если вы хотите понять, как работают расширения, или вам нужен полный контроль над вашим расширением, вы можете написать их вручную. Этот подход дает вам полный контроль, но требует больше шаблонного кода. + +### Базовая функция + +Мы рассмотрим, как написать простое расширение PHP на Go, которое определяет новую нативную функцию. Эта функция будет вызываться из PHP и будет запускать горутину, которая записывает сообщение в логи Caddy. Эта функция не принимает никаких параметров и ничего не возвращает. + +#### Определение Go-функции + +В вашем модуле вам нужно определить новую нативную функцию, которая будет вызываться из PHP. Для этого создайте файл с нужным вам именем, например, `extension.go`, и добавьте следующий код: + +```go +package example + +// #include "extension.h" +import "C" +import ( + "log/slog" + "unsafe" + + "github.com/dunglas/frankenphp" +) + +func init() { + frankenphp.RegisterExtension(unsafe.Pointer(&C.ext_module_entry)) +} + +//export go_print_something +func go_print_something() { + go func() { + slog.Info("Hello from a goroutine!") // Привет из горутины! + }() +} +``` + +Функция `frankenphp.RegisterExtension()` упрощает процесс регистрации расширения, обрабатывая внутреннюю логику регистрации PHP. Функция `go_print_something` использует директиву `//export`, чтобы указать, что она будет доступна в C-коде, который мы напишем, благодаря CGO. + +В этом примере наша новая функция будет запускать горутину, которая записывает сообщение в логи Caddy. + +#### Определение PHP-функции + +Чтобы позволить PHP вызывать нашу функцию, нам нужно определить соответствующую PHP-функцию. Для этого мы создадим файл-заглушку, например, `extension.stub.php`, который будет содержать следующий код: + +```php + + +extern zend_module_entry ext_module_entry; + +#endif +``` + +Затем создайте файл с именем `extension.c`, который будет выполнять следующие шаги: + +- Включать заголовки PHP; +- Объявлять нашу новую нативную PHP-функцию `go_print()`; +- Объявлять метаданные расширения. + +Начнем с включения необходимых заголовков: + +```c +#include +#include "extension.h" +#include "extension_arginfo.h" + +// Содержит символы, экспортируемые Go +#include "_cgo_export.h" +``` + +Затем мы определяем нашу PHP-функцию как нативную языковую функцию: + +```c +PHP_FUNCTION(go_print) +{ + ZEND_PARSE_PARAMETERS_NONE(); + + go_print_something(); +} + +zend_module_entry ext_module_entry = { + STANDARD_MODULE_HEADER, + "ext_go", + ext_functions, /* Функции */ + NULL, /* MINIT */ + NULL, /* MSHUTDOWN */ + NULL, /* RINIT */ + NULL, /* RSHUTDOWN */ + NULL, /* MINFO */ + "0.1.1", + STANDARD_MODULE_PROPERTIES +}; +``` + +В этом случае наша функция не принимает параметров и ничего не возвращает. Она просто вызывает Go-функцию, которую мы определили ранее, экспортированную с помощью директивы `//export`. + +Наконец, мы определяем метаданные расширения в структуре `zend_module_entry`, такие как его имя, версия и свойства. Эта информация необходима PHP для распознавания и загрузки нашего расширения. Обратите внимание, что `ext_functions` — это массив указателей на определенные нами PHP-функции, и он был автоматически сгенерирован скриптом `gen_stub.php` в файле `extension_arginfo.h`. + +Регистрация расширения автоматически обрабатывается функцией `RegisterExtension()` FrankenPHP, которую мы вызываем в нашем Go-коде. + +### Продвинутое использование + +Теперь, когда мы знаем, как создать базовое расширение PHP на Go, давайте усложним наш пример. Теперь мы создадим PHP-функцию, которая принимает строку в качестве параметра и возвращает ее версию в верхнем регистре. + +#### Определение заглушки PHP-функции + +Чтобы определить новую PHP-функцию, мы изменим наш файл `extension.stub.php`, чтобы включить новую сигнатуру функции: + +```php + [!TIP] +> Не пренебрегайте документацией ваших функций! Вы, вероятно, будете делиться заглушками ваших расширений с другими разработчиками, чтобы документировать, как использовать ваше расширение и какие функции доступны. + +После повторной генерации файла-заглушки с помощью скрипта `gen_stub.php` файл `extension_arginfo.h` должен выглядеть следующим образом: + +```c +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_go_upper, 0, 1, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, string, IS_STRING, 0) +ZEND_END_ARG_INFO() + +ZEND_FUNCTION(go_upper); + +static const zend_function_entry ext_functions[] = { + ZEND_FE(go_upper, arginfo_go_upper) + ZEND_FE_END +}; +``` + +Мы видим, что функция `go_upper` определена с параметром типа `string` и возвращаемым типом `string`. + +#### Согласование типов между Go и PHP/C + +Ваша Go-функция не может напрямую принимать PHP-строку в качестве параметра. Вам нужно преобразовать ее в Go-строку. К счастью, FrankenPHP предоставляет вспомогательные функции для обработки преобразования между PHP-строками и Go-строками, аналогично тому, что мы видели в подходе с генератором. + +Заголовочный файл остается простым: + +```c +#ifndef _EXTENSION_H +#define _EXTENSION_H + +#include + +extern zend_module_entry ext_module_entry; + +#endif +``` + +Теперь мы можем написать мост между Go и C в нашем файле `extension.c`. Мы передадим PHP-строку непосредственно нашей Go-функции: + +```c +PHP_FUNCTION(go_upper) +{ + zend_string *str; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_STR(str) + ZEND_PARSE_PARAMETERS_END(); + + zend_string *result = go_upper(str); + RETVAL_STR(result); +} +``` + +Вы можете узнать больше о `ZEND_PARSE_PARAMETERS_START` и разборе параметров на специальной странице [книги PHP Internals Book](https://www.phpinternalsbook.com/php7/extensions_design/php_functions.html#parsing-parameters-zend-parse-parameters). Здесь мы сообщаем PHP, что наша функция принимает один обязательный параметр типа `string` как `zend_string`. Затем мы передаем эту строку напрямую нашей Go-функции и возвращаем результат, используя `RETVAL_STR`. + +Осталось сделать только одно: реализовать функцию `go_upper` в Go. + +#### Реализация Go-функции + +Наша Go-функция будет принимать `*C.zend_string` в качестве параметра, преобразовывать ее в Go-строку с помощью вспомогательной функции FrankenPHP, обрабатывать ее и возвращать результат в виде нового `*C.zend_string`. Вспомогательные функции обрабатывают все сложности управления памятью и преобразования за нас. + +```go +package example + +// #include +import "C" +import ( + "unsafe" + "strings" + + "github.com/dunglas/frankenphp" +) + +//export go_upper +func go_upper(s *C.zend_string) *C.zend_string { + str := frankenphp.GoString(unsafe.Pointer(s)) + + upper := strings.ToUpper(str) + + return (*C.zend_string)(frankenphp.PHPString(upper, false)) +} +``` + +Этот подход намного чище и безопаснее, чем ручное управление памятью. +Вспомогательные функции FrankenPHP автоматически обрабатывают преобразование между форматом `zend_string` PHP и строками Go. +Параметр `false` в `PHPString()` указывает, что мы хотим создать новую непостоянную строку (освобождаемую в конце запроса). + +> [!TIP] +> +> В этом примере мы не выполняем никакой обработки ошибок, но вы всегда должны проверять, что указатели не `nil` и что данные действительны, прежде чем использовать их в ваших Go-функциях. + +### Интеграция расширения в FrankenPHP + +Наше расширение готово к компиляции и интеграции в FrankenPHP. Для этого обратитесь к [документации по компиляции](compile.md) FrankenPHP, чтобы узнать, как скомпилировать FrankenPHP. Добавьте модуль, используя флаг `--with`, указывающий путь к вашему модулю: + +```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 github.com/my-account/my-module +``` + +Вот и все! Ваше расширение теперь интегрировано в FrankenPHP и может быть использовано в вашем PHP-коде. + +### Тестирование вашего расширения + +После интеграции вашего расширения в FrankenPHP вы можете создать файл `index.php` с примерами реализованных вами функций: + +```php + [!WARNING] +> Эта функция предназначена **только для сред разработки**. +> Не включайте `hot_reload` в продакшене, так как отслеживание файловой системы влечет за собой накладные расходы на производительность и открывает внутренние конечные точки. + +```caddyfile +localhost + +mercure { + anonymous +} + +root public/ +php_server { + hot_reload +} +``` + +По умолчанию FrankenPHP будет отслеживать все файлы в текущей рабочей директории, соответствующие этому шаблону glob: `./**/*.{css,env,gif,htm,html,jpg,jpeg,js,mjs,php,png,svg,twig,webp,xml,yaml,yml}` + +Можно явно задать файлы для отслеживания, используя синтаксис glob: + +```caddyfile +localhost + +mercure { + anonymous +} + +root public/ +php_server { + hot_reload src/**/*{.php,.js} config/**/*.yaml +} +``` + +Используйте полную форму для указания темы Mercure, а также каталогов или файлов для отслеживания, предоставляя пути опции `hot_reload`: + +```caddyfile +localhost + +mercure { + anonymous +} + +root public/ +php_server { + hot_reload { + topic hot-reload-topic + watch src/**/*.php + watch assets/**/*.{ts,json} + watch templates/ + watch public/css/ + } +} +``` + +## Клиентская интеграция + +В то время как сервер обнаруживает изменения, браузеру необходимо подписаться на эти события для обновления страницы. +FrankenPHP предоставляет URL-адрес Mercure Hub для подписки на изменения файлов через переменную окружения `$_SERVER['FRANKENPHP_HOT_RELOAD']`. + +Удобная JavaScript-библиотека [frankenphp-hot-reload](https://www.npmjs.com/package/frankenphp-hot-reload) также доступна для обработки логики на стороне клиента. +Чтобы использовать ее, добавьте следующее в ваш основной макет: + +```php + +FrankenPHP Hot Reload + + + + + +``` + +Библиотека автоматически подпишется на Mercure hub, получит текущий URL в фоновом режиме при обнаружении изменения файла и преобразует DOM. +Она доступна как [npm](https://www.npmjs.com/package/frankenphp-hot-reload) пакет и на [GitHub](https://github.com/dunglas/frankenphp-hot-reload). + +В качестве альтернативы вы можете реализовать свою собственную клиентскую логику, подписавшись непосредственно на Mercure hub, используя нативный JavaScript-класс `EventSource`. + +### Режим воркера + +Если вы запускаете свое приложение в [режиме воркера (Worker Mode)](https://frankenphp.dev/docs/worker/), скрипт вашего приложения остается в памяти. +Это означает, что изменения в вашем PHP-коде не будут отражены немедленно, даже если браузер перезагрузится. + +Для наилучшего опыта разработки вы должны комбинировать `hot_reload` с [поддирективой `watch` в директиве `worker`](config.md#watching-for-file-changes). + +- `hot_reload`: обновляет **браузер** при изменении файлов +- `worker.watch`: перезапускает воркер при изменении файлов + +```caddy +localhost + +mercure { + anonymous +} + +root public/ +php_server { + hot_reload + worker { + file /path/to/my_worker.php + watch + } +} +``` + +### Как это работает + +1. **Отслеживание**: FrankenPHP отслеживает файловую систему на предмет модификаций, используя под капотом [библиотеку `e-dant/watcher`](https://github.com/e-dant/watcher) (мы внесли свой вклад в Go-бинденг). +2. **Перезапуск (режим воркера)**: если `watch` включен в конфигурации воркера, PHP-воркер перезапускается для загрузки нового кода. +3. **Отправка**: JSON-полезная нагрузка, содержащая список измененных файлов, отправляется во встроенный [Mercure hub](https://mercure.rocks). +4. **Получение**: Браузер, слушающий через JavaScript-библиотеку, получает событие Mercure. +5. **Обновление**: + + - Если обнаружен **Idiomorph**, он получает обновленное содержимое и преобразует текущий HTML, чтобы он соответствовал новому состоянию, применяя изменения мгновенно без потери состояния. + - В противном случае вызывается `window.location.reload()` для обновления страницы. diff --git a/docs/ru/known-issues.md b/docs/ru/known-issues.md index 06b8558ecb..2850577e1f 100644 --- a/docs/ru/known-issues.md +++ b/docs/ru/known-issues.md @@ -4,35 +4,36 @@ Следующие расширения не совместимы с 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/) | Не поддерживает потокобезопасность | - | +| Название | Причина | Альтернативы | +| ----------------------------------------------------------------------------------------------------------- | ------------------------ | -------------------------------------------------------------------------------------------------------------------- | +| [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: +Следующие расширения имеют известные ошибки и могут вести себя непредсказуемо при использовании с 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). | +| [ext-openssl](https://www.php.net/manual/en/book.openssl.php) | При использовании musl libc расширение OpenSSL может аварийно завершаться при высокой нагрузке. Проблема не возникает при использовании более распространённой GNU libc. Эта ошибка [отслеживается PHP](https://github.com/php/php-src/issues/13648). | -## `get_browser` +## 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), так как они статичны. +Функция [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 +## Автономные бинарные файлы и Docker-образы на базе 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). +Полностью автономные бинарные файлы и Docker-образы на базе 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). + +Если вы столкнётесь с проблемами, отдавайте предпочтение GNU-варианту статического бинарного файла и Docker-образам на базе Debian. ## Использование `https://127.0.0.1` с Docker По умолчанию FrankenPHP генерирует TLS-сертификат для `localhost`, что является самым простым и рекомендуемым вариантом для локальной разработки. -Если вы всё же хотите использовать `127.0.0.1`, настройте генерацию сертификата, указав в переменной окружения `SERVER_NAME` значение `127.0.0.1`. +Если вы действительно хотите использовать `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`. +К сожалению, этого недостаточно при использовании 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/): @@ -44,11 +45,11 @@ docker run \ dunglas/frankenphp ``` -Host-драйвер не поддерживается на Mac и Windows. На этих платформах нужно определить IP-адрес контейнера и включить его в `SERVER_NAME`. +Host-драйвер не поддерживается на Mac и Windows. На этих платформах нужно определить IP-адрес контейнера и включить его в имена серверов. -Выполните команду `docker network inspect bridge`, найдите ключ `Containers` и определите последний присвоенный IP из `IPv4Address`. Увеличьте его на единицу. Если контейнеров нет, первый IP обычно `172.17.0.2`. +Выполните команду `docker network inspect bridge`, найдите ключ `Containers` и определите последний присвоенный IP-адрес в разделе `IPv4Address`. Увеличьте его на единицу. Если контейнеров нет, первый присвоенный IP-адрес обычно `172.17.0.2`. -Включите этот IP в переменную окружения `SERVER_NAME`: +Затем включите это значение в переменную окружения `SERVER_NAME`: ```console docker run \ @@ -59,11 +60,12 @@ docker run \ ``` > [!CAUTION] +> > Обязательно замените `172.17.0.3` на IP, который будет присвоен вашему контейнеру. -Теперь вы должны иметь доступ к `https://127.0.0.1`. +Теперь вы должны иметь доступ к `https://127.0.0.1` с хост-машины. -Если это не так, запустите FrankenPHP в режиме отладки: +Если это не так, запустите FrankenPHP в режиме отладки, чтобы попытаться выяснить проблему: ```console docker run \ @@ -76,13 +78,12 @@ docker run \ ## Скрипты Composer с использованием `@php` -[Скрипты Composer](https://getcomposer.org/doc/articles/scripts.md) могут вызывать PHP для выполнения задач, например, в [проекте Laravel](laravel.md) для команды `@php artisan package:discover --ansi`. -Это [на данный момент не поддерживается](https://github.com/php/frankenphp/issues/483#issuecomment-1899890915) по двум причинам: +[Скрипты Composer](https://getcomposer.org/doc/articles/scripts.md) могут вызывать PHP-бинарный файл для выполнения некоторых задач, например, в [проекте Laravel](laravel.md) для команды `@php artisan package:discover --ansi`. В настоящее время [это не удаётся](https://github.com/php/frankenphp/issues/483#issuecomment-1899890915) по двум причинам: - Composer не знает, как вызывать бинарный файл FrankenPHP; -- Composer может добавлять настройки PHP через флаг `-d`, который FrankenPHP пока не поддерживает. +- Composer может добавлять настройки PHP с помощью флага `-d` в команде, что FrankenPHP пока не поддерживает. -Решение — создать shell-скрипт в `/usr/local/bin/php`, который удаляет неподдерживаемые параметры и вызывает FrankenPHP: +В качестве обходного пути мы можем создать shell-скрипт в `/usr/local/bin/php`, который удаляет неподдерживаемые параметры, а затем вызывает FrankenPHP: ```bash #!/usr/bin/env bash @@ -107,9 +108,9 @@ export PHP_BINARY=/usr/local/bin/php composer install ``` -## TLS/SSL: проблемы со статическими бинарными файлами +## Устранение неполадок TLS/SSL при использовании статических бинарных файлов -При использовании статических бинарных файлов могут возникать следующие ошибки TLS, например, при отправке писем через STARTTLS: +При использовании статических бинарных файлов могут возникать следующие ошибки, связанные с TLS, например, при отправке электронных писем с использованием STARTTLS: ```text Unable to connect with STARTTLS: stream_socket_enable_crypto(): SSL operation failed with code 5. OpenSSL Error messages: @@ -119,19 +120,20 @@ error:80000002:system library::No such file or directory error:0A000086:SSL routines::certificate verify failed ``` -Статический бинарный файл не включает TLS-сертификаты, поэтому необходимо указать OpenSSL местоположение локальных сертификатов CA. +Поскольку статический бинарный файл не включает TLS-сертификаты, вам нужно указать OpenSSL путь к вашей локальной установке сертификатов CA. -Выполните [`openssl_get_cert_locations()`](https://www.php.net/manual/en/function.openssl-get-cert-locations.php), чтобы определить, где должны находиться сертификаты CA, и поместите их туда. +Проверьте вывод [`openssl_get_cert_locations()`](https://www.php.net/manual/en/function.openssl-get-cert-locations.php), чтобы узнать, где должны быть установлены сертификаты CA, и сохраните их в этом месте. > [!WARNING] -> Веб и CLI контексты могут иметь разные настройки. -> Запустите `openssl_get_cert_locations()` в нужном контексте. +> +> Веб и CLI контексты могут иметь разные настройки. +> Убедитесь, что вы запускаете `openssl_get_cert_locations()` в соответствующем контексте. [Сертификаты CA, извлечённые из Mozilla, можно скачать с сайта cURL](https://curl.se/docs/caextract.html). -Кроме того, многие дистрибутивы, такие как Debian, Ubuntu и Alpine, предоставляют пакеты `ca-certificates`, содержащие эти сертификаты. +Кроме того, многие дистрибутивы, включая Debian, Ubuntu и Alpine, предоставляют пакеты с именем `ca-certificates`, которые содержат эти сертификаты. -Также можно использовать переменные `SSL_CERT_FILE` и `SSL_CERT_DIR`, чтобы указать OpenSSL, где искать сертификаты CA: +Также можно использовать переменные `SSL_CERT_FILE` и `SSL_CERT_DIR`, чтобы подсказать OpenSSL, где искать сертификаты CA: ```console # Установите переменные окружения для TLS-сертификатов diff --git a/docs/ru/laravel.md b/docs/ru/laravel.md index 2b6caf90fd..a8375dda92 100644 --- a/docs/ru/laravel.md +++ b/docs/ru/laravel.md @@ -16,7 +16,7 @@ docker run -p 80:80 -p 443:443 -p 443:443/udp -v $PWD:/app dunglas/frankenphp Вы также можете запустить ваши Laravel-проекты с FrankenPHP на локальной машине: -1. [Скачайте бинарный файл для вашей системы](README.md#автономный-бинарный-файл) +1. [Скачайте бинарный файл, соответствующий вашей системе](../#standalone-binary) 2. Добавьте следующую конфигурацию в файл с именем `Caddyfile` в корневой директории вашего Laravel-проекта: ```caddyfile @@ -30,8 +30,10 @@ docker run -p 80:80 -p 443:443 -p 443:443/udp -v $PWD:/app dunglas/frankenphp root public/ # Включите сжатие (опционально) encode zstd br gzip - # Выполняйте PHP-файлы из директории public/ и обслуживайте статические файлы - php_server + # Выполняйте PHP-файлы и обслуживайте статические файлы из директории public/ + php_server { + try_files {path} index.php + } } ``` @@ -45,13 +47,13 @@ Octane можно установить с помощью менеджера па composer require laravel/octane ``` -После установки Octane выполните Artisan-команду `octane:install`, которая создаст конфигурационный файл Octane в вашем приложении: +После установки Octane вы можете выполнить Artisan-команду `octane:install`, которая установит конфигурационный файл Octane в ваше приложение: ```console php artisan octane:install --server=frankenphp ``` -Сервер Octane можно запустить с помощью Artisan-команды `octane:frankenphp`: +Сервер Octane можно запустить с помощью Artisan-команды `octane:frankenphp`. ```console php artisan octane:frankenphp @@ -62,18 +64,20 @@ php artisan octane:frankenphp - `--host`: IP-адрес, к которому должен привязаться сервер (по умолчанию: `127.0.0.1`) - `--port`: Порт, на котором сервер будет доступен (по умолчанию: `8000`) - `--admin-port`: Порт, на котором будет доступен административный сервер (по умолчанию: `2019`) -- `--workers`: Количество worker-скриптов для обработки запросов (по умолчанию: `auto`) +- `--workers`: Количество воркеров, которые должны быть доступны для обработки запросов (по умолчанию: `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) +- `--http-redirect`: Включить редирект с HTTP на HTTPS (включается только если указана опция `--https`) - `--watch`: Автоматически перезагружать сервер при изменении приложения -- `--poll`: Использовать опрос файловой системы для отслеживания изменений в файлах через сеть -- `--log-level`: Установить уровень логирования, используя встроенный логгер Caddy +- `--poll`: Использовать опрос файловой системы при наблюдении, чтобы отслеживать файлы по сети +- `--log-level`: Выводить сообщения журнала на указанном уровне или выше, используя нативный логгер Caddy > [!TIP] > Чтобы получить структурированные JSON-логи (полезно при использовании решений для анализа логов), явно укажите опцию `--log-level`. +См. также [как использовать Mercure с Octane](#mercure-support). + Подробнее о [Laravel Octane читайте в официальной документации](https://laravel.com/docs/octane). ## Laravel-приложения как автономные бинарные файлы @@ -86,7 +90,7 @@ php artisan octane:frankenphp ```dockerfile FROM --platform=linux/amd64 dunglas/frankenphp:static-builder-gnu - # Если вы планируете запускать бинарный файл на системах с musl-libc, используйте static-builder-musl + # Если вы собираетесь запускать бинарный файл на системах с musl-libc, используйте static-builder-musl вместо # Скопируйте ваше приложение WORKDIR /go/src/app/dist/app @@ -161,9 +165,35 @@ php artisan octane:frankenphp Установите переменную окружения `LARAVEL_STORAGE_PATH` (например, в вашем `.env` файле) или вызовите метод `Illuminate\Foundation\Application::useStoragePath()`, чтобы использовать директорию за пределами временной директории. -### Запуск Octane как автономный бинарный файл +### Mercure Support + +[Mercure](https://mercure.rocks) — отличный способ добавить возможности реального времени в ваши Laravel-приложения. FrankenPHP включает [поддержку Mercure из коробки](mercure.md). + +Если вы не используете [Octane](#laravel-octane), см. [документацию Mercure](mercure.md). + +Если вы используете Octane, вы можете включить поддержку Mercure, добавив следующие строки в ваш файл `config/octane.php`: + +```php +// ... + +return [ + // ... + + 'mercure' => [ + 'anonymous' => true, + 'publisher_jwt' => '!ChangeThisMercureHubJWTSecretKey!', + 'subscriber_jwt' => '!ChangeThisMercureHubJWTSecretKey!', + ], +]; +``` + +Вы можете использовать [все директивы, поддерживаемые Mercure](https://mercure.rocks/docs/hub/config#directives), в этом массиве. + +Для публикации и подписки на обновления мы рекомендуем использовать библиотеку [Laravel Mercure Broadcaster](https://github.com/mvanduijker/laravel-mercure-broadcaster). В качестве альтернативы, см. [документацию Mercure](mercure.md), чтобы сделать это на чистом PHP и JavaScript. + +### Running Octane With Standalone Binaries -Можно даже упаковать приложения Laravel Octane как автономный бинарный файл! +Можно даже упаковать приложения Laravel Octane как автономные бинарные файлы! Для этого [установите Octane правильно](#laravel-octane) и следуйте шагам, описанным в [предыдущем разделе](#laravel-приложения-как-автономные-бинарные-файлы). @@ -174,4 +204,5 @@ PATH="$PWD:$PATH" frankenphp php-cli artisan octane:frankenphp ``` > [!CAUTION] +> > Для работы команды автономный бинарник **обязательно** должен быть назван `frankenphp`, так как Octane требует наличия программы с именем `frankenphp` в PATH. diff --git a/docs/ru/logging.md b/docs/ru/logging.md new file mode 100644 index 0000000000..8b40390375 --- /dev/null +++ b/docs/ru/logging.md @@ -0,0 +1,71 @@ +# Логирование + +FrankenPHP легко интегрируется с [системой логирования Caddy](https://caddyserver.com/docs/logging). +Вы можете логировать сообщения, используя стандартные функции PHP, или воспользоваться специализированной функцией `frankenphp_log()` для расширенных возможностей структурированного логирования. + +## `frankenphp_log()` + +Функция `frankenphp_log()` позволяет отправлять структурированные логи непосредственно из вашего PHP-приложения, +что значительно упрощает их прием в такие платформы, как Datadog, Grafana Loki или Elastic, а также поддерживает OpenTelemetry. + +По своей сути, `frankenphp_log()` является оберткой для [пакета Go `log/slog`](https://pkg.go.dev/log/slog), предоставляя богатые возможности логирования. + +Эти логи включают уровень серьезности и необязательные контекстные данные. + +```php +function frankenphp_log(string $message, int $level = FRANKENPHP_LOG_LEVEL_INFO, array $context = []): void +``` + +### Параметры + +- **`message`**: Строка сообщения лога. +- **`level`**: Уровень серьезности лога. Может быть любым произвольным целым числом. Для распространенных уровней предусмотрены константы: `FRANKENPHP_LOG_LEVEL_DEBUG` (`-4`), `FRANKENPHP_LOG_LEVEL_INFO` (`0`), `FRANKENPHP_LOG_LEVEL_WARN` (`4`) и `FRANKENPHP_LOG_LEVEL_ERROR` (`8`). По умолчанию установлено значение `FRANKENPHP_LOG_LEVEL_INFO`. +- **`context`**: Ассоциативный массив дополнительных данных, которые будут включены в запись лога. + +### Пример + +```php + memory_get_usage(), + 'peak_usage' => memory_get_peak_usage(), + ], +); + +``` + +При просмотре логов (например, через `docker compose logs`) вывод будет выглядеть как структурированный JSON: + +```json +{"level":"info","ts":1704067200,"logger":"frankenphp","msg":"Hello from FrankenPHP!"} +{"level":"warn","ts":1704067200,"logger":"frankenphp","msg":"Memory usage high","current_usage":10485760,"peak_usage":12582912} +``` + +## `error_log()` + +FrankenPHP также позволяет логировать с помощью стандартной функции `error_log()`. Если параметр `$message_type` равен `4` (SAPI), +эти сообщения направляются в логгер Caddy. + +По умолчанию сообщения, отправленные через `error_log()`, обрабатываются как неструктурированный текст. +Они полезны для совместимости с существующими приложениями или библиотеками, которые полагаются на стандартную библиотеку PHP. + +### Пример с error_log() + +```php +error_log("Сбой подключения к базе данных", 4); +``` + +Это появится в логах Caddy, часто с префиксом, указывающим на то, что сообщение пришло из PHP. + +> [!TIP] +> Для лучшей наблюдаемости в production-средах предпочитайте `frankenphp_log()`, +> так как она позволяет фильтровать логи по уровню (Debug, Error и т.д.) +> и запрашивать определенные поля в вашей инфраструктуре логирования. diff --git a/docs/ru/mercure.md b/docs/ru/mercure.md index 0186714e7f..88fb5258d2 100644 --- a/docs/ru/mercure.md +++ b/docs/ru/mercure.md @@ -1,12 +1,147 @@ -# Real-time режим +# Режим реального времени -FrankenPHP поставляется с встроенным хабом [Mercure](https://mercure.rocks)! -Mercure позволяет отправлять события в режиме реального времени на все подключённые устройства: они мгновенно получат JavaScript-событие. +FrankenPHP поставляется со встроенным хабом [Mercure](https://mercure.rocks)! +Mercure позволяет отправлять события в режиме реального времени на все подключенные устройства: они мгновенно получат JavaScript-событие. -Не требуются JS-библиотеки или SDK! +Это удобная альтернатива WebSockets, простая в использовании и нативно поддерживаемая всеми современными веб-браузерами! -![Mercure](../mercure-hub.png) +![Mercure](mercure-hub.png) -Чтобы включить хаб Mercure, обновите `Caddyfile` в соответствии с инструкциями [на сайте Mercure](https://mercure.rocks/docs/hub/config). +## Включение Mercure -Для отправки обновлений Mercure из вашего кода мы рекомендуем использовать [Symfony Mercure Component](https://symfony.com/components/Mercure) (для его использования не требуется полный стек Symfony). +Поддержка Mercure по умолчанию отключена. +Вот минимальный пример `Caddyfile`, включающий FrankenPHP и хаб Mercure: + +```caddyfile +# Имя хоста, на который будет отвечать +localhost + +mercure { + # Секретный ключ, используемый для подписи JWT-токенов для издателей + publisher_jwt !ChangeThisMercureHubJWTSecretKey! + # Разрешает анонимных подписчиков (без JWT) + anonymous +} + +root public/ +php_server +``` + +> [!TIP] +> +> [Пример `Caddyfile`](https://github.com/php/frankenphp/blob/main/caddy/frankenphp/Caddyfile), +> предоставляемый [образами Docker](docker.md), уже содержит закомментированную конфигурацию Mercure +> с удобными переменными окружения для настройки. +> +> Раскомментируйте раздел Mercure в `/etc/frankenphp/Caddyfile`, чтобы включить его. + +## Подписка на обновления + +По умолчанию хаб Mercure доступен по пути `/.well-known/mercure` на вашем сервере FrankenPHP. +Для подписки на обновления используйте нативный JavaScript-класс [`EventSource`](https://developer.mozilla.org/docs/Web/API/EventSource): + +```html + + +Пример Mercure + +``` + +## Публикация обновлений + +### Использование `mercure_publish()` + +FrankenPHP предоставляет удобную функцию `mercure_publish()` для публикации обновлений во встроенный хаб Mercure: + +```php + 'value'])); + +// Запись в логи FrankenPHP +error_log("update $updateID published", 4); +``` + +Полная сигнатура функции: + +```php +/** + * @param string|string[] $topics + */ +function mercure_publish(string|array $topics, string $data = '', bool $private = false, ?string $id = null, ?string $type = null, ?int $retry = null): string {} +``` + +### Использование `file_get_contents()` + +Для отправки обновления подключенным подписчикам отправьте аутентифицированный POST-запрос к хабу Mercure с параметрами `topic` и `data`: + +```php + [ + 'method' => 'POST', + 'header' => "Content-type: application/x-www-form-urlencoded\r\nAuthorization: Bearer " . JWT, + 'content' => http_build_query([ + 'topic' => 'my-topic', + 'data' => json_encode(['key' => 'value']), + ]), +]])); + +// Запись в логи FrankenPHP +error_log("update $updateID published", 4); +``` + +Ключ, переданный в качестве параметра опции `mercure.publisher_jwt` в `Caddyfile`, должен использоваться для подписи JWT-токена, применяемого в заголовке `Authorization`. + +JWT должен включать утверждение `mercure` с разрешением `publish` для тем, в которые вы хотите публиковать обновления. См. [документацию Mercure](https://mercure.rocks/spec#publishers) по авторизации. + +Чтобы сгенерировать свои собственные токены, вы можете использовать [эту ссылку jwt.io](https://www.jwt.io/#token=eyJhbGciOiJIUzI1NiJ9.eyJtZXJjdXJlIjp7InB1Ymxpc2giOlsiKiJdfX0.PXwpfIGng6KObfZlcOXvcnWCJOWTFLtswGI5DZuWSK4), но для производственных приложений рекомендуется использовать короткоживущие токены, генерируемые динамически с помощью надежной [библиотеки JWT](https://www.jwt.io/libraries?programming_language=php). + +### Использование Symfony Mercure + +В качестве альтернативы вы можете использовать [компонент Symfony Mercure](https://symfony.com/components/Mercure), автономную PHP-библиотеку. + +Эта библиотека обрабатывает генерацию JWT, публикацию обновлений, а также авторизацию на основе файлов cookie для подписчиков. + +Сначала установите библиотеку с помощью Composer: + +```console +composer require symfony/mercure lcobucci/jwt +``` + +Затем вы можете использовать её так: + +```php +publish(new \Symfony\Component\Mercure\Update('my-topic', json_encode(['key' => 'value']))); + +// Запись в логи FrankenPHP +error_log("update $updateID published", 4); +``` + +Mercure также нативно поддерживается: + +- [Laravel](laravel.md#mercure-support) +- [Symfony](https://symfony.com/doc/current/mercure.html) +- [API Platform](https://api-platform.com/docs/core/mercure/) diff --git a/docs/ru/metrics.md b/docs/ru/metrics.md index 19f1160ac5..91348ee875 100644 --- a/docs/ru/metrics.md +++ b/docs/ru/metrics.md @@ -2,14 +2,16 @@ При включении [метрик 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-скрипты всегда используют поток). +- `frankenphp_busy_threads`: Количество потоков PHP, которые в данный момент обрабатывают запрос (работающие воркеры всегда используют поток). +- `frankenphp_queue_depth`: Количество обычных запросов в очереди. +- `frankenphp_total_workers{worker="[worker_name]"}`: Общее количество воркеров. +- `frankenphp_busy_workers{worker="[worker_name]"}`: Количество воркеров, которые в данный момент обрабатывают запрос. +- `frankenphp_worker_request_time{worker="[worker_name]"}`: Время, затраченное всеми воркерами на обработку запросов. +- `frankenphp_worker_request_count{worker="[worker_name]"}`: Количество запросов, обработанных всеми воркерами. +- `frankenphp_ready_workers{worker="[worker_name]"}`: Количество воркеров, которые вызвали `frankenphp_handle_request` хотя бы один раз. +- `frankenphp_worker_crashes{worker="[worker_name]"}`: Количество случаев неожиданного завершения работы воркера. +- `frankenphp_worker_restarts{worker="[worker_name]"}`: Количество случаев, когда воркер был целенаправленно перезапущен. +- `frankenphp_worker_queue_depth{worker="[worker_name]"}`: Количество запросов в очереди. -Для метрик worker-скриптов плейсхолдер `[worker]` заменяется на путь к Worker-скрипту, указанному в Caddyfile. +Для метрик воркеров плейсхолдер `[worker_name]` заменяется на имя воркера в Caddyfile, иначе будет использоваться абсолютный путь к файлу воркера. diff --git a/docs/ru/performance.md b/docs/ru/performance.md index 06e4a52455..67f1d165c1 100644 --- a/docs/ru/performance.md +++ b/docs/ru/performance.md @@ -1,52 +1,61 @@ # Производительность -По умолчанию FrankenPHP предлагает хороший баланс между производительностью и удобством использования. -Однако, используя подходящую конфигурацию, можно существенно улучшить производительность. +По умолчанию FrankenPHP стремится обеспечить хороший компромисс между производительностью и простотой использования. +Однако можно существенно улучшить производительность, используя соответствующую конфигурацию. -## Количество потоков и worker-скриптов +## Количество потоков и воркеров -По умолчанию FrankenPHP запускает потоков и worker-скриптов (в worker режиме) вдвое больше, чем количество доступных процессорных ядер. +По умолчанию FrankenPHP запускает в 2 раза больше потоков и воркеров (в режиме воркера), чем доступное количество процессорных ядер. -Оптимальные значения зависят от структуры вашего приложения, его функциональности и аппаратного обеспечения. -Мы настоятельно рекомендуем изменить эти значения. +Подходящие значения сильно зависят от того, как написано ваше приложение, что оно делает, и вашего аппаратного обеспечения. +Мы настоятельно рекомендуем изменить эти значения. Для наилучшей стабильности системы рекомендуется, чтобы `num_threads` x `memory_limit` < `available_memory`. -Чтобы найти подходящие параметры, лучше всего провести нагрузочные тесты, имитирующие реальный трафик. -Хорошими инструментами для этого являются [k6](https://k6.io) и [Gatling](https://gatling.io). +Чтобы найти подходящие значения, лучше всего провести нагрузочные тесты, имитирующие реальный трафик. +[k6](https://k6.io) и [Gatling](https://gatling.io) являются хорошими инструментами для этого. -Чтобы настроить количество потоков, используйте опцию `num_threads` в директивах `php_server` и `php`. -Для изменения количества worker-скриптов используйте опцию `num` в секции `worker` директивы `frankenphp`. +Чтобы настроить количество потоков, используйте опцию `num_threads` директив `php_server` и `php`. +Для изменения количества воркеров используйте опцию `num` в секции `worker` директивы `frankenphp`. -## Worker режим +### `max_threads` -Включение [Worker режима](worker.md) значительно улучшает производительность, -но ваше приложение должно быть адаптировано для совместимости с этим режимом: -необходимо создать worker-скрипт и убедиться, что приложение не имеет утечек памяти. +Хотя всегда лучше точно знать, каким будет ваш трафик, реальные приложения, как правило, более непредсказуемы. [Конфигурация](config.md#caddyfile-config) `max_threads` позволяет FrankenPHP автоматически запускать дополнительные потоки во время выполнения до указанного предела. +`max_threads` может помочь вам определить, сколько потоков требуется для обработки вашего трафика, и сделать сервер более устойчивым к скачкам задержки. +Если установлено значение `auto`, лимит будет оценен на основе `memory_limit` в вашем `php.ini`. Если это невозможно, +`auto` вместо этого будет по умолчанию равно 2x `num_threads`. Имейте в виду, что `auto` может сильно недооценивать количество необходимых потоков. +`max_threads` аналогичен [pm.max_children](https://www.php.net/manual/en/install.fpm.configuration.php#pm.max-children) в PHP FPM. Основное отличие состоит в том, что FrankenPHP использует потоки вместо +процессов и автоматически делегирует их различным скриптам воркеров и 'классическому режиму' по мере необходимости. -## Избегайте использования musl +## Режим воркера -Статические бинарники, которые мы предоставляем, а также Alpine Linux-вариант официальных Docker-образов используют [библиотеку musl libc](https://musl.libc.org). +Включение [режима воркера](worker.md) значительно улучшает производительность, +но ваше приложение должно быть адаптировано для совместимости с этим режимом: +необходимо создать воркер-скрипт и убедиться, что приложение не имеет утечек памяти. -Известно, что PHP [значительно медленнее работает](https://gitlab.alpinelinux.org/alpine/aports/-/issues/14381) с этой библиотекой по сравнению с традиционной GNU libc, особенно при компиляции в ZTS режиме (потокобезопасный режим), который требуется для FrankenPHP. +## Не используйте musl -Кроме того, [некоторые ошибки проявляются исключительно при использовании musl](https://github.com/php/php-src/issues?q=sort%3Aupdated-desc+is%3Aissue+is%3Aopen+label%3ABug+musl). +Alpine Linux-вариант официальных Docker-образов и бинарники по умолчанию, которые мы предоставляем, используют [библиотеку musl libc](https://musl.libc.org). -В производственной среде настоятельно рекомендуется использовать glibc. +Известно, что PHP [медленнее работает](https://gitlab.alpinelinux.org/alpine/aports/-/issues/14381) при использовании этой альтернативной библиотеки C вместо традиционной библиотеки GNU, +особенно при компиляции в режиме ZTS (потокобезопасный), который требуется для FrankenPHP. Разница может быть существенной в среде с большим количеством потоков. -Это можно сделать, используя Debian Docker-образы (по умолчанию) и [компилируя FrankenPHP из исходников](compile.md). +Также [некоторые ошибки проявляются только при использовании musl](https://github.com/php/php-src/issues?q=sort%3Aupdated-desc+is%3Aissue+is%3Aopen+label%3ABug+musl). -В качестве альтернативы мы предоставляем статические бинарники, скомпилированные с [аллокатором mimalloc](https://github.com/microsoft/mimalloc), что делает FrankenPHP+musl быстрее (но всё же медленнее, чем FrankenPHP+glibc). +В производственных средах мы рекомендуем использовать FrankenPHP, слинкованный с glibc и скомпилированный с соответствующим уровнем оптимизации. + +Этого можно достичь, используя Docker-образы на базе Debian, наши пакеты [.deb](https://debs.henderkes.com) или [.rpm](https://rpms.henderkes.com), или [компилируя FrankenPHP из исходников](compile.md). ## Настройка среды выполнения Go FrankenPHP написан на языке Go. -В большинстве случаев среда выполнения Go не требует особой настройки, но в некоторых ситуациях специфическая конфигурация может улучшить производительность. +В целом, среда выполнения Go не требует какой-либо специальной конфигурации, но в определенных обстоятельствах специфическая конфигурация улучшает производительность. -Рекомендуется установить переменную окружения `GODEBUG` в значение `cgocheck=0` (по умолчанию в Docker-образах FrankenPHP). +Вы, вероятно, захотите установить переменную окружения `GODEBUG` в значение `cgocheck=0` (по умолчанию в Docker-образах FrankenPHP). -Если вы запускаете FrankenPHP в контейнерах (Docker, Kubernetes, LXC и т.д.) и ограничиваете доступную память, установите переменную окружения `GOMEMLIMIT` в значение доступного объёма памяти. +Если вы запускаете FrankenPHP в контейнерах (Docker, Kubernetes, LXC...) и ограничиваете память, доступную для контейнеров, +установите переменную окружения `GOMEMLIMIT` в значение доступного объёма памяти. -Для более детальной информации ознакомьтесь с [документацией Go по этой теме](https://pkg.go.dev/runtime#hdr-Environment_Variables). +Для более детальной информации, [страница документации Go, посвященная этой теме](https://pkg.go.dev/runtime#hdr-Environment_Variables), обязательна для прочтения, чтобы максимально использовать возможности среды выполнения. ## `file_server` @@ -60,16 +69,54 @@ php_server { } ``` +## `try_files` + +Помимо статических файлов и PHP-файлов, `php_server` также попытается обслуживать индексные файлы вашего приложения +и файлы индексов директорий (`/path/` -> `/path/index.php`). Если вам не нужны индексные файлы директорий, +вы можете отключить их, явно определив `try_files` следующим образом: + +```caddyfile +php_server { + try_files {path} index.php + root /root/to/your/app # явное добавление root здесь позволяет улучшить кэширование +} +``` + +Это может значительно сократить количество ненужных файловых операций. + +Альтернативный подход с 0 ненужных операций файловой системы заключается в использовании директивы `php` и разделении +файлов от PHP по пути. Этот подход хорошо работает, если всё ваше приложение обслуживается одним входным файлом. +Пример [конфигурации](config.md#caddyfile-config), которая обслуживает статические файлы из папки `/assets`, может выглядеть так: + +```caddyfile +route { + @assets { + path /assets/* + } + + # всё, что находится за /assets, обрабатывается файловым сервером + file_server @assets { + root /root/to/your/app + } + + # всё, что не находится в /assets, обрабатывается вашим индексным или воркерным PHP-файлом + rewrite index.php + php { + root /root/to/your/app # явное добавление root здесь позволяет улучшить кэширование + } +} +``` + ## Плейсхолдеры -Вы можете использовать [плейсхолдеры](https://caddyserver.com/docs/conventions#placeholders) в директивах `root` и `env`. -Однако это предотвращает кеширование значений и существенно снижает производительность. +Вы можете использовать [плейсхолдеры](https://caddyserver.com/docs/conventions#placeholders) в директивах `root` и `env`. +Однако это предотвращает кэширование значений и существенно снижает производительность. По возможности избегайте использования плейсхолдеров в этих директивах. ## `resolve_root_symlink` -По умолчанию, если корневая директория документа является символьной ссылкой, она автоматически разрешается FrankenPHP (это необходимо для корректной работы PHP). +По умолчанию, если корневая директория документа является символьной ссылкой, она автоматически разрешается FrankenPHP (это необходимо для правильной работы PHP). Если корневая директория документа не является символьной ссылкой, вы можете отключить эту функцию. ```caddyfile @@ -78,24 +125,60 @@ php_server { } ``` -Это улучшит производительность, если директива `root` содержит [плейсхолдеры](https://caddyserver.com/docs/conventions#placeholders). -В остальных случаях прирост производительности будет минимальным. +Это улучшит производительность, если директива `root` содержит [плейсхолдеры](https://caddyserver.com/docs/conventions#placeholders). +В остальных случаях прирост будет незначительным. ## Логи -Логирование, безусловно, полезно, но требует операций ввода-вывода и выделения памяти, что значительно снижает производительность. -Убедитесь, что вы [правильно настроили уровень логирования](https://caddyserver.com/docs/caddyfile/options#log) и логируете только необходимое. +Логирование, безусловно, очень полезно, но, по определению, +оно требует операций ввода-вывода и выделения памяти, что значительно снижает производительность. +Убедитесь, что вы [правильно настроили уровень логирования](https://caddyserver.com/docs/caddyfile/options#log) +и логируете только то, что необходимо. ## Производительность PHP -FrankenPHP использует официальный интерпретатор PHP. -Все стандартные оптимизации производительности PHP применимы к FrankenPHP. +FrankenPHP использует официальный интерпретатор PHP. +Все обычные оптимизации производительности, связанные с PHP, применимы к FrankenPHP. В частности: - убедитесь, что [OPcache](https://www.php.net/manual/en/book.opcache.php) установлен, включён и настроен должным образом; - включите [оптимизацию автозагрузки Composer](https://getcomposer.org/doc/articles/autoloader-optimization.md); -- убедитесь, что кеш `realpath` достаточно велик для нужд вашего приложения; +- убедитесь, что кэш `realpath` достаточно велик для нужд вашего приложения; - используйте [предварительную загрузку](https://www.php.net/manual/en/opcache.preloading.php). -Для более детальной информации ознакомьтесь с [документацией Symfony о производительности](https://symfony.com/doc/current/performance.html) (большинство советов полезны даже если вы не используете Symfony). +Для более подробной информации прочтите [посвященную этому запись в документации Symfony](https://symfony.com/doc/current/performance.html) +(большинство советов полезны, даже если вы не используете Symfony). + +## Разделение пула потоков + +Приложения часто взаимодействуют с медленными внешними сервисами, такими как +API, который становится ненадежным при высокой нагрузке или постоянно отвечает более 10 секунд. +В таких случаях может быть полезно разделить пул потоков, чтобы иметь выделенные "медленные" пулы. +Это предотвращает потребление всех ресурсов/потоков сервера медленными конечными точками и +ограничивает параллелизм запросов, направленных к медленной конечной точке, подобно +пулу соединений. + +```caddyfile +{ + frankenphp { + max_threads 100 # максимум 100 потоков, разделяемых всеми воркерами + } +} + +example.com { + php_server { + root /app/public # корень вашего приложения + worker index.php { + match /slow-endpoint/* # все запросы с путём /slow-endpoint/* обрабатываются этим пулом потоков + num 10 # минимум 10 потоков для запросов, соответствующих /slow-endpoint/* + } + worker index.php { + match * # все остальные запросы обрабатываются отдельно + num 20 # минимум 20 потоков для других запросов, даже если медленные конечные точки начнут зависать + } + } +} +``` + +В целом, также рекомендуется обрабатывать очень медленные конечные точки асинхронно, используя соответствующие механизмы, такие как очереди сообщений. diff --git a/docs/ru/production.md b/docs/ru/production.md index e438b56410..41f31155d1 100644 --- a/docs/ru/production.md +++ b/docs/ru/production.md @@ -1,10 +1,10 @@ -# Деплой в продакшен +# Развертывание в production В этом руководстве мы рассмотрим, как развернуть 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/). +Если вы используете API Platform (который также использует FrankenPHP), ознакомьтесь с [документацией по развертыванию этого фреймворка](https://api-platform.com/docs/deployment/). ## Подготовка приложения @@ -13,23 +13,26 @@ ```dockerfile FROM dunglas/frankenphp -# Замените "your-domain-name.example.com" на ваш домен +# Обязательно замените "your-domain-name.example.com" на ваше доменное имя ENV SERVER_NAME=your-domain-name.example.com # Если вы хотите отключить HTTPS, используйте вместо этого: #ENV SERVER_NAME=:80 -# Включите настройки PHP для продакшн +# Если ваш проект не использует директорию "public" в качестве корневой веб-директории, вы можете указать её здесь: +# ENV SERVER_ROOT=web/ + +# Включите настройки PHP для production RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini" # Скопируйте файлы PHP вашего проекта в публичную директорию COPY . /app/public -# Если вы используете Symfony или Laravel, необходимо скопировать весь проект: +# Если вы используете Symfony или Laravel, вам нужно скопировать весь проект вместо этого: #COPY . /app ``` -Ознакомьтесь с разделом "[Создание кастомных Docker-образов](docker.md)" для получения дополнительных подробностей и настроек, а также для установки PHP-расширений и модулей Caddy. +Ознакомьтесь с разделом "[Создание кастомных Docker-образов](docker.md)" для получения дополнительных подробностей и опций, а также чтобы узнать, как настроить конфигурацию, установить PHP-расширения и модули Caddy. -Если ваш проект использует Composer, убедитесь, что он включён в Docker-образ, и установите все зависимости. +Если ваш проект использует Composer, убедитесь, что он включён в Docker-образ, и установите свои зависимости. Затем добавьте файл `compose.yaml`: @@ -46,7 +49,7 @@ services: - caddy_data:/data - caddy_config:/config -# Томы, необходимые для сертификатов и конфигурации Caddy +# Тома, необходимые для сертификатов и конфигурации Caddy volumes: caddy_data: caddy_config: @@ -54,26 +57,31 @@ volumes: > [!NOTE] > -> Примеры выше предназначены для использования в продакшне. -> В процессе разработки вы можете использовать том для монтирования, другую конфигурацию PHP и другое значение для переменной окружения `SERVER_NAME`. +> Примеры выше предназначены для использования в production. +> В процессе разработки вы можете захотеть использовать том, другую конфигурацию PHP и другое значение для переменной окружения `SERVER_NAME`. > -> Посмотрите проект [Symfony Docker](https://github.com/dunglas/symfony-docker) (который использует FrankenPHP) для более сложного примера с использованием мультистейдж-образов, Composer, дополнительных PHP-расширений и т.д. +> Ознакомьтесь с проектом [Symfony Docker](https://github.com/dunglas/symfony-docker) (который использует FrankenPHP) для более сложного примера с использованием многостадийных образов, Composer, дополнительных PHP-расширений и т.д. -Наконец, если вы используете Git, закоммитьте эти файлы и отправьте их в репозиторий. +Наконец, если вы используете Git, зафиксируйте эти файлы и отправьте их. ## Подготовка сервера -Для деплоя приложения в продакшн требуется сервер. В этом руководстве мы будем использовать виртуальную машину, предоставляемую DigitalOcean, но подойдёт любой Linux-сервер. +Для развертывания вашего приложения в production вам потребуется сервер. В этом руководстве мы будем использовать виртуальную машину, предоставляемую DigitalOcean, но подойдёт любой Linux-сервер. Если у вас уже есть Linux-сервер с установленным Docker, вы можете сразу перейти к [следующему разделу](#настройка-доменного-имени). -В противном случае, используйте [эту ссылку](https://m.do.co/c/5d8aabe3ab80), чтобы получить $200 на баланс, создайте аккаунт, затем нажмите "Create a Droplet". -Перейдите во вкладку "Marketplace" в разделе "Choose an image" и найдите приложение "Docker". Это создаст сервер на Ubuntu с установленными Docker и Docker Compose. +В противном случае, используйте [эту партнерскую ссылку](https://m.do.co/c/5d8aabe3ab80), чтобы получить $200 бесплатного кредита, создайте аккаунт, затем нажмите "Create a Droplet". +Затем перейдите во вкладку "Marketplace" в разделе "Choose an image" и найдите приложение "Docker". Это создаст сервер на Ubuntu с уже установленными последними версиями Docker и Docker Compose! + +Для целей тестирования будет достаточно самых дешевых планов. +Для реального использования в production вы, вероятно, захотите выбрать план из раздела "general purpose" в соответствии с вашими потребностями. -Для тестов подойдут самые дешёвые тарифы. Для реального продакшна выберите тариф из раздела "general purpose" в зависимости от ваших потребностей. +![Развертывание FrankenPHP на DigitalOcean с Docker](digitalocean-droplet.png) -![Деплой FrankenPHP на DigitalOcean с Docker](../digitalocean-droplet.png) +Вы можете оставить настройки по умолчанию для других параметров или изменить их в соответствии с вашими потребностями. +Не забудьте добавить свой SSH-ключ или создать пароль, затем нажмите кнопку "Finalize and create". -После этого подключитесь к серверу через SSH: +Затем подождите несколько секунд, пока ваш Droplet будет подготавливаться. +Когда ваш Droplet будет готов, подключитесь через SSH: ```console ssh root@ @@ -81,25 +89,28 @@ ssh root@ ## Настройка доменного имени -В большинстве случаев вам потребуется связать доменное имя с вашим сайтом. -Создайте запись DNS типа `A`, указывающую на IP вашего сервера: +В большинстве случаев вам потребуется связать доменное имя с вашим сайтом. +Если у вас еще нет доменного имени, вам придется приобрести его через регистратора. + +Затем создайте DNS-запись типа `A` для вашего доменного имени, указывающую на IP-адрес вашего сервера: ```dns your-domain-name.example.com. IN A 207.154.233.113 ``` -Пример настройки через DigitalOcean ("Networking" > "Domains"): +Пример с использованием сервиса DigitalOcean Domains ("Networking" > "Domains"): -![Настройка DNS в DigitalOcean](../digitalocean-dns.png) +![Настройка DNS в DigitalOcean](digitalocean-dns.png) > [!NOTE] > -> Let's Encrypt, сервис, используемый FrankenPHP для автоматической генерации TLS-сертификатов, не поддерживает использование IP-адресов. Для работы необходим домен. +> 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). +Скопируйте ваш проект на сервер с помощью `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: @@ -107,19 +118,20 @@ your-domain-name.example.com. IN A 207.154.233.113 git clone git@github.com:/.git ``` -Перейдите в директорию проекта и запустите приложение в режиме продакшн: +Перейдите в директорию, содержащую ваш проект (``), и запустите приложение в режиме production: ```console -docker compose up -d --wait +docker compose up --wait ``` -Сервер готов, а HTTPS-сертификат был автоматически сгенерирован. Перейдите на `https://your-domain-name.example.com` и наслаждайтесь! +Ваш сервер запущен и работает, и HTTPS-сертификат был автоматически сгенерирован для вас. +Перейдите на `https://your-domain-name.example.com` и наслаждайтесь! > [!CAUTION] > -> Docker может кэшировать слои. Убедитесь, что вы используете актуальную сборку, или используйте опцию `--no-cache` для предотвращения проблем с кэшем. +> 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. +Если вы хотите развернуть ваше приложение на кластере машин, вы можете использовать [Docker Swarm](https://docs.docker.com/engine/swarm/stack-deploy/), который совместим с предоставленными файлами Compose. +Для развертывания на Kubernetes ознакомьтесь с [Helm-чартом, предоставляемым API Platform](https://api-platform.com/docs/deployment/kubernetes/), который использует FrankenPHP. diff --git a/docs/ru/static.md b/docs/ru/static.md index 40bc6db66f..1d37ded0f5 100644 --- a/docs/ru/static.md +++ b/docs/ru/static.md @@ -1,79 +1,106 @@ -# Создание статических бинарных файлов +# Создание статической сборки -Вместо использования локальной установки библиотеки PHP, можно создать статическую сборку FrankenPHP благодаря проекту [static-php-cli](https://github.com/crazywhalecc/static-php-cli) (несмотря на название, проект поддерживает все SAPI, а не только CLI). +Вместо использования локальной установки библиотеки PHP, можно создать статическую или в основном статическую сборку FrankenPHP благодаря проекту [static-php-cli](https://github.com/crazywhalecc/static-php-cli) (несмотря на название, этот проект поддерживает все SAPI, а не только CLI). -С помощью этого метода создаётся единый переносимый бинарник, который включает PHP-интерпретатор, веб-сервер Caddy и FrankenPHP! +С помощью этого метода единый, переносимый бинарный файл будет содержать PHP-интерпретатор, веб-сервер Caddy и FrankenPHP! -FrankenPHP также поддерживает [встраивание PHP-приложений в статический бинарный файл](embed.md). +Полностью статические нативные исполняемые файлы не требуют никаких зависимостей и могут быть запущены даже на образе Docker [`scratch`](https://docs.docker.com/build/building/base-images/#create-a-minimal-base-image-using-scratch). Однако они не могут загружать динамические PHP-расширения (такие как Xdebug) и имеют некоторые ограничения, поскольку используют musl libc. + +В основном статические бинарные файлы требуют только `glibc` и могут загружать динамические расширения. + +По возможности мы рекомендуем использовать в основном статические сборки на базе glibc. + +FrankenPHP также поддерживает [встраивание PHP-приложения в статический бинарный файл](embed.md). ## Linux -Мы предоставляем Docker-образ для сборки статического бинарника для Linux: +Мы предоставляем образы Docker для сборки статических бинарных файлов для Linux: + +### Полностью статическая сборка на базе musl + +Для полностью статического бинарного файла, который работает на любом дистрибутиве Linux без зависимостей, но не поддерживает динамическую загрузку расширений: + +```console +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 +``` + +Для лучшей производительности в сценариях с высокой конкуренцией рассмотрите возможность использования аллокатора [mimalloc](https://github.com/microsoft/mimalloc). ```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 +docker buildx bake --load --set static-builder-musl.args.MIMALLOC=1 static-builder-musl ``` -Созданный статический бинарный файл называется `frankenphp` и будет доступен в текущей директории. +### В основном статическая сборка на базе glibc (с поддержкой динамических расширений) -Чтобы собрать статический бинарный файл без Docker, используйте инструкции для macOS — они подходят и для Linux. +Для бинарного файла, который поддерживает динамическую загрузку PHP-расширений, при этом выбранные расширения скомпилированы статически: + +```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 +``` -### Дополнительные расширения +Этот бинарный файл поддерживает все версии glibc 2.17 и выше, но не работает в системах на базе musl (таких как Alpine Linux). + +Результирующий в основном статический (за исключением `glibc`) бинарный файл называется `frankenphp` и доступен в текущей директории. + +Если вы хотите собрать статический бинарный файл без Docker, ознакомьтесь с инструкциями для macOS, которые также работают для Linux. + +### Пользовательские расширения По умолчанию компилируются самые популярные PHP-расширения. -Чтобы уменьшить размер бинарного файла и сократить возможные векторы атак, можно указать список расширений, которые следует включить в сборку, используя Docker-аргумент `PHP_EXTENSIONS`. +Чтобы уменьшить размер бинарного файла и сократить поверхность атаки, вы можете выбрать список расширений для сборки, используя Docker-аргумент `PHP_EXTENSIONS`. Например, выполните следующую команду, чтобы собрать только расширение `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 # ... ``` -Чтобы добавить библиотеки, расширяющие функциональность включённых расширений, используйте Docker-аргумент `PHP_EXTENSION_LIBS`: +Чтобы добавить библиотеки, обеспечивающие дополнительную функциональность для включенных вами расширений, вы можете передать 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 + --set static-builder-musl.args.PHP_EXTENSIONS=gd \ + --set static-builder-musl.args.PHP_EXTENSION_LIBS=libjpeg,libwebp \ + static-builder-musl ``` ### Дополнительные модули Caddy -Чтобы добавить дополнительные модули Caddy или передать аргументы в [xcaddy](https://github.com/caddyserver/xcaddy), используйте Docker-аргумент `XCADDY_ARGS`: +Чтобы добавить дополнительные модули 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 + --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 ``` -В этом примере добавляются модуль HTTP-кэширования [Souin](https://souin.io) для Caddy, а также модули [cbrotli](https://github.com/dunglas/caddy-cbrotli), [Mercure](https://mercure.rocks) и [Vulcain](https://vulcain.rocks). +В этом примере мы добавляем модуль 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`, добавьте их явно, если хотите включить их в сборку. +> Модули cbrotli, Mercure и Vulcain включены по умолчанию, если `XCADDY_ARGS` пуст или не установлен. +> Если вы настраиваете значение `XCADDY_ARGS`, вы должны явно включить их, если хотите, чтобы они были добавлены. -См. также, как [настроить сборку](#настройка-сборки). +См. также, как [настроить сборку](#настройка-сборки) ### Токен GitHub Если вы достигли лимита запросов к API GitHub, задайте личный токен доступа GitHub в переменной окружения `GITHUB_TOKEN`: ```console -GITHUB_TOKEN="xxx" docker --load buildx bake static-builder +GITHUB_TOKEN="xxx" docker --load buildx bake static-builder-musl # ... ``` ## macOS -Запустите следующий скрипт, чтобы создать статический бинарный файл для macOS (должен быть установлен [Homebrew](https://brew.sh/)): +Запустите следующий скрипт, чтобы создать статический бинарный файл для macOS (у вас должен быть установлен [Homebrew](https://brew.sh/)): ```console git clone https://github.com/php/frankenphp @@ -81,7 +108,7 @@ cd frankenphp ./build-static.sh ``` -Примечание: этот скрипт также работает на Linux (и, вероятно, на других Unix-системах) и используется внутри предоставленного Docker-образа для статической сборки. +Примечание: этот скрипт также работает на Linux (и, вероятно, на других Unix-системах) и используется внутри предоставленных нами образов Docker. ## Настройка сборки @@ -91,10 +118,39 @@ cd 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 +- `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 +- `MIMALLOC`: (экспериментально, только для Linux) заменяет musl's mallocng на [mimalloc](https://github.com/microsoft/mimalloc) для повышения производительности. Мы рекомендуем использовать это только для сборок, нацеленных на musl; для glibc предпочтительнее отключить эту опцию и использовать [`LD_PRELOAD`](https://microsoft.github.io/mimalloc/overrides.html) при запуске вашего бинарного файла. +- `RELEASE`: (только для мейнтейнеров) если задано, результирующий бинарник будет загружен на GitHub + +## Расширения + +С бинарными файлами на базе glibc или macOS вы можете динамически загружать PHP-расширения. Однако эти расширения должны быть скомпилированы с поддержкой ZTS. Поскольку большинство менеджеров пакетов в настоящее время не предлагают ZTS-версии своих расширений, вам придется компилировать их самостоятельно. + +Для этого вы можете собрать и запустить контейнер Docker `static-builder-gnu`, подключиться к нему удаленно и скомпилировать расширения с помощью `./configure --with-php-config=/go/src/app/dist/static-php-cli/buildroot/bin/php-config`. + +Пример шагов для [расширения 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 +``` + +Это создаст `frankenphp` и `xdebug-zts.so` в текущей директории. Если вы переместите `xdebug-zts.so` в каталог ваших расширений, добавите `zend_extension=xdebug-zts.so` в ваш php.ini и запустите FrankenPHP, он загрузит Xdebug. diff --git a/docs/ru/wordpress.md b/docs/ru/wordpress.md new file mode 100644 index 0000000000..b3efe955b4 --- /dev/null +++ b/docs/ru/wordpress.md @@ -0,0 +1,59 @@ +# WordPress + +Запускайте [WordPress](https://wordpress.org/) с FrankenPHP, чтобы насладиться современным, высокопроизводительным стеком с автоматическим HTTPS, HTTP/3 и сжатием Zstandard. + +## Минимальная установка + +1. [Скачайте WordPress](https://wordpress.org/download/) +2. Извлеките ZIP-архив и откройте терминал в извлеченной директории +3. Запустите: + + ```console + frankenphp php-server + ``` + +4. Перейдите по адресу `http://localhost/wp-admin/` и следуйте инструкциям по установке +5. Наслаждайтесь! + +Для готовой к продакшену установки предпочтительнее использовать `frankenphp run` с таким `Caddyfile`: + +```caddyfile +example.com + +php_server +encode zstd br gzip +log +``` + +## Горячая перезагрузка + +Чтобы использовать функцию [горячей перезагрузки](hot-reload.md) с WordPress, включите [Mercure](mercure.md) и добавьте поддирективу `hot_reload` к директиве `php_server` в вашем `Caddyfile`: + +```caddyfile +localhost + +mercure { + anonymous +} + +php_server { + hot_reload +} +``` + +Затем добавьте код, необходимый для загрузки JavaScript-библиотек, в файл `functions.php` вашей темы WordPress: + +```php +function hot_reload() { + ?> + + + + + + [!TIP] +> Следующий раздел актуален только до Symfony 7.4, где была представлена нативная поддержка режима воркера FrankenPHP. + +Режим воркера FrankenPHP поддерживается компонентом [Symfony Runtime](https://symfony.com/doc/current/components/runtime.html). +Чтобы запустить любое Symfony-приложение в режиме воркера, установите пакет FrankenPHP для [PHP Runtime](https://github.com/php-runtime/runtime): ```console composer require runtime/frankenphp-symfony @@ -57,30 +62,36 @@ docker run \ ## Laravel Octane -Подробнее см. в [документации](laravel.md#laravel-octane). +См. [специальную документацию](laravel.md#laravel-octane). ## Пользовательские приложения -Следующий пример показывает, как создать собственный worker-скрипт без использования сторонних библиотек: +Следующий пример показывает, как создать собственный скрипт воркера без использования сторонних библиотек: ```php boot(); -// Обработчик запросов за пределами цикла для повышения производительности +// Обработчик вне цикла для лучшей производительности (меньше работы) $handler = static function () use ($myApp) { - // Выполняется при обработке запроса. - // Суперглобальные переменные, php://input и другие данные обновляются для каждого запроса. - echo $myApp->handle($_GET, $_POST, $_COOKIE, $_FILES, $_SERVER); + try { + // Вызывается при получении запроса, + // суперглобальные переменные, php://input и тому подобное сбрасываются + echo $myApp->handle($_GET, $_POST, $_COOKIE, $_FILES, $_SERVER); + } catch (\Throwable $exception) { + // `set_exception_handler` вызывается только при завершении работы скрипта воркера, + // что может быть не тем, что вы ожидаете, поэтому перехватывайте и обрабатывайте исключения здесь + (new \MyCustomExceptionHandler)->handleException($exception); + } }; $maxRequests = (int)($_SERVER['MAX_REQUESTS'] ?? 0); @@ -96,11 +107,11 @@ for ($nbRequests = 0; !$maxRequests || $nbRequests < $maxRequests; ++$nbRequests if (!$keepRunning) break; } -// Завершение +// Очистка $myApp->shutdown(); ``` -Запустите приложение, настроив worker-скрипт с помощью переменной окружения `FRANKENPHP_CONFIG`: +Затем запустите приложение и используйте переменную окружения `FRANKENPHP_CONFIG` для настройки воркера: ```console docker run \ @@ -110,10 +121,8 @@ docker run \ dunglas/frankenphp ``` -## Настройка количества worker-скриптов - -По умолчанию запускается по 2 worker-скрипта на каждый CPU. -Вы можете задать своё значение: +По умолчанию запускается 2 воркера на каждый CPU. +Вы можете также настроить количество запускаемых воркеров: ```console docker run \ @@ -123,37 +132,57 @@ docker run \ dunglas/frankenphp ``` -### Перезапуск worker-скрипта после определённого количества запросов +### Перезапуск воркера после определённого количества запросов + +Поскольку PHP изначально не предназначался для долгоживущих процессов, всё ещё существует множество библиотек и устаревшего кода, которые могут приводить к утечкам памяти. +Обходным решением для использования такого кода в режиме воркера является перезапуск скрипта воркера после обработки определённого количества запросов: -PHP изначально не предназначался для долгоживущих процессов, поэтому некоторые библиотеки и устаревший код могут приводить к утечкам памяти. -Для этого можно настроить автоматический перезапуск worker-скрипта после обработки определённого количества запросов. +Предыдущий фрагмент кода воркера позволяет настроить максимальное количество обрабатываемых запросов, установив переменную окружения с именем `MAX_REQUESTS`. -В предыдущем примере максимальное количество запросов задаётся с помощью переменной окружения `MAX_REQUESTS`. +### Перезапуск воркеров вручную -### Сбои worker-скрипта +Хотя можно перезапускать воркеры [при изменении файлов](config.md#watching-for-file-changes), также возможно корректно перезапустить все воркеры через [административный API Caddy](https://caddyserver.com/docs/api). Если административный интерфейс включен в вашем [Caddyfile](config.md#caddyfile-config), вы можете отправить POST-запрос на эндпоинт перезапуска следующим образом: + +```console +curl -X POST http://localhost:2019/frankenphp/workers/restart +``` -Если worker-скрипт завершится с ненулевым кодом выхода, FrankenPHP перезапустит его с использованием экспоненциальной задержки. -Если worker-скрипт проработает дольше, чем время последней задержки \* 2, он будет считаться стабильным, и задержка сбросится. -Однако, если worker-скрипт продолжает завершаться с ненулевым кодом выхода в течение короткого промежутка времени (например, из-за опечатки в коде), FrankenPHP завершит работу с ошибкой: `too many consecutive failures`. +### Сбои воркеров + +Если скрипт воркера завершается с ненулевым кодом выхода, FrankenPHP перезапустит его с использованием стратегии экспоненциальной задержки. +Если скрипт воркера остаётся активным дольше, чем (последняя задержка * 2), он не будет считаться сбоящим, и задержка сбросится. +Однако, если скрипт воркера продолжает завершаться с ненулевым кодом выхода в течение короткого промежутка времени +(например, из-за опечатки в скрипте), FrankenPHP завершит работу с ошибкой: `too many consecutive failures`. + +Количество последовательных сбоев можно настроить в вашем [Caddyfile](config.md#caddyfile-config) с помощью опции `max_consecutive_failures`: + +```caddyfile +frankenphp { + worker { + # ... + max_consecutive_failures 10 + } +} +``` ## Поведение суперглобальных переменных -[PHP суперглобальные переменные](https://www.php.net/manual/en/language.variables.superglobals.php) (`$_SERVER`, `$_ENV`, `$_GET` и т.д.) ведут себя следующим образом: +[PHP суперглобальные переменные](https://www.php.net/manual/en/language.variables.superglobals.php) (`$_SERVER`, `$_ENV`, `$_GET`...) +ведут себя следующим образом: -- до первого вызова `frankenphp_handle_request()` суперглобальные переменные содержат значения, связанные с самим worker-скриптом -- во время и после вызова `frankenphp_handle_request()` суперглобальные переменные содержат значения, сгенерированные на основе обработанного HTTP-запроса, каждый вызов изменяет значения суперглобальных переменных +- до первого вызова `frankenphp_handle_request()`, суперглобальные переменные содержат значения, связанные с самим скриптом воркера +- во время и после вызова `frankenphp_handle_request()`, суперглобальные переменные содержат значения, сгенерированные на основе обработанного HTTP-запроса, каждый вызов `frankenphp_handle_request()` изменяет значения суперглобальных переменных -Чтобы получить доступ к суперглобальным переменным worker-скрипта внутри колбэка, необходимо скопировать их и импортировать копию в область видимости колбэка: +Чтобы получить доступ к суперглобальным переменным скрипта воркера внутри колбэка, необходимо скопировать их и импортировать копию в область видимости колбэка: ```php > ~/.zshrc ``` @@ -45,16 +59,13 @@ Ardından yapılandırma betiğini çalıştırın: ```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/ ``` -## PHP Derleyin +#### PHP'yi Derleyin Son olarak, PHP'yi derleyin ve kurun: @@ -63,38 +74,60 @@ make -j"$(getconf _NPROCESSORS_ONLN)" sudo make install ``` -## Go Uygulamasını Derleyin +## İsteğe Bağlı Bağımlılıkları Kurun -Artık Go kütüphanesini kullanabilir ve Caddy yapımızı derleyebilirsiniz: +Bazı FrankenPHP özellikleri, kurulması gereken isteğe bağlı sistem bağımlılıklarına sahiptir. +Alternatif olarak, bu özellikler Go derleyicisine derleme etiketleri (build tags) geçirilerek devre dışı bırakılabilir. -```console -curl -L https://github.com/php/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 -``` +| Özellik | Bağımlılık | Devre dışı bırakma derleme etiketi | +| -------------------------------- | ---------------------------------------------------------------------------------------------------------------- | ---------------------------------- | +| Brotli sıkıştırma | [Brotli](https://github.com/google/brotli) | nobrotli | +| Dosya değişikliğinde işçileri yeniden başlatma | [Watcher C](https://github.com/e-dant/watcher/tree/release/watcher-c) | nowatcher | +| [Mercure](mercure.md) | [Mercure Go kütüphanesi](https://pkg.go.dev/github.com/dunglas/mercure) (otomatik olarak kurulur, AGPL lisanslı) | nomercure | + +## Go Uygulamasını Derleyin -### Xcaddy kullanımı +Artık nihai ikili dosyayı oluşturabilirsiniz. -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: +### Xcaddy Kullanarak + +Önerilen yöntem, FrankenPHP'yi derlemek için [xcaddy](https://github.com/caddyserver/xcaddy) kullanmaktır. +`xcaddy` ayrıca [özel Caddy modüllerini](https://caddyserver.com/docs/modules/) ve FrankenPHP uzantılarını kolayca eklemenizi sağlar: ```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 github.com/dunglas/caddy-cbrotli \ --with github.com/dunglas/mercure/caddy \ - --with github.com/dunglas/vulcain/caddy - # Add extra Caddy modules here + --with github.com/dunglas/vulcain/caddy \ + --with github.com/dunglas/caddy-cbrotli + # Buraya ek Caddy modülleri ve FrankenPHP uzantıları ekleyin + # isteğe bağlı olarak, frankenphp kaynaklarınızdan derlemek isterseniz: + # --with github.com/dunglas/frankenphp=$(pwd) \ + # --with github.com/dunglas/frankenphp/caddy=$(pwd)/caddy + ``` > [!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` +> Aksi takdirde, derleme sırasında `PHP Fatal error: Maximum call stack size of 83360 bytes reached during compilation. Try splitting expression` gibi hatalar alabilirsiniz. > -> Bunu yapmak için, `XCADDY_GO_BUILD_FLAGS` ortam değişkenini bu şekilde değiştirin +> Bunu yapmak için, `XCADDY_GO_BUILD_FLAGS` ortam değişkenini şöyle 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). +> (yığın boyutu değerini uygulamanızın ihtiyaçlarına göre değiştirin). + +### Xcaddy Kullanmadan + +Alternatif olarak, FrankenPHP'yi `go` komutunu doğrudan kullanarak `xcaddy` olmadan derlemek mümkündür: + +```console +curl -L https://github.com/php/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 +``` diff --git a/docs/tr/config.md b/docs/tr/config.md index 5d17f6e19a..e5a751eade 100644 --- a/docs/tr/config.md +++ b/docs/tr/config.md @@ -1,42 +1,75 @@ -# Konfigürasyon +# 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. +FrankenPHP, Caddy'nin yanı sıra [Mercure](mercure.md) ve [Vulcain](https://vulcain.rocks) 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. +En yaygın format, basit, insan tarafından okunabilir bir metin formatı olan `Caddyfile`'dır. +Varsayılan olarak, FrankenPHP mevcut dizinde bir `Caddyfile` arar. +Özel bir yolu `-c` veya `--config` seçeneğiyle belirtebilirsiniz. -PHP'nin kendisi [bir `php.ini` dosyası kullanılarak yapılandırılabilir](https://www.php.net/manual/tr/configuration.file.php). +Bir PHP uygulamasını sunmak için en az düzeyde bir `Caddyfile` aşağıda gösterilmiştir: -PHP yorumlayıcısı aşağıdaki konumlarda arama yapacaktır: +```caddyfile +# Yanıt verilecek ana bilgisayar adı +localhost + +# İsteğe bağlı olarak, dosyaların sunulacağı dizin, aksi takdirde mevcut dizine varsayılan olarak ayarlanır +#root public/ +php_server +``` + +Daha fazla özellik sağlayan ve kullanışlı ortam değişkenleri sunan daha gelişmiş bir `Caddyfile`, [FrankenPHP deposunda](https://github.com/php/frankenphp/blob/main/caddy/frankenphp/Caddyfile) ve Docker imajlarıyla birlikte sağlanır. + +PHP'nin kendisi [bir `php.ini` dosyası kullanılarak yapılandırılabilir](https://www.php.net/manual/en/configuration.file.php). + +Kurulum yönteminize bağlı olarak, FrankenPHP ve PHP yorumlayıcısı, yapılandırma dosyalarını aşağıda açıklanan konumlarda arayacaktır. -Docker: +## Docker -- php.ini: `/usr/local/etc/php/php.ini` Varsayılan olarak php.ini sağlanmaz. +FrankenPHP: + +- `/etc/frankenphp/Caddyfile`: ana yapılandırma dosyası +- `/etc/frankenphp/Caddyfile.d/*.caddyfile`: otomatik olarak yüklenen ek yapılandırma dosyaları + +PHP: + +- `php.ini`: `/usr/local/etc/php/php.ini` (varsayılan olarak bir `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-/` +- PHP uzantıları: `/usr/local/lib/php/extensions/no-debug-zts-/` - 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: +# Üretim: RUN cp $PHP_INI_DIR/php.ini-production $PHP_INI_DIR/php.ini + +# Veya geliştirme: +RUN cp $PHP_INI_DIR/php.ini-development $PHP_INI_DIR/php.ini ``` -FrankenPHP kurulumu (.rpm veya .deb): +## RPM ve Debian Paketleri -- 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/` +FrankenPHP: + +- `/etc/frankenphp/Caddyfile`: ana yapılandırma dosyası +- `/etc/frankenphp/Caddyfile.d/*.caddyfile`: otomatik olarak yüklenen ek yapılandırma dosyaları + +PHP: -Statik ikili: +- `php.ini`: `/etc/php-zts/php.ini` (varsayılan olarak üretim ön ayarlarına sahip bir `php.ini` dosyası sağlanır) +- ek yapılandırma dosyaları: `/etc/php-zts/conf.d/*.ini` -- php.ini: `frankenphp run` veya `frankenphp php-server` komutunun çalıştırıldığı dizin, ardından `/etc/frankenphp/php.ini` +## Statik İkili + +FrankenPHP: + +- Mevcut çalışma dizininde: `Caddyfile` + +PHP: + +- `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 uzantıları: yüklenemez, bunları ikili dosyanın içine paketleyin - [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 @@ -54,17 +87,22 @@ localhost { } ``` -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. +FrankenPHP'yi [global seçenek](https://caddyserver.com/docs/caddyfile/concepts#global-options) `frankenphp` kullanarak açıkça yapılandırabilirsiniz: ```caddyfile { frankenphp { - num_threads # Başlatılacak PHP iş parçacığı sayısını ayarlar. Varsayılan: Mevcut CPU çekirdek sayısının 2 katı. + num_threads # Başlatılacak PHP iş parçacığı sayısını ayarlar. Varsayılan: Mevcut CPU sayısının 2 katı. + max_threads # Çalışma zamanında başlatılabilecek ek PHP iş parçacığı sayısını sınırlar. Varsayılan: num_threads. 'auto' olarak ayarlanabilir. + max_wait_time # Bir isteğin, boş bir PHP iş parçacığı bekleyebileceği maksimum süreyi ayarlar. Varsayılan: devre dışı. + php_ini # Bir php.ini yönergesini ayarlar. Birden fazla yönerge ayarlamak için birkaç kez kullanılabilir. worker { file # Çalışan komut dosyasının yolunu ayarlar. - 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. + num # Başlatılacak PHP iş parçacığı sayısını ayarlar, varsayılan olarak mevcut CPU sayısının 2 katıdır. env # Ek bir ortam değişkenini verilen değere ayarlar. Birden fazla ortam değişkeni için birden fazla kez belirtilebilir. + watch # Dosya değişikliklerini izlemek için yolu ayarlar. Birden fazla yol için birden fazla kez belirtilebilir. + name # İşçinin adını ayarlar, loglarda ve metriklerde kullanılır. Varsayılan: işçi dosyasının mutlak yolu + max_consecutive_failures # İşçinin sağlıksız kabul edilmeden önce izin verilen maksimum ardışık hata sayısını ayarlar, -1 işçinin her zaman yeniden başlayacağı anlamına gelir. Varsayılan: 6. } } } @@ -88,7 +126,7 @@ Aynı sunucuda birden fazla uygulamaya hizmet veriyorsanız birden fazla işçi ```caddyfile app.example.com { - root /path/to/app/public + root /path/to/app/public php_server { root /path/to/app/public # daha iyi önbelleğe almayı sağlar worker index.php @@ -96,7 +134,7 @@ app.example.com { } other.example.com { - root /path/to/other/public + root /path/to/other/public php_server { root /path/to/other/public worker index.php @@ -107,19 +145,20 @@ other.example.com { ``` 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: +ancak tam kontrole ihtiyacınız varsa, daha düşük seviyeli `php` yönergesini kullanabilirsiniz. +`php` yönergesi, önce bir PHP dosyası olup olmadığını kontrol etmek yerine tüm girdiyi PHP'ye iletir. Daha fazla bilgiyi [performans sayfasında](performance.md#try_files) okuyun. -php_server` yönergesini kullanmak bu yapılandırmay ile aynıdır: +`php_server` yönergesini kullanmak bu yapılandırma ile aynıdır: ```caddyfile route { - # Dizin istekleri için sondaki eğik çizgiyi, diğer adıyla taksim işaretini ekleyin + # Dizin istekleri için sondaki eğik çizgiyi ekle @canonicalPath { file {path}/index.php not path */ } redir @canonicalPath {path}/ 308 - # İstenen dosya mevcut değilse, dizin dosyalarını deneyin + # İstenen dosya mevcut değilse, dizin dosyalarını dene @indexFiles file { try_files {path} {path}/index.php index.php split_path .php @@ -132,44 +171,165 @@ route { } ``` -php_server`ve`php` yönergeleri aşağıdaki seçeneklere sahiptir: +`php_server` ve `php` yönergeleri aşağıdaki seçeneklere sahiptir: ```caddyfile php_server [] { - root # Sitenin kök klasörünü ayarlar. Öntanımlı: `root` yönergesi. + root # Sitenin kök klasörünü ayarlar. Varsayılan: `root` yönergesi. split_path # 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 # 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 # Worker betiğinin yolunu ayarlar, php_server köküne göre göreceli olabilir - 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 + num # Başlatılacak PHP iş parçacığı sayısını ayarlar, varsayılan olarak mevcut CPU sayısının 2 katıdır 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 # Dosya değişikliklerini izlemek için yolu ayarlar. Birden fazla yol için birden fazla kez belirtilebilir. env # 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. + match # İşçiyi bir yol desenine eşleştirir. try_files'ı geçersiz kılar ve yalnızca php_server yönergesinde kullanılabilir. } worker # Global frankenphp bloğundaki gibi kısa formu da kullanabilirsiniz. } ``` +### Dosya Değişikliklerini İzleme + +İşçiler uygulamanızı yalnızca bir kez başlattığı ve bellekte tuttuğu için, PHP dosyalarınızdaki herhangi bir değişiklik hemen yansımaz. + +Bunun yerine işçiler, `watch` yönergesi aracılığıyla dosya değişikliklerinde yeniden başlatılabilir. +Bu, geliştirme ortamları için kullanışlıdır. + +```caddyfile +{ + frankenphp { + worker { + file /path/to/app/public/worker.php + watch + } + } +} +``` + +Bu özellik genellikle [hot reload](hot-reload.md) ile birlikte kullanılır. + +`watch` dizini belirtilmezse, FrankenPHP sürecinin başlatıldığı dizindeki ve alt dizinlerindeki tüm `.env`, `.php`, `.twig`, `.yaml` ve `.yml` dosyalarını izleyen `./**/*.{env,php,twig,yaml,yml}` değerine geri döner. Bunun yerine, bir [kabuk dosya adı deseni](https://pkg.go.dev/path/filepath#Match) aracılığıyla bir veya daha fazla dizin de belirtebilirsiniz: + +```caddyfile +{ + frankenphp { + worker { + file /path/to/app/public/worker.php + watch /path/to/app # /path/to/app altındaki tüm dizinlerdeki tüm dosyaları izler + watch /path/to/app/*.php # /path/to/app içindeki .php ile biten dosyaları izler + watch /path/to/app/**/*.php # /path/to/app ve alt dizinlerdeki PHP dosyalarını izler + watch /path/to/app/**/*.{php,twig} # /path/to/app ve alt dizinlerdeki PHP ve Twig dosyalarını izler + } + } +} +``` + +- `**` deseni, özyinelemeli izlemeyi ifade eder +- Dizinler göreceli de olabilir (FrankenPHP sürecinin başlatıldığı yere göre) +- Birden fazla işçi tanımladıysanız, bir dosya değiştiğinde hepsi yeniden başlatılacaktır +- Çalışma zamanında oluşturulan dosyaları (loglar gibi) izlemeye dikkat edin, çünkü bunlar istenmeyen işçi yeniden başlatmalarına neden olabilir. + +Dosya izleyici [e-dant/watcher](https://github.com/e-dant/watcher) üzerine kuruludur. + +## İşçiyi Bir Yola Eşleştirme + +Geleneksel PHP uygulamalarında, betikler her zaman public dizinine yerleştirilir. +Bu, diğer PHP betikleri gibi ele alınan işçi betikleri için de geçerlidir. +İşçi betiğini public dizininin dışına yerleştirmek isterseniz, bunu `match` yönergesi aracılığıyla yapabilirsiniz. + +`match` yönergesi, yalnızca `php_server` ve `php` içinde kullanılabilen, `try_files`'a optimize edilmiş bir alternatiftir. +Aşağıdaki örnek, mevcutsa her zaman public dizinindeki bir dosyayı sunacak +ve aksi takdirde isteği yol desenine uyan işçiye iletecektir. + +```caddyfile +{ + frankenphp { + php_server { + worker { + file /path/to/worker.php # dosya public yolunun dışında olabilir + match /api/* # /api/ ile başlayan tüm istekler bu işçi tarafından ele alınacaktır + } + } + } +} +``` + ## 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 +- `SERVER_NAME`: [dinlenecek adresleri](https://caddyserver.com/docs/caddyfile/concepts#addresses) değiştirir, sağlanan ana bilgisayar adları oluşturulan TLS sertifikası için de kullanılacaktır +- `SERVER_ROOT`: sitenin kök dizinini değiştirir, varsayılan olarak `public/` +- `CADDY_GLOBAL_OPTIONS`: [global seçenekleri](https://caddyserver.com/docs/caddyfile/options) entegre eder +- `FRANKENPHP_CONFIG`: `frankenphp` yönergesi altına yapılandırma entegre eder 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. +[`variables_order` PHP yönergesinin](https://www.php.net/manual/en/ini.core.php#ini.variables-order) `S` değeri, `E`'nin bu yönergedeki diğer yerleşiminden 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 +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. +PHP yapılandırmasını `Caddyfile` içindeki `php_ini` yönergesini kullanarak da değiştirebilirsiniz: + +```caddyfile +{ + frankenphp { + php_ini memory_limit 256M + + # veya + + php_ini { + memory_limit 256M + max_execution_time 15 + } + } +} +``` + +### HTTPS'i Devre Dışı Bırakma + +Varsayılan olarak, FrankenPHP `localhost` dahil tüm ana bilgisayar adları için otomatik olarak HTTPS'i etkinleştirir. +HTTPS'i devre dışı bırakmak isterseniz (örneğin bir geliştirme ortamında), `SERVER_NAME` ortam değişkenini `http://` veya `:80` olarak ayarlayabilirsiniz: + +Alternatif olarak, [Caddy belgelerinde](https://caddyserver.com/docs/automatic-https#activation) açıklanan diğer tüm yöntemleri kullanabilirsiniz. + +`localhost` ana bilgisayar adı yerine `127.0.0.1` IP adresiyle HTTPS kullanmak isterseniz, lütfen [bilinen sorunlar](known-issues.md#using-https127001-with-docker) bölümünü okuyun. + +### Tam Çift Yönlü (HTTP/1) + +HTTP/1.x kullanırken, yanıtın tamamı okunmadan önce bir yanıt yazılmasına izin vermek için tam çift yönlü modu etkinleştirmek istenebilir. (örneğin: [Mercure](mercure.md), WebSocket, Server-Sent Events vb.) + +Bu, `Caddyfile`'daki global seçeneklere eklenmesi gereken isteğe bağlı bir yapılandırmadır: + +```caddyfile +{ + servers { + enable_full_duplex + } +} +``` + +> [!UYARI] +> +> Bu seçeneği etkinleştirmek, tam çift yönlü desteği olmayan eski HTTP/1.x istemcilerinin kilitlenmesine neden olabilir. +> Bu ayrıca `CADDY_GLOBAL_OPTIONS` ortam yapılandırması kullanılarak da yapılandırılabilir: + +```sh +CADDY_GLOBAL_OPTIONS="servers { + enable_full_duplex +}" +``` + +Bu ayar hakkında daha fazla bilgiyi [Caddy belgelerinde](https://caddyserver.com/docs/caddyfile/options#enable-full-duplex) bulabilirsiniz. + ## 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: @@ -179,4 +339,4 @@ docker run -v $PWD:/app/public \ -e CADDY_GLOBAL_OPTIONS=debug \ -p 80:80 -p 443:443 -p 443:443/udp \ dunglas/frankenphp -``` +``` \ No newline at end of file diff --git a/docs/tr/docker.md b/docs/tr/docker.md index e1c3d6c2d4..31f07719e3 100644 --- a/docs/tr/docker.md +++ b/docs/tr/docker.md @@ -1,8 +1,17 @@ # Ö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. +[FrankenPHP Docker imajları](https://hub.docker.com/r/dunglas/frankenphp), [resmi PHP imajları](https://hub.docker.com/_/php/) temel alınarak hazırlanmıştır. +Popüler mimariler için Debian ve Alpine Linux varyantları sağlanmıştır. +Debian varyantları tavsiye edilir. -PHP 8.2, 8.3, 8.4 ve 8.5 için varyantlar sağlanmıştır. [Etiketlere göz atın](https://hub.docker.com/r/dunglas/frankenphp/tags). +PHP 8.2, 8.3, 8.4 ve 8.5 için varyantlar sağlanmıştır. + +Etiketler şu deseni takip eder: `dunglas/frankenphp:-php-` + +- `` ve `` sırasıyla FrankenPHP ve PHP'nin, ana (örn. `1`), ikincil (örn. `1.2`) ila yama sürümlerine (örn. `1.2.3`) kadar değişen sürüm numaralarıdır. +- `` ise `trixie` (Debian Trixie için), `bookworm` (Debian Bookworm için) veya `alpine` (Alpine'ın en son kararlı sürümü için) olabilir. + +[Etiketlere göz atın](https://hub.docker.com/r/dunglas/frankenphp/tags). ## İmajlar Nasıl Kullanılır @@ -21,15 +30,19 @@ docker build -t my-php-app . docker run -it --rm --name my-running-app my-php-app ``` +## Yapılandırma Nasıl Ayarlanır + +Kolaylık sağlaması açısından, faydalı ortam değişkenleri içeren [varsayılan bir `Caddyfile`](https://github.com/php/frankenphp/blob/main/caddy/frankenphp/Caddyfile) imajda sağlanmıştır. + ## 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: +[`docker-php-extension-installer`](https://github.com/mlocati/docker-php-extension-installer) betiği temel imajda sağlanmıştır. +Ek PHP eklentileri eklemek basittir: ```dockerfile FROM dunglas/frankenphp -# buraya istenilen eklentileri ekleyin: +# buraya ek eklentileri ekleyin: RUN install-php-extensions \ pdo_mysql \ gd \ @@ -47,10 +60,10 @@ FrankenPHP, Caddy'nin üzerine inşa edilmiştir ve tüm [Caddy modülleri](http ```dockerfile FROM dunglas/frankenphp:builder AS builder -# xcaddy'yi derleyen imaja kopyalayın +# xcaddy'yi oluşturucu imajına kopyalayın COPY --from=caddy:builder /usr/bin/xcaddy /usr/bin/xcaddy -# FrankenPHP oluşturmak için CGO etkinleştirilmelidir +# FrankenPHP'yi derlemek için CGO etkinleştirilmelidir RUN CGO_ENABLED=1 \ XCADDY_SETCAP=1 \ XCADDY_GO_BUILD_FLAGS="-ldflags='-w -s' -tags=nobadger,nomysql,nopgx" \ @@ -61,24 +74,24 @@ RUN CGO_ENABLED=1 \ --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 + # Mercure ve Vulcain resmi derlemeye 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 +# Resmi ikiliyi, özel modüllerinizi içeren ile 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. +[Builder 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 yığın boyutunu artırmanız](compile.md#using-xcaddy) gerekebilir. ## Varsayılan Olarak Worker Modunun Etkinleştirilmesi @@ -92,9 +105,9 @@ FROM dunglas/frankenphp ENV FRANKENPHP_CONFIG="worker ./public/index.php" ``` -## Geliştirme Sürecinde Yığın (Volume) Kullanma +## Geliştirme Sürecinde Birim (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: +FrankenPHP ile kolayca geliştirme yapmak için, uygulamanın kaynak kodunu içeren dizini ana bilgisayarınızdan Docker konteynerine bir birim (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 @@ -127,7 +140,7 @@ services: # 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) +# Caddy sertifikaları ve yapılandırması için gereken birimler (volumes) volumes: caddy_data: caddy_config: @@ -147,20 +160,45 @@ 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 + # 80 ve 443 numaralı bağlantı noktalarına bağlanmak için ek yetenek 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; + # /config/caddy ve /data/caddy dizinlerine yazma erişimi verin + chown -R ${USER}:${USER} /config/caddy /data/caddy USER ${USER} ``` +### Yetenekler Olmadan Çalıştırma + +Root yetkisi olmadan çalıştırıldığında bile, FrankenPHP web sunucusunu ayrıcalıklı bağlantı noktalarına (80 ve 443) bağlamak için `CAP_NET_BIND_SERVICE` yeteneğine ihtiyaç duyar. + +FrankenPHP'yi ayrıcalıklı olmayan bir bağlantı noktasında (1024 ve üzeri) ifşa ederseniz, web sunucusunu root olmayan bir kullanıcı olarak ve herhangi bir yeteneğe ihtiyaç duymadan çalıştırmak mümkündür: + +```dockerfile +FROM dunglas/frankenphp + +ARG USER=appuser + +RUN \ + # Alpine tabanlı dağıtımlar için "adduser -D ${USER}" kullanın + useradd ${USER}; \ + # Varsayılan yeteneği kaldırın + setcap -r /usr/local/bin/frankenphp; \ + # /config/caddy ve /data/caddy dizinlerine yazma erişimi verin + chown -R ${USER}:${USER} /config/caddy /data/caddy + +USER ${USER} +``` + +Ardından, ayrıcalıklı olmayan bir bağlantı noktası kullanmak için `SERVER_NAME` ortam değişkenini ayarlayın. +Örnek: `:8000` + ## 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 +- Her gün UTC ile saat 4'te, resmi PHP imajlarının yeni sürümleri mevcutsa ## Geliştirme Sürümleri @@ -168,4 +206,4 @@ Geliştirme sürümleri [`dunglas/frankenphp-dev`](https://hub.docker.com/reposi 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-` biçimindeki etiketler de kullanılabilir. +``sha-`` biçimindeki etiketler de mevcuttur. diff --git a/docs/tr/early-hints.md b/docs/tr/early-hints.md index c425df4fe5..6d4d9e9f8a 100644 --- a/docs/tr/early-hints.md +++ b/docs/tr/early-hints.md @@ -1,7 +1,7 @@ # 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. +Early Hints kullanmak web sayfalarınızın yüklenme süresini %30 oranında azaltabilir. ```php .env.local echo APP_DEBUG=0 >> .env.local -# Testleri kaldırın +# Testleri ve diğer gereksiz dosyaları yer kazanmak için kaldırın +# Alternatif olarak, bu dosyaları .gitattributes dosyanızdaki export-ignore niteliğiyle ekleyin rm -Rf tests/ # Bağımlılıkları yükleyin @@ -39,9 +42,13 @@ composer install --ignore-platform-reqs --no-dev -a composer dump-env prod ``` -## Linux Binary'si Oluşturma +### Yapılandırmayı Özelleştirme + +[Yapılandırmayı](config.md) özelleştirmek için, gömülecek uygulamanın ana dizinine (önceki örnekte `$TMPDIR/my-prepared-app`) bir `Caddyfile` ve bir `php.ini` dosyası yerleştirebilirsiniz. + +## Linux Çalıştırılabilir Dosyası Oluşturma -Bir Linux binary çıktısı almanın en kolay yolu, sağladığımız Docker tabanlı derleyiciyi kullanmaktır. +Bir Linux çalıştırılabilir dosyası oluşturmanı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: @@ -53,10 +60,9 @@ Bir Linux binary çıktısı almanın en kolay yolu, sağladığımız Docker ta WORKDIR /go/src/app/dist/app COPY . . - # Statik binary dosyasını oluşturun, yalnızca istediğiniz PHP eklentilerini seçtiğinizden emin olun + # Statik çalıştırılabilir dosyayı oluşturun WORKDIR /go/src/app/ - RUN EMBED=dist/app/ \ - ./build-static.sh + RUN EMBED=dist/app/ ./build-static.sh ``` > [!CAUTION] @@ -70,28 +76,27 @@ Bir Linux binary çıktısı almanın en kolay yolu, sağladığımız Docker ta docker build -t static-app -f static-build.Dockerfile . ``` -3. Binary dosyasını çıkarın: +3. Çalıştırılabilir dosyayı çı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. +Elde edilen çalıştırılabilir dosya, geçerli dizindeki `my-app` adlı dosyadır. -## Diğer İşletim Sistemleri için Binary Çıktısı Alma +## Diğer İşletim Sistemleri için Çalıştırılabilir Dosya Oluşturma -Docker kullanmak istemiyorsanız veya bir macOS binary dosyası oluşturmak istiyorsanız, sağladığımız kabuk betiğini kullanın: +Docker kullanmak istemiyorsanız veya bir macOS çalıştırılabilir dosyası oluşturmak istiyorsanız, sağladığımız kabuk betiğini kullanın: ```console git clone https://github.com/php/frankenphp cd frankenphp -EMBED=/path/to/your/app \ - ./build-static.sh +EMBED=/path/to/your/app ./build-static.sh ``` -Elde edilen binary dosyası `dist/` dizinindeki `frankenphp--` adlı dosyadır. +Elde edilen çalıştırılabilir dosya `dist/` dizinindeki `frankenphp--` adlı dosyadır. -## Binary Dosyasını Kullanma +## Çalıştırılabilir Dosyayı Kullanma İşte bu kadar! `my-app` dosyası (veya diğer işletim sistemlerinde `dist/frankenphp--`) bağımsız uygulamanızı içerir! @@ -113,17 +118,23 @@ HTTPS (Let's Encrypt sertifikası otomatik olarak oluşturulur), HTTP/2 ve HTTP/ ./my-app php-server --domain localhost ``` -Ayrıca binary dosyanıza gömülü PHP CLI betiklerini de çalıştırabilirsiniz: +Ayrıca çalıştırılabilir dosyanıza gömülü PHP CLI betiklerini de çalıştırabilirsiniz: ```console ./my-app php-cli bin/console ``` +## PHP Uzantıları + +Varsayılan olarak, betik projenizin `composer.json` dosyası tarafından (varsa) gerekli olan uzantıları derleyecektir. Eğer `composer.json` dosyası yoksa, [statik derlemeler girdisinde](static.md) belgelendiği gibi varsayılan uzantılar derlenir. + +Uzantıları özelleştirmek için `PHP_EXTENSIONS` ortam değişkenini kullanın. + ## 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). +Çalıştırılabilir dosyanın nasıl özelleştirileceğini (uzantılar, PHP sürümü...) görmek için [statik derleme dokümantasyonunu okuyun](static.md). -## Binary Dosyasının Dağıtılması +## Çalıştırılabilir Dosyanın Dağıtılması Linux'ta, oluşturulan ikili dosya [UPX](https://upx.github.io) kullanılarak sıkıştırılır. diff --git a/docs/tr/extensions.md b/docs/tr/extensions.md new file mode 100644 index 0000000000..b9ed7fcda3 --- /dev/null +++ b/docs/tr/extensions.md @@ -0,0 +1,893 @@ +# Go ile PHP Eklentileri Yazma + +FrankenPHP ile **Go dilinde PHP eklentileri yazabilir**, böylece doğrudan PHP'den çağrılabilen **yüksek performanslı yerel fonksiyonlar** oluşturabilirsiniz. Uygulamalarınız mevcut veya yeni herhangi bir Go kütüphanesini ve ayrıca PHP kodunuzdan doğrudan **gorutinlerin** meşhur eşzamanlılık modelini kullanabilir. + +PHP eklentileri genellikle C ile yazılır, ancak biraz ek çabayla başka dillerde de yazmak mümkündür. PHP eklentileri, PHP'nin işlevselliğini genişletmek için düşük seviyeli dillerin gücünden yararlanmanıza olanak tanır, örneğin yerel fonksiyonlar ekleyerek veya belirli işlemleri optimize ederek. + +Caddy modülleri sayesinde, Go dilinde PHP eklentileri yazabilir ve bunları FrankenPHP'ye çok hızlı bir şekilde entegre edebilirsiniz. + +## İki Yaklaşım + +FrankenPHP, Go dilinde PHP eklentileri oluşturmanın iki yolunu sunar: + +1. **Eklenti Üreticisini Kullanma** - Çoğu kullanım durumu için gerekli tüm temel yapıyı üreten, Go kodunuzu yazmaya odaklanmanızı sağlayan önerilen yaklaşım +2. **Manuel Uygulama** - Gelişmiş kullanım durumları için eklenti yapısı üzerinde tam kontrol + +Başlamak için en kolay yol olduğu için üretici yaklaşımıyla başlayacak, ardından tam kontrole ihtiyaç duyanlar için manuel uygulamayı göstereceğiz. + +## Eklenti Üreticisini Kullanma + +FrankenPHP, yalnızca Go kullanarak **bir PHP eklentisi oluşturmanıza** olanak tanıyan bir araçla birlikte gelir. **C kodu yazmaya** veya doğrudan CGO kullanmaya gerek yok: FrankenPHP ayrıca **PHP/C ve Go arasındaki tür dengelemesi (type juggling)** konusunda endişelenmenize gerek kalmadan uzantılarınızı Go'da yazmanıza yardımcı olacak bir **genel türler API'si** içerir. + +> [!TIP] +> Eklentilerin Go dilinde baştan sona nasıl yazılabileceğini anlamak istiyorsanız, aşağıda üretici kullanmadan Go dilinde bir PHP eklentisi yazmayı gösteren manuel uygulama bölümünü okuyabilirsiniz. + +Bu aracın **tam teşekküllü bir eklenti üreticisi olmadığını** unutmayın. Amacı, Go'da basit eklentiler yazmanıza yardımcı olmaktır, ancak PHP eklentilerinin en gelişmiş özelliklerini sağlamaz. Daha **karmaşık ve optimize edilmiş** bir eklenti yazmanız gerekiyorsa, doğrudan C kodu yazmanız veya CGO kullanmanız gerekebilir. + +### Önkoşullar + +Aşağıdaki manuel uygulama bölümünde de belirtildiği gibi, [PHP kaynaklarını edinmeniz](https://www.php.net/downloads.php) ve yeni bir Go modülü oluşturmanız gerekir. + +#### Yeni Bir Modül Oluşturun ve PHP Kaynaklarını Edinin + +Go'da bir PHP eklentisi yazmanın ilk adımı yeni bir Go modülü oluşturmaktır. Bunun için şu komutu kullanabilirsiniz: + +```console +go mod init example.com/example +``` + +İkinci adım, sonraki adımlar için [PHP kaynaklarını edinmektir](https://www.php.net/downloads.php). Kaynakları edindikten sonra, Go modülünüzün içine değil, istediğiniz bir dizine açın: + +```console +tar xf php-* +``` + +### Eklentiyi Yazma + +Yerel fonksiyonunuzu Go'da yazmak için her şey hazır. `stringext.go` adında yeni bir dosya oluşturun. İlk fonksiyonumuz argüman olarak bir dize, onu tekrarlama sayısı, dizenin ters çevrilip çevrilmeyeceğini belirten bir boole değeri alacak ve sonuç dizesini döndürecektir. Şöyle görünmelidir: + +```go +package example + +// #include +import "C" +import ( + "strings" + "unsafe" + + "github.com/dunglas/frankenphp" +) + +//export_php:function repeat_this(string $str, int $count, bool $reverse): string +func repeat_this(s *C.zend_string, count int64, reverse bool) unsafe.Pointer { + str := frankenphp.GoString(unsafe.Pointer(s)) + + result := strings.Repeat(str, int(count)) + if reverse { + runes := []rune(result) + for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 { + runes[i], runes[j] = runes[j], runes[i] + } + result = string(runes) + } + + return frankenphp.PHPString(result, false) +} +``` + +Burada dikkat edilmesi gereken iki önemli nokta var: + +- `//export_php:function` yönerge yorumu, PHP'deki fonksiyon imzasını tanımlar. Üretici, PHP fonksiyonunu doğru parametreler ve dönüş tipiyle nasıl üreteceğini bu şekilde bilir; +- Fonksiyon bir `unsafe.Pointer` döndürmelidir. FrankenPHP, C ve Go arasındaki tür dengelemesi konusunda size yardımcı olacak bir API sağlar. + +İlk nokta kendi kendini açıklasa da, ikincisi kavranması daha zor olabilir. Bir sonraki bölümde tür dengelemesine daha derinlemesine bakalım. + +### Tür Dengeleme (Type Juggling) + +Bazı değişken türlerinin C/PHP ve Go arasında aynı bellek temsiline sahip olmasına rağmen, bazı türlerin doğrudan kullanılabilmesi için daha fazla mantık gereklidir. Bu, eklenti yazarken belki de en zor kısımdır çünkü Zend Motoru'nun iç işleyişini ve değişkenlerin PHP'de dahili olarak nasıl saklandığını anlamayı gerektirir. +Bu tablo bilmeniz gerekenleri özetlemektedir: + +| PHP type | Go type | Direct conversion | C to Go helper | Go to C helper | Class Methods Support | +| :----------------- | :---------------------------- | :---------------- | :-------------------------------- | :--------------------------------- | :-------------------- | +| `int` | `int64` | ✅ | - | - | ✅ | +| `?int` | `*int64` | ✅ | - | - | ✅ | +| `float` | `float64` | ✅ | - | - | ✅ | +| `?float` | `*float64` | ✅ | - | - | ✅ | +| `bool` | `bool` | ✅ | - | - | ✅ | +| `?bool` | `*bool` | ✅ | - | - | ✅ | +| `string`/`?string` | `*C.zend_string` | ❌ | `frankenphp.GoString()` | `frankenphp.PHPString()` | ✅ | +| `array` | `frankenphp.AssociativeArray` | ❌ | `frankenphp.GoAssociativeArray()` | `frankenphp.PHPAssociativeArray()` | ✅ | +| `array` | `map[string]any` | ❌ | `frankenphp.GoMap()` | `frankenphp.PHPMap()` | ✅ | +| `array` | `[]any` | ❌ | `frankenphp.GoPackedArray()` | `frankenphp.PHPPackedArray()` | ✅ | +| `mixed` | `any` | ❌ | `GoValue()` | `PHPValue()` | ❌ | +| `callable` | `*C.zval` | ❌ | - | `frankenphp.CallPHPCallable()` | ❌ | +| `object` | `struct` | ❌ | _Henüz uygulanmadı_ | _Henüz uygulanmadı_ | ❌ | + +> [!NOTE] +> +> Bu tablo henüz kapsamlı değildir ve FrankenPHP türler API'si daha da tamamlandıkça güncellenecektir. +> +> Sınıf metotları için özel olarak, şu anda ilkel türler ve diziler desteklenmektedir. Nesneler henüz metot parametresi veya dönüş türü olarak kullanılamaz. + +Önceki bölümdeki kod parçacığına bakarsanız, ilk parametreyi ve dönüş değerini dönüştürmek için yardımcıların kullanıldığını görebilirsiniz. `repeat_this()` fonksiyonumuzun ikinci ve üçüncü parametrelerinin dönüştürülmesine gerek yoktur, çünkü temel türlerin bellek gösterimi hem C hem de Go için aynıdır. + +#### Dizilerle Çalışma + +FrankenPHP, `frankenphp.AssociativeArray` aracılığıyla veya doğrudan bir haritaya (map) veya dilime (slice) dönüştürerek PHP dizileri için yerel destek sağlar. + +`AssociativeArray`, bir `Map: map[string]any` alanı ve isteğe bağlı bir `Order: []string` alanı içeren bir [hash haritasını](https://en.wikipedia.org/wiki/Hash_table) temsil eder (PHP'nin "ilişkisel dizilerinin" aksine, Go haritaları sıralı değildir). + +Sıra veya ilişkilendirme gerekmiyorsa, doğrudan bir dilime `[]any` veya sırasız bir haritaya `map[string]any` dönüştürmek de mümkündür. + +**Go'da dizileri oluşturma ve işleme:** + +```go +package example + +// #include +import "C" +import ( + "unsafe" + + "github.com/dunglas/frankenphp" +) + +// export_php:function process_data_ordered(array $input): array +func process_data_ordered_map(arr *C.zend_array) unsafe.Pointer { + // Convert PHP associative array to Go while keeping the order + associativeArray, err := frankenphp.GoAssociativeArray[any](unsafe.Pointer(arr)) + if err != nil { + // handle error + } + + // loop over the entries in order + for _, key := range associativeArray.Order { + value, _ = associativeArray.Map[key] + // do something with key and value + } + + // return an ordered array + // if 'Order' is not empty, only the key-value pairs in 'Order' will be respected + return frankenphp.PHPAssociativeArray[string](frankenphp.AssociativeArray[string]{ + Map: map[string]string{ + "key1": "value1", + "key2": "value2", + }, + Order: []string{"key1", "key2"}, + }) +} + +// export_php:function process_data_unordered(array $input): array +func process_data_unordered_map(arr *C.zend_array) unsafe.Pointer { + // Convert PHP associative array to a Go map without keeping the order + // ignoring the order will be more performant + goMap, err := frankenphp.GoMap[any](unsafe.Pointer(arr)) + if err != nil { + // handle error + } + + // loop over the entries in no specific order + for key, value := range goMap { + // do something with key and value + } + + // return an unordered array + return frankenphp.PHPMap(map[string]string { + "key1": "value1", + "key2": "value2", + }) +} + +// export_php:function process_data_packed(array $input): array +func process_data_packed(arr *C.zend_array) unsafe.Pointer { + // Convert PHP packed array to Go + goSlice, err := frankenphp.GoPackedArray(unsafe.Pointer(arr)) + if err != nil { + // handle error + } + + // loop over the slice in order + for index, value := range goSlice { + // do something with index and value + } + + // return a packed array + return frankenphp.PHPPackedArray([]string{"value1", "value2", "value3"}) +} +``` + +**Dizi dönüşümünün temel özellikleri:** + +- **Sıralı anahtar-değer çiftleri** - İlişkisel dizinin sırasını koruma seçeneği +- **Birden fazla durum için optimize edildi** - Daha iyi performans için sırayı bırakma veya doğrudan bir dilime dönüştürme seçeneği +- **Otomatik liste tespiti** - PHP'ye dönüştürürken, dizinin sıkıştırılmış bir liste mi yoksa hash haritası mı olması gerektiğini otomatik olarak algılar +- **İç İçe Diziler** - Diziler iç içe olabilir ve tüm desteklenen türleri otomatik olarak dönüştürür (`int64`,`float64`,`string`,`bool`,`nil`,`AssociativeArray`,`map[string]any`,`[]any`) +- **Nesneler desteklenmiyor** - Şu anda yalnızca skaler türler ve diziler değer olarak kullanılabilir. Bir nesne sağlamak, PHP dizisinde `null` bir değerle sonuçlanacaktır. + +##### Mevcut metodlar: Packed ve İlişkisel + +- `frankenphp.PHPAssociativeArray(arr frankenphp.AssociativeArray) unsafe.Pointer` - Anahtar-değer çiftleriyle sıralı bir PHP dizisine dönüştürür +- `frankenphp.PHPMap(arr map[string]any) unsafe.Pointer` - Bir haritayı anahtar-değer çiftleriyle sırasız bir PHP dizisine dönüştürür +- `frankenphp.PHPPackedArray(slice []any) unsafe.Pointer` - Bir dilimi yalnızca indekslenmiş değerlere sahip bir PHP packed dizisine dönüştürür +- `frankenphp.GoAssociativeArray(arr unsafe.Pointer, ordered bool) frankenphp.AssociativeArray` - Bir PHP dizisini sıralı bir Go `AssociativeArray`'e (sıralı harita) dönüştürür +- `frankenphp.GoMap(arr unsafe.Pointer) map[string]any` - Bir PHP dizisini sırasız bir Go haritasına dönüştürür +- `frankenphp.GoPackedArray(arr unsafe.Pointer) []any` - Bir PHP dizisini bir Go dilimine dönüştürür +- `frankenphp.IsPacked(zval *C.zend_array) bool` - Bir PHP dizisinin packed (yalnızca indekslenmiş) mi yoksa ilişkisel (anahtar-değer çiftleri) mi olduğunu kontrol eder + +### Çağrılabilirler (Callables) ile Çalışma + +FrankenPHP, `frankenphp.CallPHPCallable` yardımcısı kullanarak PHP çağrılabilirleriyle çalışma olanağı sağlar. Bu, Go kodundan PHP fonksiyonlarını veya metotlarını çağırmanıza olanak tanır. + +Bunu göstermek için, çağrılabilir ve bir dizi alan, çağrılabilir'i dizinin her elemanına uygulayan ve sonuçlarla yeni bir dizi döndüren kendi `array_map()` fonksiyonumuzu oluşturalım: + +```go +// export_php:function my_array_map(array $data, callable $callback): array +func my_array_map(arr *C.zend_array, callback *C.zval) unsafe.Pointer { + goSlice, err := frankenphp.GoPackedArray[any](unsafe.Pointer(arr)) + if err != nil { + panic(err) + } + + result := make([]any, len(goSlice)) + + for index, value := range goSlice { + result[index] = frankenphp.CallPHPCallable(unsafe.Pointer(callback), []interface{}{value}) + } + + return frankenphp.PHPPackedArray(result) +} +``` + +PHP'den parametre olarak geçirilen çağrılabilir'i çağırmak için `frankenphp.CallPHPCallable()`'ı nasıl kullandığımıza dikkat edin. Bu fonksiyon, çağrılabilir'e bir işaretçi ve bir argüman dizisi alır ve çağrılabilir'in yürütme sonucunu döndürür. Alıştığınız çağrılabilir sözdizimini kullanabilirsiniz: + +```php +name` çalışmaz) +- **Yalnızca metot arayüzü** - Tüm etkileşimler, tanımladığınız metotlar aracılığıyla gerçekleşmelidir +- **Daha iyi kapsülleme** - Dahili veri yapısı tamamen Go kodu tarafından kontrol edilir +- **Tür güvenliği** - PHP kodunun yanlış türlerle dahili durumu bozma riski yok +- **Daha temiz API** - Uygun bir genel arayüz tasarlamaya zorlar + +Bu yaklaşım, daha iyi kapsülleme sağlar ve PHP kodunun Go nesnelerinizin dahili durumunu yanlışlıkla bozmasını önler. Nesneyle olan tüm etkileşimler, açıkça tanımladığınız metotlar aracılığıyla gerçekleşmelidir. + +#### Sınıflara Metot Ekleme + +Özellikler doğrudan erişilebilir olmadığından, saydam olmayan sınıflarınızla etkileşim kurmak için **metotlar tanımlamanız gerekir**. Davranışı tanımlamak için `//export_php:method` yönergesini kullanın: + +```go +package example + +// #include +import "C" +import ( + "unsafe" + + "github.com/dunglas/frankenphp" +) + +//export_php:class User +type UserStruct struct { + Name string + Age int +} + +//export_php:method User::getName(): string +func (us *UserStruct) GetUserName() unsafe.Pointer { + return frankenphp.PHPString(us.Name, false) +} + +//export_php:method User::setAge(int $age): void +func (us *UserStruct) SetUserAge(age int64) { + us.Age = int(age) +} + +//export_php:method User::getAge(): int +func (us *UserStruct) GetUserAge() int64 { + return int64(us.Age) +} + +//export_php:method User::setNamePrefix(string $prefix = "User"): void +func (us *UserStruct) SetNamePrefix(prefix *C.zend_string) { + us.Name = frankenphp.GoString(unsafe.Pointer(prefix)) + ": " + us.Name +} +``` + +#### Boş Geçilebilir Parametreler (Nullable Parameters) + +Üretici, PHP imzalarında `?` önekini kullanarak boş geçilebilir parametreleri destekler. Bir parametre boş geçilebilir olduğunda, Go fonksiyonunuzda bir işaretçiye dönüşür ve böylece PHP'de değerin `null` olup olmadığını kontrol etmenizi sağlar: + +```go +package example + +// #include +import "C" +import ( + "unsafe" + + "github.com/dunglas/frankenphp" +) + +//export_php:method User::updateInfo(?string $name, ?int $age, ?bool $active): void +func (us *UserStruct) UpdateInfo(name *C.zend_string, age *int64, active *bool) { + // Check if name was provided (not null) + if name != nil { + us.Name = frankenphp.GoString(unsafe.Pointer(name)) + } + + // Check if age was provided (not null) + if age != nil { + us.Age = int(*age) + } + + // Check if active was provided (not null) + if active != nil { + us.Active = *active + } +} +``` + +**Boş geçilebilir parametreler hakkında önemli noktalar:** + +- **Boş geçilebilir ilkel türler** (`?int`, `?float`, `?bool`) Go'da işaretçilere (`*int64`, `*float64`, `*bool`) dönüşür +- **Boş geçilebilir dizeler** (`?string`) `*C.zend_string` olarak kalır ancak `nil` olabilir +- İşaretçi değerlerini referans almadan önce `nil` kontrolü yapın +- **PHP `null` Go `nil` olur** - PHP `null` geçirdiğinde, Go fonksiyonunuz bir `nil` işaretçi alır + +> [!WARNING] +> +> Şu anda sınıf metotlarının aşağıdaki sınırlamaları bulunmaktadır. Parametre türleri veya dönüş türleri olarak **nesneler desteklenmemektedir**. **Diziler**, hem parametreler hem de dönüş türleri için tam olarak desteklenmektedir. Desteklenen türler: `string`, `int`, `float`, `bool`, `array` ve `void` (dönüş türü için). **Boş geçilebilir parametre türleri**, tüm skaler türler (`?string`, `?int`, `?float`, `?bool`) için tam olarak desteklenmektedir. + +Eklentiyi oluşturduktan sonra, sınıfı ve metotlarını PHP'de kullanmanıza izin verilecektir. Özelliklere **doğrudan erişemeyeceğinizi** unutmayın: + +```php +setAge(25); +echo $user->getName(); // Çıktı: (boş, varsayılan değer) +echo $user->getAge(); // Çıktı: 25 +$user->setNamePrefix("Employee"); + +// ✅ Bu da çalışır - boş geçilebilir parametreler +$user->updateInfo("John", 30, true); // Tüm parametreler sağlandı +$user->updateInfo("Jane", null, false); // Yaş null +$user->updateInfo(null, 25, null); // Ad ve aktif null + +// ❌ Bu ÇALIŞMAZ - doğrudan özellik erişimi +// echo $user->name; // Hata: Özel özelliğe erişilemez +// $user->age = 30; // Hata: Özel özelliğe erişilemez +``` + +Bu tasarım, Go kodunuzun nesnenin durumuna nasıl erişildiğini ve değiştirildiğini tamamen kontrol etmesini sağlayarak daha iyi kapsülleme ve tür güvenliği sunar. + +### Sabitleri Bildirme + +Üretici, Go sabitlerini PHP'ye iki yönerge kullanarak dışa aktarmayı destekler: genel sabitler için `//export_php:const` ve sınıf sabitleri için `//export_php:classconst`. Bu, yapılandırma değerlerini, durum kodlarını ve diğer sabitleri Go ve PHP kodu arasında paylaşmanıza olanak tanır. + +#### Genel Sabitler + +Genel PHP sabitleri oluşturmak için `//export_php:const` yönergesini kullanın: + +```go +package example + +//export_php:const +const MAX_CONNECTIONS = 100 + +//export_php:const +const API_VERSION = "1.2.3" + +//export_php:const +const STATUS_OK = iota + +//export_php:const +const STATUS_ERROR = iota +``` + +#### Sınıf Sabitleri + +Belirli bir PHP sınıfına ait sabitler oluşturmak için `//export_php:classconst ClassName` yönergesini kullanın: + +```go +package example + +//export_php:classconst User +const STATUS_ACTIVE = 1 + +//export_php:classconst User +const STATUS_INACTIVE = 0 + +//export_php:classconst User +const ROLE_ADMIN = "admin" + +//export_php:classconst Order +const STATE_PENDING = iota + +//export_php:classconst Order +const STATE_PROCESSING = iota + +//export_php:classconst Order +const STATE_COMPLETED = iota +``` + +Sınıf sabitleri, PHP'deki sınıf adı kapsamı kullanılarak erişilebilir: + +```php + +import "C" +import ( + "strings" + "unsafe" + + "github.com/dunglas/frankenphp" +) + +//export_php:const +const STR_REVERSE = iota + +//export_php:const +const STR_NORMAL = iota + +//export_php:classconst StringProcessor +const MODE_LOWERCASE = 1 + +//export_php:classconst StringProcessor +const MODE_UPPERCASE = 2 + +//export_php:function repeat_this(string $str, int $count, int $mode): string +func repeat_this(s *C.zend_string, count int64, mode int) unsafe.Pointer { + str := frankenphp.GoString(unsafe.Pointer(s)) + + result := strings.Repeat(str, int(count)) + if mode == STR_REVERSE { + // reverse the string + } + + if mode == STR_NORMAL { + // no-op, just to showcase the constant + } + + return frankenphp.PHPString(str, false) +} + +//export_php:class StringProcessor +type StringProcessorStruct struct { + // internal fields +} + +//export_php:method StringProcessor::process(string $input, int $mode): string +func (sp *StringProcessorStruct) Process(input *C.zend_string, mode int64) unsafe.Pointer { + str := frankenphp.GoString(unsafe.Pointer(input)) + + switch mode { + case MODE_LOWERCASE: + str = strings.ToLower(str) + case MODE_UPPERCASE: + str = strings.ToUpper(str) + } + + return frankenphp.PHPString(str, false) +} +``` + +### Ad Alanlarını Kullanma (Namespaces) + +Üretici, PHP eklentinizin fonksiyonlarını, sınıflarını ve sabitlerini `//export_php:namespace` yönergesini kullanarak bir ad alanı altında düzenlemeyi destekler. Bu, adlandırma çakışmalarını önlemeye yardımcı olur ve eklentinizin API'si için daha iyi bir organizasyon sağlar. + +#### Bir Ad Alanı Bildirme + +Tüm dışa aktarılan sembolleri belirli bir ad alanı altına yerleştirmek için Go dosyanızın en üstünde `//export_php:namespace` yönergesini kullanın: + +```go +//export_php:namespace My\Extension +package example + +import ( + "unsafe" + + "github.com/dunglas/frankenphp" +) + +//export_php:function hello(): string +func hello() string { + return "Hello from My\\Extension namespace!" +} + +//export_php:class User +type UserStruct struct { + // internal fields +} + +//export_php:method User::getName(): string +func (u *UserStruct) GetName() unsafe.Pointer { + return frankenphp.PHPString("John Doe", false) +} + +//export_php:const +const STATUS_ACTIVE = 1 +``` + +#### Ad Alanlı Eklentiyi PHP'de Kullanma + +Bir ad alanı bildirildiğinde, tüm fonksiyonlar, sınıflar ve sabitler PHP'de o ad alanı altına yerleştirilir: + +```php +getName(); // "John Doe" + +echo My\Extension\STATUS_ACTIVE; // 1 +``` + +#### Önemli Notlar + +- Dosya başına yalnızca **bir** ad alanı yönergesine izin verilir. Birden fazla ad alanı yönergesi bulunursa, üretici bir hata döndürür. +- Ad alanı, dosyada dışa aktarılan **tüm** semboller için geçerlidir: fonksiyonlar, sınıflar, metotlar ve sabitler. +- Ad alanı adları, ayırıcı olarak ters eğik çizgi (`\`) kullanarak PHP ad alanı kurallarına uyar. +- Hiçbir ad alanı bildirilmezse, semboller her zamanki gibi genel ad alanına dışa aktarılır. + +### Eklentiyi Oluşturma + +İşte sihrin gerçekleştiği yer burası ve eklentiniz artık oluşturulabilir. Üreticiyi aşağıdaki komutla çalıştırabilirsiniz: + +```console +GEN_STUB_SCRIPT=php-src/build/gen_stub.php frankenphp extension-init my_extension.go +``` + +> [!NOTE] +> `GEN_STUB_SCRIPT` ortam değişkenini, daha önce indirdiğiniz PHP kaynaklarındaki `gen_stub.php` dosyasının yoluna ayarlamayı unutmayın. Bu, manuel uygulama bölümünde bahsedilen aynı `gen_stub.php` betiğidir. + +Her şey yolunda gittiyse, `build` adında yeni bir dizin oluşturulmuş olmalıdır. Bu dizin, oluşturulan PHP fonksiyon taslaklarını içeren `my_extension.go` dosyası da dahil olmak üzere eklentiniz için oluşturulan dosyaları içerir. + +### Oluşturulan Eklentiyi FrankenPHP'ye Entegre Etme + +Eklentimiz artık derlenmeye ve FrankenPHP'ye entegre edilmeye hazır. Bunu yapmak için, FrankenPHP'yi nasıl derleyeceğinizi öğrenmek üzere FrankenPHP [derleme belgelerine](compile.md) başvurun. `--with` bayrağını kullanarak modülünüzün yolunu işaret ederek modülü ekleyin: + +```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 github.com/my-account/my-module/build +``` + +Üretim adımı sırasında oluşturulan `/build` alt dizinini işaret ettiğinizi unutmayın. Ancak bu zorunlu değildir: Oluşturulan dosyaları modül dizininize kopyalayıp doğrudan orayı da işaret edebilirsiniz. + +### Oluşturulan Eklentinizi Test Etme + +Oluşturduğunuz fonksiyonları ve sınıfları test etmek için bir PHP dosyası oluşturabilirsiniz. Örneğin, aşağıdaki içeriğe sahip bir `index.php` dosyası oluşturun: + +```php +process('Hello World', StringProcessor::MODE_LOWERCASE); // "hello world" +echo $processor->process('Hello World', StringProcessor::MODE_UPPERCASE); // "HELLO WORLD" +``` + +Eklentinizi önceki bölümde gösterildiği gibi FrankenPHP'ye entegre ettikten sonra, bu test dosyasını `./frankenphp php-server` kullanarak çalıştırabilirsiniz ve eklentinizin çalıştığını görmelisiniz. + +## Manuel Uygulama + +Eklentilerin nasıl çalıştığını anlamak veya eklentiniz üzerinde tam kontrole sahip olmak istiyorsanız, bunları manuel olarak yazabilirsiniz. Bu yaklaşım size tam kontrol sağlar ancak daha fazla temel kod (boilerplate) gerektirir. + +### Temel Fonksiyon + +Go dilinde yeni bir yerel fonksiyon tanımlayan basit bir PHP eklentisi nasıl yazılacağını göreceğiz. Bu fonksiyon PHP'den çağrılacak ve Caddy'nin loglarına bir mesaj kaydeden bir gorutin tetikleyecektir. Bu fonksiyon herhangi bir parametre almaz ve hiçbir şey döndürmez. + +#### Go Fonksiyonunu Tanımlama + +Modülünüzde, PHP'den çağrılacak yeni bir yerel fonksiyon tanımlamanız gerekir. Bunu yapmak için, örneğin `extension.go` adında istediğiniz bir dosya oluşturun ve aşağıdaki kodu ekleyin: + +```go +package example + +// #include "extension.h" +import "C" +import ( + "log/slog" + "unsafe" + + "github.com/dunglas/frankenphp" +) + +func init() { + frankenphp.RegisterExtension(unsafe.Pointer(&C.ext_module_entry)) +} + +//export go_print_something +func go_print_something() { + go func() { + slog.Info("Hello from a goroutine!") + }() +} +``` + +`frankenphp.RegisterExtension()` fonksiyonu, dahili PHP kayıt mantığını ele alarak eklenti kayıt sürecini basitleştirir. `go_print_something` fonksiyonu, CGO sayesinde yazacağımız C kodunda erişilebilir olacağını belirtmek için `//export` yönergesini kullanır. + +Bu örnekte, yeni fonksiyonumuz Caddy'nin loglarına bir mesaj kaydeden bir gorutin tetikleyecektir. + +#### PHP Fonksiyonunu Tanımlama + +PHP'nin fonksiyonumuzu çağırabilmesi için ilgili bir PHP fonksiyonu tanımlamamız gerekiyor. Bunun için, örneğin `extension.stub.php` adında bir taslak dosya oluşturacağız ve bu dosya aşağıdaki kodu içerecektir: + +```php + + +extern zend_module_entry ext_module_entry; + +#endif +``` + +Ardından, aşağıdaki adımları gerçekleştirecek `extension.c` adında bir dosya oluşturun: + +- PHP başlıklarını dahil etme; +- Yeni yerel PHP fonksiyonumuz `go_print()`'i bildirme; +- Eklenti meta verilerini bildirme. + +Gerekli başlıkları dahil ederek başlayalım: + +```c +#include +#include "extension.h" +#include "extension_arginfo.h" + +// Go tarafından dışa aktarılan sembolleri içerir +#include "_cgo_export.h" +``` + +Daha sonra PHP fonksiyonumuzu yerel bir dil fonksiyonu olarak tanımlıyoruz: + +```c +PHP_FUNCTION(go_print) +{ + ZEND_PARSE_PARAMETERS_NONE(); + + go_print_something(); +} + +zend_module_entry ext_module_entry = { + STANDARD_MODULE_HEADER, + "ext_go", + ext_functions, /* Fonksiyonlar */ + NULL, /* MINIT */ + NULL, /* MSHUTDOWN */ + NULL, /* RINIT */ + NULL, /* RSHUTDOWN */ + NULL, /* MINFO */ + "0.1.1", + STANDARD_MODULE_PROPERTIES +}; +``` + +Bu durumda, fonksiyonumuz hiçbir parametre almaz ve hiçbir şey döndürmez. Daha önce tanımladığımız ve `//export` yönergesi kullanılarak dışa aktarılan Go fonksiyonunu çağırmaktadır. + +Son olarak, eklentinin adını, sürümünü ve özelliklerini içeren `zend_module_entry` yapısında meta verilerini tanımlıyoruz. Bu bilgiler, PHP'nin eklentimizi tanıması ve yüklemesi için gereklidir. `ext_functions`'ın tanımladığımız PHP fonksiyonlarına işaretçilerden oluşan bir dizi olduğunu ve `gen_stub.php` betiği tarafından `extension_arginfo.h` dosyasında otomatik olarak oluşturulduğunu unutmayın. + +Eklenti kaydı, Go kodumuzda çağırdığımız FrankenPHP'nin `RegisterExtension()` fonksiyonu tarafından otomatik olarak halledilir. + +### Gelişmiş Kullanım + +Şimdi Go'da temel bir PHP eklentisi oluşturmayı bildiğimize göre, örneğimizi karmaşıklaştıralım. Şimdi parametre olarak bir dize alan ve büyük harfli versiyonunu döndüren bir PHP fonksiyonu oluşturacağız. + +#### PHP Fonksiyon Taslağını Tanımlama + +Yeni PHP fonksiyonunu tanımlamak için, `extension.stub.php` dosyamızı yeni fonksiyon imzasını içerecek şekilde değiştireceğiz: + +```php + [!TIP] +> Fonksiyonlarınızın dokümantasyonunu ihmal etmeyin! Eklenti taslaklarınızı diğer geliştiricilerle paylaşarak eklentinizin nasıl kullanılacağını ve hangi özelliklerin mevcut olduğunu belgeleyebilirsiniz. + +Taslak dosyasını `gen_stub.php` betiği ile yeniden oluşturarak, `extension_arginfo.h` dosyası şöyle görünmelidir: + +```c +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_go_upper, 0, 1, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, string, IS_STRING, 0) +ZEND_END_ARG_INFO() + +ZEND_FUNCTION(go_upper); + +static const zend_function_entry ext_functions[] = { + ZEND_FE(go_upper, arginfo_go_upper) + ZEND_FE_END +}; +``` + +`go_upper` fonksiyonunun `string` tipinde bir parametre ve `string` tipinde bir dönüş değeriyle tanımlandığını görebiliriz. + +#### Go ve PHP/C Arasında Tür Dengeleme + +Go fonksiyonunuz doğrudan bir PHP dizesini parametre olarak kabul edemez. Onu bir Go dizesine dönüştürmeniz gerekir. Neyse ki FrankenPHP, üretici yaklaşımında gördüğümüze benzer şekilde, PHP dizeleri ile Go dizeleri arasındaki dönüşümü yönetmek için yardımcı fonksiyonlar sağlar. + +Başlık dosyası basit kalır: + +```c +#ifndef _EXTENSION_H +#define _EXTENSION_H + +#include + +extern zend_module_entry ext_module_entry; + +#endif +``` + +Şimdi `extension.c` dosyamızda Go ve C arasındaki köprüyü yazabiliriz. PHP dizesini doğrudan Go fonksiyonumuza geçireceğiz: + +```c +PHP_FUNCTION(go_upper) +{ + zend_string *str; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_STR(str) + ZEND_PARSE_PARAMETERS_END(); + + zend_string *result = go_upper(str); + RETVAL_STR(result); +} +``` + +`ZEND_PARSE_PARAMETERS_START` ve parametre ayrıştırma hakkında daha fazla bilgiyi [PHP Dahili Kitabı'nın](https://www.phpinternalsbook.com/php7/extensions_design/php_functions.html#parsing-parameters-zend-parse-parameters) ilgili sayfasında bulabilirsiniz. Burada, PHP'ye fonksiyonumuzun `zend_string` tipinde bir zorunlu parametre aldığını söylüyoruz. Daha sonra bu dizeyi doğrudan Go fonksiyonumuza iletiyor ve sonucu `RETVAL_STR` kullanarak döndürüyoruz. + +Yapılacak tek bir şey kaldı: `go_upper` fonksiyonunu Go'da uygulamak. + +#### Go Fonksiyonunu Uygulama + +Go fonksiyonumuz parametre olarak bir `*C.zend_string` alacak, FrankenPHP'nin yardımcı fonksiyonunu kullanarak onu bir Go dizesine dönüştürecek, işleyecek ve sonucu yeni bir `*C.zend_string` olarak döndürecektir. Yardımcı fonksiyonlar, tüm bellek yönetimi ve dönüşüm karmaşıklığını bizim için halleder. + +```go +package example + +// #include +import "C" +import ( + "unsafe" + "strings" + + "github.com/dunglas/frankenphp" +) + +//export go_upper +func go_upper(s *C.zend_string) *C.zend_string { + str := frankenphp.GoString(unsafe.Pointer(s)) + + upper := strings.ToUpper(str) + + return (*C.zend_string)(frankenphp.PHPString(upper, false)) +} +``` + +Bu yaklaşım, manuel bellek yönetiminden çok daha temiz ve güvenlidir. +FrankenPHP'nin yardımcı fonksiyonları, PHP'nin `zend_string` formatı ile Go dizeleri arasındaki dönüşümü otomatik olarak halleder. +`PHPString()`'deki `false` parametresi, yeni bir kalıcı olmayan dize oluşturmak istediğimizi belirtir (isteğin sonunda serbest bırakılır). + +> [!TIP] +> +> Bu örnekte herhangi bir hata işleme yapmıyoruz, ancak Go fonksiyonlarınızda kullanmadan önce her zaman işaretçilerin `nil` olmadığını ve verilerin geçerli olduğunu kontrol etmelisiniz. + +### Eklentiyi FrankenPHP'ye Entegre Etme + +Eklentimiz artık derlenmeye ve FrankenPHP'ye entegre edilmeye hazır. Bunu yapmak için, FrankenPHP'yi nasıl derleyeceğinizi öğrenmek üzere FrankenPHP [derleme belgelerine](compile.md) başvurun. `--with` bayrağını kullanarak modülünüzün yolunu işaret ederek modülü ekleyin: + +```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 github.com/my-account/my-module +``` + +İşte bu kadar! Eklentiniz artık FrankenPHP'ye entegre edildi ve PHP kodunuzda kullanılabilir. + +### Eklentinizi Test Etme + +Eklentinizi FrankenPHP'ye entegre ettikten sonra, uyguladığınız fonksiyonlar için örnekler içeren bir `index.php` dosyası oluşturabilirsiniz: + +```php + [!UYARI] +> Bu özellik **yalnızca geliştirme ortamları** için tasarlanmıştır. +> `hot_reload`'u üretimde etkinleştirmeyin, çünkü dosya sistemini izlemek performans düşüşüne neden olur ve dahili uç noktaları açığa çıkarır. + +```caddyfile +localhost + +mercure { + anonymous +} + +root public/ +php_server { + hot_reload +} +``` + +Varsayılan olarak, FrankenPHP mevcut çalışma dizinindeki şu glob desenine uyan tüm dosyaları izleyecektir: `./**/*.{css,env,gif,htm,html,jpg,jpeg,js,mjs,php,png,svg,twig,webp,xml,yaml,yml}` + +Glob sözdizimini kullanarak izlenecek dosyaları açıkça belirtmek mümkündür: + +```caddyfile +localhost + +mercure { + anonymous +} + +root public/ +php_server { + hot_reload src/**/*{.php,.js} config/**/*.yaml +} +``` + +Mercure konusunu ve hangi dizin veya dosyaların izleneceğini belirtmek için `hot_reload` seçeneğine yollar sağlayarak uzun formu kullanın: + +```caddyfile +localhost + +mercure { + anonymous +} + +root public/ +php_server { + hot_reload { + topic hot-reload-topic + watch src/**/*.php + watch assets/**/*.{ts,json} + watch templates/ + watch public/css/ + } +} +``` + +## İstemci Tarafı Entegrasyonu + +Sunucu değişiklikleri algılarken, tarayıcının sayfayı güncellemek için bu olaylara abone olması gerekir. +FrankenPHP, dosya değişikliklerine abone olmak için kullanılacak Mercure Hub URL'sini `$_SERVER['FRANKENPHP_HOT_RELOAD']` ortam değişkeni aracılığıyla açığa çıkarır. + +İstemci tarafı mantığını işlemek için kullanışlı bir JavaScript kütüphanesi olan [frankenphp-hot-reload](https://www.npmjs.com/package/frankenphp-hot-reload) da mevcuttur. +Kullanmak için ana şablonunuza aşağıdakileri ekleyin: + +```php + +FrankenPHP Anında Yeniden Yükleme + + + + + +``` + +Kütüphane, Mercure hub'ına otomatik olarak abone olacak, bir dosya değişikliği algılandığında arka planda mevcut URL'yi çekecek ve DOM'u biçimlendirecektir. +Bir [npm](https://www.npmjs.com/package/frankenphp-hot-reload) paketi olarak ve [GitHub](https://github.com/dunglas/frankenphp-hot-reload) üzerinde mevcuttur. + +Alternatif olarak, `EventSource` yerel JavaScript sınıfını kullanarak doğrudan Mercure hub'ına abone olarak kendi istemci tarafı mantığınızı uygulayabilirsiniz. + +### Çalışan Modu + +Uygulamanızı [Çalışan Modunda](https://frankenphp.dev/docs/worker/) çalıştırıyorsanız, uygulama betiğiniz bellekte kalır. +Bu, tarayıcı yeniden yüklense bile PHP kodunuzdaki değişikliklerin hemen yansımayacağı anlamına gelir. + +En iyi geliştirici deneyimi için, `hot_reload`'u [çalışan yönergesindeki `watch` alt yönergesi](config.md#watching-for-file-changes) ile birleştirmelisiniz. + +- `hot_reload`: dosyalar değiştiğinde **tarayıcıyı** yeniler +- `worker.watch`: dosyalar değiştiğinde çalışanı yeniden başlatır + +```caddy +localhost + +mercure { + anonymous +} + +root public/ +php_server { + hot_reload + worker { + file /path/to/my_worker.php + watch + } +} +``` + +### Nasıl Çalışır + +1. **İzleme**: FrankenPHP, arka planda [e-dant/watcher kütüphanesini](https://github.com/e-dant/watcher) kullanarak dosya sistemindeki değişiklikleri izler (Go bağlamasını biz geliştirdik). +2. **Yeniden Başlatma (Çalışan Modu)**: Eğer çalışan yapılandırmasında `watch` etkinleştirilmişse, yeni kodu yüklemek için PHP çalışanı yeniden başlatılır. +3. **İtme**: Değişen dosyaların listesini içeren bir JSON yükü, yerleşik [Mercure hub](https://mercure.rocks)'ına gönderilir. +4. **Alma**: JavaScript kütüphanesi aracılığıyla dinleyen tarayıcı, Mercure olayını alır. +5. **Güncelleme**: + +- Eğer **Idiomorph** algılanırsa, güncellenmiş içeriği çeker ve mevcut HTML'i yeni duruma uyması için biçimlendirir, durumu kaybetmeden değişiklikleri anında uygular. +- Aksi takdirde, sayfayı yenilemek için `window.location.reload()` çağrılır. diff --git a/docs/tr/known-issues.md b/docs/tr/known-issues.md index a934375a12..a9abbf80e7 100644 --- a/docs/tr/known-issues.md +++ b/docs/tr/known-issues.md @@ -4,34 +4,40 @@ 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) | +| 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) | +| [newrelic](https://docs.newrelic.com/docs/apm/agents/php-agent/getting-started/introduction-new-relic-php/) | İş parçacığı güvenli değil | - | ## Sorunlu PHP Eklentileri Aşağıdaki eklentiler FrankenPHP ile kullanıldığında bilinen hatalara ve beklenmeyen davranışlara sahiptir: -| Adı | Problem | -| --- | ------- | +| Adı | Problem | +| :------------------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| [ext-openssl](https://www.php.net/manual/en/book.openssl.php) | musl libc kullanıldığında, OpenSSL eklentisi yoğun yük altında çökebilir. Bu sorun, daha popüler olan GNU libc kullanıldığında ortaya çıkmaz. Bu hata [PHP tarafından takip edilmektedir](https://github.com/php/php-src/issues/13648). | ## 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). +[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ı +## Tek Başına İkili 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. +Tek başına ikili ve Alpine tabanlı Docker imajları (`dunglas/frankenphp:*-alpine`), daha küçük bir ikili boyutu korumak için [glibc ve arkadaşları](https://www.etalabs.net/compare_libcs.html) yerine [musl libc](https://musl.libc.org/) kullanır. +Bu durum bazı uyumluluk sorunlarına yol açabilir. +Özellikle, glob bayrağı `GLOB_BRACE` [mevcut değildir](https://www.php.net/manual/en/function.glob.php). + +Sorunlarla karşılaşmanız durumunda, statik ikilinin GNU varyantını ve Debian tabanlı Docker imajlarını kullanmayı tercih edin. ## 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. +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 şekilde yapılandırmak 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. +`curl: (35) LibreSSL/3.3.6: error:1404B438:SSL routines:ST_CONNECT:tlsv1 alert internal error` benzeri 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: @@ -45,7 +51,7 @@ docker run \ 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. +`docker network inspect bridge` komutunu çalıştırın ve `Containers` anahtarının altındaki `IPv4Address` anahtarındaki son atanmış IP adresini belirlemek için 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: @@ -59,7 +65,7 @@ docker run \ > [!CAUTION] > -> 172.17.0.3`ü konteynerinize atanacak IP ile değiştirdiğinizden emin olun. +> `172.17.0.3` değerini konteynerinize atanacak IP ile değiştirdiğinizden emin olun. Artık ana makineden `https://127.0.0.1` adresine erişebilmeniz gerekir. @@ -74,17 +80,17 @@ docker run \ dunglas/frankenphp ``` -## `@php` Referanslı Composer Betikler +## `@php` Referanslı Composer Betikleri -[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/php/frankenphp/issues/483#issuecomment-1899890915) ve 2 nedeni var: +[Composer betikleri](https://getcomposer.org/doc/articles/scripts.md) bazı görevler için bir PHP ikilisi çalıştırmak isteyebilir, örneğin [bir Laravel projesinde](laravel.md) `@php artisan package:discover --ansi` çalıştırmak. Bu [şu anda başarısız oluyor](https://github.com/php/frankenphp/issues/483#issuecomment-1899890915) ve bunun iki 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. +- Composer, FrankenPHP ikilisini nasıl çağıracağını bilmiyor; +- Composer, FrankenPHP'nin henüz desteklemediği `-d` bayrağını kullanarak komuta PHP ayarları 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: +Geçici bir çözüm olarak, `/usr/local/bin/php` içinde desteklenmeyen parametreleri temizleyen ve ardından FrankenPHP'yi çağıran bir kabuk betiği oluşturabiliriz: ```bash -#!/bin/bash +#!/usr/bin/env bash args=("$@") index=0 for i in "$@" @@ -99,9 +105,42 @@ 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: +Ardından `PHP_BINARY` ortam değişkenini PHP betiğimizin yoluna ayarlayın ve Composer'ı çalıştırın: -```bash +```console export PHP_BINARY=/usr/local/bin/php composer install ``` + +## Statik İkililerle TLS/SSL Sorunlarını Giderme + +Statik ikilileri kullanırken, örneğin STARTTLS kullanarak e-posta gönderirken aşağıdaki TLS ile ilgili hatalarla karşılaşabilirsiniz: + +```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 +``` + +Statik ikili TLS sertifikalarını içermediğinden, OpenSSL'i yerel CA sertifikaları kurulumunuza yönlendirmeniz gerekir. + +CA sertifikalarının nereye yüklenmesi gerektiğini bulmak ve bu konuma kaydetmek için [`openssl_get_cert_locations()`](https://www.php.net/manual/en/function.openssl-get-cert-locations.php) çıktısını inceleyin. + +> [!WARNING] +> +> Web ve CLI bağlamları farklı ayarlara sahip olabilir. +> `openssl_get_cert_locations()` fonksiyonunu doğru bağlamda çalıştırdığınızdan emin olun. + +[Mozilla'dan çıkarılan CA sertifikaları cURL sitesinden indirilebilir](https://curl.se/docs/caextract.html). + +Alternatif olarak, Debian, Ubuntu ve Alpine dahil olmak üzere birçok dağıtım, bu sertifikaları içeren `ca-certificates` adlı paketler sağlar. + +OpenSSL'e CA sertifikalarını nerede arayacağını belirtmek için `SSL_CERT_FILE` ve `SSL_CERT_DIR` değişkenlerini kullanmak da mümkündür: + +```console +# TLS sertifikası ortam değişkenlerini ayarla +export SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt +export SSL_CERT_DIR=/etc/ssl/certs +``` diff --git a/docs/tr/laravel.md b/docs/tr/laravel.md index afa0bd9c40..d07bc68596 100644 --- a/docs/tr/laravel.md +++ b/docs/tr/laravel.md @@ -17,7 +17,7 @@ Ve tadını çıkarın! Alternatif olarak, Laravel projelerinizi FrankenPHP ile yerel makinenizden çalıştırabilirsiniz: 1. [Sisteminize karşılık gelen ikili dosyayı indirin](../#standalone-binary) -2. Aşağıdaki yapılandırmayı Laravel projenizin kök dizinindeki `Caddyfile` adlı bir dosyaya ekleyin: +2. Aşağıdaki yapılandırmayı Laravel projenizin kök dizenindeki `Caddyfile` adlı bir dosyaya ekleyin: ```caddyfile { @@ -66,24 +66,25 @@ php artisan octane:frankenphp - `--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 (varsayılan: [Laravel Octane içinde bulunan şablon `Caddyfile`](https://github.com/laravel/octane/blob/2.x/src/Commands/stubs/Caddyfile)) +- `--caddyfile`: FrankenPHP `Caddyfile` dosyasının yolu (varsayılan: [Laravel Octane'deki taslak `Caddyfile`](https://github.com/laravel/octane/blob/2.x/src/Commands/stubs/Caddyfile)) - `--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 ile birlikte geçilirse etkinleşir) - `--watch`: Uygulama değiştirildiğinde 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`: Yerel Caddy günlüğünü kullanarak belirtilen günlük seviyesinde veya üzerinde mesajları kaydedin +- `--log-level`: Yerel Caddy günlükleyicisini kullanarak belirtilen günlük seviyesinde veya üzerinde mesajları kaydedin > [!TIP] > Yapılandırılmış JSON günlükleri elde etmek için (log analitik çözümleri kullanırken faydalıdır), `--log-level` seçeneğini açıkça geçin. +Ayrıca [Octane ile Mercure nasıl kullanılır](#mercure-support) bölümüne bakın. + [Laravel Octane hakkında daha fazla bilgiyi resmi belgelerde bulabilirsiniz](https://laravel.com/docs/octane). -## Laravel Uygulamalarını Bağımsız Çalıştırılabilir Dosyalar Olarak Dağıtma +## Laravel Uygulamalarını Bağımsız İkili Dosyalar Olarak Dağıtma -[FrankenPHP'nin uygulama gömme özelliğini](embed.md) kullanarak, Laravel -uygulamalarını bağımsız çalıştırılabilir dosyalar olarak dağıtmak mümkündür. +[FrankenPHP'nin uygulama gömme özelliğini](embed.md) kullanarak, Laravel uygulamalarını bağımsız ikili dosyalar olarak dağıtmak mümkündür. -Linux için Laravel uygulamanızı bağımsız bir çalıştırılabilir olarak paketlemek için şu adımları izleyin: +Linux için Laravel uygulamanızı bağımsız bir ikili olarak paketlemek için şu adımları izleyin: 1. Uygulamanızın deposunda `static-build.Dockerfile` adında bir dosya oluşturun: @@ -156,8 +157,7 @@ Linux için Laravel uygulamanızı bağımsız bir çalıştırılabilir olarak Uygulamanız artık hazır! -Mevcut seçenekler hakkında daha fazla bilgi edinin ve diğer işletim sistemleri için nasıl ikili derleneceğini [uygulama gömme](embed.md) -belgelerinde öğrenin. +Mevcut seçenekler hakkında daha fazla bilgi edinin ve diğer işletim sistemleri için nasıl ikili derleneceğini [uygulama gömme](embed.md) belgelerinde öğrenin. ### Depolama Yolunu Değiştirme @@ -166,11 +166,37 @@ Gömülü uygulamalar için bu uygun değildir, çünkü her yeni sürüm farkl Geçici dizin dışında bir dizin kullanmak için `LARAVEL_STORAGE_PATH` ortam değişkenini ayarlayın (örneğin, `.env` dosyanızda) veya `Illuminate\Foundation\Application::useStoragePath()` metodunu çağırın. -### Bağımsız Çalıştırılabilir Dosyalarla Octane'i Çalıştırma +### Mercure Desteği + +[Mercure](https://mercure.rocks), Laravel uygulamalarınıza gerçek zamanlı yetenekler eklemenin harika bir yoludur. FrankenPHP, [Mercure desteğini kutudan çıktığı gibi](mercure.md) sunar. + +[Octane](#laravel-octane) kullanmıyorsanız, [Mercure belgelendirme girdisine](mercure.md) bakın. + +Octane kullanıyorsanız, `config/octane.php` dosyanıza aşağıdaki satırları ekleyerek Mercure desteğini etkinleştirebilirsiniz: + +```php +// ... + +return [ + // ... + + 'mercure' => [ + 'anonymous' => true, + 'publisher_jwt' => '!ChangeThisMercureHubJWTSecretKey!', + 'subscriber_jwt' => '!ChangeThisMercureHubJWTSecretKey!', + ], +]; +``` + +Bu dizide [Mercure tarafından desteklenen tüm yönergeleri](https://mercure.rocks/docs/hub/config#directives) kullanabilirsiniz. + +Güncellemeleri yayınlamak ve abone olmak için, [Laravel Mercure Broadcaster](https://github.com/mvanduijker/laravel-mercure-broadcaster) kütüphanesini kullanmanızı öneririz. Alternatif olarak, saf PHP ve JavaScript ile yapmak için [Mercure belgelerine](mercure.md) bakın. + +### Bağımsız İkili Dosyalarla Octane'i Çalıştırma -Laravel Octane uygulamalarını bağımsız çalıştırılabilir dosyalar olarak paketlemek bile mümkündür! +Laravel Octane uygulamalarını bağımsız ikili dosyalar olarak paketlemek bile mümkündür! -Bunu yapmak için, [Octane'i doğru şekilde kurun](#laravel-octane) ve [önceki bölümde](#laravel-uygulamalarını-bağımsız-çalıştırılabilir-dosyalar-olarak-dağıtma) açıklanan adımları izleyin. +Bunu yapmak için, [Octane'i doğru şekilde kurun](#laravel-octane) ve [önceki bölümde](#laravel-uygulamalarını-bağımsız-ikili-dosyalar-olarak-dağıtma) açıklanan adımları izleyin. Ardından, Octane üzerinden FrankenPHP'yi worker modunda başlatmak için şunu çalıştırın: diff --git a/docs/tr/logging.md b/docs/tr/logging.md new file mode 100644 index 0000000000..b733c9dfbd --- /dev/null +++ b/docs/tr/logging.md @@ -0,0 +1,71 @@ +# Günlüğe Kayıt + +FrankenPHP, [Caddy'nin günlüğe kayıt sistemi](https://caddyserver.com/docs/logging) ile sorunsuz bir şekilde entegre olur. +Standart PHP fonksiyonlarını kullanarak mesajları günlüğe kaydedebilir veya gelişmiş +yapılandırılmış günlüğe kayıt yetenekleri için özel `frankenphp_log()` fonksiyonunu kullanabilirsiniz. + +## `frankenphp_log()` + +`frankenphp_log()` fonksiyonu, yapılandırılmış logları doğrudan PHP uygulamanızdan yaymanıza olanak tanır, +böylece Datadog, Grafana Loki veya Elastic gibi platformlara alımı ve OpenTelemetry desteği çok daha kolay hale gelir. + +Arka planda, `frankenphp_log()`, zengin günlük kaydı özellikleri sunmak için [Go'nun `log/slog` paketi](https://pkg.go.dev/log/slog)'nı sarmalar. + +Bu loglar, önem derecesi seviyesini ve isteğe bağlı bağlam verilerini içerir. + +```php +function frankenphp_log(string $message, int $level = FRANKENPHP_LOG_LEVEL_INFO, array $context = []): void +``` + +### Parametreler + +- **`message`**: Günlük mesaj dizisi. +- **`level`**: Günlüğün önem derecesi seviyesi. Herhangi bir keyfi tam sayı olabilir. Yaygın seviyeler için kolaylık sabitleri sağlanmıştır: `FRANKENPHP_LOG_LEVEL_DEBUG` (`-4`), `FRANKENPHP_LOG_LEVEL_INFO` (`0`), `FRANKENPHP_LOG_LEVEL_WARN` (`4`) ve `FRANKENPHP_LOG_LEVEL_ERROR` (`8`)). Varsayılan değer `FRANKENPHP_LOG_LEVEL_INFO`'dur. +- **`context`**: Günlük girdisine eklenecek ek verilerden oluşan ilişkisel bir dizi. + +### Örnek + +```php + memory_get_usage(), + 'peak_usage' => memory_get_peak_usage(), + ], +); + +``` + +Logları görüntülerken (örn. `docker compose logs` aracılığıyla), çıktı yapılandırılmış JSON olarak görünecektir: + +```json +{"level":"info","ts":1704067200,"logger":"frankenphp","msg":"Hello from FrankenPHP!"} +{"level":"warn","ts":1704067200,"logger":"frankenphp","msg":"Memory usage high","current_usage":10485760,"peak_usage":12582912} +``` + +## `error_log()` + +FrankenPHP, standart `error_log()` fonksiyonunu kullanarak da günlüğe kayıt yapılmasına olanak tanır. Eğer `$message_type` parametresi `4` (SAPI) ise, bu mesajlar Caddy günlükçüsüne yönlendirilir. + +Varsayılan olarak, `error_log()` aracılığıyla gönderilen mesajlar yapılandırılmamış metin olarak ele alınır. +Mevcut uygulamalar veya standart PHP kütüphanesine dayanan kütüphanelerle uyumluluk için kullanışlıdırlar. + +### `error_log()` ile Örnek + +```php +error_log("Database connection failed", 4); +``` + +Bu, Caddy loglarında görünecektir, genellikle PHP'den kaynaklandığını belirtmek için ön eklenmiş olarak. + +> [!TIP] +> Üretim ortamlarında daha iyi gözlemlenebilirlik için, `frankenphp_log()` tercih edin +> çünkü bu, logları seviyeye göre (Hata Ayıklama, Hata vb.) filtrelemenize +> ve günlük kaydı altyapınızdaki belirli alanları sorgulamanıza olanak tanır. diff --git a/docs/tr/mercure.md b/docs/tr/mercure.md index 581d486ba5..9baba4a070 100644 --- a/docs/tr/mercure.md +++ b/docs/tr/mercure.md @@ -1,12 +1,148 @@ # Gerçek Zamanlı FrankenPHP yerleşik bir [Mercure](https://mercure.rocks) hub ile birlikte gelir! -Mercure, olayları tüm bağlı cihazlara gerçek zamanlı olarak göndermeye olanak tanır: anında bir JavaScript olayı alırlar. +Mercure, tüm bağlı cihazlara gerçek zamanlı olaylar göndermenizi sağlar: anında bir JavaScript olayı alırlar. -JS kütüphanesi veya SDK gerekmez! +WebSockets'e uygun, kullanımı kolay ve tüm modern web tarayıcıları tarafından doğal olarak desteklenen pratik bir alternatiftir! -![Mercure](../mercure-hub.png) +![Mercure](mercure-hub.png) -Mercure hub'ını etkinleştirmek için [Mercure'ün sitesinde](https://mercure.rocks/docs/hub/config) açıklandığı gibi `Caddyfile`'ı güncelleyin. +## Mercure'ü Etkinleştirme -Mercure güncellemelerini kodunuzdan göndermek için [Symfony Mercure Bileşenini](https://symfony.com/components/Mercure) öneririz (kullanmak için Symfony tam yığın çerçevesine ihtiyacınız yoktur). +Mercure desteği varsayılan olarak devre dışıdır. +İşte hem FrankenPHP'yi hem de Mercure hub'ını etkinleştiren minimal bir `Caddyfile` örneği: + +```caddyfile +# The hostname to respond to +localhost + +mercure { + # The secret key used to sign the JWT tokens for publishers + publisher_jwt !ChangeThisMercureHubJWTSecretKey! + # Allows anonymous subscribers (without JWT) + anonymous +} + +root public/ +php_server +``` + +> [!TIP] +> +> [Docker imajları](docker.md) tarafından sağlanan [örnek `Caddyfile`](https://github.com/php/frankenphp/blob/main/caddy/frankenphp/Caddyfile), +> Mercure yapılandırmasını kolay ortam değişkenleriyle yapılandırmak için zaten yorumlanmış bir şekilde içerir. +> +> Etkinleştirmek için `/etc/frankenphp/Caddyfile` içindeki Mercure bölümünün yorumunu kaldırın. + +## Güncellemelere Abone Olma + +Varsayılan olarak, Mercure hub'ı FrankenPHP sunucunuzun `/.well-known/mercure` yolu üzerinde mevcuttur. +Güncellemelere abone olmak için yerel [`EventSource`](https://developer.mozilla.org/docs/Web/API/EventSource) JavaScript sınıfını kullanın: + +```html + + +Mercure Örneği + +``` + +## Güncellemeleri Yayınlama + +### `mercure_publish()` Kullanarak + +FrankenPHP, yerleşik Mercure hub'ına güncellemeler yayınlamak için kullanışlı bir `mercure_publish()` işlevi sunar: + +```php + 'value'])); + +// FrankenPHP'nin loglarına yaz +error_log("update $updateID published", 4); +``` + +Tam işlev imzası şöyledir: + +```php +/** + * @param string|string[] $topics + */ +function mercure_publish(string|array $topics, string $data = '', bool $private = false, ?string $id = null, ?string $type = null, ?int $retry = null): string {} +``` + +### `file_get_contents()` Kullanarak + +Bağlı abonelere bir güncelleme göndermek için Mercure hub'ına `topic` ve `data` parametreleriyle kimliği doğrulanmış bir POST isteği gönderin: + +```php + [ + 'method' => 'POST', + 'header' => "Content-type: application/x-www-form-urlencoded\r\nAuthorization: Bearer " . JWT, + 'content' => http_build_query([ + 'topic' => 'my-topic', + 'data' => json_encode(['key' => 'value']), + ]), +]])); + +// FrankenPHP'nin loglarına yaz +error_log("update $updateID published", 4); +``` + +`Caddyfile` içindeki `mercure.publisher_jwt` seçeneğinin parametresi olarak geçirilen anahtar, `Authorization` başlığında kullanılan JWT tokenını imzalamak için kullanılmalıdır. + +JWT, yayınlamak istediğiniz konular için `publish` izni içeren bir `mercure` talebi içermelidir. +Yetkilendirme hakkında [Mercure dokümantasyonuna](https://mercure.rocks/spec#publishers) bakın. + +Kendi tokenlarınızı oluşturmak için [bu jwt.io bağlantısını](https://www.jwt.io/#token=eyJhbGciOiJIUzI1NiJ9.eyJtZXJjdXJlIjp7InB1Ymxpc2giOlsiKiJdfX0.PXwpfIGng6KObfZlcOXvcnWCJOWTFLtswGI5DZuWSK4) kullanabilirsiniz, +ancak üretim uygulamaları için, güvenilir bir [JWT kütüphanesi](https://www.jwt.io/libraries?programming_language=php) kullanılarak dinamik olarak oluşturulan kısa ömürlü tokenlar kullanılması önerilir. + +### Symfony Mercure Kullanarak + +Alternatif olarak, bağımsız bir PHP kütüphanesi olan [Symfony Mercure Bileşenini](https://symfony.com/components/Mercure) kullanabilirsiniz. + +Bu kütüphane, JWT oluşturma, güncelleme yayınlama ve aboneler için çerez tabanlı yetkilendirme işlemlerini ele alır. + +İlk olarak, kütüphaneyi Composer kullanarak yükleyin: + +```console +composer require symfony/mercure lcobucci/jwt +``` + +Daha sonra, bu şekilde kullanabilirsiniz: + +```php +publish(new \Symfony\Component\Mercure\Update('my-topic', json_encode(['key' => 'value']))); + +// FrankenPHP'nin loglarına yaz +error_log("update $updateID published", 4); +``` + +Mercure ayrıca aşağıdaki tarafından doğal olarak desteklenir: + +- [Laravel](laravel.md#mercure-support) +- [Symfony](https://symfony.com/doc/current/mercure.html) +- [API Platform](https://api-platform.com/docs/core/mercure/) diff --git a/docs/tr/metrics.md b/docs/tr/metrics.md new file mode 100644 index 0000000000..381bb802a5 --- /dev/null +++ b/docs/tr/metrics.md @@ -0,0 +1,17 @@ +# Metrikler + +[Caddy metrikleri](https://caddyserver.com/docs/metrics) etkinleştirildiğinde, FrankenPHP aşağıdaki metrikleri sunar: + +- `frankenphp_total_threads`: Toplam PHP iş parçacığı sayısı. +- `frankenphp_busy_threads`: Şu anda bir isteği işleyen PHP iş parçacığı sayısı (çalışan işçiler her zaman bir iş parçacığı tüketir). +- `frankenphp_queue_depth`: Düzenli olarak kuyruğa alınmış isteklerin sayısı. +- `frankenphp_total_workers{worker="[worker_name]"}`: Toplam işçi sayısı. +- `frankenphp_busy_workers{worker="[worker_name]"}`: Şu anda bir isteği işleyen işçi sayısı. +- `frankenphp_worker_request_time{worker="[worker_name]"}`: Tüm işçiler tarafından isteklerin işlenmesi için harcanan süre. +- `frankenphp_worker_request_count{worker="[worker_name]"}`: Tüm işçiler tarafından işlenen istek sayısı. +- `frankenphp_ready_workers{worker="[worker_name]"}`: `frankenphp_handle_request`'i en az bir kez çağıran işçi sayısı. +- `frankenphp_worker_crashes{worker="[worker_name]"}`: Bir işçinin beklenmedik bir şekilde sonlandığı sayı. +- `frankenphp_worker_restarts{worker="[worker_name]"}`: Bir işçinin bilerek yeniden başlatıldığı sayı. +- `frankenphp_worker_queue_depth{worker="[worker_name]"}`: Kuyruğa alınmış istek sayısı. + +İşçi metrikleri için, `[worker_name]` yer tutucusu Caddyfile'daki işçi adıyla değiştirilir, aksi takdirde işçi dosyasının mutlak yolu kullanılacaktır. diff --git a/docs/tr/production.md b/docs/tr/production.md index 92f1d0096c..5ba94e7507 100644 --- a/docs/tr/production.md +++ b/docs/tr/production.md @@ -1,10 +1,10 @@ # Production Ortamına Dağıtım -Bu dokümanda, Docker Compose kullanarak bir PHP uygulamasını tek bir sunucuya nasıl dağıtacağımızı öğreneceğiz. +Bu eğitimde, Docker Compose kullanarak bir PHP uygulamasını tek bir sunucuya nasıl dağıtacağımızı öğreneceğiz. -Symfony kullanıyorsanız, Symfony Docker projesinin (FrankenPHP kullanan) "[Production ortamına dağıtım](https://github.com/dunglas/symfony-docker/blob/main/docs/production.md)" dokümanını okumayı tercih edebilirsiniz. +Symfony kullanıyorsanız, Symfony Docker projesinin (FrankenPHP kullanan) "[Production ortamına dağıtım](https://github.com/dunglas/symfony-docker/blob/main/docs/production.md)" belgesini okumayı tercih edebilirsiniz. -API Platform (FrankenPHP de kullanır) tercih ediyorsanız, [çerçevenin dağıtım dokümanına](https://api-platform.com/docs/deployment/) bakabilirsiniz. +API Platform kullanıyorsanız (o da FrankenPHP kullanır), [çerçevenin dağıtım belgesine](https://api-platform.com/docs/deployment/) başvurabilirsiniz. ## Uygulamanızı Hazırlama @@ -18,6 +18,9 @@ ENV SERVER_NAME=your-domain-name.example.com # HTTPS'yi devre dışı bırakmak istiyorsanız, bunun yerine bu değeri kullanın: #ENV SERVER_NAME=:80 +# Projeniz "public" dizinini web kökü olarak kullanmıyorsa, bunu burada ayarlayabilirsiniz: +# ENV SERVER_ROOT=web/ + # PHP production ayarlarını etkinleştirin RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini" @@ -28,7 +31,7 @@ COPY . /app/public ``` Daha fazla ayrıntı ve seçenek için "[Özel Docker İmajı Oluşturma](docker.md)" bölümüne bakın, -ve yapılandırmayı nasıl özelleştireceğinizi öğrenmek için PHP eklentilerini ve Caddy modüllerini yükleyin. +ve yapılandırmayı nasıl özelleştireceğinizi, PHP eklentilerini ve Caddy modüllerini nasıl kuracağınızı öğrenmek için. Projeniz Composer kullanıyorsa, Docker imajına dahil ettiğinizden ve bağımlılıklarınızı yüklediğinizden emin olun. @@ -48,7 +51,7 @@ services: - caddy_data:/data - caddy_config:/config -# Caddy sertifikaları ve yapılandırması için gereken yığınlar (volumes) +# Caddy sertifikaları ve yapılandırması için gereken birimler volumes: caddy_data: caddy_config: @@ -57,17 +60,17 @@ volumes: > [!NOTE] > > Önceki örnekler production kullanımı için tasarlanmıştır. -> Geliştirme aşamasında, bir yığın (volume), farklı bir PHP yapılandırması ve `SERVER_NAME` ortam değişkeni için farklı bir değer kullanmak isteyebilirsiniz. +> Geliştirme aşamasında, bir birim (volume), farklı bir PHP yapılandırması ve `SERVER_NAME` ortam değişkeni için farklı bir değer kullanmak isteyebilirsiniz. > -> (FrankenPHP kullanan) çok aşamalı Composer, ekstra PHP eklentileri vb. içeren imajlara başvuran daha gelişmiş bir örnek için [Symfony Docker](https://github.com/dunglas/symfony-docker) projesine bir göz atın. +> Çok aşamalı imajlar, Composer, ekstra PHP eklentileri vb. kullanan daha gelişmiş bir örnek için [Symfony Docker](https://github.com/dunglas/symfony-docker) projesine (FrankenPHP kullanır) bir göz atın. Son olarak, eğer Git kullanıyorsanız, bu dosyaları commit edin ve push edin. ## Sunucu Hazırlama Uygulamanızı production ortamına dağıtmak için bir sunucuya ihtiyacınız vardır. -Bu dokümanda, DigitalOcean tarafından sağlanan bir sanal makine kullanacağız, ancak herhangi bir Linux sunucusu çalışabilir. -Docker yüklü bir Linux sunucunuz varsa, doğrudan [bir sonraki bölüme](#alan-adı-yapılandırma) geçebilirsiniz. +Bu eğitimde, DigitalOcean tarafından sağlanan bir sanal makine kullanacağız, ancak herhangi bir Linux sunucusu çalışabilir. +Docker yüklü bir Linux sunucunuz varsa, doğrudan [bir sonraki bölüme](#configuring-a-domain-name) geçebilirsiniz. Aksi takdirde, 200 $ ücretsiz kredi almak için [bu ortaklık bağlantısını](https://m.do.co/c/5d8aabe3ab80) kullanın, bir hesap oluşturun ve ardından "Create a Droplet" seçeneğine tıklayın. Ardından, "Bir imaj seçin" bölümünün altındaki "Marketplace" sekmesine tıklayın ve "Docker" adlı uygulamayı bulun. @@ -76,10 +79,10 @@ Bu, Docker ve Docker Compose'un en son sürümlerinin zaten yüklü olduğu bir Test amaçlı kullanım için en ucuz planlar yeterli olacaktır. Gerçek production kullanımı için, muhtemelen ihtiyaçlarınıza uyacak şekilde "genel amaçlı" bölümünden bir plan seçmek isteyeceksiniz. -![Docker ile DigitalOcean FrankenPHP](../digitalocean-droplet.png) +![DigitalOcean'da Docker ile FrankenPHP Dağıtımı](digitalocean-droplet.png) Diğer ayarlar için varsayılanları koruyabilir veya ihtiyaçlarınıza göre değiştirebilirsiniz. -SSH anahtarınızı eklemeyi veya bir parola oluşturmayı unutmayın, ardından "Sonlandır ve oluştur" düğmesine basın. +SSH anahtarınızı eklemeyi veya bir parola oluşturmayı unutmayın, ardından "Finalize and create" düğmesine basın. Ardından, Droplet'iniz hazırlanırken birkaç saniye bekleyin. Droplet'iniz hazır olduğunda, bağlanmak için SSH kullanın: @@ -101,11 +104,11 @@ your-domain-name.example.com. IN A 207.154.233.113 DigitalOcean Alan Adları hizmetiyle ilgili örnek ("Networking" > "Domains"): -![DigitalOcean'da DNS Yapılandırma](../digitalocean-dns.png) +![DigitalOcean'da DNS Yapılandırma](digitalocean-dns.png) > [!NOTE] > -> FrankenPHP tarafından varsayılan olarak otomatik olarak TLS sertifikası oluşturmak için kullanılan hizmet olan Let's Encrypt, direkt IP adreslerinin kullanılmasını desteklemez. Let's Encrypt'i kullanmak için alan adı kullanmak zorunludur. +> FrankenPHP tarafından varsayılan olarak otomatik olarak TLS sertifikası oluşturmak için kullanılan hizmet olan Let's Encrypt, sadece IP adreslerinin kullanılmasını desteklemez. Let's Encrypt'i kullanmak için alan adı kullanmak zorunludur. ## Dağıtım @@ -119,13 +122,13 @@ Git ile örnek: git clone git@github.com:/.git ``` -Projenizi içeren dizine gidin (``) ve uygulamayı production modunda başlatın: +Projenizi içeren dizine gidin (``) ve uygulamayı production modunda başlatın: ```console -docker compose up -d --wait +docker compose up --wait ``` -Sunucunuz hazır ve çalışıyor. Sizin için otomatik olarak bir HTTPS sertifikası oluşturuldu. +Sunucunuz hazır ve çalışıyor, ve sizin için otomatik olarak bir HTTPS sertifikası oluşturuldu. `https://your-domain-name.example.com` adresine gidin ve keyfini çıkarın! > [!CAUTION] @@ -136,4 +139,4 @@ Sunucunuz hazır ve çalışıyor. Sizin için otomatik olarak bir HTTPS sertifi Uygulamanızı bir makine kümesine dağıtmak istiyorsanız, [Docker Swarm](https://docs.docker.com/engine/swarm/stack-deploy/) kullanabilirsiniz, sağlanan Compose dosyaları ile uyumludur. -Kubernetes üzerinde dağıtım yapmak için FrankenPHP kullanan [API Platformu ile sağlanan Helm grafiğine](https://api-platform.com/docs/deployment/kubernetes/) göz atın. +Kubernetes üzerinde dağıtım yapmak için, FrankenPHP kullanan [API Platform ile sunulan Helm grafiğine](https://api-platform.com/docs/deployment/kubernetes/) göz atın. diff --git a/docs/tr/static.md b/docs/tr/static.md index 522cd70d62..4ae7abdda6 100644 --- a/docs/tr/static.md +++ b/docs/tr/static.md @@ -1,35 +1,62 @@ # Statik Yapı Oluşturun PHP kütüphanesinin yerel kurulumunu kullanmak yerine, -harika [static-php-cli projesi](https://github.com/crazywhalecc/static-php-cli) sayesinde FrankenPHP'nin statik bir yapısını oluşturmak mümkündür (adına rağmen, bu proje sadece CLI'yi değil, tüm SAPI'leri destekler). +harika [static-php-cli projesi](https://github.com/crazywhalecc/static-php-cli) sayesinde FrankenPHP'nin statik veya çoğunlukla statik bir yapısını oluşturmak mümkündür (adına rağmen, bu proje sadece CLI'yı değil, tüm SAPI'leri destekler). Bu yöntemle, tek, taşınabilir bir ikili PHP yorumlayıcısını, Caddy web sunucusunu ve FrankenPHP'yi içerecektir! -FrankenPHP ayrıca [PHP uygulamasının statik binary gömülmesini](embed.md) destekler. +Tamamen statik yerel yürütülebilir dosyalar hiçbir bağımlılık gerektirmez ve hatta [`scratch` Docker imajı](https://docs.docker.com/build/building/base-images/#create-a-minimal-base-image-using-scratch) üzerinde bile çalıştırılabilir. Ancak, dinamik PHP eklentilerini (Xdebug gibi) yükleyemezler ve musl libc kullandıkları için bazı sınırlamalara sahiptirler. + +Çoğunlukla statik ikililer yalnızca `glibc` gerektirir ve dinamik eklentileri yükleyebilir. + +Mümkün olduğunda, glibc tabanlı, çoğunlukla statik yapıları kullanmanızı öneririz. + +FrankenPHP ayrıca [PHP uygulamasının statik ikiliye gömülmesini](embed.md) destekler. ## Linux -Linux statik binary dosyası oluşturmak için bir Docker imajı sağlıyoruz: +Statik Linux ikilileri oluşturmak için Docker imajları sağlıyoruz: + +### musl Tabanlı, Tamamen Statik Yapı + +Hiçbir bağımlılık olmadan herhangi bir Linux dağıtımında çalışan ancak eklentilerin dinamik yüklenmesini desteklemeyen tamamen statik bir ikili için: + +```console +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 +``` + +Yoğun eşzamanlı senaryolarda daha iyi performans için [mimalloc](https://github.com/microsoft/mimalloc) ayırıcısını kullanmayı düşünebilirsiniz. ```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 +docker buildx bake --load --set static-builder-musl.args.MIMALLOC=1 static-builder-musl ``` -Elde edilen statik binary `frankenphp` olarak adlandırılır ve geçerli dizinde kullanılabilir. +### glibc Tabanlı, Çoğunlukla Statik Yapı (Dinamik Eklenti Desteği ile) -Statik binary dosyasını Docker olmadan oluşturmak istiyorsanız, Linux için de çalışan macOS talimatlarına bir göz atın. +Seçilen eklentiler statik olarak derlenmiş olsa da PHP eklentilerini dinamik olarak yüklemeyi destekleyen bir ikili için: + +```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 +``` + +Bu ikili tüm glibc 2.17 ve üzeri sürümlerini destekler ancak musl tabanlı sistemlerde (Alpine Linux gibi) çalışmaz. + +Elde edilen çoğunlukla statik (glibc hariç) ikili `frankenphp` olarak adlandırılır ve geçerli dizinde mevcuttur. + +Statik ikiliyi Docker olmadan oluşturmak istiyorsanız, Linux için de çalışan macOS talimatlarına bir göz atın. ### Özel Eklentiler -Varsayılan olarak, en popüler PHP eklentileri zaten derlenir. +Varsayılan olarak, en popüler PHP eklentileri derlenir. -Binary dosyanın boyutunu küçültmek ve saldırı yüzeyini azaltmak için `PHP_EXTENSIONS` Docker ARG'sini kullanarak derlenecek eklentilerin listesini seçebilirsiniz. +İkilinin boyutunu küçültmek ve saldırı yüzeyini azaltmak için, `PHP_EXTENSIONS` Docker ARG'sini kullanarak derlenecek eklentilerin listesini seçebilirsiniz. Örneğin, yalnızca `opcache` eklentisini derlemek için aşağıdaki komutu çalıştırın: ```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 # ... ``` @@ -38,20 +65,20 @@ Etkinleştirdiğiniz eklentilere ek işlevler sağlayan kütüphaneler eklemek i ```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 ``` ### Ekstra Caddy Modülleri -Ekstra Caddy modülleri eklemek veya [xcaddy](https://github.com/caddyserver/xcaddy) adresine başka argümanlar iletmek için `XCADDY_ARGS` Docker ARG'sini kullanın: +Ekstra Caddy modülleri eklemek veya [xcaddy](https://github.com/caddyserver/xcaddy)'ye diğer argümanları iletmek için `XCADDY_ARGS` Docker ARG'sini kullanın: ```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 + --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 ``` Bu örnekte, Caddy için [Souin](https://souin.io) HTTP önbellek modülünün yanı sıra [cbrotli](https://github.com/dunglas/caddy-cbrotli), [Mercure](https://mercure.rocks) ve [Vulcain](https://vulcain.rocks) modüllerini ekliyoruz. @@ -61,20 +88,20 @@ Bu örnekte, Caddy için [Souin](https://souin.io) HTTP önbellek modülünün y > cbrotli, Mercure ve Vulcain modülleri, `XCADDY_ARGS` boşsa veya ayarlanmamışsa varsayılan olarak dahil edilir. > Eğer `XCADDY_ARGS` değerini özelleştirirseniz, dahil edilmelerini istiyorsanız bunları açıkça dahil etmelisiniz. -Derlemeyi nasıl [özelleştireceğinize](#yapıyı-özelleştirme) de bakın. +Derlemeyi nasıl [özelleştireceğinize](#customizing-the-build) de bakın. ### GitHub Token GitHub API kullanım limitine ulaşırsanız, `GITHUB_TOKEN` adlı bir ortam değişkeninde bir GitHub Personal Access Token ayarlayın: ```console -GITHUB_TOKEN="xxx" docker --load buildx bake static-builder +GITHUB_TOKEN="xxx" docker --load buildx bake static-builder-musl # ... ``` ## macOS -macOS için statik bir binary oluşturmak için aşağıdaki betiği çalıştırın ([Homebrew](https://brew.sh/) yüklü olmalıdır): +macOS için statik bir ikili oluşturmak için aşağıdaki betiği çalıştırın ([Homebrew](https://brew.sh/) yüklü olmalıdır): ```console git clone https://github.com/php/frankenphp @@ -82,19 +109,50 @@ cd frankenphp ./build-static.sh ``` -Not: Bu betik Linux'ta (ve muhtemelen diğer Unix'lerde) da çalışır ve sağladığımız Docker tabanlı statik derleyici tarafından dahili olarak kullanılır. +Not: Bu betik Linux'ta (ve muhtemelen diğer Unix'lerde) da çalışır ve sağladığımız Docker imajları tarafından dahili olarak kullanılır. ## Yapıyı Özelleştirme -Aşağıdaki ortam değişkenleri `docker build` ve `build-static.sh` dosyalarına aktarılabilir -statik derlemeyi özelleştirmek için betik: +Aşağıdaki ortam değişkenleri `docker build` ve `build-static.sh` betiğine aktarılabilir +statik yapıyı özelleştirmek için: - `FRANKENPHP_VERSION`: kullanılacak FrankenPHP sürümü - `PHP_VERSION`: kullanılacak PHP sürümü - `PHP_EXTENSIONS`: oluşturulacak PHP eklentileri ([desteklenen eklentiler listesi](https://static-php.dev/en/guide/extensions.html)) - `PHP_EXTENSION_LIBS`: eklentilere özellikler ekleyen oluşturulacak ekstra kütüphaneler -- `XCADDY_ARGS`: [xcaddy](https://github.com/caddyserver/xcaddy) adresine iletilecek argümanlar, örneğin ekstra Caddy modülleri eklemek için -- `EMBED`: binary dosyaya gömülecek PHP uygulamasının yolu +- `XCADDY_ARGS`: [xcaddy](https://github.com/caddyserver/xcaddy)'ye iletilecek argümanlar, örneğin ekstra Caddy modülleri eklemek için +- `EMBED`: ikili dosyaya gömülecek PHP uygulamasının yolu - `CLEAN`: ayarlandığında, libphp ve tüm bağımlılıkları sıfırdan oluşturulur (önbellek yok) -- `DEBUG_SYMBOLS`: ayarlandığında, hata ayıklama sembolleri ayıklanmayacak ve binary dosyaya eklenecektir -- `RELEASE`: (yalnızca bakımcılar) ayarlandığında, ortaya çıkan binary dosya GitHub'a yüklenecektir +- `NO_COMPRESS`: UPX kullanarak ortaya çıkan ikiliyi sıkıştırma +- `DEBUG_SYMBOLS`: ayarlandığında, hata ayıklama sembolleri ayıklanmayacak ve ikili dosyaya eklenecektir +- `MIMALLOC`: (deneysel, yalnızca Linux) musl'un mallocng'sini [mimalloc](https://github.com/microsoft/mimalloc) ile daha iyi performans için değiştirir. Bunu yalnızca musl hedefli derlemeler için kullanmanızı öneririz, glibc için bu seçeneği devre dışı bırakmayı ve ikilinizi çalıştırırken [`LD_PRELOAD`](https://microsoft.github.io/mimalloc/overrides.html) kullanmayı tercih edin. +- `RELEASE`: (yalnızca bakımcılar) ayarlandığında, ortaya çıkan ikili dosya GitHub'a yüklenecektir + +## Eklentiler + +glibc veya macOS tabanlı ikililerle, PHP eklentilerini dinamik olarak yükleyebilirsiniz. Ancak, bu eklentilerin ZTS desteğiyle derlenmesi gerekecektir. Çoğu paket yöneticisi şu anda eklentilerinin ZTS sürümlerini sunmadığından, bunları kendiniz derlemeniz gerekecektir. + +Bunun için `static-builder-gnu` Docker kapsayıcısını derleyebilir ve çalıştırabilir, içine uzaktan bağlanabilir ve eklentileri `./configure --with-php-config=/go/src/app/dist/static-php-cli/buildroot/bin/php-config` ile derleyebilirsiniz. + +[Xdebug eklentisi](https://xdebug.org) için örnek adımlar: + +```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 +``` + +Bu, mevcut dizinde `frankenphp` ve `xdebug-zts.so`'yu oluşturmuş olacaktır. `xdebug-zts.so`'yu uzantı dizininize taşırsanız, php.ini dosyanıza `zend_extension=xdebug-zts.so` ekler ve FrankenPHP'yi çalıştırırsanız, Xdebug'ı yükleyecektir. diff --git a/docs/tr/wordpress.md b/docs/tr/wordpress.md new file mode 100644 index 0000000000..99e9e4252b --- /dev/null +++ b/docs/tr/wordpress.md @@ -0,0 +1,59 @@ +# WordPress + +[WordPress](https://wordpress.org/)'i FrankenPHP ile çalıştırarak otomatik HTTPS, HTTP/3 ve Zstandard sıkıştırma özelliklerine sahip modern, yüksek performanslı bir yığının keyfini çıkarın. + +## Minimal Kurulum + +1. [WordPress'i İndirin](https://wordpress.org/download/) +2. ZIP arşivini çıkarın ve çıkarılan dizinde bir terminal açın +3. Çalıştırın: + + ```console + frankenphp php-server + ``` + +4. `http://localhost/wp-admin/` adresine gidin ve kurulum talimatlarını izleyin +5. Keyfini çıkarın! + +Üretime hazır bir kurulum için, `frankenphp run` komutunu aşağıdaki gibi bir `Caddyfile` ile kullanmayı tercih edin: + +```caddyfile +example.com + +php_server +encode zstd br gzip +log +``` + +## Anında Yenileme + +WordPress ile [anında yenileme](hot-reload.md) özelliğini kullanmak için, [Mercure](mercure.md)'u etkinleştirin ve `Caddyfile` dosyanızdaki `php_server` direktifine `hot_reload` alt direktifini ekleyin: + +```caddyfile +localhost + +mercure { + anonymous +} + +php_server { + hot_reload +} +``` + +Ardından, JavaScript kütüphanelerini WordPress temanızın `functions.php` dosyasına yüklemek için gerekli kodu ekleyin: + +```php +function hot_reload() { + ?> + + + + + + [!TIP] +> Aşağıdaki bölüm yalnızca Symfony 7.4 öncesi için gereklidir; Symfony 7.4 ile FrankenPHP worker modu için yerel destek sunulmuştur. FrankenPHP'nin worker modu [Symfony Runtime Component](https://symfony.com/doc/current/components/runtime.html) tarafından desteklenmektedir. Herhangi bir Symfony uygulamasını bir worker'da başlatmak için [PHP Runtime](https://github.com/php-runtime/runtime)'ın FrankenPHP paketini yükleyin: @@ -54,13 +66,13 @@ Bkz. [ilgili doküman](laravel.md#laravel-octane). ## Özel Uygulamalar -Aşağıdaki örnek, üçüncü taraf bir kütüphaneye güvenmeden kendi çalışan kodunuzu nasıl oluşturacağınızı göstermektedir: +Aşağıdaki örnek, üçüncü taraf bir kütüphaneye güvenmeden kendi worker betiğinizi nasıl oluşturacağınızı göstermektedir: ```php boot(); // Daha iyi performans için döngü dışında işleyici (daha az iş yapıyor) $handler = static function () use ($myApp) { - // Bir istek alındığında çağrılır, - // superglobals, php://input ve benzerleri sıfırlanır - echo $myApp->handle($_GET, $_POST, $_COOKIE, $_FILES, $_SERVER); + try { + // Bir istek alındığında çağrılır, + // superglobals, php://input ve benzerleri sıfırlanır + echo $myApp->handle($_GET, $_POST, $_COOKIE, $_FILES, $_SERVER); + } catch (\Throwable $exception) { + // `set_exception_handler` yalnızca worker betiği sona erdiğinde çağrılır, + // bu beklediğiniz gibi olmayabilir, bu yüzden istisnaları burada yakalayın ve ele alın + (new \MyCustomExceptionHandler)->handleException($exception); + } }; -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); // HTTP yanıtını gönderdikten sonra bir şey yapın $myApp->terminate(); // Bir sayfa oluşturmanın ortasında tetiklenme olasılığını azaltmak için çöp toplayıcıyı çağırın gc_collect_cycles(); + + if (!$keepRunning) break; } // Temizleme $myApp->shutdown(); ``` -Ardından, uygulamanızı başlatın ve çalışanınızı yapılandırmak için `FRANKENPHP_CONFIG` ortam değişkenini kullanın: +Ardından, uygulamanızı başlatın ve worker'ınızı yapılandırmak için `FRANKENPHP_CONFIG` ortam değişkenini kullanın: ```console docker run \ @@ -113,12 +134,58 @@ docker run \ ### Belirli Sayıda İstekten Sonra Worker'ı Yeniden Başlatın - - PHP başlangıçta uzun süreli işlemler için tasarlanmadığından, hala bellek sızdıran birçok kütüphane ve eski kod vardır. - - - Bu tür kodları worker modunda kullanmak için geçici bir çözüm, belirli sayıda isteği işledikten sonra worker betiğini yeniden başlatmaktır: Önceki worker kod parçacığı, `MAX_REQUESTS` adlı bir ortam değişkeni ayarlayarak işlenecek maksimum istek sayısını yapılandırmaya izin verir. + +### Worker'ları Manuel Olarak Yeniden Başlatın + +Worker'ları [dosya değişikliklerinde](config.md#watching-for-file-changes) yeniden başlatmak mümkünken, tüm worker'ları +[Caddy yönetici API'si](https://caddyserver.com/docs/api) aracılığıyla sorunsuz bir şekilde yeniden başlatmak da mümkündür. Yönetici API'si +[Caddyfile](config.md#caddyfile-config) dosyanızda etkinleştirilmişse, yeniden başlatma uç noktasına aşağıdaki gibi basit bir POST isteği gönderebilirsiniz: + +```console +curl -X POST http://localhost:2019/frankenphp/workers/restart +``` + +### Worker Hataları + +Bir worker betiği sıfır olmayan bir çıkış koduyla çökerse, FrankenPHP onu üstel geri çekilme (exponential backoff) stratejisiyle yeniden başlatacaktır. +Eğer worker betiği son geri çekilmenin * 2 katından daha uzun süre çalışır durumda kalırsa, +worker betiğini cezalandırmayacak ve tekrar yeniden başlatacaktır. +Ancak, worker betiği kısa bir süre içinde sıfır olmayan bir çıkış koduyla başarısız olmaya devam ederse +(örneğin, bir betikte yazım hatası olması), FrankenPHP `too many consecutive failures` hatasıyla çökecektir. + +Ardışık hata sayısı, [Caddyfile](config.md#caddyfile-config) dosyanızda `max_consecutive_failures` seçeneğiyle yapılandırılabilir: + +```caddyfile +frankenphp { + worker { + # ... + max_consecutive_failures 10 + } +} +``` + +## Süperküresel Değişkenlerin Davranışı + +[PHP süperküresel değişkenleri](https://www.php.net/manual/en/language.variables.superglobals.php) (`$_SERVER`, `$_ENV`, `$_GET`...) +şu şekilde davranır: + +- `frankenphp_handle_request()` fonksiyonunun ilk çağrılmasından önce, süperküresel değişkenler worker betiğinin kendisine bağlı değerleri içerir +- `frankenphp_handle_request()` çağrısı sırasında ve sonrasında, süperküresel değişkenler işlenmiş HTTP isteğinden üretilen değerleri içerir; `frankenphp_handle_request()` fonksiyonunun her çağrısı süperküresel değişkenlerin değerlerini değiştirir + +Geri arama (callback) içinde worker betiğinin süperküresel değişkenlerine erişmek için, bunları kopyalamanız ve kopyayı geri aramanın kapsamına (scope) aktarmanız gerekir: + +```php + 'Chinese', + 'fr' => 'French', + 'ja' => 'Japanese', + 'pt-br' => 'Portuguese (Brazilian)', + 'ru' => 'Russian', + 'tr' => 'Turkish', +]; + +function makeGeminiRequest(string $systemPrompt, string $userPrompt, string $model, string $apiKey): string +{ + $url = "https://generativelanguage.googleapis.com/v1beta/models/$model:generateContent"; + $body = json_encode([ + "contents" => [ + ["role" => "model", "parts" => ['text' => $systemPrompt]], + ["role" => "user", "parts" => ['text' => $userPrompt]] + ], + ]); + + $response = @file_get_contents($url, false, stream_context_create([ + 'http' => [ + 'method' => 'POST', + 'header' => "Content-Type: application/json\r\nX-Goog-Api-Key: $apiKey\r\nContent-Length: " . strlen($body) . "\r\n", + 'content' => $body, + 'timeout' => 300, + ] + ])); + $generatedDocs = json_decode($response, true)['candidates'][0]['content']['parts'][0]['text'] ?? ''; + + if (!$response || !$generatedDocs) { + print_r(error_get_last()); + print_r($response); + exit(1); + } + + return $generatedDocs; +} + +function createPrompt(string $language, string $englishFile, string $currentTranslation): array +{ + $systemPrompt = << str_ends_with($filename, '.md')); +foreach ($files as $file) { + $englishFile = file_get_contents(__DIR__ . "/$file"); + if ($fileToTranslate && $fileToTranslate !== $file && "$fileToTranslate.md" !== $file) { + continue; + } + foreach (LANGUAGES as $language => $languageName) { + echo "Translating $file to $languageName\n"; + $currentTranslation = file_get_contents(__DIR__ . "/$language/$file") ?: ''; + [$systemPrompt, $userPrompt] = createPrompt($language, $englishFile, $currentTranslation); + $markdown = makeGeminiRequest($systemPrompt, $userPrompt, MODEL, $apiKey); + + echo "Writing translated file to $language/$file\n"; + file_put_contents(__DIR__ . "/$language/$file", sanitizeMarkdown($markdown)); + + echo "sleeping to avoid rate limiting...\n"; + sleep(SLEEP_SECONDS_BETWEEN_REQUESTS); + } +} From 01bcaaf7e249d2bdf5f7c0ecb0f6254ffc498dc6 Mon Sep 17 00:00:00 2001 From: Alliballibaba Date: Wed, 7 Jan 2026 21:47:49 +0100 Subject: [PATCH 2/9] LLM translation. --- docs/tr/performance.md | 154 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 154 insertions(+) create mode 100644 docs/tr/performance.md diff --git a/docs/tr/performance.md b/docs/tr/performance.md new file mode 100644 index 0000000000..6b91f44646 --- /dev/null +++ b/docs/tr/performance.md @@ -0,0 +1,154 @@ +# Performans + +Varsayılan olarak, FrankenPHP performans ve kullanım kolaylığı arasında iyi bir denge sunmaya çalışır. Ancak, uygun bir yapılandırma kullanarak performansı önemli ölçüde artırmak mümkündür. + +## İş Parçacığı ve Çalışan Sayısı + +Varsayılan olarak, FrankenPHP, mevcut CPU sayısının 2 katı kadar iş parçacığı ve çalışan (worker modunda) başlatır. + +Uygun değerler, uygulamanızın nasıl yazıldığına, ne yaptığına ve donanımınıza büyük ölçüde bağlıdır. Bu değerleri değiştirmenizi şiddetle tavsiye ederiz. En iyi sistem kararlılığı için, `num_threads` x `memory_limit` < `available_memory` olmasına sahip olmanız önerilir. + +Doğru değerleri bulmak için gerçek trafiği simüle eden yük testleri çalıştırmak en iyisidir. [k6](https://k6.io) ve [Gatling](https://gatling.io) bunun için iyi araçlardır. + +İş parçacığı sayısını yapılandırmak için `php_server` ve `php` direktiflerinin `num_threads` seçeneğini kullanın. Çalışan sayısını değiştirmek için `frankenphp` direktifinin `worker` bölümündeki `num` seçeneğini kullanın. + +### `max_threads` + +Trafiğinizin tam olarak nasıl olacağını bilmek her zaman daha iyi olsa da, gerçek hayattaki uygulamalar daha öngörülemez olma eğilimindedir. `max_threads` [yapılandırması](config.md#caddyfile-config), FrankenPHP'nin belirtilen sınıra kadar çalışma zamanında otomatik olarak ek iş parçacıkları oluşturmasına olanak tanır. `max_threads`, trafiğinizi yönetmek için kaç iş parçacığına ihtiyacınız olduğunu belirlemenize yardımcı olabilir ve sunucuyu gecikme artışlarına karşı daha dirençli hale getirebilir. `auto` olarak ayarlanırsa, limit `php.ini` dosyanızdaki `memory_limit` değerine göre tahmin edilecektir. Bunu yapamazsa, `auto` bunun yerine varsayılan olarak `num_threads`'in 2 katına ayarlanır. `auto`'nun ihtiyaç duyulan iş parçacığı sayısını büyük ölçüde hafife alabileceğini unutmayın. `max_threads`, PHP FPM'nin [pm.max_children](https://www.php.net/manual/en/install.fpm.configuration.php#pm.max-children) özelliğine benzer. Temel fark, FrankenPHP'nin işlemler yerine iş parçacıkları kullanması ve bunları gerektiğinde farklı çalışan betiklerine ve 'klasik moda' otomatik olarak devretmesidir. + +## Çalışan Modu + +[Çalışan modunu](worker.md) etkinleştirmek performansı önemli ölçüde artırır, ancak uygulamanızın bu modla uyumlu olacak şekilde uyarlanması gerekir: bir çalışan betiği oluşturmanız ve uygulamanın bellek sızıntısı yapmadığından emin olmanız gerekir. + +## musl Kullanmayın + +Resmi Docker imajlarının Alpine Linux varyantı ve sağladığımız varsayılan ikili dosyalar [musl libc](https://musl.libc.org) kullanmaktadır. + +PHP'nin, geleneksel GNU kütüphanesi yerine bu alternatif C kütüphanesini kullanırken [daha yavaş](https://gitlab.alpinelinux.org/alpine/aports/-/issues/14381) olduğu bilinmektedir, özellikle FrankenPHP için gerekli olan ZTS modunda (iş parçacığı güvenli) derlendiğinde. Fark, yoğun iş parçacıklı bir ortamda önemli olabilir. + +Ayrıca, [bazı hatalar sadece musl kullanılırken](https://github.com/php/php-src/issues?q=sort%3Aupdated-desc+is%3Aissue+is%3Aopen+label%3ABug+musl) ortaya çıkar. + +Üretim ortamlarında, glibc'ye bağlanmış, uygun bir optimizasyon seviyesiyle derlenmiş FrankenPHP kullanmanızı öneririz. + +Bu, Debian Docker imajlarını kullanarak, sürdürücülerimizin [.deb](https://debs.henderkes.com) veya [.rpm](https://rpms.henderkes.com) paketlerini kullanarak veya [FrankenPHP'yi kaynak koddan derleyerek](compile.md) başarılabilir. + +## Go Çalışma Zamanı Yapılandırması + +FrankenPHP Go dilinde yazılmıştır. + +Genel olarak, Go çalışma zamanı özel bir yapılandırma gerektirmez, ancak belirli durumlarda, özel yapılandırma performansı artırır. + +`GODEBUG` ortam değişkenini `cgocheck=0` olarak ayarlamak isteyebilirsiniz (FrankenPHP Docker imajlarında varsayılan değerdir). + +FrankenPHP'yi kapsayıcılarda (Docker, Kubernetes, LXC...) çalıştırıyorsanız ve kapsayıcılar için ayrılan belleği sınırlıyorsanız, `GOMEMLIMIT` ortam değişkenini kullanılabilir bellek miktarına ayarlayın. + +Daha fazla ayrıntı için, çalışma zamanından en iyi şekilde yararlanmak için [bu konuya ayrılmış Go dokümantasyon sayfası](https://pkg.go.dev/runtime#hdr-Environment_Variables) mutlaka okunmalıdır. + +## `file_server` + +Varsayılan olarak, `php_server` direktifi, kök dizinde depolanan statik dosyaları (varlıkları) sunmak için otomatik olarak bir dosya sunucusu kurar. + +Bu özellik kullanışlıdır, ancak bir maliyeti vardır. Devre dışı bırakmak için aşağıdaki yapılandırmayı kullanın: + +```caddyfile +php_server { + file_server off +} +``` + +## `try_files` + +Statik dosyalar ve PHP dosyalarının yanı sıra, `php_server` uygulamanızın dizin ve dizin dizin dosyalarını (`/path/` -> `/path/index.php`) da sunmaya çalışacaktır. Dizin dizinlerine ihtiyacınız yoksa, `try_files`'ı aşağıdaki gibi açıkça tanımlayarak bunları devre dışı bırakabilirsiniz: + +```caddyfile +php_server { + try_files {path} index.php + root /root/to/your/app # buraya kökü açıkça eklemek daha iyi önbelleğe alma sağlar +} +``` + +Bu, gereksiz dosya işlemlerinin sayısını önemli ölçüde azaltabilir. + +0 gereksiz dosya sistemi işlemi ile alternatif bir yaklaşım, bunun yerine `php` direktifini kullanmak ve dosyaları PHP'den yola göre ayırmaktır. Bu yaklaşım, tüm uygulamanızın tek bir giriş dosyası tarafından sunulması durumunda iyi çalışır. `/assets` klasörünün arkasındaki statik dosyaları sunan örnek bir [yapılandırma](config.md#caddyfile-config) şöyle görünebilir: + +```caddyfile +route { + @assets { + path /assets/* + } + + # /assets arkasındaki her şey dosya sunucusu tarafından işlenir + file_server @assets { + root /root/to/your/app + } + + # /assets içinde olmayan her şey index veya worker PHP dosyanız tarafından işlenir + rewrite index.php + php { + root /root/to/your/app # buraya kökü açıkça eklemek daha iyi önbelleğe alma sağlar + } +} +``` + +## Yer Tutucular + +`root` ve `env` direktiflerinde [yer tutucular](https://caddyserver.com/docs/conventions#placeholders) kullanabilirsiniz. Ancak bu, bu değerlerin önbelleğe alınmasını engeller ve önemli bir performans maliyeti getirir. + +Mümkünse, bu direktiflerde yer tutuculardan kaçının. + +## `resolve_root_symlink` + +Varsayılan olarak, belge kökü sembolik bir bağlantıysa, FrankenPHP tarafından otomatik olarak çözümlenir (bu, PHP'nin düzgün çalışması için gereklidir). Belge kökü bir sembolik bağlantı değilse, bu özelliği devre dışı bırakabilirsiniz. + +```caddyfile +php_server { + resolve_root_symlink false +} +``` + +Bu, `root` direktifi [yer tutucular](https://caddyserver.com/docs/conventions#placeholders) içeriyorsa performansı artıracaktır. Diğer durumlarda kazanç ihmal edilebilir olacaktır. + +## Günlükler + +Günlükleme açıkça çok faydalıdır, ancak tanımı gereği, önemli ölçüde performansı düşüren G/Ç işlemleri ve bellek tahsisleri gerektirir. [Günlükleme seviyesini](https://caddyserver.com/docs/caddyfile/options#log) doğru ayarladığınızdan ve yalnızca gerekli olanı günlüğe kaydettiğinizden emin olun. + +## PHP Performansı + +FrankenPHP resmi PHP yorumlayıcısını kullanır. Tüm olağan PHP ile ilgili performans optimizasyonları FrankenPHP ile uygulanır. + +Özellikle: + +- [OPcache](https://www.php.net/manual/en/book.opcache.php)'in kurulu, etkin ve doğru şekilde yapılandırılmış olduğundan emin olun +- [Composer otomatik yükleyici optimizasyonlarını](https://getcomposer.org/doc/articles/autoloader-optimization.md) etkinleştirin +- `realpath` önbelleğinin uygulamanızın ihtiyaçları için yeterince büyük olduğundan emin olun +- [ön yükleme (preloading)](https://www.php.net/manual/en/opcache.preloading.php) kullanın + +Daha fazla ayrıntı için, [Symfony'nin bu konuya ayrılmış dokümantasyon girişini](https://symfony.com/doc/current/performance.html) okuyun (çoğu ipucu Symfony kullanmasanız bile faydalıdır). + +## İş Parçacığı Havuzunu Ayırma + +Uygulamaların, yüksek yük altında güvenilmez olma eğiliminde olan veya sürekli olarak 10 saniyeden fazla yanıt veren bir API gibi yavaş harici servislerle etkileşime girmesi yaygındır. Bu gibi durumlarda, iş parçacığı havuzunu özel "yavaş" havuzlara ayırmak faydalı olabilir. Bu, yavaş uç noktaların tüm sunucu kaynaklarını/iş parçacıklarını tüketmesini önler ve bir bağlantı havuzuna benzer şekilde, yavaş uç noktaya giden isteklerin eşzamanlılığını sınırlar. + +```caddyfile +{ + frankenphp { + max_threads 100 # tüm çalışanlar tarafından paylaşılan maksimum 100 iş parçacığı + } +} + +example.com { + php_server { + root /app/public # uygulamanızın kökü + worker index.php { + match /slow-endpoint/* # /slow-endpoint/* yoluyla gelen tüm istekler bu iş parçacığı havuzu tarafından işlenir + num 10 # /slow-endpoint/* ile eşleşen istekler için minimum 10 iş parçacığı + } + worker index.php { + match * # diğer tüm istekler ayrı ayrı işlenir + num 20 # yavaş uç noktalar asılı kalmaya başlasa bile diğer istekler için minimum 20 iş parçacığı + } + } +} +``` + +Genellikle, çok yavaş uç noktaları, mesaj kuyrukları gibi ilgili mekanizmalar kullanarak eşzamansız olarak ele almak da tavsiye edilir. From d2721e23c629ba97ae8ec8a07b5c22232be1f3ee Mon Sep 17 00:00:00 2001 From: Alliballibaba Date: Thu, 8 Jan 2026 21:58:33 +0100 Subject: [PATCH 3/9] Action test. --- .github/workflows/translate.yaml | 49 ++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 .github/workflows/translate.yaml diff --git a/.github/workflows/translate.yaml b/.github/workflows/translate.yaml new file mode 100644 index 0000000000..4db2ff1a5d --- /dev/null +++ b/.github/workflows/translate.yaml @@ -0,0 +1,49 @@ +--- +name: Translate DOcs +concurrency: + cancel-in-progress: true + group: ${{ github.workflow }}-${{ github.ref }} +on: + workflow_dispatch: +permissions: + contents: write + pull-requests: write +jobs: + build: + name: Translate Docs + runs-on: ubuntu-latest + steps: + - name: Checkout Code + uses: actions/checkout@v6 + with: + fetch-depth: 0 + persist-credentials: false + - name: Set up PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.5' + - name: run translation script + env: + GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }} + run: | + php ./docs/translate.php + - name: Run Linter + uses: super-linter/super-linter@v8 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + LINTER_RULES_PATH: / + MARKDOWN_CONFIG_FILE: .markdown-lint.yaml + FIX_NATURAL_LANGUAGE: true + FIX_MARKDOWN: true + - name: Commit & push changes + run: | + BRANCH="translations/$(date +%s)" + git checkout -b "$BRANCH" + git config --global user.email "github-actions[bot]@users.noreply.github.com" + git config --global user.name "github-actions[bot]" + git add . + git commit -m "chore: auto-lint fixes" + git push --set-upstream origin "$BRANCH" + echo "branch=$BRANCH" >> $GITHUB_OUTPUT + - name: Create Pull Request + uses: peter-evans/create-pull-request@v8 From d5776e58cca6d6085171390de3208f2efb81e1c5 Mon Sep 17 00:00:00 2001 From: Alliballibaba Date: Thu, 8 Jan 2026 21:58:55 +0100 Subject: [PATCH 4/9] More sleeping. --- docs/translate.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/translate.php b/docs/translate.php index 500a20f479..5da049db1d 100644 --- a/docs/translate.php +++ b/docs/translate.php @@ -5,7 +5,7 @@ # needs: php with openssl and gemini api key const MODEL = 'gemini-2.5-flash'; -const SLEEP_SECONDS_BETWEEN_REQUESTS = 5; +const SLEEP_SECONDS_BETWEEN_REQUESTS = 10; const LANGUAGES = [ 'cn' => 'Chinese', 'fr' => 'French', From 6eef23f45edc13f1fa1c0bfa093bed9c57c759af Mon Sep 17 00:00:00 2001 From: Alliballibaba Date: Sat, 10 Jan 2026 17:53:16 +0100 Subject: [PATCH 5/9] Fixes workflow. --- .github/workflows/translate.yaml | 27 ++++++++++--- docs/cn/extensions.md | 60 ++++++++++++++--------------- docs/cn/hot-reload.md | 4 +- docs/cn/mercure.md | 6 +-- docs/fr/extensions.md | 60 ++++++++++++++--------------- docs/ja/extensions.md | 60 ++++++++++++++--------------- docs/ja/logging.md | 6 +-- docs/known-issues.md | 2 +- docs/pt-br/extensions.md | 65 +++++++++++++++----------------- docs/pt-br/logging.md | 6 +-- docs/ru/extensions.md | 60 ++++++++++++++--------------- docs/tr/extensions.md | 56 +++++++++++++-------------- docs/translate.php | 5 ++- 13 files changed, 214 insertions(+), 203 deletions(-) diff --git a/.github/workflows/translate.yaml b/.github/workflows/translate.yaml index 4db2ff1a5d..7ab93a5cdd 100644 --- a/.github/workflows/translate.yaml +++ b/.github/workflows/translate.yaml @@ -1,10 +1,14 @@ --- -name: Translate DOcs +name: Translate Docs concurrency: cancel-in-progress: true group: ${{ github.workflow }}-${{ github.ref }} on: - workflow_dispatch: + push: + branches: + - main + paths: + - 'docs/*' permissions: contents: write pull-requests: write @@ -18,17 +22,27 @@ jobs: with: fetch-depth: 0 persist-credentials: false + - id: md_files + run: | + # Check for changed .md files under docs/*.md + FILES=$(git diff --name-only ${{ github.event.before }} ${{ github.sha }} -- 'docs/*.md') + FILES=$(echo "$FILES" | xargs -n1 basename | tr '\n' ' ') + [ -z "$FILES" ] && echo "found=false" >> $GITHUB_OUTPUT || echo "found=true" >> $GITHUB_OUTPUT + echo "files=$FILES" >> $GITHUB_OUTPUT - name: Set up PHP + if: steps.md_files.outputs.found == 'true' uses: shivammathur/setup-php@v2 with: php-version: '8.5' - name: run translation script + if: steps.md_files.outputs.found == 'true' env: - GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }} + GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }} run: | - php ./docs/translate.php + php ./docs/translate.php ${{ steps.md_files.outputs.files }} - name: Run Linter - uses: super-linter/super-linter@v8 + if: steps.md_files.outputs.found == 'true' + uses: super-linter/super-linter/slim@v8 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} LINTER_RULES_PATH: / @@ -36,6 +50,7 @@ jobs: FIX_NATURAL_LANGUAGE: true FIX_MARKDOWN: true - name: Commit & push changes + if: steps.md_files.outputs.found == 'true' run: | BRANCH="translations/$(date +%s)" git checkout -b "$BRANCH" @@ -44,6 +59,6 @@ jobs: git add . git commit -m "chore: auto-lint fixes" git push --set-upstream origin "$BRANCH" - echo "branch=$BRANCH" >> $GITHUB_OUTPUT + echo "branch=$BRANCH" >> "$GITHUB_OUTPUT" - name: Create Pull Request uses: peter-evans/create-pull-request@v8 diff --git a/docs/cn/extensions.md b/docs/cn/extensions.md index d10f06dc89..2192138c89 100644 --- a/docs/cn/extensions.md +++ b/docs/cn/extensions.md @@ -77,8 +77,8 @@ func repeat_this(s *C.zend_string, count int64, reverse bool) unsafe.Pointer { 这里有两个重要的事情要注意: -- 指令注释 `//export_php:function` 定义了 PHP 中的函数签名。这是生成器知道如何使用正确的参数和返回类型生成 PHP 函数的方式; -- 函数必须返回 `unsafe.Pointer`。FrankenPHP 提供了一个 API 来帮助你在 C 和 Go 之间进行类型转换。 +- 指令注释 `//export_php:function` 定义了 PHP 中的函数签名。这是生成器知道如何使用正确的参数和返回类型生成 PHP 函数的方式; +- 函数必须返回 `unsafe.Pointer`。FrankenPHP 提供了一个 API 来帮助你在 C 和 Go 之间进行类型转换。 虽然第一点不言自明,但第二点可能更难理解。让我们在下一节中深入了解类型转换。 @@ -197,21 +197,21 @@ func process_data_packed(arr *C.zend_array) unsafe.Pointer { **数组转换的关键特性:** -- **有序键值对** - 可选择保留关联数组的顺序 -- **针对多种情况优化** - 可选择放弃顺序以获得更好的性能,或直接转换为切片 -- **自动列表检测** - 转换为 PHP 时,自动检测数组应该是打包列表还是哈希映射 -- **嵌套数组** - 数组可以嵌套,并将自动转换所有支持的类型(`int64`、`float64`、`string`、`bool`、`nil`、`AssociativeArray`、`map[string]any`、`[]any`) -- **不支持对象** - 目前,只有标量类型和数组可以用作值。提供对象将导致 PHP 数组中的 `null` 值。 +- **有序键值对** - 可选择保留关联数组的顺序 +- **针对多种情况优化** - 可选择放弃顺序以获得更好的性能,或直接转换为切片 +- **自动列表检测** - 转换为 PHP 时,自动检测数组应该是打包列表还是哈希映射 +- **嵌套数组** - 数组可以嵌套,并将自动转换所有支持的类型(`int64`、`float64`、`string`、`bool`、`nil`、`AssociativeArray`、`map[string]any`、`[]any`) +- **不支持对象** - 目前,只有标量类型和数组可以用作值。提供对象将导致 PHP 数组中的 `null` 值。 ##### 可用方法:打包和关联 -- `frankenphp.PHPAssociativeArray(arr frankenphp.AssociativeArray) unsafe.Pointer` - 转换为带有键值对的有序 PHP 数组 -- `frankenphp.PHPMap(arr map[string]any) unsafe.Pointer` - 将 map 转换为带有键值对的无序 PHP 数组 -- `frankenphp.PHPPackedArray(slice []any) unsafe.Pointer` - 将 slice 转换为仅带有索引值的 PHP 打包数组 -- `frankenphp.GoAssociativeArray(arr unsafe.Pointer, ordered bool) frankenphp.AssociativeArray` - 将 PHP 数组转换为有序的 Go `AssociativeArray`(带有顺序的 map) -- `frankenphp.GoMap(arr unsafe.Pointer) map[string]any` - 将 PHP 数组转换为无序的 Go map -- `frankenphp.GoPackedArray(arr unsafe.Pointer) []any` - 将 PHP 数组转换为 Go slice -- `frankenphp.IsPacked(zval *C.zend_array) bool` - 检查 PHP 数组是打包(仅索引)还是关联(键值对) +- `frankenphp.PHPAssociativeArray(arr frankenphp.AssociativeArray) unsafe.Pointer` - 转换为带有键值对的有序 PHP 数组 +- `frankenphp.PHPMap(arr map[string]any) unsafe.Pointer` - 将 map 转换为带有键值对的无序 PHP 数组 +- `frankenphp.PHPPackedArray(slice []any) unsafe.Pointer` - 将 slice 转换为仅带有索引值的 PHP 打包数组 +- `frankenphp.GoAssociativeArray(arr unsafe.Pointer, ordered bool) frankenphp.AssociativeArray` - 将 PHP 数组转换为有序的 Go `AssociativeArray`(带有顺序的 map) +- `frankenphp.GoMap(arr unsafe.Pointer) map[string]any` - 将 PHP 数组转换为无序的 Go map +- `frankenphp.GoPackedArray(arr unsafe.Pointer) []any` - 将 PHP 数组转换为 Go slice +- `frankenphp.IsPacked(zval *C.zend_array) bool` - 检查 PHP 数组是打包(仅索引)还是关联(键值对) ### 处理可调用对象 @@ -268,11 +268,11 @@ type UserStruct struct { **不透明类**是内部结构(属性)对 PHP 代码隐藏的类。这意味着: -- **无直接属性访问**:你不能直接从 PHP 读取或写入属性(`$user->name` 不起作用) -- **仅方法接口** - 所有交互必须通过你定义的方法进行 -- **更好的封装** - 内部数据结构完全由 Go 代码控制 -- **类型安全** - 没有 PHP 代码使用错误类型破坏内部状态的风险 -- **更清晰的 API** - 强制设计适当的公共接口 +- **无直接属性访问**:你不能直接从 PHP 读取或写入属性(`$user->name` 不起作用) +- **仅方法接口** - 所有交互必须通过你定义的方法进行 +- **更好的封装** - 内部数据结构完全由 Go 代码控制 +- **类型安全** - 没有 PHP 代码使用错误类型破坏内部状态的风险 +- **更清晰的 API** - 强制设计适当的公共接口 这种方法提供了更好的封装,并防止 PHP 代码意外破坏 Go 对象的内部状态。与对象的所有交互都必须通过你明确定义的方法进行。 @@ -355,10 +355,10 @@ func (us *UserStruct) UpdateInfo(name *C.zend_string, age *int64, active *bool) **关于可空参数的要点:** -- **可空原始类型**(`?int`、`?float`、`?bool`)在 Go 中变成指针(`*int64`、`*float64`、`*bool`) -- **可空字符串**(`?string`)仍然是 `*C.zend_string`,但可以是 `nil` -- **在解引用指针值之前检查 `nil`** -- **PHP `null` 变成 Go `nil`** - 当 PHP 传递 `null` 时,你的 Go 函数接收 `nil` 指针 +- **可空原始类型**(`?int`、`?float`、`?bool`)在 Go 中变成指针(`*int64`、`*float64`、`*bool`) +- **可空字符串**(`?string`)仍然是 `*C.zend_string`,但可以是 `nil` +- **在解引用指针值之前检查 `nil`** +- **PHP `null` 变成 Go `nil`** - 当 PHP 传递 `null` 时,你的 Go 函数接收 `nil` 指针 > [!WARNING] > @@ -572,10 +572,10 @@ echo My\Extension\STATUS_ACTIVE; // 1 #### 重要说明 -- 每个文件只允许**一个**命名空间指令。如果找到多个命名空间指令,生成器将返回错误。 -- 命名空间适用于文件中的**所有**导出符号:函数、类、方法和常量。 -- 命名空间名称遵循 PHP 命名空间约定,使用反斜杠(`\`)作为分隔符。 -- 如果没有声明命名空间,符号将照常导出到全局命名空间。 +- 每个文件只允许**一个**命名空间指令。如果找到多个命名空间指令,生成器将返回错误。 +- 命名空间适用于文件中的**所有**导出符号:函数、类、方法和常量。 +- 命名空间名称遵循 PHP 命名空间约定,使用反斜杠(`\`)作为分隔符。 +- 如果没有声明命名空间,符号将照常导出到全局命名空间。 ### 生成扩展 @@ -703,9 +703,9 @@ extern zend_module_entry ext_module_entry; 接下来,创建一个名为 `extension.c` 的文件,该文件将执行以下步骤: -- 包含 PHP 头文件; -- 声明我们的新原生 PHP 函数 `go_print()`; -- 声明扩展元数据。 +- 包含 PHP 头文件; +- 声明我们的新原生 PHP 函数 `go_print()`; +- 声明扩展元数据。 让我们首先包含所需的头文件: diff --git a/docs/cn/hot-reload.md b/docs/cn/hot-reload.md index 371685758b..a4a1b9b4ce 100644 --- a/docs/cn/hot-reload.md +++ b/docs/cn/hot-reload.md @@ -135,5 +135,5 @@ php_server { 4. **接收**:浏览器通过 JavaScript 库监听,接收 Mercure 事件。 5. **更新**: - - 如果检测到 **Idiomorph**,它会获取更新的内容并修改当前的 HTML 以匹配新状态,即时应用更改而不会丢失状态。 - - 否则,将调用 `window.location.reload()` 以刷新页面。 +- 如果检测到 **Idiomorph**,它会获取更新的内容并修改当前的 HTML 以匹配新状态,即时应用更改而不会丢失状态。 +- 否则,将调用 `window.location.reload()` 以刷新页面。 diff --git a/docs/cn/mercure.md b/docs/cn/mercure.md index 76575efd3b..ffd6c3dccf 100644 --- a/docs/cn/mercure.md +++ b/docs/cn/mercure.md @@ -143,6 +143,6 @@ error_log("update $updateID published", 4); Mercure 也被以下框架原生支持: -- [Laravel](laravel.md#mercure-support) -- [Symfony](https://symfony.com/doc/current/mercure.html) -- [API Platform](https://api-platform.com/docs/core/mercure/) +- [Laravel](laravel.md#mercure-support) +- [Symfony](https://symfony.com/doc/current/mercure.html) +- [API Platform](https://api-platform.com/docs/core/mercure/) diff --git a/docs/fr/extensions.md b/docs/fr/extensions.md index e7519bacd1..ad125299b8 100644 --- a/docs/fr/extensions.md +++ b/docs/fr/extensions.md @@ -77,8 +77,8 @@ func repeat_this(s *C.zend_string, count int64, reverse bool) unsafe.Pointer { Il y a deux choses importantes à noter ici : -- Une directive `//export_php:function` définit la signature de la fonction en PHP. C'est ainsi que le générateur sait comment générer la fonction PHP avec les bons paramètres et le bon type de retour ; -- La fonction doit retourner un `unsafe.Pointer`. FrankenPHP fournit une API pour vous aider avec le jonglage de types entre C et Go. +- Une directive `//export_php:function` définit la signature de la fonction en PHP. C'est ainsi que le générateur sait comment générer la fonction PHP avec les bons paramètres et le bon type de retour ; +- La fonction doit retourner un `unsafe.Pointer`. FrankenPHP fournit une API pour vous aider avec le jonglage de types entre C et Go. Alors que le premier point parle de lui-même, le second peut être plus difficile à appréhender. Plongeons plus profondément dans le jonglage de types dans la section suivante. @@ -196,21 +196,21 @@ func process_data_packed(arr *C.zend_array) unsafe.Pointer { **Fonctionnalités clés de la conversion de tableaux :** -- **Paires clé-valeur ordonnées** - Option pour conserver l'ordre du tableau associatif -- **Optimisé pour plusieurs cas** - Option de ne pas conserver l'ordre pour de meilleures performances ou conversion directe vers un slice -- **Détection automatique de liste** - Lors de la conversion vers PHP, détecte automatiquement si le tableau doit être une liste packed ou un hashmap -- **Tableaux imbriqués** - Les tableaux peuvent être imbriqués et convertiront automatiquement tous les types supportés (`int64`,`float64`,`string`,`bool`,`nil`,`AssociativeArray`,`map[string]any`,`[]any`) -- **Les objets ne sont pas supportés** - Actuellement, seuls les types scalaires et les tableaux peuvent être utilisés comme valeurs. Fournir un objet résultera en une valeur `null` dans le tableau PHP. +- **Paires clé-valeur ordonnées** - Option pour conserver l'ordre du tableau associatif +- **Optimisé pour plusieurs cas** - Option de ne pas conserver l'ordre pour de meilleures performances ou conversion directe vers un slice +- **Détection automatique de liste** - Lors de la conversion vers PHP, détecte automatiquement si le tableau doit être une liste packed ou un hashmap +- **Tableaux imbriqués** - Les tableaux peuvent être imbriqués et convertiront automatiquement tous les types supportés (`int64`,`float64`,`string`,`bool`,`nil`,`AssociativeArray`,`map[string]any`,`[]any`) +- **Les objets ne sont pas supportés** - Actuellement, seuls les types scalaires et les tableaux peuvent être utilisés comme valeurs. Fournir un objet résultera en une valeur `null` dans le tableau PHP. ##### Méthodes disponibles : Packed et Associatif -- `frankenphp.PHPAssociativeArray(arr frankenphp.AssociativeArray) unsafe.Pointer` - Convertir vers un tableau PHP ordonné avec des paires clé-valeur -- `frankenphp.PHPMap(arr map[string]any) unsafe.Pointer` - Convertir une map vers un tableau PHP non ordonné avec des paires clé-valeur -- `frankenphp.PHPPackedArray(slice []any) unsafe.Pointer` - Convertir un slice vers un tableau PHP packed avec uniquement des valeurs indexées -- `frankenphp.GoAssociativeArray(arr unsafe.Pointer, ordered bool) frankenphp.AssociativeArray` - Convertir un tableau PHP vers un `AssociativeArray` Go ordonné (map avec ordre) -- `frankenphp.GoMap(arr unsafe.Pointer) map[string]any` - Convertir un tableau PHP vers une map Go non ordonnée -- `frankenphp.GoPackedArray(arr unsafe.Pointer) []any` - Convertir un tableau PHP vers un slice Go -- `frankenphp.IsPacked(zval *C.zend_array) bool` - Vérifie si le tableau PHP est une liste ou un tableau associatif +- `frankenphp.PHPAssociativeArray(arr frankenphp.AssociativeArray) unsafe.Pointer` - Convertir vers un tableau PHP ordonné avec des paires clé-valeur +- `frankenphp.PHPMap(arr map[string]any) unsafe.Pointer` - Convertir une map vers un tableau PHP non ordonné avec des paires clé-valeur +- `frankenphp.PHPPackedArray(slice []any) unsafe.Pointer` - Convertir un slice vers un tableau PHP packed avec uniquement des valeurs indexées +- `frankenphp.GoAssociativeArray(arr unsafe.Pointer, ordered bool) frankenphp.AssociativeArray` - Convertir un tableau PHP vers un `AssociativeArray` Go ordonné (map avec ordre) +- `frankenphp.GoMap(arr unsafe.Pointer) map[string]any` - Convertir un tableau PHP vers une map Go non ordonnée +- `frankenphp.GoPackedArray(arr unsafe.Pointer) []any` - Convertir un tableau PHP vers un slice Go +- `frankenphp.IsPacked(zval *C.zend_array) bool` - Vérifie si le tableau PHP est une liste ou un tableau associatif ### Travailler avec des Callables @@ -266,11 +266,11 @@ type UserStruct struct { Les **classes opaques** sont des classes où la structure interne est cachée du code PHP. Cela signifie : -- **Pas d'accès direct aux propriétés** : Vous ne pouvez pas lire ou écrire des propriétés directement depuis PHP (`$user->name` ne fonctionnera pas) -- **Interface uniquement par méthodes** - Toutes les interactions doivent passer par les méthodes que vous définissez -- **Meilleure encapsulation** - La structure de données interne est complètement contrôlée par le code Go -- **Sécurité de type** - Aucun risque que le code PHP corrompe l'état interne avec de mauvais types -- **API plus propre** - Force à concevoir une interface publique appropriée +- **Pas d'accès direct aux propriétés** : Vous ne pouvez pas lire ou écrire des propriétés directement depuis PHP (`$user->name` ne fonctionnera pas) +- **Interface uniquement par méthodes** - Toutes les interactions doivent passer par les méthodes que vous définissez +- **Meilleure encapsulation** - La structure de données interne est complètement contrôlée par le code Go +- **Sécurité de type** - Aucun risque que le code PHP corrompe l'état interne avec de mauvais types +- **API plus propre** - Force à concevoir une interface publique appropriée Cette approche fournit une meilleure encapsulation et empêche le code PHP de corrompre accidentellement l'état interne de vos objets Go. Toutes les interactions avec l'objet doivent passer par les méthodes que vous définissez explicitement. @@ -352,10 +352,10 @@ func (us *UserStruct) UpdateInfo(name *C.zend_string, age *int64, active *bool) **Points clés sur les paramètres nullables :** -- **Types primitifs nullables** (`?int`, `?float`, `?bool`) deviennent des pointeurs (`*int64`, `*float64`, `*bool`) en Go -- **Chaînes nullables** (`?string`) restent comme `*C.zend_string` mais peuvent être `nil` -- **Vérifiez `nil`** avant de déréférencer les valeurs de pointeur -- **PHP `null` devient Go `nil`** - quand PHP passe `null`, votre fonction Go reçoit un pointeur `nil` +- **Types primitifs nullables** (`?int`, `?float`, `?bool`) deviennent des pointeurs (`*int64`, `*float64`, `*bool`) en Go +- **Chaînes nullables** (`?string`) restent comme `*C.zend_string` mais peuvent être `nil` +- **Vérifiez `nil`** avant de déréférencer les valeurs de pointeur +- **PHP `null` devient Go `nil`** - quand PHP passe `null`, votre fonction Go reçoit un pointeur `nil` > [!WARNING] > Actuellement, les méthodes de classe ont les limitations suivantes. **Les objets ne sont pas supportés** comme types de paramètres ou types de retour. **Les tableaux sont entièrement supportés** pour les paramètres et types de retour. Types supportés : `string`, `int`, `float`, `bool`, `array`, et `void` (pour le type de retour). **Les types de paramètres nullables sont entièrement supportés** pour tous les types scalaires (`?string`, `?int`, `?float`, `?bool`). @@ -568,10 +568,10 @@ echo My\Extension\STATUS_ACTIVE; // 1 #### Notes Importantes -- Seule **une** directive d'espace de noms est autorisée par fichier. Si plusieurs directives d'espace de noms sont trouvées, le générateur retournera une erreur. -- L'espace de noms s'applique à **tous** les symboles exportés dans le fichier : fonctions, classes, méthodes et constantes. -- Les noms d'espaces de noms suivent les conventions des espaces de noms PHP en utilisant les barres obliques inverses (`\`) comme séparateurs. -- Si aucun espace de noms n'est déclaré, les symboles sont exportés vers l'espace de noms global comme d'habitude. +- Seule **une** directive d'espace de noms est autorisée par fichier. Si plusieurs directives d'espace de noms sont trouvées, le générateur retournera une erreur. +- L'espace de noms s'applique à **tous** les symboles exportés dans le fichier : fonctions, classes, méthodes et constantes. +- Les noms d'espaces de noms suivent les conventions des espaces de noms PHP en utilisant les barres obliques inverses (`\`) comme séparateurs. +- Si aucun espace de noms n'est déclaré, les symboles sont exportés vers l'espace de noms global comme d'habitude. ### Générer l'Extension @@ -699,9 +699,9 @@ extern zend_module_entry ext_module_entry; Ensuite, créez un fichier nommé `extension.c` qui effectuera les étapes suivantes : -- Inclure les en-têtes PHP ; -- Déclarer notre nouvelle fonction PHP native `go_print()` ; -- Déclarer les métadonnées de l'extension. +- Inclure les en-têtes PHP ; +- Déclarer notre nouvelle fonction PHP native `go_print()` ; +- Déclarer les métadonnées de l'extension. Commençons par inclure les en-têtes requis : diff --git a/docs/ja/extensions.md b/docs/ja/extensions.md index ec5c4a0153..fde719ae47 100644 --- a/docs/ja/extensions.md +++ b/docs/ja/extensions.md @@ -77,8 +77,8 @@ func repeat_this(s *C.zend_string, count int64, reverse bool) unsafe.Pointer { ここで重要なポイントが2つあります: -- ディレクティブコメント`//export_php:function`はPHPでの関数シグネチャを定義します。これにより、ジェネレーターは適切なパラメータと戻り値の型でPHP関数を生成する方法を知ることができます。 -- 関数は`unsafe.Pointer`を返さなければなりません。FrankenPHPはCとGo間の型変換を支援するAPIを提供しています。 +- ディレクティブコメント`//export_php:function`はPHPでの関数シグネチャを定義します。これにより、ジェネレーターは適切なパラメータと戻り値の型でPHP関数を生成する方法を知ることができます。 +- 関数は`unsafe.Pointer`を返さなければなりません。FrankenPHPはCとGo間の型変換を支援するAPIを提供しています。 前者は理解しやすいですが、後者は少し複雑かもしれません。次のセクションで型変換について詳しく説明します。 @@ -200,21 +200,21 @@ func process_data_packed(arr *C.zend_array) unsafe.Pointer { **配列変換の主な機能:** -- **順序付けされたキーと値のペア** - 連想配列の順序を保持するオプション -- **複数のケースに最適化** - パフォーマンス向上のために順序を破棄したり、直接スライスに変換したりするオプション -- **自動リスト検出** - PHPに変換する際、配列がパックされたリストになるべきかハッシュマップになるべきかを自動的に検出 -- **ネストされた配列** - 配列はネストでき、すべてのサポートされる型(`int64`、`float64`、`string`、`bool`、`nil`、`AssociativeArray`、`map[string]any`、`[]any`)を自動的に変換します -- **オブジェクトはサポートされていません** - 現在、スカラー型と配列のみが値として使用できます。オブジェクトを提供するとPHP配列内で`null`値になります。 +- **順序付けされたキーと値のペア** - 連想配列の順序を保持するオプション +- **複数のケースに最適化** - パフォーマンス向上のために順序を破棄したり、直接スライスに変換したりするオプション +- **自動リスト検出** - PHPに変換する際、配列がパックされたリストになるべきかハッシュマップになるべきかを自動的に検出 +- **ネストされた配列** - 配列はネストでき、すべてのサポートされる型(`int64`、`float64`、`string`、`bool`、`nil`、`AssociativeArray`、`map[string]any`、`[]any`)を自動的に変換します +- **オブジェクトはサポートされていません** - 現在、スカラー型と配列のみが値として使用できます。オブジェクトを提供するとPHP配列内で`null`値になります。 ##### 利用可能なメソッド: パックおよび連想 -- `frankenphp.PHPAssociativeArray(arr frankenphp.AssociativeArray) unsafe.Pointer` - キーと値のペアを持つ順序付きPHP配列に変換 -- `frankenphp.PHPMap(arr map[string]any) unsafe.Pointer` - マップをキーと値のペアを持つ順序なしPHP配列に変換 -- `frankenphp.PHPPackedArray(slice []any) unsafe.Pointer` - スライスをインデックス付き値のみのPHPパックされた配列に変換 -- `frankenphp.GoAssociativeArray(arr unsafe.Pointer, ordered bool) frankenphp.AssociativeArray` - PHP配列を順序付きGo `AssociativeArray` (順序付きマップ) に変換 -- `frankenphp.GoMap(arr unsafe.Pointer) map[string]any` - PHP配列を順序なしGoマップに変換 -- `frankenphp.GoPackedArray(arr unsafe.Pointer) []any` - PHP配列をGoスライスに変換 -- `frankenphp.IsPacked(zval *C.zend_array) bool` - PHP配列がパックされている(インデックスのみ)か連想配列(キーと値のペア)かを確認 +- `frankenphp.PHPAssociativeArray(arr frankenphp.AssociativeArray) unsafe.Pointer` - キーと値のペアを持つ順序付きPHP配列に変換 +- `frankenphp.PHPMap(arr map[string]any) unsafe.Pointer` - マップをキーと値のペアを持つ順序なしPHP配列に変換 +- `frankenphp.PHPPackedArray(slice []any) unsafe.Pointer` - スライスをインデックス付き値のみのPHPパックされた配列に変換 +- `frankenphp.GoAssociativeArray(arr unsafe.Pointer, ordered bool) frankenphp.AssociativeArray` - PHP配列を順序付きGo `AssociativeArray` (順序付きマップ) に変換 +- `frankenphp.GoMap(arr unsafe.Pointer) map[string]any` - PHP配列を順序なしGoマップに変換 +- `frankenphp.GoPackedArray(arr unsafe.Pointer) []any` - PHP配列をGoスライスに変換 +- `frankenphp.IsPacked(zval *C.zend_array) bool` - PHP配列がパックされている(インデックスのみ)か連想配列(キーと値のペア)かを確認 ### Working with Callables @@ -281,11 +281,11 @@ type UserStruct struct { **不透明クラス(opaque classes)**は、内部構造(プロパティ)がPHPコードから隠されているクラスです。これは以下を意味します: -- **プロパティへの直接アクセス不可** :PHPから直接プロパティを読み書きできません(`$user->name`は機能しません) -- **メソッド経由のみで操作** - すべてのやりとりはGoで定義したメソッドを通じて行う必要があります -- **より良いカプセル化** - 内部データ構造は完全にGoコードによって制御されます -- **型安全性** - PHP側から誤った型で内部状態が破壊されるリスクがありません -- **よりクリーンなAPI** - 適切な公開インターフェースを設計することを強制します +- **プロパティへの直接アクセス不可** :PHPから直接プロパティを読み書きできません(`$user->name`は機能しません) +- **メソッド経由のみで操作** - すべてのやりとりはGoで定義したメソッドを通じて行う必要があります +- **より良いカプセル化** - 内部データ構造は完全にGoコードによって制御されます +- **型安全性** - PHP側から誤った型で内部状態が破壊されるリスクがありません +- **よりクリーンなAPI** - 適切な公開インターフェースを設計することを強制します このアプローチは優れたカプセル化を実現し、PHPコードがGoオブジェクトの内部状態を意図せずに破壊してしまうことを防ぎます。オブジェクトとのすべてのやりとりは、明示的に定義したメソッドを通じて行う必要があります。 @@ -367,10 +367,10 @@ func (us *UserStruct) UpdateInfo(name *C.zend_string, age *int64, active *bool) **Nullableパラメータの重要なポイント:** -- **プリミティブ型のnullable** (`?int`, `?float`, `?bool`) はGoではそれぞれポインタ (`*int64`, `*float64`, `*bool`) になります -- **nullable文字列** (`?string`) は `*C.zend_string` のままですが、`nil` になることがあります -- ポインタ値を逆参照する前に **`nil`をチェック** してください -- **PHPの`null`はGoの`nil`になります** - PHPが`null`を渡すと、Go関数は`nil`ポインタを受け取ります +- **プリミティブ型のnullable** (`?int`, `?float`, `?bool`) はGoではそれぞれポインタ (`*int64`, `*float64`, `*bool`) になります +- **nullable文字列** (`?string`) は `*C.zend_string` のままですが、`nil` になることがあります +- ポインタ値を逆参照する前に **`nil`をチェック** してください +- **PHPの`null`はGoの`nil`になります** - PHPが`null`を渡すと、Go関数は`nil`ポインタを受け取ります > [!WARNING] > @@ -584,10 +584,10 @@ echo My\Extension\STATUS_ACTIVE; // 1 #### Important Notes -- 1つのファイルにつき**1つ**の名前空間ディレクティブのみが許可されます。複数の名前空間ディレクティブが見つかった場合、ジェネレーターはエラーを返します。 -- 名前空間はファイル内の**すべて**のエクスポートされたシンボル(関数、クラス、メソッド、定数)に適用されます。 -- 名前空間名は、バックスラッシュ(`\`)を区切り文字とするPHPの名前空間の慣習に従います。 -- 名前空間が宣言されていない場合、シンボルは通常通りグローバル名前空間にエクスポートされます。 +- 1つのファイルにつき**1つ**の名前空間ディレクティブのみが許可されます。複数の名前空間ディレクティブが見つかった場合、ジェネレーターはエラーを返します。 +- 名前空間はファイル内の**すべて**のエクスポートされたシンボル(関数、クラス、メソッド、定数)に適用されます。 +- 名前空間名は、バックスラッシュ(`\`)を区切り文字とするPHPの名前空間の慣習に従います。 +- 名前空間が宣言されていない場合、シンボルは通常通りグローバル名前空間にエクスポートされます。 ### 拡張モジュールの生成 @@ -715,9 +715,9 @@ extern zend_module_entry ext_module_entry; 次に、以下のステップを実行する`extension.c`という名前のファイルを作成します: -- PHPヘッダーをインクルードする -- 新しいネイティブPHP関数`go_print()`を宣言する -- 拡張モジュールのメタデータを宣言する +- PHPヘッダーをインクルードする +- 新しいネイティブPHP関数`go_print()`を宣言する +- 拡張モジュールのメタデータを宣言する まずは必要なヘッダーのインクルードから始めましょう: diff --git a/docs/ja/logging.md b/docs/ja/logging.md index 45f75737c1..65f3e139a1 100644 --- a/docs/ja/logging.md +++ b/docs/ja/logging.md @@ -18,9 +18,9 @@ function frankenphp_log(string $message, int $level = FRANKENPHP_LOG_LEVEL_INFO, ### パラメータ -- **`message`**: ログメッセージ文字列。 -- **`level`**: ログの深刻度レベル。任意の整数値を指定できます。一般的なレベルには便利な定数が用意されています: `FRANKENPHP_LOG_LEVEL_DEBUG` (`-4`)、`FRANKENPHP_LOG_LEVEL_INFO` (`0`)、`FRANKENPHP_LOG_LEVEL_WARN` (`4`)、`FRANKENPHP_LOG_LEVEL_ERROR` (`8`)。デフォルトは `FRANKENPHP_LOG_LEVEL_INFO` です。 -- **`context`**: ログエントリに含める追加データの連想配列。 +- **`message`**: ログメッセージ文字列。 +- **`level`**: ログの深刻度レベル。任意の整数値を指定できます。一般的なレベルには便利な定数が用意されています: `FRANKENPHP_LOG_LEVEL_DEBUG` (`-4`)、`FRANKENPHP_LOG_LEVEL_INFO` (`0`)、`FRANKENPHP_LOG_LEVEL_WARN` (`4`)、`FRANKENPHP_LOG_LEVEL_ERROR` (`8`)。デフォルトは `FRANKENPHP_LOG_LEVEL_INFO` です。 +- **`context`**: ログエントリに含める追加データの連想配列。 ### 例 diff --git a/docs/known-issues.md b/docs/known-issues.md index da22690373..f6912960e2 100644 --- a/docs/known-issues.md +++ b/docs/known-issues.md @@ -7,7 +7,7 @@ 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 | - | +| [newrelic](https://docs.newrelic.com/docs/apm/agents/php-agent/getting-started/introduction-new-relic-php/) | Not thread-safe | - * | ## Buggy PHP Extensions diff --git a/docs/pt-br/extensions.md b/docs/pt-br/extensions.md index fe23aefe8e..3815025564 100644 --- a/docs/pt-br/extensions.md +++ b/docs/pt-br/extensions.md @@ -114,13 +114,8 @@ func repeat_this(s *C.zend_string, count int64, reverse bool) unsafe.Pointer { Há duas coisas importantes a serem observadas aqui: -- Um comentário de diretiva `//export_php:function` define a assinatura da - função no PHP. - É assim que o gerador sabe como gerar a função PHP com os parâmetros e o tipo - de retorno corretos; -- A função deve retornar um `unsafe.Pointer`. - O FrankenPHP fornece uma API para ajudar você com o malabarismo de tipos entre - C e Go. +- Um comentário de diretiva `//export_php:function` define a assinatura da função no PHP. É assim que o gerador sabe como gerar a função PHP com os parâmetros e o tipo de retorno corretos; +- A função deve retornar um `unsafe.Pointer`. O FrankenPHP fornece uma API para ajudar você com o malabarismo de tipos entre C e Go. Embora o primeiro ponto fale por si, o segundo pode ser mais difícil de entender. @@ -262,34 +257,34 @@ func process_data_packed(arr *C.zend_array) unsafe.Pointer { **Principais recursos da conversão de arrays:** -- **Pares chave-valor ordenados** - Opção para manter a ordem do array +- **Pares chave-valor ordenados** - Opção para manter a ordem do array associativo; -- **Otimizado para múltiplos casos** - Opção para ignorar a ordem para melhor +- **Otimizado para múltiplos casos** - Opção para ignorar a ordem para melhor desempenho ou converter diretamente para um slice; -- **Detecção automática de listas** - Ao converter para PHP, detecta +- **Detecção automática de listas** - Ao converter para PHP, detecta automaticamente se o array deve ser uma lista compactada ou um hashmap; -- **Arrays aninhados** - Os arrays podem ser aninhados e converterão todos os +- **Arrays aninhados** - Os arrays podem ser aninhados e converterão todos os tipos suportados automaticamente (`int64`, `float64`, `string`, `bool`, `nil`, `AssociativeArray`, `map[string]any`, `[]any`); -- **Objetos não são suportados** - Atualmente, apenas tipos escalares e arrays +- **Objetos não são suportados** - Atualmente, apenas tipos escalares e arrays podem ser usados como valores. Fornecer um objeto resultará em um valor `null` no array PHP. ##### Métodos disponíveis: empacotado e associativo -- `frankenphp.PHPAssociativeArray(arr frankenphp.AssociativeArray) unsafe.Pointer` +- `frankenphp.PHPAssociativeArray(arr frankenphp.AssociativeArray) unsafe.Pointer` \- Converte para um array PHP ordenado com pares chave-valor; -- `frankenphp.PHPMap(arr map[string]any) unsafe.Pointer` - Converte um mapa em +- `frankenphp.PHPMap(arr map[string]any) unsafe.Pointer` - Converte um mapa em um array PHP não ordenado com pares chave-valor; -- `frankenphp.PHPPackedArray(slice []any) unsafe.Pointer` - Converte um slice +- `frankenphp.PHPPackedArray(slice []any) unsafe.Pointer` - Converte um slice em um array PHP compactado apenas com valores indexados; -- `frankenphp.GoAssociativeArray(arr unsafe.Pointer, ordered bool) frankenphp.AssociativeArray` +- `frankenphp.GoAssociativeArray(arr unsafe.Pointer, ordered bool) frankenphp.AssociativeArray` \- Converte um array PHP em um `AssociativeArray` Go ordenado (mapa com ordem); -- `frankenphp.GoMap(arr unsafe.Pointer) map[string]any` - Converte um array PHP +- `frankenphp.GoMap(arr unsafe.Pointer) map[string]any` - Converte um array PHP em um mapa Go não ordenado; -- `frankenphp.GoPackedArray(arr unsafe.Pointer) []any` - Converte um array PHP +- `frankenphp.GoPackedArray(arr unsafe.Pointer) []any` - Converte um array PHP em um slice Go; -- `frankenphp.IsPacked(zval *C.zend_array) bool` - Verifica se um array PHP é +- `frankenphp.IsPacked(zval *C.zend_array) bool` - Verifica se um array PHP é compactado (apenas indexado) ou associativo (pares chave-valor). ### Trabalhando com Callables @@ -369,15 +364,15 @@ type UserStruct struct { do código PHP. Isso significa: -- **Sem acesso direto às propriedades**: Você não pode ler ou escrever +- **Sem acesso direto às propriedades**: Você não pode ler ou escrever propriedades diretamente do PHP (`$user->name` não funcionará); -- **Interface somente para métodos** - Todas as interações devem passar pelos +- **Interface somente para métodos** - Todas as interações devem passar pelos métodos que você definir; -- **Melhor encapsulamento** - A estrutura interna de dados é completamente +- **Melhor encapsulamento** - A estrutura interna de dados é completamente controlada pelo código Go; -- **Segurança de tipos** - Sem risco do código PHP corromper o estado interno +- **Segurança de tipos** - Sem risco do código PHP corromper o estado interno com tipos incorretos; -- **API mais limpa** - Força o design de uma interface pública adequada. +- **API mais limpa** - Força o design de uma interface pública adequada. Essa abordagem fornece melhor encapsulamento e evita que o código PHP corrompa acidentalmente o estado interno dos seus objetos Go. @@ -466,12 +461,12 @@ func (us *UserStruct) UpdateInfo(name *C.zend_string, age *int64, active *bool) **Pontos-chave sobre parâmetros anuláveis:** -- **Tipos primitivos anuláveis** (`?int`, `?float`, `?bool`) tornam-se ponteiros +- **Tipos primitivos anuláveis** (`?int`, `?float`, `?bool`) tornam-se ponteiros (`*int64`, `*float64`, `*bool`) em Go; -- **Strings anuláveis** (`?string`) permanecem como `*C.zend_string`, mas podem +- **Strings anuláveis** (`?string`) permanecem como `*C.zend_string`, mas podem ser `nil`; -- **Verifique `nil`** antes de dereferenciar valores de ponteiro; -- **`null` do PHP torna-se `nil` do Go** - quando o PHP passa `null`, sua função +- **Verifique `nil`** antes de dereferenciar valores de ponteiro; +- **`null` do PHP torna-se `nil` do Go** - quando o PHP passa `null`, sua função em Go recebe um ponteiro `nil`. > [!WARNING] @@ -715,14 +710,14 @@ echo My\Extension\STATUS_ACTIVE; // 1 #### Notas importantes -- Apenas **uma** diretiva de namespace é permitida por arquivo. +- Apenas **uma** diretiva de namespace é permitida por arquivo. Se várias diretivas de namespace forem encontradas, o gerador retornará um erro; -- O namespace se aplica a **todos** os símbolos exportados no arquivo: funções, +- O namespace se aplica a **todos** os símbolos exportados no arquivo: funções, classes, métodos e constantes; -- Os nomes de namespace seguem as convenções de namespace do PHP, usando barras +- Os nomes de namespace seguem as convenções de namespace do PHP, usando barras invertidas (`\`) como separadores; -- Se nenhum namespace for declarado, os símbolos serão exportados para o +- Se nenhum namespace for declarado, os símbolos serão exportados para o namespace global como de costume. ### Gerando a extensão @@ -893,9 +888,9 @@ extern zend_module_entry ext_module_entry; Em seguida, crie um arquivo chamado `extension.c` que executará as seguintes etapas: -- Incluir cabeçalhos PHP; -- Declarar nossa nova função nativa PHP `go_print()`; -- Declarar os metadados da extensão. +- Incluir cabeçalhos PHP; +- Declarar nossa nova função nativa PHP `go_print()`; +- Declarar os metadados da extensão. Vamos começar incluindo os cabeçalhos necessários: diff --git a/docs/pt-br/logging.md b/docs/pt-br/logging.md index 6bd73049f7..5817f30a22 100644 --- a/docs/pt-br/logging.md +++ b/docs/pt-br/logging.md @@ -20,9 +20,9 @@ function frankenphp_log(string $message, int $level = FRANKENPHP_LOG_LEVEL_INFO, ### Parâmetros -- **`message`**: A string da mensagem de log. -- **`level`**: O nível de severidade do log. Pode ser qualquer número inteiro arbitrário. Constantes de conveniência são fornecidas para níveis comuns: `FRANKENPHP_LOG_LEVEL_DEBUG` (`-4`), `FRANKENPHP_LOG_LEVEL_INFO` (`0`), `FRANKENPHP_LOG_LEVEL_WARN` (`4`) e `FRANKENPHP_LOG_LEVEL_ERROR` (`8`)). O padrão é `FRANKENPHP_LOG_LEVEL_INFO`. -- **`context`**: Um array associativo de dados adicionais a serem incluídos na entrada de log. +- **`message`**: A string da mensagem de log. +- **`level`**: O nível de severidade do log. Pode ser qualquer número inteiro arbitrário. Constantes de conveniência são fornecidas para níveis comuns: `FRANKENPHP_LOG_LEVEL_DEBUG` (`-4`), `FRANKENPHP_LOG_LEVEL_INFO` (`0`), `FRANKENPHP_LOG_LEVEL_WARN` (`4`) e `FRANKENPHP_LOG_LEVEL_ERROR` (`8`)). O padrão é `FRANKENPHP_LOG_LEVEL_INFO`. +- **`context`**: Um array associativo de dados adicionais a serem incluídos na entrada de log. ### Exemplo diff --git a/docs/ru/extensions.md b/docs/ru/extensions.md index 6090014aeb..1985c1ded4 100644 --- a/docs/ru/extensions.md +++ b/docs/ru/extensions.md @@ -77,8 +77,8 @@ func repeat_this(s *C.zend_string, count int64, reverse bool) unsafe.Pointer { Здесь следует отметить две важные вещи: -- Директива-комментарий `//export_php:function` определяет сигнатуру функции в PHP. Таким образом генератор знает, как создать PHP-функцию с правильными параметрами и типом возвращаемого значения; -- Функция должна возвращать `unsafe.Pointer`. FrankenPHP предоставляет API, чтобы помочь вам с согласованием типов между C и Go. +- Директива-комментарий `//export_php:function` определяет сигнатуру функции в PHP. Таким образом генератор знает, как создать PHP-функцию с правильными параметрами и типом возвращаемого значения; +- Функция должна возвращать `unsafe.Pointer`. FrankenPHP предоставляет API, чтобы помочь вам с согласованием типов между C и Go. В то время как первый пункт говорит сам за себя, второй может быть сложнее для понимания. Давайте углубимся в согласование типов в следующем разделе. @@ -198,21 +198,21 @@ func process_data_packed(arr *C.zend_array) unsafe.Pointer { **Ключевые особенности преобразования массивов:** -- **Упорядоченные пары ключ-значение** — Возможность сохранять порядок ассоциативного массива -- **Оптимизировано для различных случаев** — Возможность отказаться от порядка для лучшей производительности или преобразовать напрямую в срез -- **Автоматическое обнаружение списка** — При преобразовании в PHP автоматически определяет, должен ли массив быть упакованным списком или хеш-картой -- **Вложенные массивы** — Массивы могут быть вложенными и автоматически преобразуют все поддерживаемые типы (`int64`, `float64`, `string`, `bool`, `nil`, `AssociativeArray`, `map[string]any`, `[]any`) -- **Объекты не поддерживаются** — В настоящее время в качестве значений могут использоваться только скалярные типы и массивы. Предоставление объекта приведет к значению `null` в массиве PHP. +- **Упорядоченные пары ключ-значение** — Возможность сохранять порядок ассоциативного массива +- **Оптимизировано для различных случаев** — Возможность отказаться от порядка для лучшей производительности или преобразовать напрямую в срез +- **Автоматическое обнаружение списка** — При преобразовании в PHP автоматически определяет, должен ли массив быть упакованным списком или хеш-картой +- **Вложенные массивы** — Массивы могут быть вложенными и автоматически преобразуют все поддерживаемые типы (`int64`, `float64`, `string`, `bool`, `nil`, `AssociativeArray`, `map[string]any`, `[]any`) +- **Объекты не поддерживаются** — В настоящее время в качестве значений могут использоваться только скалярные типы и массивы. Предоставление объекта приведет к значению `null` в массиве PHP. ##### Доступные методы: Packed и Associative -- `frankenphp.PHPAssociativeArray(arr frankenphp.AssociativeArray) unsafe.Pointer` — Преобразует в упорядоченный массив PHP с парами ключ-значение -- `frankenphp.PHPMap(arr map[string]any) unsafe.Pointer` — Преобразует карту в неупорядоченный массив PHP с парами ключ-значение -- `frankenphp.PHPPackedArray(slice []any) unsafe.Pointer` — Преобразует срез в упакованный массив PHP только с индексированными значениями -- `frankenphp.GoAssociativeArray(arr unsafe.Pointer, ordered bool) frankenphp.AssociativeArray` — Преобразует массив PHP в упорядоченный `AssociativeArray` Go (карту с порядком) -- `frankenphp.GoMap(arr unsafe.Pointer) map[string]any` — Преобразует массив PHP в неупорядоченную карту Go -- `frankenphp.GoPackedArray(arr unsafe.Pointer) []any` — Преобразует массив PHP в срез Go -- `frankenphp.IsPacked(zval *C.zend_array) bool` — Проверяет, является ли массив PHP упакованным (только индексированным) или ассоциативным (пары ключ-значение) +- `frankenphp.PHPAssociativeArray(arr frankenphp.AssociativeArray) unsafe.Pointer` — Преобразует в упорядоченный массив PHP с парами ключ-значение +- `frankenphp.PHPMap(arr map[string]any) unsafe.Pointer` — Преобразует карту в неупорядоченный массив PHP с парами ключ-значение +- `frankenphp.PHPPackedArray(slice []any) unsafe.Pointer` — Преобразует срез в упакованный массив PHP только с индексированными значениями +- `frankenphp.GoAssociativeArray(arr unsafe.Pointer, ordered bool) frankenphp.AssociativeArray` — Преобразует массив PHP в упорядоченный `AssociativeArray` Go (карту с порядком) +- `frankenphp.GoMap(arr unsafe.Pointer) map[string]any` — Преобразует массив PHP в неупорядоченную карту Go +- `frankenphp.GoPackedArray(arr unsafe.Pointer) []any` — Преобразует массив PHP в срез Go +- `frankenphp.IsPacked(zval *C.zend_array) bool` — Проверяет, является ли массив PHP упакованным (только индексированным) или ассоциативным (пары ключ-значение) ### Работа с вызываемыми объектами (Callables) @@ -268,11 +268,11 @@ type UserStruct struct { **Непрозрачные классы** — это классы, внутренняя структура (свойства) которых скрыта от PHP-кода. Это означает: -- **Нет прямого доступа к свойствам**: Вы не можете читать или записывать свойства напрямую из PHP (`$user->name` не будет работать) -- **Только интерфейс методов** — Все взаимодействия должны происходить через методы, которые вы определяете -- **Лучшая инкапсуляция** — Внутренняя структура данных полностью контролируется кодом Go -- **Типобезопасность** — Нет риска, что PHP-код повредит внутреннее состояние неправильными типами -- **Более чистый API** — Принуждает к разработке правильного публичного интерфейса +- **Нет прямого доступа к свойствам**: Вы не можете читать или записывать свойства напрямую из PHP (`$user->name` не будет работать) +- **Только интерфейс методов** — Все взаимодействия должны происходить через методы, которые вы определяете +- **Лучшая инкапсуляция** — Внутренняя структура данных полностью контролируется кодом Go +- **Типобезопасность** — Нет риска, что PHP-код повредит внутреннее состояние неправильными типами +- **Более чистый API** — Принуждает к разработке правильного публичного интерфейса Этот подход обеспечивает лучшую инкапсуляцию и предотвращает случайное повреждение PHP-кодом внутреннего состояния ваших Go-объектов. Все взаимодействия с объектом должны проходить через явно определенные вами методы. @@ -354,10 +354,10 @@ func (us *UserStruct) UpdateInfo(name *C.zend_string, age *int64, active *bool) **Ключевые моменты об обнуляемых параметрах:** -- **Обнуляемые примитивные типы** (`?int`, `?float`, `?bool`) становятся указателями (`*int64`, `*float64`, `*bool`) в Go -- **Обнуляемые строки** (`?string`) остаются `*C.zend_string`, но могут быть `nil` -- **Проверяйте на `nil`** перед разыменованием значений указателей -- **PHP `null` становится Go `nil`** — когда PHP передает `null`, ваша Go-функция получает `nil`-указатель +- **Обнуляемые примитивные типы** (`?int`, `?float`, `?bool`) становятся указателями (`*int64`, `*float64`, `*bool`) в Go +- **Обнуляемые строки** (`?string`) остаются `*C.zend_string`, но могут быть `nil` +- **Проверяйте на `nil`** перед разыменованием значений указателей +- **PHP `null` становится Go `nil`** — когда PHP передает `null`, ваша Go-функция получает `nil`-указатель > [!WARNING] > @@ -571,10 +571,10 @@ echo My\Extension\STATUS_ACTIVE; // 1 #### Важные замечания -- Разрешена только **одна** директива пространства имен на файл. Если найдено несколько директив пространства имен, генератор вернет ошибку. -- Пространство имен применяется ко **всем** экспортируемым символам в файле: функциям, классам, методам и константам. -- Имена пространств имен следуют соглашениям PHP, используя обратные слеши (`\`) в качестве разделителей. -- Если пространство имен не объявлено, символы экспортируются в глобальное пространство имен как обычно. +- Разрешена только **одна** директива пространства имен на файл. Если найдено несколько директив пространства имен, генератор вернет ошибку. +- Пространство имен применяется ко **всем** экспортируемым символам в файле: функциям, классам, методам и константам. +- Имена пространств имен следуют соглашениям PHP, используя обратные слеши (`\`) в качестве разделителей. +- Если пространство имен не объявлено, символы экспортируются в глобальное пространство имен как обычно. ### Генерация расширения @@ -702,9 +702,9 @@ extern zend_module_entry ext_module_entry; Затем создайте файл с именем `extension.c`, который будет выполнять следующие шаги: -- Включать заголовки PHP; -- Объявлять нашу новую нативную PHP-функцию `go_print()`; -- Объявлять метаданные расширения. +- Включать заголовки PHP; +- Объявлять нашу новую нативную PHP-функцию `go_print()`; +- Объявлять метаданные расширения. Начнем с включения необходимых заголовков: diff --git a/docs/tr/extensions.md b/docs/tr/extensions.md index b9ed7fcda3..0c461d57cb 100644 --- a/docs/tr/extensions.md +++ b/docs/tr/extensions.md @@ -198,21 +198,21 @@ func process_data_packed(arr *C.zend_array) unsafe.Pointer { **Dizi dönüşümünün temel özellikleri:** -- **Sıralı anahtar-değer çiftleri** - İlişkisel dizinin sırasını koruma seçeneği -- **Birden fazla durum için optimize edildi** - Daha iyi performans için sırayı bırakma veya doğrudan bir dilime dönüştürme seçeneği -- **Otomatik liste tespiti** - PHP'ye dönüştürürken, dizinin sıkıştırılmış bir liste mi yoksa hash haritası mı olması gerektiğini otomatik olarak algılar -- **İç İçe Diziler** - Diziler iç içe olabilir ve tüm desteklenen türleri otomatik olarak dönüştürür (`int64`,`float64`,`string`,`bool`,`nil`,`AssociativeArray`,`map[string]any`,`[]any`) -- **Nesneler desteklenmiyor** - Şu anda yalnızca skaler türler ve diziler değer olarak kullanılabilir. Bir nesne sağlamak, PHP dizisinde `null` bir değerle sonuçlanacaktır. +- **Sıralı anahtar-değer çiftleri** - İlişkisel dizinin sırasını koruma seçeneği +- **Birden fazla durum için optimize edildi** - Daha iyi performans için sırayı bırakma veya doğrudan bir dilime dönüştürme seçeneği +- **Otomatik liste tespiti** - PHP'ye dönüştürürken, dizinin sıkıştırılmış bir liste mi yoksa hash haritası mı olması gerektiğini otomatik olarak algılar +- **İç İçe Diziler** - Diziler iç içe olabilir ve tüm desteklenen türleri otomatik olarak dönüştürür (`int64`,`float64`,`string`,`bool`,`nil`,`AssociativeArray`,`map[string]any`,`[]any`) +- **Nesneler desteklenmiyor** - Şu anda yalnızca skaler türler ve diziler değer olarak kullanılabilir. Bir nesne sağlamak, PHP dizisinde `null` bir değerle sonuçlanacaktır. ##### Mevcut metodlar: Packed ve İlişkisel -- `frankenphp.PHPAssociativeArray(arr frankenphp.AssociativeArray) unsafe.Pointer` - Anahtar-değer çiftleriyle sıralı bir PHP dizisine dönüştürür -- `frankenphp.PHPMap(arr map[string]any) unsafe.Pointer` - Bir haritayı anahtar-değer çiftleriyle sırasız bir PHP dizisine dönüştürür -- `frankenphp.PHPPackedArray(slice []any) unsafe.Pointer` - Bir dilimi yalnızca indekslenmiş değerlere sahip bir PHP packed dizisine dönüştürür -- `frankenphp.GoAssociativeArray(arr unsafe.Pointer, ordered bool) frankenphp.AssociativeArray` - Bir PHP dizisini sıralı bir Go `AssociativeArray`'e (sıralı harita) dönüştürür -- `frankenphp.GoMap(arr unsafe.Pointer) map[string]any` - Bir PHP dizisini sırasız bir Go haritasına dönüştürür -- `frankenphp.GoPackedArray(arr unsafe.Pointer) []any` - Bir PHP dizisini bir Go dilimine dönüştürür -- `frankenphp.IsPacked(zval *C.zend_array) bool` - Bir PHP dizisinin packed (yalnızca indekslenmiş) mi yoksa ilişkisel (anahtar-değer çiftleri) mi olduğunu kontrol eder +- `frankenphp.PHPAssociativeArray(arr frankenphp.AssociativeArray) unsafe.Pointer` - Anahtar-değer çiftleriyle sıralı bir PHP dizisine dönüştürür +- `frankenphp.PHPMap(arr map[string]any) unsafe.Pointer` - Bir haritayı anahtar-değer çiftleriyle sırasız bir PHP dizisine dönüştürür +- `frankenphp.PHPPackedArray(slice []any) unsafe.Pointer` - Bir dilimi yalnızca indekslenmiş değerlere sahip bir PHP packed dizisine dönüştürür +- `frankenphp.GoAssociativeArray(arr unsafe.Pointer, ordered bool) frankenphp.AssociativeArray` - Bir PHP dizisini sıralı bir Go `AssociativeArray`'e (sıralı harita) dönüştürür +- `frankenphp.GoMap(arr unsafe.Pointer) map[string]any` - Bir PHP dizisini sırasız bir Go haritasına dönüştürür +- `frankenphp.GoPackedArray(arr unsafe.Pointer) []any` - Bir PHP dizisini bir Go dilimine dönüştürür +- `frankenphp.IsPacked(zval *C.zend_array) bool` - Bir PHP dizisinin packed (yalnızca indekslenmiş) mi yoksa ilişkisel (anahtar-değer çiftleri) mi olduğunu kontrol eder ### Çağrılabilirler (Callables) ile Çalışma @@ -268,11 +268,11 @@ type UserStruct struct { **Saydam olmayan sınıflar**, dahili yapının (özelliklerin) PHP kodundan gizlendiği sınıflardır. Bu şu anlama gelir: -- **Doğrudan özellik erişimi yok**: PHP'den özellikleri doğrudan okuyamaz veya yazamazsınız (`$user->name` çalışmaz) -- **Yalnızca metot arayüzü** - Tüm etkileşimler, tanımladığınız metotlar aracılığıyla gerçekleşmelidir -- **Daha iyi kapsülleme** - Dahili veri yapısı tamamen Go kodu tarafından kontrol edilir -- **Tür güvenliği** - PHP kodunun yanlış türlerle dahili durumu bozma riski yok -- **Daha temiz API** - Uygun bir genel arayüz tasarlamaya zorlar +- **Doğrudan özellik erişimi yok**: PHP'den özellikleri doğrudan okuyamaz veya yazamazsınız (`$user->name` çalışmaz) +- **Yalnızca metot arayüzü** - Tüm etkileşimler, tanımladığınız metotlar aracılığıyla gerçekleşmelidir +- **Daha iyi kapsülleme** - Dahili veri yapısı tamamen Go kodu tarafından kontrol edilir +- **Tür güvenliği** - PHP kodunun yanlış türlerle dahili durumu bozma riski yok +- **Daha temiz API** - Uygun bir genel arayüz tasarlamaya zorlar Bu yaklaşım, daha iyi kapsülleme sağlar ve PHP kodunun Go nesnelerinizin dahili durumunu yanlışlıkla bozmasını önler. Nesneyle olan tüm etkileşimler, açıkça tanımladığınız metotlar aracılığıyla gerçekleşmelidir. @@ -354,10 +354,10 @@ func (us *UserStruct) UpdateInfo(name *C.zend_string, age *int64, active *bool) **Boş geçilebilir parametreler hakkında önemli noktalar:** -- **Boş geçilebilir ilkel türler** (`?int`, `?float`, `?bool`) Go'da işaretçilere (`*int64`, `*float64`, `*bool`) dönüşür -- **Boş geçilebilir dizeler** (`?string`) `*C.zend_string` olarak kalır ancak `nil` olabilir -- İşaretçi değerlerini referans almadan önce `nil` kontrolü yapın -- **PHP `null` Go `nil` olur** - PHP `null` geçirdiğinde, Go fonksiyonunuz bir `nil` işaretçi alır +- **Boş geçilebilir ilkel türler** (`?int`, `?float`, `?bool`) Go'da işaretçilere (`*int64`, `*float64`, `*bool`) dönüşür +- **Boş geçilebilir dizeler** (`?string`) `*C.zend_string` olarak kalır ancak `nil` olabilir +- İşaretçi değerlerini referans almadan önce `nil` kontrolü yapın +- **PHP `null` Go `nil` olur** - PHP `null` geçirdiğinde, Go fonksiyonunuz bir `nil` işaretçi alır > [!WARNING] > @@ -571,10 +571,10 @@ echo My\Extension\STATUS_ACTIVE; // 1 #### Önemli Notlar -- Dosya başına yalnızca **bir** ad alanı yönergesine izin verilir. Birden fazla ad alanı yönergesi bulunursa, üretici bir hata döndürür. -- Ad alanı, dosyada dışa aktarılan **tüm** semboller için geçerlidir: fonksiyonlar, sınıflar, metotlar ve sabitler. -- Ad alanı adları, ayırıcı olarak ters eğik çizgi (`\`) kullanarak PHP ad alanı kurallarına uyar. -- Hiçbir ad alanı bildirilmezse, semboller her zamanki gibi genel ad alanına dışa aktarılır. +- Dosya başına yalnızca **bir** ad alanı yönergesine izin verilir. Birden fazla ad alanı yönergesi bulunursa, üretici bir hata döndürür. +- Ad alanı, dosyada dışa aktarılan **tüm** semboller için geçerlidir: fonksiyonlar, sınıflar, metotlar ve sabitler. +- Ad alanı adları, ayırıcı olarak ters eğik çizgi (`\`) kullanarak PHP ad alanı kurallarına uyar. +- Hiçbir ad alanı bildirilmezse, semboller her zamanki gibi genel ad alanına dışa aktarılır. ### Eklentiyi Oluşturma @@ -702,9 +702,9 @@ extern zend_module_entry ext_module_entry; Ardından, aşağıdaki adımları gerçekleştirecek `extension.c` adında bir dosya oluşturun: -- PHP başlıklarını dahil etme; -- Yeni yerel PHP fonksiyonumuz `go_print()`'i bildirme; -- Eklenti meta verilerini bildirme. +- PHP başlıklarını dahil etme; +- Yeni yerel PHP fonksiyonumuz `go_print()`'i bildirme; +- Eklenti meta verilerini bildirme. Gerekli başlıkları dahil ederek başlayalım: diff --git a/docs/translate.php b/docs/translate.php index 5da049db1d..045fe3393c 100644 --- a/docs/translate.php +++ b/docs/translate.php @@ -92,7 +92,8 @@ function sanitizeMarkdown(string $markdown): string return trim($markdown) . "\n"; } -$fileToTranslate = $argv[1] ?? ''; +$fileToTranslate = $argv; +array_shift($fileToTranslate); $apiKey = $_SERVER['GEMINI_API_KEY'] ?? $_ENV['GEMINI_API_KEY'] ?? ''; if (!$apiKey) { echo 'Enter gemini api key ($GEMINI_API_KEY): '; @@ -102,7 +103,7 @@ function sanitizeMarkdown(string $markdown): string $files = array_filter(scandir(__DIR__), fn($filename) => str_ends_with($filename, '.md')); foreach ($files as $file) { $englishFile = file_get_contents(__DIR__ . "/$file"); - if ($fileToTranslate && $fileToTranslate !== $file && "$fileToTranslate.md" !== $file) { + if ($fileToTranslate && !in_array($file, $fileToTranslate)) { continue; } foreach (LANGUAGES as $language => $languageName) { From 6fa23d3dec6101095b31e4d9684fdf8a51b06d87 Mon Sep 17 00:00:00 2001 From: Alliballibaba Date: Sat, 10 Jan 2026 18:35:31 +0100 Subject: [PATCH 6/9] Removes doc changes. --- .github/workflows/translate.yaml | 36 +- dev.Dockerfile | 2 - docs/cn/classic.md | 6 +- docs/cn/compile.md | 18 +- docs/cn/config.md | 136 ++--- docs/cn/docker.md | 48 +- docs/cn/early-hints.md | 2 +- docs/cn/embed.md | 33 +- docs/cn/extensions.md | 331 ++++-------- docs/cn/github-actions.md | 31 +- docs/cn/hot-reload.md | 139 ----- docs/cn/known-issues.md | 40 +- docs/cn/laravel.md | 46 +- docs/cn/logging.md | 66 --- docs/cn/mercure.md | 149 +----- docs/cn/performance.md | 35 +- docs/cn/production.md | 23 +- docs/cn/static.md | 62 +-- docs/cn/wordpress.md | 59 -- docs/cn/worker.md | 20 +- docs/fr/classic.md | 12 +- docs/fr/compile.md | 39 +- docs/fr/config.md | 282 ++++------ docs/fr/docker.md | 56 +- docs/fr/embed.md | 19 +- docs/fr/extensions.md | 167 +++--- docs/fr/github-actions.md | 2 +- docs/fr/hot-reload.md | 139 ----- docs/fr/known-issues.md | 16 +- docs/fr/laravel.md | 54 +- docs/fr/logging.md | 67 --- docs/fr/mercure.md | 147 +---- docs/fr/metrics.md | 18 +- docs/fr/performance.md | 90 ++-- docs/fr/production.md | 20 +- docs/fr/static.md | 19 +- docs/fr/wordpress.md | 59 -- docs/fr/worker.md | 37 +- docs/fr/x-sendfile.md | 7 +- docs/ja/classic.md | 2 +- docs/ja/compile.md | 34 +- docs/ja/config.md | 148 ++--- docs/ja/docker.md | 19 +- docs/ja/extensions.md | 401 +++----------- docs/ja/hot-reload.md | 138 ----- docs/ja/known-issues.md | 15 +- docs/ja/laravel.md | 36 +- docs/ja/logging.md | 71 --- docs/ja/mercure.md | 140 +---- docs/ja/performance.md | 44 +- docs/ja/production.md | 4 +- docs/ja/static.md | 8 +- docs/ja/wordpress.md | 59 -- docs/ja/worker.md | 29 +- docs/ja/x-sendfile.md | 1 + docs/known-issues.md | 2 +- docs/pt-br/classic.md | 29 +- docs/pt-br/compile.md | 65 ++- docs/pt-br/config.md | 381 +++++++------ docs/pt-br/docker.md | 78 ++- docs/pt-br/embed.md | 18 +- docs/pt-br/extensions.md | 393 +++++--------- docs/pt-br/github-actions.md | 25 +- docs/pt-br/hot-reload.md | 139 ----- docs/pt-br/known-issues.md | 100 ++-- docs/pt-br/laravel.md | 145 ++--- docs/pt-br/logging.md | 73 --- docs/pt-br/mercure.md | 152 +----- docs/pt-br/metrics.md | 34 +- docs/pt-br/performance.md | 104 ++-- docs/pt-br/production.md | 15 +- docs/pt-br/static.md | 6 +- docs/pt-br/wordpress.md | 59 -- docs/pt-br/worker.md | 28 +- docs/pt-br/x-sendfile.md | 19 +- docs/ru/classic.md | 11 - docs/ru/compile.md | 95 ++-- docs/ru/config.md | 250 +++------ docs/ru/docker.md | 54 +- docs/ru/early-hints.md | 2 +- docs/ru/embed.md | 47 +- docs/ru/extensions.md | 893 ------------------------------- docs/ru/github-actions.md | 4 +- docs/ru/hot-reload.md | 139 ----- docs/ru/known-issues.md | 62 ++- docs/ru/laravel.md | 55 +- docs/ru/logging.md | 71 --- docs/ru/mercure.md | 149 +----- docs/ru/metrics.md | 20 +- docs/ru/performance.md | 155 ++---- docs/ru/production.md | 78 ++- docs/ru/static.md | 114 +--- docs/ru/wordpress.md | 59 -- docs/ru/worker.md | 107 ++-- docs/ru/x-sendfile.md | 71 --- docs/tr/classic.md | 11 - docs/tr/compile.md | 103 ++-- docs/tr/config.md | 238 ++------ docs/tr/docker.md | 76 +-- docs/tr/early-hints.md | 2 +- docs/tr/embed.md | 59 +- docs/tr/extensions.md | 893 ------------------------------- docs/tr/github-actions.md | 31 +- docs/tr/hot-reload.md | 139 ----- docs/tr/known-issues.md | 79 +-- docs/tr/laravel.md | 50 +- docs/tr/logging.md | 71 --- docs/tr/mercure.md | 146 +---- docs/tr/metrics.md | 17 - docs/tr/performance.md | 154 ------ docs/tr/production.md | 37 +- docs/tr/static.md | 110 +--- docs/tr/wordpress.md | 59 -- docs/tr/worker.md | 101 +--- docs/tr/x-sendfile.md | 69 --- docs/translate.php | 7 +- 116 files changed, 2156 insertions(+), 8278 deletions(-) delete mode 100644 docs/cn/hot-reload.md delete mode 100644 docs/cn/logging.md delete mode 100644 docs/cn/wordpress.md delete mode 100644 docs/fr/hot-reload.md delete mode 100644 docs/fr/logging.md delete mode 100644 docs/fr/wordpress.md delete mode 100644 docs/ja/hot-reload.md delete mode 100644 docs/ja/logging.md delete mode 100644 docs/ja/wordpress.md delete mode 100644 docs/pt-br/hot-reload.md delete mode 100644 docs/pt-br/logging.md delete mode 100644 docs/pt-br/wordpress.md delete mode 100644 docs/ru/classic.md delete mode 100644 docs/ru/extensions.md delete mode 100644 docs/ru/hot-reload.md delete mode 100644 docs/ru/logging.md delete mode 100644 docs/ru/wordpress.md delete mode 100644 docs/ru/x-sendfile.md delete mode 100644 docs/tr/classic.md delete mode 100644 docs/tr/extensions.md delete mode 100644 docs/tr/hot-reload.md delete mode 100644 docs/tr/logging.md delete mode 100644 docs/tr/metrics.md delete mode 100644 docs/tr/performance.md delete mode 100644 docs/tr/wordpress.md delete mode 100644 docs/tr/x-sendfile.md diff --git a/.github/workflows/translate.yaml b/.github/workflows/translate.yaml index 7ab93a5cdd..6d8f91ac6f 100644 --- a/.github/workflows/translate.yaml +++ b/.github/workflows/translate.yaml @@ -25,10 +25,10 @@ jobs: - id: md_files run: | # Check for changed .md files under docs/*.md - FILES=$(git diff --name-only ${{ github.event.before }} ${{ github.sha }} -- 'docs/*.md') + FILES=$(git diff --name-only "${{ github.event.before }}" "${{ github.sha }}" -- 'docs/*.md') FILES=$(echo "$FILES" | xargs -n1 basename | tr '\n' ' ') - [ -z "$FILES" ] && echo "found=false" >> $GITHUB_OUTPUT || echo "found=true" >> $GITHUB_OUTPUT - echo "files=$FILES" >> $GITHUB_OUTPUT + [ -z "$FILES" ] && echo "found=false" >> $GITHUB_OUTPUT || echo "found=true" >> "$GITHUB_OUTPUT" + echo "files=$FILES" >> "$GITHUB_OUTPUT" - name: Set up PHP if: steps.md_files.outputs.found == 'true' uses: shivammathur/setup-php@v2 @@ -39,7 +39,8 @@ jobs: env: GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }} run: | - php ./docs/translate.php ${{ steps.md_files.outputs.files }} + IFS=' ' read -r -a FILES <<< "${{ steps.md_files.outputs.files }}" + php ./docs/translate.php "${FILES[@]}" - name: Run Linter if: steps.md_files.outputs.found == 'true' uses: super-linter/super-linter/slim@v8 @@ -49,16 +50,21 @@ jobs: MARKDOWN_CONFIG_FILE: .markdown-lint.yaml FIX_NATURAL_LANGUAGE: true FIX_MARKDOWN: true - - name: Commit & push changes - if: steps.md_files.outputs.found == 'true' - run: | - BRANCH="translations/$(date +%s)" - git checkout -b "$BRANCH" - git config --global user.email "github-actions[bot]@users.noreply.github.com" - git config --global user.name "github-actions[bot]" - git add . - git commit -m "chore: auto-lint fixes" - git push --set-upstream origin "$BRANCH" - echo "branch=$BRANCH" >> "$GITHUB_OUTPUT" - name: Create Pull Request + if: steps.md_files.outputs.found == 'true' uses: peter-evans/create-pull-request@v8 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + commit-message: Update translations + committer: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> + author: ${{ github.actor }} <${{ github.actor_id }}+${{ github.actor }}@users.noreply.github.com> + branch: translations/${{ github.run_id }} + delete-branch: true + title: '[Translations] Update non-english docs' + body: | + Translation updates for: ${{ steps.md_files.outputs.files }}. + labels: | + translations + bot-generated + draft: false diff --git a/dev.Dockerfile b/dev.Dockerfile index 5fbbc5732b..62e83e3821 100644 --- a/dev.Dockerfile +++ b/dev.Dockerfile @@ -37,7 +37,6 @@ RUN apt-get update && \ # Dev tools \ git \ clang \ - clang-format \ cmake \ llvm \ gdb \ @@ -57,7 +56,6 @@ RUN git clone --branch=PHP-8.5 https://github.com/php/php-src.git . && \ EXTENSION_DIR=/usr/lib/frankenphp/modules ./configure \ --enable-embed \ --enable-zts \ - --with-openssl \ --disable-zend-signals \ --enable-zend-max-execution-timers \ --with-config-file-path=/etc/frankenphp/php.ini \ diff --git a/docs/cn/classic.md b/docs/cn/classic.md index 4b05cc31e5..5fbe797060 100644 --- a/docs/cn/classic.md +++ b/docs/cn/classic.md @@ -2,10 +2,10 @@ 在没有任何额外配置的情况下,FrankenPHP 以经典模式运行。在此模式下,FrankenPHP 的功能类似于传统的 PHP 服务器,直接提供 PHP 文件服务。这使其成为 PHP-FPM 或 Apache with mod_php 的无缝替代品。 -与 Caddy 类似,FrankenPHP 接受无限数量的连接,并使用[固定数量的线程](config.md#caddyfile-config)来为它们提供服务。接受和排队的连接数量仅受可用系统资源的限制。 -PHP 线程池以启动时初始化的固定数量的线程运行,这类似于 PHP-FPM 的静态模式。线程也可以在[运行时自动扩展](performance.md#max_threads),类似于 PHP-FPM 的动态模式。 +与 Caddy 类似,FrankenPHP 接受无限数量的连接,并使用[固定数量的线程](config.md#caddyfile-配置)来为它们提供服务。接受和排队的连接数量仅受可用系统资源的限制。 +PHP 线程池使用在启动时初始化的固定数量的线程运行,类似于 PHP-FPM 的静态模式。也可以让线程在[运行时自动扩展](performance.md#max_threads),类似于 PHP-FPM 的动态模式。 -排队的连接将无限期等待,直到有 PHP 线程可以为它们提供服务。为了避免这种情况,你可以在 FrankenPHP 的全局配置中使用 `max_wait_time` [配置](config.md#caddyfile-config)来限制请求在被拒绝之前等待空闲 PHP 线程的时间。 +排队的连接将无限期等待,直到有 PHP 线程可以为它们提供服务。为了避免这种情况,你可以在 FrankenPHP 的全局配置中使用 max_wait_time [配置](config.md#caddyfile-配置)来限制请求可以等待空闲的 PHP 线程的时间,超时后将被拒绝。 此外,你还可以在 Caddy 中设置合理的[写超时](https://caddyserver.com/docs/caddyfile/options#timeouts)。 每个 Caddy 实例只会启动一个 FrankenPHP 线程池,该线程池将在所有 `php_server` 块之间共享。 diff --git a/docs/cn/compile.md b/docs/cn/compile.md index 3553849ea6..5432ab5ea6 100644 --- a/docs/cn/compile.md +++ b/docs/cn/compile.md @@ -1,13 +1,13 @@ # 从源代码编译 -本文档解释了如何创建一个 FrankenPHP 二进制文件,它将 PHP 加载为一个动态库。 +本文档解释了如何创建一个 FrankenPHP 构建,它将 PHP 加载为一个动态库。 这是推荐的方法。 -或者,也可以创建 [完全静态和半静态构建](static.md)。 +或者,你也可以 [编译静态版本](static.md)。 ## 安装 PHP -FrankenPHP 兼容 PHP 8.2 及更高版本。 +FrankenPHP 支持 PHP 8.2 及更高版本。 ### 使用 Homebrew (Linux 和 Mac) @@ -55,7 +55,7 @@ brew install libiconv bison brotli re2c pkg-config watcher echo 'export PATH="/opt/homebrew/opt/bison/bin:$PATH"' >> ~/.zshrc ``` -然后运行 configure 脚本: +然后运行 `./configure` 脚本: ```console ./configure \ @@ -80,10 +80,9 @@ sudo make install 或者,可以通过向 Go 编译器传递构建标签来禁用这些功能。 | 功能 | 依赖项 | 用于禁用的构建标签 | -| :-------------------- | :-------------------------------------------------------------------- | :----------------- | +| --------------------- | --------------------------------------------------------------------- | ------------------ | | Brotli 压缩 | [Brotli](https://github.com/google/brotli) | nobrotli | | 文件更改时重启 worker | [Watcher C](https://github.com/e-dant/watcher/tree/release/watcher-c) | nowatcher | -| [Mercure](mercure.md) | [Mercure Go 库](https://pkg.go.dev/github.com/dunglas/mercure)(自动安装,AGPL 许可) | nomercure | ## 编译 Go 应用 @@ -103,12 +102,8 @@ xcaddy build \ --output frankenphp \ --with github.com/dunglas/frankenphp/caddy \ --with github.com/dunglas/mercure/caddy \ - --with github.com/dunglas/vulcain/caddy \ - --with github.com/dunglas/caddy-cbrotli + --with github.com/dunglas/vulcain/caddy # 在这里添加额外的 Caddy 模块和 FrankenPHP 扩展 - # (可选)如果你想从 FrankenPHP 源代码编译: - # --with github.com/dunglas/frankenphp=$(pwd) \ - # --with github.com/dunglas/frankenphp/caddy=$(pwd)/caddy ``` > [!TIP] @@ -129,3 +124,4 @@ xcaddy build \ curl -L https://github.com/php/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 +``` diff --git a/docs/cn/config.md b/docs/cn/config.md index c278aefa5f..3b46811251 100644 --- a/docs/cn/config.md +++ b/docs/cn/config.md @@ -1,35 +1,17 @@ # 配置 -FrankenPHP、Caddy 以及 [Mercure](mercure.md) 和 [Vulcain](https://vulcain.rocks) 模块可以使用 [Caddy 支持的格式](https://caddyserver.com/docs/getting-started#your-first-config) 进行配置。 +FrankenPHP、Caddy 以及 Mercure 和 Vulcain 模块可以使用 [Caddy 支持的格式](https://caddyserver.com/docs/getting-started#your-first-config) 进行配置。 -最常见的格式是 `Caddyfile`,它是一种简单、人类可读的文本格式。默认情况下,FrankenPHP 会在当前目录中查找 `Caddyfile`。你可以使用 `-c` 或 `--config` 选项指定自定义路径。 - -以下显示了一个用于服务 PHP 应用程序的最小 `Caddyfile`: - -```caddyfile -# 响应的主机名 -localhost - -# (可选)提供文件服务的目录,否则默认为当前目录 -#root public/ -php_server -``` - -FrankenPHP 仓库中提供了 [一个更高级的 `Caddyfile`](https://github.com/php/frankenphp/blob/main/caddy/frankenphp/Caddyfile),该文件支持更多功能并提供方便的环境变量,Docker 镜像也附带了该文件。 +在 [Docker 镜像](docker.md) 中,`Caddyfile` 位于 `/etc/frankenphp/Caddyfile`。 +静态二进制文件也会在执行 `frankenphp run` 命令的目录中查找 `Caddyfile`。 +你可以使用 `-c` 或 `--config` 选项指定自定义路径。 PHP 本身可以[使用 `php.ini` 文件](https://www.php.net/manual/zh/configuration.file.php)进行配置。 -根据你的安装方法,FrankenPHP 和 PHP 解释器将在下面描述的位置查找配置文件。 +根据你的安装方法,PHP 解释器将在上述位置查找配置文件。 ## Docker -FrankenPHP: - -- `/etc/frankenphp/Caddyfile`:主配置文件 -- `/etc/frankenphp/Caddyfile.d/*.caddyfile`:自动加载的附加配置文件 - -PHP: - - `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-/` @@ -47,24 +29,12 @@ RUN cp $PHP_INI_DIR/php.ini-development $PHP_INI_DIR/php.ini ## RPM 和 Debian 包 -FrankenPHP: - -- `/etc/frankenphp/Caddyfile`:主配置文件 -- `/etc/frankenphp/Caddyfile.d/*.caddyfile`:自动加载的附加配置文件 - -PHP: - -- `php.ini`: `/etc/php-zts/php.ini`(默认情况下提供带有生产预设的 `php.ini` 文件) -- 附加配置文件: `/etc/php-zts/conf.d/*.ini` +- `php.ini`: `/etc/frankenphp/php.ini`(默认情况下提供带有生产预设的 `php.ini` 文件) +- 附加配置文件: `/etc/frankenphp/php.d/*.ini` +- PHP 扩展: `/usr/lib/frankenphp/modules/` ## 静态二进制文件 -FrankenPHP: - -- 在当前工作目录中:`Caddyfile` - -PHP: - - `php.ini`: 执行 `frankenphp run` 或 `frankenphp php-server` 的目录,然后是 `/etc/frankenphp/php.ini` - 附加配置文件: `/etc/frankenphp/php.d/*.ini` - PHP 扩展: 无法加载,将它们打包在二进制文件本身中 @@ -85,13 +55,14 @@ localhost { } ``` -你还可以使用 `frankenphp` [全局选项](https://caddyserver.com/docs/caddyfile/concepts#global-options) 显式配置 FrankenPHP: +你还可以使用全局选项显式配置 FrankenPHP: +`frankenphp` [全局选项](https://caddyserver.com/docs/caddyfile/concepts#global-options) 可用于配置 FrankenPHP。 ```caddyfile { frankenphp { num_threads # 设置要启动的 PHP 线程数量。默认:可用 CPU 数量的 2 倍。 - max_threads # 限制可以在运行时启动的额外 PHP 线程的数量。默认值:num_threads。可以设置为 'auto'。 + max_threads # 限制可以在运行时启动的额外 PHP 线程的数量。默认值:num_threads。可以设置为 'auto'。 max_wait_time # 设置请求在超时之前可以等待的最大时间,直到找到一个空闲的 PHP 线程。 默认:禁用。 php_ini # 设置一个 php.ini 指令。可以多次使用以设置多个指令。 worker { @@ -108,7 +79,7 @@ localhost { # ... ``` -或者,您可以使用 `worker` 选项的一行简短形式: +或者,您可以使用 `worker` 选项的一行简短形式。 ```caddyfile { @@ -120,7 +91,7 @@ localhost { # ... ``` -如果您在同一服务器上服务多个应用程序,您还可以定义多个工作线程: +如果您在同一服务器上服务多个应用程序,您还可以定义多个工作线程: ```caddyfile app.example.com { @@ -147,7 +118,7 @@ other.example.com { `php` 指令将所有输入传递给 PHP,而不是先检查是否 是一个PHP文件。在[性能页面](performance.md#try_files)中了解更多关于它的信息。 -使用 `php_server` 指令等同于以下配置: +使用 `php_server` 指令等同于以下配置: ```caddyfile route { @@ -181,7 +152,7 @@ php_server [] { file_server off # 禁用内置的 file_server 指令。 worker { # 为此服务器创建特定的worker。可以多次指定以创建多个workers。 file # 设置工作脚本的路径,可以相对于 php_server 根目录 - num # 设置要启动的 PHP 线程数,默认为可用 CPU 数量的 2 倍 + num # 设置要启动的 PHP 线程数,默认为可用数量的 2 倍 name # 为worker设置名称,用于日志和指标。默认值:worker文件的绝对路径。定义在 php_server 块中时,始终以 m# 开头。 watch # 设置要监视文件更改的路径。可以为多个路径多次指定。 env # 设置一个额外的环境变量为给定值。可以多次指定以设置多个环境变量。此工作进程的环境变量也从 php_server 父进程继承,但可以在此处覆盖。 @@ -196,7 +167,7 @@ php_server [] { 由于 workers 只会启动您的应用程序一次并将其保留在内存中, 因此对您的 PHP 文件的任何更改不会立即反映出来。 -工作线程可以通过 `watch` 指令在文件更改时重新启动。 +Wworkers 可以通过 `watch` 指令在文件更改时重新启动。 这对开发环境很有用。 ```caddyfile @@ -210,10 +181,8 @@ php_server [] { } ``` -此功能通常与 [热重载](hot-reload.md) 结合使用。 - -如果没有指定 `watch` 目录,它将回退到 `./**/*.{env,php,twig,yaml,yml}`, -这将监视启动 FrankenPHP 进程的目录及其子目录中的所有 `.env`、`.php`、`.twig`、`.yaml` 和 `.yml` 文件。 +如果没有指定 `watch` 目录,它将回退到 `./**/*.{php,yaml,yml,twig,env}`, +这将监视启动 FrankenPHP 进程的目录及其子目录中的所有 `.php`、`.yaml`、`.yml`、`.twig` 和 `.env` 文件。 你也可以通过 [shell 文件名模式](https://pkg.go.dev/path/filepath#Match) 指定一个或多个目录: ```caddyfile @@ -260,6 +229,34 @@ php_server [] { } ``` +### 全双工 (HTTP/1) + +在使用HTTP/1.x时,可能希望启用全双工模式,以便在完整主体之前写入响应。 +已被阅读。(例如:WebSocket、服务器发送事件等。) + +这是一个可选配置,需要添加到 `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` 的情况下注入 Caddy 指令: @@ -271,12 +268,12 @@ php_server [] { 至于 FPM 和 CLI SAPIs,环境变量默认在 `$_SERVER` 超全局中暴露。 -[the `variables_order` PHP 指令](https://www.php.net/manual/zh/ini.core.php#ini.variables-order) 的 `S` 值始终等于 `ES`,无论 `E` 在该指令中的其他位置如何。 +[the `variables_order` PHP 指令](https://www.php.net/manual/en/ini.core.php#ini.variables-order) 的 `S` 值始终等于 `ES`,无论 `E` 在该指令中的其他位置如何。 ## PHP 配置 -要加载[附加的 PHP 配置文件](https://www.php.net/manual/zh/configuration.file.php#configuration.file.scan), -可以使用 `PHP_INI_SCAN_DIR` 环境变量。 +加载[附加的 PHP 配置文件](https://www.php.net/manual/en/configuration.file.php#configuration.file.scan), +`PHP_INI_SCAN_DIR`环境变量可以被使用。 设置后,PHP 将加载给定目录中所有带有 `.ini` 扩展名的文件。 您还可以通过在 `Caddyfile` 中使用 `php_ini` 指令来更改 PHP 配置: @@ -296,41 +293,6 @@ php_server [] { } ``` -### 禁用 HTTPS - -默认情况下,FrankenPHP 会为所有主机名(包括 `localhost`)自动启用 HTTPS。如果你想禁用 HTTPS(例如在开发环境中),你可以将 `SERVER_NAME` 环境变量设置为 `http://` 或 `:80`: - -另外,你可以使用 [Caddy 文档](https://caddyserver.com/docs/automatic-https#activation) 中描述的所有其他方法。 - -如果你想使用 `127.0.0.1` IP 地址而不是 `localhost` 主机名来使用 HTTPS,请阅读 [已知问题](known-issues.md#using-https127001-with-docker) 部分。 - -### 全双工 (HTTP/1) - -在使用 HTTP/1.x 时,可能希望启用全双工模式,以允许在读取整个请求体之前写入响应。(例如:[Mercure](mercure.md)、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)中找到有关此设置的更多信息。 - ## 启用调试模式 使用Docker镜像时,将`CADDY_GLOBAL_OPTIONS`环境变量设置为`debug`以启用调试模式: diff --git a/docs/cn/docker.md b/docs/cn/docker.md index eba151b7e2..7d7402221f 100644 --- a/docs/cn/docker.md +++ b/docs/cn/docker.md @@ -7,7 +7,7 @@ 标签遵循此模式:`dunglas/frankenphp:-php-` - `` 和 `` 分别是 FrankenPHP 和 PHP 的版本号,范围从主版本(例如 `1`)、次版本(例如 `1.2`)到补丁版本(例如 `1.2.3`)。 -- `` 可以是 `trixie`(用于 Debian Trixie)、`bookworm`(用于 Debian Bookworm),也可以是 `alpine`(用于 Alpine 的最新稳定版本)。 +- `` 要么是 `bookworm`(用于 Debian Bookworm)要么是 `alpine`(用于 Alpine 的最新稳定版本)。 [浏览标签](https://hub.docker.com/r/dunglas/frankenphp/tags)。 @@ -28,10 +28,6 @@ docker build -t my-php-app . docker run -it --rm --name my-running-app my-php-app ``` -## 如何调整配置 - -为方便起见,镜像中提供了一个包含有用环境变量的[默认 Caddyfile](https://github.com/php/frankenphp/blob/main/caddy/frankenphp/Caddyfile)。 - ## 如何安装更多 PHP 扩展 [`docker-php-extension-installer`](https://github.com/mlocati/docker-php-extension-installer) 脚本在基础镜像中提供。 @@ -84,13 +80,14 @@ 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,包括 Debian 和 Alpine。 +[用于构建的镜像](https://hub.docker.com/r/dunglas/frankenphp/tags?name=builder) 适用于所有版本的 FrankenPHP 和 PHP,包括 Alpine 和 Debian。 > [!TIP] > -> 如果你正在使用 Alpine Linux 和 Symfony,你可能需要 [增加默认堆栈大小](compile.md#using-xcaddy)。 +> 如果你的系统基于 musl libc(Alpine Linux 上默认使用)并搭配 Symfony 使用, +> 你可能需要 [增加默认堆栈大小](compile.md#using-xcaddy)。 -## 默认启用 Worker 模式 +## 默认启用 worker 模式 设置 `FRANKENPHP_CONFIG` 环境变量以使用 worker 脚本启动 FrankenPHP: @@ -102,9 +99,9 @@ FROM dunglas/frankenphp ENV FRANKENPHP_CONFIG="worker ./public/index.php" ``` -## 开发时使用卷 +## 开发挂载宿主机目录 -要使用 FrankenPHP 轻松开发,请将包含应用程序源代码的主机目录作为卷挂载到 Docker 容器中: +要使用 FrankenPHP 轻松开发,请从包含应用程序源代码的主机挂载目录作为 Docker 容器中的 volume: ```console docker run -v $PWD:/app/public -p 80:80 -p 443:443 -p 443:443/udp --tty my-php-app @@ -134,16 +131,16 @@ services: - ./:/app/public - caddy_data:/data - caddy_config:/config - # 在生产环境中注释以下行,它允许在开发中输出清晰可读的日志 + # 在生产环境中注释以下行,它允许在 dev 中使用清晰可读日志 tty: true -# Caddy 证书和配置所需的卷 +# Caddy 证书和配置所需的挂载目录 volumes: caddy_data: caddy_config: ``` -## 以非 Root 用户身份运行 +## 以非 root 用户身份运行 FrankenPHP 可以在 Docker 中以非 root 用户身份运行。 @@ -157,21 +154,21 @@ ARG USER=appuser RUN \ # 在基于 alpine 的发行版使用 "adduser -D ${USER}" useradd ${USER}; \ - # 添加绑定到 80 和 443 端口的额外能力 + # 需要开放80和443端口的权限 setcap CAP_NET_BIND_SERVICE=+eip /usr/local/bin/frankenphp; \ - # 授予 /config/caddy 和 /data/caddy 写入权限 + # 需要 /config/caddy 和 /data/caddy 目录的写入权限 chown -R ${USER}:${USER} /config/caddy /data/caddy USER ${USER} ``` -### 无能力运行 +### 无权限运行 -即使在无根运行时,FrankenPHP 也需要 `CAP_NET_BIND_SERVICE` 能力来将 +即使在无根运行时,FrankenPHP 也需要 `CAP_NET_BIND_SERVICE` 权限来将 Web 服务器绑定到特权端口(80 和 443)。 如果你在非特权端口(1024 及以上)上公开 FrankenPHP,则可以以非 root 用户身份运行 -Web 服务器,并且不需要任何能力: +Web 服务器,并且不需要任何权限: ```dockerfile FROM dunglas/frankenphp @@ -181,7 +178,7 @@ ARG USER=appuser RUN \ # 在基于 alpine 的发行版使用 "adduser -D ${USER}" useradd ${USER}; \ - # 移除默认能力 + # 移除默认权限 setcap -r /usr/local/bin/frankenphp; \ # 给予 /config/caddy 和 /data/caddy 写入权限 chown -R ${USER}:${USER} /config/caddy /data/caddy @@ -194,15 +191,14 @@ USER ${USER} ## 更新 -Docker 镜像会在以下情况下构建: +Docker 镜像会按照以下条件更新: -- 发布新的版本时 -- 每日 UTC 时间 4 点,如果官方 PHP 镜像有新版本可用时 +- 发布新的版本后 +- 每日 4:00(UTC 时间)检查新的 PHP 镜像 ## 开发版本 -开发版本可在 [`dunglas/frankenphp-dev`](https://hub.docker.com/repository/docker/dunglas/frankenphp-dev) Docker 仓库中提供。 -每次有新的提交推送到 GitHub 仓库的主分支时,都会触发一次新的构建。 +可在此 [`dunglas/frankenphp-dev`](https://hub.docker.com/repository/docker/dunglas/frankenphp-dev) 仓库获取开发版本。 +每次在 GitHub 仓库的主分支有新的 commit 都会触发一次新的 build。 -`latest*` 标签指向 `main` 分支的头部。 -同时,`sha-` 形式的标签也可用。 \ No newline at end of file +`latest*` tag 指向最新的 `main` 分支,且同样支持 `sha-` 的 tag。 diff --git a/docs/cn/early-hints.md b/docs/cn/early-hints.md index 5a58880816..a45eb4cc41 100644 --- a/docs/cn/early-hints.md +++ b/docs/cn/early-hints.md @@ -9,7 +9,7 @@ FrankenPHP 原生支持 [103 Early Hints 状态码](https://developer.chrome.com header('Link: ; rel=preload; as=style'); headers_send(103); -// 你的慢速算法和 SQL 查询 🤪 +// 慢速算法和 SQL 查询 echo <<<'HTML' diff --git a/docs/cn/embed.md b/docs/cn/embed.md index 2aab5c2e65..2ef8b936e3 100644 --- a/docs/cn/embed.md +++ b/docs/cn/embed.md @@ -4,25 +4,25 @@ FrankenPHP 能够将 PHP 应用程序的源代码和资源文件嵌入到静态 由于这个特性,PHP 应用程序可以作为独立的二进制文件分发,包括应用程序本身、PHP 解释器和生产级 Web 服务器 Caddy。 -了解有关此功能的更多信息 [Kévin 在 SymfonyCon 2023 上的演讲](https://dunglas.dev/2023/12/php-and-symfony-apps-as-standalone-binaries/)。 +了解有关此功能的更多信息 [Kévin 在 SymfonyCon 上的演讲](https://dunglas.dev/2023/12/php-and-symfony-apps-as-standalone-binaries/)。 -要嵌入 Laravel 应用程序,请[阅读此特定文档条目](laravel.md#laravel-apps-as-standalone-binaries)。 +有关嵌入 Laravel 应用程序,请[阅读此特定文档条目](laravel.md#laravel-apps-as-standalone-binaries)。 ## 准备你的应用 -在创建独立二进制文件之前,请确保应用已准备好进行嵌入。 +在创建独立二进制文件之前,请确保应用已准备好进行打包。 例如,你可能希望: - 给应用安装生产环境的依赖 -- 生成自动加载器 -- 为应用程序启用生产模式(如果有的话) -- 剔除不需要的文件,例如 `.git` 或测试文件,以减小最终二进制文件的大小 +- 导出 autoloader +- 如果可能,为应用启用生产模式 +- 丢弃不需要的文件,例如 `.git` 或测试文件,以减小最终二进制文件的大小 例如,对于 Symfony 应用程序,你可以使用以下命令: ```console -# 导出项目以清除 .git/ 等 +# 导出项目以避免 .git/ 等目录 mkdir $TMPDIR/my-prepared-app git archive HEAD | tar -x -C $TMPDIR/my-prepared-app cd $TMPDIR/my-prepared-app @@ -32,7 +32,7 @@ echo APP_ENV=prod > .env.local echo APP_DEBUG=0 >> .env.local # 删除测试和其他不需要的文件以节省空间 -# 或者,在 .gitattributes 文件中使用 export-ignore 属性添加这些文件 +# 或者,将这些文件添加到您的 .gitattributes 文件中,并设置 export-ignore 属性 rm -Rf tests/ # 安装依赖项 @@ -44,19 +44,20 @@ composer dump-env prod ### 自定义配置 -要自定义[配置](config.md),您可以将 `Caddyfile` 和 `php.ini` 文件放置在要嵌入的应用程序的主目录中(在之前的示例中是 `$TMPDIR/my-prepared-app`)。 +要自定义[配置](config.md),您可以放置一个 `Caddyfile` 以及一个 `php.ini` 文件 +在应用程序的主目录中嵌入(在之前的示例中是`$TMPDIR/my-prepared-app`)。 ## 创建 Linux 二进制文件 创建 Linux 二进制文件的最简单方法是使用我们提供的基于 Docker 的构建器。 -1. 在你的应用程序的存储库中创建一个名为 `static-build.Dockerfile` 的文件: +1. 在准备好的应用的存储库中创建一个名为 `static-build.Dockerfile` 的文件。 ```dockerfile FROM --platform=linux/amd64 dunglas/frankenphp:static-builder-gnu - # 如果你打算在 musl-libc 系统上运行该二进制文件,请改用 static-builder-musl + # 如果你打算在 glibc 系统上运行该二进制文件,请使用 static-builder-gnu - # 复制你的应用程序 + # 复制应用代码 WORKDIR /go/src/app/dist/app COPY . . @@ -76,7 +77,7 @@ composer dump-env prod docker build -t static-app -f static-build.Dockerfile . ``` -3. 提取二进制文件: +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 @@ -94,7 +95,7 @@ cd frankenphp EMBED=/path/to/your/app ./build-static.sh ``` -生成的二进制文件是 `dist/` 目录中名为 `frankenphp--` 的文件。 +在 `dist/` 目录中生成的二进制文件名称为 `frankenphp--`。 ## 使用二进制文件 @@ -137,7 +138,7 @@ EMBED=/path/to/your/app ./build-static.sh ## 分发二进制文件 -在 Linux 上,创建的二进制文件使用 [UPX](https://upx.github.io) 进行压缩。 +在Linux上,创建的二进制文件使用[UPX](https://upx.github.io)进行压缩。 -在 Mac 上,您可以在发送文件之前压缩它以减小文件大小。 +在Mac上,您可以在发送文件之前压缩它以减小文件大小。 我们推荐使用 `xz`。 diff --git a/docs/cn/extensions.md b/docs/cn/extensions.md index 2192138c89..4e2c120f17 100644 --- a/docs/cn/extensions.md +++ b/docs/cn/extensions.md @@ -1,6 +1,6 @@ # 使用 Go 编写 PHP 扩展 -使用 FrankenPHP,你可以**使用 Go 编写 PHP 扩展**,这允许你创建**高性能的原生函数**,可以直接从 PHP 调用。你的应用程序可以利用任何现有或新的 Go 库,以及直接从你的 PHP 代码中使用**臭名昭著的协程(goroutines)并发模型**。 +使用 FrankenPHP,你可以**使用 Go 编写 PHP 扩展**,这允许你创建**高性能的原生函数**,可以直接从 PHP 调用。你的应用程序可以利用任何现有或新的 Go 库,以及直接从你的 PHP 代码中使用**协程(goroutines)的并发模型**。 编写 PHP 扩展通常使用 C 语言完成,但通过一些额外的工作,也可以使用其他语言编写。PHP 扩展允许你利用底层语言的强大功能来扩展 PHP 的功能,例如,通过添加原生函数或优化特定操作。 @@ -8,10 +8,10 @@ ## 两种方法 -FrankenPHP 提供两种方式来**用 Go 创建 PHP 扩展**: +FrankenPHP 提供两种方式来创建 Go 语言的 PHP 扩展: -1. **使用扩展生成器** - 推荐的方法,为大多数用例生成所有必要的样板代码,让你专注于编写 Go 代码 -2. **手动实现** - 对于高级用例,完全控制扩展结构 +1. **使用扩展生成器** - 推荐的方法,为大多数用例生成所有必要的样板代码,让你专注于编写 Go 代码 +2. **手动实现** - 对于高级用例,完全控制扩展结构 我们将从生成器方法开始,因为这是最简单的入门方式,然后为那些需要完全控制的人展示手动实现。 @@ -33,7 +33,7 @@ FrankenPHP 捆绑了一个工具,允许你**仅使用 Go 创建 PHP 扩展** 在 Go 中编写 PHP 扩展的第一步是创建一个新的 Go 模块。你可以使用以下命令: ```console -go mod init example.com/example +go mod init github.com/my-account/my-module ``` 第二步是为后续步骤[获取 PHP 源代码](https://www.php.net/downloads.php)。获取后,将它们解压到你选择的目录中,不要放在你的 Go 模块内: @@ -47,15 +47,10 @@ tar xf php-* 现在一切都设置好了,可以在 Go 中编写你的原生函数。创建一个名为 `stringext.go` 的新文件。我们的第一个函数将接受一个字符串作为参数,重复次数,一个布尔值来指示是否反转字符串,并返回结果字符串。这应该看起来像这样: ```go -package example - -// #include -import "C" import ( + "C" + "github.com/dunglas/frankenphp" "strings" - "unsafe" - - "github.com/dunglas/frankenphp" ) //export_php:function repeat_this(string $str, int $count, bool $reverse): string @@ -86,24 +81,20 @@ func repeat_this(s *C.zend_string, count int64, reverse bool) unsafe.Pointer { 虽然一些变量类型在 C/PHP 和 Go 之间具有相同的内存表示,但某些类型需要更多逻辑才能直接使用。这可能是编写扩展时最困难的部分,因为它需要了解 Zend 引擎的内部结构以及变量在 PHP 中的内部存储方式。此表总结了你需要知道的内容: -| PHP 类型 | Go 类型 | 直接转换 | C 到 Go 助手 | Go 到 C 助手 | 类方法支持 | -| :----------------- | :---------------------------- | :------- | :-------------------------------- | :--------------------------------- | :--------- | -| `int` | `int64` | ✅ | - | - | ✅ | -| `?int` | `*int64` | ✅ | - | - | ✅ | -| `float` | `float64` | ✅ | - | - | ✅ | -| `?float` | `*float64` | ✅ | - | - | ✅ | -| `bool` | `bool` | ✅ | - | - | ✅ | -| `?bool` | `*bool` | ✅ | - | - | ✅ | -| `string`/`?string` | `*C.zend_string` | ❌ | `frankenphp.GoString()` | `frankenphp.PHPString()` | ✅ | -| `array` | `frankenphp.AssociativeArray` | ❌ | `frankenphp.GoAssociativeArray()` | `frankenphp.PHPAssociativeArray()` | ✅ | -| `array` | `map[string]any` | ❌ | `frankenphp.GoMap()` | `frankenphp.PHPMap()` | ✅ | -| `array` | `[]any` | ❌ | `frankenphp.GoPackedArray()` | `frankenphp.PHPPackedArray()` | ✅ | -| `mixed` | `any` | ❌ | `GoValue()` | `PHPValue()` | ❌ | -| `callable` | `*C.zval` | ❌ | - | `frankenphp.CallPHPCallable()` | ❌ | -| `object` | `struct` | ❌ | _尚未实现_ | _尚未实现_ | ❌ | +| PHP 类型 | Go 类型 | 直接转换 | C 到 Go 助手 | Go 到 C 助手 | 类方法支持 | +| ------------------ | ------------------- | -------- | --------------------- | ---------------------- | ---------- | +| `int` | `int64` | ✅ | - | - | ✅ | +| `?int` | `*int64` | ✅ | - | - | ✅ | +| `float` | `float64` | ✅ | - | - | ✅ | +| `?float` | `*float64` | ✅ | - | - | ✅ | +| `bool` | `bool` | ✅ | - | - | ✅ | +| `?bool` | `*bool` | ✅ | - | - | ✅ | +| `string`/`?string` | `*C.zend_string` | ❌ | frankenphp.GoString() | frankenphp.PHPString() | ✅ | +| `array` | `*frankenphp.Array` | ❌ | frankenphp.GoArray() | frankenphp.PHPArray() | ✅ | +| `mixed` | `any` | ❌ | `GoValue()` | `PHPValue()` | ❌ | +| `object` | `struct` | ❌ | _尚未实现_ | _尚未实现_ | ❌ | > [!NOTE] -> > 此表尚不详尽,将随着 FrankenPHP 类型 API 变得更加完整而完善。 > > 特别是对于类方法,目前支持原始类型和数组。对象尚不能用作方法参数或返回类型。 @@ -112,155 +103,65 @@ func repeat_this(s *C.zend_string, count int64, reverse bool) unsafe.Pointer { #### 处理数组 -FrankenPHP 通过 `frankenphp.AssociativeArray` 或直接转换为 map 或 slice 来为 PHP 数组提供原生支持。 - -`AssociativeArray` 表示一个 [哈希映射](https://en.wikipedia.org/wiki/Hash_table),由一个 `Map: map[string]any` 字段和一个可选的 `Order: []string` 字段组成(与 PHP 的“关联数组”不同,Go map 是无序的)。 - -如果不需要顺序或关联,也可以直接转换为 slice `[]any` 或无序 map `map[string]any`。 +FrankenPHP 通过 `frankenphp.Array` 类型为 PHP 数组提供原生支持。此类型表示 PHP 索引数组(列表)和关联数组(哈希映射),具有有序的键值对。 **在 Go 中创建和操作数组:** ```go -package example - -// #include -import "C" -import ( - "unsafe" - - "github.com/dunglas/frankenphp" -) - -// export_php:function process_data_ordered(array $input): array -func process_data_ordered_map(arr *C.zend_array) unsafe.Pointer { - // 将 PHP 关联数组转换为 Go,同时保留顺序 - associativeArray, err := frankenphp.GoAssociativeArray[any](unsafe.Pointer(arr)) - if err != nil { - // 处理错误 - } - - // 按顺序遍历条目 - for _, key := range associativeArray.Order { - value := associativeArray.Map[key] - // 处理键和值 - } - - // 返回一个有序数组 - // 如果 'Order' 不为空,将只尊重 'Order' 中的键值对 - return frankenphp.PHPAssociativeArray[string](frankenphp.AssociativeArray[string]{ - Map: map[string]string{ - "key1": "value1", - "key2": "value2", - }, - Order: []string{"key1", "key2"}, - }) -} - -// export_php:function process_data_unordered(array $input): array -func process_data_unordered_map(arr *C.zend_array) unsafe.Pointer { - // 将 PHP 关联数组转换为 Go map,不保留顺序 - // 忽略顺序会获得更好的性能 - goMap, err := frankenphp.GoMap[any](unsafe.Pointer(arr)) - if err != nil { - // 处理错误 - } - - // 以无特定顺序遍历条目 - for key, value := range goMap { - // 处理键和值 - } - - // 返回一个无序数组 - return frankenphp.PHPMap(map[string]string { - "key1": "value1", - "key2": "value2", - }) -} - -// export_php:function process_data_packed(array $input): array -func process_data_packed(arr *C.zend_array) unsafe.Pointer { - // 将 PHP 打包数组转换为 Go - goSlice, err := frankenphp.GoPackedArray(unsafe.Pointer(arr)) - if err != nil { - // 处理错误 +//export_php:function process_data(array $input): array +func process_data(arr *C.zval) unsafe.Pointer { + // 将 PHP 数组转换为 Go + goArray := frankenphp.GoArray(unsafe.Pointer(arr)) + + result := &frankenphp.Array{} + + result.SetInt(0, "first") + result.SetInt(1, "second") + result.Append("third") // 自动分配下一个整数键 + + result.SetString("name", "John") + result.SetString("age", int64(30)) + + for i := uint32(0); i < goArray.Len(); i++ { + key, value := goArray.At(i) + if key.Type == frankenphp.PHPStringKey { + result.SetString("processed_"+key.Str, value) + } else { + result.SetInt(key.Int+100, value) + } } - // 按顺序遍历切片 - for index, value := range goSlice { - // 处理索引和值 - } - - // 返回一个打包数组 - return frankenphp.PHPPackedArray([]string{"value1", "value2", "value3"}) + // 转换回 PHP 数组 + return frankenphp.PHPArray(result) } ``` -**数组转换的关键特性:** +**`frankenphp.Array` 的关键特性:** -- **有序键值对** - 可选择保留关联数组的顺序 -- **针对多种情况优化** - 可选择放弃顺序以获得更好的性能,或直接转换为切片 +- **有序键值对** - 像 PHP 数组一样维护插入顺序 +- **混合键类型** - 在同一数组中支持整数和字符串键 +- **类型安全** - `PHPKey` 类型确保正确的键处理 - **自动列表检测** - 转换为 PHP 时,自动检测数组应该是打包列表还是哈希映射 -- **嵌套数组** - 数组可以嵌套,并将自动转换所有支持的类型(`int64`、`float64`、`string`、`bool`、`nil`、`AssociativeArray`、`map[string]any`、`[]any`) - **不支持对象** - 目前,只有标量类型和数组可以用作值。提供对象将导致 PHP 数组中的 `null` 值。 -##### 可用方法:打包和关联 - -- `frankenphp.PHPAssociativeArray(arr frankenphp.AssociativeArray) unsafe.Pointer` - 转换为带有键值对的有序 PHP 数组 -- `frankenphp.PHPMap(arr map[string]any) unsafe.Pointer` - 将 map 转换为带有键值对的无序 PHP 数组 -- `frankenphp.PHPPackedArray(slice []any) unsafe.Pointer` - 将 slice 转换为仅带有索引值的 PHP 打包数组 -- `frankenphp.GoAssociativeArray(arr unsafe.Pointer, ordered bool) frankenphp.AssociativeArray` - 将 PHP 数组转换为有序的 Go `AssociativeArray`(带有顺序的 map) -- `frankenphp.GoMap(arr unsafe.Pointer) map[string]any` - 将 PHP 数组转换为无序的 Go map -- `frankenphp.GoPackedArray(arr unsafe.Pointer) []any` - 将 PHP 数组转换为 Go slice -- `frankenphp.IsPacked(zval *C.zend_array) bool` - 检查 PHP 数组是打包(仅索引)还是关联(键值对) - -### 处理可调用对象 - -FrankenPHP 提供了一种使用 `frankenphp.CallPHPCallable` 助手来处理 PHP 可调用对象的方法。这允许你从 Go 代码中调用 PHP 函数或方法。 - -为了展示这一点,让我们创建自己的 `array_map()` 函数,它接受一个可调用对象和一个数组,将可调用对象应用于数组的每个元素,并返回一个包含结果的新数组: - -```go -// export_php:function my_array_map(array $data, callable $callback): array -func my_array_map(arr *C.zend_array, callback *C.zval) unsafe.Pointer { - goSlice, err := frankenphp.GoPackedArray[any](unsafe.Pointer(arr)) - if err != nil { - panic(err) - } - - result := make([]any, len(goSlice)) - - for index, value := range goSlice { - result[index] = frankenphp.CallPHPCallable(unsafe.Pointer(callback), []interface{}{value}) - } +**可用方法:** - return frankenphp.PHPPackedArray(result) -} -``` - -请注意我们如何使用 `frankenphp.CallPHPCallable()` 来调用作为参数传递的 PHP 可调用对象。此函数接受一个指向可调用对象的指针和一个参数数组,并返回可调用对象执行的结果。你可以使用你习惯的可调用语法: - -```php - -import "C" -import ( - "unsafe" - - "github.com/dunglas/frankenphp" -) - //export_php:class User type UserStruct struct { - Name string - Age int - Active bool // Added for the UpdateInfo example + Name string + Age int } //export_php:method User::getName(): string @@ -324,16 +214,6 @@ func (us *UserStruct) SetNamePrefix(prefix *C.zend_string) { 生成器支持在 PHP 签名中使用 `?` 前缀的可空参数。当参数可空时,它在你的 Go 函数中变成指针,允许你检查值在 PHP 中是否为 `null`: ```go -package example - -// #include -import "C" -import ( - "unsafe" - - "github.com/dunglas/frankenphp" -) - //export_php:method User::updateInfo(?string $name, ?int $age, ?bool $active): void func (us *UserStruct) UpdateInfo(name *C.zend_string, age *int64, active *bool) { // 检查是否提供了 name(不为 null) @@ -361,7 +241,6 @@ func (us *UserStruct) UpdateInfo(name *C.zend_string, age *int64, active *bool) - **PHP `null` 变成 Go `nil`** - 当 PHP 传递 `null` 时,你的 Go 函数接收 `nil` 指针 > [!WARNING] -> > 目前,类方法有以下限制。**不支持对象**作为参数类型或返回类型。**完全支持数组**作为参数和返回类型。支持的类型:`string`、`int`、`float`、`bool`、`array` 和 `void`(用于返回类型)。**完全支持可空参数类型**,适用于所有标量类型(`?string`、`?int`、`?float`、`?bool`)。 生成扩展后,你将被允许在 PHP 中使用类及其方法。请注意,你**不能直接访问属性**: @@ -398,8 +277,6 @@ $user->updateInfo(null, 25, null); // Name 和 active 为 null 使用 `//export_php:const` 指令创建全局 PHP 常量: ```go -package example - //export_php:const const MAX_CONNECTIONS = 100 @@ -418,8 +295,6 @@ const STATUS_ERROR = iota 使用 `//export_php:classconst ClassName` 指令创建属于特定 PHP 类的常量: ```go -package example - //export_php:classconst User const STATUS_ACTIVE = 1 @@ -454,20 +329,15 @@ echo User::ROLE_ADMIN; // "admin" echo Order::STATE_PENDING; // 0 ``` -该指令支持各种值类型,包括字符串、整数、布尔值、浮点数和 iota 常量。使用 `iota` 时,生成器自动分配顺序值(0、1、2 等)。全局常量在你的 PHP 代码中作为全局常量可用,而类常量使用公共可见性限定在各自的类中。使用整数时,不同的可能记法(二进制、十六进制、八进制)都受支持,并在 PHP 存根文件中按原样转储。 +该指令支持各种值类型,包括字符串、整数、布尔值、浮点数和 iota 常量。使用 `iota` 时,生成器自动分配顺序值(0、1、2 等)。全局常量在你的 PHP 代码中作为全局常量可用,而类常量使用公共可见性限定在各自的类中。使用整数时,支持不同的可能记法(二进制、十六进制、八进制)并在 PHP 存根文件中按原样转储。 你可以像在 Go 代码中习惯的那样使用常量。例如,让我们采用我们之前声明的 `repeat_this()` 函数,并将最后一个参数更改为整数: ```go -package example - -// #include -import "C" import ( - "strings" - "unsafe" - - "github.com/dunglas/frankenphp" + "C" + "github.com/dunglas/frankenphp" + "strings" ) //export_php:const @@ -484,37 +354,37 @@ const MODE_UPPERCASE = 2 //export_php:function repeat_this(string $str, int $count, int $mode): string func repeat_this(s *C.zend_string, count int64, mode int) unsafe.Pointer { - str := frankenphp.GoString(unsafe.Pointer(s)) + str := frankenphp.GoString(unsafe.Pointer(s)) - result := strings.Repeat(str, int(count)) - if mode == STR_REVERSE { - // 反转字符串 - } + result := strings.Repeat(str, int(count)) + if mode == STR_REVERSE { + // 反转字符串 + } - if mode == STR_NORMAL { - // 无操作,只是为了展示常量 - } + if mode == STR_NORMAL { + // 无操作,只是为了展示常量 + } - return frankenphp.PHPString(result, false) + return frankenphp.PHPString(result, false) } //export_php:class StringProcessor type StringProcessorStruct struct { - // internal fields + // 内部字段 } //export_php:method StringProcessor::process(string $input, int $mode): string func (sp *StringProcessorStruct) Process(input *C.zend_string, mode int64) unsafe.Pointer { - str := frankenphp.GoString(unsafe.Pointer(input)) + str := frankenphp.GoString(unsafe.Pointer(input)) - switch mode { - case MODE_LOWERCASE: - str = strings.ToLower(str) - case MODE_UPPERCASE: - str = strings.ToUpper(str) - } + switch mode { + case MODE_LOWERCASE: + str = strings.ToLower(str) + case MODE_UPPERCASE: + str = strings.ToUpper(str) + } - return frankenphp.PHPString(str, false) + return frankenphp.PHPString(str, false) } ``` @@ -528,13 +398,9 @@ func (sp *StringProcessorStruct) Process(input *C.zend_string, mode int64) unsaf ```go //export_php:namespace My\Extension -package example +package main -import ( - "unsafe" - - "github.com/dunglas/frankenphp" -) +import "C" //export_php:function hello(): string func hello() string { @@ -543,7 +409,7 @@ func hello() string { //export_php:class User type UserStruct struct { - // internal fields + // 内部字段 } //export_php:method User::getName(): string @@ -637,26 +503,25 @@ echo $processor->process('Hello World', StringProcessor::MODE_UPPERCASE); // "H 在你的模块中,你需要定义一个新的原生函数,该函数将从 PHP 调用。为此,创建一个你想要的名称的文件,例如 `extension.go`,并添加以下代码: ```go -package example +package ext_go -// #include "extension.h" +//#include "extension.h" import "C" import ( - "log/slog" - "unsafe" - - "github.com/dunglas/frankenphp" + "unsafe" + "github.com/caddyserver/caddy/v2" + "github.com/dunglas/frankenphp" ) func init() { - frankenphp.RegisterExtension(unsafe.Pointer(&C.ext_module_entry)) + frankenphp.RegisterExtension(unsafe.Pointer(&C.ext_module_entry)) } //export go_print_something func go_print_something() { - go func() { - slog.Info("Hello from a goroutine!") - }() + go func() { + caddy.Log().Info("Hello from a goroutine!") + }() } ``` @@ -832,16 +697,7 @@ PHP_FUNCTION(go_upper) 我们的 Go 函数将接受 `*C.zend_string` 作为参数,使用 FrankenPHP 的助手函数将其转换为 Go 字符串,处理它,并将结果作为新的 `*C.zend_string` 返回。助手函数为我们处理所有内存管理和转换复杂性。 ```go -package example - -// #include -import "C" -import ( - "unsafe" - "strings" - - "github.com/dunglas/frankenphp" -) +import "strings" //export go_upper func go_upper(s *C.zend_string) *C.zend_string { @@ -856,7 +712,6 @@ func go_upper(s *C.zend_string) *C.zend_string { 这种方法比手动内存管理更清洁、更安全。FrankenPHP 的助手函数自动处理 PHP 的 `zend_string` 格式和 Go 字符串之间的转换。`PHPString()` 中的 `false` 参数表示我们想要创建一个新的非持久字符串(在请求结束时释放)。 > [!TIP] -> > 在此示例中,我们不执行任何错误处理,但你应该始终检查指针不是 `nil` 并且数据在 Go 函数中使用之前是有效的。 ### 将扩展集成到 FrankenPHP 中 diff --git a/docs/cn/github-actions.md b/docs/cn/github-actions.md index 8a411b9a77..04f5cbbd61 100644 --- a/docs/cn/github-actions.md +++ b/docs/cn/github-actions.md @@ -1,30 +1,31 @@ # 使用 GitHub Actions -此存储库会在每次获得批准的拉取请求或在您自己的 fork 配置完成后,构建 Docker 镜像并将其部署到 [Docker Hub](https://hub.docker.com/r/dunglas/frankenphp)。 +此存储库构建 Docker 镜像并将其部署到 [Docker Hub](https://hub.docker.com/r/dunglas/frankenphp) 上 +每个批准的拉取请求或设置后在你自己的分支上。 ## 设置 GitHub Actions -在存储库设置的 `Secrets` (密钥) 下,添加以下密钥: +在存储库设置中的 `secrets` 下,添加以下字段: -- `REGISTRY_LOGIN_SERVER`: 要使用的 Docker registry(例如 `docker.io`)。 -- `REGISTRY_USERNAME`: 用于登录 registry 的用户名(例如 `dunglas`)。 -- `REGISTRY_PASSWORD`: 用于登录 registry 的密码(例如,访问密钥)。 -- `IMAGE_NAME`: 镜像的名称(例如 `dunglas/frankenphp`)。 +- `REGISTRY_LOGIN_SERVER`: 要使用的 Docker registry(如 `docker.io`)。 +- `REGISTRY_USERNAME`: 用于登录 registry 的用户名(如 `dunglas`)。 +- `REGISTRY_PASSWORD`: 用于登录 registry 的密码(如 `access key`)。 +- `IMAGE_NAME`: 镜像的名称(如 `dunglas/frankenphp`)。 ## 构建和推送镜像 -1. 创建 Pull Request 或推送到您的 Fork 分支。 -2. GitHub Actions 将构建镜像并运行所有测试。 -3. 如果构建成功,镜像将使用 `pr-x`(其中 `x` 是 PR 编号)作为标签推送到注册表。 +1. 创建 Pull Request 或推送到你的 Fork 分支。 +2. GitHub Actions 将生成镜像并运行每项测试。 +3. 如果生成成功,则将使用 `pr-x` 推送 registry,其中 `x` 是 PR 编号,作为标记将镜像推送到注册表。 ## 部署镜像 -1. Pull Request 合并后,GitHub Actions 将再次运行测试并构建新的镜像。 -2. 如果构建成功,Docker 注册表中的 `main` 标签将被更新。 +1. 合并 Pull Request 后,GitHub Actions 将再次运行测试并生成新镜像。 +2. 如果构建成功,则 Docker 注册表中的 `main` tag 将更新。 ## 发布 -1. 在仓库中创建新标签。 -2. GitHub Actions 将构建镜像并运行所有测试。 -3. 如果构建成功,镜像将使用标签名称作为标签推送到注册表(例如,将创建 `v1.2.3` 和 `v1.2`)。 -4. `latest` 标签也将被更新。 +1. 在项目仓库中创建新 Tag。 +2. GitHub Actions 将生成镜像并运行每项测试。 +3. 如果构建成功,镜像将使用标记名称作为标记推送到 registry(例如,将创建 `v1.2.3` 和 `v1.2`)。 +4. `latest` 标签也将更新。 diff --git a/docs/cn/hot-reload.md b/docs/cn/hot-reload.md deleted file mode 100644 index a4a1b9b4ce..0000000000 --- a/docs/cn/hot-reload.md +++ /dev/null @@ -1,139 +0,0 @@ -# 热重载 - -FrankenPHP 内置的**热重载**功能旨在极大改善开发者体验。 - -![Mercure](hot-reload.png) - -此功能提供类似于现代 JavaScript 工具(如 Vite 或 webpack)中**模块热替换 (HMR)** 的工作流。 -开发者无需在每次文件更改(PHP 代码、模板、JavaScript 和 CSS 文件...)后手动刷新浏览器, -FrankenPHP 会实时更新内容。 - -热重载原生支持 WordPress、Laravel、Symfony 以及任何其他 PHP 应用或框架。 - -启用后,FrankenPHP 会监视当前工作目录的文件系统更改。 -当文件被修改时,它会将 [Mercure](mercure.md) 更新推送到浏览器。 - -根据您的设置,浏览器将: - -- 如果加载了 [Idiomorph](https://github.com/bigskysoftware/idiomorph),则**修改 DOM**(保留滚动位置和输入状态)。 -- 如果没有 Idiomorph,则**重新加载页面**(标准实时重载)。 - -## 配置 - -要启用热重载,请先启用 Mercure,然后在 `Caddyfile` 中的 `php_server` 指令中添加 `hot_reload` 子指令。 - -> [!WARNING] -> 此功能仅适用于**开发环境**。 -> 请勿在生产环境中启用 `hot_reload`,因为监视文件系统会带来性能开销并暴露内部端点。 - -```caddyfile -localhost - -mercure { - anonymous -} - -root public/ -php_server { - hot_reload -} -``` - -默认情况下,FrankenPHP 将监视当前工作目录中匹配以下全局模式的所有文件:`./**/*.{css,env,gif,htm,html,jpg,jpeg,js,mjs,php,png,svg,twig,webp,xml,yaml,yml}` - -可以通过全局语法明确设置要监视的文件: - -```caddyfile -localhost - -mercure { - anonymous -} - -root public/ -php_server { - hot_reload src/**/*{.php,.js} config/**/*.yaml -} -``` - -使用长格式来指定要使用的 Mercure 主题以及通过向 `hot_reload` 选项提供路径来监视哪些目录或文件: - -```caddyfile -localhost - -mercure { - anonymous -} - -root public/ -php_server { - hot_reload { - topic hot-reload-topic - watch src/**/*.php - watch assets/**/*.{ts,json} - watch templates/ - watch public/css/ - } -} -``` - -## 客户端集成 - -虽然服务器检测到更改,但浏览器需要订阅这些事件才能更新页面。 -FrankenPHP 通过 `$_SERVER['FRANKENPHP_HOT_RELOAD']` 环境变量暴露 Mercure Hub URL,用于订阅文件更改。 - -还提供了一个便利的 JavaScript 库 [frankenphp-hot-reload](https://www.npmjs.com/package/frankenphp-hot-reload) 来处理客户端逻辑。 -要使用它,请将以下内容添加到您的主布局中: - -```php - -FrankenPHP Hot Reload - - - - - -``` - -该库将自动订阅 Mercure hub,当检测到文件更改时在后台获取当前 URL 并修改 DOM。 -它作为一个 [npm](https://www.npmjs.com/package/frankenphp-hot-reload) 包和在 [GitHub](https://github.com/dunglas/frankenphp-hot-reload) 上可用。 - -或者,您可以通过使用 `EventSource` 原生 JavaScript 类直接订阅 Mercure hub 来实现自己的客户端逻辑。 - -### Worker 模式 - -如果您在 [Worker 模式](https://frankenphp.dev/docs/worker/)下运行您的应用程序,您的应用程序脚本会保留在内存中。 -这意味着即使浏览器重新加载,您对 PHP 代码的更改也不会立即反映。 - -为了获得最佳的开发体验,您应该将 `hot_reload` 与 [worker 指令中的 `watch` 子指令](config.md#watching-for-file-changes)结合使用。 - -- `hot_reload`:当文件更改时刷新**浏览器** -- `worker.watch`:当文件更改时重启 worker - -```caddy -localhost - -mercure { - anonymous -} - -root public/ -php_server { - hot_reload - worker { - file /path/to/my_worker.php - watch - } -} -``` - -### 工作原理 - -1. **监视**:FrankenPHP 使用底层 [e-dant/watcher 库](https://github.com/e-dant/watcher)监视文件系统修改(我们贡献了 Go 绑定)。 -2. **重启 (Worker 模式)**:如果在 worker 配置中启用了 `watch`,PHP worker 将重新启动以加载新代码。 -3. **推送**:包含更改文件列表的 JSON 有效载荷被发送到内置的 [Mercure hub](https://mercure.rocks)。 -4. **接收**:浏览器通过 JavaScript 库监听,接收 Mercure 事件。 -5. **更新**: - -- 如果检测到 **Idiomorph**,它会获取更新的内容并修改当前的 HTML 以匹配新状态,即时应用更改而不会丢失状态。 -- 否则,将调用 `window.location.reload()` 以刷新页面。 diff --git a/docs/cn/known-issues.md b/docs/cn/known-issues.md index 3105b51152..a2bc0f8fb6 100644 --- a/docs/cn/known-issues.md +++ b/docs/cn/known-issues.md @@ -4,28 +4,26 @@ 已知以下扩展与 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/) | 非线程安全 | - | +| 名称 | 原因 | 替代方案 | +| ----------------------------------------------------------------------------------------------------------- | ------------ | -------------------------------------------------------------------------------------------------------------------- | +| [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) | 在使用 musl libc 时,OpenSSL 扩展在重负载下可能会崩溃。在使用更流行的 GNU libc 时,不会出现此问题。此错误正在由 PHP 跟踪。[查看问题](https://github.com/php/php-src/issues/13648)。 | +| 名称 | 问题 | +| ------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| [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) 函数在一段时间后似乎表现不佳。解决方法是缓存(例如使用 [APCu](https://www.php.net/manual/en/book.apcu.php))每个 User Agent 的结果,因为它们是静态的。 +[get_browser()](https://www.php.net/manual/en/function.get-browser.php) 函数在一段时间后似乎表现不佳。解决方法是缓存(例如使用 [APCu](https://www.php.net/manual/zh/book.apcu.php))每个 User-Agent,因为它们是不变的。 -## 独立二进制和基于 Alpine 的 Docker 镜像 +## 独立的二进制和基于 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)。 - -如果遇到问题,请优先使用 GNU 变体的静态二进制文件和基于 Debian 的 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` @@ -34,10 +32,10 @@ 如果确实想使用 `127.0.0.1` 作为主机,可以通过将服务器名称设置为 `127.0.0.1` 来配置它以为其生成证书。 -不幸的是,在使用 Docker 时,由于其[网络系统](https://docs.docker.com/network/),这还不够。 +如果你使用 Docker,因为 [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/): +如果你使用的是 Linux,解决方案是使用 [使用宿主机网络](https://docs.docker.com/network/network-tutorial-host/): ```console docker run \ @@ -47,9 +45,9 @@ docker run \ dunglas/frankenphp ``` -主机网络驱动程序在 Mac 和 Windows 上不受支持。在这些平台上,你必须猜测容器的 IP 地址并将其包含在服务器名称中。 +Mac 和 Windows 不支持 Docker 使用宿主机网络。在这些平台上,你必须猜测容器的 IP 地址并将其包含在服务器名称中。 -运行 `docker network inspect bridge` 并查看 `Containers` 键,以找到 `IPv4Address` 键下当前分配的最后一个 IP 地址,并将其增加一。如果没有容器正在运行,则第一个分配的 IP 地址通常为 `172.17.0.2`。 +运行 `docker network inspect bridge` 并查看 `Containers`,找到 `IPv4Address` 当前分配的最后一个 IP 地址,并增加 1。如果没有容器正在运行,则第一个分配的 IP 地址通常为 `172.17.0.2`。 然后将其包含在 `SERVER_NAME` 环境变量中: @@ -110,9 +108,9 @@ export PHP_BINARY=/usr/local/bin/php composer install ``` -## 排查静态二进制文件的 TLS/SSL 问题 +## 使用静态二进制文件排查 TLS/SSL 问题 -在使用静态二进制文件时,您可能会遇到以下与 TLS 相关的错误,例如在使用 STARTTLS 发送电子邮件时: +在使用静态二进制文件时,您可能会遇到以下与TLS相关的错误,例如在使用STARTTLS发送电子邮件时: ```text Unable to connect with STARTTLS: stream_socket_enable_crypto(): SSL operation failed with code 5. OpenSSL Error messages: @@ -129,10 +127,10 @@ error:0A000086:SSL routines::certificate verify failed > [!WARNING] > -> Web 和 CLI 上下文可能有不同的设置。 -> 请务必在适当的上下文中运行 `openssl_get_cert_locations()`。 +> Web 和命令行界面可能有不同的设置。 +> 确保在适当的上下文中运行 `openssl_get_cert_locations()`。 -[从 Mozilla 提取的 CA 证书可以在 cURL 网站上下载](https://curl.se/docs/caextract.html)。 +[从Mozilla提取的CA证书可以在curl网站上下载](https://curl.se/docs/caextract.html)。 或者,许多发行版,包括 Debian、Ubuntu 和 Alpine,提供名为 `ca-certificates` 的软件包,其中包含这些证书。 diff --git a/docs/cn/laravel.md b/docs/cn/laravel.md index 7d98b6b6f2..07fa646ab5 100644 --- a/docs/cn/laravel.md +++ b/docs/cn/laravel.md @@ -16,7 +16,7 @@ docker run -p 80:80 -p 443:443 -p 443:443/udp -v $PWD:/app dunglas/frankenphp 或者,你可以从本地机器上使用 FrankenPHP 运行 Laravel 项目: -1. [下载与您的系统相对应的二进制文件](../#standalone-binary) +1. [下载与你的系统相对应的二进制文件](https://github.com/php/frankenphp/releases) 2. 将以下配置添加到 Laravel 项目根目录中名为 `Caddyfile` 的文件中: ```caddyfile @@ -30,7 +30,7 @@ docker run -p 80:80 -p 443:443 -p 443:443/udp -v $PWD:/app dunglas/frankenphp root public/ # 启用压缩(可选) encode zstd br gzip - # 从 public/ 目录执行 PHP 文件并提供静态文件 + # 执行当前目录中的 PHP 文件并提供资源 php_server { try_files {path} index.php } @@ -65,22 +65,20 @@ php artisan octane:frankenphp - `--port`: 服务器应可用的端口(默认值: `8000`) - `--admin-port`: 管理服务器应可用的端口(默认值: `2019`) - `--workers`: 应可用于处理请求的 worker 数(默认值: `auto`) -- `--max-requests`: 在服务器重新加载前处理的请求数量(默认值: `500`) +- `--max-requests`: 在 worker 重启之前要处理的请求数(默认值: `500`) - `--caddyfile`:FrankenPHP `Caddyfile` 文件的路径(默认: [Laravel Octane 中的存根 `Caddyfile`](https://github.com/laravel/octane/blob/2.x/src/Commands/stubs/Caddyfile)) -- `--https`: 开启 HTTPS、HTTP/2 和 HTTP/3,自动生成和续订证书 +- `--https`: 开启 HTTPS、HTTP/2 和 HTTP/3,自动生成和延长证书 - `--http-redirect`: 启用 HTTP 到 HTTPS 重定向(仅在使用 `--https` 时启用) - `--watch`: 修改应用程序时自动重新加载服务器 - `--poll`: 在监视时使用文件系统轮询,以便通过网络监视文件 -- `--log-level`: 使用原生 Caddy 日志记录器,记录指定日志级别或更高级别的消息 +- `--log-level`: 在指定日志级别或高于指定日志级别的日志消息 > [!TIP] > 要获取结构化的 JSON 日志(在使用日志分析解决方案时非常有用),请明确传递 `--log-level` 选项。 -另请参阅 [如何在 Octane 中使用 Mercure](#mercure-support)。 +你可以了解更多关于 [Laravel Octane 官方文档](https://laravel.com/docs/octane)。 -在 [Laravel Octane 官方文档](https://laravel.com/docs/octane) 中了解更多信息。 - -## Laravel 应用程序作为独立二进制文件 +## Laravel 应用程序作为独立的可执行文件 使用[FrankenPHP 的应用嵌入功能](embed.md),可以将 Laravel 应用程序作为 独立的二进制文件分发。 @@ -169,39 +167,11 @@ php artisan octane:frankenphp 设置 `LARAVEL_STORAGE_PATH` 环境变量(例如,在 `.env` 文件中)或调用 `Illuminate\Foundation\Application::useStoragePath()` 方法以使用临时目录之外的目录。 -### Mercure 支持 - -[Mercure](https://mercure.rocks) 是为您的 Laravel 应用程序添加实时功能的绝佳方式。 -FrankenPHP [开箱即用地支持 Mercure](mercure.md)。 - -如果您未使用 [Octane](#laravel-octane),请参阅 [Mercure 文档条目](mercure.md)。 - -如果您正在使用 Octane,可以通过在您的 `config/octane.php` 文件中添加以下行来启用 Mercure 支持: - -```php -// ... - -return [ - // ... - - 'mercure' => [ - 'anonymous' => true, - 'publisher_jwt' => '!ChangeThisMercureHubJWTSecretKey!', - 'subscriber_jwt' => '!ChangeThisMercureHubJWTSecretKey!', - ], -]; -``` - -您可以在此数组中使用 [Mercure 支持的所有指令](https://mercure.rocks/docs/hub/config#directives)。 - -要发布和订阅更新,我们推荐使用 [Laravel Mercure Broadcaster](https://github.com/mvanduijker/laravel-mercure-broadcaster) 库。 -或者,请参阅 [Mercure 文档](mercure.md) 以纯 PHP 和 JavaScript 实现。 - ### 使用独立二进制文件运行 Octane 甚至可以将 Laravel Octane 应用打包为独立的二进制文件! -为此,[正确安装 Octane](#laravel-octane) 并遵循 [独立二进制文件形式的 Laravel 应用程序](#laravel-apps-as-standalone-binaries) 中描述的步骤。 +为此,[正确安装 Octane](#laravel-octane) 并遵循 [前一部分](#laravel-应用程序作为独立的可执行文件) 中描述的步骤。 然后,通过 Octane 在工作模式下启动 FrankenPHP,运行: diff --git a/docs/cn/logging.md b/docs/cn/logging.md deleted file mode 100644 index 989d93ddae..0000000000 --- a/docs/cn/logging.md +++ /dev/null @@ -1,66 +0,0 @@ -# 日志 - -FrankenPHP 与 [Caddy 的日志系统](https://caddyserver.com/docs/logging)无缝集成。 -您可以使用标准的 PHP 函数记录消息,也可以利用专用的 `frankenphp_log()` 函数进行高级结构化日志记录。 - -## `frankenphp_log()` - -`frankenphp_log()` 函数允许您直接从 PHP 应用程序发出结构化日志,从而更容易地将其摄取到 Datadog、Grafana Loki 或 Elastic 等平台中,并支持 OpenTelemetry。 - -在底层,`frankenphp_log()` 封装了 [Go 的 `log/slog` 包](https://pkg.go.dev/log/slog)以提供丰富的日志功能。 - -这些日志包括严重性级别和可选的上下文数据。 - -```php -function frankenphp_log(string $message, int $level = FRANKENPHP_LOG_LEVEL_INFO, array $context = []): void -``` - -### 参数 - -- **`message`**: 日志消息字符串。 -- **`level`**: 日志的严重性级别。可以是任意整数。提供了用于常见级别的便捷常量:`FRANKENPHP_LOG_LEVEL_DEBUG` (`-4`)、`FRANKENPHP_LOG_LEVEL_INFO` (`0`)、`FRANKENPHP_LOG_LEVEL_WARN` (`4`) 和 `FRANKENPHP_LOG_LEVEL_ERROR` (`8`)。默认值为 `FRANKENPHP_LOG_LEVEL_INFO`。 -- **`context`**: 一个关联数组,包含要包含在日志条目中的附加数据。 - -### 示例 - -```php - memory_get_usage(), - 'peak_usage' => memory_get_peak_usage(), - ], -); - -``` - -当查看日志时(例如,通过 `docker compose logs`),输出将显示为结构化 JSON: - -```json -{"level":"info","ts":1704067200,"logger":"frankenphp","msg":"Hello from FrankenPHP!"} -{"level":"warn","ts":1704067200,"logger":"frankenphp","msg":"Memory usage high","current_usage":10485760,"peak_usage":12582912} -``` - -## `error_log()` - -FrankenPHP 也允许使用标准的 `error_log()` 函数进行日志记录。如果 `$message_type` 参数为 `4` (SAPI),这些消息将被路由到 Caddy 日志记录器。 - -默认情况下,通过 `error_log()` 发送的消息被视为非结构化文本。它们对于与依赖标准 PHP 库的现有应用程序或库的兼容性非常有用。 - -### `error_log()` 示例 - -```php -error_log("Database connection failed", 4); -``` - -这将在 Caddy 日志中显示,通常带有前缀以表明它源自 PHP。 - -> [!TIP] -> 为了在生产环境中获得更好的可观测性,建议优先使用 `frankenphp_log()`,因为它允许您按级别(调试、错误等)过滤日志并在您的日志基础设施中查询特定字段。 diff --git a/docs/cn/mercure.md b/docs/cn/mercure.md index ffd6c3dccf..d51c9eb818 100644 --- a/docs/cn/mercure.md +++ b/docs/cn/mercure.md @@ -1,148 +1,15 @@ # 实时 -FrankenPHP 内置了 [Mercure](https://mercure.rocks) 中心! -Mercure 允许你将实时事件推送到所有连接的设备:它们将立即收到 JavaScript 事件。 +FrankenPHP 配备了内置的 [Mercure](https://mercure.rocks) 中心! +Mercure 允许将事件实时推送到所有连接的设备:它们将立即收到 JavaScript 事件。 -它是 WebSockets 的便捷替代方案,使用简单,并原生支持所有现代网络浏览器! +无需 JS 库或 SDK! -![Mercure](mercure-hub.png) +![Mercure](../mercure-hub.png) -## 启用 Mercure +要启用 Mercure Hub,请按照 [Mercure 网站](https://mercure.rocks/docs/hub/config) 中的说明更新 `Caddyfile`。 -Mercure 支持默认是禁用的。 -这是一个启用 FrankenPHP 和 Mercure 中心的 `Caddyfile` 最小示例: +Mercure hub 的路径是`/.well-known/mercure`. +在 Docker 中运行 FrankenPHP 时,完整的发送 URL 将类似于 `http://php/.well-known/mercure` (其中 `php` 是运行 FrankenPHP 的容器名称)。 -```caddyfile -# 要响应的主机名 -localhost - -mercure { - # 用于签署发布者 JWT 令牌的密钥 - publisher_jwt !ChangeThisMercureHubJWTSecretKey! - # 允许匿名订阅者(无需 JWT) - anonymous -} - -root public/ -php_server -``` - -> [!TIP] -> -> [Docker 镜像](docker.md) 提供的 [示例 `Caddyfile`](https://github.com/php/frankenphp/blob/main/caddy/frankenphp/Caddyfile) -> 已经包含一个被注释掉的 Mercure 配置,其中带有方便的环境变量来配置它。 -> -> 取消注释 `/etc/frankenphp/Caddyfile` 中的 Mercure 部分即可启用它。 - -## 订阅更新 - -默认情况下,Mercure 中心在你的 FrankenPHP 服务器的 `/.well-known/mercure` 路径上可用。 -要订阅更新,请使用原生的 [`EventSource`](https://developer.mozilla.org/docs/Web/API/EventSource) JavaScript 类: - -```html - - -Mercure 示例 - -``` - -## 发布更新 - -### 使用 `mercure_publish()` - -FrankenPHP 提供了一个方便的 `mercure_publish()` 函数来将更新发布到内置的 Mercure 中心: - -```php - 'value'])); - -// 写入 FrankenPHP 的日志 -error_log("update $updateID published", 4); -``` - -完整的函数签名是: - -```php -/** - * @param string|string[] $topics - */ -function mercure_publish(string|array $topics, string $data = '', bool $private = false, ?string $id = null, ?string $type = null, ?int $retry = null): string {} -``` - -### 使用 `file_get_contents()` - -要将更新分发给连接的订阅者,请向 Mercure 中心发送一个带 `topic` 和 `data` 参数的认证 POST 请求: - -```php - [ - 'method' => 'POST', - 'header' => "Content-type: application/x-www-form-urlencoded\r\nAuthorization: Bearer " . JWT, - 'content' => http_build_query([ - 'topic' => 'my-topic', - 'data' => json_encode(['key' => 'value']), - ]), -]])); - -// 写入 FrankenPHP 的日志 -error_log("update $updateID published", 4); -``` - -在 `Caddyfile` 中作为 `mercure.publisher_jwt` 选项参数传入的密钥必须用于签署 `Authorization` 头中使用的 JWT 令牌。 - -JWT 必须包含一个 `mercure` 声明,其中包含你希望发布到的 topic 的 `publish` 权限。 -有关授权,请参阅 [Mercure 文档](https://mercure.rocks/spec#publishers)。 - -要生成自己的令牌,你可以使用 [这个 jwt.io 链接](https://www.jwt.io/#token=eyJhbGciOiJIUzI1NiJ9.eyJtZXJjdXJlIjp7InB1Ymxpc2giOlsiKiJdfX0.PXwpfIGng6KObfZlcOXvcnWCJOWTFLtswGI5DZuWSK4), -但对于生产应用程序,建议使用受信任的 [JWT 库](https://www.jwt.io/libraries?programming_language=php) 动态生成的短期令牌。 - -### 使用 Symfony Mercure - -或者,你可以使用 [Symfony Mercure Component](https://symfony.com/components/Mercure),这是一个独立的 PHP 库。 - -该库处理 JWT 生成、更新发布以及订阅者的基于 cookie 的授权。 - -首先,使用 Composer 安装该库: - -```console -composer require symfony/mercure lcobucci/jwt -``` - -然后,你可以像这样使用它: - -```php -publish(new \Symfony\Component\Mercure\Update('my-topic', json_encode(['key' => 'value']))); - -// 写入 FrankenPHP 的日志 -error_log("update $updateID published", 4); -``` - -Mercure 也被以下框架原生支持: - -- [Laravel](laravel.md#mercure-support) -- [Symfony](https://symfony.com/doc/current/mercure.html) -- [API Platform](https://api-platform.com/docs/core/mercure/) +要从你的代码中推送 Mercure 更新,我们推荐 [Symfony Mercure Component](https://symfony.com/components/Mercure)(不需要 Symfony 框架来使用)。 diff --git a/docs/cn/performance.md b/docs/cn/performance.md index 64628a6e98..ee11bb07e9 100644 --- a/docs/cn/performance.md +++ b/docs/cn/performance.md @@ -41,9 +41,11 @@ 另外,[一些错误只在使用 musl 时发生](https://github.com/php/php-src/issues?q=sort%3Aupdated-desc+is%3Aissue+is%3Aopen+label%3ABug+musl)。 -在生产环境中,我们建议使用链接到 glibc,并以适当优化级别编译的 FrankenPHP。 +在生产环境中,我们建议使用链接到 glibc 的 FrankenPHP。 -这可以通过使用 Debian Docker 镜像、使用我们的维护者提供的 [.deb](https://debs.henderkes.com) 或 [.rpm](https://rpms.henderkes.com) 包,或通过[从源代码编译 FrankenPHP](compile.md) 来实现。 +这可以通过使用 Debian Docker 镜像(默认)、从我们的 [Releases](https://github.com/php/frankenphp/releases) 下载 -gnu 后缀二进制文件,或通过[从源代码编译 FrankenPHP](compile.md) 来实现。 + +或者,我们提供使用 [mimalloc 分配器](https://github.com/microsoft/mimalloc) 编译的静态 musl 二进制文件,这缓解了线程场景中的问题。 ## Go 运行时配置 @@ -153,32 +155,3 @@ FrankenPHP 使用官方 PHP 解释器。 有关更多详细信息,请阅读[专门的 Symfony 文档条目](https://symfony.com/doc/current/performance.html) (即使你不使用 Symfony,大多数提示也很有用)。 - -## 分割线程池 - -应用程序通常会与缓慢的外部服务交互,例如在高负载下容易变得不可靠或持续需要 10 秒以上才能响应的 API。 -在这种情况下,将线程池拆分以拥有专用的“慢速”池可能会很有益。这可以防止慢速端点耗尽所有服务器资源/线程,并限制指向慢速端点的请求并发性,类似于连接池。 - -```caddyfile -{ - frankenphp { - max_threads 100 # 所有 worker 共享最多 100 个线程 - } -} - -example.com { - php_server { - root /app/public # 你的应用程序的根目录 - worker index.php { - match /slow-endpoint/* # 所有路径为 /slow-endpoint/* 的请求都由这个线程池处理 - num 10 # 匹配 /slow-endpoint/* 的请求至少有 10 个线程 - } - worker index.php { - match * # 所有其他请求单独处理 - num 20 # 其他请求至少有 20 个线程,即使慢速端点开始挂起 - } - } -} -``` - -通常,也建议通过使用消息队列等相关机制,异步处理非常慢的端点。 diff --git a/docs/cn/production.md b/docs/cn/production.md index ac971c33e8..01777f932c 100644 --- a/docs/cn/production.md +++ b/docs/cn/production.md @@ -30,7 +30,8 @@ COPY . /app/public #COPY . /app ``` -有关更多详细信息和选项,以及如何自定义配置、安装 PHP 扩展和 Caddy 模块,请参阅“[构建自定义 Docker 镜像](docker.md)”。 +有关更多详细信息和选项,请参阅 [构建自定义 Docker 镜像](docker.md)。 +要了解如何自定义配置,请安装 PHP 扩展和 Caddy 模块。 如果你的项目使用 Composer, 请务必将其包含在 Docker 镜像中并安装你的依赖。 @@ -50,7 +51,7 @@ services: - caddy_data:/data - caddy_config:/config -# Caddy 证书和配置所需的数据卷 +# Caddy 证书和配置所需的挂载目录 volumes: caddy_data: caddy_config: @@ -59,11 +60,11 @@ volumes: > [!NOTE] > > 前面的示例适用于生产用途。 -> 在开发中,你可能希望使用一个数据卷,一个不同的 PHP 配置和不同的 `SERVER_NAME` 环境变量值。 +> 在开发中,你可能希望使用挂载目录,不同的 PHP 配置和不同的 `SERVER_NAME` 环境变量值。 > -> 请查看 [Symfony Docker](https://github.com/dunglas/symfony-docker) 项目 -> (它也使用了 FrankenPHP),以获取使用多阶段镜像、 -> Composer、额外 PHP 扩展等更高级的示例。 +> 见 [Symfony Docker](https://github.com/dunglas/symfony-docker) 项目 +> (使用 FrankenPHP)作为使用多阶段镜像的更高级示例, +> Composer、额外的 PHP 扩展等。 最后,如果你使用 Git,请提交这些文件并推送。 @@ -77,13 +78,13 @@ volumes: 然后,单击“Choose an image”部分下的“Marketplace”选项卡,然后搜索名为“Docker”的应用程序。 这将配置已安装最新版本的 Docker 和 Docker Compose 的 Ubuntu 服务器! -出于测试目的,最廉价的套餐就足够了。 +出于测试目的,最便宜的就足够了。 对于实际的生产用途,你可能需要在“general purpose”部分中选择一个计划来满足你的需求。 -![使用 Docker 在 DigitalOcean 上部署 FrankenPHP](digitalocean-droplet.png) +![使用 Docker 在 DigitalOcean 上部署 FrankenPHP](../digitalocean-droplet.png) 你可以保留其他设置的默认值,也可以根据需要进行调整。 -不要忘记添加你的 SSH 密钥或创建密码,然后点击“Finalize and create”按钮。 +不要忘记添加你的 SSH 密钥或创建密码,然后点击“完成并创建”按钮。 然后,在 Droplet 预配时等待几秒钟。 Droplet 准备就绪后,使用 SSH 进行连接: @@ -105,7 +106,7 @@ your-domain-name.example.com. IN A 207.154.233.113 DigitalOcean 域服务示例(“Networking” > “Domains”): -![在 DigitalOcean 上配置 DNS](digitalocean-dns.png) +![在 DigitalOcean 上配置 DNS](../digitalocean-dns.png) > [!NOTE] > @@ -130,7 +131,7 @@ docker compose up --wait ``` 你的服务器已启动并运行,并且已自动为你生成 HTTPS 证书。 -访问 `https://your-domain-name.example.com` 即可体验! +去 `https://your-domain-name.example.com` 享受吧! > [!CAUTION] > diff --git a/docs/cn/static.md b/docs/cn/static.md index 5ebb9f44f8..5680a4f7ca 100644 --- a/docs/cn/static.md +++ b/docs/cn/static.md @@ -1,26 +1,26 @@ # 创建静态构建 -与使用本地安装的 PHP 库不同, -借助于优秀的 [static-php-cli 项目](https://github.com/crazywhalecc/static-php-cli)(尽管其名称,该项目支持所有 SAPI,而不仅仅是 CLI),可以创建一个静态或基本静态的 FrankenPHP 构建。 +与其使用本地安装的PHP库, +由于伟大的 [static-php-cli 项目](https://github.com/crazywhalecc/static-php-cli),创建一个静态或基本静态的 FrankenPHP 构建是可能的(尽管它的名字,这个项目支持所有的 SAPI,而不仅仅是 CLI)。 -通过这种方法,一个单一的、可移植的二进制文件将包含 PHP 解释器、Caddy Web 服务器和 FrankenPHP! +使用这种方法,我们可构建一个包含 PHP 解释器、Caddy Web 服务器和 FrankenPHP 的可移植二进制文件! -完全静态的本地可执行文件完全不需要任何依赖,甚至可以在 [`scratch` Docker 镜像](https://docs.docker.com/build/building/base-images/#create-a-minimal-base-image-using-scratch) 上运行。 -然而,它们无法加载动态 PHP 扩展(例如 Xdebug),并且由于使用了 musl libc,存在一些限制。 +完全静态的本地可执行文件不需要任何依赖,并且可以在 [`scratch` Docker 镜像](https://docs.docker.com/build/building/base-images/#create-a-minimal-base-image-using-scratch) 上运行。 +然而,它们无法加载动态 PHP 扩展(例如 Xdebug),并且由于使用了 musl libc,有一些限制。 -基本静态的二进制文件只需要 `glibc` 即可加载动态扩展。 +大多数静态二进制文件只需要 `glibc` 并且可以加载动态扩展。 -在可能的情况下,我们建议使用基于 glibc 的基本静态构建。 +在可能的情况下,我们建议使用基于glibc的、主要是静态构建的版本。 FrankenPHP 还支持 [将 PHP 应用程序嵌入到静态二进制文件中](embed.md)。 ## Linux -我们提供了 Docker 镜像来构建 Linux 静态二进制文件: +我们提供了一个 Docker 镜像来构建 Linux 静态二进制文件: -### 基于 musl 的完全静态构建 +### 基于musl的完全静态构建 -对于一个在任何 Linux 发行版上运行且无需依赖项,但不支持动态加载扩展的完全静态二进制文件: +对于一个在任何Linux发行版上运行且不需要依赖项的完全静态二进制文件,但不支持动态加载扩展: ```console docker buildx bake --load static-builder-musl @@ -33,18 +33,18 @@ docker cp $(docker create --name static-builder-musl dunglas/frankenphp:static-b docker buildx bake --load --set static-builder-musl.args.MIMALLOC=1 static-builder-musl ``` -### 基于 glibc 的基本静态构建(支持动态扩展) +### 基于glibc的,主要静态构建(支持动态扩展) -对于一个支持动态加载 PHP 扩展,同时将所选扩展静态编译的二进制文件: +对于一个支持动态加载 PHP 扩展的二进制文件,同时又将所选扩展静态编译: ```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 ``` -该二进制文件支持所有 glibc 2.17 及以上版本,但不支持基于 musl 的系统(如 Alpine Linux)。 +该二进制文件支持所有glibc版本2.17及以上,但不支持基于musl的系统(如Alpine Linux)。 -生成的除了 `glibc` 外基本静态的二进制文件名为 `frankenphp`,并且可以在当前目录中找到。 +生成的主要是静态的(除了 `glibc`)二进制文件名为 `frankenphp`,并且可以在当前目录中找到。 如果你想在没有 Docker 的情况下构建静态二进制文件,请查看 macOS 说明,它也适用于 Linux。 @@ -52,7 +52,7 @@ docker cp $(docker create --name static-builder-gnu dunglas/frankenphp:static-bu 默认情况下,大多数流行的 PHP 扩展都会被编译。 -为了减少二进制文件的大小和减少攻击面,您可以选择使用 `PHP_EXTENSIONS` Docker 参数来构建扩展列表。 +为了减少二进制文件的大小和减少攻击面,您可以选择使用 `PHP_EXTENSIONS` Docker ARG 构建的扩展列表。 例如,运行以下命令仅构建 `opcache` 扩展: @@ -89,11 +89,11 @@ docker buildx bake \ > 如果 `XCADDY_ARGS` 为空或未设置,则默认包含 cbrotli、Mercure 和 Vulcain 模块。 > 如果自定义了 `XCADDY_ARGS` 的值,则必须显式地包含它们。 -另请参阅[如何自定义构建](#customizing-the-build)。 +参见:[自定义构建](#自定义构建) ### GitHub Token -如果遇到了 GitHub API 速率限制,请在名为 `GITHUB_TOKEN` 的环境变量中设置 GitHub 个人访问令牌: +如果遇到了 GitHub API 速率限制,请在 `GITHUB_TOKEN` 的环境变量中设置 GitHub Personal Access Token: ```console GITHUB_TOKEN="xxx" docker --load buildx bake static-builder-musl @@ -102,7 +102,7 @@ GITHUB_TOKEN="xxx" docker --load buildx bake static-builder-musl ## macOS -运行以下脚本以创建适用于 macOS 的静态二进制文件(您必须已安装 [Homebrew](https://brew.sh/)): +运行以下脚本以创建适用于 macOS 的静态二进制文件(需要先安装 [Homebrew](https://brew.sh/)): ```console git clone https://github.com/php/frankenphp @@ -110,7 +110,7 @@ cd frankenphp ./build-static.sh ``` -注意:此脚本也适用于 Linux(以及可能在其他类 Unix 系统上),并且我们提供的 Docker 镜像在内部也使用了此脚本。 +注意:此脚本也适用于 Linux(可能也适用于其他 Unix 系统),我们提供的用于构建静态二进制的 Docker 镜像也在内部使用这个脚本。 ## 自定义构建 @@ -119,24 +119,24 @@ cd frankenphp - `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 模块 +- `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 及其所有依赖项都将从头开始构建(不使用缓存) -- `NO_COMPRESS`: 不使用 UPX 压缩生成的二进制文件 -- `DEBUG_SYMBOLS`: 设置后,调试符号将不会被剥离,并会被添加到二进制文件中 -- `MIMALLOC`: (实验性,仅限 Linux) 使用 [mimalloc](https://github.com/microsoft/mimalloc) 替换 musl 的 mallocng,以提高性能。我们仅建议将其用于面向 musl 的构建;对于 glibc,建议禁用此选项,并在运行二进制文件时改用 [`LD_PRELOAD`](https://microsoft.github.io/mimalloc/overrides.html)。 -- `RELEASE`: (仅限维护者)设置后,生成的二进制文件将上传到 GitHub +- `CLEAN`: 设置后,libphp 及其所有依赖项都是重新构建的(不使用缓存) +- `NO_COMPRESS`: 不要使用UPX压缩生成的二进制文件 +- `DEBUG_SYMBOLS`: 设置后,调试符号将被保留在二进制文件内 +- `MIMALLOC`: (实验性,仅限Linux) 用[mimalloc](https://github.com/microsoft/mimalloc)替换musl的mallocng,以提高性能。我们仅建议在musl目标构建中使用此选项,对于glibc,建议禁用此选项,并在运行二进制文件时使用[`LD_PRELOAD`](https://microsoft.github.io/mimalloc/overrides.html)。 +- `RELEASE`: (仅限维护者)设置后,生成的二进制文件将上传到 GitHub 上 ## 扩展 -使用基于 glibc 或 macOS 的二进制文件,您可以动态加载 PHP 扩展。然而,这些扩展必须使用 ZTS 支持进行编译。 +使用glibc或基于macOS的二进制文件,您可以动态加载PHP扩展。然而,这些扩展必须使用ZTS支持进行编译。 由于大多数软件包管理器目前不提供其扩展的 ZTS 版本,因此您必须自己编译它们。 -为此,您可以构建并运行 `static-builder-gnu` Docker 容器,连接到它,并使用 `./configure --with-php-config=/go/src/app/dist/static-php-cli/buildroot/bin/php-config` 编译扩展。 +为此,您可以构建并运行 `static-builder-gnu` Docker 容器,远程进入它,并使用 `./configure --with-php-config=/go/src/app/dist/static-php-cli/buildroot/bin/php-config` 编译扩展。 -以下是针对 [Xdebug 扩展](https://xdebug.org) 的示例步骤: +关于 [Xdebug 扩展](https://xdebug.org) 的示例步骤: ```console docker build -t gnu-ext -f static-builder-gnu.Dockerfile --build-arg FRANKENPHP_VERSION=1.0 . @@ -158,4 +158,4 @@ docker rmi gnu-ext ``` 这将在当前目录中创建 `frankenphp` 和 `xdebug-zts.so`。 -如果您将 `xdebug-zts.so` 移动到您的扩展目录中,并在您的 `php.ini` 中添加 `zend_extension=xdebug-zts.so`,然后运行 FrankenPHP,它将加载 Xdebug。 +如果你将 `xdebug-zts.so` 移动到你的扩展目录中,添加 `zend_extension=xdebug-zts.so` 到你的 php.ini 并运行 FrankenPHP,它将加载 Xdebug。 diff --git a/docs/cn/wordpress.md b/docs/cn/wordpress.md deleted file mode 100644 index f2584ab397..0000000000 --- a/docs/cn/wordpress.md +++ /dev/null @@ -1,59 +0,0 @@ -# WordPress - -使用 FrankenPHP 运行 [WordPress](https://wordpress.org/),享受现代、高性能的堆栈,具备自动 HTTPS、HTTP/3 和 Zstandard 压缩功能。 - -## 最小安装 - -1. [下载 WordPress](https://wordpress.org/download/) -2. 解压 ZIP 存档并在解压后的目录中打开终端 -3. 运行: - - ```console - frankenphp php-server - ``` - -4. 访问 `http://localhost/wp-admin/` 并按照安装说明进行操作 -5. 享受吧! - -对于生产环境就绪的设置,请优先使用 `frankenphp run` 配合如下 `Caddyfile`: - -```caddyfile -example.com - -php_server -encode zstd br gzip -log -``` - -## 热重载 - -要将 [热重载](hot-reload.md) 功能与 WordPress 配合使用,请启用 [Mercure](mercure.md) 并在您的 `Caddyfile` 中为 `php_server` 指令添加 `hot_reload` 子指令: - -```caddyfile -localhost - -mercure { - anonymous -} - -php_server { - hot_reload -} -``` - -然后,在您的 WordPress 主题的 `functions.php` 文件中添加加载 JavaScript 库所需的代码: - -```php -function hot_reload() { - ?> - - - - - - [!TIP] -> 以下部分仅在 Symfony 7.4 之前是必需的,Symfony 7.4 引入了对 FrankenPHP worker 模式的原生支持。 - FrankenPHP 的 worker 模式由 [Symfony Runtime Component](https://symfony.com/doc/current/components/runtime.html) 支持。 要在 worker 中启动任何 Symfony 应用程序,请安装 [PHP Runtime](https://github.com/php-runtime/runtime) 的 FrankenPHP 包: @@ -83,15 +78,9 @@ $myApp->boot(); // 在循环外的处理器以获得更好的性能(减少工作量) $handler = static function () use ($myApp) { - try { - // 当收到请求时调用, - // 超全局变量、php://input 等都会被重置 - echo $myApp->handle($_GET, $_POST, $_COOKIE, $_FILES, $_SERVER); - } catch (\Throwable $exception) { - // `set_exception_handler` 仅在 worker 脚本结束时调用, - // 这可能不是你期望的,因此在此处捕获并处理异常 - (new \MyCustomExceptionHandler)->handleException($exception); - } + // 当收到请求时调用, + // 超全局变量、php://input 等都会被重置 + echo $myApp->handle($_GET, $_POST, $_COOKIE, $_FILES, $_SERVER); }; $maxRequests = (int)($_SERVER['MAX_REQUESTS'] ?? 0); @@ -155,7 +144,7 @@ curl -X POST http://localhost:2019/frankenphp/workers/restart 但是,如果 worker 脚本在短时间内继续以非零退出代码失败 (例如,脚本中有拼写错误),FrankenPHP 将崩溃并出现错误:`too many consecutive failures`。 -可以在你的 [Caddyfile](config.md#caddyfile-config) 中使用 `max_consecutive_failures` 选项配置连续失败的次数: +可以在你的 [Caddyfile](config.md#caddyfile-配置) 中使用 `max_consecutive_failures` 选项配置连续失败的次数: ```caddyfile frankenphp { @@ -187,3 +176,4 @@ $handler = static function () use ($workerServer) { }; // ... +``` diff --git a/docs/fr/classic.md b/docs/fr/classic.md index ef558e43d3..68de11c2b7 100644 --- a/docs/fr/classic.md +++ b/docs/fr/classic.md @@ -1,11 +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 transparent pour PHP-FPM ou Apache avec mod_php. +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#caddyfile-config) pour les servir. Le nombre de connexions acceptées et mises en file d'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), à l'instar du mode dynamique de PHP-FPM. +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 mises 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#caddyfile-config) `max_wait_time` dans la configuration globale de FrankenPHP afin de limiter la durée pendant laquelle une requête peut attendre un thread PHP libre avant d'être rejetée. -De plus, vous pouvez définir un [délai d'écriture raisonnable dans Caddy](https://caddyserver.com/docs/caddyfile/options#timeouts). +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` dans la configuration globale de FrankenPHP 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 ne lancera qu'un seul pool de threads FrankenPHP, qui sera partagé entre tous les blocs `php_server`. +Chaque instance de Caddy n'utilisera qu'un seul pool de threads FrankenPHP, qui sera partagé par tous les blocs `php_server`. diff --git a/docs/fr/compile.md b/docs/fr/compile.md index 7e7b4a44d5..5a3d1f139a 100644 --- a/docs/fr/compile.md +++ b/docs/fr/compile.md @@ -1,9 +1,9 @@ # Compiler depuis les sources -Ce document explique comment créer un binaire FrankenPHP qui chargera PHP en tant que bibliothèque dynamique. +Ce document explique comment créer un build FrankenPHP qui chargera PHP en tant que bibliothèque dynamique. C'est la méthode recommandée. -Alternativement, des [versions entièrement ou partiellement statiques](static.md) peuvent également être créées. +Alternativement, il est aussi possible de [créer des builds statiques](static.md). ## Installer PHP @@ -33,10 +33,11 @@ tar xf php-* cd php-*/ ``` -Ensuite, exécutez le script `configure` avec les options nécessaires pour votre plateforme. -Les `flags` `./configure` suivants sont obligatoires, mais vous pouvez en ajouter d'autres, par exemple, pour compiler des extensions ou des fonctionnalités supplémentaires. +Ensuite, configurez PHP pour votre système d'exploitation. -#### Linux +Les options de configuration suivantes sont nécessaires pour la compilation, mais vous pouvez également inclure d'autres options selon vos besoins, par exemple pour ajouter des extensions et fonctionnalités supplémentaires. + +### Linux ```console ./configure \ @@ -46,7 +47,7 @@ Les `flags` `./configure` suivants sont obligatoires, mais vous pouvez en ajoute --enable-zend-max-execution-timers ``` -#### Mac +### Mac Utilisez le gestionnaire de paquets [Homebrew](https://brew.sh/) pour installer les dépendances obligatoires et optionnelles : @@ -62,10 +63,11 @@ Puis exécutez le script de configuration : --enable-embed \ --enable-zts \ --disable-zend-signals \ + --disable-opcache-jit \ --with-iconv=/opt/homebrew/opt/libiconv/ ``` -#### Compiler PHP +### Compilez PHP Finalement, compilez et installez PHP : @@ -74,21 +76,18 @@ make -j"$(getconf _NPROCESSORS_ONLN)" sudo make install ``` -## Installer les dépendances optionnelles +## Installez les dépendances optionnelles -Certaines fonctionnalités de FrankenPHP dépendent de dépendances système optionnelles qui doivent être installées. +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 | -| [Mercure](mercure.md) | [Bibliothèque Mercure Go](https://pkg.go.dev/github.com/dunglas/mercure) (installée automatiquement, licence AGPL) | nomercure | +| 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 construire le binaire final. - ### Utiliser xcaddy La méthode recommandée consiste à utiliser [xcaddy](https://github.com/caddyserver/xcaddy) pour compiler FrankenPHP. @@ -102,13 +101,10 @@ CGO_LDFLAGS="$(php-config --ldflags) $(php-config --libs)" \ 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 \ - --with github.com/dunglas/caddy-cbrotli + --with github.com/dunglas/vulcain/caddy # Ajoutez les modules Caddy supplémentaires et les extensions FrankenPHP ici - # Facultativement, si vous souhaitez compiler à partir de vos sources frankenphp : - # --with github.com/dunglas/frankenphp=$(pwd) \ - # --with github.com/dunglas/frankenphp/caddy=$(pwd)/caddy ``` > [!TIP] @@ -129,3 +125,4 @@ Il est également possible de compiler FrankenPHP sans `xcaddy` en utilisant dir curl -L https://github.com/php/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 +``` diff --git a/docs/fr/config.md b/docs/fr/config.md index be14fa9994..7c52cea7cb 100644 --- a/docs/fr/config.md +++ b/docs/fr/config.md @@ -1,41 +1,19 @@ # Configuration -FrankenPHP, Caddy ainsi que les modules [Mercure](mercure.md) et [Vulcain](https://vulcain.rocks) peuvent être configurés en utilisant [les formats pris en charge par Caddy](https://caddyserver.com/docs/getting-started#your-first-config). +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). -Le format le plus courant est le `Caddyfile`, un format texte simple et lisible par l'homme. -Par défaut, FrankenPHP cherchera un `Caddyfile` dans le répertoire actuel. -Vous pouvez spécifier un chemin personnalisé avec l'option `-c` ou `--config`. - -Un `Caddyfile` minimal pour servir une application PHP est présenté ci-dessous : - -```caddyfile -# The hostname to respond to -localhost - -# Optionaly, the directory to serve files from, otherwise defaults to the current directory -#root public/ -php_server -``` - -Un `Caddyfile` plus avancé, activant davantage de fonctionnalités et fournissant des variables d'environnement pratiques, est disponible [dans le dépôt FrankenPHP](https://github.com/php/frankenphp/blob/main/caddy/frankenphp/Caddyfile), -ainsi qu'avec les images Docker. +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). -Selon votre méthode d'installation, FrankenPHP et l'interpréteur PHP chercheront les fichiers de configuration aux emplacements décrits ci-dessous. +L'interpréteur PHP cherchera dans les emplacements suivants : -## Docker +Docker : -FrankenPHP : - -- `/etc/frankenphp/Caddyfile` : le fichier de configuration principal -- `/etc/frankenphp/Caddyfile.d/*.caddyfile` : fichiers de configuration additionnels chargés automatiquement - -PHP : - -- `php.ini` : `/usr/local/etc/php/php.ini` (aucun `php.ini` n'est fourni par défaut) +- 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-/` +- extensions php : `/usr/local/lib/php/extensions/no-debug-zts-/` - Vous devriez copier un modèle officiel fourni par le projet PHP : ```dockerfile @@ -48,29 +26,17 @@ 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 ``` -## Paquets RPM et Debian - -FrankenPHP : - -- `/etc/frankenphp/Caddyfile` : le fichier de configuration principal -- `/etc/frankenphp/Caddyfile.d/*.caddyfile` : fichiers de configuration additionnels chargés automatiquement +Installation de FrankenPHP (.rpm ou .deb) : -PHP : - -- `php.ini` : `/etc/php-zts/php.ini` (un fichier `php.ini` avec des préréglages de production est fourni par défaut) -- fichiers de configuration supplémentaires : `/etc/php-zts/conf.d/*.ini` - -## Binaire statique - -FrankenPHP : - -- Dans le répertoire de travail actuel : `Caddyfile` +- 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/` -PHP : +Binaire statique : -- `php.ini` : Le répertoire dans lequel `frankenphp run` ou `frankenphp php-server` est exécuté, puis `/etc/frankenphp/php.ini` +- 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, intégrez-les dans le binaire lui-même +- 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 @@ -81,43 +47,44 @@ Exemple minimal : ```caddyfile 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 } ``` -Vous pouvez également configurer explicitement FrankenPHP en utilisant l'[option globale](https://caddyserver.com/docs/caddyfile/concepts#global-options) `frankenphp` : +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 # Définit le nombre de threads PHP à démarrer. Par défaut : 2x le nombre de CPUs disponibles. - max_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 # 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 # Définit une directive php.ini. Peut être utilisé plusieurs fois pour définir plusieurs directives. - worker { - file # Définit le chemin vers le script worker. - num # Définit le nombre de threads PHP à démarrer, par défaut 2x le nombre de CPUs disponibles. - env # 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 # Définit le chemin d'accès à surveiller pour les modifications de fichiers. Peut être spécifié plusieurs fois pour plusieurs chemins. - name # Définit le nom du worker, utilisé dans les journaux et les métriques. Défaut : chemin absolu du fichier worker - max_consecutive_failures # Définit le nombre maximum d'échecs consécutifs avant que le worker ne soit considéré comme défaillant, -1 signifie que le worker redémarre toujours. Par défaut : 6. - } - } + frankenphp { + num_threads # Définit le nombre de threads PHP à démarrer. Par défaut : 2x le nombre de CPUs disponibles. + max_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 # 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 Définit une directive php.ini. Peut être utilisé plusieurs fois pour définir plusieurs directives. + worker { + file # Définit le chemin vers le script worker. + num # Définit le nombre de threads PHP à démarrer, par défaut 2x le nombre de CPUs disponibles. + env # 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 # Définit le chemin d'accès à surveiller pour les modifications de fichiers. Peut être spécifié plusieurs fois pour plusieurs chemins. + name # Définit le nom du worker, utilisé dans les journaux et les métriques. Défaut : chemin absolu du fichier du worker + max_consecutive_failures # Définit le nombre maximum d'échecs consécutifs avant que le worker ne soit considéré comme défaillant, -1 signifie que le worker redémarre toujours. Par défaut : 6. + } + } } # ... ``` -Alternativement, vous pouvez utiliser la forme courte en une seule ligne de l'option `worker` : +Vous pouvez également utiliser la forme courte de l'option worker en une seule ligne : ```caddyfile { - frankenphp { - worker - } + frankenphp { + worker + } } # ... @@ -128,48 +95,48 @@ Vous pouvez aussi définir plusieurs workers si vous servez plusieurs applicatio ```caddyfile app.example.com { root /path/to/app/public - php_server { - root /path/to/app/public # permet une meilleure mise en cache - worker index.php - } + php_server { + root /path/to/app/public # permet une meilleure mise en cache + worker index.php + } } other.example.com { root /path/to/other/public - php_server { - root /path/to/other/public - worker index.php - } + php_server { + root /path/to/other/public + worker index.php + } } # ... ``` -L'utilisation de la directive `php_server` est généralement ce dont vous avez besoin, -mais si vous avez besoin d'un contrôle total, vous pouvez utiliser la directive `php` de niveau inférieur. +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 non. En savoir plus à ce sujet dans la [page performances](performance.md#try_files). +c'est un fichier PHP ou pas. En savoir plus à ce sujet dans la [page performances](performance.md#try_files). 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 } ``` @@ -177,26 +144,25 @@ Les directives `php_server` et `php` disposent des options suivantes : ```caddyfile php_server [] { - root # Définit le dossier racine du site. Par défaut : la directive `root`. - split_path # 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 # 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 # Définit le chemin vers le script worker, peut être relatif à la racine du php_server - num # Définit le nombre de threads PHP à démarrer, par défaut 2x le nombre de CPUs disponibles. - name # Définit le nom du worker, utilisé dans les journaux et les métriques. Défaut : chemin absolu du fichier worker. Commence toujours par m# lorsqu'il est défini dans un bloc php_server. - watch # Définit le chemin d'accès à surveiller pour les modifications de fichiers. Peut être spécifié plusieurs fois pour plusieurs chemins. - env # 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. - match # fait correspondre le worker à un motif de chemin. Remplace try_files et ne peut être utilisé que dans la directive php_server. - } - worker # Peut également utiliser la forme courte comme dans le bloc frankenphp global. + root # Définit le dossier racine du le site. Par défaut : valeur de la directive `root` parente. + split_path # 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 # 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 # Définit le chemin vers le script worker, peut être relatif à la racine du php_server + num # Définit le nombre de threads PHP à démarrer, par défaut 2x le nombre de CPUs disponibles + 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 # Définit le chemin d'accès à surveiller pour les modifications de fichiers. Peut être spécifié plusieurs fois pour plusieurs chemins. + env # 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 # Peut également utiliser la forme courte comme dans le bloc frankenphp global. } ``` ### Surveillance des modifications de fichier -Étant donné que les workers ne démarrent votre application qu'une seule fois et la gardent en mémoire, toute modification +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`. @@ -213,11 +179,9 @@ Ceci est utile pour les environnements de développement. } ``` -Cette fonctionnalité est souvent utilisée en combinaison avec le [rechargement à chaud (hot reload)](hot-reload.md). - -Si le répertoire `watch` n'est pas spécifié, il se rabattra sur `./**/*.{env,php,twig,yaml,yml}`, -qui surveille tous les fichiers `.env`, `.php`, `.twig`, `.yaml` et `.yml` dans le répertoire et les sous-répertoires -où le processus FrankenPHP a été démarré. Vous pouvez également spécifier un ou plusieurs répertoires via un +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 @@ -235,35 +199,40 @@ où le processus FrankenPHP a été démarré. Vous pouvez également spécifier ``` - Le motif `**` signifie une surveillance récursive. -- Les répertoires peuvent également être relatifs (par rapport à l'emplacement de démarrage du processus FrankenPHP). +- 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 journaux) car ils peuvent provoquer des redémarrages intempestifs du worker. +- 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. -Le système de surveillance des fichiers est basé sur [e-dant/watcher](https://github.com/e-dant/watcher). +La surveillance des fichiers est basé sur [e-dant/watcher](https://github.com/e-dant/watcher). -## Faire correspondre le worker à un chemin +### Full Duplex (HTTP/1) -Dans les applications PHP traditionnelles, les scripts sont toujours placés dans le répertoire public. -Ceci est également vrai pour les scripts worker, qui sont traités comme tout autre script PHP. -Si vous souhaitez plutôt placer le script worker en dehors du répertoire public, vous pouvez le faire via la directive `match`. +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.) -La directive `match` est une alternative optimisée à `try_files`, disponible uniquement au sein de `php_server` et `php`. -L'exemple suivant servira toujours un fichier dans le répertoire public s'il est présent, -et transmettra sinon la requête au worker correspondant au motif de chemin. +Il s'agit d'une configuration optionnelle qui doit être ajoutée aux options globales dans le fichier `Caddyfile` : ```caddyfile { - frankenphp { - php_server { - worker { - file /path/to/worker.php # le fichier peut être en dehors du chemin public - match /api/* # toutes les requêtes commençant par /api/ seront gérées par ce worker - } - } - } + 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 : @@ -273,9 +242,9 @@ Les variables d'environnement suivantes peuvent être utilisées pour insérer d - `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 sont exposées par défaut dans la superglobale `$_SERVER`. +Comme pour les SAPI FPM et CLI, les variables d'environnement ne sont exposées par défaut dans la superglobale `$_SERVER`. -La valeur `S` de [la directive `variables_order` de PHP](https://www.php.net/manual/fr/ini.core.php#ini.variables-order) est toujours équivalente à `ES` quelle que soit la position de `E` ailleurs dans cette directive. +La valeur `S` de [la directive `variables_order` de PHP](https://www.php.net/manual/fr/ini.core.php#ini.variables-order) est toujours équivalente à `ES`, que `E` soit défini ailleurs dans cette directive ou non. ## Configuration PHP @@ -300,43 +269,6 @@ Vous pouvez également modifier la configuration de PHP en utilisant la directiv } ``` -### Désactivation de HTTPS - -Par défaut, FrankenPHP activera automatiquement HTTPS pour tous les noms d'hôte, y compris `localhost`. -Si vous souhaitez désactiver HTTPS (par exemple dans un environnement de développement), vous pouvez définir la variable d'environnement `SERVER_NAME` à `http://` ou `:80` : - -Alternativement, vous pouvez utiliser toutes les autres méthodes décrites dans la [documentation de Caddy](https://caddyserver.com/docs/automatic-https#activation). - -Si vous souhaitez utiliser HTTPS avec l'adresse IP `127.0.0.1` au lieu du nom d'hôte `localhost`, veuillez consulter la section [problèmes connus](known-issues.md#using-https127001-with-docker). - -### 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 : [Mercure](mercure.md), WebSocket, événements envoyés par le serveur, etc.) - -Il s'agit d'une configuration à activer explicitement qui doit être ajoutée aux options globales dans le `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). - ## 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 : diff --git a/docs/fr/docker.md b/docs/fr/docker.md index d9c8a1e328..7f97bcc938 100644 --- a/docs/fr/docker.md +++ b/docs/fr/docker.md @@ -1,14 +1,13 @@ # Création d'une image Docker personnalisée -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. +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, 8.3, 8.4 et 8.5 sont disponibles. +Des variantes pour PHP 8.2, 8.3, 8.4 et 8.5 sont disponibles. [Parcourir les tags](https://hub.docker.com/r/dunglas/frankenphp/tags). -Les tags suivent ce modèle : `dunglas/frankenphp:-php-` +Les tags suivent le pattern suivant: `dunglas/frankenphp:-php-` -- `` et `` sont respectivement 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`). -- `` est soit `trixie` (pour Debian Trixie), `bookworm` (pour Debian Bookworm), ou `alpine` (pour la dernière version stable d'Alpine). +- `` et `` 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`). +- `` est soit `trixie` (pour Debian Trixie), `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). @@ -29,15 +28,10 @@ docker build -t my-php-app . docker run -it --rm --name my-running-app my-php-app ``` -## Comment ajuster la configuration - -Pour des raisons de commodité, [un `Caddyfile` par défaut](https://github.com/php/frankenphp/blob/main/caddy/frankenphp/Caddyfile) contenant -des variables d'environnement utiles est fourni dans l'image. - ## Comment installer plus d'extensions PHP Le script [`docker-php-extension-installer`](https://github.com/mlocati/docker-php-extension-installer) est fourni dans l'image de base. -L'ajout d'extensions PHP supplémentaires est simple : +Il est facile d'ajouter des extensions PHP supplémentaires : ```dockerfile FROM dunglas/frankenphp @@ -60,7 +54,7 @@ La manière la plus simple d'installer des modules Caddy personnalisés est d'ut ```dockerfile FROM dunglas/frankenphp:builder AS builder -# Copier xcaddy dans l'image builder +# Copier xcaddy dans l'image du constructeur COPY --from=caddy:builder /usr/bin/xcaddy /usr/bin/xcaddy # CGO doit être activé pour construire FrankenPHP @@ -81,12 +75,12 @@ RUN CGO_ENABLED=1 \ FROM dunglas/frankenphp AS runner -# Remplacer le binaire officiel par celui qui contient vos modules personnalisés +# Remplacer le binaire officiel par celui contenant vos modules personnalisés COPY --from=builder /usr/local/bin/frankenphp /usr/local/bin/frankenphp ``` -L'image `builder` fournie par FrankenPHP contient une version compilée de `libphp`. -[Les images `builder`](https://hub.docker.com/r/dunglas/frankenphp/tags?name=builder) sont fournies pour toutes les versions de FrankenPHP et PHP, à la fois pour Debian et Alpine. +L'image builder fournie par FrankenPHP contient une version compilée de `libphp`. +[Les images builder](https://hub.docker.com/r/dunglas/frankenphp/tags?name=builder) sont fournies pour toutes les versions de FrankenPHP et PHP, à la fois pour Debian et Alpine. > [!TIP] > @@ -115,7 +109,7 @@ docker run -v $PWD:/app/public -p 80:80 -p 443:443 -p 443:443/udp --tty my-php-a > [!TIP] > -> L'option `--tty` permet d'avoir des logs lisibles par l'homme au lieu de logs JSON. +> L'option --tty permet d'avoir des logs lisibles par un humain au lieu de logs JSON. Avec Docker Compose : @@ -137,7 +131,7 @@ services: - ./:/app/public - caddy_data:/data - caddy_config:/config - # commentez la ligne suivante en production, elle permet d'avoir des logs lisibles par l'homme en dev + # commentez la ligne suivante en production, elle permet d'avoir de beaux logs lisibles en dev tty: true # Volumes nécessaires pour les certificats et la configuration de Caddy @@ -162,19 +156,18 @@ RUN \ 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 à /config/caddy et /data/caddy - chown -R ${USER}:${USER} /config/caddy /data/caddy + # Donner l'accès en écriture à /data/caddy et /config/caddy + chown -R ${USER}:${USER} /data/caddy && chown -R ${USER}:${USER} /config/caddy USER ${USER} ``` ### Exécution sans capacité -Même lorsqu'il s'exécute sans les privilèges root, FrankenPHP a besoin de la capacité `CAP_NET_BIND_SERVICE` -pour lier le serveur web aux ports privilégiés (80 et 443). +Même lorsqu'il s'exécute en tant qu'utilisateur autre que root, FrankenPHP a besoin de la capacité `CAP_NET_BIND_SERVICE` +pour que son serveur utilise les ports privilégiés (80 et 443). -Si vous exposez FrankenPHP sur un port non privilégié (1024 et au-delà), il est possible -d'exécuter le serveur web en tant qu'utilisateur non-root, et sans avoir besoin d'aucune capacité : +Si vous exposez FrankenPHP sur un port non privilégié (à partir de 1024), il est possible de faire fonctionner le serveur web avec un utilisateur qui n'est pas root, et sans avoir besoin d'aucune capacité. ```dockerfile FROM dunglas/frankenphp @@ -182,18 +175,18 @@ FROM dunglas/frankenphp ARG USER=appuser RUN \ - # Utilisez "adduser -D ${USER}" pour les distributions basées sur Alpine + # Utiliser "adduser -D ${USER}" pour les distros basées sur Alpine useradd ${USER}; \ - # Supprimer la capacité par défaut + # Supprimer la capacité par défaut \ setcap -r /usr/local/bin/frankenphp; \ - # Donner un accès en écriture à /config/caddy et /data/caddy - chown -R ${USER}:${USER} /config/caddy /data/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} ``` Ensuite, définissez la variable d'environnement `SERVER_NAME` pour utiliser un port non privilégié. -Exemple : `:8000` +Exemple `:8000` ## Mises à jour @@ -204,8 +197,7 @@ Les images Docker sont construites : ## Versions de développement -Les versions de développement sont disponibles dans le dépôt Docker [`dunglas/frankenphp-dev`](https://hub.docker.com/repository/docker/dunglas/frankenphp-dev). -Un nouveau build est déclenché chaque fois qu'un commit est poussé sur la branche principale du dépôt GitHub. +Les versions de développement sont disponibles dans le dépôt Docker [`dunglas/frankenphp-dev`](https://hub.docker.com/repository/docker/dunglas/frankenphp-dev). Un nouveau build est déclenché chaque fois qu'un commit est poussé sur la branche principale du dépôt GitHub. Les tags `latest*` pointent vers la tête de la branche `main`. -Les tags sous la forme `sha-` sont également disponibles. \ No newline at end of file +Les tags sous la forme `sha-` sont également disponibles. diff --git a/docs/fr/embed.md b/docs/fr/embed.md index 77fb5dbacc..1523a392be 100644 --- a/docs/fr/embed.md +++ b/docs/fr/embed.md @@ -6,20 +6,20 @@ 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#applications-laravel-en-tant-que-binaires-autonomes). +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. -Par exemple, vous voudrez probablement : +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 -Par exemple, pour une application Symfony, vous pouvez utiliser les commandes suivantes : +Par exemple, pour une application Symfony, lancez les commandes suivantes : ```console # Exporter le projet pour se débarrasser de .git/, etc. @@ -53,17 +53,16 @@ dans le répertoire principal de l'application à intégrer La manière la plus simple de créer un binaire Linux est d'utiliser le builder basé sur Docker que nous fournissons. -1. Créez un fichier nommé `static-build.Dockerfile` dans le dépôt de votre application : +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-gnu # Si vous envisagez d'exécuter le binaire sur des systèmes musl-libc, utilisez plutôt static-builder-musl - # Copiez votre application + # Copy your app WORKDIR /go/src/app/dist/app COPY . . - # Construisez le binaire statique WORKDIR /go/src/app/ RUN EMBED=dist/app/ ./build-static.sh ``` @@ -85,7 +84,7 @@ La manière la plus simple de créer un binaire Linux est d'utiliser le builder 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 résultant est le fichier nommé `my-app` dans le répertoire courant. +Le binaire généré sera nommé `my-app` dans le répertoire courant. ## Créer un binaire pour d'autres systèmes d'exploitation @@ -97,7 +96,7 @@ cd frankenphp EMBED=/path/to/your/app ./build-static.sh ``` -Le binaire résultant est le fichier nommé `frankenphp--` dans le répertoire `dist/`. +Le binaire obtenu est le fichier nommé `frankenphp--` dans le répertoire `dist/`. ## Utiliser le binaire @@ -130,7 +129,7 @@ Vous pouvez également exécuter les scripts CLI PHP incorporés dans votre bina ## 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 [la documentation sur la compilation statique](static.md). +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`. @@ -146,7 +145,7 @@ PHP_EXTENSIONS=ctype,iconv,pdo_sqlite \ ## Distribuer le binaire -Sous Linux, le binaire créé est compressé à l'aide de [UPX](https://upx.github.io). +Sous Linux, le binaire est compressé par défaut à l'aide de [UPX](https://upx.github.io). Sous Mac, pour réduire la taille du fichier avant de l'envoyer, vous pouvez le compresser. Nous recommandons `xz`. diff --git a/docs/fr/extensions.md b/docs/fr/extensions.md index ad125299b8..caf50e9ca7 100644 --- a/docs/fr/extensions.md +++ b/docs/fr/extensions.md @@ -10,8 +10,8 @@ Grâce aux modules Caddy, vous pouvez écrire des extensions PHP en Go et les in FrankenPHP offre deux façons de créer des extensions PHP en Go : -1. **Utilisation du Générateur d'Extensions** - L'approche recommandée qui génère tout le code standard nécessaire pour la plupart des cas d'usage, vous permettant de vous concentrer sur l'écriture de votre code Go -2. **Implémentation Manuelle** - Contrôle total sur la structure de l'extension pour les cas d'usage avancés +1. **Utilisation du Générateur d'Extensions** - L'approche recommandée qui génère tout le code standard nécessaire pour la plupart des cas d'usage, vous permettant de vous concentrer sur l'écriture de votre code Go +2. **Implémentation Manuelle** - Contrôle total sur la structure de l'extension pour les cas d'usage avancés Nous commencerons par l'approche du générateur, car c'est le moyen le plus facile de commencer, puis nous montrerons l'implémentation manuelle pour ceux qui ont besoin d'un contrôle complet. @@ -26,14 +26,14 @@ Gardez à l'esprit que cet outil n'est **pas un générateur d'extensions comple ### Prérequis -Comme indiqué également dans la section d'implémentation manuelle ci-dessous, vous devez [obtenir les sources PHP](https://www.php.net/downloads.php) et créer un nouveau module Go. +Comme aussi couvert dans la section d'implémentation manuelle ci-dessous, vous devez [obtenir les sources PHP](https://www.php.net/downloads.php) et créer un nouveau module Go. #### Créer un Nouveau Module et Obtenir les Sources PHP La première étape pour écrire une extension PHP en Go est de créer un nouveau module Go. Vous pouvez utiliser la commande suivante pour cela : ```console -go mod init example.com/example +go mod init github.com/my-account/my-module ``` La seconde étape est [l'obtention des sources PHP](https://www.php.net/downloads.php) pour les étapes suivantes. Une fois que vous les avez, décompressez-les dans le répertoire de votre choix, mais pas à l'intérieur de votre module Go : @@ -53,9 +53,9 @@ package example import "C" import ( "strings" - "unsafe" + "unsafe" - "github.com/dunglas/frankenphp" + "github.com/dunglas/frankenphp" ) //export_php:function repeat_this(string $str, int $count, bool $reverse): string @@ -87,19 +87,18 @@ Alors que le premier point parle de lui-même, le second peut être plus diffici Bien que certains types de variables aient la même représentation mémoire entre C/PHP et Go, certains types nécessitent plus de logique pour être directement utilisés. C'est peut-être la partie la plus difficile quand il s'agit d'écrire des extensions car cela nécessite de comprendre les fonctionnements internes du moteur Zend et comment les variables sont stockées dans le moteur de PHP. Ce tableau résume ce que vous devez savoir : | Type PHP | Type Go | Conversion directe | Assistant C vers Go | Assistant Go vers C | Support des Méthodes de Classe | -| :----------------- | :---------------------------- | :----------------- | :-------------------------------- | :--------------------------------- | :----------------------------- | +| ------------------ | ----------------------------- | ------------------ | --------------------------------- | ---------------------------------- | ------------------------------ | | `int` | `int64` | ✅ | - | - | ✅ | | `?int` | `*int64` | ✅ | - | - | ✅ | | `float` | `float64` | ✅ | - | - | ✅ | | `?float` | `*float64` | ✅ | - | - | ✅ | | `bool` | `bool` | ✅ | - | - | ✅ | | `?bool` | `*bool` | ✅ | - | - | ✅ | -| `string`/`?string` | `*C.zend_string` | ❌ | `frankenphp.GoString()` | `frankenphp.PHPString()` | ✅ | +| `string`/`?string` | `*C.zend_string` | ❌ | frankenphp.GoString() | frankenphp.PHPString() | ✅ | | `array` | `frankenphp.AssociativeArray` | ❌ | `frankenphp.GoAssociativeArray()` | `frankenphp.PHPAssociativeArray()` | ✅ | | `array` | `map[string]any` | ❌ | `frankenphp.GoMap()` | `frankenphp.PHPMap()` | ✅ | | `array` | `[]any` | ❌ | `frankenphp.GoPackedArray()` | `frankenphp.PHPPackedArray()` | ✅ | | `mixed` | `any` | ❌ | `GoValue()` | `PHPValue()` | ❌ | -| `callable` | `*C.zval` | ❌ | - | `frankenphp.CallPHPCallable()` | ❌ | | `object` | `struct` | ❌ | _Pas encore implémenté_ | _Pas encore implémenté_ | ❌ | > [!NOTE] @@ -113,7 +112,7 @@ Si vous vous référez à l'extrait de code de la section précédente, vous pou FrankenPHP fournit un support natif pour les tableaux PHP à travers `frankenphp.AssociativeArray` ou une conversion directe vers une map ou un slice. -`AssociativeArray` représente une [table de hachage](https://fr.wikipedia.org/wiki/Table_de_hachage) composée d'un champ `Map: map[string]any` et d'un champ optionnel `Order: []string` (contrairement aux "tableaux associatifs" PHP, les maps Go ne sont pas ordonnées). +`AssociativeArray` représente une [hash map](https://fr.wikipedia.org/wiki/Table_de_hachage) composée d'un champ `Map: map[string]any` et d'un champ optionnel `Order: []string` (contrairement aux "tableaux associatifs" PHP, les maps Go ne sont pas ordonnées). Si l'ordre ou l'association ne sont pas nécessaires, il est également possible de convertir directement vers un slice `[]any` ou une map non ordonnée `map[string]any`. @@ -131,66 +130,66 @@ import ( ) // export_php:function process_data_ordered(array $input): array -func process_data_ordered_map(arr *C.zend_array) unsafe.Pointer { - // Convertir le tableau associatif PHP vers Go en conservant l'ordre - associativeArray, err := frankenphp.GoAssociativeArray[any](unsafe.Pointer(arr)) +func process_data_ordered_map(arr *C.zval) unsafe.Pointer { + // Convertir le tableau associatif PHP vers Go en conservant l'ordre + associativeArray, err := frankenphp.GoAssociativeArray[any](unsafe.Pointer(arr)) if err != nil { // gérer l'erreur } - // parcourir les entrées dans l'ordre - for _, key := range associativeArray.Order { - // value, _ = associativeArray.Map[key] // 'value' is not declared - // faire quelque chose avec key et value - } + // parcourir les entrées dans l'ordre + for _, key := range associativeArray.Order { + value, _ = associativeArray.Map[key] + // faire quelque chose avec key et value + } - // retourner un tableau ordonné - // si 'Order' n'est pas vide, seules les paires clé-valeur dans 'Order' seront respectées - return frankenphp.PHPAssociativeArray[string](frankenphp.AssociativeArray[string]{ - Map: map[string]string{ - "key1": "value1", - "key2": "value2", - }, - Order: []string{"key1", "key2"}, - }) + // retourner un tableau ordonné + // si 'Order' n'est pas vide, seules les paires clé-valeur dans 'Order' seront respectées + return frankenphp.PHPAssociativeArray[string](frankenphp.AssociativeArray[string]{ + Map: map[string]string{ + "key1": "value1", + "key2": "value2", + }, + Order: []string{"key1", "key2"}, + }) } // export_php:function process_data_unordered(array $input): array -func process_data_unordered_map(arr *C.zend_array) unsafe.Pointer { - // Convertir le tableau associatif PHP vers une map Go sans conserver l'ordre - // ignorer l'ordre sera plus performant - goMap, err := frankenphp.GoMap[any](unsafe.Pointer(arr)) +func process_data_unordered_map(arr *C.zval) unsafe.Pointer { + // Convertir le tableau associatif PHP vers une map Go sans conserver l'ordre + // ignorer l'ordre sera plus performant + goMap, err := frankenphp.GoMap[any](unsafe.Pointer(arr)) if err != nil { // gérer l'erreur } - // parcourir les entrées sans ordre spécifique - for key, value := range goMap { - // faire quelque chose avec key et value - } + // parcourir les entrées sans ordre spécifique + for key, value := range goMap { + // faire quelque chose avec key et value + } - // retourner un tableau non ordonné - return frankenphp.PHPMap(map[string]string { - "key1": "value1", - "key2": "value2", - }) + // retourner un tableau non ordonné + return frankenphp.PHPMap(map[string]string { + "key1": "value1", + "key2": "value2", + }) } // export_php:function process_data_packed(array $input): array -func process_data_packed(arr *C.zend_array) unsafe.Pointer { - // Convertir le tableau packed PHP vers Go - goSlice, err := frankenphp.GoPackedArray(unsafe.Pointer(arr)) +func process_data_packed(arr *C.zval) unsafe.Pointer { + // Convertir le tableau packed PHP vers Go + goSlice, err := frankenphp.GoPackedArray(unsafe.Pointer(arr)) if err != nil { // gérer l'erreur } - // parcourir le slice dans l'ordre - for index, value := range goSlice { - // faire quelque chose avec index et value - } + // parcourir le slice dans l'ordre + for index, value := range goSlice { + // faire quelque chose avec index et value + } - // retourner un tableau packed - return frankenphp.PHPPackedArray([]string{"value1", "value2", "value3"}) + // retourner un tableau packed + return frankenphp.PHPPackedArray([]string{"value1", "value2", "value3"}) } ``` @@ -199,7 +198,7 @@ func process_data_packed(arr *C.zend_array) unsafe.Pointer { - **Paires clé-valeur ordonnées** - Option pour conserver l'ordre du tableau associatif - **Optimisé pour plusieurs cas** - Option de ne pas conserver l'ordre pour de meilleures performances ou conversion directe vers un slice - **Détection automatique de liste** - Lors de la conversion vers PHP, détecte automatiquement si le tableau doit être une liste packed ou un hashmap -- **Tableaux imbriqués** - Les tableaux peuvent être imbriqués et convertiront automatiquement tous les types supportés (`int64`,`float64`,`string`,`bool`,`nil`,`AssociativeArray`,`map[string]any`,`[]any`) +- **Tableaux imbriqués** - Les tableaux peuvent être imbriqués et convertiront automatiquement tous les types supportés (`int64`, `float64`, `string`, `bool`, `nil`, `AssociativeArray`, `map[string]any`, `[]any`) - **Les objets ne sont pas supportés** - Actuellement, seuls les types scalaires et les tableaux peuvent être utilisés comme valeurs. Fournir un objet résultera en une valeur `null` dans le tableau PHP. ##### Méthodes disponibles : Packed et Associatif @@ -214,7 +213,7 @@ func process_data_packed(arr *C.zend_array) unsafe.Pointer { ### Travailler avec des Callables -FrankenPHP propose un moyen de travailler avec les _callables_ PHP grâce au helper `frankenphp.CallPHPCallable`. Cela permet d'appeler des fonctions ou des méthodes PHP depuis du code Go. +FrankenPHP propose un moyen de travailler avec les _callables_ PHP grâce au helper `frankenphp.CallPHPCallable()`. Cela permet d'appeler des fonctions ou des méthodes PHP depuis du code Go. Pour illustrer cela, créons notre propre fonction `array_map()` qui prend un _callable_ et un tableau, applique le _callable_ à chaque élément du tableau, et retourne un nouveau tableau avec les résultats : @@ -264,7 +263,7 @@ type UserStruct struct { #### Que sont les Classes Opaques ? -Les **classes opaques** sont des classes où la structure interne est cachée du code PHP. Cela signifie : +Les **classes opaques** sont des classes avec lesquelles la structure interne (comprendre : les propriétés) est cachée du code PHP. Cela signifie : - **Pas d'accès direct aux propriétés** : Vous ne pouvez pas lire ou écrire des propriétés directement depuis PHP (`$user->name` ne fonctionnera pas) - **Interface uniquement par méthodes** - Toutes les interactions doivent passer par les méthodes que vous définissez @@ -326,9 +325,9 @@ package example // #include import "C" import ( - "unsafe" + "unsafe" - "github.com/dunglas/frankenphp" + "github.com/dunglas/frankenphp" ) //export_php:method User::updateInfo(?string $name, ?int $age, ?bool $active): void @@ -460,10 +459,10 @@ package example // #include import "C" import ( - "strings" - "unsafe" + "strings" + "unsafe" - "github.com/dunglas/frankenphp" + "github.com/dunglas/frankenphp" ) //export_php:const @@ -480,43 +479,43 @@ const MODE_UPPERCASE = 2 //export_php:function repeat_this(string $str, int $count, int $mode): string func repeat_this(s *C.zend_string, count int64, mode int) unsafe.Pointer { - str := frankenphp.GoString(unsafe.Pointer(s)) + str := frankenphp.GoString(unsafe.Pointer(s)) - result := strings.Repeat(str, int(count)) - if mode == STR_REVERSE { - // inverser la chaîne - } + result := strings.Repeat(str, int(count)) + if mode == STR_REVERSE { + // inverser la chaîne + } - if mode == STR_NORMAL { - // no-op, juste pour montrer la constante - } + if mode == STR_NORMAL { + // no-op, juste pour montrer la constante + } - return frankenphp.PHPString(result, false) + return frankenphp.PHPString(result, false) } //export_php:class StringProcessor type StringProcessorStruct struct { - // champs internes + // champs internes } //export_php:method StringProcessor::process(string $input, int $mode): string func (sp *StringProcessorStruct) Process(input *C.zend_string, mode int64) unsafe.Pointer { - str := frankenphp.GoString(unsafe.Pointer(input)) + str := frankenphp.GoString(unsafe.Pointer(input)) - switch mode { - case MODE_LOWERCASE: - str = strings.ToLower(str) - case MODE_UPPERCASE: - str = strings.ToUpper(str) - } + switch mode { + case MODE_LOWERCASE: + str = strings.ToLower(str) + case MODE_UPPERCASE: + str = strings.ToUpper(str) + } - return frankenphp.PHPString(str, false) + return frankenphp.PHPString(str, false) } ``` ### Utilisation des Espaces de Noms -Le générateur prend en charge l'organisation des fonctions, classes et constantes de votre extension PHP sous un espace de noms en utilisant la directive `//export_php:namespace`. Cela aide à éviter les conflits de noms et fournit une meilleure organisation pour l'API de votre extension. +Le générateur prend en charge l'organisation des fonctions, classes et constantes de votre extension PHP sous un espace de noms (namespace) en utilisant la directive `//export_php:namespace`. Cela aide à éviter les conflits de noms et fournit une meilleure organisation pour l'API de votre extension. #### Déclarer un Espace de Noms @@ -622,7 +621,7 @@ Une fois que vous avez intégré votre extension dans FrankenPHP comme indiqué ## Implémentation Manuelle -Si vous voulez comprendre comment les extensions fonctionnent ou avez besoin d'un contrôle total sur votre extension, vous pouvez les écrire manuellement. Cette approche vous donne un contrôle complet mais nécessite plus de code standard. +Si vous voulez comprendre comment les extensions fonctionnent ou avez besoin d'un contrôle total sur votre extension, vous pouvez les écrire manuellement. Cette approche vous donne un contrôle complet mais nécessite plus de code intermédiaire. ### Fonction de Base @@ -638,21 +637,21 @@ package example // #include "extension.h" import "C" import ( - "log/slog" - "unsafe" + "log/slog" + "unsafe" - "github.com/dunglas/frankenphp" + "github.com/dunglas/frankenphp" ) func init() { - frankenphp.RegisterExtension(unsafe.Pointer(&C.ext_module_entry)) + frankenphp.RegisterExtension(unsafe.Pointer(&C.ext_module_entry)) } //export go_print_something func go_print_something() { - go func() { - slog.Info("Hello from a goroutine!") - }() + go func() { + slog.Info("Hello from a goroutine!") + }() } ``` diff --git a/docs/fr/github-actions.md b/docs/fr/github-actions.md index c701548e41..2a151aab6b 100644 --- a/docs/fr/github-actions.md +++ b/docs/fr/github-actions.md @@ -27,5 +27,5 @@ Dans les paramètres du dépôt, sous "secrets", ajoutez les secrets suivants : 1. Créez un nouveau tag dans le dépôt. 2. GitHub Actions va construire l'image et exécuter tous les tests. -3. Si la construction est réussie, l'image sera poussée vers le registre en utilisant le nom du tag comme tag (par exemple, `v1.2.3` et `v1.2` seront créés). +3. Si la compilation est réussie, l'image sera poussée vers le registre en utilisant le nom du tag comme tag (par exemple, `v1.2.3` et `v1.2` seront créés). 4. Le tag `latest` sera également mis à jour. diff --git a/docs/fr/hot-reload.md b/docs/fr/hot-reload.md deleted file mode 100644 index 2bf5fc5243..0000000000 --- a/docs/fr/hot-reload.md +++ /dev/null @@ -1,139 +0,0 @@ -# Rechargement à chaud - -FrankenPHP inclut une fonctionnalité de **rechargement à chaud** intégrée, conçue pour améliorer considérablement l'expérience développeur. - -![Mercure](hot-reload.png) - -Cette fonctionnalité offre un flux de travail similaire au **Remplacement de Module à Chaud (HMR)** que l'on trouve dans les outils JavaScript modernes (comme Vite ou webpack). -Au lieu de rafraîchir manuellement le navigateur après chaque modification de fichier (code PHP, templates, fichiers JavaScript et CSS...), -FrankenPHP met à jour le contenu en temps réel. - -Le rechargement à chaud fonctionne nativement avec WordPress, Laravel, Symfony, et toute autre application ou framework PHP. - -Lorsqu'il est activé, FrankenPHP surveille votre répertoire de travail actuel pour les modifications du système de fichiers. -Lorsqu'un fichier est modifié, il envoie une mise à jour [Mercure](mercure.md) au navigateur. - -Selon votre configuration, le navigateur : - -- **Transformera le DOM** (en préservant la position de défilement et l'état des entrées) si [Idiomorph](https://github.com/bigskysoftware/idiomorph) est chargé. -- **Rechargera la page** (rechargement en direct standard) si Idiomorph n'est pas présent. - -## Configuration - -Pour activer le rechargement à chaud, activez Mercure, puis ajoutez la sous-directive `hot_reload` à la directive `php_server` dans votre `Caddyfile`. - -> [!WARNING] -> Cette fonctionnalité est destinée aux **environnements de développement uniquement**. -> N'activez pas `hot_reload` en production, car la surveillance du système de fichiers entraîne une surcharge de performance et expose des points d'accès internes. - -```caddyfile -localhost - -mercure { - anonymous -} - -root public/ -php_server { - hot_reload -} -``` - -Par défaut, FrankenPHP surveillera tous les fichiers dans le répertoire de travail actuel correspondant à ce modèle glob : `./**/*.{css,env,gif,htm,html,jpg,jpeg,js,mjs,php,png,svg,twig,webp,xml,yaml,yml}` - -Il est possible de définir explicitement les fichiers à surveiller en utilisant la syntaxe glob : - -```caddyfile -localhost - -mercure { - anonymous -} - -root public/ -php_server { - hot_reload src/**/*{.php,.js} config/**/*.yaml -} -``` - -Utilisez la forme longue pour spécifier le sujet Mercure à utiliser ainsi que les répertoires ou fichiers à surveiller en fournissant des chemins à l'option `hot_reload` : - -```caddyfile -localhost - -mercure { - anonymous -} - -root public/ -php_server { - hot_reload { - topic hot-reload-topic - watch src/**/*.php - watch assets/**/*.{ts,json} - watch templates/ - watch public/css/ - } -} -``` - -## Intégration côté client - -Alors que le serveur détecte les changements, le navigateur doit s'abonner à ces événements pour mettre à jour la page. -FrankenPHP expose l'URL du Hub Mercure à utiliser pour s'abonner aux changements de fichiers via la variable d'environnement `$_SERVER['FRANKENPHP_HOT_RELOAD']`. - -Une bibliothèque JavaScript pratique, [frankenphp-hot-reload](https://www.npmjs.com/package/frankenphp-hot-reload), est également disponible pour gérer la logique côté client. -Pour l'utiliser, ajoutez ce qui suit à votre mise en page principale : - -```php - -Rechargement à chaud FrankenPHP - - - - - -``` - -La bibliothèque s'abonnera automatiquement au hub Mercure, récupérera l'URL actuelle en arrière-plan lorsqu'un changement de fichier est détecté et transformera le DOM. -Elle est disponible en tant que package [npm](https://www.npmjs.com/package/frankenphp-hot-reload) et sur [GitHub](https://github.com/dunglas/frankenphp-hot-reload). - -Alternativement, vous pouvez implémenter votre propre logique côté client en vous abonnant directement au hub Mercure en utilisant la classe JavaScript native `EventSource`. - -### Mode Worker - -Si vous exécutez votre application en [Mode Worker](https://frankenphp.dev/docs/worker/), le script de votre application reste en mémoire. -Cela signifie que les modifications apportées à votre code PHP ne seront pas reflétées immédiatement, même si le navigateur se recharge. - -Pour la meilleure expérience développeur, vous devez combiner `hot_reload` avec [la sous-directive `watch` dans la directive `worker`](config.md#watching-for-file-changes). - -- `hot_reload` : rafraîchit le **navigateur** lorsque les fichiers changent -- `worker.watch` : redémarre le worker lorsque les fichiers changent - -```caddy -localhost - -mercure { - anonymous -} - -root public/ -php_server { - hot_reload - worker { - file /path/to/my_worker.php - watch - } -} -``` - -### Comment ça marche - -1. **Surveillance** : FrankenPHP surveille le système de fichiers pour les modifications en utilisant la bibliothèque [`e-dant/watcher`](https://github.com/e-dant/watcher) en coulisses (nous avons contribué au binding Go). -2. **Redémarrage (Mode Worker)** : si `watch` est activé dans la configuration du worker, le worker PHP est redémarré pour charger le nouveau code. -3. **Poussée** : une charge utile JSON contenant la liste des fichiers modifiés est envoyée au [hub Mercure](https://mercure.rocks) intégré. -4. **Réception** : le navigateur, écoutant via la bibliothèque JavaScript, reçoit l'événement Mercure. -5. **Mise à jour** : - -- Si **Idiomorph** est détecté, il récupère le contenu mis à jour et transforme le HTML actuel pour correspondre au nouvel état, appliquant les changements instantanément sans perdre l'état. -- Sinon, `window.location.reload()` est appelé pour rafraîchir la page. diff --git a/docs/fr/known-issues.md b/docs/fr/known-issues.md index 5d97e5544b..8387e38811 100644 --- a/docs/fr/known-issues.md +++ b/docs/fr/known-issues.md @@ -11,11 +11,11 @@ Les extensions suivantes sont connues pour ne pas être compatibles avec Franken ## Extensions PHP boguées -Les extensions suivantes ont des bugs connus et des comportements inattendus lorsqu'elles sont utilisées avec FrankenPHP : +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 de musl libc, l'extension OpenSSL peut planter sous de fortes charges. Le problème ne se produit pas lors de l'utilisation de la plus populaire GNU libc. Ce bogue est [suivi par PHP](https://github.com/php/php-src/issues/13648). | +| 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 @@ -25,8 +25,6 @@ La fonction [get_browser()](https://www.php.net/manual/fr/function.get-browser.p Le binaire autonome et les images Docker basées sur Alpine (`dunglas/frankenphp:*-alpine`) utilisent [musl libc](https://musl.libc.org/) au lieu de [glibc et ses amis](https://www.etalabs.net/compare_libcs.html), pour garder une taille de binaire plus petite. Cela peut entraîner des problèmes de compatibilité. En particulier, le drapeau glob `GLOB_BRACE` n'est [pas disponible](https://www.php.net/manual/fr/function.glob.php). -Préférez utiliser la variante GNU du binaire statique et les images Docker basées sur Debian si vous rencontrez des problèmes. - ## Utilisation de `https://127.0.0.1` avec Docker Par défaut, FrankenPHP génère un certificat TLS pour `localhost`. @@ -80,7 +78,7 @@ docker run \ ## 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 [échoue actuellement](https://github.com/php/frankenphp/issues/483#issuecomment-1899890915) pour deux raisons : +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/php/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. @@ -124,7 +122,7 @@ 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/fr/function.openssl-get-cert-locations.php), +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] @@ -136,7 +134,7 @@ pour trouver l'endroit où les certificats CA doivent être installés et stocke 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 les variables `SSL_CERT_FILE` et `SSL_CERT_DIR` pour indiquer à OpenSSL où chercher les certificats CA : +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 diff --git a/docs/fr/laravel.md b/docs/fr/laravel.md index adda906196..82677aef02 100644 --- a/docs/fr/laravel.md +++ b/docs/fr/laravel.md @@ -2,7 +2,7 @@ ## Docker -Déployer une application web [Laravel](https://laravel.com) avec FrankenPHP est aussi simple que 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 : @@ -16,7 +16,7 @@ Et profitez ! Vous pouvez également exécuter vos projets Laravel avec FrankenPHP depuis votre machine locale : -1. [Téléchargez le binaire correspondant à votre système](../#binaire-autonome) +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 @@ -66,7 +66,7 @@ La commande `octane:frankenphp` peut prendre les options suivantes : - `--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 (par défaut : [fichier `Caddyfile` pré-configuré dans Laravel Octane](https://github.com/laravel/octane/blob/2.x/src/Commands/stubs/Caddyfile)) +- `--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 @@ -74,11 +74,9 @@ La commande `octane:frankenphp` peut prendre les options suivantes : - `--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 JSON structurés (utile lorsque vous utilisez des solutions d'analyse de logs), passez explicitement l'option `--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`. -Voir aussi [comment utiliser Mercure avec Octane](#mercure-support). - -En savoir plus sur [Laravel Octane dans sa documentation officielle](https://laravel.com/docs/octane). +En savoir plus sur Laravel Octane [dans sa documentation officielle](https://laravel.com/docs/octane). ## Les Applications Laravel En Tant Que Binaires Autonomes @@ -103,7 +101,7 @@ Suivez ces étapes pour empaqueter votre application Laravel en tant que binaire # Copiez le fichier .env RUN cp .env.example .env - # Modifiez APP_ENV et APP_DEBUG pour qu'ils soient prêts pour la production + # 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 @@ -111,7 +109,7 @@ Suivez ces étapes pour empaqueter votre application Laravel en tant que binaire # Installez les dépendances RUN composer install --ignore-platform-reqs --no-dev -a - # Construisez le binaire statique + # Construire le binaire statique WORKDIR /go/src/app/ RUN EMBED=dist/app/ ./build-static.sh ``` @@ -121,19 +119,19 @@ Suivez ces étapes pour empaqueter votre application Laravel en tant que binaire > 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. Construction : +2. Build: ```console docker build -t static-laravel-app -f static-build.Dockerfile . ``` -3. Extraction du binaire : +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. Population des caches : +4. Remplir les caches : ```console frankenphp php-cli artisan optimize @@ -145,7 +143,7 @@ Suivez ces étapes pour empaqueter votre application Laravel en tant que binaire frankenphp php-cli artisan migrate ``` -6. Génération de la clé secrète de l'application : +6. Générer la clé secrète de l'application : ```console frankenphp php-cli artisan key:generate @@ -169,34 +167,6 @@ Ceci n'est pas adapté aux applications embarquées, car chaque nouvelle version 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. -### Mercure Support - -[Mercure](https://mercure.rocks) est un excellent moyen d'ajouter des capacités en temps réel à vos applications Laravel. -FrankenPHP inclut le [support de Mercure nativement](mercure.md). - -Si vous n'utilisez pas [Octane](#laravel-octane), consultez l'[entrée de la documentation Mercure](mercure.md). - -Si vous utilisez Octane, vous pouvez activer le support de Mercure en ajoutant les lignes suivantes à votre fichier `config/octane.php` : - -```php -// ... - -return [ - // ... - - 'mercure' => [ - 'anonymous' => true, - 'publisher_jwt' => '!ChangeThisMercureHubJWTSecretKey!', - 'subscriber_jwt' => '!ChangeThisMercureHubJWTSecretKey!', - ], -]; -``` - -Vous pouvez utiliser [toutes les directives supportées par Mercure](https://mercure.rocks/docs/hub/config#directives) dans ce tableau. - -Pour publier et s'abonner aux mises à jour, nous recommandons d'utiliser la bibliothèque [Laravel Mercure Broadcaster](https://github.com/mvanduijker/laravel-mercure-broadcaster). -Alternativement, consultez [la documentation Mercure](mercure.md) pour le faire en PHP pur et JavaScript. - ### Exécuter Octane avec des binaires autonomes Il est même possible d'empaqueter les applications Laravel Octane en tant que binaires autonomes ! @@ -212,4 +182,4 @@ 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. +> car Octane a besoin d'un programme nommé `frankenphp` disponible dans le chemin diff --git a/docs/fr/logging.md b/docs/fr/logging.md deleted file mode 100644 index 40c3183c13..0000000000 --- a/docs/fr/logging.md +++ /dev/null @@ -1,67 +0,0 @@ -# Journalisation - -FrankenPHP s'intègre parfaitement au [système de journalisation de Caddy](https://caddyserver.com/docs/logging). Vous pouvez journaliser des messages en utilisant les fonctions PHP standard ou exploiter la fonction dédiée `frankenphp_log()` pour des capacités de journalisation structurée avancées. - -## `frankenphp_log()` - -La fonction `frankenphp_log()` vous permet d'émettre des journaux structurés directement depuis votre application PHP, facilitant grandement leur ingestion dans des plateformes comme Datadog, Grafana Loki ou Elastic, ainsi que le support d'OpenTelemetry. - -En interne, `frankenphp_log()` enveloppe le [package `log/slog` de Go](https://pkg.go.dev/log/slog) pour offrir des fonctionnalités de journalisation riches. - -Ces journaux incluent le niveau de sévérité et des données de contexte optionnelles. - -```php -function frankenphp_log(string $message, int $level = FRANKENPHP_LOG_LEVEL_INFO, array $context = []): void -``` - -### Paramètres - -- **`message`** : La chaîne de caractères du message de journal. -- **`level`** : Le niveau de sévérité du journal. Peut être n'importe quel entier arbitraire. Des constantes de commodité sont fournies pour les niveaux courants : `FRANKENPHP_LOG_LEVEL_DEBUG` (`-4`), `FRANKENPHP_LOG_LEVEL_INFO` (`0`), `FRANKENPHP_LOG_LEVEL_WARN` (`4`) et `FRANKENPHP_LOG_LEVEL_ERROR` (`8`)). La valeur par défaut est `FRANKENPHP_LOG_LEVEL_INFO`. -- **`context`** : Un tableau associatif de données additionnelles à inclure dans l'entrée du journal. - -### Exemple - -```php - memory_get_usage(), - 'peak_usage' => memory_get_peak_usage(), - ], -); - -``` - -Lorsque vous consultez les journaux (par exemple, via `docker compose logs`), la sortie apparaîtra sous forme de JSON structuré : - -```json -{"level":"info","ts":1704067200,"logger":"frankenphp","msg":"Hello from FrankenPHP!"} -{"level":"warn","ts":1704067200,"logger":"frankenphp","msg":"Memory usage high","current_usage":10485760,"peak_usage":12582912} -``` - -## `error_log()` - -FrankenPHP permet également la journalisation en utilisant la fonction standard `error_log()`. Si le paramètre `$message_type` est `4` (SAPI), ces messages sont acheminés vers le journaliseur de Caddy. - -Par défaut, les messages envoyés via `error_log()` sont traités comme du texte non structuré. Ils sont utiles pour la compatibilité avec les applications ou bibliothèques existantes qui s'appuient sur la bibliothèque PHP standard. - -### Exemple avec error_log() - -```php -error_log("Database connection failed", 4); -``` - -Ceci apparaîtra dans les journaux de Caddy, souvent préfixé pour indiquer qu'il provient de PHP. - -> [!TIP] -> Pour une meilleure observabilité dans les environnements de production, préférez `frankenphp_log()` -> car cela vous permet de filtrer les journaux par niveau (Débogage, Erreur, etc.) -> et d'interroger des champs spécifiques dans votre infrastructure de journalisation. diff --git a/docs/fr/mercure.md b/docs/fr/mercure.md index 908f6ce278..4a2af8a506 100644 --- a/docs/fr/mercure.md +++ b/docs/fr/mercure.md @@ -1,149 +1,12 @@ # Temps Réel -FrankenPHP est livré avec un hub [Mercure](https://mercure.rocks) intégré ! +FrankenPHP est livré avec un hub [Mercure](https://mercure.rocks) intégré. Mercure permet de pousser des événements en temps réel vers tous les appareils connectés : ils recevront un événement JavaScript instantanément. -C'est une alternative pratique aux WebSockets, simple à utiliser et nativement prise en charge par tous les navigateurs web modernes ! +Aucune bibliothèque JS ou SDK requis ! -![Mercure](mercure-hub.png) +![Mercure](../mercure-hub.png) -## Activer Mercure +Pour activer le hub Mercure, mettez à jour le `Caddyfile` comme décrit [sur le site de Mercure](https://mercure.rocks/docs/hub/config). -Le support de Mercure est désactivé par défaut. -Voici un exemple minimal de `Caddyfile` activant à la fois FrankenPHP et le hub Mercure : - -```caddyfile -# L'hostname auquel répondre -localhost - -mercure { - # La clé secrète utilisée pour signer les jetons JWT pour les éditeurs - publisher_jwt !ChangeThisMercureHubJWTSecretKey! - # Autorise les abonnés anonymes (sans JWT) - anonymous -} - -root public/ -php_server -``` - -> [!TIP] -> -> Le [fichier Caddyfile d'exemple](https://github.com/php/frankenphp/blob/main/caddy/frankenphp/Caddyfile) -> fourni par [les images Docker](docker.md) inclut déjà une configuration Mercure commentée -> avec des variables d'environnement pratiques pour le configurer. -> -> Décommentez la section Mercure dans `/etc/frankenphp/Caddyfile` pour l'activer. - -## S'abonner aux mises à jour - -Par défaut, le hub Mercure est disponible sur le chemin `/.well-known/mercure` de votre serveur FrankenPHP. -Pour vous abonner aux mises à jour, utilisez la classe JavaScript native [`EventSource`](https://developer.mozilla.org/docs/Web/API/EventSource) : - -```html - - -Exemple Mercure - -``` - -## Publier des mises à jour - -### Utilisation de `mercure_publish()` - -FrankenPHP fournit une fonction pratique `mercure_publish()` pour publier des mises à jour vers le hub Mercure intégré : - -```php - 'value'])); - -// Écrit dans les logs de FrankenPHP -error_log("update $updateID published", 4); -``` - -La signature complète de la fonction est : - -```php -/** - * @param string|string[] $topics - */ -function mercure_publish(string|array $topics, string $data = '', bool $private = false, ?string $id = null, ?string $type = null, ?int $retry = null): string {} -``` - -### Utilisation de `file_get_contents()` - -Pour distribuer une mise à jour aux abonnés connectés, envoyez une requête POST authentifiée au hub Mercure avec les paramètres `topic` et `data` : - -```php - [ - 'method' => 'POST', - 'header' => "Content-type: application/x-www-form-urlencoded\r\nAuthorization: Bearer " . JWT, - 'content' => http_build_query([ - 'topic' => 'my-topic', - 'data' => json_encode(['key' => 'value']), - ]), -]])); - -// Écrit dans les logs de FrankenPHP -error_log("update $updateID published", 4); -``` - -La clé passée en paramètre de l'option `mercure.publisher_jwt` dans le `Caddyfile` doit être utilisée pour signer le jeton JWT utilisé dans l'en-tête `Authorization`. - -Le JWT doit inclure une revendication `mercure` avec une permission `publish` pour les sujets auxquels vous souhaitez publier. -Consultez [la documentation Mercure](https://mercure.rocks/spec#publishers) concernant l'autorisation. - -Pour générer vos propres jetons, vous pouvez utiliser [ce lien jwt.io](https://www.jwt.io/#token=eyJhbGciOiJIUzI1NiJ9.eyJtZXJjdXJlIjp7InB1Ymxpc2giOlsiKiJdfX0.PXwpfIGng6KObfZlcOXvcnWCJOWTFLtswGI5DZuWSK4), -mais pour les applications en production, il est recommandé d'utiliser des jetons de courte durée générés dynamiquement à l'aide d'une [bibliothèque JWT](https://www.jwt.io/libraries?programming_language=php) fiable. - -### Utilisation de Symfony Mercure - -Alternativement, vous pouvez utiliser le [Composant Mercure de Symfony](https://symfony.com/components/Mercure), une bibliothèque PHP autonome. - -Cette bibliothèque gère la génération de JWT, la publication de mises à jour ainsi que l'autorisation basée sur les cookies pour les abonnés. - -Tout d'abord, installez la bibliothèque à l'aide de Composer : - -```console -composer require symfony/mercure lcobucci/jwt -``` - -Ensuite, vous pouvez l'utiliser comme ceci : - -```php -publish(new \Symfony\Component\Mercure\Update('my-topic', json_encode(['key' => 'value']))); - -// Écrit dans les logs de FrankenPHP -error_log("update $updateID published", 4); -``` - -Mercure est également pris en charge nativement par : - -- [Laravel](laravel.md#mercure-support) -- [Symfony](https://symfony.com/doc/current/mercure.html) -- [API Platform](https://api-platform.com/docs/core/mercure/) +Pour pousser des mises à jour Mercure depuis votre code, nous recommandons le [Composant Mercure de Symfony](https://symfony.com/components/Mercure) (vous n'avez pas besoin du framework full stack Symfony pour l'utiliser). diff --git a/docs/fr/metrics.md b/docs/fr/metrics.md index e0445a553d..697d2eebf1 100644 --- a/docs/fr/metrics.md +++ b/docs/fr/metrics.md @@ -5,13 +5,13 @@ Lorsque les [métriques Caddy](https://caddyserver.com/docs/metrics) sont activ - `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. +- `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, l'espace réservé `[nom_du_worker]` est remplacé par le nom du worker dans le Caddyfile, sinon le chemin absolu du fichier du worker sera utilisé. +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é. diff --git a/docs/fr/performance.md b/docs/fr/performance.md index a065ca48a0..e1531624a3 100644 --- a/docs/fr/performance.md +++ b/docs/fr/performance.md @@ -8,23 +8,23 @@ Cependant, il est possible d'améliorer considérablement les performances en ut 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. Pour une stabilité optimale du système, il est recommandé d'avoir `num_threads` x `memory_limit` < `available_memory`. +Nous recommandons vivement de modifier ces valeurs. Pour trouver les bonnes valeurs, il est souhaitable d'effectuer des tests de charge simulant le trafic réel. [k6](https://k6.io) et [Gatling](https://gatling.io) sont de bons outils pour cela. Pour configurer le nombre de threads, utilisez l'option `num_threads` des directives `php_server` et `php`. -Pour changer le nombre de workers, utilisez l'option `num` de la section `worker` de la directive `frankenphp`. +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. La [configuration](config.md#caddyfile-config) `max_threads` permet à FrankenPHP de créer automatiquement des threads supplémentaires au moment de l'exécution, jusqu'à la limite spécifiée. +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 worker et au 'mode classique' selon les besoins. +processus et les délègue automatiquement à différents scripts de travail et au `mode classique` selon les besoins. ## Mode worker @@ -34,16 +34,18 @@ vous devez créer un script worker et vous assurer que l'application n'a pas de ## Ne pas utiliser musl -La variante Alpine Linux des images Docker officielles et les binaires par défaut que nous fournissons utilisent [la bibliothèque musl](https://musl.libc.org). +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 [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. La différence peut être significative dans un environnement fortement threadé. +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. -De plus, [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). +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 d'utiliser FrankenPHP lié à glibc, compilé avec un niveau d'optimisation approprié. +Dans les environnements de production, nous recommandons fortement d'utiliser la glibc. -Cela peut être réalisé en utilisant les images Docker Debian, en utilisant nos paquets [.deb](https://debs.henderkes.com) ou [.rpm](https://rpms.henderkes.com) fournis par nos mainteneurs, ou en [compilant FrankenPHP à partir des sources](compile.md). +Cela peut être réalisé en utilisant les images Docker Debian (par défaut) et [en compilant FrankenPHP à partir des sources](compile.md). + +Alternativement, nous fournissons des binaires statiques compilés avec [l'allocateur mimalloc](https://github.com/microsoft/mimalloc), ce qui rend FrankenPHP+musl plus rapide (mais toujours plus lent que FrankenPHP+glibc). ## Configuration du runtime Go @@ -52,12 +54,12 @@ FrankenPHP est écrit en Go. En général, le runtime Go ne nécessite pas de configuration particulière, mais dans certaines circonstances, une configuration spécifique améliore les performances. -Vous voudrez probablement définir la variable d'environnement `GODEBUG` à `cgocheck=0` (la valeur par défaut dans les images Docker de FrankenPHP). +Vous voudrez probablement mettre la variable d'environnement `GODEBUG` à `cgocheck=0` (la valeur par défaut dans les images Docker de FrankenPHP). Si vous exécutez FrankenPHP dans des conteneurs (Docker, Kubernetes, LXC...) et que vous limitez la mémoire disponible pour les conteneurs, -définissez la variable d'environnement `GOMEMLIMIT` à la quantité de mémoire disponible. +mettez la variable d'environnement `GOMEMLIMIT` à la quantité de mémoire disponible. -Pour plus de détails, [la page de documentation Go dédiée à ce sujet](https://pkg.go.dev/runtime#hdr-Environment_Variables) est une lecture indispensable pour tirer le meilleur parti du runtime. +Pour plus de détails, [la page de documentation Go dédiée à ce sujet](https://pkg.go.dev/runtime#hdr-Environment_Variables) est à lire absolument pour tirer le meilleur parti du runtime. ## `file_server` @@ -75,22 +77,22 @@ php_server { ## `try_files` -Outre les fichiers statiques et les fichiers PHP, `php_server` essaiera également 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 d'indices de répertoire, +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 + 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ération inutile sur le système de fichiers serait d'utiliser la directive `php` -et de séparer 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#caddyfile-config) qui sert des fichiers statiques derrière un dossier `/assets` pourrait ressembler à ceci : +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 { @@ -103,25 +105,25 @@ route { root /root/to/your/app } - # tout ce qui n'est pas dans /assets est géré par votre index ou votre fichier PHP worker + # 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 + root /root/to/your/app # l'ajout explicite de la racine ici permet une meilleure mise en cache } } ``` -## Placeholders +## _Placeholders_ -Vous pouvez utiliser des [placeholders](https://caddyserver.com/docs/conventions#placeholders) dans les directives `root` et `env`. +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 que PHP fonctionne correctement). -Si le document root n'est pas un lien symbolique, vous pouvez désactiver cette fonctionnalité. +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 php_server { @@ -129,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. @@ -153,35 +155,3 @@ En particulier : 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). - -## Division du pool de threads - -Il est courant que les applications interagissent avec des services externes lents, comme une -API qui a tendance à être peu fiable sous une forte charge ou qui met constamment plus de 10 secondes à répondre. -Dans de tels cas, il peut être bénéfique de diviser le pool de threads pour avoir des pools "lents" dédiés. -Cela empêche les points d'accès lents de consommer toutes les ressources/threads du serveur et -limite la concurrence des requêtes allant vers le point d'accès lent, à l'instar d'un pool de connexions. - -```caddyfile -{ - frankenphp { - max_threads 100 # 100 threads maximum partagés par tous les workers - } -} - -example.com { - php_server { - root /app/public # la racine de votre application - worker index.php { - match /slow-endpoint/* # toutes les requêtes avec le chemin /slow-endpoint/* sont gérées par ce pool de threads - num 10 # minimum 10 threads pour les requêtes correspondant à /slow-endpoint/* - } - worker index.php { - match * # toutes les autres requêtes sont gérées séparément - num 20 # minimum 20 threads pour les autres requêtes, même si les points d'accès lents commencent à bloquer - } - } -} -``` - -De manière générale, il est également conseillé de gérer les points d'accès très lents de manière asynchrone, en utilisant des mécanismes pertinents tels que les files de messages. diff --git a/docs/fr/production.md b/docs/fr/production.md index 50278b3140..7e1606c50e 100644 --- a/docs/fr/production.md +++ b/docs/fr/production.md @@ -2,7 +2,7 @@ Dans ce tutoriel, nous apprendrons comment déployer une application PHP sur un serveur unique en utilisant Docker Compose. -Si vous utilisez Symfony, préférez lire l'entrée de documentation "[Déployer en production](https://github.com/dunglas/symfony-docker/blob/main/docs/production.md)" du projet Symfony Docker (qui utilise FrankenPHP). +Si vous utilisez Symfony, lisez plutôt la page de documentation "[Déployer en production](https://github.com/dunglas/symfony-docker/blob/main/docs/production.md)" du projet Symfony Docker (qui utilise FrankenPHP). Si vous utilisez API Platform (qui utilise également FrankenPHP), référez-vous à [la documentation de déploiement du framework](https://api-platform.com/docs/deployment/). @@ -65,7 +65,7 @@ volumes: > (qui utilise FrankenPHP) pour un exemple plus avancé utilisant des images multi-étapes, > Composer, des extensions PHP supplémentaires, etc. -Enfin, si vous utilisez Git, commitez ces fichiers et poussez-les. +Pour finir, si vous utilisez Git, commitez ces fichiers et poussez-les. ## Préparer un serveur @@ -73,14 +73,14 @@ Pour déployer votre application en production, vous avez besoin d'un serveur. Dans ce tutoriel, nous utiliserons une machine virtuelle fournie par DigitalOcean, mais n'importe quel serveur Linux peut fonctionner. Si vous avez déjà un serveur Linux avec Docker installé, vous pouvez passer directement à [la section suivante](#configurer-un-nom-de-domaine). -Sinon, utilisez [ce lien affilié](https://m.do.co/c/5d8aabe3ab80) pour obtenir 200 $ de crédit gratuit, créez un compte, puis cliquez sur "Create a Droplet". -Ensuite, cliquez sur l'onglet "Marketplace" sous la section "Choose an image" et recherchez l'application nommée "Docker". +Sinon, utilisez [ce lien affilié](https://m.do.co/c/5d8aabe3ab80) pour obtenir 200$ de crédit gratuit, créez un compte, puis cliquez sur "Créer un Droplet". +Ensuite, cliquez sur l'onglet "Marketplace" sous la section "Choisir une image" et recherchez l'application nommée "Docker". Cela provisionnera un serveur Ubuntu avec les dernières versions de Docker et Docker Compose déjà installées ! Pour des fins de test, les plans les moins chers seront suffisants. -Pour une utilisation en production réelle, vous voudrez probablement choisir un plan dans la section "general purpose" pour répondre à vos besoins. +Pour une utilisation en production réelle, vous voudrez probablement choisir un plan dans la section "General Usage" pour répondre à vos besoins. -![Déployer FrankenPHP sur DigitalOcean avec Docker](digitalocean-droplet.png) +![Déployer FrankenPHP sur DigitalOcean avec Docker](../digitalocean-droplet.png) Vous pouvez conserver les paramètres par défaut pour les autres paramètres, ou les ajuster selon vos besoins. N'oubliez pas d'ajouter votre clé SSH ou de créer un mot de passe puis appuyez sur le bouton "Finalize and create". @@ -105,7 +105,7 @@ your-domain-name.example.com. IN A 207.154.233.113 Exemple avec le service DigitalOcean Domains ("Networking" > "Domains") : -![Configurer les DNS sur DigitalOcean](digitalocean-dns.png) +![Configurer les DNS sur DigitalOcean](../digitalocean-dns.png) > [!NOTE] > @@ -114,7 +114,7 @@ Exemple avec le service DigitalOcean Domains ("Networking" > "Domains") : ## Déploiement Copiez votre projet sur le serveur en utilisant `git clone`, `scp`, ou tout autre outil qui pourrait répondre à votre besoin. -Si vous utilisez GitHub, vous voudrez peut-être utiliser [une clé de déploiement](https://docs.github.com/en/free-pro-team@latest/developers/overview/managing-deploy-keys#deploy-keys). +Si vous utilisez GitHub, vous voudrez peut-être utiliser [une clef de déploiement](https://docs.github.com/en/free-pro-team@latest/developers/overview/managing-deploy-keys#deploy-keys). Les clés de déploiement sont également [prises en charge par GitLab](https://docs.gitlab.com/ee/user/project/deploy_keys/). Exemple avec Git : @@ -126,11 +126,11 @@ git clone git@github.com:/.git Accédez au répertoire contenant votre projet (``), et démarrez l'application en mode production : ```console -docker compose up --wait +docker compose up -d --wait ``` Votre serveur est opérationnel, et un certificat HTTPS a été automatiquement généré pour vous. -Rendez-vous sur `https://your-domain-name.example.com` et profitez-en ! +Rendez-vous sur `https://your-domain-name.example.com` ! > [!CAUTION] > diff --git a/docs/fr/static.md b/docs/fr/static.md index 76db2698a9..1efebb18df 100644 --- a/docs/fr/static.md +++ b/docs/fr/static.md @@ -1,16 +1,15 @@ # Créer un binaire statique -Au lieu d'utiliser une installation locale de la bibliothèque PHP, -il est possible de créer un build statique ou principalement statique de FrankenPHP grâce à l'excellent projet [static-php-cli](https://github.com/crazywhalecc/static-php-cli) (malgré son nom, ce projet prend en charge tous les SAPIs, pas seulement CLI). +Au lieu d'utiliser une installation locale de la bibliothèque PHP, il est possible de créer un build statique de FrankenPHP grâce à l'excellent projet [static-php-cli](https://github.com/crazywhalecc/static-php-cli) (malgré son nom, ce projet prend en charge tous les SAPIs, pas seulement CLI). 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 libc musl. +Cependant, ils ne peuvent pas charger les extensions dynamiques de PHP (comme Xdebug) et ont quelques limitations parce qu'ils utilisent la librairie musl. -Les binaires principalement statiques ne nécessitent que la `glibc` et peuvent charger des extensions dynamiques. +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 constructions principalement statiques basées sur glibc. +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). @@ -62,7 +61,7 @@ docker buildx bake --load --set static-builder-musl.args.PHP_EXTENSIONS=opcache, # ... ``` -Pour ajouter des bibliothèques permettant des fonctionnalités supplémentaires aux extensions que vous avez activées, vous pouvez passer l'argument Docker `PHP_EXTENSION_LIBS` : +Pour ajouter des bibliothèques permettant des fonctionnalités supplémentaires aux extensions que vous avez activées, vous pouvez utiliser l'argument Docker `PHP_EXTENSION_LIBS` : ```console docker buildx bake \ @@ -111,7 +110,7 @@ cd frankenphp ./build-static.sh ``` -Note : ce script fonctionne également sur Linux (et probablement sur d'autres Unix) et est utilisé en interne par les images Docker que nous fournissons. +Note : ce script fonctionne également sur Linux (et probablement sur d'autres Unix) et est utilisé en interne par le builder statique basé sur Docker que nous fournissons. ## Personnalisation de la construction @@ -124,9 +123,9 @@ Les variables d'environnement suivantes peuvent être transmises à `docker buil - `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) -- `NO_COMPRESS` : ne pas compresser le binaire résultant avec UPX -- `DEBUG_SYMBOLS` : lorsque défini, les symboles de débogage ne seront pas supprimés et seront ajoutés au binaire -- `MIMALLOC` : (expérimental, Linux seulement) remplace l'allocateur mallocng de musl par [mimalloc](https://github.com/microsoft/mimalloc) pour des performances améliorées. Nous recommandons d'utiliser cette option uniquement pour les constructions ciblant musl ; pour glibc, il est préférable de la désactiver et d'utiliser [`LD_PRELOAD`](https://microsoft.github.io/mimalloc/overrides.html) lors de l'exécution de votre binaire. +- `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 diff --git a/docs/fr/wordpress.md b/docs/fr/wordpress.md deleted file mode 100644 index 741ce5be7d..0000000000 --- a/docs/fr/wordpress.md +++ /dev/null @@ -1,59 +0,0 @@ -# WordPress - -Exécutez [WordPress](https://wordpress.org/) avec FrankenPHP pour profiter d'une pile moderne et performante avec HTTPS automatique, HTTP/3 et la compression Zstandard. - -## Installation Minimale - -1. [Téléchargez WordPress](https://wordpress.org/download/) -2. Extrayez l'archive ZIP et ouvrez un terminal dans le répertoire extrait -3. Exécutez : - - ```console - frankenphp php-server - ``` - -4. Allez sur `http://localhost/wp-admin/` et suivez les instructions d'installation -5. Profitez-en ! - -Pour une configuration prête pour la production, préférez utiliser `frankenphp run` avec un `Caddyfile` comme celui-ci : - -```caddyfile -example.com - -php_server -encode zstd br gzip -log -``` - -## Rechargement à chaud - -Pour utiliser la fonctionnalité de [rechargement à chaud](hot-reload.md) avec WordPress, activez [Mercure](mercure.md) et ajoutez la sous-directive `hot_reload` à la directive `php_server` dans votre `Caddyfile` : - -```caddyfile -localhost - -mercure { - anonymous -} - -php_server { - hot_reload -} -``` - -Ensuite, ajoutez le code nécessaire pour charger les bibliothèques JavaScript dans le fichier `functions.php` de votre thème WordPress : - -```php -function hot_reload() { - ?> - - - - - - [!TIP] @@ -81,17 +79,11 @@ require __DIR__.'/vendor/autoload.php'; $myApp = new \App\Kernel(); $myApp->boot(); -// Gestionnaire en dehors de la boucle pour de meilleures performances (moins de travail effectué) +// En dehors de la boucle pour de meilleures performances (moins de travail effectué) $handler = static function () use ($myApp) { - try { - // Appelé lorsqu'une requête est reçue, - // les superglobales, php://input, etc., sont réinitialisés - echo $myApp->handle($_GET, $_POST, $_COOKIE, $_FILES, $_SERVER); - } catch (\Throwable $exception) { - // `set_exception_handler` n'est appelé que lorsque le script worker se termine, - // ce qui n'est peut-être pas ce à quoi vous vous attendez, alors attrapez et gérez les exceptions ici - (new \MyCustomExceptionHandler)->handleException($exception); - } + // Appelé lorsqu'une requête est reçue, + // les superglobales, php://input, etc., sont réinitialisés + echo $myApp->handle($_GET, $_POST, $_COOKIE, $_FILES, $_SERVER); }; $maxRequests = (int)($_SERVER['MAX_REQUESTS'] ?? 0); @@ -141,23 +133,27 @@ Le code du worker précédent permet de configurer un nombre maximal de requête ### Redémarrer les workers manuellement -Bien qu'il soit possible de redémarrer les workers [en cas de changement de fichier](config.md#watching-for-file-changes), +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#caddyfile-config), vous pouvez envoyer un ping +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 ``` -### Échecs des workers +> [!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`. +(par exemple, une faute de frappe dans un script), FrankenPHP plantera avec l'erreur : `too many consecutive failures` (trop d'échecs consécutifs). -Le nombre d'échecs consécutifs peut être configuré dans votre [Caddyfile](config.md#caddyfile-config) avec l'option `max_consecutive_failures` : +Le nombre d'échecs consécutifs peut être configuré dans votre [Caddyfile](config.md#configuration-du-caddyfile) avec l'option `max_consecutive_failures` : ```caddyfile frankenphp { @@ -189,3 +185,4 @@ $handler = static function () use ($workerServer) { }; // ... +``` diff --git a/docs/fr/x-sendfile.md b/docs/fr/x-sendfile.md index 449b643f9a..1269436a89 100644 --- a/docs/fr/x-sendfile.md +++ b/docs/fr/x-sendfile.md @@ -27,7 +27,7 @@ Tout d'abord, ajoutez la configuration suivante à votre `Caddyfile` pour active root public/ # ... -+ # Nécessaire pour Symfony, Laravel et d'autres projets utilisant le composant Symfony HttpFoundation ++ # 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 + @@ -38,7 +38,7 @@ Tout d'abord, ajoutez la configuration suivante à votre `Caddyfile` pour active + rewrite * {resp.header.X-Accel-Redirect} + method * GET + -+ # Supprime l'en-tête X-Accel-Redirect défini par PHP pour une sécurité accrue ++ # Remove the X-Accel-Redirect header set by PHP for increased security + header -X-Accel-Redirect + + file_server @@ -53,7 +53,7 @@ Tout d'abord, ajoutez la configuration suivante à votre `Caddyfile` pour active 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'); +header('X-Accel-Redirect: file.txt') ; ``` ## Projets utilisant le composant Symfony HttpFoundation (Symfony, Laravel, Drupal...) @@ -68,3 +68,4 @@ BinaryFileResponse::trustXSendfileTypeHeader(); $response = new BinaryFileResponse(__DIR__.'/../private-files/file.txt'); // ... +``` diff --git a/docs/ja/classic.md b/docs/ja/classic.md index 2fec116e17..17e99641ad 100644 --- a/docs/ja/classic.md +++ b/docs/ja/classic.md @@ -5,7 +5,7 @@ Caddyと同様に、FrankenPHPは無制限の接続を受け付け、[固定数のスレッド](config.md#caddyfile-config)でそれらを処理します。受け入れられキューに入れられる接続の数は、利用可能なシステムリソースによってのみ制限されます。 PHPスレッドプールは、起動時に初期化された固定数のスレッドで動作し、これはPHP-FPMの静的モードに相当します。また、PHP-FPMの動的モードと同様に、[実行時にスレッドを自動的にスケール](performance.md#max_threads)させることも可能です。 -キューに入った接続は、PHPスレッドが空くまで無期限に待機します。これを避けるために、FrankenPHP のグローバル設定内の `max_wait_time` [設定](config.md#caddyfile-config)を使って、リクエストが空きPHPスレッドを待てる最大時間を制限し、それを超えるとリクエストが拒否されるようにできます。 +キューに入った接続は、PHPスレッドが空くまで無期限に待機します。これを避けるために、FrankenPHP のグローバル設定内の `max_wait_time` [設定](config.md#caddyfile-config)を使って、リクエストが空きスレッドを待てる最大時間を制限し、それを超えるとリクエストが拒否されるようにできます。 加えて、[Caddy側で適切な書き込みタイムアウト](https://caddyserver.com/docs/caddyfile/options#timeouts)を設定することも可能です。 各Caddyインスタンスは、1つのFrankenPHPスレッドプールのみを起動し、すべての`php_server`ブロック間でこのプールを共有します。 diff --git a/docs/ja/compile.md b/docs/ja/compile.md index 40f42b23db..03e7d6217a 100644 --- a/docs/ja/compile.md +++ b/docs/ja/compile.md @@ -15,7 +15,7 @@ FrankenPHPと互換性のあるlibphpのバージョンをインストールす まず、まだインストールしていない場合は[Homebrew](https://brew.sh)をインストールしてください。 -次に、PHPのZTSバリアント、Brotli(オプション、圧縮サポート用)、およびwatcher(オプション、ファイル変更検出用)をインストールします: +次に、PHPのZTSバリアント、Brotli(オプション、圧縮サポート用)、watcher(オプション、ファイル変更検出用)をインストールします: ```console brew install shivammathur/php/php-zts brotli watcher @@ -24,7 +24,7 @@ brew link --overwrite --force shivammathur/php/php-zts ### PHPをコンパイルする場合 -別の方法として、FrankenPHPに必要なオプションを指定してPHPをソースからコンパイルすることもできます。以下の手順に従ってください。 +別の方法として、FrankenPHPに必要なオプションを指定してPHPをソースからコンパイルすることもできます。 まず、[PHPのソース](https://www.php.net/downloads.php)を取得して展開します: @@ -34,7 +34,7 @@ cd php-*/ ``` 次に、プラットフォームに応じて必要なオプションを指定して`configure`スクリプトを実行します。 -以下の`./configure`フラグは必須ですが、例えば拡張機能や追加機能をコンパイルするために他のフラグを追加することもできます。 +以下の`./configure`フラグは必須ですが、例えば拡張機能モジュールや追加機能をコンパイルするために他のフラグを追加することもできます。 #### Linux @@ -55,7 +55,7 @@ brew install libiconv bison brotli re2c pkg-config watcher echo 'export PATH="/opt/homebrew/opt/bison/bin:$PATH"' >> ~/.zshrc ``` -その後、`configure`スクリプトを実行します: +その後、以下のようにconfigureスクリプトを実行します: ```console ./configure \ @@ -76,20 +76,19 @@ sudo make install ## オプション依存関係のインストール -FrankenPHPの一部の機能は、システムにインストールされているオプションの依存関係に依存しています。 +FrankenPHPの一部の機能は、システムにインストールされているオプションの依存パッケージに依存しています。 または、Goコンパイラにビルドタグを渡すことで、これらの機能を無効にできます。 -| 機能 | 依存関係 | 無効にするためのビルドタグ | -| :----------------------------- | :----------------------------------------------------------------------------------------------------------- | :------------------------- | -| Brotli圧縮 | [Brotli](https://github.com/google/brotli) | `nobrotli` | -| ファイル変更時のワーカー再起動 | [Watcher C](https://github.com/e-dant/watcher/tree/release/watcher-c) | `nowatcher` | -| [Mercure](mercure.md) | [Mercure Goライブラリ](https://pkg.go.dev/github.com/dunglas/mercure)(自動インストール、AGPLライセンス) | `nomercure` | +| 機能 | 依存関係 | 無効にするためのビルドタグ | +| ------------------------------ | --------------------------------------------------------------------- | -------------------------- | +| Brotli圧縮 | [Brotli](https://github.com/google/brotli) | nobrotli | +| ファイル変更時のワーカー再起動 | [Watcher C](https://github.com/e-dant/watcher/tree/release/watcher-c) | nowatcher | ## Goアプリのコンパイル -これで最終的なバイナリをビルドできます。 +いよいよ最終的なバイナリをビルドできるようになりました。 -### `xcaddy`を使う場合 +### xcaddyを使う場合 推奨される方法は、[xcaddy](https://github.com/caddyserver/xcaddy)を使用してFrankenPHPをコンパイルする方法です。 `xcaddy`を使うと、[Caddyのカスタムモジュール](https://caddyserver.com/docs/modules/)やFrankenPHP拡張を簡単に追加できます: @@ -103,18 +102,13 @@ xcaddy build \ --output frankenphp \ --with github.com/dunglas/frankenphp/caddy \ --with github.com/dunglas/mercure/caddy \ - --with github.com/dunglas/vulcain/caddy \ - --with github.com/dunglas/caddy-cbrotli + --with github.com/dunglas/vulcain/caddy # 追加のCaddyモジュールとFrankenPHP拡張をここに追加 - # オプションで、FrankenPHPのソースからコンパイルしたい場合: - # --with github.com/dunglas/frankenphp=$(pwd) \ - # --with github.com/dunglas/frankenphp/caddy=$(pwd)/caddy - ``` > [!TIP] > -> `musl libc`(Alpine Linuxのデフォルト)とSymfonyを使用している場合、 +> musl libc(Alpine Linuxのデフォルト)とSymfonyを使用している場合、 > デフォルトのスタックサイズを増やす必要がある場合があります。 > そうしないと、`PHP Fatal error: Maximum call stack size of 83360 bytes reached during compilation. Try splitting expression`のようなエラーが発生する可能性があります。 > @@ -122,7 +116,7 @@ xcaddy build \ > `XCADDY_GO_BUILD_FLAGS=$'-ldflags "-w -s -extldflags \'-Wl,-z,stack-size=0x80000\'"'`のようなものに変更してください > (アプリの要件に応じてスタックサイズの値を変更してください)。 -### `xcaddy`を使用しない場合 +### xcaddyを使用しない場合 代替として、`xcaddy`を使わずに`go`コマンドを直接使ってFrankenPHPをコンパイルすることも可能です: diff --git a/docs/ja/config.md b/docs/ja/config.md index 1de0a942f5..f11e117a5f 100644 --- a/docs/ja/config.md +++ b/docs/ja/config.md @@ -1,76 +1,44 @@ # 設定 -FrankenPHP、Caddy、そして[Mercure](mercure.md)や[Vulcain](https://vulcain.rocks)モジュールは、[Caddyでサポートされる形式](https://caddyserver.com/docs/getting-started#your-first-config)を使用して設定できます。 +FrankenPHP、Caddy、そしてMercureやVulcainモジュールは、[Caddyでサポートされる形式](https://caddyserver.com/docs/getting-started#your-first-config)を使用して設定できます。 -最も一般的な形式は`Caddyfile`で、これはシンプルで人間が読みやすいテキスト形式です。 -デフォルトでは、FrankenPHPは現在のディレクトリで`Caddyfile`を探します。 -`-c`または`--config`オプションでカスタムパスを指定できます。 +[Dockerイメージ](docker.md)では、`Caddyfile`は`/etc/frankenphp/Caddyfile`に配置されています。 +静的バイナリは、`frankenphp run`コマンドを実行したディレクトリ内の`Caddyfile`を参照します。 +また、`-c`または`--config`オプションでカスタムのパスを指定できます。 -PHPアプリケーションを配信するための最小限の`Caddyfile`を以下に示します: +PHP自体の設定は[`php.ini` ファイルを使用](https://www.php.net/manual/en/configuration.file.php)して行えます。 -```caddyfile -# The hostname to respond to -localhost - -# Optionaly, the directory to serve files from, otherwise defaults to the current directory -#root public/ -php_server -``` - -より多くの機能を有効にし、便利な環境変数を提供する、より高度な`Caddyfile`は、[FrankenPHPリポジトリ](https://github.com/php/frankenphp/blob/main/caddy/frankenphp/Caddyfile)およびDockerイメージに含まれています。 - -PHP自体は、[`php.ini`ファイルを使用](https://www.php.net/manual/en/configuration.file.php)して設定できます。 - -インストール方法に応じて、FrankenPHPとPHPインタープリターは、以下に説明する場所で設定ファイルを探します。 +インストール方法に応じて、PHPインタープリターは上記いずれかの場所にある設定ファイルを参照します。 ## Docker -FrankenPHP: - -- `/etc/frankenphp/Caddyfile`: メイン設定ファイル -- `/etc/frankenphp/Caddyfile.d/*.caddyfile`: 自動的に読み込まれる追加の設定ファイル - -PHP: - - `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-/` +- PHP拡張モジュール: `/usr/local/lib/php/extensions/no-debug-zts-/` - PHPプロジェクトが提供する公式テンプレートをコピーすることを推奨します: ```dockerfile FROM dunglas/frankenphp -# Production: +# 本番環境: RUN cp $PHP_INI_DIR/php.ini-production $PHP_INI_DIR/php.ini -# Or development: +# または開発環境: RUN cp $PHP_INI_DIR/php.ini-development $PHP_INI_DIR/php.ini ``` ## RPMおよびDebianパッケージ -FrankenPHP: - -- `/etc/frankenphp/Caddyfile`: メイン設定ファイル -- `/etc/frankenphp/Caddyfile.d/*.caddyfile`: 自動的に読み込まれる追加の設定ファイル - -PHP: - -- `php.ini`: `/etc/php-zts/php.ini`(本番環境向けのプリセットの`php.ini`ファイルがデフォルトで提供されます) -- 追加の設定ファイル: `/etc/php-zts/conf.d/*.ini` +- `php.ini`: `/etc/frankenphp/php.ini`(本番環境向けのプリセットの`php.ini`ファイルがデフォルトで提供されます) +- 追加の設定ファイル: `/etc/frankenphp/php.d/*.ini` +- PHP拡張モジュール: `/usr/lib/frankenphp/modules/` ## 静的バイナリ -FrankenPHP: - -- 現在の作業ディレクトリ: `Caddyfile` - -PHP: - -- `php.ini`: `frankenphp run`または`frankenphp php-server`が実行されたディレクトリ、次に`/etc/frankenphp/php.ini` +- `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`のいずれかをコピーしてください。 +- PHP拡張モジュール: ロードできません、バイナリ自体にバンドルする必要があります +- [PHPソース](https://github.com/php/php-src/)で提供される`php.ini-production`または`php.ini-development`のいずれかをコピーしてください ## Caddyfileの設定 @@ -80,14 +48,15 @@ PHP: ```caddyfile localhost { - # Enable compression (optional) + # 圧縮を有効化(オプション) encode zstd br gzip - # Execute PHP files in the current directory and serve assets + # 現在のディレクトリ内のPHPファイルを実行し、アセットを配信 php_server } ``` -FrankenPHPを明示的に設定するには、`frankenphp`の[グローバルオプション](https://caddyserver.com/docs/caddyfile/concepts#global-options)を使用します: +グローバルオプションを使用してFrankenPHPを明示的に設定することもできます: +`frankenphp`の[グローバルオプション](https://caddyserver.com/docs/caddyfile/concepts#global-options)を使用してFrankenPHPを構成できます。 ```caddyfile { @@ -153,13 +122,13 @@ other.example.com { ```caddyfile route { - # Add trailing slash for directory requests + # ディレクトリへのリクエストに末尾スラッシュを追加 @canonicalPath { file {path}/index.php not path */ } redir @canonicalPath {path}/ 308 - # If the requested file does not exist, try index files + # 要求されたファイルが存在しない場合は、indexファイルを試行 @indexFiles file { try_files {path} {path}/index.php index.php split_path .php @@ -183,7 +152,7 @@ php_server [] { file_server off # 組み込みのfile_serverディレクティブを無効にします。 worker { # このサーバー固有のワーカーを作成します。複数のワーカーに対して複数回指定できます。 file # ワーカースクリプトへのパスを設定します。php_serverのルートからの相対パスとなります。 - num # 起動するPHPスレッド数を設定します。デフォルトは利用可能なCPU数の 2 倍です。 + num # 起動するPHPスレッド数を設定します。デフォルトは利用可能なスレッド数の 2 倍です。 name # ログとメトリクスで使用されるワーカーの名前を設定します。デフォルト: ワーカーファイルの絶対パス。php_server ブロックで定義されている場合は、常にm#で始まります。 watch # ファイルの変更を監視するパスを設定する。複数のパスに対して複数回指定することができる。 env # 追加の環境変数を指定された値に設定する。複数の環境変数を指定する場合は、複数回指定することができます。このワーカーの環境変数もphp_serverの親から継承されますが、 ここで上書きすることもできます。 @@ -212,10 +181,8 @@ PHPファイルに変更を加えても即座には反映されません。 } ``` -この機能は、[ホットリロード](hot-reload.md)と組み合わせてよく使用されます。 - -`watch`ディレクトリが指定されていない場合、`./**/*.{env,php,twig,yaml,yml}`にフォールバックします。 -これは、FrankenPHPプロセスが開始されたディレクトリおよびそのサブディレクトリ内のすべての`.env`、`.php`、`.twig`、`.yaml`、`.yml`ファイルを監視します。 +`watch`ディレクトリが指定されていない場合、`./**/*.{php,yaml,yml,twig,env}`にフォールバックします。 +これは、FrankenPHPプロセスが開始されたディレクトリおよびそのサブディレクトリ内のすべての`.php`、`.yaml`、`.yml`、`.twig`、`.env`ファイルすべてを監視します。 代わりに、[シェルのファイル名パターン](https://pkg.go.dev/path/filepath#Match)を使用して 1つ以上のディレクトリを指定することもできます: @@ -263,6 +230,34 @@ PHPファイルに変更を加えても即座には反映されません。 } ``` +### フルデュプレックス(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`を直接変更せずにCaddyディレクティブを注入できます: @@ -289,7 +284,7 @@ FPM や CLI SAPI と同様に、環境変数はデフォルトで`$_SERVER`ス frankenphp { php_ini memory_limit 256M - # or + # または php_ini { memory_limit 256M @@ -299,43 +294,6 @@ FPM や CLI SAPI と同様に、環境変数はデフォルトで`$_SERVER`ス } ``` -### HTTPSの無効化 - -デフォルトでは、FrankenPHPは`localhost`を含むすべてのホスト名に対してHTTPSを自動的に有効にします。 -HTTPSを無効にしたい場合(例えば開発環境で)、`SERVER_NAME`環境変数を`http://`または`:80`に設定できます: - -あるいは、[Caddyドキュメント](https://caddyserver.com/docs/automatic-https#activation)に記載されている他のすべての方法を使用することもできます。 - -`localhost`ホスト名ではなく`127.0.0.1`IPアドレスでHTTPSを使用したい場合は、[既知の問題](known-issues.md#using-https127001-with-docker)セクションをご覧ください。 - -### フルデュプレックス(HTTP/1) - -HTTP/1.xを使用する場合、全体のボディが読み取られる前にレスポンスを書き込めるようにするため、 -フルデュプレックスモードを有効にすることが望ましい場合があります(例:[Mercure](mercure.md)、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)をご覧ください。 - ## デバッグモードの有効化 Dockerイメージを使用する場合、`CADDY_GLOBAL_OPTIONS`環境変数に`debug`を設定するとデバッグモードが有効になります: diff --git a/docs/ja/docker.md b/docs/ja/docker.md index 1d8ff22721..0cf141bfc4 100644 --- a/docs/ja/docker.md +++ b/docs/ja/docker.md @@ -1,14 +1,13 @@ # カスタムDockerイメージのビルド -[FrankenPHPのDockerイメージ](https://hub.docker.com/r/dunglas/frankenphp)は、[公式PHPイメージ](https://hub.docker.com/_/php/)をベースにしています。 -主要なアーキテクチャに対してDebianとAlpine Linuxのバリアントを提供しており、Debianバリアントの使用を推奨しています。 +[FrankenPHPのDockerイメージ](https://hub.docker.com/r/dunglas/frankenphp)は、[公式PHPイメージ](https://hub.docker.com/_/php/)をベースにしています。主要なアーキテクチャに対してDebianとAlpine Linuxのバリアントを提供しており、Debianバリアントの使用を推奨しています。 PHP 8.2、8.3、8.4、8.5向けのバリアントが提供されています。 タグは次のパターンに従います:`dunglas/frankenphp:-php-` - ``および``は、それぞれFrankenPHPおよびPHPのバージョン番号で、メジャー(例:`1`)、マイナー(例:`1.2`)からパッチバージョン(例:`1.2.3`)まであります。 -- ``は`trixie`(Debian Trixie用)、`bookworm`(Debian Bookworm用)、または`alpine`(Alpine最新安定版用)のいずれかです。 +- ``は`bookworm`(Debian Bookworm用)または`alpine`(Alpine最新安定版用)のいずれかです。 [タグを閲覧](https://hub.docker.com/r/dunglas/frankenphp/tags)。 @@ -29,10 +28,6 @@ docker build -t my-php-app . docker run -it --rm --name my-running-app my-php-app ``` -## 設定を微調整する方法 - -利便性のため、有用な環境変数を含む[デフォルトの`Caddyfile`](https://github.com/php/frankenphp/blob/main/caddy/frankenphp/Caddyfile)がイメージに含まれています。 - ## PHP拡張モジュールの追加インストール方法 ベースイメージには[`docker-php-extension-installer`](https://github.com/mlocati/docker-php-extension-installer)スクリプトが含まれており、 @@ -161,8 +156,8 @@ RUN \ useradd ${USER}; \ # ポート 80 や 443 にバインドするための追加ケーパビリティを追加 setcap CAP_NET_BIND_SERVICE=+eip /usr/local/bin/frankenphp; \ - # /config/caddy および /data/caddy への書き込み権限を付与 - chown -R ${USER}:${USER} /config/caddy /data/caddy + # /data/caddy および /config/caddy への書き込み権限を付与 + chown -R ${USER}:${USER} /data/caddy && chown -R ${USER}:${USER} /config/caddy USER ${USER} ``` @@ -185,8 +180,8 @@ RUN \ useradd ${USER}; \ # デフォルトのケーパビリティを削除 setcap -r /usr/local/bin/frankenphp; \ - # /config/caddy と /data/caddy への書き込み権限を付与 - chown -R ${USER}:${USER} /config/caddy /data/caddy + # /data/caddy と /config/caddy への書き込み権限を付与 + chown -R ${USER}:${USER} /data/caddy && chown -R ${USER}:${USER} /config/caddy USER ${USER} ``` @@ -207,4 +202,4 @@ Dockerイメージは以下のタイミングでビルドされます: GitHubリポジトリのmainブランチにコミットがpushされるたびに新しいビルドが実行されます。 `latest*`タグは`main`ブランチのヘッドを指しており、 -`sha-` 形式のタグも利用可能です。 \ No newline at end of file +`sha-` 形式のタグも利用可能です。 diff --git a/docs/ja/extensions.md b/docs/ja/extensions.md index fde719ae47..9c140a5be6 100644 --- a/docs/ja/extensions.md +++ b/docs/ja/extensions.md @@ -10,8 +10,8 @@ Caddyモジュールのおかげで、GoでPHP拡張モジュールを書いてF FrankenPHPでは、GoでPHP拡張モジュールを作成する2つの方法を提供します: -1. **拡張モジュールジェネレーターを使用** - ほとんどのユースケースに必要なボイラープレートを自動生成する推奨アプローチで、Goコードの記述に集中できます -2. **手動実装** - 拡張モジュール構造を細かく制御したい高度なユースケース +1. **拡張モジュールジェネレーターを使用** - ほとんどのユースケースに必要なボイラープレートを自動生成する推奨アプローチで、Goコードの記述に集中できます +2. **手動実装** - 拡張モジュール構造を細かく制御したい高度なユースケース 最初に始めやすいジェネレーター方式を紹介し、その後で完全な制御が必要な場合の手動実装方式を説明します。 @@ -33,7 +33,7 @@ FrankenPHPにはGoのみを使用して**PHP拡張モジュールを作成する GoでPHP拡張モジュールを書く最初のステップは、新しいGoモジュールの作成です。以下のコマンドを使用できます: ```console -go mod init example.com/example +go mod init github.com/my-account/my-module ``` 2番目のステップは、次のステップのために[PHPのソースを取得](https://www.php.net/downloads.php)することです。取得したら、Goモジュールのディレクトリ内ではなく、任意のディレクトリに展開します: @@ -47,15 +47,10 @@ tar xf php-* これでGoでネイティブ関数を書く準備が整いました。`stringext.go`という名前の新しいファイルを作成します。最初の関数は文字列を引数として取り、それを指定された回数だけ繰り返し、文字列を逆転するかどうかを示すブール値を受け取り、結果の文字列を返します。これは以下のようになります: ```go -package example - -// #include -import "C" import ( + "C" + "github.com/dunglas/frankenphp" "strings" - "unsafe" - - "github.com/dunglas/frankenphp" ) //export_php:function repeat_this(string $str, int $count, bool $reverse): string @@ -86,190 +81,31 @@ func repeat_this(s *C.zend_string, count int64, reverse bool) unsafe.Pointer { C/PHPとGoの間でメモリ表現が同じ変数型もありますが、直接使用するにはより多くのロジックが必要な型もあります。これは拡張モジュールを書く際の最も挑戦的な部分かもしれません。Zendエンジンの内部仕組みや、変数がPHP内でどのように格納されているかを理解する必要があるためです。以下の表は、知っておくべき重要な情報をまとめています: -| PHP型 | Go型 | 直接変換 | CからGoヘルパー | GoからCヘルパー | クラスメソッドサポート | -| :----------------- | :---------------------------- | :------- | :-------------------------------- | :--------------------------------- | :--------------------- | -| `int` | `int64` | ✅ | - | - | ✅ | -| `?int` | `*int64` | ✅ | - | - | ✅ | -| `float` | `float64` | ✅ | - | - | ✅ | -| `?float` | `*float64` | ✅ | - | - | ✅ | -| `bool` | `bool` | ✅ | - | - | ✅ | -| `?bool` | `*bool` | ✅ | - | - | ✅ | -| `string`/`?string` | `*C.zend_string` | ❌ | `frankenphp.GoString()` | `frankenphp.PHPString()` | ✅ | -| `array` | `frankenphp.AssociativeArray` | ❌ | `frankenphp.GoAssociativeArray()` | `frankenphp.PHPAssociativeArray()` | ✅ | -| `array` | `map[string]any` | ❌ | `frankenphp.GoMap()` | `frankenphp.PHPMap()` | ✅ | -| `array` | `[]any` | ❌ | `frankenphp.GoPackedArray()` | `frankenphp.PHPPackedArray()` | ✅ | -| `mixed` | `any` | ❌ | `GoValue()` | `PHPValue()` | ❌ | -| `callable` | `*C.zval` | ❌ | - | `frankenphp.CallPHPCallable()` | ❌ | -| `object` | `struct` | ❌ | _未実装_ | _未実装_ | ❌ | +| PHP型 | Go型 | 直接変換 | CからGoヘルパー | GoからCヘルパー | クラスメソッドサポート | +| ------------------ | ---------------- | -------- | --------------------- | ---------------------- | ---------------------- | +| `int` | `int64` | ✅ | - | - | ✅ | +| `?int` | `*int64` | ✅ | - | - | ✅ | +| `float` | `float64` | ✅ | - | - | ✅ | +| `?float` | `*float64` | ✅ | - | - | ✅ | +| `bool` | `bool` | ✅ | - | - | ✅ | +| `?bool` | `*bool` | ✅ | - | - | ✅ | +| `string`/`?string` | `*C.zend_string` | ❌ | frankenphp.GoString() | frankenphp.PHPString() | ✅ | +| `array` | `slice`/`map` | ❌ | _未実装_ | _未実装_ | ❌ | +| `mixed` | `any` | ❌ | `GoValue()` | `PHPValue()` | ❌ | +| `object` | `struct` | ❌ | _未実装_ | _未実装_ | ❌ | > [!NOTE] > この表はまだ完全ではなく、FrankenPHPの型APIがより完全になるにつれて完成されます。 > -> クラスメソッドについては、現在プリミティブ型と配列がサポートされています。オブジェクトはまだメソッドパラメータや戻り値の型として使用できません。 +> クラスメソッドについては、現在プリミティブ型のみがサポートされています。配列とオブジェクトはまだメソッドパラメータや戻り値の型として使用できません。 前のセクションのコードスニペットを参照すると、最初のパラメータと戻り値の変換にヘルパーが使用されていることがわかります。 `repeat_this()`関数の2番目と3番目の引数は、基礎となる型のメモリ表現がCとGoで同じであるため、変換する必要がありません。 -#### Working with Arrays - -FrankenPHPは`frankenphp.AssociativeArray`またはマップやスライスへの直接変換を通じて、PHP配列のネイティブサポートを提供します。 - -`AssociativeArray`は、`Map: map[string]any`フィールドと、オプションの`Order: []string`フィールド(PHPの「連想配列」とは異なり、Goのマップは順序付けされていません)で構成される[ハッシュマップ](https://en.wikipedia.org/wiki/Hash_table)を表します。 - -順序や関連付けが必要ない場合は、スライス`[]any`または順序なしマップ`map[string]any`に直接変換することも可能です。 - -**Goで配列を作成および操作する:** - -```go -package example - -// #include -import "C" -import ( - "unsafe" - - "github.com/dunglas/frankenphp" -) - -// export_php:function process_data_ordered(array $input): array -func process_data_ordered_map(arr *C.zend_array) unsafe.Pointer { - // Convert PHP associative array to Go while keeping the order - associativeArray, err := frankenphp.GoAssociativeArray[any](unsafe.Pointer(arr)) - if err != nil { - // handle error - } - - // loop over the entries in order - for _, key := range associativeArray.Order { - value, _ := associativeArray.Map[key] // Original has 'value, _ =' but 'value' is not declared, fixed to 'value, _ :='. It should be 'value, _ = associativeArray.Map[key]' - // do something with key and value - } - - // return an ordered array - // if 'Order' is not empty, only the key-value pairs in 'Order' will be respected - return frankenphp.PHPAssociativeArray[string](frankenphp.AssociativeArray[string]{ - Map: map[string]string{ - "key1": "value1", - "key2": "value2", - }, - Order: []string{"key1", "key2"}, - }) -} - -// export_php:function process_data_unordered(array $input): array -func process_data_unordered_map(arr *C.zend_array) unsafe.Pointer { - // Convert PHP associative array to a Go map without keeping the order - // ignoring the order will be more performant - goMap, err := frankenphp.GoMap[any](unsafe.Pointer(arr)) - if err != nil { - // handle error - } - - // loop over the entries in no specific order - for key, value := range goMap { - // do something with key and value - _ = key // Added to prevent unused variable error, not in original - _ = value // Added to prevent unused variable error, not in original - } - - // return an unordered array - return frankenphp.PHPMap(map[string]string { - "key1": "value1", - "key2": "value2", - }) -} - -// export_php:function process_data_packed(array $input): array -func process_data_packed(arr *C.zend_array) unsafe.Pointer { - // Convert PHP packed array to Go - goSlice, err := frankenphp.GoPackedArray(unsafe.Pointer(arr)) - if err != nil { - // handle error - } - - // loop over the slice in order - for index, value := range goSlice { - // do something with index and value - _ = index // Added to prevent unused variable error, not in original - _ = value // Added to prevent unused variable error, not in original - } - - // return a packed array - return frankenphp.PHPPackedArray([]string{"value1", "value2", "value3"}) -} -``` - -**配列変換の主な機能:** - -- **順序付けされたキーと値のペア** - 連想配列の順序を保持するオプション -- **複数のケースに最適化** - パフォーマンス向上のために順序を破棄したり、直接スライスに変換したりするオプション -- **自動リスト検出** - PHPに変換する際、配列がパックされたリストになるべきかハッシュマップになるべきかを自動的に検出 -- **ネストされた配列** - 配列はネストでき、すべてのサポートされる型(`int64`、`float64`、`string`、`bool`、`nil`、`AssociativeArray`、`map[string]any`、`[]any`)を自動的に変換します -- **オブジェクトはサポートされていません** - 現在、スカラー型と配列のみが値として使用できます。オブジェクトを提供するとPHP配列内で`null`値になります。 - -##### 利用可能なメソッド: パックおよび連想 - -- `frankenphp.PHPAssociativeArray(arr frankenphp.AssociativeArray) unsafe.Pointer` - キーと値のペアを持つ順序付きPHP配列に変換 -- `frankenphp.PHPMap(arr map[string]any) unsafe.Pointer` - マップをキーと値のペアを持つ順序なしPHP配列に変換 -- `frankenphp.PHPPackedArray(slice []any) unsafe.Pointer` - スライスをインデックス付き値のみのPHPパックされた配列に変換 -- `frankenphp.GoAssociativeArray(arr unsafe.Pointer, ordered bool) frankenphp.AssociativeArray` - PHP配列を順序付きGo `AssociativeArray` (順序付きマップ) に変換 -- `frankenphp.GoMap(arr unsafe.Pointer) map[string]any` - PHP配列を順序なしGoマップに変換 -- `frankenphp.GoPackedArray(arr unsafe.Pointer) []any` - PHP配列をGoスライスに変換 -- `frankenphp.IsPacked(zval *C.zend_array) bool` - PHP配列がパックされている(インデックスのみ)か連想配列(キーと値のペア)かを確認 - -### Working with Callables - -FrankenPHPは`frankenphp.CallPHPCallable`ヘルパーを使用してPHPコール可能オブジェクトを扱う方法を提供します。これにより、GoコードからPHP関数やメソッドを呼び出すことができます。 - -これを示すために、独自の`array_map()`関数を作成してみましょう。この関数はコール可能オブジェクトと配列を受け取り、配列の各要素にコール可能オブジェクトを適用し、結果を含む新しい配列を返します: - -```go -package example - -// #include -import "C" -import ( - "unsafe" - - "github.com/dunglas/frankenphp" -) - -// export_php:function my_array_map(array $data, callable $callback): array -func my_array_map(arr *C.zend_array, callback *C.zval) unsafe.Pointer { - goSlice, err := frankenphp.GoPackedArray[any](unsafe.Pointer(arr)) - if err != nil { - panic(err) - } - - result := make([]any, len(goSlice)) - - for index, value := range goSlice { - _ = index // Added to prevent unused variable error, not in original - result[index] = frankenphp.CallPHPCallable(unsafe.Pointer(callback), []interface{}{value}) - } - - return frankenphp.PHPPackedArray(result) -} -``` - -パラメータとして渡されたPHPコール可能オブジェクトを呼び出すために`frankenphp.CallPHPCallable()`を使用していることに注目してください。この関数はコール可能オブジェクトへのポインタと引数の配列を受け取り、コール可能オブジェクトの実行結果を返します。使い慣れたコール可能構文を使用できます: - -```php - -import "C" -import ( - "unsafe" - - "github.com/dunglas/frankenphp" -) - //export_php:class User type UserStruct struct { Name string @@ -336,29 +162,19 @@ func (us *UserStruct) SetNamePrefix(prefix *C.zend_string) { ジェネレーターは、PHPシグネチャにおける`?`プレフィックスを使用ったnullableパラメータをサポートしています。パラメータがnullableの場合、Go関数内ではポインタとして扱われ、PHP側で値が`null`だったかどうかを確認できます: ```go -package example - -// #include -import "C" -import ( - "unsafe" - - "github.com/dunglas/frankenphp" -) - //export_php:method User::updateInfo(?string $name, ?int $age, ?bool $active): void func (us *UserStruct) UpdateInfo(name *C.zend_string, age *int64, active *bool) { - // Check if name was provided (not null) + // nameが渡された(nullではない)かチェック if name != nil { us.Name = frankenphp.GoString(unsafe.Pointer(name)) } - // Check if age was provided (not null) + // ageが渡された(nullではない)かチェック if age != nil { us.Age = int(*age) } - // Check if active was provided (not null) + // activeが渡された(nullではない)かチェック if active != nil { us.Active = *active } @@ -373,8 +189,7 @@ func (us *UserStruct) UpdateInfo(name *C.zend_string, age *int64, active *bool) - **PHPの`null`はGoの`nil`になります** - PHPが`null`を渡すと、Go関数は`nil`ポインタを受け取ります > [!WARNING] -> -> 現在、クラスメソッドには次の制限があります。オブジェクトはパラメータ型や戻り値の型としてサポートされていません。配列はパラメータと戻り値の型の両方で完全にサポートされています。サポートされる型: `string`、`int`、`float`、`bool`、`array`、および `void`(戻り値の型)。Nullableパラメータ型は、すべてのスカラー型(`?string`、`?int`、`?float`、`?bool`)で完全にサポートされています。 +> 現在、クラスメソッドには次の制限があります。**配列とオブジェクトはパラメータ型や戻り値の型としてサポートされていません**。サポートされるのは`string`、`int`、`float`、`bool`、`void`(戻り値の型)といったスカラー型のみです。**nullableなスカラー型はすべてサポートされています** (`?string`、`?int`、`?float`、`?bool`)。 拡張を生成した後、PHP側でクラスとそのメソッドを使用できるようになります。ただし**プロパティに直接アクセスできない**ことに注意してください: @@ -383,20 +198,20 @@ func (us *UserStruct) UpdateInfo(name *C.zend_string, age *int64, active *bool) $user = new User(); -// ✅ This works - using methods +// ✅ これは動作します - メソッドの使用 $user->setAge(25); -echo $user->getName(); // Output: (empty, default value) -echo $user->getAge(); // Output: 25 +echo $user->getName(); // 出力: (empty、デフォルト値) +echo $user->getAge(); // 出力: 25 $user->setNamePrefix("Employee"); -// ✅ This also works - nullable parameters -$user->updateInfo("John", 30, true); // All parameters provided -$user->updateInfo("Jane", null, false); // Age is null -$user->updateInfo(null, 25, null); // Name and active are null +// ✅ これも動作します - nullableパラメータ +$user->updateInfo("John", 30, true); // すべて指定 +$user->updateInfo("Jane", null, false); // Ageがnull +$user->updateInfo(null, 25, null); // Nameとactiveがnull -// ❌ This will NOT work - direct property access -// echo $user->name; // Error: Cannot access private property -// $user->age = 30; // Error: Cannot access private property +// ❌ これは動作しません - プロパティへの直接アクセス +// echo $user->name; // エラー: privateプロパティにアクセスできません +// $user->age = 30; // エラー: privateプロパティにアクセスできません ``` この設計により、Goコードがオブジェクトの状態へのアクセスと変更方法を完全に制御でき、より良いカプセル化と型安全性を提供します。 @@ -410,8 +225,6 @@ $user->updateInfo(null, 25, null); // Name and active are null `//export_php:const`ディレクティブを使用してグローバルなPHP定数を作成できます: ```go -package example - //export_php:const const MAX_CONNECTIONS = 100 @@ -430,8 +243,6 @@ const STATUS_ERROR = iota `//export_php:classconst ClassName`ディレクティブを使用して、特定のPHPクラスに属する定数を作成できます: ```go -package example - //export_php:classconst User const STATUS_ACTIVE = 1 @@ -456,11 +267,11 @@ const STATE_COMPLETED = iota ```php -import "C" import ( - "strings" - "unsafe" - - "github.com/dunglas/frankenphp" + "C" + "github.com/dunglas/frankenphp" + "strings" ) //export_php:const @@ -496,99 +302,40 @@ const MODE_UPPERCASE = 2 //export_php:function repeat_this(string $str, int $count, int $mode): string func repeat_this(s *C.zend_string, count int64, mode int) unsafe.Pointer { - str := frankenphp.GoString(unsafe.Pointer(s)) + str := frankenphp.GoString(unsafe.Pointer(s)) - result := strings.Repeat(str, int(count)) - if mode == STR_REVERSE { - // reverse the string - } + result := strings.Repeat(str, int(count)) + if mode == STR_REVERSE { + // 文字列を逆転 + } - if mode == STR_NORMAL { - // no-op, just to showcase the constant - } + if mode == STR_NORMAL { + // 何もしない、定数を示すためのみ記載 + } - return frankenphp.PHPString(result, false) + return frankenphp.PHPString(result, false) } //export_php:class StringProcessor type StringProcessorStruct struct { - // internal fields + // 内部フィールド } //export_php:method StringProcessor::process(string $input, int $mode): string func (sp *StringProcessorStruct) Process(input *C.zend_string, mode int64) unsafe.Pointer { - str := frankenphp.GoString(unsafe.Pointer(input)) - - switch mode { - case MODE_LOWERCASE: - str = strings.ToLower(str) - case MODE_UPPERCASE: - str = strings.ToUpper(str) - } - - return frankenphp.PHPString(str, false) -} -``` - -### Using Namespaces - -ジェネレーターは、`//export_php:namespace`ディレクティブを使用してPHP拡張モジュールの関数、クラス、定数を名前空間の下に整理することをサポートしています。これにより、命名衝突を避け、拡張モジュールのAPIの整理が向上します。 - -#### Declaring a Namespace - -Goファイルの先頭で`//export_php:namespace`ディレクティブを使用し、すべてのエクスポートされたシンボルを特定の名前空間の下に配置します: - -```go -//export_php:namespace My\Extension -package example - -import ( - "unsafe" - - "github.com/dunglas/frankenphp" -) + str := frankenphp.GoString(unsafe.Pointer(input)) -//export_php:function hello(): string -func hello() string { - return "Hello from My\\Extension namespace!" -} - -//export_php:class User -type UserStruct struct { - // internal fields -} + switch mode { + case MODE_LOWERCASE: + str = strings.ToLower(str) + case MODE_UPPERCASE: + str = strings.ToUpper(str) + } -//export_php:method User::getName(): string -func (u *UserStruct) GetName() unsafe.Pointer { - return frankenphp.PHPString("John Doe", false) + return frankenphp.PHPString(str, false) } - -//export_php:const -const STATUS_ACTIVE = 1 -``` - -#### Using Namespaced Extension in PHP - -名前空間が宣言されると、すべての関数、クラス、および定数はPHPでその名前空間の下に配置されます: - -```php -getName(); // "John Doe" - -echo My\Extension\STATUS_ACTIVE; // 1 ``` -#### Important Notes - -- 1つのファイルにつき**1つ**の名前空間ディレクティブのみが許可されます。複数の名前空間ディレクティブが見つかった場合、ジェネレーターはエラーを返します。 -- 名前空間はファイル内の**すべて**のエクスポートされたシンボル(関数、クラス、メソッド、定数)に適用されます。 -- 名前空間名は、バックスラッシュ(`\`)を区切り文字とするPHPの名前空間の慣習に従います。 -- 名前空間が宣言されていない場合、シンボルは通常通りグローバル名前空間にエクスポートされます。 - ### 拡張モジュールの生成 ここでいよいよ、拡張モジュールを生成できるようになります。以下のコマンドでジェネレーターを実行できます: @@ -597,8 +344,7 @@ echo My\Extension\STATUS_ACTIVE; // 1 GEN_STUB_SCRIPT=php-src/build/gen_stub.php frankenphp extension-init my_extension.go ``` -> [!NOTE] -> `GEN_STUB_SCRIPT`環境変数に、先ほどダウンロードしたPHPソースの`gen_stub.php`ファイルのパスを設定するのを忘れないでください。これは手動実装セクションで言及されているのと同じ`gen_stub.php`スクリプトです。 +> [!NOTE] > `GEN_STUB_SCRIPT`環境変数に、先ほどダウンロードしたPHPソースの`gen_stub.php`ファイルのパスを設定するのを忘れないでください。これは手動実装セクションで言及されているのと同じ`gen_stub.php`スクリプトです。 すべてがうまくいけば、`build`という名前の新しいディレクトリが作成されているはずです。このディレクトリには、生成されたPHP関数スタブを含む`my_extension.go`ファイルなど、拡張用の生成されたファイルが含まれています。 @@ -649,32 +395,31 @@ echo $processor->process('Hello World', StringProcessor::MODE_UPPERCASE); // "H モジュール内で、PHPから呼び出される新しいネイティブ関数を定義する必要があります。これを行うには、例えば`extension.go`のように任意の名前でファイルを作成し、以下のコードを追加します: ```go -package example +package ext_go -// #include "extension.h" +//#include "extension.h" import "C" import ( - "log/slog" - "unsafe" - - "github.com/dunglas/frankenphp" + "unsafe" + "github.com/caddyserver/caddy/v2" + "github.com/dunglas/frankenphp" ) func init() { - frankenphp.RegisterExtension(unsafe.Pointer(&C.ext_module_entry)) + frankenphp.RegisterExtension(unsafe.Pointer(&C.ext_module_entry)) } //export go_print_something func go_print_something() { - go func() { - slog.Info("Hello from a goroutine!") - }() + go func() { + caddy.Log().Info("Hello from a goroutine!") + }() } ``` `frankenphp.RegisterExtension()`関数は、内部のPHP登録ロジックを処理することで拡張登録プロセスを簡素化します。`go_print_something`関数は`//export`ディレクティブを使用して、CGOのおかげで、これから書くCコードでアクセスできるようになることを示しています。 -この例では、新しい関数がgoroutineをトリガーし、メッセージをCaddyのログに出力します。 +この例では、新しい関数がCaddyのログにメッセージ出力するgoroutineをトリガーします。 #### PHP関数の定義 @@ -800,7 +545,7 @@ static const zend_function_entry ext_functions[] = { }; ``` -この出力から、`go_upper`関数が`string`型の引数を1つ受け取り、`string`型の戻り値を返すことが定義されてのがわかります。 +この出力から、`go_upper`関数が`string`型の引数を1つ受け取り、`string`型の戻り値を返すことが定義されていのがわかります。 #### GoとPHP/C間の型変換(Type Juggling) @@ -844,16 +589,7 @@ PHP_FUNCTION(go_upper) Go側の関数では`*C.zend_string`を引数として受け取り、FrankenPHPのヘルパー関数を使用してGoの文字列に変換し、処理を行ったうえで、結果を新たな`*C.zend_string`として返します。メモリ管理と変換の複雑さは、ヘルパー関数がすべて対応してくれます。 ```go -package example - -// #include -import "C" -import ( - "unsafe" - "strings" - - "github.com/dunglas/frankenphp" -) +import "strings" //export go_upper func go_upper(s *C.zend_string) *C.zend_string { @@ -868,7 +604,6 @@ func go_upper(s *C.zend_string) *C.zend_string { このアプローチは、手動メモリ管理よりもはるかにクリーンで安全です。FrankenPHPのヘルパー関数は、PHPの`zend_string`形式とGoの文字列間の変換を自動的に処理してくれます。`PHPString()`に`false`引数を指定していることで、新しい非永続文字列(リクエストの終了時に解放される)を作成したいことを示しています。 > [!TIP] -> > この例ではエラーハンドリングを省略していますが、Go関数内でポインタが`nil`ではないこと、渡されたデータが有効であることを常に確認するべきです。 ### 拡張モジュールのFrankenPHPへの統合 diff --git a/docs/ja/hot-reload.md b/docs/ja/hot-reload.md deleted file mode 100644 index ad2d9b6863..0000000000 --- a/docs/ja/hot-reload.md +++ /dev/null @@ -1,138 +0,0 @@ -# ホットリロード - -FrankenPHPには、開発者の体験を大幅に向上させるために設計された組み込みの**ホットリロード**機能が含まれています。 - -![Mercure](hot-reload.png) - -この機能は、最新のJavaScriptツール(Viteやwebpackなど)に見られる**ホットモジュールリプレイスメント(HMR)**に似たワークフローを提供します。 -ファイル変更(PHPコード、テンプレート、JavaScript、CSSファイルなど)のたびに手動でブラウザを更新する代わりに、FrankenPHPはコンテンツをリアルタイムで更新します。 - -ホットリロードは、WordPress、Laravel、Symfony、およびその他のあらゆるPHPアプリケーションやフレームワークでネイティブに動作します。 - -有効にすると、FrankenPHPは現在の作業ディレクトリのファイルシステム変更を監視します。 -ファイルが変更されると、[Mercure](mercure.md)の更新をブラウザにプッシュします。 - -設定によっては、ブラウザは次のいずれかを行います。 - -- [Idiomorph](https://github.com/bigskysoftware/idiomorph)がロードされている場合、**DOMをモーフィング**します(スクロール位置と入力状態を保持)。 -- Idiomorphが存在しない場合、**ページをリロード**します(標準のライブリロード)。 - -## 設定 - -ホットリロードを有効にするには、Mercureを有効にし、`Caddyfile`の`php_server`ディレクティブに`hot_reload`サブディレクティブを追加します。 - -> [!WARNING] -> この機能は**開発環境でのみ**使用することを目的としています。 -> ファイルシステムの監視はパフォーマンスのオーバーヘッドを引き起こし、内部エンドポイントを公開するため、本番環境で`hot_reload`を有効にしないでください。 - -```caddyfile -localhost - -mercure { - anonymous -} - -root public/ -php_server { - hot_reload -} -``` - -デフォルトでは、FrankenPHPは現在の作業ディレクトリ内の以下のglobパターンに一致するすべてのファイルを監視します: `./**/*.{css,env,gif,htm,html,jpg,jpeg,js,mjs,php,png,svg,twig,webp,xml,yaml,yml}` - -glob構文を使用して、監視するファイルを明示的に設定することもできます。 - -```caddyfile -localhost - -mercure { - anonymous -} - -root public/ -php_server { - hot_reload src/**/*{.php,.js} config/**/*.yaml -} -``` - -Mercureのトピックと監視するディレクトリやファイルを指定するには、`hot_reload`オプションにパスを指定する長い形式を使用します。 - -```caddyfile -localhost - -mercure { - anonymous -} - -root public/ -php_server { - hot_reload { - topic hot-reload-topic - watch src/**/*.php - watch assets/**/*.{ts,json} - watch templates/ - watch public/css/ - } -} -``` - -## クライアントサイドの統合 - -サーバーが変更を検出する一方で、ブラウザはページを更新するためにこれらのイベントを購読する必要があります。 -FrankenPHPは、ファイル変更を購読するために使用するMercureハブのURLを、`$_SERVER['FRANKENPHP_HOT_RELOAD']`環境変数を通じて公開します。 - -クライアントサイドのロジックを処理するための便利なJavaScriptライブラリ、[frankenphp-hot-reload](https://www.npmjs.com/package/frankenphp-hot-reload)も利用可能です。 -これを使用するには、メインレイアウトに以下を追加します。 - -```php - -FrankenPHP ホットリロード - - - - - -``` - -このライブラリは自動的にMercureハブを購読し、ファイル変更が検出されるとバックグラウンドで現在のURLを取得し、DOMをモーフィングします。 -これは[npm](https://www.npmjs.com/package/frankenphp-hot-reload)パッケージとして、また[GitHub](https://github.com/dunglas/frankenphp-hot-reload)で利用可能です。 - -または、`EventSource`ネイティブJavaScriptクラスを使用してMercureハブを直接購読することで、独自のクライアントサイドロジックを実装することもできます。 - -### ワーカーモード - -アプリケーションを[ワーカーモード](https://frankenphp.dev/docs/worker/)で実行している場合、アプリケーションスクリプトはメモリに残ります。 -これは、ブラウザがリロードされても、PHPコードの変更がすぐに反映されないことを意味します。 - -最高の開発者体験を得るには、`hot_reload`を[ワーカーディレクティブ内の`watch`サブディレクティブ](config.md#watching-for-file-changes)と組み合わせる必要があります。 - -- `hot_reload`: ファイル変更時に**ブラウザ**を更新します -- `worker.watch`: ファイル変更時にワーカーを再起動します - -```caddy -localhost - -mercure { - anonymous -} - -root public/ -php_server { - hot_reload - worker { - file /path/to/my_worker.php - watch - } -} -``` - -### 仕組み - -1. **監視**: FrankenPHPは、内部で[e-dant/watcherライブラリ](https://github.com/e-dant/watcher)を使用してファイルシステムの変更を監視します(我々はGoバインディングに貢献しました)。 -2. **再起動(ワーカーモード)**: ワーカー設定で`watch`が有効になっている場合、PHPワーカーは新しいコードをロードするために再起動されます。 -3. **プッシュ**: 変更されたファイルのリストを含むJSONペイロードが、組み込みの[Mercureハブ](https://mercure.rocks)に送信されます。 -4. **受信**: JavaScriptライブラリを介してリッスンしているブラウザは、Mercureイベントを受信します。 -5. **更新**: - -- **Idiomorph**が検出された場合、更新されたコンテンツを取得し、現在のHTMLを新しい状態にモーフィングして、状態を失うことなく即座に変更を適用します。 -- それ以外の場合、ページを更新するために`window.location.reload()`が呼び出されます。 diff --git a/docs/ja/known-issues.md b/docs/ja/known-issues.md index 80460a7883..19e6330698 100644 --- a/docs/ja/known-issues.md +++ b/docs/ja/known-issues.md @@ -13,9 +13,9 @@ 以下の拡張モジュールはFrankenPHPとの組み合わせで既知のバグや予期しない動作が確認されています: -| 名前 | 問題 | -| ------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| [ext-openssl](https://www.php.net/manual/en/book.openssl.php) | musl libcを使用している場合、OpenSSL拡張モジュールは高負荷時にクラッシュすることがあります。この問題は、より普及しているGNU libcを使用する場合には発生しません。このバグは[PHPによって追跡されています](https://github.com/php/php-src/issues/13648)。 | +| 名前 | 問題 | +| ------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| [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 @@ -23,9 +23,7 @@ ## スタンドアロンバイナリおよびAlpineベースのDockerイメージ -スタンドアロンバイナリおよびAlpineベースのDockerイメージ(`dunglas/frankenphp:*-alpine`)は、バイナリサイズを小さく保つために[glibc and friends](https://www.etalabs.net/compare_libcs.html)ではなく[musl libc](https://musl.libc.org/)を使用しています。これによりいくつかの互換性問題が発生する可能性があります。特に、globフラグ`GLOB_BRACE`は [サポートされていません](https://www.php.net/manual/en/function.glob.php)。 - -問題が発生した場合は、静的バイナリのGNUバリアントおよびDebianベースのDockerイメージを使用することをお勧めします。 +スタンドアロンバイナリおよびAlpineベースのDockerイメージ(`dunglas/frankenphp:*-alpine`)は、バイナリサイズを小さく保つために[glibc and friends](https://www.etalabs.net/compare_libcs.html)ではなく[musl libc](https://musl.libc.org/)を使用しています。これによりいくつかの互換性問題が発生する可能性があります。特に、globフラグ`GLOB_BRACE`は [サポートされていません](https://www.php.net/manual/en/function.glob.php) 。 ## Dockerで`https://127.0.0.1`を使用する @@ -80,7 +78,7 @@ docker run \ ## `@php` を参照するComposerスクリプト -[Composerスクリプト](https://getcomposer.org/doc/articles/scripts.md)では、いくつかのタスクでPHPバイナリを実行したい場合があります。例えば、[Laravelプロジェクト](laravel.md)で`@php artisan package:discover --ansi`を実行する場合です。しかし現在これは以下の2つの理由で[失敗します](https://github.com/php/frankenphp/issues/483#issuecomment-1899890915): +[Composerスクリプト](https://getcomposer.org/doc/articles/scripts.md)では、いくつかのタスクでPHPバイナリを実行したい場合があります。例えば、[Laravelプロジェクト](laravel.md)で`@php artisan package:discover --ansi`を実行する場合です。しかし現在これは以下の2つの理由で[失敗します](https://github.com/dunglas/frankenphp/issues/483#issuecomment-1899890915): - ComposerはFrankenPHPバイナリを呼び出す方法を知りません - Composerはコマンドで`-d`フラグを使用してPHP設定を追加する場合があり、FrankenPHPはまだサポートしていません @@ -132,7 +130,7 @@ CA証明書をどこにインストールすべきか確認し、その場所に > WebとCLIコンテキストでは設定が異なる場合があります。 > 適切なコンテキストで`openssl_get_cert_locations()`を実行してください。 -[Mozillaから抽出されたCA証明書はcURLのサイトでダウンロードできます](https://curl.se/docs/caextract.html)。 +[Mozillaから抽出されたCA証明書はcurlのサイトでダウンロードできます](https://curl.se/docs/caextract.html)。 または、Debian、Ubuntu、Alpineなどのディストリビューションでも、これらの証明書を含む`ca-certificates`というパッケージを提供しています。 @@ -142,3 +140,4 @@ CA証明書をどこにインストールすべきか確認し、その場所に # TLS 証明書の環境変数を設定 export SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt export SSL_CERT_DIR=/etc/ssl/certs +``` diff --git a/docs/ja/laravel.md b/docs/ja/laravel.md index adc0064215..18e01e95af 100644 --- a/docs/ja/laravel.md +++ b/docs/ja/laravel.md @@ -2,7 +2,7 @@ ## Docker -FrankenPHPを使用して[Laravel](https://laravel.com)のWebアプリケーションを提供するのは簡単で、公式Dockerイメージの`/app`ディレクトリにプロジェクトをマウントするだけです。 +FrankenPHPを使用して[Laravel](https://laravel.com)のWebアプリケーションを配信するのは簡単で、公式Dockerイメージの`/app`ディレクトリにプロジェクトをマウントするだけです。 Laravelアプリのメインディレクトリからこのコマンドを実行してください: @@ -76,8 +76,6 @@ php artisan octane:frankenphp > [!TIP] > 構造化されたJSONログ(ログ分析ソリューションを使用する際に便利)を取得するには、明示的に`--log-level`オプションを指定してください。 -[OctaneでMercureを使用する方法](#mercure-support)も参照してください。 - 詳しくは[Laravel Octaneの公式ドキュメント](https://laravel.com/docs/octane)をご覧ください。 ## Laravelアプリのスタンドアロンバイナリ化 @@ -166,41 +164,13 @@ LaravelアプリをLinux用のスタンドアロンバイナリとしてパッ Laravelはアップロードされたファイルやキャッシュ、ログなどをデフォルトでアプリケーションの`storage/`ディレクトリに保存します。 しかし、これは埋め込みアプリケーションには適していません。なぜなら、アプリの新しいバージョンごとに異なる一時ディレクトリに展開されるためです。 -`LARAVEL_STORAGE_PATH`環境変数を設定(例:`.env`ファイル内)するか、 `Illuminate\Foundation\Application::useStoragePath()`メソッドを呼び出して、一時ディレクトリの外にある任意のディレクトリを使用してください。 - -### Mercureサポート - -[Mercure](https://mercure.rocks)は、Laravelアプリにリアルタイム機能を追加する優れた方法です。 -FrankenPHPは、[Mercureのサポートをすぐに利用できる](mercure.md)ように組み込んでいます。 - -[Octane](#laravel-octane)を使用していない場合は、[Mercureのドキュメント](mercure.md)を参照してください。 - -Octaneを使用している場合は、`config/octane.php`ファイルに以下の行を追加することでMercureサポートを有効にできます: - -```php -// ... - -return [ - // ... - - 'mercure' => [ - 'anonymous' => true, - 'publisher_jwt' => '!ChangeThisMercureHubJWTSecretKey!', - 'subscriber_jwt' => '!ChangeThisMercureHubJWTSecretKey!', - ], -]; -``` - -この配列では、[Mercureがサポートするすべてのディレクティブ](https://mercure.rocks/docs/hub/config#directives)を使用できます。 - -更新の公開と購読には、[Laravel Mercure Broadcaster](https://github.com/mvanduijker/laravel-mercure-broadcaster)ライブラリの使用をお勧めします。 -または、純粋なPHPとJavaScriptで実装する方法については、[Mercureのドキュメント](mercure.md)を参照してください。 +この問題を回避するには、`LARAVEL_STORAGE_PATH`環境変数を設定(例:`.env`ファイル内)するか、 `Illuminate\Foundation\Application::useStoragePath()`メソッドを呼び出して、一時ディレクトリの外にある任意のディレクトリを使用してください。 ### スタンドアロンバイナリでOctaneを実行する Laravel Octaneアプリもスタンドアロンバイナリとしてパッケージ化することが可能です! -そのためには、[Octaneを正しくインストール](#laravel-octane)し、[前のセクション](#laravel-apps-as-standalone-binaries)で説明した手順に従ってください。 +そのためには、[Octaneを正しくインストール](#laravel-octane)し、[前のセクション](#laravelアプリのスタンドアロンバイナリ化)で説明した手順に従ってください。 次に、Octaneを通じてワーカーモードでFrankenPHPを起動するには、以下を実行してください: diff --git a/docs/ja/logging.md b/docs/ja/logging.md deleted file mode 100644 index 65f3e139a1..0000000000 --- a/docs/ja/logging.md +++ /dev/null @@ -1,71 +0,0 @@ -# ロギング - -FrankenPHP は、[Caddy のロギングシステム](https://caddyserver.com/docs/logging)とシームレスに統合されています。 -標準の PHP 関数を使用してメッセージをログに記録するか、高度な構造化ロギング機能のために専用の `frankenphp_log()` 関数を利用できます。 - -## `frankenphp_log()` - -`frankenphp_log()` 関数を使用すると、PHP アプリケーションから直接構造化ログを出力でき、 -Datadog、Grafana Loki、Elastic などのプラットフォームへの取り込みや OpenTelemetry のサポートがはるかに容易になります。 - -内部的に、`frankenphp_log()` は [Go の `log/slog` パッケージ](https://pkg.go.dev/log/slog)をラップして、豊富なロギング機能を提供します。 - -これらのログには、深刻度レベルとオプションのコンテキストデータが含まれます。 - -```php -function frankenphp_log(string $message, int $level = FRANKENPHP_LOG_LEVEL_INFO, array $context = []): void -``` - -### パラメータ - -- **`message`**: ログメッセージ文字列。 -- **`level`**: ログの深刻度レベル。任意の整数値を指定できます。一般的なレベルには便利な定数が用意されています: `FRANKENPHP_LOG_LEVEL_DEBUG` (`-4`)、`FRANKENPHP_LOG_LEVEL_INFO` (`0`)、`FRANKENPHP_LOG_LEVEL_WARN` (`4`)、`FRANKENPHP_LOG_LEVEL_ERROR` (`8`)。デフォルトは `FRANKENPHP_LOG_LEVEL_INFO` です。 -- **`context`**: ログエントリに含める追加データの連想配列。 - -### 例 - -```php - memory_get_usage(), - 'peak_usage' => memory_get_peak_usage(), - ], -); - -``` - -ログを表示すると (例: `docker compose logs` 経由)、出力は構造化された JSON として表示されます。 - -```json -{"level":"info","ts":1704067200,"logger":"frankenphp","msg":"Hello from FrankenPHP!"} -{"level":"warn","ts":1704067200,"logger":"frankenphp","msg":"Memory usage high","current_usage":10485760,"peak_usage":12582912} -``` - -## `error_log()` - -FrankenPHP は、標準の `error_log()` 関数を使用したロギングも可能です。`$message_type` パラメータが `4` (SAPI) の場合、 -これらのメッセージは Caddy ロガーにルーティングされます。 - -デフォルトでは、`error_log()` を介して送信されるメッセージは非構造化テキストとして扱われます。 -これらは、標準の PHP ライブラリに依存する既存のアプリケーションやライブラリとの互換性のために有用です。 - -### `error_log()` の例 - -```php -error_log("Database connection failed", 4); -``` - -これは Caddy ログに表示され、多くの場合、PHP から発信されたことを示すプレフィックスが付けられます。 - -> [!TIP] -> 本番環境での可観測性を高めるには、`frankenphp_log()` を使用することをお勧めします。 -> これは、レベル (デバッグ、エラーなど) でログをフィルタリングしたり、 -> ロギングインフラストラクチャ内の特定のフィールドをクエリしたりできるためです。 diff --git a/docs/ja/mercure.md b/docs/ja/mercure.md index 559e9d06f1..0aae6d3055 100644 --- a/docs/ja/mercure.md +++ b/docs/ja/mercure.md @@ -3,143 +3,13 @@ FrankenPHPには組み込みの[Mercure](https://mercure.rocks)ハブが付属しています! Mercureを使用すると、接続されているすべてのデバイスにリアルタイムイベントをプッシュでき、各デバイスは即座にJavaScriptイベントを受信します。 -これはWebSocketsに代わる便利な方法で、使い方が簡単で、すべてのモダンなウェブブラウザでネイティブにサポートされています! +JSライブラリやSDKは必要ありません! ![Mercure](mercure-hub.png) -## Mercureを有効にする +Mercureハブを有効にするには、[Mercureのサイト](https://mercure.rocks/docs/hub/config)で説明されているように`Caddyfile`を更新してください。 -Mercureのサポートはデフォルトで無効になっています。 -以下は、FrankenPHPとMercureハブの両方を有効にする`Caddyfile`の最小限の例です。 +Mercureハブのパスは`/.well-known/mercure`です。 +FrankenPHPをDocker内で実行している場合、完全な送信URLは`http://php/.well-known/mercure`のようになります。ここでの`php`はFrankenPHPを実行するコンテナの名前です。 -```caddyfile -# 応答するホスト名 -localhost - -mercure { - # パブリッシャー用のJWTトークンを署名するために使用される秘密鍵 - publisher_jwt !ChangeThisMercureHubJWTSecretKey! - # 匿名購読者(JWTなし)を許可する - anonymous -} - -root public/ -php_server -``` - -> [!ヒント] -> -> [Dockerイメージ](docker.md)によって提供される[サンプル `Caddyfile`](https://github.com/php/frankenphp/blob/main/caddy/frankenphp/Caddyfile)には、便利な環境変数で設定できる、コメントアウトされたMercure設定がすでに含まれています。 -> -> `/etc/frankenphp/Caddyfile`のMercureセクションのコメントを解除して有効にしてください。 - -## 更新を購読する - -デフォルトでは、MercureハブはFrankenPHPサーバーの`/.well-known/mercure`パスで利用可能です。 -更新を購読するには、ネイティブの[`EventSource`](https://developer.mozilla.org/docs/Web/API/EventSource) JavaScriptクラスを使用します。 - -```html - - -Mercureの例 - -``` - -## 更新を公開する - -### `mercure_publish()`を使用する - -FrankenPHPは、組み込みのMercureハブに更新を公開するための便利な`mercure_publish()`関数を提供します。 - -```php - 'value'])); - -// FrankenPHPのログに書き込む -error_log("update $updateID published", 4); -``` - -関数の完全なシグネチャは次のとおりです。 - -```php -/** - * @param string|string[] $topics - */ -function mercure_publish(string|array $topics, string $data = '', bool $private = false, ?string $id = null, ?string $type = null, ?int $retry = null): string {} -``` - -### `file_get_contents()`を使用する - -接続された購読者に更新をディスパッチするには、`topic`および`data`パラメーターを含む認証済みPOSTリクエストをMercureハブに送信します。 - -```php - [ - 'method' => 'POST', - 'header' => "Content-type: application/x-www-form-urlencoded\r\nAuthorization: Bearer " . JWT, - 'content' => http_build_query([ - 'topic' => 'my-topic', - 'data' => json_encode(['key' => 'value']), - ]), -]])); - -// FrankenPHPのログに書き込む -error_log("update $updateID published", 4); -``` - -`Caddyfile`の`mercure.publisher_jwt`オプションのパラメーターとして渡されたキーは、`Authorization`ヘッダーで使用されるJWTトークンの署名に用いられなければなりません。 - -JWTには、公開したいトピックに対する`publish`権限を持つ`mercure`クレームを含める必要があります。認可については、[Mercureのドキュメント](https://mercure.rocks/spec#publishers)を参照してください。 - -独自のトークンを生成するには、[このjwt.ioリンク](https://www.jwt.io/#token=eyJhbGciOiJIUzI1NiJ9.eyJtZXJjdXJlIjp7InB1Ymxpc2giOlsiKiJdfX0.PXwpfIGng6KObfZlcOXvcnWCJOWTFLtswGI5DZuWSK4)を使用できますが、本番環境のアプリケーションでは、信頼できる[JWTライブラリ](https://www.jwt.io/libraries?programming_language=php)を使用して動的に生成される短命のトークンを使用することをお勧めします。 - -### Symfony Mercureを使用する - -あるいは、スタンドアロンのPHPライブラリである[Symfony Mercureコンポーネント](https://symfony.com/components/Mercure)を使用することもできます。 - -このライブラリは、JWTの生成、更新の公開、および購読者向けのクッキーベースの認可を処理します。 - -まず、Composerを使用してライブラリをインストールします。 - -```console -composer require symfony/mercure lcobucci/jwt -``` - -その後、次のように使用できます。 - -```php -publish(new \Symfony\Component\Mercure\Update('my-topic', json_encode(['key' => 'value']))); - -// FrankenPHPのログに書き込む -error_log("update $updateID published", 4); -``` - -Mercureは、以下によってもネイティブにサポートされています。 - -- [Laravel](laravel.md#mercure-support) -- [Symfony](https://symfony.com/doc/current/mercure.html) -- [API Platform](https://api-platform.com/docs/core/mercure/) +コードからMercureの更新をプッシュするには、[Symfony Mercure Component](https://symfony.com/components/Mercure)をお勧めします。なお、Symfonyのフルスタックフレームワークは必要ありません。 diff --git a/docs/ja/performance.md b/docs/ja/performance.md index b26bc73f7f..5139a0347e 100644 --- a/docs/ja/performance.md +++ b/docs/ja/performance.md @@ -1,6 +1,6 @@ # パフォーマンス -デフォルトでは、FrankenPHPはパフォーマンスと使いやすさの良い妥協点を提供するよう設計されています。 +デフォルトでは、FrankenPHPはパフォーマンスと使いやすさのバランスが取れた構成を提供するよう設計されています。 ただし、適切な設定により、パフォーマンスを大幅に向上させることが可能です。 ## スレッド数とワーカー数 @@ -11,10 +11,10 @@ これらの値を調整することを強く推奨します。最適なシステムの安定性のためには、`num_threads` x `memory_limit` < `available_memory`とすることをお勧めします。 適切な値を見つけるには、実際のトラフィックをシミュレートした負荷テストを実行するのが最も効果的です。 -[k6](https://k6.io)や[Gatling](https://gatling.io)がそのための有用なツールです。 +そのためのツールとして、[k6](https://k6.io)や[Gatling](https://gatling.io)が有用です。 -スレッド数を設定するには、`php_server`や`php`ディレクティブの`num_threads`オプションを使用してください。 -ワーカー数を変更するには、`frankenphp`ディレクティブの`worker`セクションにある`num`オプションを使用してください。 +スレッド数を設定するには、`php_server`や`php`ディレクティブ内の`num_threads`オプションを使用してください。 +ワーカー数を変更するには、`frankenphp`ディレクティブ内の`worker`セクションにある`num`オプションを使用してください。 ### `max_threads` @@ -30,7 +30,7 @@ [ワーカーモード](worker.md)を有効にするとパフォーマンスが劇的に向上しますが、 アプリがこのモードと互換性があるように適応する必要があります: -ワーカースクリプトを作成し、アプリがメモリリークしていないことを確認してください。 +ワーカースクリプトを作成し、アプリがメモリリークしていないことを確認する必要があります。 ## muslを使用しない @@ -41,9 +41,11 @@ PHPは、従来のGNUライブラリの代わりにこの代替Cライブラリ また、[一部のバグはmuslを使用した場合にのみ発生します](https://github.com/php/php-src/issues?q=sort%3Aupdated-desc+is%3Aissue+is%3Aopen+label%3ABug+musl)。 -本番環境では、glibcにリンクされ、適切な最適化レベルでコンパイルされたFrankenPHPを使用することをお勧めします。 +本番環境では、glibcにリンクされたFrankenPHPを使用することをお勧めします。 -これは、Debian Dockerイメージを使用するか、メンテナーの[.deb](https://debs.henderkes.com)または[.rpm](https://rpms.henderkes.com)パッケージを使用するか、または[FrankenPHPをソースからコンパイル](compile.md)することで実現できます。 +これは、Debian Dockerイメージ(デフォルト)を使用するか、[リリースページ](https://github.com/php/frankenphp/releases)から -gnu サフィックス付きバイナリをダウンロードするか、あるいは[FrankenPHPをソースからコンパイル](compile.md)することで実現できます。 + +または、[mimalloc allocator](https://github.com/microsoft/mimalloc)でコンパイルされた静的muslバイナリも提供しており、これによりスレッド環境での問題を軽減できます。 ## Go Runtime設定 @@ -153,31 +155,3 @@ FrankenPHPは公式のPHPインタープリターを使用しています。 詳細については、[Symfonyの専用ドキュメントエントリ](https://symfony.com/doc/current/performance.html)をお読みください (Symfonyを使用していなくても、多くのヒントが役立ちます)。 - -## スレッドプールの分割 - -アプリケーションが、高負荷時に不安定になったり、常に10秒以上応答に時間がかかったりするAPIのような、低速な外部サービスとやり取りすることはよくあります。このような場合、専用の「低速」プールを持つようにスレッドプールを分割すると有益です。これにより、低速なエンドポイントがサーバーのリソース/スレッドをすべて消費してしまうのを防ぎ、コネクションプールのように低速なエンドポイントへのリクエストの同時実行数を制限できます。 - -```caddyfile -{ - frankenphp { - max_threads 100 # すべてのワーカーで共有される最大100スレッド - } -} - -example.com { - php_server { - root /app/public # アプリケーションのルート - worker index.php { - match /slow-endpoint/* # パスが /slow-endpoint/* のすべてリクエストはこのスレッドプールによって処理される - num 10 # /slow-endpoint/* に一致するリクエストの最小スレッド数10 - } - worker index.php { - match * # その他すべてのリクエストは個別に処理される - num 20 # 低速なエンドポイントがハングし始めた場合でも、その他のリクエストに最小20スレッド - } - } -} -``` - -一般的に、メッセージキューなどの適切なメカニズムを使用して、非常に低速なエンドポイントを非同期に処理することも推奨されます。 diff --git a/docs/ja/production.md b/docs/ja/production.md index f028185071..cd5f821728 100644 --- a/docs/ja/production.md +++ b/docs/ja/production.md @@ -2,9 +2,9 @@ このチュートリアルでは、Docker Composeを使用して単一サーバーにPHPアプリケーションをデプロイする方法を学びます。 -Symfonyを使用している場合は、Symfony Dockerプロジェクトの「[本番環境へのデプロイ](https://github.com/dunglas/symfony-docker/blob/main/docs/production.md)」ドキュメントエントリー(FrankenPHPを使用しています)を参照してください。 +Symfonyを使用している場合は、Symfony Dockerプロジェクトの「[本番環境へのデプロイ](https://github.com/dunglas/symfony-docker/blob/main/docs/production.md)」ドキュメントを参照してください。 -API Platform(こちらもFrankenPHPを使用しています)を使用している場合は、[フレームワークのデプロイドキュメント](https://api-platform.com/docs/deployment/)を参照してください。 +API Platformを使用している場合は、[フレームワークのデプロイドキュメント](https://api-platform.com/docs/deployment/)を参照してください。 ## アプリの準備 diff --git a/docs/ja/static.md b/docs/ja/static.md index 27991085b4..1887201221 100644 --- a/docs/ja/static.md +++ b/docs/ja/static.md @@ -1,7 +1,7 @@ # 静的ビルドの作成 PHPライブラリのローカルインストールを使用する代わりに、 -素晴らしい[static-php-cli プロジェクト](https://github.com/crazywhalecc/static-php-cli)を利用して、FrankenPHPの静的またはほぼ静的なビルドを作成することが可能です(プロジェクト名に「CLI」とありますが、CLIだけでなく全てのSAPIをサポートしています)。 +[static-php-cli プロジェクト](https://github.com/crazywhalecc/static-php-cli)を利用して、FrankenPHPの静的またはほぼ静的なビルドを作成することが可能です(プロジェクト名に「CLI」とありますが、CLIだけでなく全てのSAPIをサポートしています)。 この方法を使えば、PHPインタープリター、Caddy Webサーバー、FrankenPHPをすべて含んだ単一でポータブルなバイナリを作成できます! @@ -73,7 +73,7 @@ docker buildx bake \ ### 追加のCaddyモジュール -Caddyの追加モジュールを追加したり、[xcaddy](https://github.com/caddyserver/xcaddy)に他の引数を渡したりするには、`XCADDY_ARGS`というDocker ARGを使用します: +Caddyの拡張モジュールを追加したい場合は、`XCADDY_ARGS`というDocker引数を使用して、[xcaddy](https://github.com/caddyserver/xcaddy)に渡す引数を以下のように指定できます: ```console docker buildx bake \ @@ -87,7 +87,7 @@ docker buildx bake \ > [!TIP] > > cbrotli、Mercure、Vulcainモジュールは、`XCADDY_ARGS`が空または設定されていない場合はデフォルトで含まれます。 -> `XCADDY_ARGS`の値をカスタマイズする場合、含めたいのであれば明示的にそれらを含める必要があります。 +> `XCADDY_ARGS`の値をカスタマイズする場合、デフォルトのモジュールは含まれなくなるため、必要なものは明示的に記述してください。 [ビルドのカスタマイズ](#ビルドのカスタマイズ)も参照してください @@ -121,7 +121,7 @@ cd frankenphp - `PHP_VERSION`: 使用するPHPのバージョン - `PHP_EXTENSIONS`: ビルドするPHP拡張([サポートされる拡張のリスト](https://static-php.dev/en/guide/extensions.html)) - `PHP_EXTENSION_LIBS`: 拡張モジュールに追加機能を持たせるためにビルドする追加ライブラリ -- `XCADDY_ARGS`: Caddyの追加モジュールを追加したり、[xcaddy](https://github.com/caddyserver/xcaddy)に他の引数を渡したりするための引数 +- `XCADDY_ARGS`: 追加のCaddyモジュールを導入するなど[xcaddy](https://github.com/caddyserver/xcaddy)に渡す引数 - `EMBED`: バイナリに埋め込むPHPアプリケーションのパス - `CLEAN`: 指定するとlibphpおよびそのすべての依存関係がスクラッチからビルドされます(キャッシュなし) - `NO_COMPRESS`: UPXを使用して結果のバイナリを圧縮しない diff --git a/docs/ja/wordpress.md b/docs/ja/wordpress.md deleted file mode 100644 index 6749ef51bd..0000000000 --- a/docs/ja/wordpress.md +++ /dev/null @@ -1,59 +0,0 @@ -# WordPress - -自動HTTPS、HTTP/3、Zstandard圧縮を備えたモダンで高性能なスタックを楽しむために、FrankenPHPで[WordPress](https://wordpress.org/)を実行してください。 - -## 最小限のインストール - -1. [WordPressをダウンロード](https://wordpress.org/download/) -2. ZIPアーカイブを解凍し、解凍したディレクトリでターミナルを開きます -3. 実行: - - ```console - frankenphp php-server - ``` - -4. `http://localhost/wp-admin/`にアクセスし、インストール手順に従います -5. お楽しみください! - -本番環境向けのセットアップには、次のような`Caddyfile`を使用して`frankenphp run`を実行することをお勧めします: - -```caddyfile -example.com - -php_server -encode zstd br gzip -log -``` - -## ホットリロード - -WordPressで[ホットリロード](hot-reload.md)機能を使用するには、[Mercure](mercure.md)を有効にし、`hot_reload`サブディレクティブを`Caddyfile`の`php_server`ディレクティブに追加します: - -```caddyfile -localhost - -mercure { - anonymous -} - -php_server { - hot_reload -} -``` - -次に、WordPressテーマの`functions.php`ファイルにJavaScriptライブラリをロードするために必要なコードを追加します: - -```php -function hot_reload() { - ?> - - - - - - [!TIP] -> このセクションは、FrankenPHPワーカーモードのネイティブサポートが導入されたSymfony 7.4より前のバージョンでのみ必要です。 - FrankenPHPのワーカーモードは[Symfony Runtime Component](https://symfony.com/doc/current/components/runtime.html)によってサポートされています。 ワーカーでSymfonyアプリケーションを開始するには、FrankenPHP用の[PHP Runtime](https://github.com/php-runtime/runtime)パッケージをインストールします: @@ -71,7 +67,7 @@ docker run \ boot(); -// パフォーマンス向上のため、ループの外でハンドラーを定義(処理量を削減) +// ループの外側にハンドラーを配置してパフォーマンスを向上(処理量を減らす) $handler = static function () use ($myApp) { - try { - // リクエストを受信すると呼び出され、 - // スーパーグローバル、php://input などがリセットされます - echo $myApp->handle($_GET, $_POST, $_COOKIE, $_FILES, $_SERVER); - } catch (\Throwable $exception) { - // `set_exception_handler` はワーカースクリプトが終了するときにのみ呼び出されるため、 - // 期待する動作と異なる場合があります。ここで例外をキャッチして処理します - (new \MyCustomExceptionHandler)->handleException($exception); - } + // リクエストを受信した際に呼び出され、 + // スーパーグローバルや 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レスポンス送信後に何らかの処理を実行 + // HTTPレスポンスの送信後に何か処理を行います $myApp->terminate(); - // ページ生成中にガベージコレクタがトリガーされる可能性を減らすため、ここでガベージコレクタを呼び出します + // ページ生成の途中でガベージコレクタが起動する可能性を減らすために、ここでガベージコレクタを明示的に呼び出す。 gc_collect_cycles(); if (!$keepRunning) break; @@ -188,3 +178,4 @@ $handler = static function () use ($workerServer) { }; // ... +``` diff --git a/docs/ja/x-sendfile.md b/docs/ja/x-sendfile.md index f4d4694dc4..2e5ec256f5 100644 --- a/docs/ja/x-sendfile.md +++ b/docs/ja/x-sendfile.md @@ -68,3 +68,4 @@ BinaryFileResponse::trustXSendfileTypeHeader(); $response = new BinaryFileResponse(__DIR__.'/../private-files/file.txt'); // ... +``` diff --git a/docs/known-issues.md b/docs/known-issues.md index f6912960e2..da22690373 100644 --- a/docs/known-issues.md +++ b/docs/known-issues.md @@ -7,7 +7,7 @@ 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 | - * | +| [newrelic](https://docs.newrelic.com/docs/apm/agents/php-agent/getting-started/introduction-new-relic-php/) | Not thread-safe | - | ## Buggy PHP Extensions diff --git a/docs/pt-br/classic.md b/docs/pt-br/classic.md index 42ee827df8..e41c27d27a 100644 --- a/docs/pt-br/classic.md +++ b/docs/pt-br/classic.md @@ -1,9 +1,28 @@ -# Usando o Modo Clássico +# Usando o modo clássico -Sem nenhuma configuração adicional, o FrankenPHP opera no modo clássico. Neste modo, o FrankenPHP funciona como um servidor PHP tradicional, servindo diretamente arquivos PHP. Isso o torna um substituto direto e sem falhas para PHP-FPM ou Apache com mod_php. +Sem nenhuma configuração adicional, o FrankenPHP opera no modo clássico. +Neste modo, o FrankenPHP funciona como um servidor PHP tradicional, servindo +diretamente arquivos PHP. +Isso o torna um substituto perfeito para PHP-FPM ou Apache com mod_php. -Assim como o Caddy, o FrankenPHP aceita um número ilimitado de conexões e usa um [número fixo de threads](config.md#caddyfile-config) para servi-las. O número de conexões aceitas e enfileiradas é limitado apenas pelos recursos disponíveis do sistema. O pool de threads do PHP opera com um número fixo de threads inicializadas na inicialização, comparável ao modo estático do PHP-FPM. Também é possível permitir que as threads [escalem automaticamente em tempo de execução](performance.md#max_threads), similar ao modo dinâmico do PHP-FPM. +Semelhante ao Caddy, o FrankenPHP aceita um número ilimitado de conexões e usa +um [número fixo de threads](config.md#configuracao-do-caddyfile) para servi-las. +O número de conexões aceitas e enfileiradas é limitado apenas pelos recursos +disponíveis no sistema. +O pool de threads do PHP opera com um número fixo de threads inicializadas na +inicialização, comparável ao modo estático do PHP-FPM. +Também é possível permitir que as threads +[escalem automaticamente em tempo de execução](performance.md#max_threads), +semelhante ao modo dinâmico do PHP-FPM. -As conexões enfileiradas aguardarão indefinidamente até que uma thread PHP esteja disponível para servi-las. Para evitar isso, você pode usar a `max_wait_time` [configuração](config.md#caddyfile-config) na configuração global do FrankenPHP para limitar a duração que uma requisição pode esperar por uma thread PHP livre antes de ser rejeitada. Além disso, você pode definir um [tempo limite de escrita razoável no Caddy](https://caddyserver.com/docs/caddyfile/options#timeouts). +As conexões enfileiradas aguardarão indefinidamente até que uma thread PHP +esteja disponível para servi-las. +Para evitar isso, você pode usar a +[configuração](config.md#configuracao-do-caddyfile) `max_wait_time` na +configuração global do FrankenPHP para limitar o tempo que uma requisição pode +esperar por uma thread PHP livre antes de ser rejeitada. +Além disso, você pode definir um +[tempo limite de escrita razoável no Caddy](https://caddyserver.com/docs/caddyfile/options#timeouts). -Cada instância do Caddy ativará apenas um pool de threads do FrankenPHP, que será compartilhado entre todos os blocos `php_server`. +Cada instância do Caddy ativará apenas um pool de threads do FrankenPHP, que +será compartilhado entre todos os blocos `php_server`. diff --git a/docs/pt-br/compile.md b/docs/pt-br/compile.md index 76f3dd4e5c..a2df4221ea 100644 --- a/docs/pt-br/compile.md +++ b/docs/pt-br/compile.md @@ -1,9 +1,11 @@ # Compilar a partir do código-fonte -Este documento explica como criar um binário FrankenPHP que carregará o PHP como uma biblioteca dinâmica. +Este documento explica como criar um binário FrankenPHP que carregará o PHP como +uma biblioteca dinâmica. Este é o método recomendado. -Como alternativa, [compilações totalmente e principalmente estáticas](static.md) também podem ser criadas. +Como alternativa, [compilações totalmente e principalmente estáticas](static.md) +também podem ser criadas. ## Instalar o PHP @@ -11,11 +13,14 @@ O FrankenPHP é compatível com PHP 8.2 e versões superiores. ### Com o Homebrew (Linux e Mac) -A maneira mais fácil de instalar uma versão da `libphp` compatível com o FrankenPHP é usar os pacotes ZTS fornecidos pelo [Homebrew PHP](https://github.com/shivammathur/homebrew-php). +A maneira mais fácil de instalar uma versão da `libphp` compatível com o +FrankenPHP é usar os pacotes ZTS fornecidos pelo +[Homebrew PHP](https://github.com/shivammathur/homebrew-php). Primeiro, se ainda não o fez, instale o [Homebrew](https://brew.sh). -Em seguida, instale a variante ZTS do PHP, o Brotli (opcional, para suporte à compressão) e o watcher (opcional, para detecção de alterações em arquivos): +Em seguida, instale a variante ZTS do PHP, o Brotli (opcional, para suporte à +compressão) e o watcher (opcional, para detecção de alterações em arquivos): ```console brew install shivammathur/php/php-zts brotli watcher @@ -24,17 +29,21 @@ brew link --overwrite --force shivammathur/php/php-zts ### Compilando o PHP -Alternativamente, você pode compilar o PHP a partir do código-fonte com as opções necessárias para o FrankenPHP seguindo estes passos. +Alternativamente, você pode compilar o PHP a partir do código-fonte com as +opções necessárias para o FrankenPHP seguindo estes passos. -Primeiro, [obtenha o código-fonte do PHP](https://www.php.net/downloads.php) e extraia-os: +Primeiro, [obtenha o código-fonte do PHP](https://www.php.net/downloads.php) e +extraia-os: ```console tar xf php-* cd php-*/ ``` -Em seguida, execute o script `configure` com as opções necessárias para sua plataforma. -As seguintes flags `./configure` são obrigatórias, mas você pode adicionar outras, por exemplo, para compilar extensões ou recursos adicionais. +Em seguida, execute o script `configure` com as opções necessárias para sua +plataforma. +As seguintes flags `./configure` são obrigatórias, mas você pode adicionar +outras, por exemplo, para compilar extensões ou recursos adicionais. #### Linux @@ -48,7 +57,8 @@ As seguintes flags `./configure` são obrigatórias, mas você pode adicionar ou #### Mac -Use o gerenciador de pacotes [Homebrew](https://brew.sh/) para instalar as dependências necessárias e opcionais: +Use o gerenciador de pacotes [Homebrew](https://brew.sh/) para instalar as +dependências necessárias e opcionais: ```console brew install libiconv bison brotli re2c pkg-config watcher @@ -76,14 +86,15 @@ sudo make install ## Instalar dependências opcionais -Alguns recursos do FrankenPHP dependem de dependências opcionais do sistema que devem ser instaladas. -Alternativamente, esses recursos podem ser desabilitados passando as tags de compilação para o compilador Go. +Alguns recursos do FrankenPHP dependem de dependências opcionais do sistema que +devem ser instaladas. +Alternativamente, esses recursos podem ser desabilitados passando as tags de +compilação para o compilador Go. -| Recurso | Dependência | Tag de compilação para desabilitá-lo | -| ------------------------------------- | ------------------------------------------------------------------------------------------------------------ | ------------------------------------ | -| Compressão Brotli | [Brotli](https://github.com/google/brotli) | nobrotli | -| Reiniciar workers ao alterar arquivos | [Watcher C](https://github.com/e-dant/watcher/tree/release/watcher-c) | nowatcher | -| [Mercure](mercure.md) | [Biblioteca Go do Mercure](https://pkg.go.dev/github.com/dunglas/mercure) (instalada automaticamente, licenciada sob AGPL) | nomercure | +| Recurso | Dependência | Tag de compilação para desabilitá-lo | +| ------------------------------------- | --------------------------------------------------------------------- | ------------------------------------ | +| Compressão Brotli | [Brotli](https://github.com/google/brotli) | `nobrotli` | +| Reiniciar workers ao alterar arquivos | [Watcher C](https://github.com/e-dant/watcher/tree/release/watcher-c) | `nowatcher` | ## Compilando a aplicação Go @@ -91,8 +102,11 @@ Agora você pode compilar o binário final. ### Usando o `xcaddy` -A maneira recomendada é usar o [`xcaddy`](https://github.com/caddyserver/xcaddy) para compilar o FrankenPHP. -O `xcaddy` também permite adicionar facilmente [módulos Caddy personalizados](https://caddyserver.com/docs/modules/) e extensões FrankenPHP: +A maneira recomendada é usar o [`xcaddy`](https://github.com/caddyserver/xcaddy) +para compilar o FrankenPHP. +O `xcaddy` também permite adicionar facilmente +[módulos Caddy personalizados](https://caddyserver.com/docs/modules/) e +extensões FrankenPHP: ```console CGO_ENABLED=1 \ @@ -101,21 +115,19 @@ CGO_CFLAGS=$(php-config --includes) \ CGO_LDFLAGS="$(php-config --ldflags) $(php-config --libs)" \ xcaddy build \ --output frankenphp \ - --with github.com/dunglas/frankenphp/caddy \ + --with github.com/php/frankenphp/caddy \ --with github.com/dunglas/mercure/caddy \ - --with github.com/dunglas/vulcain/caddy \ - --with github.com/dunglas/caddy-cbrotli + --with github.com/dunglas/vulcain/caddy # Adicione módulos Caddy e extensões FrankenPHP extras aqui - # Opcionalmente, se você quiser compilar a partir do seu código-fonte frankenphp: - # --with github.com/dunglas/frankenphp=$(pwd) \ - # --with github.com/dunglas/frankenphp/caddy=$(pwd)/caddy ``` > [!TIP] > > Se você estiver usando a `libc` `musl` (o padrão no Alpine Linux) e Symfony, > pode ser necessário aumentar o tamanho da pilha padrão. -> Caso contrário, você poderá receber erros como `PHP Fatal error: Maximum call stack size of 83360 bytes reached during compilation. Try splitting expression`. +> Caso contrário, você poderá receber erros como `PHP Fatal error: Maximum call +stack size of 83360 bytes reached during compilation. +Try splitting expression`. > > Para fazer isso, altere a variável de ambiente `XCADDY_GO_BUILD_FLAGS` para > algo como @@ -125,7 +137,8 @@ xcaddy build \ ### Sem o `xcaddy` -Alternativamente, é possível compilar o FrankenPHP sem o `xcaddy` usando o comando `go` diretamente: +Alternativamente, é possível compilar o FrankenPHP sem o `xcaddy` usando o +comando `go` diretamente: ```console curl -L https://github.com/php/frankenphp/archive/refs/heads/main.tar.gz | tar xz diff --git a/docs/pt-br/config.md b/docs/pt-br/config.md index 68c0691c0a..e36a144d75 100644 --- a/docs/pt-br/config.md +++ b/docs/pt-br/config.md @@ -1,40 +1,27 @@ # Configuração -FrankenPHP, Caddy, bem como os módulos [Mercure](mercure.md) e [Vulcain](https://vulcain.rocks), podem ser configurados usando [os formatos suportados pelo Caddy](https://caddyserver.com/docs/getting-started#your-first-config). - -O formato mais comum é o `Caddyfile`, que é um formato de texto simples e legível por humanos. -Por padrão, o FrankenPHP procurará por um `Caddyfile` no diretório atual. +FrankenPHP, Caddy, bem como os módulos Mercure e Vulcain, podem ser configurados +usando +[os formatos suportados pelo Caddy](https://caddyserver.com/docs/getting-started#your-first-config). + +Nas [imagens Docker](docker.md), o `Caddyfile` está localizado em +`/etc/frankenphp/Caddyfile`. +O binário estático também procurará pelo `Caddyfile` no diretório onde o comando +`frankenphp run` é executado. Você pode especificar um caminho personalizado com a opção `-c` ou `--config`. -Um `Caddyfile` mínimo para servir uma aplicação PHP é mostrado abaixo: - -```caddyfile -# O nome do host para responder -localhost - -# Opcionalmente, o diretório para servir arquivos, caso contrário, o padrão é o diretório atual -#root public/ -php_server -``` - -Um `Caddyfile` mais avançado, que habilita mais recursos e fornece variáveis de ambiente convenientes, é disponibilizado [no repositório do FrankenPHP](https://github.com/php/frankenphp/blob/main/caddy/frankenphp/Caddyfile) e com as imagens Docker. - -O próprio PHP pode ser configurado [usando um arquivo `php.ini`](https://www.php.net/manual/en/configuration.file.php). +O próprio PHP pode ser configurado +[usando um arquivo `php.ini`](https://www.php.net/manual/pt_BR/configuration.file.php). -Dependendo do seu método de instalação, o FrankenPHP e o interpretador PHP procurarão por arquivos de configuração nos locais descritos abaixo. +Dependendo do seu método de instalação, o interpretador PHP procurará por +arquivos de configuração nos locais descritos acima. ## Docker -FrankenPHP: - -- `/etc/frankenphp/Caddyfile`: o arquivo de configuração principal -- `/etc/frankenphp/Caddyfile.d/*.caddyfile`: arquivos de configuração adicionais que são carregados automaticamente - -PHP: - -- `php.ini`: `/usr/local/etc/php/php.ini` (nenhum `php.ini` é fornecido por padrão) -- Arquivos de configuração adicionais: `/usr/local/etc/php/conf.d/*.ini` -- Extensões PHP: `/usr/local/lib/php/extensions/no-debug-zts-/` +- `php.ini`: `/usr/local/etc/php/php.ini` (nenhum `php.ini` é fornecido por + padrão); +- Arquivos de configuração adicionais: `/usr/local/etc/php/conf.d/*.ini`; +- Extensões PHP: `/usr/local/lib/php/extensions/no-debug-zts-/`; - Você deve copiar um template oficial fornecido pelo projeto PHP: ```dockerfile @@ -49,126 +36,125 @@ RUN cp $PHP_INI_DIR/php.ini-development $PHP_INI_DIR/php.ini ## Pacotes RPM e Debian -FrankenPHP: - -- `/etc/frankenphp/Caddyfile`: o arquivo de configuração principal -- `/etc/frankenphp/Caddyfile.d/*.caddyfile`: arquivos de configuração adicionais que são carregados automaticamente - -PHP: - -- `php.ini`: `/etc/php-zts/php.ini` (um arquivo `php.ini` com predefinições de produção é fornecido por padrão) -- Arquivos de configuração adicionais: `/etc/php-zts/conf.d/*.ini` +- `php.ini`: `/etc/frankenphp/php.ini` (um arquivo `php.ini` com configurações + de produção é fornecido por padrão); +- Arquivos de configuração adicionais: `/etc/frankenphp/php.d/*.ini`; +- Extensões PHP: `/usr/lib/frankenphp/modules/`. ## Binário estático -FrankenPHP: - -- No diretório de trabalho atual: `Caddyfile` - -PHP: - -- `php.ini`: O diretório no qual `frankenphp run` ou `frankenphp php-server` é executado, depois `/etc/frankenphp/php.ini` -- Arquivos de configuração adicionais: `/etc/frankenphp/php.d/*.ini` -- Extensões PHP: não podem ser carregadas, empacote-as no próprio binário -- Copie um dos `php.ini-production` ou `php.ini-development` fornecidos [nas fontes do PHP](https://github.com/php/php-src/). +- `php.ini`: O diretório no qual `frankenphp run` ou `frankenphp php-server` é + executado e, em seguida, `/etc/frankenphp/php.ini`; +- Arquivos de configuração adicionais: `/etc/frankenphp/php.d/*.ini`; +- Extensões PHP: não podem ser carregadas, empacote-as no próprio binário; +- Copie um dos arquivos `php.ini-production` ou `php.ini-development` fornecidos + [no código-fonte do PHP](https://github.com/php/php-src/). ## Configuração do Caddyfile -As diretivas HTTP `php_server` ou `php` [diretivas HTTP](https://caddyserver.com/docs/caddyfile/concepts#directives) podem ser usadas dentro dos blocos de site para servir sua aplicação PHP. +As [diretivas HTTP](https://caddyserver.com/docs/caddyfile/concepts#directives) +`php_server` ou `php` podem ser usadas dentro dos blocos de site para servir sua +aplicação PHP. Exemplo mínimo: ```caddyfile localhost { - # Habilita compressão (opcional) - encode zstd br gzip - # Executa arquivos PHP no diretório atual e serve assets - php_server + # Habilita compressão (opcional) + encode zstd br gzip + # Executa arquivos PHP no diretório atual e serve assets + php_server } ``` -Você também pode configurar explicitamente o FrankenPHP usando a [opção global](https://caddyserver.com/docs/caddyfile/concepts#global-options) `frankenphp`: +Você também pode configurar explicitamente o FrankenPHP usando a opção global: +A [opção global](https://caddyserver.com/docs/caddyfile/concepts#global-options) +`frankenphp` pode ser usada para configurar o FrankenPHP. ```caddyfile { - frankenphp { - num_threads # Define o número de threads PHP a serem iniciadas. Padrão: 2x o número de CPUs disponíveis. - max_threads # Limita o número de threads PHP adicionais que podem ser iniciadas em tempo de execução. Padrão: num_threads. Pode ser definido como 'auto'. - max_wait_time # Define o tempo máximo que uma requisição pode esperar por uma thread PHP livre antes de atingir o tempo limite. Padrão: desabilitado. - php_ini # Define uma diretiva php.ini. Pode ser usada várias vezes para definir múltiplas diretivas. - worker { - file # Define o caminho para o worker script. - num # Define o número de threads PHP a serem iniciadas, o padrão é 2x o número de CPUs disponíveis. - env # Define uma variável de ambiente extra para o valor fornecido. Pode ser especificada mais de uma vez para múltiplas variáveis de ambiente. - watch # Define o caminho para monitorar alterações em arquivos. Pode ser especificada mais de uma vez para múltiplos caminhos. - name # Define o nome do worker, usado em logs e métricas. Padrão: caminho absoluto do arquivo do worker. - max_consecutive_failures # Define o número máximo de falhas consecutivas antes do worker ser considerado inoperante. -1 significa que o worker sempre reiniciará. Padrão: 6. - } - } + frankenphp { + num_threads # Define o número de threads PHP a serem iniciadas. Padrão: 2x o número de CPUs disponíveis. + max_threads # Limita o número de threads PHP adicionais que podem ser iniciadas em tempo de execução. Padrão: num_threads. Pode ser definido como 'auto'. + max_wait_time # Define o tempo máximo que uma requisição pode esperar por uma thread PHP livre antes de atingir o tempo limite. Padrão: disabled. + php_ini # Define uma diretiva php.ini. Pode ser usada várias vezes para definir múltiplas diretivas. + worker { + file # Define o caminho para o worker script. + num # Define o número de threads PHP a serem iniciadas, o padrão é 2x o número de CPUs disponíveis. + env # Define uma variável de ambiente extra para o valor fornecido. Pode ser especificada mais de uma vez para múltiplas variáveis de ambiente. + watch # Define o caminho para monitorar alterações em arquivos. Pode ser especificada mais de uma vez para múltiplos caminhos. + name # Define o nome do worker, usado em logs e métricas. Padrão: caminho absoluto do arquivo do worker. + max_consecutive_failures # Define o número máximo de falhas consecutivas antes do worker ser considerado inoperante. -1 significa que o worker sempre reiniciará. Padrão: 6. + } + } } # ... ``` -Alternativamente, você pode usar a forma abreviada de uma linha da opção `worker`: +Alternativamente, você pode usar a forma abreviada de uma linha da opção +`worker`: ```caddyfile { - frankenphp { - worker - } + frankenphp { + worker + } } # ... ``` -Você também pode definir vários workers se servir várias aplicações no mesmo servidor: +Você também pode definir vários workers se servir várias aplicações no mesmo +servidor: ```caddyfile app.example.com { root /caminho/para/aplicacao/public - php_server { - root /caminho/para/aplicacao/public # permite melhor armazenamento em cache - worker index.php - } + php_server { + root /caminho/para/aplicacao/public # permite melhor armazenamento em cache + worker index.php + } } outra.example.com { root /caminho/para/outra/aplicacao/public - php_server { - root /caminho/para/outra/aplicacao/public - worker index.php - } + php_server { + root /caminho/para/outra/aplicacao/public + worker index.php + } } # ... ``` -Usar a diretiva `php_server` geralmente é o que você precisa, -mas se precisar de controle total, você pode usar a diretiva `php` de mais baixo nível. +Usar a diretiva `php_server` geralmente é o que você precisa, mas se precisar de +controle total, você pode usar a diretiva `php` de mais baixo nível. A diretiva `php` passa toda a entrada para o PHP, em vez de primeiro verificar -se é um arquivo PHP ou não. Leia mais sobre isso na [página de desempenho](performance.md#try_files). +se é um arquivo PHP ou não. +Leia mais sobre isso na [página de desempenho](performance.md#try_files). Usar a diretiva `php_server` é equivalente a esta configuração: ```caddyfile route { - # Adiciona barra final para requisições de diretório - @canonicalPath { - file {path}/index.php - not path */ - } - redir @canonicalPath {path}/ 308 - # Se o arquivo requisitado não existir, tenta os arquivos 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 + # Adiciona barra final para requisições de diretório + @canonicalPath { + file {path}/index.php + not path */ + } + redir @canonicalPath {path}/ 308 + # Se o arquivo requisitado não existir, tenta os arquivos 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 } ``` @@ -176,20 +162,20 @@ As diretivas `php_server` e `php` têm as seguintes opções: ```caddyfile php_server [] { - root # Define a pasta raiz para o site. Padrão: diretiva `root`. - split_path # Define as substrings para dividir o URI em duas partes. A primeira substring correspondente será usada para separar as "informações de caminho" do caminho. A primeira parte é sufixada com a substring correspondente e será assumida como o nome real do recurso (script CGI). A segunda parte será definida como PATH_INFO para o script usar. Padrão: `.php` - resolve_root_symlink false # Desabilita a resolução do diretório `root` para seu valor real avaliando um link simbólico, se houver (habilitado por padrão). - env # Define uma variável de ambiente extra para o valor fornecido. Pode ser especificada mais de uma vez para múltiplas variáveis de ambiente. - file_server off # Desabilita a diretiva interna file_server. - worker { # Cria um worker específico para este servidor. Pode ser especificada mais de uma vez para múltiplos workers. - file # Define o caminho para o worker script, pode ser relativo à raiz do php_server. - num # Define o número de threads PHP a serem iniciadas, o padrão é 2x o número de threads disponíveis. - name # Define o nome para o worker, usado em logs e métricas. Padrão: caminho absoluto do arquivo do worker. Sempre começa com m# quando definido em um bloco php_server. - watch # Define o caminho para monitorar alterações em arquivos. Pode ser especificada mais de uma vez para múltiplos caminhos. - env # Define uma variável de ambiente extra para o valor fornecido. Pode ser especificada mais de uma vez para múltiplas variáveis de ambiente. As variáveis de ambiente para este worker também são herdadas do pai do php_server, mas podem ser sobrescritas aqui. - match # Corresponde o worker a um padrão de caminho. Substitui try_files e só pode ser usada na diretiva php_server. - } - worker # Também pode usar a forma abreviada, como no bloco global frankenphp. + root # Define a pasta raiz para o site. Padrão: diretiva `root`. + split_path # Define as substrings para dividir o URI em duas partes. A primeira substring correspondente será usada para separar as "informações de caminho" do caminho. A primeira parte é sufixada com a substring correspondente e será assumida como o nome real do recurso (script CGI). A segunda parte será definida como PATH_INFO para o script usar. Padrão: `.php` + resolve_root_symlink false # Desabilita a resolução do diretório `root` para seu valor real avaliando um link simbólico, se houver (habilitado por padrão). + env # Define uma variável de ambiente extra para o valor fornecido. Pode ser especificada mais de uma vez para múltiplas variáveis de ambiente. + file_server off # Desabilita a diretiva interna file_server. + worker { # Cria um worker específico para este servidor. Pode ser especificada mais de uma vez para múltiplos workers. + file # Define o caminho para o worker script, pode ser relativo à raiz do php_server. + num # Define o número de threads PHP a serem iniciadas, o padrão é 2x o número de threads disponíveis. + name # Define o nome do worker, usado em logs e métricas. Padrão: caminho absoluto do arquivo do worker. Sempre começa com m# quando definido em um bloco php_server. + watch # Define o caminho para monitorar alterações em arquivos. Pode ser especificada mais de uma vez para múltiplos caminhos. + env # Define uma variável de ambiente extra para o valor fornecido. Pode ser especificada mais de uma vez para múltiplas variáveis de ambiente. As variáveis de ambiente para este worker também são herdadas do pai do php_server, mas podem ser sobrescritas aqui. + match # Corresponde o worker a um padrão de caminho. Substitui try_files e só pode ser usada na diretiva php_server. + } + worker # Também pode usar a forma abreviada, como no bloco global frankenphp. } ``` @@ -204,46 +190,48 @@ Isso é útil para ambientes de desenvolvimento. ```caddyfile { - frankenphp { - worker { - file /caminho/para/aplicacao/public/worker.php - watch - } - } + frankenphp { + worker { + file /caminho/para/aplicacao/public/worker.php + watch + } + } } ``` -Este recurso é frequentemente usado em combinação com [recarregamento a quente](hot-reload.md). - Se o diretório `watch` não for especificado, ele usará o valor padrão -`./**/*.{env,php,twig,yaml,yml}`, -que monitora todos os arquivos `.env`, `.php`, `.twig`, `.yaml` e `.yml` no -diretório e subdiretórios onde o processo FrankenPHP foi iniciado. Você pode, em vez disso, também especificar um ou mais diretórios por meio de um +`./**/*.{php,yaml,yml,twig,env}`, +que monitora todos os arquivos `.php`, `.yaml`, `.yml`, `.twig` e `.env` no +diretório e subdiretórios onde o processo FrankenPHP foi iniciado. +Você também pode especificar um ou mais diretórios por meio de um [padrão de nome de arquivo shell](https://pkg.go.dev/path/filepath#Match): ```caddyfile { - frankenphp { - worker { - file /caminho/para/aplicacao/public/worker.php - watch /caminho/para/aplicacao # monitora todos os arquivos em todos os subdiretórios de /caminho/para/aplicacao - watch /caminho/para/aplicacao/*.php # monitora arquivos terminados em .php em /caminho/para/aplicacao - watch /caminho/para/aplicacao/**/*.php # monitora arquivos PHP em /caminho/para/aplicacao e subdiretórios - watch /caminho/para/aplicacao/**/*.{php,twig} # monitora arquivos PHP e Twig em /caminho/para/aplicacao e subdiretórios - } - } + frankenphp { + worker { + file /caminho/para/aplicacao/public/worker.php + watch /caminho/para/aplicacao # monitora todos os arquivos em todos os subdiretórios de /caminho/para/aplicacao + watch /caminho/para/aplicacao/*.php # monitora arquivos terminados em .php em /caminho/para/aplicacao + watch /caminho/para/aplicacao/**/*.php # monitora arquivos PHP em /caminho/para/aplicacao e subdiretórios + watch /caminho/para/aplicacao/**/*.{php,twig} # monitora arquivos PHP e Twig em /caminho/para/aplicacao e subdiretórios + } + } } ``` - O padrão `**` significa monitoramento recursivo -- Diretórios também podem ser relativos (ao local de início do processo FrankenPHP) -- Se você tiver vários workers definidos, todos eles serão reiniciados quando um arquivo for alterado -- Tenha cuidado ao monitorar arquivos criados em tempo de execução (como logs), pois eles podem causar reinicializações indesejadas de workers. +- Diretórios também podem ser relativos (ao local de início do processo + FrankenPHP) +- Se você tiver vários workers definidos, todos eles serão reiniciados quando um + arquivo for alterado +- Tenha cuidado ao monitorar arquivos criados em tempo de execução (como logs), + pois eles podem causar reinicializações indesejadas de workers. O monitor de arquivos é baseado no [e-dant/watcher](https://github.com/e-dant/watcher). -## Correspondendo o Worker a um caminho +## Correspondendo o worker a um caminho Em aplicações PHP tradicionais, os scripts são sempre colocados no diretório público. @@ -260,37 +248,81 @@ padrão de caminho. ```caddyfile { - frankenphp { - php_server { - worker { - file /caminho/para/worker.php # arquivo pode estar fora do caminho público - match /api/* # todas as requisições que começam com /api/ serão tratadas por este worker - } - } - } + frankenphp { + php_server { + worker { + file /caminho/para/worker.php # arquivo pode estar fora do caminho público + match /api/* # todas as requisições que começam com /api/ serão tratadas por este worker + } + } + } +} +``` + +### Full Duplex (HTTP/1) + +Ao usar HTTP/1.x, pode ser desejável habilitar o modo full-duplex para permitir +a gravação de uma resposta antes que todo o corpo tenha sido lido. +(por exemplo: WebSocket, Server-Sent Events, etc.) + +Esta é uma configuração opcional que precisa ser adicionada às opções globais no +`Caddyfile`: + +```caddyfile +{ + servers { + enable_full_duplex + } } ``` +> [!CAUTION] +> +> Habilitar esta opção pode causar deadlock em clientes HTTP/1.x antigos que não +> suportam full-duplex. +> Isso também pode ser configurado usando a configuração de ambiente +> `CADDY_GLOBAL_OPTIONS`: + +```sh +CADDY_GLOBAL_OPTIONS="servers { + enable_full_duplex +}" +``` + +Você pode encontrar mais informações sobre esta configuração na +[documentação do Caddy](https://caddyserver.com/docs/caddyfile/options#enable-full-duplex). + ## Variáveis de ambiente -As seguintes variáveis de ambiente podem ser usadas para injetar diretivas Caddy no `Caddyfile` sem modificá-lo: +As seguintes variáveis de ambiente podem ser usadas para injetar diretivas Caddy +no `Caddyfile` sem modificá-lo: -- `SERVER_NAME`: altera [os endereços nos quais escutar](https://caddyserver.com/docs/caddyfile/concepts#addresses), os nomes de host fornecidos também serão usados para o certificado TLS gerado. -- `SERVER_ROOT`: altera o diretório raiz do site, o padrão é `public/`. -- `CADDY_GLOBAL_OPTIONS`: injeta [opções globais](https://caddyserver.com/docs/caddyfile/options). +- `SERVER_NAME`: altera + [os endereços nos quais escutar](https://caddyserver.com/docs/caddyfile/concepts#addresses), + os nomes de host fornecidos também serão usados para o certificado TLS gerado; +- `SERVER_ROOT`: altera o diretório raiz do site, o padrão é `public/`; +- `CADDY_GLOBAL_OPTIONS`: injeta + [opções globais](https://caddyserver.com/docs/caddyfile/options); - `FRANKENPHP_CONFIG`: injeta a configuração sob a diretiva `frankenphp`. -Quanto às SAPIs FPM e CLI, as variáveis de ambiente são expostas por padrão na superglobal `$_SERVER`. +Quanto às SAPIs FPM e CLI, as variáveis de ambiente são expostas por padrão na +superglobal `$_SERVER`. -O valor `S` da [diretiva `variables_order` do PHP](https://www.php.net/manual/en/ini.core.php#ini.variables-order) é sempre equivalente a `ES`, independentemente da colocação de `E` em outra parte desta diretiva. +O valor `S` da +[diretiva `variables_order` do PHP](https://www.php.net/manual/pt_BR/ini.core.php#ini.variables-order) +é sempre equivalente a `ES`, independentemente da colocação de `E` em outra +parte desta diretiva. ## Configuração do PHP -Para carregar [arquivos de configuração adicionais do PHP](https://www.php.net/manual/en/configuration.file.php#configuration.file.scan), +Para carregar +[arquivos de configuração adicionais do PHP](https://www.php.net/manual/pt_BR/configuration.file.php#configuration.file.scan), a variável de ambiente `PHP_INI_SCAN_DIR` pode ser usada. -Quando definida, o PHP carregará todos os arquivos com a extensão `.ini` presentes nos diretórios fornecidos. +Quando definida, o PHP carregará todos os arquivos com a extensão `.ini` +presentes nos diretórios fornecidos. -Você também pode alterar a configuração do PHP usando a diretiva `php_ini` no `Caddyfile`: +Você também pode alterar a configuração do PHP usando a diretiva `php_ini` no +`Caddyfile`: ```caddyfile { @@ -307,45 +339,10 @@ Você também pode alterar a configuração do PHP usando a diretiva `php_ini` n } ``` -### Desabilitando HTTPS - -Por padrão, o FrankenPHP habilitará automaticamente HTTPS para todos os nomes de host, incluindo `localhost`. -Se você deseja desabilitar o HTTPS (por exemplo, em um ambiente de desenvolvimento), você pode definir a variável de ambiente `SERVER_NAME` para `http://` ou `:80`: - -Alternativamente, você pode usar todos os outros métodos descritos na [documentação do Caddy](https://caddyserver.com/docs/automatic-https#activation). - -Se você deseja usar HTTPS com o endereço IP `127.0.0.1` em vez do nome de host `localhost`, por favor, leia a seção de [problemas conhecidos](known-issues.md#using-https127001-with-docker). - -### Full Duplex (HTTP/1) - -Ao usar `HTTP/1.x`, pode ser desejável habilitar o modo `full-duplex` para permitir a gravação de uma resposta antes que todo o corpo tenha sido lido. (por exemplo: [Mercure](mercure.md), WebSocket, Server-Sent Events, etc.) - -Esta é uma configuração opcional que precisa ser adicionada às opções globais no `Caddyfile`: - -```caddyfile -{ - servers { - enable_full_duplex - } -} -``` - -> [!CAUTION] -> -> Habilitar esta opção pode causar deadlock em clientes `HTTP/1.x` antigos que não suportam `full-duplex`. -> Isso também pode ser configurado usando a configuração de ambiente `CADDY_GLOBAL_OPTIONS`: - -```sh -CADDY_GLOBAL_OPTIONS="servers { - enable_full_duplex -}" -``` - -Você pode encontrar mais informações sobre esta configuração na [documentação do Caddy](https://caddyserver.com/docs/caddyfile/options#enable-full-duplex). - ## Habilitar o modo de depuração -Ao usar a imagem Docker, defina a variável de ambiente `CADDY_GLOBAL_OPTIONS` como `debug` para habilitar o modo de depuração: +Ao usar a imagem Docker, defina a variável de ambiente `CADDY_GLOBAL_OPTIONS` +como `debug` para habilitar o modo de depuração: ```console docker run -v $PWD:/app/public \ diff --git a/docs/pt-br/docker.md b/docs/pt-br/docker.md index e544b7689c..a6ad90dc40 100644 --- a/docs/pt-br/docker.md +++ b/docs/pt-br/docker.md @@ -1,15 +1,21 @@ # Construindo uma imagem Docker personalizada -[As imagens Docker do FrankenPHP](https://hub.docker.com/r/dunglas/frankenphp) são baseadas em [imagens oficiais do PHP](https://hub.docker.com/_/php/). -Variantes do Debian e do Alpine Linux são fornecidas para arquiteturas populares. +[As imagens Docker do FrankenPHP](https://hub.docker.com/r/dunglas/frankenphp) +são baseadas em [imagens oficiais do PHP](https://hub.docker.com/_/php/). +Variantes do Debian e do Alpine Linux são fornecidas para arquiteturas +populares. Variantes do Debian são recomendadas. Variantes para PHP 8.2, 8.3, 8.4 e 8.5 são fornecidas. -As tags seguem este padrão: `dunglas/frankenphp:-php-` +As tags seguem este padrão: +`dunglas/frankenphp:-php-`. -- `` e `` são números de versão do FrankenPHP e do PHP, respectivamente, variando de maior (ex.: `1`), menor (ex.: `1.2`) a versões de patch (ex.: `1.2.3`). -- `` é `trixie` (para Debian Trixie), `bookworm` (para Debian Bookworm) ou `alpine` (para a versão estável mais recente do Alpine). +- `` e `` são números de versão do + FrankenPHP e do PHP, respectivamente, variando de maior (ex.: `1`), menor + (ex.: `1.2`) a versões de patch (ex.: `1.2.3`). +- `` é `bookworm` (para Debian Bookworm) ou `alpine` (para a versão estável + mais recente do Alpine). [Navegue pelas tags](https://hub.docker.com/r/dunglas/frankenphp/tags). @@ -30,13 +36,11 @@ docker build -t minha-app-php . docker run -it --rm --name minha-app-rodando minha-app-php ``` -## Como ajustar a configuração - -Para sua conveniência, [um `Caddyfile` padrão](https://github.com/php/frankenphp/blob/main/caddy/frankenphp/Caddyfile) contendo variáveis de ambiente úteis é fornecido na imagem. - ## Como instalar mais extensões PHP -O script [`docker-php-extension-installer`](https://github.com/mlocati/docker-php-extension-installer) é fornecido na imagem base. +O script +[`docker-php-extension-installer`](https://github.com/mlocati/docker-php-extension-installer) +é fornecido na imagem base. Adicionar extensões PHP adicionais é simples: ```dockerfile @@ -53,9 +57,12 @@ RUN install-php-extensions \ ## Como instalar mais módulos Caddy -O FrankenPHP é construído sobre o Caddy, e todos os [módulos Caddy](https://caddyserver.com/docs/modules/) podem ser usados com o FrankenPHP. +O FrankenPHP é construído sobre o Caddy, e todos os +[módulos Caddy](https://caddyserver.com/docs/modules/) podem ser usados com o +FrankenPHP. -A maneira mais fácil de instalar módulos Caddy personalizados é usar o [xcaddy](https://github.com/caddyserver/xcaddy): +A maneira mais fácil de instalar módulos Caddy personalizados é usar o +[xcaddy](https://github.com/caddyserver/xcaddy): ```dockerfile FROM dunglas/frankenphp:builder AS builder @@ -71,8 +78,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 github.com/php/frankenphp=./ \ + --with github.com/php/frankenphp/caddy=./caddy/ \ --with github.com/dunglas/caddy-cbrotli \ # Mercure e Vulcain estão incluídos na compilação oficial, mas sinta-se # à vontade para removê-los @@ -86,8 +93,11 @@ FROM dunglas/frankenphp AS runner COPY --from=builder /usr/local/bin/frankenphp /usr/local/bin/frankenphp ``` -A imagem `builder` fornecida pelo FrankenPHP contém uma versão compilada da `libphp`. -[Imagens de builder](https://hub.docker.com/r/dunglas/frankenphp/tags?name=builder) são fornecidas para todas as versões do FrankenPHP e do PHP, tanto para Debian quanto para Alpine. +A imagem `builder` fornecida pelo FrankenPHP contém uma versão compilada da +`libphp`. +[Imagens de builder](https://hub.docker.com/r/dunglas/frankenphp/tags?name=builder) +são fornecidas para todas as versões do FrankenPHP e do PHP, tanto para Debian +quanto para Alpine. > [!TIP] > @@ -96,7 +106,8 @@ A imagem `builder` fornecida pelo FrankenPHP contém uma versão compilada da `l ## Habilitando o modo worker por padrão -Defina a variável de ambiente `FRANKENPHP_CONFIG` para iniciar o FrankenPHP com um worker script: +Defina a variável de ambiente `FRANKENPHP_CONFIG` para iniciar o FrankenPHP com +um worker script: ```dockerfile FROM dunglas/frankenphp @@ -108,7 +119,8 @@ ENV FRANKENPHP_CONFIG="worker ./public/index.php" ## Usando um volume em desenvolvimento -Para desenvolver facilmente com o FrankenPHP, monte o diretório do seu host que contém o código-fonte da aplicação como um volume no contêiner Docker: +Para desenvolver facilmente com o FrankenPHP, monte o diretório do seu host que +contém o código-fonte da aplicação como um volume no contêiner Docker: ```console docker run -v $PWD:/app/public -p 80:80 -p 443:443 -p 443:443/udp --tty minha-app-php @@ -126,9 +138,9 @@ Com o Docker Compose: services: php: image: dunglas/frankenphp - # descomente a linha a seguir se quiser usar um Dockerfile personalizado + # Descomente a linha a seguir se quiser usar um Dockerfile personalizado #build: . - # descomente a linha a seguir se quiser executar isso em um ambiente de + # Descomente a linha a seguir se quiser executar isso em um ambiente de # produção # restart: always ports: @@ -139,7 +151,7 @@ services: - ./:/app/public - caddy_data:/data - caddy_config:/config - # comente a linha a seguir em produção, isso permite ter logs legíveis em + # Comente a linha a seguir em produção, isso permite ter logs legíveis em # desenvolvimento tty: true @@ -173,9 +185,13 @@ USER ${USER} ### Executando sem capacidades adicionais -Mesmo executando sem root, o FrankenPHP precisa da capacidade `CAP_NET_BIND_SERVICE` para vincular o servidor web em portas privilegiadas (80 e 443). +Mesmo executando sem root, o FrankenPHP precisa do recurso +`CAP_NET_BIND_SERVICE` para vincular o servidor web em portas privilegiadas (80 +e 443). -Se você expor o FrankenPHP em uma porta não privilegiada (1024 e acima), é possível executar o servidor web como um usuário não root e sem a necessidade de nenhuma capacidade adicional: +Se você expor o FrankenPHP em uma porta não privilegiada (1024 e acima), é +possível executar o servidor web como um usuário não root e sem a necessidade de +nenhuma capacidade adicional: ```dockerfile FROM dunglas/frankenphp @@ -193,20 +209,24 @@ RUN \ USER ${USER} ``` -Em seguida, defina a variável de ambiente `SERVER_NAME` para usar uma porta sem privilégios. +Em seguida, defina a variável de ambiente `SERVER_NAME` para usar uma porta sem +privilégios. Exemplo: `:8000` ## Atualizações As imagens Docker são construídas: -- quando uma nova versão é marcada com uma tag; -- Diariamente às 4h UTC, se novas versões das imagens oficiais do PHP estiverem disponíveis. +- Quando uma tag de uma nova versão é criada; +- Diariamente às 4h UTC, se novas versões das imagens oficiais do PHP estiverem + disponíveis. ## Versões de desenvolvimento -As versões de desenvolvimento estão disponíveis no repositório Docker [`dunglas/frankenphp-dev`](https://hub.docker.com/repository/docker/dunglas/frankenphp-dev). -Uma nova construção é acionada sempre que um commit é enviado para o branch principal do repositório do GitHub. +As versões de desenvolvimento estão disponíveis no repositório Docker +[`dunglas/frankenphp-dev`](https://hub.docker.com/repository/docker/dunglas/frankenphp-dev). +Uma nova construção é acionada sempre que um commit é enviado para o branch +principal do repositório do GitHub. As tags `latest*` apontam para o HEAD do branch `main`. -Tags no formato `sha-` também estão disponíveis. \ No newline at end of file +Tags no formato `sha-` também estão disponíveis. diff --git a/docs/pt-br/embed.md b/docs/pt-br/embed.md index 105ab416c2..36002f6c65 100644 --- a/docs/pt-br/embed.md +++ b/docs/pt-br/embed.md @@ -67,7 +67,7 @@ Docker que fornecemos. ```dockerfile FROM --platform=linux/amd64 dunglas/frankenphp:static-builder-gnu - # Se você pretende executar o binário em sistemas musl-libc, use static-builder-musl + # Se você pretende executar o binário em sistemas musl-libc, use o static-builder-musl # Copia sua aplicação WORKDIR /go/src/app/dist/app @@ -89,16 +89,16 @@ Docker que fornecemos. 2. Construa: ```console - docker build -t static-app -f static-build.Dockerfile . + docker build -t aplicacao-estatica -f static-build.Dockerfile . ``` 3. Extraia o binário: ```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 + docker cp $(docker create --name aplicacao-estatica-tmp aplicacao-estatica):/go/src/app/dist/frankenphp-linux-x86_64 minha-aplicacao ; docker rm aplicacao-estatica-tmp ``` -O binário resultante é o arquivo chamado `my-app` no diretório atual. +O binário resultante é o arquivo `minha-aplicacao` no diretório atual. ## Criando um binário para outros sistemas operacionais @@ -115,33 +115,33 @@ O binário resultante é o arquivo `frankenphp--` no diretório `dist/ ## Usando o binário -É isso! O arquivo `my-app` (ou `dist/frankenphp--` em outros +É isso! O arquivo `minha-aplicacao` (ou `dist/frankenphp--` em outros sistemas operacionais) contém sua aplicação independente! Para iniciar a aplicação web, execute: ```console -./my-app php-server +./minha-aplicacao php-server ``` Se a sua aplicação contiver um [worker script](worker.md), inicie o worker com algo como: ```console -./my-app php-server --worker public/index.php +./minha-aplicacao php-server --worker public/index.php ``` Para habilitar HTTPS (um certificado Let's Encrypt é criado automaticamente), HTTP/2 e HTTP/3, especifique o nome de domínio a ser usado: ```console -./my-app php-server --domain localhost +./minha-aplicacao php-server --domain localhost ``` Você também pode executar os scripts PHP CLI incorporados ao seu binário: ```console -./my-app php-cli bin/console +./minha-aplicacao php-cli bin/console ``` ## Extensões PHP diff --git a/docs/pt-br/extensions.md b/docs/pt-br/extensions.md index 3815025564..d7c2c76669 100644 --- a/docs/pt-br/extensions.md +++ b/docs/pt-br/extensions.md @@ -2,7 +2,8 @@ Com o FrankenPHP, você pode **escrever extensões PHP em Go**, o que permite criar **funções nativas de alto desempenho** que podem ser chamadas diretamente -do PHP. Suas aplicações podem aproveitar qualquer biblioteca Go existente ou nova, bem +do PHP. +Suas aplicações podem aproveitar qualquer biblioteca Go existente ou nova, bem como o famoso modelo de concorrência de **goroutines diretamente do seu código PHP**. @@ -19,11 +20,11 @@ rapidamente ao FrankenPHP. O FrankenPHP oferece duas maneiras de criar extensões PHP em Go: -1. **Usando o gerador de extensões** - A abordagem recomendada que gera todo o - código boilerplate necessário para a maioria dos casos de uso, permitindo que - você se concentre em escrever seu código em Go. -2. **Implementação manual** - Controle total sobre a estrutura da extensão para - casos de uso avançados. +1. **Usando o gerador de extensões** - A abordagem recomendada que gera todo o + código boilerplate necessário para a maioria dos casos de uso, permitindo que + você se concentre em escrever seu código em Go. +2. **Implementação manual** - Controle total sobre a estrutura da extensão para + casos de uso avançados. Começaremos com a abordagem do gerador, pois é a maneira mais fácil de começar, e, em seguida, mostraremos a implementação manual para aqueles que precisam de @@ -61,7 +62,7 @@ O primeiro passo para escrever uma extensão PHP em Go é criar um novo módulo Você pode usar o seguinte comando para isso: ```console -go mod init example.com/example +go mod init github.com// ``` O segundo passo é @@ -84,15 +85,10 @@ retornará a string resultante. Deve ficar assim: ```go -package example - -// #include -import "C" import ( + "C" + "github.com/dunglas/frankenphp" "strings" - "unsafe" - - "github.com/dunglas/frankenphp" ) //export_php:function repeat_this(string $str, int $count, bool $reverse): string @@ -114,8 +110,13 @@ func repeat_this(s *C.zend_string, count int64, reverse bool) unsafe.Pointer { Há duas coisas importantes a serem observadas aqui: -- Um comentário de diretiva `//export_php:function` define a assinatura da função no PHP. É assim que o gerador sabe como gerar a função PHP com os parâmetros e o tipo de retorno corretos; -- A função deve retornar um `unsafe.Pointer`. O FrankenPHP fornece uma API para ajudar você com o malabarismo de tipos entre C e Go. +- Um comentário de diretiva `//export_php:function` define a assinatura da + função no PHP. + É assim que o gerador sabe como gerar a função PHP com os parâmetros e o tipo + de retorno corretos; +- A função deve retornar um `unsafe.Pointer`. + O FrankenPHP fornece uma API para ajudar você com o malabarismo de tipos entre + C e Go. Embora o primeiro ponto fale por si, o segundo pode ser mais difícil de entender. @@ -131,27 +132,27 @@ variáveis são armazenadas internamente no PHP. Esta tabela resume o que você precisa saber: | Tipo PHP | Tipo Go | Conversão direta | Auxiliar de C para Go | Auxiliar de Go para C | Suporte a métodos de classe | -| :----------------- | :---------------------------- | :--------------- | :-------------------------------- | :--------------------------------- | :-------------------------- | +| ------------------ | ----------------------------- | ---------------- | --------------------------------- | ---------------------------------- | --------------------------- | | `int` | `int64` | ✅ | - | - | ✅ | | `?int` | `*int64` | ✅ | - | - | ✅ | | `float` | `float64` | ✅ | - | - | ✅ | | `?float` | `*float64` | ✅ | - | - | ✅ | | `bool` | `bool` | ✅ | - | - | ✅ | | `?bool` | `*bool` | ✅ | - | - | ✅ | +| `?bool` | `*bool` | ✅ | - | - | ✅ | | `string`/`?string` | `*C.zend_string` | ❌ | `frankenphp.GoString()` | `frankenphp.PHPString()` | ✅ | | `array` | `frankenphp.AssociativeArray` | ❌ | `frankenphp.GoAssociativeArray()` | `frankenphp.PHPAssociativeArray()` | ✅ | | `array` | `map[string]any` | ❌ | `frankenphp.GoMap()` | `frankenphp.PHPMap()` | ✅ | | `array` | `[]any` | ❌ | `frankenphp.GoPackedArray()` | `frankenphp.PHPPackedArray()` | ✅ | | `mixed` | `any` | ❌ | `GoValue()` | `PHPValue()` | ❌ | -| `callable` | `*C.zval` | ❌ | - | `frankenphp.CallPHPCallable()` | ❌ | | `object` | `struct` | ❌ | _Ainda não implementado_ | _Ainda não implementado_ | ❌ | > [!NOTE] > Esta tabela ainda não é exaustiva e será completada à medida que a API de > tipos do FrankenPHP se tornar mais completa. > -> Para métodos de classe especificamente, tipos primitivos e arrays são -> suportados atualmente. +> Tipos primitivos e arrays são suportados atualmente, especificamente para +> métodos de classe. > Objetos ainda não podem ser usados como parâmetros de métodos ou tipos de > retorno. @@ -177,168 +178,91 @@ diretamente para um slice `[]any` ou um mapa não ordenado `map[string]any`. **Criando e manipulando arrays em Go:** ```go -package example - -// #include -import "C" -import ( - "unsafe" - - "github.com/dunglas/frankenphp" -) - -// export_php:function process_data_ordered(array $input): array -func process_data_ordered_map(arr *C.zend_array) unsafe.Pointer { - // Converte um array associativo PHP para Go, mantendo a ordem - associativeArray, err := frankenphp.GoAssociativeArray[any](unsafe.Pointer(arr)) - if err != nil { - // handle error +//export_php:function process_data_ordered(array $input): array +func process_data_ordered_map(arr *C.zval) unsafe.Pointer { + // Converte um array associativo PHP para Go, mantendo a ordem + associativeArray := frankenphp.GoAssociativeArray(unsafe.Pointer(arr)) + + // percorre as entradas em ordem + for _, key := range associativeArray.Order { + value, _ = associativeArray.Map[key] + // faz algo com a chave e o valor } - // percorre as entradas em ordem - for _, key := range associativeArray.Order { - value, _ := associativeArray.Map[key] - // faz algo com a chave e o valor - _ = value // Usar 'value' para evitar erro de variável não utilizada - } - - // retorna um array ordenado - // se 'Order' não estiver vazio, apenas os pares chave-valor em 'Order' - // serão respeitados - return frankenphp.PHPAssociativeArray[string](frankenphp.AssociativeArray[string]{ - Map: map[string]string{ - "key1": "value1", - "key2": "value2", - }, - Order: []string{"key1", "key2"}, - }) + // retorna um array ordenado + // se 'Order' não estiver vazio, apenas os pares chave-valor em 'Order' + // serão respeitados + return frankenphp.PHPAssociativeArray(AssociativeArray{ + Map: map[string]any{ + "chave1": "valor1", + "chave2": "valor2", + }, + Order: []string{"chave1", "chave2"}, + }) } -// export_php:function process_data_unordered(array $input): array -func process_data_unordered_map(arr *C.zend_array) unsafe.Pointer { - // Converte um array associativo PHP em um mapa Go sem manter a ordem - // Ignorar a ordem terá melhor desempenho - goMap, err := frankenphp.GoMap[any](unsafe.Pointer(arr)) - if err != nil { - // handle error +//export_php:function process_data_unordered(array $input): array +func process_data_unordered_map(arr *C.zval) unsafe.Pointer { + // Converte um array associativo PHP em um mapa Go sem manter a ordem + // Ignorar a ordem terá melhor desempenho + goMap := frankenphp.GoMap(unsafe.Pointer(arr)) + + // percorre as entradas sem nenhuma ordem específica + for key, value := range goMap { + // faz algo com a chave e o valor } - // percorre as entradas sem nenhuma ordem específica - for key, value := range goMap { - // faz algo com a chave e o valor - _, _ = key, value // Usar 'key' e 'value' para evitar erro de variável não utilizada - } - - // retorna um array não ordenado - return frankenphp.PHPMap(map[string]string { - "key1": "value1", - "key2": "value2", - }) + // retorna um array não ordenado + return frankenphp.PHPMap(map[string]any{ + "chave1": "valor1", + "chave2": "valor2", + }) } -// export_php:function process_data_packed(array $input): array -func process_data_packed(arr *C.zend_array) unsafe.Pointer { - // Converte um array compactado PHP para Go - goSlice, err := frankenphp.GoPackedArray(unsafe.Pointer(arr)) - if err != nil { - // handle error - } +//export_php:function process_data_packed(array $input): array +func process_data_packed(arr *C.zval) unsafe.Pointer { + // Converte um array compactado PHP para Go + goSlice := frankenphp.GoPackedArray(unsafe.Pointer(arr), false) - // percorre o slice em ordem - for index, value := range goSlice { - // faz algo com o índice e o valor - _, _ = index, value // Usar 'index' e 'value' para evitar erro de variável não utilizada - } + // percorre o slice em ordem + for index, value := range goSlice { + // faz algo com a chave e o valor + } - // retorna um array compactado - return frankenphp.PHPPackedArray([]string{"value1", "value2", "value3"}) + // retorna um array compactado + return frankenphp.PHPackedArray([]any{"valor1", "valor2", "value3"}) } ``` **Principais recursos da conversão de arrays:** - **Pares chave-valor ordenados** - Opção para manter a ordem do array - associativo; + associativo; - **Otimizado para múltiplos casos** - Opção para ignorar a ordem para melhor - desempenho ou converter diretamente para um slice; + desempenho ou converter diretamente para um slice; - **Detecção automática de listas** - Ao converter para PHP, detecta - automaticamente se o array deve ser uma lista compactada ou um hashmap; + automaticamente se o array deve ser uma lista compactada ou um hashmap; - **Arrays aninhados** - Os arrays podem ser aninhados e converterão todos os - tipos suportados automaticamente (`int64`, `float64`, `string`, `bool`, `nil`, - `AssociativeArray`, `map[string]any`, `[]any`); + tipos suportados automaticamente (`int64`, `float64`, `string`, `bool`, `nil`, + `AssociativeArray`, `map[string]any`, `[]any`); - **Objetos não são suportados** - Atualmente, apenas tipos escalares e arrays - podem ser usados como valores. - Fornecer um objeto resultará em um valor `null` no array PHP. + podem ser usados como valores. + Fornecer um objeto resultará em um valor `null` no array PHP. ##### Métodos disponíveis: empacotado e associativo - `frankenphp.PHPAssociativeArray(arr frankenphp.AssociativeArray) unsafe.Pointer` - \- Converte para um array PHP ordenado com pares chave-valor; + \- Converte para um array PHP ordenado com pares chave-valor; - `frankenphp.PHPMap(arr map[string]any) unsafe.Pointer` - Converte um mapa em - um array PHP não ordenado com pares chave-valor; + um array PHP não ordenado com pares chave-valor; - `frankenphp.PHPPackedArray(slice []any) unsafe.Pointer` - Converte um slice - em um array PHP compactado apenas com valores indexados; + em um array PHP compactado apenas com valores indexados; - `frankenphp.GoAssociativeArray(arr unsafe.Pointer, ordered bool) frankenphp.AssociativeArray` - \- Converte um array PHP em um `AssociativeArray` Go ordenado (mapa com ordem); + \- Converte um array PHP em um `AssociativeArray` Go ordenado (mapa com ordem); - `frankenphp.GoMap(arr unsafe.Pointer) map[string]any` - Converte um array PHP - em um mapa Go não ordenado; + em um mapa Go não ordenado; - `frankenphp.GoPackedArray(arr unsafe.Pointer) []any` - Converte um array PHP - em um slice Go; -- `frankenphp.IsPacked(zval *C.zend_array) bool` - Verifica se um array PHP é - compactado (apenas indexado) ou associativo (pares chave-valor). - -### Trabalhando com Callables - -O FrankenPHP fornece uma maneira de trabalhar com callables PHP usando o helper -`frankenphp.CallPHPCallable`. Isso permite que você chame funções ou métodos PHP -a partir do código Go. - -Para demonstrar isso, vamos criar nossa própria função `array_map()` que recebe -um callable e um array, aplica o callable a cada elemento do array e retorna um -novo array com os resultados: - -```go -package example - -// #include -import "C" -import ( - "unsafe" - - "github.com/dunglas/frankenphp" -) - -// export_php:function my_array_map(array $data, callable $callback): array -func my_array_map(arr *C.zend_array, callback *C.zval) unsafe.Pointer { - goSlice, err := frankenphp.GoPackedArray[any](unsafe.Pointer(arr)) - if err != nil { - panic(err) - } - - result := make([]any, len(goSlice)) - - for index, value := range goSlice { - result[index] = frankenphp.CallPHPCallable(unsafe.Pointer(callback), []interface{}{value}) - } - - return frankenphp.PHPPackedArray(result) -} -``` - -Observe como usamos `frankenphp.CallPHPCallable()` para chamar o callable PHP -passado como parâmetro. Esta função recebe um ponteiro para o callable e um -array de argumentos, e retorna o resultado da execução do callable. Você pode -usar a sintaxe de callable a que está acostumado: - -```php -name` não funcionará); + propriedades diretamente do PHP (`$user->name` não funcionará); - **Interface somente para métodos** - Todas as interações devem passar pelos - métodos que você definir; + métodos que você definir; - **Melhor encapsulamento** - A estrutura interna de dados é completamente - controlada pelo código Go; + controlada pelo código Go; - **Segurança de tipos** - Sem risco do código PHP corromper o estado interno - com tipos incorretos; + com tipos incorretos; - **API mais limpa** - Força o design de uma interface pública adequada. Essa abordagem fornece melhor encapsulamento e evita que o código PHP corrompa @@ -386,16 +308,6 @@ métodos** para interagir com suas classes opacas. Use a diretiva `//export_php:method` para definir o comportamento: ```go -package example - -// #include -import "C" -import ( - "unsafe" - - "github.com/dunglas/frankenphp" -) - //export_php:class User type UserStruct struct { Name string @@ -430,16 +342,6 @@ Quando um parâmetro é anulável, ele se torna um ponteiro na sua função Go, permitindo que você verifique se o valor era `null` no PHP: ```go -package example - -// #include -import "C" -import ( - "unsafe" - - "github.com/dunglas/frankenphp" -) - //export_php:method User::updateInfo(?string $name, ?int $age, ?bool $active): void func (us *UserStruct) UpdateInfo(name *C.zend_string, age *int64, active *bool) { // Verifica se o parâmetro name foi fornecido (não nulo) @@ -452,7 +354,7 @@ func (us *UserStruct) UpdateInfo(name *C.zend_string, age *int64, active *bool) us.Age = int(*age) } - // Verifica se o parâmetro active foi fornecido (não nulo) + // Verifique se o parâmetro active foi fornecido (não nulo) if active != nil { us.Active = *active } @@ -462,12 +364,12 @@ func (us *UserStruct) UpdateInfo(name *C.zend_string, age *int64, active *bool) **Pontos-chave sobre parâmetros anuláveis:** - **Tipos primitivos anuláveis** (`?int`, `?float`, `?bool`) tornam-se ponteiros - (`*int64`, `*float64`, `*bool`) em Go; + (`*int64`, `*float64`, `*bool`) em Go; - **Strings anuláveis** (`?string`) permanecem como `*C.zend_string`, mas podem - ser `nil`; + ser `*nil`; - **Verifique `nil`** antes de dereferenciar valores de ponteiro; - **`null` do PHP torna-se `nil` do Go** - quando o PHP passa `null`, sua função - em Go recebe um ponteiro `nil`. + em Go recebe um ponteiro `nil`. > [!WARNING] > Atualmente, os métodos de classe têm as seguintes limitações. @@ -491,11 +393,11 @@ $user = new User(); $user->setAge(25); echo $user->getName(); // Saída: (vazio, valor padrão) echo $user->getAge(); // Saída: 25 -$user->setNamePrefix("Employee"); +$user->setNamePrefix("Funcionária"); // ✅ Isso também funciona - parâmetros anuláveis -$user->updateInfo("John", 30, true); // Todos os parâmetros fornecidos -$user->updateInfo("Jane", null, false); // Age é nulo +$user->updateInfo("João", 30, true); // Todos os parâmetros fornecidos +$user->updateInfo("Joana", null, false); // Age é nulo $user->updateInfo(null, 25, null); // Name e active são nulos // ❌ Isso NÃO funcionará - acesso direto à propriedade @@ -520,8 +422,6 @@ outras constantes entre código Go e PHP. Use a diretiva `//export_php:const` para criar constantes PHP globais: ```go -package example - //export_php:const const MAX_CONNECTIONS = 100 @@ -541,8 +441,6 @@ Use a diretiva `//export_php:classconst ClassName` para criar constantes que pertencem a uma classe PHP específica: ```go -package example - //export_php:classconst User const STATUS_ACTIVE = 1 @@ -592,15 +490,10 @@ Por exemplo, vamos pegar a função `repeat_this()` que declaramos anteriormente alterar o último argumento para um inteiro: ```go -package example - -// #include -import "C" import ( - "strings" - "unsafe" - - "github.com/dunglas/frankenphp" + "C" + "github.com/dunglas/frankenphp" + "strings" ) //export_php:const @@ -617,37 +510,37 @@ const MODE_UPPERCASE = 2 //export_php:function repeat_this(string $str, int $count, int $mode): string func repeat_this(s *C.zend_string, count int64, mode int) unsafe.Pointer { - str := frankenphp.GoString(unsafe.Pointer(s)) + str := frankenphp.GoString(unsafe.Pointer(s)) - result := strings.Repeat(str, int(count)) - if mode == STR_REVERSE { - // inverte a string - } + result := strings.Repeat(str, int(count)) + if mode == STR_REVERSE { + // inverte a string + } - if mode == STR_NORMAL { - // sem operação, apenas para mostrar a constante - } + if mode == STR_NORMAL { + // sem operação, apenas para mostrar a constante + } - return frankenphp.PHPString(result, false) + return frankenphp.PHPString(result, false) } //export_php:class StringProcessor type StringProcessorStruct struct { - // campos internos + // campos internos } //export_php:method StringProcessor::process(string $input, int $mode): string func (sp *StringProcessorStruct) Process(input *C.zend_string, mode int64) unsafe.Pointer { - str := frankenphp.GoString(unsafe.Pointer(input)) + str := frankenphp.GoString(unsafe.Pointer(input)) - switch mode { - case MODE_LOWERCASE: - str = strings.ToLower(str) - case MODE_UPPERCASE: - str = strings.ToUpper(str) - } + switch mode { + case MODE_LOWERCASE: + str = strings.ToLower(str) + case MODE_UPPERCASE: + str = strings.ToUpper(str) + } - return frankenphp.PHPString(str, false) + return frankenphp.PHPString(str, false) } ``` @@ -665,17 +558,13 @@ todos os símbolos exportados em um namespace específico: ```go //export_php:namespace My\Extension -package example +package main -import ( - "unsafe" - - "github.com/dunglas/frankenphp" -) +import "C" //export_php:function hello(): string func hello() string { - return "Hello from My\\Extension namespace!" + return "Olá do namespace My\\Extension!" } //export_php:class User @@ -685,7 +574,7 @@ type UserStruct struct { //export_php:method User::getName(): string func (u *UserStruct) GetName() unsafe.Pointer { - return frankenphp.PHPString("John Doe", false) + return frankenphp.PHPString("João Ninguém", false) } //export_php:const @@ -700,10 +589,10 @@ colocadas sob esse namespace no PHP: ```php getName(); // "John Doe" +echo $user->getName(); // "João Ninguém" echo My\Extension\STATUS_ACTIVE; // 1 ``` @@ -711,14 +600,14 @@ echo My\Extension\STATUS_ACTIVE; // 1 #### Notas importantes - Apenas **uma** diretiva de namespace é permitida por arquivo. - Se várias diretivas de namespace forem encontradas, o gerador retornará um - erro; + Se várias diretivas de namespace forem encontradas, o gerador retornará um + erro; - O namespace se aplica a **todos** os símbolos exportados no arquivo: funções, - classes, métodos e constantes; + classes, métodos e constantes; - Os nomes de namespace seguem as convenções de namespace do PHP, usando barras - invertidas (`\`) como separadores; + invertidas (`\`) como separadores; - Se nenhum namespace for declarado, os símbolos serão exportados para o - namespace global como de costume. + namespace global como de costume. ### Gerando a extensão @@ -755,7 +644,7 @@ CGO_CFLAGS=$(php-config --includes) \ CGO_LDFLAGS="$(php-config --ldflags) $(php-config --libs)" \ xcaddy build \ --output frankenphp \ - --with example.com/example/build + --with github.com///build ``` Observe que você aponta para o subdiretório `/build` que foi criado durante a @@ -772,12 +661,12 @@ Por exemplo, crie um arquivo `index.php` com o seguinte conteúdo: process('Hello World', StringProcessor::MODE_LOWERCASE); // "hello world" -echo $processor->process('Hello World', StringProcessor::MODE_UPPERCASE); // "HELLO WORLD" +echo $processor->process('Olá mundo', StringProcessor::MODE_LOWERCASE); // "olá mundo" +echo $processor->process('Olá mundo', StringProcessor::MODE_UPPERCASE); // "OLÁ MUNDO" ``` Depois de integrar sua extensão ao FrankenPHP, como demonstrado na seção @@ -806,26 +695,25 @@ Para fazer isso, crie um arquivo com o nome desejado, por exemplo, `extension.go`, e adicione o seguinte código: ```go -package example +package ext_go -// #include "extension.h" +//#include "extension.h" import "C" import ( - "log/slog" - "unsafe" - - "github.com/dunglas/frankenphp" + "unsafe" + "github.com/caddyserver/caddy/v2" + "github.com/dunglas/frankenphp" ) func init() { - frankenphp.RegisterExtension(unsafe.Pointer(&C.ext_module_entry)) + frankenphp.RegisterExtension(unsafe.Pointer(&C.ext_module_entry)) } //export go_print_something func go_print_something() { - go func() { - slog.Info("Hello from a goroutine!") - }() + go func() { + caddy.Log().Info("Olá de uma goroutine!") + }() } ``` @@ -1049,16 +937,7 @@ As funções auxiliares cuidam de todo o gerenciamento de memória e da complexidade da conversão para nós. ```go -package example - -// #include -import "C" -import ( - "unsafe" - "strings" - - "github.com/dunglas/frankenphp" -) +import "strings" //export go_upper func go_upper(s *C.zend_string) *C.zend_string { @@ -1097,7 +976,7 @@ CGO_CFLAGS=$(php-config --includes) \ CGO_LDFLAGS="$(php-config --ldflags) $(php-config --libs)" \ xcaddy build \ --output frankenphp \ - --with example.com/example + --with github.com// ``` Pronto! @@ -1116,7 +995,7 @@ com exemplos para as funções que você implementou: go_print(); // Testa a função avançada -echo go_upper("hello world") . "\n"; +echo go_upper("olá mundo") . "\n"; ``` Agora você pode executar o FrankenPHP com este arquivo usando diff --git a/docs/pt-br/github-actions.md b/docs/pt-br/github-actions.md index b3c2af653b..8ce398cf1b 100644 --- a/docs/pt-br/github-actions.md +++ b/docs/pt-br/github-actions.md @@ -1,30 +1,39 @@ # Usando GitHub Actions -Este repositório constrói e implanta a imagem Docker no [Docker Hub](https://hub.docker.com/r/dunglas/frankenphp) a cada pull request aprovado ou em seu próprio fork após a configuração. +Este repositório constrói e implanta a imagem Docker no +[Docker Hub](https://hub.docker.com/r/dunglas/frankenphp) a cada pull request +aprovado ou em seu próprio fork após a configuração. ## Configurando GitHub Actions Nas configurações do repositório, em "Secrets", adicione os seguintes segredos: -- `REGISTRY_LOGIN_SERVER`: O registro do Docker a ser usado (por exemplo, `docker.io`). -- `REGISTRY_USERNAME`: O nome de usuário a ser usado para fazer login no registro (por exemplo, `dunglas`). -- `REGISTRY_PASSWORD`: A senha a ser usada para fazer login no registro (por exemplo, uma chave de acesso). +- `REGISTRY_LOGIN_SERVER`: O registro do Docker a ser usado (por exemplo, + `docker.io`). +- `REGISTRY_USERNAME`: O nome de usuário a ser usado para fazer login no + registro (por exemplo, `dunglas`). +- `REGISTRY_PASSWORD`: A senha a ser usada para fazer login no registro (por + exemplo, uma chave de acesso). - `IMAGE_NAME`: O nome da imagem (por exemplo, `dunglas/frankenphp`). ## Construindo e enviando a imagem 1. Crie um pull request ou faça o push para o seu fork. 2. O GitHub Actions construirá a imagem e executará os testes. -3. Se a construção for bem-sucedida, a imagem será enviada para o registro usando a tag `pr-x`, onde `x` é o número do PR. +3. Se a construção for bem-sucedida, a imagem será enviada para o registro + usando a tag `pr-x`, onde `x` é o número do PR. ## Implantando a imagem -1. Após o merge do pull request, o GitHub Actions executará os testes novamente e criará uma nova imagem. -2. Se a construção for bem-sucedida, a tag `main` será atualizada no registro do Docker. +1. Após o merge do pull request, o GitHub Actions executará os testes novamente + e criará uma nova imagem. +2. Se a construção for bem-sucedida, a tag `main` será atualizada no registro do + Docker. ## Versões 1. Crie uma nova tag no repositório. 2. O GitHub Actions construirá a imagem e executará os testes. -3. Se a construção for bem-sucedida, a imagem será enviada para o registro usando o nome da tag como tag (por exemplo, `v1.2.3` e `v1.2` serão criadas). +3. Se a construção for bem-sucedida, a imagem será enviada para o registro + usando o nome da tag como tag (por exemplo, `v1.2.3` e `v1.2` serão criadas). 4. A tag `latest` também será atualizada. diff --git a/docs/pt-br/hot-reload.md b/docs/pt-br/hot-reload.md deleted file mode 100644 index 299dd249f0..0000000000 --- a/docs/pt-br/hot-reload.md +++ /dev/null @@ -1,139 +0,0 @@ -# Hot Reload - -FrankenPHP inclui um recurso de **hot reload** integrado projetado para melhorar significativamente a experiência do desenvolvedor. - -![Mercure](hot-reload.png) - -Este recurso oferece um fluxo de trabalho semelhante ao **Hot Module Replacement (HMR)** encontrado em ferramentas JavaScript modernas (como Vite ou webpack). -Em vez de atualizar manualmente o navegador após cada alteração de arquivo (código PHP, templates, arquivos JavaScript e CSS...), -FrankenPHP atualiza o conteúdo em tempo real. - -O Hot Reload funciona nativamente com WordPress, Laravel, Symfony e qualquer outra aplicação ou framework PHP. - -Quando ativado, o FrankenPHP monitora seu diretório de trabalho atual em busca de alterações no sistema de arquivos. -Quando um arquivo é modificado, ele envia uma atualização [Mercure](mercure.md) para o navegador. - -Dependendo da sua configuração, o navegador irá: - -- **Transformar o DOM** (preservando a posição de rolagem e o estado dos inputs) se o [Idiomorph](https://github.com/bigskysoftware/idiomorph) for carregado. -- **Recarregar a página** (recarregamento ao vivo padrão) se o Idiomorph não estiver presente. - -## Configuração - -Para habilitar o hot reload, ative o Mercure e adicione a subdiretiva `hot_reload` à diretiva `php_server` no seu `Caddyfile`. - -> [!AVISO] -> Este recurso é destinado **apenas a ambientes de desenvolvimento**. -> Não habilite `hot_reload` em produção, pois o monitoramento do sistema de arquivos acarreta sobrecarga de desempenho e expõe endpoints internos. - -```caddyfile -localhost - -mercure { - anonymous -} - -root public/ -php_server { - hot_reload -} -``` - -Por padrão, o FrankenPHP irá monitorar todos os arquivos no diretório de trabalho atual que correspondem a este padrão glob: `./**/*.{css,env,gif,htm,html,jpg,jpeg,js,mjs,php,png,svg,twig,webp,xml,yaml,yml}` - -É possível definir explicitamente os arquivos a serem monitorados usando a sintaxe glob: - -```caddyfile -localhost - -mercure { - anonymous -} - -root public/ -php_server { - hot_reload src/**/*{.php,.js} config/**/*.yaml -} -``` - -Use a forma longa para especificar o tópico Mercure a ser usado, bem como quais diretórios ou arquivos monitorar, fornecendo caminhos para a opção `hot_reload`: - -```caddyfile -localhost - -mercure { - anonymous -} - -root public/ -php_server { - hot_reload { - topic hot-reload-topic - watch src/**/*.php - watch assets/**/*.{ts,json} - watch templates/ - watch public/css/ - } -} -``` - -## Integração no Lado do Cliente - -Enquanto o servidor detecta as alterações, o navegador precisa se inscrever nesses eventos para atualizar a página. -FrankenPHP expõe a URL do Mercure Hub a ser usada para se inscrever em alterações de arquivo através da variável de ambiente `$_SERVER['FRANKENPHP_HOT_RELOAD']`. - -Uma biblioteca JavaScript conveniente, [frankenphp-hot-reload](https://www.npmjs.com/package/frankenphp-hot-reload), também está disponível para lidar com a lógica do lado do cliente. -Para usá-la, adicione o seguinte ao seu layout principal: - -```php - -FrankenPHP Hot Reload - - - - - -``` - -A biblioteca irá se inscrever automaticamente no hub Mercure, buscar a URL atual em segundo plano quando uma alteração de arquivo for detectada e transformar o DOM. -Ela está disponível como um pacote [npm](https://www.npmjs.com/package/frankenphp-hot-reload) e no [GitHub](https://github.com/dunglas/frankenphp-hot-reload). - -Alternativamente, você pode implementar sua própria lógica do lado do cliente inscrevendo-se diretamente no hub Mercure usando a classe nativa JavaScript `EventSource`. - -### Modo Worker - -Se você estiver executando sua aplicação no [Modo Worker](https://frankenphp.dev/docs/worker/), seu script de aplicação permanece na memória. -Isso significa que as alterações no seu código PHP não serão refletidas imediatamente, mesmo que o navegador recarregue. - -Para a melhor experiência do desenvolvedor, você deve combinar `hot_reload` com [a subdiretiva `watch` na diretiva `worker`](config.md#watching-for-file-changes). - -- `hot_reload`: atualiza o **navegador** quando arquivos mudam -- `worker.watch`: reinicia o worker quando arquivos mudam - -```caddy -localhost - -mercure { - anonymous -} - -root public/ -php_server { - hot_reload - worker { - file /path/to/my_worker.php - watch - } -} -``` - -### Como funciona - -1. **Monitoramento**: FrankenPHP monitora o sistema de arquivos em busca de modificações usando a [biblioteca `e-dant/watcher`](https://github.com/e-dant/watcher) por baixo dos panos (contribuímos com o binding Go). -2. **Reinício (Modo Worker)**: se `watch` estiver ativado na configuração do worker, o worker PHP é reiniciado para carregar o novo código. -3. **Envio**: um payload JSON contendo a lista de arquivos alterados é enviado para o [hub Mercure](https://mercure.rocks) integrado. -4. **Recebimento**: O navegador, escutando via a biblioteca JavaScript, recebe o evento Mercure. -5. **Atualização**: - -- Se **Idiomorph** for detectado, ele busca o conteúdo atualizado e transforma o HTML atual para corresponder ao novo estado, aplicando as alterações instantaneamente sem perder o estado. -- Caso contrário, `window.location.reload()` é chamado para recarregar a página. diff --git a/docs/pt-br/known-issues.md b/docs/pt-br/known-issues.md index ad51865fb4..cdc209c793 100644 --- a/docs/pt-br/known-issues.md +++ b/docs/pt-br/known-issues.md @@ -2,44 +2,57 @@ ## Extensões PHP não suportadas -As seguintes extensões são conhecidas por não serem compatíveis com o FrankenPHP: +As seguintes extensões são conhecidas por não serem compatíveis com o +FrankenPHP: | Nome | Motivo | Alternativas | | ----------------------------------------------------------------------------------------------------------- | ----------------- | -------------------------------------------------------------------------------------------------------------------- | -| [imap](https://www.php.net/manual/en/imap.installation.php) | Não é thread-safe | [javanile/php-imap2](https://github.com/javanile/php-imap2), [webklex/php-imap](https://github.com/Webklex/php-imap) | +| [imap](https://www.php.net/manual/pt_BR/imap.installation.php) | Não é 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/) | Não é thread-safe | - | ## Extensões PHP com falhas -As seguintes extensões apresentam falhas conhecidas e comportamentos inesperados quando usadas com o FrankenPHP: +As seguintes extensões apresentam falhas conhecidas e comportamentos inesperados +quando usadas com o FrankenPHP: -| Nome | Problema | -| ------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| [ext-openssl](https://www.php.net/manual/en/book.openssl.php) | Ao usar a musl libc, a extensão OpenSSL pode falhar sob cargas pesadas. O problema não ocorre ao usar a GNU libc, que é mais popular. Este bug está [sendo monitorado pelo PHP](https://github.com/php/php-src/issues/13648). | +| Nome | Problema | +| ---------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| [ext-openssl](https://www.php.net/manual/pt_BR/book.openssl.php) | Ao usar uma versão estática do FrankenPHP (compilada com a `libc` `musl`), a extensão OpenSSL pode quebrar sob cargas pesadas. Uma solução alternativa é usar uma versão vinculada dinamicamente (como a usada em imagens Docker). Esta falha está [sendo monitorada pelo PHP](https://github.com/php/php-src/issues/13648) | -## get_browser +## `get_browser` -A função [`get_browser()`](https://www.php.net/manual/en/function.get-browser.php) parece apresentar mau desempenho após algum tempo. Uma solução alternativa é armazenar em cache (por exemplo, com [APCu](https://www.php.net/manual/en/book.apcu.php)) os resultados por Agente de Usuário, pois são estáticos. +A função +[`get_browser()`](https://www.php.net/manual/pt_BR/function.get-browser.php) +parece apresentar mau desempenho após algum tempo. +Uma solução alternativa é armazenar em cache (por exemplo, com +[APCu](https://www.php.net/manual/pt_BR/book.apcu.php)) os resultados por Agente +de Usuário, pois são estáticos. -## Imagens Docker binárias standalone e baseadas em Alpine +## Imagens Docker binárias independentes e baseadas em Alpine -As imagens Docker binárias standalone e baseadas em Alpine (`dunglas/frankenphp:*-alpine`) usam [musl libc](https://musl.libc.org/) em vez de [glibc e similares](https://www.etalabs.net/compare_libcs.html), para manter um tamanho binário menor. +As imagens Docker binárias independentes e baseadas em Alpine +(`dunglas/frankenphp:*-alpine`) usam a [`libc` `musl`](https://musl.libc.org/) +em vez de [`glibc` e similares](https://www.etalabs.net/compare_libcs.html) para +manter um tamanho binário menor. Isso pode levar a alguns problemas de compatibilidade. -Em particular, o sinalizador glob `GLOB_BRACE` [não está disponível](https://www.php.net/manual/en/function.glob.php) - -Prefira usar a variante GNU do binário estático e as imagens Docker baseadas em Debian se você encontrar problemas. +Em particular, o sinalizador glob `GLOB_BRACE` +[não está disponível](https://www.php.net/manual/pt_BR/function.glob.php) ## Usando `https://127.0.0.1` com o Docker Por padrão, o FrankenPHP gera um certificado TLS para `localhost`. É a opção mais fácil e recomendada para desenvolvimento local. -Se você realmente deseja usar `127.0.0.1` como host, é possível configurá-lo para gerar um certificado definindo o nome do servidor como `127.0.0.1`. +Se você realmente deseja usar `127.0.0.1` como host, é possível configurá-lo +para gerar um certificado definindo o nome do servidor como `127.0.0.1`. -Infelizmente, isso não é suficiente ao usar o Docker devido ao [seu sistema de rede](https://docs.docker.com/network/). -Você receberá um erro TLS semelhante a `curl: (35) LibreSSL/3.3.6: erro:1404B438:SSL routines:ST_CONNECT:tlsv1 alert internal error`. +Infelizmente, isso não é suficiente ao usar o Docker devido ao +[seu sistema de rede](https://docs.docker.com/network/). +Você receberá um erro TLS semelhante a +`curl: (35) LibreSSL/3.3.6: erro:1404B438:SSL routines:ST_CONNECT:tlsv1 alert internal error`. -Se você estiver usando Linux, uma solução é usar [o driver de rede do host](https://docs.docker.com/network/network-tutorial-host/): +Se você estiver usando Linux, uma solução é usar +[o driver de rede do host](https://docs.docker.com/network/network-tutorial-host/): ```console docker run \ @@ -50,9 +63,14 @@ docker run \ ``` O driver de rede do host não é compatível com Mac e Windows. -Nessas plataformas, você terá que descobrir o endereço IP do contêiner e incluí-lo nos nomes dos servidores. +Nessas plataformas, você terá que descobrir o endereço IP do contêiner e +incluí-lo nos nomes dos servidores. -Execute o comando `docker network inspect bridge` e verifique a chave `Containers` para identificar o último endereço IP atribuído atualmente sob a chave `IPv4Address` e incremente-o em um. Se nenhum contêiner estiver em execução, o primeiro endereço IP atribuído geralmente é `172.17.0.2`. +Execute o comando `docker network inspect bridge` e verifique a chave +`Containers` para identificar o último endereço IP atribuído atualmente sob a +chave `IPv4Address` e incremente-o em um. +Se nenhum contêiner estiver em execução, o primeiro endereço IP atribuído +geralmente é `172.17.0.2`. Em seguida, inclua isso na variável de ambiente `SERVER_NAME`: @@ -66,11 +84,13 @@ docker run \ > [!CAUTION] > -> Certifique-se de substituir `172.17.0.3` pelo IP que será atribuído ao seu contêiner. +> Certifique-se de substituir `172.17.0.3` pelo IP que será atribuído ao seu +> contêiner. Agora você deve conseguir acessar `https://127.0.0.1` a partir da máquina host. -Se este não for o caso, inicie o FrankenPHP em modo de depuração para tentar descobrir o problema: +Se este não for o caso, inicie o FrankenPHP em modo de depuração para tentar +descobrir o problema: ```console docker run \ @@ -83,12 +103,21 @@ docker run \ ## Scripts do Composer que referenciam `@php` -[Scripts do Composer](https://getcomposer.org/doc/articles/scripts.md) podem querer executar um binário PHP para algumas tarefas, por exemplo, em [um projeto Laravel](laravel.md) para executar `@php artisan package:discover --ansi`. Isso [atualmente falha](https://github.com/php/frankenphp/issues/483#issuecomment-1899890915) por dois motivos: +[Scripts do Composer](https://getcomposer.org/doc/articles/scripts.md) podem +querer executar um binário PHP para algumas tarefas, por exemplo, em +[um projeto Laravel](laravel.md) para executar +`@php artisan package:discover --ansi`. +Isso +[atualmente falha](https://github.com/php/frankenphp/issues/483#issuecomment-1899890915) +por dois motivos: - O Composer não sabe como chamar o binário do FrankenPHP; -- O Composer pode adicionar configurações do PHP usando a flag `-d` no comando, que o FrankenPHP ainda não suporta. +- O Composer pode adicionar configurações do PHP usando a flag `-d` no comando, + que o FrankenPHP ainda não suporta. -Como solução alternativa, podemos criar um script de shell em `/usr/local/bin/php` que remove os parâmetros não suportados e, em seguida, chama o FrankenPHP: +Como solução alternativa, podemos criar um script de shell em +`/usr/local/bin/php` que remove os parâmetros não suportados e, em seguida, +chama o FrankenPHP: ```bash #!/usr/bin/env bash @@ -106,7 +135,8 @@ done /usr/local/bin/frankenphp php-cli ${args[@]} ``` -Em seguida, defina a variável de ambiente `PHP_BINARY` para o caminho do nosso script `php` e execute o Composer: +Em seguida, defina a variável de ambiente `PHP_BINARY` para o caminho do nosso +script `php` e execute o Composer: ```console export PHP_BINARY=/usr/local/bin/php @@ -115,7 +145,8 @@ composer install ## Solução de problemas de TLS/SSL com binários estáticos -Ao usar binários estáticos, você pode encontrar os seguintes erros relacionados a TLS, por exemplo, ao enviar emails usando STARTTLS: +Ao usar binários estáticos, você pode encontrar os seguintes erros relacionados +a TLS, por exemplo, ao enviar emails usando STARTTLS: ```text Unable to connect with STARTTLS: stream_socket_enable_crypto(): SSL operation failed with code 5. OpenSSL Error messages: @@ -125,20 +156,27 @@ error:80000002:system library::No such file or directory error:0A000086:SSL routines::certificate verify failed ``` -Como o binário estático não empacota certificados TLS, você precisa apontar o OpenSSL para a instalação local de certificados de CA. +Como o binário estático não empacota certificados TLS, você precisa apontar o +OpenSSL para a instalação local de certificados de CA. -Inspecione a saída de [`openssl_get_cert_locations()`](https://www.php.net/manual/en/function.openssl-get-cert-locations.php), para descobrir onde os certificados de CA devem ser instalados e armazene-os neste local. +Inspecione a saída de +[`openssl_get_cert_locations()`](https://www.php.net/manual/pt_BR/function.openssl-get-cert-locations.php), +para descobrir onde os certificados de CA devem ser instalados e armazene-os +neste local. > [!WARNING] > > Contextos web e CLI podem ter configurações diferentes. -> Certifique-se de executar `openssl_get_cert_locations()` no contexto apropriado. +> Certifique-se de executar `openssl_get_cert_locations()` no contexto +> apropriado. [Certificados CA extraídos do Mozilla podem ser baixados no site do cURL](https://curl.se/docs/caextract.html). -Como alternativa, muitas distribuições, incluindo Debian, Ubuntu e Alpine, fornecem pacotes chamados `ca-certificates` que contêm esses certificados. +Como alternativa, muitas distribuições, incluindo Debian, Ubuntu e Alpine, +fornecem pacotes chamados `ca-certificates` que contêm esses certificados. -Também é possível usar `SSL_CERT_FILE` e `SSL_CERT_DIR` para indicar à OpenSSL onde procurar certificados CA: +Também é possível usar `SSL_CERT_FILE` e `SSL_CERT_DIR` para indicar à OpenSSL +onde procurar certificados CA: ```console # Define variáveis de ambiente para certificados TLS diff --git a/docs/pt-br/laravel.md b/docs/pt-br/laravel.md index 4a2df61dd2..05205c53a4 100644 --- a/docs/pt-br/laravel.md +++ b/docs/pt-br/laravel.md @@ -2,7 +2,8 @@ ## Docker -Servir uma aplicação web [Laravel](https://laravel.com) com FrankenPHP é tão fácil quanto montar o projeto no diretório `/app` da imagem Docker oficial. +Servir uma aplicação web [Laravel](https://laravel.com) com FrankenPHP é tão +fácil quanto montar o projeto no diretório `/app` da imagem Docker oficial. Execute este comando a partir do diretório principal da sua aplicação Laravel: @@ -14,30 +15,33 @@ E divirta-se! ## Instalação local -Alternativamente, você pode executar seus projetos Laravel com FrankenPHP a partir da sua máquina local: +Alternativamente, você pode executar seus projetos Laravel com FrankenPHP a +partir da sua máquina local: 1. [Baixe o binário correspondente ao seu sistema](../#standalone-binary). -2. Adicione a seguinte configuração a um arquivo chamado `Caddyfile` no diretório raiz do seu projeto Laravel: +2. Adicione a seguinte configuração a um arquivo chamado `Caddyfile` no + diretório raiz do seu projeto Laravel: ```caddyfile { - frankenphp + frankenphp } # O nome de domínio do seu servidor localhost { - # Define o diretório raiz como public/ - root public/ - # Habilita a compressão (opcional) - encode zstd br gzip - # Executa os arquivos PHP a partir do diretório public/ e serve os assets - php_server { - try_files {path} index.php - } + # Define o diretório raiz como public/ + root public/ + # Habilita a compressão (opcional) + encode zstd br gzip + # Executa os arquivos PHP a partir do diretório public/ e serve os assets + php_server { + try_files {path} index.php + } } ``` -3. Inicie o FrankenPHP a partir do diretório raiz do seu projeto Laravel: `frankenphp run`. +3. Inicie o FrankenPHP a partir do diretório raiz do seu projeto Laravel: + `frankenphp run`. ## Laravel Octane @@ -47,13 +51,15 @@ O Octane pode ser instalado através do gerenciador de pacotes Composer: composer require laravel/octane ``` -Após instalar o Octane, você pode executar o comando `octane:install` do Artisan, que instalará o arquivo de configuração do Octane em sua aplicação: +Após instalar o Octane, você pode executar o comando `octane:install` do +Artisan, que instalará o arquivo de configuração do Octane em sua aplicação: ```console php artisan octane:install --server=frankenphp ``` -O servidor Octane pode ser iniciado por meio do comando `octane:frankenphp` do Artisan. +O servidor Octane pode ser iniciado por meio do comando `octane:frankenphp` do +Artisan. ```console php artisan octane:frankenphp @@ -61,36 +67,49 @@ php artisan octane:frankenphp O comando `octane:frankenphp` pode receber as seguintes opções: -- `--host`: O endereço IP ao qual o servidor deve se vincular (padrão: `127.0.0.1`); +- `--host`: O endereço IP ao qual o servidor deve se vincular (padrão: + `127.0.0.1`); - `--port`: A porta na qual o servidor deve estar disponível (padrão: `8000`); -- `--admin-port`: A porta na qual o servidor de administração deve estar disponível (padrão: `2019`); -- `--workers`: O número de workers que devem estar disponíveis para processar requisições (padrão: `auto`); -- `--max-requests`: O número de requisições a serem processadas antes de recarregar o servidor (padrão: `500`); -- `--caddyfile`: O caminho para o arquivo `Caddyfile` do FrankenPHP (padrão: [stub do `Caddyfile` no Laravel Octane](https://github.com/laravel/octane/blob/2.x/src/Commands/stubs/Caddyfile)); -- `--https`: Habilita HTTPS, HTTP/2 e HTTP/3, e gera e renova certificados automaticamente; -- `--http-redirect`: Habilita o redirecionamento de HTTP para HTTPS (somente habilitado se `--https` for passada); -- `--watch`: Recarrega o servidor automaticamente quando a aplicação é modificada; -- `--poll`: Usa o polling do sistema de arquivos durante a verificação para monitorar arquivos em uma rede; -- `--log-level`: Registra mensagens de log no nível de log especificado ou acima dele, usando o logger nativo do Caddy. +- `--admin-port`: A porta na qual o servidor de administração deve estar + disponível (padrão: `2019`); +- `--workers`: O número de workers que devem estar disponíveis para processar + requisições (padrão: `auto`); +- `--max-requests`: O número de requisições a serem processadas antes de + recarregar o servidor (padrão: `500`); +- `--caddyfile`: O caminho para o arquivo `Caddyfile` do FrankenPHP (padrão: + [stub do `Caddyfile` no Laravel Octane](https://github.com/laravel/octane/blob/2.x/src/Commands/stubs/Caddyfile)); +- `--https`: Habilita HTTPS, HTTP/2 e HTTP/3 e gera e renova certificados + automaticamente; +- `--http-redirect`: Habilita o redirecionamento de HTTP para HTTPS (somente +- habilitado se `--https` for passada); +- `--watch`: Recarrega o servidor automaticamente quando a aplicação é + modificada; +- `--poll`: Usa o polling do sistema de arquivos durante a verificação para + monitorar arquivos em uma rede; +- `--log-level`: Registra mensagens de log no nível de log especificado ou acima + dele, usando o logger nativo do Caddy. > [!TIP] -> Para obter logs JSON estruturados (útil ao usar soluções de análise de logs), passe explicitamente a opção `--log-level`. +> Para obter logs JSON estruturados (útil ao usar soluções de análise de logs), +> passe explicitamente a opção `--log-level`. -Veja também [como usar Mercure com Octane](#mercure-support). - -Saiba mais sobre o [Laravel Octane em sua documentação oficial](https://laravel.com/docs/octane). +Saiba mais sobre o +[Laravel Octane em sua documentação oficial](https://laravel.com/docs/octane). ## Aplicações Laravel como binários independentes -Usando o [recurso de incorporação de aplicações do FrankenPHP](embed.md), é possível distribuir aplicações Laravel como binários independentes. +Usando o [recurso de incorporação de aplicações do FrankenPHP](embed.md), é +possível distribuir aplicações Laravel como binários independentes. -Siga estes passos para empacotar sua aplicação Laravel como um binário independente para Linux: +Siga estes passos para empacotar sua aplicação Laravel como um binário +independente para Linux: -1. Crie um arquivo chamado `static-build.Dockerfile` no repositório da sua aplicação: +1. Crie um arquivo chamado `static-build.Dockerfile` no repositório da sua + aplicação: ```dockerfile FROM --platform=linux/amd64 dunglas/frankenphp:static-builder-gnu - # Se você pretende executar o binário em sistemas musl-libc, use o static-builder-musl em vez disso + # Se você pretende executar o binário em sistemas musl-libc, use o static-builder-musl # Copia sua aplicação WORKDIR /go/src/app/dist/app @@ -117,8 +136,10 @@ Siga estes passos para empacotar sua aplicação Laravel como um binário indepe > [!CAUTION] > - > Alguns arquivos `.dockerignore` ignorarão o diretório `vendor/` e os arquivos `.env`. - > Certifique-se de ajustar ou remover o arquivo `.dockerignore` antes da compilação. + > Alguns arquivos `.dockerignore` ignorarão o diretório `vendor/` e os + > arquivos `.env`. + > Certifique-se de ajustar ou remover o arquivo `.dockerignore` antes da + > compilação. 2. Construa: @@ -158,48 +179,30 @@ Siga estes passos para empacotar sua aplicação Laravel como um binário indepe Agora sua aplicação está pronta! -Saiba mais sobre as opções disponíveis e como compilar binários para outros sistemas operacionais na documentação de [incorporação de aplicações](embed.md). +Saiba mais sobre as opções disponíveis e como compilar binários para outros +sistemas operacionais na documentação de +[incorporação de aplicações](embed.md). ### Alterando o caminho do armazenamento -Por padrão, o Laravel armazena arquivos enviados, caches, logs, etc., no diretório `storage/` da aplicação. -Isso não é adequado para aplicações embarcadas, pois cada nova versão será extraída para um diretório temporário diferente. - -Defina a variável de ambiente `LARAVEL_STORAGE_PATH` (por exemplo, no seu arquivo `.env`) ou chame o método `Illuminate\Foundation\Application::useStoragePath()` para usar um diretório fora do diretório temporário. - -### Suporte a Mercure - -[Mercure](https://mercure.rocks) é uma ótima maneira de adicionar recursos em tempo real às suas aplicações Laravel. -FrankenPHP inclui [suporte a Mercure nativamente](mercure.md). - -Se você não estiver usando [Octane](#laravel-octane), consulte [a documentação do Mercure](mercure.md). - -Se você estiver usando Octane, pode habilitar o suporte a Mercure adicionando as seguintes linhas ao seu arquivo `config/octane.php`: - -```php -// ... - -return [ - // ... - - 'mercure' => [ - 'anonymous' => true, - 'publisher_jwt' => '!ChangeThisMercureHubJWTSecretKey!', - 'subscriber_jwt' => '!ChangeThisMercureHubJWTSecretKey!', - ], -]; -``` - -Você pode usar [todas as diretivas suportadas pelo Mercure](https://mercure.rocks/docs/hub/config#directives) neste array. +Por padrão, o Laravel armazena arquivos enviados, caches, logs, etc., no +diretório `storage/` da aplicação. +Isso não é adequado para aplicações embarcadas, pois cada nova versão será +extraída para um diretório temporário diferente. -Para publicar e assinar atualizações, recomendamos usar a biblioteca [Laravel Mercure Broadcaster](https://github.com/mvanduijker/laravel-mercure-broadcaster). -Alternativamente, consulte [a documentação do Mercure](mercure.md) para fazer isso em PHP puro e JavaScript. +Defina a variável de ambiente `LARAVEL_STORAGE_PATH` (por exemplo, no seu +arquivo `.env`) ou chame o método +`Illuminate\Foundation\Application::useStoragePath()` para usar um diretório +fora do diretório temporário. ### Executando o Octane com binários independentes -É possível até empacotar aplicações Octane do Laravel como binários independentes! +É possível até empacotar aplicações Octane do Laravel como binários +independentes! -Para fazer isso, [instale o Octane corretamente](#laravel-octane) e siga os passos descritos na [seção anterior](#aplicações-laravel-como-binários-independentes). +Para fazer isso, [instale o Octane corretamente](#laravel-octane) e siga os +passos descritos na +[seção anterior](#aplicações-laravel-como-binários-independentes). Em seguida, para iniciar o FrankenPHP no modo worker através do Octane, execute: @@ -209,4 +212,6 @@ PATH="$PWD:$PATH" frankenphp php-cli artisan octane:frankenphp > [!CAUTION] > -> Para que o comando funcione, o binário independente **deve** ser nomeado `frankenphp` porque o Octane precisa de um programa chamado `frankenphp` disponível no caminho. +> Para que o comando funcione, o binário independente **deve** ser nomeado +> `frankenphp` porque o Octane precisa de um programa chamado `frankenphp` +> disponível no caminho. diff --git a/docs/pt-br/logging.md b/docs/pt-br/logging.md deleted file mode 100644 index 5817f30a22..0000000000 --- a/docs/pt-br/logging.md +++ /dev/null @@ -1,73 +0,0 @@ -# Registro (Logging) - -FrankenPHP se integra perfeitamente com o [sistema de registro do Caddy](https://caddyserver.com/docs/logging). -Você pode registrar mensagens usando funções PHP padrão ou aproveitar a função dedicada `frankenphp_log()` para recursos avançados -de registro estruturado. - -## `frankenphp_log()` - -A função `frankenphp_log()` permite que você emita logs estruturados diretamente de sua aplicação PHP, -facilitando muito a ingestão em plataformas como Datadog, Grafana Loki ou Elastic, bem como o suporte a OpenTelemetry. - -Internamente, `frankenphp_log()` envolve o [pacote `log/slog` do Go](https://pkg.go.dev/log/slog) para fornecer recursos de registro -ricos. - -Esses logs incluem o nível de severidade e dados de contexto opcionais. - -```php -function frankenphp_log(string $message, int $level = FRANKENPHP_LOG_LEVEL_INFO, array $context = []): void -``` - -### Parâmetros - -- **`message`**: A string da mensagem de log. -- **`level`**: O nível de severidade do log. Pode ser qualquer número inteiro arbitrário. Constantes de conveniência são fornecidas para níveis comuns: `FRANKENPHP_LOG_LEVEL_DEBUG` (`-4`), `FRANKENPHP_LOG_LEVEL_INFO` (`0`), `FRANKENPHP_LOG_LEVEL_WARN` (`4`) e `FRANKENPHP_LOG_LEVEL_ERROR` (`8`)). O padrão é `FRANKENPHP_LOG_LEVEL_INFO`. -- **`context`**: Um array associativo de dados adicionais a serem incluídos na entrada de log. - -### Exemplo - -```php - memory_get_usage(), - 'peak_usage' => memory_get_peak_usage(), - ], -); - -``` - -Ao visualizar os logs (por exemplo, via `docker compose logs`), a saída aparecerá como JSON estruturado: - -```json -{"level":"info","ts":1704067200,"logger":"frankenphp","msg":"Hello from FrankenPHP!"} -{"level":"warn","ts":1704067200,"logger":"frankenphp","msg":"Memory usage high","current_usage":10485760,"peak_usage":12582912} -``` - -## `error_log()` - -FrankenPHP também permite o registro usando a função padrão `error_log()`. Se o parâmetro `$message_type` for `4` (SAPI), -essas mensagens são roteadas para o logger do Caddy. - -Por padrão, as mensagens enviadas via `error_log()` são tratadas como texto não estruturado. -Elas são úteis para compatibilidade com aplicações ou bibliotecas existentes que dependem da biblioteca PHP padrão. - -### Exemplo com error_log() - -```php -error_log("Falha na conexão com o banco de dados", 4); -``` - -Isso aparecerá nos logs do Caddy, muitas vezes prefixado para indicar que se originou do PHP. - -> [!TIP] -> Para melhor observabilidade em ambientes de produção, prefira `frankenphp_log()` -> pois ele permite filtrar logs por nível (Debug, Error, etc.) -> e consultar campos específicos em sua infraestrutura de registro. diff --git a/docs/pt-br/mercure.md b/docs/pt-br/mercure.md index 376d6491f3..8c10e8d7cb 100644 --- a/docs/pt-br/mercure.md +++ b/docs/pt-br/mercure.md @@ -1,149 +1,21 @@ # Tempo real O FrankenPHP vem com um hub [Mercure](https://mercure.rocks) integrado! -O Mercure permite que você envie eventos em tempo real para todos os dispositivos conectados: eles receberão um evento JavaScript instantaneamente. +O Mercure permite que você envie eventos em tempo real para todos os +dispositivos conectados: eles receberão um evento JavaScript instantaneamente. -É uma alternativa conveniente aos WebSockets, simples de usar e com suporte nativo em todos os navegadores web modernos! +Não é necessária nenhuma biblioteca JS ou SDK! ![Mercure](mercure-hub.png) -## Habilitando o Mercure +Para habilitar o hub Mercure, atualize o `Caddyfile` conforme descrito +[no site do Mercure](https://mercure.rocks/docs/hub/config). -O suporte ao Mercure é desativado por padrão. -Aqui está um exemplo mínimo de um `Caddyfile` habilitando tanto o FrankenPHP quanto o hub Mercure: +O caminho do hub Mercure é `/.well-known/mercure`. +Ao executar o FrankenPHP dentro do Docker, a URL de envio completa seria +`http://php/.well-known/mercure` (com `php` sendo o nome do contêiner que +executa o FrankenPHP). -```caddyfile -# O nome do host para responder -localhost - -mercure { - # A chave secreta usada para assinar os tokens JWT para publicadores - publisher_jwt !ChangeThisMercureHubJWTSecretKey! - # Permite assinantes anônimos (sem JWT) - anonymous -} - -root public/ -php_server -``` - -> [!TIP] -> -> O [exemplo de `Caddyfile`](https://github.com/php/frankenphp/blob/main/caddy/frankenphp/Caddyfile) -> fornecido pelas [imagens Docker](docker.md) já inclui uma configuração Mercure comentada -> com variáveis de ambiente convenientes para configurá-lo. -> -> Descomente a seção Mercure em `/etc/frankenphp/Caddyfile` para habilitá-lo. - -## Assinando Atualizações - -Por padrão, o hub Mercure está disponível no caminho `/.well-known/mercure` do seu servidor FrankenPHP. -Para assinar atualizações, use a classe JavaScript nativa [`EventSource`](https://developer.mozilla.org/docs/Web/API/EventSource): - -```html - - -Exemplo Mercure - -``` - -## Publicando Atualizações - -### Usando `mercure_publish()` - -FrankenPHP fornece uma função conveniente `mercure_publish()` para publicar atualizações no hub Mercure integrado: - -```php - 'value'])); - -// Escreve nos logs do FrankenPHP -error_log("update $updateID published", 4); -``` - -A assinatura completa da função é: - -```php -/** - * @param string|string[] $topics - */ -function mercure_publish(string|array $topics, string $data = '', bool $private = false, ?string $id = null, ?string $type = null, ?int $retry = null): string {} -``` - -### Usando `file_get_contents()` - -Para despachar uma atualização para os assinantes conectados, envie uma requisição POST autenticada para o hub Mercure com os parâmetros `topic` e `data`: - -```php - [ - 'method' => 'POST', - 'header' => "Content-type: application/x-www-form-urlencoded\r\nAuthorization: Bearer " . JWT, - 'content' => http_build_query([ - 'topic' => 'my-topic', - 'data' => json_encode(['key' => 'value']), - ]), -]])); - -// Escreve nos logs do FrankenPHP -error_log("update $updateID published", 4); -``` - -A chave passada como parâmetro da opção `mercure.publisher_jwt` no `Caddyfile` deve ser usada para assinar o token JWT usado no cabeçalho `Authorization`. - -O JWT deve incluir uma reivindicação `mercure` com permissão `publish` para os tópicos nos quais você deseja publicar. -Consulte [a documentação do Mercure](https://mercure.rocks/spec#publishers) sobre autorização. - -Para gerar seus próprios tokens, você pode usar [este link do jwt.io](https://www.jwt.io/#token=eyJhbGciOiJIUzI1NiJ9.eyJtZXJjdXJlIjp7InB1Ymxpc2giOlsiKiJdfX0.PXwpfIGng6KObfZlcOXvcnWCJOWTFLtswGI5DZuWSK4), -mas para aplicações de produção, é recomendado usar tokens de curta duração gerados dinamicamente com uma [biblioteca JWT](https://www.jwt.io/libraries?programming_language=php) confiável. - -### Usando o Symfony Mercure - -Alternativamente, você pode usar o [Componente Symfony Mercure](https://symfony.com/components/Mercure), uma biblioteca PHP autônoma. - -Esta biblioteca lida com a geração de JWT, publicação de atualizações, bem como autorização baseada em cookies para assinantes. - -Primeiro, instale a biblioteca usando o Composer: - -```console -composer require symfony/mercure lcobucci/jwt -``` - -Então, você pode usá-lo assim: - -```php -publish(new \Symfony\Component\Mercure\Update('my-topic', json_encode(['key' => 'value']))); - -// Escreve nos logs do FrankenPHP -error_log("update $updateID published", 4); -``` - -O Mercure também é nativamente suportado por: - -- [Laravel](laravel.md#mercure-support) -- [Symfony](https://symfony.com/doc/current/mercure.html) -- [API Platform](https://api-platform.com/docs/core/mercure/) +Para enviar atualizações do Mercure a partir do seu código, recomendamos o +[Componente Symfony Mercure](https://symfony.com/components/Mercure) (você não +precisa do framework full-stack do Symfony para usá-lo). diff --git a/docs/pt-br/metrics.md b/docs/pt-br/metrics.md index 8b6c79f869..95c918fb13 100644 --- a/docs/pt-br/metrics.md +++ b/docs/pt-br/metrics.md @@ -1,17 +1,29 @@ # Métricas -Quando as [métricas do Caddy](https://caddyserver.com/docs/metrics) estão habilitadas, o FrankenPHP expõe as seguintes métricas: +Quando as [métricas do Caddy](https://caddyserver.com/docs/metrics) estão +habilitadas, o FrankenPHP expõe as seguintes métricas: - `frankenphp_total_threads`: O número total de threads PHP. -- `frankenphp_busy_threads`: O número de threads PHP processando uma requisição no momento (workers em execução sempre consomem uma thread). +- `frankenphp_busy_threads`: O número de threads PHP processando uma requisição + no momento (workers em execução sempre consomem uma thread). - `frankenphp_queue_depth`: O número de requisições regulares na fila. -- `frankenphp_total_workers{worker="[nome_do_worker]"}`: O número total de workers. -- `frankenphp_busy_workers{worker="[nome_do_worker]"}`: O número de workers processando uma requisição no momento. -- `frankenphp_worker_request_time{worker="[nome_do_worker]"}`: O tempo gasto no processamento de requisições por todos os workers. -- `frankenphp_worker_request_count{worker="[nome_do_worker]"}`: O número de requisições processadas por todos os workers. -- `frankenphp_ready_workers{worker="[nome_do_worker]"}`: O número de workers que chamaram `frankenphp_handle_request` pelo menos uma vez. -- `frankenphp_worker_crashes{worker="[nome_do_worker]"}`: O número de vezes que um worker foi encerrado inesperadamente. -- `frankenphp_worker_restarts{worker="[nome_do_worker]"}`: O número de vezes que um worker foi reiniciado deliberadamente. -- `frankenphp_worker_queue_depth{worker="[nome_do_worker]"}`: O número de requisições na fila. +- `frankenphp_total_workers{worker="[nome_do_worker]"}`: O número total de + workers. +- `frankenphp_busy_workers{worker="[nome_do_worker]"}`: O número de workers + processando uma requisição no momento. +- `frankenphp_worker_request_time{worker="[nome_do_worker]"}`: O tempo gasto no + processamento de requisições por todos os workers. +- `frankenphp_worker_request_count{worker="[nome_do_worker]"}`: O número de + requisições processadas por todos os workers. +- `frankenphp_ready_workers{worker="[nome_do_worker]"}`: O número de workers que + chamaram `frankenphp_handle_request` pelo menos uma vez. +- `frankenphp_worker_crashes{worker="[nome_do_worker]"}`: O número de vezes que + um worker foi encerrado inesperadamente. +- `frankenphp_worker_restarts{worker="[nome_do_worker]"}`: O número de vezes que + um worker foi reiniciado deliberadamente. +- `frankenphp_worker_queue_depth{worker="[nome_do_worker]"}`: O número de + requisições na fila. -Para métricas de worker, o placeholder `[nome_do_worker]` é substituído pelo nome do worker no Caddyfile; caso contrário, o caminho absoluto do arquivo do worker será usado. +Para métricas de worker, o placeholder `[nome_do_worker]` é substituído pelo +nome do worker no Caddyfile; caso contrário, o caminho absoluto do arquivo do +worker será usado. diff --git a/docs/pt-br/performance.md b/docs/pt-br/performance.md index 0dd824efd5..8b825be99e 100644 --- a/docs/pt-br/performance.md +++ b/docs/pt-br/performance.md @@ -5,10 +5,10 @@ facilidade de uso. No entanto, é possível melhorar substancialmente o desempenho usando uma configuração apropriada. -## Número de Threads e Workers +## Número de threads e workers Por padrão, o FrankenPHP inicia 2 vezes mais threads e workers (no modo worker) -do que o número de CPUs disponíveis. +do que a quantidade de CPU disponível. Os valores apropriados dependem muito de como sua aplicação foi escrita, do que ela faz e do seu hardware. @@ -28,9 +28,9 @@ diretiva `frankenphp`. ### `max_threads` -Embora seja sempre melhor saber exatamente como será o seu tráfego, aplicativos +Embora seja sempre melhor saber exatamente como será o seu tráfego, aplicações reais tendem a ser mais imprevisíveis. -A [configuração](config.md#caddyfile-config) `max_threads` permite que +A [configuração](config.md#configuracao-do-caddyfile) `max_threads` permite que o FrankenPHP crie threads adicionais automaticamente em tempo de execução até o limite especificado. `max_threads` pode ajudar você a descobrir quantas threads são necessárias para @@ -38,25 +38,24 @@ lidar com seu tráfego e pode tornar o servidor mais resiliente a picos de latência. Se definido como `auto`, o limite será estimado com base no `memory_limit` em seu `php.ini`. -Se não for possível fazer isso, `auto` assumirá como padrão o valor 2x -`num_threads`. +Caso contrário, `auto` assumirá como padrão o valor 2x `num_threads`. Lembre-se de que `auto` pode subestimar bastante o número de threads necessárias. `max_threads` é semelhante ao -[pm.max_children](https://www.php.net/manual/en/install.fpm.configuration.php#pm.max-children) +[pm.max_children](https://www.php.net/manual/pt_BR/install.fpm.configuration.php#pm.max-children) do PHP FPM. A principal diferença é que o FrankenPHP usa threads em vez de processos e as -delega automaticamente entre diferentes scripts worker e o 'modo clássico', +delega automaticamente entre diferentes worker scripts e o modo clássico, conforme necessário. -## Modo Worker +## Modo worker Habilitar [o modo worker](worker.md) melhora drasticamente o desempenho, mas sua aplicação precisa ser adaptada para ser compatível com este modo: você precisa -criar um script worker e garantir que a aplicação não esteja com vazamento de +criar um worker script e garantir que a aplicação não esteja com vazamento de memória. -## Não use musl +## Não use `musl` A variante Alpine Linux das imagens oficiais do Docker e os binários padrão que fornecemos usam [a biblioteca C `musl`](https://musl.libc.org). @@ -64,19 +63,23 @@ fornecemos usam [a biblioteca C `musl`](https://musl.libc.org). O PHP é conhecido por ser [mais lento](https://gitlab.alpinelinux.org/alpine/aports/-/issues/14381) ao usar esta biblioteca C alternativa em vez da biblioteca GNU tradicional, -especialmente quando compilado no modo ZTS (thread-safe), que é necessário para -o FrankenPHP. +especialmente quando compilado no modo ZTS (thread-safe), necessário para o +FrankenPHP. A diferença pode ser significativa em um ambiente com muitas threads. Além disso, -[alguns bugs só acontecem ao usar musl](https://github.com/php/php-src/issues?q=sort%3Aupdated-desc+is%3Aissue+is%3Aopen+label%3ABug+musl). +[alguns bugs só acontecem ao usar `musl`](https://github.com/php/php-src/issues?q=sort%3Aupdated-desc+is%3Aissue+is%3Aopen+label%3ABug+musl). + +Em ambientes de produção, recomendamos o uso do FrankenPHP vinculado à `glibc`. -Em ambientes de produção, recomendamos o uso do FrankenPHP vinculado à glibc, -compilado com um nível de otimização apropriado. +Isso pode ser feito usando as imagens Docker do Debian (o padrão), baixando o +binário com sufixo -gnu de nossos +[Lançamentos](https://github.com/php/frankenphp/releases) ou +[compilando o FrankenPHP a partir do código-fonte](compile.md). -Isso pode ser alcançado usando as imagens Docker do Debian, usando nossos -pacotes [.deb](https://debs.henderkes.com) ou [.rpm](https://rpms.henderkes.com) -dos mantenedores, ou [compilando o FrankenPHP a partir do código-fonte](compile.md). +Como alternativa, fornecemos binários `musl` estáticos compilados com +[o alocador `mimalloc`](https://github.com/microsoft/mimalloc), o que alivia os +problemas em cenários com threads. ## Configuração do runtime do Go @@ -121,7 +124,7 @@ explicitamente `try_files` assim: ```caddyfile php_server { try_files {path} index.php - root /root/to/your/app # adicionar explicitamente o root aqui permite um melhor cache + root /raiz/da/sua/aplicacao # adicionar explicitamente a raiz aqui permite um melhor armazenamento em cache } ``` @@ -129,10 +132,11 @@ Isso pode reduzir significativamente o número de operações desnecessárias co arquivos. Uma abordagem alternativa com 0 operações desnecessárias no sistema de arquivos -seria usar a diretiva `php` e separar os arquivos estáticos do PHP por caminho. +seria usar a diretiva `php` e separar os arquivos estáticos do PHP usando +caminhos. Essa abordagem funciona bem se toda a sua aplicação for servida por um arquivo de entrada. -Um exemplo de [configuração](config.md#caddyfile-config) que serve +Um exemplo de [configuração](config.md#configuracao-do-caddyfile) que serve arquivos estáticos a partir de uma pasta `/assets` poderia ser assim: ```caddyfile @@ -141,15 +145,15 @@ route { path /assets/* } - # tudo em /assets é gerenciado pelo servidor de arquivos + # tudo a partir de /assets é gerenciado pelo servidor de arquivos file_server @assets { - root /root/to/your/app + root /raiz/da/sua/aplicacao } - # tudo o que não está em /assets é gerenciado pelo seu arquivo PHP `index` ou `worker` + # tudo o que não está em /assets é gerenciado pelo seu arquivo index ou worker PHP rewrite index.php php { - root /root/to/your/app # adicionar explicitamente o root aqui permite um melhor cache + root /raiz/da/sua/aplicacao # adicionar explicitamente a raiz aqui permite um melhor armazenamento em cache } } ``` @@ -197,51 +201,15 @@ FrankenPHP. Em particular: -- Verificar se o [OPcache](https://www.php.net/manual/en/book.opcache.php) +- Verifique se o [OPcache](https://www.php.net/manual/pt_BR/book.opcache.php) está instalado, habilitado e configurado corretamente; -- Habilitar as +- Habilite as [otimizações do carregador automático do Composer](https://getcomposer.org/doc/articles/autoloader-optimization.md); -- Garantir que o cache do `realpath` seja grande o suficiente para as +- Certifique-se de que o cache do `realpath` seja grande o suficiente para as necessidades da sua aplicação; -- Usar - [pré-carregamento](https://www.php.net/manual/en/opcache.preloading.php). +- Use + [pré-carregamento](https://www.php.net/manual/pt_BR/opcache.preloading.php). Para mais detalhes, leia [a entrada dedicada na documentação do Symfony](https://symfony.com/doc/current/performance.html) -(a maioria das dicas é útil mesmo que você não utilize o Symfony). - -## Dividindo o Pool de Threads - -É comum que aplicativos interajam com serviços externos lentos, como uma -API que tende a ser não confiável sob alta carga ou consistentemente leva mais -de 10 segundos para responder. -Nesses casos, pode ser benéfico dividir o pool de threads para ter pools -"lentos" dedicados. -Isso evita que os endpoints lentos consumam todos os recursos/threads do servidor -e limita a concorrência de requisições indo em direção ao endpoint lento, -semelhante a um pool de conexões. - -```caddyfile -{ - frankenphp { - max_threads 100 # máximo de 100 threads compartilhadas por todos os workers - } -} - -example.com { - php_server { - root /app/public # o diretório raiz da sua aplicação - worker index.php { - match /slow-endpoint/* # todas as requisições com o caminho /slow-endpoint/* são tratadas por este pool de threads - num 10 # mínimo de 10 threads para requisições que correspondem a /slow-endpoint/* - } - worker index.php { - match * # todas as outras requisições são tratadas separadamente - num 20 # mínimo de 20 threads para outras requisições, mesmo que os endpoints lentos comecem a travar - } - } -} -``` - -Geralmente, também é aconselhável lidar com endpoints muito lentos de forma -assíncrona, usando mecanismos relevantes, como filas de mensagens. +(a maioria das dicas é útil mesmo se você não usa o Symfony). diff --git a/docs/pt-br/production.md b/docs/pt-br/production.md index 35ec62e308..2a51ecb64b 100644 --- a/docs/pt-br/production.md +++ b/docs/pt-br/production.md @@ -1,8 +1,9 @@ # Implantando em produção -Neste tutorial, aprenderemos como implantar uma aplicação PHP em um único servidor usando o Docker Compose. +Neste tutorial, aprenderemos como implantar uma aplicação PHP em um único +servidor usando o Docker Compose. -Se você estiver usando o Symfony, prefira ler a documentação +Se você estiver usando o Symfony, leia a documentação [Implantar em produção](https://github.com/dunglas/symfony-docker/blob/main/docs/production.md) do projeto Docker do Symfony (que usa FrankenPHP). @@ -16,7 +17,7 @@ Primeiro, crie um `Dockerfile` no diretório raiz do seu projeto PHP: ```dockerfile FROM dunglas/frankenphp -# Certifique-se de substituir "your-domain-name.example.com" pelo seu nome de +# Certifique-se de substituir "seu-nome-de-dominio.example.com" pelo seu nome de # domínio ENV SERVER_NAME=seu-nome-de-dominio.example.com # Se quiser desabilitar o HTTPS, use este valor: @@ -69,7 +70,7 @@ volumes: > Em desenvolvimento, você pode querer usar um volume, uma configuração PHP > diferente e um valor diferente para a variável de ambiente `SERVER_NAME`. > -> Confira o projeto [Symfony Docker](https://github.com/dunglas/symfony-docker) +> Consulte o projeto [Symfony Docker](https://github.com/dunglas/symfony-docker) > (que usa FrankenPHP) para um exemplo mais avançado usando imagens > multiestágio, Composer, extensões PHP extras, etc. @@ -119,7 +120,7 @@ Em seguida, crie um registro DNS do tipo `A` para o seu nome de domínio, apontando para o endereço IP do seu servidor: ```dns -seu-nome-de-dominio.example.com. IN A 207.154.233.113 +seu-nome-de-dominio.example.com. IN A ``` Exemplo com o serviço DigitalOcean Domains ("Networking" > "Domains"): @@ -143,10 +144,10 @@ Chaves de implantação também são [suportadas pelo GitLab](https://docs.gitla Exemplo com Git: ```console -git clone git@github.com:/.git +git clone git@github.com:/.git ``` -Acesse o diretório que contém seu projeto (``) e inicie a +Acesse o diretório que contém seu projeto (``) e inicie a aplicação em modo de produção: ```console diff --git a/docs/pt-br/static.md b/docs/pt-br/static.md index 795e67d0c5..3836099fc9 100644 --- a/docs/pt-br/static.md +++ b/docs/pt-br/static.md @@ -14,8 +14,8 @@ ser executados na No entanto, eles não podem carregar extensões PHP dinâmicas (como o Xdebug) e têm algumas limitações por usarem a `libc` `musl`. -Binários principalmente estáticos requerem apenas `glibc` e podem carregar -extensões dinâmicas. +A maioria dos binários estáticos requer apenas `glibc` e pode carregar extensões +dinâmicas. Sempre que possível, recomendamos o uso de compilações principalmente estáticas baseadas na `glibc`. @@ -202,7 +202,7 @@ docker rm static-builder-gnu docker rmi gnu-ext ``` -Isso terá criado `frankenphp` e `xdebug-zts.so` no diretório atual. +Isso criará `frankenphp` e `xdebug-zts.so` no diretório atual. Se você mover `xdebug-zts.so` para o diretório de extensões, adicione `zend_extension=xdebug-zts.so` ao seu `php.ini` e execute o FrankenPHP, ele carregará o Xdebug. diff --git a/docs/pt-br/wordpress.md b/docs/pt-br/wordpress.md deleted file mode 100644 index a8db381a71..0000000000 --- a/docs/pt-br/wordpress.md +++ /dev/null @@ -1,59 +0,0 @@ -# WordPress - -Execute [WordPress](https://wordpress.org/) com FrankenPHP para desfrutar de uma pilha moderna e de alta performance com HTTPS automático, HTTP/3 e compressão Zstandard. - -## Instalação Mínima - -1. [Baixe o WordPress](https://wordpress.org/download/) -2. Extraia o arquivo ZIP e abra um terminal no diretório extraído -3. Execute: - - ```console - frankenphp php-server - ``` - -4. Acesse `http://localhost/wp-admin/` e siga as instruções de instalação -5. Aproveite! - -Para uma configuração pronta para produção, prefira usar `frankenphp run` com um `Caddyfile` como este: - -```caddyfile -example.com - -php_server -encode zstd br gzip -log -``` - -## Recarregamento Instantâneo - -Para usar o recurso de [recarregamento instantâneo](hot-reload.md) com WordPress, habilite o [Mercure](mercure.md) e adicione a sub-diretiva `hot_reload` à diretiva `php_server` no seu `Caddyfile`: - -```caddyfile -localhost - -mercure { - anonymous -} - -php_server { - hot_reload -} -``` - -Em seguida, adicione o código necessário para carregar as bibliotecas JavaScript no arquivo `functions.php` do seu tema WordPress: - -```php -function hot_reload() { - ?> - - - - - - [!TIP] -> A seção a seguir é necessária apenas antes do Symfony 7.4, onde o suporte nativo para o modo worker do FrankenPHP foi introduzido. - O modo worker do FrankenPHP é suportado pelo [Componente Symfony Runtime](https://symfony.com/doc/current/components/runtime.html). Para iniciar qualquer aplicação Symfony em um worker, instale o pacote @@ -83,7 +78,8 @@ uma biblioteca de terceiros: boot(); // Manipulador fora do loop para melhor desempenho (fazendo menos trabalho) $handler = static function () use ($myApp) { - try { - // Chamado quando uma requisição é recebida, - // superglobals, php://input e similares são redefinidos - echo $myApp->handle($_GET, $_POST, $_COOKIE, $_FILES, $_SERVER); - } catch (\Throwable $exception) { - // `set_exception_handler` é chamado apenas quando o script do worker termina, - // o que pode não ser o que você espera, então capture e trate as exceções aqui - (new \MyCustomExceptionHandler)->handleException($exception); - } + // Chamado quando uma requisição é recebida, + // superglobals, php://input e similares são redefinidos + echo $myApp->handle($_GET, $_POST, $_COOKIE, $_FILES, $_SERVER); }; $maxRequests = (int)($_SERVER['MAX_REQUESTS'] ?? 0); @@ -159,11 +149,12 @@ requisições a serem processadas, definindo uma variável de ambiente chamada ### Reiniciar os workers manualmente Embora seja possível reiniciar os workers -[em alterações de arquivo](config.md#watching-for-file-changes), também +[em alterações de arquivo](config.md#monitorando-alteracoes-em-arquivos), também é possível reiniciar todos os workers graciosamente por meio da [API de administração do Caddy](https://caddyserver.com/docs/api). Se o administrador estiver habilitado no seu -[Caddyfile](config.md#caddyfile-config), você pode acessar o endpoint de reinicialização com uma simples requisição POST como esta: +[Caddyfile](config.md#configuracao-do-caddyfile), você pode executar ping no +endpoint de reinicialização com uma simples requisição POST como esta: ```console curl -X POST http://localhost:2019/frankenphp/workers/restart @@ -220,3 +211,4 @@ $handler = static function () use ($workerServer) { }; // ... +``` diff --git a/docs/pt-br/x-sendfile.md b/docs/pt-br/x-sendfile.md index c8a6c2231a..6ad940434f 100644 --- a/docs/pt-br/x-sendfile.md +++ b/docs/pt-br/x-sendfile.md @@ -20,7 +20,7 @@ Esse recurso é conhecido como **`X-Sendfile`** para Apache e Nos exemplos a seguir, assumimos que o diretório raiz do projeto é o diretório `public/` e que queremos usar PHP para servir arquivos armazenados fora do -diretório `public/`, de um diretório chamado `private-files/`. +diretório `public/`, de um diretório chamado `arquivos-privados/`. ## Configuração @@ -31,18 +31,20 @@ este recurso: root public/ # ... -+ # Necessário para Symfony, Laravel e outros projetos que usam o componente Symfony HttpFoundation ++ # Necessário para Symfony, Laravel e outros projetos que usam o componente ++ # Symfony HttpFoundation + request_header X-Sendfile-Type x-accel-redirect -+ request_header X-Accel-Mapping ../private-files=/private-files ++ request_header X-Accel-Mapping ../arquivos-privados=/arquivos-privados + + intercept { + @accel header X-Accel-Redirect * + handle_response @accel { -+ root private-files/ ++ root arquivos-privados/ + rewrite * {resp.header.X-Accel-Redirect} + method * GET + -+ # Remove o cabeçalho X-Accel-Redirect definido pelo PHP para maior segurança ++ # Remove o cabeçalho X-Accel-Redirect definido pelo PHP para maior ++ # segurança + header -X-Accel-Redirect + + file_server @@ -54,11 +56,11 @@ este recurso: ## PHP simples -Defina o caminho relativo do arquivo (do diretório `private-files/`) como o valor do +Defina o caminho relativo do arquivo (de `arquivos-privados/`) como o valor do cabeçalho `X-Accel-Redirect`: ```php -header('X-Accel-Redirect: file.txt'); +header('X-Accel-Redirect: arquivo.txt'); ``` ## Projetos que utilizam o componente Symfony HttpFoundation (Symfony, Laravel, Drupal...) @@ -72,6 +74,7 @@ Ele determinará automaticamente o valor correto para o cabeçalho use Symfony\Component\HttpFoundation\BinaryFileResponse; BinaryFileResponse::trustXSendfileTypeHeader(); -$response = new BinaryFileResponse(__DIR__.'/../private-files/file.txt'); +$response = new BinaryFileResponse(__DIR__.'/../arquivos-privados/arquivo.txt'); // ... +``` diff --git a/docs/ru/classic.md b/docs/ru/classic.md deleted file mode 100644 index bce296646c..0000000000 --- a/docs/ru/classic.md +++ /dev/null @@ -1,11 +0,0 @@ -# Использование классического режима - -Без какой-либо дополнительной конфигурации FrankenPHP работает в классическом режиме. В этом режиме FrankenPHP функционирует как традиционный PHP-сервер, напрямую обслуживая PHP-файлы. Это делает его бесшовной заменой для PHP-FPM или Apache с mod_php. - -Подобно Caddy, FrankenPHP принимает неограниченное количество соединений и использует [фиксированное количество потоков](config.md#caddyfile-config) для их обслуживания. Количество принятых и поставленных в очередь соединений ограничено только доступными системными ресурсами. -Пул потоков PHP работает с фиксированным количеством потоков, инициализируемых при запуске, что сравнимо со статическим режимом PHP-FPM. Также возможно позволить потокам [автоматически масштабироваться во время выполнения](performance.md#max_threads), аналогично динамическому режиму PHP-FPM. - -Соединения, поставленные в очередь, будут ждать неопределенное время, пока не станет доступен поток PHP для их обслуживания. Чтобы избежать этого, вы можете использовать [конфигурацию](config.md#caddyfile-config) `max_wait_time` в глобальной конфигурации FrankenPHP, чтобы ограничить время ожидания запроса свободного потока PHP до его отклонения. -Кроме того, вы можете установить разумный [таймаут записи в Caddy](https://caddyserver.com/docs/caddyfile/options#timeouts). - -Каждый экземпляр Caddy запускает только один пул потоков FrankenPHP, который будет использоваться всеми блоками `php_server`. diff --git a/docs/ru/compile.md b/docs/ru/compile.md index 6161a720db..549f03d0d1 100644 --- a/docs/ru/compile.md +++ b/docs/ru/compile.md @@ -1,30 +1,13 @@ # Компиляция из исходников -Этот документ объясняет, как создать бинарный файл FrankenPHP, который будет загружать PHP как динамическую библиотеку. +Этот документ объясняет, как создать бинарный файл FrankenPHP, который будет загружать PHP как динамическую библиотеку. Это рекомендуемый способ. -Альтернативно, могут быть созданы [полностью и преимущественно статические сборки](static.md). +Альтернативно можно создать [статическую сборку](static.md). ## Установка PHP -FrankenPHP совместим с PHP 8.2 и выше. - -### С помощью Homebrew (Linux и Mac) - -Самый простой способ установить версию `libphp`, совместимую с FrankenPHP, — использовать ZTS-пакеты, предоставляемые [Homebrew PHP](https://github.com/shivammathur/homebrew-php). - -Сначала, если вы еще не сделали этого, установите [Homebrew](https://brew.sh). - -Затем установите ZTS-вариант PHP, Brotli (опционально, для поддержки сжатия) и watcher (опционально, для обнаружения изменений файлов): - -```console -brew install shivammathur/php/php-zts brotli watcher -brew link --overwrite --force shivammathur/php/php-zts -``` - -### Путем компиляции PHP - -Альтернативно, вы можете скомпилировать PHP из исходников с опциями, необходимыми для FrankenPHP, выполнив следующие шаги. +FrankenPHP совместим с PHP версии 8.2 и выше. Сначала [загрузите исходники PHP](https://www.php.net/downloads.php) и распакуйте их: @@ -33,10 +16,10 @@ tar xf php-* cd php-*/ ``` -Далее выполните скрипт `configure` с параметрами, необходимыми для вашей платформы. +Далее выполните скрипт `configure` с параметрами, необходимыми для вашей платформы. Следующие флаги `./configure` обязательны, но вы можете добавить и другие, например, для компиляции расширений или дополнительных функций. -#### Linux +### Linux ```console ./configure \ @@ -46,12 +29,13 @@ cd php-*/ --enable-zend-max-execution-timers ``` -#### Mac +### Mac -Используйте пакетный менеджер [Homebrew](https://brew.sh/) для установки необходимых и опциональных зависимостей: +Используйте пакетный менеджер [Homebrew](https://brew.sh/) для установки +`libiconv`, `bison`, `re2c` и `pkg-config`: ```console -brew install libiconv bison brotli re2c pkg-config watcher +brew install libiconv bison brotli re2c pkg-config echo 'export PATH="/opt/homebrew/opt/bison/bin:$PATH"' >> ~/.zshrc ``` @@ -59,13 +43,16 @@ echo 'export PATH="/opt/homebrew/opt/bison/bin:$PATH"' >> ~/.zshrc ```console ./configure \ - --enable-embed \ + --enable-embed=static \ --enable-zts \ --disable-zend-signals \ + --disable-opcache-jit \ + --enable-static \ + --enable-shared=no \ --with-iconv=/opt/homebrew/opt/libiconv/ ``` -#### Компиляция PHP +## Компиляция PHP Наконец, скомпилируйте и установите PHP: @@ -76,23 +63,27 @@ sudo make install ## Установка дополнительных зависимостей -Некоторые функции FrankenPHP зависят от опциональных системных зависимостей, которые должны быть установлены. -Альтернативно, эти функции можно отключить, передав теги сборки компилятору Go. +Некоторые функции FrankenPHP зависят от опциональных системных зависимостей. +Альтернативно, эти функции можно отключить, передав соответствующие теги сборки компилятору Go. -| Функция | Зависимость | Тег сборки для отключения | -| :---------------------------------------------- | :------------------------------------------------------------------------------------------------------------ | :------------------------ | -| Сжатие Brotli | [Brotli](https://github.com/google/brotli) | nobrotli | -| Перезапуск worker-скриптов при изменении файлов | [Watcher C](https://github.com/e-dant/watcher/tree/release/watcher-c) | nowatcher | -| [Mercure](mercure.md) | [Библиотека Mercure Go](https://pkg.go.dev/github.com/dunglas/mercure) (устанавливается автоматически, лицензия AGPL) | nomercure | +| Функция | Зависимость | Тег сборки для отключения | +| ----------------------------------------------- | --------------------------------------------------------------------- | ------------------------- | +| Сжатие 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/php/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. -`xcaddy` также позволяет легко добавлять [пользовательские модули Caddy](https://caddyserver.com/docs/modules/) и расширения FrankenPHP: +Альтернативно, используйте [xcaddy](https://github.com/caddyserver/xcaddy) для компиляции FrankenPHP с [пользовательскими модулями Caddy](https://caddyserver.com/docs/modules/): ```console CGO_ENABLED=1 \ @@ -103,31 +94,17 @@ xcaddy build \ --output frankenphp \ --with github.com/dunglas/frankenphp/caddy \ --with github.com/dunglas/mercure/caddy \ - --with github.com/dunglas/vulcain/caddy \ - --with github.com/dunglas/caddy-cbrotli - # Добавьте дополнительные модули Caddy и расширения FrankenPHP здесь - # опционально, если вы хотите скомпилировать из ваших исходников frankenphp: - # --with github.com/dunglas/frankenphp=$(pwd) \ - # --with github.com/dunglas/frankenphp/caddy=$(pwd)/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` +> Если вы используете 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\'"'` +> Для этого измените значение переменной окружения `XCADDY_GO_BUILD_FLAGS`, например: +> `XCADDY_GO_BUILD_FLAGS=$'-ldflags "-w -s -extldflags \'-Wl,-z,stack-size=0x80000\'"'` > (измените значение размера стека в зависимости от требований вашего приложения). - -### Без xcaddy - -Альтернативно, можно скомпилировать FrankenPHP без `xcaddy`, используя команду `go` напрямую: - -```console -curl -L https://github.com/php/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 -``` diff --git a/docs/ru/config.md b/docs/ru/config.md index 6b6e821ca7..7a2c25ba66 100644 --- a/docs/ru/config.md +++ b/docs/ru/config.md @@ -1,81 +1,47 @@ -# Конфигурация +# Конфигурация -FrankenPHP, Caddy, а также модули [Mercure](mercure.md) и [Vulcain](https://vulcain.rocks) могут быть настроены с использованием [форматов, поддерживаемых Caddy](https://caddyserver.com/docs/getting-started#your-first-config). +FrankenPHP, Caddy, а также модули Mercure и Vulcain могут быть настроены с использованием [конфигурационных форматов, поддерживаемых Caddy](https://caddyserver.com/docs/getting-started#your-first-config). -Наиболее распространенным форматом является `Caddyfile` — простой, человекочитаемый текстовый формат. -По умолчанию FrankenPHP будет искать `Caddyfile` в текущей директории. -Вы можете указать собственный путь с помощью опции `-c` или `--config`. +В [Docker-образах](docker.md) файл `Caddyfile` находится по пути `/etc/frankenphp/Caddyfile`. +Статический бинарный файл будет искать `Caddyfile` в директории запуска. -Минимальный `Caddyfile` для обслуживания PHP-приложения показан ниже: +PHP можно настроить [с помощью файла `php.ini`](https://www.php.net/manual/en/configuration.file.php). -```caddyfile -# The hostname to respond to -localhost - -# Optionaly, the directory to serve files from, otherwise defaults to the current directory -#root public/ -php_server -``` - -Более продвинутый `Caddyfile`, включающий больше возможностей и предоставляющий удобные переменные окружения, находится [в репозитории FrankenPHP](https://github.com/php/frankenphp/blob/main/caddy/frankenphp/Caddyfile), -а также поставляется с Docker-образами. - -Сам PHP может быть настроен [с помощью файла `php.ini`](https://www.php.net/manual/en/configuration.file.php). - -В зависимости от метода установки, FrankenPHP и PHP-интерпретатор будут искать конфигурационные файлы в местах, описанных ниже. - -## Docker - -FrankenPHP: +PHP-интерпретатор будет искать в следующих местах: -- `/etc/frankenphp/Caddyfile`: основной конфигурационный файл -- `/etc/frankenphp/Caddyfile.d/*.caddyfile`: дополнительные конфигурационные файлы, загружаемые автоматически +Docker: -PHP: - -- `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-/` -- Вам следует скопировать официальный шаблон, предоставляемый проектом PHP: +- 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-/` +- Вы должны скопировать официальный шаблон, предоставляемый проектом PHP: ```dockerfile FROM dunglas/frankenphp -# Production: +# Для production: RUN cp $PHP_INI_DIR/php.ini-production $PHP_INI_DIR/php.ini -# Or development: +# Или для development: RUN cp $PHP_INI_DIR/php.ini-development $PHP_INI_DIR/php.ini ``` -## RPM и Debian пакеты - -FrankenPHP: - -- `/etc/frankenphp/Caddyfile`: основной конфигурационный файл -- `/etc/frankenphp/Caddyfile.d/*.caddyfile`: дополнительные конфигурационные файлы, загружаемые автоматически - -PHP: - -- `php.ini`: `/etc/php-zts/php.ini` (файл `php.ini` с производственными настройками предоставляется по умолчанию) -- дополнительные конфигурационные файлы: `/etc/php-zts/conf.d/*.ini` - -## Статический бинарный файл - -FrankenPHP: +Установка FrankenPHP (.rpm или .deb): -- В текущей рабочей директории: `Caddyfile` +- php.ini: `/etc/frankenphp/php.ini` По умолчанию предоставляется файл php.ini с производственными настройками. +- дополнительные файлы конфигурации: `/etc/frankenphp/php.d/*.ini` +- расширения php: `/usr/lib/frankenphp/modules/` -PHP: +Статический бинарный файл: -- `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/). +- 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-приложения. +[HTTP-директивы](https://caddyserver.com/docs/caddyfile/concepts#directives) `php_server` или `php` могут быть использованы в блоках сайта для обработки вашего PHP-приложения. Минимальный пример: @@ -88,22 +54,18 @@ localhost { } ``` -Вы также можете явно настроить FrankenPHP с помощью [глобальной опции](https://caddyserver.com/docs/caddyfile/concepts#global-options) `frankenphp`: +Вы также можете явно настроить FrankenPHP с помощью глобальной опции: +[Глобальная опция](https://caddyserver.com/docs/caddyfile/concepts#global-options) `frankenphp` может быть использована для настройки FrankenPHP. ```caddyfile { frankenphp { - num_threads # Устанавливает количество PHP-потоков для запуска. По умолчанию: 2x от числа доступных CPU. - max_threads # Ограничивает количество дополнительных PHP-потоков, которые могут быть запущены во время выполнения. По умолчанию: num_threads. Может быть установлено в 'auto'. - max_wait_time # Устанавливает максимальное время, в течение которого запрос может ожидать свободный PHP-поток до тайм-аута. По умолчанию: отключено. - php_ini # Устанавливает директиву php.ini. Может быть использовано несколько раз для установки нескольких директив. + num_threads # Указывает количество потоков PHP. По умолчанию: 2x от числа доступных CPU. worker { - file # Устанавливает путь к worker-скрипту. - num # Устанавливает количество PHP-потоков для запуска, по умолчанию 2x от числа доступных CPU. - env # Устанавливает дополнительную переменную окружения с указанным значением. Может быть указано несколько раз для нескольких переменных окружения. - watch # Устанавливает путь для отслеживания изменений файлов. Может быть указано несколько раз для нескольких путей. - name # Устанавливает имя worker, используемое в логах и метриках. По умолчанию: абсолютный путь к файлу worker. - max_consecutive_failures # Устанавливает максимальное количество последовательных сбоев, после которых worker считается неработоспособным; -1 означает, что worker будет всегда перезапускаться. По умолчанию: 6. + file # Указывает путь к worker-скрипту. + num # Указывает количество потоков PHP. По умолчанию: 2x от числа доступных CPU. + env # Устанавливает дополнительную переменную окружения. Можно указать несколько раз для разных переменных. + watch # Указывает путь для отслеживания изменений файлов.Можно указать несколько раз для разных путей. } } } @@ -127,7 +89,7 @@ localhost { ```caddyfile app.example.com { - root /path/to/app/public + root /path/to/app/public php_server { root /path/to/app/public # позволяет лучше кэшировать worker index.php @@ -135,7 +97,7 @@ app.example.com { } other.example.com { - root /path/to/other/public + root /path/to/other/public php_server { root /path/to/other/public worker index.php @@ -145,21 +107,19 @@ other.example.com { # ... ``` -Использование директивы `php_server` — это то, что нужно в большинстве случаев, -но если вам нужен полный контроль, вы можете использовать более низкоуровневую директиву `php`. -Директива `php` передает все входные данные в PHP, вместо того чтобы сначала проверять, является ли это PHP-файлом или нет. Подробнее об этом читайте на [странице производительности](performance.md#try_files). +Использование директивы `php_server` — это то, что нужно в большинстве случаев. Однако если требуется полный контроль, вы можете использовать более низкоуровневую директиву `php`: Использование директивы `php_server` эквивалентно следующей конфигурации: ```caddyfile route { - # Add trailing slash for directory requests + # Добавить слэш в конец запросов к директориям @canonicalPath { file {path}/index.php not path */ } redir @canonicalPath {path}/ 308 - # If the requested file does not exist, try index files + # Если запрошенный файл не существует, попытаться использовать файлы index @indexFiles file { try_files {path} {path}/index.php index.php split_path .php @@ -176,18 +136,17 @@ route { ```caddyfile php_server [] { - root # Устанавливает корневую папку сайта. По умолчанию: директива `root`. - split_path # Устанавливает подстроки для разделения URI на две части. Первая соответствующая подстрока будет использована для разделения "path info" от пути. Первая часть будет дополнена соответствующей подстрокой и будет считаться именем фактического ресурса (CGI-скрипта). Вторая часть будет установлена как PATH_INFO для использования скриптом. По умолчанию: `.php`. - resolve_root_symlink false # Отключает разрешение корневой директории до ее фактического значения путем оценки символической ссылки, если таковая существует (включено по умолчанию). - env # Устанавливает дополнительную переменную окружения с указанным значением. Может быть указано несколько раз для нескольких переменных окружения. + root # Указывает корневую директорию сайта. По умолчанию: директива `root`. + split_path # Устанавливает подстроки для разделения URI на две части. Первая часть будет использована как имя ресурса (CGI-скрипта), вторая часть — как PATH_INFO. По умолчанию: `.php`. + resolve_root_symlink false # Отключает разрешение символьных ссылок для `root` (включено по умолчанию). + env # Устанавливает дополнительные переменные окружения. Можно указать несколько раз для разных переменных. file_server off # Отключает встроенную директиву file_server. - worker { # Создает worker, специфичный для этого сервера. Может быть указано несколько раз для нескольких workers. - file # Устанавливает путь к worker-скрипту, может быть относительным к корню php_server. - num # Устанавливает количество PHP-потоков для запуска, по умолчанию 2x от числа доступных CPU. + worker { # Создает worker, специфичный для этого сервера. Можно указать несколько раз для разных workers. + file # Указывает путь к worker-скрипту, может быть относительным к корню php_server + num # Указывает количество потоков PHP. По умолчанию: 2x от числа доступных CPU. name # Устанавливает имя для worker, используемое в логах и метриках. По умолчанию: абсолютный путь к файлу worker. Всегда начинается с m# при определении в блоке php_server. - watch # Устанавливает путь для отслеживания изменений файлов. Может быть указано несколько раз для нескольких путей. - env # Устанавливает дополнительную переменную окружения с указанным значением. Может быть указано несколько раз для нескольких переменных окружения. Переменные окружения для этого worker также наследуются от родительского php_server, но могут быть переопределены здесь. - match # сопоставляет worker с шаблоном пути. Переопределяет try_files и может быть использовано только в директиве php_server. + watch # Указывает путь для отслеживания изменений файлов. Можно указать несколько раз для разных путей. + env # Устанавливает дополнительную переменную окружения. Можно указать несколько раз для разных переменных. Переменные окружения для этого worker также наследуются от родительского php_server, но могут быть переопределены здесь. } worker # Также можно использовать краткую форму как в глобальном блоке frankenphp. } @@ -195,11 +154,9 @@ php_server [] { ### Отслеживание изменений файлов -Поскольку workers запускают ваше приложение только один раз и держат его в памяти, любые изменения -в ваших PHP-файлах не будут отражены немедленно. +Поскольку workers запускают ваше приложение только один раз и держат его в памяти, изменения в PHP-файлах не будут применяться сразу. -Workers могут быть перезапущены при изменении файлов с помощью директивы `watch`. -Это полезно для сред разработки. +Для разработки можно настроить перезапуск workers при изменении файлов с помощью директивы `watch`: ```caddyfile { @@ -212,12 +169,8 @@ Workers могут быть перезапущены при изменении } ``` -Эта функция часто используется в сочетании с [горячей перезагрузкой](hot-reload.md). - -Если директория `watch` не указана, по умолчанию будет использоваться `./**/*.{env,php,twig,yaml,yml}`, -который отслеживает все файлы `.env`, `.php`, `.twig`, `.yaml` и `.yml` в директории и поддиректориях, -где был запущен процесс FrankenPHP. Вы также можете указать одну или несколько директорий с помощью -[шаблона имени файла оболочки](https://pkg.go.dev/path/filepath#Match): +Если директория для `watch` не указана, по умолчанию будет использоваться путь `./**/*.{php,yaml,yml,twig,env}`, +который отслеживает все файлы с расширениями `.php`, `.yaml`, `.yml`, `.twig` и `.env` в директории, где был запущен процесс FrankenPHP, и во всех её поддиректориях. Вы также можете указать одну или несколько директорий с использованием [шаблона имён файлов](https://pkg.go.dev/path/filepath#Match): ```caddyfile { @@ -225,94 +178,26 @@ Workers могут быть перезапущены при изменении 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 # отслеживает PHP-файлы в /path/to/app и поддиректориях - watch /path/to/app/**/*.{php,twig} # отслеживает PHP- и Twig-файлы в /path/to/app и поддиректориях + watch /path/to/app/**/*.{php,twig} # отслеживает PHP и Twig-файлы в /path/to/app и поддиректориях } } } ``` -- Шаблон `**` означает рекурсивное отслеживание. -- Директории также могут быть относительными (относительно места запуска процесса FrankenPHP). -- Если у вас определено несколько workers, все они будут перезапущены при изменении файла. -- Будьте осторожны с отслеживанием файлов, которые создаются во время выполнения (например, логов), так как это может вызвать нежелательные перезапуски worker. - -Наблюдатель за файлами основан на [e-dant/watcher](https://github.com/e-dant/watcher). - -## Сопоставление Worker с путем - -В традиционных PHP-приложениях скрипты всегда размещаются в публичной директории. -Это также относится и к worker-скриптам, которые обрабатываются как любые другие PHP-скрипты. -Если вы хотите разместить worker-скрипт вне публичной директории, вы можете сделать это с помощью директивы `match`. - -Директива `match` — это оптимизированная альтернатива `try_files`, доступная только внутри `php_server` и `php`. -Следующий пример всегда будет отдавать файл из публичной директории, если он присутствует, -и в противном случае перенаправлять запрос worker, соответствующему шаблону пути. - -```caddyfile -{ - frankenphp { - php_server { - worker { - file /path/to/worker.php # файл может находиться за пределами публичного пути - match /api/* # все запросы, начинающиеся с /api/, будут обрабатываться этим worker - } - } - } -} -``` - -## Переменные окружения +- Шаблон `**` указывает на рекурсивное отслеживание. +- Директории могут быть указаны относительно директории запуска FrankenPHP. +- Если у вас определено несколько workers, все они будут перезапущены при изменении файлов. +- Избегайте отслеживания файлов, создаваемых во время выполнения (например, логов), так как это может вызвать нежелательные перезапуски. -Следующие переменные окружения могут быть использованы для внедрения директив Caddy в `Caddyfile` без его изменения: - -- `SERVER_NAME`: изменение [адресов для прослушивания](https://caddyserver.com/docs/caddyfile/concepts#addresses); предоставленные хостнеймы также будут использованы для генерации TLS-сертификата -- `SERVER_ROOT`: изменение корневой директории сайта, по умолчанию `public/` -- `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`, присутствующие в указанных директориях. - -Вы также можете изменить конфигурацию PHP с помощью директивы `php_ini` в `Caddyfile`: - -```caddyfile -{ - frankenphp { - php_ini memory_limit 256M - - # or - - php_ini { - memory_limit 256M - max_execution_time 15 - } - } -} -``` - -### Отключение HTTPS - -По умолчанию FrankenPHP автоматически включает HTTPS для всех хостнеймов, включая `localhost`. -Если вы хотите отключить HTTPS (например, в среде разработки), вы можете установить переменную окружения `SERVER_NAME` в `http://` или `:80`: - -В качестве альтернативы вы можете использовать все другие методы, описанные в [документации Caddy](https://caddyserver.com/docs/automatic-https#activation). - -Если вы хотите использовать HTTPS с IP-адресом `127.0.0.1` вместо хостнейма `localhost`, пожалуйста, прочтите раздел [известных проблем](known-issues.md#using-https127001-with-docker). +Механизм отслеживания файлов основан на [e-dant/watcher](https://github.com/e-dant/watcher). ### Полный дуплекс (HTTP/1) -При использовании HTTP/1.x может быть желательно включить режим полного дуплекса, чтобы разрешить запись ответа до того, как будет прочитано все тело запроса. (например: [Mercure](mercure.md), WebSocket, Server-Sent Events и т.д.) +При использовании HTTP/1.x можно включить режим полного дуплекса, чтобы разрешить запись ответа до завершения чтения тела запроса (например, для WebSocket, Server-Sent Events и т.д.). -Это опциональная конфигурация, которую необходимо добавить в глобальные опции в `Caddyfile`: +Эта опция включается вручную и должна быть добавлена в глобальные настройки `Caddyfile`: ```caddyfile { @@ -324,8 +209,8 @@ Workers могут быть перезапущены при изменении > [!CAUTION] > -> Включение этой опции может привести к зависанию старых HTTP/1.x клиентов, которые не поддерживают полный дуплекс. -> Это также можно настроить с помощью переменной окружения `CADDY_GLOBAL_OPTIONS`: +> Включение этой опции может привести к зависанию устаревших HTTP/1.x клиентов, которые не поддерживают полный дуплекс. +> Настройка также доступна через переменную окружения `CADDY_GLOBAL_OPTIONS`: ```sh CADDY_GLOBAL_OPTIONS="servers { @@ -335,6 +220,23 @@ CADDY_GLOBAL_OPTIONS="servers { Дополнительную информацию об этой настройке можно найти в [документации 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`, чтобы включить режим отладки: diff --git a/docs/ru/docker.md b/docs/ru/docker.md index 0967868837..4376dc7ee9 100644 --- a/docs/ru/docker.md +++ b/docs/ru/docker.md @@ -2,14 +2,14 @@ [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 и 8.5. +Доступны версии для PHP 8.2, 8.3, 8.4 и 8.5. Теги следуют следующему шаблону: `dunglas/frankenphp:-php-`. -- `` и `` — номера версий FrankenPHP и PHP соответственно, от мажорных (например, `1`), минорных (например, `1.2`) до патч-версий (например, `1.2.3`). +- `` и `` — версии FrankenPHP и PHP соответственно: от основных (например, `1`) до минорных (например, `1.2`) и патч-версий (например, `1.2.3`). - `` может быть `trixie` (для Debian Trixie), `bookworm` (для Debian Bookworm) или `alpine` (для последней стабильной версии Alpine). -[Просмотреть теги](https://hub.docker.com/r/dunglas/frankenphp/tags). +[Просмотреть доступные теги](https://hub.docker.com/r/dunglas/frankenphp/tags). ## Как использовать образы @@ -28,13 +28,9 @@ docker build -t my-php-app . docker run -it --rm --name my-running-app my-php-app ``` -## Как настроить конфигурацию - -Для удобства в образ включен [Caddyfile по умолчанию](https://github.com/php/frankenphp/blob/main/caddy/frankenphp/Caddyfile), содержащий полезные переменные окружения. - ## Как установить дополнительные PHP-расширения -Скрипт [`docker-php-extension-installer`](https://github.com/mlocati/docker-php-extension-installer) предоставляется в базовом образе. Установка дополнительных PHP-расширений осуществляется просто: +Скрипт [`docker-php-extension-installer`](https://github.com/mlocati/docker-php-extension-installer) включён в базовый образ. Установка дополнительных PHP-расширений осуществляется просто: ```dockerfile FROM dunglas/frankenphp @@ -82,14 +78,14 @@ FROM dunglas/frankenphp AS runner COPY --from=builder /usr/local/bin/frankenphp /usr/local/bin/frankenphp ``` -Образ `builder`, предоставляемый FrankenPHP, содержит скомпилированную версию `libphp`. +Образ `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-режима по умолчанию +## Активировать worker режим по умолчанию Установите переменную окружения `FRANKENPHP_CONFIG`, чтобы запускать FrankenPHP с Worker-скриптом: @@ -123,7 +119,7 @@ services: image: dunglas/frankenphp # раскомментируйте следующую строку, если хотите использовать собственный Dockerfile #build: . - # раскомментируйте следующую строку, если вы запускаете это в производственной среде + # раскомментируйте следующую строку, если вы запускаете это в продакшн среде # restart: always ports: - "80:80" # HTTP @@ -133,7 +129,7 @@ services: - ./:/app/public - caddy_data:/data - caddy_config:/config - # закомментируйте следующую строку в производственной среде, она позволяет получать удобочитаемые логи в режиме разработки + # закомментируйте следующую строку в продакшн среде, она позволяет получать удобочитаемые логи в режиме разработки tty: true # Томы, необходимые для сертификатов и конфигурации Caddy @@ -142,11 +138,11 @@ volumes: caddy_config: ``` -## Запуск от имени пользователя без прав root +## Запуск под обычным пользователем -FrankenPHP может работать от имени пользователя без прав root в Docker. +FrankenPHP поддерживает запуск под обычным пользователем в Docker. -Вот пример `Dockerfile` для этого: +Пример `Dockerfile` для этого: ```dockerfile FROM dunglas/frankenphp @@ -156,19 +152,19 @@ ARG USER=appuser RUN \ # Для дистрибутивов на основе Alpine используйте "adduser -D ${USER}" useradd ${USER}; \ - # Добавьте дополнительную возможность привязываться к портам 80 и 443 + # Добавьте возможность привязываться к портам 80 и 443 setcap CAP_NET_BIND_SERVICE=+eip /usr/local/bin/frankenphp; \ - # Дайте права на запись для /config/caddy и /data/caddy - chown -R ${USER}:${USER} /config/caddy /data/caddy + # Дайте права на запись для /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). +Даже при запуске без root-прав, FrankenPHP требуется возможность `CAP_NET_BIND_SERVICE` для привязки веб-сервера к зарезервированным портам (80 и 443). -Если вы открываете доступ к FrankenPHP на непривилегированном порту (1024 и выше), можно запустить веб-сервер от имени пользователя без прав root, и без необходимости предоставления дополнительных возможностей: +Если вы открываете доступ к FrankenPHP на непривилегированном порту (1024 и выше), можно запустить веб-сервер от имени обычного пользователя без необходимости предоставления дополнительных возможностей: ```dockerfile FROM dunglas/frankenphp @@ -180,26 +176,26 @@ RUN \ useradd ${USER}; \ # Удалите стандартные возможности setcap -r /usr/local/bin/frankenphp; \ - # Дайте права на запись для /config/caddy и /data/caddy - chown -R ${USER}:${USER} /config/caddy /data/caddy + # Дайте права на запись для /data/caddy и /config/caddy + chown -R ${USER}:${USER} /data/caddy && chown -R ${USER}:${USER} /config/caddy USER ${USER} ``` -Затем установите переменную окружения `SERVER_NAME`, чтобы использовать непривилегированный порт. +Затем установите переменную окружения `SERVER_NAME`, чтобы использовать непривилегированный порт. Пример: `:8000`. ## Обновления -Docker-образы собираются: +Docker-образы обновляются: - при выпуске новой версии; - ежедневно в 4 утра UTC, если доступны новые версии официальных PHP-образов. ## Версии для разработки -Версии для разработки доступны в Docker-репозитории [`dunglas/frankenphp-dev`](https://hub.docker.com/repository/docker/dunglas/frankenphp-dev). -Новая сборка запускается каждый раз, когда коммит отправляется в основную ветку GitHub-репозитория. +Версии для разработки доступны в Docker-репозитории [`dunglas/frankenphp-dev`](https://hub.docker.com/repository/docker/dunglas/frankenphp-dev). +Сборка запускается автоматически при каждом коммите в основную ветку GitHub-репозитория -Теги с префиксом `latest*` указывают на актуальное состояние ветки `main`. -Также доступны теги в формате `sha-`. \ No newline at end of file +Теги с префиксом `latest*` указывают на актуальное состояние ветки `main`. +Также доступны теги в формате `sha-`. diff --git a/docs/ru/early-hints.md b/docs/ru/early-hints.md index d907939203..e27c3b1945 100644 --- a/docs/ru/early-hints.md +++ b/docs/ru/early-hints.md @@ -1,6 +1,6 @@ # Early Hints -FrankenPHP изначально поддерживает [код состояния 103 Early Hints](https://developer.chrome.com/blog/early-hints/). +FrankenPHP изначально поддерживает [Early Hints (103 HTTP статус код)](https://developer.chrome.com/blog/early-hints/). Использование Early Hints может улучшить время загрузки ваших веб-страниц на 30%. ```php diff --git a/docs/ru/embed.md b/docs/ru/embed.md index 3c9c61793b..2ac91307b5 100644 --- a/docs/ru/embed.md +++ b/docs/ru/embed.md @@ -6,7 +6,7 @@ FrankenPHP позволяет встраивать исходный код и р Подробнее об этой функции [в презентации Кевина на SymfonyCon 2023](https://dunglas.dev/2023/12/php-and-symfony-apps-as-standalone-binaries/). -Для встраивания Laravel-приложений ознакомьтесь с [этой специальной записью в документации](laravel.md#laravel-apps-as-standalone-binaries). +Для встраивания Laravel-приложений ознакомьтесь с [документацией](laravel.md#laravel-приложения-как-автономные-бинарные-файлы). ## Подготовка приложения @@ -19,20 +19,19 @@ FrankenPHP позволяет встраивать исходный код и р - Включить продакшн-режим приложения (если он есть). - Удалить ненужные файлы, такие как `.git` или тесты, чтобы уменьшить размер итогового бинарного файла. -Например, для приложения на Symfony вы можете использовать следующие команды: +Для приложения на Symfony это может выглядеть так: ```console -# Экспорт проекта, чтобы избавиться от .git/ и т.д. +# Экспорт проекта, чтобы избавиться от .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 -# Удалить тесты и другие ненужные файлы, чтобы сэкономить место -# В качестве альтернативы, добавьте эти файлы с атрибутом export-ignore в ваш файл .gitattributes +# Удалить тесты и другие ненужные файлы rm -Rf tests/ # Установить зависимости @@ -44,8 +43,7 @@ composer dump-env prod ### Настройка конфигурации -Чтобы настроить [конфигурацию](config.md), вы можете поместить файл `Caddyfile`, а также файл `php.ini` -в основную директорию приложения для встраивания (`$TMPDIR/my-prepared-app` в предыдущем примере). +Чтобы настроить [конфигурацию](config.md), вы можете разместить файлы `Caddyfile` и `php.ini` в основной директории приложения (`$TMPDIR/my-prepared-app` в примере выше). ## Создание бинарного файла для Linux @@ -55,23 +53,23 @@ composer dump-env prod ```dockerfile FROM --platform=linux/amd64 dunglas/frankenphp:static-builder-gnu - # Если вы планируете запускать бинарный файл на системах с musl-libc, используйте static-builder-musl вместо него + # Если вы планируете запускать бинарный файл на системах с musl-libc, используйте static-builder-musl - # Скопировать ваше приложение + # Скопировать приложение 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` перед сборкой. + > Некоторые `.dockerignore` файлы (например, [Symfony Docker `.dockerignore`](https://github.com/dunglas/symfony-docker/blob/main/.dockerignore)) + > игнорируют директорию `vendor/` и файлы `.env`. Перед сборкой убедитесь, что `.dockerignore` файл настроен корректно или удалён. -2. Соберите: +2. Соберите образ: ```console docker build -t static-app -f static-build.Dockerfile . @@ -83,7 +81,7 @@ composer dump-env prod 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` в текущей директории. +Созданный бинарный файл сохранится в текущей директории под именем `my-app`. ## Создание бинарного файла для других ОС @@ -95,11 +93,11 @@ cd frankenphp EMBED=/path/to/your/app ./build-static.sh ``` -Итоговым бинарным файлом является файл с именем `frankenphp--` в директории `dist/`. +Итоговый бинарный файл будет находиться в директории `dist/` под именем `frankenphp--`. ## Использование бинарного файла -Готово! Файл `my-app` (или `dist/frankenphp--` на других ОС) содержит ваше автономное приложение! +Готово! Файл `my-app` (или `dist/frankenphp--` для других ОС) содержит ваше автономное приложение. Для запуска веб-приложения выполните: @@ -107,19 +105,19 @@ EMBED=/path/to/your/app ./build-static.sh ./my-app php-server ``` -Если ваше приложение содержит [worker-скрипт](worker.md), запустите воркер следующим образом: +Если ваше приложение содержит [worker-скрипт](worker.md), запустите его следующим образом: ```console ./my-app php-server --worker public/index.php ``` -Чтобы включить HTTPS (сертификат Let's Encrypt создается автоматически), HTTP/2 и HTTP/3, укажите доменное имя: +Чтобы включить HTTPS (Let's Encrypt автоматически создаст сертификат), HTTP/2 и HTTP/3, укажите доменное имя: ```console ./my-app php-server --domain localhost ``` -Вы также можете запускать PHP CLI-скрипты, встроенные в бинарный файл: +Вы также можете запускать PHP-скрипты CLI, встроенные в бинарный файл: ```console ./my-app php-cli bin/console @@ -127,10 +125,10 @@ EMBED=/path/to/your/app ./build-static.sh ## PHP-расширения -По умолчанию скрипт собирает расширения, требуемые файлом `composer.json` вашего проекта, если таковой имеется. -Если файла `composer.json` не существует, собираются стандартные расширения, как указано в [записи о статических сборках](static.md). +По умолчанию скрипт собирает расширения, указанные в `composer.json` вашего проекта. +Если файла `composer.json` нет, собираются стандартные расширения, как указано в [документации по статической сборке](static.md). -Чтобы настроить расширения, используйте переменную окружения `PHP_EXTENSIONS`. +Чтобы настроить список расширений, используйте переменную окружения `PHP_EXTENSIONS`. ## Настройка сборки @@ -140,5 +138,4 @@ EMBED=/path/to/your/app ./build-static.sh На Linux созданный бинарный файл сжимается с помощью [UPX](https://upx.github.io). -На Mac для уменьшения размера файла перед отправкой его можно сжать. -Мы рекомендуем `xz`. +На Mac для уменьшения размера файла перед отправкой его можно сжать. Рекомендуется использовать `xz`. diff --git a/docs/ru/extensions.md b/docs/ru/extensions.md deleted file mode 100644 index 1985c1ded4..0000000000 --- a/docs/ru/extensions.md +++ /dev/null @@ -1,893 +0,0 @@ -# Написание расширений PHP на Go - -С FrankenPHP вы можете **писать расширения PHP на Go**, что позволяет создавать **высокопроизводительные нативные функции**, которые можно вызывать непосредственно из PHP. Ваши приложения могут использовать любые существующие или новые Go библиотеки, а также знаменитую модель параллелизма **горутин прямо из вашего PHP-кода**. - -Написание расширений PHP обычно осуществляется на C, но их также можно писать на других языках с небольшими дополнительными усилиями. Расширения PHP позволяют использовать мощь низкоуровневых языков для расширения функциональности PHP, например, путем добавления нативных функций или оптимизации конкретных операций. - -Благодаря модулям Caddy, вы можете писать расширения PHP на Go и очень быстро интегрировать их в FrankenPHP. - -## Два подхода - -FrankenPHP предоставляет два способа создания расширений PHP на Go: - -1. **Использование генератора расширений** – Рекомендуемый подход, который генерирует весь необходимый шаблонный код для большинства случаев использования, позволяя вам сосредоточиться на написании кода на Go. -2. **Ручная реализация** – Полный контроль над структурой расширения для продвинутых случаев использования. - -Мы начнем с подхода с генератором, так как это самый простой способ начать работу, а затем покажем ручную реализацию для тех, кому нужен полный контроль. - -## Использование генератора расширений - -FrankenPHP поставляется с инструментом, который позволяет **создавать расширения PHP**, используя только Go. **Не нужно писать код на C** или напрямую использовать CGO: FrankenPHP также включает **публичный API типов**, который поможет вам писать расширения на Go, не беспокоясь о **согласовании типов между PHP/C и Go**. - -> [!TIP] -> Если вы хотите понять, как расширения могут быть написаны на Go с нуля, вы можете прочитать раздел "Ручная реализация" ниже, демонстрирующий, как написать расширение PHP на Go без использования генератора. - -Имейте в виду, что этот инструмент **не является полноценным генератором расширений**. Он предназначен для помощи в написании простых расширений на Go, но не предоставляет самых продвинутых функций расширений PHP. Если вам нужно написать более **сложное и оптимизированное** расширение, вам может потребоваться написать некоторый код на C или напрямую использовать CGO. - -### Предварительные требования - -Как описано также в разделе "Ручная реализация" ниже, вам необходимо [получить исходники PHP](https://www.php.net/downloads.php) и создать новый модуль Go. - -#### Создайте новый модуль и получите исходники PHP - -Первым шагом к написанию расширения PHP на Go является создание нового модуля Go. Вы можете использовать следующую команду для этого: - -```console -go mod init example.com/example -``` - -Вторым шагом является [получение исходников PHP](https://www.php.net/downloads.php) для следующих шагов. Как только вы их получите, распакуйте их в каталог по вашему выбору, но не внутрь вашего модуля Go: - -```console -tar xf php-* -``` - -### Написание расширения - -Теперь все настроено для написания вашей нативной функции на Go. Создайте новый файл с именем `stringext.go`. Наша первая функция будет принимать строку в качестве аргумента, количество раз для ее повторения, булево значение, указывающее, нужно ли инвертировать строку, и возвращать результирующую строку. Это должно выглядеть так: - -```go -package example - -// #include -import "C" -import ( - "strings" - "unsafe" - - "github.com/dunglas/frankenphp" -) - -//export_php:function repeat_this(string $str, int $count, bool $reverse): string -func repeat_this(s *C.zend_string, count int64, reverse bool) unsafe.Pointer { - str := frankenphp.GoString(unsafe.Pointer(s)) - - result := strings.Repeat(str, int(count)) - if reverse { - runes := []rune(result) - for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 { - runes[i], runes[j] = runes[j], runes[i] - } - result = string(runes) - } - - return frankenphp.PHPString(result, false) -} -``` - -Здесь следует отметить две важные вещи: - -- Директива-комментарий `//export_php:function` определяет сигнатуру функции в PHP. Таким образом генератор знает, как создать PHP-функцию с правильными параметрами и типом возвращаемого значения; -- Функция должна возвращать `unsafe.Pointer`. FrankenPHP предоставляет API, чтобы помочь вам с согласованием типов между C и Go. - -В то время как первый пункт говорит сам за себя, второй может быть сложнее для понимания. Давайте углубимся в согласование типов в следующем разделе. - -### Согласование типов - -В то время как некоторые типы переменных имеют одно и то же представление в памяти между C/PHP и Go, некоторые типы требуют большей логики для прямого использования. Это, возможно, самая сложная часть при написании расширений, поскольку она требует понимания внутренних механизмов движка Zend и того, как переменные хранятся внутри PHP. -Эта таблица суммирует то, что вам нужно знать: - -| Тип PHP | Тип Go | Прямое преобразование | Помощник C в Go | Помощник Go в C | Поддержка методов класса | -| :----------------- | :---------------------------- | :------------------ | :-------------------------------- | :--------------------------------- | :--------------------- | -| `int` | `int64` | ✅ | - | - | ✅ | -| `?int` | `*int64` | ✅ | - | - | ✅ | -| `float` | `float64` | ✅ | - | - | ✅ | -| `?float` | `*float64` | ✅ | - | - | ✅ | -| `bool` | `bool` | ✅ | - | - | ✅ | -| `?bool` | `*bool` | ✅ | - | - | ✅ | -| `string`/`?string` | `*C.zend_string` | ❌ | `frankenphp.GoString()` | `frankenphp.PHPString()` | ✅ | -| `array` | `frankenphp.AssociativeArray` | ❌ | `frankenphp.GoAssociativeArray()` | `frankenphp.PHPAssociativeArray()` | ✅ | -| `array` | `map[string]any` | ❌ | `frankenphp.GoMap()` | `frankenphp.PHPMap()` | ✅ | -| `array` | `[]any` | ❌ | `frankenphp.GoPackedArray()` | `frankenphp.PHPPackedArray()` | ✅ | -| `mixed` | `any` | ❌ | `GoValue()` | `PHPValue()` | ❌ | -| `callable` | `*C.zval` | ❌ | - | frankenphp.CallPHPCallable() | ❌ | -| `object` | `struct` | ❌ | _Еще не реализовано_ | _Еще не реализовано_ | ❌ | - -> [!NOTE] -> -> Эта таблица еще не исчерпывающая и будет дополнена по мере того, как API типов FrankenPHP станет более полным. -> -> Для методов классов, в частности, в настоящее время поддерживаются примитивные типы и массивы. Объекты пока не могут использоваться в качестве параметров методов или возвращаемых типов. - -Если вы обратитесь к фрагменту кода из предыдущего раздела, вы увидите, что для преобразования первого параметра и возвращаемого значения используются вспомогательные функции. Второй и третий параметры нашей функции `repeat_this()` не требуют преобразования, так как представление в памяти базовых типов одинаково как для C, так и для Go. - -#### Работа с массивами - -FrankenPHP обеспечивает нативную поддержку массивов PHP через `frankenphp.AssociativeArray` или прямое преобразование в карту (map) или срез (slice). - -`AssociativeArray` представляет собой [хеш-таблицу](https://en.wikipedia.org/wiki/Hash_table), состоящую из поля `Map: map[string]any` и необязательного поля `Order: []string` (в отличие от "ассоциативных массивов" PHP, Go-карты не упорядочены). - -Если порядок или ассоциация не требуются, также можно напрямую преобразовать в срез `[]any` или неупорядоченную карту `map[string]any`. - -**Создание и манипулирование массивами в Go:** - -```go -package example - -// #include -import "C" -import ( - "unsafe" - - "github.com/dunglas/frankenphp" -) - -// export_php:function process_data_ordered(array $input): array -func process_data_ordered_map(arr *C.zend_array) unsafe.Pointer { - // Преобразование ассоциативного массива PHP в Go с сохранением порядка - associativeArray, err := frankenphp.GoAssociativeArray[any](unsafe.Pointer(arr)) - if err != nil { - // обработка ошибки - } - - // итерация по элементам в порядке - for _, key := range associativeArray.Order { - value, _ = associativeArray.Map[key] - // что-то делаем с ключом и значением - } - - // возвращаем упорядоченный массив - // если 'Order' не пуст, будут учитываться только пары ключ-значение, указанные в 'Order' - return frankenphp.PHPAssociativeArray[string](frankenphp.AssociativeArray[string]{ - Map: map[string]string{ - "key1": "value1", - "key2": "value2", - }, - Order: []string{"key1", "key2"}, - }) -} - -// export_php:function process_data_unordered(array $input): array -func process_data_unordered_map(arr *C.zend_array) unsafe.Pointer { - // Преобразование ассоциативного массива PHP в Go-карту без сохранения порядка - // игнорирование порядка будет более производительным - goMap, err := frankenphp.GoMap[any](unsafe.Pointer(arr)) - if err != nil { - // обработка ошибки - } - - // итерация по элементам без определенного порядка - for key, value := range goMap { - // что-то делаем с ключом и значением - } - - // возвращаем неупорядоченный массив - return frankenphp.PHPMap(map[string]string { - "key1": "value1", - "key2": "value2", - }) -} - -// export_php:function process_data_packed(array $input): array -func process_data_packed(arr *C.zend_array) unsafe.Pointer { - // Преобразование упакованного массива PHP в Go - goSlice, err := frankenphp.GoPackedArray(unsafe.Pointer(arr)) - if err != nil { - // обработка ошибки - } - - // итерация по срезу в порядке - for index, value := range goSlice { - // что-то делаем с индексом и значением - } - - // возвращаем упакованный массив - return frankenphp.PHPPackedArray([]string{"value1", "value2", "value3"}) -} -``` - -**Ключевые особенности преобразования массивов:** - -- **Упорядоченные пары ключ-значение** — Возможность сохранять порядок ассоциативного массива -- **Оптимизировано для различных случаев** — Возможность отказаться от порядка для лучшей производительности или преобразовать напрямую в срез -- **Автоматическое обнаружение списка** — При преобразовании в PHP автоматически определяет, должен ли массив быть упакованным списком или хеш-картой -- **Вложенные массивы** — Массивы могут быть вложенными и автоматически преобразуют все поддерживаемые типы (`int64`, `float64`, `string`, `bool`, `nil`, `AssociativeArray`, `map[string]any`, `[]any`) -- **Объекты не поддерживаются** — В настоящее время в качестве значений могут использоваться только скалярные типы и массивы. Предоставление объекта приведет к значению `null` в массиве PHP. - -##### Доступные методы: Packed и Associative - -- `frankenphp.PHPAssociativeArray(arr frankenphp.AssociativeArray) unsafe.Pointer` — Преобразует в упорядоченный массив PHP с парами ключ-значение -- `frankenphp.PHPMap(arr map[string]any) unsafe.Pointer` — Преобразует карту в неупорядоченный массив PHP с парами ключ-значение -- `frankenphp.PHPPackedArray(slice []any) unsafe.Pointer` — Преобразует срез в упакованный массив PHP только с индексированными значениями -- `frankenphp.GoAssociativeArray(arr unsafe.Pointer, ordered bool) frankenphp.AssociativeArray` — Преобразует массив PHP в упорядоченный `AssociativeArray` Go (карту с порядком) -- `frankenphp.GoMap(arr unsafe.Pointer) map[string]any` — Преобразует массив PHP в неупорядоченную карту Go -- `frankenphp.GoPackedArray(arr unsafe.Pointer) []any` — Преобразует массив PHP в срез Go -- `frankenphp.IsPacked(zval *C.zend_array) bool` — Проверяет, является ли массив PHP упакованным (только индексированным) или ассоциативным (пары ключ-значение) - -### Работа с вызываемыми объектами (Callables) - -FrankenPHP предоставляет способ работы с вызываемыми объектами PHP с помощью вспомогательной функции `frankenphp.CallPHPCallable`. Это позволяет вызывать функции или методы PHP из кода Go. - -Чтобы продемонстрировать это, давайте создадим нашу собственную функцию `array_map()`, которая принимает вызываемый объект (callable) и массив, применяет вызываемый объект к каждому элементу массива и возвращает новый массив с результатами: - -```go -// export_php:function my_array_map(array $data, callable $callback): array -func my_array_map(arr *C.zend_array, callback *C.zval) unsafe.Pointer { - goSlice, err := frankenphp.GoPackedArray[any](unsafe.Pointer(arr)) - if err != nil { - panic(err) - } - - result := make([]any, len(goSlice)) - - for index, value := range goSlice { - result[index] = frankenphp.CallPHPCallable(unsafe.Pointer(callback), []interface{}{value}) - } - - return frankenphp.PHPPackedArray(result) -} -``` - -Обратите внимание, как мы используем `frankenphp.CallPHPCallable()` для вызова PHP-вызываемого объекта, переданного в качестве параметра. Эта функция принимает указатель на вызываемый объект и массив аргументов, а затем возвращает результат выполнения вызываемого объекта. Вы можете использовать привычный синтаксис вызываемых объектов: - -```php -name` не будет работать) -- **Только интерфейс методов** — Все взаимодействия должны происходить через методы, которые вы определяете -- **Лучшая инкапсуляция** — Внутренняя структура данных полностью контролируется кодом Go -- **Типобезопасность** — Нет риска, что PHP-код повредит внутреннее состояние неправильными типами -- **Более чистый API** — Принуждает к разработке правильного публичного интерфейса - -Этот подход обеспечивает лучшую инкапсуляцию и предотвращает случайное повреждение PHP-кодом внутреннего состояния ваших Go-объектов. Все взаимодействия с объектом должны проходить через явно определенные вами методы. - -#### Добавление методов к классам - -Поскольку свойства недоступны напрямую, вы **должны определить методы** для взаимодействия с вашими непрозрачными классами. Используйте директиву `//export_php:method` для определения поведения: - -```go -package example - -// #include -import "C" -import ( - "unsafe" - - "github.com/dunglas/frankenphp" -) - -//export_php:class User -type UserStruct struct { - Name string - Age int -} - -//export_php:method User::getName(): string -func (us *UserStruct) GetUserName() unsafe.Pointer { - return frankenphp.PHPString(us.Name, false) -} - -//export_php:method User::setAge(int $age): void -func (us *UserStruct) SetUserAge(age int64) { - us.Age = int(age) -} - -//export_php:method User::getAge(): int -func (us *UserStruct) GetUserAge() int64 { - return int64(us.Age) -} - -//export_php:method User::setNamePrefix(string $prefix = "User"): void -func (us *UserStruct) SetNamePrefix(prefix *C.zend_string) { - us.Name = frankenphp.GoString(unsafe.Pointer(prefix)) + ": " + us.Name -} -``` - -#### Обнуляемые параметры - -Генератор поддерживает обнуляемые параметры с использованием префикса `?` в сигнатурах PHP. Когда параметр является обнуляемым, он становится указателем в вашей Go-функции, что позволяет вам проверить, было ли значение `null` в PHP: - -```go -package example - -// #include -import "C" -import ( - "unsafe" - - "github.com/dunglas/frankenphp" -) - -//export_php:method User::updateInfo(?string $name, ?int $age, ?bool $active): void -func (us *UserStruct) UpdateInfo(name *C.zend_string, age *int64, active *bool) { - // Проверить, было ли предоставлено имя (не null) - if name != nil { - us.Name = frankenphp.GoString(unsafe.Pointer(name)) - } - - // Проверить, был ли предоставлен возраст (не null) - if age != nil { - us.Age = int(*age) - } - - // Проверить, был ли предоставлен статус активности (не null) - if active != nil { - us.Active = *active - } -} -``` - -**Ключевые моменты об обнуляемых параметрах:** - -- **Обнуляемые примитивные типы** (`?int`, `?float`, `?bool`) становятся указателями (`*int64`, `*float64`, `*bool`) в Go -- **Обнуляемые строки** (`?string`) остаются `*C.zend_string`, но могут быть `nil` -- **Проверяйте на `nil`** перед разыменованием значений указателей -- **PHP `null` становится Go `nil`** — когда PHP передает `null`, ваша Go-функция получает `nil`-указатель - -> [!WARNING] -> -> В настоящее время методы классов имеют следующие ограничения. **Объекты не поддерживаются** в качестве типов параметров или возвращаемых типов. **Массивы полностью поддерживаются** как для параметров, так и для возвращаемых типов. Поддерживаемые типы: `string`, `int`, `float`, `bool`, `array` и `void` (для возвращаемого типа). **Обнуляемые типы параметров полностью поддерживаются** для всех скалярных типов (`?string`, `?int`, `?float`, `?bool`). - -После генерации расширения вы сможете использовать класс и его методы в PHP. Обратите внимание, что вы **не можете получать доступ к свойствам напрямую**: - -```php -setAge(25); -echo $user->getName(); // Вывод: (пусто, значение по умолчанию) -echo $user->getAge(); // Вывод: 25 -$user->setNamePrefix("Employee"); - -// ✅ Это тоже работает - обнуляемые параметры -$user->updateInfo("John", 30, true); // Все параметры предоставлены -$user->updateInfo("Jane", null, false); // Возраст null -$user->updateInfo(null, 25, null); // Имя и активность null - -// ❌ Это НЕ будет работать - прямой доступ к свойствам -// echo $user->name; // Ошибка: Невозможно получить доступ к приватному свойству -// $user->age = 30; // Ошибка: Невозможно получить доступ к приватному свойству -``` - -Этот дизайн гарантирует, что ваш Go-код полностью контролирует то, как состояние объекта доступно и изменяется, обеспечивая лучшую инкапсуляцию и типобезопасность. - -### Объявление констант - -Генератор поддерживает экспорт Go-констант в PHP с использованием двух директив: `//export_php:const` для глобальных констант и `//export_php:classconst` для констант класса. Это позволяет вам обмениваться значениями конфигурации, кодами состояния и другими константами между Go- и PHP-кодом. - -#### Глобальные константы - -Используйте директиву `//export_php:const` для создания глобальных PHP-констант: - -```go -package example - -//export_php:const -const MAX_CONNECTIONS = 100 - -//export_php:const -const API_VERSION = "1.2.3" - -//export_php:const -const STATUS_OK = iota - -//export_php:const -const STATUS_ERROR = iota -``` - -#### Константы класса - -Используйте директиву `//export_php:classconst ClassName` для создания констант, принадлежащих определенному PHP-классу: - -```go -package example - -//export_php:classconst User -const STATUS_ACTIVE = 1 - -//export_php:classconst User -const STATUS_INACTIVE = 0 - -//export_php:classconst User -const ROLE_ADMIN = "admin" - -//export_php:classconst Order -const STATE_PENDING = iota - -//export_php:classconst Order -const STATE_PROCESSING = iota - -//export_php:classconst Order -const STATE_COMPLETED = iota -``` - -Константы класса доступны с использованием области видимости имени класса в PHP: - -```php - -import "C" -import ( - "strings" - "unsafe" - - "github.com/dunglas/frankenphp" -) - -//export_php:const -const STR_REVERSE = iota - -//export_php:const -const STR_NORMAL = iota - -//export_php:classconst StringProcessor -const MODE_LOWERCASE = 1 - -//export_php:classconst StringProcessor -const MODE_UPPERCASE = 2 - -//export_php:function repeat_this(string $str, int $count, int $mode): string -func repeat_this(s *C.zend_string, count int64, mode int) unsafe.Pointer { - str := frankenphp.GoString(unsafe.Pointer(s)) - - result := strings.Repeat(str, int(count)) - if mode == STR_REVERSE { - // инвертировать строку - } - - if mode == STR_NORMAL { - // ничего не делать, просто для демонстрации константы - } - - return frankenphp.PHPString(result, false) -} - -//export_php:class StringProcessor -type StringProcessorStruct struct { - // внутренние поля -} - -//export_php:method StringProcessor::process(string $input, int $mode): string -func (sp *StringProcessorStruct) Process(input *C.zend_string, mode int64) unsafe.Pointer { - str := frankenphp.GoString(unsafe.Pointer(input)) - - switch mode { - case MODE_LOWERCASE: - str = strings.ToLower(str) - case MODE_UPPERCASE: - str = strings.ToUpper(str) - } - - return frankenphp.PHPString(str, false) -} -``` - -### Использование пространств имен - -Генератор поддерживает организацию функций, классов и констант вашего PHP-расширения в пространстве имен с использованием директивы `//export_php:namespace`. Это помогает избежать конфликтов имен и обеспечивает лучшую организацию API вашего расширения. - -#### Объявление пространства имен - -Используйте директиву `//export_php:namespace` в начале вашего Go-файла, чтобы разместить все экспортируемые символы в определенном пространстве имен: - -```go -//export_php:namespace My\Extension -package example - -import ( - "unsafe" - - "github.com/dunglas/frankenphp" -) - -//export_php:function hello(): string -func hello() string { - return "Hello from My\\Extension namespace!" -} - -//export_php:class User -type UserStruct struct { - // внутренние поля -} - -//export_php:method User::getName(): string -func (u *UserStruct) GetName() unsafe.Pointer { - return frankenphp.PHPString("John Doe", false) -} - -//export_php:const -const STATUS_ACTIVE = 1 -``` - -#### Использование расширения с пространством имен в PHP - -Когда объявляется пространство имен, все функции, классы и константы помещаются в это пространство имен в PHP: - -```php -getName(); // "John Doe" - -echo My\Extension\STATUS_ACTIVE; // 1 -``` - -#### Важные замечания - -- Разрешена только **одна** директива пространства имен на файл. Если найдено несколько директив пространства имен, генератор вернет ошибку. -- Пространство имен применяется ко **всем** экспортируемым символам в файле: функциям, классам, методам и константам. -- Имена пространств имен следуют соглашениям PHP, используя обратные слеши (`\`) в качестве разделителей. -- Если пространство имен не объявлено, символы экспортируются в глобальное пространство имен как обычно. - -### Генерация расширения - -Здесь происходит волшебство, и ваше расширение теперь может быть сгенерировано. Вы можете запустить генератор следующей командой: - -```console -GEN_STUB_SCRIPT=php-src/build/gen_stub.php frankenphp extension-init my_extension.go -``` - -> [!NOTE] -> Не забудьте установить переменную окружения `GEN_STUB_SCRIPT` на путь к файлу `gen_stub.php` в исходниках PHP, которые вы скачали ранее. Это тот же скрипт `gen_stub.php`, упомянутый в разделе "Ручная реализация". - -Если все прошло хорошо, должна быть создана новая директория с именем `build`. Эта директория содержит сгенерированные файлы для вашего расширения, включая файл `my_extension.go` с сгенерированными заглушками функций PHP. - -### Интеграция сгенерированного расширения в FrankenPHP - -Наше расширение готово к компиляции и интеграции в FrankenPHP. Для этого обратитесь к [документации по компиляции](compile.md) FrankenPHP, чтобы узнать, как скомпилировать FrankenPHP. Добавьте модуль, используя флаг `--with`, указывающий путь к вашему модулю: - -```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 github.com/my-account/my-module/build -``` - -Обратите внимание, что вы указываете на подкаталог `/build`, который был создан на этапе генерации. Однако это не является обязательным: вы также можете скопировать сгенерированные файлы в каталог вашего модуля и указать на него напрямую. - -### Тестирование сгенерированного расширения - -Вы можете создать PHP-файл для тестирования созданных вами функций и классов. Например, создайте файл `index.php` со следующим содержимым: - -```php -process('Hello World', StringProcessor::MODE_LOWERCASE); // "hello world" -echo $processor->process('Hello World', StringProcessor::MODE_UPPERCASE); // "HELLO WORLD" -``` - -После того как вы интегрировали ваше расширение в FrankenPHP, как показано в предыдущем разделе, вы можете запустить этот тестовый файл, используя `./frankenphp php-server`, и вы должны увидеть работу вашего расширения. - -## Ручная реализация - -Если вы хотите понять, как работают расширения, или вам нужен полный контроль над вашим расширением, вы можете написать их вручную. Этот подход дает вам полный контроль, но требует больше шаблонного кода. - -### Базовая функция - -Мы рассмотрим, как написать простое расширение PHP на Go, которое определяет новую нативную функцию. Эта функция будет вызываться из PHP и будет запускать горутину, которая записывает сообщение в логи Caddy. Эта функция не принимает никаких параметров и ничего не возвращает. - -#### Определение Go-функции - -В вашем модуле вам нужно определить новую нативную функцию, которая будет вызываться из PHP. Для этого создайте файл с нужным вам именем, например, `extension.go`, и добавьте следующий код: - -```go -package example - -// #include "extension.h" -import "C" -import ( - "log/slog" - "unsafe" - - "github.com/dunglas/frankenphp" -) - -func init() { - frankenphp.RegisterExtension(unsafe.Pointer(&C.ext_module_entry)) -} - -//export go_print_something -func go_print_something() { - go func() { - slog.Info("Hello from a goroutine!") // Привет из горутины! - }() -} -``` - -Функция `frankenphp.RegisterExtension()` упрощает процесс регистрации расширения, обрабатывая внутреннюю логику регистрации PHP. Функция `go_print_something` использует директиву `//export`, чтобы указать, что она будет доступна в C-коде, который мы напишем, благодаря CGO. - -В этом примере наша новая функция будет запускать горутину, которая записывает сообщение в логи Caddy. - -#### Определение PHP-функции - -Чтобы позволить PHP вызывать нашу функцию, нам нужно определить соответствующую PHP-функцию. Для этого мы создадим файл-заглушку, например, `extension.stub.php`, который будет содержать следующий код: - -```php - - -extern zend_module_entry ext_module_entry; - -#endif -``` - -Затем создайте файл с именем `extension.c`, который будет выполнять следующие шаги: - -- Включать заголовки PHP; -- Объявлять нашу новую нативную PHP-функцию `go_print()`; -- Объявлять метаданные расширения. - -Начнем с включения необходимых заголовков: - -```c -#include -#include "extension.h" -#include "extension_arginfo.h" - -// Содержит символы, экспортируемые Go -#include "_cgo_export.h" -``` - -Затем мы определяем нашу PHP-функцию как нативную языковую функцию: - -```c -PHP_FUNCTION(go_print) -{ - ZEND_PARSE_PARAMETERS_NONE(); - - go_print_something(); -} - -zend_module_entry ext_module_entry = { - STANDARD_MODULE_HEADER, - "ext_go", - ext_functions, /* Функции */ - NULL, /* MINIT */ - NULL, /* MSHUTDOWN */ - NULL, /* RINIT */ - NULL, /* RSHUTDOWN */ - NULL, /* MINFO */ - "0.1.1", - STANDARD_MODULE_PROPERTIES -}; -``` - -В этом случае наша функция не принимает параметров и ничего не возвращает. Она просто вызывает Go-функцию, которую мы определили ранее, экспортированную с помощью директивы `//export`. - -Наконец, мы определяем метаданные расширения в структуре `zend_module_entry`, такие как его имя, версия и свойства. Эта информация необходима PHP для распознавания и загрузки нашего расширения. Обратите внимание, что `ext_functions` — это массив указателей на определенные нами PHP-функции, и он был автоматически сгенерирован скриптом `gen_stub.php` в файле `extension_arginfo.h`. - -Регистрация расширения автоматически обрабатывается функцией `RegisterExtension()` FrankenPHP, которую мы вызываем в нашем Go-коде. - -### Продвинутое использование - -Теперь, когда мы знаем, как создать базовое расширение PHP на Go, давайте усложним наш пример. Теперь мы создадим PHP-функцию, которая принимает строку в качестве параметра и возвращает ее версию в верхнем регистре. - -#### Определение заглушки PHP-функции - -Чтобы определить новую PHP-функцию, мы изменим наш файл `extension.stub.php`, чтобы включить новую сигнатуру функции: - -```php - [!TIP] -> Не пренебрегайте документацией ваших функций! Вы, вероятно, будете делиться заглушками ваших расширений с другими разработчиками, чтобы документировать, как использовать ваше расширение и какие функции доступны. - -После повторной генерации файла-заглушки с помощью скрипта `gen_stub.php` файл `extension_arginfo.h` должен выглядеть следующим образом: - -```c -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_go_upper, 0, 1, IS_STRING, 0) - ZEND_ARG_TYPE_INFO(0, string, IS_STRING, 0) -ZEND_END_ARG_INFO() - -ZEND_FUNCTION(go_upper); - -static const zend_function_entry ext_functions[] = { - ZEND_FE(go_upper, arginfo_go_upper) - ZEND_FE_END -}; -``` - -Мы видим, что функция `go_upper` определена с параметром типа `string` и возвращаемым типом `string`. - -#### Согласование типов между Go и PHP/C - -Ваша Go-функция не может напрямую принимать PHP-строку в качестве параметра. Вам нужно преобразовать ее в Go-строку. К счастью, FrankenPHP предоставляет вспомогательные функции для обработки преобразования между PHP-строками и Go-строками, аналогично тому, что мы видели в подходе с генератором. - -Заголовочный файл остается простым: - -```c -#ifndef _EXTENSION_H -#define _EXTENSION_H - -#include - -extern zend_module_entry ext_module_entry; - -#endif -``` - -Теперь мы можем написать мост между Go и C в нашем файле `extension.c`. Мы передадим PHP-строку непосредственно нашей Go-функции: - -```c -PHP_FUNCTION(go_upper) -{ - zend_string *str; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_STR(str) - ZEND_PARSE_PARAMETERS_END(); - - zend_string *result = go_upper(str); - RETVAL_STR(result); -} -``` - -Вы можете узнать больше о `ZEND_PARSE_PARAMETERS_START` и разборе параметров на специальной странице [книги PHP Internals Book](https://www.phpinternalsbook.com/php7/extensions_design/php_functions.html#parsing-parameters-zend-parse-parameters). Здесь мы сообщаем PHP, что наша функция принимает один обязательный параметр типа `string` как `zend_string`. Затем мы передаем эту строку напрямую нашей Go-функции и возвращаем результат, используя `RETVAL_STR`. - -Осталось сделать только одно: реализовать функцию `go_upper` в Go. - -#### Реализация Go-функции - -Наша Go-функция будет принимать `*C.zend_string` в качестве параметра, преобразовывать ее в Go-строку с помощью вспомогательной функции FrankenPHP, обрабатывать ее и возвращать результат в виде нового `*C.zend_string`. Вспомогательные функции обрабатывают все сложности управления памятью и преобразования за нас. - -```go -package example - -// #include -import "C" -import ( - "unsafe" - "strings" - - "github.com/dunglas/frankenphp" -) - -//export go_upper -func go_upper(s *C.zend_string) *C.zend_string { - str := frankenphp.GoString(unsafe.Pointer(s)) - - upper := strings.ToUpper(str) - - return (*C.zend_string)(frankenphp.PHPString(upper, false)) -} -``` - -Этот подход намного чище и безопаснее, чем ручное управление памятью. -Вспомогательные функции FrankenPHP автоматически обрабатывают преобразование между форматом `zend_string` PHP и строками Go. -Параметр `false` в `PHPString()` указывает, что мы хотим создать новую непостоянную строку (освобождаемую в конце запроса). - -> [!TIP] -> -> В этом примере мы не выполняем никакой обработки ошибок, но вы всегда должны проверять, что указатели не `nil` и что данные действительны, прежде чем использовать их в ваших Go-функциях. - -### Интеграция расширения в FrankenPHP - -Наше расширение готово к компиляции и интеграции в FrankenPHP. Для этого обратитесь к [документации по компиляции](compile.md) FrankenPHP, чтобы узнать, как скомпилировать FrankenPHP. Добавьте модуль, используя флаг `--with`, указывающий путь к вашему модулю: - -```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 github.com/my-account/my-module -``` - -Вот и все! Ваше расширение теперь интегрировано в FrankenPHP и может быть использовано в вашем PHP-коде. - -### Тестирование вашего расширения - -После интеграции вашего расширения в FrankenPHP вы можете создать файл `index.php` с примерами реализованных вами функций: - -```php - [!WARNING] -> Эта функция предназначена **только для сред разработки**. -> Не включайте `hot_reload` в продакшене, так как отслеживание файловой системы влечет за собой накладные расходы на производительность и открывает внутренние конечные точки. - -```caddyfile -localhost - -mercure { - anonymous -} - -root public/ -php_server { - hot_reload -} -``` - -По умолчанию FrankenPHP будет отслеживать все файлы в текущей рабочей директории, соответствующие этому шаблону glob: `./**/*.{css,env,gif,htm,html,jpg,jpeg,js,mjs,php,png,svg,twig,webp,xml,yaml,yml}` - -Можно явно задать файлы для отслеживания, используя синтаксис glob: - -```caddyfile -localhost - -mercure { - anonymous -} - -root public/ -php_server { - hot_reload src/**/*{.php,.js} config/**/*.yaml -} -``` - -Используйте полную форму для указания темы Mercure, а также каталогов или файлов для отслеживания, предоставляя пути опции `hot_reload`: - -```caddyfile -localhost - -mercure { - anonymous -} - -root public/ -php_server { - hot_reload { - topic hot-reload-topic - watch src/**/*.php - watch assets/**/*.{ts,json} - watch templates/ - watch public/css/ - } -} -``` - -## Клиентская интеграция - -В то время как сервер обнаруживает изменения, браузеру необходимо подписаться на эти события для обновления страницы. -FrankenPHP предоставляет URL-адрес Mercure Hub для подписки на изменения файлов через переменную окружения `$_SERVER['FRANKENPHP_HOT_RELOAD']`. - -Удобная JavaScript-библиотека [frankenphp-hot-reload](https://www.npmjs.com/package/frankenphp-hot-reload) также доступна для обработки логики на стороне клиента. -Чтобы использовать ее, добавьте следующее в ваш основной макет: - -```php - -FrankenPHP Hot Reload - - - - - -``` - -Библиотека автоматически подпишется на Mercure hub, получит текущий URL в фоновом режиме при обнаружении изменения файла и преобразует DOM. -Она доступна как [npm](https://www.npmjs.com/package/frankenphp-hot-reload) пакет и на [GitHub](https://github.com/dunglas/frankenphp-hot-reload). - -В качестве альтернативы вы можете реализовать свою собственную клиентскую логику, подписавшись непосредственно на Mercure hub, используя нативный JavaScript-класс `EventSource`. - -### Режим воркера - -Если вы запускаете свое приложение в [режиме воркера (Worker Mode)](https://frankenphp.dev/docs/worker/), скрипт вашего приложения остается в памяти. -Это означает, что изменения в вашем PHP-коде не будут отражены немедленно, даже если браузер перезагрузится. - -Для наилучшего опыта разработки вы должны комбинировать `hot_reload` с [поддирективой `watch` в директиве `worker`](config.md#watching-for-file-changes). - -- `hot_reload`: обновляет **браузер** при изменении файлов -- `worker.watch`: перезапускает воркер при изменении файлов - -```caddy -localhost - -mercure { - anonymous -} - -root public/ -php_server { - hot_reload - worker { - file /path/to/my_worker.php - watch - } -} -``` - -### Как это работает - -1. **Отслеживание**: FrankenPHP отслеживает файловую систему на предмет модификаций, используя под капотом [библиотеку `e-dant/watcher`](https://github.com/e-dant/watcher) (мы внесли свой вклад в Go-бинденг). -2. **Перезапуск (режим воркера)**: если `watch` включен в конфигурации воркера, PHP-воркер перезапускается для загрузки нового кода. -3. **Отправка**: JSON-полезная нагрузка, содержащая список измененных файлов, отправляется во встроенный [Mercure hub](https://mercure.rocks). -4. **Получение**: Браузер, слушающий через JavaScript-библиотеку, получает событие Mercure. -5. **Обновление**: - - - Если обнаружен **Idiomorph**, он получает обновленное содержимое и преобразует текущий HTML, чтобы он соответствовал новому состоянию, применяя изменения мгновенно без потери состояния. - - В противном случае вызывается `window.location.reload()` для обновления страницы. diff --git a/docs/ru/known-issues.md b/docs/ru/known-issues.md index 2850577e1f..06b8558ecb 100644 --- a/docs/ru/known-issues.md +++ b/docs/ru/known-issues.md @@ -4,36 +4,35 @@ Следующие расширения не совместимы с 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/) | Не является потокобезопасным | - | +| Название | Причина | Альтернативы | +| ----------------------------------------------------------------------------------------------------------- | ---------------------------------- | -------------------------------------------------------------------------------------------------------------------- | +| [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: +Следующие расширения имеют известные ошибки или могут вести себя непредсказуемо при использовании с FrankenPHP: | Название | Проблема | | ------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| [ext-openssl](https://www.php.net/manual/en/book.openssl.php) | При использовании musl libc расширение OpenSSL может аварийно завершаться при высокой нагрузке. Проблема не возникает при использовании более распространённой GNU libc. Эта ошибка [отслеживается PHP](https://github.com/php/php-src/issues/13648). | +| [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` -Функция [get_browser()](https://www.php.net/manual/en/function.get-browser.php) начинает работать медленно через некоторое время. Решение — кэшировать результаты для каждого User Agent, например, с помощью [APCu](https://www.php.net/manual/en/book.apcu.php), так как они статичны. +Функция [get_browser()](https://www.php.net/manual/en/function.get-browser.php) начинает работать медленно через некоторое время. Решение — кэшировать результаты для каждого User-Agent, например, с помощью [APCu](https://www.php.net/manual/en/book.apcu.php), так как они статичны. -## Автономные бинарные файлы и Docker-образы на базе Alpine +## Автономные бинарные файлы и образы на базе Alpine -Полностью автономные бинарные файлы и Docker-образы на базе 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). - -Если вы столкнётесь с проблемами, отдавайте предпочтение GNU-варианту статического бинарного файла и Docker-образам на базе Debian. +Автономные бинарные файлы и образы на базе 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` в качестве хоста, то можно настроить генерацию сертификата для него, установив имя сервера в `127.0.0.1`. +Если вы всё же хотите использовать `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`. +Однако этого может не хватить при использовании 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/): @@ -45,11 +44,11 @@ docker run \ dunglas/frankenphp ``` -Host-драйвер не поддерживается на Mac и Windows. На этих платформах нужно определить IP-адрес контейнера и включить его в имена серверов. +Host-драйвер не поддерживается на Mac и Windows. На этих платформах нужно определить IP-адрес контейнера и включить его в `SERVER_NAME`. -Выполните команду `docker network inspect bridge`, найдите ключ `Containers` и определите последний присвоенный IP-адрес в разделе `IPv4Address`. Увеличьте его на единицу. Если контейнеров нет, первый присвоенный IP-адрес обычно `172.17.0.2`. +Выполните команду `docker network inspect bridge`, найдите ключ `Containers` и определите последний присвоенный IP из `IPv4Address`. Увеличьте его на единицу. Если контейнеров нет, первый IP обычно `172.17.0.2`. -Затем включите это значение в переменную окружения `SERVER_NAME`: +Включите этот IP в переменную окружения `SERVER_NAME`: ```console docker run \ @@ -60,12 +59,11 @@ docker run \ ``` > [!CAUTION] -> > Обязательно замените `172.17.0.3` на IP, который будет присвоен вашему контейнеру. -Теперь вы должны иметь доступ к `https://127.0.0.1` с хост-машины. +Теперь вы должны иметь доступ к `https://127.0.0.1`. -Если это не так, запустите FrankenPHP в режиме отладки, чтобы попытаться выяснить проблему: +Если это не так, запустите FrankenPHP в режиме отладки: ```console docker run \ @@ -78,12 +76,13 @@ docker run \ ## Скрипты Composer с использованием `@php` -[Скрипты Composer](https://getcomposer.org/doc/articles/scripts.md) могут вызывать PHP-бинарный файл для выполнения некоторых задач, например, в [проекте Laravel](laravel.md) для команды `@php artisan package:discover --ansi`. В настоящее время [это не удаётся](https://github.com/php/frankenphp/issues/483#issuecomment-1899890915) по двум причинам: +[Скрипты Composer](https://getcomposer.org/doc/articles/scripts.md) могут вызывать PHP для выполнения задач, например, в [проекте Laravel](laravel.md) для команды `@php artisan package:discover --ansi`. +Это [на данный момент не поддерживается](https://github.com/php/frankenphp/issues/483#issuecomment-1899890915) по двум причинам: - Composer не знает, как вызывать бинарный файл FrankenPHP; -- Composer может добавлять настройки PHP с помощью флага `-d` в команде, что FrankenPHP пока не поддерживает. +- Composer может добавлять настройки PHP через флаг `-d`, который FrankenPHP пока не поддерживает. -В качестве обходного пути мы можем создать shell-скрипт в `/usr/local/bin/php`, который удаляет неподдерживаемые параметры, а затем вызывает FrankenPHP: +Решение — создать shell-скрипт в `/usr/local/bin/php`, который удаляет неподдерживаемые параметры и вызывает FrankenPHP: ```bash #!/usr/bin/env bash @@ -108,9 +107,9 @@ export PHP_BINARY=/usr/local/bin/php composer install ``` -## Устранение неполадок TLS/SSL при использовании статических бинарных файлов +## TLS/SSL: проблемы со статическими бинарными файлами -При использовании статических бинарных файлов могут возникать следующие ошибки, связанные с TLS, например, при отправке электронных писем с использованием STARTTLS: +При использовании статических бинарных файлов могут возникать следующие ошибки TLS, например, при отправке писем через STARTTLS: ```text Unable to connect with STARTTLS: stream_socket_enable_crypto(): SSL operation failed with code 5. OpenSSL Error messages: @@ -120,20 +119,19 @@ error:80000002:system library::No such file or directory error:0A000086:SSL routines::certificate verify failed ``` -Поскольку статический бинарный файл не включает TLS-сертификаты, вам нужно указать OpenSSL путь к вашей локальной установке сертификатов CA. +Статический бинарный файл не включает TLS-сертификаты, поэтому необходимо указать OpenSSL местоположение локальных сертификатов CA. -Проверьте вывод [`openssl_get_cert_locations()`](https://www.php.net/manual/en/function.openssl-get-cert-locations.php), чтобы узнать, где должны быть установлены сертификаты CA, и сохраните их в этом месте. +Выполните [`openssl_get_cert_locations()`](https://www.php.net/manual/en/function.openssl-get-cert-locations.php), чтобы определить, где должны находиться сертификаты CA, и поместите их туда. > [!WARNING] -> -> Веб и CLI контексты могут иметь разные настройки. -> Убедитесь, что вы запускаете `openssl_get_cert_locations()` в соответствующем контексте. +> Веб и CLI контексты могут иметь разные настройки. +> Запустите `openssl_get_cert_locations()` в нужном контексте. [Сертификаты CA, извлечённые из Mozilla, можно скачать с сайта cURL](https://curl.se/docs/caextract.html). -Кроме того, многие дистрибутивы, включая Debian, Ubuntu и Alpine, предоставляют пакеты с именем `ca-certificates`, которые содержат эти сертификаты. +Кроме того, многие дистрибутивы, такие как Debian, Ubuntu и Alpine, предоставляют пакеты `ca-certificates`, содержащие эти сертификаты. -Также можно использовать переменные `SSL_CERT_FILE` и `SSL_CERT_DIR`, чтобы подсказать OpenSSL, где искать сертификаты CA: +Также можно использовать переменные `SSL_CERT_FILE` и `SSL_CERT_DIR`, чтобы указать OpenSSL, где искать сертификаты CA: ```console # Установите переменные окружения для TLS-сертификатов diff --git a/docs/ru/laravel.md b/docs/ru/laravel.md index a8375dda92..2b6caf90fd 100644 --- a/docs/ru/laravel.md +++ b/docs/ru/laravel.md @@ -16,7 +16,7 @@ docker run -p 80:80 -p 443:443 -p 443:443/udp -v $PWD:/app dunglas/frankenphp Вы также можете запустить ваши Laravel-проекты с FrankenPHP на локальной машине: -1. [Скачайте бинарный файл, соответствующий вашей системе](../#standalone-binary) +1. [Скачайте бинарный файл для вашей системы](README.md#автономный-бинарный-файл) 2. Добавьте следующую конфигурацию в файл с именем `Caddyfile` в корневой директории вашего Laravel-проекта: ```caddyfile @@ -30,10 +30,8 @@ docker run -p 80:80 -p 443:443 -p 443:443/udp -v $PWD:/app dunglas/frankenphp root public/ # Включите сжатие (опционально) encode zstd br gzip - # Выполняйте PHP-файлы и обслуживайте статические файлы из директории public/ - php_server { - try_files {path} index.php - } + # Выполняйте PHP-файлы из директории public/ и обслуживайте статические файлы + php_server } ``` @@ -47,13 +45,13 @@ Octane можно установить с помощью менеджера па composer require laravel/octane ``` -После установки Octane вы можете выполнить Artisan-команду `octane:install`, которая установит конфигурационный файл Octane в ваше приложение: +После установки Octane выполните Artisan-команду `octane:install`, которая создаст конфигурационный файл Octane в вашем приложении: ```console php artisan octane:install --server=frankenphp ``` -Сервер Octane можно запустить с помощью Artisan-команды `octane:frankenphp`. +Сервер Octane можно запустить с помощью Artisan-команды `octane:frankenphp`: ```console php artisan octane:frankenphp @@ -64,20 +62,18 @@ php artisan octane:frankenphp - `--host`: IP-адрес, к которому должен привязаться сервер (по умолчанию: `127.0.0.1`) - `--port`: Порт, на котором сервер будет доступен (по умолчанию: `8000`) - `--admin-port`: Порт, на котором будет доступен административный сервер (по умолчанию: `2019`) -- `--workers`: Количество воркеров, которые должны быть доступны для обработки запросов (по умолчанию: `auto`) +- `--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`) +- `--http-redirect`: Включить редирект с HTTP на HTTPS (включается только при передаче --https) - `--watch`: Автоматически перезагружать сервер при изменении приложения -- `--poll`: Использовать опрос файловой системы при наблюдении, чтобы отслеживать файлы по сети -- `--log-level`: Выводить сообщения журнала на указанном уровне или выше, используя нативный логгер Caddy +- `--poll`: Использовать опрос файловой системы для отслеживания изменений в файлах через сеть +- `--log-level`: Установить уровень логирования, используя встроенный логгер Caddy > [!TIP] > Чтобы получить структурированные JSON-логи (полезно при использовании решений для анализа логов), явно укажите опцию `--log-level`. -См. также [как использовать Mercure с Octane](#mercure-support). - Подробнее о [Laravel Octane читайте в официальной документации](https://laravel.com/docs/octane). ## Laravel-приложения как автономные бинарные файлы @@ -90,7 +86,7 @@ php artisan octane:frankenphp ```dockerfile FROM --platform=linux/amd64 dunglas/frankenphp:static-builder-gnu - # Если вы собираетесь запускать бинарный файл на системах с musl-libc, используйте static-builder-musl вместо + # Если вы планируете запускать бинарный файл на системах с musl-libc, используйте static-builder-musl # Скопируйте ваше приложение WORKDIR /go/src/app/dist/app @@ -165,35 +161,9 @@ php artisan octane:frankenphp Установите переменную окружения `LARAVEL_STORAGE_PATH` (например, в вашем `.env` файле) или вызовите метод `Illuminate\Foundation\Application::useStoragePath()`, чтобы использовать директорию за пределами временной директории. -### Mercure Support - -[Mercure](https://mercure.rocks) — отличный способ добавить возможности реального времени в ваши Laravel-приложения. FrankenPHP включает [поддержку Mercure из коробки](mercure.md). - -Если вы не используете [Octane](#laravel-octane), см. [документацию Mercure](mercure.md). - -Если вы используете Octane, вы можете включить поддержку Mercure, добавив следующие строки в ваш файл `config/octane.php`: - -```php -// ... - -return [ - // ... - - 'mercure' => [ - 'anonymous' => true, - 'publisher_jwt' => '!ChangeThisMercureHubJWTSecretKey!', - 'subscriber_jwt' => '!ChangeThisMercureHubJWTSecretKey!', - ], -]; -``` - -Вы можете использовать [все директивы, поддерживаемые Mercure](https://mercure.rocks/docs/hub/config#directives), в этом массиве. - -Для публикации и подписки на обновления мы рекомендуем использовать библиотеку [Laravel Mercure Broadcaster](https://github.com/mvanduijker/laravel-mercure-broadcaster). В качестве альтернативы, см. [документацию Mercure](mercure.md), чтобы сделать это на чистом PHP и JavaScript. - -### Running Octane With Standalone Binaries +### Запуск Octane как автономный бинарный файл -Можно даже упаковать приложения Laravel Octane как автономные бинарные файлы! +Можно даже упаковать приложения Laravel Octane как автономный бинарный файл! Для этого [установите Octane правильно](#laravel-octane) и следуйте шагам, описанным в [предыдущем разделе](#laravel-приложения-как-автономные-бинарные-файлы). @@ -204,5 +174,4 @@ PATH="$PWD:$PATH" frankenphp php-cli artisan octane:frankenphp ``` > [!CAUTION] -> > Для работы команды автономный бинарник **обязательно** должен быть назван `frankenphp`, так как Octane требует наличия программы с именем `frankenphp` в PATH. diff --git a/docs/ru/logging.md b/docs/ru/logging.md deleted file mode 100644 index 8b40390375..0000000000 --- a/docs/ru/logging.md +++ /dev/null @@ -1,71 +0,0 @@ -# Логирование - -FrankenPHP легко интегрируется с [системой логирования Caddy](https://caddyserver.com/docs/logging). -Вы можете логировать сообщения, используя стандартные функции PHP, или воспользоваться специализированной функцией `frankenphp_log()` для расширенных возможностей структурированного логирования. - -## `frankenphp_log()` - -Функция `frankenphp_log()` позволяет отправлять структурированные логи непосредственно из вашего PHP-приложения, -что значительно упрощает их прием в такие платформы, как Datadog, Grafana Loki или Elastic, а также поддерживает OpenTelemetry. - -По своей сути, `frankenphp_log()` является оберткой для [пакета Go `log/slog`](https://pkg.go.dev/log/slog), предоставляя богатые возможности логирования. - -Эти логи включают уровень серьезности и необязательные контекстные данные. - -```php -function frankenphp_log(string $message, int $level = FRANKENPHP_LOG_LEVEL_INFO, array $context = []): void -``` - -### Параметры - -- **`message`**: Строка сообщения лога. -- **`level`**: Уровень серьезности лога. Может быть любым произвольным целым числом. Для распространенных уровней предусмотрены константы: `FRANKENPHP_LOG_LEVEL_DEBUG` (`-4`), `FRANKENPHP_LOG_LEVEL_INFO` (`0`), `FRANKENPHP_LOG_LEVEL_WARN` (`4`) и `FRANKENPHP_LOG_LEVEL_ERROR` (`8`). По умолчанию установлено значение `FRANKENPHP_LOG_LEVEL_INFO`. -- **`context`**: Ассоциативный массив дополнительных данных, которые будут включены в запись лога. - -### Пример - -```php - memory_get_usage(), - 'peak_usage' => memory_get_peak_usage(), - ], -); - -``` - -При просмотре логов (например, через `docker compose logs`) вывод будет выглядеть как структурированный JSON: - -```json -{"level":"info","ts":1704067200,"logger":"frankenphp","msg":"Hello from FrankenPHP!"} -{"level":"warn","ts":1704067200,"logger":"frankenphp","msg":"Memory usage high","current_usage":10485760,"peak_usage":12582912} -``` - -## `error_log()` - -FrankenPHP также позволяет логировать с помощью стандартной функции `error_log()`. Если параметр `$message_type` равен `4` (SAPI), -эти сообщения направляются в логгер Caddy. - -По умолчанию сообщения, отправленные через `error_log()`, обрабатываются как неструктурированный текст. -Они полезны для совместимости с существующими приложениями или библиотеками, которые полагаются на стандартную библиотеку PHP. - -### Пример с error_log() - -```php -error_log("Сбой подключения к базе данных", 4); -``` - -Это появится в логах Caddy, часто с префиксом, указывающим на то, что сообщение пришло из PHP. - -> [!TIP] -> Для лучшей наблюдаемости в production-средах предпочитайте `frankenphp_log()`, -> так как она позволяет фильтровать логи по уровню (Debug, Error и т.д.) -> и запрашивать определенные поля в вашей инфраструктуре логирования. diff --git a/docs/ru/mercure.md b/docs/ru/mercure.md index 88fb5258d2..0186714e7f 100644 --- a/docs/ru/mercure.md +++ b/docs/ru/mercure.md @@ -1,147 +1,12 @@ -# Режим реального времени +# Real-time режим -FrankenPHP поставляется со встроенным хабом [Mercure](https://mercure.rocks)! -Mercure позволяет отправлять события в режиме реального времени на все подключенные устройства: они мгновенно получат JavaScript-событие. +FrankenPHP поставляется с встроенным хабом [Mercure](https://mercure.rocks)! +Mercure позволяет отправлять события в режиме реального времени на все подключённые устройства: они мгновенно получат JavaScript-событие. -Это удобная альтернатива WebSockets, простая в использовании и нативно поддерживаемая всеми современными веб-браузерами! +Не требуются JS-библиотеки или SDK! -![Mercure](mercure-hub.png) +![Mercure](../mercure-hub.png) -## Включение Mercure +Чтобы включить хаб Mercure, обновите `Caddyfile` в соответствии с инструкциями [на сайте Mercure](https://mercure.rocks/docs/hub/config). -Поддержка Mercure по умолчанию отключена. -Вот минимальный пример `Caddyfile`, включающий FrankenPHP и хаб Mercure: - -```caddyfile -# Имя хоста, на который будет отвечать -localhost - -mercure { - # Секретный ключ, используемый для подписи JWT-токенов для издателей - publisher_jwt !ChangeThisMercureHubJWTSecretKey! - # Разрешает анонимных подписчиков (без JWT) - anonymous -} - -root public/ -php_server -``` - -> [!TIP] -> -> [Пример `Caddyfile`](https://github.com/php/frankenphp/blob/main/caddy/frankenphp/Caddyfile), -> предоставляемый [образами Docker](docker.md), уже содержит закомментированную конфигурацию Mercure -> с удобными переменными окружения для настройки. -> -> Раскомментируйте раздел Mercure в `/etc/frankenphp/Caddyfile`, чтобы включить его. - -## Подписка на обновления - -По умолчанию хаб Mercure доступен по пути `/.well-known/mercure` на вашем сервере FrankenPHP. -Для подписки на обновления используйте нативный JavaScript-класс [`EventSource`](https://developer.mozilla.org/docs/Web/API/EventSource): - -```html - - -Пример Mercure - -``` - -## Публикация обновлений - -### Использование `mercure_publish()` - -FrankenPHP предоставляет удобную функцию `mercure_publish()` для публикации обновлений во встроенный хаб Mercure: - -```php - 'value'])); - -// Запись в логи FrankenPHP -error_log("update $updateID published", 4); -``` - -Полная сигнатура функции: - -```php -/** - * @param string|string[] $topics - */ -function mercure_publish(string|array $topics, string $data = '', bool $private = false, ?string $id = null, ?string $type = null, ?int $retry = null): string {} -``` - -### Использование `file_get_contents()` - -Для отправки обновления подключенным подписчикам отправьте аутентифицированный POST-запрос к хабу Mercure с параметрами `topic` и `data`: - -```php - [ - 'method' => 'POST', - 'header' => "Content-type: application/x-www-form-urlencoded\r\nAuthorization: Bearer " . JWT, - 'content' => http_build_query([ - 'topic' => 'my-topic', - 'data' => json_encode(['key' => 'value']), - ]), -]])); - -// Запись в логи FrankenPHP -error_log("update $updateID published", 4); -``` - -Ключ, переданный в качестве параметра опции `mercure.publisher_jwt` в `Caddyfile`, должен использоваться для подписи JWT-токена, применяемого в заголовке `Authorization`. - -JWT должен включать утверждение `mercure` с разрешением `publish` для тем, в которые вы хотите публиковать обновления. См. [документацию Mercure](https://mercure.rocks/spec#publishers) по авторизации. - -Чтобы сгенерировать свои собственные токены, вы можете использовать [эту ссылку jwt.io](https://www.jwt.io/#token=eyJhbGciOiJIUzI1NiJ9.eyJtZXJjdXJlIjp7InB1Ymxpc2giOlsiKiJdfX0.PXwpfIGng6KObfZlcOXvcnWCJOWTFLtswGI5DZuWSK4), но для производственных приложений рекомендуется использовать короткоживущие токены, генерируемые динамически с помощью надежной [библиотеки JWT](https://www.jwt.io/libraries?programming_language=php). - -### Использование Symfony Mercure - -В качестве альтернативы вы можете использовать [компонент Symfony Mercure](https://symfony.com/components/Mercure), автономную PHP-библиотеку. - -Эта библиотека обрабатывает генерацию JWT, публикацию обновлений, а также авторизацию на основе файлов cookie для подписчиков. - -Сначала установите библиотеку с помощью Composer: - -```console -composer require symfony/mercure lcobucci/jwt -``` - -Затем вы можете использовать её так: - -```php -publish(new \Symfony\Component\Mercure\Update('my-topic', json_encode(['key' => 'value']))); - -// Запись в логи FrankenPHP -error_log("update $updateID published", 4); -``` - -Mercure также нативно поддерживается: - -- [Laravel](laravel.md#mercure-support) -- [Symfony](https://symfony.com/doc/current/mercure.html) -- [API Platform](https://api-platform.com/docs/core/mercure/) +Для отправки обновлений Mercure из вашего кода мы рекомендуем использовать [Symfony Mercure Component](https://symfony.com/components/Mercure) (для его использования не требуется полный стек Symfony). diff --git a/docs/ru/metrics.md b/docs/ru/metrics.md index 91348ee875..19f1160ac5 100644 --- a/docs/ru/metrics.md +++ b/docs/ru/metrics.md @@ -2,16 +2,14 @@ При включении [метрик 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, которые в данный момент обрабатывают запрос (работающие воркеры всегда используют поток). -- `frankenphp_queue_depth`: Количество обычных запросов в очереди. -- `frankenphp_total_workers{worker="[worker_name]"}`: Общее количество воркеров. -- `frankenphp_busy_workers{worker="[worker_name]"}`: Количество воркеров, которые в данный момент обрабатывают запрос. -- `frankenphp_worker_request_time{worker="[worker_name]"}`: Время, затраченное всеми воркерами на обработку запросов. -- `frankenphp_worker_request_count{worker="[worker_name]"}`: Количество запросов, обработанных всеми воркерами. -- `frankenphp_ready_workers{worker="[worker_name]"}`: Количество воркеров, которые вызвали `frankenphp_handle_request` хотя бы один раз. -- `frankenphp_worker_crashes{worker="[worker_name]"}`: Количество случаев неожиданного завершения работы воркера. -- `frankenphp_worker_restarts{worker="[worker_name]"}`: Количество случаев, когда воркер был целенаправленно перезапущен. -- `frankenphp_worker_queue_depth{worker="[worker_name]"}`: Количество запросов в очереди. +- `frankenphp_busy_threads`: Количество потоков PHP, которые в данный момент обрабатывают запрос (работающие worker-скрипты всегда используют поток). -Для метрик воркеров плейсхолдер `[worker_name]` заменяется на имя воркера в Caddyfile, иначе будет использоваться абсолютный путь к файлу воркера. +Для метрик worker-скриптов плейсхолдер `[worker]` заменяется на путь к Worker-скрипту, указанному в Caddyfile. diff --git a/docs/ru/performance.md b/docs/ru/performance.md index 67f1d165c1..06e4a52455 100644 --- a/docs/ru/performance.md +++ b/docs/ru/performance.md @@ -1,61 +1,52 @@ # Производительность -По умолчанию FrankenPHP стремится обеспечить хороший компромисс между производительностью и простотой использования. -Однако можно существенно улучшить производительность, используя соответствующую конфигурацию. +По умолчанию FrankenPHP предлагает хороший баланс между производительностью и удобством использования. +Однако, используя подходящую конфигурацию, можно существенно улучшить производительность. -## Количество потоков и воркеров +## Количество потоков и worker-скриптов -По умолчанию FrankenPHP запускает в 2 раза больше потоков и воркеров (в режиме воркера), чем доступное количество процессорных ядер. +По умолчанию FrankenPHP запускает потоков и worker-скриптов (в worker режиме) вдвое больше, чем количество доступных процессорных ядер. -Подходящие значения сильно зависят от того, как написано ваше приложение, что оно делает, и вашего аппаратного обеспечения. -Мы настоятельно рекомендуем изменить эти значения. Для наилучшей стабильности системы рекомендуется, чтобы `num_threads` x `memory_limit` < `available_memory`. +Оптимальные значения зависят от структуры вашего приложения, его функциональности и аппаратного обеспечения. +Мы настоятельно рекомендуем изменить эти значения. -Чтобы найти подходящие значения, лучше всего провести нагрузочные тесты, имитирующие реальный трафик. -[k6](https://k6.io) и [Gatling](https://gatling.io) являются хорошими инструментами для этого. +Чтобы найти подходящие параметры, лучше всего провести нагрузочные тесты, имитирующие реальный трафик. +Хорошими инструментами для этого являются [k6](https://k6.io) и [Gatling](https://gatling.io). -Чтобы настроить количество потоков, используйте опцию `num_threads` директив `php_server` и `php`. -Для изменения количества воркеров используйте опцию `num` в секции `worker` директивы `frankenphp`. +Чтобы настроить количество потоков, используйте опцию `num_threads` в директивах `php_server` и `php`. +Для изменения количества worker-скриптов используйте опцию `num` в секции `worker` директивы `frankenphp`. -### `max_threads` +## Worker режим -Хотя всегда лучше точно знать, каким будет ваш трафик, реальные приложения, как правило, более непредсказуемы. [Конфигурация](config.md#caddyfile-config) `max_threads` позволяет FrankenPHP автоматически запускать дополнительные потоки во время выполнения до указанного предела. -`max_threads` может помочь вам определить, сколько потоков требуется для обработки вашего трафика, и сделать сервер более устойчивым к скачкам задержки. -Если установлено значение `auto`, лимит будет оценен на основе `memory_limit` в вашем `php.ini`. Если это невозможно, -`auto` вместо этого будет по умолчанию равно 2x `num_threads`. Имейте в виду, что `auto` может сильно недооценивать количество необходимых потоков. -`max_threads` аналогичен [pm.max_children](https://www.php.net/manual/en/install.fpm.configuration.php#pm.max-children) в PHP FPM. Основное отличие состоит в том, что FrankenPHP использует потоки вместо -процессов и автоматически делегирует их различным скриптам воркеров и 'классическому режиму' по мере необходимости. +Включение [Worker режима](worker.md) значительно улучшает производительность, +но ваше приложение должно быть адаптировано для совместимости с этим режимом: +необходимо создать worker-скрипт и убедиться, что приложение не имеет утечек памяти. -## Режим воркера +## Избегайте использования musl -Включение [режима воркера](worker.md) значительно улучшает производительность, -но ваше приложение должно быть адаптировано для совместимости с этим режимом: -необходимо создать воркер-скрипт и убедиться, что приложение не имеет утечек памяти. +Статические бинарники, которые мы предоставляем, а также Alpine Linux-вариант официальных Docker-образов используют [библиотеку musl libc](https://musl.libc.org). -## Не используйте musl +Известно, что PHP [значительно медленнее работает](https://gitlab.alpinelinux.org/alpine/aports/-/issues/14381) с этой библиотекой по сравнению с традиционной GNU libc, особенно при компиляции в ZTS режиме (потокобезопасный режим), который требуется для FrankenPHP. -Alpine Linux-вариант официальных Docker-образов и бинарники по умолчанию, которые мы предоставляем, используют [библиотеку musl libc](https://musl.libc.org). +Кроме того, [некоторые ошибки проявляются исключительно при использовании musl](https://github.com/php/php-src/issues?q=sort%3Aupdated-desc+is%3Aissue+is%3Aopen+label%3ABug+musl). -Известно, что PHP [медленнее работает](https://gitlab.alpinelinux.org/alpine/aports/-/issues/14381) при использовании этой альтернативной библиотеки C вместо традиционной библиотеки GNU, -особенно при компиляции в режиме ZTS (потокобезопасный), который требуется для FrankenPHP. Разница может быть существенной в среде с большим количеством потоков. +В производственной среде настоятельно рекомендуется использовать glibc. -Также [некоторые ошибки проявляются только при использовании musl](https://github.com/php/php-src/issues?q=sort%3Aupdated-desc+is%3Aissue+is%3Aopen+label%3ABug+musl). +Это можно сделать, используя Debian Docker-образы (по умолчанию) и [компилируя FrankenPHP из исходников](compile.md). -В производственных средах мы рекомендуем использовать FrankenPHP, слинкованный с glibc и скомпилированный с соответствующим уровнем оптимизации. - -Этого можно достичь, используя Docker-образы на базе Debian, наши пакеты [.deb](https://debs.henderkes.com) или [.rpm](https://rpms.henderkes.com), или [компилируя FrankenPHP из исходников](compile.md). +В качестве альтернативы мы предоставляем статические бинарники, скомпилированные с [аллокатором mimalloc](https://github.com/microsoft/mimalloc), что делает FrankenPHP+musl быстрее (но всё же медленнее, чем FrankenPHP+glibc). ## Настройка среды выполнения Go FrankenPHP написан на языке Go. -В целом, среда выполнения Go не требует какой-либо специальной конфигурации, но в определенных обстоятельствах специфическая конфигурация улучшает производительность. +В большинстве случаев среда выполнения Go не требует особой настройки, но в некоторых ситуациях специфическая конфигурация может улучшить производительность. -Вы, вероятно, захотите установить переменную окружения `GODEBUG` в значение `cgocheck=0` (по умолчанию в Docker-образах FrankenPHP). +Рекомендуется установить переменную окружения `GODEBUG` в значение `cgocheck=0` (по умолчанию в Docker-образах FrankenPHP). -Если вы запускаете FrankenPHP в контейнерах (Docker, Kubernetes, LXC...) и ограничиваете память, доступную для контейнеров, -установите переменную окружения `GOMEMLIMIT` в значение доступного объёма памяти. +Если вы запускаете FrankenPHP в контейнерах (Docker, Kubernetes, LXC и т.д.) и ограничиваете доступную память, установите переменную окружения `GOMEMLIMIT` в значение доступного объёма памяти. -Для более детальной информации, [страница документации Go, посвященная этой теме](https://pkg.go.dev/runtime#hdr-Environment_Variables), обязательна для прочтения, чтобы максимально использовать возможности среды выполнения. +Для более детальной информации ознакомьтесь с [документацией Go по этой теме](https://pkg.go.dev/runtime#hdr-Environment_Variables). ## `file_server` @@ -69,54 +60,16 @@ php_server { } ``` -## `try_files` - -Помимо статических файлов и PHP-файлов, `php_server` также попытается обслуживать индексные файлы вашего приложения -и файлы индексов директорий (`/path/` -> `/path/index.php`). Если вам не нужны индексные файлы директорий, -вы можете отключить их, явно определив `try_files` следующим образом: - -```caddyfile -php_server { - try_files {path} index.php - root /root/to/your/app # явное добавление root здесь позволяет улучшить кэширование -} -``` - -Это может значительно сократить количество ненужных файловых операций. - -Альтернативный подход с 0 ненужных операций файловой системы заключается в использовании директивы `php` и разделении -файлов от PHP по пути. Этот подход хорошо работает, если всё ваше приложение обслуживается одним входным файлом. -Пример [конфигурации](config.md#caddyfile-config), которая обслуживает статические файлы из папки `/assets`, может выглядеть так: - -```caddyfile -route { - @assets { - path /assets/* - } - - # всё, что находится за /assets, обрабатывается файловым сервером - file_server @assets { - root /root/to/your/app - } - - # всё, что не находится в /assets, обрабатывается вашим индексным или воркерным PHP-файлом - rewrite index.php - php { - root /root/to/your/app # явное добавление root здесь позволяет улучшить кэширование - } -} -``` - ## Плейсхолдеры -Вы можете использовать [плейсхолдеры](https://caddyserver.com/docs/conventions#placeholders) в директивах `root` и `env`. -Однако это предотвращает кэширование значений и существенно снижает производительность. +Вы можете использовать [плейсхолдеры](https://caddyserver.com/docs/conventions#placeholders) в директивах `root` и `env`. +Однако это предотвращает кеширование значений и существенно снижает производительность. По возможности избегайте использования плейсхолдеров в этих директивах. ## `resolve_root_symlink` -По умолчанию, если корневая директория документа является символьной ссылкой, она автоматически разрешается FrankenPHP (это необходимо для правильной работы PHP). +По умолчанию, если корневая директория документа является символьной ссылкой, она автоматически разрешается FrankenPHP (это необходимо для корректной работы PHP). Если корневая директория документа не является символьной ссылкой, вы можете отключить эту функцию. ```caddyfile @@ -125,60 +78,24 @@ php_server { } ``` -Это улучшит производительность, если директива `root` содержит [плейсхолдеры](https://caddyserver.com/docs/conventions#placeholders). -В остальных случаях прирост будет незначительным. +Это улучшит производительность, если директива `root` содержит [плейсхолдеры](https://caddyserver.com/docs/conventions#placeholders). +В остальных случаях прирост производительности будет минимальным. ## Логи -Логирование, безусловно, очень полезно, но, по определению, -оно требует операций ввода-вывода и выделения памяти, что значительно снижает производительность. -Убедитесь, что вы [правильно настроили уровень логирования](https://caddyserver.com/docs/caddyfile/options#log) -и логируете только то, что необходимо. +Логирование, безусловно, полезно, но требует операций ввода-вывода и выделения памяти, что значительно снижает производительность. +Убедитесь, что вы [правильно настроили уровень логирования](https://caddyserver.com/docs/caddyfile/options#log) и логируете только необходимое. ## Производительность PHP -FrankenPHP использует официальный интерпретатор PHP. -Все обычные оптимизации производительности, связанные с PHP, применимы к FrankenPHP. +FrankenPHP использует официальный интерпретатор PHP. +Все стандартные оптимизации производительности PHP применимы к FrankenPHP. В частности: - убедитесь, что [OPcache](https://www.php.net/manual/en/book.opcache.php) установлен, включён и настроен должным образом; - включите [оптимизацию автозагрузки Composer](https://getcomposer.org/doc/articles/autoloader-optimization.md); -- убедитесь, что кэш `realpath` достаточно велик для нужд вашего приложения; +- убедитесь, что кеш `realpath` достаточно велик для нужд вашего приложения; - используйте [предварительную загрузку](https://www.php.net/manual/en/opcache.preloading.php). -Для более подробной информации прочтите [посвященную этому запись в документации Symfony](https://symfony.com/doc/current/performance.html) -(большинство советов полезны, даже если вы не используете Symfony). - -## Разделение пула потоков - -Приложения часто взаимодействуют с медленными внешними сервисами, такими как -API, который становится ненадежным при высокой нагрузке или постоянно отвечает более 10 секунд. -В таких случаях может быть полезно разделить пул потоков, чтобы иметь выделенные "медленные" пулы. -Это предотвращает потребление всех ресурсов/потоков сервера медленными конечными точками и -ограничивает параллелизм запросов, направленных к медленной конечной точке, подобно -пулу соединений. - -```caddyfile -{ - frankenphp { - max_threads 100 # максимум 100 потоков, разделяемых всеми воркерами - } -} - -example.com { - php_server { - root /app/public # корень вашего приложения - worker index.php { - match /slow-endpoint/* # все запросы с путём /slow-endpoint/* обрабатываются этим пулом потоков - num 10 # минимум 10 потоков для запросов, соответствующих /slow-endpoint/* - } - worker index.php { - match * # все остальные запросы обрабатываются отдельно - num 20 # минимум 20 потоков для других запросов, даже если медленные конечные точки начнут зависать - } - } -} -``` - -В целом, также рекомендуется обрабатывать очень медленные конечные точки асинхронно, используя соответствующие механизмы, такие как очереди сообщений. +Для более детальной информации ознакомьтесь с [документацией Symfony о производительности](https://symfony.com/doc/current/performance.html) (большинство советов полезны даже если вы не используете Symfony). diff --git a/docs/ru/production.md b/docs/ru/production.md index 41f31155d1..e438b56410 100644 --- a/docs/ru/production.md +++ b/docs/ru/production.md @@ -1,10 +1,10 @@ -# Развертывание в production +# Деплой в продакшен В этом руководстве мы рассмотрим, как развернуть 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/). +Если вы используете API Platform (который также работает с FrankenPHP), ознакомьтесь с [документацией по деплою этого фреймворка](https://api-platform.com/docs/deployment/). ## Подготовка приложения @@ -13,26 +13,23 @@ ```dockerfile FROM dunglas/frankenphp -# Обязательно замените "your-domain-name.example.com" на ваше доменное имя +# Замените "your-domain-name.example.com" на ваш домен ENV SERVER_NAME=your-domain-name.example.com # Если вы хотите отключить HTTPS, используйте вместо этого: #ENV SERVER_NAME=:80 -# Если ваш проект не использует директорию "public" в качестве корневой веб-директории, вы можете указать её здесь: -# ENV SERVER_ROOT=web/ - -# Включите настройки PHP для production +# Включите настройки PHP для продакшн RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini" # Скопируйте файлы PHP вашего проекта в публичную директорию COPY . /app/public -# Если вы используете Symfony или Laravel, вам нужно скопировать весь проект вместо этого: +# Если вы используете Symfony или Laravel, необходимо скопировать весь проект: #COPY . /app ``` -Ознакомьтесь с разделом "[Создание кастомных Docker-образов](docker.md)" для получения дополнительных подробностей и опций, а также чтобы узнать, как настроить конфигурацию, установить PHP-расширения и модули Caddy. +Ознакомьтесь с разделом "[Создание кастомных Docker-образов](docker.md)" для получения дополнительных подробностей и настроек, а также для установки PHP-расширений и модулей Caddy. -Если ваш проект использует Composer, убедитесь, что он включён в Docker-образ, и установите свои зависимости. +Если ваш проект использует Composer, убедитесь, что он включён в Docker-образ, и установите все зависимости. Затем добавьте файл `compose.yaml`: @@ -49,7 +46,7 @@ services: - caddy_data:/data - caddy_config:/config -# Тома, необходимые для сертификатов и конфигурации Caddy +# Томы, необходимые для сертификатов и конфигурации Caddy volumes: caddy_data: caddy_config: @@ -57,31 +54,26 @@ volumes: > [!NOTE] > -> Примеры выше предназначены для использования в production. -> В процессе разработки вы можете захотеть использовать том, другую конфигурацию PHP и другое значение для переменной окружения `SERVER_NAME`. +> Примеры выше предназначены для использования в продакшне. +> В процессе разработки вы можете использовать том для монтирования, другую конфигурацию PHP и другое значение для переменной окружения `SERVER_NAME`. > -> Ознакомьтесь с проектом [Symfony Docker](https://github.com/dunglas/symfony-docker) (который использует FrankenPHP) для более сложного примера с использованием многостадийных образов, Composer, дополнительных PHP-расширений и т.д. +> Посмотрите проект [Symfony Docker](https://github.com/dunglas/symfony-docker) (который использует FrankenPHP) для более сложного примера с использованием мультистейдж-образов, Composer, дополнительных PHP-расширений и т.д. -Наконец, если вы используете Git, зафиксируйте эти файлы и отправьте их. +Наконец, если вы используете Git, закоммитьте эти файлы и отправьте их в репозиторий. ## Подготовка сервера -Для развертывания вашего приложения в production вам потребуется сервер. В этом руководстве мы будем использовать виртуальную машину, предоставляемую DigitalOcean, но подойдёт любой Linux-сервер. +Для деплоя приложения в продакшн требуется сервер. В этом руководстве мы будем использовать виртуальную машину, предоставляемую DigitalOcean, но подойдёт любой Linux-сервер. Если у вас уже есть Linux-сервер с установленным Docker, вы можете сразу перейти к [следующему разделу](#настройка-доменного-имени). -В противном случае, используйте [эту партнерскую ссылку](https://m.do.co/c/5d8aabe3ab80), чтобы получить $200 бесплатного кредита, создайте аккаунт, затем нажмите "Create a Droplet". -Затем перейдите во вкладку "Marketplace" в разделе "Choose an image" и найдите приложение "Docker". Это создаст сервер на Ubuntu с уже установленными последними версиями Docker и Docker Compose! - -Для целей тестирования будет достаточно самых дешевых планов. -Для реального использования в production вы, вероятно, захотите выбрать план из раздела "general purpose" в соответствии с вашими потребностями. +В противном случае, используйте [эту ссылку](https://m.do.co/c/5d8aabe3ab80), чтобы получить $200 на баланс, создайте аккаунт, затем нажмите "Create a Droplet". +Перейдите во вкладку "Marketplace" в разделе "Choose an image" и найдите приложение "Docker". Это создаст сервер на Ubuntu с установленными Docker и Docker Compose. -![Развертывание FrankenPHP на DigitalOcean с Docker](digitalocean-droplet.png) +Для тестов подойдут самые дешёвые тарифы. Для реального продакшна выберите тариф из раздела "general purpose" в зависимости от ваших потребностей. -Вы можете оставить настройки по умолчанию для других параметров или изменить их в соответствии с вашими потребностями. -Не забудьте добавить свой SSH-ключ или создать пароль, затем нажмите кнопку "Finalize and create". +![Деплой FrankenPHP на DigitalOcean с Docker](../digitalocean-droplet.png) -Затем подождите несколько секунд, пока ваш Droplet будет подготавливаться. -Когда ваш Droplet будет готов, подключитесь через SSH: +После этого подключитесь к серверу через SSH: ```console ssh root@ @@ -89,28 +81,25 @@ ssh root@ ## Настройка доменного имени -В большинстве случаев вам потребуется связать доменное имя с вашим сайтом. -Если у вас еще нет доменного имени, вам придется приобрести его через регистратора. - -Затем создайте DNS-запись типа `A` для вашего доменного имени, указывающую на IP-адрес вашего сервера: +В большинстве случаев вам потребуется связать доменное имя с вашим сайтом. +Создайте запись DNS типа `A`, указывающую на IP вашего сервера: ```dns your-domain-name.example.com. IN A 207.154.233.113 ``` -Пример с использованием сервиса DigitalOcean Domains ("Networking" > "Domains"): +Пример настройки через DigitalOcean ("Networking" > "Domains"): -![Настройка DNS в DigitalOcean](digitalocean-dns.png) +![Настройка DNS в DigitalOcean](../digitalocean-dns.png) > [!NOTE] > -> Let's Encrypt, сервис, используемый FrankenPHP по умолчанию для автоматической генерации TLS-сертификата, не поддерживает использование чистых IP-адресов. Для использования Let's Encrypt обязательно наличие доменного имени. +> 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). -Ключи развёртывания также [поддерживаются GitLab](https://docs.gitlab.com/ee/user/project/deploy_keys/). +Скопируйте ваш проект на сервер с помощью `git clone`, `scp` или любого другого инструмента. +Если вы используете GitHub, настройте [ключи развёртывания](https://docs.github.com/en/free-pro-team@latest/developers/overview/managing-deploy-keys#deploy-keys). Пример с использованием Git: @@ -118,20 +107,19 @@ your-domain-name.example.com. IN A 207.154.233.113 git clone git@github.com:/.git ``` -Перейдите в директорию, содержащую ваш проект (``), и запустите приложение в режиме production: +Перейдите в директорию проекта и запустите приложение в режиме продакшн: ```console -docker compose up --wait +docker compose up -d --wait ``` -Ваш сервер запущен и работает, и HTTPS-сертификат был автоматически сгенерирован для вас. -Перейдите на `https://your-domain-name.example.com` и наслаждайтесь! +Сервер готов, а HTTPS-сертификат был автоматически сгенерирован. Перейдите на `https://your-domain-name.example.com` и наслаждайтесь! > [!CAUTION] > -> Docker может иметь слой кэша, убедитесь, что у вас правильная сборка для каждого развертывания, или пересоберите ваш проект с опцией `--no-cache` во избежание проблем с кэшем. +> 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. +Если вам нужно развернуть приложение на кластер машин, используйте [Docker Swarm](https://docs.docker.com/engine/swarm/stack-deploy/), который совместим с предоставленными файлами Compose. +Для деплоя на Kubernetes ознакомьтесь с [Helm-чартом API Platform](https://api-platform.com/docs/deployment/kubernetes/), который использует FrankenPHP. diff --git a/docs/ru/static.md b/docs/ru/static.md index 1d37ded0f5..40bc6db66f 100644 --- a/docs/ru/static.md +++ b/docs/ru/static.md @@ -1,106 +1,79 @@ -# Создание статической сборки +# Создание статических бинарных файлов -Вместо использования локальной установки библиотеки PHP, можно создать статическую или в основном статическую сборку FrankenPHP благодаря проекту [static-php-cli](https://github.com/crazywhalecc/static-php-cli) (несмотря на название, этот проект поддерживает все SAPI, а не только CLI). +Вместо использования локальной установки библиотеки PHP, можно создать статическую сборку FrankenPHP благодаря проекту [static-php-cli](https://github.com/crazywhalecc/static-php-cli) (несмотря на название, проект поддерживает все SAPI, а не только CLI). -С помощью этого метода единый, переносимый бинарный файл будет содержать PHP-интерпретатор, веб-сервер Caddy и FrankenPHP! +С помощью этого метода создаётся единый переносимый бинарник, который включает PHP-интерпретатор, веб-сервер Caddy и FrankenPHP! -Полностью статические нативные исполняемые файлы не требуют никаких зависимостей и могут быть запущены даже на образе Docker [`scratch`](https://docs.docker.com/build/building/base-images/#create-a-minimal-base-image-using-scratch). Однако они не могут загружать динамические PHP-расширения (такие как Xdebug) и имеют некоторые ограничения, поскольку используют musl libc. - -В основном статические бинарные файлы требуют только `glibc` и могут загружать динамические расширения. - -По возможности мы рекомендуем использовать в основном статические сборки на базе glibc. - -FrankenPHP также поддерживает [встраивание PHP-приложения в статический бинарный файл](embed.md). +FrankenPHP также поддерживает [встраивание PHP-приложений в статический бинарный файл](embed.md). ## Linux -Мы предоставляем образы Docker для сборки статических бинарных файлов для Linux: - -### Полностью статическая сборка на базе musl - -Для полностью статического бинарного файла, который работает на любом дистрибутиве Linux без зависимостей, но не поддерживает динамическую загрузку расширений: - -```console -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 -``` - -Для лучшей производительности в сценариях с высокой конкуренцией рассмотрите возможность использования аллокатора [mimalloc](https://github.com/microsoft/mimalloc). +Мы предоставляем Docker-образ для сборки статического бинарника для Linux: ```console -docker buildx bake --load --set static-builder-musl.args.MIMALLOC=1 static-builder-musl +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 ``` -### В основном статическая сборка на базе glibc (с поддержкой динамических расширений) +Созданный статический бинарный файл называется `frankenphp` и будет доступен в текущей директории. -Для бинарного файла, который поддерживает динамическую загрузку PHP-расширений, при этом выбранные расширения скомпилированы статически: - -```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 -``` +Чтобы собрать статический бинарный файл без Docker, используйте инструкции для macOS — они подходят и для Linux. -Этот бинарный файл поддерживает все версии glibc 2.17 и выше, но не работает в системах на базе musl (таких как Alpine Linux). - -Результирующий в основном статический (за исключением `glibc`) бинарный файл называется `frankenphp` и доступен в текущей директории. - -Если вы хотите собрать статический бинарный файл без Docker, ознакомьтесь с инструкциями для macOS, которые также работают для Linux. - -### Пользовательские расширения +### Дополнительные расширения По умолчанию компилируются самые популярные PHP-расширения. -Чтобы уменьшить размер бинарного файла и сократить поверхность атаки, вы можете выбрать список расширений для сборки, используя Docker-аргумент `PHP_EXTENSIONS`. +Чтобы уменьшить размер бинарного файла и сократить возможные векторы атак, можно указать список расширений, которые следует включить в сборку, используя Docker-аргумент `PHP_EXTENSIONS`. Например, выполните следующую команду, чтобы собрать только расширение `opcache`: ```console -docker buildx bake --load --set static-builder-musl.args.PHP_EXTENSIONS=opcache,pdo_sqlite static-builder-musl +docker buildx bake --load --set static-builder.args.PHP_EXTENSIONS=opcache,pdo_sqlite static-builder # ... ``` -Чтобы добавить библиотеки, обеспечивающие дополнительную функциональность для включенных вами расширений, вы можете передать Docker-аргумент `PHP_EXTENSION_LIBS`: +Чтобы добавить библиотеки, расширяющие функциональность включённых расширений, используйте Docker-аргумент `PHP_EXTENSION_LIBS`: ```console docker buildx bake \ --load \ - --set static-builder-musl.args.PHP_EXTENSIONS=gd \ - --set static-builder-musl.args.PHP_EXTENSION_LIBS=libjpeg,libwebp \ - static-builder-musl + --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`: +Чтобы добавить дополнительные модули Caddy или передать аргументы в [xcaddy](https://github.com/caddyserver/xcaddy), используйте 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 + --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). +В этом примере добавляются модуль 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`, вы должны явно включить их, если хотите, чтобы они были добавлены. +> Модули cbrotli, Mercure и Vulcain включены по умолчанию, если `XCADDY_ARGS` пуст или не установлен. +> Если вы изменяете значение `XCADDY_ARGS`, добавьте их явно, если хотите включить их в сборку. -См. также, как [настроить сборку](#настройка-сборки) +См. также, как [настроить сборку](#настройка-сборки). ### Токен GitHub Если вы достигли лимита запросов к API GitHub, задайте личный токен доступа GitHub в переменной окружения `GITHUB_TOKEN`: ```console -GITHUB_TOKEN="xxx" docker --load buildx bake static-builder-musl +GITHUB_TOKEN="xxx" docker --load buildx bake static-builder # ... ``` ## macOS -Запустите следующий скрипт, чтобы создать статический бинарный файл для macOS (у вас должен быть установлен [Homebrew](https://brew.sh/)): +Запустите следующий скрипт, чтобы создать статический бинарный файл для macOS (должен быть установлен [Homebrew](https://brew.sh/)): ```console git clone https://github.com/php/frankenphp @@ -108,7 +81,7 @@ cd frankenphp ./build-static.sh ``` -Примечание: этот скрипт также работает на Linux (и, вероятно, на других Unix-системах) и используется внутри предоставленных нами образов Docker. +Примечание: этот скрипт также работает на Linux (и, вероятно, на других Unix-системах) и используется внутри предоставленного Docker-образа для статической сборки. ## Настройка сборки @@ -118,39 +91,10 @@ cd 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 +- `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) для повышения производительности. Мы рекомендуем использовать это только для сборок, нацеленных на musl; для glibc предпочтительнее отключить эту опцию и использовать [`LD_PRELOAD`](https://microsoft.github.io/mimalloc/overrides.html) при запуске вашего бинарного файла. -- `RELEASE`: (только для мейнтейнеров) если задано, результирующий бинарник будет загружен на GitHub - -## Расширения - -С бинарными файлами на базе glibc или macOS вы можете динамически загружать PHP-расширения. Однако эти расширения должны быть скомпилированы с поддержкой ZTS. Поскольку большинство менеджеров пакетов в настоящее время не предлагают ZTS-версии своих расширений, вам придется компилировать их самостоятельно. - -Для этого вы можете собрать и запустить контейнер Docker `static-builder-gnu`, подключиться к нему удаленно и скомпилировать расширения с помощью `./configure --with-php-config=/go/src/app/dist/static-php-cli/buildroot/bin/php-config`. - -Пример шагов для [расширения 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 -``` - -Это создаст `frankenphp` и `xdebug-zts.so` в текущей директории. Если вы переместите `xdebug-zts.so` в каталог ваших расширений, добавите `zend_extension=xdebug-zts.so` в ваш php.ini и запустите FrankenPHP, он загрузит Xdebug. +- `MIMALLOC`: (экспериментально, только для Linux) заменяет musl's mallocng на [mimalloc](https://github.com/microsoft/mimalloc) для повышения производительности +- `RELEASE`: (только для мейнтейнеров) если задано, бинарник будет загружен на GitHub diff --git a/docs/ru/wordpress.md b/docs/ru/wordpress.md deleted file mode 100644 index b3efe955b4..0000000000 --- a/docs/ru/wordpress.md +++ /dev/null @@ -1,59 +0,0 @@ -# WordPress - -Запускайте [WordPress](https://wordpress.org/) с FrankenPHP, чтобы насладиться современным, высокопроизводительным стеком с автоматическим HTTPS, HTTP/3 и сжатием Zstandard. - -## Минимальная установка - -1. [Скачайте WordPress](https://wordpress.org/download/) -2. Извлеките ZIP-архив и откройте терминал в извлеченной директории -3. Запустите: - - ```console - frankenphp php-server - ``` - -4. Перейдите по адресу `http://localhost/wp-admin/` и следуйте инструкциям по установке -5. Наслаждайтесь! - -Для готовой к продакшену установки предпочтительнее использовать `frankenphp run` с таким `Caddyfile`: - -```caddyfile -example.com - -php_server -encode zstd br gzip -log -``` - -## Горячая перезагрузка - -Чтобы использовать функцию [горячей перезагрузки](hot-reload.md) с WordPress, включите [Mercure](mercure.md) и добавьте поддирективу `hot_reload` к директиве `php_server` в вашем `Caddyfile`: - -```caddyfile -localhost - -mercure { - anonymous -} - -php_server { - hot_reload -} -``` - -Затем добавьте код, необходимый для загрузки JavaScript-библиотек, в файл `functions.php` вашей темы WordPress: - -```php -function hot_reload() { - ?> - - - - - - [!TIP] -> Следующий раздел актуален только до Symfony 7.4, где была представлена нативная поддержка режима воркера FrankenPHP. - -Режим воркера FrankenPHP поддерживается компонентом [Symfony Runtime](https://symfony.com/doc/current/components/runtime.html). -Чтобы запустить любое Symfony-приложение в режиме воркера, установите пакет FrankenPHP для [PHP Runtime](https://github.com/php-runtime/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 @@ -62,36 +57,30 @@ docker run \ ## Laravel Octane -См. [специальную документацию](laravel.md#laravel-octane). +Подробнее см. в [документации](laravel.md#laravel-octane). ## Пользовательские приложения -Следующий пример показывает, как создать собственный скрипт воркера без использования сторонних библиотек: +Следующий пример показывает, как создать собственный worker-скрипт без использования сторонних библиотек: ```php boot(); -// Обработчик вне цикла для лучшей производительности (меньше работы) +// Обработчик запросов за пределами цикла для повышения производительности $handler = static function () use ($myApp) { - try { - // Вызывается при получении запроса, - // суперглобальные переменные, php://input и тому подобное сбрасываются - echo $myApp->handle($_GET, $_POST, $_COOKIE, $_FILES, $_SERVER); - } catch (\Throwable $exception) { - // `set_exception_handler` вызывается только при завершении работы скрипта воркера, - // что может быть не тем, что вы ожидаете, поэтому перехватывайте и обрабатывайте исключения здесь - (new \MyCustomExceptionHandler)->handleException($exception); - } + // Выполняется при обработке запроса. + // Суперглобальные переменные, php://input и другие данные обновляются для каждого запроса. + echo $myApp->handle($_GET, $_POST, $_COOKIE, $_FILES, $_SERVER); }; $maxRequests = (int)($_SERVER['MAX_REQUESTS'] ?? 0); @@ -107,11 +96,11 @@ for ($nbRequests = 0; !$maxRequests || $nbRequests < $maxRequests; ++$nbRequests if (!$keepRunning) break; } -// Очистка +// Завершение $myApp->shutdown(); ``` -Затем запустите приложение и используйте переменную окружения `FRANKENPHP_CONFIG` для настройки воркера: +Запустите приложение, настроив worker-скрипт с помощью переменной окружения `FRANKENPHP_CONFIG`: ```console docker run \ @@ -121,8 +110,10 @@ docker run \ dunglas/frankenphp ``` -По умолчанию запускается 2 воркера на каждый CPU. -Вы можете также настроить количество запускаемых воркеров: +## Настройка количества worker-скриптов + +По умолчанию запускается по 2 worker-скрипта на каждый CPU. +Вы можете задать своё значение: ```console docker run \ @@ -132,57 +123,37 @@ docker run \ dunglas/frankenphp ``` -### Перезапуск воркера после определённого количества запросов - -Поскольку PHP изначально не предназначался для долгоживущих процессов, всё ещё существует множество библиотек и устаревшего кода, которые могут приводить к утечкам памяти. -Обходным решением для использования такого кода в режиме воркера является перезапуск скрипта воркера после обработки определённого количества запросов: +### Перезапуск worker-скрипта после определённого количества запросов -Предыдущий фрагмент кода воркера позволяет настроить максимальное количество обрабатываемых запросов, установив переменную окружения с именем `MAX_REQUESTS`. +PHP изначально не предназначался для долгоживущих процессов, поэтому некоторые библиотеки и устаревший код могут приводить к утечкам памяти. +Для этого можно настроить автоматический перезапуск worker-скрипта после обработки определённого количества запросов. -### Перезапуск воркеров вручную +В предыдущем примере максимальное количество запросов задаётся с помощью переменной окружения `MAX_REQUESTS`. -Хотя можно перезапускать воркеры [при изменении файлов](config.md#watching-for-file-changes), также возможно корректно перезапустить все воркеры через [административный API Caddy](https://caddyserver.com/docs/api). Если административный интерфейс включен в вашем [Caddyfile](config.md#caddyfile-config), вы можете отправить POST-запрос на эндпоинт перезапуска следующим образом: - -```console -curl -X POST http://localhost:2019/frankenphp/workers/restart -``` +### Сбои worker-скрипта -### Сбои воркеров - -Если скрипт воркера завершается с ненулевым кодом выхода, FrankenPHP перезапустит его с использованием стратегии экспоненциальной задержки. -Если скрипт воркера остаётся активным дольше, чем (последняя задержка * 2), он не будет считаться сбоящим, и задержка сбросится. -Однако, если скрипт воркера продолжает завершаться с ненулевым кодом выхода в течение короткого промежутка времени -(например, из-за опечатки в скрипте), FrankenPHP завершит работу с ошибкой: `too many consecutive failures`. - -Количество последовательных сбоев можно настроить в вашем [Caddyfile](config.md#caddyfile-config) с помощью опции `max_consecutive_failures`: - -```caddyfile -frankenphp { - worker { - # ... - max_consecutive_failures 10 - } -} -``` +Если worker-скрипт завершится с ненулевым кодом выхода, FrankenPHP перезапустит его с использованием экспоненциальной задержки. +Если worker-скрипт проработает дольше, чем время последней задержки \* 2, он будет считаться стабильным, и задержка сбросится. +Однако, если worker-скрипт продолжает завершаться с ненулевым кодом выхода в течение короткого промежутка времени (например, из-за опечатки в коде), FrankenPHP завершит работу с ошибкой: `too many consecutive failures`. ## Поведение суперглобальных переменных -[PHP суперглобальные переменные](https://www.php.net/manual/en/language.variables.superglobals.php) (`$_SERVER`, `$_ENV`, `$_GET`...) -ведут себя следующим образом: +[PHP суперглобальные переменные](https://www.php.net/manual/en/language.variables.superglobals.php) (`$_SERVER`, `$_ENV`, `$_GET` и т.д.) ведут себя следующим образом: -- до первого вызова `frankenphp_handle_request()`, суперглобальные переменные содержат значения, связанные с самим скриптом воркера -- во время и после вызова `frankenphp_handle_request()`, суперглобальные переменные содержат значения, сгенерированные на основе обработанного HTTP-запроса, каждый вызов `frankenphp_handle_request()` изменяет значения суперглобальных переменных +- до первого вызова `frankenphp_handle_request()` суперглобальные переменные содержат значения, связанные с самим worker-скриптом +- во время и после вызова `frankenphp_handle_request()` суперглобальные переменные содержат значения, сгенерированные на основе обработанного HTTP-запроса, каждый вызов изменяет значения суперглобальных переменных -Чтобы получить доступ к суперглобальным переменным скрипта воркера внутри колбэка, необходимо скопировать их и импортировать копию в область видимости колбэка: +Чтобы получить доступ к суперглобальным переменным worker-скрипта внутри колбэка, необходимо скопировать их и импортировать копию в область видимости колбэка: ```php > ~/.zshrc ``` @@ -59,13 +45,16 @@ Ardından yapılandırma betiğini çalıştırın: ```console ./configure \ - --enable-embed \ + --enable-embed=static \ --enable-zts \ --disable-zend-signals \ + --disable-opcache-jit \ + --enable-static \ + --enable-shared=no \ --with-iconv=/opt/homebrew/opt/libiconv/ ``` -#### PHP'yi Derleyin +## PHP Derleyin Son olarak, PHP'yi derleyin ve kurun: @@ -74,60 +63,38 @@ make -j"$(getconf _NPROCESSORS_ONLN)" sudo make install ``` -## İsteğe Bağlı Bağımlılıkları Kurun - -Bazı FrankenPHP özellikleri, kurulması gereken isteğe bağlı sistem bağımlılıklarına sahiptir. -Alternatif olarak, bu özellikler Go derleyicisine derleme etiketleri (build tags) geçirilerek devre dışı bırakılabilir. - -| Özellik | Bağımlılık | Devre dışı bırakma derleme etiketi | -| -------------------------------- | ---------------------------------------------------------------------------------------------------------------- | ---------------------------------- | -| Brotli sıkıştırma | [Brotli](https://github.com/google/brotli) | nobrotli | -| Dosya değişikliğinde işçileri yeniden başlatma | [Watcher C](https://github.com/e-dant/watcher/tree/release/watcher-c) | nowatcher | -| [Mercure](mercure.md) | [Mercure Go kütüphanesi](https://pkg.go.dev/github.com/dunglas/mercure) (otomatik olarak kurulur, AGPL lisanslı) | nomercure | - ## Go Uygulamasını Derleyin -Artık nihai ikili dosyayı oluşturabilirsiniz. +Artık Go kütüphanesini kullanabilir ve Caddy yapımızı derleyebilirsiniz: -### Xcaddy Kullanarak +```console +curl -L https://github.com/php/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 +``` -Önerilen yöntem, FrankenPHP'yi derlemek için [xcaddy](https://github.com/caddyserver/xcaddy) kullanmaktır. -`xcaddy` ayrıca [özel Caddy modüllerini](https://caddyserver.com/docs/modules/) ve FrankenPHP uzantılarını kolayca eklemenizi sağlar: +### 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' -tags=nobadger,nomysql,nopgx" \ -CGO_CFLAGS=$(php-config --includes) \ -CGO_LDFLAGS="$(php-config --ldflags) $(php-config --libs)" \ +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 \ - --with github.com/dunglas/caddy-cbrotli - # Buraya ek Caddy modülleri ve FrankenPHP uzantıları ekleyin - # isteğe bağlı olarak, frankenphp kaynaklarınızdan derlemek isterseniz: - # --with github.com/dunglas/frankenphp=$(pwd) \ - # --with github.com/dunglas/frankenphp/caddy=$(pwd)/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, derleme sırasında `PHP Fatal error: Maximum call stack size of 83360 bytes reached during compilation. Try splitting expression` gibi hatalar alabilirsiniz. +> 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 şöyle değiştirin: +> 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 boyutu değerini uygulamanızın ihtiyaçlarına göre değiştirin). - -### Xcaddy Kullanmadan - -Alternatif olarak, FrankenPHP'yi `go` komutunu doğrudan kullanarak `xcaddy` olmadan derlemek mümkündür: - -```console -curl -L https://github.com/php/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 -``` +> (yığın boyutunun değerini uygulamanızın ihtiyaçlarına göre değiştirin). diff --git a/docs/tr/config.md b/docs/tr/config.md index e5a751eade..5d17f6e19a 100644 --- a/docs/tr/config.md +++ b/docs/tr/config.md @@ -1,75 +1,42 @@ -# Konfigürasyon +# Konfigürasyon -FrankenPHP, Caddy'nin yanı sıra [Mercure](mercure.md) ve [Vulcain](https://vulcain.rocks) modülleri [Caddy tarafından desteklenen formatlar](https://caddyserver.com/docs/getting-started#your-first-config) kullanılarak yapılandırılabilir. +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. -En yaygın format, basit, insan tarafından okunabilir bir metin formatı olan `Caddyfile`'dır. -Varsayılan olarak, FrankenPHP mevcut dizinde bir `Caddyfile` arar. -Özel bir yolu `-c` veya `--config` seçeneğiyle belirtebilirsiniz. +Docker imajlarında] (docker.md), `Caddyfile` `/etc/frankenphp/Caddyfile` adresinde bulunur. +Statik ikili, başlatıldığı dizinde `Caddyfile` dosyasını arayacaktır. -Bir PHP uygulamasını sunmak için en az düzeyde bir `Caddyfile` aşağıda gösterilmiştir: +PHP'nin kendisi [bir `php.ini` dosyası kullanılarak yapılandırılabilir](https://www.php.net/manual/tr/configuration.file.php). -```caddyfile -# Yanıt verilecek ana bilgisayar adı -localhost - -# İsteğe bağlı olarak, dosyaların sunulacağı dizin, aksi takdirde mevcut dizine varsayılan olarak ayarlanır -#root public/ -php_server -``` - -Daha fazla özellik sağlayan ve kullanışlı ortam değişkenleri sunan daha gelişmiş bir `Caddyfile`, [FrankenPHP deposunda](https://github.com/php/frankenphp/blob/main/caddy/frankenphp/Caddyfile) ve Docker imajlarıyla birlikte sağlanır. - -PHP'nin kendisi [bir `php.ini` dosyası kullanılarak yapılandırılabilir](https://www.php.net/manual/en/configuration.file.php). - -Kurulum yönteminize bağlı olarak, FrankenPHP ve PHP yorumlayıcısı, yapılandırma dosyalarını aşağıda açıklanan konumlarda arayacaktır. +PHP yorumlayıcısı aşağıdaki konumlarda arama yapacaktır: -## Docker +Docker: -FrankenPHP: - -- `/etc/frankenphp/Caddyfile`: ana yapılandırma dosyası -- `/etc/frankenphp/Caddyfile.d/*.caddyfile`: otomatik olarak yüklenen ek yapılandırma dosyaları - -PHP: - -- `php.ini`: `/usr/local/etc/php/php.ini` (varsayılan olarak bir `php.ini` sağlanmaz) +- 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-/` +- php uzantıları: `/usr/local/lib/php/extensions/no-debug-zts-/` - PHP projesi tarafından sağlanan resmi bir şablonu kopyalamalısınız: ```dockerfile FROM dunglas/frankenphp -# Üretim: -RUN cp $PHP_INI_DIR/php.ini-production $PHP_INI_DIR/php.ini - -# Veya geliştirme: +# Developement: RUN cp $PHP_INI_DIR/php.ini-development $PHP_INI_DIR/php.ini -``` - -## RPM ve Debian Paketleri - -FrankenPHP: - -- `/etc/frankenphp/Caddyfile`: ana yapılandırma dosyası -- `/etc/frankenphp/Caddyfile.d/*.caddyfile`: otomatik olarak yüklenen ek yapılandırma dosyaları - -PHP: -- `php.ini`: `/etc/php-zts/php.ini` (varsayılan olarak üretim ön ayarlarına sahip bir `php.ini` dosyası sağlanır) -- ek yapılandırma dosyaları: `/etc/php-zts/conf.d/*.ini` - -## Statik İkili +# Veya production: +RUN cp $PHP_INI_DIR/php.ini-production $PHP_INI_DIR/php.ini +``` -FrankenPHP: +FrankenPHP kurulumu (.rpm veya .deb): -- Mevcut çalışma dizininde: `Caddyfile` +- 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/` -PHP: +Statik ikili: -- `php.ini`: `frankenphp run` veya `frankenphp php-server` komutunun çalıştırıldığı dizin, ardından `/etc/frankenphp/php.ini` +- 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, bunları ikili dosyanın içine paketleyin +- 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 @@ -87,22 +54,17 @@ localhost { } ``` -FrankenPHP'yi [global seçenek](https://caddyserver.com/docs/caddyfile/concepts#global-options) `frankenphp` kullanarak açıkça yapılandırabilirsiniz: +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 # Başlatılacak PHP iş parçacığı sayısını ayarlar. Varsayılan: Mevcut CPU sayısının 2 katı. - max_threads # Çalışma zamanında başlatılabilecek ek PHP iş parçacığı sayısını sınırlar. Varsayılan: num_threads. 'auto' olarak ayarlanabilir. - max_wait_time # Bir isteğin, boş bir PHP iş parçacığı bekleyebileceği maksimum süreyi ayarlar. Varsayılan: devre dışı. - php_ini # Bir php.ini yönergesini ayarlar. Birden fazla yönerge ayarlamak için birkaç kez kullanılabilir. + 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 # Çalışan komut dosyasının yolunu ayarlar. - num # Başlatılacak PHP iş parçacığı sayısını ayarlar, varsayılan olarak mevcut CPU sayısının 2 katıdır. + 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 # Ek bir ortam değişkenini verilen değere ayarlar. Birden fazla ortam değişkeni için birden fazla kez belirtilebilir. - watch # Dosya değişikliklerini izlemek için yolu ayarlar. Birden fazla yol için birden fazla kez belirtilebilir. - name # İşçinin adını ayarlar, loglarda ve metriklerde kullanılır. Varsayılan: işçi dosyasının mutlak yolu - max_consecutive_failures # İşçinin sağlıksız kabul edilmeden önce izin verilen maksimum ardışık hata sayısını ayarlar, -1 işçinin her zaman yeniden başlayacağı anlamına gelir. Varsayılan: 6. } } } @@ -126,7 +88,7 @@ Aynı sunucuda birden fazla uygulamaya hizmet veriyorsanız birden fazla işçi ```caddyfile app.example.com { - root /path/to/app/public + root /path/to/app/public php_server { root /path/to/app/public # daha iyi önbelleğe almayı sağlar worker index.php @@ -134,7 +96,7 @@ app.example.com { } other.example.com { - root /path/to/other/public + root /path/to/other/public php_server { root /path/to/other/public worker index.php @@ -145,20 +107,19 @@ other.example.com { ``` 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` yönergesi, önce bir PHP dosyası olup olmadığını kontrol etmek yerine tüm girdiyi PHP'ye iletir. Daha fazla bilgiyi [performans sayfasında](performance.md#try_files) okuyun. +ancak tam kontrole ihtiyacınız varsa, daha düşük seviyeli `php` yönergesini kullanabilirsiniz: -`php_server` yönergesini kullanmak bu yapılandırma ile aynıdır: +php_server` yönergesini kullanmak bu yapılandırmay ile aynıdır: ```caddyfile route { - # Dizin istekleri için sondaki eğik çizgiyi ekle + # 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ı dene + # İstenen dosya mevcut değilse, dizin dosyalarını deneyin @indexFiles file { try_files {path} {path}/index.php index.php split_path .php @@ -171,165 +132,44 @@ route { } ``` -`php_server` ve `php` yönergeleri aşağıdaki seçeneklere sahiptir: +php_server`ve`php` yönergeleri aşağıdaki seçeneklere sahiptir: ```caddyfile php_server [] { - root # Sitenin kök klasörünü ayarlar. Varsayılan: `root` yönergesi. + root # Sitenin kök klasörünü ayarlar. Öntanımlı: `root` yönergesi. split_path # 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 # 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 # Worker betiğinin yolunu ayarlar, php_server köküne göre göreceli olabilir - num # Başlatılacak PHP iş parçacığı sayısını ayarlar, varsayılan olarak mevcut CPU sayısının 2 katıdır + 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 # 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 # Dosya değişikliklerini izlemek için yolu ayarlar. Birden fazla yol için birden fazla kez belirtilebilir. env # 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. - match # İşçiyi bir yol desenine eşleştirir. try_files'ı geçersiz kılar ve yalnızca php_server yönergesinde kullanılabilir. } worker # Global frankenphp bloğundaki gibi kısa formu da kullanabilirsiniz. } ``` -### Dosya Değişikliklerini İzleme - -İşçiler uygulamanızı yalnızca bir kez başlattığı ve bellekte tuttuğu için, PHP dosyalarınızdaki herhangi bir değişiklik hemen yansımaz. - -Bunun yerine işçiler, `watch` yönergesi aracılığıyla dosya değişikliklerinde yeniden başlatılabilir. -Bu, geliştirme ortamları için kullanışlıdır. - -```caddyfile -{ - frankenphp { - worker { - file /path/to/app/public/worker.php - watch - } - } -} -``` - -Bu özellik genellikle [hot reload](hot-reload.md) ile birlikte kullanılır. - -`watch` dizini belirtilmezse, FrankenPHP sürecinin başlatıldığı dizindeki ve alt dizinlerindeki tüm `.env`, `.php`, `.twig`, `.yaml` ve `.yml` dosyalarını izleyen `./**/*.{env,php,twig,yaml,yml}` değerine geri döner. Bunun yerine, bir [kabuk dosya adı deseni](https://pkg.go.dev/path/filepath#Match) aracılığıyla bir veya daha fazla dizin de belirtebilirsiniz: - -```caddyfile -{ - frankenphp { - worker { - file /path/to/app/public/worker.php - watch /path/to/app # /path/to/app altındaki tüm dizinlerdeki tüm dosyaları izler - watch /path/to/app/*.php # /path/to/app içindeki .php ile biten dosyaları izler - watch /path/to/app/**/*.php # /path/to/app ve alt dizinlerdeki PHP dosyalarını izler - watch /path/to/app/**/*.{php,twig} # /path/to/app ve alt dizinlerdeki PHP ve Twig dosyalarını izler - } - } -} -``` - -- `**` deseni, özyinelemeli izlemeyi ifade eder -- Dizinler göreceli de olabilir (FrankenPHP sürecinin başlatıldığı yere göre) -- Birden fazla işçi tanımladıysanız, bir dosya değiştiğinde hepsi yeniden başlatılacaktır -- Çalışma zamanında oluşturulan dosyaları (loglar gibi) izlemeye dikkat edin, çünkü bunlar istenmeyen işçi yeniden başlatmalarına neden olabilir. - -Dosya izleyici [e-dant/watcher](https://github.com/e-dant/watcher) üzerine kuruludur. - -## İşçiyi Bir Yola Eşleştirme - -Geleneksel PHP uygulamalarında, betikler her zaman public dizinine yerleştirilir. -Bu, diğer PHP betikleri gibi ele alınan işçi betikleri için de geçerlidir. -İşçi betiğini public dizininin dışına yerleştirmek isterseniz, bunu `match` yönergesi aracılığıyla yapabilirsiniz. - -`match` yönergesi, yalnızca `php_server` ve `php` içinde kullanılabilen, `try_files`'a optimize edilmiş bir alternatiftir. -Aşağıdaki örnek, mevcutsa her zaman public dizinindeki bir dosyayı sunacak -ve aksi takdirde isteği yol desenine uyan işçiye iletecektir. - -```caddyfile -{ - frankenphp { - php_server { - worker { - file /path/to/worker.php # dosya public yolunun dışında olabilir - match /api/* # /api/ ile başlayan tüm istekler bu işçi tarafından ele alınacaktır - } - } - } -} -``` - ## 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`: [dinlenecek adresleri](https://caddyserver.com/docs/caddyfile/concepts#addresses) değiştirir, sağlanan ana bilgisayar adları oluşturulan TLS sertifikası için de kullanılacaktır -- `SERVER_ROOT`: sitenin kök dizinini değiştirir, varsayılan olarak `public/` -- `CADDY_GLOBAL_OPTIONS`: [global seçenekleri](https://caddyserver.com/docs/caddyfile/options) entegre eder -- `FRANKENPHP_CONFIG`: `frankenphp` yönergesi altına yapılandırma entegre eder +- `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` PHP yönergesinin](https://www.php.net/manual/en/ini.core.php#ini.variables-order) `S` değeri, `E`'nin bu yönergedeki diğer yerleşiminden bağımsız olarak her zaman `ES` ile eşdeğerdir. +[`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, +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. -PHP yapılandırmasını `Caddyfile` içindeki `php_ini` yönergesini kullanarak da değiştirebilirsiniz: - -```caddyfile -{ - frankenphp { - php_ini memory_limit 256M - - # veya - - php_ini { - memory_limit 256M - max_execution_time 15 - } - } -} -``` - -### HTTPS'i Devre Dışı Bırakma - -Varsayılan olarak, FrankenPHP `localhost` dahil tüm ana bilgisayar adları için otomatik olarak HTTPS'i etkinleştirir. -HTTPS'i devre dışı bırakmak isterseniz (örneğin bir geliştirme ortamında), `SERVER_NAME` ortam değişkenini `http://` veya `:80` olarak ayarlayabilirsiniz: - -Alternatif olarak, [Caddy belgelerinde](https://caddyserver.com/docs/automatic-https#activation) açıklanan diğer tüm yöntemleri kullanabilirsiniz. - -`localhost` ana bilgisayar adı yerine `127.0.0.1` IP adresiyle HTTPS kullanmak isterseniz, lütfen [bilinen sorunlar](known-issues.md#using-https127001-with-docker) bölümünü okuyun. - -### Tam Çift Yönlü (HTTP/1) - -HTTP/1.x kullanırken, yanıtın tamamı okunmadan önce bir yanıt yazılmasına izin vermek için tam çift yönlü modu etkinleştirmek istenebilir. (örneğin: [Mercure](mercure.md), WebSocket, Server-Sent Events vb.) - -Bu, `Caddyfile`'daki global seçeneklere eklenmesi gereken isteğe bağlı bir yapılandırmadır: - -```caddyfile -{ - servers { - enable_full_duplex - } -} -``` - -> [!UYARI] -> -> Bu seçeneği etkinleştirmek, tam çift yönlü desteği olmayan eski HTTP/1.x istemcilerinin kilitlenmesine neden olabilir. -> Bu ayrıca `CADDY_GLOBAL_OPTIONS` ortam yapılandırması kullanılarak da yapılandırılabilir: - -```sh -CADDY_GLOBAL_OPTIONS="servers { - enable_full_duplex -}" -``` - -Bu ayar hakkında daha fazla bilgiyi [Caddy belgelerinde](https://caddyserver.com/docs/caddyfile/options#enable-full-duplex) bulabilirsiniz. - ## 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: @@ -339,4 +179,4 @@ docker run -v $PWD:/app/public \ -e CADDY_GLOBAL_OPTIONS=debug \ -p 80:80 -p 443:443 -p 443:443/udp \ dunglas/frankenphp -``` \ No newline at end of file +``` diff --git a/docs/tr/docker.md b/docs/tr/docker.md index 31f07719e3..e1c3d6c2d4 100644 --- a/docs/tr/docker.md +++ b/docs/tr/docker.md @@ -1,17 +1,8 @@ # Özel Docker İmajı Oluşturma -[FrankenPHP Docker imajları](https://hub.docker.com/r/dunglas/frankenphp), [resmi PHP imajları](https://hub.docker.com/_/php/) temel alınarak hazırlanmıştır. -Popüler mimariler için Debian ve Alpine Linux varyantları sağlanmıştır. -Debian varyantları tavsiye edilir. +[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, 8.4 ve 8.5 için varyantlar sağlanmıştır. - -Etiketler şu deseni takip eder: `dunglas/frankenphp:-php-` - -- `` ve `` sırasıyla FrankenPHP ve PHP'nin, ana (örn. `1`), ikincil (örn. `1.2`) ila yama sürümlerine (örn. `1.2.3`) kadar değişen sürüm numaralarıdır. -- `` ise `trixie` (Debian Trixie için), `bookworm` (Debian Bookworm için) veya `alpine` (Alpine'ın en son kararlı sürümü için) olabilir. - -[Etiketlere göz atın](https://hub.docker.com/r/dunglas/frankenphp/tags). +PHP 8.2, 8.3, 8.4 ve 8.5 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 @@ -30,19 +21,15 @@ docker build -t my-php-app . docker run -it --rm --name my-running-app my-php-app ``` -## Yapılandırma Nasıl Ayarlanır - -Kolaylık sağlaması açısından, faydalı ortam değişkenleri içeren [varsayılan bir `Caddyfile`](https://github.com/php/frankenphp/blob/main/caddy/frankenphp/Caddyfile) imajda sağlanmıştır. - ## 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 basittir: +[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 ek eklentileri ekleyin: +# buraya istenilen eklentileri ekleyin: RUN install-php-extensions \ pdo_mysql \ gd \ @@ -60,10 +47,10 @@ FrankenPHP, Caddy'nin üzerine inşa edilmiştir ve tüm [Caddy modülleri](http ```dockerfile FROM dunglas/frankenphp:builder AS builder -# xcaddy'yi oluşturucu imajına kopyalayın +# xcaddy'yi derleyen imaja kopyalayın COPY --from=caddy:builder /usr/bin/xcaddy /usr/bin/xcaddy -# FrankenPHP'yi derlemek için CGO etkinleştirilmelidir +# 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" \ @@ -74,24 +61,24 @@ RUN CGO_ENABLED=1 \ --with github.com/dunglas/frankenphp=./ \ --with github.com/dunglas/frankenphp/caddy=./caddy/ \ --with github.com/dunglas/caddy-cbrotli \ - # Mercure ve Vulcain resmi derlemeye dahil edilmiştir, ancak bunları kaldırmaktan çekinmeyin + # 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 ikiliyi, özel modüllerinizi içeren ile değiştirin +# 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. -[Builder 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. +[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#using-xcaddy) gerekebilir. +> [varsayılan yığın boyutunu artırmanız](compile.md#xcaddy-kullanımı) gerekebilir. ## Varsayılan Olarak Worker Modunun Etkinleştirilmesi @@ -105,9 +92,9 @@ FROM dunglas/frankenphp ENV FRANKENPHP_CONFIG="worker ./public/index.php" ``` -## Geliştirme Sürecinde Birim (Volume) Kullanma +## 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 birim (volume) olarak bağlayın: +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 @@ -140,7 +127,7 @@ services: # 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 birimler (volumes) +# Caddy sertifikaları ve yapılandırması için gereken yığınlar (volumes) volumes: caddy_data: caddy_config: @@ -160,45 +147,20 @@ 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 yetenek ekleyin + # 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; \ - # /config/caddy ve /data/caddy dizinlerine yazma erişimi verin - chown -R ${USER}:${USER} /config/caddy /data/caddy + # /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} ``` -### Yetenekler Olmadan Çalıştırma - -Root yetkisi olmadan çalıştırıldığında bile, FrankenPHP web sunucusunu ayrıcalıklı bağlantı noktalarına (80 ve 443) bağlamak için `CAP_NET_BIND_SERVICE` yeteneğine ihtiyaç duyar. - -FrankenPHP'yi ayrıcalıklı olmayan bir bağlantı noktasında (1024 ve üzeri) ifşa ederseniz, web sunucusunu root olmayan bir kullanıcı olarak ve herhangi bir yeteneğe ihtiyaç duymadan çalıştırmak mümkündür: - -```dockerfile -FROM dunglas/frankenphp - -ARG USER=appuser - -RUN \ - # Alpine tabanlı dağıtımlar için "adduser -D ${USER}" kullanın - useradd ${USER}; \ - # Varsayılan yeteneği kaldırın - setcap -r /usr/local/bin/frankenphp; \ - # /config/caddy ve /data/caddy dizinlerine yazma erişimi verin - chown -R ${USER}:${USER} /config/caddy /data/caddy - -USER ${USER} -``` - -Ardından, ayrıcalıklı olmayan bir bağlantı noktası kullanmak için `SERVER_NAME` ortam değişkenini ayarlayın. -Örnek: `:8000` - ## 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 +- Her gün UTC ile saat 4'te Resmi PHP imajlarının yeni sürümleri mevcutsa ## Geliştirme Sürümleri @@ -206,4 +168,4 @@ Geliştirme sürümleri [`dunglas/frankenphp-dev`](https://hub.docker.com/reposi 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-`` biçimindeki etiketler de mevcuttur. +`sha-` biçimindeki etiketler de kullanılabilir. diff --git a/docs/tr/early-hints.md b/docs/tr/early-hints.md index 6d4d9e9f8a..c425df4fe5 100644 --- a/docs/tr/early-hints.md +++ b/docs/tr/early-hints.md @@ -1,7 +1,7 @@ # 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 azaltabilir. +Early Hints kullanmak web sayfalarınızın yüklenme süresini %30 oranında artırabilir. ```php .env.local echo APP_DEBUG=0 >> .env.local -# Testleri ve diğer gereksiz dosyaları yer kazanmak için kaldırın -# Alternatif olarak, bu dosyaları .gitattributes dosyanızdaki export-ignore niteliğiyle ekleyin +# Testleri kaldırın rm -Rf tests/ # Bağımlılıkları yükleyin @@ -42,13 +39,9 @@ composer install --ignore-platform-reqs --no-dev -a composer dump-env prod ``` -### Yapılandırmayı Özelleştirme - -[Yapılandırmayı](config.md) özelleştirmek için, gömülecek uygulamanın ana dizinine (önceki örnekte `$TMPDIR/my-prepared-app`) bir `Caddyfile` ve bir `php.ini` dosyası yerleştirebilirsiniz. - -## Linux Çalıştırılabilir Dosyası Oluşturma +## Linux Binary'si Oluşturma -Bir Linux çalıştırılabilir dosyası oluşturmanın en kolay yolu, sağladığımız Docker tabanlı derleyiciyi kullanmaktır. +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: @@ -60,9 +53,10 @@ Bir Linux çalıştırılabilir dosyası oluşturmanın en kolay yolu, sağladı WORKDIR /go/src/app/dist/app COPY . . - # Statik çalıştırılabilir dosyayı oluşturun + # 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/ ./build-static.sh + RUN EMBED=dist/app/ \ + ./build-static.sh ``` > [!CAUTION] @@ -76,27 +70,28 @@ Bir Linux çalıştırılabilir dosyası oluşturmanın en kolay yolu, sağladı docker build -t static-app -f static-build.Dockerfile . ``` -3. Çalıştırılabilir dosyayı çıkarın: +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 çalıştırılabilir dosya, geçerli dizindeki `my-app` adlı dosyadır. +Elde edilen binary dosyası, geçerli dizindeki `my-app` adlı dosyadır. -## Diğer İşletim Sistemleri için Çalıştırılabilir Dosya Oluşturma +## Diğer İşletim Sistemleri için Binary Çıktısı Alma -Docker kullanmak istemiyorsanız veya bir macOS çalıştırılabilir dosyası oluşturmak istiyorsanız, sağladığımız kabuk betiğini kullanın: +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/php/frankenphp cd frankenphp -EMBED=/path/to/your/app ./build-static.sh +EMBED=/path/to/your/app \ + ./build-static.sh ``` -Elde edilen çalıştırılabilir dosya `dist/` dizinindeki `frankenphp--` adlı dosyadır. +Elde edilen binary dosyası `dist/` dizinindeki `frankenphp--` adlı dosyadır. -## Çalıştırılabilir Dosyayı Kullanma +## Binary Dosyasını Kullanma İşte bu kadar! `my-app` dosyası (veya diğer işletim sistemlerinde `dist/frankenphp--`) bağımsız uygulamanızı içerir! @@ -118,23 +113,17 @@ HTTPS (Let's Encrypt sertifikası otomatik olarak oluşturulur), HTTP/2 ve HTTP/ ./my-app php-server --domain localhost ``` -Ayrıca çalıştırılabilir dosyanıza gömülü PHP CLI betiklerini de çalıştırabilirsiniz: +Ayrıca binary dosyanıza gömülü PHP CLI betiklerini de çalıştırabilirsiniz: ```console ./my-app php-cli bin/console ``` -## PHP Uzantıları - -Varsayılan olarak, betik projenizin `composer.json` dosyası tarafından (varsa) gerekli olan uzantıları derleyecektir. Eğer `composer.json` dosyası yoksa, [statik derlemeler girdisinde](static.md) belgelendiği gibi varsayılan uzantılar derlenir. - -Uzantıları özelleştirmek için `PHP_EXTENSIONS` ortam değişkenini kullanın. - ## Yapıyı Özelleştirme -Çalıştırılabilir dosyanın nasıl özelleştirileceğini (uzantılar, PHP sürümü...) görmek için [statik derleme dokümantasyonunu okuyun](static.md). +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). -## Çalıştırılabilir Dosyanın Dağıtılması +## 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. diff --git a/docs/tr/extensions.md b/docs/tr/extensions.md deleted file mode 100644 index 0c461d57cb..0000000000 --- a/docs/tr/extensions.md +++ /dev/null @@ -1,893 +0,0 @@ -# Go ile PHP Eklentileri Yazma - -FrankenPHP ile **Go dilinde PHP eklentileri yazabilir**, böylece doğrudan PHP'den çağrılabilen **yüksek performanslı yerel fonksiyonlar** oluşturabilirsiniz. Uygulamalarınız mevcut veya yeni herhangi bir Go kütüphanesini ve ayrıca PHP kodunuzdan doğrudan **gorutinlerin** meşhur eşzamanlılık modelini kullanabilir. - -PHP eklentileri genellikle C ile yazılır, ancak biraz ek çabayla başka dillerde de yazmak mümkündür. PHP eklentileri, PHP'nin işlevselliğini genişletmek için düşük seviyeli dillerin gücünden yararlanmanıza olanak tanır, örneğin yerel fonksiyonlar ekleyerek veya belirli işlemleri optimize ederek. - -Caddy modülleri sayesinde, Go dilinde PHP eklentileri yazabilir ve bunları FrankenPHP'ye çok hızlı bir şekilde entegre edebilirsiniz. - -## İki Yaklaşım - -FrankenPHP, Go dilinde PHP eklentileri oluşturmanın iki yolunu sunar: - -1. **Eklenti Üreticisini Kullanma** - Çoğu kullanım durumu için gerekli tüm temel yapıyı üreten, Go kodunuzu yazmaya odaklanmanızı sağlayan önerilen yaklaşım -2. **Manuel Uygulama** - Gelişmiş kullanım durumları için eklenti yapısı üzerinde tam kontrol - -Başlamak için en kolay yol olduğu için üretici yaklaşımıyla başlayacak, ardından tam kontrole ihtiyaç duyanlar için manuel uygulamayı göstereceğiz. - -## Eklenti Üreticisini Kullanma - -FrankenPHP, yalnızca Go kullanarak **bir PHP eklentisi oluşturmanıza** olanak tanıyan bir araçla birlikte gelir. **C kodu yazmaya** veya doğrudan CGO kullanmaya gerek yok: FrankenPHP ayrıca **PHP/C ve Go arasındaki tür dengelemesi (type juggling)** konusunda endişelenmenize gerek kalmadan uzantılarınızı Go'da yazmanıza yardımcı olacak bir **genel türler API'si** içerir. - -> [!TIP] -> Eklentilerin Go dilinde baştan sona nasıl yazılabileceğini anlamak istiyorsanız, aşağıda üretici kullanmadan Go dilinde bir PHP eklentisi yazmayı gösteren manuel uygulama bölümünü okuyabilirsiniz. - -Bu aracın **tam teşekküllü bir eklenti üreticisi olmadığını** unutmayın. Amacı, Go'da basit eklentiler yazmanıza yardımcı olmaktır, ancak PHP eklentilerinin en gelişmiş özelliklerini sağlamaz. Daha **karmaşık ve optimize edilmiş** bir eklenti yazmanız gerekiyorsa, doğrudan C kodu yazmanız veya CGO kullanmanız gerekebilir. - -### Önkoşullar - -Aşağıdaki manuel uygulama bölümünde de belirtildiği gibi, [PHP kaynaklarını edinmeniz](https://www.php.net/downloads.php) ve yeni bir Go modülü oluşturmanız gerekir. - -#### Yeni Bir Modül Oluşturun ve PHP Kaynaklarını Edinin - -Go'da bir PHP eklentisi yazmanın ilk adımı yeni bir Go modülü oluşturmaktır. Bunun için şu komutu kullanabilirsiniz: - -```console -go mod init example.com/example -``` - -İkinci adım, sonraki adımlar için [PHP kaynaklarını edinmektir](https://www.php.net/downloads.php). Kaynakları edindikten sonra, Go modülünüzün içine değil, istediğiniz bir dizine açın: - -```console -tar xf php-* -``` - -### Eklentiyi Yazma - -Yerel fonksiyonunuzu Go'da yazmak için her şey hazır. `stringext.go` adında yeni bir dosya oluşturun. İlk fonksiyonumuz argüman olarak bir dize, onu tekrarlama sayısı, dizenin ters çevrilip çevrilmeyeceğini belirten bir boole değeri alacak ve sonuç dizesini döndürecektir. Şöyle görünmelidir: - -```go -package example - -// #include -import "C" -import ( - "strings" - "unsafe" - - "github.com/dunglas/frankenphp" -) - -//export_php:function repeat_this(string $str, int $count, bool $reverse): string -func repeat_this(s *C.zend_string, count int64, reverse bool) unsafe.Pointer { - str := frankenphp.GoString(unsafe.Pointer(s)) - - result := strings.Repeat(str, int(count)) - if reverse { - runes := []rune(result) - for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 { - runes[i], runes[j] = runes[j], runes[i] - } - result = string(runes) - } - - return frankenphp.PHPString(result, false) -} -``` - -Burada dikkat edilmesi gereken iki önemli nokta var: - -- `//export_php:function` yönerge yorumu, PHP'deki fonksiyon imzasını tanımlar. Üretici, PHP fonksiyonunu doğru parametreler ve dönüş tipiyle nasıl üreteceğini bu şekilde bilir; -- Fonksiyon bir `unsafe.Pointer` döndürmelidir. FrankenPHP, C ve Go arasındaki tür dengelemesi konusunda size yardımcı olacak bir API sağlar. - -İlk nokta kendi kendini açıklasa da, ikincisi kavranması daha zor olabilir. Bir sonraki bölümde tür dengelemesine daha derinlemesine bakalım. - -### Tür Dengeleme (Type Juggling) - -Bazı değişken türlerinin C/PHP ve Go arasında aynı bellek temsiline sahip olmasına rağmen, bazı türlerin doğrudan kullanılabilmesi için daha fazla mantık gereklidir. Bu, eklenti yazarken belki de en zor kısımdır çünkü Zend Motoru'nun iç işleyişini ve değişkenlerin PHP'de dahili olarak nasıl saklandığını anlamayı gerektirir. -Bu tablo bilmeniz gerekenleri özetlemektedir: - -| PHP type | Go type | Direct conversion | C to Go helper | Go to C helper | Class Methods Support | -| :----------------- | :---------------------------- | :---------------- | :-------------------------------- | :--------------------------------- | :-------------------- | -| `int` | `int64` | ✅ | - | - | ✅ | -| `?int` | `*int64` | ✅ | - | - | ✅ | -| `float` | `float64` | ✅ | - | - | ✅ | -| `?float` | `*float64` | ✅ | - | - | ✅ | -| `bool` | `bool` | ✅ | - | - | ✅ | -| `?bool` | `*bool` | ✅ | - | - | ✅ | -| `string`/`?string` | `*C.zend_string` | ❌ | `frankenphp.GoString()` | `frankenphp.PHPString()` | ✅ | -| `array` | `frankenphp.AssociativeArray` | ❌ | `frankenphp.GoAssociativeArray()` | `frankenphp.PHPAssociativeArray()` | ✅ | -| `array` | `map[string]any` | ❌ | `frankenphp.GoMap()` | `frankenphp.PHPMap()` | ✅ | -| `array` | `[]any` | ❌ | `frankenphp.GoPackedArray()` | `frankenphp.PHPPackedArray()` | ✅ | -| `mixed` | `any` | ❌ | `GoValue()` | `PHPValue()` | ❌ | -| `callable` | `*C.zval` | ❌ | - | `frankenphp.CallPHPCallable()` | ❌ | -| `object` | `struct` | ❌ | _Henüz uygulanmadı_ | _Henüz uygulanmadı_ | ❌ | - -> [!NOTE] -> -> Bu tablo henüz kapsamlı değildir ve FrankenPHP türler API'si daha da tamamlandıkça güncellenecektir. -> -> Sınıf metotları için özel olarak, şu anda ilkel türler ve diziler desteklenmektedir. Nesneler henüz metot parametresi veya dönüş türü olarak kullanılamaz. - -Önceki bölümdeki kod parçacığına bakarsanız, ilk parametreyi ve dönüş değerini dönüştürmek için yardımcıların kullanıldığını görebilirsiniz. `repeat_this()` fonksiyonumuzun ikinci ve üçüncü parametrelerinin dönüştürülmesine gerek yoktur, çünkü temel türlerin bellek gösterimi hem C hem de Go için aynıdır. - -#### Dizilerle Çalışma - -FrankenPHP, `frankenphp.AssociativeArray` aracılığıyla veya doğrudan bir haritaya (map) veya dilime (slice) dönüştürerek PHP dizileri için yerel destek sağlar. - -`AssociativeArray`, bir `Map: map[string]any` alanı ve isteğe bağlı bir `Order: []string` alanı içeren bir [hash haritasını](https://en.wikipedia.org/wiki/Hash_table) temsil eder (PHP'nin "ilişkisel dizilerinin" aksine, Go haritaları sıralı değildir). - -Sıra veya ilişkilendirme gerekmiyorsa, doğrudan bir dilime `[]any` veya sırasız bir haritaya `map[string]any` dönüştürmek de mümkündür. - -**Go'da dizileri oluşturma ve işleme:** - -```go -package example - -// #include -import "C" -import ( - "unsafe" - - "github.com/dunglas/frankenphp" -) - -// export_php:function process_data_ordered(array $input): array -func process_data_ordered_map(arr *C.zend_array) unsafe.Pointer { - // Convert PHP associative array to Go while keeping the order - associativeArray, err := frankenphp.GoAssociativeArray[any](unsafe.Pointer(arr)) - if err != nil { - // handle error - } - - // loop over the entries in order - for _, key := range associativeArray.Order { - value, _ = associativeArray.Map[key] - // do something with key and value - } - - // return an ordered array - // if 'Order' is not empty, only the key-value pairs in 'Order' will be respected - return frankenphp.PHPAssociativeArray[string](frankenphp.AssociativeArray[string]{ - Map: map[string]string{ - "key1": "value1", - "key2": "value2", - }, - Order: []string{"key1", "key2"}, - }) -} - -// export_php:function process_data_unordered(array $input): array -func process_data_unordered_map(arr *C.zend_array) unsafe.Pointer { - // Convert PHP associative array to a Go map without keeping the order - // ignoring the order will be more performant - goMap, err := frankenphp.GoMap[any](unsafe.Pointer(arr)) - if err != nil { - // handle error - } - - // loop over the entries in no specific order - for key, value := range goMap { - // do something with key and value - } - - // return an unordered array - return frankenphp.PHPMap(map[string]string { - "key1": "value1", - "key2": "value2", - }) -} - -// export_php:function process_data_packed(array $input): array -func process_data_packed(arr *C.zend_array) unsafe.Pointer { - // Convert PHP packed array to Go - goSlice, err := frankenphp.GoPackedArray(unsafe.Pointer(arr)) - if err != nil { - // handle error - } - - // loop over the slice in order - for index, value := range goSlice { - // do something with index and value - } - - // return a packed array - return frankenphp.PHPPackedArray([]string{"value1", "value2", "value3"}) -} -``` - -**Dizi dönüşümünün temel özellikleri:** - -- **Sıralı anahtar-değer çiftleri** - İlişkisel dizinin sırasını koruma seçeneği -- **Birden fazla durum için optimize edildi** - Daha iyi performans için sırayı bırakma veya doğrudan bir dilime dönüştürme seçeneği -- **Otomatik liste tespiti** - PHP'ye dönüştürürken, dizinin sıkıştırılmış bir liste mi yoksa hash haritası mı olması gerektiğini otomatik olarak algılar -- **İç İçe Diziler** - Diziler iç içe olabilir ve tüm desteklenen türleri otomatik olarak dönüştürür (`int64`,`float64`,`string`,`bool`,`nil`,`AssociativeArray`,`map[string]any`,`[]any`) -- **Nesneler desteklenmiyor** - Şu anda yalnızca skaler türler ve diziler değer olarak kullanılabilir. Bir nesne sağlamak, PHP dizisinde `null` bir değerle sonuçlanacaktır. - -##### Mevcut metodlar: Packed ve İlişkisel - -- `frankenphp.PHPAssociativeArray(arr frankenphp.AssociativeArray) unsafe.Pointer` - Anahtar-değer çiftleriyle sıralı bir PHP dizisine dönüştürür -- `frankenphp.PHPMap(arr map[string]any) unsafe.Pointer` - Bir haritayı anahtar-değer çiftleriyle sırasız bir PHP dizisine dönüştürür -- `frankenphp.PHPPackedArray(slice []any) unsafe.Pointer` - Bir dilimi yalnızca indekslenmiş değerlere sahip bir PHP packed dizisine dönüştürür -- `frankenphp.GoAssociativeArray(arr unsafe.Pointer, ordered bool) frankenphp.AssociativeArray` - Bir PHP dizisini sıralı bir Go `AssociativeArray`'e (sıralı harita) dönüştürür -- `frankenphp.GoMap(arr unsafe.Pointer) map[string]any` - Bir PHP dizisini sırasız bir Go haritasına dönüştürür -- `frankenphp.GoPackedArray(arr unsafe.Pointer) []any` - Bir PHP dizisini bir Go dilimine dönüştürür -- `frankenphp.IsPacked(zval *C.zend_array) bool` - Bir PHP dizisinin packed (yalnızca indekslenmiş) mi yoksa ilişkisel (anahtar-değer çiftleri) mi olduğunu kontrol eder - -### Çağrılabilirler (Callables) ile Çalışma - -FrankenPHP, `frankenphp.CallPHPCallable` yardımcısı kullanarak PHP çağrılabilirleriyle çalışma olanağı sağlar. Bu, Go kodundan PHP fonksiyonlarını veya metotlarını çağırmanıza olanak tanır. - -Bunu göstermek için, çağrılabilir ve bir dizi alan, çağrılabilir'i dizinin her elemanına uygulayan ve sonuçlarla yeni bir dizi döndüren kendi `array_map()` fonksiyonumuzu oluşturalım: - -```go -// export_php:function my_array_map(array $data, callable $callback): array -func my_array_map(arr *C.zend_array, callback *C.zval) unsafe.Pointer { - goSlice, err := frankenphp.GoPackedArray[any](unsafe.Pointer(arr)) - if err != nil { - panic(err) - } - - result := make([]any, len(goSlice)) - - for index, value := range goSlice { - result[index] = frankenphp.CallPHPCallable(unsafe.Pointer(callback), []interface{}{value}) - } - - return frankenphp.PHPPackedArray(result) -} -``` - -PHP'den parametre olarak geçirilen çağrılabilir'i çağırmak için `frankenphp.CallPHPCallable()`'ı nasıl kullandığımıza dikkat edin. Bu fonksiyon, çağrılabilir'e bir işaretçi ve bir argüman dizisi alır ve çağrılabilir'in yürütme sonucunu döndürür. Alıştığınız çağrılabilir sözdizimini kullanabilirsiniz: - -```php -name` çalışmaz) -- **Yalnızca metot arayüzü** - Tüm etkileşimler, tanımladığınız metotlar aracılığıyla gerçekleşmelidir -- **Daha iyi kapsülleme** - Dahili veri yapısı tamamen Go kodu tarafından kontrol edilir -- **Tür güvenliği** - PHP kodunun yanlış türlerle dahili durumu bozma riski yok -- **Daha temiz API** - Uygun bir genel arayüz tasarlamaya zorlar - -Bu yaklaşım, daha iyi kapsülleme sağlar ve PHP kodunun Go nesnelerinizin dahili durumunu yanlışlıkla bozmasını önler. Nesneyle olan tüm etkileşimler, açıkça tanımladığınız metotlar aracılığıyla gerçekleşmelidir. - -#### Sınıflara Metot Ekleme - -Özellikler doğrudan erişilebilir olmadığından, saydam olmayan sınıflarınızla etkileşim kurmak için **metotlar tanımlamanız gerekir**. Davranışı tanımlamak için `//export_php:method` yönergesini kullanın: - -```go -package example - -// #include -import "C" -import ( - "unsafe" - - "github.com/dunglas/frankenphp" -) - -//export_php:class User -type UserStruct struct { - Name string - Age int -} - -//export_php:method User::getName(): string -func (us *UserStruct) GetUserName() unsafe.Pointer { - return frankenphp.PHPString(us.Name, false) -} - -//export_php:method User::setAge(int $age): void -func (us *UserStruct) SetUserAge(age int64) { - us.Age = int(age) -} - -//export_php:method User::getAge(): int -func (us *UserStruct) GetUserAge() int64 { - return int64(us.Age) -} - -//export_php:method User::setNamePrefix(string $prefix = "User"): void -func (us *UserStruct) SetNamePrefix(prefix *C.zend_string) { - us.Name = frankenphp.GoString(unsafe.Pointer(prefix)) + ": " + us.Name -} -``` - -#### Boş Geçilebilir Parametreler (Nullable Parameters) - -Üretici, PHP imzalarında `?` önekini kullanarak boş geçilebilir parametreleri destekler. Bir parametre boş geçilebilir olduğunda, Go fonksiyonunuzda bir işaretçiye dönüşür ve böylece PHP'de değerin `null` olup olmadığını kontrol etmenizi sağlar: - -```go -package example - -// #include -import "C" -import ( - "unsafe" - - "github.com/dunglas/frankenphp" -) - -//export_php:method User::updateInfo(?string $name, ?int $age, ?bool $active): void -func (us *UserStruct) UpdateInfo(name *C.zend_string, age *int64, active *bool) { - // Check if name was provided (not null) - if name != nil { - us.Name = frankenphp.GoString(unsafe.Pointer(name)) - } - - // Check if age was provided (not null) - if age != nil { - us.Age = int(*age) - } - - // Check if active was provided (not null) - if active != nil { - us.Active = *active - } -} -``` - -**Boş geçilebilir parametreler hakkında önemli noktalar:** - -- **Boş geçilebilir ilkel türler** (`?int`, `?float`, `?bool`) Go'da işaretçilere (`*int64`, `*float64`, `*bool`) dönüşür -- **Boş geçilebilir dizeler** (`?string`) `*C.zend_string` olarak kalır ancak `nil` olabilir -- İşaretçi değerlerini referans almadan önce `nil` kontrolü yapın -- **PHP `null` Go `nil` olur** - PHP `null` geçirdiğinde, Go fonksiyonunuz bir `nil` işaretçi alır - -> [!WARNING] -> -> Şu anda sınıf metotlarının aşağıdaki sınırlamaları bulunmaktadır. Parametre türleri veya dönüş türleri olarak **nesneler desteklenmemektedir**. **Diziler**, hem parametreler hem de dönüş türleri için tam olarak desteklenmektedir. Desteklenen türler: `string`, `int`, `float`, `bool`, `array` ve `void` (dönüş türü için). **Boş geçilebilir parametre türleri**, tüm skaler türler (`?string`, `?int`, `?float`, `?bool`) için tam olarak desteklenmektedir. - -Eklentiyi oluşturduktan sonra, sınıfı ve metotlarını PHP'de kullanmanıza izin verilecektir. Özelliklere **doğrudan erişemeyeceğinizi** unutmayın: - -```php -setAge(25); -echo $user->getName(); // Çıktı: (boş, varsayılan değer) -echo $user->getAge(); // Çıktı: 25 -$user->setNamePrefix("Employee"); - -// ✅ Bu da çalışır - boş geçilebilir parametreler -$user->updateInfo("John", 30, true); // Tüm parametreler sağlandı -$user->updateInfo("Jane", null, false); // Yaş null -$user->updateInfo(null, 25, null); // Ad ve aktif null - -// ❌ Bu ÇALIŞMAZ - doğrudan özellik erişimi -// echo $user->name; // Hata: Özel özelliğe erişilemez -// $user->age = 30; // Hata: Özel özelliğe erişilemez -``` - -Bu tasarım, Go kodunuzun nesnenin durumuna nasıl erişildiğini ve değiştirildiğini tamamen kontrol etmesini sağlayarak daha iyi kapsülleme ve tür güvenliği sunar. - -### Sabitleri Bildirme - -Üretici, Go sabitlerini PHP'ye iki yönerge kullanarak dışa aktarmayı destekler: genel sabitler için `//export_php:const` ve sınıf sabitleri için `//export_php:classconst`. Bu, yapılandırma değerlerini, durum kodlarını ve diğer sabitleri Go ve PHP kodu arasında paylaşmanıza olanak tanır. - -#### Genel Sabitler - -Genel PHP sabitleri oluşturmak için `//export_php:const` yönergesini kullanın: - -```go -package example - -//export_php:const -const MAX_CONNECTIONS = 100 - -//export_php:const -const API_VERSION = "1.2.3" - -//export_php:const -const STATUS_OK = iota - -//export_php:const -const STATUS_ERROR = iota -``` - -#### Sınıf Sabitleri - -Belirli bir PHP sınıfına ait sabitler oluşturmak için `//export_php:classconst ClassName` yönergesini kullanın: - -```go -package example - -//export_php:classconst User -const STATUS_ACTIVE = 1 - -//export_php:classconst User -const STATUS_INACTIVE = 0 - -//export_php:classconst User -const ROLE_ADMIN = "admin" - -//export_php:classconst Order -const STATE_PENDING = iota - -//export_php:classconst Order -const STATE_PROCESSING = iota - -//export_php:classconst Order -const STATE_COMPLETED = iota -``` - -Sınıf sabitleri, PHP'deki sınıf adı kapsamı kullanılarak erişilebilir: - -```php - -import "C" -import ( - "strings" - "unsafe" - - "github.com/dunglas/frankenphp" -) - -//export_php:const -const STR_REVERSE = iota - -//export_php:const -const STR_NORMAL = iota - -//export_php:classconst StringProcessor -const MODE_LOWERCASE = 1 - -//export_php:classconst StringProcessor -const MODE_UPPERCASE = 2 - -//export_php:function repeat_this(string $str, int $count, int $mode): string -func repeat_this(s *C.zend_string, count int64, mode int) unsafe.Pointer { - str := frankenphp.GoString(unsafe.Pointer(s)) - - result := strings.Repeat(str, int(count)) - if mode == STR_REVERSE { - // reverse the string - } - - if mode == STR_NORMAL { - // no-op, just to showcase the constant - } - - return frankenphp.PHPString(str, false) -} - -//export_php:class StringProcessor -type StringProcessorStruct struct { - // internal fields -} - -//export_php:method StringProcessor::process(string $input, int $mode): string -func (sp *StringProcessorStruct) Process(input *C.zend_string, mode int64) unsafe.Pointer { - str := frankenphp.GoString(unsafe.Pointer(input)) - - switch mode { - case MODE_LOWERCASE: - str = strings.ToLower(str) - case MODE_UPPERCASE: - str = strings.ToUpper(str) - } - - return frankenphp.PHPString(str, false) -} -``` - -### Ad Alanlarını Kullanma (Namespaces) - -Üretici, PHP eklentinizin fonksiyonlarını, sınıflarını ve sabitlerini `//export_php:namespace` yönergesini kullanarak bir ad alanı altında düzenlemeyi destekler. Bu, adlandırma çakışmalarını önlemeye yardımcı olur ve eklentinizin API'si için daha iyi bir organizasyon sağlar. - -#### Bir Ad Alanı Bildirme - -Tüm dışa aktarılan sembolleri belirli bir ad alanı altına yerleştirmek için Go dosyanızın en üstünde `//export_php:namespace` yönergesini kullanın: - -```go -//export_php:namespace My\Extension -package example - -import ( - "unsafe" - - "github.com/dunglas/frankenphp" -) - -//export_php:function hello(): string -func hello() string { - return "Hello from My\\Extension namespace!" -} - -//export_php:class User -type UserStruct struct { - // internal fields -} - -//export_php:method User::getName(): string -func (u *UserStruct) GetName() unsafe.Pointer { - return frankenphp.PHPString("John Doe", false) -} - -//export_php:const -const STATUS_ACTIVE = 1 -``` - -#### Ad Alanlı Eklentiyi PHP'de Kullanma - -Bir ad alanı bildirildiğinde, tüm fonksiyonlar, sınıflar ve sabitler PHP'de o ad alanı altına yerleştirilir: - -```php -getName(); // "John Doe" - -echo My\Extension\STATUS_ACTIVE; // 1 -``` - -#### Önemli Notlar - -- Dosya başına yalnızca **bir** ad alanı yönergesine izin verilir. Birden fazla ad alanı yönergesi bulunursa, üretici bir hata döndürür. -- Ad alanı, dosyada dışa aktarılan **tüm** semboller için geçerlidir: fonksiyonlar, sınıflar, metotlar ve sabitler. -- Ad alanı adları, ayırıcı olarak ters eğik çizgi (`\`) kullanarak PHP ad alanı kurallarına uyar. -- Hiçbir ad alanı bildirilmezse, semboller her zamanki gibi genel ad alanına dışa aktarılır. - -### Eklentiyi Oluşturma - -İşte sihrin gerçekleştiği yer burası ve eklentiniz artık oluşturulabilir. Üreticiyi aşağıdaki komutla çalıştırabilirsiniz: - -```console -GEN_STUB_SCRIPT=php-src/build/gen_stub.php frankenphp extension-init my_extension.go -``` - -> [!NOTE] -> `GEN_STUB_SCRIPT` ortam değişkenini, daha önce indirdiğiniz PHP kaynaklarındaki `gen_stub.php` dosyasının yoluna ayarlamayı unutmayın. Bu, manuel uygulama bölümünde bahsedilen aynı `gen_stub.php` betiğidir. - -Her şey yolunda gittiyse, `build` adında yeni bir dizin oluşturulmuş olmalıdır. Bu dizin, oluşturulan PHP fonksiyon taslaklarını içeren `my_extension.go` dosyası da dahil olmak üzere eklentiniz için oluşturulan dosyaları içerir. - -### Oluşturulan Eklentiyi FrankenPHP'ye Entegre Etme - -Eklentimiz artık derlenmeye ve FrankenPHP'ye entegre edilmeye hazır. Bunu yapmak için, FrankenPHP'yi nasıl derleyeceğinizi öğrenmek üzere FrankenPHP [derleme belgelerine](compile.md) başvurun. `--with` bayrağını kullanarak modülünüzün yolunu işaret ederek modülü ekleyin: - -```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 github.com/my-account/my-module/build -``` - -Üretim adımı sırasında oluşturulan `/build` alt dizinini işaret ettiğinizi unutmayın. Ancak bu zorunlu değildir: Oluşturulan dosyaları modül dizininize kopyalayıp doğrudan orayı da işaret edebilirsiniz. - -### Oluşturulan Eklentinizi Test Etme - -Oluşturduğunuz fonksiyonları ve sınıfları test etmek için bir PHP dosyası oluşturabilirsiniz. Örneğin, aşağıdaki içeriğe sahip bir `index.php` dosyası oluşturun: - -```php -process('Hello World', StringProcessor::MODE_LOWERCASE); // "hello world" -echo $processor->process('Hello World', StringProcessor::MODE_UPPERCASE); // "HELLO WORLD" -``` - -Eklentinizi önceki bölümde gösterildiği gibi FrankenPHP'ye entegre ettikten sonra, bu test dosyasını `./frankenphp php-server` kullanarak çalıştırabilirsiniz ve eklentinizin çalıştığını görmelisiniz. - -## Manuel Uygulama - -Eklentilerin nasıl çalıştığını anlamak veya eklentiniz üzerinde tam kontrole sahip olmak istiyorsanız, bunları manuel olarak yazabilirsiniz. Bu yaklaşım size tam kontrol sağlar ancak daha fazla temel kod (boilerplate) gerektirir. - -### Temel Fonksiyon - -Go dilinde yeni bir yerel fonksiyon tanımlayan basit bir PHP eklentisi nasıl yazılacağını göreceğiz. Bu fonksiyon PHP'den çağrılacak ve Caddy'nin loglarına bir mesaj kaydeden bir gorutin tetikleyecektir. Bu fonksiyon herhangi bir parametre almaz ve hiçbir şey döndürmez. - -#### Go Fonksiyonunu Tanımlama - -Modülünüzde, PHP'den çağrılacak yeni bir yerel fonksiyon tanımlamanız gerekir. Bunu yapmak için, örneğin `extension.go` adında istediğiniz bir dosya oluşturun ve aşağıdaki kodu ekleyin: - -```go -package example - -// #include "extension.h" -import "C" -import ( - "log/slog" - "unsafe" - - "github.com/dunglas/frankenphp" -) - -func init() { - frankenphp.RegisterExtension(unsafe.Pointer(&C.ext_module_entry)) -} - -//export go_print_something -func go_print_something() { - go func() { - slog.Info("Hello from a goroutine!") - }() -} -``` - -`frankenphp.RegisterExtension()` fonksiyonu, dahili PHP kayıt mantığını ele alarak eklenti kayıt sürecini basitleştirir. `go_print_something` fonksiyonu, CGO sayesinde yazacağımız C kodunda erişilebilir olacağını belirtmek için `//export` yönergesini kullanır. - -Bu örnekte, yeni fonksiyonumuz Caddy'nin loglarına bir mesaj kaydeden bir gorutin tetikleyecektir. - -#### PHP Fonksiyonunu Tanımlama - -PHP'nin fonksiyonumuzu çağırabilmesi için ilgili bir PHP fonksiyonu tanımlamamız gerekiyor. Bunun için, örneğin `extension.stub.php` adında bir taslak dosya oluşturacağız ve bu dosya aşağıdaki kodu içerecektir: - -```php - - -extern zend_module_entry ext_module_entry; - -#endif -``` - -Ardından, aşağıdaki adımları gerçekleştirecek `extension.c` adında bir dosya oluşturun: - -- PHP başlıklarını dahil etme; -- Yeni yerel PHP fonksiyonumuz `go_print()`'i bildirme; -- Eklenti meta verilerini bildirme. - -Gerekli başlıkları dahil ederek başlayalım: - -```c -#include -#include "extension.h" -#include "extension_arginfo.h" - -// Go tarafından dışa aktarılan sembolleri içerir -#include "_cgo_export.h" -``` - -Daha sonra PHP fonksiyonumuzu yerel bir dil fonksiyonu olarak tanımlıyoruz: - -```c -PHP_FUNCTION(go_print) -{ - ZEND_PARSE_PARAMETERS_NONE(); - - go_print_something(); -} - -zend_module_entry ext_module_entry = { - STANDARD_MODULE_HEADER, - "ext_go", - ext_functions, /* Fonksiyonlar */ - NULL, /* MINIT */ - NULL, /* MSHUTDOWN */ - NULL, /* RINIT */ - NULL, /* RSHUTDOWN */ - NULL, /* MINFO */ - "0.1.1", - STANDARD_MODULE_PROPERTIES -}; -``` - -Bu durumda, fonksiyonumuz hiçbir parametre almaz ve hiçbir şey döndürmez. Daha önce tanımladığımız ve `//export` yönergesi kullanılarak dışa aktarılan Go fonksiyonunu çağırmaktadır. - -Son olarak, eklentinin adını, sürümünü ve özelliklerini içeren `zend_module_entry` yapısında meta verilerini tanımlıyoruz. Bu bilgiler, PHP'nin eklentimizi tanıması ve yüklemesi için gereklidir. `ext_functions`'ın tanımladığımız PHP fonksiyonlarına işaretçilerden oluşan bir dizi olduğunu ve `gen_stub.php` betiği tarafından `extension_arginfo.h` dosyasında otomatik olarak oluşturulduğunu unutmayın. - -Eklenti kaydı, Go kodumuzda çağırdığımız FrankenPHP'nin `RegisterExtension()` fonksiyonu tarafından otomatik olarak halledilir. - -### Gelişmiş Kullanım - -Şimdi Go'da temel bir PHP eklentisi oluşturmayı bildiğimize göre, örneğimizi karmaşıklaştıralım. Şimdi parametre olarak bir dize alan ve büyük harfli versiyonunu döndüren bir PHP fonksiyonu oluşturacağız. - -#### PHP Fonksiyon Taslağını Tanımlama - -Yeni PHP fonksiyonunu tanımlamak için, `extension.stub.php` dosyamızı yeni fonksiyon imzasını içerecek şekilde değiştireceğiz: - -```php - [!TIP] -> Fonksiyonlarınızın dokümantasyonunu ihmal etmeyin! Eklenti taslaklarınızı diğer geliştiricilerle paylaşarak eklentinizin nasıl kullanılacağını ve hangi özelliklerin mevcut olduğunu belgeleyebilirsiniz. - -Taslak dosyasını `gen_stub.php` betiği ile yeniden oluşturarak, `extension_arginfo.h` dosyası şöyle görünmelidir: - -```c -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_go_upper, 0, 1, IS_STRING, 0) - ZEND_ARG_TYPE_INFO(0, string, IS_STRING, 0) -ZEND_END_ARG_INFO() - -ZEND_FUNCTION(go_upper); - -static const zend_function_entry ext_functions[] = { - ZEND_FE(go_upper, arginfo_go_upper) - ZEND_FE_END -}; -``` - -`go_upper` fonksiyonunun `string` tipinde bir parametre ve `string` tipinde bir dönüş değeriyle tanımlandığını görebiliriz. - -#### Go ve PHP/C Arasında Tür Dengeleme - -Go fonksiyonunuz doğrudan bir PHP dizesini parametre olarak kabul edemez. Onu bir Go dizesine dönüştürmeniz gerekir. Neyse ki FrankenPHP, üretici yaklaşımında gördüğümüze benzer şekilde, PHP dizeleri ile Go dizeleri arasındaki dönüşümü yönetmek için yardımcı fonksiyonlar sağlar. - -Başlık dosyası basit kalır: - -```c -#ifndef _EXTENSION_H -#define _EXTENSION_H - -#include - -extern zend_module_entry ext_module_entry; - -#endif -``` - -Şimdi `extension.c` dosyamızda Go ve C arasındaki köprüyü yazabiliriz. PHP dizesini doğrudan Go fonksiyonumuza geçireceğiz: - -```c -PHP_FUNCTION(go_upper) -{ - zend_string *str; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_STR(str) - ZEND_PARSE_PARAMETERS_END(); - - zend_string *result = go_upper(str); - RETVAL_STR(result); -} -``` - -`ZEND_PARSE_PARAMETERS_START` ve parametre ayrıştırma hakkında daha fazla bilgiyi [PHP Dahili Kitabı'nın](https://www.phpinternalsbook.com/php7/extensions_design/php_functions.html#parsing-parameters-zend-parse-parameters) ilgili sayfasında bulabilirsiniz. Burada, PHP'ye fonksiyonumuzun `zend_string` tipinde bir zorunlu parametre aldığını söylüyoruz. Daha sonra bu dizeyi doğrudan Go fonksiyonumuza iletiyor ve sonucu `RETVAL_STR` kullanarak döndürüyoruz. - -Yapılacak tek bir şey kaldı: `go_upper` fonksiyonunu Go'da uygulamak. - -#### Go Fonksiyonunu Uygulama - -Go fonksiyonumuz parametre olarak bir `*C.zend_string` alacak, FrankenPHP'nin yardımcı fonksiyonunu kullanarak onu bir Go dizesine dönüştürecek, işleyecek ve sonucu yeni bir `*C.zend_string` olarak döndürecektir. Yardımcı fonksiyonlar, tüm bellek yönetimi ve dönüşüm karmaşıklığını bizim için halleder. - -```go -package example - -// #include -import "C" -import ( - "unsafe" - "strings" - - "github.com/dunglas/frankenphp" -) - -//export go_upper -func go_upper(s *C.zend_string) *C.zend_string { - str := frankenphp.GoString(unsafe.Pointer(s)) - - upper := strings.ToUpper(str) - - return (*C.zend_string)(frankenphp.PHPString(upper, false)) -} -``` - -Bu yaklaşım, manuel bellek yönetiminden çok daha temiz ve güvenlidir. -FrankenPHP'nin yardımcı fonksiyonları, PHP'nin `zend_string` formatı ile Go dizeleri arasındaki dönüşümü otomatik olarak halleder. -`PHPString()`'deki `false` parametresi, yeni bir kalıcı olmayan dize oluşturmak istediğimizi belirtir (isteğin sonunda serbest bırakılır). - -> [!TIP] -> -> Bu örnekte herhangi bir hata işleme yapmıyoruz, ancak Go fonksiyonlarınızda kullanmadan önce her zaman işaretçilerin `nil` olmadığını ve verilerin geçerli olduğunu kontrol etmelisiniz. - -### Eklentiyi FrankenPHP'ye Entegre Etme - -Eklentimiz artık derlenmeye ve FrankenPHP'ye entegre edilmeye hazır. Bunu yapmak için, FrankenPHP'yi nasıl derleyeceğinizi öğrenmek üzere FrankenPHP [derleme belgelerine](compile.md) başvurun. `--with` bayrağını kullanarak modülünüzün yolunu işaret ederek modülü ekleyin: - -```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 github.com/my-account/my-module -``` - -İşte bu kadar! Eklentiniz artık FrankenPHP'ye entegre edildi ve PHP kodunuzda kullanılabilir. - -### Eklentinizi Test Etme - -Eklentinizi FrankenPHP'ye entegre ettikten sonra, uyguladığınız fonksiyonlar için örnekler içeren bir `index.php` dosyası oluşturabilirsiniz: - -```php - [!UYARI] -> Bu özellik **yalnızca geliştirme ortamları** için tasarlanmıştır. -> `hot_reload`'u üretimde etkinleştirmeyin, çünkü dosya sistemini izlemek performans düşüşüne neden olur ve dahili uç noktaları açığa çıkarır. - -```caddyfile -localhost - -mercure { - anonymous -} - -root public/ -php_server { - hot_reload -} -``` - -Varsayılan olarak, FrankenPHP mevcut çalışma dizinindeki şu glob desenine uyan tüm dosyaları izleyecektir: `./**/*.{css,env,gif,htm,html,jpg,jpeg,js,mjs,php,png,svg,twig,webp,xml,yaml,yml}` - -Glob sözdizimini kullanarak izlenecek dosyaları açıkça belirtmek mümkündür: - -```caddyfile -localhost - -mercure { - anonymous -} - -root public/ -php_server { - hot_reload src/**/*{.php,.js} config/**/*.yaml -} -``` - -Mercure konusunu ve hangi dizin veya dosyaların izleneceğini belirtmek için `hot_reload` seçeneğine yollar sağlayarak uzun formu kullanın: - -```caddyfile -localhost - -mercure { - anonymous -} - -root public/ -php_server { - hot_reload { - topic hot-reload-topic - watch src/**/*.php - watch assets/**/*.{ts,json} - watch templates/ - watch public/css/ - } -} -``` - -## İstemci Tarafı Entegrasyonu - -Sunucu değişiklikleri algılarken, tarayıcının sayfayı güncellemek için bu olaylara abone olması gerekir. -FrankenPHP, dosya değişikliklerine abone olmak için kullanılacak Mercure Hub URL'sini `$_SERVER['FRANKENPHP_HOT_RELOAD']` ortam değişkeni aracılığıyla açığa çıkarır. - -İstemci tarafı mantığını işlemek için kullanışlı bir JavaScript kütüphanesi olan [frankenphp-hot-reload](https://www.npmjs.com/package/frankenphp-hot-reload) da mevcuttur. -Kullanmak için ana şablonunuza aşağıdakileri ekleyin: - -```php - -FrankenPHP Anında Yeniden Yükleme - - - - - -``` - -Kütüphane, Mercure hub'ına otomatik olarak abone olacak, bir dosya değişikliği algılandığında arka planda mevcut URL'yi çekecek ve DOM'u biçimlendirecektir. -Bir [npm](https://www.npmjs.com/package/frankenphp-hot-reload) paketi olarak ve [GitHub](https://github.com/dunglas/frankenphp-hot-reload) üzerinde mevcuttur. - -Alternatif olarak, `EventSource` yerel JavaScript sınıfını kullanarak doğrudan Mercure hub'ına abone olarak kendi istemci tarafı mantığınızı uygulayabilirsiniz. - -### Çalışan Modu - -Uygulamanızı [Çalışan Modunda](https://frankenphp.dev/docs/worker/) çalıştırıyorsanız, uygulama betiğiniz bellekte kalır. -Bu, tarayıcı yeniden yüklense bile PHP kodunuzdaki değişikliklerin hemen yansımayacağı anlamına gelir. - -En iyi geliştirici deneyimi için, `hot_reload`'u [çalışan yönergesindeki `watch` alt yönergesi](config.md#watching-for-file-changes) ile birleştirmelisiniz. - -- `hot_reload`: dosyalar değiştiğinde **tarayıcıyı** yeniler -- `worker.watch`: dosyalar değiştiğinde çalışanı yeniden başlatır - -```caddy -localhost - -mercure { - anonymous -} - -root public/ -php_server { - hot_reload - worker { - file /path/to/my_worker.php - watch - } -} -``` - -### Nasıl Çalışır - -1. **İzleme**: FrankenPHP, arka planda [e-dant/watcher kütüphanesini](https://github.com/e-dant/watcher) kullanarak dosya sistemindeki değişiklikleri izler (Go bağlamasını biz geliştirdik). -2. **Yeniden Başlatma (Çalışan Modu)**: Eğer çalışan yapılandırmasında `watch` etkinleştirilmişse, yeni kodu yüklemek için PHP çalışanı yeniden başlatılır. -3. **İtme**: Değişen dosyaların listesini içeren bir JSON yükü, yerleşik [Mercure hub](https://mercure.rocks)'ına gönderilir. -4. **Alma**: JavaScript kütüphanesi aracılığıyla dinleyen tarayıcı, Mercure olayını alır. -5. **Güncelleme**: - -- Eğer **Idiomorph** algılanırsa, güncellenmiş içeriği çeker ve mevcut HTML'i yeni duruma uyması için biçimlendirir, durumu kaybetmeden değişiklikleri anında uygular. -- Aksi takdirde, sayfayı yenilemek için `window.location.reload()` çağrılır. diff --git a/docs/tr/known-issues.md b/docs/tr/known-issues.md index a9abbf80e7..a934375a12 100644 --- a/docs/tr/known-issues.md +++ b/docs/tr/known-issues.md @@ -4,40 +4,34 @@ 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) | -| [newrelic](https://docs.newrelic.com/docs/apm/agents/php-agent/getting-started/introduction-new-relic-php/) | İş parçacığı güvenli değil | - | +| 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 | -| :------------------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| [ext-openssl](https://www.php.net/manual/en/book.openssl.php) | musl libc kullanıldığında, OpenSSL eklentisi yoğun yük altında çökebilir. Bu sorun, daha popüler olan GNU libc kullanıldığında ortaya çıkmaz. Bu hata [PHP tarafından takip edilmektedir](https://github.com/php/php-src/issues/13648). | +| 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). +[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). -## Tek Başına İkili ve Alpine Tabanlı Docker İmajları +## Binary Çıktısı ve Alpine Tabanlı Docker İmajları -Tek başına ikili ve Alpine tabanlı Docker imajları (`dunglas/frankenphp:*-alpine`), daha küçük bir ikili boyutu korumak için [glibc ve arkadaşları](https://www.etalabs.net/compare_libcs.html) yerine [musl libc](https://musl.libc.org/) kullanır. -Bu durum bazı uyumluluk sorunlarına yol açabilir. -Özellikle, glob bayrağı `GLOB_BRACE` [mevcut değildir](https://www.php.net/manual/en/function.glob.php). - -Sorunlarla karşılaşmanız durumunda, statik ikilinin GNU varyantını ve Debian tabanlı Docker imajlarını kullanmayı tercih edin. +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 şekilde yapılandırmak mümkündür. +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` benzeri bir TLS hatası alırsınız. +`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: @@ -51,7 +45,7 @@ docker run \ 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` komutunu çalıştırın ve `Containers` anahtarının altındaki `IPv4Address` anahtarındaki son atanmış IP adresini belirlemek için 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. +`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: @@ -65,7 +59,7 @@ docker run \ > [!CAUTION] > -> `172.17.0.3` değerini konteynerinize atanacak IP ile değiştirdiğinizden emin olun. +> 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. @@ -80,17 +74,17 @@ docker run \ dunglas/frankenphp ``` -## `@php` Referanslı Composer Betikleri +## `@php` Referanslı Composer Betikler -[Composer betikleri](https://getcomposer.org/doc/articles/scripts.md) bazı görevler için bir PHP ikilisi çalıştırmak isteyebilir, örneğin [bir Laravel projesinde](laravel.md) `@php artisan package:discover --ansi` çalıştırmak. Bu [şu anda başarısız oluyor](https://github.com/php/frankenphp/issues/483#issuecomment-1899890915) ve bunun iki nedeni var: +[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/php/frankenphp/issues/483#issuecomment-1899890915) ve 2 nedeni var: -- Composer, FrankenPHP ikilisini nasıl çağıracağını bilmiyor; -- Composer, FrankenPHP'nin henüz desteklemediği `-d` bayrağını kullanarak komuta PHP ayarları ekleyebilir. +- 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 temizleyen ve ardından FrankenPHP'yi çağıran bir kabuk betiği oluşturabiliriz: +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 -#!/usr/bin/env bash +#!/bin/bash args=("$@") index=0 for i in "$@" @@ -105,42 +99,9 @@ done /usr/local/bin/frankenphp php-cli ${args[@]} ``` -Ardından `PHP_BINARY` ortam değişkenini PHP betiğimizin yoluna ayarlayın ve Composer'ı çalıştırın: +Ardından `PHP_BINARY` ortam değişkenini PHP betiğimizin yoluna ayarlayın ve Composer bu yolla çalışacaktır: -```console +```bash export PHP_BINARY=/usr/local/bin/php composer install ``` - -## Statik İkililerle TLS/SSL Sorunlarını Giderme - -Statik ikilileri kullanırken, örneğin STARTTLS kullanarak e-posta gönderirken aşağıdaki TLS ile ilgili hatalarla karşılaşabilirsiniz: - -```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 -``` - -Statik ikili TLS sertifikalarını içermediğinden, OpenSSL'i yerel CA sertifikaları kurulumunuza yönlendirmeniz gerekir. - -CA sertifikalarının nereye yüklenmesi gerektiğini bulmak ve bu konuma kaydetmek için [`openssl_get_cert_locations()`](https://www.php.net/manual/en/function.openssl-get-cert-locations.php) çıktısını inceleyin. - -> [!WARNING] -> -> Web ve CLI bağlamları farklı ayarlara sahip olabilir. -> `openssl_get_cert_locations()` fonksiyonunu doğru bağlamda çalıştırdığınızdan emin olun. - -[Mozilla'dan çıkarılan CA sertifikaları cURL sitesinden indirilebilir](https://curl.se/docs/caextract.html). - -Alternatif olarak, Debian, Ubuntu ve Alpine dahil olmak üzere birçok dağıtım, bu sertifikaları içeren `ca-certificates` adlı paketler sağlar. - -OpenSSL'e CA sertifikalarını nerede arayacağını belirtmek için `SSL_CERT_FILE` ve `SSL_CERT_DIR` değişkenlerini kullanmak da mümkündür: - -```console -# TLS sertifikası ortam değişkenlerini ayarla -export SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt -export SSL_CERT_DIR=/etc/ssl/certs -``` diff --git a/docs/tr/laravel.md b/docs/tr/laravel.md index d07bc68596..afa0bd9c40 100644 --- a/docs/tr/laravel.md +++ b/docs/tr/laravel.md @@ -17,7 +17,7 @@ Ve tadını çıkarın! Alternatif olarak, Laravel projelerinizi FrankenPHP ile yerel makinenizden çalıştırabilirsiniz: 1. [Sisteminize karşılık gelen ikili dosyayı indirin](../#standalone-binary) -2. Aşağıdaki yapılandırmayı Laravel projenizin kök dizenindeki `Caddyfile` adlı bir dosyaya ekleyin: +2. Aşağıdaki yapılandırmayı Laravel projenizin kök dizinindeki `Caddyfile` adlı bir dosyaya ekleyin: ```caddyfile { @@ -66,25 +66,24 @@ php artisan octane:frankenphp - `--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 (varsayılan: [Laravel Octane'deki taslak `Caddyfile`](https://github.com/laravel/octane/blob/2.x/src/Commands/stubs/Caddyfile)) +- `--caddyfile`: FrankenPHP `Caddyfile` dosyasının yolu (varsayılan: [Laravel Octane içinde bulunan şablon `Caddyfile`](https://github.com/laravel/octane/blob/2.x/src/Commands/stubs/Caddyfile)) - `--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 ile birlikte geçilirse etkinleşir) - `--watch`: Uygulama değiştirildiğinde 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`: Yerel Caddy günlükleyicisini kullanarak belirtilen günlük seviyesinde veya üzerinde mesajları kaydedin +- `--log-level`: Yerel Caddy günlüğünü kullanarak belirtilen günlük seviyesinde veya üzerinde mesajları kaydedin > [!TIP] > Yapılandırılmış JSON günlükleri elde etmek için (log analitik çözümleri kullanırken faydalıdır), `--log-level` seçeneğini açıkça geçin. -Ayrıca [Octane ile Mercure nasıl kullanılır](#mercure-support) bölümüne bakın. - [Laravel Octane hakkında daha fazla bilgiyi resmi belgelerde bulabilirsiniz](https://laravel.com/docs/octane). -## Laravel Uygulamalarını Bağımsız İkili Dosyalar Olarak Dağıtma +## Laravel Uygulamalarını Bağımsız Çalıştırılabilir Dosyalar Olarak Dağıtma -[FrankenPHP'nin uygulama gömme özelliğini](embed.md) kullanarak, Laravel uygulamalarını bağımsız ikili dosyalar olarak dağıtmak mümkündür. +[FrankenPHP'nin uygulama gömme özelliğini](embed.md) kullanarak, Laravel +uygulamalarını bağımsız çalıştırılabilir dosyalar olarak dağıtmak mümkündür. -Linux için Laravel uygulamanızı bağımsız bir ikili olarak paketlemek için şu adımları izleyin: +Linux için Laravel uygulamanızı bağımsız bir çalıştırılabilir olarak paketlemek için şu adımları izleyin: 1. Uygulamanızın deposunda `static-build.Dockerfile` adında bir dosya oluşturun: @@ -157,7 +156,8 @@ Linux için Laravel uygulamanızı bağımsız bir ikili olarak paketlemek için Uygulamanız artık hazır! -Mevcut seçenekler hakkında daha fazla bilgi edinin ve diğer işletim sistemleri için nasıl ikili derleneceğini [uygulama gömme](embed.md) belgelerinde öğrenin. +Mevcut seçenekler hakkında daha fazla bilgi edinin ve diğer işletim sistemleri için nasıl ikili derleneceğini [uygulama gömme](embed.md) +belgelerinde öğrenin. ### Depolama Yolunu Değiştirme @@ -166,37 +166,11 @@ Gömülü uygulamalar için bu uygun değildir, çünkü her yeni sürüm farkl Geçici dizin dışında bir dizin kullanmak için `LARAVEL_STORAGE_PATH` ortam değişkenini ayarlayın (örneğin, `.env` dosyanızda) veya `Illuminate\Foundation\Application::useStoragePath()` metodunu çağırın. -### Mercure Desteği - -[Mercure](https://mercure.rocks), Laravel uygulamalarınıza gerçek zamanlı yetenekler eklemenin harika bir yoludur. FrankenPHP, [Mercure desteğini kutudan çıktığı gibi](mercure.md) sunar. - -[Octane](#laravel-octane) kullanmıyorsanız, [Mercure belgelendirme girdisine](mercure.md) bakın. - -Octane kullanıyorsanız, `config/octane.php` dosyanıza aşağıdaki satırları ekleyerek Mercure desteğini etkinleştirebilirsiniz: - -```php -// ... - -return [ - // ... - - 'mercure' => [ - 'anonymous' => true, - 'publisher_jwt' => '!ChangeThisMercureHubJWTSecretKey!', - 'subscriber_jwt' => '!ChangeThisMercureHubJWTSecretKey!', - ], -]; -``` - -Bu dizide [Mercure tarafından desteklenen tüm yönergeleri](https://mercure.rocks/docs/hub/config#directives) kullanabilirsiniz. - -Güncellemeleri yayınlamak ve abone olmak için, [Laravel Mercure Broadcaster](https://github.com/mvanduijker/laravel-mercure-broadcaster) kütüphanesini kullanmanızı öneririz. Alternatif olarak, saf PHP ve JavaScript ile yapmak için [Mercure belgelerine](mercure.md) bakın. - -### Bağımsız İkili Dosyalarla Octane'i Çalıştırma +### Bağımsız Çalıştırılabilir Dosyalarla Octane'i Çalıştırma -Laravel Octane uygulamalarını bağımsız ikili dosyalar olarak paketlemek bile mümkündür! +Laravel Octane uygulamalarını bağımsız çalıştırılabilir dosyalar olarak paketlemek bile mümkündür! -Bunu yapmak için, [Octane'i doğru şekilde kurun](#laravel-octane) ve [önceki bölümde](#laravel-uygulamalarını-bağımsız-ikili-dosyalar-olarak-dağıtma) açıklanan adımları izleyin. +Bunu yapmak için, [Octane'i doğru şekilde kurun](#laravel-octane) ve [önceki bölümde](#laravel-uygulamalarını-bağımsız-çalıştırılabilir-dosyalar-olarak-dağıtma) açıklanan adımları izleyin. Ardından, Octane üzerinden FrankenPHP'yi worker modunda başlatmak için şunu çalıştırın: diff --git a/docs/tr/logging.md b/docs/tr/logging.md deleted file mode 100644 index b733c9dfbd..0000000000 --- a/docs/tr/logging.md +++ /dev/null @@ -1,71 +0,0 @@ -# Günlüğe Kayıt - -FrankenPHP, [Caddy'nin günlüğe kayıt sistemi](https://caddyserver.com/docs/logging) ile sorunsuz bir şekilde entegre olur. -Standart PHP fonksiyonlarını kullanarak mesajları günlüğe kaydedebilir veya gelişmiş -yapılandırılmış günlüğe kayıt yetenekleri için özel `frankenphp_log()` fonksiyonunu kullanabilirsiniz. - -## `frankenphp_log()` - -`frankenphp_log()` fonksiyonu, yapılandırılmış logları doğrudan PHP uygulamanızdan yaymanıza olanak tanır, -böylece Datadog, Grafana Loki veya Elastic gibi platformlara alımı ve OpenTelemetry desteği çok daha kolay hale gelir. - -Arka planda, `frankenphp_log()`, zengin günlük kaydı özellikleri sunmak için [Go'nun `log/slog` paketi](https://pkg.go.dev/log/slog)'nı sarmalar. - -Bu loglar, önem derecesi seviyesini ve isteğe bağlı bağlam verilerini içerir. - -```php -function frankenphp_log(string $message, int $level = FRANKENPHP_LOG_LEVEL_INFO, array $context = []): void -``` - -### Parametreler - -- **`message`**: Günlük mesaj dizisi. -- **`level`**: Günlüğün önem derecesi seviyesi. Herhangi bir keyfi tam sayı olabilir. Yaygın seviyeler için kolaylık sabitleri sağlanmıştır: `FRANKENPHP_LOG_LEVEL_DEBUG` (`-4`), `FRANKENPHP_LOG_LEVEL_INFO` (`0`), `FRANKENPHP_LOG_LEVEL_WARN` (`4`) ve `FRANKENPHP_LOG_LEVEL_ERROR` (`8`)). Varsayılan değer `FRANKENPHP_LOG_LEVEL_INFO`'dur. -- **`context`**: Günlük girdisine eklenecek ek verilerden oluşan ilişkisel bir dizi. - -### Örnek - -```php - memory_get_usage(), - 'peak_usage' => memory_get_peak_usage(), - ], -); - -``` - -Logları görüntülerken (örn. `docker compose logs` aracılığıyla), çıktı yapılandırılmış JSON olarak görünecektir: - -```json -{"level":"info","ts":1704067200,"logger":"frankenphp","msg":"Hello from FrankenPHP!"} -{"level":"warn","ts":1704067200,"logger":"frankenphp","msg":"Memory usage high","current_usage":10485760,"peak_usage":12582912} -``` - -## `error_log()` - -FrankenPHP, standart `error_log()` fonksiyonunu kullanarak da günlüğe kayıt yapılmasına olanak tanır. Eğer `$message_type` parametresi `4` (SAPI) ise, bu mesajlar Caddy günlükçüsüne yönlendirilir. - -Varsayılan olarak, `error_log()` aracılığıyla gönderilen mesajlar yapılandırılmamış metin olarak ele alınır. -Mevcut uygulamalar veya standart PHP kütüphanesine dayanan kütüphanelerle uyumluluk için kullanışlıdırlar. - -### `error_log()` ile Örnek - -```php -error_log("Database connection failed", 4); -``` - -Bu, Caddy loglarında görünecektir, genellikle PHP'den kaynaklandığını belirtmek için ön eklenmiş olarak. - -> [!TIP] -> Üretim ortamlarında daha iyi gözlemlenebilirlik için, `frankenphp_log()` tercih edin -> çünkü bu, logları seviyeye göre (Hata Ayıklama, Hata vb.) filtrelemenize -> ve günlük kaydı altyapınızdaki belirli alanları sorgulamanıza olanak tanır. diff --git a/docs/tr/mercure.md b/docs/tr/mercure.md index 9baba4a070..581d486ba5 100644 --- a/docs/tr/mercure.md +++ b/docs/tr/mercure.md @@ -1,148 +1,12 @@ # Gerçek Zamanlı FrankenPHP yerleşik bir [Mercure](https://mercure.rocks) hub ile birlikte gelir! -Mercure, tüm bağlı cihazlara gerçek zamanlı olaylar göndermenizi sağlar: anında bir JavaScript olayı alırlar. +Mercure, olayları tüm bağlı cihazlara gerçek zamanlı olarak göndermeye olanak tanır: anında bir JavaScript olayı alırlar. -WebSockets'e uygun, kullanımı kolay ve tüm modern web tarayıcıları tarafından doğal olarak desteklenen pratik bir alternatiftir! +JS kütüphanesi veya SDK gerekmez! -![Mercure](mercure-hub.png) +![Mercure](../mercure-hub.png) -## Mercure'ü Etkinleştirme +Mercure hub'ını etkinleştirmek için [Mercure'ün sitesinde](https://mercure.rocks/docs/hub/config) açıklandığı gibi `Caddyfile`'ı güncelleyin. -Mercure desteği varsayılan olarak devre dışıdır. -İşte hem FrankenPHP'yi hem de Mercure hub'ını etkinleştiren minimal bir `Caddyfile` örneği: - -```caddyfile -# The hostname to respond to -localhost - -mercure { - # The secret key used to sign the JWT tokens for publishers - publisher_jwt !ChangeThisMercureHubJWTSecretKey! - # Allows anonymous subscribers (without JWT) - anonymous -} - -root public/ -php_server -``` - -> [!TIP] -> -> [Docker imajları](docker.md) tarafından sağlanan [örnek `Caddyfile`](https://github.com/php/frankenphp/blob/main/caddy/frankenphp/Caddyfile), -> Mercure yapılandırmasını kolay ortam değişkenleriyle yapılandırmak için zaten yorumlanmış bir şekilde içerir. -> -> Etkinleştirmek için `/etc/frankenphp/Caddyfile` içindeki Mercure bölümünün yorumunu kaldırın. - -## Güncellemelere Abone Olma - -Varsayılan olarak, Mercure hub'ı FrankenPHP sunucunuzun `/.well-known/mercure` yolu üzerinde mevcuttur. -Güncellemelere abone olmak için yerel [`EventSource`](https://developer.mozilla.org/docs/Web/API/EventSource) JavaScript sınıfını kullanın: - -```html - - -Mercure Örneği - -``` - -## Güncellemeleri Yayınlama - -### `mercure_publish()` Kullanarak - -FrankenPHP, yerleşik Mercure hub'ına güncellemeler yayınlamak için kullanışlı bir `mercure_publish()` işlevi sunar: - -```php - 'value'])); - -// FrankenPHP'nin loglarına yaz -error_log("update $updateID published", 4); -``` - -Tam işlev imzası şöyledir: - -```php -/** - * @param string|string[] $topics - */ -function mercure_publish(string|array $topics, string $data = '', bool $private = false, ?string $id = null, ?string $type = null, ?int $retry = null): string {} -``` - -### `file_get_contents()` Kullanarak - -Bağlı abonelere bir güncelleme göndermek için Mercure hub'ına `topic` ve `data` parametreleriyle kimliği doğrulanmış bir POST isteği gönderin: - -```php - [ - 'method' => 'POST', - 'header' => "Content-type: application/x-www-form-urlencoded\r\nAuthorization: Bearer " . JWT, - 'content' => http_build_query([ - 'topic' => 'my-topic', - 'data' => json_encode(['key' => 'value']), - ]), -]])); - -// FrankenPHP'nin loglarına yaz -error_log("update $updateID published", 4); -``` - -`Caddyfile` içindeki `mercure.publisher_jwt` seçeneğinin parametresi olarak geçirilen anahtar, `Authorization` başlığında kullanılan JWT tokenını imzalamak için kullanılmalıdır. - -JWT, yayınlamak istediğiniz konular için `publish` izni içeren bir `mercure` talebi içermelidir. -Yetkilendirme hakkında [Mercure dokümantasyonuna](https://mercure.rocks/spec#publishers) bakın. - -Kendi tokenlarınızı oluşturmak için [bu jwt.io bağlantısını](https://www.jwt.io/#token=eyJhbGciOiJIUzI1NiJ9.eyJtZXJjdXJlIjp7InB1Ymxpc2giOlsiKiJdfX0.PXwpfIGng6KObfZlcOXvcnWCJOWTFLtswGI5DZuWSK4) kullanabilirsiniz, -ancak üretim uygulamaları için, güvenilir bir [JWT kütüphanesi](https://www.jwt.io/libraries?programming_language=php) kullanılarak dinamik olarak oluşturulan kısa ömürlü tokenlar kullanılması önerilir. - -### Symfony Mercure Kullanarak - -Alternatif olarak, bağımsız bir PHP kütüphanesi olan [Symfony Mercure Bileşenini](https://symfony.com/components/Mercure) kullanabilirsiniz. - -Bu kütüphane, JWT oluşturma, güncelleme yayınlama ve aboneler için çerez tabanlı yetkilendirme işlemlerini ele alır. - -İlk olarak, kütüphaneyi Composer kullanarak yükleyin: - -```console -composer require symfony/mercure lcobucci/jwt -``` - -Daha sonra, bu şekilde kullanabilirsiniz: - -```php -publish(new \Symfony\Component\Mercure\Update('my-topic', json_encode(['key' => 'value']))); - -// FrankenPHP'nin loglarına yaz -error_log("update $updateID published", 4); -``` - -Mercure ayrıca aşağıdaki tarafından doğal olarak desteklenir: - -- [Laravel](laravel.md#mercure-support) -- [Symfony](https://symfony.com/doc/current/mercure.html) -- [API Platform](https://api-platform.com/docs/core/mercure/) +Mercure güncellemelerini kodunuzdan göndermek için [Symfony Mercure Bileşenini](https://symfony.com/components/Mercure) öneririz (kullanmak için Symfony tam yığın çerçevesine ihtiyacınız yoktur). diff --git a/docs/tr/metrics.md b/docs/tr/metrics.md deleted file mode 100644 index 381bb802a5..0000000000 --- a/docs/tr/metrics.md +++ /dev/null @@ -1,17 +0,0 @@ -# Metrikler - -[Caddy metrikleri](https://caddyserver.com/docs/metrics) etkinleştirildiğinde, FrankenPHP aşağıdaki metrikleri sunar: - -- `frankenphp_total_threads`: Toplam PHP iş parçacığı sayısı. -- `frankenphp_busy_threads`: Şu anda bir isteği işleyen PHP iş parçacığı sayısı (çalışan işçiler her zaman bir iş parçacığı tüketir). -- `frankenphp_queue_depth`: Düzenli olarak kuyruğa alınmış isteklerin sayısı. -- `frankenphp_total_workers{worker="[worker_name]"}`: Toplam işçi sayısı. -- `frankenphp_busy_workers{worker="[worker_name]"}`: Şu anda bir isteği işleyen işçi sayısı. -- `frankenphp_worker_request_time{worker="[worker_name]"}`: Tüm işçiler tarafından isteklerin işlenmesi için harcanan süre. -- `frankenphp_worker_request_count{worker="[worker_name]"}`: Tüm işçiler tarafından işlenen istek sayısı. -- `frankenphp_ready_workers{worker="[worker_name]"}`: `frankenphp_handle_request`'i en az bir kez çağıran işçi sayısı. -- `frankenphp_worker_crashes{worker="[worker_name]"}`: Bir işçinin beklenmedik bir şekilde sonlandığı sayı. -- `frankenphp_worker_restarts{worker="[worker_name]"}`: Bir işçinin bilerek yeniden başlatıldığı sayı. -- `frankenphp_worker_queue_depth{worker="[worker_name]"}`: Kuyruğa alınmış istek sayısı. - -İşçi metrikleri için, `[worker_name]` yer tutucusu Caddyfile'daki işçi adıyla değiştirilir, aksi takdirde işçi dosyasının mutlak yolu kullanılacaktır. diff --git a/docs/tr/performance.md b/docs/tr/performance.md deleted file mode 100644 index 6b91f44646..0000000000 --- a/docs/tr/performance.md +++ /dev/null @@ -1,154 +0,0 @@ -# Performans - -Varsayılan olarak, FrankenPHP performans ve kullanım kolaylığı arasında iyi bir denge sunmaya çalışır. Ancak, uygun bir yapılandırma kullanarak performansı önemli ölçüde artırmak mümkündür. - -## İş Parçacığı ve Çalışan Sayısı - -Varsayılan olarak, FrankenPHP, mevcut CPU sayısının 2 katı kadar iş parçacığı ve çalışan (worker modunda) başlatır. - -Uygun değerler, uygulamanızın nasıl yazıldığına, ne yaptığına ve donanımınıza büyük ölçüde bağlıdır. Bu değerleri değiştirmenizi şiddetle tavsiye ederiz. En iyi sistem kararlılığı için, `num_threads` x `memory_limit` < `available_memory` olmasına sahip olmanız önerilir. - -Doğru değerleri bulmak için gerçek trafiği simüle eden yük testleri çalıştırmak en iyisidir. [k6](https://k6.io) ve [Gatling](https://gatling.io) bunun için iyi araçlardır. - -İş parçacığı sayısını yapılandırmak için `php_server` ve `php` direktiflerinin `num_threads` seçeneğini kullanın. Çalışan sayısını değiştirmek için `frankenphp` direktifinin `worker` bölümündeki `num` seçeneğini kullanın. - -### `max_threads` - -Trafiğinizin tam olarak nasıl olacağını bilmek her zaman daha iyi olsa da, gerçek hayattaki uygulamalar daha öngörülemez olma eğilimindedir. `max_threads` [yapılandırması](config.md#caddyfile-config), FrankenPHP'nin belirtilen sınıra kadar çalışma zamanında otomatik olarak ek iş parçacıkları oluşturmasına olanak tanır. `max_threads`, trafiğinizi yönetmek için kaç iş parçacığına ihtiyacınız olduğunu belirlemenize yardımcı olabilir ve sunucuyu gecikme artışlarına karşı daha dirençli hale getirebilir. `auto` olarak ayarlanırsa, limit `php.ini` dosyanızdaki `memory_limit` değerine göre tahmin edilecektir. Bunu yapamazsa, `auto` bunun yerine varsayılan olarak `num_threads`'in 2 katına ayarlanır. `auto`'nun ihtiyaç duyulan iş parçacığı sayısını büyük ölçüde hafife alabileceğini unutmayın. `max_threads`, PHP FPM'nin [pm.max_children](https://www.php.net/manual/en/install.fpm.configuration.php#pm.max-children) özelliğine benzer. Temel fark, FrankenPHP'nin işlemler yerine iş parçacıkları kullanması ve bunları gerektiğinde farklı çalışan betiklerine ve 'klasik moda' otomatik olarak devretmesidir. - -## Çalışan Modu - -[Çalışan modunu](worker.md) etkinleştirmek performansı önemli ölçüde artırır, ancak uygulamanızın bu modla uyumlu olacak şekilde uyarlanması gerekir: bir çalışan betiği oluşturmanız ve uygulamanın bellek sızıntısı yapmadığından emin olmanız gerekir. - -## musl Kullanmayın - -Resmi Docker imajlarının Alpine Linux varyantı ve sağladığımız varsayılan ikili dosyalar [musl libc](https://musl.libc.org) kullanmaktadır. - -PHP'nin, geleneksel GNU kütüphanesi yerine bu alternatif C kütüphanesini kullanırken [daha yavaş](https://gitlab.alpinelinux.org/alpine/aports/-/issues/14381) olduğu bilinmektedir, özellikle FrankenPHP için gerekli olan ZTS modunda (iş parçacığı güvenli) derlendiğinde. Fark, yoğun iş parçacıklı bir ortamda önemli olabilir. - -Ayrıca, [bazı hatalar sadece musl kullanılırken](https://github.com/php/php-src/issues?q=sort%3Aupdated-desc+is%3Aissue+is%3Aopen+label%3ABug+musl) ortaya çıkar. - -Üretim ortamlarında, glibc'ye bağlanmış, uygun bir optimizasyon seviyesiyle derlenmiş FrankenPHP kullanmanızı öneririz. - -Bu, Debian Docker imajlarını kullanarak, sürdürücülerimizin [.deb](https://debs.henderkes.com) veya [.rpm](https://rpms.henderkes.com) paketlerini kullanarak veya [FrankenPHP'yi kaynak koddan derleyerek](compile.md) başarılabilir. - -## Go Çalışma Zamanı Yapılandırması - -FrankenPHP Go dilinde yazılmıştır. - -Genel olarak, Go çalışma zamanı özel bir yapılandırma gerektirmez, ancak belirli durumlarda, özel yapılandırma performansı artırır. - -`GODEBUG` ortam değişkenini `cgocheck=0` olarak ayarlamak isteyebilirsiniz (FrankenPHP Docker imajlarında varsayılan değerdir). - -FrankenPHP'yi kapsayıcılarda (Docker, Kubernetes, LXC...) çalıştırıyorsanız ve kapsayıcılar için ayrılan belleği sınırlıyorsanız, `GOMEMLIMIT` ortam değişkenini kullanılabilir bellek miktarına ayarlayın. - -Daha fazla ayrıntı için, çalışma zamanından en iyi şekilde yararlanmak için [bu konuya ayrılmış Go dokümantasyon sayfası](https://pkg.go.dev/runtime#hdr-Environment_Variables) mutlaka okunmalıdır. - -## `file_server` - -Varsayılan olarak, `php_server` direktifi, kök dizinde depolanan statik dosyaları (varlıkları) sunmak için otomatik olarak bir dosya sunucusu kurar. - -Bu özellik kullanışlıdır, ancak bir maliyeti vardır. Devre dışı bırakmak için aşağıdaki yapılandırmayı kullanın: - -```caddyfile -php_server { - file_server off -} -``` - -## `try_files` - -Statik dosyalar ve PHP dosyalarının yanı sıra, `php_server` uygulamanızın dizin ve dizin dizin dosyalarını (`/path/` -> `/path/index.php`) da sunmaya çalışacaktır. Dizin dizinlerine ihtiyacınız yoksa, `try_files`'ı aşağıdaki gibi açıkça tanımlayarak bunları devre dışı bırakabilirsiniz: - -```caddyfile -php_server { - try_files {path} index.php - root /root/to/your/app # buraya kökü açıkça eklemek daha iyi önbelleğe alma sağlar -} -``` - -Bu, gereksiz dosya işlemlerinin sayısını önemli ölçüde azaltabilir. - -0 gereksiz dosya sistemi işlemi ile alternatif bir yaklaşım, bunun yerine `php` direktifini kullanmak ve dosyaları PHP'den yola göre ayırmaktır. Bu yaklaşım, tüm uygulamanızın tek bir giriş dosyası tarafından sunulması durumunda iyi çalışır. `/assets` klasörünün arkasındaki statik dosyaları sunan örnek bir [yapılandırma](config.md#caddyfile-config) şöyle görünebilir: - -```caddyfile -route { - @assets { - path /assets/* - } - - # /assets arkasındaki her şey dosya sunucusu tarafından işlenir - file_server @assets { - root /root/to/your/app - } - - # /assets içinde olmayan her şey index veya worker PHP dosyanız tarafından işlenir - rewrite index.php - php { - root /root/to/your/app # buraya kökü açıkça eklemek daha iyi önbelleğe alma sağlar - } -} -``` - -## Yer Tutucular - -`root` ve `env` direktiflerinde [yer tutucular](https://caddyserver.com/docs/conventions#placeholders) kullanabilirsiniz. Ancak bu, bu değerlerin önbelleğe alınmasını engeller ve önemli bir performans maliyeti getirir. - -Mümkünse, bu direktiflerde yer tutuculardan kaçının. - -## `resolve_root_symlink` - -Varsayılan olarak, belge kökü sembolik bir bağlantıysa, FrankenPHP tarafından otomatik olarak çözümlenir (bu, PHP'nin düzgün çalışması için gereklidir). Belge kökü bir sembolik bağlantı değilse, bu özelliği devre dışı bırakabilirsiniz. - -```caddyfile -php_server { - resolve_root_symlink false -} -``` - -Bu, `root` direktifi [yer tutucular](https://caddyserver.com/docs/conventions#placeholders) içeriyorsa performansı artıracaktır. Diğer durumlarda kazanç ihmal edilebilir olacaktır. - -## Günlükler - -Günlükleme açıkça çok faydalıdır, ancak tanımı gereği, önemli ölçüde performansı düşüren G/Ç işlemleri ve bellek tahsisleri gerektirir. [Günlükleme seviyesini](https://caddyserver.com/docs/caddyfile/options#log) doğru ayarladığınızdan ve yalnızca gerekli olanı günlüğe kaydettiğinizden emin olun. - -## PHP Performansı - -FrankenPHP resmi PHP yorumlayıcısını kullanır. Tüm olağan PHP ile ilgili performans optimizasyonları FrankenPHP ile uygulanır. - -Özellikle: - -- [OPcache](https://www.php.net/manual/en/book.opcache.php)'in kurulu, etkin ve doğru şekilde yapılandırılmış olduğundan emin olun -- [Composer otomatik yükleyici optimizasyonlarını](https://getcomposer.org/doc/articles/autoloader-optimization.md) etkinleştirin -- `realpath` önbelleğinin uygulamanızın ihtiyaçları için yeterince büyük olduğundan emin olun -- [ön yükleme (preloading)](https://www.php.net/manual/en/opcache.preloading.php) kullanın - -Daha fazla ayrıntı için, [Symfony'nin bu konuya ayrılmış dokümantasyon girişini](https://symfony.com/doc/current/performance.html) okuyun (çoğu ipucu Symfony kullanmasanız bile faydalıdır). - -## İş Parçacığı Havuzunu Ayırma - -Uygulamaların, yüksek yük altında güvenilmez olma eğiliminde olan veya sürekli olarak 10 saniyeden fazla yanıt veren bir API gibi yavaş harici servislerle etkileşime girmesi yaygındır. Bu gibi durumlarda, iş parçacığı havuzunu özel "yavaş" havuzlara ayırmak faydalı olabilir. Bu, yavaş uç noktaların tüm sunucu kaynaklarını/iş parçacıklarını tüketmesini önler ve bir bağlantı havuzuna benzer şekilde, yavaş uç noktaya giden isteklerin eşzamanlılığını sınırlar. - -```caddyfile -{ - frankenphp { - max_threads 100 # tüm çalışanlar tarafından paylaşılan maksimum 100 iş parçacığı - } -} - -example.com { - php_server { - root /app/public # uygulamanızın kökü - worker index.php { - match /slow-endpoint/* # /slow-endpoint/* yoluyla gelen tüm istekler bu iş parçacığı havuzu tarafından işlenir - num 10 # /slow-endpoint/* ile eşleşen istekler için minimum 10 iş parçacığı - } - worker index.php { - match * # diğer tüm istekler ayrı ayrı işlenir - num 20 # yavaş uç noktalar asılı kalmaya başlasa bile diğer istekler için minimum 20 iş parçacığı - } - } -} -``` - -Genellikle, çok yavaş uç noktaları, mesaj kuyrukları gibi ilgili mekanizmalar kullanarak eşzamansız olarak ele almak da tavsiye edilir. diff --git a/docs/tr/production.md b/docs/tr/production.md index 5ba94e7507..92f1d0096c 100644 --- a/docs/tr/production.md +++ b/docs/tr/production.md @@ -1,10 +1,10 @@ # Production Ortamına Dağıtım -Bu eğitimde, Docker Compose kullanarak bir PHP uygulamasını tek bir sunucuya nasıl dağıtacağımızı öğreneceğiz. +Bu dokümanda, Docker Compose kullanarak bir PHP uygulamasını tek bir sunucuya nasıl dağıtacağımızı öğreneceğiz. -Symfony kullanıyorsanız, Symfony Docker projesinin (FrankenPHP kullanan) "[Production ortamına dağıtım](https://github.com/dunglas/symfony-docker/blob/main/docs/production.md)" belgesini okumayı tercih edebilirsiniz. +Symfony kullanıyorsanız, Symfony Docker projesinin (FrankenPHP kullanan) "[Production ortamına dağıtım](https://github.com/dunglas/symfony-docker/blob/main/docs/production.md)" dokümanını okumayı tercih edebilirsiniz. -API Platform kullanıyorsanız (o da FrankenPHP kullanır), [çerçevenin dağıtım belgesine](https://api-platform.com/docs/deployment/) başvurabilirsiniz. +API Platform (FrankenPHP de kullanır) tercih ediyorsanız, [çerçevenin dağıtım dokümanına](https://api-platform.com/docs/deployment/) bakabilirsiniz. ## Uygulamanızı Hazırlama @@ -18,9 +18,6 @@ ENV SERVER_NAME=your-domain-name.example.com # HTTPS'yi devre dışı bırakmak istiyorsanız, bunun yerine bu değeri kullanın: #ENV SERVER_NAME=:80 -# Projeniz "public" dizinini web kökü olarak kullanmıyorsa, bunu burada ayarlayabilirsiniz: -# ENV SERVER_ROOT=web/ - # PHP production ayarlarını etkinleştirin RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini" @@ -31,7 +28,7 @@ COPY . /app/public ``` Daha fazla ayrıntı ve seçenek için "[Özel Docker İmajı Oluşturma](docker.md)" bölümüne bakın, -ve yapılandırmayı nasıl özelleştireceğinizi, PHP eklentilerini ve Caddy modüllerini nasıl kuracağınızı öğrenmek için. +ve yapılandırmayı nasıl özelleştireceğinizi öğrenmek için PHP eklentilerini ve Caddy modüllerini yükleyin. Projeniz Composer kullanıyorsa, Docker imajına dahil ettiğinizden ve bağımlılıklarınızı yüklediğinizden emin olun. @@ -51,7 +48,7 @@ services: - caddy_data:/data - caddy_config:/config -# Caddy sertifikaları ve yapılandırması için gereken birimler +# Caddy sertifikaları ve yapılandırması için gereken yığınlar (volumes) volumes: caddy_data: caddy_config: @@ -60,17 +57,17 @@ volumes: > [!NOTE] > > Önceki örnekler production kullanımı için tasarlanmıştır. -> Geliştirme aşamasında, bir birim (volume), farklı bir PHP yapılandırması ve `SERVER_NAME` ortam değişkeni için farklı bir değer kullanmak isteyebilirsiniz. +> Geliştirme aşamasında, bir yığın (volume), farklı bir PHP yapılandırması ve `SERVER_NAME` ortam değişkeni için farklı bir değer kullanmak isteyebilirsiniz. > -> Çok aşamalı imajlar, Composer, ekstra PHP eklentileri vb. kullanan daha gelişmiş bir örnek için [Symfony Docker](https://github.com/dunglas/symfony-docker) projesine (FrankenPHP kullanır) bir göz atın. +> (FrankenPHP kullanan) çok aşamalı Composer, ekstra PHP eklentileri vb. içeren imajlara başvuran daha gelişmiş bir örnek için [Symfony Docker](https://github.com/dunglas/symfony-docker) projesine bir göz atın. Son olarak, eğer Git kullanıyorsanız, bu dosyaları commit edin ve push edin. ## Sunucu Hazırlama Uygulamanızı production ortamına dağıtmak için bir sunucuya ihtiyacınız vardır. -Bu eğitimde, DigitalOcean tarafından sağlanan bir sanal makine kullanacağız, ancak herhangi bir Linux sunucusu çalışabilir. -Docker yüklü bir Linux sunucunuz varsa, doğrudan [bir sonraki bölüme](#configuring-a-domain-name) geçebilirsiniz. +Bu dokümanda, DigitalOcean tarafından sağlanan bir sanal makine kullanacağız, ancak herhangi bir Linux sunucusu çalışabilir. +Docker yüklü bir Linux sunucunuz varsa, doğrudan [bir sonraki bölüme](#alan-adı-yapılandırma) geçebilirsiniz. Aksi takdirde, 200 $ ücretsiz kredi almak için [bu ortaklık bağlantısını](https://m.do.co/c/5d8aabe3ab80) kullanın, bir hesap oluşturun ve ardından "Create a Droplet" seçeneğine tıklayın. Ardından, "Bir imaj seçin" bölümünün altındaki "Marketplace" sekmesine tıklayın ve "Docker" adlı uygulamayı bulun. @@ -79,10 +76,10 @@ Bu, Docker ve Docker Compose'un en son sürümlerinin zaten yüklü olduğu bir Test amaçlı kullanım için en ucuz planlar yeterli olacaktır. Gerçek production kullanımı için, muhtemelen ihtiyaçlarınıza uyacak şekilde "genel amaçlı" bölümünden bir plan seçmek isteyeceksiniz. -![DigitalOcean'da Docker ile FrankenPHP Dağıtımı](digitalocean-droplet.png) +![Docker ile DigitalOcean FrankenPHP](../digitalocean-droplet.png) Diğer ayarlar için varsayılanları koruyabilir veya ihtiyaçlarınıza göre değiştirebilirsiniz. -SSH anahtarınızı eklemeyi veya bir parola oluşturmayı unutmayın, ardından "Finalize and create" düğmesine basın. +SSH anahtarınızı eklemeyi veya bir parola oluşturmayı unutmayın, ardından "Sonlandır ve oluştur" düğmesine basın. Ardından, Droplet'iniz hazırlanırken birkaç saniye bekleyin. Droplet'iniz hazır olduğunda, bağlanmak için SSH kullanın: @@ -104,11 +101,11 @@ your-domain-name.example.com. IN A 207.154.233.113 DigitalOcean Alan Adları hizmetiyle ilgili örnek ("Networking" > "Domains"): -![DigitalOcean'da DNS Yapılandırma](digitalocean-dns.png) +![DigitalOcean'da DNS Yapılandırma](../digitalocean-dns.png) > [!NOTE] > -> FrankenPHP tarafından varsayılan olarak otomatik olarak TLS sertifikası oluşturmak için kullanılan hizmet olan Let's Encrypt, sadece IP adreslerinin kullanılmasını desteklemez. Let's Encrypt'i kullanmak için alan adı kullanmak zorunludur. +> FrankenPHP tarafından varsayılan olarak otomatik olarak TLS sertifikası oluşturmak için kullanılan hizmet olan Let's Encrypt, direkt IP adreslerinin kullanılmasını desteklemez. Let's Encrypt'i kullanmak için alan adı kullanmak zorunludur. ## Dağıtım @@ -122,13 +119,13 @@ Git ile örnek: git clone git@github.com:/.git ``` -Projenizi içeren dizine gidin (``) ve uygulamayı production modunda başlatın: +Projenizi içeren dizine gidin (``) ve uygulamayı production modunda başlatın: ```console -docker compose up --wait +docker compose up -d --wait ``` -Sunucunuz hazır ve çalışıyor, ve sizin için otomatik olarak bir HTTPS sertifikası oluşturuldu. +Sunucunuz hazır ve çalışıyor. Sizin için otomatik olarak bir HTTPS sertifikası oluşturuldu. `https://your-domain-name.example.com` adresine gidin ve keyfini çıkarın! > [!CAUTION] @@ -139,4 +136,4 @@ Sunucunuz hazır ve çalışıyor, ve sizin için otomatik olarak bir HTTPS sert Uygulamanızı bir makine kümesine dağıtmak istiyorsanız, [Docker Swarm](https://docs.docker.com/engine/swarm/stack-deploy/) kullanabilirsiniz, sağlanan Compose dosyaları ile uyumludur. -Kubernetes üzerinde dağıtım yapmak için, FrankenPHP kullanan [API Platform ile sunulan Helm grafiğine](https://api-platform.com/docs/deployment/kubernetes/) göz atın. +Kubernetes üzerinde dağıtım yapmak için FrankenPHP kullanan [API Platformu ile sağlanan Helm grafiğine](https://api-platform.com/docs/deployment/kubernetes/) göz atın. diff --git a/docs/tr/static.md b/docs/tr/static.md index 4ae7abdda6..522cd70d62 100644 --- a/docs/tr/static.md +++ b/docs/tr/static.md @@ -1,62 +1,35 @@ # Statik Yapı Oluşturun PHP kütüphanesinin yerel kurulumunu kullanmak yerine, -harika [static-php-cli projesi](https://github.com/crazywhalecc/static-php-cli) sayesinde FrankenPHP'nin statik veya çoğunlukla statik bir yapısını oluşturmak mümkündür (adına rağmen, bu proje sadece CLI'yı değil, tüm SAPI'leri destekler). +harika [static-php-cli projesi](https://github.com/crazywhalecc/static-php-cli) sayesinde FrankenPHP'nin statik bir yapısını oluşturmak mümkündür (adına rağmen, bu proje sadece CLI'yi değil, tüm SAPI'leri destekler). Bu yöntemle, tek, taşınabilir bir ikili PHP yorumlayıcısını, Caddy web sunucusunu ve FrankenPHP'yi içerecektir! -Tamamen statik yerel yürütülebilir dosyalar hiçbir bağımlılık gerektirmez ve hatta [`scratch` Docker imajı](https://docs.docker.com/build/building/base-images/#create-a-minimal-base-image-using-scratch) üzerinde bile çalıştırılabilir. Ancak, dinamik PHP eklentilerini (Xdebug gibi) yükleyemezler ve musl libc kullandıkları için bazı sınırlamalara sahiptirler. - -Çoğunlukla statik ikililer yalnızca `glibc` gerektirir ve dinamik eklentileri yükleyebilir. - -Mümkün olduğunda, glibc tabanlı, çoğunlukla statik yapıları kullanmanızı öneririz. - -FrankenPHP ayrıca [PHP uygulamasının statik ikiliye gömülmesini](embed.md) destekler. +FrankenPHP ayrıca [PHP uygulamasının statik binary gömülmesini](embed.md) destekler. ## Linux -Statik Linux ikilileri oluşturmak için Docker imajları sağlıyoruz: - -### musl Tabanlı, Tamamen Statik Yapı - -Hiçbir bağımlılık olmadan herhangi bir Linux dağıtımında çalışan ancak eklentilerin dinamik yüklenmesini desteklemeyen tamamen statik bir ikili için: - -```console -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 -``` - -Yoğun eşzamanlı senaryolarda daha iyi performans için [mimalloc](https://github.com/microsoft/mimalloc) ayırıcısını kullanmayı düşünebilirsiniz. +Linux statik binary dosyası oluşturmak için bir Docker imajı sağlıyoruz: ```console -docker buildx bake --load --set static-builder-musl.args.MIMALLOC=1 static-builder-musl +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 ``` -### glibc Tabanlı, Çoğunlukla Statik Yapı (Dinamik Eklenti Desteği ile) +Elde edilen statik binary `frankenphp` olarak adlandırılır ve geçerli dizinde kullanılabilir. -Seçilen eklentiler statik olarak derlenmiş olsa da PHP eklentilerini dinamik olarak yüklemeyi destekleyen bir ikili için: - -```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 -``` - -Bu ikili tüm glibc 2.17 ve üzeri sürümlerini destekler ancak musl tabanlı sistemlerde (Alpine Linux gibi) çalışmaz. - -Elde edilen çoğunlukla statik (glibc hariç) ikili `frankenphp` olarak adlandırılır ve geçerli dizinde mevcuttur. - -Statik ikiliyi Docker olmadan oluşturmak istiyorsanız, Linux için de çalışan macOS talimatlarına bir göz atın. +Statik binary dosyasını Docker olmadan oluşturmak istiyorsanız, Linux için de çalışan macOS talimatlarına bir göz atın. ### Özel Eklentiler -Varsayılan olarak, en popüler PHP eklentileri derlenir. +Varsayılan olarak, en popüler PHP eklentileri zaten derlenir. -İkilinin boyutunu küçültmek ve saldırı yüzeyini azaltmak için, `PHP_EXTENSIONS` Docker ARG'sini kullanarak derlenecek eklentilerin listesini seçebilirsiniz. +Binary dosyanın boyutunu küçültmek ve saldırı yüzeyini azaltmak için `PHP_EXTENSIONS` Docker ARG'sini kullanarak derlenecek eklentilerin listesini seçebilirsiniz. Örneğin, yalnızca `opcache` eklentisini derlemek için aşağıdaki komutu çalıştırın: ```console -docker buildx bake --load --set static-builder-musl.args.PHP_EXTENSIONS=opcache,pdo_sqlite static-builder-musl +docker buildx bake --load --set static-builder.args.PHP_EXTENSIONS=opcache,pdo_sqlite static-builder # ... ``` @@ -65,20 +38,20 @@ Etkinleştirdiğiniz eklentilere ek işlevler sağlayan kütüphaneler eklemek i ```console docker buildx bake \ --load \ - --set static-builder-musl.args.PHP_EXTENSIONS=gd \ - --set static-builder-musl.args.PHP_EXTENSION_LIBS=libjpeg,libwebp \ - static-builder-musl + --set static-builder.args.PHP_EXTENSIONS=gd \ + --set static-builder.args.PHP_EXTENSION_LIBS=libjpeg,libwebp \ + static-builder ``` ### Ekstra Caddy Modülleri -Ekstra Caddy modülleri eklemek veya [xcaddy](https://github.com/caddyserver/xcaddy)'ye diğer argümanları iletmek için `XCADDY_ARGS` Docker ARG'sini kullanın: +Ekstra Caddy modülleri eklemek veya [xcaddy](https://github.com/caddyserver/xcaddy) adresine başka argümanlar iletmek için `XCADDY_ARGS` Docker ARG'sini kullanın: ```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 + --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 ``` Bu örnekte, Caddy için [Souin](https://souin.io) HTTP önbellek modülünün yanı sıra [cbrotli](https://github.com/dunglas/caddy-cbrotli), [Mercure](https://mercure.rocks) ve [Vulcain](https://vulcain.rocks) modüllerini ekliyoruz. @@ -88,20 +61,20 @@ Bu örnekte, Caddy için [Souin](https://souin.io) HTTP önbellek modülünün y > cbrotli, Mercure ve Vulcain modülleri, `XCADDY_ARGS` boşsa veya ayarlanmamışsa varsayılan olarak dahil edilir. > Eğer `XCADDY_ARGS` değerini özelleştirirseniz, dahil edilmelerini istiyorsanız bunları açıkça dahil etmelisiniz. -Derlemeyi nasıl [özelleştireceğinize](#customizing-the-build) de bakın. +Derlemeyi nasıl [özelleştireceğinize](#yapıyı-özelleştirme) de bakın. ### GitHub Token GitHub API kullanım limitine ulaşırsanız, `GITHUB_TOKEN` adlı bir ortam değişkeninde bir GitHub Personal Access Token ayarlayın: ```console -GITHUB_TOKEN="xxx" docker --load buildx bake static-builder-musl +GITHUB_TOKEN="xxx" docker --load buildx bake static-builder # ... ``` ## macOS -macOS için statik bir ikili oluşturmak için aşağıdaki betiği çalıştırın ([Homebrew](https://brew.sh/) yüklü olmalıdır): +macOS için statik bir binary oluşturmak için aşağıdaki betiği çalıştırın ([Homebrew](https://brew.sh/) yüklü olmalıdır): ```console git clone https://github.com/php/frankenphp @@ -109,50 +82,19 @@ cd frankenphp ./build-static.sh ``` -Not: Bu betik Linux'ta (ve muhtemelen diğer Unix'lerde) da çalışır ve sağladığımız Docker imajları tarafından dahili olarak kullanılır. +Not: Bu betik Linux'ta (ve muhtemelen diğer Unix'lerde) da çalışır ve sağladığımız Docker tabanlı statik derleyici tarafından dahili olarak kullanılır. ## Yapıyı Özelleştirme -Aşağıdaki ortam değişkenleri `docker build` ve `build-static.sh` betiğine aktarılabilir -statik yapıyı özelleştirmek için: +Aşağıdaki ortam değişkenleri `docker build` ve `build-static.sh` dosyalarına aktarılabilir +statik derlemeyi özelleştirmek için betik: - `FRANKENPHP_VERSION`: kullanılacak FrankenPHP sürümü - `PHP_VERSION`: kullanılacak PHP sürümü - `PHP_EXTENSIONS`: oluşturulacak PHP eklentileri ([desteklenen eklentiler listesi](https://static-php.dev/en/guide/extensions.html)) - `PHP_EXTENSION_LIBS`: eklentilere özellikler ekleyen oluşturulacak ekstra kütüphaneler -- `XCADDY_ARGS`: [xcaddy](https://github.com/caddyserver/xcaddy)'ye iletilecek argümanlar, örneğin ekstra Caddy modülleri eklemek için -- `EMBED`: ikili dosyaya gömülecek PHP uygulamasının yolu +- `XCADDY_ARGS`: [xcaddy](https://github.com/caddyserver/xcaddy) adresine iletilecek argümanlar, örneğin ekstra Caddy modülleri eklemek için +- `EMBED`: binary dosyaya gömülecek PHP uygulamasının yolu - `CLEAN`: ayarlandığında, libphp ve tüm bağımlılıkları sıfırdan oluşturulur (önbellek yok) -- `NO_COMPRESS`: UPX kullanarak ortaya çıkan ikiliyi sıkıştırma -- `DEBUG_SYMBOLS`: ayarlandığında, hata ayıklama sembolleri ayıklanmayacak ve ikili dosyaya eklenecektir -- `MIMALLOC`: (deneysel, yalnızca Linux) musl'un mallocng'sini [mimalloc](https://github.com/microsoft/mimalloc) ile daha iyi performans için değiştirir. Bunu yalnızca musl hedefli derlemeler için kullanmanızı öneririz, glibc için bu seçeneği devre dışı bırakmayı ve ikilinizi çalıştırırken [`LD_PRELOAD`](https://microsoft.github.io/mimalloc/overrides.html) kullanmayı tercih edin. -- `RELEASE`: (yalnızca bakımcılar) ayarlandığında, ortaya çıkan ikili dosya GitHub'a yüklenecektir - -## Eklentiler - -glibc veya macOS tabanlı ikililerle, PHP eklentilerini dinamik olarak yükleyebilirsiniz. Ancak, bu eklentilerin ZTS desteğiyle derlenmesi gerekecektir. Çoğu paket yöneticisi şu anda eklentilerinin ZTS sürümlerini sunmadığından, bunları kendiniz derlemeniz gerekecektir. - -Bunun için `static-builder-gnu` Docker kapsayıcısını derleyebilir ve çalıştırabilir, içine uzaktan bağlanabilir ve eklentileri `./configure --with-php-config=/go/src/app/dist/static-php-cli/buildroot/bin/php-config` ile derleyebilirsiniz. - -[Xdebug eklentisi](https://xdebug.org) için örnek adımlar: - -```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 -``` - -Bu, mevcut dizinde `frankenphp` ve `xdebug-zts.so`'yu oluşturmuş olacaktır. `xdebug-zts.so`'yu uzantı dizininize taşırsanız, php.ini dosyanıza `zend_extension=xdebug-zts.so` ekler ve FrankenPHP'yi çalıştırırsanız, Xdebug'ı yükleyecektir. +- `DEBUG_SYMBOLS`: ayarlandığında, hata ayıklama sembolleri ayıklanmayacak ve binary dosyaya eklenecektir +- `RELEASE`: (yalnızca bakımcılar) ayarlandığında, ortaya çıkan binary dosya GitHub'a yüklenecektir diff --git a/docs/tr/wordpress.md b/docs/tr/wordpress.md deleted file mode 100644 index 99e9e4252b..0000000000 --- a/docs/tr/wordpress.md +++ /dev/null @@ -1,59 +0,0 @@ -# WordPress - -[WordPress](https://wordpress.org/)'i FrankenPHP ile çalıştırarak otomatik HTTPS, HTTP/3 ve Zstandard sıkıştırma özelliklerine sahip modern, yüksek performanslı bir yığının keyfini çıkarın. - -## Minimal Kurulum - -1. [WordPress'i İndirin](https://wordpress.org/download/) -2. ZIP arşivini çıkarın ve çıkarılan dizinde bir terminal açın -3. Çalıştırın: - - ```console - frankenphp php-server - ``` - -4. `http://localhost/wp-admin/` adresine gidin ve kurulum talimatlarını izleyin -5. Keyfini çıkarın! - -Üretime hazır bir kurulum için, `frankenphp run` komutunu aşağıdaki gibi bir `Caddyfile` ile kullanmayı tercih edin: - -```caddyfile -example.com - -php_server -encode zstd br gzip -log -``` - -## Anında Yenileme - -WordPress ile [anında yenileme](hot-reload.md) özelliğini kullanmak için, [Mercure](mercure.md)'u etkinleştirin ve `Caddyfile` dosyanızdaki `php_server` direktifine `hot_reload` alt direktifini ekleyin: - -```caddyfile -localhost - -mercure { - anonymous -} - -php_server { - hot_reload -} -``` - -Ardından, JavaScript kütüphanelerini WordPress temanızın `functions.php` dosyasına yüklemek için gerekli kodu ekleyin: - -```php -function hot_reload() { - ?> - - - - - - [!TIP] -> Aşağıdaki bölüm yalnızca Symfony 7.4 öncesi için gereklidir; Symfony 7.4 ile FrankenPHP worker modu için yerel destek sunulmuştur. +## Symfony Çalışma Zamanı FrankenPHP'nin worker modu [Symfony Runtime Component](https://symfony.com/doc/current/components/runtime.html) tarafından desteklenmektedir. Herhangi bir Symfony uygulamasını bir worker'da başlatmak için [PHP Runtime](https://github.com/php-runtime/runtime)'ın FrankenPHP paketini yükleyin: @@ -66,13 +54,13 @@ Bkz. [ilgili doküman](laravel.md#laravel-octane). ## Özel Uygulamalar -Aşağıdaki örnek, üçüncü taraf bir kütüphaneye güvenmeden kendi worker betiğinizi nasıl oluşturacağınızı göstermektedir: +Aşağıdaki örnek, üçüncü taraf bir kütüphaneye güvenmeden kendi çalışan kodunuzu nasıl oluşturacağınızı göstermektedir: ```php boot(); // Daha iyi performans için döngü dışında işleyici (daha az iş yapıyor) $handler = static function () use ($myApp) { - try { - // Bir istek alındığında çağrılır, - // superglobals, php://input ve benzerleri sıfırlanır - echo $myApp->handle($_GET, $_POST, $_COOKIE, $_FILES, $_SERVER); - } catch (\Throwable $exception) { - // `set_exception_handler` yalnızca worker betiği sona erdiğinde çağrılır, - // bu beklediğiniz gibi olmayabilir, bu yüzden istisnaları burada yakalayın ve ele alın - (new \MyCustomExceptionHandler)->handleException($exception); - } + // Bir istek alındığında çağrılır, + // superglobals, php://input ve benzerleri sıfırlanır + 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); +for ($nbRequests = 0, $running = true; isset($_SERVER['MAX_REQUESTS']) && ($nbRequests < ((int)$_SERVER['MAX_REQUESTS'])) && $running; ++$nbRequests) { + $running = \frankenphp_handle_request($handler); // HTTP yanıtını gönderdikten sonra bir şey yapın $myApp->terminate(); // Bir sayfa oluşturmanın ortasında tetiklenme olasılığını azaltmak için çöp toplayıcıyı çağırın gc_collect_cycles(); - - if (!$keepRunning) break; } // Temizleme $myApp->shutdown(); ``` -Ardından, uygulamanızı başlatın ve worker'ınızı yapılandırmak için `FRANKENPHP_CONFIG` ortam değişkenini kullanın: +Ardından, uygulamanızı başlatın ve çalışanınızı yapılandırmak için `FRANKENPHP_CONFIG` ortam değişkenini kullanın: ```console docker run \ @@ -134,58 +113,12 @@ docker run \ ### Belirli Sayıda İstekten Sonra Worker'ı Yeniden Başlatın -PHP başlangıçta uzun süreli işlemler için tasarlanmadığından, hala bellek sızdıran birçok kütüphane ve eski kod vardır. -Bu tür kodları worker modunda kullanmak için geçici bir çözüm, belirli sayıda isteği işledikten sonra worker betiğini yeniden başlatmaktır: - -Önceki worker kod parçacığı, `MAX_REQUESTS` adlı bir ortam değişkeni ayarlayarak işlenecek maksimum istek sayısını yapılandırmaya izin verir. - -### Worker'ları Manuel Olarak Yeniden Başlatın - -Worker'ları [dosya değişikliklerinde](config.md#watching-for-file-changes) yeniden başlatmak mümkünken, tüm worker'ları -[Caddy yönetici API'si](https://caddyserver.com/docs/api) aracılığıyla sorunsuz bir şekilde yeniden başlatmak da mümkündür. Yönetici API'si -[Caddyfile](config.md#caddyfile-config) dosyanızda etkinleştirilmişse, yeniden başlatma uç noktasına aşağıdaki gibi basit bir POST isteği gönderebilirsiniz: - -```console -curl -X POST http://localhost:2019/frankenphp/workers/restart -``` + -### Worker Hataları - -Bir worker betiği sıfır olmayan bir çıkış koduyla çökerse, FrankenPHP onu üstel geri çekilme (exponential backoff) stratejisiyle yeniden başlatacaktır. -Eğer worker betiği son geri çekilmenin * 2 katından daha uzun süre çalışır durumda kalırsa, -worker betiğini cezalandırmayacak ve tekrar yeniden başlatacaktır. -Ancak, worker betiği kısa bir süre içinde sıfır olmayan bir çıkış koduyla başarısız olmaya devam ederse -(örneğin, bir betikte yazım hatası olması), FrankenPHP `too many consecutive failures` hatasıyla çökecektir. - -Ardışık hata sayısı, [Caddyfile](config.md#caddyfile-config) dosyanızda `max_consecutive_failures` seçeneğiyle yapılandırılabilir: - -```caddyfile -frankenphp { - worker { - # ... - max_consecutive_failures 10 - } -} -``` - -## Süperküresel Değişkenlerin Davranışı - -[PHP süperküresel değişkenleri](https://www.php.net/manual/en/language.variables.superglobals.php) (`$_SERVER`, `$_ENV`, `$_GET`...) -şu şekilde davranır: - -- `frankenphp_handle_request()` fonksiyonunun ilk çağrılmasından önce, süperküresel değişkenler worker betiğinin kendisine bağlı değerleri içerir -- `frankenphp_handle_request()` çağrısı sırasında ve sonrasında, süperküresel değişkenler işlenmiş HTTP isteğinden üretilen değerleri içerir; `frankenphp_handle_request()` fonksiyonunun her çağrısı süperküresel değişkenlerin değerlerini değiştirir - -Geri arama (callback) içinde worker betiğinin süperküresel değişkenlerine erişmek için, bunları kopyalamanız ve kopyayı geri aramanın kapsamına (scope) aktarmanız gerekir: +PHP başlangıçta uzun süreli işlemler için tasarlanmadığından, hala bellek sızdıran birçok kütüphane ve eski kod vardır. -```php - -$handler = static function () use ($workerServer) { - var_dump($_SERVER); // İsteğe bağlı $_SERVER - var_dump($workerServer); // worker betiğinin $_SERVER'ı -}; +Bu tür kodları worker modunda kullanmak için geçici bir çözüm, belirli sayıda isteği işledikten sonra worker betiğini yeniden başlatmaktır: -// ... +Önceki worker kod parçacığı, `MAX_REQUESTS` adlı bir ortam değişkeni ayarlayarak işlenecek maksimum istek sayısını yapılandırmaya izin verir. diff --git a/docs/tr/x-sendfile.md b/docs/tr/x-sendfile.md deleted file mode 100644 index 54526e0496..0000000000 --- a/docs/tr/x-sendfile.md +++ /dev/null @@ -1,69 +0,0 @@ -# Büyük Statik Dosyaları Verimli Bir Şekilde Sunma (X-Sendfile/X-Accel-Redirect) - -Genellikle statik dosyalar doğrudan web sunucusu tarafından sunulabilir, -ancak bazen bunları göndermeden önce bazı PHP kodları çalıştırmak gerekebilir: -erişim kontrolü, istatistikler, özel HTTP başlıkları... - -Maalesef, büyük statik dosyaları sunmak için PHP kullanmak, -web sunucusunu doğrudan kullanmaya kıyasla verimsizdir (bellek aşırı yüklenmesi, performans düşüşü...). - -FrankenPHP, özelleştirilmiş PHP kodu çalıştırıldıktan **sonra** -statik dosyaların web sunucusuna gönderilmesini devretmenizi sağlar. - -Bunu yapmak için, PHP uygulamanızın sunulacak dosyanın yolunu içeren özel bir HTTP başlığı tanımlaması yeterlidir. Gerisini FrankenPHP halleder. - -Bu özellik Apache için **`X-Sendfile`**, NGINX için ise **`X-Accel-Redirect`** olarak bilinir. - -Aşağıdaki örneklerde, projenin belge kökünün `public/` dizini olduğunu -ve `public/` dizininin dışında, `private-files/` adlı bir dizinde depolanan dosyaları sunmak için PHP kullanmak istediğimizi varsayıyoruz. - -## Yapılandırma - -İlk olarak, bu özelliği etkinleştirmek için `Caddyfile` dosyanıza aşağıdaki yapılandırmayı ekleyin: - -```patch - root public/ - # ... - -+ # Symfony, Laravel ve Symfony HttpFoundation bileşenini kullanan diğer projeler için gereklidir -+ 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 -+ -+ # Artırılmış güvenlik için PHP tarafından ayarlanan X-Accel-Redirect başlığını kaldırın -+ header -X-Accel-Redirect -+ -+ file_server -+ } -+ } - - php_server -``` - -## Yalın PHP - -Göreceli dosya yolunu (`private-files/` dizininden) `X-Accel-Redirect` başlığının değeri olarak ayarlayın: - -```php -header('X-Accel-Redirect: file.txt'); -``` - -## Symfony HttpFoundation bileşenini kullanan projeler (Symfony, Laravel, Drupal...) - -Symfony HttpFoundation [bu özelliği yerel olarak destekler](https://symfony.com/doc/current/components/http_foundation.html#serving-files). -Bu, `X-Accel-Redirect` başlığı için doğru değeri otomatik olarak belirleyecek ve yanıta ekleyecektir. - -```php -use Symfony\Component\HttpFoundation\BinaryFileResponse; - -BinaryFileResponse::trustXSendfileTypeHeader(); -$response = new BinaryFileResponse(__DIR__.'/../private-files/file.txt'); - -// ... -``` diff --git a/docs/translate.php b/docs/translate.php index 045fe3393c..d491b58e8c 100644 --- a/docs/translate.php +++ b/docs/translate.php @@ -15,7 +15,7 @@ 'tr' => 'Turkish', ]; -function makeGeminiRequest(string $systemPrompt, string $userPrompt, string $model, string $apiKey): string +function makeGeminiRequest(string $systemPrompt, string $userPrompt, string $model, string $apiKey, int $reties = 2): string { $url = "https://generativelanguage.googleapis.com/v1beta/models/$model:generateContent"; $body = json_encode([ @@ -38,6 +38,11 @@ function makeGeminiRequest(string $systemPrompt, string $userPrompt, string $mod if (!$response || !$generatedDocs) { print_r(error_get_last()); print_r($response); + if ($reties > 0) { + echo "Retrying... ($reties retries left)\n"; + sleep(SLEEP_SECONDS_BETWEEN_REQUESTS); + return makeGeminiRequest($systemPrompt, $userPrompt, $model, $apiKey, $reties - 1); + } exit(1); } From 947a9a5bdfec08261e4f231ccd005bc9aa8a3a16 Mon Sep 17 00:00:00 2001 From: Alliballibaba Date: Sat, 10 Jan 2026 18:49:37 +0100 Subject: [PATCH 7/9] linting. --- .github/workflows/translate.yaml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/translate.yaml b/.github/workflows/translate.yaml index 6d8f91ac6f..8b9d1d575a 100644 --- a/.github/workflows/translate.yaml +++ b/.github/workflows/translate.yaml @@ -22,12 +22,12 @@ jobs: with: fetch-depth: 0 persist-credentials: false + # Check for changed .md files under docs/*.md - id: md_files run: | - # Check for changed .md files under docs/*.md FILES=$(git diff --name-only "${{ github.event.before }}" "${{ github.sha }}" -- 'docs/*.md') FILES=$(echo "$FILES" | xargs -n1 basename | tr '\n' ' ') - [ -z "$FILES" ] && echo "found=false" >> $GITHUB_OUTPUT || echo "found=true" >> "$GITHUB_OUTPUT" + [ -z "$FILES" ] && echo "found=false" >> "$GITHUB_OUTPUT" || echo "found=true" >> "$GITHUB_OUTPUT" echo "files=$FILES" >> "$GITHUB_OUTPUT" - name: Set up PHP if: steps.md_files.outputs.found == 'true' @@ -39,8 +39,7 @@ jobs: env: GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }} run: | - IFS=' ' read -r -a FILES <<< "${{ steps.md_files.outputs.files }}" - php ./docs/translate.php "${FILES[@]}" + php ./docs/translate.php "${{ steps.md_files.outputs.files }}" - name: Run Linter if: steps.md_files.outputs.found == 'true' uses: super-linter/super-linter/slim@v8 From 8bea1084a3e6825a23195e9d9bf199074bae10ce Mon Sep 17 00:00:00 2001 From: Alliballibaba Date: Sat, 10 Jan 2026 19:01:52 +0100 Subject: [PATCH 8/9] linting. --- .github/workflows/translate.yaml | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/.github/workflows/translate.yaml b/.github/workflows/translate.yaml index 8b9d1d575a..a27a8fa8b0 100644 --- a/.github/workflows/translate.yaml +++ b/.github/workflows/translate.yaml @@ -1,4 +1,3 @@ ---- name: Translate Docs concurrency: cancel-in-progress: true @@ -8,7 +7,7 @@ on: branches: - main paths: - - 'docs/*' + - "docs/*" permissions: contents: write pull-requests: write @@ -22,7 +21,6 @@ jobs: with: fetch-depth: 0 persist-credentials: false - # Check for changed .md files under docs/*.md - id: md_files run: | FILES=$(git diff --name-only "${{ github.event.before }}" "${{ github.sha }}" -- 'docs/*.md') @@ -37,9 +35,10 @@ jobs: - name: run translation script if: steps.md_files.outputs.found == 'true' env: - GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }} + GEMINI_API_KEY: '${{ secrets.GEMINI_API_KEY }}' + MD_FILES: '${{ steps.md_files.outputs.files }}' run: | - php ./docs/translate.php "${{ steps.md_files.outputs.files }}" + php ./docs/translate.php "$MD_FILES" - name: Run Linter if: steps.md_files.outputs.found == 'true' uses: super-linter/super-linter/slim@v8 @@ -65,5 +64,5 @@ jobs: Translation updates for: ${{ steps.md_files.outputs.files }}. labels: | translations - bot-generated + bot draft: false From f8db90630145f50c3c94e753651c35535db10e4e Mon Sep 17 00:00:00 2001 From: Alliballibaba Date: Sat, 10 Jan 2026 19:12:59 +0100 Subject: [PATCH 9/9] Suggestions by @dunglas --- .github/workflows/translate.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/translate.yaml b/.github/workflows/translate.yaml index a27a8fa8b0..69a633f056 100644 --- a/.github/workflows/translate.yaml +++ b/.github/workflows/translate.yaml @@ -54,12 +54,12 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: - commit-message: Update translations + title: "docs: update translations" + commit-message: "docs: update translations" committer: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> author: ${{ github.actor }} <${{ github.actor_id }}+${{ github.actor }}@users.noreply.github.com> branch: translations/${{ github.run_id }} delete-branch: true - title: '[Translations] Update non-english docs' body: | Translation updates for: ${{ steps.md_files.outputs.files }}. labels: |