refactor(std): rename ordered_map to orderedMap and add object function

- Renamed std::ordered_map to std::orderedMap following camelCase naming convention
- Added new std::object() function to restore concrete object type information
- Updated all test files to use the new orderedMap naming
- Modified parser to recognize orderedmap in assign operations
- Updated documentation to reflect the new naming and function
- Added polyfill implementation for std::object()
- Updated README and changelog with
master
韩天峰 1 month ago
parent 055dee01f8
commit 2fe09697fa
  1. 6
      CHANGELOG.md
  2. 9
      README-CN.md
  3. 10
      README.md
  4. 10
      docs/en/COMPILE_TIME_FUNCTIONS.md
  5. 2
      docs/en/INCOMPATIBLE_PHP_FEATURES.md
  6. 4
      docs/en/NATIVE_CLASS_OBJECT.md
  7. 6
      docs/en/NATIVE_TYPES.md
  8. 2
      docs/en/PATENT_01_CPP_TEMPLATE_TYPED_CONTAINER.md
  9. 22
      docs/en/STD_CONTAINERS.md
  10. 9
      docs/zh-cn/COMPILE_TIME_FUNCTIONS.md
  11. 2
      docs/zh-cn/INCOMPATIBLE_PHP_FEATURES.md
  12. 4
      docs/zh-cn/NATIVE_CLASS_OBJECT.md
  13. 6
      docs/zh-cn/NATIVE_TYPES.md
  14. 2
      docs/zh-cn/PATENT_01_CPP_TEMPLATE_TYPED_CONTAINER.md
  15. 22
      docs/zh-cn/STD_CONTAINERS.md
  16. 40
      phpunit/StdObjectPolyfillTest.php
  17. 13
      phpunit/StdOrderedMapPolyfillTest.php
  18. 9
      src/CompilerBase.php
  19. 2
      src/NativeClass/NativeClassSupportTrait.php
  20. 2
      src/Parser/AssignOpTrait.php
  21. 19
      src/Parser/FunctionCallTrait.php
  22. 3
      src/Parser/MethodCallTrait.php
  23. 8
      src/Parser/StdContainerTrait.php
  24. 20
      src/polyfills.php
  25. 60
      tests/compiler/class/std-object.phpt
  26. 2
      tests/compiler/native-class/std-containers.phpt
  27. 2
      tests/compiler/std-map/missing-read.phpt
  28. 4
      tests/compiler/std-ordered-map/001.phpt
  29. 6
      tests/compiler/std-ordered-map/002.phpt
  30. 10
      tests/compiler/std-ordered-map/003.phpt
  31. 2
      tests/compiler/std-ordered-map/004.phpt
  32. 4
      tests/compiler/std-ordered-map/005.phpt
  33. 4
      tests/compiler/std-ordered-map/006.phpt
  34. 4
      tests/compiler/std-ordered-map/007.phpt
  35. 6
      tests/compiler/std-ordered-map/008.phpt
  36. 6
      tests/compiler/std-ordered-map/009.phpt
  37. 6
      tests/compiler/std-ordered-map/010.phpt
  38. 2
      tests/compiler/std-vector/014.phpt
  39. 2
      tests/compiler/std-vector/foreach-element-update.phpt
  40. 2
      tests/wasm/runtime/phpx-numeric-containers.phpt

