3: 运行时配置

data/app.json 描述了 build,即被转译的内容。本章涵盖 runtime,即每个请求发生的事情。该配置位于 www/app.php(对于阶段/发布,位于 release/www/app.php)作为 phlo_app(...) 的参数。

<?php
require('/opt/phlo/phlo.php');
phlo_app (
	id:        'Example',
	host:      'dev.example.nl',
	auth:      true,
	build:     true,
	debug:     true,
	app:       '/var/www/example.nl/',
)

每个键都是一个 命名参数,同时也成为一个 PHP 常量,可以在你的应用程序中作为裸字(hostdatacomposer 等)随处使用。你自定义的键也是如此。

3.1: Identity and host

Key Default Purpose
id none Free-form name for the app, used by the Phlo Control Center, the Phlo Dashboard and logs
host null Canonical host used by the app for URL generation, credentials and diagnostics. Bind and validate incoming hosts in the web server; Phlo does not reject a request by its Host header

3.2: 模式: `build`, `debug`, `auth`, `thread`

默认 效果
build false 启用 build/lint/reflect CLI,并让 Phlo 在每个请求中检测和重建更改的源
debug false 加载 debug.php,激活 debug() / dx() / 调试助手,提供完整的堆栈跟踪,而不是通用的 500 错误
auth false 使用 data/auth.ini 中的凭据进行全站 HTTP 基本认证
thread false 工作模式(见 3.7),true = 无限,整数 = 每个工作者的请求数量

请勿组合build: truethread: true。Build 在请求之间写入文件;在长时间运行的工作者中这是不安全的。如果两者都开启,Phlo 会抛出运行时错误。

要求auth: true 需要 build: true。如果在没有 build 的情况下设置了 auth,Phlo 在启动时会出错。(这是一个实现细节:全站认证处理程序在构建层中提供。为了保护非构建阶段的主机,请在 Phlo 前面的 Web 服务器中放置 HTTP 基本认证。)

控制中心路径:当 build: truedebug: true 时,Phlo 控制中心会自动挂载在 /phlo,无需配置。使用可选的 control: 键将其挂载到不同的路径(control: 'admin'/admin 提供服务)或设置 control: false 以关闭它。在非构建+调试状态下,它始终关闭。

3.3: Paths

Only app is required. The rest falls back to subdirectories of app:

Key Default Intended for
app (required) App root
data <app>/data/ Config (app.json, auth.ini), credentials, runtime state
php <app>/php/ Generated PHP, only change this when release output lives elsewhere
www <app>/www/ Web root

Your release entrypoint typically sets app: '<app>/release/' and php: '<app>/release/' so release output is served without build mode.

3.3.1 Your own path keys

Every key you pass becomes a constant, so an app declares its own directories the same way:

phlo_app (
	app:      '/srv/site/release/',
	php:      '/srv/site/release/',
	guide:    '/srv/site/guide/',
	snippets: '/srv/site/snippets/',
);

guide.'1. intro.md' and snippets.'01-intro.txt' then resolve wherever the entrypoint points them.

Lesson. Content read at runtime needs a key of its own; do not build its path from app. The assumption "app is my project directory" holds in development and breaks on release, because there app points at release/, which contains generated PHP and nothing else. Markdown, code samples and fixtures loaded with file_get_contents() are then silently missing: the route still answers 200 and the page renders with an empty section, so nothing fails loudly. Give each such directory a key, and the same source is served in both modes.

3.4: 控制中心和 WebSocket

默认值 效果
control 'phlo'build+debug 一起使用,其他情况下为 false 控制 UI 所在的 URL 前缀。例如 'beheer'/beheerfalse = 关闭
daemon null 此应用的 Phlo Daemon 端口。成为常量 daemon,由 wsCast() 和运行时助手使用

控制 UI 需要 build: true。有关 Phlo Daemon 设置,请参见 Daemon 和 WebSocket 章节。

3.5: Composer 自动加载

想要使用 vendor/ 中的 PHP 包吗?请提供路径:

phlo_app (
	composer: '/var/www/example.nl/',
	...
)

Phlo 然后注册一个 lazy autoloader,仅在需要解析未知类时加载 <composer>/vendor/autoload.php。如果您从未接触 Composer 包,则没有冷启动成本;一旦您使用它,就会获得完整的 Composer 自动加载。

约定: composer: '<app>/data/'composer.jsonvendor/ 然后位于 data/ 中,位于 webroot 之外)。

3.6: 跟踪和 CLI

默认值 效果
trace false 启用跟踪模式,参见 Trace 章节
cli 'php-zts'(如果是 ZTS)或 'php' Phlo 用于子进程(构建、任务、websocket)的 PHP 二进制文件路径。如果您的系统有非标准的 PHP 二进制文件,请覆盖此项

cli 在 v4 中是一个字符串(路径),而不是 v1 中的布尔值。

3.7: Worker mode rules

thread: true puts Phlo in long-running FrankenPHP worker mode. The runtime stays in memory between requests. Three rules you need to know:

1. No die() or exit() in the HTTP path. Both kill the entire worker. view(), apply() and location() prepare or render a response but do not terminate PHP execution, so return them from a route guard or otherwise let the routine end immediately afterwards.

2. No request state in static properties. PHP static properties and function-local statics survive between requests. Phlo clears its computed-static cache (obj::$classProps) during the worker reset, but request and user state still belongs on an instance with an explicit lifetime or on %req, never in a PHP static.

