12: 高级

Phlo 故意保持模块化。您可以保持应用程序小巧,仅激活所需的资源,或组合多个源路径和资源组。

12.1: 应用程序代码和运行时资源

应用特定的代码应放在应用本身中。不要将其放在 /srv/phlo/resources/ 中。

/srv/phlo/resources/ 是 Phlo 运行时目录:可以在多个应用之间共享的框架范围资源,并且与运行时一起有意维护。只有通用的、稳定的代码才能放在那里。

如果您想在应用之间共享代码,请首先创建一个明确的共享模块或应用库路径,并指定明确的所有者。只有在代码确实是框架功能时,才可以将其提升到 Phlo 运行时目录。

运行时资源可以提供对象、函数、样式或脚本。文件顶部的元数据有助于 Phlo Control Center 和手册:

@ summary: Send app notifications
@ package: notifications
@ frontend: false
@ backend: true

method send($message){
    return HTTP(%creds->notify->url, POST: ['message' => $message])
}

12.2: 多个源路径

保持默认设置简单:应用程序源代码位于应用程序路径中。仅在代码库确实需要共享时才添加额外路径。

路径选择应保持可预测:

12.3: 与现有的 PHP 集成

在现有的 PHP 旁边使用 Phlo,通过加载运行时并使 Phlo 入口点仅负责应用处理的 routes。现有的静态文件仍然由 webserver 直接提供服务。

<?php
require('/srv/phlo/phlo.php');
phlo_app (
    id: 'Legacy',
    host: 'dev.legacy.test',
    build: true,
    debug: true,
    app: '/srv/legacy/',
);

12.4: 安全与访客

对于公共网站,通常的基线是:

{
    "resources": [
        "cookies",
        "security/security",
        "security/token",
        "session",
        "useragent",
        "visitors",
        "phlo.async",
        "DOM/form"
    ]
}

对于本地开发,您可以排除跟踪:

{
    "exclude": [
        "visitors",
        "useragent",
        "wsCast"
    ]
}

12.5: Cookiewall:GDPR 同意

DOM/cookiewall 是一个内置的、微妙的同意横幅。通过以下 3 个步骤激活它:

1. 在 data/app.json 中添加资源

{ "resources": [..., "DOM/cookiewall"] }

2. 在您的布局中添加横幅

view layout:
<body>
    {{ %cookiewall->banner }}
    <main>...</main>
</body>

当访客尚未做出选择时,横幅才会出现。提供两个按钮:“仅限必要”和“接受”。选择存储在一个名为 cookieChoice 的 cookie 中('essential''all'),有效期为 1 年。

3. 在跟踪周围添加保护

<if %cookiewall->canTrack>
    <script src="https://analytics.example.com/script.js"></script>
</if>
方法 返回值
%cookiewall->hasChosen() 一旦访客做出选择,返回 true
%cookiewall->canTrack 仅在选择为 'all' 时返回 true
%cookiewall->canAnalytics canTrack 的别名,在分析桥接中语义上有用
%cookiewall->choice 'essential' / 'all' / null

多语言变体:DOM/cookiewall.translated(使用 {nl: ...} 简写表示横幅文本)。

12.6: 工作模式

默认情况下,Phlo 每个请求运行一次:PHP 进程启动,处理请求,进程结束。在 phlo_app(...) 中使用 thread: true 时,运行时在请求之间保持在内存中,旨在用于 FrankenPHP、ReactPHP 或 RoadRunner。

性能提升非常显著(每个请求无需启动),但有三条规则适用:

1. 在 HTTP 路径中不要使用 die()exit() 两者都会终止整个 worker,而不仅仅是当前请求。使用 return 或让终止调用(view()apply()location())发送响应。

2. 静态属性中不要存储请求状态。 静态属性在请求之间存活。请求 A 的数据会泄漏到请求 B。静态属性仅适用于类结构或对所有请求都相同的计算元数据,而不适用于会话、用户、有效负载、时间或数据库状态。

3. 用 $objPers = true 标记长生命周期对象。 默认情况下,Phlo 在请求之间清除其实例映射。对于您明确希望重用的对象(数据库连接、预处理语句),设置 $this->objPers = true,以便清理时不影响它们。

build: true 结合使用是 不允许的:构建在请求之间写入文件,而在 worker 中这会导致竞争条件。如果您同时启用这两者,Phlo 会抛出运行时错误。

12.7: 在不分叉的情况下修改资源

有时您希望共享资源在一个应用程序中表现得略有不同,而不必复制或更改该资源。在任何 .phlo 文件中,您可以通过将节点命名为 %<class>.<node> 来注入或覆盖 不同 类中的节点:

static %visitors.table = 'control.visitors'
prop %visitors.db = 'control'
method %model.greet => 'hi'

第一行覆盖了 visitors 模型的 static $table;第二行向 visitors 添加了一个 db prop;第三行向 model 添加了一个 greet 方法。在构建过程中,编译器会去掉 %<class>. 前缀,并将节点写入 <class>:具有该名称的现有节点将被 覆盖,一个新的节点将被添加。目标类必须是构建的一部分(其资源已加载),否则修饰符将被静默忽略。保持节点类型与您替换的类型相同(static 替换为 staticprop 替换为 prop):整个节点将被交换。

一个实际的例子:让共享的 visitors 模型写入一个中央分析数据库,而应用程序中的所有其他查询保持在应用程序自己的连接上:

static %visitors.table = 'control.visitors'

这使得共享资源保持中立,同时每个应用程序都给它自己的解释。

12.8: 文件元数据:完整的 @ 引用

每个 .phlo 文件可以以 @ key: value 行开头。任何键都作为文件元数据存储;这些具有引擎或工具的含义:

效果
@ class: 重写 PHP 类名
@ extends: PHP 继承(默认:obj
@ implements: PHP 接口,以逗号分隔
@ use: PHP 使用语句(Full\Name as Alias
@ namespace: PHP 命名空间
@ type: class(默认),abstract classinterfacetrait
@ summary: 一行描述,在手册、反射和 Phlo Control Center 中显示
@ package: 工具的组名
@ frontend: / @ backend: 标记为仅前端或后端的资源
@ requires: 依赖项,在资源启用时解析;name? 是可选的,php-ext:creds: 条目是信息性
@ provides: / @ binds: 提供的前端 API / 连接的选择器;供 reflect::selectorGraph 使用
@ tags: 自由格式标签,在反射索引中显示
@ advice: 开发者指导,在 reflect::objectIndex 中显示

12.9: 最佳实践

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