@ -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. | | `refval($target)` | `std::ref($target)` | Explicit reference wrapper for call arguments. |
| `expected($condition)` | `std::expected($condition)` | Emits the `EXPECTED(...)` branch hint. | | `expected($condition)` | `std::expected($condition)` | Emits the `EXPECTED(...)` branch hint. |
| `unexpected($condition)` | `std::unexpected($condition)` | Emits the `UNEXPECTED(...)` 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 No compatibility functions are installed in the global namespace. Applications
may define and call their own `any()`, `refval()`, `expected()`, `unexpected()`, may define and call their own `any()`, `refval()`, `expected()`, `unexpected()`,
@ -153,7 +154,8 @@ TypePHP 编译期 API 现在只占用两个全局类符号:
| `refval($target)` | `std::ref($target)` | 调用参数的显式引用包装器。 | | `refval($target)` | `std::ref($target)` | 调用参数的显式引用包装器。 |
| `expected($condition)` | `std::expected($condition)` | 生成 `EXPECTED(...)` 分支提示。 | | `expected($condition)` | `std::expected($condition)` | 生成 `EXPECTED(...)` 分支提示。 |
| `unexpected($condition)` | `std::unexpected($condition)` | 生成 `UNEXPECTED(...)` 分支提示。 | | `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 不在全局命名空间安装兼容函数。应用可以自行定义并正常调用 TypePHP 不在全局命名空间安装兼容函数。应用可以自行定义并正常调用
`any()`、`refval()`、`expected()`、`unexpected()` 和 `objval()`,编译器不会拦截。 `any()`、`refval()`、`expected()`、`unexpected()` 和 `objval()`,编译器不会拦截。

@ -72,14 +72,15 @@ AST,待全部项目符号就绪后再在 convert 阶段解析。这一两阶
(`int64_t`、`double`、`bool`),数值代码可获得数量级的性能提升。 (`int64_t`、`double`、`bool`),数值代码可获得数量级的性能提升。
- **高精度数值** —— `bigInt`(GMP)、`decimal`(libmpdec)、`bigFloat`(MPFR), - **高精度数值** —— `bigInt`(GMP)、`decimal`(libmpdec)、`bigFloat`(MPFR),
提供强类型运算符和方法 API。 提供强类型运算符和方法 API。
- **强类型容器** —— `std::array`、`std::vector`、`std::map`、`std::ordered_map`, - **强类型容器** —— `std::array`、`std::vector`、`std::map`、`std::orderedMap`,
元素类型在编译期确定;最高比 PHP 数组快 **10 倍**,性能与 C++ `std::vector` 相当。 元素类型在编译期确定;最高比 PHP 数组快 **10 倍**,性能与 C++ `std::vector` 相当。
- **通用方法(Universal Methods)** —— 在原生类型上直接调用方法 - **通用方法(Universal Methods)** —— 在原生类型上直接调用方法
(`$s->upper()`、`$arr->contains()`、`$big->mul(2)`);静态类型已知时在编译期 (`$s->upper()`、`$arr->contains()`、`$big->mul(2)`);静态类型已知时在编译期
直接解析调用。 直接解析调用。
- **混合 C++ / PHP 编程** —— 在性能关键内核中直接调用 C++ 函数(反之亦然)。 - **混合 C++ / PHP 编程** —— 在性能关键内核中直接调用 C++ 函数(反之亦然)。
- **编译期函数与关键词** —— `std::any()`、`std::ref()`、`std::expected()`、 - **编译期函数与关键词** —— `std::any()`、`std::object()`、`std::ref()`、
`std::unexpected()`,以及 `toObject()`、`toInt()`、`toString()`、`toArray()` 等。 `std::expected()`、`std::unexpected()`,以及 `toObject()`、`toInt()`、
`toString()`、`toArray()` 等。
- **编译期安全检查** —— `#[Immutable]` 只读契约和 `#[ArrayDef]` 数组结构元数据, - **编译期安全检查** —— `#[Immutable]` 只读契约和 `#[ArrayDef]` 数组结构元数据,
在编译期检查,零运行时开销。 在编译期检查,零运行时开销。
- **编译期代码生成** —— `#[Getter]`、`#[Setter]`、`#[With]`、`#[Constructor]`、 - **编译期代码生成** —— `#[Getter]`、`#[Setter]`、`#[With]`、`#[Constructor]`、
@ -450,7 +451,7 @@ function main(): void
echo $vector[1] . "\n"; // 2 echo $vector[1] . "\n"; // 2
// 固定 key/value 类型的映射 // 固定 key/value 类型的映射
$map = std::ordered_map(Type::String, Type::Int); $map = std::orderedMap(Type::String, Type::Int);
$map["a"] = 1; $map["a"] = 1;
$map["b"] = 2; $map["b"] = 2;
} }

@ -83,16 +83,16 @@ This two-phase design keeps multi-file and self-hosted builds deterministic.
- **High-precision numerics** — `bigInt` (GMP), `decimal` (libmpdec), and - **High-precision numerics** — `bigInt` (GMP), `decimal` (libmpdec), and
`bigFloat` (MPFR), with typed operators and method APIs. `bigFloat` (MPFR), with typed operators and method APIs.
- **Strongly-typed containers** — `std::array`, `std::vector`, `std::map`, and - **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`. PHP arrays and on par with C++ `std::vector`.
- **Universal methods** — call methods directly on primitives - **Universal methods** — call methods directly on primitives
(`$s->upper()`, `$arr->contains()`, `$big->mul(2)`); statically-known calls (`$s->upper()`, `$arr->contains()`, `$big->mul(2)`); statically-known calls
are resolved directly at compile time. are resolved directly at compile time.
- **Mixed C++ / PHP** — call C++ functions from PHP (and vice versa) for - **Mixed C++ / PHP** — call C++ functions from PHP (and vice versa) for
performance-critical kernels. performance-critical kernels.
- **Compile-time functions & keywords** — `std::any()`, `std::ref()`, - **Compile-time functions & keywords** — `std::any()`, `std::object()`,
`std::expected()`, `std::unexpected()`, plus `toObject()`, `toInt()`, `toString()`, `toArray()` and `std::ref()`, `std::expected()`, `std::unexpected()`, plus `toObject()`,
friends. `toInt()`, `toString()`, `toArray()` and friends.
- **Compile-time safety** — `#[Immutable]` read-only contracts and `#[ArrayDef]` - **Compile-time safety** — `#[Immutable]` read-only contracts and `#[ArrayDef]`
array-shape metadata, checked at compile time with zero runtime cost. array-shape metadata, checked at compile time with zero runtime cost.
- **Compile-time code generation** — `#[Getter]`, `#[Setter]`, `#[With]`, - **Compile-time code generation** — `#[Getter]`, `#[Setter]`, `#[With]`,
@ -490,7 +490,7 @@ function main(): void
echo $vector[1] . "\n"; // 2 echo $vector[1] . "\n"; // 2
// key-value map with fixed key/value types // 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["a"] = 1;
$map["b"] = 2; $map["b"] = 2;
} }

@ -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 TypePHP does not reserve global function names for compiler directives. The
compile-time API occupies two global class symbols: `Type::*` only describes compile-time API occupies two global class symbols: `Type::*` only describes
types for extension-method metadata, while `std::*` contains TypePHP built-in 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 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 PHP class and method names are. `Type::*` members are class constants, whose
@ -40,7 +41,7 @@ Constraints:
## `std::` compile-time entry points ## `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 | | 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::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::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::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::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::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::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::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::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::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 ## 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::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. - `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. - `std::expected()` / `std::unexpected()` generate `EXPECTED(...)` / `UNEXPECTED(...)` respectively and produce no PHP runtime function call.
Future refactoring goals: Future refactoring goals:

