TypePHP 编译器 https://swoole.com/aot/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

11 KiB

AOT/PHP Incompatibility List

This document lists only the key areas in which the current AOT compiler is incompatible with or more restrictive than standard PHP.

Program structure

  • Executable statements are not allowed at global scope; only static constructs such as declarations, use, declare, and constant definitions are allowed.
  • Function declarations are not allowed inside functions or methods.
  • Named class declarations are not allowed inside functions or methods.
  • Binary mode must define a global main().
  • main() may either take no parameters or have the signature (int $argc, array $argv).
  • main() must return void.

Declarations and types

  • Variable variables $$var are not supported.
  • PHP 8.5 #[NoDiscard] is not supported yet.
  • The PHP 8.5 (void) cast is supported for explicitly discarding a value; the operand is still evaluated and its side effects are preserved, and the cast cannot be used in value contexts such as assignments, returns, arguments, or conditions.
  • Support for PHP 8.5 clone() / clone-with requires the linked libphp to be version 8.5 or later. Public and dynamic properties, private/protected/readonly properties, property hooks, call ordering, error propagation, and callable paths are all covered by PHPT tests.
  • PHP 8.4 property hooks are compiled into AOT getters/setters and register the corresponding Zend hook metadata; direct property reads/writes, Reflection, and object iteration are all supported. Taking a reference to a hooked property is currently not supported.
  • PHP 8.4 Reflection Lazy Objects cannot be used with TypePHP AOT classes. AOT classes are registered as persistent internal classes, and Zend's zend_object_make_lazy() explicitly rejects internal classes. Zend PHP user classes loaded dynamically at runtime are not subject to this restriction.
  • private(set) and protected(set) asymmetric property visibility is supported, including constructor property promotion. Zend-backed objects perform the scope check through the PHP 8.4+ class-level object handler and preserve the promoted / set-visibility / implicit-final reflection flags; Native objects enforce the equivalent scope rules through compile-time access checks.
  • Final properties declared through constructor promotion are supported, but TypePHP requires an explicit public, protected, or private modifier; PHP 8.5's implicitly public form, final int $value, is not accepted. As a TypePHP extension, this syntax is independent of the PHP source-syntax version supported by the linked libphp and remains available when using a PHP 8.4 libphp.so.
  • TypePHP forbids attributes on global or namespaced constant declarations; PHP 8.5 global constant attributes are out of scope. Class constant attributes are not affected by this restriction.
  • Returning by reference from closures or arrow functions is not supported.
  • PHP 8.5 static function expressions in global constants, class constants, parameter defaults, or property defaults are not supported yet. Closures nested inside initializer expressions are likewise rejected at compile time.
  • __construct() may not have a return value.
  • A parameter with a default value may not appear before a required parameter (PHP permits this legacy pattern but treats the former parameter as required).
  • Variadic parameters by reference &...$args are not supported.
  • Union, intersection, and nullable types are still represented as mixed/any in C++, but the static analysis phase uses known expression types to reject definitely incompatible arguments, return values, and property assignments ahead of time; dynamic values still retain their runtime type checks.
  • Once a local variable's type has been statically inferred as a concrete native type, reassigning it to an incompatible type within the same scope is not supported.

declare

  • declare(ticks=...) is not supported.
  • declare(encoding=...) accepts only UTF-8.
  • declare(strict_types=...) accepts only strict_types=1.
  • No other declare directives are supported.

Calls and references

  • exit(message: $value) is available as a TypePHP named-argument extension; it enters the same exit path as the positional form exit($value).
  • TypePHP uses strict argument-count rules: non-variadic functions do not accept extra arguments beyond the declared signature, and func_get_args() does not implicitly relax the signature.
  • Reference parameters and write-back semantics are supported for ordinary functions, ordinary methods, and native direct calls with known signatures; do not mistakenly describe the compiler's internal cross-trait dynamic-dispatch limitation as "TypePHP does not support reference parameters".
  • Closures and arrow functions do not support reference parameters.
  • Reference assignment cannot create a reference from a complex static-property expression.
  • Calls whose argument signature cannot be determined at compile time — dynamic calls, closure calls, and the like — cannot convert reference parameters automatically; refval() or the equivalent keyword method toRef() must be used explicitly.
  • refval() / toRef() only accept variables, array elements, or object properties.
  • A call that uses argument unpacking followed by named arguments falls back to dynamic dispatch and cannot use the native call path.

