- Add objects section to project.yml for linking precompiled .o/.obj files
- Implement getProjectObjectFiles method to retrieve precompiled objects
- Add c-flags and asm-flags support in CompilerBase
- Extend GCC backend to handle cflags and nativeflags configuration
- Update compiler to include project object files in linking process
- Add freestanding C++ allocation ABI implementation using Zend MM
- Include example TypePHP OS with kernel entry point and hardware stubs
- Add assembly files for boot sequence and kernel entry point
- Implement basic libc compatibility layer for Zend bootstrap
- Add try/catch macro replacement in generated code indentation
- Create bridge between C++ kernel and PHP code
- Add linker script for 64-bit kernel binary generation
- Add native source project compilation workflow with project.xml configuration
- Implement closure reference capture analysis and type degradation for native objects
- Add WASI and nano mode function support checks with comprehensive API exclusions
- Introduce Composer native package discovery and validation system
- Add build scripts for WASI nano program compilation with browser module support
- Implement closure generator enhancements for reference capture handling
- Add CLI usage tests and compiler base API validations
- Integrate php-nano runtime dependencies and native package management
- Add type degradation analysis for closure use variables in generators and functions
- 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
* fix(compiler): support first-class callable of internal functions
PHP 8.1 `foo(...)` builds a Closure from a single VariadicPlaceholder
argument instead of calling foo. Two paths mishandled it:
- checkInternalFunctionArgCount() counted the placeholder as a real
argument, so first-class callables of internal functions requiring 2+
parameters (e.g. `posix_kill(...)`) failed with a bogus "expects at
least N arguments, 1 given".
- The math-function return-type optimization read `$expr->args[0]->value`
on callables such as `round(...)`, crashing on the undefined
VariadicPlaceholder::$value property.
Guard both with isFirstClassCallable() so the expression falls through to
Closure (Type::OBJECT) resolution.
* fix(compiler): infer builtin callables before return optimizations
---------
Co-authored-by: Tinywan <tinywan@gmail.com>
- Removed redundant zval macro handling for property access
- Replaced Z_LVAL_P and Z_DVAL_P calls with direct attribute accessors
- Updated property getter logic to use attrInt and attrFloat methods
- Modified tests to match new property access pattern
- Added comprehensive test case for inherited property reference handling
- Implement Android platform class with API level detection and compiler configuration
- Add Android-specific library linking with proper archive grouping and system libraries
- Integrate Android target detection and SDK directory resolution in build configuration
- Add Android-specific compile flags including static libstdc++ and security hardening
- Include Android platform support in translator and compiler base classes
- Add PHPX_ANDROID=1 define flag for Android builds
- Support
- Add five new reference types (INT_REF, STR_REF, FLOAT_REF, BOOL_REF, ARRAY_REF)
- Create typed reference ABI using C++ references for strict type safety
- Implement RefWrap<T> bridge for dynamic PHP reference compatibility
- Add getRawVarType() and update getVarType() to handle reference unwrapping
- Enforce strict typing rules preventing fixed storage corruption via references
- Support typed property and array element reference handling
- Implement exception-safe reference binding with proper
- 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
- Add function call cache infrastructure with typephp_call_cached wrapper
- Implement method call caching using typephp_call_method_cached for named methods
- Add scoped method call caching via typephp_call_method_scoped_cached for dynamic scopes
- Create late-static-bound class entry tracking with getCalledCeExpr and getCalledClassExpr
- Implement lazy static property slot resolution with typephp_get_static_property_cached
- Add benchmark cases for dynamic static method calls and scoped method invocations
- Update call cache test expectations to reflect new cached call implementations
- Integrate call caching into function and method call compilation paths
- Add nullsafe method call caching support in nullsafe access trait
- Implement static property slot registration and resolution mechanisms
- Update native property handling to use cached static property access
- Add comprehensive test coverage for various dynamic call scenarios
- Implement exact-class fast path for zero-argument late-static calls with fallback mechanism
- Add static property fetch resolution using typephp_get_static_property symbol
- Enhance isset operations on static array properties with direct typephp_array_isset calls
- Update class reference detection to return Type::STR for class constant fetch expressions
- Add benchmark suite for static cache performance measurement including late static dispatch
- Modify compiler symbols to use typephp variants for called class and static property access
- Create comprehensive tests for static array single offset presence and late static call exact guards
- Update unit tests to reflect new symbol names and variable
- Add fullStatic property to CompilerBase for --full-static flag
- Register --full-static option in Constants with description
- Implement getFullStaticSdkDir method to resolve SDK path
- Modify getIncludePaths to use SDK headers when full static enabled
- Update getLibraryPaths to use
The >=16-significant-digit float-literal-to-php::Decimal promotion
(docs/en/HIGH_PRECISION_TYPES.md) counted every digit in the raw
literal with preg_replace('/[^0-9]/'), so exponent digits and trailing
zeros counted as significant: 1.23456789012345e300 (15 significant
digits) and 999999999999999.0 became Decimal, making
is_float(2.220446049250313E-16) compile to false. Hex literals whose
digits contain E (0x123456789E1234567) matched the [.eE] test and
became Decimal("0x..."), where Zend folds an overflowing hex literal
to its exact double.
Three fixes, keeping the documented feature:
- Count true mantissa significant digits (strip sign, exponent,
leading and trailing zeros) and additionally require that the double
cannot reproduce the literal exactly - a literal that round-trips
(every var_export/serialize output, PHP_FLOAT_EPSILON) has lost
nothing and stays float, while 3.14159265358979323846 still promotes.
- Exclude hex/octal/binary notation from the reclassification.
- When a Decimal-classified literal meets a float-typed expression in
a binary op or comparison, demote the literal to its exact double
instead of the "Cannot convert float expression to Decimal" fatal:
PHP evaluates every float literal as a double, so
0.1 + 0.2 == 0.30000000000000004 is valid PHP and must be true.
The classMethodOverride registration in prepareClassMethod() depends on
file preprocessing order. With a "sandwich" order (ancestor first, leaf
second, intermediate class last), the ancestor method's override flag is
missed: the upward marking cannot cross the not-yet-registered
intermediate class, and the downward subclass lookup ran before the leaf
was prepared. findNativeMethod() then devirtualizes the late-bound call
into a direct native call, silently ignoring the override. In Hyperf this
turns SoftDeletes::delete() into a physical DELETE.
Finalize the flags once the complete class graph is known:
finalizeMethodOverrideFlags() walks every declared method's complete
parent chain and marks each existing ancestor method of the same name as
overridden (method count x inheritance depth). It runs from
Translator::convertFile() next to finalizeDeclarationExpressions(), so
both the project pipeline and the public prepareFile()/convertFile() API
share the same pre-conversion finalization, guarded by a dirty/finalized
flag reset in prepareFile().
Tests:
- tests/compiler/devirtualize/override-order-sandwich.phpt (fails on
master with "base", passes with "leaf")
- tests/compiler/devirtualize/override-order-normal.phpt (control)
- phpunit/src/DevirtualizeOrderTest.php driving the public
prepareFile()/convertFile() API in both orders, asserting the
generated Base::delete() body dispatches dynamically
Consolidate float-to-C++ literal generation into Utils::genFloatLiteral().
Previously:
- genCValue() stringified floats directly via (string), emitting 'INF', '-INF', 'NAN', or losing the floating-point decimal point for whole numbers like 1.0 -> '1'.
- BinaryOpTrait::genFloatLiteral() used sprintf('%.17g') without handling INF or NAN.
Now all float code generation paths delegate to Utils::genFloatLiteral(), mapping INF/-INF/NAN to std::numeric_limits<double> and ensuring whole numbers retain .0.
The flag checks that translate `break N` / `continue N` were emitted only
at the end of each enclosing loop body. After the inner construct exited
with the countdown flag set, every trailing statement of the enclosing
body still executed before the check ran:
foreach ([1] as $x) {
foreach ([1] as $y) { break 2; }
echo "leaked"; // ran in compiled output, not in PHP
}
The native (int-typed) switch path was worse: its check sat inside the
do-while(0) wrapper, decrementing the flag a second time for the switch
level the C++ `break` had already exited. A `break 2` from a native
switch inside a loop therefore never exited the loop at all.
Emit the propagation check immediately after every nested loop / switch
statement instead, from the statement dispatcher, and drop the dead
end-of-body emissions. The check now also distinguishes the enclosing
construct: when it sits inside a switch, a continue that lands on the
switch level lowers to `break`, matching PHP's continue-targets-switch
semantics.
parseBreak/parseContinue now reject levels exceeding the number of
enclosing breakable constructs - the same compile-time validation PHP
performs (`Cannot 'break' 2 levels`) - which the countdown scheme
relies on to terminate at an enclosing construct.
The continue-2-while scenario in break-continue-level.phpt encoded the
old leaked behavior: its `$i++` after the inner loop only ran because of
the misplaced check; standard PHP loops forever on it. The counter now
advances before the inner loop.
The flag checks that translate `break N` / `continue N` were emitted only
at the end of each enclosing loop body. After the inner construct exited
with the countdown flag set, every trailing statement of the enclosing
body still executed before the check ran:
foreach ([1] as $x) {
foreach ([1] as $y) { break 2; }
echo "leaked"; // ran in compiled output, not in PHP
}
The native (int-typed) switch path was worse: its check sat inside the
do-while(0) wrapper, decrementing the flag a second time for the switch
level the C++ `break` had already exited. A `break 2` from a native
switch inside a loop therefore never exited the loop at all.
Emit the propagation check immediately after every nested loop / switch
statement instead, from the statement dispatcher, and drop the dead
end-of-body emissions. The check now also distinguishes the enclosing
construct: when it sits inside a switch, a continue that lands on the
switch level lowers to `break`, matching PHP's continue-targets-switch
semantics.
parseBreak/parseContinue now reject levels exceeding the number of
enclosing breakable constructs - the same compile-time validation PHP
performs (`Cannot 'break' 2 levels`) - which the countdown scheme
relies on to terminate at an enclosing construct.
The continue-2-while scenario in break-continue-level.phpt encoded the
old leaked behavior: its `$i++` after the inner loop only ran because of
the misplaced check; standard PHP loops forever on it. The counter now
advances before the inner loop.
- Add useImportAliases property to track import aliases by type domain
- Implement registerUseImportAlias method with duplicate detection logic
- Update use declaration processing to utilize import alias registration
- Make class and function aliases case-insensitive while constants remain case-sensitive
- Add comprehensive test coverage for import alias domain isolation
- Include negative test cases for duplicate alias detection across domains
- Move AST name resolution earlier in preprocessing steps
- Add proper error handling for PHP parser errors during traversal
- Implement clone-with functionality with one/two argument support
- Add preservation of evaluation order for clone-with operations
- Support dynamic, numeric and object-valued properties in clone-with
- Handle error cases and stop property updates at first error
- Reject active references and unwrap sole remaining references
- Respect property scope and unlock readonly properties during cloning
- Add first-class callable and string callable support for clone
- Preserve lexical class scope in Zend call frames for method calls
- Update documentation with clone-with compatibility requirements
- Add comprehensive test coverage for clone-with features
- Implement scope argument handling in runtime function calls
- Add support for __NAMESPACE__ magic constant resolution in namespace, class, and global scopes
- Add support for __PROPERTY__ magic constant that resolves to property name within property context
- Implement namespace\name relative name resolution for functions, constants, classes, and members
- Update AnonClassGenerator to properly parse identifiers instead of using toString()
- Extend CompilerBase to handle Scalar_MagicConst_Namespace and Scalar_MagicConst_Property
- Modify class inheritance and interface implementation to use parseIdentifier for proper name resolution
- Add comprehensive tests for namespace magic constant behavior across different scopes
- Add tests for property magic constant resolving correctly in property contexts only
- Implement runtime attribute factory lowering with proper namespace context handling
- Update visitor pattern to track property magic boundaries and namespace context correctly
- Document incompatibility with PHP 8.5 #[NoDiscard]
- Removed isConstFetch and isAssignOp methods from AstNodeType
- Removed corresponding test cases for isConstFetch and isAssignOp
- Updated CallArgumentGenerator to use setValue and appendValue instead of set and append
- Removed parseNamedCallArgs method from CallArgumentGenerator
- Removed unused getPlatformFullLinkFlags method from Clang backend
- Updated ClosureGenerator to use appendValue instead of append
- Removed abstract compileFile and linkObjects methods from CompilerBackend
- Removed unused getObjectPropVarInfo and getDecimalLiteralString methods
- Removed resetReturnType and mustNoCall methods from CompilerBase
- Removed testParseIncludesUsesNewArchitecture, testParseLdflagsUsesNewArchitecture,
testParseLibsUsesNewArchitecture and testBackwardCompatibility methods
- Removed isUniversalCtags property and related logic from Extractor
- Removed extractWithMetadata, generateStatistics and exportToMarkdown methods from Extractor
- Removed extractMetadata, extractComments, isPHPFunction and parseDocTags methods from Extractor
- Removed FINAL_TEST_SUCCESS.md file containing old test results