@ -175,7 +175,7 @@ incompatible with or more restrictive than standard PHP.
case fallthrough. The native `int/bool` switch path can currently retain C++ case fallthrough. The native `int/bool` switch path can currently retain C++
fallthrough, so project code should terminate every non-empty case explicitly. fallthrough, so project code should terminate every non-empty case explicitly.
- Appending, inserting, `unset()`, and wholesale replacement of `std::vector`, - 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 non-structural updates of existing elements can still be done with assignment
operators. operators.
- Fixed native typed object properties cannot be freely `unset()` with PHP's - Fixed native typed object properties cannot be freely `unset()` with PHP's

@ -248,7 +248,7 @@ The following types are explicitly forbidden as Native Class property types:
- `std\array` - `std\array`
- `std\vector` - `std\vector`
- `std\map` - `std\map`
- `std\ordered_map` - `std\orderedMap`
- Other Std Container types added later - 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. 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()`. - Used as the receiver of dynamic callbacks such as `call_user_func()`.
- Saved into ZendVM global variables or object properties. - 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. 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.

@ -171,13 +171,13 @@ $obj = (new MyClass())->toObject(MyClass::class); // Not needed
### Differences from std:: Types ### 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 | | **Purpose** | Numeric/boolean type optimization | Object type declaration |
| **Performance** | ⚡ High performance (native type) | 🐢 Standard (ZVAL) | | **Performance** | ⚡ High performance (native type) | 🐢 Standard (ZVAL) |
| **Memory** | 8B/1B | Pointer (16B+) | | **Memory** | 8B/1B | Pointer (16B+) |
| **Timing** | Runtime optimization | Compile-time lowering plus runtime check when needed | | **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::string` - strings use ZVAL (php::Str)
- ❌ `std::array` - arrays use ZVAL (php::Array) - ❌ `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) - ❌ All other types - use ZVAL (php::Var)
## Type Mapping Table ## Type Mapping Table

@ -105,7 +105,7 @@ The present invention defines a set of strongly-typed container construction syn
```php ```php
$a = std::array(Type::Int, 100); $a = std::array(Type::Int, 100);
$v = std::vector(Type::Float); $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); $h = std::map(Type::Int, User::class);
``` ```

@ -95,7 +95,7 @@ Currently supported:
- `std::array` - `std::array`
- `std::vector` - `std::vector`
- `std::ordered_map` - `std::orderedMap`
- `std::map` - `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. 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 ### 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 ```php
foreach ($vector as $index => $value) { 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. 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 ```php
function main(): void function main(): void
{ {
$map = std::ordered_map( $map = std::orderedMap(
Type::String, Type::String,
Type::Int Type::Int
); );
@ -242,17 +242,17 @@ Characteristics:
Example: Example:
```php ```php
$map = std::ordered_map(Type::Int, Type::Float); $map = std::orderedMap(Type::Int, Type::Float);
$map[10] = 1.25; $map[10] = 1.25;
$map[20] = 3.5; $map[20] = 3.5;
``` ```
ordered_map of the same type can be copied: orderedMap containers of the same type can be copied:
```php ```php
$a = std::ordered_map(Type::Int, Type::Int); $a = std::orderedMap(Type::Int, Type::Int);
$b = std::ordered_map(Type::Int, Type::Int); $b = std::orderedMap(Type::Int, Type::Int);
$b[10] = 100; $b[10] = 100;
$a = $b; $a = $b;
@ -319,7 +319,7 @@ class User
$vector = std::vector(User::class); $vector = std::vector(User::class);
$array = std::array(User::class, 10); $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. 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::array` to express fixed-length strongly-typed arrays
- use `std::vector` to express dynamic contiguous 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 - an ordinary variable receiving a std container is automatically converted to a PHP Array
- std containers of the same type support native copy - std containers of the same type support native copy
- UnsafePtr supports safely passing container references between native functions - UnsafePtr supports safely passing container references between native functions

@ -6,7 +6,7 @@
TypePHP 不再为编译器指令保留任何全局函数名。编译期 API 最终只占用两个全局类符号: TypePHP 不再为编译器指令保留任何全局函数名。编译期 API 最终只占用两个全局类符号:
`Type::*` 仅用于扩展方法等元数据中的类型表示,`std::*` 承载 TypePHP 内置函数。 `Type::*` 仅用于扩展方法等元数据中的类型表示,`std::*` 承载 TypePHP 内置函数。
对象类型断言使用 `toObject()` 关键词方法。 对象类型断言使用 `std::object()` 或等价的 `toObject()` 关键词方法。
遵循 PHP 规则,`std` / `Type` 类名以及 `std` 方法名不区分大小写;`Type::*` 遵循 PHP 规则,`std` / `Type` 类名以及 `std` 方法名不区分大小写;`Type::*`
成员是类常量,其常量名仍区分大小写。 成员是类常量,其常量名仍区分大小写。
@ -38,7 +38,7 @@ TypePHP 不再为编译器指令保留任何全局函数名。编译期 API 最
## `std::` 编译期入口 ## `std::` 编译期入口
当前 `std::` 编译期入口共 14 个。 当前 `std::` 编译期入口共 15 个。
| 名称 | 作用 | 主要限制 | | 名称 | 作用 | 主要限制 |
| --- | --- | --- | | --- | --- | --- |
@ -49,13 +49,14 @@ TypePHP 不再为编译器指令保留任何全局函数名。编译期 API 最
| `std::decimal($value)` | 构造 Decimal。 | float 变量需改用字符串或整型;float 字面量会按原始字面量处理。 | | `std::decimal($value)` | 构造 Decimal。 | float 变量需改用字符串或整型;float 字面量会按原始字面量处理。 |
| `std::bigFloat($value)` | 构造 BigFloat。 | 需要 1 个值参数。 | | `std::bigFloat($value)` | 构造 BigFloat。 | 需要 1 个值参数。 |
| `std::any([$value])` | 将表达式降级为 `mixed/any`;省略参数时,初始值为 `null`。 | Native 对象及包含 Native 对象的 std 容器不能通过它逃逸。 | | `std::any([$value])` | 将表达式降级为 `mixed/any`;省略参数时,初始值为 `null`。 | Native 对象及包含 Native 对象的 std 容器不能通过它逃逸。 |
| `std::object($value, ClassName::class)` | 检查对象并恢复具体类信息。 | 必须传入 2 个非展开参数,且类名必须能在编译期解析。 |
| `std::ref($target)` | 显式以引用方式传递目标。 | 只接受变量、数组元素或对象属性,且仅可作为调用参数的引用包装器。 | | `std::ref($target)` | 显式以引用方式传递目标。 | 只接受变量、数组元素或对象属性,且仅可作为调用参数的引用包装器。 |
| `std::expected($condition)` | 标记条件通常为真。 | 只接受一个非展开参数并返回 bool。 | | `std::expected($condition)` | 标记条件通常为真。 | 只接受一个非展开参数并返回 bool。 |
| `std::unexpected($condition)` | 标记条件通常为假。 | 只接受一个非展开参数并返回 bool。 | | `std::unexpected($condition)` | 标记条件通常为假。 | 只接受一个非展开参数并返回 bool。 |
| `std::array($type, $size[, ...$sizes])` | 构造固定大小 std array。 | 只能在变量首次赋值的顶层作用域使用。 | | `std::array($type, $size[, ...$sizes])` | 构造固定大小 std array。 | 只能在变量首次赋值的顶层作用域使用。 |
| `std::vector($type[, $size])` | 构造 std vector。 | 只能在变量首次赋值的顶层作用域使用。 | | `std::vector($type[, $size])` | 构造 std vector。 | 只能在变量首次赋值的顶层作用域使用。 |
| `std::map($keyType, $valueType)` | 构造 std map。 | 只能在变量首次赋值的顶层作用域使用。 | | `std::map($keyType, $valueType)` | 构造 std map。 | 只能在变量首次赋值的顶层作用域使用。 |
| `std::ordered_map($keyType, $valueType)` | 构造 std ordered map。 | 只能在变量首次赋值的顶层作用域使用。 | | `std::orderedMap($keyType, $valueType)` | 构造 std ordered map。 | 只能在变量首次赋值的顶层作用域使用。 |
## Std 容器转换关键词方法 ## Std 容器转换关键词方法
@ -80,7 +81,7 @@ TypePHP 不再为编译器指令保留任何全局函数名。编译期 API 最
- `std::any()` 使用统一的降级入口;赋值、参数、返回值、数组元素和运算子表达式共用相同语义。 - `std::any()` 使用统一的降级入口;赋值、参数、返回值、数组元素和运算子表达式共用相同语义。
- `std::ref()` / `toRef()` 在参数解析、SSA 和优化器路径中共用同一个引用包装识别入口。 - `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 运行时函数调用。 - `std::expected()` / `std::unexpected()` 分别生成 `EXPECTED(...)` / `UNEXPECTED(...)`,不产生 PHP 运行时函数调用。
后续重构目标: 后续重构目标:

@ -84,7 +84,7 @@
- `foreach` by reference 的 value 只能是变量。 - `foreach` by reference 的 value 只能是变量。
- `foreach` list destructuring 不支持按引用绑定元素。 - `foreach` list destructuring 不支持按引用绑定元素。
- 非 `int/bool` lowering 路径中的非空 `switch` case 必须以 `return`、`break`、`continue`、`exit` 或 `throw` 结束;不要依赖 PHP 的隐式 case fallthrough。当前 `int/bool` native switch 路径仍可保留 C++ fallthrough,因此项目代码应统一显式终止每个非空 case。 - 非 `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 typed object property 不允许按 PHP 未初始化语义自由 `unset()`。
- native 类型变量执行 `unset()` 不会产生标准 PHP 的变量删除语义。 - native 类型变量执行 `unset()` 不会产生标准 PHP 的变量删除语义。

@ -263,7 +263,7 @@ PHP 本身不允许将 `resource` 写成属性类型;TypePHP 中的 stream res
- `std\array` - `std\array`
- `std\vector` - `std\vector`
- `std\map` - `std\map`
- `std\ordered_map` - `std\orderedMap`
- 后续增加的其他 Std Container 类型 - 后续增加的其他 Std Container 类型
这些类型具有独立的泛型布局、引用或所有权语义,将它们嵌入 Native Class 会显著扩大首版类型组合和生命周期分析范围。开发者可以使用普通 PHP `array` 字段;PHP array 中仍然不能保存 Native Object,因为 Native Object 没有 `zval` 表示。 这些类型具有独立的泛型布局、引用或所有权语义,将它们嵌入 Native Class 会显著扩大首版类型组合和生命周期分析范围。开发者可以使用普通 PHP `array` 字段;PHP array 中仍然不能保存 Native Object,因为 Native Object 没有 `zval` 表示。
@ -864,7 +864,7 @@ Native Class 的字段可以保存 `php::Var`、`php::Array` 或 `php::Object`
- 保存到 ZendVM 全局变量或对象属性中。 - 保存到 ZendVM 全局变量或对象属性中。
Box 不能保存 Native Object。Std Container 不能作为 Native Class 属性,但局部 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 `NativeClass::class` 作为 value type,并保存该类或其 Native 子类。普通 PHP array
仍然不能保存 Native Object。 仍然不能保存 Native Object。

@ -170,13 +170,13 @@ $obj = (new MyClass())->toObject(MyClass::class); // 不需要
### 与 std:: 类型的区别 ### 与 std:: 类型的区别
| 特性 | std::int/float/bool | toObject | | 特性 | std::int/float/bool | std::object/toObject |
|------|---------------------|--------| |------|---------------------|--------|
| **用途** | 数值/布尔类型优化 | 对象类型声明 | | **用途** | 数值/布尔类型优化 | 对象类型声明 |
| **性能** | ⚡ 高性能(原生类型) | 🐢 标准(ZVAL) | | **性能** | ⚡ 高性能(原生类型) | 🐢 标准(ZVAL) |
| **内存** | 8B/1B | 指针(16B+) | | **内存** | 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::string` - 字符串使用 ZVAL (php::Str)
- ❌ `std::array` - 数组使用 ZVAL (php::Array) - ❌ `std::array` - 数组使用 ZVAL (php::Array)
- ❌ `std::object` - 对象使用 ZVAL (php::Object) - ❌ 对象仍使用 ZVAL(`php::Object`);`std::object()` 只恢复类信息,不引入原生对象存储
- ❌ 其他所有类型 - 使用 ZVAL (php::Var) - ❌ 其他所有类型 - 使用 ZVAL (php::Var)
## 类型映射表 ## 类型映射表

@ -105,7 +105,7 @@ Swoole-Compiler 是一种 PHP AOT 编译器。它将 PHP 源文件解析为抽
```php ```php
$a = std::array(Type::Int, 100); $a = std::array(Type::Int, 100);
$v = std::vector(Type::Float); $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); $h = std::map(Type::Int, User::class);
``` ```

@ -94,7 +94,7 @@ Swoole AOT 提供 `std` 容器,用来表达“这个容器的结构和元素
- `std::array` - `std::array`
- `std::vector` - `std::vector`
- `std::ordered_map` - `std::orderedMap`
- `std::map` - `std::map`
它们的目标不是完全替代 PHP Array,而是用于性能敏感、结构稳定、类型明确的代码路径。 它们的目标不是完全替代 PHP Array,而是用于性能敏感、结构稳定、类型明确的代码路径。
@ -202,7 +202,7 @@ $a = $b; // 允许,类型完全一致,执行容器 copy
### foreach 中修改元素 ### foreach 中修改元素
遍历 `std::vector`、`std::map` 或 `std::ordered_map` 时,可以更新已经存在的元素值,例如使用 `+=`: 遍历 `std::vector`、`std::map` 或 `std::orderedMap` 时,可以更新已经存在的元素值,例如使用 `+=`:
```php ```php
foreach ($vector as $index => $value) { foreach ($vector as $index => $value) {
@ -212,14 +212,14 @@ foreach ($vector as $index => $value) {
遍历期间不能执行可能使 C++ iterator 失效的结构修改,包括追加元素、插入或覆盖 key、`unset()` 以及整体替换容器。编译器会直接报告错误。需要改变结构时,先记录待处理的 key,结束 `foreach` 后再统一修改。 遍历期间不能执行可能使 C++ iterator 失效的结构修改,包括追加元素、插入或覆盖 key、`unset()` 以及整体替换容器。编译器会直接报告错误。需要改变结构时,先记录待处理的 key,结束 `foreach` 后再统一修改。
## std::ordered_map ## std::orderedMap
`std::ordered_map` 是有序 key-value 容器。 `std::orderedMap` 是有序 key-value 容器。
```php ```php
function main(): void function main(): void
{ {
$map = std::ordered_map( $map = std::orderedMap(
Type::String, Type::String,
Type::Int Type::Int
); );
@ -241,17 +241,17 @@ function main(): void
示例: 示例:
```php ```php
$map = std::ordered_map(Type::Int, Type::Float); $map = std::orderedMap(Type::Int, Type::Float);
$map[10] = 1.25; $map[10] = 1.25;
$map[20] = 3.5; $map[20] = 3.5;
``` ```
同类型 ordered_map 可以 copy: 同类型 orderedMap 容器可以 copy:
```php ```php
$a = std::ordered_map(Type::Int, Type::Int); $a = std::orderedMap(Type::Int, Type::Int);
$b = std::ordered_map(Type::Int, Type::Int); $b = std::orderedMap(Type::Int, Type::Int);
$b[10] = 100; $b[10] = 100;
$a = $b; $a = $b;
@ -318,7 +318,7 @@ class User
$vector = std::vector(User::class); $vector = std::vector(User::class);
$array = std::array(User::class, 10); $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::array` 表达固定长度强类型数组
- 用 `std::vector` 表达动态连续强类型数组 - 用 `std::vector` 表达动态连续强类型数组
- 用 `std::ordered_map` / `std::map` 表达强类型映射 - 用 `std::orderedMap` / `std::map` 表达强类型映射
- 普通变量接收 std 容器时自动转 PHP Array - 普通变量接收 std 容器时自动转 PHP Array
- 同类型 std 容器之间支持原生 copy - 同类型 std 容器之间支持原生 copy
- UnsafePtr 支持 native 函数间安全地传递容器引用 - UnsafePtr 支持 native 函数间安全地传递容器引用

@ -0,0 +1,40 @@
<?php
use PHPUnit\Framework\TestCase;
class StdObjectPolyfillBase
{
}
final class StdObjectPolyfillChild extends StdObjectPolyfillBase
{
}
final class StdObjectPolyfillTest extends TestCase
{
public function testReturnsAnObjectOfTheRequestedClass(): void
{
$object = new StdObjectPolyfillBase();
self::assertSame($object, std::object($object, StdObjectPolyfillBase::class));
$child = new StdObjectPolyfillChild();
self::assertSame($child, std::object($child, StdObjectPolyfillBase::class));
}
public function testRejectsANonObjectValue(): void
{
$this->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);
}
}

