- Add comprehensive C++ naming rules for typephp_, php::, typephp_<project>, and php_ prefixes - Document ABI boundaries and symbol collision prevention guidelines - Update MIXED_CPP_PHP.md with cross-reference to new naming convention document - Clarify php_ prefix usage for user callable ABI mapping vs internal helpers - Add project-specific namespace details for typephp_<target> data declarations - Include code review checklist for naming convention compliance - Reference new documentation in main README table of contentsmaster
parent
e722501b3d
commit
65a44c7f65
3 changed files with 343 additions and 1 deletions
@ -0,0 +1,338 @@ |
||||
# C++ 命名空间、前缀与符号 ABI 规则 |
||||
|
||||
本文是 TypePHP、PHPX 以及 TypePHP 生成代码的内部 C++ 命名规范。它解决以下问题: |
||||
|
||||
- 区分 TypePHP 运行时逻辑、PHPX ZendAPI 封装、项目私有实现和用户 PHP 符号; |
||||
- 防止框架 helper 与用户定义的 PHP 函数或类方法生成相同的 C++ 符号; |
||||
- 明确哪些名称属于稳定 ABI,哪些名称仅限单个生成项目内部使用; |
||||
- 为新增 helper、缓存、入口函数和生成符号提供统一的命名决策。 |
||||
|
||||
## 1. 总体规则 |
||||
|
||||
| 命名域 | 含义 | 典型形式 | 可见范围 | ABI 属性 | |
||||
| --- | --- | --- | --- | --- | |
||||
| `typephp_` | TypePHP 独有的运行时或编译产物支持逻辑 | `typephp_call_parent_constructor()` | TypePHP/PHPX 运行时 | 内部或显式导出 ABI | |
||||
| `php::` | 对 ZendAPI、zval、HashTable、call frame 等 PHP 运行时能力的 C++ 封装 | `php::deindirect()` | PHPX C++ API | PHPX API | |
||||
| `typephp_<project>` | 单个编译项目的私有 C++ 命名空间 | `namespace typephp_tpc` | 当前生成项目 | 非公共 ABI | |
||||
| `php_` | 用户 PHP 函数和类方法映射后的 C++ callable 符号 | `php_app__user__save()` | 链接器可见 | TypePHP/stub callable ABI | |
||||
|
||||
核心约束: |
||||
|
||||
1. 不得新增全局的框架 `php_*` helper。 |
||||
2. 与 TypePHP 无关、只是包装 ZendAPI 的能力必须放入 `namespace php`。 |
||||
3. TypePHP 独有且需要跨生成文件调用的逻辑使用 `typephp_` 前缀。 |
||||
4. 只服务于一个编译项目的数据和函数放入 `typephp_<project>` 命名空间。 |
||||
5. 全局 `php_*` callable 名称保留给用户 PHP 声明的编译 ABI。 |
||||
|
||||
## 2. `typephp_`:TypePHP 独有逻辑 |
||||
|
||||
`typephp_` 表示该 API 的语义由 TypePHP 定义,不是 ZendAPI 的一般性 C++ 包装。常见场景包括: |
||||
|
||||
- TypePHP 属性读写规则; |
||||
- TypePHP 构造、克隆和父方法调用链; |
||||
- TypePHP 编译期 Attribute 的运行时支持; |
||||
- TypePHP Native Class、Property Hook 等专属运行时逻辑; |
||||
- TypePHP embed runtime 的初始化和关闭入口。 |
||||
|
||||
示例: |
||||
|
||||
```cpp |
||||
typephp_call_parent_constructor(object, constructor, args); |
||||
typephp_call_parent_clone(object, clone_method); |
||||
typephp_install_property_handlers(class_entry, handlers); |
||||
typephp_write_property_scoped(object, member, value, scope); |
||||
typephp_runtime_init(argc, argv); |
||||
``` |
||||
|
||||
### 2.1 使用边界 |
||||
|
||||
- 该前缀是 TypePHP 内部 C/C++ 名称空间,不代表 PHP 用户函数。 |
||||
- 新增 API 时应使用完整、可识别的 snake_case 名称,不能使用含义过宽的名称,例如 `typephp_call()`。 |
||||
- 仅在一个 `.cc` 文件中使用的函数还应增加 `static` 或放入匿名命名空间。 |
||||
- 需要跨动态库边界时,使用对应的导出宏;不需要导出的 helper 不应扩大符号可见性。 |
||||
- 不要仅因为代码位于 `typephp_helper.h` 就使用 `typephp_`;判断依据是语义是否为 TypePHP 独有。 |
||||
|
||||
### 2.2 正反例 |
||||
|
||||
```cpp |
||||
// 正确:TypePHP 独有的构造链语义。 |
||||
typephp_call_parent_constructor(object, constructor, args); |
||||
|
||||
// 错误:只是将 INDIRECT zval 物化为普通值,并非 TypePHP 独有。 |
||||
typephp_deindirect(value); |
||||
|
||||
// 正确:通用 Zend 值包装属于 PHPX。 |
||||
php::deindirect(value); |
||||
``` |
||||
|
||||
## 3. `php::`:ZendAPI 的 C++ 封装 |
||||
|
||||
`namespace php` 由 PHPX 提供,用于把 Zend 的 C API、宏、裸指针和手工资源管理封装为类型安全、RAII 友好的 C++ API。 |
||||
|
||||
这一命名域包含两类能力: |
||||
|
||||
1. PHP 值和运行时对象,如 `php::Var`、`php::Str`、`php::Array`、`php::Object`; |
||||
2. ZendAPI 的安全包装,如符号查询、作用域管理、值转换、对象创建和调用。 |
||||
|
||||
示例: |
||||
|
||||
```cpp |
||||
php::Var value; |
||||
php::Array arguments; |
||||
|
||||
auto plain = php::deindirect(value); |
||||
auto called_ce = php::getCalledCe(this_); |
||||
auto scope = php::getCallableScope(function, this_); |
||||
auto create_object = php::getCreateObjectFn(class_entry); |
||||
auto globals = php::globalsArray(); |
||||
``` |
||||
|
||||
### 3.1 何时使用 `php::` |
||||
|
||||
满足以下条件时应放入 `namespace php`: |
||||
|
||||
- API 对任何 PHPX C++ 调用者都有意义; |
||||
- API 的行为可以完全用 Zend/PHP 运行时语义解释; |
||||
- API 不依赖 TypePHP AST、编译期 Attribute 或 TypePHP 特有语言规则; |
||||
- API 的主要作用是隐藏 Zend 宏、裸 `zval *`、引用计数或异常检查。 |
||||
|
||||
### 3.2 禁止全局 `php_*` helper |
||||
|
||||
以下旧式写法是禁止的: |
||||
|
||||
```cpp |
||||
php::Var php_deindirect(const php::Var &value); |
||||
php::Str php_get_called_class(php::Object &this_); |
||||
zend_class_entry *php_get_called_ce(php::Object &this_); |
||||
auto php_get_create_object_fn(zend_class_entry *ce); |
||||
``` |
||||
|
||||
它们必须写成: |
||||
|
||||
```cpp |
||||
namespace php { |
||||
|
||||
Var deindirect(const Var &value); |
||||
Str getCalledClass(Object &this_); |
||||
zend_class_entry *getCalledCe(Object &this_); |
||||
auto getCreateObjectFn(zend_class_entry *ce); |
||||
|
||||
} // namespace php |
||||
``` |
||||
|
||||
原因是用户可以合法声明: |
||||
|
||||
```php |
||||
function deindirect(mixed $value): mixed {} |
||||
function get_called_ce(): string {} |
||||
function get_create_object_fn(): string {} |
||||
``` |
||||
|
||||
这些 PHP 函数会生成 `php_deindirect`、`php_get_called_ce` 和 |
||||
`php_get_create_object_fn`。如果 PHPX 也在全局定义同名 helper,可能在声明、重载解析或链接阶段发生冲突。 |
||||
|
||||
### 3.3 命名风格 |
||||
|
||||
PHPX C++ API 使用现有的 camelCase 风格: |
||||
|
||||
```cpp |
||||
php::getCalledClass(); |
||||
php::getClassEntrySafe(); |
||||
php::getPersistentCache(); |
||||
php::stdCreateObject(); |
||||
``` |
||||
|
||||
不要把 Zend 的 snake_case 名称机械地保留为全局 C++ 名称。底层调用可以继续使用 Zend 原始 API,例如 `zend_objects_new()`,但对生成代码暴露的包装层应使用 `php::`。 |
||||
|
||||
## 4. `typephp_<project>`:项目私有命名空间 |
||||
|
||||
每个 TypePHP 编译项目拥有独立的 C++ 命名空间: |
||||
|
||||
```text |
||||
typephp_<target-name> |
||||
``` |
||||
|
||||
例如项目名为 `tpc`: |
||||
|
||||
```cpp |
||||
namespace typephp_tpc { |
||||
// Project-private generated state and helpers. |
||||
} |
||||
``` |
||||
|
||||
项目名中的 `-` 和 `*` 会转换为 `_`,其余字符必须满足编译器的 target identifier 校验。由于固定带有 `typephp_` 前缀,即使项目名以数字开头,最终 C++ namespace 仍是合法标识符。 |
||||
|
||||
### 4.1 应放入该命名空间的内容 |
||||
|
||||
- literal string 表和 `get_str()`; |
||||
- class/function/property cache 表及其访问函数; |
||||
- 当前项目的全局变量存储; |
||||
- class entry、object handler 和默认属性模板; |
||||
- module entry、MINIT/RINIT/RSHUTDOWN 辅助状态; |
||||
- `module_init()`、`module_clean()` 等仅在生成 extension 文件内部调用的函数; |
||||
- Python module cache 等项目级生成状态。 |
||||
|
||||
示意: |
||||
|
||||
```cpp |
||||
namespace typephp_demo { |
||||
|
||||
static php::Str literal_strings[] = { |
||||
php::Str{"hello"}, |
||||
}; |
||||
|
||||
php::Str &get_str(uint32_t index) { |
||||
return literal_strings[index]; |
||||
} |
||||
|
||||
static THREAD_LOCAL zend_class_entry *class_map[8]; |
||||
|
||||
zend_class_entry *get_class(int id, const php::Str &name) { |
||||
// Resolve and cache a symbol owned by this project. |
||||
} |
||||
|
||||
static void module_init() { |
||||
// Initialize this project's generated state. |
||||
} |
||||
|
||||
} // namespace typephp_demo |
||||
``` |
||||
|
||||
### 4.2 可见性与 ABI |
||||
|
||||
- `typephp_<project>` 内的名称是实现细节,不是 library stub ABI。 |
||||
- 可限制为 `static` 的对象和函数应继续标记为 `static`。 |
||||
- 生成头文件可以声明必须跨 translation unit 使用的项目内部 accessor,但不应暴露底层数组或缓存表。 |
||||
- 外部手写 C++ 代码不得依赖 literal index、cache index 或项目内部 storage 名称。 |
||||
- 不同 TypePHP 项目可以链接到同一进程,因为相同的内部短名称位于不同的项目 namespace 中。 |
||||
|
||||
### 4.3 作用域优先于名称拼写 |
||||
|
||||
项目 namespace 中仍可能出现历史生成名称,例如: |
||||
|
||||
```cpp |
||||
typephp_demo::php_class_entry_App_User |
||||
``` |
||||
|
||||
虽然成员名以 `php_` 开头,但完整符号位于 `typephp_demo` 中,因此它属于项目私有实现,而不是第 5 节所述的全局用户 callable ABI。新增项目内部 helper 应优先使用不带 `php_` 的短名称,例如 `get_class()`、`get_func()` 和 `get_str()`。 |
||||
|
||||
## 5. `php_`:用户 PHP callable 的 C++ ABI |
||||
|
||||
全局 `php_` 前缀用于 TypePHP 将用户声明的 PHP 函数和类方法映射为 C++ callable 符号。这套命名同时被生成代码、library stub 和外部 C++ 实现使用,因此不能随意改变。 |
||||
|
||||
示例: |
||||
|
||||
```php |
||||
namespace App; |
||||
|
||||
function greet(string $name): string {} |
||||
|
||||
class User |
||||
{ |
||||
public function save(): bool {} |
||||
} |
||||
``` |
||||
|
||||
概念上的 C++ 符号为: |
||||
|
||||
```cpp |
||||
php::Str php_app__greet(php::Str name); |
||||
php::Bool php_app__user__save(php::Object &this_); |
||||
``` |
||||
|
||||
规则包括: |
||||
|
||||
- 使用 `php_` 标识“由 PHP 声明映射而来”; |
||||
- PHP namespace、class 和 method/function 名经过规范化后组合; |
||||
- `__` 是现有 ABI 的组合分隔符; |
||||
- 实例方法的第一个参数是对象 `this_`; |
||||
- stub、library 和消费方必须使用完全相同的映射规则。 |
||||
|
||||
### 5.1 为什么内部 helper 不能使用 `php_` |
||||
|
||||
`php_` 映射不是独立的保留关键字空间,而是用户 PHP 名称的机械 ABI。以下用户声明: |
||||
|
||||
```php |
||||
function deindirect(mixed $value): mixed {} |
||||
``` |
||||
|
||||
会自然生成: |
||||
|
||||
```cpp |
||||
php::Var php_deindirect(php::Var value); |
||||
``` |
||||
|
||||
因此框架若定义全局 `php_deindirect()`,就侵占了用户符号空间。正确做法是 `php::deindirect()`。 |
||||
|
||||
### 5.2 组合冲突 |
||||
|
||||
由于当前 ABI 使用 `__` 组合 PHP namespace、class 和 callable 名,下列两个 PHP 声明可能映射到同一个 C++ 符号: |
||||
|
||||
```php |
||||
function App\user__test(): void {} |
||||
|
||||
namespace App; |
||||
class User |
||||
{ |
||||
public function test(): void {} |
||||
} |
||||
``` |
||||
|
||||
编译器必须在预处理阶段检测这种情况并抛出 FatalError,不能通过覆盖、链接顺序或增加运行时分派来处理。修改映射分隔规则会破坏既有 stub/ABI,因此冲突必须由用户重命名解决。 |
||||
|
||||
### 5.3 入口符号例外 |
||||
|
||||
少量 C ABI/嵌入入口由生成器固定定义,不属于普通用户 callable。例如: |
||||
|
||||
```cpp |
||||
php_<project>_embed_get_module(); |
||||
``` |
||||
|
||||
它是 binary/library embed runtime 与当前项目 module entry 的连接点。该名称包含项目名并由构建器和 `typephp_main.cc` 成对生成,不得作为通用 helper 命名模板。 |
||||
|
||||
## 6. 名称选择流程 |
||||
|
||||
新增 C++ API 时按以下顺序判断: |
||||
|
||||
1. **它是否是用户 PHP 函数或类方法的编译本体?** |
||||
- 是:使用既有 `php_` callable ABI 生成器,禁止手写另一套映射。 |
||||
2. **它是否只服务于当前一个 TypePHP 项目?** |
||||
- 是:放入 `typephp_<project>`,并尽可能使用 `static` 或私有 accessor。 |
||||
3. **它是否实现 TypePHP 独有语义?** |
||||
- 是:使用 `typephp_` 前缀。 |
||||
4. **它是否只是对 Zend/PHP 运行时能力的 C++ 封装?** |
||||
- 是:放入 `namespace php`,使用 PHPX camelCase 风格。 |
||||
5. **以上都不是?** |
||||
- 不应随意加入 `typephp_helper.h`;应重新确认所属模块和公共 API 边界。 |
||||
|
||||
## 7. 代码审查清单 |
||||
|
||||
新增或修改生成 helper 时必须检查: |
||||
|
||||
- [ ] `typephp_helper.h` 中没有新增全局 `php_*` helper; |
||||
- [ ] ZendAPI 包装位于 `namespace php`; |
||||
- [ ] TypePHP 独有逻辑使用 `typephp_`; |
||||
- [ ] 项目缓存和 storage 位于 `typephp_<project>`; |
||||
- [ ] 项目私有表没有通过生成头文件直接 `extern` 暴露; |
||||
- [ ] 用户 callable 仍使用统一的 `php_` ABI 生成器; |
||||
- [ ] 新名称不会与用户可声明的 PHP 函数或方法发生冲突; |
||||
- [ ] bin、lib、ext 和 WASM 构建使用相同的项目名推导规则; |
||||
- [ ] 修改公开 callable 映射时同步评估 stub 和既有 ABI; |
||||
- [ ] 至少增加一个用户同名函数的编译回归测试。 |
||||
|
||||
当前相关回归测试为: |
||||
|
||||
```text |
||||
tests/compiler/basic/helper-symbol-collision.phpt |
||||
``` |
||||
|
||||
## 8. 主要实现位置 |
||||
|
||||
| 责任 | 文件 | |
||||
| --- | --- | |
||||
| `php_` callable 前缀与组合分隔符 | `src/CompilerBase.php` | |
||||
| callable 组合冲突检测 | `src/Preprocessor.php` | |
||||
| `typephp_<project>` 生成及项目私有表 | `src/Translator.php` | |
||||
| TypePHP extension 前缀常量 | `src/Metadata/Constants.php` | |
||||
| PHPX/TypePHP helper 分类 | `vendor/swoole/phpx/src/misc/typephp_helper.h` | |
||||
| embed module accessor 拼接 | `vendor/swoole/phpx/src/misc/typephp_main.cc` | |
||||
Loading…
Reference in new issue