3. Mark long-lived objects with $objPers = true. By default Phlo clears its instance map between requests. For objects you explicitly want to reuse (DB connection, prepared statements), set $this->objPers = true so the cleanup leaves them alone.

3.8: 自定义键:您自己的常量

您传递给 phlo_app() 的每个额外命名参数都会自动成为一个 PHP 常量。这就是声明应用程序范围内路径或功能标志的方式:

phlo_app (
	app:      '/var/www/example.nl/',
	langs:    '/var/www/example.nl/langs/',
	files:    '/var/www/example.nl/files/',
	uploads:  '/var/www/example.nl/data/uploads/',
)

可以直接在 .phlo 中使用:

$dict = parse_ini_file(langs.'en.ini')
$path = files.'avatars/'.$user->id.'.jpg'
$url  = uploads

reflect::runtime 显示所有定义的常量。这对于在不打开 www/app.php 的情况下发现应用程序提供的内容非常有用。

3.9: 示例:开发和发布并排显示

phlo_app (
	id:        'Example',
	host:      'dev.example.nl',
	auth:      true,
	build:     true,
	debug:     true,
	trace:     true,
	app:       '/var/www/example.nl/',
	composer:  '/var/www/example.nl/data/',
	daemon:    3001,
	langs:     '/var/www/example.nl/langs/',
)
phlo_app (
	id:        'Example',
	host:      'example.nl',
	thread:    true,
	app:       '/var/www/example.nl/release/',
	php:       '/var/www/example.nl/release/',
	data:      '/var/www/example.nl/data/',
	composer:  '/var/www/example.nl/data/',
	daemon:    3001,
	langs:     '/var/www/example.nl/langs/',
)

开发者已经构建、调试、认证了控制中心和追踪。发布具有工作模式(thread),并将 webroot/php 输出指向 release/datacomposerlangs 保持不变,这是共享状态。

3.10: 请求和响应对象

两个运行时对象携带每个请求。%req(读取)和%res(写入)始终可用。

%req 计算属性:

属性 包含内容
%req->method HTTP 动词,转换为大写
%req->path 请求路径,不带前导斜杠
%req->part($i) 按索引获取路径段
%req->query 解析后的查询字符串数组
%req->async 当请求来自 phlo.js(SPA 导航,异步表单)时为 true
%req->cli 当从命令行运行时为 true
%req->secure, %req->scheme, %req->base, %req->url URL 部分,计算一次
%req->referer, %req->acceptLanguage 常见头部,已标准化

%res 表面:

成员 功能
%res->header($key, $value) 排队响应头(在渲染时发送)
%res->type 响应的 Content-Type
%res->text($body) / %res->json(...) / %res->xml($body) 设置主体(以及 json/xml 的类型);可链式调用
%res->render($code = null) 发送状态、头部和主体;标记响应完成
%res->streaming true 切换 apply() 为立即按命令刷新(见 WebSocket 章节)
%res->status, %res->done 状态码;输出是否已发送

output($content, $filename = null, $attachment = null, $file = null, $code = null, $type = null) 是用于文件、二进制大对象和带状态的 JSON 的响应函数:它提供一个文件(按名称的 mime,附加的 attachment 可选),或者当 $content 是数组时返回 JSON(output(['id' => $id], code: 201)output(['error' => 'not found'], code: 404));type 会覆盖预编码字符串主体的内容类型。view()/apply()/output()/error()/location() 是应用代码使用的响应函数;上面的 %res->json/text/xml/render 成员是它们构建的低级原语,用于少见的手动组装响应(自定义头部,特殊内容类型)。不要将它们包装在每个应用的 jsonOut()/respond() 辅助函数中。

课程。 die($content) 看起来像是“发送响应”,但它绕过了 render():排队的头部(包括 Content-Type)永远不会离开服务器,并且在工作模式下 die() 会终止整个工作进程。这个网站以 text/html 的形式提供其机器可读的端点已经好几周了。始终以 output(...)view()apply()location() 或显式的 %res->...->render() 结束。

运行时错误记录在 data/errors.json 中,以稳定的 8 字符引用 ID(主机、位置和去掉路径的消息的哈希)为键,这样相同的错误会去重为一个条目,并带有计数器和最后发生的时间戳。每个条目还记录了源映射的 .phlo 文件和行号、主机和请求路径;保留最新的 200 条。可以通过 reflect::errors [limit] 或在 Phlo Control Center 中读取它们。

该引用 ID 是用户与您之间的安全句柄:它会显示在生产错误页面上(Reference: a3f9c1d2),并在异步/JSON 响应的 error 负载中传递,因此用户可以引用它,您可以在 errors.json 中查找完整条目。生产页面永远不会打印消息或追踪;这些仅限于 debug

应用可以通过在其 app 类上声明一个静态的 errorPage(int $code, string $id, ?string $msg): string 来渲染自己的品牌错误页面。引擎会静态调用它,传入 HTTP 代码、模糊的引用 ID,以及真实的错误消息 仅在 debug(否则为 null),因此生产页面无法泄露内部信息,而调试环境可以在样式页面上显示消息。如果钩子抛出异常或返回空,引擎会回退到自己的页面:在 debug 下是诊断信息,其他情况下是最简页面。

最近更新于 2026年8月23日

我们使用必要的cookie来使该网站正常工作。在您的许可下,我们还使用分析工具来改善网站。