@ -0,0 +1,13 @@
<?php
use PHPUnit\Framework\TestCase;
final class StdOrderedMapPolyfillTest extends TestCase
{
public function testUsesCamelCaseFactoryName(): void
{
self::assertSame([], std::orderedMap(Type::String, Type::Int));
self::assertTrue(method_exists(std::class, 'orderedMap'));
self::assertFalse(method_exists(std::class, 'ordered_map'));
}
}

@ -2200,6 +2200,14 @@ class CompilerBase implements PropertyAccessContext
} }
} }
if ($this->isStaticCall($expr) and $this->isNameExpr($expr->class) and $this->isNamedMethod($expr->name)) { if ($this->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); $class = $this->parseIdentifier($expr->class);
if ($class === 'self') { if ($class === 'self') {
$class = $this->class; $class = $this->class;
@ -3239,6 +3247,7 @@ class CompilerBase implements PropertyAccessContext
'bigint' => Type::BIGINT, 'bigint' => Type::BIGINT,
'decimal' => Type::DECIMAL, 'decimal' => Type::DECIMAL,
'bigfloat' => Type::BIGFLOAT, 'bigfloat' => Type::BIGFLOAT,
'object' => Type::OBJECT,
'expected', 'unexpected' => Type::BOOL, 'expected', 'unexpected' => Type::BOOL,
default => Type::VAR, default => Type::VAR,
}; };

@ -1662,7 +1662,7 @@ trait NativeClassSupportTrait
'std\\array', 'std\\array',
'std\\vector', 'std\\vector',
'std\\map', 'std\\map',
'std\\ordered_map', 'std\\orderedmap',
], true); ], true);
} }

