diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e51217f..5224ab7d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -73,7 +73,8 @@ The former global compile-time functions have changed as follows: | `refval($target)` | `std::ref($target)` | Explicit reference wrapper for call arguments. | | `expected($condition)` | `std::expected($condition)` | Emits the `EXPECTED(...)` branch hint. | | `unexpected($condition)` | `std::unexpected($condition)` | Emits the `UNEXPECTED(...)` branch hint. | -| `objval($value, Foo::class)` | `$value->toObject(Foo::class)` | Replaced by the existing keyword method. | +| `objval($value, Foo::class)` | `std::object($value, Foo::class)` | Restores concrete object type information; the equivalent keyword method remains available as `$value->toObject(Foo::class)`. | +| `std::ordered_map($keyType, $valueType)` | `std::orderedMap($keyType, $valueType)` | Uses the standard camelCase spelling for a multi-word method name. | No compatibility functions are installed in the global namespace. Applications may define and call their own `any()`, `refval()`, `expected()`, `unexpected()`, @@ -153,7 +154,8 @@ TypePHP 编译期 API 现在只占用两个全局类符号: | `refval($target)` | `std::ref($target)` | 调用参数的显式引用包装器。 | | `expected($condition)` | `std::expected($condition)` | 生成 `EXPECTED(...)` 分支提示。 | | `unexpected($condition)` | `std::unexpected($condition)` | 生成 `UNEXPECTED(...)` 分支提示。 | -| `objval($value, Foo::class)` | `$value->toObject(Foo::class)` | 改用现有关键词方法。 | +| `objval($value, Foo::class)` | `std::object($value, Foo::class)` | 恢复具体对象类型;仍可使用等价关键词方法 `$value->toObject(Foo::class)`。 | +| `std::ordered_map($keyType, $valueType)` | `std::orderedMap($keyType, $valueType)` | 多单词方法统一使用 camelCase 命名。 | TypePHP 不在全局命名空间安装兼容函数。应用可以自行定义并正常调用 `any()`、`refval()`、`expected()`、`unexpected()` 和 `objval()`,编译器不会拦截。 diff --git a/README-CN.md b/README-CN.md index 5fd9b830..96f866f8 100644 --- a/README-CN.md +++ b/README-CN.md @@ -72,14 +72,15 @@ AST,待全部项目符号就绪后再在 convert 阶段解析。这一两阶 (`int64_t`、`double`、`bool`),数值代码可获得数量级的性能提升。 - **高精度数值** —— `bigInt`(GMP)、`decimal`(libmpdec)、`bigFloat`(MPFR), 提供强类型运算符和方法 API。 -- **强类型容器** —— `std::array`、`std::vector`、`std::map`、`std::ordered_map`, +- **强类型容器** —— `std::array`、`std::vector`、`std::map`、`std::orderedMap`, 元素类型在编译期确定;最高比 PHP 数组快 **10 倍**,性能与 C++ `std::vector` 相当。 - **通用方法(Universal Methods)** —— 在原生类型上直接调用方法 (`$s->upper()`、`$arr->contains()`、`$big->mul(2)`);静态类型已知时在编译期 直接解析调用。 - **混合 C++ / PHP 编程** —— 在性能关键内核中直接调用 C++ 函数(反之亦然)。 -- **编译期函数与关键词** —— `std::any()`、`std::ref()`、`std::expected()`、 - `std::unexpected()`,以及 `toObject()`、`toInt()`、`toString()`、`toArray()` 等。 +- **编译期函数与关键词** —— `std::any()`、`std::object()`、`std::ref()`、 + `std::expected()`、`std::unexpected()`,以及 `toObject()`、`toInt()`、 + `toString()`、`toArray()` 等。 - **编译期安全检查** —— `#[Immutable]` 只读契约和 `#[ArrayDef]` 数组结构元数据, 在编译期检查,零运行时开销。 - **编译期代码生成** —— `#[Getter]`、`#[Setter]`、`#[With]`、`#[Constructor]`、 @@ -450,7 +451,7 @@ function main(): void echo $vector[1] . "\n"; // 2 // 固定 key/value 类型的映射 - $map = std::ordered_map(Type::String, Type::Int); + $map = std::orderedMap(Type::String, Type::Int); $map["a"] = 1; $map["b"] = 2; } diff --git a/README.md b/README.md index fbac0932..53bd2f9c 100644 --- a/README.md +++ b/README.md @@ -83,16 +83,16 @@ This two-phase design keeps multi-file and self-hosted builds deterministic. - **High-precision numerics** — `bigInt` (GMP), `decimal` (libmpdec), and `bigFloat` (MPFR), with typed operators and method APIs. - **Strongly-typed containers** — `std::array`, `std::vector`, `std::map`, and - `std::ordered_map` with compile-time element types; up to **10×** faster than + `std::orderedMap` with compile-time element types; up to **10×** faster than PHP arrays and on par with C++ `std::vector`. - **Universal methods** — call methods directly on primitives (`$s->upper()`, `$arr->contains()`, `$big->mul(2)`); statically-known calls are resolved directly at compile time. - **Mixed C++ / PHP** — call C++ functions from PHP (and vice versa) for performance-critical kernels. -- **Compile-time functions & keywords** — `std::any()`, `std::ref()`, - `std::expected()`, `std::unexpected()`, plus `toObject()`, `toInt()`, `toString()`, `toArray()` and - friends. +- **Compile-time functions & keywords** — `std::any()`, `std::object()`, + `std::ref()`, `std::expected()`, `std::unexpected()`, plus `toObject()`, + `toInt()`, `toString()`, `toArray()` and friends. - **Compile-time safety** — `#[Immutable]` read-only contracts and `#[ArrayDef]` array-shape metadata, checked at compile time with zero runtime cost. - **Compile-time code generation** — `#[Getter]`, `#[Setter]`, `#[With]`, @@ -490,7 +490,7 @@ function main(): void echo $vector[1] . "\n"; // 2 // key-value map with fixed key/value types - $map = std::ordered_map(Type::String, Type::Int); + $map = std::orderedMap(Type::String, Type::Int); $map["a"] = 1; $map["b"] = 2; } diff --git a/docs/en/COMPILE_TIME_FUNCTIONS.md b/docs/en/COMPILE_TIME_FUNCTIONS.md index 191d514d..4d8a8c6d 100644 --- a/docs/en/COMPILE_TIME_FUNCTIONS.md +++ b/docs/en/COMPILE_TIME_FUNCTIONS.md @@ -7,7 +7,8 @@ This document records the compile-time functions, keyword methods, and related c TypePHP does not reserve global function names for compiler directives. The compile-time API occupies two global class symbols: `Type::*` only describes types for extension-method metadata, while `std::*` contains TypePHP built-in -functions. Object type assertions use the `toObject()` keyword method. +functions. Object type assertions use `std::object()` or the equivalent +`toObject()` keyword method. The `std` / `Type` class names and `std` method names are case-insensitive, as PHP class and method names are. `Type::*` members are class constants, whose @@ -40,7 +41,7 @@ Constraints: ## `std::` compile-time entry points -There are currently 14 `std::` compile-time entry points. +There are currently 15 `std::` compile-time entry points. | Name | Purpose | Main limitation | | --- | --- | --- | @@ -51,13 +52,14 @@ There are currently 14 `std::` compile-time entry points. | `std::decimal($value)` | Constructs a Decimal. | A float variable must be converted via string or integer; float literals are handled per the original literal. | | `std::bigFloat($value)` | Constructs a BigFloat. | Requires 1 value parameter. | | `std::any([$value])` | Degrades the expression to `mixed/any`; when omitted, the value defaults to `null`. | Native objects and native-object std containers cannot escape through it. | +| `std::object($value, ClassName::class)` | Checks an object and restores its concrete class information. | Requires exactly 2 non-unpacked arguments and a compile-time-resolvable class name. | | `std::ref($target)` | Explicitly passes a target by reference. | Only accepts variables, array elements, or object properties and is only valid as a call argument wrapper. | | `std::expected($condition)` | Marks a condition as usually true. | Accepts exactly one non-unpacked argument and returns bool. | | `std::unexpected($condition)` | Marks a condition as usually false. | Accepts exactly one non-unpacked argument and returns bool. | | `std::array($type, $size[, ...$sizes])` | Constructs a fixed-size std array. | Can only be used in the top-level scope of the variable's first assignment. | | `std::vector($type[, $size])` | Constructs a std vector. | Can only be used in the top-level scope of the variable's first assignment. | | `std::map($keyType, $valueType)` | Constructs a std map. | Can only be used in the top-level scope of the variable's first assignment. | -| `std::ordered_map($keyType, $valueType)` | Constructs a std ordered map. | Can only be used in the top-level scope of the variable's first assignment. | +| `std::orderedMap($keyType, $valueType)` | Constructs a std ordered map. | Can only be used in the top-level scope of the variable's first assignment. | ## Std container conversion keyword methods @@ -82,7 +84,7 @@ Compile-time functions should be usable in any legal expression position and mai - `std::any()` is handled through one lowering entry; assignments, parameters, return values, array elements, and operator subexpressions share the same semantics. - `std::ref()` / `toRef()` share one reference-wrapper recognizer across argument parsing, SSA, and optimizer paths. -- `toObject(ClassName::class)` replaces the removed global `objval()` helper and provides object type assertion through the existing keyword-method path. +- `std::object($value, ClassName::class)` replaces the removed global `objval()` helper. It is equivalent to `$value->toObject(ClassName::class)`, while allowing ordinary PHP projects to provide a compatible `std::object()` polyfill. - `std::expected()` / `std::unexpected()` generate `EXPECTED(...)` / `UNEXPECTED(...)` respectively and produce no PHP runtime function call. Future refactoring goals: diff --git a/docs/en/INCOMPATIBLE_PHP_FEATURES.md b/docs/en/INCOMPATIBLE_PHP_FEATURES.md index c5578323..b9c05d08 100644 --- a/docs/en/INCOMPATIBLE_PHP_FEATURES.md +++ b/docs/en/INCOMPATIBLE_PHP_FEATURES.md @@ -175,7 +175,7 @@ incompatible with or more restrictive than standard PHP. case fallthrough. The native `int/bool` switch path can currently retain C++ fallthrough, so project code should terminate every non-empty case explicitly. - Appending, inserting, `unset()`, and wholesale replacement of `std::vector`, - `std::map`, and `std::ordered_map` are forbidden during a `foreach`; + `std::map`, and `std::orderedMap` are forbidden during a `foreach`; non-structural updates of existing elements can still be done with assignment operators. - Fixed native typed object properties cannot be freely `unset()` with PHP's diff --git a/docs/en/NATIVE_CLASS_OBJECT.md b/docs/en/NATIVE_CLASS_OBJECT.md index d7be36cc..1e985963 100644 --- a/docs/en/NATIVE_CLASS_OBJECT.md +++ b/docs/en/NATIVE_CLASS_OBJECT.md @@ -248,7 +248,7 @@ The following types are explicitly forbidden as Native Class property types: - `std\array` - `std\vector` - `std\map` -- `std\ordered_map` +- `std\orderedMap` - Other Std Container types added later These types have independent generic layouts and reference or ownership semantics; embedding them in Native Class would significantly expand the first version's type-combination and lifetime-analysis scope. Developers can use ordinary PHP `array` fields; Native Objects still cannot be stored in a PHP array, because Native Object has no `zval` representation. @@ -832,7 +832,7 @@ The first version forbids a Native Object from being: - Used as the receiver of dynamic callbacks such as `call_user_func()`. - Saved into ZendVM global variables or object properties. -Box cannot hold Native Objects. Std Containers cannot be Native Class properties, but local `std::array`, `std::vector`, `std::map`, and `std::ordered_map` can use a concrete `NativeClass::class` as the value type and hold that class or its Native subclasses. Ordinary PHP arrays still cannot hold Native Objects. +Box cannot hold Native Objects. Std Containers cannot be Native Class properties, but local `std::array`, `std::vector`, `std::map`, and `std::orderedMap` can use a concrete `NativeClass::class` as the value type and hold that class or its Native subclasses. Ordinary PHP arrays still cannot hold Native Objects. TypePHP's current Std Containers themselves are only allowed as local variables inside functions, not as global/static, so there is no long-term container ownership that needs separate design for Native elements. A Native-element Std Container further requires it to be a top-level local variable of the function. The compiler generates a `NativeContainerRootFrame` matching its lexical lifetime for that local container; therefore it cannot be saved to global/static, Zend or Native properties, PHP arrays, and cannot be returned, taken by reference, captured into a Closure/arrow function, or converted via `toArray()`/`toAny()`. All of the above would make the raw-pointer-holding `StdContainerBox` outlive the root frame, and must be uniformly rejected at compile time. Reading or writing a single typed Native element still stays within the Native pointer model and does not constitute container escape. diff --git a/docs/en/NATIVE_TYPES.md b/docs/en/NATIVE_TYPES.md index 269fd1c0..f0f8715e 100644 --- a/docs/en/NATIVE_TYPES.md +++ b/docs/en/NATIVE_TYPES.md @@ -171,13 +171,13 @@ $obj = (new MyClass())->toObject(MyClass::class); // Not needed ### Differences from std:: Types -| Feature | std::int/float/bool | toObject | +| Feature | std::int/float/bool | std::object/toObject | |------|---------------------|--------| | **Purpose** | Numeric/boolean type optimization | Object type declaration | | **Performance** | ⚡ High performance (native type) | 🐢 Standard (ZVAL) | | **Memory** | 8B/1B | Pointer (16B+) | | **Timing** | Runtime optimization | Compile-time lowering plus runtime check when needed | -| **Syntax** | `std::int(value)` | `$value->toObject(ClassName::class)` | +| **Syntax** | `std::int(value)` | `std::object($value, ClassName::class)` or `$value->toObject(ClassName::class)` | --- @@ -187,7 +187,7 @@ The following types **do not** use native types and still use ZVAL: - ❌ `std::string` - strings use ZVAL (php::Str) - ❌ `std::array` - arrays use ZVAL (php::Array) -- ❌ `std::object` - objects use ZVAL (php::Object) +- ❌ Objects remain ZVAL-backed (`php::Object`); `std::object()` restores class information but does not introduce native object storage - ❌ All other types - use ZVAL (php::Var) ## Type Mapping Table diff --git a/docs/en/PATENT_01_CPP_TEMPLATE_TYPED_CONTAINER.md b/docs/en/PATENT_01_CPP_TEMPLATE_TYPED_CONTAINER.md index 97e5b0b9..0c43a390 100644 --- a/docs/en/PATENT_01_CPP_TEMPLATE_TYPED_CONTAINER.md +++ b/docs/en/PATENT_01_CPP_TEMPLATE_TYPED_CONTAINER.md @@ -105,7 +105,7 @@ The present invention defines a set of strongly-typed container construction syn ```php $a = std::array(Type::Int, 100); $v = std::vector(Type::Float); -$m = std::ordered_map(Type::String, Type::Int); +$m = std::orderedMap(Type::String, Type::Int); $h = std::map(Type::Int, User::class); ``` diff --git a/docs/en/STD_CONTAINERS.md b/docs/en/STD_CONTAINERS.md index 8438918a..2e09af88 100644 --- a/docs/en/STD_CONTAINERS.md +++ b/docs/en/STD_CONTAINERS.md @@ -95,7 +95,7 @@ Currently supported: - `std::array` - `std::vector` -- `std::ordered_map` +- `std::orderedMap` - `std::map` Their goal is not to fully replace PHP Array, but to be used in performance-sensitive, structurally stable, and clearly-typed code paths. @@ -203,7 +203,7 @@ $a = $b; // allowed, types are exactly identical, performs a container copy ### Modifying Elements in foreach -When iterating over `std::vector`, `std::map`, or `std::ordered_map`, you can update the values of existing elements, for example using `+=`: +When iterating over `std::vector`, `std::map`, or `std::orderedMap`, you can update the values of existing elements, for example using `+=`: ```php foreach ($vector as $index => $value) { @@ -213,14 +213,14 @@ foreach ($vector as $index => $value) { During iteration you cannot perform structural modifications that may invalidate the C++ iterator, including appending elements, inserting or overwriting keys, `unset()`, and replacing the container as a whole. The compiler reports these directly as errors. When the structure needs to change, record the keys to be processed first and apply the modifications uniformly after the `foreach` ends. -## std::ordered_map +## std::orderedMap -`std::ordered_map` is an ordered key-value container. +`std::orderedMap` is an ordered key-value container. ```php function main(): void { - $map = std::ordered_map( + $map = std::orderedMap( Type::String, Type::Int ); @@ -242,17 +242,17 @@ Characteristics: Example: ```php -$map = std::ordered_map(Type::Int, Type::Float); +$map = std::orderedMap(Type::Int, Type::Float); $map[10] = 1.25; $map[20] = 3.5; ``` -ordered_map of the same type can be copied: +orderedMap containers of the same type can be copied: ```php -$a = std::ordered_map(Type::Int, Type::Int); -$b = std::ordered_map(Type::Int, Type::Int); +$a = std::orderedMap(Type::Int, Type::Int); +$b = std::orderedMap(Type::Int, Type::Int); $b[10] = 100; $a = $b; @@ -319,7 +319,7 @@ class User $vector = std::vector(User::class); $array = std::array(User::class, 10); -$map = std::ordered_map(Type::String, User::class); +$map = std::orderedMap(Type::String, User::class); ``` Class-typed containers check the object type at write time to prevent mixing in incorrect objects. @@ -597,7 +597,7 @@ Swoole AOT's std containers provide a path better suited for compiler optimizati - use `std::array` to express fixed-length strongly-typed arrays - use `std::vector` to express dynamic contiguous strongly-typed arrays -- use `std::ordered_map` / `std::map` to express strongly-typed mappings +- use `std::orderedMap` / `std::map` to express strongly-typed mappings - an ordinary variable receiving a std container is automatically converted to a PHP Array - std containers of the same type support native copy - UnsafePtr supports safely passing container references between native functions diff --git a/docs/zh-cn/COMPILE_TIME_FUNCTIONS.md b/docs/zh-cn/COMPILE_TIME_FUNCTIONS.md index 9b456a38..a993a387 100644 --- a/docs/zh-cn/COMPILE_TIME_FUNCTIONS.md +++ b/docs/zh-cn/COMPILE_TIME_FUNCTIONS.md @@ -6,7 +6,7 @@ TypePHP 不再为编译器指令保留任何全局函数名。编译期 API 最终只占用两个全局类符号: `Type::*` 仅用于扩展方法等元数据中的类型表示,`std::*` 承载 TypePHP 内置函数。 -对象类型断言使用 `toObject()` 关键词方法。 +对象类型断言使用 `std::object()` 或等价的 `toObject()` 关键词方法。 遵循 PHP 规则,`std` / `Type` 类名以及 `std` 方法名不区分大小写;`Type::*` 成员是类常量,其常量名仍区分大小写。 @@ -38,7 +38,7 @@ TypePHP 不再为编译器指令保留任何全局函数名。编译期 API 最 ## `std::` 编译期入口 -当前 `std::` 编译期入口共 14 个。 +当前 `std::` 编译期入口共 15 个。 | 名称 | 作用 | 主要限制 | | --- | --- | --- | @@ -49,13 +49,14 @@ TypePHP 不再为编译器指令保留任何全局函数名。编译期 API 最 | `std::decimal($value)` | 构造 Decimal。 | float 变量需改用字符串或整型;float 字面量会按原始字面量处理。 | | `std::bigFloat($value)` | 构造 BigFloat。 | 需要 1 个值参数。 | | `std::any([$value])` | 将表达式降级为 `mixed/any`;省略参数时,初始值为 `null`。 | Native 对象及包含 Native 对象的 std 容器不能通过它逃逸。 | +| `std::object($value, ClassName::class)` | 检查对象并恢复具体类信息。 | 必须传入 2 个非展开参数,且类名必须能在编译期解析。 | | `std::ref($target)` | 显式以引用方式传递目标。 | 只接受变量、数组元素或对象属性,且仅可作为调用参数的引用包装器。 | | `std::expected($condition)` | 标记条件通常为真。 | 只接受一个非展开参数并返回 bool。 | | `std::unexpected($condition)` | 标记条件通常为假。 | 只接受一个非展开参数并返回 bool。 | | `std::array($type, $size[, ...$sizes])` | 构造固定大小 std array。 | 只能在变量首次赋值的顶层作用域使用。 | | `std::vector($type[, $size])` | 构造 std vector。 | 只能在变量首次赋值的顶层作用域使用。 | | `std::map($keyType, $valueType)` | 构造 std map。 | 只能在变量首次赋值的顶层作用域使用。 | -| `std::ordered_map($keyType, $valueType)` | 构造 std ordered map。 | 只能在变量首次赋值的顶层作用域使用。 | +| `std::orderedMap($keyType, $valueType)` | 构造 std ordered map。 | 只能在变量首次赋值的顶层作用域使用。 | ## Std 容器转换关键词方法 @@ -80,7 +81,7 @@ TypePHP 不再为编译器指令保留任何全局函数名。编译期 API 最 - `std::any()` 使用统一的降级入口;赋值、参数、返回值、数组元素和运算子表达式共用相同语义。 - `std::ref()` / `toRef()` 在参数解析、SSA 和优化器路径中共用同一个引用包装识别入口。 -- 已移除的全局 `objval()` 由 `toObject(ClassName::class)` 取代,对象类型断言复用现有关键词方法路径。 +- 已移除的全局 `objval()` 由 `std::object($value, ClassName::class)` 取代。它与 `$value->toObject(ClassName::class)` 等价,同时允许普通 PHP 项目提供兼容的 `std::object()` polyfill。 - `std::expected()` / `std::unexpected()` 分别生成 `EXPECTED(...)` / `UNEXPECTED(...)`,不产生 PHP 运行时函数调用。 后续重构目标: diff --git a/docs/zh-cn/INCOMPATIBLE_PHP_FEATURES.md b/docs/zh-cn/INCOMPATIBLE_PHP_FEATURES.md index 4861940b..31ae903a 100644 --- a/docs/zh-cn/INCOMPATIBLE_PHP_FEATURES.md +++ b/docs/zh-cn/INCOMPATIBLE_PHP_FEATURES.md @@ -84,7 +84,7 @@ - `foreach` by reference 的 value 只能是变量。 - `foreach` list destructuring 不支持按引用绑定元素。 - 非 `int/bool` lowering 路径中的非空 `switch` case 必须以 `return`、`break`、`continue`、`exit` 或 `throw` 结束;不要依赖 PHP 的隐式 case fallthrough。当前 `int/bool` native switch 路径仍可保留 C++ fallthrough,因此项目代码应统一显式终止每个非空 case。 -- `std::vector`、`std::map`、`std::ordered_map` 在 `foreach` 期间禁止追加、插入、`unset()` 或整体替换;已有元素的非结构性更新仍可使用赋值运算符完成。 +- `std::vector`、`std::map`、`std::orderedMap` 在 `foreach` 期间禁止追加、插入、`unset()` 或整体替换;已有元素的非结构性更新仍可使用赋值运算符完成。 - 固定 native typed object property 不允许按 PHP 未初始化语义自由 `unset()`。 - native 类型变量执行 `unset()` 不会产生标准 PHP 的变量删除语义。 diff --git a/docs/zh-cn/NATIVE_CLASS_OBJECT.md b/docs/zh-cn/NATIVE_CLASS_OBJECT.md index 1f3c9f25..eff8f7fc 100644 --- a/docs/zh-cn/NATIVE_CLASS_OBJECT.md +++ b/docs/zh-cn/NATIVE_CLASS_OBJECT.md @@ -263,7 +263,7 @@ PHP 本身不允许将 `resource` 写成属性类型;TypePHP 中的 stream res - `std\array` - `std\vector` - `std\map` -- `std\ordered_map` +- `std\orderedMap` - 后续增加的其他 Std Container 类型 这些类型具有独立的泛型布局、引用或所有权语义,将它们嵌入 Native Class 会显著扩大首版类型组合和生命周期分析范围。开发者可以使用普通 PHP `array` 字段;PHP array 中仍然不能保存 Native Object,因为 Native Object 没有 `zval` 表示。 @@ -864,7 +864,7 @@ Native Class 的字段可以保存 `php::Var`、`php::Array` 或 `php::Object` - 保存到 ZendVM 全局变量或对象属性中。 Box 不能保存 Native Object。Std Container 不能作为 Native Class 属性,但局部 -`std::array`、`std::vector`、`std::map` 和 `std::ordered_map` 可以使用具体 +`std::array`、`std::vector`、`std::map` 和 `std::orderedMap` 可以使用具体 `NativeClass::class` 作为 value type,并保存该类或其 Native 子类。普通 PHP array 仍然不能保存 Native Object。 diff --git a/docs/zh-cn/NATIVE_TYPES.md b/docs/zh-cn/NATIVE_TYPES.md index b8f29e4f..207ce651 100644 --- a/docs/zh-cn/NATIVE_TYPES.md +++ b/docs/zh-cn/NATIVE_TYPES.md @@ -170,13 +170,13 @@ $obj = (new MyClass())->toObject(MyClass::class); // 不需要 ### 与 std:: 类型的区别 -| 特性 | std::int/float/bool | toObject | +| 特性 | std::int/float/bool | std::object/toObject | |------|---------------------|--------| | **用途** | 数值/布尔类型优化 | 对象类型声明 | | **性能** | ⚡ 高性能(原生类型) | 🐢 标准(ZVAL) | | **内存** | 8B/1B | 指针(16B+) | | **时机** | 运行时优化 | 编译期降级,必要时运行时检查 | -| **语法** | `std::int(值)` | `$value->toObject(ClassName::class)` | +| **语法** | `std::int(值)` | `std::object($value, ClassName::class)` 或 `$value->toObject(ClassName::class)` | --- @@ -186,7 +186,7 @@ $obj = (new MyClass())->toObject(MyClass::class); // 不需要 - ❌ `std::string` - 字符串使用 ZVAL (php::Str) - ❌ `std::array` - 数组使用 ZVAL (php::Array) -- ❌ `std::object` - 对象使用 ZVAL (php::Object) +- ❌ 对象仍使用 ZVAL(`php::Object`);`std::object()` 只恢复类信息,不引入原生对象存储 - ❌ 其他所有类型 - 使用 ZVAL (php::Var) ## 类型映射表 diff --git a/docs/zh-cn/PATENT_01_CPP_TEMPLATE_TYPED_CONTAINER.md b/docs/zh-cn/PATENT_01_CPP_TEMPLATE_TYPED_CONTAINER.md index aa70d925..f5fd5d0d 100644 --- a/docs/zh-cn/PATENT_01_CPP_TEMPLATE_TYPED_CONTAINER.md +++ b/docs/zh-cn/PATENT_01_CPP_TEMPLATE_TYPED_CONTAINER.md @@ -105,7 +105,7 @@ Swoole-Compiler 是一种 PHP AOT 编译器。它将 PHP 源文件解析为抽 ```php $a = std::array(Type::Int, 100); $v = std::vector(Type::Float); -$m = std::ordered_map(Type::String, Type::Int); +$m = std::orderedMap(Type::String, Type::Int); $h = std::map(Type::Int, User::class); ``` diff --git a/docs/zh-cn/STD_CONTAINERS.md b/docs/zh-cn/STD_CONTAINERS.md index 75303383..bd0e4674 100644 --- a/docs/zh-cn/STD_CONTAINERS.md +++ b/docs/zh-cn/STD_CONTAINERS.md @@ -94,7 +94,7 @@ Swoole AOT 提供 `std` 容器,用来表达“这个容器的结构和元素 - `std::array` - `std::vector` -- `std::ordered_map` +- `std::orderedMap` - `std::map` 它们的目标不是完全替代 PHP Array,而是用于性能敏感、结构稳定、类型明确的代码路径。 @@ -202,7 +202,7 @@ $a = $b; // 允许,类型完全一致,执行容器 copy ### foreach 中修改元素 -遍历 `std::vector`、`std::map` 或 `std::ordered_map` 时,可以更新已经存在的元素值,例如使用 `+=`: +遍历 `std::vector`、`std::map` 或 `std::orderedMap` 时,可以更新已经存在的元素值,例如使用 `+=`: ```php foreach ($vector as $index => $value) { @@ -212,14 +212,14 @@ foreach ($vector as $index => $value) { 遍历期间不能执行可能使 C++ iterator 失效的结构修改,包括追加元素、插入或覆盖 key、`unset()` 以及整体替换容器。编译器会直接报告错误。需要改变结构时,先记录待处理的 key,结束 `foreach` 后再统一修改。 -## std::ordered_map +## std::orderedMap -`std::ordered_map` 是有序 key-value 容器。 +`std::orderedMap` 是有序 key-value 容器。 ```php function main(): void { - $map = std::ordered_map( + $map = std::orderedMap( Type::String, Type::Int ); @@ -241,17 +241,17 @@ function main(): void 示例: ```php -$map = std::ordered_map(Type::Int, Type::Float); +$map = std::orderedMap(Type::Int, Type::Float); $map[10] = 1.25; $map[20] = 3.5; ``` -同类型 ordered_map 可以 copy: +同类型 orderedMap 容器可以 copy: ```php -$a = std::ordered_map(Type::Int, Type::Int); -$b = std::ordered_map(Type::Int, Type::Int); +$a = std::orderedMap(Type::Int, Type::Int); +$b = std::orderedMap(Type::Int, Type::Int); $b[10] = 100; $a = $b; @@ -318,7 +318,7 @@ class User $vector = std::vector(User::class); $array = std::array(User::class, 10); -$map = std::ordered_map(Type::String, User::class); +$map = std::orderedMap(Type::String, User::class); ``` 类类型容器会在写入时检查对象类型,避免错误对象混入。 @@ -596,7 +596,7 @@ Swoole AOT 的 std 容器提供了一条更适合编译优化的路径: - 用 `std::array` 表达固定长度强类型数组 - 用 `std::vector` 表达动态连续强类型数组 -- 用 `std::ordered_map` / `std::map` 表达强类型映射 +- 用 `std::orderedMap` / `std::map` 表达强类型映射 - 普通变量接收 std 容器时自动转 PHP Array - 同类型 std 容器之间支持原生 copy - UnsafePtr 支持 native 函数间安全地传递容器引用 diff --git a/phpunit/StdObjectPolyfillTest.php b/phpunit/StdObjectPolyfillTest.php new file mode 100644 index 00000000..0e0f7e01 --- /dev/null +++ b/phpunit/StdObjectPolyfillTest.php @@ -0,0 +1,40 @@ +expectException(TypeError::class); + $this->expectExceptionMessage('must be an object, int given'); + + std::object(42, StdObjectPolyfillBase::class); + } + + public function testRejectsAnUnrelatedObject(): void + { + $this->expectException(TypeError::class); + $this->expectExceptionMessage('must be an instance of StdObjectPolyfillBase, stdClass given'); + + std::object(new stdClass(), StdObjectPolyfillBase::class); + } +} diff --git a/phpunit/StdOrderedMapPolyfillTest.php b/phpunit/StdOrderedMapPolyfillTest.php new file mode 100644 index 00000000..66643982 --- /dev/null +++ b/phpunit/StdOrderedMapPolyfillTest.php @@ -0,0 +1,13 @@ +isStaticCall($expr) and $this->isNameExpr($expr->class) and $this->isNamedMethod($expr->name)) { + if ($this->isStdClassExpr($expr->class) + && strtolower($this->parseIdentifier($expr->name)) === 'object' + && count($expr->args) === 2 + && $expr->args[1] instanceof Node\Arg + && !$expr->args[1]->unpack + ) { + return $this->resolveClassNameArg($expr->args[1]->value); + } $class = $this->parseIdentifier($expr->class); if ($class === 'self') { $class = $this->class; @@ -3239,6 +3247,7 @@ class CompilerBase implements PropertyAccessContext 'bigint' => Type::BIGINT, 'decimal' => Type::DECIMAL, 'bigfloat' => Type::BIGFLOAT, + 'object' => Type::OBJECT, 'expected', 'unexpected' => Type::BOOL, default => Type::VAR, }; diff --git a/src/NativeClass/NativeClassSupportTrait.php b/src/NativeClass/NativeClassSupportTrait.php index 9b7515da..caae7163 100644 --- a/src/NativeClass/NativeClassSupportTrait.php +++ b/src/NativeClass/NativeClassSupportTrait.php @@ -1662,7 +1662,7 @@ trait NativeClassSupportTrait 'std\\array', 'std\\vector', 'std\\map', - 'std\\ordered_map', + 'std\\orderedmap', ], true); } diff --git a/src/Parser/AssignOpTrait.php b/src/Parser/AssignOpTrait.php index 2e4e9fd5..45435532 100644 --- a/src/Parser/AssignOpTrait.php +++ b/src/Parser/AssignOpTrait.php @@ -614,7 +614,7 @@ trait AssignOpTrait $class = $this->parseIdentifier($right->class); if ($this->isStdClassExpr($right->class)) { $stdMethod = strtolower($right->name->toString()); - if (in_array($stdMethod, ['array', 'vector', 'map', 'ordered_map'], true)) { + if (in_array($stdMethod, ['array', 'vector', 'map', 'orderedmap'], true)) { if ($this->hasScopeGlobalVar($var) || $this->hasStaticVar($var)) { $this->assertNativeStdContainerFunctionLocal($right); } diff --git a/src/Parser/FunctionCallTrait.php b/src/Parser/FunctionCallTrait.php index 2c42292d..d47a0cf5 100644 --- a/src/Parser/FunctionCallTrait.php +++ b/src/Parser/FunctionCallTrait.php @@ -247,6 +247,25 @@ trait FunctionCallTrait return $this->parseExprAsValue($value); } + /** + * Restore a concrete Zend object type through std::object(). + */ + protected function parseObjectCompileTimeCall(Expr\StaticCall $expr): string + { + if (count($expr->args) !== 2 + || !$expr->args[0] instanceof Node\Arg + || !$expr->args[1] instanceof Node\Arg + || $expr->args[0]->unpack + || $expr->args[1]->unpack + ) { + $this->fatalError($expr, 'The std::object function expects exactly two non-unpacked arguments'); + } + + $value = $this->parseExprAsValue($expr->args[0]->value); + $className = $this->resolveClassNameArg($expr->args[1]->value); + return 'php::toObject(' . $value . ', ' . $this->getClassEntryPtr($className) . ')'; + } + private function parseNamedExitMessageCall(string $name, Expr\FuncCall $expr): ?string { if (!in_array(strtolower($name), ['exit', 'die'], true) diff --git a/src/Parser/MethodCallTrait.php b/src/Parser/MethodCallTrait.php index b65df513..da434992 100644 --- a/src/Parser/MethodCallTrait.php +++ b/src/Parser/MethodCallTrait.php @@ -384,6 +384,9 @@ trait MethodCallTrait if ($func === 'any') { return $this->parseAnyCompileTimeCall($expr); } + if ($func === 'object') { + return $this->parseObjectCompileTimeCall($expr); + } if ($func === 'expected' || $func === 'unexpected') { if (count($expr->args) !== 1 || !$expr->args[0] instanceof Node\Arg diff --git a/src/Parser/StdContainerTrait.php b/src/Parser/StdContainerTrait.php index aefff434..c72a2f97 100644 --- a/src/Parser/StdContainerTrait.php +++ b/src/Parser/StdContainerTrait.php @@ -37,7 +37,7 @@ trait StdContainerTrait } $method = strtolower($expr->name->toString()); - if (!in_array($method, ['array', 'vector', 'map', 'ordered_map'], true)) { + if (!in_array($method, ['array', 'vector', 'map', 'orderedmap'], true)) { return ''; } @@ -69,7 +69,7 @@ trait StdContainerTrait } $typeInfo = $this->parseStdValueTypeInfo( $expr->args[$valueIndex]->value, - 'std::' . $method, + $method === 'orderedmap' ? 'std::orderedMap' : 'std::' . $method, ); $class = $typeInfo['class'] ?? ''; return is_string($class) && $this->isNativeObjectClass($class) ? $class : ''; @@ -565,7 +565,7 @@ trait StdContainerTrait $this->fatalError($expr, 'std container expects a variable'); } if (count($dims) !== 1) { - $this->fatalError($expr, 'Nested std::vector/std::map/std::ordered_map access is not supported'); + $this->fatalError($expr, 'Nested std::vector/std::map/std::orderedMap access is not supported'); } $dim = $dims[0]; if ($dim === null) { @@ -953,7 +953,7 @@ trait StdContainerTrait protected function parseStdOrderedMap(string $var, Expr\StaticCall $expr): string { - return $this->parseStdMapBase($var, $expr, 'std::ordered_map', Type::STD_ORDERED_MAP, 'ordered_map'); + return $this->parseStdMapBase($var, $expr, 'std::orderedMap', Type::STD_ORDERED_MAP, 'ordered_map'); } private function parseStdMapBase(string $var, Expr\StaticCall $expr, string $funcName, string $containerType, string $kind): string diff --git a/src/polyfills.php b/src/polyfills.php index cbd59a7e..4710992b 100644 --- a/src/polyfills.php +++ b/src/polyfills.php @@ -185,6 +185,24 @@ class std return $value; } + public static function object(mixed $value, string $class): object + { + if (!is_object($value)) { + throw new TypeError(sprintf( + 'std::object(): Argument #1 ($value) must be an object, %s given', + get_debug_type($value), + )); + } + if (!$value instanceof $class) { + throw new TypeError(sprintf( + 'std::object(): Argument #1 ($value) must be an instance of %s, %s given', + $class, + $value::class, + )); + } + return $value; + } + public static function &ref(mixed &$var): mixed { return $var; @@ -205,7 +223,7 @@ class std return []; } - public static function ordered_map(mixed $key_type, mixed $value_type): array + public static function orderedMap(mixed $key_type, mixed $value_type): array { return []; } diff --git a/tests/compiler/class/std-object.phpt b/tests/compiler/class/std-object.phpt new file mode 100644 index 00000000..d1d71853 --- /dev/null +++ b/tests/compiler/class/std-object.phpt @@ -0,0 +1,60 @@ +--TEST-- +std::object restores concrete object types and validates them at runtime +--FILE-- +childName()); + var_dump(restore_child_by_name($child)->childName()); + var_dump($child->fromSelf($child)->childName()); + var_dump($child->fromParent($child)->name()); + + try { + std::object(new stdClass(), StdObjectChild::class); + } catch (TypeError $error) { + echo $error::class, "\n"; + } + + try { + std::object(42, StdObjectChild::class); + } catch (TypeError $error) { + echo $error::class, "\n"; + } +} +?> +--EXPECT-- +string(5) "child" +string(5) "child" +string(5) "child" +string(4) "base" +TypeError +TypeError diff --git a/tests/compiler/native-class/std-containers.phpt b/tests/compiler/native-class/std-containers.phpt index ebb4bf15..208b21c1 100644 --- a/tests/compiler/native-class/std-containers.phpt +++ b/tests/compiler/native-class/std-containers.phpt @@ -19,7 +19,7 @@ function main(): void $array = std::array(NativeContainerValue::class, 1); $vector = std::vector(NativeContainerValue::class); $map = std::map(Type::String, NativeContainerValue::class); - $ordered = std::ordered_map(Type::Int, NativeContainerValue::class); + $ordered = std::orderedMap(Type::Int, NativeContainerValue::class); $array[0] = new NativeContainerValue(11); $vector[] = new NativeContainerValue(22); diff --git a/tests/compiler/std-map/missing-read.phpt b/tests/compiler/std-map/missing-read.phpt index 2b697f74..aaee9774 100644 --- a/tests/compiler/std-map/missing-read.phpt +++ b/tests/compiler/std-map/missing-read.phpt @@ -11,7 +11,7 @@ function main() { } var_dump(count($map)); - $ordered = std::ordered_map(Type::String, Type::Int); + $ordered = std::orderedMap(Type::String, Type::Int); try { var_dump($ordered['missing']); } catch (Throwable $e) { diff --git a/tests/compiler/std-ordered-map/001.phpt b/tests/compiler/std-ordered-map/001.phpt index d2712188..252fbc2d 100644 --- a/tests/compiler/std-ordered-map/001.phpt +++ b/tests/compiler/std-ordered-map/001.phpt @@ -1,9 +1,9 @@ --TEST-- -std ordered_map: 001 +std orderedMap: 001 --FILE-- "map", "value" => 84]; $array = $arrays[2]; var_dump($array["name"]); var_dump($array["value"]); - $objects = std::ordered_map(Type::String, Type::Object); + $objects = std::orderedMap(Type::String, Type::Object); $objects["item"] = new StdMapComplexValue(14); var_dump($objects["item"] instanceof StdMapComplexValue); $object = $objects["item"]->toObject(StdMapComplexValue::class); var_dump($object->getValue()); - $variants = std::ordered_map(Type::Int, Type::Any); + $variants = std::orderedMap(Type::Int, Type::Any); $variants[3] = 12.5; $variants[4] = "any"; var_dump($variants[3]); diff --git a/tests/compiler/std-ordered-map/004.phpt b/tests/compiler/std-ordered-map/004.phpt index 188448a1..303d0ebf 100644 --- a/tests/compiler/std-ordered-map/004.phpt +++ b/tests/compiler/std-ordered-map/004.phpt @@ -28,7 +28,7 @@ function std_container_class_value_mixed(mixed $value): mixed } function main() { - $map = std::ordered_map(Type::String, StdContainerClassValue::class); + $map = std::orderedMap(Type::String, StdContainerClassValue::class); $map["a"] = new StdContainerClassValue(1); $item = $map["a"]; var_dump($item->getValue()); diff --git a/tests/compiler/std-ordered-map/005.phpt b/tests/compiler/std-ordered-map/005.phpt index d106dafa..2bf293f2 100644 --- a/tests/compiler/std-ordered-map/005.phpt +++ b/tests/compiler/std-ordered-map/005.phpt @@ -1,5 +1,5 @@ --TEST-- -std ordered_map: unsafe_cast +std orderedMap: unsafe_cast --FILE-- int(20) -} \ No newline at end of file +} diff --git a/tests/compiler/std-ordered-map/010.phpt b/tests/compiler/std-ordered-map/010.phpt index b1760c74..52e113e1 100644 --- a/tests/compiler/std-ordered-map/010.phpt +++ b/tests/compiler/std-ordered-map/010.phpt @@ -1,9 +1,9 @@ --TEST-- -std ordered_map: unset +std orderedMap: unset --FILE-- getValue()); - $ordered = std::ordered_map(Type::String, StdContainerInterfaceValue::class); + $ordered = std::orderedMap(Type::String, StdContainerInterfaceValue::class); $ordered["item"] = std_container_interface_mixed(new StdContainerInterfaceImpl(5)); var_dump($ordered["item"]->getValue()); diff --git a/tests/compiler/std-vector/foreach-element-update.phpt b/tests/compiler/std-vector/foreach-element-update.phpt index 72c0aa3e..673948a1 100644 --- a/tests/compiler/std-vector/foreach-element-update.phpt +++ b/tests/compiler/std-vector/foreach-element-update.phpt @@ -13,7 +13,7 @@ function main(): void } var_dump($vector[0], $vector[1]); - $map = std::ordered_map(Type::String, Type::Int); + $map = std::orderedMap(Type::String, Type::Int); $map['a'] = 3; $map['b'] = 4; foreach ($map as $mapKey => $mapValue) { diff --git a/tests/wasm/runtime/phpx-numeric-containers.phpt b/tests/wasm/runtime/phpx-numeric-containers.phpt index b8a5bc7f..f90ddd78 100644 --- a/tests/wasm/runtime/phpx-numeric-containers.phpt +++ b/tests/wasm/runtime/phpx-numeric-containers.phpt @@ -18,7 +18,7 @@ function main(): void $vector[] = 'wasm'; $map = std::map(Type::String, Type::Int); $map['answer'] = 42; - $ordered = std::ordered_map(Type::String, Type::Int); + $ordered = std::orderedMap(Type::String, Type::Int); $ordered['first'] = 1; echo $array[0], '|', $vector[0], '|', $map['answer'], '|', $ordered['first'], "\n";