- Replace any() with std::any() in test files and compiler logic
- Replace refval() with std::ref() for reference wrapping operations
- Replace expected() with std::expected() for branch prediction hints
- Replace unexpected() with std::unexpected() for branch prediction hints
- Remove deprecated objval() function and use toObject() keyword method
- Update parser to recognize std:: class expressions with case insensitivity
- Add comprehensive changelog documenting breaking API changes
- Update documentation to reflect new std:: namespace conventions
- Add tests for case-insensitive std class and method names
- Verify global function names remain available for user-defined functions
This document records the compile-time functions, keyword methods, and related construction entry points that are specific to the AOT compiler. They are not part of standard PHP syntax, and an ordinary PHP runtime can only rely on the compatibility stubs provided by `src/polyfills.php`.
## Core compile-time functions
## Global function names
There are currently 5 core global compile-time functions.
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.
| Name | Parameters | Purpose | Current primary handling location |
| --- | --- | --- | --- |
| `any($value)` | 1 | Degrades the expression to `mixed/any`, preventing further processing as a static native/object type. | General function-call expression entry. |
| `refval($target)` | 1 | Explicitly passes a variable, array element, or object property by reference to a dynamic call or a call whose reference parameter cannot be statically identified. | Argument parsing, dynamic calls, SSA/optimizer reference escape analysis. |
| `objval($value, ClassName::class or 'ClassName')` | 2 | Tells the compiler that `$value` is an object of the specified class, and generates the `php::toObject(..., target_ce)` runtime fallback check. | Function-call resolution, object type inference. |
| `expected($condition)` | 1 | Marks the condition as usually true, generating the Zend `EXPECTED(...)` branch prediction macro. | General function-call expression entry. |
| `unexpected($condition)` | 1 | Marks the condition as usually false, generating the Zend `UNEXPECTED(...)` branch prediction macro. | General function-call expression entry. |
Constraints:
- `refval()` only accepts variables, array elements, or object properties.
- The second parameter of `objval()` must be a compile-time-resolvable class-name string or `ClassName::class`.
- `any()` can be used in any expression position; it directly expands its single argument at compile time without generating a runtime function call.
- `expected()` / `unexpected()` accept exactly one non-expanded argument and return bool; they are usually used in `if`, `elseif`, and loop conditions, and do not change the argument's evaluation count or true/false semantics.
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
names remain case-sensitive.
## Keyword methods
@ -27,8 +19,8 @@ There are currently 12 built-in keyword methods.
| Name | Equivalent behavior | Description |
| --- | --- | --- |
| `toAny()` | `any($receiver)` | Returns the receiver itself, but with the type degraded to `mixed/any`. |
| `toRef()` | `refval($receiver)` | Returns a reference to the receiver; parameter restrictions are the same as `refval()`. |
| `toAny()` | `std::any($receiver)` | Returns the receiver itself, but with the type degraded to `mixed/any`. |
| `toRef()` | `std::ref($receiver)` | Returns a reference to the receiver; parameter restrictions are the same as `std::ref()`. |
| `toObject()` | `php::toObject($receiver)` | May take a target-class parameter, performing object conversion/checking. |
| `toInt()` | `php::toInt($receiver)` | Converts to a native int expression. |
| `toFloat()` | `php::toFloat($receiver)` | Converts to a native float expression. |
@ -46,9 +38,9 @@ Constraints:
- `toRef()` only applies to receivers that can take references.
- Keyword methods take precedence over ordinary methods and universal method dispatch.
## `std::` compile-time construction entry points
## `std::` compile-time entry points
There are currently 10 `std::` compile-time construction entry points.
There are currently 14 `std::` compile-time entry points.
| Name | Purpose | Main limitation |
| --- | --- | --- |
@ -58,6 +50,10 @@ There are currently 10 `std::` compile-time construction entry points.
| `std::bigInt($value)` | Constructs a BigInt. | Implicit construction from a float variable is not allowed. |
| `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`. | Native objects and native-object std containers cannot escape through it. |
| `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. |
@ -76,7 +72,7 @@ There are currently 4 Std container conversion keyword methods.
## Mechanisms not counted in this list
- `$array->any()` is a universal method that maps to PHP `array_any()`, not the `any()` compile-time function.
- `$array->any()` is a universal method that maps to PHP `array_any()`, not the `std::any()` compile-time function.
- `Type::*` are compile-time type-description constants, not functions.
- keyword extension methods are a user-defined extension method mechanism and are not part of the fixed built-in compile-time function list.
@ -84,13 +80,13 @@ There are currently 4 Std container conversion keyword methods.
Compile-time functions should be usable in any legal expression position and maintain consistent semantics across all paths:
- `any()` is already handled uniformly at the ordinary function-call expression entry; assignments, parameters, return values, array elements, and operator subexpressions share the same semantics.
- `refval()` / `toRef()` have many special cases in argument parsing and dynamic call paths and should later be unified into a single "reference-wrapping expression" resolution entry.
- `objval()` is currently recognized through the function-call resolution and type-inference paths and is relatively centralized.
- `expected()` / `unexpected()` generate `EXPECTED(...)` / `UNEXPECTED(...)` respectively at the ordinary function-call entry and produce no PHP runtime function call.
- `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::expected()` / `std::unexpected()` generate `EXPECTED(...)` / `UNEXPECTED(...)` respectively and produce no PHP runtime function call.
Future refactoring goals:
- Establish a unified `CompileTimeFunctionResolver` or equivalent module.
- Reuse the same compile-time function metadata in `parseExpr()` / `detectTypeOfExpr()` / `detectClassOfExpr()` / argument parsing paths.
- Continue unifying the behavior of `refval()` and `objval()` across different expression paths.
- Continue unifying reference-wrapper behavior across different expression paths.
When obtaining objects from sources such as arrays or function return values, variables lose their type context information. In such cases you need to use `objval()` to explicitly declare the class of the object.
When obtaining objects from sources such as arrays or function return values, variables lose their type context information. Use the `toObject()` keyword method to assert the object's class.
### Basic Syntax
```php
<?php
// objval takes two arguments:
// 1. The object variable (must be a PHP variable expression)
// 2. The class name (must be a literal string)
$obj = objval($array['object'], 'ClassName');
// The receiver is the value to check. The argument is a compile-time class name.
@ -77,7 +77,7 @@ These items should be documented with the exact boundary.
| Reserved keyword methods such as `toArray()` | Intentional Rule | Conversion keywords are resolved before ordinary object methods to keep conversion lowering static and predictable. |
| Zero-initialized fixed typed property slots | Intentional Rule / Partial | Native fixed-layout slots use their type's zero value instead of preserving every Zend uninitialized-property transition. |
| Structural mutation of `std` containers during `foreach` | Intentional Rule | Native C++ iterators may be invalidated by append, insertion, erase or whole-container replacement. TypePHP rejects these operations inside the active loop while allowing non-structural element updates. |
| Automatic reference inference for dynamic calls | Intentional Rule | A runtime callable may resolve to a function, method, or Closure unknown to the compiler. TypePHP does not mirror callable signatures at runtime; callers must use `refval()` / `toRef()` explicitly. |
| Automatic reference inference for dynamic calls | Intentional Rule | A runtime callable may resolve to a function, method, or Closure unknown to the compiler. TypePHP does not mirror callable signatures at runtime; callers must use `std::ref()` / `toRef()` explicitly. |
| By-reference variadic parameters on dynamic Closures | Intentional Rule | Supporting `&...` here would require signature-aware runtime argument packing. Statically resolved ordinary functions and methods support `&...`; dynamic Closures do not. |
## Implementable but Currently Unsupported
@ -107,7 +107,7 @@ These items should be documented with the exact boundary.
| Feature | Classification | Boundary |
|---|---|---|
| `eval()` | Partial / Hard Limit | `eval()` can execute PHP code through Zend VM, but it cannot access compiled local variables. Use return values or `$GLOBALS` for data exchange. |
| Dynamic calls and callbacks | Partial | Zend runtime fallback handles dynamic calls and callbacks. By-reference arguments still need explicit `refval()` / `toRef()`, and native-call optimization is not guaranteed. |
| Dynamic calls and callbacks | Partial | Zend runtime fallback handles dynamic calls and callbacks. By-reference arguments still need explicit `std::ref()` / `toRef()`, and native-call optimization is not guaranteed. |
| Dynamic properties and dynamic property chains | Partial | Dynamic property reads and writes use the runtime property API; native property optimization is not guaranteed. |
| Native typed properties | Partial / Intentional Rule | Fast native paths may not preserve every PHP dynamic state transition. Unknown or incompatible values can fall back to `setProperty()`. |
| Reflection metadata | Partial | Runtime declarations preserve constructor-promotion and asymmetric-visibility flags; other AOT-specific metadata may still be incomplete. |
- [Three Object Storage and Passing Models](OBJECT_STORAGE_AND_PASSING_MODELS.md): the responsibilities, ABI, and non-substitutable boundaries of Zend Object, PHPX Box, and Native Class Object.
- [Universal and Extension Methods](UNIVERSAL_METHODS.md), [Generator](YIELD_GENERATOR.md).
The current core classes of the AOT compiler carry too many responsibilities. In particular, classes such as `CompilerBase` and `Translator` simultaneously contain AST dispatch, type inference, property access resolution, call resolution, code generation, diagnostics, and context state maintenance. As functionality continues to grow, this structure causes the following problems:
- Insufficient encapsulation: modifying one semantic point easily affects multiple code paths.
- Insufficient code reuse: similar logic is repeatedly implemented across normal properties, static properties, nullsafe, assignment, isset/empty/refval, and other paths.
- Insufficient code reuse: similar logic is repeatedly implemented across normal properties, static properties, nullsafe, assignment, isset/empty/`std::ref()`, and other paths.
- Compile-time checks are prone to bypass paths: for example, some dynamic fallbacks do not reuse the static resolver.
- Individual classes are too large, and review, test localization, and long-term maintenance costs keep rising.
- Design boundaries are unclear: the type system, symbol resolution, property access, and call generation are too deeply coupled.
@ -143,7 +143,7 @@ Paths that need unified coverage:
- `static::$prop`
- `isset($obj->prop)`
- `empty($obj->prop)`
- `refval($obj->prop)`
- `std::ref($obj->prop)`
- normal assignment, compound assignment, increment/decrement, unset.
It is recommended to start from this module as the first priority, because recent problems are concentrated in property access and visibility bypass, and the test boundaries are relatively clear.
@ -169,7 +169,7 @@ Recommended interfaces:
Design requirements:
- When the parameter information of a static function or built-in function is clear, references can be automatically converted.
- For dynamic calls, closures, and cases where by-ref parameter information cannot be obtained at compile time, an explicit `refval()` must be required.
- For dynamic calls, closures, and cases where by-ref parameter information cannot be obtained at compile time, an explicit `std::ref()` must be required.
- When using unpack with trailing named args appended, it should degrade to a dynamic call and must not go through a native call.
### 5. ExpressionEmitter
@ -255,7 +255,7 @@ Current progress:
Status:
- Phase 1 is essentially wrapped up. Unless property read resolver bypass or behavior regression is found later, the scope of Phase 1 will not be further expanded.
- Phase 2 has begun; assignment, compound assignment, inc/dec, unset, and refval paths related to property writes still need to be further unified.
- Phase 2 has begun; assignment, compound assignment, inc/dec, unset, and `std::ref()` paths related to property writes still need to be further unified.
Verification:
@ -297,7 +297,7 @@ Current progress:
- `getProperty()` / `setProperty()` generation for dynamic object properties has been converged into the `emitDynamicPropertyRead()` / `emitDynamicPropertyWrite()` helpers; normal dynamic property assignment, compound assignment, and increment/decrement now reuse this entry point.
- The dynamic property path of compound assignment has been connected to `preparePropertyWriteTarget()`, uniformly completing property write target preparation and static checks first.
- `PropertyWriteTarget` has begun carrying the object/property expressions of safe dynamic property write targets; normal dynamic property assignment, compound assignment, and increment/decrement now prefer emitting code through target-level read/write helpers.
- Dynamic property `unset`, property array dimension writes, and safe object property reference paths in reference arguments/refval/reference assignment have begun reusing target-level unset/ref helpers.
- Dynamic property `unset`, property array dimension writes, and safe object property reference paths in reference arguments/`std::ref()`/reference assignment have begun reusing target-level unset/ref helpers.
- Target/ref generation for object property reference expressions has been converged into `emitDynamicPropertyFetchRef()`; the unused old static property assignment entry point has been deleted, and static property assignment continues through the unified assignment target path.
- The dynamic object/property fields of `PropertyWriteTarget` have been encapsulated as getters; property array dimension writes have been connected to target-level append/update emitters.
- Established the `emitDynamicPropertyFetchRead/Write/Unset/AppendArray/UpdateArray()` wrapper layer; callers only pass in the property access AST and an optional target, and `CompilerBase` uniformly selects the target path or the old fallback path.