@ -614,7 +614,7 @@ trait AssignOpTrait
$class = $this->parseIdentifier($right->class); $class = $this->parseIdentifier($right->class);
if ($this->isStdClassExpr($right->class)) { if ($this->isStdClassExpr($right->class)) {
$stdMethod = strtolower($right->name->toString()); $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)) { if ($this->hasScopeGlobalVar($var) || $this->hasStaticVar($var)) {
$this->assertNativeStdContainerFunctionLocal($right); $this->assertNativeStdContainerFunctionLocal($right);
} }

@ -247,6 +247,25 @@ trait FunctionCallTrait
return $this->parseExprAsValue($value); 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 private function parseNamedExitMessageCall(string $name, Expr\FuncCall $expr): ?string
{ {
if (!in_array(strtolower($name), ['exit', 'die'], true) if (!in_array(strtolower($name), ['exit', 'die'], true)

@ -384,6 +384,9 @@ trait MethodCallTrait
if ($func === 'any') { if ($func === 'any') {
return $this->parseAnyCompileTimeCall($expr); return $this->parseAnyCompileTimeCall($expr);
} }
if ($func === 'object') {
return $this->parseObjectCompileTimeCall($expr);
}
if ($func === 'expected' || $func === 'unexpected') { if ($func === 'expected' || $func === 'unexpected') {
if (count($expr->args) !== 1 if (count($expr->args) !== 1
|| !$expr->args[0] instanceof Node\Arg || !$expr->args[0] instanceof Node\Arg

@ -37,7 +37,7 @@ trait StdContainerTrait
} }
$method = strtolower($expr->name->toString()); $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 ''; return '';
} }
@ -69,7 +69,7 @@ trait StdContainerTrait
} }
$typeInfo = $this->parseStdValueTypeInfo( $typeInfo = $this->parseStdValueTypeInfo(
$expr->args[$valueIndex]->value, $expr->args[$valueIndex]->value,
'std::' . $method, $method === 'orderedmap' ? 'std::orderedMap' : 'std::' . $method,
); );
$class = $typeInfo['class'] ?? ''; $class = $typeInfo['class'] ?? '';
return is_string($class) && $this->isNativeObjectClass($class) ? $class : ''; return is_string($class) && $this->isNativeObjectClass($class) ? $class : '';
@ -565,7 +565,7 @@ trait StdContainerTrait
$this->fatalError($expr, 'std container expects a variable'); $this->fatalError($expr, 'std container expects a variable');
} }
if (count($dims) !== 1) { 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]; $dim = $dims[0];
if ($dim === null) { if ($dim === null) {
@ -953,7 +953,7 @@ trait StdContainerTrait
protected function parseStdOrderedMap(string $var, Expr\StaticCall $expr): string 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 private function parseStdMapBase(string $var, Expr\StaticCall $expr, string $funcName, string $containerType, string $kind): string

@ -185,6 +185,24 @@ class std
return $value; 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 public static function &ref(mixed &$var): mixed
{ {
return $var; return $var;
@ -205,7 +223,7 @@ class std
return []; 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 []; return [];
} }

@ -0,0 +1,60 @@
--TEST--
std::object restores concrete object types and validates them at runtime
--FILE--
<?php
class StdObjectBase {
public function name(): string {
return 'base';
}
}
class StdObjectChild extends StdObjectBase {
public function childName(): string {
return 'child';
}
public function fromSelf(mixed $value): self {
return STD::ObJeCt($value, self::class);
}
public function fromParent(mixed $value): StdObjectBase {
return std::object($value, parent::class);
}
}
function restore_child(mixed $value): StdObjectChild {
return std::object($value, StdObjectChild::class);
}
function restore_child_by_name(mixed $value): StdObjectChild {
return std::object($value, 'StdObjectChild');
}
function main(): void {
$child = new StdObjectChild();
var_dump(restore_child($child)->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

@ -19,7 +19,7 @@ function main(): void
$array = std::array(NativeContainerValue::class, 1); $array = std::array(NativeContainerValue::class, 1);
$vector = std::vector(NativeContainerValue::class); $vector = std::vector(NativeContainerValue::class);
$map = std::map(Type::String, 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); $array[0] = new NativeContainerValue(11);
$vector[] = new NativeContainerValue(22); $vector[] = new NativeContainerValue(22);

@ -11,7 +11,7 @@ function main() {
} }
var_dump(count($map)); var_dump(count($map));
$ordered = std::ordered_map(Type::String, Type::Int); $ordered = std::orderedMap(Type::String, Type::Int);
try { try {
var_dump($ordered['missing']); var_dump($ordered['missing']);
} catch (Throwable $e) { } catch (Throwable $e) {

@ -1,9 +1,9 @@
--TEST-- --TEST--
std ordered_map: 001 std orderedMap: 001
--FILE-- --FILE--
<?php <?php
function main() { function main() {
$map = std::ordered_map(Type::Int, Type::Float); $map = std::orderedMap(Type::Int, Type::Float);
$map[10] = 1.25; $map[10] = 1.25;
$map[10] += 0.75; $map[10] += 0.75;
$map[11] = 3.5; $map[11] = 3.5;

@ -1,9 +1,9 @@
--TEST-- --TEST--
std ordered_map: string key std orderedMap: string key
--FILE-- --FILE--
<?php <?php
function main() { function main() {
$map = std::ordered_map(Type::String, Type::Int); $map = std::orderedMap(Type::String, Type::Int);
$map["alpha"] = 10; $map["alpha"] = 10;
$map["beta"] = 20; $map["beta"] = 20;
$map["beta"] += 22; $map["beta"] += 22;
@ -13,7 +13,7 @@ function main() {
var_dump($map["beta"]); var_dump($map["beta"]);
var_dump(count($map)); var_dump(count($map));
$map2 = std::ordered_map(Type::String, Type::Float); $map2 = std::orderedMap(Type::String, Type::Float);
$map2["pi"] = 3.14; $map2["pi"] = 3.14;
var_dump($map2["pi"] == 3.14); var_dump($map2["pi"] == 3.14);
} }

@ -1,5 +1,5 @@
--TEST-- --TEST--
std ordered_map: complex value types std orderedMap: complex value types
--FILE-- --FILE--
<?php <?php
class StdMapComplexValue class StdMapComplexValue
@ -15,23 +15,23 @@ class StdMapComplexValue
} }
function main() { function main() {
$strings = std::ordered_map(Type::Int, Type::String); $strings = std::orderedMap(Type::Int, Type::String);
$strings[1] = 456; $strings[1] = 456;
var_dump($strings[1]); var_dump($strings[1]);
$arrays = std::ordered_map(Type::Int, Type::Array); $arrays = std::orderedMap(Type::Int, Type::Array);
$arrays[2] = ["name" => "map", "value" => 84]; $arrays[2] = ["name" => "map", "value" => 84];
$array = $arrays[2]; $array = $arrays[2];
var_dump($array["name"]); var_dump($array["name"]);
var_dump($array["value"]); var_dump($array["value"]);
$objects = std::ordered_map(Type::String, Type::Object); $objects = std::orderedMap(Type::String, Type::Object);
$objects["item"] = new StdMapComplexValue(14); $objects["item"] = new StdMapComplexValue(14);
var_dump($objects["item"] instanceof StdMapComplexValue); var_dump($objects["item"] instanceof StdMapComplexValue);
$object = $objects["item"]->toObject(StdMapComplexValue::class); $object = $objects["item"]->toObject(StdMapComplexValue::class);
var_dump($object->getValue()); var_dump($object->getValue());
$variants = std::ordered_map(Type::Int, Type::Any); $variants = std::orderedMap(Type::Int, Type::Any);
$variants[3] = 12.5; $variants[3] = 12.5;
$variants[4] = "any"; $variants[4] = "any";
var_dump($variants[3]); var_dump($variants[3]);

@ -28,7 +28,7 @@ function std_container_class_value_mixed(mixed $value): mixed
} }
function main() { function main() {
$map = std::ordered_map(Type::String, StdContainerClassValue::class); $map = std::orderedMap(Type::String, StdContainerClassValue::class);
$map["a"] = new StdContainerClassValue(1); $map["a"] = new StdContainerClassValue(1);
$item = $map["a"]; $item = $map["a"];
var_dump($item->getValue()); var_dump($item->getValue());

@ -1,5 +1,5 @@
--TEST-- --TEST--
std ordered_map: unsafe_cast std orderedMap: unsafe_cast
--FILE-- --FILE--
<?php <?php
function std_map_unsafe_ptr_update($source): void function std_map_unsafe_ptr_update($source): void
@ -15,7 +15,7 @@ function std_map_unsafe_ptr_update($source): void
} }
function main() { function main() {
$map = std::ordered_map(Type::String, Type::Int); $map = std::orderedMap(Type::String, Type::Int);
$map["a"] = 1; $map["a"] = 1;
$map["b"] = 7; $map["b"] = 7;
$map["c"] = 3; $map["c"] = 3;

@ -1,5 +1,5 @@
--TEST-- --TEST--
std ordered_map: unsafe_cast type mismatch std orderedMap: unsafe_cast type mismatch
--FILE-- --FILE--
<?php <?php
function std_map_unsafe_ptr_type_mismatch($source): void function std_map_unsafe_ptr_type_mismatch($source): void
@ -8,7 +8,7 @@ function std_map_unsafe_ptr_type_mismatch($source): void
} }
function main() { function main() {
$map = std::ordered_map(Type::String, Type::Int); $map = std::orderedMap(Type::String, Type::Int);
try { try {
std_map_unsafe_ptr_type_mismatch($map); std_map_unsafe_ptr_type_mismatch($map);
} catch (TypeError $e) { } catch (TypeError $e) {

@ -1,9 +1,9 @@
--TEST-- --TEST--
std ordered_map: assign to PHP array std orderedMap: assign to PHP array
--FILE-- --FILE--
<?php <?php
function main() { function main() {
$map = std::ordered_map(Type::Int, Type::Int); $map = std::orderedMap(Type::Int, Type::Int);
$map[10] = 100; $map[10] = 100;
$map[20] = 200; $map[20] = 200;

@ -1,10 +1,10 @@
--TEST-- --TEST--
std ordered_map: same type copy std orderedMap: same type copy
--FILE-- --FILE--
<?php <?php
function main() { function main() {
$a = std::ordered_map(Type::Int, Type::Int); $a = std::orderedMap(Type::Int, Type::Int);
$b = std::ordered_map(Type::Int, Type::Int); $b = std::orderedMap(Type::Int, Type::Int);
$b[10] = 100; $b[10] = 100;
$b[20] = 200; $b[20] = 200;

@ -1,9 +1,9 @@
--TEST-- --TEST--
std ordered_map: unset std orderedMap: unset
--FILE-- --FILE--
<?php <?php
function main() { function main() {
$map = std::ordered_map(Type::String, Type::Int); $map = std::orderedMap(Type::String, Type::Int);
$map["alpha"] = 10; $map["alpha"] = 10;
$map["beta"] = 20; $map["beta"] = 20;
var_dump($map); var_dump($map);
@ -21,4 +21,4 @@ array(2) {
array(1) { array(1) {
["beta"]=> ["beta"]=>
int(20) int(20)
} }

@ -1,9 +1,9 @@
--TEST-- --TEST--
std ordered_map: unset std orderedMap: unset
--FILE-- --FILE--
<?php <?php
function main() { function main() {
$map = std::ordered_map(Type::String, Type::Int); $map = std::orderedMap(Type::String, Type::Int);
$map["alpha"] = 10; $map["alpha"] = 10;
$map["beta"] = 20; $map["beta"] = 20;
$map["gamma"] = 30; $map["gamma"] = 30;
@ -29,4 +29,4 @@ unset-------------
string(5) "alpha" string(5) "alpha"
int(10) int(10)
string(5) "gamma" string(5) "gamma"
int(30) int(30)

@ -43,7 +43,7 @@ function main() {
$map["item"] = std_container_interface_mixed(new StdContainerInterfaceImpl(4)); $map["item"] = std_container_interface_mixed(new StdContainerInterfaceImpl(4));
var_dump($map["item"]->getValue()); var_dump($map["item"]->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)); $ordered["item"] = std_container_interface_mixed(new StdContainerInterfaceImpl(5));
var_dump($ordered["item"]->getValue()); var_dump($ordered["item"]->getValue());

@ -13,7 +13,7 @@ function main(): void
} }
var_dump($vector[0], $vector[1]); 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['a'] = 3;
$map['b'] = 4; $map['b'] = 4;
foreach ($map as $mapKey => $mapValue) { foreach ($map as $mapKey => $mapValue) {

@ -18,7 +18,7 @@ function main(): void
$vector[] = 'wasm'; $vector[] = 'wasm';
$map = std::map(Type::String, Type::Int); $map = std::map(Type::String, Type::Int);
$map['answer'] = 42; $map['answer'] = 42;
$ordered = std::ordered_map(Type::String, Type::Int); $ordered = std::orderedMap(Type::String, Type::Int);
$ordered['first'] = 1; $ordered['first'] = 1;
echo $array[0], '|', $vector[0], '|', $map['answer'], '|', $ordered['first'], "\n"; echo $array[0], '|', $vector[0], '|', $map['answer'], '|', $ordered['first'], "\n";

Loading…
Cancel
Save