Object model

  • Reserved keyword methods such as toInt(), toString(), and toArray() are resolved before ordinary object methods; an application method of the same name that takes arguments is not called with ordinary object-method semantics.
  • toAny() and toRef() are non-overridable TypePHP keyword methods, and ordinary class-like declarations must not define methods with these names (method names are case-insensitive, per PHP rules). A Native class may only explicitly define a toAny() conversion method returning mixed/any; no implicit conversion is provided. Native classes do not support toRef().
  • Fixed-layout typed properties that are not explicitly initialized use the zero value of their type and do not preserve Zend PHP's full uninitialized state; expressions such as ?? that depend on the uninitialized state may therefore behave differently.
  • A subclass may not shadow a parent's private property with a private property of the same name; public / protected declarations of the same name are treated as the same inherited property slot and must still satisfy the type, visibility, and readonly compatibility requirements.
  • To avoid introducing extra dynamic checks in the typed-property write path, a native typed property falls back to setProperty() when the right-hand side's type is unknown or inconsistent with the property type; some scalar assignments may then follow Zend's weak type conversion instead of the AOT default strict semantics.

Expressions and control flow

  • A match arm condition may not itself be a match expression.
  • The value target in a by-reference foreach may only be a variable.
  • foreach list destructuring does not support binding elements by reference.
  • Appending, inserting, unset(), and wholesale replacement of std::vector, std::map, and std::ordered_map 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 standard uninitialized-property semantics.
  • Calling unset() on a native-typed variable does not delete the variable as it would in standard PHP.

Runtime dynamic capabilities

  • ClassName::class only supports string literals or statically resolvable class names.
  • static::class is not supported in positions that require a compile-time constant class name.
  • __CLASS__ may only be used within a class definition (PHP allows it elsewhere and returns an empty string).
  • __TRAIT__ may only be used within a trait definition (PHP allows it elsewhere and returns an empty string).
  • Dynamic property chains, dynamic class names, dynamic function names, and dynamic callbacks all go through the Zend runtime fallback and are not guaranteed to be natively optimized; reference parameters of dynamic calls still require an explicit refval() or toRef().
  • When Closure::bind() binds a static closure that accesses private members, the current behavior is not fully consistent with standard PHP.
  • Storing a first-class callable in a nullable typed Closure property currently has runtime stability limitations.
  • All source files must be encoded as UTF-8.

Compiler bootstrapping and internal refactoring constraints

This section describes the constraints that apply when the compiler itself is compiled with TypePHP. These are not additional PHP semantic differences for user code.

  • Before the refactoring, a statically resolvable $this->method() call within the same core class generated a native C++ direct call. Reference parameters were mapped directly to php::Ref or a C++ reference, and write-back semantics worked normally.
  • After splitting the caller and callee into different traits, compiling the trait body on its own makes it impossible to determine the final host class from the trait's $this. The current method resolver may lower a cross-trait call to a Zend method call, for example generating this_.call(..., php::ArgList{value}).
  • The ArgList of a dynamic method call does not automatically promote ordinary arguments to references based solely on the callee wrapper's arginfo. If the callee method declares &$value, the wrapper fetches the argument through getCallArgByRef(), while the caller passes an ordinary value; the result is a must be passed by reference warning, and modifications made by the callee cannot be written back to the caller.
  • Therefore, cross-trait APIs inside the compiler must not use reference output parameters or a protocol in which a passed scalar or array is modified and then read by the caller. They should return a result value, a tuple array, or a DTO — for example, use [$type, $class] = resolveTypeDecl(...) instead of parseTypeDecl(..., &$class).
  • Internal helpers for string accumulation, array sorting, parse-result output, and the like should preferably be designed around pure return values: $code .= format(...), $files = sort(...). Only when the call is confirmed to stay a native direct call may reference write-back be relied upon.
  • Every time a method is moved to a trait, a parent class, or a separate component, at least one test covering that call must be recompiled with the bootstrapped artifact; running tests only with bin/tpc.php cannot reveal problems where "the source compiler works but the bootstrapped compiler regresses".