Compare commits

..

193 Commits

Author SHA1 Message Date
韩天峰 c9d9f58de0 Merge branch 'bug-fix-gh-5' 35 minutes ago
韩天峰 8918fae3ba fix(parser): stabilize compound array access paths 37 minutes ago
hafung ec251655ba Fix repeated array offset evaluation in compound assignments 53 minutes ago
韩天峰 0f335a8135 fix(optimizer): fold is_null for native scalars 1 hour ago
韩天峰 d321666721 fix(parser): parse do-while body before condition 1 hour ago
韩天峰 98321138ce test(gen_stub): cover namespaced composite property types 2 hours ago
韩天峰 f2b0e9add9 chore(version): bump version from 0.1.0 to 0.6.6 2 hours ago
韩天峰 4a9b918e7c
Merge pull request #6 from yuan-dian/fix/union-type-arginfo-escape 2 hours ago
韩天峰 1b92ae6e7b fix(namespace): isolate explicit import aliases 2 hours ago
原点 6e593b3d46 fix(gen_stub): use toVarEscapedName for union type ZEND_TYPE_INIT_CLASS 3 hours ago
韩天峰 af2b03c111 fix(cli): support Composer entrypoint and isolate stub symbols 5 hours ago
韩天峰 8ddd04e1b1 ci: restructure platform build and test checks 21 hours ago
韩天峰 ee1d6e7b54 ci: add ZTS ARM64 and macOS release builds 22 hours ago
韩天峰 fef462af33 build: exclude redundant archives from Windows package 23 hours ago
韩天峰 875f123403 ci: enable zip for Windows release packaging 23 hours ago
韩天峰 ed27572ca1 chore(version): bump version from 0.6.3 to 0.6.5 1 day ago
韩天峰 3d2933ec7f ci: package tagged compiler releases 1 day ago
韩天峰 87c328b3db ci: add Windows PHP ABI build matrix 1 day ago
韩天峰 063476360b fix(build): correct Windows mpdecimal DLL copying in CMake build 1 day ago
韩天峰 022ca55ff0 docs(readme): update documentation with comprehensive improvements 1 day ago
韩天峰 ced2ebb1ba docs(readme): update documentation with self-hosting details and benchmark data --skip-tests 1 day ago
韩天峰 7973f99f47 test: split PHP 8.5 numeric warnings 1 day ago
韩天峰 74274f8328 fix(compiler): defer cache IDs to convert phase 2 days ago
韩天峰 6693d95cd6 ci: restore full PHP test matrix 2 days ago
韩天峰 8f32497de7 refactor(compiler): strongly type symbol cache IDs 2 days ago
韩天峰 1b235d0769 test(compiler): reject mixed cache lifetime IDs 2 days ago
韩天峰 8c6ea1d14e fix(build): make source discovery deterministic 2 days ago
韩天峰 0ac6dfe499 fix(compiler): keep method cache lifetime aligned with class 2 days ago
韩天峰 a6cc11c6d7 ci: narrow class cache debugging workflow 2 days ago
韩天峰 2d14c9e432 ``` 2 days ago
韩天峰 7f587749f1 chore(ci): replace custom PHP setup action with community action 2 days ago
韩天峰 821f639e8f fix(ci): remove compiler flag from PHPT test runner 2 days ago
韩天峰 983333dc2e refactor(php): conditionally install and enable OPcache based on availability 2 days ago
韩天峰 978e553480 chore(ci): migrate PHP setup to custom action with Ubuntu 24.04 2 days ago
韩天峰 e4817819dc fix(ci): remove embed extension from PHP setup and add explicit embed library linking 2 days ago
韩天峰 8a30c5e58d rm composer.lock 2 days ago
韩天峰 0061d1595a refactor(examples): replace file reading with hardcoded rounds value 2 days ago
韩天峰 2c74be470e docs(readme): rewrite documentation with comprehensive TypePHP guide 2 days ago
韩天峰 2986f3db70 test(compiler): update test configuration and execution setup 2 days ago
韩天峰 e4bb7b2605 refactor(build): separate project-specific compile options from common pch 2 days ago
韩天峰 305d0d09d9 config(tests): update PHP configuration with operator overloading setting 2 days ago
韩天峰 a5700d24c9 chore(tests): update PHP configuration settings in workflow 2 days ago
韩天峰 642e9a7e4b fix(generator): use interned strings for persistent class metadata 2 days ago
韩天峰 422bdc4ce6 docs: remove documentation and plans for code reuse and encapsulation 2 days ago
韩天峰 208541d00b chore(build): add PHP header patch action and update dependencies 2 days ago
韩天峰 19a570ff3f feat(testing): add test coverage analyzer tool 2 days ago
韩天峰 178636a577 feat(config): add ext-deps alias for extension-dependencies configuration 2 days ago
韩天峰 1118c1e8ec feat(compiler): add PHP extension dependencies support 2 days ago
韩天峰 02fc0fb319 test(parser): add comprehensive tests for toArray method validation 2 days ago
韩天峰 3def25cc3c feat(compiler): add support for PHP 8.5 constant expression casts 2 days ago
韩天峰 44095252d2 feat(parser): add support for final promoted properties and exit named arguments 3 days ago
韩天峰 9f1cf07511 feat(parser): add support for promoted asymmetric visibility properties 3 days ago
韩天峰 36fd0228f0 feat(property): add property override attribute support and validation 3 days ago
韩天峰 4c59d9a21b feat(compiler): add DNF type support for method signatures and variance 3 days ago
韩天峰 b2246cd456 test(clone-with): add comprehensive tests for PHP 8.5 clone-with functionality 3 days ago
韩天峰 795a8e0a1d feat(compiler): add PHP 8.5 clone-with support and scope handling 3 days ago
韩天峰 9fc5755544 feat(parser): add support for PHP 8.5 void cast syntax 3 days ago
韩天峰 6dcafb652b test(compiler): add comprehensive negative compatibility tests and improve error handling 3 days ago
韩天峰 23a2bf44d7 test(compiler): add comprehensive test coverage for PHP language features 3 days ago
韩天峰 6d45c64b4d feat(parser): add validation for PHP 8.5 closure usage in constants and defaults 3 days ago
韩天峰 c5f478766e feat(compiler): add support for PHP 8.5 magic constants and namespace relative names 3 days ago
韩天峰 f64a1d5cb7 refactor(parser): remove unused methods and update call argument generation 3 days ago
韩天峰 00420215b3 feat(compiler): add native class support with export annotations 3 days ago
韩天峰 2471ed1db7 feat(compiler): add native class export validation for library stubs 3 days ago
韩天峰 0a97daa663 fix(compiler): simplify root slot registration in CompilerBase 5 days ago
韩天峰 4e00799c4a test(entry-script): add test for main line padding optimization 5 days ago
韩天峰 41e11a66e6 refactor(build): update runtime initialization and linking for native modules 5 days ago
韩天峰 0f6cc17450 chore(deps): update swoole/phpx dependency and project version 5 days ago
韩天峰 101b9f6261 fix(parser): handle array value write dereferences correctly 5 days ago
韩天峰 65a44c7f65 docs: add C++ naming convention documentation and update related references 6 days ago
韩天峰 e722501b3d refactor(generator): replace php prefixed helper functions with php namespace 6 days ago
韩天峰 d80f4a0d20 perf(parser): optimize array dimension fetch and selection operations 6 days ago
韩天峰 4713a58b06 feat(compiler): add local class entry hoisting control for parameter defaults 6 days ago
韩天峰 cb1e12f11f perf(compiler): optimize array statement writes and class constant lookups 6 days ago
韩天峰 7418b6247e feat(parser): optimize string concatenation assignment operations 6 days ago
韩天峰 65d3710a61 fix(parser): handle PHP array overflow in compound assignments and improve method name parsing 6 days ago
韩天峰 70d13a3601 修复 C++ 代码的缩进问题 6 days ago
韩天峰 4b8d0eb685 优化生成的 C++ 代码体积,减少无效行 6 days ago
韩天峰 a08f73c995 test(compiler): add signed integer arithmetic operator tests 1 week ago
韩天峰 729000fa0c chore(deps): update swoole/phpx dependency to version ~2.6.1 1 week ago
韩天峰 0d0e62fbc7 fix(array-def): update list index validation to use PHP's append position 1 week ago
韩天峰 ab1854beb0 refactor(parser): update local variable initializer hoisting logic 1 week ago
韩天峰 d33073c021 feat(compiler): add ArrayDef compile-time array contract support 1 week ago
韩天峰 68bdedaa31 perf(gc): increase native heap initial threshold to 16 MiB 1 week ago
韩天峰 d1704b42ef docs(native-class): update memory layout documentation and add padding fields to test classes 1 week ago
韩天峰 f1ef66622c chore(version): bump version to 0.6.0 and update dependencies 1 week ago
韩天峰 244fa4d8b2 Immutable 实现 1 week ago
韩天峰 7fece68f12 Native Class 支持 迭代器 1 week ago
韩天峰 464de23051 Native Class 支持 ArrayAccess 1 week ago
韩天峰 2ffd4ded19 Native Class 支持 [完结] 1 week ago
韩天峰 2e6ddf98f1 Native Class 支持 [第九阶段] 1 week ago
韩天峰 c9666664aa Native Class 支持 [第八阶段] 1 week ago
韩天峰 587818d5f9 Native Class 支持 [第七阶段] 1 week ago
韩天峰 7d1a90a340 Native Class 支持 [第六阶段] 1 week ago
韩天峰 13344ace72 Native Class 支持 [第五阶段] 1 week ago
韩天峰 7cf265cbc7 Native Class 支持 [第四阶段] 1 week ago
韩天峰 de8fdaf464 Native Class 支持 [第三阶段] 1 week ago
韩天峰 d5f0a113e6 Native Class 支持 [第二阶段] 1 week ago
韩天峰 981b8f54d7 Native Class 支持 2 weeks ago
韩天峰 af0b6f9582 移除 8.2、8.3 版本的支持 2 weeks ago
韩天峰 2f229ee0ac feat(generator): add PHP 8.4 interface property hook contracts support 2 weeks ago
韩天峰 ea08344c92 feat(parser): implement nullsafe operator temporary variable lifecycle management 2 weeks ago
韩天峰 1456305ade chore(deps): update swoole/phpx dependency version 2 weeks ago
韩天峰 5162261c75 fix(stub): remove pseudo-type aliases that could rewrite property names 2 weeks ago
韩天峰 a536172f82 feat(python): implement native bridge for python object calls and property access 2 weeks ago
韩天峰 aaf1ec11b3 feat(compiler): enhance WASI toolchain detection with library mode support 2 weeks ago
韩天峰 e4f6475452 build(wasm): update wit-bindgen dependency management 2 weeks ago
韩天峰 96960e9da1 refactor(scope): optimize callback map handling for preg_replace_callback_array 2 weeks ago
韩天峰 bca77588b5 fix(optimizer): handle first-class callable placeholders in property optimizer 2 weeks ago
韩天峰 f79d40f8ee docs: add PHPX WASM static library rebuild guide 2 weeks ago
韩天峰 496e6a4d69 feat(build): add bash completion support for TypePHP compiler 2 weeks ago
韩天峰 a510530d9b feat(cli): add bash completion support 2 weeks ago
韩天峰 6c9adbccad feat(wasm): upgrade preview2-shim and add mutable filesystem support 2 weeks ago
韩天峰 343084c4ba Merge pull request '使用PHP内置的宏来检查参数数量' (#48) from args-count-check into master 2 weeks ago
NathanFreeman af7ae9df82 fix(compiler): validate dynamic call argument counts 2 weeks ago
韩天峰 42e96f1103 chore(version): bump version to 1106 2 weeks ago
韩天峰 23e88efb6a Merge branch 'master' of git.code-galaxy.net:aot/compiler 2 weeks ago
韩天峰 463b427dc6 update version 2 weeks ago
韩天峰 e1018ca87e fix(anon-class): handle null return in anonymous class methods 2 weeks ago
韩天峰 bd6335ec3b chore(version): bump version to 1104 2 weeks ago
韩天峰 a79610fb2c fix(compiler): resolve anonymous class names with proper namespace imports 2 weeks ago
韩天峰 5fb3fdf490 feat(compiler): handle inherited private property array dim access correctly 2 weeks ago
韩天峰 2a1ca3b1a0 feat(compiler): add PHP 8.4 property hooks support with Zend metadata registration 2 weeks ago
韩天峰 01ea7c40be feat(parser): support dynamic class constant name fetch expressions 2 weeks ago
韩天峰 8aa8cdb065 fix(compiler): restore zend callback frames after caught exceptions 2 weeks ago
韩天峰 6e5fadcbc6 chore(deps): update swoole/phpx dependency version 2 weeks ago
韩天峰 18dd9a8a32 fix(compiler): prevent static method call cache pollution with dynamic trampolines 2 weeks ago
韩天峰 e07b97d6cf feat(scope): enhance callback scope management with dynamic callable support 2 weeks ago
韩天峰 8a3b5ae9a4 refactor(scope): replace dynamic call flag with unpacked callback scope requirement 2 weeks ago
韩天峰 8e48b9d7fe feat(compiler): implement scoped callback support for dynamic method calls 2 weeks ago
韩天峰 f5ca997e65 feat(converter): add Python to TypePHP conversion features 2 weeks ago
韩天峰 c47ef90593 docs(py2php): add comprehensive documentation and test coverage for Python to TypePHP converter 2 weeks ago
韩天峰 b77fee697e feat(parser): add runtime method scope detection for dynamic calls 2 weeks ago
韩天峰 7190bea4a5 refactor(compiler): replace internal symbol caches with persistent cache mechanism 2 weeks ago
韩天峰 6fb8187b07 fix(parser): handle constant expression type detection with namespace fallback 2 weeks ago
韩天峰 997bbffb65 重构,拆分内置类、函数、属性表,这些是 MINIT 阶段就确定的,无需 thread_local,可预加载,request shutdown 阶段无需释放 2 weeks ago
韩天峰 8118fbd3dc feat(readonly): enforce direct assignment only for readonly properties 2 weeks ago
韩天峰 e5f61eabfe 重构 readonly:__construct() 和 __clone() 都属于初始化窗口 ,其他地方不允许写。与 PHP 不一致。 2 weeks ago
韩天峰 a3ea91918d feat(parser): add readonly property unset protection 2 weeks ago
韩天峰 3b51eaa6d0 test(json): add test case for JSON exception boundary behavior 2 weeks ago
韩天峰 0ba5079336 fix(parser): handle temporary variable cleanup for weak reference expressions 2 weeks ago
韩天峰 7d7fa75d6f feat(compiler): add dynamic scope tracking for runtime method calls 2 weeks ago
韩天峰 64afbc9656 feat(python): generate builtin helpers and convert native calls 2 weeks ago
韩天峰 6c97b4f301 feat(python): add Python IDE helper generation and update object conversion methods 2 weeks ago
韩天峰 f61a542255 refactor(python): update module access syntax from static to namespace notation 2 weeks ago
NathanFreeman 3b6a48ef74 修复编译后的程序通过 Zend VM 调用自身的函数,如果此时临时变量过多,会导致段错误的问题 (#47) 2 weeks ago
韩天峰 22160661a9 docs(python): update design documentation and implementation plan 3 weeks ago
韩天峰 069d3e61bb Python 互调用支持 3 weeks ago
韩天峰 2be13eefcd feat(parser): add static call handling and Python module integration 3 weeks ago
韩天峰 cb03570212 refactor(context): remove runtime checked objects tracking mechanism 3 weeks ago
韩天峰 70927ee68a feat(type-system): enforce typed object constraints after unset operations 3 weeks ago
韩天峰 a4f1188a7e test(wasm): add comprehensive WASM testing infrastructure and runtime 3 weeks ago
韩天峰 1072e9d1c7 chore(version): bump version from 0.4.3 to 0.5.0 3 weeks ago
韩天峰 1847b4c926 test(datetime): update datetime builtin class tests with explicit format parsing 3 weeks ago
韩天峰 f630fcf661 Merge pull request '支持declare(strict_types=1);,这样通过zendVM调用其他函数的时候可以触发类型检查' (#46) from strict_types into master 3 weeks ago
韩天峰 525a4e4412 fix: register TypePHP functions with strict types 3 weeks ago
韩天峰 b1b40d6a09 fix(parser): ensure correct argument evaluation order for native calls 3 weeks ago
韩天峰 8d4801ebd2 test(trait): add test case for trait class constant inheritance behavior 3 weeks ago
韩天峰 ca41af9bb5 fix(parser): ensure left-to-right evaluation order for PHP expressions in C++ 3 weeks ago
韩天峰 833b74b344 重构 Trait 实现 3 weeks ago
NathanFreeman b69582011a Force enable type checking 3 weeks ago
韩天峰 c4b8db030d Update version.txt 3 weeks ago
NathanFreeman 7f46445a38 support strict_types 3 weeks ago
韩天峰 39354af70b chore(deps): update swoole/phpx dependency and increment version 3 weeks ago
韩天峰 dbd12d249d Merge pull request '修复三元表达式 issue' (#45) from fix-bool-ternary-expr into master 3 weeks ago
韩天峰 1baefe166f Merge branch 'master' into fix-bool-ternary-expr 3 weeks ago
韩天峰 c4404c15c5 fix(compiler): complete captured ternary type handling 3 weeks ago
韩天峰 abbaa11c7c feat(wasm): add WASI SDK build orchestration and numeric libraries support 3 weeks ago
韩天峰 3d36928e8b feat(wasm): add WASI component library mode with WIT interface generation 3 weeks ago
韩天峰 ac16f5fbac feat(wasm): update WASI build system with explicit browser/component profiles 3 weeks ago
韩天峰 9e26887c03 feat(wasm): add WASI program build script and HTTP fetch support 3 weeks ago
韩天峰 07c7d4b84a feat(compiler): add WASI 0.2 browser and component compilation support 3 weeks ago
韩天峰 9992793a02 feat(compiler): upgrade WASI support to Preview 2 with enhanced function validation 3 weeks ago
韩天峰 8503c7c32c 支持 wasm 3 weeks ago
韩天峰 cee9ee6e1b 修复三元表达式 issue 3 weeks ago
韩天峰 c3614aa6d3 chore(deps): update swoole/phpx dependency version 3 weeks ago
韩天峰 580d877ecd chore(version): bump version from 1099 to 1100 3 weeks ago
韩天峰 3b3711c139 feat(compiler): implement std container default initialization and mutation guards 3 weeks ago
韩天峰 b71ef5ca1a refactor(parser): change numeric parsing to always return strings 3 weeks ago
韩天峰 9d1ba7ee7a fix(generator): ensure all generated C values are returned as strings 3 weeks ago
韩天峰 8dd33c1400 feat(closure): implement proper lexical scope handling for closures 3 weeks ago
韩天峰 db4b7f9e28 feat(wasm): add TypePHP WASM implementation plan and trait scope guard support 3 weeks ago
韩天峰 2310c574ab feat(parser): enhance constant expression evaluation with nested operations support 3 weeks ago
韩天峰 8a23368cf1 feat(msvc): add compiler pdb support for debug builds 3 weeks ago
韩天峰 ec298e66fe feat(compiler): implement strict scalar type validation for parameters and returns 3 weeks ago
韩天峰 6d7e68a9ff chore(deps): update swoole/phpx dependency and add magic method tests 3 weeks ago
韩天峰 a7b16dc1be feat(parser): enhance constant integer arithmetic handling with native mode checks 3 weeks ago
韩天峰 c6c04d7e96 修复读取动态属性多调用一次 __isset 魔术方法的问题 3 weeks ago
韩天峰 78135d75ef 修复位移操作中未定义行为 3 weeks ago
韩天峰 f2c9309857 修复常量字面量运算溢出未转浮点的问题 3 weeks ago
韩天峰 09b99e357d Add exit code phpunit tests 3 weeks ago
韩天峰 560ea46692 add phpt tests 3 weeks ago
yangweijie a92fc0dfd7 add onepiece-ddz game example 3 weeks ago
  1. 179
      .agents/wiki/data/catalog.json
  2. 43
      .agents/wiki/data/catalog.xml
  3. 19
      .agents/wiki/index.html
  4. 26
      .agents/wiki/pages/_welcome.html
  5. 39
      .agents/wiki/pages/backend.html
  6. 45
      .agents/wiki/pages/backend.md
  7. 47
      .agents/wiki/pages/build-pipeline.html
  8. 56
      .agents/wiki/pages/build-pipeline.md
  9. 38
      .agents/wiki/pages/build-speed.html
  10. 46
      .agents/wiki/pages/build-speed.md
  11. 46
      .agents/wiki/pages/compilation-modes.html
  12. 54
      .agents/wiki/pages/compilation-modes.md
  13. 37
      .agents/wiki/pages/compile-time-attributes.html
  14. 38
      .agents/wiki/pages/compile-time-attributes.md
  15. 44
      .agents/wiki/pages/gen-extension.html
  16. 47
      .agents/wiki/pages/gen-extension.md
  17. 41
      .agents/wiki/pages/gen-functions-classes.html
  18. 48
      .agents/wiki/pages/gen-functions-classes.md
  19. 37
      .agents/wiki/pages/generator.html
  20. 51
      .agents/wiki/pages/generator.md
  21. 47
      .agents/wiki/pages/high-precision-types.html
  22. 58
      .agents/wiki/pages/high-precision-types.md
  23. 38
      .agents/wiki/pages/incompatible-features.html
  24. 45
      .agents/wiki/pages/incompatible-features.md
  25. 42
      .agents/wiki/pages/incremental-cache.html
  26. 46
      .agents/wiki/pages/incremental-cache.md
  27. 43
      .agents/wiki/pages/native-types.html
  28. 55
      .agents/wiki/pages/native-types.md
  29. 36
      .agents/wiki/pages/optimizer.html
  30. 37
      .agents/wiki/pages/optimizer.md
  31. 35
      .agents/wiki/pages/overview.html
  32. 32
      .agents/wiki/pages/overview.md
  33. 45
      .agents/wiki/pages/parser.html
  34. 66
      .agents/wiki/pages/parser.md
  35. 48
      .agents/wiki/pages/pipeline.html
  36. 55
      .agents/wiki/pages/pipeline.md
  37. 41
      .agents/wiki/pages/platform.html
  38. 45
      .agents/wiki/pages/platform.md
  39. 45
      .agents/wiki/pages/preprocessor.html
  40. 56
      .agents/wiki/pages/preprocessor.md
  41. 53
      .agents/wiki/pages/quickstart.html
  42. 68
      .agents/wiki/pages/quickstart.md
  43. 44
      .agents/wiki/pages/runtime.html
  44. 50
      .agents/wiki/pages/runtime.md
  45. 37
      .agents/wiki/pages/transform.html
  46. 45
      .agents/wiki/pages/transform.md
  47. 38
      .agents/wiki/pages/type-check.html
  48. 41
      .agents/wiki/pages/type-check.md
  49. 2024
      .agents/wiki/script/mermaid.min.js
  50. 274
      .claude/plans/code-reuse-improvement.md
  51. 325
      .claude/plans/encapsulation-review.md
  52. 21
      .github/actions/patch-php-headers/action.yml
  53. 155
      .github/actions/unix-arm64-build/action.yml
  54. 90
      .github/copilot-instructions.md
  55. 11
      .github/patches/php-hash-cxx.patch
  56. 44
      .github/smoke/linux-arm64/cpp-src/platform.cc
  57. 12
      .github/smoke/linux-arm64/cpp-src/platform.stub.php
  58. 22
      .github/smoke/linux-arm64/main.php
  59. 9
      .github/smoke/linux-arm64/project.yml
  60. 45
      .github/smoke/macos/cpp-src/platform.cc
  61. 12
      .github/smoke/macos/cpp-src/platform.stub.php
  62. 22
      .github/smoke/macos/main.php
  63. 9
      .github/smoke/macos/project.yml
  64. 43
      .github/workflows/linux-arm64.yml
  65. 360
      .github/workflows/linux-x64.yml
  66. 43
      .github/workflows/macos-arm64.yml
  67. 141
      .github/workflows/release.yml
  68. 449
      .github/workflows/windows-build.yml
  69. 8
      .gitignore
  70. 4
      .php-cs-fixer.dist.php
  71. 76
      .workbuddy/memory/2026-08-03.md
  72. 154
      .workbuddy/memory/2026-08-04.md
  73. 103
      CLAUDE.md
  74. 674
      LICENSE
  75. 53
      LICENSE.md
  76. 700
      README-CN.md
  77. 754
      README.md
  78. 52
      app.manifest
  79. 495
      ast.py
  80. 146
      bin/analyze-test-coverage.php
  81. 19
      bin/bootstrap.php
  82. 2
      bin/dump-ast.php
  83. 4
      bin/extractor.php
  84. 6
      bin/tpc.php
  85. 178
      cleanup-typephp-tmp.sh
  86. 151
      completions/tpc.bash
  87. 11
      composer.json
  88. 90
      composer.lock
  89. 2
      docs/AOT_BUILD_SPEED_RESEARCH.md
  90. 42
      docs/ARRAY_DEF.md
  91. 224
      docs/COMPILATION_PERFORMANCE_ANALYSIS.md
  92. 52
      docs/COMPILER_CLI.md
  93. 356
      docs/CPP_SYMBOL_NAMING.md
  94. 114
      docs/IMMUTABLE.md
  95. 17
      docs/INCOMPATIBLE_PHP_FEATURES.md
  96. 116
      docs/INTERFACE_PROPERTY_HOOKS.md
  97. 5
      docs/MIXED_CPP_PHP.md
  98. 137
      docs/NATIVE_CLASS_IMPLEMENTATION_AUDIT.md
  99. 1442
      docs/NATIVE_CLASS_OBJECT.md
  100. 212
      docs/OBJECT_CREATION.md
  101. Some files were not shown because too many files have changed in this diff Show More

@ -1,179 +0,0 @@
{
"language": "zh",
"sections": [
{
"name": "项目概览",
"topics": [
{
"title": "项目简介",
"level": 1,
"slug": "overview",
"group": null
},
{
"title": "编译流水线",
"level": 1,
"slug": "pipeline",
"group": null
},
{
"title": "编译模式",
"level": 1,
"slug": "compilation-modes",
"group": null
},
{
"title": "快速开始",
"level": 1,
"slug": "quickstart",
"group": null
}
],
"groups": []
},
{
"name": "前端:解析与符号",
"topics": [
{
"title": "解析器与 AST",
"level": 1,
"slug": "parser",
"group": null
},
{
"title": "预处理与符号表",
"level": 1,
"slug": "preprocessor",
"group": null
},
{
"title": "AST 转换(Lowering)",
"level": 1,
"slug": "transform",
"group": null
}
],
"groups": []
},
{
"name": "类型系统",
"topics": [
{
"title": "原生类型映射",
"level": 1,
"slug": "native-types",
"group": null
},
{
"title": "类型检查与兼容",
"level": 1,
"slug": "type-check",
"group": null
},
{
"title": "高精度数值类型",
"level": 1,
"slug": "high-precision-types",
"group": null
}
],
"groups": []
},
{
"name": "代码生成",
"topics": [
{
"title": "生成器总览",
"level": 1,
"slug": "generator",
"group": null
},
{
"title": "函数与类生成",
"level": 1,
"slug": "gen-functions-classes",
"group": null
},
{
"title": "扩展模块生成",
"level": 1,
"slug": "gen-extension",
"group": null
}
],
"groups": []
},
{
"name": "后端与构建",
"topics": [
{
"title": "编译器后端",
"level": 1,
"slug": "backend",
"group": null
},
{
"title": "构建流水线",
"level": 1,
"slug": "build-pipeline",
"group": null
},
{
"title": "增量编译缓存",
"level": 1,
"slug": "incremental-cache",
"group": null
},
{
"title": "跨平台支持",
"level": 1,
"slug": "platform",
"group": null
}
],
"groups": []
},
{
"name": "优化与性能",
"topics": [
{
"title": "优化器",
"level": 1,
"slug": "optimizer",
"group": null
},
{
"title": "编译期属性",
"level": 1,
"slug": "compile-time-attributes",
"group": null
},
{
"title": "构建速度与性能",
"level": 1,
"slug": "build-speed",
"group": null
}
],
"groups": []
},
{
"name": "运行时与边界",
"topics": [
{
"title": "PHPX 运行时集成",
"level": 1,
"slug": "runtime",
"group": null
},
{
"title": "不支持的 PHP 特性",
"level": 1,
"slug": "incompatible-features",
"group": null
}
],
"groups": []
}
]
}

@ -1,43 +0,0 @@
<section>
项目概览
<topic level="1" slug="overview">项目简介</topic>
<topic level="1" slug="pipeline">编译流水线</topic>
<topic level="1" slug="compilation-modes">编译模式</topic>
<topic level="1" slug="quickstart">快速开始</topic>
</section>
<section>
前端:解析与符号
<topic level="1" slug="parser">解析器与 AST</topic>
<topic level="1" slug="preprocessor">预处理与符号表</topic>
<topic level="1" slug="transform">AST 转换(Lowering)</topic>
</section>
<section>
类型系统
<topic level="1" slug="native-types">原生类型映射</topic>
<topic level="1" slug="type-check">类型检查与兼容</topic>
<topic level="1" slug="high-precision-types">高精度数值类型</topic>
</section>
<section>
代码生成
<topic level="1" slug="generator">生成器总览</topic>
<topic level="1" slug="gen-functions-classes">函数与类生成</topic>
<topic level="1" slug="gen-extension">扩展模块生成</topic>
</section>
<section>
后端与构建
<topic level="1" slug="backend">编译器后端</topic>
<topic level="1" slug="build-pipeline">构建流水线</topic>
<topic level="1" slug="incremental-cache">增量编译缓存</topic>
<topic level="1" slug="platform">跨平台支持</topic>
</section>
<section>
优化与性能
<topic level="1" slug="optimizer">优化器</topic>
<topic level="1" slug="compile-time-attributes">编译期属性</topic>
<topic level="1" slug="build-speed">构建速度与性能</topic>
</section>
<section>
运行时与边界
<topic level="1" slug="runtime">PHPX 运行时集成</topic>
<topic level="1" slug="incompatible-features">不支持的 PHP 特性</topic>
</section>

@ -1,19 +0,0 @@
<!doctype html><html lang="zh"><head><meta charset="utf-8">
<title>Project Wiki</title><style>
:root{--bg:#0f1117;--fg:#e6e6e6;--muted:#9aa0aa;--accent:#6ea8fe;--card:#171a21;--border:#262b36;}
*{box-sizing:border-box}
body{margin:0;font-family:-apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif;background:var(--bg);color:var(--fg)}
#app{display:flex;height:100vh}
#side{width:300px;flex:0 0 300px;background:var(--card);border-right:1px solid var(--border);overflow:auto;padding:16px}
#side h2{font-size:15px;color:#fff;margin:18px 0 8px;padding-bottom:6px;border-bottom:1px solid var(--border)}
#side a{display:block;padding:5px 8px;border-radius:6px;color:var(--fg);text-decoration:none;font-size:13px}
#side a:hover{background:#222633}
#side a.lvl2{margin-left:12px;font-size:12px;color:var(--muted)}
#main{flex:1;background:var(--bg)}
#main iframe{width:100%;height:100%;border:none}
.meta{color:var(--muted);font-size:11px;margin-top:20px;border-top:1px solid var(--border);padding-top:10px}
</style></head>
<body><div id="app">
<nav id="side"><h2>项目概览</h2><a class="lvl1" href="pages/overview.html" target="view">项目简介</a><a class="lvl1" href="pages/pipeline.html" target="view">编译流水线</a><a class="lvl1" href="pages/compilation-modes.html" target="view">编译模式</a><a class="lvl1" href="pages/quickstart.html" target="view">快速开始</a><h2>前端:解析与符号</h2><a class="lvl1" href="pages/parser.html" target="view">解析器与 AST</a><a class="lvl1" href="pages/preprocessor.html" target="view">预处理与符号表</a><a class="lvl1" href="pages/transform.html" target="view">AST 转换(Lowering)</a><h2>类型系统</h2><a class="lvl1" href="pages/native-types.html" target="view">原生类型映射</a><a class="lvl1" href="pages/type-check.html" target="view">类型检查与兼容</a><a class="lvl1" href="pages/high-precision-types.html" target="view">高精度数值类型</a><h2>代码生成</h2><a class="lvl1" href="pages/generator.html" target="view">生成器总览</a><a class="lvl1" href="pages/gen-functions-classes.html" target="view">函数与类生成</a><a class="lvl1" href="pages/gen-extension.html" target="view">扩展模块生成</a><h2>后端与构建</h2><a class="lvl1" href="pages/backend.html" target="view">编译器后端</a><a class="lvl1" href="pages/build-pipeline.html" target="view">构建流水线</a><a class="lvl1" href="pages/incremental-cache.html" target="view">增量编译缓存</a><a class="lvl1" href="pages/platform.html" target="view">跨平台支持</a><h2>优化与性能</h2><a class="lvl1" href="pages/optimizer.html" target="view">优化器</a><a class="lvl1" href="pages/compile-time-attributes.html" target="view">编译期属性</a><a class="lvl1" href="pages/build-speed.html" target="view">构建速度与性能</a><h2>运行时与边界</h2><a class="lvl1" href="pages/runtime.html" target="view">PHPX 运行时集成</a><a class="lvl1" href="pages/incompatible-features.html" target="view">不支持的 PHP 特性</a><div class="meta">rendered by code-wiki · static HTML</div></nav>
<main id="main"><iframe name="view" src="pages/overview.html"></iframe></main>
</div></body></html>

@ -1,26 +0,0 @@
<!doctype html><html lang="zh"><head><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Project Wiki</title>
<script src="../script/mermaid.min.js"></script>
<script>document.addEventListener('DOMContentLoaded',function(){if(window.mermaid)mermaid.initialize({startOnLoad:true});});</script>
<style>
:root{--bg:#0f1117;--fg:#e6e6e6;--muted:#9aa0aa;--accent:#6ea8fe;--card:#171a21;--border:#262b36;}
*{box-sizing:border-box}
body{margin:0;font-family:-apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif;background:var(--bg);color:var(--fg);line-height:1.65}
.wrap{max-width:900px;margin:0 auto;padding:32px 24px 80px}
h1,h2,h3{color:#fff;line-height:1.3;margin-top:1.6em}
h1{border-bottom:1px solid var(--border);padding-bottom:.3em}
a{color:var(--accent);text-decoration:none}
a:hover{text-decoration:underline}
code{background:#0b0d12;padding:.15em .4em;border-radius:4px;font-size:.9em}
pre{background:#0b0d12;border:1px solid var(--border);border-radius:8px;padding:14px;overflow:auto}
pre code{background:none;padding:0}
table{border-collapse:collapse;width:100%;margin:1em 0}
th,td{border:1px solid var(--border);padding:8px 10px;text-align:left}
th{background:var(--card)}
blockquote{border-left:3px solid var(--accent);margin:1em 0;padding:.2em 1em;color:var(--muted);background:var(--card)}
hr{border:none;border-top:1px solid var(--border);margin:2em 0}
</style></head>
<body><div class="wrap"><h1>Project Wiki</h1>
<p>This wiki was generated by <strong>code-wiki</strong> from the repository at <code>D:\git\php\aot-compiler</code>.</p>
<p>Select a topic from the left navigation to read its documentation.</p></div></body></html>

@ -1,39 +0,0 @@
<!doctype html><html lang="zh"><head><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>编译器后端</title>
<script src="../script/mermaid.min.js"></script>
<script>document.addEventListener('DOMContentLoaded',function(){if(window.mermaid)mermaid.initialize({startOnLoad:true});});</script>
<style>
:root{--bg:#0f1117;--fg:#e6e6e6;--muted:#9aa0aa;--accent:#6ea8fe;--card:#171a21;--border:#262b36;}
*{box-sizing:border-box}
body{margin:0;font-family:-apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif;background:var(--bg);color:var(--fg);line-height:1.65}
.wrap{max-width:900px;margin:0 auto;padding:32px 24px 80px}
h1,h2,h3{color:#fff;line-height:1.3;margin-top:1.6em}
h1{border-bottom:1px solid var(--border);padding-bottom:.3em}
a{color:var(--accent);text-decoration:none}
a:hover{text-decoration:underline}
code{background:#0b0d12;padding:.15em .4em;border-radius:4px;font-size:.9em}
pre{background:#0b0d12;border:1px solid var(--border);border-radius:8px;padding:14px;overflow:auto}
pre code{background:none;padding:0}
table{border-collapse:collapse;width:100%;margin:1em 0}
th,td{border:1px solid var(--border);padding:8px 10px;text-align:left}
th{background:var(--card)}
blockquote{border-left:3px solid var(--accent);margin:1em 0;padding:.2em 1em;color:var(--muted);background:var(--card)}
hr{border:none;border-top:1px solid var(--border);margin:2em 0}
</style></head>
<body><div class="wrap"><h1>编译器后端</h1>
<p>TypePHP 不直接调 <code>g++</code>/<code>cl</code>,而是抽象出 <strong>平台(Platform)+ 编译器后端(Backend)</strong> 两层,由工厂在启动时自动探测。这样同一份生成代码能在 GCC / Clang / MSVC 下编译。</p>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\src\Translator.php#L164-L190">src/Translator.php</a> · <a href="file:///D:\git\php\aot-compiler\src\Backend">src/Backend/</a></p>
<h2>抽象结构</h2>
<table><thead><tr><th></th><th>角色</th></tr></thead><tbody><tr><td><code>PlatformFactory</code></td><td>按 OS 创建 <code>Windows</code> / <code>Linux</code> / <code>macOS</code> 平台对象</td></tr><tr><td><code>CompilerFactory</code></td><td><code>detectCompilerName()</code> / <code>createByName()</code> / <code>autoDetect()</code> 选择后端</td></tr><tr><td><code>CompilerBackend</code>(接口)</td><td>定义编译/链接命令抽象</td></tr><tr><td><code>GccLikeBackend</code></td><td>GCC / Clang 共用逻辑</td></tr><tr><td><code>Gcc</code> / <code>Clang</code></td><td>具体后端</td></tr><tr><td><code>Msvc</code></td><td>MSVC(Windows)后端</td></tr></tbody></table>
<p><code>detectPlatform()</code> 先建平台,再探测编译器名,最后 <code>CompilerFactory::createByName()</code> 生成后端;若失败则告警并回退到旧逻辑(<code>initializeNewArchitecture()</code>)。</p>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\src\Translator.php#L164-L190">src/Translator.php</a> · <a href="file:///D:\git\php\aot-compiler\src\Translator.php#L538-L560">src/Translator.php</a></p>
<h2>命令生成</h2>
<p>后端只负责「给定源/目标/选项 → 返回命令字符串」。真实的进程执行在 <a href="pages/build-pipeline.html">构建流水线</a><code>NativeBuilder</code> 中。例如:</p>
<ul><li><code>compileCommand()</code> / <code>linkCommand()</code> 产出可复现的命令行;</li><li>命令行本身被写进<strong>缓存键</strong>,确保编译参数变化能触发重编译。</li></ul>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\src\Translator.php#L1408-L1417">src/Translator.php</a> · <a href="file:///D:\git\php\aot-compiler\src\Translator.php#L1644-L1647">src/Translator.php</a></p>
<h2>预编译头</h2>
<p>后端是否支持 PCH 由 <code>supportsPrecompiledHeaders()</code> 决定;支持的会在构建期预编译 <code>phpx.h</code> 等全局头,显著加速后续编译单元。</p>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\src\Translator.php#L1449-L1487">src/Translator.php</a> · <a href="file:///D:\git\php\aot-compiler\src\Build\PrecompiledHeaderManager.php">src/Build/PrecompiledHeaderManager.php</a></p>
<h2>相关阅读</h2>
<ul><li>命令如何被执行与排队 → <a href="pages/build-pipeline.html">构建流水线</a></li><li>缓存如何依赖编译命令 → <a href="pages/incremental-cache.html">增量编译缓存</a></li><li>平台差异(Windows SDK / PHP lib) → <a href="pages/platform.html">跨平台支持</a></li></ul></div></body></html>

@ -1,45 +0,0 @@
<blog>
# 编译器后端
TypePHP 不直接调 `g++`/`cl`,而是抽象出 **平台(Platform)+ 编译器后端(Backend)** 两层,由工厂在启动时自动探测。这样同一份生成代码能在 GCC / Clang / MSVC 下编译。
Sources: [src/Translator.php](src/Translator.php#L164-L190) · [src/Backend/](src/Backend/)
## 抽象结构
| 类 | 角色 |
|---|---|
| `PlatformFactory` | 按 OS 创建 `Windows` / `Linux` / `macOS` 平台对象 |
| `CompilerFactory` | `detectCompilerName()` / `createByName()` / `autoDetect()` 选择后端 |
| `CompilerBackend`(接口) | 定义编译/链接命令抽象 |
| `GccLikeBackend` | GCC / Clang 共用逻辑 |
| `Gcc` / `Clang` | 具体后端 |
| `Msvc` | MSVC(Windows)后端 |
`detectPlatform()` 先建平台,再探测编译器名,最后 `CompilerFactory::createByName()` 生成后端;若失败则告警并回退到旧逻辑(`initializeNewArchitecture()`)。
Sources: [src/Translator.php](src/Translator.php#L164-L190) · [src/Translator.php](src/Translator.php#L538-L560)
## 命令生成
后端只负责「给定源/目标/选项 → 返回命令字符串」。真实的进程执行在 [构建流水线](build-pipeline) 的 `NativeBuilder` 中。例如:
- `compileCommand()` / `linkCommand()` 产出可复现的命令行;
- 命令行本身被写进**缓存键**,确保编译参数变化能触发重编译。
Sources: [src/Translator.php](src/Translator.php#L1408-L1417) · [src/Translator.php](src/Translator.php#L1644-L1647)
## 预编译头
后端是否支持 PCH 由 `supportsPrecompiledHeaders()` 决定;支持的会在构建期预编译 `phpx.h` 等全局头,显著加速后续编译单元。
Sources: [src/Translator.php](src/Translator.php#L1449-L1487) · [src/Build/PrecompiledHeaderManager.php](src/Build/PrecompiledHeaderManager.php)
## 相关阅读
- 命令如何被执行与排队 → [构建流水线](build-pipeline)
- 缓存如何依赖编译命令 → [增量编译缓存](incremental-cache)
- 平台差异(Windows SDK / PHP lib) → [跨平台支持](platform)
</blog>

@ -1,47 +0,0 @@
<!doctype html><html lang="zh"><head><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>构建流水线</title>
<script src="../script/mermaid.min.js"></script>
<script>document.addEventListener('DOMContentLoaded',function(){if(window.mermaid)mermaid.initialize({startOnLoad:true});});</script>
<style>
:root{--bg:#0f1117;--fg:#e6e6e6;--muted:#9aa0aa;--accent:#6ea8fe;--card:#171a21;--border:#262b36;}
*{box-sizing:border-box}
body{margin:0;font-family:-apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif;background:var(--bg);color:var(--fg);line-height:1.65}
.wrap{max-width:900px;margin:0 auto;padding:32px 24px 80px}
h1,h2,h3{color:#fff;line-height:1.3;margin-top:1.6em}
h1{border-bottom:1px solid var(--border);padding-bottom:.3em}
a{color:var(--accent);text-decoration:none}
a:hover{text-decoration:underline}
code{background:#0b0d12;padding:.15em .4em;border-radius:4px;font-size:.9em}
pre{background:#0b0d12;border:1px solid var(--border);border-radius:8px;padding:14px;overflow:auto}
pre code{background:none;padding:0}
table{border-collapse:collapse;width:100%;margin:1em 0}
th,td{border:1px solid var(--border);padding:8px 10px;text-align:left}
th{background:var(--card)}
blockquote{border-left:3px solid var(--accent);margin:1em 0;padding:.2em 1em;color:var(--muted);background:var(--card)}
hr{border:none;border-top:1px solid var(--border);margin:2em 0}
</style></head>
<body><div class="wrap"><h1>构建流水线</h1>
<p><code>Translator::compile()</code> 把生成的 C++ 与 phpx 运行时源文件编译为对象文件,<code>build()</code> 再链接成最终产物。两者都委托给 <code>src/Build/NativeBuilder</code>,它负责真正派发编译/链接进程。</p>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\src\Translator.php#L1419-L1447">src/Translator.php</a> · <a href="file:///D:\git\php\aot-compiler\src\Translator.php#L1649-L1680">src/Translator.php</a></p>
<h2>compile() 步骤</h2>
<pre class="mermaid">flowchart TD
A[&quot;compile(sourceFiles)&quot;] --&gt; B[&quot;追加 phpx misc 源\nfiber_generator / helper / main / cli_title&quot;]
B --&gt; C[&quot;preparePhpXPrecompiledHeader()&quot;]
C --&gt; D[&quot;compileResourceFile() (Windows)&quot;]
D --&gt; E{&quot;支持 pcntl 且 -j&gt;1?&quot;}
E -- &quot;&quot; --&gt; F[&quot;compileSourceFile() 串行&quot;]
E -- &quot;&quot; --&gt; G[&quot;compileWithPcntl() 并行&quot;]</pre>
<p>phpx misc 源(<code>typephp_fiber_generator.cc</code><code>typephp_helper.cc</code><code>typephp_main.cc</code><code>php_cli_process_title.c</code><code>ps_title.c</code>)按模式追加:embed 加 <code>typephp_main.cc</code>,bin 加 cli 标题源。</p>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\src\Translator.php#L1423-L1434">src/Translator.php</a></p>
<h2>并行编译</h2>
<p><code>compileWithPcntl()</code> 在 Unix/Linux/macOS 用 <code>pcntl_fork</code> 多进程编译,进度条实时刷新;无 <code>pcntl</code> 扩展时自动降级为串行。每个 <code>compileFile()</code> 调用前都会先查 <a href="pages/incremental-cache.html">增量缓存</a>,命中则跳过。</p>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\src\Translator.php#L1584-L1637">src/Translator.php</a> · <a href="file:///D:\git\php\aot-compiler\src\Translator.php#L1349-L1392">src/Translator.php</a></p>
<h2>NativeBuilder 与命令选项</h2>
<table><thead><tr><th>组件</th><th>职责</th></tr></thead><tbody><tr><td><code>NativeBuilder</code></td><td>真正执行 <code>compile</code> / <code>link</code>,返回 <code>command</code>/<code>status</code>/<code>output</code></td></tr><tr><td><code>NativeCommandOptionsTrait</code></td><td>汇总编译/链接选项</td></tr><tr><td><code>CompileOptions</code> / <code>LinkOptions</code> / <code>CommandOptions</code></td><td>选项数据对象</td></tr><tr><td><code>FileScanner</code></td><td>识别原生源文件(C/C++/汇编/ObjC)</td></tr><tr><td><code>SourceCompileQueue</code> / <code>SourcePipelineTrait</code></td><td>编译队列与流水线调度</td></tr><tr><td><code>ResourceCompilationTrait</code></td><td>Windows <code>.rc</code><code>.res</code></td></tr></tbody></table>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\src\Build\NativeBuilder.php">src/Build/NativeBuilder.php</a> · <a href="file:///D:\git\php\aot-compiler\src\Build">src/Build/</a></p>
<h2>build() 链接</h2>
<p><code>build()</code><code>NativeBuilder::link()</code>,Windows 模式下把 <code>.res</code> 资源文件并入目标;链接失败或产物未生成则 <code>error()</code> 退出。</p>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\src\Translator.php#L1649-L1680">src/Translator.php</a> · <a href="file:///D:\git\php\aot-compiler\src\Translator.php#L1653-L1659">src/Translator.php</a></p>
<h2>相关阅读</h2>
<ul><li>后端如何产出命令 → <a href="pages/backend.html">编译器后端</a></li><li>缓存如何避免重复编译 → <a href="pages/incremental-cache.html">增量编译缓存</a></li><li>平台相关的链接差异 → <a href="pages/platform.html">跨平台支持</a></li></ul></div></body></html>

@ -1,56 +0,0 @@
<blog>
# 构建流水线
`Translator::compile()` 把生成的 C++ 与 phpx 运行时源文件编译为对象文件,`build()` 再链接成最终产物。两者都委托给 `src/Build/NativeBuilder`,它负责真正派发编译/链接进程。
Sources: [src/Translator.php](src/Translator.php#L1419-L1447) · [src/Translator.php](src/Translator.php#L1649-L1680)
## compile() 步骤
```mermaid
flowchart TD
A["compile(sourceFiles)"] --> B["追加 phpx misc 源\nfiber_generator / helper / main / cli_title"]
B --> C["preparePhpXPrecompiledHeader()"]
C --> D["compileResourceFile() (Windows)"]
D --> E{"支持 pcntl 且 -j>1?"}
E -- "否" --> F["compileSourceFile() 串行"]
E -- "是" --> G["compileWithPcntl() 并行"]
```
phpx misc 源(`typephp_fiber_generator.cc`、`typephp_helper.cc`、`typephp_main.cc`、`php_cli_process_title.c`、`ps_title.c`)按模式追加:embed 加 `typephp_main.cc`,bin 加 cli 标题源。
Sources: [src/Translator.php](src/Translator.php#L1423-L1434)
## 并行编译
`compileWithPcntl()` 在 Unix/Linux/macOS 用 `pcntl_fork` 多进程编译,进度条实时刷新;无 `pcntl` 扩展时自动降级为串行。每个 `compileFile()` 调用前都会先查 [增量缓存](incremental-cache),命中则跳过。
Sources: [src/Translator.php](src/Translator.php#L1584-L1637) · [src/Translator.php](src/Translator.php#L1349-L1392)
## NativeBuilder 与命令选项
| 组件 | 职责 |
|---|---|
| `NativeBuilder` | 真正执行 `compile` / `link`,返回 `command`/`status`/`output` |
| `NativeCommandOptionsTrait` | 汇总编译/链接选项 |
| `CompileOptions` / `LinkOptions` / `CommandOptions` | 选项数据对象 |
| `FileScanner` | 识别原生源文件(C/C++/汇编/ObjC) |
| `SourceCompileQueue` / `SourcePipelineTrait` | 编译队列与流水线调度 |
| `ResourceCompilationTrait` | Windows `.rc``.res` |
Sources: [src/Build/NativeBuilder.php](src/Build/NativeBuilder.php) · [src/Build/](src/Build/)
## build() 链接
`build()``NativeBuilder::link()`,Windows 模式下把 `.res` 资源文件并入目标;链接失败或产物未生成则 `error()` 退出。
Sources: [src/Translator.php](src/Translator.php#L1649-L1680) · [src/Translator.php](src/Translator.php#L1653-L1659)
## 相关阅读
- 后端如何产出命令 → [编译器后端](backend)
- 缓存如何避免重复编译 → [增量编译缓存](incremental-cache)
- 平台相关的链接差异 → [跨平台支持](platform)
</blog>

@ -1,38 +0,0 @@
<!doctype html><html lang="zh"><head><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>构建速度与性能</title>
<script src="../script/mermaid.min.js"></script>
<script>document.addEventListener('DOMContentLoaded',function(){if(window.mermaid)mermaid.initialize({startOnLoad:true});});</script>
<style>
:root{--bg:#0f1117;--fg:#e6e6e6;--muted:#9aa0aa;--accent:#6ea8fe;--card:#171a21;--border:#262b36;}
*{box-sizing:border-box}
body{margin:0;font-family:-apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif;background:var(--bg);color:var(--fg);line-height:1.65}
.wrap{max-width:900px;margin:0 auto;padding:32px 24px 80px}
h1,h2,h3{color:#fff;line-height:1.3;margin-top:1.6em}
h1{border-bottom:1px solid var(--border);padding-bottom:.3em}
a{color:var(--accent);text-decoration:none}
a:hover{text-decoration:underline}
code{background:#0b0d12;padding:.15em .4em;border-radius:4px;font-size:.9em}
pre{background:#0b0d12;border:1px solid var(--border);border-radius:8px;padding:14px;overflow:auto}
pre code{background:none;padding:0}
table{border-collapse:collapse;width:100%;margin:1em 0}
th,td{border:1px solid var(--border);padding:8px 10px;text-align:left}
th{background:var(--card)}
blockquote{border-left:3px solid var(--accent);margin:1em 0;padding:.2em 1em;color:var(--muted);background:var(--card)}
hr{border:none;border-top:1px solid var(--border);margin:2em 0}
</style></head>
<body><div class="wrap"><h1>构建速度与性能</h1>
<p>TypePHP 的「编译速度」包含两个维度:<strong>编译期(PHP→C++→二进制)自身耗时</strong><strong>生成产物的运行性能</strong>。本章聚焦前者——哪些环节最慢,以及已落地的优化。</p>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\docs\AOT_BUILD_SPEED_RESEARCH.md">docs/AOT_BUILD_SPEED_RESEARCH.md</a> · <a href="file:///D:\git\php\aot-compiler\docs\aot-optimization-priority.md">docs/aot-optimization-priority.md</a></p>
<h2>时间都花在哪</h2>
<p>编译期主要成本:</p>
<ol><li><strong>PHP 解析</strong>:每个文件被多次解析(预处理一次、转换一次)——这是主要冗余;</li><li><strong>C++ 翻译</strong><code>doConvert()</code> 遍历 AST 并发射 C++;</li><li><strong>C++ 编译</strong>:大量 <code>.cc</code> 单元编译,受 PCH 与并行度影响;</li><li><strong>链接</strong>:最终链接往往是大项目瓶颈之一。</li></ol>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\docs\AOT_BUILD_SPEED_RESEARCH.md">docs/AOT_BUILD_SPEED_RESEARCH.md</a> · <a href="file:///D:\git\php\aot-compiler\src\Translator.php#L2455-L2459">src/Translator.php</a></p>
<h2>已落地的优化</h2>
<table><thead><tr><th>优化</th><th>说明</th><th>位置</th></tr></thead><tbody><tr><td><strong>S1 合并解析</strong></td><td>同一文件 AST 在进程内缓存复用(<code>parseCachedAst</code>),消除重复解析</td><td><code>src/Translator.php</code> <code>parseCachedAst()</code></td></tr><tr><td><strong>S2 增量对象缓存</strong></td><td>生成单元按「编译命令 + PHP ABI + 生成头内容指纹」缓存 <code>.o</code>,改动一处不必全量重编</td><td><a href="pages/incremental-cache.html">增量编译缓存</a></td></tr><tr><td><strong>PCH 预编译头</strong></td><td>预编译 <code>phpx.h</code> 等全局头,加速每个编译单元</td><td><code>src/Build/PrecompiledHeaderManager.php</code></td></tr><tr><td><strong>并行编译</strong></td><td><code>-j</code><code>pcntl</code> 多进程编译</td><td><code>src/Translator.php</code> <code>compileWithPcntl()</code></td></tr><tr><td><strong>misc 缓存</strong></td><td>phpx 运行时源按 ABI 指纹缓存</td><td><code>hasMiscObjectFileCache()</code></td></tr></tbody></table>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\src\Translator.php#L1116-L1160">src/Translator.php</a> · <a href="file:///D:\git\php\aot-compiler\src\Translator.php#L1449-L1487">src/Translator.php</a> · <a href="file:///D:\git\php\aot-compiler\src\Translator.php#L1584-L1637">src/Translator.php</a></p>
<h2>调试与剖析</h2>
<ul><li><code>--dry</code>:只生成 C++,跳过编译/链接,用于检查翻译与定位;</li><li><code>--profile</code>:启用 gperftools 剖析(仅 Linux),并在 <code>./tpc app.prof</code> 模式调 <code>pprof --web</code></li><li><code>--no-progress</code> / <code>-j</code>:控制进度展示与并行度。</li></ul>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\src\compiler.php#L48-L73">src/compiler.php</a> · <a href="file:///D:\git\php\aot-compiler\src\Translator.php#L30-L36">src/Translator.php</a> · <a href="file:///D:\git\php\aot-compiler\src\Translator.php#L251-L255">src/Translator.php</a></p>
<h2>相关阅读</h2>
<ul><li>缓存机制细节 → <a href="pages/incremental-cache.html">增量编译缓存</a></li><li>优化器如何提升产物性能 → <a href="pages/optimizer.html">优化器</a></li><li>优先级规划 → <code>docs/aot-optimization-priority.md</code></li></ul></div></body></html>

@ -1,46 +0,0 @@
<blog>
# 构建速度与性能
TypePHP 的「编译速度」包含两个维度:**编译期(PHP→C++→二进制)自身耗时** 与 **生成产物的运行性能**。本章聚焦前者——哪些环节最慢,以及已落地的优化。
Sources: [docs/AOT_BUILD_SPEED_RESEARCH.md](docs/AOT_BUILD_SPEED_RESEARCH.md) · [docs/aot-optimization-priority.md](docs/aot-optimization-priority.md)
## 时间都花在哪
编译期主要成本:
1. **PHP 解析**:每个文件被多次解析(预处理一次、转换一次)——这是主要冗余;
2. **C++ 翻译**:`doConvert()` 遍历 AST 并发射 C++;
3. **C++ 编译**:大量 `.cc` 单元编译,受 PCH 与并行度影响;
4. **链接**:最终链接往往是大项目瓶颈之一。
Sources: [docs/AOT_BUILD_SPEED_RESEARCH.md](docs/AOT_BUILD_SPEED_RESEARCH.md) · [src/Translator.php](src/Translator.php#L2455-L2459)
## 已落地的优化
| 优化 | 说明 | 位置 |
|---|---|---|
| **S1 合并解析** | 同一文件 AST 在进程内缓存复用(`parseCachedAst`),消除重复解析 | `src/Translator.php` `parseCachedAst()` |
| **S2 增量对象缓存** | 生成单元按「编译命令 + PHP ABI + 生成头内容指纹」缓存 `.o`,改动一处不必全量重编 | [增量编译缓存](incremental-cache) |
| **PCH 预编译头** | 预编译 `phpx.h` 等全局头,加速每个编译单元 | `src/Build/PrecompiledHeaderManager.php` |
| **并行编译** | `-j``pcntl` 多进程编译 | `src/Translator.php` `compileWithPcntl()` |
| **misc 缓存** | phpx 运行时源按 ABI 指纹缓存 | `hasMiscObjectFileCache()` |
Sources: [src/Translator.php](src/Translator.php#L1116-L1160) · [src/Translator.php](src/Translator.php#L1449-L1487) · [src/Translator.php](src/Translator.php#L1584-L1637)
## 调试与剖析
- `--dry`:只生成 C++,跳过编译/链接,用于检查翻译与定位;
- `--profile`:启用 gperftools 剖析(仅 Linux),并在 `./tpc app.prof` 模式调 `pprof --web`
- `--no-progress` / `-j`:控制进度展示与并行度。
Sources: [src/compiler.php](src/compiler.php#L48-L73) · [src/Translator.php](src/Translator.php#L30-L36) · [src/Translator.php](src/Translator.php#L251-L255)
## 相关阅读
- 缓存机制细节 → [增量编译缓存](incremental-cache)
- 优化器如何提升产物性能 → [优化器](optimizer)
- 优先级规划 → `docs/aot-optimization-priority.md`
</blog>

@ -1,46 +0,0 @@
<!doctype html><html lang="zh"><head><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>编译模式</title>
<script src="../script/mermaid.min.js"></script>
<script>document.addEventListener('DOMContentLoaded',function(){if(window.mermaid)mermaid.initialize({startOnLoad:true});});</script>
<style>
:root{--bg:#0f1117;--fg:#e6e6e6;--muted:#9aa0aa;--accent:#6ea8fe;--card:#171a21;--border:#262b36;}
*{box-sizing:border-box}
body{margin:0;font-family:-apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif;background:var(--bg);color:var(--fg);line-height:1.65}
.wrap{max-width:900px;margin:0 auto;padding:32px 24px 80px}
h1,h2,h3{color:#fff;line-height:1.3;margin-top:1.6em}
h1{border-bottom:1px solid var(--border);padding-bottom:.3em}
a{color:var(--accent);text-decoration:none}
a:hover{text-decoration:underline}
code{background:#0b0d12;padding:.15em .4em;border-radius:4px;font-size:.9em}
pre{background:#0b0d12;border:1px solid var(--border);border-radius:8px;padding:14px;overflow:auto}
pre code{background:none;padding:0}
table{border-collapse:collapse;width:100%;margin:1em 0}
th,td{border:1px solid var(--border);padding:8px 10px;text-align:left}
th{background:var(--card)}
blockquote{border-left:3px solid var(--accent);margin:1em 0;padding:.2em 1em;color:var(--muted);background:var(--card)}
hr{border:none;border-top:1px solid var(--border);margin:2em 0}
</style></head>
<body><div class="wrap"><h1>编译模式</h1>
<p>TypePHP 通过 <code>-m / --mode</code> 支持三种产物形态,由 <code>Translator::setBuildMode()</code> 归一化。三种模式共享同一套 PHP→C++ 翻译,区别只在于<strong>入口约定与链接目标</strong></p>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\src\Translator.php#L574-L589">src/Translator.php</a> · <a href="file:///D:\git\php\aot-compiler\docs\COMPILATION_MODES.md">docs/COMPILATION_MODES.md</a></p>
<h2>三种模式对比</h2>
<table><thead><tr><th>模式</th><th>参数</th><th>产物</th><th>入口要求</th><th>典型用途</th></tr></thead><tbody><tr><td>二进制</td><td><code>bin</code>(默认)</td><td>独立可执行文件</td><td>必须定义 <code>main()</code></td><td>CLI 工具、独立服务</td></tr><tr><td>共享库</td><td><code>lib</code></td><td><code>.so</code> / <code>.dll</code> / <code>.dylib</code></td><td>不需要 <code>main()</code></td><td>供其它程序调用的逻辑库</td></tr><tr><td>扩展</td><td><code>ext</code></td><td>PHP 扩展 <code>.so</code></td><td>不需要 <code>main()</code></td><td>嵌入现有 PHP 应用(php-fpm 等)</td></tr></tbody></table>
<p><code>bin</code><code>binary/cli</code> 的别名;<code>lib</code> 涵盖 <code>library/shared/dll/dylib/so</code><code>ext</code> 等价于 <code>extension</code>。未知值会报错退出。</p>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\src\Translator.php#L574-L589">src/Translator.php</a></p>
<h2>二进制模式(embed)</h2>
<p><code>bin</code> 模式在 <code>genExtension()</code> 中嵌入 <code>cli_set_process_title</code> / <code>cli_get_process_title</code> 内置函数,并在 <code>RINIT</code> 阶段用 <code>php::eval</code> 直接执行 <code>main()</code>(带 <code>$argc/$argv</code> 或空参数两种签名)。</p>
<pre class="mermaid">flowchart LR
A[&quot;main()&quot;] --&gt; B[&quot;RINIT: php_app_init()&quot;]
B --&gt; C[&quot;php::eval(&#x27;main($argc,$argv)&#x27;)&quot;]
C --&gt; D[&quot;RSHUTDOWN: php_app_clean()&quot;]</pre>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\src\Translator.php#L1043-L1066">src/Translator.php</a> · <a href="file:///D:\git\php\aot-compiler\src\Translator.php#L1055-L1059">src/Translator.php</a></p>
<h2>共享库模式</h2>
<p>库模式下,编译器会:</p>
<ul><li><code>exported</code> 函数加上 <code>TYPEPHP_&lt;NAME&gt;_API</code> 导出宏;</li><li>生成 <code>&lt;target&gt;.stub.php</code> 导入桩,供其它 TypePHP 工程按库调用;</li><li>在 Linux 上自动给目标名加 <code>lib</code> 前缀。</li></ul>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\src\Translator.php#L652-L666">src/Translator.php</a> · <a href="file:///D:\git\php\aot-compiler\src\Translator.php#L633-L650">src/Translator.php#L1736-L1738) · [src/Translator.php</a></p>
<h2>扩展模式</h2>
<p><code>ext</code> 模式会跳过 <code>main()</code>,使用 <code>ZEND_GET_MODULE()</code> 注册 Zend 模块,并在 <code>RSHUTDOWN</code> 清空函数/类/属性表以支持重复加载。</p>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\src\Translator.php#L1033-L1037">src/Translator.php</a> · <a href="file:///D:\git\php\aot-compiler\src\Translator.php#L1090-L1091">src/Translator.php</a></p>
<h2>相关阅读</h2>
<ul><li>命令行全量参数见 <a href="pages/compile-time-attributes.html">编译期属性</a><code>docs/COMPILER_CLI.md</code></li><li>产物如何被链接 → <a href="pages/build-pipeline.html">构建流水线</a></li></ul></div></body></html>

@ -1,54 +0,0 @@
<blog>
# 编译模式
TypePHP 通过 `-m / --mode` 支持三种产物形态,由 `Translator::setBuildMode()` 归一化。三种模式共享同一套 PHP→C++ 翻译,区别只在于**入口约定与链接目标**。
Sources: [src/Translator.php](src/Translator.php#L574-L589) · [docs/COMPILATION_MODES.md](docs/COMPILATION_MODES.md)
## 三种模式对比
| 模式 | 参数 | 产物 | 入口要求 | 典型用途 |
|---|---|---|---|---|
| 二进制 | `bin`(默认) | 独立可执行文件 | 必须定义 `main()` | CLI 工具、独立服务 |
| 共享库 | `lib` | `.so` / `.dll` / `.dylib` | 不需要 `main()` | 供其它程序调用的逻辑库 |
| 扩展 | `ext` | PHP 扩展 `.so` | 不需要 `main()` | 嵌入现有 PHP 应用(php-fpm 等) |
`bin``binary/cli` 的别名;`lib` 涵盖 `library/shared/dll/dylib/so`;`ext` 等价于 `extension`。未知值会报错退出。
Sources: [src/Translator.php](src/Translator.php#L574-L589)
## 二进制模式(embed)
`bin` 模式在 `genExtension()` 中嵌入 `cli_set_process_title` / `cli_get_process_title` 内置函数,并在 `RINIT` 阶段用 `php::eval` 直接执行 `main()`(带 `$argc/$argv` 或空参数两种签名)。
```mermaid
flowchart LR
A["main()"] --> B["RINIT: php_app_init()"]
B --> C["php::eval('main($argc,$argv)')"]
C --> D["RSHUTDOWN: php_app_clean()"]
```
Sources: [src/Translator.php](src/Translator.php#L1043-L1066) · [src/Translator.php](src/Translator.php#L1055-L1059)
## 共享库模式
库模式下,编译器会:
- 对 `exported` 函数加上 `TYPEPHP_<NAME>_API` 导出宏;
- 生成 `<target>.stub.php` 导入桩,供其它 TypePHP 工程按库调用;
- 在 Linux 上自动给目标名加 `lib` 前缀。
Sources: [src/Translator.php](src/Translator.php#L652-L666) · [src/Translator.php#L1736-L1738) · [src/Translator.php](src/Translator.php#L633-L650)
## 扩展模式
`ext` 模式会跳过 `main()`,使用 `ZEND_GET_MODULE()` 注册 Zend 模块,并在 `RSHUTDOWN` 清空函数/类/属性表以支持重复加载。
Sources: [src/Translator.php](src/Translator.php#L1033-L1037) · [src/Translator.php](src/Translator.php#L1090-L1091)
## 相关阅读
- 命令行全量参数见 [编译期属性](compile-time-attributes) 与 `docs/COMPILER_CLI.md`
- 产物如何被链接 → [构建流水线](build-pipeline)。
</blog>

@ -1,37 +0,0 @@
<!doctype html><html lang="zh"><head><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>编译期属性</title>
<script src="../script/mermaid.min.js"></script>
<script>document.addEventListener('DOMContentLoaded',function(){if(window.mermaid)mermaid.initialize({startOnLoad:true});});</script>
<style>
:root{--bg:#0f1117;--fg:#e6e6e6;--muted:#9aa0aa;--accent:#6ea8fe;--card:#171a21;--border:#262b36;}
*{box-sizing:border-box}
body{margin:0;font-family:-apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif;background:var(--bg);color:var(--fg);line-height:1.65}
.wrap{max-width:900px;margin:0 auto;padding:32px 24px 80px}
h1,h2,h3{color:#fff;line-height:1.3;margin-top:1.6em}
h1{border-bottom:1px solid var(--border);padding-bottom:.3em}
a{color:var(--accent);text-decoration:none}
a:hover{text-decoration:underline}
code{background:#0b0d12;padding:.15em .4em;border-radius:4px;font-size:.9em}
pre{background:#0b0d12;border:1px solid var(--border);border-radius:8px;padding:14px;overflow:auto}
pre code{background:none;padding:0}
table{border-collapse:collapse;width:100%;margin:1em 0}
th,td{border:1px solid var(--border);padding:8px 10px;text-align:left}
th{background:var(--card)}
blockquote{border-left:3px solid var(--accent);margin:1em 0;padding:.2em 1em;color:var(--muted);background:var(--card)}
hr{border:none;border-top:1px solid var(--border);margin:2em 0}
</style></head>
<body><div class="wrap"><h1>编译期属性</h1>
<p>除了 PHP 8 的运行时 attribute,TypePHP 引入一组<strong>编译期函数与属性</strong>,用于在翻译阶段向编译器传递类型/语义提示。它们被 <code>Transform</code> 层的 Lowering 算子在遍历 AST 时识别与校验。</p>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\src\Transform\CompileTimeAttribute.php">src/Transform/CompileTimeAttribute.php</a> · <a href="file:///D:\git\php\aot-compiler\src\Transform\CompileTimeAttributeRegistry.php">src/Transform/CompileTimeAttributeRegistry.php</a> · <a href="file:///D:\git\php\aot-compiler\docs\COMPILE_TIME_FUNCTIONS.md">docs/COMPILE_TIME_FUNCTIONS.md</a></p>
<h2>核心编译期函数</h2>
<table><thead><tr><th>函数</th><th>用途</th></tr></thead><tbody><tr><td><code>any($v)</code></td><td>显式标记变量为「任意 PHP 值」(通用 <code>php::Var</code>),绕过原生类型推断</td></tr><tr><td><code>refval($v, Type)</code></td><td>以指定类型解释变量(类型恢复/断言)</td></tr><tr><td><code>objval($v, &#x27;ClassName&#x27;)</code></td><td>从数组/返回值恢复对象的类信息(见 <a href="pages/native-types.html">native-types</a></td></tr><tr><td><code>expected()</code> / <code>unexpected()</code></td><td>编译期断言/分支提示</td></tr></tbody></table>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\docs\COMPILE_TIME_FUNCTIONS.md">docs/COMPILE_TIME_FUNCTIONS.md</a></p>
<h2>编译期属性</h2>
<p><code>CompileTimeAttribute::validateNode()</code><code>Visitor::enterNode</code> 中对每个节点校验编译期属性;<code>CompileTimeAttributeRegistry</code> 维护已知属性注册表。典型如 <code>MustUse</code>(要求函数/方法有返回值且调用方使用)、<code>Any</code>(类型放宽)等——<code>Preprocessor</code><code>parseFunctionDecl()</code> 中也会对 <code>MustUse</code> 强行校验返回类型。</p>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\src\Transform\Visitor.php#L30">src/Transform/Visitor.php</a> · <a href="file:///D:\git\php\aot-compiler\src\Preprocessor.php#L537">src/Preprocessor.php</a></p>
<h2>与 Lowering 的配合</h2>
<p><code>FunctionAttributeLowering</code> / <code>ConstructorLowering</code> / <code>GetterLowering</code> / <code>PropertyMethodLowering</code> 把带编译期属性的节点降级为可翻译结构;<code>RuntimeAttributeFactoryLowering</code> 作为独立访客挂在遍历链上,处理运行时工厂属性。</p>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\src\Transform\Visitor.php#L30-L35">src/Transform/Visitor.php</a> · <a href="file:///D:\git\php\aot-compiler\src\Translator.php#L2467">src/Translator.php</a></p>
<h2>相关阅读</h2>
<ul><li>遍历中如何调用这些算子 → <a href="pages/transform.html">AST 转换</a></li><li>类型如何因此被放宽/恢复 → <a href="pages/type-check.html">类型检查与兼容</a></li><li>SSA 优化如何消费类型信息 → <a href="pages/optimizer.html">优化器</a></li></ul></div></body></html>

@ -1,38 +0,0 @@
<blog>
# 编译期属性
除了 PHP 8 的运行时 attribute,TypePHP 引入一组**编译期函数与属性**,用于在翻译阶段向编译器传递类型/语义提示。它们被 `Transform` 层的 Lowering 算子在遍历 AST 时识别与校验。
Sources: [src/Transform/CompileTimeAttribute.php](src/Transform/CompileTimeAttribute.php) · [src/Transform/CompileTimeAttributeRegistry.php](src/Transform/CompileTimeAttributeRegistry.php) · [docs/COMPILE_TIME_FUNCTIONS.md](docs/COMPILE_TIME_FUNCTIONS.md)
## 核心编译期函数
| 函数 | 用途 |
|---|---|
| `any($v)` | 显式标记变量为「任意 PHP 值」(通用 `php::Var`),绕过原生类型推断 |
| `refval($v, Type)` | 以指定类型解释变量(类型恢复/断言) |
| `objval($v, 'ClassName')` | 从数组/返回值恢复对象的类信息(见 [native-types](native-types)) |
| `expected()` / `unexpected()` | 编译期断言/分支提示 |
Sources: [docs/COMPILE_TIME_FUNCTIONS.md](docs/COMPILE_TIME_FUNCTIONS.md)
## 编译期属性
`CompileTimeAttribute::validateNode()``Visitor::enterNode` 中对每个节点校验编译期属性;`CompileTimeAttributeRegistry` 维护已知属性注册表。典型如 `MustUse`(要求函数/方法有返回值且调用方使用)、`Any`(类型放宽)等——`Preprocessor` 在 `parseFunctionDecl()` 中也会对 `MustUse` 强行校验返回类型。
Sources: [src/Transform/Visitor.php](src/Transform/Visitor.php#L30) · [src/Preprocessor.php](src/Preprocessor.php#L537)
## 与 Lowering 的配合
`FunctionAttributeLowering` / `ConstructorLowering` / `GetterLowering` / `PropertyMethodLowering` 把带编译期属性的节点降级为可翻译结构;`RuntimeAttributeFactoryLowering` 作为独立访客挂在遍历链上,处理运行时工厂属性。
Sources: [src/Transform/Visitor.php](src/Transform/Visitor.php#L30-L35) · [src/Translator.php](src/Translator.php#L2467)
## 相关阅读
- 遍历中如何调用这些算子 → [AST 转换](transform)
- 类型如何因此被放宽/恢复 → [类型检查与兼容](type-check)
- SSA 优化如何消费类型信息 → [优化器](optimizer)
</blog>

@ -1,44 +0,0 @@
<!doctype html><html lang="zh"><head><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>扩展模块生成</title>
<script src="../script/mermaid.min.js"></script>
<script>document.addEventListener('DOMContentLoaded',function(){if(window.mermaid)mermaid.initialize({startOnLoad:true});});</script>
<style>
:root{--bg:#0f1117;--fg:#e6e6e6;--muted:#9aa0aa;--accent:#6ea8fe;--card:#171a21;--border:#262b36;}
*{box-sizing:border-box}
body{margin:0;font-family:-apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif;background:var(--bg);color:var(--fg);line-height:1.65}
.wrap{max-width:900px;margin:0 auto;padding:32px 24px 80px}
h1,h2,h3{color:#fff;line-height:1.3;margin-top:1.6em}
h1{border-bottom:1px solid var(--border);padding-bottom:.3em}
a{color:var(--accent);text-decoration:none}
a:hover{text-decoration:underline}
code{background:#0b0d12;padding:.15em .4em;border-radius:4px;font-size:.9em}
pre{background:#0b0d12;border:1px solid var(--border);border-radius:8px;padding:14px;overflow:auto}
pre code{background:none;padding:0}
table{border-collapse:collapse;width:100%;margin:1em 0}
th,td{border:1px solid var(--border);padding:8px 10px;text-align:left}
th{background:var(--card)}
blockquote{border-left:3px solid var(--accent);margin:1em 0;padding:.2em 1em;color:var(--muted);background:var(--card)}
hr{border:none;border-top:1px solid var(--border);margin:2em 0}
</style></head>
<body><div class="wrap"><h1>扩展模块生成</h1>
<p><code>genExtension()</code> 是流水线里最大的一块生成逻辑,产出 <code>extension-&lt;target&gt;.cc</code>——一个标准 Zend 扩展模块。它把前面收集的所有类、函数、常量、全局变量缝合进 Zend 的生命周期钩子(MINIT / MINIT / RINIT / RSHUTDOWN)。</p>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\src\Translator.php#L739-L1105">src/Translator.php</a></p>
<h2>生成结构</h2>
<pre class="mermaid">flowchart TD
A[&quot;extension-&lt;target&gt;.cc&quot;] --&gt; B[&quot;全局变量 / 类表 / 函数表 / 属性表 声明&quot;]
B --&gt; C[&quot;php_get_class / php_get_func / php_get_method / php_get_prop&quot;]
C --&gt; D[&quot;ext_functions[] (ZEND_FE 列表)&quot;]
D --&gt; E[&quot;PHP_MINIT: 注册类/反射处理器/符号&quot;]
E --&gt; F[&quot;PHP_RINIT: php_app_init + eval(main)&quot;]
F --&gt; G[&quot;PHP_RSHUTDOWN: php_app_clean + request_shutdown&quot;]
G --&gt; H[&quot;zend_module_entry&quot;]</pre>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\src\Translator.php#L769-L1087">src/Translator.php</a></p>
<h2>关键片段</h2>
<ul><li><strong>符号解析函数</strong><code>php_get_class</code> / <code>php_get_func</code> / <code>php_get_method</code> / <code>php_get_prop</code> 用缓存的 <code>class_map</code> / <code>func_map</code> / <code>prop_map</code> 把字符串名映射到运行时 <code>zend_class_entry<em></code> / <code>zend_function</em></code>,带 <code>UNEXPECTED</code> 快路径。</li><li><strong>MINIT</strong>:安装 Fiber/Generator 类、反射属性处理器,逐个注册类并设置属性处理器;最后调用各 <code>registerSymbols</code> 函数。</li><li><strong>RINIT</strong><code>php_app_init()</code> 注册常量、全局变量、静态属性、类数组常量;<code>bin</code> 模式下 <code>php::eval</code> 执行 <code>main()</code>(按 <code>$argc/$argv</code> 或空参两种签名)。</li><li><strong>RSHUTDOWN</strong><code>php_app_clean()</code> 清理全局变量、VAR 常量、类数组常量(含继承来的数组常量),<code>ext</code> 模式额外清空三张映射表以支持模块重载。</li></ul>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\src\Translator.php#L791-L837">src/Translator.php</a> · <a href="file:///D:\git\php\aot-compiler\src\Translator.php#L884-L1066">src/Translator.php</a></p>
<h2>模式差异</h2>
<ul><li><code>bin</code>:嵌入 <code>cli_set_process_title</code><code>RINIT</code> 直接跑 <code>main()</code></li><li><code>lib</code>:Linux 下导出 <code>save_ps_args</code> 占位符;<code>getModuleName()</code> 仍生成模块入口;</li><li><code>ext</code>:用 <code>ZEND_GET_MODULE()</code>,跳过 <code>main()</code>,RSHUTDOWN 清空映射表。</li></ul>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\src\Translator.php#L754-L767">src/Translator.php</a> · <a href="file:///D:\git\php\aot-compiler\src\Translator.php#L1090-L1096">src/Translator.php</a></p>
<h2>相关阅读</h2>
<ul><li>函数/类声明如何生成 → <a href="pages/gen-functions-classes.html">函数与类生成</a></li><li>生成的 <code>.cc</code> 如何被编译/缓存 → <a href="pages/incremental-cache.html">增量编译缓存</a></li><li>三种产物形态 → <a href="pages/compilation-modes.html">编译模式</a></li></ul></div></body></html>

@ -1,47 +0,0 @@
<blog>
# 扩展模块生成
`genExtension()` 是流水线里最大的一块生成逻辑,产出 `extension-<target>.cc`——一个标准 Zend 扩展模块。它把前面收集的所有类、函数、常量、全局变量缝合进 Zend 的生命周期钩子(MINIT / MINIT / RINIT / RSHUTDOWN)。
Sources: [src/Translator.php](src/Translator.php#L739-L1105)
## 生成结构
```mermaid
flowchart TD
A["extension-<target>.cc"] --> B["全局变量 / 类表 / 函数表 / 属性表 声明"]
B --> C["php_get_class / php_get_func / php_get_method / php_get_prop"]
C --> D["ext_functions[] (ZEND_FE 列表)"]
D --> E["PHP_MINIT: 注册类/反射处理器/符号"]
E --> F["PHP_RINIT: php_app_init + eval(main)"]
F --> G["PHP_RSHUTDOWN: php_app_clean + request_shutdown"]
G --> H["zend_module_entry"]
```
Sources: [src/Translator.php](src/Translator.php#L769-L1087)
## 关键片段
- **符号解析函数**:`php_get_class` / `php_get_func` / `php_get_method` / `php_get_prop` 用缓存的 `class_map` / `func_map` / `prop_map` 把字符串名映射到运行时 `zend_class_entry*` / `zend_function*`,带 `UNEXPECTED` 快路径。
- **MINIT**:安装 Fiber/Generator 类、反射属性处理器,逐个注册类并设置属性处理器;最后调用各 `registerSymbols` 函数。
- **RINIT**:`php_app_init()` 注册常量、全局变量、静态属性、类数组常量;`bin` 模式下 `php::eval` 执行 `main()`(按 `$argc/$argv` 或空参两种签名)。
- **RSHUTDOWN**:`php_app_clean()` 清理全局变量、VAR 常量、类数组常量(含继承来的数组常量),`ext` 模式额外清空三张映射表以支持模块重载。
Sources: [src/Translator.php](src/Translator.php#L791-L837) · [src/Translator.php](src/Translator.php#L884-L1066)
## 模式差异
- `bin`:嵌入 `cli_set_process_title`,`RINIT` 直接跑 `main()`
- `lib`:Linux 下导出 `save_ps_args` 占位符;`getModuleName()` 仍生成模块入口;
- `ext`:用 `ZEND_GET_MODULE()`,跳过 `main()`,RSHUTDOWN 清空映射表。
Sources: [src/Translator.php](src/Translator.php#L754-L767) · [src/Translator.php](src/Translator.php#L1090-L1096)
## 相关阅读
- 函数/类声明如何生成 → [函数与类生成](gen-functions-classes)
- 生成的 `.cc` 如何被编译/缓存 → [增量编译缓存](incremental-cache)
- 三种产物形态 → [编译模式](compilation-modes)
</blog>

@ -1,41 +0,0 @@
<!doctype html><html lang="zh"><head><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>函数与类生成</title>
<script src="../script/mermaid.min.js"></script>
<script>document.addEventListener('DOMContentLoaded',function(){if(window.mermaid)mermaid.initialize({startOnLoad:true});});</script>
<style>
:root{--bg:#0f1117;--fg:#e6e6e6;--muted:#9aa0aa;--accent:#6ea8fe;--card:#171a21;--border:#262b36;}
*{box-sizing:border-box}
body{margin:0;font-family:-apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif;background:var(--bg);color:var(--fg);line-height:1.65}
.wrap{max-width:900px;margin:0 auto;padding:32px 24px 80px}
h1,h2,h3{color:#fff;line-height:1.3;margin-top:1.6em}
h1{border-bottom:1px solid var(--border);padding-bottom:.3em}
a{color:var(--accent);text-decoration:none}
a:hover{text-decoration:underline}
code{background:#0b0d12;padding:.15em .4em;border-radius:4px;font-size:.9em}
pre{background:#0b0d12;border:1px solid var(--border);border-radius:8px;padding:14px;overflow:auto}
pre code{background:none;padding:0}
table{border-collapse:collapse;width:100%;margin:1em 0}
th,td{border:1px solid var(--border);padding:8px 10px;text-align:left}
th{background:var(--card)}
blockquote{border-left:3px solid var(--accent);margin:1em 0;padding:.2em 1em;color:var(--muted);background:var(--card)}
hr{border:none;border-top:1px solid var(--border);margin:2em 0}
</style></head>
<body><div class="wrap"><h1>函数与类生成</h1>
<p>本章聚焦两个最频繁的发射动作:<code>genFunctionWrapper()</code>(函数/方法 → C++ 函数)与 <code>genClassWrapper()</code>(类/接口/枚举 → C++ 类与 zend_class_entry 注册)。</p>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\src\Translator.php#L2515-L2528">src/Translator.php</a></p>
<h2>函数生成</h2>
<p><code>genFunctionDeclarations()</code> 为每个 <code>FunctionDef</code> 生成前向声明,规则包括:</p>
<ul><li><strong>方法</strong>:首参加 <code>php::Object &amp;this_</code>;trait 父类场景追加 <code>zend_class_entry *trait_parent_ce</code></li><li><strong>变参</strong>:声明为 <code>php::Array</code> 并附默认参数表达式;</li><li><strong>多返回</strong>:在 <code>typephp_multi_return</code> 命名空间再生成一份返回元组版本(<code>hasMultiReturn()</code>);</li><li><strong>热/冷路径</strong><code>hot</code>/<code>cold</code><code>TYPEPHP_HOT_ATTRIBUTE</code> / <code>TYPEPHP_COLD_ATTRIBUTE</code></li><li><strong>导入/导出</strong>:跨库函数用 <code>TYPEPHP_&lt;LIB&gt;_IMPORT</code> 宏,库导出函数用 <code>TYPEPHP_&lt;NAME&gt;_API</code></li></ul>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\src\Translator.php#L1740-L1830">src/Translator.php</a></p>
<h2>类生成</h2>
<p><code>genClassWrapper()</code> 为每个 <code>ClassDef</code> / <code>InterfaceDef</code> 生成:</p>
<ol><li>C++ 类结构与属性布局;</li><li>静态属性默认值(<code>php::setStaticProperty</code>);</li><li><code>create_object</code> 钩子与属性处理器安装(<code>genClassPropertyInit()</code>);</li><li>数组常量(<code>genClassArrayConstants()</code>)。</li></ol>
<p>属性初始化分两种情况(<a href="file:///D:\git\php\aot-compiler\src\Translator.php#L1909-L1963">src/Translator.php</a>):</p>
<ul><li><strong>标量/常量默认值</strong>:包成 <code>php::Var</code> 写入属性表;</li><li><strong>数组默认值</strong>:用 <code>ArrayInitPlan</code> 生成构造表达式,避免重复求值。</li></ul>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\src\Translator.php#L1894-L1966">src/Translator.php</a> · <a href="file:///D:\git\php\aot-compiler\src\Preprocessor.php#L974-L1002">src/Preprocessor.php</a></p>
<h2>类注册排序</h2>
<p><code>genClassCeList()</code><code>marcj/topsort</code> 按继承依赖拓扑排序生成 <code>zend_class_entry</code> 注册顺序,确保父类先于子类注册;内置类/接口(不在符号表中)直接加入排序而不参与依赖解析。</p>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\src\Translator.php#L2539-L2568">src/Translator.php</a></p>
<h2>相关阅读</h2>
<ul><li>所有声明头如何汇总 → <a href="pages/generator.html">生成器总览</a></li><li>模块入口如何引用这些符号 → <a href="pages/gen-extension.html">扩展模块生成</a></li><li>属性类型约束 → <a href="pages/native-types.html">原生类型映射</a></li></ul></div></body></html>

@ -1,48 +0,0 @@
<blog>
# 函数与类生成
本章聚焦两个最频繁的发射动作:`genFunctionWrapper()`(函数/方法 → C++ 函数)与 `genClassWrapper()`(类/接口/枚举 → C++ 类与 zend_class_entry 注册)。
Sources: [src/Translator.php](src/Translator.php#L2515-L2528)
## 函数生成
`genFunctionDeclarations()` 为每个 `FunctionDef` 生成前向声明,规则包括:
- **方法**:首参加 `php::Object &this_`;trait 父类场景追加 `zend_class_entry *trait_parent_ce`
- **变参**:声明为 `php::Array` 并附默认参数表达式;
- **多返回**:在 `typephp_multi_return` 命名空间再生成一份返回元组版本(`hasMultiReturn()`);
- **热/冷路径**:`hot`/`cold` 加 `TYPEPHP_HOT_ATTRIBUTE` / `TYPEPHP_COLD_ATTRIBUTE`
- **导入/导出**:跨库函数用 `TYPEPHP_<LIB>_IMPORT` 宏,库导出函数用 `TYPEPHP_<NAME>_API`
Sources: [src/Translator.php](src/Translator.php#L1740-L1830)
## 类生成
`genClassWrapper()` 为每个 `ClassDef` / `InterfaceDef` 生成:
1. C++ 类结构与属性布局;
2. 静态属性默认值(`php::setStaticProperty`);
3. `create_object` 钩子与属性处理器安装(`genClassPropertyInit()`);
4. 数组常量(`genClassArrayConstants()`)。
属性初始化分两种情况([src/Translator.php](src/Translator.php#L1909-L1963)):
- **标量/常量默认值**:包成 `php::Var` 写入属性表;
- **数组默认值**:用 `ArrayInitPlan` 生成构造表达式,避免重复求值。
Sources: [src/Translator.php](src/Translator.php#L1894-L1966) · [src/Preprocessor.php](src/Preprocessor.php#L974-L1002)
## 类注册排序
`genClassCeList()``marcj/topsort` 按继承依赖拓扑排序生成 `zend_class_entry` 注册顺序,确保父类先于子类注册;内置类/接口(不在符号表中)直接加入排序而不参与依赖解析。
Sources: [src/Translator.php](src/Translator.php#L2539-L2568)
## 相关阅读
- 所有声明头如何汇总 → [生成器总览](generator)
- 模块入口如何引用这些符号 → [扩展模块生成](gen-extension)
- 属性类型约束 → [原生类型映射](native-types)
</blog>

@ -1,37 +0,0 @@
<!doctype html><html lang="zh"><head><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>生成器总览</title>
<script src="../script/mermaid.min.js"></script>
<script>document.addEventListener('DOMContentLoaded',function(){if(window.mermaid)mermaid.initialize({startOnLoad:true});});</script>
<style>
:root{--bg:#0f1117;--fg:#e6e6e6;--muted:#9aa0aa;--accent:#6ea8fe;--card:#171a21;--border:#262b36;}
*{box-sizing:border-box}
body{margin:0;font-family:-apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif;background:var(--bg);color:var(--fg);line-height:1.65}
.wrap{max-width:900px;margin:0 auto;padding:32px 24px 80px}
h1,h2,h3{color:#fff;line-height:1.3;margin-top:1.6em}
h1{border-bottom:1px solid var(--border);padding-bottom:.3em}
a{color:var(--accent);text-decoration:none}
a:hover{text-decoration:underline}
code{background:#0b0d12;padding:.15em .4em;border-radius:4px;font-size:.9em}
pre{background:#0b0d12;border:1px solid var(--border);border-radius:8px;padding:14px;overflow:auto}
pre code{background:none;padding:0}
table{border-collapse:collapse;width:100%;margin:1em 0}
th,td{border:1px solid var(--border);padding:8px 10px;text-align:left}
th{background:var(--card)}
blockquote{border-left:3px solid var(--accent);margin:1em 0;padding:.2em 1em;color:var(--muted);background:var(--card)}
hr{border:none;border-top:1px solid var(--border);margin:2em 0}
</style></head>
<body><div class="wrap"><h1>生成器总览</h1>
<p><code>doConvert()</code> 收集完一个文件的语句后,调用各 <code>genXxx</code> 方法把 PHP 构造发射为 C++。所有发射逻辑分两类:<strong>声明头文件生成</strong><code><em>.h</code>)与 <strong>实现单元生成</strong><code></em>.cc</code>)。<code>src/Generator/</code> 下是一组辅助生成器,负责闭包、匿名类、参数、资源文件等专项结构。</p>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\src\Translator.php#L2477-L2536">src/Translator.php</a> · <a href="file:///D:\git\php\aot-compiler\src\Generator">src/Generator/</a></p>
<h2>主发射方法(在 Translator / CompilerBase)</h2>
<table><thead><tr><th>方法</th><th>产物</th></tr></thead><tbody><tr><td><code>genFunctionDeclarations()</code></td><td><code>php_&lt;target&gt;_func_decl.h</code>:所有函数前向声明</td></tr><tr><td><code>genDataDeclarations()</code></td><td><code>php_&lt;target&gt;_data_decl.h</code>:全局变量 / 常量 / 类映射表声明</td></tr><tr><td><code>genExtension()</code></td><td><code>extension-&lt;target&gt;.cc</code>:模块入口(MINIT/RINIT/RSHUTDOWN)</td></tr><tr><td><code>genClassWrapper()</code></td><td>单个类/接口的 C++ 实现</td></tr><tr><td><code>genFunctionWrapper()</code></td><td>单个函数的 C++ 实现</td></tr><tr><td><code>genClassPropertyInit()</code></td><td>属性默认值与 create_object 初始化块</td></tr></tbody></table>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\src\Translator.php#L1728-L1786">src/Translator.php</a> · <a href="file:///D:\git\php\aot-compiler\src\Translator.php#L692-L737">src/Translator.php</a> · <a href="file:///D:\git\php\aot-compiler\src\Translator.php#L739-L1105">src/Translator.php</a></p>
<h2>专项生成器(src/Generator/)</h2>
<table><thead><tr><th>生成器</th><th>负责</th></tr></thead><tbody><tr><td><code>ClosureGenerator</code></td><td>匿名函数 / 闭包 → C++ lambda 包装</td></tr><tr><td><code>AnonClassGenerator</code></td><td>匿名类</td></tr><tr><td><code>CallArgumentGenerator</code></td><td>调用实参的默认值/展开处理</td></tr><tr><td><code>DefaultArgumentGenerator</code></td><td>默认参数 helper(<code>genDefaultArgumentHelper*</code></td></tr><tr><td><code>FiberGenerator</code></td><td>协程 / Fiber 生成</td></tr><tr><td><code>PropertyPromotion</code></td><td>构造函数属性提升</td></tr><tr><td><code>ResourceFileGenerator</code></td><td>Windows <code>.rc</code> 资源文件</td></tr><tr><td><code>LibraryImportStubGenerator</code></td><td>跨库导入桩 <code>&lt;target&gt;.stub.php</code></td></tr><tr><td><code>TypeCheckGenerator</code></td><td>运行时类型检查代码</td></tr><tr><td><code>Symbol</code></td><td>符号名转义工具</td></tr></tbody></table>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\src\Generator">src/Generator/</a></p>
<h2>头文件依赖与增量缓存</h2>
<p>每个生成的 <code>.cc</code><code>#include</code> 项目级 <code>func_decl.h</code> / <code>data_decl.h</code> 与各自的 <code>_arginfo.h</code>。这些共享头文件的内容指纹正是 <a href="pages/incremental-cache.html">增量编译缓存</a> 判定是否重编译的依据——任一头变化都会让依赖它的 <code>.cc</code> 失效。</p>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\src\Translator.php#L1253-L1270">src/Translator.php</a> · <a href="file:///D:\git\php\aot-compiler\src\Translator.php#L1880-L1892">src/Translator.php</a></p>
<h2>相关阅读</h2>
<ul><li>函数与类如何逐个生成 → <a href="pages/gen-functions-classes.html">函数与类生成</a></li><li>模块入口如何拼装 → <a href="pages/gen-extension.html">扩展模块生成</a></li><li>翻译前的 AST 改写 → <a href="pages/transform.html">AST 转换</a></li></ul></div></body></html>

@ -1,51 +0,0 @@
<blog>
# 生成器总览
`doConvert()` 收集完一个文件的语句后,调用各 `genXxx` 方法把 PHP 构造发射为 C++。所有发射逻辑分两类:**声明头文件生成**(`*.h`)与 **实现单元生成**(`*.cc`)。`src/Generator/` 下是一组辅助生成器,负责闭包、匿名类、参数、资源文件等专项结构。
Sources: [src/Translator.php](src/Translator.php#L2477-L2536) · [src/Generator/](src/Generator/)
## 主发射方法(在 Translator / CompilerBase)
| 方法 | 产物 |
|---|---|
| `genFunctionDeclarations()` | `php_<target>_func_decl.h`:所有函数前向声明 |
| `genDataDeclarations()` | `php_<target>_data_decl.h`:全局变量 / 常量 / 类映射表声明 |
| `genExtension()` | `extension-<target>.cc`:模块入口(MINIT/RINIT/RSHUTDOWN) |
| `genClassWrapper()` | 单个类/接口的 C++ 实现 |
| `genFunctionWrapper()` | 单个函数的 C++ 实现 |
| `genClassPropertyInit()` | 属性默认值与 create_object 初始化块 |
Sources: [src/Translator.php](src/Translator.php#L1728-L1786) · [src/Translator.php](src/Translator.php#L692-L737) · [src/Translator.php](src/Translator.php#L739-L1105)
## 专项生成器(src/Generator/)
| 生成器 | 负责 |
|---|---|
| `ClosureGenerator` | 匿名函数 / 闭包 → C++ lambda 包装 |
| `AnonClassGenerator` | 匿名类 |
| `CallArgumentGenerator` | 调用实参的默认值/展开处理 |
| `DefaultArgumentGenerator` | 默认参数 helper(`genDefaultArgumentHelper*`) |
| `FiberGenerator` | 协程 / Fiber 生成 |
| `PropertyPromotion` | 构造函数属性提升 |
| `ResourceFileGenerator` | Windows `.rc` 资源文件 |
| `LibraryImportStubGenerator` | 跨库导入桩 `<target>.stub.php` |
| `TypeCheckGenerator` | 运行时类型检查代码 |
| `Symbol` | 符号名转义工具 |
Sources: [src/Generator/](src/Generator/)
## 头文件依赖与增量缓存
每个生成的 `.cc``#include` 项目级 `func_decl.h` / `data_decl.h` 与各自的 `_arginfo.h`。这些共享头文件的内容指纹正是 [增量编译缓存](incremental-cache) 判定是否重编译的依据——任一头变化都会让依赖它的 `.cc` 失效。
Sources: [src/Translator.php](src/Translator.php#L1253-L1270) · [src/Translator.php](src/Translator.php#L1880-L1892)
## 相关阅读
- 函数与类如何逐个生成 → [函数与类生成](gen-functions-classes)
- 模块入口如何拼装 → [扩展模块生成](gen-extension)
- 翻译前的 AST 改写 → [AST 转换](transform)
</blog>

@ -1,47 +0,0 @@
<!doctype html><html lang="zh"><head><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>高精度数值类型</title>
<script src="../script/mermaid.min.js"></script>
<script>document.addEventListener('DOMContentLoaded',function(){if(window.mermaid)mermaid.initialize({startOnLoad:true});});</script>
<style>
:root{--bg:#0f1117;--fg:#e6e6e6;--muted:#9aa0aa;--accent:#6ea8fe;--card:#171a21;--border:#262b36;}
*{box-sizing:border-box}
body{margin:0;font-family:-apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif;background:var(--bg);color:var(--fg);line-height:1.65}
.wrap{max-width:900px;margin:0 auto;padding:32px 24px 80px}
h1,h2,h3{color:#fff;line-height:1.3;margin-top:1.6em}
h1{border-bottom:1px solid var(--border);padding-bottom:.3em}
a{color:var(--accent);text-decoration:none}
a:hover{text-decoration:underline}
code{background:#0b0d12;padding:.15em .4em;border-radius:4px;font-size:.9em}
pre{background:#0b0d12;border:1px solid var(--border);border-radius:8px;padding:14px;overflow:auto}
pre code{background:none;padding:0}
table{border-collapse:collapse;width:100%;margin:1em 0}
th,td{border:1px solid var(--border);padding:8px 10px;text-align:left}
th{background:var(--card)}
blockquote{border-left:3px solid var(--accent);margin:1em 0;padding:.2em 1em;color:var(--muted);background:var(--card)}
hr{border:none;border-top:1px solid var(--border);margin:2em 0}
</style></head>
<body><div class="wrap"><h1>高精度数值类型</h1>
<p>PHP 原生 <code>int</code>(64 位)与 <code>float</code>(IEEE754 double)在金融、科学、密码学场景精度不足。TypePHP 提供三种高精度类型,底层直接调用成熟 C/C++ 数学库,<strong>零方法查找/解释器分派开销</strong>(运算本身仍有数学库计算与装箱成本)。</p>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\docs\HIGH_PRECISION_TYPES.md#L1-L44">docs/HIGH_PRECISION_TYPES.md</a></p>
<h2>三种类型概览</h2>
<table><thead><tr><th>类型</th><th>底层库</th><th>特点</th></tr></thead><tbody><tr><td><code>bigInt</code> (<code>php::BigInt</code>)</td><td>GMP (<code>libgmp</code>)</td><td>任意精度整数,永不溢出</td></tr><tr><td><code>decimal</code> (<code>php::Decimal</code>)</td><td>libmpdec</td><td>十进制小数,约 50 位有效数字,无二进制浮点误差</td></tr><tr><td><code>bigFloat</code> (<code>php::BigFloat</code>)</td><td>MPFR (<code>libmpfr</code>)</td><td>默认 256 bit,字符串输出 64 位有效数字</td></tr></tbody></table>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\docs\HIGH_PRECISION_TYPES.md#L39-L43">docs/HIGH_PRECISION_TYPES.md</a> · <a href="file:///D:\git\php\aot-compiler\src\Type.php#L15-L17">src/Type.php</a></p>
<h2>使用前置</h2>
<pre><code>&lt;?php
declare(strict_types=1);
use native_types;
function main(): void {
$x = bigInt(123456789012345678901234567890); // 30 位整数不丢精度
}</code></pre>
<p>系统需安装:<code>libgmp-dev</code><code>libmpdec-dev</code><code>libmpfr-dev</code>(见 README 依赖章节)。</p>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\docs\HIGH_PRECISION_TYPES.md#L51-L59">docs/HIGH_PRECISION_TYPES.md</a> · <a href="file:///D:\git\php\aot-compiler\README.md#L29-L40">README.md</a></p>
<h2>能力</h2>
<ul><li><strong>算术/比较/复合赋值</strong><code>+ - * / %</code><code>&lt; &gt; ==</code> 以及 <code>+=</code> 等均可用于高精度类型;</li><li><strong>通用方法调用</strong>:通过 <a href="file:///D:\git\php\aot-compiler\universal-methods">通用方法</a>(见 <code>docs/UNIVERSAL_METHODS.md</code>)统一接口;</li><li><strong>类型转换</strong>:高精度类型之间及与原生类型可互转;</li><li><strong>超长字面量自动识别</strong>:超出原生范围的整数字面量自动识别为 <code>bigInt</code></li><li><strong>混合运算类型提升</strong>:不同精度参与运算时按规则提升。</li></ul>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\docs\HIGH_PRECISION_TYPES.md#L7-L17">docs/HIGH_PRECISION_TYPES.md</a></p>
<h2>设计取舍与优化</h2>
<p>相关设计文档(属于调研/规划,可能与当前实现有出入):</p>
<ul><li><code>docs/BIG_NUMBER_INPLACE_OPTIMIZATION_PLAN.md</code> —— 高精度类型原地运算优化方案;</li><li><code>docs/GMP_GAP.md</code> —— GMP 能力差异分析。</li></ul>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\docs\BIG_NUMBER_INPLACE_OPTIMIZATION_PLAN.md">docs/BIG_NUMBER_INPLACE_OPTIMIZATION_PLAN.md</a> · <a href="file:///D:\git\php\aot-compiler\docs\GMP_GAP.md">docs/GMP_GAP.md</a></p>
<h2>相关阅读</h2>
<ul><li>类型如何映射到 C++ → <a href="pages/native-types.html">原生类型映射</a></li><li>类型判定逻辑 → <a href="pages/type-check.html">类型检查与兼容</a></li><li>数组/容器配合 → <code>docs/STD_CONTAINERS.md</code></li></ul></div></body></html>

@ -1,58 +0,0 @@
<blog>
# 高精度数值类型
PHP 原生 `int`(64 位)与 `float`(IEEE754 double)在金融、科学、密码学场景精度不足。TypePHP 提供三种高精度类型,底层直接调用成熟 C/C++ 数学库,**零方法查找/解释器分派开销**(运算本身仍有数学库计算与装箱成本)。
Sources: [docs/HIGH_PRECISION_TYPES.md](docs/HIGH_PRECISION_TYPES.md#L1-L44)
## 三种类型概览
| 类型 | 底层库 | 特点 |
|---|---|---|
| `bigInt` (`php::BigInt`) | GMP (`libgmp`) | 任意精度整数,永不溢出 |
| `decimal` (`php::Decimal`) | libmpdec | 十进制小数,约 50 位有效数字,无二进制浮点误差 |
| `bigFloat` (`php::BigFloat`) | MPFR (`libmpfr`) | 默认 256 bit,字符串输出 64 位有效数字 |
Sources: [docs/HIGH_PRECISION_TYPES.md](docs/HIGH_PRECISION_TYPES.md#L39-L43) · [src/Type.php](src/Type.php#L15-L17)
## 使用前置
```php
<?php
declare(strict_types=1);
use native_types;
function main(): void {
$x = bigInt(123456789012345678901234567890); // 30 位整数不丢精度
}
```
系统需安装:`libgmp-dev`、`libmpdec-dev`、`libmpfr-dev`(见 README 依赖章节)。
Sources: [docs/HIGH_PRECISION_TYPES.md](docs/HIGH_PRECISION_TYPES.md#L51-L59) · [README.md](README.md#L29-L40)
## 能力
- **算术/比较/复合赋值**:`+ - * / %`、`< > ==` 以及 `+=` 等均可用于高精度类型;
- **通用方法调用**:通过 [通用方法](universal-methods)(见 `docs/UNIVERSAL_METHODS.md`)统一接口;
- **类型转换**:高精度类型之间及与原生类型可互转;
- **超长字面量自动识别**:超出原生范围的整数字面量自动识别为 `bigInt`
- **混合运算类型提升**:不同精度参与运算时按规则提升。
Sources: [docs/HIGH_PRECISION_TYPES.md](docs/HIGH_PRECISION_TYPES.md#L7-L17)
## 设计取舍与优化
相关设计文档(属于调研/规划,可能与当前实现有出入):
- `docs/BIG_NUMBER_INPLACE_OPTIMIZATION_PLAN.md` —— 高精度类型原地运算优化方案;
- `docs/GMP_GAP.md` —— GMP 能力差异分析。
Sources: [docs/BIG_NUMBER_INPLACE_OPTIMIZATION_PLAN.md](docs/BIG_NUMBER_INPLACE_OPTIMIZATION_PLAN.md) · [docs/GMP_GAP.md](docs/GMP_GAP.md)
## 相关阅读
- 类型如何映射到 C++ → [原生类型映射](native-types)
- 类型判定逻辑 → [类型检查与兼容](type-check)
- 数组/容器配合 → `docs/STD_CONTAINERS.md`
</blog>

@ -1,38 +0,0 @@
<!doctype html><html lang="zh"><head><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>不支持的 PHP 特性</title>
<script src="../script/mermaid.min.js"></script>
<script>document.addEventListener('DOMContentLoaded',function(){if(window.mermaid)mermaid.initialize({startOnLoad:true});});</script>
<style>
:root{--bg:#0f1117;--fg:#e6e6e6;--muted:#9aa0aa;--accent:#6ea8fe;--card:#171a21;--border:#262b36;}
*{box-sizing:border-box}
body{margin:0;font-family:-apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif;background:var(--bg);color:var(--fg);line-height:1.65}
.wrap{max-width:900px;margin:0 auto;padding:32px 24px 80px}
h1,h2,h3{color:#fff;line-height:1.3;margin-top:1.6em}
h1{border-bottom:1px solid var(--border);padding-bottom:.3em}
a{color:var(--accent);text-decoration:none}
a:hover{text-decoration:underline}
code{background:#0b0d12;padding:.15em .4em;border-radius:4px;font-size:.9em}
pre{background:#0b0d12;border:1px solid var(--border);border-radius:8px;padding:14px;overflow:auto}
pre code{background:none;padding:0}
table{border-collapse:collapse;width:100%;margin:1em 0}
th,td{border:1px solid var(--border);padding:8px 10px;text-align:left}
th{background:var(--card)}
blockquote{border-left:3px solid var(--accent);margin:1em 0;padding:.2em 1em;color:var(--muted);background:var(--card)}
hr{border:none;border-top:1px solid var(--border);margin:2em 0}
</style></head>
<body><div class="wrap"><h1>不支持的 PHP 特性</h1>
<p>AOT 编译对 PHP 运行时做了静态化假设,因此<strong>并非所有 PHP 特性都可编译</strong>。遇到编译报错(如 <code>Not implemented</code>)时,应据此清单判断是设计限制、有意规则,还是尚未实现。</p>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\docs\INCOMPATIBLE_PHP_FEATURES.md">docs/INCOMPATIBLE_PHP_FEATURES.md</a> · <a href="file:///D:\git\php\aot-compiler\docs\PHP_INCOMPATIBILITY_CLASSIFICATION.md">docs/PHP_INCOMPATIBILITY_CLASSIFICATION.md</a></p>
<h2>不兼容分类</h2>
<p><code>PHP_INCOMPATIBILITY_CLASSIFICATION.md</code> 把限制分成四类,避免维护多份清单:</p>
<table><thead><tr><th>类别</th><th>含义</th></tr></thead><tbody><tr><td><strong>Hard Limit</strong></td><td>架构上无法支持(如依赖运行时动态加载的特性)</td></tr><tr><td><strong>Intentional Rule</strong></td><td>有意为之的设计规则(如可执行代码必须放进 <code>main()</code></td></tr><tr><td><strong>Pending</strong></td><td>计划支持但尚未实现</td></tr><tr><td><strong>Partial</strong></td><td>部分支持,有约束条件</td></tr></tbody></table>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\docs\PHP_INCOMPATIBILITY_CLASSIFICATION.md">docs/PHP_INCOMPATIBILITY_CLASSIFICATION.md</a></p>
<h2>常见设计规则</h2>
<ul><li><strong>游离可执行代码</strong>:文件顶层的可执行语句(循环、函数调用)不被允许,必须放在 <code>main()</code> 或函数体内;</li><li><strong>固定值类型属性不可 <code>unset</code>/赋 <code>null</code></strong>:见 <a href="pages/native-types.html">原生类型映射</a></li><li><strong>显式返回类型</strong>:函数/方法必须声明返回类型(<code>parseFunctionDecl()</code><code>fatalError</code>)。</li></ul>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\docs\QUICKSTART.md#L191-L203">docs/QUICKSTART.md</a> · <a href="file:///D:\git\php\aot-compiler\src\Preprocessor.php#L497-L536">src/Preprocessor.php</a></p>
<h2>维护约定</h2>
<ol><li>兼容性变化须<strong>同时更新</strong> <code>INCOMPATIBLE_PHP_FEATURES.md</code> 与分类文档;</li><li>所有语法/语义限制统一链接到当前兼容性清单,避免重复;</li><li>功能是否支持以 <strong>PHPT / PHPUnit 回归测试</strong> 为准;</li><li>历史研究文档(hhvm/kphp/peachpie 对比等)保留原始结论,不悄然改写为当前状态。</li></ol>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\docs\README.md#L30-L35">docs/README.md</a></p>
<h2>相关阅读</h2>
<ul><li>运行期能力边界 → <a href="pages/runtime.html">PHPX 运行时集成</a></li><li>类型约束 → <a href="pages/native-types.html">原生类型映射</a></li><li>属性/类约束 → <a href="pages/gen-functions-classes.html">函数与类生成</a></li></ul></div></body></html>

@ -1,45 +0,0 @@
<blog>
# 不支持的 PHP 特性
AOT 编译对 PHP 运行时做了静态化假设,因此**并非所有 PHP 特性都可编译**。遇到编译报错(如 `Not implemented`)时,应据此清单判断是设计限制、有意规则,还是尚未实现。
Sources: [docs/INCOMPATIBLE_PHP_FEATURES.md](docs/INCOMPATIBLE_PHP_FEATURES.md) · [docs/PHP_INCOMPATIBILITY_CLASSIFICATION.md](docs/PHP_INCOMPATIBILITY_CLASSIFICATION.md)
## 不兼容分类
`PHP_INCOMPATIBILITY_CLASSIFICATION.md` 把限制分成四类,避免维护多份清单:
| 类别 | 含义 |
|---|---|
| **Hard Limit** | 架构上无法支持(如依赖运行时动态加载的特性) |
| **Intentional Rule** | 有意为之的设计规则(如可执行代码必须放进 `main()`) |
| **Pending** | 计划支持但尚未实现 |
| **Partial** | 部分支持,有约束条件 |
Sources: [docs/PHP_INCOMPATIBILITY_CLASSIFICATION.md](docs/PHP_INCOMPATIBILITY_CLASSIFICATION.md)
## 常见设计规则
- **游离可执行代码**:文件顶层的可执行语句(循环、函数调用)不被允许,必须放在 `main()` 或函数体内;
- **固定值类型属性不可 `unset`/赋 `null`**:见 [原生类型映射](native-types);
- **显式返回类型**:函数/方法必须声明返回类型(`parseFunctionDecl()` 会 `fatalError`)。
Sources: [docs/QUICKSTART.md](docs/QUICKSTART.md#L191-L203) · [src/Preprocessor.php](src/Preprocessor.php#L497-L536)
## 维护约定
1. 兼容性变化须**同时更新** `INCOMPATIBLE_PHP_FEATURES.md` 与分类文档;
2. 所有语法/语义限制统一链接到当前兼容性清单,避免重复;
3. 功能是否支持以 **PHPT / PHPUnit 回归测试** 为准;
4. 历史研究文档(hhvm/kphp/peachpie 对比等)保留原始结论,不悄然改写为当前状态。
Sources: [docs/README.md](docs/README.md#L30-L35)
## 相关阅读
- 运行期能力边界 → [PHPX 运行时集成](runtime)
- 类型约束 → [原生类型映射](native-types)
- 属性/类约束 → [函数与类生成](gen-functions-classes)
</blog>

@ -1,42 +0,0 @@
<!doctype html><html lang="zh"><head><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>增量编译缓存</title>
<script src="../script/mermaid.min.js"></script>
<script>document.addEventListener('DOMContentLoaded',function(){if(window.mermaid)mermaid.initialize({startOnLoad:true});});</script>
<style>
:root{--bg:#0f1117;--fg:#e6e6e6;--muted:#9aa0aa;--accent:#6ea8fe;--card:#171a21;--border:#262b36;}
*{box-sizing:border-box}
body{margin:0;font-family:-apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif;background:var(--bg);color:var(--fg);line-height:1.65}
.wrap{max-width:900px;margin:0 auto;padding:32px 24px 80px}
h1,h2,h3{color:#fff;line-height:1.3;margin-top:1.6em}
h1{border-bottom:1px solid var(--border);padding-bottom:.3em}
a{color:var(--accent);text-decoration:none}
a:hover{text-decoration:underline}
code{background:#0b0d12;padding:.15em .4em;border-radius:4px;font-size:.9em}
pre{background:#0b0d12;border:1px solid var(--border);border-radius:8px;padding:14px;overflow:auto}
pre code{background:none;padding:0}
table{border-collapse:collapse;width:100%;margin:1em 0}
th,td{border:1px solid var(--border);padding:8px 10px;text-align:left}
th{background:var(--card)}
blockquote{border-left:3px solid var(--accent);margin:1em 0;padding:.2em 1em;color:var(--muted);background:var(--card)}
hr{border:none;border-top:1px solid var(--border);margin:2em 0}
</style></head>
<body><div class="wrap"><h1>增量编译缓存</h1>
<p>TypePHP 的二次编译速度,很大程度上取决于<strong>对象文件缓存是否命中</strong>。编译器对两类源文件做内容指纹缓存:phpx 的 <code>misc</code> 源,以及编译器自己生成的 <code>.cc</code> 单元。</p>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\src\Translator.php#L1116-L1292">src/Translator.php</a></p>
<h2>两类缓存</h2>
<table><thead><tr><th>方法</th><th>适用对象</th><th>缓存键组成</th></tr></thead><tbody><tr><td><code>hasMiscObjectFileCache()</code></td><td><code>phpx/src/misc/*.cc</code></td><td>编译命令 + PHP ABI(<code>PHP_VERSION_ID</code> / <code>ZEND_MODULE_API_NO</code> / <code>PHP_ZTS</code> / <code>PHP_INT_SIZE</code> 等)+ phpx 头文件 mtime</td></tr><tr><td><code>hasGeneratedObjectFileCache()</code></td><td>构建目录下的生成 <code>.cc</code></td><td>编译命令 + PHP ABI + <strong>所有生成头文件内容指纹</strong><code>func_decl.h</code> / <code>data_decl.h</code> / 各 <code>_arginfo.h</code></td></tr></tbody></table>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\src\Translator.php#L1116-L1160">src/Translator.php</a> · <a href="file:///D:\git\php\aot-compiler\src\Translator.php#L1207-L1240">src/Translator.php</a></p>
<h2>缓存键构造</h2>
<pre><code>hash(&#x27;sha256&#x27;,
buildCompileFileCommand(source, object) // 可复现的编译命令行
. &quot;\0&quot; . serialize($abi) // PHP ABI 快照
[. &quot;\0&quot; . header . &quot;\0&quot; . hash_file(header)] // 仅生成单元:逐头文件内容
)</code></pre>
<p>键写入 <code>&lt;object&gt;.typephp-cache</code> 元数据文件(<code>getMiscObjectCacheMetadataFile()</code>)。每次 <code>compileFile()</code> 先比较元数据文件中的键,再比较 <code>.o</code> 与源/头的 mtime——<strong>任一不满足即失效并重编译</strong></p>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\src\Translator.php#L1162-L1187">src/Translator.php</a> · <a href="file:///D:\git\php\aot-compiler\src\Translator.php#L1272-L1292">src/Translator.php</a></p>
<h2>设计要点</h2>
<ul><li><strong><code>extension-&lt;target&gt;.cc</code> 故意不进共享头依赖</strong>:它的内容随任意类变化而改变,若纳入指纹会让每个 <code>.cc</code> 都失效,破坏增量构建。它自身作为生成单元拥有独立缓存项。</li><li><strong><code>--force</code> / <code>--profile</code> 强制失效</strong>:这两类参数会跳过全部缓存,确保 misc 文件也被重编(profiler 需 <code>PPROF_ON</code> 宏生效)。</li><li><strong>头文件依赖集排序后参与哈希</strong>,避免路径顺序差异导致误失效。</li></ul>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\src\Translator.php#L1242-L1271">src/Translator.php</a> · <a href="file:///D:\git\php\aot-compiler\src\Translator.php#L1218-L1226">src/Translator.php</a></p>
<h2>相关阅读</h2>
<ul><li>缓存何时被查询 → <a href="pages/build-pipeline.html">构建流水线</a></li><li>编译命令从何而来 → <a href="pages/backend.html">编译器后端</a></li><li>整体性能分析 → <a href="pages/build-speed.html">构建速度与性能</a></li></ul></div></body></html>

@ -1,46 +0,0 @@
<blog>
# 增量编译缓存
TypePHP 的二次编译速度,很大程度上取决于**对象文件缓存是否命中**。编译器对两类源文件做内容指纹缓存:phpx 的 `misc` 源,以及编译器自己生成的 `.cc` 单元。
Sources: [src/Translator.php](src/Translator.php#L1116-L1292)
## 两类缓存
| 方法 | 适用对象 | 缓存键组成 |
|---|---|---|
| `hasMiscObjectFileCache()` | `phpx/src/misc/*.cc` | 编译命令 + PHP ABI(`PHP_VERSION_ID` / `ZEND_MODULE_API_NO` / `PHP_ZTS` / `PHP_INT_SIZE` 等)+ phpx 头文件 mtime |
| `hasGeneratedObjectFileCache()` | 构建目录下的生成 `.cc` | 编译命令 + PHP ABI + **所有生成头文件内容指纹**(`func_decl.h` / `data_decl.h` / 各 `_arginfo.h`) |
Sources: [src/Translator.php](src/Translator.php#L1116-L1160) · [src/Translator.php](src/Translator.php#L1207-L1240)
## 缓存键构造
```php
hash('sha256',
buildCompileFileCommand(source, object) // 可复现的编译命令行
. "\0" . serialize($abi) // PHP ABI 快照
[. "\0" . header . "\0" . hash_file(header)] // 仅生成单元:逐头文件内容
)
```
键写入 `<object>.typephp-cache` 元数据文件(`getMiscObjectCacheMetadataFile()`)。每次 `compileFile()` 先比较元数据文件中的键,再比较 `.o` 与源/头的 mtime——**任一不满足即失效并重编译**。
Sources: [src/Translator.php](src/Translator.php#L1162-L1187) · [src/Translator.php](src/Translator.php#L1272-L1292)
## 设计要点
- **`extension-<target>.cc` 故意不进共享头依赖**:它的内容随任意类变化而改变,若纳入指纹会让每个 `.cc` 都失效,破坏增量构建。它自身作为生成单元拥有独立缓存项。
- **`--force` / `--profile` 强制失效**:这两类参数会跳过全部缓存,确保 misc 文件也被重编(profiler 需 `PPROF_ON` 宏生效)。
- **头文件依赖集排序后参与哈希**,避免路径顺序差异导致误失效。
Sources: [src/Translator.php](src/Translator.php#L1242-L1271) · [src/Translator.php](src/Translator.php#L1218-L1226)
## 相关阅读
- 缓存何时被查询 → [构建流水线](build-pipeline)
- 编译命令从何而来 → [编译器后端](backend)
- 整体性能分析 → [构建速度与性能](build-speed)
</blog>

@ -1,43 +0,0 @@
<!doctype html><html lang="zh"><head><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>原生类型映射</title>
<script src="../script/mermaid.min.js"></script>
<script>document.addEventListener('DOMContentLoaded',function(){if(window.mermaid)mermaid.initialize({startOnLoad:true});});</script>
<style>
:root{--bg:#0f1117;--fg:#e6e6e6;--muted:#9aa0aa;--accent:#6ea8fe;--card:#171a21;--border:#262b36;}
*{box-sizing:border-box}
body{margin:0;font-family:-apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif;background:var(--bg);color:var(--fg);line-height:1.65}
.wrap{max-width:900px;margin:0 auto;padding:32px 24px 80px}
h1,h2,h3{color:#fff;line-height:1.3;margin-top:1.6em}
h1{border-bottom:1px solid var(--border);padding-bottom:.3em}
a{color:var(--accent);text-decoration:none}
a:hover{text-decoration:underline}
code{background:#0b0d12;padding:.15em .4em;border-radius:4px;font-size:.9em}
pre{background:#0b0d12;border:1px solid var(--border);border-radius:8px;padding:14px;overflow:auto}
pre code{background:none;padding:0}
table{border-collapse:collapse;width:100%;margin:1em 0}
th,td{border:1px solid var(--border);padding:8px 10px;text-align:left}
th{background:var(--card)}
blockquote{border-left:3px solid var(--accent);margin:1em 0;padding:.2em 1em;color:var(--muted);background:var(--card)}
hr{border:none;border-top:1px solid var(--border);margin:2em 0}
</style></head>
<body><div class="wrap"><h1>原生类型映射</h1>
<p>TypePHP 的核心性能来源之一:<strong>PHP 标量直接映射 C++ 原生类型</strong>,而不是一律装箱成 <code>zval</code>。所有可发射的 C++ 类型集中在 <code>Type</code> 类(<code>src/Type.php</code>),以字符串常量形式供生成器引用。</p>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\src\Type.php#L5-L27">src/Type.php</a> · <a href="file:///D:\git\php\aot-compiler\docs\NATIVE_TYPES.md">docs/NATIVE_TYPES.md</a></p>
<h2>类型常量表</h2>
<table><thead><tr><th>TypePHP 类型</th><th>C++ 映射</th><th>说明</th></tr></thead><tbody><tr><td><code>int</code></td><td><code>php::Int</code></td><td>zend_long,8 字节</td></tr><tr><td><code>float</code></td><td><code>php::Float</code></td><td>double,8 字节</td></tr><tr><td><code>bool</code></td><td><code>php::Bool</code></td><td>原生 bool</td></tr><tr><td><code>string</code></td><td><code>php::Str</code></td><td>字符串</td></tr><tr><td><code>array</code></td><td><code>php::Array</code></td><td>PHP 数组</td></tr><tr><td><code>object</code></td><td><code>php::Object</code></td><td>对象</td></tr><tr><td><code>mixed</code> / 动态</td><td><code>php::Var</code></td><td>通用 zval 盒子</td></tr><tr><td><code>resource</code> / <code>stream</code></td><td><code>php::Resource</code> / <code>php::Stream</code></td><td>资源</td></tr><tr><td><code>bigInt</code> / <code>decimal</code> / <code>bigFloat</code></td><td><code>php::BigInt</code> / <code>Decimal</code> / <code>php::BigFloat</code></td><td>高精度(见 <a href="pages/high-precision-types.html">high-precision-types</a></td></tr><tr><td><code>std</code> 容器</td><td><code>php::StdArray/StdVector/StdMap/StdOrderedMap</code></td><td>Std 容器</td></tr></tbody></table>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\src\Type.php#L7-L24">src/Type.php</a></p>
<h2>固定值类型属性约束</h2>
<p>AOT 要求<strong>对象属性在生命周期内保持声明类型</strong>。对 <code>int/float/bool/string/array</code> 这类固定值类型属性:</p>
<ul><li><strong>不允许</strong> <code>unset()</code> 或赋值 <code>null</code>(等价于把类型改成 null);</li><li>需要可空时显式声明 <code>?int $id = null;</code></li></ul>
<p>对象属性遵循静态类型规则:非空赋值需满足 <code>is-a</code> 继承关系,可用子类对象赋基类属性,反之不行。</p>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\docs\NATIVE_TYPES.md#L19-L51">docs/NATIVE_TYPES.md</a></p>
<h2>使用前置</h2>
<p>高精度/原生类型需在文件头声明 <code>use native_types</code>(并建议 <code>declare(strict_types=1)</code>),例如:</p>
<pre><code>&lt;?php
declare(strict_types=1);
use native_types;
function main(): void { /* ... */ }</code></pre>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\docs\HIGH_PRECISION_TYPES.md#L51-L59">docs/HIGH_PRECISION_TYPES.md</a></p>
<h2>相关阅读</h2>
<ul><li>类型如何被检查与兼容判定 → <a href="pages/type-check.html">类型检查与兼容</a></li><li>高精度类型的运算语义 → <a href="pages/high-precision-types.html">高精度数值类型</a></li><li>类型如何进入 C++ 生成 → <a href="pages/generator.html">生成器总览</a></li></ul></div></body></html>

@ -1,55 +0,0 @@
<blog>
# 原生类型映射
TypePHP 的核心性能来源之一:**PHP 标量直接映射 C++ 原生类型**,而不是一律装箱成 `zval`。所有可发射的 C++ 类型集中在 `Type` 类(`src/Type.php`),以字符串常量形式供生成器引用。
Sources: [src/Type.php](src/Type.php#L5-L27) · [docs/NATIVE_TYPES.md](docs/NATIVE_TYPES.md)
## 类型常量表
| TypePHP 类型 | C++ 映射 | 说明 |
|---|---|---|
| `int` | `php::Int` | zend_long,8 字节 |
| `float` | `php::Float` | double,8 字节 |
| `bool` | `php::Bool` | 原生 bool |
| `string` | `php::Str` | 字符串 |
| `array` | `php::Array` | PHP 数组 |
| `object` | `php::Object` | 对象 |
| `mixed` / 动态 | `php::Var` | 通用 zval 盒子 |
| `resource` / `stream` | `php::Resource` / `php::Stream` | 资源 |
| `bigInt` / `decimal` / `bigFloat` | `php::BigInt` / `Decimal` / `php::BigFloat` | 高精度(见 [high-precision-types](high-precision-types)) |
| `std` 容器 | `php::StdArray/StdVector/StdMap/StdOrderedMap` | Std 容器 |
Sources: [src/Type.php](src/Type.php#L7-L24)
## 固定值类型属性约束
AOT 要求**对象属性在生命周期内保持声明类型**。对 `int/float/bool/string/array` 这类固定值类型属性:
- **不允许** `unset()` 或赋值 `null`(等价于把类型改成 null);
- 需要可空时显式声明 `?int $id = null;`
对象属性遵循静态类型规则:非空赋值需满足 `is-a` 继承关系,可用子类对象赋基类属性,反之不行。
Sources: [docs/NATIVE_TYPES.md](docs/NATIVE_TYPES.md#L19-L51)
## 使用前置
高精度/原生类型需在文件头声明 `use native_types`(并建议 `declare(strict_types=1)`),例如:
```php
<?php
declare(strict_types=1);
use native_types;
function main(): void { /* ... */ }
```
Sources: [docs/HIGH_PRECISION_TYPES.md](docs/HIGH_PRECISION_TYPES.md#L51-L59)
## 相关阅读
- 类型如何被检查与兼容判定 → [类型检查与兼容](type-check)
- 高精度类型的运算语义 → [高精度数值类型](high-precision-types)
- 类型如何进入 C++ 生成 → [生成器总览](generator)
</blog>

@ -1,36 +0,0 @@
<!doctype html><html lang="zh"><head><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>优化器</title>
<script src="../script/mermaid.min.js"></script>
<script>document.addEventListener('DOMContentLoaded',function(){if(window.mermaid)mermaid.initialize({startOnLoad:true});});</script>
<style>
:root{--bg:#0f1117;--fg:#e6e6e6;--muted:#9aa0aa;--accent:#6ea8fe;--card:#171a21;--border:#262b36;}
*{box-sizing:border-box}
body{margin:0;font-family:-apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif;background:var(--bg);color:var(--fg);line-height:1.65}
.wrap{max-width:900px;margin:0 auto;padding:32px 24px 80px}
h1,h2,h3{color:#fff;line-height:1.3;margin-top:1.6em}
h1{border-bottom:1px solid var(--border);padding-bottom:.3em}
a{color:var(--accent);text-decoration:none}
a:hover{text-decoration:underline}
code{background:#0b0d12;padding:.15em .4em;border-radius:4px;font-size:.9em}
pre{background:#0b0d12;border:1px solid var(--border);border-radius:8px;padding:14px;overflow:auto}
pre code{background:none;padding:0}
table{border-collapse:collapse;width:100%;margin:1em 0}
th,td{border:1px solid var(--border);padding:8px 10px;text-align:left}
th{background:var(--card)}
blockquote{border-left:3px solid var(--accent);margin:1em 0;padding:.2em 1em;color:var(--muted);background:var(--card)}
hr{border:none;border-top:1px solid var(--border);margin:2em 0}
</style></head>
<body><div class="wrap"><h1>优化器</h1>
<p>TypePHP 在语义分析阶段运行一组 <strong>SSA(静态单赋值)驱动</strong>的优化器,目标是减少运行时开销与生成的 C++ 体积。优化入口与 SSA 构建分别在 <code>src/Optimizer/</code><code>src/Analysis/SsaBuilder.php</code></p>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\src\Optimizer">src/Optimizer/</a> · <a href="file:///D:\git\php\aot-compiler\src\Analysis\SsaBuilder.php">src/Analysis/SsaBuilder.php</a></p>
<h2>优化器清单</h2>
<table><thead><tr><th></th><th>作用</th></tr></thead><tbody><tr><td><code>SsaBuilder</code></td><td>构建 SSA 形式,为下游优化提供定义-使用链</td></tr><tr><td><code>SsaTypeOptimizer</code></td><td>基于 SSA 的类型推导,传播确定类型以去除运行时 <code>php::Var</code> 装箱</td></tr><tr><td><code>SsaPropOptimizer</code></td><td>属性类型的 SSA 推导与去虚化</td></tr><tr><td><code>FuncCallOptimizer</code></td><td>函数调用优化(内联候选、已知函数特化等)</td></tr><tr><td><code>LoopVarOptimizer</code></td><td>循环变量类型/作用域优化</td></tr></tbody></table>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\src\Optimizer\SsaTypeOptimizer.php">src/Optimizer/SsaTypeOptimizer.php</a> · <a href="file:///D:\git\php\aot-compiler\src\Optimizer\SsaPropOptimizer.php">src/Optimizer/SsaPropOptimizer.php</a> · <a href="file:///D:\git\php\aot-compiler\src\Optimizer\FuncCallOptimizer.php">src/Optimizer/FuncCallOptimizer.php</a> · <a href="file:///D:\git\php\aot-compiler\src\Optimizer\LoopVarOptimizer.php">src/Optimizer/LoopVarOptimizer.php</a></p>
<h2>与优化级别的关系</h2>
<p>命令行 <code>-O &lt;level&gt;</code>(0–3)控制优化强度:级别越高,优化器介入越深、生成代码越紧凑,但编译期耗时也越大;<code>-d/--debug</code> 会关闭优化并加调试符号。</p>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\src\Translator.php#L234-L236">src/Translator.php</a> · <a href="file:///D:\git\php\aot-compiler\src\Translator.php#L272-L274">src/Translator.php</a></p>
<h2>与类型系统的协同</h2>
<p>SSA 类型优化直接消费 <a href="pages/type-check.html">类型检查与兼容</a> 的判定结果——一旦推导确认某变量是固定原生类型,生成器即可发射 <code>php::Int</code> 而非通用 <code>php::Var</code>,省去 zval 装箱。这是「原生类型优先」原则在运行期的落地。</p>
<h2>相关阅读</h2>
<ul><li>类型如何判定 → <a href="pages/type-check.html">类型检查与兼容</a></li><li>编译期属性如何辅助优化 → <a href="pages/compile-time-attributes.html">编译期属性</a></li><li>整体速度瓶颈与改进 → <a href="pages/build-speed.html">构建速度与性能</a></li></ul></div></body></html>

@ -1,37 +0,0 @@
<blog>
# 优化器
TypePHP 在语义分析阶段运行一组 **SSA(静态单赋值)驱动**的优化器,目标是减少运行时开销与生成的 C++ 体积。优化入口与 SSA 构建分别在 `src/Optimizer/``src/Analysis/SsaBuilder.php`
Sources: [src/Optimizer/](src/Optimizer/) · [src/Analysis/SsaBuilder.php](src/Analysis/SsaBuilder.php)
## 优化器清单
| 类 | 作用 |
|---|---|
| `SsaBuilder` | 构建 SSA 形式,为下游优化提供定义-使用链 |
| `SsaTypeOptimizer` | 基于 SSA 的类型推导,传播确定类型以去除运行时 `php::Var` 装箱 |
| `SsaPropOptimizer` | 属性类型的 SSA 推导与去虚化 |
| `FuncCallOptimizer` | 函数调用优化(内联候选、已知函数特化等) |
| `LoopVarOptimizer` | 循环变量类型/作用域优化 |
Sources: [src/Optimizer/SsaTypeOptimizer.php](src/Optimizer/SsaTypeOptimizer.php) · [src/Optimizer/SsaPropOptimizer.php](src/Optimizer/SsaPropOptimizer.php) · [src/Optimizer/FuncCallOptimizer.php](src/Optimizer/FuncCallOptimizer.php) · [src/Optimizer/LoopVarOptimizer.php](src/Optimizer/LoopVarOptimizer.php)
## 与优化级别的关系
命令行 `-O <level>`(0–3)控制优化强度:级别越高,优化器介入越深、生成代码越紧凑,但编译期耗时也越大;`-d/--debug` 会关闭优化并加调试符号。
Sources: [src/Translator.php](src/Translator.php#L234-L236) · [src/Translator.php](src/Translator.php#L272-L274)
## 与类型系统的协同
SSA 类型优化直接消费 [类型检查与兼容](type-check) 的判定结果——一旦推导确认某变量是固定原生类型,生成器即可发射 `php::Int` 而非通用 `php::Var`,省去 zval 装箱。这是「原生类型优先」原则在运行期的落地。
## 相关阅读
- 类型如何判定 → [类型检查与兼容](type-check)
- 编译期属性如何辅助优化 → [编译期属性](compile-time-attributes)
- 整体速度瓶颈与改进 → [构建速度与性能](build-speed)
</blog>

@ -1,35 +0,0 @@
<!doctype html><html lang="zh"><head><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>项目简介</title>
<script src="../script/mermaid.min.js"></script>
<script>document.addEventListener('DOMContentLoaded',function(){if(window.mermaid)mermaid.initialize({startOnLoad:true});});</script>
<style>
:root{--bg:#0f1117;--fg:#e6e6e6;--muted:#9aa0aa;--accent:#6ea8fe;--card:#171a21;--border:#262b36;}
*{box-sizing:border-box}
body{margin:0;font-family:-apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif;background:var(--bg);color:var(--fg);line-height:1.65}
.wrap{max-width:900px;margin:0 auto;padding:32px 24px 80px}
h1,h2,h3{color:#fff;line-height:1.3;margin-top:1.6em}
h1{border-bottom:1px solid var(--border);padding-bottom:.3em}
a{color:var(--accent);text-decoration:none}
a:hover{text-decoration:underline}
code{background:#0b0d12;padding:.15em .4em;border-radius:4px;font-size:.9em}
pre{background:#0b0d12;border:1px solid var(--border);border-radius:8px;padding:14px;overflow:auto}
pre code{background:none;padding:0}
table{border-collapse:collapse;width:100%;margin:1em 0}
th,td{border:1px solid var(--border);padding:8px 10px;text-align:left}
th{background:var(--card)}
blockquote{border-left:3px solid var(--accent);margin:1em 0;padding:.2em 1em;color:var(--muted);background:var(--card)}
hr{border:none;border-top:1px solid var(--border);margin:2em 0}
</style></head>
<body><div class="wrap"><h1>项目简介</h1>
<p><strong>TypePHP</strong> 是 Swoole 团队开发的 <strong>PHP 原生 AOT 编译器</strong>(仓库名 <code>aot-compiler</code>,Composer 包名 <code>swoole/typephp</code>)。它把 PHP 源码在编译期直接翻译成 <strong>C++</strong>,再借助 PHPX 运行时与 Zend 引擎嵌入(<code>embed</code>)能力,链接为 <strong>Windows / Linux / macOS</strong> 平台原生的可执行文件、共享库或 PHP 扩展。</p>
<p>当前版本 <code>v0.4.3</code>(见 <code>src/Translator.php</code> 常量 <code>VERSION</code>)。编译器自身需要 <strong>PHP 8.4+</strong>;生成的产物可面向 <strong>PHP 8.2 ~ 8.5</strong> 的运行时语义。</p>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\composer.json#L2-L4">composer.json</a> · <a href="file:///D:\git\php\aot-compiler\src\Translator.php#L61-L62">src/Translator.php</a> · <a href="file:///D:\git\php\aot-compiler\README.md#L1-L6">README.md</a></p>
<h2>为什么不是解释执行</h2>
<p>传统 PHP 每次运行都要经过「词法 → 语法 → 字节码 → Zend 解释」四步。TypePHP 把前两步(以及大量语义分析)在编译期一次性完成,生成的 C++ 再被优化编译器(GCC/Clang/MSVC)进一步内联、逃逸分析与向量化,最终产出的二进制<strong>不再依赖 <code>php</code> 解释器即可运行</strong>——只依赖编译进来的 Zend 引擎(<code>libphp.so</code> / <code>php8ts.lib</code>)。</p>
<blockquote>注意:预览版目前<strong>仅支持 Linux</strong>(建议 Ubuntu 22.04),Windows 编译需在具备 Windows SDK 与 PHP embed 库的环境下进行。</blockquote>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\README.md#L42-L43">README.md</a> · <a href="file:///D:\git\php\aot-compiler\composer.json#L8-L16">composer.json</a></p>
<h2>设计原则</h2>
<ul><li><strong>原生类型优先</strong>:标量(int/float/bool/string)直接映射 C++ 原生类型,避免 zval 装箱开销。</li><li><strong>PHP 语义对齐</strong>:类、接口、trait、枚举、反射、属性(attribute)、生成器(Generator)等尽量保持运行时行为。</li><li><strong>可增量构建</strong>:phpx misc 文件与编译器生成单元都带内容指纹缓存,改动一处不必全量重编译。</li></ul>
<h2>阅读路线</h2>
<ul><li>想了解「PHP 如何一步步变成二进制」→ <a href="pages/pipeline.html">编译流水线</a></li><li>想区分三种产物形态 → <a href="pages/compilation-modes.html">编译模式</a></li><li>想立刻跑通一个例子 → <a href="pages/quickstart.html">快速开始</a></li><li>想深入每个子系统 → 依次阅读 <a href="pages/parser.html">解析器与 AST</a><a href="pages/preprocessor.html">预处理与符号表</a><a href="pages/transform.html">AST 转换</a><a href="pages/generator.html">代码生成</a></li></ul></div></body></html>

@ -1,32 +0,0 @@
<blog>
# 项目简介
**TypePHP** 是 Swoole 团队开发的 **PHP 原生 AOT 编译器**(仓库名 `aot-compiler`,Composer 包名 `swoole/typephp`)。它把 PHP 源码在编译期直接翻译成 **C++**,再借助 PHPX 运行时与 Zend 引擎嵌入(`embed`)能力,链接为 **Windows / Linux / macOS** 平台原生的可执行文件、共享库或 PHP 扩展。
当前版本 `v0.4.3`(见 `src/Translator.php` 常量 `VERSION`)。编译器自身需要 **PHP 8.4+**;生成的产物可面向 **PHP 8.2 ~ 8.5** 的运行时语义。
Sources: [composer.json](composer.json#L2-L4) · [src/Translator.php](src/Translator.php#L61-L62) · [README.md](README.md#L1-L6)
## 为什么不是解释执行
传统 PHP 每次运行都要经过「词法 → 语法 → 字节码 → Zend 解释」四步。TypePHP 把前两步(以及大量语义分析)在编译期一次性完成,生成的 C++ 再被优化编译器(GCC/Clang/MSVC)进一步内联、逃逸分析与向量化,最终产出的二进制**不再依赖 `php` 解释器即可运行**——只依赖编译进来的 Zend 引擎(`libphp.so` / `php8ts.lib`)。
> 注意:预览版目前**仅支持 Linux**(建议 Ubuntu 22.04),Windows 编译需在具备 Windows SDK 与 PHP embed 库的环境下进行。
Sources: [README.md](README.md#L42-L43) · [composer.json](composer.json#L8-L16)
## 设计原则
- **原生类型优先**:标量(int/float/bool/string)直接映射 C++ 原生类型,避免 zval 装箱开销。
- **PHP 语义对齐**:类、接口、trait、枚举、反射、属性(attribute)、生成器(Generator)等尽量保持运行时行为。
- **可增量构建**:phpx misc 文件与编译器生成单元都带内容指纹缓存,改动一处不必全量重编译。
## 阅读路线
- 想了解「PHP 如何一步步变成二进制」→ [编译流水线](pipeline)
- 想区分三种产物形态 → [编译模式](compilation-modes)
- 想立刻跑通一个例子 → [快速开始](quickstart)
- 想深入每个子系统 → 依次阅读 [解析器与 AST](parser) → [预处理与符号表](preprocessor) → [AST 转换](transform) → [代码生成](generator)
</blog>

@ -1,45 +0,0 @@
<!doctype html><html lang="zh"><head><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>解析器与 AST</title>
<script src="../script/mermaid.min.js"></script>
<script>document.addEventListener('DOMContentLoaded',function(){if(window.mermaid)mermaid.initialize({startOnLoad:true});});</script>
<style>
:root{--bg:#0f1117;--fg:#e6e6e6;--muted:#9aa0aa;--accent:#6ea8fe;--card:#171a21;--border:#262b36;}
*{box-sizing:border-box}
body{margin:0;font-family:-apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif;background:var(--bg);color:var(--fg);line-height:1.65}
.wrap{max-width:900px;margin:0 auto;padding:32px 24px 80px}
h1,h2,h3{color:#fff;line-height:1.3;margin-top:1.6em}
h1{border-bottom:1px solid var(--border);padding-bottom:.3em}
a{color:var(--accent);text-decoration:none}
a:hover{text-decoration:underline}
code{background:#0b0d12;padding:.15em .4em;border-radius:4px;font-size:.9em}
pre{background:#0b0d12;border:1px solid var(--border);border-radius:8px;padding:14px;overflow:auto}
pre code{background:none;padding:0}
table{border-collapse:collapse;width:100%;margin:1em 0}
th,td{border:1px solid var(--border);padding:8px 10px;text-align:left}
th{background:var(--card)}
blockquote{border-left:3px solid var(--accent);margin:1em 0;padding:.2em 1em;color:var(--muted);background:var(--card)}
hr{border:none;border-top:1px solid var(--border);margin:2em 0}
</style></head>
<body><div class="wrap"><h1>解析器与 AST</h1>
<p>TypePHP 复用 <strong>nikic/php-parser</strong> 作为前端词法/语法分析器(依赖固定版本 <code>5.6.1</code>)。解析得到的 PHP AST 是后续所有翻译与符号收集的输入。本章说明 AST 从何而来、以何种形态进入编译器。</p>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\composer.json#L10">composer.json</a> · <a href="file:///D:\git\php\aot-compiler\project.yml#L34-L35">project.yml</a></p>
<h2>AST 来源</h2>
<p>解析发生在两个时机:</p>
<ol><li><strong>预处理阶段</strong><code>Preprocessor::prepareFile()</code> 解析每个 PHP 文件,收集类/函数/常量/属性等符号,构建全局符号表(详见 <a href="pages/preprocessor.html">预处理与符号表</a>)。</li><li><strong>转换阶段</strong><code>doConvert()</code> 通过 <code>parseCachedAst()</code> 解析同一文件,得到供代码生成的 AST。</li></ol>
<blockquote><code>parseCachedAst()</code> 做了两层缓存——同一文件的 AST 在进程内复用,避免重复解析;这也是编译性能优化(S1)的关键点之一。</blockquote>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\src\Preprocessor.php#L116">src/Preprocessor.php</a> · <a href="file:///D:\git\php\aot-compiler\src\Translator.php#L2459">src/Translator.php</a></p>
<h2>表达式翻译:Parser Trait 集合</h2>
<p>编译器主体 <code>CompilerBase</code> 并未把「PHP 表达式 → C++」的逻辑塞进一个巨型方法,而是拆成 <code>src/Parser/</code> 下的 <strong>21 个 trait</strong>,每个 trait 负责一类语法结构:</p>
<table><thead><tr><th>Trait</th><th>负责的 PHP 结构</th></tr></thead><tbody><tr><td><code>BinaryOpTrait</code></td><td><code>+ - * /</code> 等二元运算</td></tr><tr><td><code>AssignOpTrait</code></td><td><code>+=</code><code>??=</code> 等复合赋值</td></tr><tr><td><code>UnaryExpressionTrait</code></td><td><code>!</code><code>-</code><code>++</code> 等一元运算</td></tr><tr><td><code>FunctionCallTrait</code></td><td>普通函数调用</td></tr><tr><td><code>MethodCallTrait</code> / <code>UniversalMethodCall</code></td><td>对象/静态方法调用</td></tr><tr><td><code>PropertyAccessTrait</code></td><td>属性读写</td></tr><tr><td><code>ArrayExpressionTrait</code></td><td>数组字面量</td></tr><tr><td><code>ForeachTrait</code> / <code>LoopControlTrait</code></td><td><code>foreach</code> / <code>for</code> / <code>while</code></td></tr><tr><td><code>SelectionExpressionTrait</code> / <code>SwitchTrait</code></td><td><code>if</code> / <code>switch</code></td></tr><tr><td><code>ConditionalControlTrait</code></td><td><code>?:</code> / <code>match</code></td></tr><tr><td><code>NullsafeAccessTrait</code></td><td><code>?-&gt;</code></td></tr><tr><td><code>ClassConstantFetchTrait</code></td><td>类常量读取</td></tr><tr><td><code>TypeConversionTrait</code> / <code>TypeDetectionTrait</code></td><td>类型转换与 <code>instanceof</code></td></tr><tr><td><code>ConstantExpressionTrait</code></td><td>编译期常量折叠</td></tr><tr><td><code>ExceptionControlFlowTrait</code></td><td><code>try/catch/throw</code></td></tr><tr><td><code>StdContainerTrait</code></td><td><code>std</code> 容器操作</td></tr></tbody></table>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\src\CompilerBase.php#L43-L63">src/CompilerBase.php</a> · <a href="file:///D:\git\php\aot-compiler\src\Parser">src/Parser/</a></p>
<h2>访客链</h2>
<p><code>doConvert()</code> 在遍历 AST 前先挂载四个 <code>NodeVisitor</code></p>
<pre><code>$traverser-&gt;addVisitor(new NameResolver(...)); // 全限定名解析
$traverser-&gt;addVisitor(new Visitor(...)); // Transform lowering(见 transform)
$traverser-&gt;addVisitor(new ConstantExpressionValidationVisitor(...));
$traverser-&gt;addVisitor(new RuntimeAttributeFactoryLowering(...));</code></pre>
<p><code>NameResolver</code> 把短名解析为全限定名(但不替换节点),让后续生成能拿到准确的命名空间路径。</p>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\src\Translator.php#L2460-L2467">src/Translator.php</a></p>
<h2>相关阅读</h2>
<ul><li>符号如何被收集 → <a href="pages/preprocessor.html">预处理与符号表</a></li><li>遍历中做了哪些改写 → <a href="pages/transform.html">AST 转换</a></li><li>解析结果的消费方 → <a href="pages/generator.html">代码生成</a></li></ul></div></body></html>

@ -1,66 +0,0 @@
<blog>
# 解析器与 AST
TypePHP 复用 **nikic/php-parser** 作为前端词法/语法分析器(依赖固定版本 `5.6.1`)。解析得到的 PHP AST 是后续所有翻译与符号收集的输入。本章说明 AST 从何而来、以何种形态进入编译器。
Sources: [composer.json](composer.json#L10) · [project.yml](project.yml#L34-L35)
## AST 来源
解析发生在两个时机:
1. **预处理阶段**:`Preprocessor::prepareFile()` 解析每个 PHP 文件,收集类/函数/常量/属性等符号,构建全局符号表(详见 [预处理与符号表](preprocessor))。
2. **转换阶段**:`doConvert()` 通过 `parseCachedAst()` 解析同一文件,得到供代码生成的 AST。
> `parseCachedAst()` 做了两层缓存——同一文件的 AST 在进程内复用,避免重复解析;这也是编译性能优化(S1)的关键点之一。
Sources: [src/Preprocessor.php](src/Preprocessor.php#L116) · [src/Translator.php](src/Translator.php#L2459)
## 表达式翻译:Parser Trait 集合
编译器主体 `CompilerBase` 并未把「PHP 表达式 → C++」的逻辑塞进一个巨型方法,而是拆成 `src/Parser/` 下的 **21 个 trait**,每个 trait 负责一类语法结构:
| Trait | 负责的 PHP 结构 |
|---|---|
| `BinaryOpTrait` | `+ - * /` 等二元运算 |
| `AssignOpTrait` | `+=`、`??=` 等复合赋值 |
| `UnaryExpressionTrait` | `!`、`-`、`++` 等一元运算 |
| `FunctionCallTrait` | 普通函数调用 |
| `MethodCallTrait` / `UniversalMethodCall` | 对象/静态方法调用 |
| `PropertyAccessTrait` | 属性读写 |
| `ArrayExpressionTrait` | 数组字面量 |
| `ForeachTrait` / `LoopControlTrait` | `foreach` / `for` / `while` |
| `SelectionExpressionTrait` / `SwitchTrait` | `if` / `switch` |
| `ConditionalControlTrait` | `?:` / `match` |
| `NullsafeAccessTrait` | `?->` |
| `ClassConstantFetchTrait` | 类常量读取 |
| `TypeConversionTrait` / `TypeDetectionTrait` | 类型转换与 `instanceof` |
| `ConstantExpressionTrait` | 编译期常量折叠 |
| `ExceptionControlFlowTrait` | `try/catch/throw` |
| `StdContainerTrait` | `std` 容器操作 |
Sources: [src/CompilerBase.php](src/CompilerBase.php#L43-L63) · [src/Parser/](src/Parser/)
## 访客链
`doConvert()` 在遍历 AST 前先挂载四个 `NodeVisitor`
```php
$traverser->addVisitor(new NameResolver(...)); // 全限定名解析
$traverser->addVisitor(new Visitor(...)); // Transform lowering(见 transform)
$traverser->addVisitor(new ConstantExpressionValidationVisitor(...));
$traverser->addVisitor(new RuntimeAttributeFactoryLowering(...));
```
`NameResolver` 把短名解析为全限定名(但不替换节点),让后续生成能拿到准确的命名空间路径。
Sources: [src/Translator.php](src/Translator.php#L2460-L2467)
## 相关阅读
- 符号如何被收集 → [预处理与符号表](preprocessor)
- 遍历中做了哪些改写 → [AST 转换](transform)
- 解析结果的消费方 → [代码生成](generator)
</blog>

@ -1,48 +0,0 @@
<!doctype html><html lang="zh"><head><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>编译流水线</title>
<script src="../script/mermaid.min.js"></script>
<script>document.addEventListener('DOMContentLoaded',function(){if(window.mermaid)mermaid.initialize({startOnLoad:true});});</script>
<style>
:root{--bg:#0f1117;--fg:#e6e6e6;--muted:#9aa0aa;--accent:#6ea8fe;--card:#171a21;--border:#262b36;}
*{box-sizing:border-box}
body{margin:0;font-family:-apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif;background:var(--bg);color:var(--fg);line-height:1.65}
.wrap{max-width:900px;margin:0 auto;padding:32px 24px 80px}
h1,h2,h3{color:#fff;line-height:1.3;margin-top:1.6em}
h1{border-bottom:1px solid var(--border);padding-bottom:.3em}
a{color:var(--accent);text-decoration:none}
a:hover{text-decoration:underline}
code{background:#0b0d12;padding:.15em .4em;border-radius:4px;font-size:.9em}
pre{background:#0b0d12;border:1px solid var(--border);border-radius:8px;padding:14px;overflow:auto}
pre code{background:none;padding:0}
table{border-collapse:collapse;width:100%;margin:1em 0}
th,td{border:1px solid var(--border);padding:8px 10px;text-align:left}
th{background:var(--card)}
blockquote{border-left:3px solid var(--accent);margin:1em 0;padding:.2em 1em;color:var(--muted);background:var(--card)}
hr{border:none;border-top:1px solid var(--border);margin:2em 0}
</style></head>
<body><div class="wrap"><h1>编译流水线</h1>
<p>TypePHP 的核心入口是 <code>bin/tpc.php</code>,它通过 <code>main()</code> 串起整条流水线:<code>prepare → convert → compile → build → run</code>。本章用一张图和一个阶段表把全链路讲清楚。</p>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\bin\tpc.php#L1-L9">bin/tpc.php</a> · <a href="file:///D:\git\php\aot-compiler\src\compiler.php#L4-L46">src/compiler.php</a></p>
<h2>总体流程</h2>
<pre class="mermaid">flowchart TD
A[&quot;main(argc, argv)&quot;] --&gt; B[&quot;prepare(argv)\n扫描 PHP 文件 + 预处理&quot;]
B --&gt; C[&quot;convert(files)\nPHP AST → C++ 源文件&quot;]
C --&gt; D{&quot;--dry?&quot;}
D -- &quot;&quot; --&gt; Z[&quot;仅输出 C++,结束&quot;]
D -- &quot;&quot; --&gt; E[&quot;compile(sourceFiles)\nC++ → .o / .obj&quot;]
E --&gt; F[&quot;build(objectFiles)\n链接为二进制 / 库 / 扩展&quot;]
F --&gt; G{&quot;--run?&quot;}
G -- &quot;&quot; --&gt; H[&quot;run(binary)\n立即执行&quot;]
G -- &quot;&quot; --&gt; Z</pre>
<p><code>.prof</code> 后缀会被切换为 <code>profileAnalyze</code> 模式,调用 <code>pprof --web</code> 做性能剖析。</p>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\src\compiler.php#L14-L18">src/compiler.php</a> · <a href="file:///D:\git\php\aot-compiler\src\compiler.php#L26-L45">src/compiler.php</a></p>
<h2>各阶段职责</h2>
<table><thead><tr><th>阶段</th><th>方法</th><th>关键工作</th></tr></thead><tbody><tr><td>准备</td><td><code>Translator::prepare()</code></td><td>扫描所有 PHP 文件、解析命令行、构建符号表(类/函数/常量/属性)</td></tr><tr><td>转换</td><td><code>Translator::convert()</code><code>convertFile()</code><code>doConvert()</code></td><td>对每个 PHP 文件解析 AST,跑 Transform 访客,生成 <code>.cc</code><code>_arginfo.h</code></td></tr><tr><td>编译</td><td><code>Translator::compile()</code></td><td>追加 phpx misc 源文件、准备 PCH、单进程或 <code>pcntl</code> 并行编译</td></tr><tr><td>构建</td><td><code>Translator::build()</code></td><td>调用原生链接器把目标文件 + 资源文件链接成最终产物</td></tr><tr><td>运行</td><td><code>Translator::run()</code></td><td>仅在 <code>bin</code> 模式且带 <code>--run</code> 时执行产物</td></tr></tbody></table>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\src\Translator.php#L504-L527">src/Translator.php</a> · <a href="file:///D:\git\php\aot-compiler\src\Translator.php#L1419-L1447">src/Translator.php</a> · <a href="file:///D:\git\php\aot-compiler\src\Translator.php#L1649-L1680">src/Translator.php</a></p>
<h2>转换阶段的两趟扫描</h2>
<p><code>doConvert()</code> 内部做了两件事(<a href="file:///D:\git\php\aot-compiler\src\Translator.php#L2455-L2537">src/Translator.php</a>):</p>
<ol><li><strong>AST 遍历</strong>:用 <code>NodeTraverser</code> 串联 <code>NameResolver</code><code>Transform\Visitor</code><code>ConstantExpressionValidationVisitor</code><code>RuntimeAttributeFactoryLowering</code> 四个访客,做名称解析、编译期属性 lowering、常量表达式校验。</li><li><strong>语句分发</strong>:遍历顶层语句,按 <code>Stmt_Class</code> / <code>Stmt_Function</code> / <code>Stmt_Const</code> 等类型调用对应的 <code>parseXxx()</code>,最后为每个类、接口、函数生成 C++ 包装代码(<code>genClassWrapper</code> / <code>genFunctionWrapper</code>)。</li></ol>
<blockquote>前端的解析/符号表逻辑在 <a href="pages/preprocessor.html">preprocessor</a><a href="pages/transform.html">transform</a> 章节展开;C++ 生成细节在 <a href="pages/generator.html">generator</a></blockquote>
<h2>关键文件</h2>
<ul><li><code>src/compiler.php</code> — 顶层 <code>main()</code><code>.prof</code> 剖析模式。</li><li><code>src/Translator.php</code> — 流水线编排 + 构建/链接/缓存。</li><li><code>src/Preprocessor.php</code> / <code>src/CompilerBase.php</code> — 解析、符号收集、C++ 代码发射(占 7000+ 行,是编译器主体)。</li></ul></div></body></html>

@ -1,55 +0,0 @@
<blog>
# 编译流水线
TypePHP 的核心入口是 `bin/tpc.php`,它通过 `main()` 串起整条流水线:`prepare → convert → compile → build → run`。本章用一张图和一个阶段表把全链路讲清楚。
Sources: [bin/tpc.php](bin/tpc.php#L1-L9) · [src/compiler.php](src/compiler.php#L4-L46)
## 总体流程
```mermaid
flowchart TD
A["main(argc, argv)"] --> B["prepare(argv)\n扫描 PHP 文件 + 预处理"]
B --> C["convert(files)\nPHP AST → C++ 源文件"]
C --> D{"--dry?"}
D -- "是" --> Z["仅输出 C++,结束"]
D -- "否" --> E["compile(sourceFiles)\nC++ → .o / .obj"]
E --> F["build(objectFiles)\n链接为二进制 / 库 / 扩展"]
F --> G{"--run?"}
G -- "是" --> H["run(binary)\n立即执行"]
G -- "否" --> Z
```
`.prof` 后缀会被切换为 `profileAnalyze` 模式,调用 `pprof --web` 做性能剖析。
Sources: [src/compiler.php](src/compiler.php#L14-L18) · [src/compiler.php](src/compiler.php#L26-L45)
## 各阶段职责
| 阶段 | 方法 | 关键工作 |
|---|---|---|
| 准备 | `Translator::prepare()` | 扫描所有 PHP 文件、解析命令行、构建符号表(类/函数/常量/属性) |
| 转换 | `Translator::convert()``convertFile()``doConvert()` | 对每个 PHP 文件解析 AST,跑 Transform 访客,生成 `.cc``_arginfo.h` |
| 编译 | `Translator::compile()` | 追加 phpx misc 源文件、准备 PCH、单进程或 `pcntl` 并行编译 |
| 构建 | `Translator::build()` | 调用原生链接器把目标文件 + 资源文件链接成最终产物 |
| 运行 | `Translator::run()` | 仅在 `bin` 模式且带 `--run` 时执行产物 |
Sources: [src/Translator.php](src/Translator.php#L504-L527) · [src/Translator.php](src/Translator.php#L1419-L1447) · [src/Translator.php](src/Translator.php#L1649-L1680)
## 转换阶段的两趟扫描
`doConvert()` 内部做了两件事([src/Translator.php](src/Translator.php#L2455-L2537)):
1. **AST 遍历**:用 `NodeTraverser` 串联 `NameResolver`、`Transform\Visitor`、`ConstantExpressionValidationVisitor`、`RuntimeAttributeFactoryLowering` 四个访客,做名称解析、编译期属性 lowering、常量表达式校验。
2. **语句分发**:遍历顶层语句,按 `Stmt_Class` / `Stmt_Function` / `Stmt_Const` 等类型调用对应的 `parseXxx()`,最后为每个类、接口、函数生成 C++ 包装代码(`genClassWrapper` / `genFunctionWrapper`)。
> 前端的解析/符号表逻辑在 [preprocessor](preprocessor) 与 [transform](transform) 章节展开;C++ 生成细节在 [generator](generator)。
## 关键文件
- `src/compiler.php` — 顶层 `main()``.prof` 剖析模式。
- `src/Translator.php` — 流水线编排 + 构建/链接/缓存。
- `src/Preprocessor.php` / `src/CompilerBase.php` — 解析、符号收集、C++ 代码发射(占 7000+ 行,是编译器主体)。
</blog>

@ -1,41 +0,0 @@
<!doctype html><html lang="zh"><head><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>跨平台支持</title>
<script src="../script/mermaid.min.js"></script>
<script>document.addEventListener('DOMContentLoaded',function(){if(window.mermaid)mermaid.initialize({startOnLoad:true});});</script>
<style>
:root{--bg:#0f1117;--fg:#e6e6e6;--muted:#9aa0aa;--accent:#6ea8fe;--card:#171a21;--border:#262b36;}
*{box-sizing:border-box}
body{margin:0;font-family:-apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif;background:var(--bg);color:var(--fg);line-height:1.65}
.wrap{max-width:900px;margin:0 auto;padding:32px 24px 80px}
h1,h2,h3{color:#fff;line-height:1.3;margin-top:1.6em}
h1{border-bottom:1px solid var(--border);padding-bottom:.3em}
a{color:var(--accent);text-decoration:none}
a:hover{text-decoration:underline}
code{background:#0b0d12;padding:.15em .4em;border-radius:4px;font-size:.9em}
pre{background:#0b0d12;border:1px solid var(--border);border-radius:8px;padding:14px;overflow:auto}
pre code{background:none;padding:0}
table{border-collapse:collapse;width:100%;margin:1em 0}
th,td{border:1px solid var(--border);padding:8px 10px;text-align:left}
th{background:var(--card)}
blockquote{border-left:3px solid var(--accent);margin:1em 0;padding:.2em 1em;color:var(--muted);background:var(--card)}
hr{border:none;border-top:1px solid var(--border);margin:2em 0}
</style></head>
<body><div class="wrap"><h1>跨平台支持</h1>
<p>TypePHP 目标 <strong>Windows / Linux / macOS</strong> 三大桌面平台,通过 <code>Platform</code> 抽象隔离系统差异(编译器名、目标扩展名、PHP embed 库查找等)。</p>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\src\Translator.php#L164-L190">src/Translator.php</a> · <a href="file:///D:\git\php\aot-compiler\src\Platform">src/Platform/</a></p>
<h2>平台抽象</h2>
<p><code>PlatformFactory::create()</code> 按 OS 返回具体 <code>Platform</code> 对象(Windows / Linux / macOS)。<code>detectPlatform()</code><code>Translator</code> 构造时调用,得到平台名 + 后端名(如 <code>Windows + MSVC</code>)。</p>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\src\Translator.php#L164-L190">src/Translator.php</a></p>
<h2>各平台要点</h2>
<table><thead><tr><th>平台</th><th>默认后端</th><th>链接产物</th><th>特殊点</th></tr></thead><tbody><tr><td>Linux</td><td>GCC/Clang</td><td>ELF 可执行 / <code>.so</code></td><td>推荐 Ubuntu 22.04,需 <code>libphp.so</code></td></tr><tr><td>macOS</td><td>Clang</td><td>Mach-O / <code>.dylib</code></td><td><code>strip</code> 可选</td></tr><tr><td>Windows</td><td>MSVC</td><td>PE <code>.exe</code> / <code>.dll</code></td><td>需 Windows SDK + <code>php8ts.lib</code>;资源文件编译</td></tr></tbody></table>
<p>Windows 分支会额外 <code>detectPhpLibs()</code> 探测 embed 与 core 的 <code>.lib</code>,并把 <code>isZts</code>、SDK 路径注入 <code>Windows</code> 对象。</p>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\src\Translator.php#L170-L181">src/Translator.php</a> · <a href="file:///D:\git\php\aot-compiler\README.md#L42-L43">README.md</a></p>
<h2>Windows 资源文件</h2>
<p><code>bin</code> 模式在 Windows 下编译 <code>.rc</code>(图标、版本信息、UAC 等,由 <code>project.yml</code><code>resource</code> 配置),生成的 <code>.res</code><code>build()</code> 链接时并入。资源文件生成见 <code>Generator/ResourceFileGenerator</code><code>Build/ResourceCompilationTrait</code></p>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\project.yml#L10-L24">project.yml</a> · <a href="file:///D:\git\php\aot-compiler\src\Translator.php#L1653-L1659">src/Translator.php</a> · <a href="file:///D:\git\php\aot-compiler\src\Build\ResourceCompilationTrait.php">src/Build/ResourceCompilationTrait.php</a></p>
<h2>libphp.so 自动安装</h2>
<p>Linux 缺 <code>libphp.so</code> 时,<code>tpc.php</code> 可交互式下载 PHP 源码并自动构建,详见 <code>docs/LIBPHP_INSTALLER.md</code>;安装器逻辑在 <code>src/Installer/</code></p>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\README.md#L27">README.md</a> · <a href="file:///D:\git\php\aot-compiler\src\Installer">src/Installer/</a></p>
<h2>相关阅读</h2>
<ul><li>后端命令如何生成 → <a href="pages/backend.html">编译器后端</a></li><li>链接与资源并入 → <a href="pages/build-pipeline.html">构建流水线</a></li><li>二进制打包(UPX/strip) → README 的 Release packaging 段</li></ul></div></body></html>

@ -1,45 +0,0 @@
<blog>
# 跨平台支持
TypePHP 目标 **Windows / Linux / macOS** 三大桌面平台,通过 `Platform` 抽象隔离系统差异(编译器名、目标扩展名、PHP embed 库查找等)。
Sources: [src/Translator.php](src/Translator.php#L164-L190) · [src/Platform/](src/Platform/)
## 平台抽象
`PlatformFactory::create()` 按 OS 返回具体 `Platform` 对象(Windows / Linux / macOS)。`detectPlatform()` 在 `Translator` 构造时调用,得到平台名 + 后端名(如 `Windows + MSVC`)。
Sources: [src/Translator.php](src/Translator.php#L164-L190)
## 各平台要点
| 平台 | 默认后端 | 链接产物 | 特殊点 |
|---|---|---|---|
| Linux | GCC/Clang | ELF 可执行 / `.so` | 推荐 Ubuntu 22.04,需 `libphp.so` |
| macOS | Clang | Mach-O / `.dylib` | `strip` 可选 |
| Windows | MSVC | PE `.exe` / `.dll` | 需 Windows SDK + `php8ts.lib`;资源文件编译 |
Windows 分支会额外 `detectPhpLibs()` 探测 embed 与 core 的 `.lib`,并把 `isZts`、SDK 路径注入 `Windows` 对象。
Sources: [src/Translator.php](src/Translator.php#L170-L181) · [README.md](README.md#L42-L43)
## Windows 资源文件
`bin` 模式在 Windows 下编译 `.rc`(图标、版本信息、UAC 等,由 `project.yml``resource` 配置),生成的 `.res``build()` 链接时并入。资源文件生成见 `Generator/ResourceFileGenerator``Build/ResourceCompilationTrait`
Sources: [project.yml](project.yml#L10-L24) · [src/Translator.php](src/Translator.php#L1653-L1659) · [src/Build/ResourceCompilationTrait.php](src/Build/ResourceCompilationTrait.php)
## libphp.so 自动安装
Linux 缺 `libphp.so` 时,`tpc.php` 可交互式下载 PHP 源码并自动构建,详见 `docs/LIBPHP_INSTALLER.md`;安装器逻辑在 `src/Installer/`
Sources: [README.md](README.md#L27) · [src/Installer/](src/Installer/)
## 相关阅读
- 后端命令如何生成 → [编译器后端](backend)
- 链接与资源并入 → [构建流水线](build-pipeline)
- 二进制打包(UPX/strip) → README 的 Release packaging 段
</blog>

@ -1,45 +0,0 @@
<!doctype html><html lang="zh"><head><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>预处理与符号表</title>
<script src="../script/mermaid.min.js"></script>
<script>document.addEventListener('DOMContentLoaded',function(){if(window.mermaid)mermaid.initialize({startOnLoad:true});});</script>
<style>
:root{--bg:#0f1117;--fg:#e6e6e6;--muted:#9aa0aa;--accent:#6ea8fe;--card:#171a21;--border:#262b36;}
*{box-sizing:border-box}
body{margin:0;font-family:-apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif;background:var(--bg);color:var(--fg);line-height:1.65}
.wrap{max-width:900px;margin:0 auto;padding:32px 24px 80px}
h1,h2,h3{color:#fff;line-height:1.3;margin-top:1.6em}
h1{border-bottom:1px solid var(--border);padding-bottom:.3em}
a{color:var(--accent);text-decoration:none}
a:hover{text-decoration:underline}
code{background:#0b0d12;padding:.15em .4em;border-radius:4px;font-size:.9em}
pre{background:#0b0d12;border:1px solid var(--border);border-radius:8px;padding:14px;overflow:auto}
pre code{background:none;padding:0}
table{border-collapse:collapse;width:100%;margin:1em 0}
th,td{border:1px solid var(--border);padding:8px 10px;text-align:left}
th{background:var(--card)}
blockquote{border-left:3px solid var(--accent);margin:1em 0;padding:.2em 1em;color:var(--muted);background:var(--card)}
hr{border:none;border-top:1px solid var(--border);margin:2em 0}
</style></head>
<body><div class="wrap"><h1>预处理与符号表</h1>
<p>在生成任何 C++ 之前,编译器必须先<strong>通读所有 PHP 文件、收集符号、解决依赖顺序</strong>。这一步由 <code>Preprocessor</code>(父类)与 <code>CompilerBase</code>(主体)承担,产物是贯穿全程的全局符号表。</p>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\src\Preprocessor.php">src/Preprocessor.php</a> · <a href="file:///D:\git\php\aot-compiler\src\CompilerBase.php#L97">src/CompilerBase.php</a></p>
<h2>文件排序</h2>
<p><code>prepare()</code> 扫描出 PHP 文件后,用 <code>getSortedFiles()</code> 做拓扑排序,保证被依赖的文件先被处理。<code>marcj/topsort</code> 负责依赖排序(Composer 依赖之一)。</p>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\src\Preprocessor.php#L43-L75">src/Preprocessor.php</a> · <a href="file:///D:\git\php\aot-compiler\composer.json#L13">composer.json</a></p>
<h2>符号收集</h2>
<p>每个文件经 <code>prepareFile()</code> 解析后,编译器为每个语言构造建立 <code>Entity</code> 定义对象:</p>
<table><thead><tr><th>实体</th><th>定义类</th><th>收集方法</th></tr></thead><tbody><tr><td>函数</td><td><code>FunctionDef</code></td><td><code>prepareFunction()</code> / <code>parseFunctionDecl()</code></td></tr><tr><td>类 / trait / 枚举</td><td><code>ClassDef</code></td><td><code>prepareClass()</code></td></tr><tr><td>接口</td><td><code>InterfaceDef</code></td><td><code>prepareClass()</code></td></tr><tr><td>属性</td><td><code>PropertyDef</code></td><td><code>addClassProperty()</code></td></tr><tr><td>常量</td><td><code>ConstantDef</code></td><td><code>parseClassLikeConstant()</code> / <code>parseConstDef()</code></td></tr><tr><td>方法</td><td><code>MethodDef</code></td><td><code>parseFunctionDecl()</code></td></tr></tbody></table>
<p><code>main()</code> 有专门校验:参数必须形如 <code>(int $argc, array $argv)</code> 且返回 <code>void</code>(仅 <code>bin</code> 模式强制要求)。</p>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\src\Preprocessor.php#L489-L589">src/Preprocessor.php</a> · <a href="file:///D:\git\php\aot-compiler\src\Preprocessor.php#L685">src/Preprocessor.php</a> · <a href="file:///D:\git\php\aot-compiler\src\Preprocessor.php#L1124">src/Preprocessor.php</a></p>
<h2>符号仓库</h2>
<p>收集到的符号统一存放在 <code>Symbol\SymbolRepository</code><code>$this-&gt;symbols</code>),按 functions / classes / interfaces 分别索引。<code>genExtension()</code>、各类 <code>genXxx</code> 方法都从仓库读取,保证「声明一次、多处生成」。</p>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\src\Symbol\SymbolRepository.php">src/Symbol/SymbolRepository.php</a> · <a href="file:///D:\git\php\aot-compiler\src\Translator.php#L863-L880">src/Translator.php</a></p>
<h2>属性默认值计划</h2>
<p>标量/数组属性默认值会被预处理成 <code>ArrayInitPlan</code>,避免 C++ 初始化时重复求值;数组常量也会被标记 special 处理(见 <a href="pages/generator.html">generator</a> 的属性初始化)。</p>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\src\Preprocessor.php#L974-L1002">src/Preprocessor.php</a></p>
<h2>数值与常量</h2>
<p><code>loadInternalConstants()</code> 把 PHP 内置常量(排除用户态、<code>TypePHP</code> 扩展组)载入编译期常量表,供常量折叠使用。</p>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\src\Translator.php#L139-L159">src/Translator.php</a></p>
<h2>相关阅读</h2>
<ul><li>解析器如何产出 AST → <a href="pages/parser.html">解析器与 AST</a></li><li>遍历中如何改写节点 → <a href="pages/transform.html">AST 转换</a></li><li>符号如何变成 C++ → <a href="pages/generator.html">代码生成</a></li></ul></div></body></html>

@ -1,56 +0,0 @@
<blog>
# 预处理与符号表
在生成任何 C++ 之前,编译器必须先**通读所有 PHP 文件、收集符号、解决依赖顺序**。这一步由 `Preprocessor`(父类)与 `CompilerBase`(主体)承担,产物是贯穿全程的全局符号表。
Sources: [src/Preprocessor.php](src/Preprocessor.php) · [src/CompilerBase.php](src/CompilerBase.php#L97)
## 文件排序
`prepare()` 扫描出 PHP 文件后,用 `getSortedFiles()` 做拓扑排序,保证被依赖的文件先被处理。`marcj/topsort` 负责依赖排序(Composer 依赖之一)。
Sources: [src/Preprocessor.php](src/Preprocessor.php#L43-L75) · [composer.json](composer.json#L13)
## 符号收集
每个文件经 `prepareFile()` 解析后,编译器为每个语言构造建立 `Entity` 定义对象:
| 实体 | 定义类 | 收集方法 |
|---|---|---|
| 函数 | `FunctionDef` | `prepareFunction()` / `parseFunctionDecl()` |
| 类 / trait / 枚举 | `ClassDef` | `prepareClass()` |
| 接口 | `InterfaceDef` | `prepareClass()` |
| 属性 | `PropertyDef` | `addClassProperty()` |
| 常量 | `ConstantDef` | `parseClassLikeConstant()` / `parseConstDef()` |
| 方法 | `MethodDef` | `parseFunctionDecl()` |
`main()` 有专门校验:参数必须形如 `(int $argc, array $argv)` 且返回 `void`(仅 `bin` 模式强制要求)。
Sources: [src/Preprocessor.php](src/Preprocessor.php#L489-L589) · [src/Preprocessor.php](src/Preprocessor.php#L685) · [src/Preprocessor.php](src/Preprocessor.php#L1124)
## 符号仓库
收集到的符号统一存放在 `Symbol\SymbolRepository`(`$this->symbols`),按 functions / classes / interfaces 分别索引。`genExtension()`、各类 `genXxx` 方法都从仓库读取,保证「声明一次、多处生成」。
Sources: [src/Symbol/SymbolRepository.php](src/Symbol/SymbolRepository.php) · [src/Translator.php](src/Translator.php#L863-L880)
## 属性默认值计划
标量/数组属性默认值会被预处理成 `ArrayInitPlan`,避免 C++ 初始化时重复求值;数组常量也会被标记 special 处理(见 [generator](generator) 的属性初始化)。
Sources: [src/Preprocessor.php](src/Preprocessor.php#L974-L1002)
## 数值与常量
`loadInternalConstants()` 把 PHP 内置常量(排除用户态、`TypePHP` 扩展组)载入编译期常量表,供常量折叠使用。
Sources: [src/Translator.php](src/Translator.php#L139-L159)
## 相关阅读
- 解析器如何产出 AST → [解析器与 AST](parser)
- 遍历中如何改写节点 → [AST 转换](transform)
- 符号如何变成 C++ → [代码生成](generator)
</blog>

@ -1,53 +0,0 @@
<!doctype html><html lang="zh"><head><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>快速开始</title>
<script src="../script/mermaid.min.js"></script>
<script>document.addEventListener('DOMContentLoaded',function(){if(window.mermaid)mermaid.initialize({startOnLoad:true});});</script>
<style>
:root{--bg:#0f1117;--fg:#e6e6e6;--muted:#9aa0aa;--accent:#6ea8fe;--card:#171a21;--border:#262b36;}
*{box-sizing:border-box}
body{margin:0;font-family:-apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif;background:var(--bg);color:var(--fg);line-height:1.65}
.wrap{max-width:900px;margin:0 auto;padding:32px 24px 80px}
h1,h2,h3{color:#fff;line-height:1.3;margin-top:1.6em}
h1{border-bottom:1px solid var(--border);padding-bottom:.3em}
a{color:var(--accent);text-decoration:none}
a:hover{text-decoration:underline}
code{background:#0b0d12;padding:.15em .4em;border-radius:4px;font-size:.9em}
pre{background:#0b0d12;border:1px solid var(--border);border-radius:8px;padding:14px;overflow:auto}
pre code{background:none;padding:0}
table{border-collapse:collapse;width:100%;margin:1em 0}
th,td{border:1px solid var(--border);padding:8px 10px;text-align:left}
th{background:var(--card)}
blockquote{border-left:3px solid var(--accent);margin:1em 0;padding:.2em 1em;color:var(--muted);background:var(--card)}
hr{border:none;border-top:1px solid var(--border);margin:2em 0}
</style></head>
<body><div class="wrap"><h1>快速开始</h1>
<p>本章给出一个最小可编译示例,以及常用命令。完整的用户手册在独立的 <code>aot/docs</code> 仓库,这里聚焦「能在 5 分钟内跑起来」。</p>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\docs\QUICKSTART.md">docs/QUICKSTART.md</a> · <a href="file:///D:\git\php\aot-compiler\README.md#L7-L26">README.md</a></p>
<h2>安装依赖</h2>
<pre><code>composer install # 安装 nikic/php-parser, phpx 等
php bin/tpc.php --help # 验证安装</code></pre>
<p>Linux 还需高精度数学库:<code>sudo apt install libgmp-dev libmpfr-dev libmpdec-dev</code></p>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\README.md#L29-L40">README.md</a></p>
<h2>写一个最小程序</h2>
<p>可执行代码必须放在 <code>main()</code> 里,类/函数/常量可在全局定义:</p>
<pre><code>&lt;?php
class Calculator {
public function add(int $a, int $b): int { return $a + $b; }
}
function main(): void {
$c = new Calculator();
echo $c-&gt;add(5, 3) . PHP_EOL;
}</code></pre>
<p>编译并运行:</p>
<pre><code>php bin/tpc.php main.php -o myapp
./myapp # 输出 8</code></pre>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\docs\QUICKSTART.md#L166-L189">docs/QUICKSTART.md</a> · <a href="file:///D:\git\php\aot-compiler\docs\QUICKSTART.md#L99-L127">docs/QUICKSTART.md</a></p>
<h2>常用命令</h2>
<table><thead><tr><th>命令</th><th>作用</th></tr></thead><tbody><tr><td><code>php bin/tpc.php hello.php -O2</code></td><td>指定优化级别编译</td></tr><tr><td><code>php bin/tpc.php src/ -o app -j4</code></td><td>目录编译 + 4 路并行</td></tr><tr><td><code>php bin/tpc.php src/ --dry --build-dir /tmp/b</code></td><td>只生成 C++ 不编译(调试用)</td></tr><tr><td><code>php bin/tpc.php app.php -r -O2 -- --flag1 v1</code></td><td>编译后直接运行,并传参</td></tr><tr><td><code>php bin/tpc.php project.yml</code></td><td><code>project.yml</code> 配置编译</td></tr></tbody></table>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\src\Translator.php#L225-L258">src/Translator.php</a> · <a href="file:///D:\git\php\aot-compiler\docs\QUICKSTART.md#L271-L280">docs/QUICKSTART.md</a></p>
<h2>调试中间产物</h2>
<p><code>--dry</code> 会把生成的 <code>.cc</code> / <code>_arginfo.h</code> 写入 <code>--build-dir</code> 指定的目录,便于检查翻译结果;<code>--no-progress</code> 则逐文件打印进度。</p>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\src\Translator.php#L30-L36">src/Translator.php</a></p>
<h2>下一步</h2>
<ul><li>了解产物形态差异 → <a href="pages/compilation-modes.html">编译模式</a></li><li>理解 <code>project.yml</code> 字段 → 见仓库根 <code>project.yml</code><code>docs/COMPILER_CLI.md</code></li><li>排查「Not implemented」→ <a href="pages/incompatible-features.html">不支持的 PHP 特性</a></li></ul></div></body></html>

@ -1,68 +0,0 @@
<blog>
# 快速开始
本章给出一个最小可编译示例,以及常用命令。完整的用户手册在独立的 `aot/docs` 仓库,这里聚焦「能在 5 分钟内跑起来」。
Sources: [docs/QUICKSTART.md](docs/QUICKSTART.md) · [README.md](README.md#L7-L26)
## 安装依赖
```bash
composer install # 安装 nikic/php-parser, phpx 等
php bin/tpc.php --help # 验证安装
```
Linux 还需高精度数学库:`sudo apt install libgmp-dev libmpfr-dev libmpdec-dev`。
Sources: [README.md](README.md#L29-L40)
## 写一个最小程序
可执行代码必须放在 `main()` 里,类/函数/常量可在全局定义:
```php
<?php
class Calculator {
public function add(int $a, int $b): int { return $a + $b; }
}
function main(): void {
$c = new Calculator();
echo $c->add(5, 3) . PHP_EOL;
}
```
编译并运行:
```bash
php bin/tpc.php main.php -o myapp
./myapp # 输出 8
```
Sources: [docs/QUICKSTART.md](docs/QUICKSTART.md#L166-L189) · [docs/QUICKSTART.md](docs/QUICKSTART.md#L99-L127)
## 常用命令
| 命令 | 作用 |
|---|---|
| `php bin/tpc.php hello.php -O2` | 指定优化级别编译 |
| `php bin/tpc.php src/ -o app -j4` | 目录编译 + 4 路并行 |
| `php bin/tpc.php src/ --dry --build-dir /tmp/b` | 只生成 C++ 不编译(调试用) |
| `php bin/tpc.php app.php -r -O2 -- --flag1 v1` | 编译后直接运行,并传参 |
| `php bin/tpc.php project.yml` | 用 `project.yml` 配置编译 |
Sources: [src/Translator.php](src/Translator.php#L225-L258) · [docs/QUICKSTART.md](docs/QUICKSTART.md#L271-L280)
## 调试中间产物
`--dry` 会把生成的 `.cc` / `_arginfo.h` 写入 `--build-dir` 指定的目录,便于检查翻译结果;`--no-progress` 则逐文件打印进度。
Sources: [src/Translator.php](src/Translator.php#L30-L36)
## 下一步
- 了解产物形态差异 → [编译模式](compilation-modes)
- 理解 `project.yml` 字段 → 见仓库根 `project.yml``docs/COMPILER_CLI.md`
- 排查「Not implemented」→ [不支持的 PHP 特性](incompatible-features)
</blog>

@ -1,44 +0,0 @@
<!doctype html><html lang="zh"><head><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>PHPX 运行时集成</title>
<script src="../script/mermaid.min.js"></script>
<script>document.addEventListener('DOMContentLoaded',function(){if(window.mermaid)mermaid.initialize({startOnLoad:true});});</script>
<style>
:root{--bg:#0f1117;--fg:#e6e6e6;--muted:#9aa0aa;--accent:#6ea8fe;--card:#171a21;--border:#262b36;}
*{box-sizing:border-box}
body{margin:0;font-family:-apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif;background:var(--bg);color:var(--fg);line-height:1.65}
.wrap{max-width:900px;margin:0 auto;padding:32px 24px 80px}
h1,h2,h3{color:#fff;line-height:1.3;margin-top:1.6em}
h1{border-bottom:1px solid var(--border);padding-bottom:.3em}
a{color:var(--accent);text-decoration:none}
a:hover{text-decoration:underline}
code{background:#0b0d12;padding:.15em .4em;border-radius:4px;font-size:.9em}
pre{background:#0b0d12;border:1px solid var(--border);border-radius:8px;padding:14px;overflow:auto}
pre code{background:none;padding:0}
table{border-collapse:collapse;width:100%;margin:1em 0}
th,td{border:1px solid var(--border);padding:8px 10px;text-align:left}
th{background:var(--card)}
blockquote{border-left:3px solid var(--accent);margin:1em 0;padding:.2em 1em;color:var(--muted);background:var(--card)}
hr{border:none;border-top:1px solid var(--border);margin:2em 0}
</style></head>
<body><div class="wrap"><h1>PHPX 运行时集成</h1>
<p>TypePHP 不是从零实现 PHP 语义,而是站在 <strong>PHPX</strong>(Swoole 的 C++ PHP 绑定库)与 <strong>Zend 引擎 embed</strong> 之上。生成的 C++ 大量调用 <code>phpx.h</code> 提供的 <code>php::</code> 命名空间 API,最终把 Zend 引擎链接进产物。</p>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\src\Translator.php#L79-L89">src/Translator.php</a> · <a href="file:///D:\git\php\aot-compiler\README.md#L44-L61">README.md</a></p>
<h2>全局头文件</h2>
<p><code>Translator::$globalHeaders</code> 列出每个编译单元默认包含的头:</p>
<pre><code>[&#x27;cstring&#x27;, &#x27;phpx.h&#x27;, &#x27;phpx_helper.h&#x27;, &#x27;phpx_big_int.h&#x27;,
&#x27;phpx_big_float.h&#x27;, &#x27;phpx_decimal.h&#x27;, &#x27;typephp_helper.h&#x27;,
&#x27;typephp_fiber_generator.h&#x27;, &#x27;phpx_std.h&#x27;]</code></pre>
<p>高精度类型(<code>BigInt</code>/<code>BigFloat</code>/<code>Decimal</code>)的头正是此处引入;<code>typephp_*</code> 是 TypePHP 自带的扩展头(Fiber/Generator、helper)。</p>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\src\Translator.php#L79-L89">src/Translator.php</a></p>
<h2>运行时依赖源</h2>
<p><code>compile()</code> 会追加 phpx 的 <code>src/misc/</code> 源文件(C++ 运行时实现),以及 embed 模式下的 <code>typephp_main.cc</code>、bin 模式下的 cli 标题源。这些 misc 源受 <a href="pages/incremental-cache.html">增量编译缓存</a> 的 ABI 指纹保护。</p>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\src\Translator.php#L1423-L1434">src/Translator.php</a></p>
<h2>关键运行时能力</h2>
<ul><li><strong>Fiber / Generator</strong><code>typephp_fiber_generator.cc/.h</code> 提供协程与生成器支持,详见 <code>docs/YIELD_GENERATOR.md</code></li><li><strong>混合 C++/PHP</strong>:可在工程中直接写 C++ 并与 PHP 互调,见 <code>docs/MIXED_CPP_PHP.md</code></li><li><strong>反射</strong><code>typephp_install_reflection_attribute_handlers()</code> 在 MINIT 安装反射/属性处理器;</li><li><strong>类继承</strong>:PHP 类到 C++ 的映射与 <code>extends</code> 处理见 <code>docs/CLASS_INHERITANCE.md</code></li></ul>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\src\Translator.php#L887-L890">src/Translator.php</a> · <a href="file:///D:\git\php\aot-compiler\docs\YIELD_GENERATOR.md">docs/YIELD_GENERATOR.md</a> · <a href="file:///D:\git\php\aot-compiler\docs\MIXED_CPP_PHP.md">docs/MIXED_CPP_PHP.md</a> · <a href="file:///D:\git\php\aot-compiler\docs\CLASS_INHERITANCE.md">docs/CLASS_INHERITANCE.md</a></p>
<h2>动态链接库</h2>
<p>运行时要求 <code>libphp.so</code>(Linux)/ <code>php8ts.lib</code>(Windows)与 <code>libphpx.so</code> 就位;缺库时可用 <code>docs/LIBPHP_INSTALLER.md</code> 自动构建。</p>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\README.md#L57-L70">README.md</a> · <a href="file:///D:\git\php\aot-compiler\docs\LIBPHP_INSTALLER.md">docs/LIBPHP_INSTALLER.md</a></p>
<h2>相关阅读</h2>
<ul><li>产物如何被链接 → <a href="pages/build-pipeline.html">构建流水线</a></li><li>跨平台库差异 → <a href="pages/platform.html">跨平台支持</a></li><li>哪些 PHP 特性尚不支持 → <a href="pages/incompatible-features.html">不支持的 PHP 特性</a></li></ul></div></body></html>

@ -1,50 +0,0 @@
<blog>
# PHPX 运行时集成
TypePHP 不是从零实现 PHP 语义,而是站在 **PHPX**(Swoole 的 C++ PHP 绑定库)与 **Zend 引擎 embed** 之上。生成的 C++ 大量调用 `phpx.h` 提供的 `php::` 命名空间 API,最终把 Zend 引擎链接进产物。
Sources: [src/Translator.php](src/Translator.php#L79-L89) · [README.md](README.md#L44-L61)
## 全局头文件
`Translator::$globalHeaders` 列出每个编译单元默认包含的头:
```php
['cstring', 'phpx.h', 'phpx_helper.h', 'phpx_big_int.h',
'phpx_big_float.h', 'phpx_decimal.h', 'typephp_helper.h',
'typephp_fiber_generator.h', 'phpx_std.h']
```
高精度类型(`BigInt`/`BigFloat`/`Decimal`)的头正是此处引入;`typephp_*` 是 TypePHP 自带的扩展头(Fiber/Generator、helper)。
Sources: [src/Translator.php](src/Translator.php#L79-L89)
## 运行时依赖源
`compile()` 会追加 phpx 的 `src/misc/` 源文件(C++ 运行时实现),以及 embed 模式下的 `typephp_main.cc`、bin 模式下的 cli 标题源。这些 misc 源受 [增量编译缓存](incremental-cache) 的 ABI 指纹保护。
Sources: [src/Translator.php](src/Translator.php#L1423-L1434)
## 关键运行时能力
- **Fiber / Generator**:`typephp_fiber_generator.cc/.h` 提供协程与生成器支持,详见 `docs/YIELD_GENERATOR.md`
- **混合 C++/PHP**:可在工程中直接写 C++ 并与 PHP 互调,见 `docs/MIXED_CPP_PHP.md`
- **反射**:`typephp_install_reflection_attribute_handlers()` 在 MINIT 安装反射/属性处理器;
- **类继承**:PHP 类到 C++ 的映射与 `extends` 处理见 `docs/CLASS_INHERITANCE.md`
Sources: [src/Translator.php](src/Translator.php#L887-L890) · [docs/YIELD_GENERATOR.md](docs/YIELD_GENERATOR.md) · [docs/MIXED_CPP_PHP.md](docs/MIXED_CPP_PHP.md) · [docs/CLASS_INHERITANCE.md](docs/CLASS_INHERITANCE.md)
## 动态链接库
运行时要求 `libphp.so`(Linux)/ `php8ts.lib`(Windows)与 `libphpx.so` 就位;缺库时可用 `docs/LIBPHP_INSTALLER.md` 自动构建。
Sources: [README.md](README.md#L57-L70) · [docs/LIBPHP_INSTALLER.md](docs/LIBPHP_INSTALLER.md)
## 相关阅读
- 产物如何被链接 → [构建流水线](build-pipeline)
- 跨平台库差异 → [跨平台支持](platform)
- 哪些 PHP 特性尚不支持 → [不支持的 PHP 特性](incompatible-features)
</blog>

@ -1,37 +0,0 @@
<!doctype html><html lang="zh"><head><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>AST 转换(Lowering)</title>
<script src="../script/mermaid.min.js"></script>
<script>document.addEventListener('DOMContentLoaded',function(){if(window.mermaid)mermaid.initialize({startOnLoad:true});});</script>
<style>
:root{--bg:#0f1117;--fg:#e6e6e6;--muted:#9aa0aa;--accent:#6ea8fe;--card:#171a21;--border:#262b36;}
*{box-sizing:border-box}
body{margin:0;font-family:-apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif;background:var(--bg);color:var(--fg);line-height:1.65}
.wrap{max-width:900px;margin:0 auto;padding:32px 24px 80px}
h1,h2,h3{color:#fff;line-height:1.3;margin-top:1.6em}
h1{border-bottom:1px solid var(--border);padding-bottom:.3em}
a{color:var(--accent);text-decoration:none}
a:hover{text-decoration:underline}
code{background:#0b0d12;padding:.15em .4em;border-radius:4px;font-size:.9em}
pre{background:#0b0d12;border:1px solid var(--border);border-radius:8px;padding:14px;overflow:auto}
pre code{background:none;padding:0}
table{border-collapse:collapse;width:100%;margin:1em 0}
th,td{border:1px solid var(--border);padding:8px 10px;text-align:left}
th{background:var(--card)}
blockquote{border-left:3px solid var(--accent);margin:1em 0;padding:.2em 1em;color:var(--muted);background:var(--card)}
hr{border:none;border-top:1px solid var(--border);margin:2em 0}
</style></head>
<body><div class="wrap"><h1>AST 转换(Lowering)</h1>
<p><code>doConvert()</code> 遍历 AST 时,<code>src/Transform/Visitor.php</code> 作为 <code>NodeVisitor</code> 挂载在遍历链上,对节点做<strong>编译期改写(lowering)</strong>——把 PHP 8.x 的高层语法(属性钩子、构造函数提升、getter、编译期属性等)降级为编译器能直接翻译的普通结构。</p>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\src\Transform\Visitor.php#L19-L36">src/Transform/Visitor.php</a> · <a href="file:///D:\git\php\aot-compiler\src\Translator.php#L2462">src/Translator.php</a></p>
<h2>两个遍历钩子</h2>
<p><code>Visitor</code> 实现 <code>enterNode()</code><code>leaveNode()</code> 两阶段处理:</p>
<ul><li><strong>enterNode</strong>(进入节点前):校验 + 即时 lowering。依次调用 <code>CompileTimeAttribute::validateNode</code><code>FunctionAttributeLowering::lower</code><code>GetterLowering::validateTarget</code><code>PropertyMethodLowering::validateTarget</code><code>ConstructorLowering::validateTarget</code></li><li><strong>leaveNode</strong>(离开节点后):对函数/闭包做 <code>ParameterValidationLowering</code>;对类/属性做 <code>PropertyHookLowering</code><code>GetterLowering</code><code>PropertyMethodLowering</code>,把钩子属性、getter、提升属性改写成方法或标记。</li></ul>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\src\Transform\Visitor.php#L28-L80">src/Transform/Visitor.php</a></p>
<h2>Lowering 算子清单</h2>
<table><thead><tr><th></th><th>作用</th></tr></thead><tbody><tr><td><code>CompileTimeAttribute</code> / <code>CompileTimeAttributeRegistry</code></td><td>识别并校验编译期属性(如 <code>Any</code><code>MustUse</code></td></tr><tr><td><code>FunctionAttributeLowering</code></td><td>处理函数上的编译期属性</td></tr><tr><td><code>ConstructorLowering</code></td><td>处理构造函数提升参数</td></tr><tr><td><code>GetterLowering</code></td><td><code>get</code> 钩子属性 → getter 方法</td></tr><tr><td><code>PropertyHookLowering</code></td><td><code>get</code>/<code>set</code> 钩子属性 → 访问器方法</td></tr><tr><td><code>PropertyMethodLowering</code></td><td>属性 ↔ 方法互转</td></tr><tr><td><code>ParameterValidationLowering</code></td><td>参数类型校验,拒绝箭头函数误用</td></tr><tr><td><code>PrinterLowering</code> / <code>ValidateLowering</code></td><td>打印/校验相关降级</td></tr><tr><td><code>ArrayableLowering</code> / <code>NotEmptyLowering</code> / <code>NotNullLowering</code></td><td>特定表达式降级</td></tr><tr><td><code>RuntimeAttributeFactoryLowering</code></td><td>运行时工厂属性(遍历链上独立挂载)</td></tr></tbody></table>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\src\Transform">src/Transform/</a> · <a href="file:///D:\git\php\aot-compiler\src\Translator.php#L2467">src/Translator.php</a></p>
<h2>顺序意义</h2>
<p>Lowering 必须早于 C++ 生成:例如属性钩子若不先降级成方法,后续 <code>genClassWrapper</code> 就无法生成对应的 C++ 访问逻辑。它处在 <a href="pages/parser.html">解析器与 AST</a> 之后、<a href="pages/generator.html">代码生成</a> 之前。</p>
<h2>相关阅读</h2>
<ul><li>编译期属性语义 → <a href="pages/compile-time-attributes.html">编译期属性</a></li><li>降级后如何发射 C++ → <a href="pages/generator.html">生成器总览</a></li><li>常量表达式校验 → <a href="pages/type-check.html">类型检查与兼容</a></li></ul></div></body></html>

@ -1,45 +0,0 @@
<blog>
# AST 转换(Lowering)
`doConvert()` 遍历 AST 时,`src/Transform/Visitor.php` 作为 `NodeVisitor` 挂载在遍历链上,对节点做**编译期改写(lowering)**——把 PHP 8.x 的高层语法(属性钩子、构造函数提升、getter、编译期属性等)降级为编译器能直接翻译的普通结构。
Sources: [src/Transform/Visitor.php](src/Transform/Visitor.php#L19-L36) · [src/Translator.php](src/Translator.php#L2462)
## 两个遍历钩子
`Visitor` 实现 `enterNode()``leaveNode()` 两阶段处理:
- **enterNode**(进入节点前):校验 + 即时 lowering。依次调用 `CompileTimeAttribute::validateNode`、`FunctionAttributeLowering::lower`、`GetterLowering::validateTarget`、`PropertyMethodLowering::validateTarget`、`ConstructorLowering::validateTarget`。
- **leaveNode**(离开节点后):对函数/闭包做 `ParameterValidationLowering`;对类/属性做 `PropertyHookLowering`、`GetterLowering`、`PropertyMethodLowering`,把钩子属性、getter、提升属性改写成方法或标记。
Sources: [src/Transform/Visitor.php](src/Transform/Visitor.php#L28-L80)
## Lowering 算子清单
| 类 | 作用 |
|---|---|
| `CompileTimeAttribute` / `CompileTimeAttributeRegistry` | 识别并校验编译期属性(如 `Any`、`MustUse`) |
| `FunctionAttributeLowering` | 处理函数上的编译期属性 |
| `ConstructorLowering` | 处理构造函数提升参数 |
| `GetterLowering` | `get` 钩子属性 → getter 方法 |
| `PropertyHookLowering` | `get`/`set` 钩子属性 → 访问器方法 |
| `PropertyMethodLowering` | 属性 ↔ 方法互转 |
| `ParameterValidationLowering` | 参数类型校验,拒绝箭头函数误用 |
| `PrinterLowering` / `ValidateLowering` | 打印/校验相关降级 |
| `ArrayableLowering` / `NotEmptyLowering` / `NotNullLowering` | 特定表达式降级 |
| `RuntimeAttributeFactoryLowering` | 运行时工厂属性(遍历链上独立挂载) |
Sources: [src/Transform/](src/Transform/) · [src/Translator.php](src/Translator.php#L2467)
## 顺序意义
Lowering 必须早于 C++ 生成:例如属性钩子若不先降级成方法,后续 `genClassWrapper` 就无法生成对应的 C++ 访问逻辑。它处在 [解析器与 AST](parser) 之后、[代码生成](generator) 之前。
## 相关阅读
- 编译期属性语义 → [编译期属性](compile-time-attributes)
- 降级后如何发射 C++ → [生成器总览](generator)
- 常量表达式校验 → [类型检查与兼容](type-check)
</blog>

@ -1,38 +0,0 @@
<!doctype html><html lang="zh"><head><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>类型检查与兼容</title>
<script src="../script/mermaid.min.js"></script>
<script>document.addEventListener('DOMContentLoaded',function(){if(window.mermaid)mermaid.initialize({startOnLoad:true});});</script>
<style>
:root{--bg:#0f1117;--fg:#e6e6e6;--muted:#9aa0aa;--accent:#6ea8fe;--card:#171a21;--border:#262b36;}
*{box-sizing:border-box}
body{margin:0;font-family:-apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif;background:var(--bg);color:var(--fg);line-height:1.65}
.wrap{max-width:900px;margin:0 auto;padding:32px 24px 80px}
h1,h2,h3{color:#fff;line-height:1.3;margin-top:1.6em}
h1{border-bottom:1px solid var(--border);padding-bottom:.3em}
a{color:var(--accent);text-decoration:none}
a:hover{text-decoration:underline}
code{background:#0b0d12;padding:.15em .4em;border-radius:4px;font-size:.9em}
pre{background:#0b0d12;border:1px solid var(--border);border-radius:8px;padding:14px;overflow:auto}
pre code{background:none;padding:0}
table{border-collapse:collapse;width:100%;margin:1em 0}
th,td{border:1px solid var(--border);padding:8px 10px;text-align:left}
th{background:var(--card)}
blockquote{border-left:3px solid var(--accent);margin:1em 0;padding:.2em 1em;color:var(--muted);background:var(--card)}
hr{border:none;border-top:1px solid var(--border);margin:2em 0}
</style></head>
<body><div class="wrap"><h1>类型检查与兼容</h1>
<p>类型检查负责回答两个问题:<strong>「这个值在此处是什么类型?」</strong><strong>「把 A 赋给期望 B 的槽位是否合法?」</strong>。TypePHP 的类型逻辑集中在 <code>src/TypeSystem/</code> 两个 trait 中,并在遍历/生成阶段被大量调用。</p>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\src\TypeSystem">src/TypeSystem/</a></p>
<h2>两个核心 Trait</h2>
<table><thead><tr><th>Trait</th><th>职责</th></tr></thead><tbody><tr><td><code>NativeTypeCompatibilityTrait</code></td><td>判断原生类型之间的赋值/参数兼容(含子类→基类、可空、协变等规则)</td></tr><tr><td><code>CompositeTypeCheckerTrait</code></td><td>处理联合类型、<code>mixed</code>、交集等复合类型的成员判定</td></tr></tbody></table>
<p>它们被 <code>CompilerBase</code> 通过 <code>use</code> 引入(与 Parser 各 trait 并列),为 <code>parseParams</code><code>genArgumentDeclaration</code> 等生成逻辑提供判定依据。</p>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\src\CompilerBase.php#L79-L80">src/CompilerBase.php</a></p>
<h2>检查发生的时机</h2>
<ol><li><strong>参数解析</strong><code>parseParameterType()</code> 决定形参的 C++ 类型;不兼容的默认/传入会在此报错。</li><li><strong>返回值</strong>:函数/方法返回类型在 <code>parseFunctionDecl()</code> 校验,未声明返回类型会 <code>fatalError</code>(要求显式声明)。</li><li><strong>属性赋值</strong>:固定值类型属性赋值受 <a href="pages/native-types.html">native-types</a> 的静态类型规则约束。</li><li><strong>常量表达式</strong><code>ConstantExpressionValidationVisitor</code> 在遍历链上校验编译期常量表达式的合法性。</li></ol>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\src\Preprocessor.php#L328-L368">src/Preprocessor.php</a> · <a href="file:///D:\git\php\aot-compiler\src\Preprocessor.php#L497-L536">src/Preprocessor.php</a> · <a href="file:///D:\git\php\aot-compiler\src\Translator.php#L2463-L2466">src/Translator.php</a></p>
<h2>类型丢失与恢复</h2>
<p>从数组/函数返回值取出对象时会丢失类型上下文,需用编译期函数 <code>objval($var, &#x27;ClassName&#x27;)</code> 显式恢复类信息;<code>refval()</code> / <code>any()</code> 等见 <a href="pages/compile-time-attributes.html">编译期属性</a></p>
<p>Sources: <a href="file:///D:\git\php\aot-compiler\docs\NATIVE_TYPES.md#L55-L70">docs/NATIVE_TYPES.md</a></p>
<h2>相关阅读</h2>
<ul><li>类型如何映射到 C++ → <a href="pages/native-types.html">原生类型映射</a></li><li>编译期属性如何参与类型推断 → <a href="pages/compile-time-attributes.html">编译期属性</a></li><li>生成时如何消费类型 → <a href="pages/generator.html">生成器总览</a></li></ul></div></body></html>

@ -1,41 +0,0 @@
<blog>
# 类型检查与兼容
类型检查负责回答两个问题:**「这个值在此处是什么类型?」** 与 **「把 A 赋给期望 B 的槽位是否合法?」**。TypePHP 的类型逻辑集中在 `src/TypeSystem/` 两个 trait 中,并在遍历/生成阶段被大量调用。
Sources: [src/TypeSystem/](src/TypeSystem/)
## 两个核心 Trait
| Trait | 职责 |
|---|---|
| `NativeTypeCompatibilityTrait` | 判断原生类型之间的赋值/参数兼容(含子类→基类、可空、协变等规则) |
| `CompositeTypeCheckerTrait` | 处理联合类型、`mixed`、交集等复合类型的成员判定 |
它们被 `CompilerBase` 通过 `use` 引入(与 Parser 各 trait 并列),为 `parseParams`、`genArgumentDeclaration` 等生成逻辑提供判定依据。
Sources: [src/CompilerBase.php](src/CompilerBase.php#L79-L80)
## 检查发生的时机
1. **参数解析**:`parseParameterType()` 决定形参的 C++ 类型;不兼容的默认/传入会在此报错。
2. **返回值**:函数/方法返回类型在 `parseFunctionDecl()` 校验,未声明返回类型会 `fatalError`(要求显式声明)。
3. **属性赋值**:固定值类型属性赋值受 [native-types](native-types) 的静态类型规则约束。
4. **常量表达式**:`ConstantExpressionValidationVisitor` 在遍历链上校验编译期常量表达式的合法性。
Sources: [src/Preprocessor.php](src/Preprocessor.php#L328-L368) · [src/Preprocessor.php](src/Preprocessor.php#L497-L536) · [src/Translator.php](src/Translator.php#L2463-L2466)
## 类型丢失与恢复
从数组/函数返回值取出对象时会丢失类型上下文,需用编译期函数 `objval($var, 'ClassName')` 显式恢复类信息;`refval()` / `any()` 等见 [编译期属性](compile-time-attributes)。
Sources: [docs/NATIVE_TYPES.md](docs/NATIVE_TYPES.md#L55-L70)
## 相关阅读
- 类型如何映射到 C++ → [原生类型映射](native-types)
- 编译期属性如何参与类型推断 → [编译期属性](compile-time-attributes)
- 生成时如何消费类型 → [生成器总览](generator)
</blog>

File diff suppressed because one or more lines are too long

@ -1,274 +0,0 @@
# Code Reuse Improvement Plan
## Analysis Summary
| Metric | Value |
|--------|-------|
| Total source lines | ~11,000 (PHP only) |
| CompilerBase | 5,917 lines, 269 methods, 20 traits |
| Gcc↔Clang duplication | ~70-80% of methods |
| Linux↔Macos duplication | ~80% of methods |
| `fatalError()` call sites | 174+ across codebase |
| Test setUp/tearDown dup | 4+ test classes |
---
## Phase 1: High-Impact Backend/Platform Deduplication (P0)
### 1.1 Extract `UnixPlatform` base class
**Files**: `Platform/Linux.php` (249 lines), `Platform/Macos.php` (278 lines)
These 13 methods are 100% identical between Linux and Macos:
- `getIncludeFlags()`, `getLibraryPathFlags()`, `getObjectExtension()`, `getExecutableExtension()`, `getPathSeparator()`, `getPhpDir()`, `getRpathOptions()`, `getPicFlag()`, `buildPhpIncludePaths()`, `findPhpConfig()`, `buildPhpLibPaths()`
Near-identical with minor parameterization:
- `getLibraryFlags()` — only the regex differs (`.a|.so` vs `.a|.dylib`)
- `detectPhpLibs()` — only the lib name differs (`libphp.so` vs `libphp.dylib`)
**Plan**: Create `UnixPlatform extends PlatformBase` between `PlatformBase` and `Linux`/`Macos`. Move all identical methods up. Add abstract `getSharedLibraryExtension()` (already exists) and a protected `getSharedLibName()` for the single differing method.
**Expected savings**: ~180 lines removed, ~150 lines added = net ~30 lines but massive maintainability gain.
### 1.2 Extract `GccLikeBackend` base class
**Files**: `Backend/Gcc.php` (379 lines), `Backend/Clang.php` (515 lines)
These methods are structurally identical with only Windows-specific branching:
- `compileFile()`, `linkObjects()`, `buildCompileCommand()`, `buildCCompileCommand()`, `buildNativeCompileCommand()`, `buildLinkCommand()`, `buildCompileOptions()`, `buildLinkOptions()`, `buildFullCompileOptions()`, `buildFullLinkOptions()`
**Plan**: Create `GccLikeBackend extends CompilerBackend` with all shared logic. Define template-method hooks for the differences:
- `getCompilerSpecificFlags()` — empty for Gcc, MSVC compat flags for Clang/Windows
- `getOutputFlag($isWindows)``-o` vs `/OUT:`
- `getSanitizerFlag($type)` — handle the `address`/`addr` aliasing difference
- `getPICHandling($config)` — Gcc always adds `-fPIC`, Clang skips on Windows
**Expected savings**: ~250+ lines removed from Gcc.php and Clang.php. Msvc.php is sufficiently different (different flag syntax) to remain standalone.
---
## Phase 2: CompilerBase Internal Deduplication (P1)
### 2.1 Consolidate Big* type dispatch in BinaryOpTrait
**File**: `Parser/BinaryOpTrait.php` (lines 31-107)
The three blocks for BigFloat (lines 31-54), Decimal (lines 56-76), and BigInt (lines 78-107) in `parseBinaryOp()` share identical structure:
1. Check if either operand is the big type
2. Guard against incompatible mixing
3. Convert the non-matching operand
4. Dispatch to arithmetic or comparison operator
**Plan**: Extract `parseBigNumBinaryOp(string $type, string $left, string $right, ...)` parameterized by type name, conversion function, and operator maps. Same refactoring applies to `genBigNumericCmp()` (lines 315-355).
**Expected savings**: ~40 lines.
### 2.2 Data-driven operator dispatch tables
**Files**: `Parser/BinaryOpTrait.php` (16 wrapper methods), `Parser/AssignOpTrait.php` (14 wrapper methods)
30+ thin methods that are just `parseBinaryOp($left, $right, '+')` / `parseAssignOp($node, '+=')`.
**Plan**: Replace with a static map in `parseExpr()`:
```php
private const BINARY_OP_MAP = [
'Expr_BinaryOp_Plus' => '+',
'Expr_BinaryOp_Minus' => '-',
// ...
];
private const ASSIGN_OP_MAP = [
'Expr_AssignOp_Plus' => '+=',
// ...
];
```
**Expected savings**: ~200 lines removed (boilerplate method bodies).
### 2.3 Deduplicate call dispatch patterns
**Files**: `CompilerBase.php` (`parseFuncCall`, `parseMethodCall`, `parseStaticCall` — ~300 lines combined), `UniversalMethodCall.php` (`tryOptimizePhpFn` vs `dispatchFuncCall`)
These share the same overall flow: resolve callable → try native/optimized path → on `PlaceHolder` fall back to placeholder → parse args → wrap in `php::call()`. Additionally, `tryOptimizePhpFn()` (UniversalMethodCall lines 720-772) duplicates the argument type conversion logic already present in `dispatchFuncCall()` (FuncCallOptimizer lines 234-269).
**Plan**: Extract a shared `resolveCall(CallLike $expr, ...)` method. Unify arg conversion so `tryOptimizePhpFn` delegates to `dispatchFuncCall` instead of reimplementing it.
**Expected savings**: ~40 lines, fixes double-calculation of arg conversions.
### 2.4 Deduplicate UNIVERSAL_METHODS math entries
**File**: `UniversalMethodCall.php` (lines 12-69)
The INT block (lines 12-41) and FLOAT block (lines 42-69) contain 20 identical math method entries (`abs`, `ceil`, `floor`, `sqrt`, `sin`, `cos`, etc.) differing only in `return_type`. Also the `calc_op` entries (add/sub/mul/div) are duplicated.
**Plan**: Define math method names once in a shared array, generate both INT and FLOAT entries in the constructor with the appropriate `return_type`.
**Expected savings**: ~25 lines of config data.
### 2.5 Deduplicate constant folding methods
**File**: `Optimizer/FuncCallOptimizer.php` (lines 515-593)
8 methods (`doFoldStringLen`, `doFoldStringCase`, `doFoldCmp2`, `doFoldCmp3`, `doFoldCountLiteral`, `doFoldKnownClass`, `doFoldKnownConstant`, `doFoldSsaType`) follow the identical pattern: extract args → check types → compute → return literal or false.
**Plan**: Create a generic `tryFold(callable $check, callable $compute)` that handles the arg extraction and short-circuit boilerplate. Each folder becomes a one-liner.
**Expected savings**: ~50 lines.
### 2.6 Remove MSVC compat flag duplication in Clang
**File**: `Backend/Clang.php`
The 4-line MSVC compatibility block (`-fms-compatibility`, `-fms-compatibility-version=19.40`, `-fdelayed-template-parsing`, `-fms-extensions`) appears 7 times (compileFile, buildCompileCommand, buildCCompileCommand, buildNativeCompileCommand, buildFullCompileOptions, buildCompileOptions, buildLinkOptions).
**Plan**: Extract `private function getMsvcCompatFlags(): string` method. Called once per method that needs it instead of repeated inline.
**Expected savings**: ~24 lines, single point of change if MSVC compat flags need updating.
### 2.7 Merge return-check blocks
**File**: `CompilerBase.php`, `parseReturn()` (line 1621) and `genReturnCode()` (line 5741)
Identical 7-line union type check blocks.
**Plan**: Extract `genUnionReturnWrapper(string $exprVar)` method.
**Expected savings**: ~10 lines, eliminates drift risk.
### 2.8 Fix `buildCCompileCommand()` inconsistency between Gcc and Clang
**Files**: `Backend/Gcc.php` (lines 131-137), `Backend/Clang.php` (lines 198-206)
Gcc unconditionally appends `-O$level` then conditionally appends `-g`. Clang treats debug and optimization as mutually exclusive (`if debug: -O0 -g` else `-O$level`). This is a behavioral inconsistency between backends implementing the same abstract method.
**Plan**: Standardize on one behavior (the Clang pattern of `-O0 -g` for debug is the correct one — debug builds should not optimize). This will be automatically resolved by Phase 1.2 (GccLikeBackend).
---
## Phase 3: Structural Improvements (P2)
### 3.1 Entity flag-check consistency
**File**: `Entity/PropertyDef.php` has `isPrivate()`, `isProtected()`, `isPublic()`, `isStatic()`. `Entity/MethodDef.php` has none — flag checks are done inline in CompilerBase.
**Plan**: Add a `HasFlags` trait used by both `PropertyDef` and `MethodDef`:
```php
trait HasFlags {
public function isPrivate(): bool { return $this->flags & Modifiers::PRIVATE; }
public function isProtected(): bool { return $this->flags & Modifiers::PROTECTED; }
public function isPublic(): bool { return !$this->isPrivate() && !$this->isProtected(); }
public function isStatic(): bool { return $this->flags & Modifiers::STATIC; }
public function isAbstract(): bool { return $this->flags & Modifiers::ABSTRACT; }
}
```
**Expected savings**: Removes inline flag checks from CompilerBase, adds clarity.
### 3.2 Test infrastructure base class
**Files**: `phpunit/src/AstNodeTypeTest.php`, `CompilerBaseAdapterTest.php`, `TraitsTest.php`, `PreprocessorTest.php`
All 4 duplicate the same setUp/tearDown pattern: create temp dir, `CompilerTest::create()`, recursive cleanup.
**Plan**: Add `CompilerTestCase extends \PHPUnit\Framework\TestCase` to `phpunit/bootstrap.php`:
```php
abstract class CompilerTestCase extends TestCase {
protected string $tmpDir;
protected CompilerTest $compiler;
protected function setUp(): void {
parent::setUp();
$this->tmpDir = sys_get_temp_dir() . '/compiler_test_' . uniqid();
mkdir($this->tmpDir, 0777, true);
$this->compiler = CompilerTest::create($this->tmpDir);
}
protected function tearDown(): void {
parent::tearDown();
// recursive cleanup
}
}
```
### 3.3 Eliminate `buildFull*Options` / `build*Options` duality
**Files**: `Backend/Gcc.php`, `Backend/Clang.php`, `Backend/Msvc.php`
All three backends implement both `buildFullCompileOptions()` / `buildCompileOptions()` and `buildFullLinkOptions()` / `buildLinkOptions()`. The "full" variants are subsets of the "standard" variants working from differently-keyed option arrays. They have drifted independently (e.g., RPATH handling differs between the two in Gcc/Clang).
**Plan**: Make the "full" variants delegate to the "standard" variants by normalizing their option keys once at the call site. Keep only one code path for each (compile/link).
**Expected savings**: ~100+ lines, eliminates drift between the two variants.
### 3.4 Deduplicate Preprocessor AST switch
**File**: `Preprocessor.php`
`prepareFile()` (lines 115-151) and `prepareNamespace()` (lines 196-223) both switch over the same set of AST `Stmt_*` types with nearly identical case bodies.
**Plan**: Extract `processStmt(Node $v)` method that both callers share.
**Expected savings**: ~25 lines.
### 3.5 Remove dead code: `ScopeContext`
**File**: `Context/ScopeContext.php` (7 lines)
An empty class with no properties or methods. Used only as a type annotation in `FunctionContext`. Either populate it with scope-relevant state, or remove it and use plain `\stdClass` / array / null.
### 3.6 Reduce StdContainerTrait coupling
**File**: `Parser/StdContainerTrait.php` (823 lines, 48 methods)
This is effectively a standalone subsystem for std container handling. As a trait, it has unrestricted access to CompilerBase's internals.
**Plan**: Extract core logic into `StdContainerHandler` service class. The trait becomes a thin facade that delegates to the handler.
**Expected savings**: Better testability, clearer boundaries, easier to understand.
---
## Phase 4: Longer-Term Architectural (P3)
### 4.1 Break CompilerBase into domain-specific classes
Currently CompilerBase is a 5,917-line god class using 20 traits as a workaround for PHP's single inheritance. Consider:
- `ExpressionCompiler` — all parseExpr sub-dispatch (~500 lines)
- `StatementCompiler` — parseStmts, parseIf, parseWhile, parseFor, parseSwitch, etc.
- `TypeResolver` — parseTypeDecl, detectClassOfExpr, type checking
- `CallResolver` — parseFuncCall, parseMethodCall, parseStaticCall, parseNew
These would be injected services rather than traits, making CompilerBase a coordinator.
### 4.2 Shared AST walker pattern with Python Translator
Both PHP and Python translators implement the same "walk-collect-indent-emit" pipeline independently. `Core\Translator` could define a standard `walkAst($nodes, callable $visitor)` that handles indentation and line collection.
---
## Implementation Order & Impact Matrix
| # | Item | Savings | Risk | Effort |
|---|------|---------|------|--------|
| 1.1 | UnixPlatform base class | ~180 dup lines | Low | 2-3h |
| 1.2 | GccLikeBackend base class | ~250 dup lines | Medium | 3-4h |
| 2.1 | BigNum dispatch consolidation | ~40 lines | Low | 1h |
| 2.2 | Data-driven op dispatch | ~200 lines | Low | 1-2h |
| 2.3 | Unify call dispatch patterns | ~40 lines | Low | 1-2h |
| 2.4 | UNIVERSAL_METHODS math dedup | ~25 lines | Low | 30m |
| 2.5 | Fold method template | ~50 lines | Low | 1h |
| 2.6 | MSVC compat flags in Clang | ~24 lines | Low | 30m |
| 2.7 | Merge return-check blocks | ~10 lines | Low | 30m |
| 2.8 | Fix buildCCompileCommand drift | bug fix | Low | 30m |
| 3.1 | HasFlags trait | clarity | Low | 1h |
| 3.2 | CompilerTestCase base class | boilerplate | Low | 1h |
| 3.3 | Eliminate Full*Options duality | ~100 lines | Medium | 2h |
| 3.4 | Preprocessor AST switch dedup | ~25 lines | Low | 1h |
| 3.5 | Remove dead ScopeContext | 7 lines | Low | 15m |
| 3.6 | StdContainer service class | boundary | Medium | 3-4h |
| 4.1 | Domain classes | architecture | High | 1-2 weeks |
| 4.2 | AST walker pattern | architecture | Medium | 3-5h |
**Total estimated savings**: ~950+ lines of duplicated / dead code.
**Recommended execution**: Phase 1 → Phase 2 → Phase 3. Items within each phase are independent and can be parallelized.

@ -1,325 +0,0 @@
# Encapsulation Review
## Summary
| Metric | Value |
|--------|-------|
| Entity classes with all-public fields | 9/9 (100%) |
| FunctionContext public properties | 26 (all mutable) |
| CompilerBase private methods | 5/269 (1.9%) |
| Traits with direct `$this->context->` access | 6 traits, 60+ sites |
| ScopeContext (dead code) | 7 lines, empty class |
---
## Issue 1: Entity classes — all-public mutable fields
**Severity**: High. Every entity class exposes all internal state as public writable properties. External code in Preprocessor/CompilerBase directly mutates them.
### 1.1 ClassDef (18 public properties)
`src/Php/Entity/ClassDef.php`
```php
public array $methods = []; // externally populated: $classDef->properties[$name] = ...
public array $properties = []; // externally populated
public array $constants = []; // externally populated
public array $implements = []; // externally populated
public string $extends = ''; // externally set: $this->classDef->extends = ...
public bool $requireCtor = false;
public bool $enum = false;
public ?string $enumBackingType = null;
public array $enumCases = [];
public array $abstractMethods = [];
public ?Trait_ $trait = null;
public array $traitAliases = [];
public array $traitIgnored = [];
public int $flags; // no visibility checks, raw bitmask
public bool $inheritedFromInternalClass = false;
public string $ctorInit = ''; // mutated during code generation
public string $ctorClean = ''; // mutated during code generation
public FunctionContext $propertyContext; // set after construction
```
**Issues**:
- `$properties`, `$methods`, `$constants` — exposed as raw arrays. External code does `$classDef->properties[$name] = $propDef`. No validation that the key matches `$propDef->name`, no type enforcement.
- `$flags` — raw int, no guarantee it's a valid Modifiers bitmask.
- `$ctorInit` / `$ctorClean` — mutated by CompilerBase during code generation, not initialization.
- Property additions use `addMethod()`, `addAbstractMethod()` but array properties are also set directly via `[] =`.
- `$extends` — set directly as raw string, bypasses `parent::__construct()` which also sets it on ClassLikeDef.
**Recommendation**:
- Make `$methods`, `$properties`, `$constants` private, expose via `addMethod()`/`getMethod()` (already exists)
- Make `$flags` private, expose `isAbstract()` (already exists), add `isFinal()`, `isReadonly()`
- Make `$extends` write-once via `setExtends(string)` with validation
- Add `appendCtorInit(string)` and `appendCtorClean(string)` methods instead of direct string mutation
### 1.2 FunctionDef (12 public properties)
`src/Php/Entity/FunctionDef.php`
```php
public string $name;
public string $returnType;
public array $argInfoList = []; // externally populated: $functionDef->argInfoList[] = $argInfo
public int $argCountRequired = 0;
public string $params = ''; // generated C++ param string, mutated during compilation
public string $namespace;
public bool $method = false;
public bool $stub = false;
public bool $returnTypeUndeclared = false;
public string $returnClass = '';
public ?array $returnTypeCheck = null;
public string $returnTypeStr = '';
public ?NodeAbstract $returnTypeNode = null;
```
**Issues**:
- `$name` and `$namespace` are set in constructor but still publicly writable — should be readonly
- `$argInfoList[]` is directly appended to by Preprocessor (line 329)
- `$params` is a codegen artifact stored on the entity — belongs in a separate compilation context
**Recommendation**:
- Make constructor-set properties readonly (`$name`, `$namespace`, `$returnType`)
- Add `addArg(ArgInfo $arg)` method instead of direct array mutation
- Extract `$params` to a compilation context separate from the definition entity
### 1.3 PropertyDef (7 public properties)
`src/Php/Entity/PropertyDef.php`
```php
public string $name;
public string $type;
public int $flags;
public ?string $default = null;
public ?ArrayInitPlan $arrayInitPlan = null;
public bool $nullable = false;
public string $class = ''; // set after construction
```
**Issues**:
- `$class` is set after construction externally (`$propDef->class = $fullClassName`)
- `$flags` is raw int — already has `isPrivate()`/`isProtected()`/`isPublic()`/`isStatic()` methods, good
- Constructor already sets all core fields — `$class` should be added to the constructor
**Recommendation**:
- Add `$class` to the constructor (it's always known at construction time)
- Make constructor-set fields readonly or private
### 1.4 MethodDef (4 public properties)
`src/Php/Entity/MethodDef.php`
```php
public int $flags;
public string $name;
public ?FunctionDef $functionDef = null; // set after construction
public bool $hasDynamicCall = false;
```
**Issues**:
- No flag-check methods — inline checks in CompilerBase should use `$methodDef->isPrivate()` instead
- `$functionDef` is set externally: `$this->methodDef->functionDef = $functionDef` (Preprocessor line 411)
**Recommendation**:
- Add `HasFlags` trait (from Phase 3.1 of reuse plan)
- Add `setFunctionDef(FunctionDef $fd)` method with validation
### 1.5 ConstantDef (8 public properties)
```php
public string $name;
public string $type;
public int $flags;
public string $value;
public string $arrayExpr = '';
public string $class = '';
public ?NodeAbstract $valueExpr = null;
```
**Issues**: Same pattern — constructor sets core fields, but `$class` is set externally afterward.
**Recommendation**: Add `$class` to constructor.
---
## Issue 2: FunctionContext — public mutable grab-bag
**Severity**: High. 26 public properties, all writable by any code with access to the context object.
`src/Php/Context/FunctionContext.php`
```php
public ?SsaBuilder $ssaBuilder = null; // transient analysis state
public array $stableObjects = []; // SSA optimizer state
public array $hoistedProps = []; // SSA optimizer state
public array $unsafeObjectProps = []; // SSA optimizer state
public array $objects = []; // object variable tracking
public array $stdArrays = []; // std container tracking
public array $stdContainers = []; // std container tracking
public array $localVars = []; // local variable table
public array $staticVars = []; // static variable table
public array $globalVars = []; // global variable table
public array $ceWrappers = []; // class entry wrappers
public int $tmpVarIndex = 0; // auto-increment counter
public array $arguments = []; // function arguments
public bool $inLoop = false; // control-flow state
public bool $inClosure = false; // control-flow state
public bool $hasMultiLevelBreak = false;
public bool $hasMultiLevelContinue = false;
public bool $inAssignExpr = false; // expression context
public array $beforeStmtLines = []; // deferred code (flushed before stmts)
public array $afterStmtLines = []; // deferred code (flushed after stmts)
public array $objectProps; // (uninitialized!)
public array $staticPropRefs = []; // static property references
public int $scopeLevel = 0; // lexical scope depth
/** @var array<int, ScopeContext> */
public array $scopeLayouts = []; // per-scope data
```
**Issues**:
- Traits directly mutate deeply nested state: `$this->context->stdArrays[$var] = ...`, `$this->context->localVars[$name] = ...`
- No semantic grouping — analysis state, variable tracking, control-flow flags all mixed
- `$objectProps` is declared but never initialized (could be null at runtime)
- `$scopeLayouts` is managed through `enterScope()`/`leaveScope()` — but can be bypassed
- `$tmpVarIndex` auto-increment — should use a method instead of direct `++`
**Recommendation**:
- Group related properties into sub-objects: `VariableTable`, `ControlFlowState`, `ScopeManager`
- Make properties that should only be read by the compiler layer private/protected with getters
- Add `incrementTmpVar(): int`, `addLocalVar()`, `addBeforeStmt()` methods
- Initialize `$objectProps = []`
---
## Issue 3: CompilerBase — only 1.9% private methods
**Severity**: Medium. Virtually everything is public or protected.
`src/Php/CompilerBase.php` — 269 methods total:
- ~25 public methods (many should be protected or internal)
- ~239 protected methods (most should be private — internal helpers)
- **5 private methods** (1.9%)
### 3.1 Methods that should be private
The following methods are internal helpers only called from within CompilerBase (not from Preprocessor, Translator, or traits). They are unnecessarily `protected`:
| Method | Line | Called from |
|--------|------|-------------|
| `resetFunction()` | 833 | Internal only |
| `resetMethod()` | 840 | Internal only |
| `resetClass()` | 846 | Internal only |
| `resolveObjectClassDef()` | 816 | Already private ✓ |
| `getBigIntLiteralString()` | 1183 | Already private ✓ |
| `getDecimalLiteralString()` | 1188 | Already private ✓ |
| `parseBeforeStmtLines()` | 1321 | Internal, but accessed by traits |
| `parseAfterStmtLines()` | 1331 | Internal, but accessed by traits |
| `genTmpVarName()` | 734 | Public — should at least be protected |
### 3.2 Public methods that are internal concern
| Method | Current visibility | Issue |
|--------|-------------------|-------|
| `genTmpVarName()` | public | Only used internally for variable name generation |
| `writeFile()` | public | File I/O — should be a separate service |
| `stop()` | public | Error helper — could be internal |
| `isScalarInt()` | public | AST helper, only used internally |
| `getType()` | public | AST helper, only used internally |
| `getObjectType()` | public | Type mapping, used internally |
| `getTypeFromZendType()` | public | Type mapping, used internally |
| `getIncludeDir()` | public | Config getter — should be on a Config object |
| `getBuildDir()` | public | Config getter — should be on a Config object |
### 3.3 Public constants leaked as API
30 public constants for internal type names, literal values, etc. These are needed by traits but expose internal naming conventions.
---
## Issue 4: Trait → Context coupling
**Severity**: Medium. Traits bypass any encapsulation boundary and directly mutate `$this->context`.
| Trait | `$this->context->` accesses |
|-------|---------------------------|
| `StdContainerTrait` | 40+ accesses to `stdArrays`, `stdContainers`, `objects`, `localVars` |
| `LoopVarOptimizer` | accesses to `localVars`, `arguments`, `scopeLevel`, `inLoop` |
| `SsaPropOptimizer` | accesses to `stableObjects`, `hoistedProps`, `unsafeObjectProps`, `objects` |
| `FuncCallOptimizer` | accesses to `beforeStmtLines`, `arguments`, `localVars` |
| `SsaTypeOptimizer` | accesses to `localVars` |
| `BinaryOpTrait` | accesses to `objects`, `localVars` |
**Issues**:
- Traits have no declared contract — they assume `$this->context` exists and has specific properties
- If a property name changes in FunctionContext, all 6 traits break silently
- No type safety — arrays are indexed by string but accessed with arbitrary keys
**Recommendation**:
- Define a `ContextAccess` interface that traits must use instead of direct property access
- Or: inject context into trait methods as a parameter instead of reading from `$this`
- Short-term: add `@property-read` annotations to document the contract
---
## Issue 5: Preprocessor directly mutates entity state
**Severity**: Medium. Preprocessor bypasses entity boundaries.
`src/Php/Preprocessor.php`:
```php
line 279: $argInfo->name = $name; // direct property set
line 329: $functionDef->argInfoList[] = $argInfo; // direct array append
line 411: $this->methodDef->functionDef = $functionDef; // direct property set
line 447: $this->classDef->extends = $this->parentClass; // direct property set
line 585: $this->classDef->constants[$constInfo->name] = ...; // direct array set
line 618: $this->classDef->properties[$name] = $propDef; // direct array set
```
**Recommendation**: Use entity methods: `$functionDef->addArg($argInfo)`, `$this->methodDef->setFunctionDef($functionDef)`, `$classDef->addProperty($propDef)`, etc.
---
## Issue 6: CompilerBase protected state leaked to inheritance chain
**Severity**: Low-Medium. The chain CompilerBase → Preprocessor → Translator means any protected property in CompilerBase is accessible from Translator.
CompilerBase has ~50 protected properties. Translator is 3301 lines and accesses many of them. There's no way to know which properties are "safe to use" vs "internal to CompilerBase."
**Recommendation**: Migrate internal-only properties to `private` over time, with explicit getter methods where needed.
---
## Issue 7: Platform/Backend — well encapsulated
**Severity**: None. The Platform and Backend layers are well-encapsulated:
- All state is private (e.g., `$compilerCommand`, `$linkerCommand` in GccLikeBackend)
- Only methods are public
- Abstract contracts are clear
- Factory pattern is used consistently
**No changes needed in this layer.**
---
## Issue 8: ScopeContext is dead code
**Severity**: Low. `src/Php/Context/ScopeContext.php` — 7 lines, empty class body. Used as a placeholder type in FunctionContext's `$scopeLayouts` array. Either populate it or remove it.
---
## Implementation Priority
| # | Issue | Impact | Effort | Risk |
|---|-------|--------|--------|------|
| 1.1 | Entity: readonly for constructor fields | Data integrity | 2h | Low |
| 1.2 | Entity: add mutation methods (addArg, addProperty, etc.) | Safe mutation | 3h | Medium |
| 2.1 | FunctionContext: group properties into sub-objects | Clarity | 4h | Medium |
| 2.2 | FunctionContext: add accessor methods | Controlled mutation | 3h | Medium |
| 3 | CompilerBase: demote public→protected, protected→private | Boundary clarity | 4h | Medium |
| 4 | Define trait context contract | Safe coupling | 3h | Medium |
| 5 | Preprocessor: use entity methods | Consistent mutation | 2h | Low |
| 8 | Remove ScopeContext dead code | Cleanup | 15m | None |
**Recommended order**: Start with 8 (quick win), then 1.1 + 1.2 (entity cleanup), then 2.1 + 2.2 (context cleanup), then 3 + 5 + 4 (CompilerBase boundary).

@ -0,0 +1,21 @@
name: Patch PHP headers for C++
description: Apply temporary upstream PHP header fixes required by generated C++.
runs:
using: composite
steps:
# Temporary workaround for php/php-src#22935. Some supported PHP packages
# contain a php_hash.h revision that is valid C but invalid C++. Remove
# this action when all PHP 8.4/8.5 packages include php/php-src#22940.
- name: Patch php_hash.h
shell: bash
run: |
php_include_dir="$(php-config --include-dir)"
php_hash_header="${php_include_dir}/ext/hash/php_hash.h"
if grep -Fq 'char *base = ecalloc(' "${php_hash_header}"; then
sudo patch --directory="${php_include_dir}" --strip=1 \
< "${GITHUB_ACTION_PATH}/../../patches/php-hash-cxx.patch"
else
echo "php_hash.h already contains the upstream C++ fix"
fi

@ -0,0 +1,155 @@
name: Unix ARM64 build
description: Build and smoke-test TypePHP on a Unix ARM64 runner
inputs:
php-version:
description: PHP minor version
required: true
os:
description: Package operating-system identifier
required: true
library-extension:
description: Shared-library extension used by PHPX
required: true
smoke-directory:
description: Platform smoke project directory below .github/smoke
required: true
smoke-binary:
description: Platform smoke executable name
required: true
smoke-output:
description: Expected platform smoke output
required: true
runs:
using: composite
steps:
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ inputs.php-version }}
coverage: none
extensions: mbstring
ini-values: precision=17, memory_limit=4G, error_reporting=E_ERROR|E_WARNING, display_errors=1, display_startup_errors=1, log_errors=0
tools: composer:v2
env:
fail-fast: true
phpts: ts
update: true
- name: Install Linux build dependencies
if: inputs.os == 'linux'
shell: bash
run: |
sudo apt-get update
sudo apt-get install --yes build-essential cmake libgmp-dev libmpfr-dev pkg-config
- name: Install macOS build dependencies
if: inputs.os == 'macos'
shell: bash
run: brew install cmake gmp mpfr pkg-config
- name: Verify ZTS PHP and architecture
shell: bash
run: |
php -r 'if (!PHP_ZTS) { fwrite(STDERR, "Expected a ZTS PHP build\n"); exit(1); }'
case "$(php -r 'echo PHP_VERSION;')" in
"${{ inputs.php-version }}"*) ;;
*) echo "setup-php installed an unexpected PHP version" >&2; exit 1 ;;
esac
case "$(uname -m)" in
arm64|aarch64) ;;
*) echo "Expected an ARM64 runner, got $(uname -m)" >&2; exit 1 ;;
esac
php_home="$(php-config --prefix)"
embed_library="${php_home}/lib/libphp.${{ inputs.library-extension }}"
test -x "${php_home}/bin/php-config"
test -f "${embed_library}"
echo "PHP_HOME=${php_home}" >> "${GITHUB_ENV}"
if [[ '${{ inputs.os }}' == 'linux' ]]; then
echo "LD_LIBRARY_PATH=${PHPX_HOME}/lib:${php_home}/lib" >> "${GITHUB_ENV}"
else
echo "DYLD_LIBRARY_PATH=${PHPX_HOME}/lib:${php_home}/lib" >> "${GITHUB_ENV}"
fi
php -v
php-config --configure-options
- name: Patch php_hash.h C++ compatibility
uses: ./.github/actions/patch-php-headers
- name: Install Composer dependencies
shell: bash
run: composer install --prefer-dist --no-progress
- name: Build PHPX
shell: bash
run: |
cmake -S "${PHPX_HOME}" -B "${PHPX_HOME}/build" \
-D CMAKE_BUILD_TYPE=Release \
-D BUILD_TESTS=OFF \
-D BUILD_EXT=OFF \
-D GITHUB_ACTION=ON \
-D php_dir="${PHP_HOME}"
cmake --build "${PHPX_HOME}/build" --target phpx --parallel 2
test -f "${PHPX_HOME}/lib/libphpx.${{ inputs.library-extension }}"
- name: Build tpc
shell: bash
run: |
php bin/tpc.php project.yml --job 2 --no-progress
test -x ./tpc
file ./tpc
file ./tpc | grep -Eiq 'arm64|aarch64|ARM aarch64'
./tpc --version
- name: Run platform smoke test
shell: bash
run: |
smoke_root=".github/smoke/${{ inputs.smoke-directory }}"
./tpc "${smoke_root}/project.yml" --job 1 --no-progress
smoke_binary="${smoke_root}/${{ inputs.smoke-binary }}"
test -x "${smoke_binary}"
output="$("${smoke_binary}")"
test "${output}" = '${{ inputs.smoke-output }}'
- name: Show native dependencies
shell: bash
run: |
if [[ '${{ inputs.os }}' == 'linux' ]]; then
ldd ./tpc
else
otool -L ./tpc
fi
- name: Package tested compiler
if: startsWith(github.ref, 'refs/tags/') && inputs.php-version == '8.5'
shell: bash
run: |
composer install --no-dev --prefer-dist --no-progress --classmap-authoritative
export TYPEPHP_PACKAGE_VERSION="${GITHUB_REF_NAME}"
php package.php
test "$(find . -maxdepth 1 -name 'tpc_v*_${{ inputs.os }}_arm64.tar.gz' -type f | wc -l)" -eq 1
- name: Upload release package
if: startsWith(github.ref, 'refs/tags/') && inputs.php-version == '8.5'
uses: actions/upload-artifact@v4
with:
name: release-${{ inputs.os }}-arm64-php-${{ inputs.php-version }}-zts
if-no-files-found: error
retention-days: 1
path: tpc_v*_${{ inputs.os }}_arm64.tar.gz
- name: Upload platform build outputs
if: always()
uses: actions/upload-artifact@v4
with:
name: tpc-${{ inputs.os }}-arm64-php-${{ inputs.php-version }}-zts
include-hidden-files: true
if-no-files-found: warn
retention-days: 7
path: |
tpc
.github/smoke/${{ inputs.smoke-directory }}/${{ inputs.smoke-binary }}
.github/smoke/${{ inputs.smoke-directory }}/build/**/*.cc
.github/smoke/${{ inputs.smoke-directory }}/build/**/*.h

@ -1,90 +0,0 @@
# Copilot instructions for this repository
## Project overview
TypePHP is a PHP native compilation project. Its `tpc` command is TypePHP Compiler (AOT), which translates PHP source into C++, then compiles and links it into a native binary or a PHP extension. The primary entrypoint boots `src/compiler.php`; that drives `TypePhp\Translator` through a fixed pipeline:
1. `prepare()` scans files, parses ASTs, collects symbols, and topologically sorts PHP files by cross-file symbol usage.
2. `convert()` turns PHP ASTs into generated `.cc` files while passing through native source files (`.cpp`, `.c`, `.s`, `.m`, `.mm`).
3. `compile()` chooses the platform/compiler backend, generates support sources and headers, and compiles sources, using `pcntl` parallelism when available.
4. `build()` links object files into the final executable or extension.
`src/CompilerBase.php` contains most PHP-to-C++ translation logic and mixes in many traits for syntax handling and optimizations. `src/Preprocessor.php` owns dependency discovery and file ordering. Platform-specific behavior lives under `src/Platform/`, compiler backends under `src/Backend/`, and metadata/state objects under `src/Entity/` and `src/Context/`.
## Setup and build commands
The repo expects PHP 8.4+, GCC 9+ with C++17, CMake 3.24+, and a compiled `swoole/phpx` dependency. Install PHP dependencies with:
```bash
composer install
```
Build `phpx` before relying on compiler runs:
```bash
cd vendor/swoole/phpx
cmake .
make -j32
```
Compile a project, directory, single file, or `project.yml`:
```bash
./tpc <path-to-project-or-file>
./tpc <path> -O2
./tpc <path> --mode=ext -o <output_name>
```
## Test commands
Run the PHPUnit suite:
```bash
./vendor/bin/phpunit
```
Run a single PHPUnit file or a single test method:
```bash
./vendor/bin/phpunit phpunit/src/Platform/PlatformTest.php
./vendor/bin/phpunit --filter testWindowsBasic phpunit/src/Platform/PlatformTest.php
```
Run PHPT integration tests:
```bash
php run-tests.php tests/compiler/
php run-tests.php tests/compiler/arrays.phpt
```
For parser/runtime comparison without AOT compilation, there are docs using:
```bash
php run-tests.php --no-aot tests/compiler/arrow-functions.phpt
```
## Formatting
The repo ships a PHP CS Fixer config in `.php-cs-fixer.dist.php`:
```bash
php vendor/bin/php-cs-fixer fix --config=.php-cs-fixer.dist.php <path>
```
Generated C++ is auto-formatted by the compiler itself when `clang-format` is available.
## Configuration and repository conventions
- `project.yml` is the project-level build config. Important keys include `name`, `build-mode`, `cxx-std`, `cxx-flags`, `ld-flags`, `sources`, `ignore`, and `resource`.
- Command-line options intentionally override YAML values. `Translator` parses YAML first, then applies CLI arguments last.
- YAML parsing accepts both hyphenated and underscored variants for several keys, but existing examples use hyphenated names such as `build-mode` and `cxx-std`.
- In `bin` mode, compiled programs must define `main()`. In `ext` mode they do not.
- File discovery is mixed-language by design: PHP is translated, while native sources are compiled directly if they appear in configured sources.
- Generated files are written under `build/`, with generated C++ paths mirroring the source tree and generated headers under `build/include/`.
- Platform/compiler selection is centralized: `PlatformFactory` detects the OS, and `CompilerFactory` picks the backend (`Gcc`, `Clang`, `Msvc`) with environment/config overrides.
## Test-specific conventions
- PHPUnit tests for compiler internals should use `CompilerTest::create(ROOT_PATH)`, which enables test mode instead of normal fatal exits.
- `phpunit/bootstrap.php` exposes a `BaseTest::exec()` helper that expects compilation failures to surface as `TypePhp\Exception\TestError`.
- PHPT end-to-end tests live in `tests/compiler/`; existing guidance and examples generally put executable test logic inside a `main()` function.

@ -0,0 +1,11 @@
--- a/ext/hash/php_hash.h
+++ b/ext/hash/php_hash.h
@@ -158,7 +158,7 @@ static inline void *php_hash_alloc_context(const php_hash_ops *ops) {
/* Zero out context memory so serialization doesn't expose internals */
if (ops->context_align > 0) {
size_t align = ops->context_align;
- char *base = ecalloc(1, ops->context_size + align);
+ char *base = (char *) ecalloc(1, ops->context_size + align);
size_t offset = align - ((uintptr_t)base & (align - 1));
char *ptr = base + offset;
ptr[-1] = (char)offset;

@ -0,0 +1,44 @@
#include <phpx.h>
#include <cstring>
#include <sys/utsname.h>
#include <unistd.h>
using namespace php;
Int php_linux_current_process_id()
{
return static_cast<Int>(getpid());
}
Int php_linux_online_processor_count()
{
return static_cast<Int>(sysconf(_SC_NPROCESSORS_ONLN));
}
Bool php_linux_uname_machine_is_arm64()
{
utsname info{};
if (uname(&info) != 0) {
return false;
}
return std::strcmp(info.machine, "aarch64") == 0 || std::strcmp(info.machine, "arm64") == 0;
}
Bool php_linux_native_is_arm64()
{
#if defined(__aarch64__) || defined(__arm64__)
return true;
#else
return false;
#endif
}
Bool php_linux_native_php_is_zts()
{
#ifdef ZTS
return true;
#else
return false;
#endif
}

@ -0,0 +1,12 @@
<?php
/** Linux ARM64 platform declarations implemented by platform.cc. */
function linux_current_process_id(): int {}
function linux_online_processor_count(): int {}
function linux_uname_machine_is_arm64(): bool {}
function linux_native_is_arm64(): bool {}
function linux_native_php_is_zts(): bool {}

@ -0,0 +1,22 @@
<?php
function requireLinuxArm64Condition(bool $condition, string $message): void
{
if (!$condition) {
throw new RuntimeException($message);
}
}
function main(): void
{
requireLinuxArm64Condition(PHP_OS_FAMILY === 'Linux', 'Expected PHP_OS_FAMILY=Linux');
requireLinuxArm64Condition(DIRECTORY_SEPARATOR === '/', 'Expected the Unix directory separator');
requireLinuxArm64Condition(PHP_ZTS !== 0 && PHP_ZTS !== false, 'Expected a ZTS PHP runtime');
requireLinuxArm64Condition(linux_native_php_is_zts(), 'The native ZTS macro is not enabled');
requireLinuxArm64Condition(linux_native_is_arm64(), 'The native compiler target is not ARM64');
requireLinuxArm64Condition(linux_current_process_id() > 0, 'getpid() failed');
requireLinuxArm64Condition(linux_online_processor_count() > 0, 'sysconf() returned no processors');
requireLinuxArm64Condition(linux_uname_machine_is_arm64(), 'uname() did not report ARM64');
echo 'linux-arm64-smoke-ok:zts';
}

@ -0,0 +1,9 @@
name: linux-arm64-smoke
mode: bin
build-dir: build
output: linux_arm64_smoke
cxx-std: c++17
sources:
- main.php
- cpp-src

@ -0,0 +1,45 @@
#include <phpx.h>
#include <mach/mach.h>
#include <sys/sysctl.h>
#include <unistd.h>
using namespace php;
Int php_macos_current_process_id()
{
return static_cast<Int>(getpid());
}
Int php_macos_logical_processor_count()
{
int count = 0;
size_t size = sizeof(count);
if (sysctlbyname("hw.logicalcpu", &count, &size, nullptr, 0) != 0) {
return 0;
}
return static_cast<Int>(count);
}
Bool php_macos_has_mach_host_port()
{
return mach_host_self() != MACH_PORT_NULL;
}
Bool php_macos_native_is_arm64()
{
#if defined(__aarch64__) || defined(__arm64__)
return true;
#else
return false;
#endif
}
Bool php_macos_native_php_is_zts()
{
#ifdef ZTS
return true;
#else
return false;
#endif
}

@ -0,0 +1,12 @@
<?php
/** macOS platform declarations implemented by platform.cc. */
function macos_current_process_id(): int {}
function macos_logical_processor_count(): int {}
function macos_has_mach_host_port(): bool {}
function macos_native_is_arm64(): bool {}
function macos_native_php_is_zts(): bool {}

@ -0,0 +1,22 @@
<?php
function requireMacosCondition(bool $condition, string $message): void
{
if (!$condition) {
throw new RuntimeException($message);
}
}
function main(): void
{
requireMacosCondition(PHP_OS_FAMILY === 'Darwin', 'Expected PHP_OS_FAMILY=Darwin');
requireMacosCondition(DIRECTORY_SEPARATOR === '/', 'Expected the Unix directory separator');
requireMacosCondition(PHP_ZTS !== 0 && PHP_ZTS !== false, 'Expected a ZTS PHP runtime');
requireMacosCondition(macos_native_php_is_zts(), 'The native ZTS macro is not enabled');
requireMacosCondition(macos_native_is_arm64(), 'The native compiler target is not ARM64');
requireMacosCondition(macos_current_process_id() > 0, 'getpid() failed');
requireMacosCondition(macos_logical_processor_count() > 0, 'sysctl() returned no processors');
requireMacosCondition(macos_has_mach_host_port(), 'mach_host_self() failed');
echo 'macos-arm64-smoke-ok:zts';
}

@ -0,0 +1,9 @@
name: macos-smoke
mode: bin
build-dir: build
output: macos_smoke
cxx-std: c++17
sources:
- main.php
- cpp-src

@ -0,0 +1,43 @@
name: Linux ARM64
on:
push:
pull_request:
workflow_dispatch:
permissions:
contents: read
concurrency:
group: linux-arm64-${{ github.ref }}
cancel-in-progress: true
env:
COMPOSER_NO_INTERACTION: 1
COMPOSER_PROCESS_TIMEOUT: 0
jobs:
build:
name: Build - PHP ${{ matrix.php }} ZTS
runs-on: ubuntu-22.04-arm
timeout-minutes: 90
strategy:
fail-fast: false
matrix:
php: ["8.4", "8.5"]
env:
PHPX_HOME: ${{ github.workspace }}/vendor/swoole/phpx
steps:
- name: Checkout TypePHP
uses: actions/checkout@v4
- name: Build and smoke test
uses: ./.github/actions/unix-arm64-build
with:
php-version: ${{ matrix.php }}
os: linux
library-extension: so
smoke-directory: linux-arm64
smoke-binary: linux_arm64_smoke
smoke-output: linux-arm64-smoke-ok:zts

@ -0,0 +1,360 @@
name: Linux x64
on:
push:
pull_request:
workflow_dispatch:
permissions:
contents: read
concurrency:
group: tests-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
COMPOSER_NO_INTERACTION: 1
COMPOSER_PROCESS_TIMEOUT: 0
jobs:
build:
# Compilation is always required; `--skip-tests` only suppresses downstream test jobs.
name: Build - PHP ${{ matrix.php }} ZTS
runs-on: ubuntu-22.04
timeout-minutes: 60
strategy:
fail-fast: false
matrix:
php: ["8.4", "8.5"]
env:
PHPX_HOME: ${{ github.workspace }}/vendor/swoole/phpx
steps:
- name: Checkout TypePHP
uses: actions/checkout@v4
- name: Checkout phpy
uses: actions/checkout@v4
with:
repository: swoole/phpy
path: third_party/phpy
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
coverage: none
ini-values: precision=17, memory_limit=4G, error_reporting=E_ERROR|E_WARNING, display_errors=1, display_startup_errors=1, log_errors=0
tools: composer:v2
env:
fail-fast: true
phpts: ts
update: true
- name: Verify ZTS PHP
shell: bash
run: |
php -r 'if (!PHP_ZTS) { fwrite(STDERR, "Expected a ZTS PHP build\n"); exit(1); }'
case "$(php -r 'echo PHP_VERSION;')" in
"${{ matrix.php }}"*) ;;
*) echo "setup-php installed an unexpected PHP version" >&2; exit 1 ;;
esac
- name: Patch php_hash.h C++ compatibility
uses: ./.github/actions/patch-php-headers
- name: Install native build dependencies
run: |
sudo apt-get update
sudo apt-get install --yes build-essential cmake libgmp-dev libmpfr-dev pkg-config python3-dev
- name: Configure ZTS PHP embed library
shell: bash
run: |
php_home="$(php-config --prefix)"
embed_library="${php_home}/lib/libphp.so"
test -x "${php_home}/bin/php-config"
test -f "${embed_library}"
echo "PHP_HOME=${php_home}" >> "${GITHUB_ENV}"
echo "Using ZTS PHP $(php-config --version) embed library: ${embed_library}"
- name: Install Composer dependencies
run: composer install --prefer-dist --no-progress
- name: Build PHPX
shell: bash
run: |
cmake -S "${PHPX_HOME}" -B "${PHPX_HOME}/build" \
-D CMAKE_BUILD_TYPE=Release \
-D BUILD_TESTS=OFF \
-D BUILD_EXT=OFF \
-D GITHUB_ACTION=ON \
-D php_dir="${PHP_HOME}"
cmake --build "${PHPX_HOME}/build" --target phpx --parallel 2
test -f "${PHPX_HOME}/lib/libphpx.so"
- name: Build phpy
working-directory: third_party/phpy
run: |
phpize
./configure
make -j2
test -f modules/phpy.so
- name: Enable phpy extension
shell: bash
run: |
php_ini_dir="$(php-config --ini-dir)"
echo "extension=${GITHUB_WORKSPACE}/third_party/phpy/modules/phpy.so" \
| sudo tee "${php_ini_dir}/90-phpy.ini"
echo "PHP_INI_SCAN_DIR=${php_ini_dir}" >> "${GITHUB_ENV}"
php --ri phpy
- name: Configure native library path
shell: bash
run: echo "LD_LIBRARY_PATH=${PHPX_HOME}/lib:${PHP_HOME}/lib" >> "${GITHUB_ENV}"
- name: Build tpc
shell: bash
run: |
php bin/tpc.php project.yml --job 2 --no-progress
test -x ./tpc
file ./tpc
file ./tpc | grep -Eiq 'x86-64|x86_64|amd64'
./tpc --version
- name: Upload Linux x64 build outputs
uses: actions/upload-artifact@v4
with:
name: tpc-linux-x64-php-${{ matrix.php }}-zts
if-no-files-found: error
retention-days: 7
path: |
tpc
vendor/swoole/phpx/lib/libphpx.so
third_party/phpy/modules/phpy.so
phpunit:
name: PHPUnit - PHP ${{ matrix.php }} ZTS
if: ${{ startsWith(github.ref, 'refs/tags/') || !contains(github.event.head_commit.message || '', '--skip-tests') }}
runs-on: ubuntu-22.04
timeout-minutes: 30
needs: build
strategy:
fail-fast: false
matrix:
php: ["8.4", "8.5"]
env:
PHPX_HOME: ${{ github.workspace }}/vendor/swoole/phpx
steps:
- name: Checkout TypePHP
uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
coverage: none
extensions: curl, redis, mbstring, ffi
ini-values: ffi.enable=1, phpy.enable_operator_overloading=0, precision=17, memory_limit=4G, error_reporting=E_ERROR|E_WARNING, display_errors=1, display_startup_errors=1, log_errors=0
tools: composer:v2
env:
fail-fast: true
phpts: ts
update: true
- name: Verify ZTS PHP
shell: bash
run: |
php -r 'if (!PHP_ZTS) { fwrite(STDERR, "Expected a ZTS PHP build\n"); exit(1); }'
case "$(php -r 'echo PHP_VERSION;')" in
"${{ matrix.php }}"*) ;;
*) echo "setup-php installed an unexpected PHP version" >&2; exit 1 ;;
esac
- name: Patch php_hash.h C++ compatibility
uses: ./.github/actions/patch-php-headers
- name: Install native build dependencies
run: |
sudo apt-get update
sudo apt-get install --yes build-essential cmake libgmp-dev libmpfr-dev pkg-config python3-dev
- name: Install Composer dependencies
run: composer install --prefer-dist --no-progress
- name: Download Linux x64 build outputs
uses: actions/download-artifact@v4
with:
name: tpc-linux-x64-php-${{ matrix.php }}-zts
path: .
- name: Verify Linux x64 build outputs
shell: bash
run: |
test -f ./tpc
test -f "${PHPX_HOME}/lib/libphpx.so"
test -f third_party/phpy/modules/phpy.so
chmod +x ./tpc
file ./tpc | grep -Eiq 'x86-64|x86_64|amd64'
- name: Enable phpy extension
shell: bash
run: |
php_ini_dir="$(php-config --ini-dir)"
echo "extension=${GITHUB_WORKSPACE}/third_party/phpy/modules/phpy.so" \
| sudo tee "${php_ini_dir}/90-phpy.ini"
echo "PHP_INI_SCAN_DIR=${php_ini_dir}" >> "${GITHUB_ENV}"
php --ri phpy
- name: Configure native library path
shell: bash
run: echo "LD_LIBRARY_PATH=${PHPX_HOME}/lib:$(php-config --prefix)/lib" >> "${GITHUB_ENV}"
- name: Run PHPUnit
run: vendor/bin/phpunit
phpt:
name: PHPT - PHP ${{ matrix.php }} ZTS
if: ${{ startsWith(github.ref, 'refs/tags/') || !contains(github.event.head_commit.message || '', '--skip-tests') }}
runs-on: ubuntu-22.04
timeout-minutes: 180
needs: build
strategy:
fail-fast: false
matrix:
php: ["8.4", "8.5"]
env:
PHPX_HOME: ${{ github.workspace }}/vendor/swoole/phpx
NO_INTERACTION: 1
REPORT_EXIT_STATUS: 1
TYPEPHP_PHPT_GENERATED_ARTIFACT_DIR: ${{ github.workspace }}/build/phpt-generated
steps:
- name: Checkout TypePHP
uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
coverage: none
extensions: curl, redis, mbstring, ffi
ini-values: ffi.enable=1, phpy.enable_operator_overloading=0, opcache.jit=0, precision=17, memory_limit=4G, error_reporting=E_ERROR|E_WARNING, display_errors=1, display_startup_errors=1, log_errors=0
tools: composer:v2
env:
fail-fast: true
phpts: ts
update: true
- name: Verify ZTS PHP
shell: bash
run: |
php -r 'if (!PHP_ZTS) { fwrite(STDERR, "Expected a ZTS PHP build\n"); exit(1); }'
case "$(php -r 'echo PHP_VERSION;')" in
"${{ matrix.php }}"*) ;;
*) echo "setup-php installed an unexpected PHP version" >&2; exit 1 ;;
esac
- name: Patch php_hash.h C++ compatibility
uses: ./.github/actions/patch-php-headers
- name: Install native build dependencies
run: |
sudo apt-get update
sudo apt-get install --yes build-essential cmake libgmp-dev libmpfr-dev pkg-config python3-dev
- name: Configure ZTS PHP embed library
shell: bash
run: |
php_home="$(php-config --prefix)"
embed_library="${php_home}/lib/libphp.so"
test -x "${php_home}/bin/php-config"
test -f "${embed_library}"
echo "PHP_HOME=${php_home}" >> "${GITHUB_ENV}"
echo "Using ZTS PHP $(php-config --version) embed library: ${embed_library}"
- name: Install Composer dependencies
run: composer install --prefer-dist --no-progress
- name: Download Linux x64 build outputs
uses: actions/download-artifact@v4
with:
name: tpc-linux-x64-php-${{ matrix.php }}-zts
path: .
- name: Verify Linux x64 build outputs
shell: bash
run: |
test -f ./tpc
test -f "${PHPX_HOME}/lib/libphpx.so"
test -f third_party/phpy/modules/phpy.so
chmod +x ./tpc
file ./tpc | grep -Eiq 'x86-64|x86_64|amd64'
- name: Enable phpy extension
shell: bash
run: |
php_ini_dir="$(php-config --ini-dir)"
echo "extension=${GITHUB_WORKSPACE}/third_party/phpy/modules/phpy.so" \
| sudo tee "${php_ini_dir}/90-phpy.ini"
echo "PHP_INI_SCAN_DIR=${php_ini_dir}" >> "${GITHUB_ENV}"
php --ri phpy
- name: Configure native library path
shell: bash
run: |
test -f "${PHPX_HOME}/lib/libphpx.so"
test -f "${PHP_HOME}/lib/libphp.so"
echo "LD_LIBRARY_PATH=${PHPX_HOME}/lib:${PHP_HOME}/lib" >> "${GITHUB_ENV}"
- name: Show build environment
run: |
./tpc --version
php -v
php --ini
ldd ./tpc | grep -E 'libphp(x)?[0-9.]*\.so'
cmake --version
c++ --version
- name: Run compiler PHPT suite with bootstrap compiler
run: |
mkdir -p build
php run-tests.php -q -j8 --compiler ./tpc \
-w build/failed-tests.txt -W build/test-results.txt tests/compiler
- name: Package tested Linux compiler
if: startsWith(github.ref, 'refs/tags/') && matrix.php == '8.5'
shell: bash
run: |
composer install --no-dev --prefer-dist --no-progress --classmap-authoritative
export TYPEPHP_PACKAGE_VERSION="${GITHUB_REF_NAME}"
php package.php
test "$(find . -maxdepth 1 -name 'tpc_v*_linux_*.tar.gz' -type f | wc -l)" -eq 1
- name: Upload Linux release package
if: startsWith(github.ref, 'refs/tags/') && matrix.php == '8.5'
uses: actions/upload-artifact@v4
with:
name: release-linux-x64-php-${{ matrix.php }}-zts
if-no-files-found: error
retention-days: 1
path: tpc_v*_linux_*.tar.gz
- name: Upload PHPT failure artifacts
if: failure()
uses: actions/upload-artifact@v4
with:
name: phpt-failures-linux-x64-php-${{ matrix.php }}-zts
if-no-files-found: ignore
retention-days: 7
path: |
build/failed-tests.txt
build/test-results.txt
build/**/*.cc
build/**/*.h
php_test_results_*.txt
tests/compiler/**/*.diff
tests/compiler/**/*.log
tests/compiler/**/*.out

@ -0,0 +1,43 @@
name: macOS ARM64
on:
push:
pull_request:
workflow_dispatch:
permissions:
contents: read
concurrency:
group: macos-arm64-${{ github.ref }}
cancel-in-progress: true
env:
COMPOSER_NO_INTERACTION: 1
COMPOSER_PROCESS_TIMEOUT: 0
jobs:
build:
name: Build - PHP ${{ matrix.php }} ZTS
runs-on: macos-15
timeout-minutes: 90
strategy:
fail-fast: false
matrix:
php: ["8.4", "8.5"]
env:
PHPX_HOME: ${{ github.workspace }}/vendor/swoole/phpx
steps:
- name: Checkout TypePHP
uses: actions/checkout@v4
- name: Build and smoke test
uses: ./.github/actions/unix-arm64-build
with:
php-version: ${{ matrix.php }}
os: macos
library-extension: dylib
smoke-directory: macos
smoke-binary: macos_smoke
smoke-output: macos-arm64-smoke-ok:zts

@ -0,0 +1,141 @@
name: Package release
on:
push:
tags:
- "v*"
permissions:
actions: read
contents: write
concurrency:
group: release-${{ github.ref }}
cancel-in-progress: false
jobs:
publish:
name: Publish ${{ github.ref_name }}
runs-on: ubuntu-22.04
timeout-minutes: 240
env:
GH_TOKEN: ${{ github.token }}
steps:
- name: Wait for tested binary packages
shell: bash
run: |
set -euo pipefail
wait_for_workflow() {
local workflow="$1"
local output_name="$2"
local run_json run_id status conclusion
for attempt in $(seq 1 960); do
run_json="$(gh run list \
--repo "${GITHUB_REPOSITORY}" \
--workflow "${workflow}" \
--commit "${GITHUB_SHA}" \
--event push \
--limit 20 \
--json databaseId,status,conclusion,headBranch,createdAt)"
run_id="$(jq -r --arg tag "${GITHUB_REF_NAME}" '
[.[] | select(.headBranch == $tag)]
| sort_by(.createdAt)
| last
| .databaseId // empty
' <<<"${run_json}")"
if [[ -z "${run_id}" ]]; then
echo "Waiting for ${workflow} to start for tag ${GITHUB_REF_NAME}..."
sleep 15
continue
fi
status="$(jq -r --argjson id "${run_id}" '
.[] | select(.databaseId == $id) | .status
' <<<"${run_json}")"
conclusion="$(jq -r --argjson id "${run_id}" '
.[] | select(.databaseId == $id) | .conclusion
' <<<"${run_json}")"
if [[ "${status}" == "completed" ]]; then
if [[ "${conclusion}" != "success" ]]; then
echo "${workflow} run ${run_id} completed with ${conclusion}" >&2
exit 1
fi
echo "${workflow} run ${run_id} succeeded"
echo "${output_name}=${run_id}" >> "${GITHUB_ENV}"
return 0
fi
echo "Waiting for ${workflow} run ${run_id}: ${status}"
sleep 15
done
echo "Timed out waiting for ${workflow}" >&2
exit 1
}
wait_for_workflow linux-x64.yml TESTS_RUN_ID
wait_for_workflow windows-build.yml WINDOWS_RUN_ID
wait_for_workflow linux-arm64.yml LINUX_ARM64_RUN_ID
wait_for_workflow macos-arm64.yml MACOS_ARM64_RUN_ID
- name: Download tested packages
shell: bash
run: |
set -euo pipefail
mkdir -p dist/linux-x64 dist/linux-arm64 dist/macos-arm64 dist/windows
gh run download "${TESTS_RUN_ID}" \
--repo "${GITHUB_REPOSITORY}" \
--pattern 'release-linux-*' \
--dir dist/linux-x64
gh run download "${WINDOWS_RUN_ID}" \
--repo "${GITHUB_REPOSITORY}" \
--pattern 'release-windows-*' \
--dir dist/windows
gh run download "${LINUX_ARM64_RUN_ID}" \
--repo "${GITHUB_REPOSITORY}" \
--pattern 'release-linux-arm64-*' \
--dir dist/linux-arm64
gh run download "${MACOS_ARM64_RUN_ID}" \
--repo "${GITHUB_REPOSITORY}" \
--pattern 'release-macos-arm64-*' \
--dir dist/macos-arm64
test "$(find dist -type f -name 'tpc_v*_linux_x64.tar.gz' | wc -l)" -eq 1
test "$(find dist -type f -name 'tpc_v*_linux_arm64.tar.gz' | wc -l)" -eq 1
test "$(find dist -type f -name 'tpc_v*_macos_arm64.tar.gz' | wc -l)" -eq 1
test "$(find dist -type f -name 'tpc_v*_windows_x64.zip' | wc -l)" -eq 1
- name: Prepare release assets
shell: bash
run: |
set -euo pipefail
mkdir -p release-assets
find dist -type f \( -name '*.tar.gz' -o -name '*.zip' \) \
-exec cp '{}' release-assets/ \;
test "$(find release-assets -maxdepth 1 -type f \( -name '*.tar.gz' -o -name '*.zip' \) | wc -l)" -eq 4
(
cd release-assets
sha256sum ./*.tar.gz ./*.zip > SHA256SUMS
)
ls -lh release-assets
- name: Publish GitHub release
shell: bash
run: |
set -euo pipefail
mapfile -t assets < <(find release-assets -maxdepth 1 -type f | sort)
if gh release view "${GITHUB_REF_NAME}" --repo "${GITHUB_REPOSITORY}" >/dev/null 2>&1; then
gh release upload "${GITHUB_REF_NAME}" "${assets[@]}" \
--repo "${GITHUB_REPOSITORY}" \
--clobber
else
gh release create "${GITHUB_REF_NAME}" "${assets[@]}" \
--repo "${GITHUB_REPOSITORY}" \
--verify-tag \
--title "TypePHP ${GITHUB_REF_NAME}" \
--generate-notes
fi

@ -0,0 +1,449 @@
name: Windows x64
on:
push:
pull_request:
workflow_dispatch:
permissions:
contents: read
concurrency:
group: windows-tpc-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
COMPOSER_NO_INTERACTION: 1
COMPOSER_PROCESS_TIMEOUT: 0
jobs:
build-tpc:
name: Build - PHP ${{ matrix.php }} ZTS
runs-on: windows-2022
timeout-minutes: 90
strategy:
fail-fast: false
matrix:
php: ["8.4", "8.5"]
steps:
- name: Checkout TypePHP
uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
coverage: none
extensions: zip
tools: composer:v2
env:
fail-fast: true
phpts: ts
update: true
- name: Resolve PHP build environment
id: php-build-env
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
$phpVersion = php -r 'echo PHP_VERSION;'
if (-not $phpVersion.StartsWith('${{ matrix.php }}.')) {
throw "setup-php installed PHP $phpVersion, expected ${{ matrix.php }}.x"
}
$threadSafety = php -r 'echo PHP_ZTS ? "zts" : "nts";'
if ($threadSafety -ne 'zts') {
throw "setup-php installed $threadSafety PHP, expected zts"
}
$zipAvailable = php -r 'echo class_exists("ZipArchive") ? "yes" : "no";'
if ($zipAvailable -ne 'yes') {
throw 'setup-php did not enable the ZipArchive extension required by package.php'
}
$phpExe = (Get-Command php).Source
$phpHome = Split-Path -Parent $phpExe
$archiveName = "php-devel-pack-$phpVersion-Win32-vs17-x64.zip"
$archive = Join-Path $env:RUNNER_TEMP $archiveName
"PHP_VERSION=$phpVersion" | Out-File $env:GITHUB_ENV -Append -Encoding utf8
"PHP_THREAD_SAFETY=$threadSafety" | Out-File $env:GITHUB_ENV -Append -Encoding utf8
"PHP_HOME=$phpHome" | Out-File $env:GITHUB_ENV -Append -Encoding utf8
"PHPX_HOME=${{ github.workspace }}\vendor\swoole\phpx" |
Out-File $env:GITHUB_ENV -Append -Encoding utf8
"PHP_DEVEL_ARCHIVE=$archive" | Out-File $env:GITHUB_ENV -Append -Encoding utf8
"version=$phpVersion" | Out-File $env:GITHUB_OUTPUT -Append -Encoding utf8
"thread_safety=$threadSafety" | Out-File $env:GITHUB_OUTPUT -Append -Encoding utf8
"archive=$archive" | Out-File $env:GITHUB_OUTPUT -Append -Encoding utf8
- name: Cache PHP development package
id: cache-php-devel
uses: actions/cache/restore@v4
with:
path: ${{ steps.php-build-env.outputs.archive }}
key: windows-2022-php-devel-${{ steps.php-build-env.outputs.version }}-${{ steps.php-build-env.outputs.thread_safety }}-vs17-x64
- name: Install matching PHP development SDK
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
$phpVersion = $env:PHP_VERSION
$phpHome = $env:PHP_HOME
$threadSafety = $env:PHP_THREAD_SAFETY
$archive = $env:PHP_DEVEL_ARCHIVE
$archiveName = Split-Path -Leaf $archive
$extractDir = Join-Path $env:RUNNER_TEMP "php-devel-$phpVersion"
if (-not (Test-Path $archive)) {
$downloaded = $false
foreach ($baseUrl in @(
'https://downloads.php.net/~windows/releases',
'https://downloads.php.net/~windows/releases/archives'
)) {
try {
Invoke-WebRequest -Uri "$baseUrl/$archiveName" -OutFile $archive
$downloaded = $true
break
} catch {
Remove-Item $archive -Force -ErrorAction SilentlyContinue
}
}
if (-not $downloaded) {
throw "Unable to download the PHP $phpVersion ZTS development pack"
}
}
Expand-Archive -Path $archive -DestinationPath $extractDir -Force
$sdkSource = Get-ChildItem $extractDir -Directory |
Where-Object { Test-Path (Join-Path $_.FullName 'include\main\php.h') } |
Select-Object -First 1
if ($null -eq $sdkSource) {
throw "The PHP development archive has an unexpected layout: $archiveName"
}
$sdk = Join-Path $phpHome 'SDK'
$sdkInclude = Join-Path $sdk 'include'
$sdkLib = Join-Path $sdk 'lib'
New-Item $sdkInclude, $sdkLib -ItemType Directory -Force | Out-Null
Copy-Item (Join-Path $sdkSource.FullName 'include\*') $sdkInclude -Recurse -Force
Copy-Item (Join-Path $sdkSource.FullName 'lib\*') $sdkLib -Force
$embedLibrary = Join-Path $phpHome 'php8embed.lib'
if (-not (Test-Path $embedLibrary)) {
throw "setup-php did not install php8embed.lib in $phpHome"
}
Copy-Item $embedLibrary $sdkLib -Force
$coreLibrary = 'php8ts.lib'
$runtimeLibrary = 'php8ts.dll'
# Temporary compatibility fix for PHP packages predating php/php-src#22940.
$hashHeader = Join-Path $sdkInclude 'ext\hash\php_hash.h'
$hashSource = [IO.File]::ReadAllText($hashHeader)
$invalidAllocation = 'char *base = ecalloc('
if ($hashSource.Contains($invalidAllocation)) {
$hashSource = $hashSource.Replace(
$invalidAllocation,
'char *base = (char *) ecalloc('
)
[IO.File]::WriteAllText(
$hashHeader,
$hashSource,
[Text.UTF8Encoding]::new($false)
)
}
foreach ($required in @(
(Join-Path $sdkInclude 'main\php.h'),
(Join-Path $sdkLib $coreLibrary),
(Join-Path $sdkLib 'php8embed.lib'),
(Join-Path $phpHome $runtimeLibrary)
)) {
if (-not (Test-Path $required)) {
throw "Required PHP SDK file is missing: $required"
}
}
Write-Host "Using PHP $phpVersion from $phpHome"
- name: Save PHP development package
if: steps.cache-php-devel.outputs.cache-hit != 'true'
uses: actions/cache/save@v4
with:
path: ${{ steps.php-build-env.outputs.archive }}
key: ${{ steps.cache-php-devel.outputs.cache-primary-key }}
- name: Install Composer dependencies
run: composer install --prefer-dist --no-progress
- name: Patch PHPX Windows CMake target ordering
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
$cmakeFile = Join-Path $env:PHPX_HOME 'CMakeLists.txt'
$source = [IO.File]::ReadAllText($cmakeFile).Replace("`r`n", "`n")
$copyBlockPattern = '(?ms)^ # 复制 DLL 到输出目录\n file\(GLOB MPDEC_DLLS .*?^ endforeach\(\)\n'
$copyBlock = [regex]::Match($source, $copyBlockPattern)
if (-not $copyBlock.Success) {
throw 'Unable to locate the pre-target PHPX mpdecimal copy block'
}
$source = $source.Remove($copyBlock.Index, $copyBlock.Length)
$targetPattern = '(?ms)(add_library\(phpx SHARED \$\{SRC_FILES\}\)\nset_target_properties\(phpx PROPERTIES\n CLEAN_DIRECT_OUTPUT 1\n\)\n)'
$target = [regex]::Match($source, $targetPattern)
if (-not $target.Success) {
throw 'Unable to locate the PHPX target declaration'
}
$copyBlockText = $copyBlock.Value.Replace(' # 复制 DLL 到输出目录', ' # Copy mpdecimal DLLs after the phpx target exists.')
$guardedCopyBlock = "`nif (IS_WINDOWS)`n$copyBlockText" + "endif()`n"
$source = $source.Insert($target.Index + $target.Length, $guardedCopyBlock)
[IO.File]::WriteAllText($cmakeFile, $source, [Text.UTF8Encoding]::new($false))
$targetOffset = $source.IndexOf('add_library(phpx SHARED')
$copyOffset = $source.IndexOf('add_custom_command(TARGET phpx POST_BUILD')
if ($targetOffset -lt 0 -or $copyOffset -le $targetOffset) {
throw 'PHPX post-build command still precedes its target declaration'
}
- name: Configure MSVC
uses: ilammy/msvc-dev-cmd@v1
with:
arch: x64
- name: Cache GMP and MPFR
id: cache-gmp-mpfr
uses: actions/cache/restore@v4
with:
path: ${{ runner.temp }}\typephp-cache\vcpkg-x64-windows
key: windows-2022-msvc-vcpkg-gmp-mpfr-x64-v1
- name: Install GMP and MPFR
if: steps.cache-gmp-mpfr.outputs.cache-hit != 'true'
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
$triplet = 'x64-windows'
$vcpkg = Join-Path $env:VCPKG_INSTALLATION_ROOT 'vcpkg.exe'
& $vcpkg install "gmp:$triplet" "mpfr:$triplet"
if ($LASTEXITCODE -ne 0) {
throw "vcpkg failed with exit code $LASTEXITCODE"
}
$installed = Join-Path $env:VCPKG_INSTALLATION_ROOT "installed\$triplet"
$cache = Join-Path $env:RUNNER_TEMP 'typephp-cache\vcpkg-x64-windows'
New-Item "$cache\include", "$cache\lib", "$cache\bin" -ItemType Directory -Force |
Out-Null
Copy-Item (Join-Path $installed 'include\*') "$cache\include" -Recurse -Force
foreach ($library in @('gmp.lib', 'gmpxx.lib', 'mpfr.lib')) {
$source = Join-Path $installed "lib\$library"
if (-not (Test-Path $source)) {
throw "vcpkg did not install $library"
}
Copy-Item $source "$cache\lib" -Force
}
Copy-Item (Join-Path $installed 'bin\*.dll') "$cache\bin" -Force
- name: Save GMP and MPFR
if: steps.cache-gmp-mpfr.outputs.cache-hit != 'true'
uses: actions/cache/save@v4
with:
path: ${{ runner.temp }}\typephp-cache\vcpkg-x64-windows
key: ${{ steps.cache-gmp-mpfr.outputs.cache-primary-key }}
- name: Stage GMP and MPFR
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
$cache = Join-Path $env:RUNNER_TEMP 'typephp-cache\vcpkg-x64-windows'
$sdkInclude = Join-Path $env:PHP_HOME 'SDK\include'
$sdkLib = Join-Path $env:PHP_HOME 'SDK\lib'
Copy-Item "$cache\include\*" $sdkInclude -Recurse -Force
foreach ($library in @('gmp.lib', 'gmpxx.lib', 'mpfr.lib')) {
$source = Join-Path $cache "lib\$library"
if (-not (Test-Path $source)) {
throw "The dependency cache does not contain $library"
}
Copy-Item $source $sdkLib -Force
}
Copy-Item "$cache\bin\*.dll" $env:PHP_HOME -Force
- name: Cache mpdecimal
id: cache-mpdecimal
uses: actions/cache/restore@v4
with:
path: ${{ env.PHPX_HOME }}\thirdparty\mpdecimal\vcbuild\dist64
key: windows-2022-msvc-mpdecimal-x64-${{ hashFiles('vendor/swoole/phpx/thirdparty/mpdecimal/**') }}
- name: Build mpdecimal
if: steps.cache-mpdecimal.outputs.cache-hit != 'true'
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
$buildScript = Join-Path $env:PHPX_HOME 'thirdparty\mpdecimal\vcbuild\vcbuild64.bat'
$buildDirectory = Split-Path -Parent $buildScript
Push-Location $buildDirectory
try {
& cmd.exe /d /s /c vcbuild64.bat
if ($LASTEXITCODE -ne 0) {
throw "mpdecimal build failed with exit code $LASTEXITCODE"
}
} finally {
Pop-Location
}
- name: Save mpdecimal
if: steps.cache-mpdecimal.outputs.cache-hit != 'true'
uses: actions/cache/save@v4
with:
path: ${{ env.PHPX_HOME }}\thirdparty\mpdecimal\vcbuild\dist64
key: ${{ steps.cache-mpdecimal.outputs.cache-primary-key }}
- name: Stage mpdecimal
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
$dist = Join-Path $env:PHPX_HOME 'thirdparty\mpdecimal\vcbuild\dist64'
$sdkInclude = Join-Path $env:PHP_HOME 'SDK\include'
$sdkLib = Join-Path $env:PHP_HOME 'SDK\lib'
Copy-Item (Join-Path $dist '*.lib') $sdkLib -Force
Copy-Item (Join-Path $dist '*.dll') $sdkLib -Force
Copy-Item (Join-Path $dist '*.dll') $env:PHP_HOME -Force
Copy-Item (Join-Path $dist 'mpdecimal.h') $sdkInclude -Force
Copy-Item (Join-Path $dist 'decimal.hh') $sdkInclude -Force
- name: Build PHPX
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
$phpxBuild = Join-Path $env:PHPX_HOME 'build'
$sdkLib = Join-Path $env:PHP_HOME 'SDK\lib'
cmake -S $env:PHPX_HOME -B $phpxBuild -G Ninja `
-D CMAKE_BUILD_TYPE=Release `
-D BUILD_TESTS=OFF `
-D BUILD_EXT=OFF `
-D GITHUB_ACTION=ON `
-D MPDECIMAL_LIBRARY="$sdkLib\libmpdec-4.0.1.dll.lib" `
-D MPDECIMALXX_LIBRARY="$sdkLib\libmpdec++-4.0.1.dll.lib"
if ($LASTEXITCODE -ne 0) {
throw "PHPX configuration failed with exit code $LASTEXITCODE"
}
cmake --build $phpxBuild --target phpx --parallel 2
if ($LASTEXITCODE -ne 0) {
throw "PHPX build failed with exit code $LASTEXITCODE"
}
foreach ($required in @(
(Join-Path $env:PHPX_HOME 'lib\phpx.lib'),
(Join-Path $phpxBuild 'phpx.dll')
)) {
if (-not (Test-Path $required)) {
throw "Required PHPX build output is missing: $required"
}
}
- name: Build tpc.exe
shell: pwsh
run: |
php bin\tpc.php project.yml --job 1 --no-progress
if ($LASTEXITCODE -ne 0) {
throw "TypePHP build failed with exit code $LASTEXITCODE"
}
if (-not (Test-Path 'tpc.exe')) {
throw 'The compiler did not produce tpc.exe'
}
- name: Run Windows smoke tests
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
$env:PATH = "$env:PHPX_HOME\build;$env:PATH"
& .\tpc.exe tests\windows\smoke\project.yml --job 1 --no-progress
if ($LASTEXITCODE -ne 0) {
throw "Windows smoke project compilation failed with exit code $LASTEXITCODE"
}
$smokeExe = Join-Path '${{ github.workspace }}' 'tests\windows\smoke\windows_smoke.exe'
if (-not (Test-Path $smokeExe)) {
throw "Windows smoke executable was not generated: $smokeExe"
}
$processInfo = [Diagnostics.ProcessStartInfo]::new()
$processInfo.FileName = $smokeExe
$processInfo.ArgumentList.Add('zts')
$processInfo.UseShellExecute = $false
$processInfo.RedirectStandardOutput = $true
$processInfo.RedirectStandardError = $true
$process = [Diagnostics.Process]::new()
$process.StartInfo = $processInfo
if (-not $process.Start()) {
throw 'Unable to start the Windows smoke executable'
}
$stdout = $process.StandardOutput.ReadToEnd()
$stderr = $process.StandardError.ReadToEnd()
$process.WaitForExit()
Write-Host "Windows smoke stdout: $stdout"
Write-Host "Windows smoke stderr: $stderr"
Write-Host "Windows smoke exit code: $($process.ExitCode)"
if ($process.ExitCode -ne 0) {
throw "Windows smoke executable failed with exit code $($process.ExitCode)"
}
if ($stdout.Trim() -ne 'windows-smoke-ok:zts') {
throw "Unexpected Windows smoke output: $stdout"
}
- name: Package tested Windows compiler
if: startsWith(github.ref, 'refs/tags/') && matrix.php == '8.5'
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
composer install --no-dev --prefer-dist --no-progress --classmap-authoritative
if ($LASTEXITCODE -ne 0) {
throw "Production Composer install failed with exit code $LASTEXITCODE"
}
$env:TYPEPHP_PACKAGE_VERSION = $env:GITHUB_REF_NAME
php package.php
if ($LASTEXITCODE -ne 0) {
throw "Windows packaging failed with exit code $LASTEXITCODE"
}
$packages = @(Get-ChildItem 'tpc_v*_windows_*.zip' -File)
if ($packages.Count -ne 1) {
throw "Expected one Windows release package, found $($packages.Count)"
}
- name: Upload Windows release package
if: startsWith(github.ref, 'refs/tags/') && matrix.php == '8.5'
uses: actions/upload-artifact@v4
with:
name: release-windows-x64-php-${{ matrix.php }}-zts
if-no-files-found: error
retention-days: 1
path: tpc_v*_windows_*.zip
- name: Upload Windows build outputs
if: always()
uses: actions/upload-artifact@v4
with:
name: tpc-windows-x64-php-${{ matrix.php }}-zts
if-no-files-found: warn
retention-days: 7
path: |
tpc.exe
tests/windows/smoke/windows_smoke.exe
tests/windows/smoke/build/**/*.cc
tests/windows/smoke/build/**/*.h
tests/windows/smoke/build/**/*.rsp

8
.gitignore vendored

@ -11,6 +11,7 @@
/.php-cs-fixer.cache /.php-cs-fixer.cache
/.phpunit.cache /.phpunit.cache
/__pycache__ /__pycache__
/composer.lock
*.o *.o
*.dll *.dll
*.so *.so
@ -21,7 +22,7 @@
*.lib *.lib
*.exp *.exp
*.class *.class
/swoole_compiler *.wasm
/tpc /tpc
tests/**/*.diff tests/**/*.diff
tests/**/*.exp tests/**/*.exp
@ -29,4 +30,7 @@ tests/**/*.log
tests/**/*.out tests/**/*.out
tests/**/*.php tests/**/*.php
tests/**/*.sh tests/**/*.sh
/.git-backup-20260803/ /*.browser/
/tests/wasm/harness/node_modules/
/ide-helper/
/debug/

@ -1,9 +1,9 @@
<?php <?php
$header = <<<'EOF' $header = <<<'EOF'
This file is part of Swoole-Compiler(AOT). This file is part of TypePHP(AOT).
@link https://www.swoole.com/ @link https://www.swoole.com/aot/
@contact service@swoole.com @contact service@swoole.com
EOF; EOF;

@ -1,76 +0,0 @@
# 2026-08-03 工作日志
## TypePHP 编译性能分析(docs/COMPILATION_PERFORMANCE_ANALYSIS.md)
**任务**:分析编译实现、提升编译速度、评估 mago 替代 AST 解析。
**编译管线**(4 阶段):
1. prepare:扫描+parse(#1)+符号表+拓扑排序(SourcePipelineTrait::prepare → Preprocessor::prepareFile)
2. convert:parse(#2,重复)+NodeTraverser+生成 C++(Translator::doConvert)
3. compile:PCH + pcntl 并行(Windows 串行)
4. build:链接
**实测瓶颈**(src/ 139 文件,php-parser 5.6.1):
- 解析吞吐仅 3.18 MB/s,单遍 parse+resolve ≈ 599ms
- **每个 PHP 文件解析两次**(prepare + convert)→ 纯 PHP 解析 ~1.2s 起步
- 生成 .cc 无增量缓存(只有 phpx misc 有),writeFile 无内容比对 → mtime 必变触发全量重编
- prepare/convert 串行;Windows 无 pcntl 串行编译
- AST serialize 体积膨胀 28.8 倍(47.6MB/1.65MB),unserialize 260ms vs parse+resolve 599ms(快 2.3 倍)
**提速方案**(按 ROI):
- S1(P0) prepare/convert 合并单次解析(AST 内存/磁盘缓存)
- S2(P0) 生成 .cc 增量对象缓存 + writeFile 内容比对(key 须含公共声明头)
- S3(P0) PCH/misc 指纹轻量化(mtime 先行,内容 hash 兜底)
- S4(P1) 解析阶段 pcntl 并行
- S5(P1) Windows 并行(Msvc /MP 或 proc_open)
- S7(P2) 合并多次 NodeFinder 全树扫描
**mago 评估结论:不建议替换**。mago 是 Rust 的 PHP 工具链(lint/format/analyze),AST 是 Rust 结构 + CST 模型,与 TypePHP 深度耦合的 php-parser API(296 处引用/75 文件,依赖 NodeTraverser/NameResolver/ConstExprEvaluator/PrettyPrinter/attribute 机制)不兼容。JSON 桥接或 FFI 的成本会吃掉解析速度优势。合理定位:前置语法预检或未来原生前端路线参考。
**产物**:docs/COMPILATION_PERFORMANCE_ANALYSIS.md(完整报告)、.workbuddy/bench_ast.php(可复现基准脚本)
## 实施 S1+S2(下午追加)
**S1 合并两次解析**:
- `CompilerBase` 新增 `parseCachedAst()`(按 `$this->file` 缓存原始 AST)+ `cloneAst()`(CloningVisitor 深拷贝)
- `prepareFile()``doConvert()` 都改用它 → 每个文件只 parse 一次
- 关键约束:`RuntimeAttributeFactoryLowering`/`Visitor` 是有状态 visitor(会向 AST 追加节点),**同一棵 AST 不能二次遍历**,必须返回深拷贝
**S2 增量缓存**:
- `writeFile()` 内容比对:相同不覆盖,返回 bool(mtime 稳定,防头文件抖动)
- 新增 `hasGeneratedObjectFileCache()`:key = sha256(编译命令 + PHP ABI + 生成的 func_decl.h/data_decl.h/_arginfo.h 内容 hash)
- `isGeneratedSourceFile()`:buildDir 前缀 + .cc 扩展判断
- `compileFile()` 对生成的 .cc 走缓存;`save()`/`genExtension()` 仅实际写入时 format
- 注意:extension-<target>.cc **不应**列入 header 依赖(内容随任何类变化,会让所有 .cc 全量失效)
**实测**(302 文件合成项目,dry,3 次均值):
- 全量:基线 6011ms → 改动后 5629ms(-6.4%)
- 增量(不清理 build,第 3 次):5023ms(-16.4% 相对基线冷构建)
- dry 不含 C++ 编译,S2 完整收益需在 build 场景验证
**环境/验证要点**:
- 本机完整编译需要 `PHP_HOME=D:/git/php/tpc_v1095_windows_x86_64`(含 php8embed.lib、SDK/lib/php8ts.lib、phpx/)+ 复制 `tpc_v1095/phpx.dll``phpx/build/phpx.dll`
- `tpc_v1095/tpc.exe` 是编译自 `D:\workspace\compiler` 的二进制,本机无法运行(内嵌路径不符)
- typephp-think(554 文件)补丁后 prepare 通过,convert 阶段 `Cannot re-assign` 是仓库源码类型系统比 v1095 正式版严格(`$str` array→str),非本次改动问题
- **git 事故**:git stash 失败损坏 refs(HEAD→speed_build 指向 a92fc0df 对象缺失)。已恢复:分支指向 18decac(可用 commit),onepiece-doudizhu 示例内容在暂存区完好。备份在 `.git-backup-20260803/`,确认后删除
## git 恢复与提交(下午追加 2)
- **git stash 事故根因确认**:stash 失败不仅丢 refs,还导致 onepiece-doudizhu-win32 全部 19 个文件的 blob 对象缺失(index 引用了不存在的对象)。工作区文件完好。
- **恢复方法**:`git reset -- <dir>` 取消暂存 → `git add <dir>`(从工作区重建 blob)→ commit。已恢复为 `449afc1 add onepiece-ddz game example`(4226 行,与原提交同名)。
- **S1/S2 已提交**:`ac7813d perf: cache AST across prepare/convert and add generated object cache`(3 文件 +206/-14)。
- **最终 git fsck = 0 missing,仓库健康**
- 遗留:`.git-backup-20260803/`(18MB,git 事故备份,确认后删)、`.workbuddy/onepiece-backup/`(文件级备份,可删)。
## code-wiki 文档生成(2026-08-04)
- 为 aot-compiler 生成了中文 AI Wiki:22 页,7 个章节(概览 / 前端解析 / 类型系统 / 代码生成 / 后端构建 / 优化性能 / 运行时边界)。
- **重要修正**:code-wiki skill 的文档写 `<workspace>/.agents/wiki/`,但 `scripts/render.py` 实际读取 `<workspace>/.workbuddy/wiki/`(`wiki_dir = os.path.join(workspace, ".workbuddy", "wiki")`)。写 catalog.xml / pages 必须放 `.workbuddy/wiki/`,否则 render 报 "catalog.xml not found"。
- 产物:`D:\git\php\aot-compiler\.workbuddy\wiki/index.html`(左侧目录导航 + 右侧 iframe,离线 Mermaid)。render 命令:`python <skill>/scripts/render.py render <workspace> --lang zh`。
## 环境结论(重要)
- **本机 MSVC 工具链不完整**:cl.exe + C++ 标准库头存在(`C:/Program Files (x86)/Microsoft Visual Studio/2022/BuildTools/VC/Tools/MSVC/14.44.35207/include/cstring`),但 **Windows SDK(Windows Kits 目录)完全缺失** → 无法完成真实 C++ 编译链接,只能验证到 dry 模式(prepare+convert+生成 .cc)。
- `tpc_v1095_windows_x86_64` 是完整 PHP 构建环境:php8embed.lib、SDK/lib/php8ts.lib、phpx/(含 lib/phpx.lib);phpx.dll 需复制到 `phpx/build/phpx.dll` 才能通过校验。
- 完整编译需在装有 Windows SDK 的机器执行(用户原编译机)。

@ -1,154 +0,0 @@
# 2026-08-04
## code-wiki 渲染路径修正
- 用户将 code-wiki skill 的 render.py 渲染目录从 `.workbuddy/wiki` 改为 `.agents/wiki`
- 实测发现:render.py 的**文档注释**改了,但 `render()`(第243行)与 `browse()`(第328行)函数体仍硬编码 `.workbuddy`。已把这两处改为 `.agents` 并重新渲染成功。
- 结论:code-wiki 产物目录现统一为 **`.agents/wiki/`**(catalog.xml 在 `.agents/wiki/data/`,页面 md 在 `.agents/wiki/pages/`)。下次 render 直接 `python render.py render <ws> --lang zh`,无需再复制。
- 重新渲染结果:`[done] rendered 22 pages -> .agents/wiki/index.html`。
## 修复 Windows 资源编译 RC2135
- 报错:`app_resource.rc(1) : error RC2135 : file not found: by`(资源编译阶段)。
- 根因:`src/Build/ResourceCompilationTrait.php` 写 rc 文件时强制加了 UTF-8 BOM(`"\xEF\xBB\xBF" . $rcContent`),而 `src/Generator/ResourceFileGenerator.php` 生成的注释用 `//`。BOM 顶在文件头导致 rc.exe 第1行识别不出 `//` 注释,把 `Generated by TypePHP` 当语句、`by` 当文件名 → RC2135(老版/部分 Windows SDK 的 rc.exe 有此坑)。
- 修复:① 去掉 BOM(直接写 `$rcContent`);② 生成内容里的 `//` 注释全改为 `;`(rc 全版本支持的注释符)。中文靠 `#pragma code_page(65001)` 正常处理,无需 BOM。`.h` 头文件里的 `//` 不动(rc 不处理)。
- 重要:此修复需**重新编译 tpc.exe** 才生效——当前运行的预编译 `tpc.exe` 每次构建都会按自身逻辑重新生成 rc 文件,改 build/app_resource.rc 不持久。
- 临时绕过(不重编也能跑通):把 project.yml 的 `resource:` 整段(第10~24行 icon + version-info)注释掉,`hasResource()` 返回 false,资源编译被跳过,可先验证后续链接;编好带修复的 tpc.exe 后再恢复该段即可重新带图标/版本。
- 已应用临时绕过:project.yml 的 `resource:` 整段已改为全注释(带"待带修复的 tpc.exe 重新编译后恢复"标记)。用户可立即重跑 tpc.exe 验证链接链路。
## LNK1104 链接失败(输出 tpc.exe 被锁)
- 报错:`LINK : fatal error LNK1104: 无法打开文件"tpc.exe"`(乱码是 GBK 被当 UTF-8 解码的"无法打开文件")。
- 根因:project.yml `name: tpc` → 输出 `tpc.exe`;`name` 无路径 → `outputDir` 为空,TypePHP 把最终 exe 输出到 cwd 相对路径(`getTargetFileName()` 返回 `'tpc.exe'`,`/OUT:"tpc.exe"`)。用户从编译器自身目录 `D:\git\php\tpc_v1095_windows_x86_64` 启动 tpc.exe(cwd=该目录),于是 `/OUT:"tpc.exe"` 落到正在运行的 `D:\git\php\tpc_v1095_windows_x86_64\tpc.exe` 上,系统拒绝改写正在执行的 exe → LNK1104。
- 佐证:无残留 tpc 进程;`build\tpc.exe` 不存在;`build\` 可写;中间产物 rc 在 `tpc_v1095/build/` → cwd 即编译器目录。
- 修复:从项目目录 `D:\git\php\aot-compiler` 启动编译器(cd 过去再用绝对路径调 tpc.exe),输出落到 `aot-compiler/tpc.exe`,避开与运行中编译器同名冲突。备选:给 project.yml 换输出名/输出目录(不建议改 `name`,会影响生成符号)。
## tpc.exe 编译 hello.php 时 OOM(VirtualAlloc 失败)
- 现象:`.\tpc.exe examples/hello.php` 跑到打印第3条 cl 命令(typephp_fiber_generator.cc)后,`VirtualAlloc() failed [0x8 内存资源不足]` x2,随后 `Fatal error: Out of memory (allocated 8388608) (tried to allocate 140736835400432 ≈128TB) in src/compiler.php on line 4`
- 诊断结论:**不是机器内存不够**。实测本机 96GB 物理内存(空闲 74GB)、页面文件空闲 81GB、20 核。`VirtualAlloc` 拒绝是因为请求了一个被算坏的 ~128TB 尺寸(符号扩展/未初始化),OOM 报告把这个坏尺寸打印出来——属症状非根因。
- Windows 上 `Platform/Windows::supportsPcntlParallelCompile()` 返回 false(`Windows.php:226`),`Translator.php:1441` 走 `compileSourceFile()` **顺序编译**(`passthru` 逐个跑 cl),**不并行**,`-j` 在此无效。
- 隔离测试:`--dry`(仅 prepare+convert 生成 C++)能干净退出(exit 0,不 OOM)→ bug 只在 **compile/build(链接)阶段**。源码 `compiler.php:8``ini_set('memory_limit','-1')`,且报错走的是 Zend MM "out of system memory" 路径(OS 拒 VirtualAlloc),非撞 memory_limit。
- 诡异点:该 tpc.exe 能编译整个 tpc 工程(project.yml)却编不了 151B 的 hello.php → 疑似"单文件 .php→exe"模式的专属 bug(parseArgv 对单文件默认也是 BUILD_MODE_BIN,与 project.yml 同模式,故非 buildMode 差异)。
- 待办/建议给用户:① `.\tpc.exe project.yml -O2 -j 8`(从 aot-compiler 目录)确认工程模式仍正常,隔离是否单文件模式专属;② 用 v1095 预编译 `tpc.exe``examples/hello.php` 对比,判断是"当前源码回归"还是"v1095 误编译";③ 若需进一步定位,查单文件 build/link 路径里计算分配尺寸的代码(读取文件/字符串长度处)。
## 确认:崩的是"S1+S2 优化版",v1095 基础版不崩
- 用户确认:`tpc_v1095`(基础版,未做编译速度优化)编译 `examples/hello.php` **不崩**;刚编出来的 tpc.exe(带 S1+S2:ac7813d)崩 128TB。→ 回归在 S1+S2,或 v1095 误编译了 S1+S2 源码。
- S1+S2(ac7813d)只改 3 文件:`CompilerBase.php`/`Preprocessor.php`/`Translator.php`。compile 阶段新增代码**只有** S2 的 generated 缓存:`hasGeneratedObjectFileCache`/`getGeneratedObjectCacheKey`/`getGeneratedHeaderDependencies`/`isGeneratedSourceFile`/`compileFile` 的 `$isGenerated` 分支。
- 已逐行审 S2 缓存代码:均读小 metadata、算 sha256、拼字符串,**无直接大块分配**;且前两个 generated 文件(hello.cc/extension-hello.cc)已成功跑过缓存检查+编译(日志里 cl 命令已打印),说明新函数本身能跑。
- **S2 设计缺陷(确定要修)**:`getGeneratedHeaderDependencies()` 用 `FilesystemIterator` 扫描整个 `build/include` 收集所有 `*_arginfo.h`。但 `build/include` 被**上一次 tpc 工程编译的残留污染**(含 `php_src_CompilerBase_arginfo.h` 423KB、`php_tpc_func_decl.h`、`php_src_Translator_arginfo.h` 等几十个 tpc 工程头)。后果:① 编译 hello.php 时缓存 key 竟依赖无关 tpc 工程头文件(增量缓存正确性错误);② 每个 generated 单元算 key 都要 `hash_update_file` 这些大残留头,浪费且可能触发异常路径。
- **本机 Bash 无法复现**:缺 vcvars/cl 时该 AOT 二进制静默 exit 0 无输出,必须在用户带 VS 环境的命令行里测。
- 给用户的两个隔离测试(在 VS 命令行执行):
- 测试1:`.\tpc.exe examples/hello.php --force`(关掉两套缓存检查)。若通过→缓存路径是触发点。
- 测试2(若测试1通过):`rmdir /s /q build` 后 `.\tpc.exe examples/hello.php` 跑两次。若第1次(缓存miss)过、第2次(缓存hit,完整算key并 hash_update_file 污染头)崩→bug 在缓存命中路径的 key 计算;若第1次就崩→在 compile/link 本身(更可能是 v1095 误编译)。
- **用户实测测试2:清空 build 后首次(缓存 miss)就崩** → 直接排除"缓存命中路径"(缓存命中路径只在第2次才触发 `hash_update_file` 扫描污染头;首次 `$objectFile` 不存在,`hasGenerated/hasMiscObjectFileCache` 在 1215/1123 行 early-return false,根本走不到 key 计算)。
- 进一步逐行审查首次 compile 路径全部可能大分配点:
- `writeFile` 改返回 bool:7 处调用(gen_stub/ResourceCompilationTrait/Translator 498/663/736/1100/1785)均安全,无副作用。
- `parseCachedAst`/`cloneAst`:仅 prepare/convert 用,`--dry` 已验证正常。
- `compileFile` 缓存 `if` 块(1354-1360):首次恒 false,benign。
- `NativeBuilder::compile()`(31-41):Windows 顺序模式走 `passthru($command,$status)`,**不把 cl 输出累积进数组**,无"读输出撑爆"路径。
- `compileSourceFile` 循环(1489-1532):每文件 `compileFile(false)` → miss → `compile()` → passthru 跑 cl → 打印命令。前3个文件成功(cl 命令已打印=status 0),崩在第4个文件进入时。
- **最终结论(高置信)**:S1+S2 在首次 compile 路径上没有任何大分配来源,128TB 在 PHP 源码层无出处;而 v1095 基础版编 hello.php 正常、我们的版本崩 → **判定为 v1095 编译器对我们当前源码(含 S1+S2)的误编译(miscompilation)**:v1095 在 AOT 编译改动后的 `Translator.php` 时,把 `compileFile`/编译循环邻近某处 size 符号扩展/算坏(典型 ~2⁴⁷)。`--dry` 不进 compile 循环故不触发。
- **决定性验证 = Bootstrap 自举**:用当前 tpc.exe(它编工程成功,编 hello.php 崩)重编 tpc 工程得自举版 tpc.exe-B,再用它编 hello.php。
- 通过 → 确认 v1095 误编译,自举换了编译上下文/偏移绕开,后续用自举版做 typephp-think 速度对比即可。
- 仍崩 → 源码确有隐藏 bug,需在 `compileFile` 入口加 STDERR 文件名日志 + `NativeBuilder::compile` 前后标记,再编一次精确定位第4个文件及崩因。
- 待修(不影响崩溃):`getGeneratedHeaderDependencies()` 通配 `*_arginfo.h` 会纳入 build/include 残留 tpc 工程头(`php_src_*`),应限定为当前 `$this->targetName` 前缀(`str_starts_with($name,'php_'.$targetName.'_') && str_ends_with($name,'_arginfo.h')`)。等自举成功后再单独修并验证,避免重编混入变量。
## Bootstrap 自举也崩 → 推翻"纯误编译"早期判断
- 用户实测:`.\tpc.exe project.yml -O2 -j 8`(用我们的 tpc.exe 重编工程,即自举)**同样崩在 typephp_fiber_generator.cc**,与基础版崩溃签名完全一致(2⁴⁷)。
- 关键修正:自举崩**不能区分**"v1095 误编译"与"源码真 bug"——因为自举二进制由我们的 tpc.exe(本身可能已被 v1095 误编译)再编一次,缺陷会继承传递。所以自举崩对两种假设都"符合",不是决定性证据。
- 进一步精读定位:
- `getLanguageFromExtension('.cc')` 返回 **null** → misc 文件走 `getCompileCommandOptions()` → 合法 `cl /c` 命令(解释了日志里 misc 文件没抛 RuntimeException,而是正常打印 cl 命令)。
- `getMiscObjectCacheKey()` 只做 `hash('sha256', buildCompileFileCommand(...) . "\0" . serialize($abi))`;而 `buildCompileFileCommand`(generated 文件也用)在 generated 路径已验证正常 → **misc 专属 metadata 写入路径不是 2⁴⁷ 来源**
- 仓库真实状态:HEAD=ac7813d(S1+S2);工作树仅我的 RC 修复 + project.yml 注释,无其它碰编译链路的改动。
- S1 是前端 AST 缓存(misc 文件不是 PHP,根本不进前端)→ 已排除。S2 是唯一落在 compile 阶段的改动。
## 二分开关:已禁用 S2 缓存逻辑(compileFile)
- 在 `src/Translator.php``compileFile()` 里,把 S2 增量对象缓存逻辑**整段去掉**(缓存命中跳过 + `invalidateMisc/GeneratedObjectCache` + `writeMisc/GeneratedObjectCacheMetadata`),退回 S2 之前的纯编译行为。加了 `// [bisect]` 注释标记,定位清楚后删除。
- 目的:若用 v1095 重新编译(带此开关的源码)后 `.\tpc.exe examples/hello.php` **不再崩** → 坐实"S2 那段代码被 v1095 AOT 误编译",下一步改写 S2 避开该结构(如去掉 SPL `RecursiveIteratorIterator`、改用更简单 size 计算)。
- 若仍崩 → 不是 S2,需扩大范围(revert 全部 S1+S2、或排查 S1+S2 之外的更早提交/phpx 依赖更新 2129145)。
- 用户重编命令(必须用 v1095,因为我们的 tpc.exe 也会崩,不能用来重编):
- 从 `D:\git\php\aot-compiler` 目录:`D:\git\php\tpc_v1095_windows_x86_64\tpc.exe project.yml -O2 -j 8`(RC 修复已在源码、resource 已注释,且无输出冲突 → 可编译+链接成功)。
- 再测:`.\tpc.exe examples/hello.php`。
## 坐实:S2 触发 v1095 误编译 + 已保守化重写
- 用户实测:用 v1095 重编"二分开关版"(S2 在 compileFile 的调用整段去掉)源码,产出的 `tpc2.exe``examples/hello.php` **不再崩,成功产出 hello.exe**(7 文件编译+链接全过)。→ **坐实 S2 那段增量对象缓存代码被 v1095 AOT 误编译**(去掉调用即不再触发 2⁴⁷)。S1 已确认安全(--dry 正常)。
- 首次编译实际会执行的 S2 高危路径:`writeGeneratedObjectCacheMetadata` → `getGeneratedObjectCacheKey`。该函数原版用了 `hash_init`/`hash_update`/`hash_update_file`/`hash_final` 哈希**资源流**,并调用 `getGeneratedHeaderDependencies()`(含 `FilesystemIterator` SPL 迭代器)。这两类结构在 v1095 AOT 编译下最易被误编译成坏 size(2⁴⁷ 符号扩展)。`hasMiscObjectFileCache` 原版也含 `RecursiveIteratorIterator`/`RecursiveDirectoryIterator`。
- **已重写 S2 为保守版**(src/Translator.php),消除全部高危结构:
1. `getGeneratedObjectCacheKey`:去掉 `hash_init/update/update_file/final` 流,改用 `hash('sha256', implode("\0", $parts))` 单次 + 每个依赖头 `hash_file('sha256', $header)` 单次。
2. `getGeneratedHeaderDependencies`:去掉 `FilesystemIterator`,改用 `scandir` 一层遍历;并把 `*_arginfo.h` 通配**限定为当前 target 前缀**(`str_starts_with($name,'php_'.$targetName.'_')`),顺手修了"扫描整个 build/include 含 tpc 工程残留头"的设计缺陷。
3. `hasMiscObjectFileCache`:去掉 `RecursiveIteratorIterator`,新增 `collectHeaderFiles()`(scandir 递归,无 SPL 迭代器)替代。
4. `getMiscObjectCacheKey` 保持原 `hash('sha256', ...)` 单次调用(已验证安全)。
5. 恢复 `compileFile` 里的 S2 调用(缓存命中跳过 + invalidate + writeMetadata),删除二分 `[bisect]` 标记。
- 验证:`php -l src/Translator.php` 无语法错误;Grep 确认 Translator.php 内已无 RecursiveIteratorIterator/RecursiveDirectoryIterator/FilesystemIterator/hash_init/hash_update/hash_update_file/hash_final 残留。
- **未动的同类结构**:`FileScanner.php`、`Build/PrecompiledHeaderManager.php`、`gen_stub.php` 仍有 SPL 迭代器 + hash 资源流,但**不是本次崩溃元凶**(v1095 编整个工程时这些文件都被正常 AOT 编译且没崩),保持最小改动原则暂不碰。
- **下一步验证(用户执行,必须用 v1095 重编,因为我们的 tpc.exe 自己也崩)**
```
cd /d D:\git\php\aot-compiler
D:\git\php\tpc_v1095_windows_x86_64\tpc.exe project.yml -O2 -j 8 # 产新 tpc.exe:含保守版S2 + 源码内修复版ResourceFileGenerator(自身无图标,因resource注释)
rmdir /s /q build
.\tpc.exe examples\hello.php # 第一次:应不崩出 hello.exe(验证S2修复)
.\tpc.exe examples\hello.php # 第二次:应命中缓存([cache] skip),更快(验证S2加速)
```
- 若第一次仍崩 → 误编译源不在已改这几处,需更细二分(如逐个恢复 hasMisc/hasGenerated 与 write*Metadata)。
- 若两次都过 → S2 修复生效。之后可恢复 project.yml 的 resource 段,用新 tpc.exe 编 project.yml 验证 RC2135 也修好且带图标。
## 关键反转:保守化重写的 S2 仍崩 128TB → 判定为 v1095 跨函数误编译
- 用户实测:用 v1095 重编"保守版 S2"(去 SPL 迭代器 + 去 hash 资源流,compileFile 已恢复 S2 调用)源码,产出的 `tpc2.exe``examples/hello.php` **仍然崩 128TB**,崩溃签名与原始版完全一致(`tried to allocate 140736835400432 ≈2⁴⁷`)。
- 这**推翻了"是 SPL 迭代器 / hash 资源流被误编译"的假设**。进一步定位:崩溃发生在**首次编译** `hello.cc`(generated 文件)打印 cl 命令后、进入 `getNativeBuilder()->compile()` 时。首次编译对 `hello.cc` 实际执行的 S2 代码只有 `isPhpxMiscFile()`、`isGeneratedSourceFile()`、`hasGeneratedObjectFileCache()`(obj 不存在→early-return false)、`invalidateGeneratedObjectCache()`;而我新写的保守 `hash()`/`hash_file()` 此时**还没轮到**(它在编译后才写的 metadata 里)。→ 崩点根本到不了哈希,说明它不是某个 helper 的内部写法问题。
- **最终判定:v1095 在 AOT 编译 `compileFile` 里那段 S2 包裹代码时,连带把后续 `getNativeBuilder()->compile()` 编坏了**(典型跨函数误编译:A 函数误编→B 函数运行时崩)。窄重写 helper 救不了,因为触发点是"这段 S2 代码被编译/被调用"这件事本身,而不是具体哪行语句。
- **这是 v1095 编译器(AOT)的 bug,不是我们源码的逻辑 bug**。在源码层面做局部改写无法可靠规避。
- **决策:禁用 S2**(从 `compileFile` 移除全部 S2 调用,退回纯编译态,等同于已证明稳定的 bisect 版),保留 S1(合并两次解析,主要提速点,已确认安全)。S2 的 helper 方法(hasMisc/hasGenerated/getXxxCacheKey/writeXxxMetadata/collectHeaderFiles)作为"死代码"**保留**(已证明:定义但不被 compileFile 调用就安全),待编译器修复后复用。
- 当前 `compileFile`(src/Translator.php ~1378)已加注释明确 S2 禁用原因;`php -l` 通过。
- **后续可选项(暂不阻塞)**
1. 用 v1095 重编当前源码(S1 开、S2 关)→ 应稳定,可正常编 hello.php 与 project.yml。
2. 想保留 S2 加速的可试验:用**更低优化级别**(如 `project.yml -O1` 而非 `-O2`)重编 tpc.exe。若 v1095 的误编译是优化级别相关,低级别可能绕开 → 届时再恢复 compileFile 的 S2 调用测试。但 -O1 会让 tpc.exe 自身变慢,需权衡。
3. 向 v1095 上游报告该 AOT 误编译(含最小复现:带 S2 包裹的 compileFile → 2⁴⁷ VirtualAlloc)。
- 当前 HEAD 仍为 ac7813d 之上的未提交改动(RC2135 修复 + S2 禁用 + resource 段已注释)。待用户验证稳定后,建议提交 S1 部分(已稳定)并将 S2 作为"被编译器 bug 阻塞"的待办保留。
## typephp-think 编译报 Duplicate class(重复类 fatal)
- 现象:`.\tpc.exe`(S1 开/S2 关的稳定版)编译 `D:\git\php\typephp-think` 时,`Preprocessor->prepareFile` 报 `Duplicate class think\exception\ClassNotFoundException`(首次撞在 `think-container/src/exception/ClassNotFoundException.php`)。
- **不是 S1/S2 引入**:`Preprocessor.php:697` 的重复检测 `isset($this->symbolDeclInFile[...])` 是 prepare 阶段的**全局符号表**(第733行赋值、无 resetClass 重置、ac7813d 未动过它)。我们的编译器对跨文件重名类是全局 fatal,比 v1095(别的电脑能编过的预编译版)更严格 → 这正是"别的电脑能编过"的原因(v1095 容忍重复类)。
- **根因 = 工程的重名类 + 补丁机制不会解决它们**:`typephp-think` 有 `patches/` 目录镜像 `vendor/`,由 `patch.php`(`copyPatchesSafely()`)在 `composer post-autoload-dump` 时把 `patches/*` **覆盖拷贝**到 `vendor/*` 并写 `.patches_applied`。但 `patches/topthink/` 只含 `framework/think-dumper/think-filesystem/think-orm/think-trace`,**不含 think-container**;覆盖拷贝不会删文件。所以重名类依然存在。用户"今天只更新代码没打补丁"使 vendor 回到未打补丁态(需重跑 `php patch.php`)。
- **vendor/ 内真实重名类只有 4 个**(编译器实际会撞的;patches/vendor 成对的不算,因编译器不扫 patches/):
1. `think\exception\ClassNotFoundException`:framework vs **think-container**(两份字节一致,删 think-container 那份安全)。
2. `think\Exception`:framework vs **think-orm/stubs**/Exception.php(stub,删 stub 安全)。
3. `think\Facade`:think-container vs **think-orm/stubs**/Facade.php(stub,删 stub 安全)。
4. `think\route\Dispatch`:framework 自身两份(`route/Dispatch.php` 规范基类 vs `route/dispatch/Dispatch.php` 命名空间写错成 `think\route`、全工程无引用 `think\route\dispatch\Dispatch` → 死代码,删后者安全)。
- **决策:放宽编译器重复检测,而非删 vendor**(为公平对比:v1095 容忍重复类,我们的 tpc.exe 也应如此,避免改输入导致 our-tpc vs v1095 不对等)。改 `Preprocessor.php`
- `prepareClass`(~697):重复时 `$this->warning(...)` + `resetClass()` + `return ''`(不再 fatal,保留首个声明)。
- `parseInterface`(~1507):重复时 `$this->warning(...)` + `return;`(不再 fatal)。
- warning 方法签名 `warning(Node $node, string $msg): void`(CompilerDiagnosticTrait)。`php -l` 通过。
- 验证:重编 tpc.exe(v1095)→ 编 typephp-think,应越过该 Duplicate class 继续。若再撞别的重名,说明还有未覆盖的 vendor 内部重复(再扫描确认)。
- 备选(不推荐):直接删上述 4 个冗余 vendor 文件。缺点:composer 会还原 + 让我们与 v1095 的输入不一致,破坏速度对比公平性。
## typephp-think 编译报 Cannot re-assign $str(string→array 类型冲突 fatal)
- 现象:`.\tpc.exe` 编 `typephp-think` 走到 `symfony/var-dumper/Dumper/CliDumper.php:207` 时,`Preprocessor/Translator` 在 convert 阶段 fatal:`Cannot re-assign $str from php::Array to php::Str`。该行是 `dumpString(Cursor $cursor, string $str, bool $bin, int $cut)``$str = $bin && str_contains($str,"\0") ? [$str] : explode("\n",$str);` —— 形参 `string $str` 先当字符串用,后在 207 行被赋值为数组。
- **根因 = 我们的编译器对局部变量/形参做了"单静态类型"假设**:`AssignOpTrait::parseAssignFinally`(~337)对已存在变量调用 `checkVarAssignExpr($left, 现有类型, 新值类型)`;`CompilerBase::checkVarAssignExpr` 对"不兼容"的两次赋值直接 fatal。但 PHP 是动态类型,变量中途换类型是合法的——v1095(别的电脑能编过的预编译版)对此类变量用运行时 variant(`php::Var`)承载,所以不崩。我们之前对 vendor 的容忍(Duplicate class→warning)没覆盖到这种"标量→数组"类型变更。
- **关键正确性约束**:不能简单把 fatal 降级为 warning——否则 C++ 里 `php::Str str;` 后面接 `str = <数组>;` 会是类型错误甚至**静默误编译**。正确做法是:被重新赋以不兼容类型的变量,从一开始(声明处)就声明为运行时 variant `php::Var`
- **修复(src 三处)**
1. `CompilerBase.php`:新增 `protected array $variantVars = [];`(resetFunction 里清空);新增 `areTypesAssignable($existing,$new): bool`,与 `checkVarAssignExpr` 的兼容规则逐字一致(VAR/REF/同类型/双 native/big 类型 都兼容,其余冲突),仅返回 bool 不 fatal,供预扫描判定。
2. `Translator.php`:新增 `computeVariantVars()` + `collectAssignTypes()/collectAssignTypesNode()`(递归走函数体,跳过嵌套 function/closure/arrow,收集每个简单变量赋值 RHS 的 detectTypeOfExpr 类型,并以形参 argInfo->type 作种子);`parseFunction()` 在 SSA 之后调用它,若某变量类型集合存在不兼容对 → 记入 `variantVars`,并把这些形参的 `argInfo->type` 改为 `php::Var`、重拼 `functionDef->params`(用 `genArgumentDeclaration`)。
3. `Parser/AssignOpTrait.php`:`parseAssignFinally` 首次声明局部变量时,若 `$var``variantVars` 中 → `finalVarType = Type::VAR`(声明即 variant),后续赋值走 variant 路径、`checkVarAssignExpr` 因 VAR 提前返回,不再 fatal。
- **兼容性**:决策逻辑与旧 `checkVarAssignExpr` 等价——原来能通过的不兼容赋值(int↔float、同类型重复、含 VAR/big)仍不触发;原来会 fatal 的(str↔arr、str↔int 等)现在正确升级为 variant,而非静默误编译。已用独立脚本验证决策矩阵。
- **限制**:预扫描只覆盖 `Function_`/`ClassMethod`(走 parseFunction);`Closure`/`ArrowFunction` 走 `parseStmts` 不经过 computeVariantVars,其形参若中途换类型仍可能 fatal(vendor 里少见,遇到再补)。`php -l` 三个文件均通过。
- **验证状态**:本机无法端到端重编验证——缺 PHP/Zend 头文件(php.h 不存在),且当前 `PHP_HOME=D:\git\php\tpc_v1092_windows_x86_64`(陈旧残留,非 PHP 安装)导致 `detectPhpLibs` 找不到 php8ts.lib。用户的真实构建环境(含 PHP dev 头 + 正确的 PHP_HOME)在另一台机器,已多次成功重编。须由用户执行重编+重跑验证(见下条命令)。
- **用户重编+重跑序列(必须用 v1095,因我们的 tpc.exe 自身也需它编译;S2 已禁用故稳定)**
```
cd /d D:\git\php\typephp-think && php patch.php
cd /d D:\git\php\aot-compiler
set PHP_HOME=<用户正确的 PHP TS dev 安装目录> # 例 D:\Program Files\PhpWebStudy-Data\app\php-8.3.24-ts(且其 dev\php8ts.lib 需在 $phpDir/lib 或 $phpDir 下,必要时拷贝)
D:\git\php\tpc_v1095_windows_x86_64\tpc.exe project.yml -O2 -j 8
rmdir /s /q build
.\tpc.exe examples\hello.php # 确认自举稳定
cd /d D:\git\php\typephp-think
D:\git\php\aot-compiler\tpc.exe project.yml -O2 -j 8 # 应越过 Duplicate class 与 Cannot re-assign,继续编译
```
- **注意**:本机实测 `php-8.3.24-ts` 根目录有 `php8embed.lib`、但 `php8ts.lib` 只在 `dev/` 子目录;`detectPhpLibs` 只查 `$phpDir/$phpDir\SDK\lib/$phpDir\lib`,故需把 `dev\php8ts.lib` 拷到 `php-8.3.24-ts\lib\`(或根)才能让 v1095 的 tpc.exe 找到核心库——这是用户环境的常规布局操作。

@ -1,103 +0,0 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Overview
TypePHP is a PHP native compilation project. Its `tpc` command is TypePHP Compiler (AOT), which translates PHP source code to C++, then compiles it with GCC/Clang/MSVC into native binaries. It supports Linux (primary), macOS, and Windows.
**Prerequisites**: PHP 8.2+, GCC 9+ (C++17), CMake 3.24+. The `swoole/phpx` extension must be compiled (see README.md).
## TypePHP Language Design Principles
TypePHP should not blindly mirror every PHP language behavior. Most PHP syntax and semantics should remain compatible with ZendPHP, but some legal PHP constructs are historical baggage or language-design mistakes that conflict with static compilation, clear semantics, or robust generated C++ code.
When reviewing or changing compiler behavior:
- Prefer PHP compatibility for common, well-defined syntax that does not weaken the TypePHP static model.
- Reject PHP historical baggage when the syntax is ambiguous, surprising, or only preserved for legacy compatibility.
- Diagnose such cases as early as possible during preprocessing/static compilation, instead of deferring to runtime TypeCheck or ZendVM errors.
- Provide precise errors that include the relevant function/method name, parameter/property name, and type information where applicable.
- Compare with other statically compiled languages such as C/C++, Java, C#, Go, Rust, Kotlin, and TypeScript before deciding whether TypePHP should preserve or reject a PHP behavior.
Example: `function test($a = 1, $b, $c) {}` is legal in PHP, but the default value for `$a` is effectively ignored and all parameters become required. This is a PHP historical compatibility artifact. TypePHP should reject it during preprocessing instead of preserving the behavior.
Example: PHP permits `return $value;` inside `__construct()` and lets callers consume `parent::__construct()` as a value, even though constructors cannot declare a return type. TypePHP treats constructors consistently with C++/Java-style semantics: constructors initialize objects and must not return values. `return;` is allowed, but `return $value;` or using a constructor call as a value must be rejected during static compilation.
## Build & Test Commands
```bash
# Install PHP dependencies
composer install
# Compile a PHP project to a native binary
./tpc <path-to-project-or-file>
# Run all PHPUnit tests
./vendor/bin/phpunit
# Run a single PHPUnit test class
./vendor/bin/phpunit phpunit/src/AstNodeTypeTest.php
# Run PHPT integration tests (all)
php run-tests.php tests/compiler/
# Run a single PHPT test
php run-tests.php tests/compiler/arrays.phpt
```
## Architecture
### Translation Pipeline
The compiler follows a 4-stage pipeline, orchestrated by `src/Translator.php` (the main entry point):
1. **prepare()** — Scan PHP files, collect symbol declarations and dependencies, topological-sort for compilation order
2. **convert()** — Parse PHP AST via `nikic/php-parser`, translate each node to C++ source code
3. **compile()** — Invoke the platform C++ compiler (GCC/Clang/MSVC) on generated `.cc` files
4. **build()** — Link object files into a native binary executable
### Class Hierarchy
```
src/CompilerBase.php (core PHP→C++ translation logic, indent/output/mode helpers)
├─ uses traits: AstNodeType, FuncCallOptimizer, ClosureGenerator,
│ PlaceHolderGenerator, PropertyPromotion, MagicMethodDetector
└─ src/Preprocessor.php (scanning, symbol tables, dependency sort, YAML config)
└─ src/Translator.php (full pipeline: prepare→convert→compile→build)
└─ src/CompilerTest.php (test-only subclass, used by PHPUnit tests)
```
### Key Components
| Directory | Purpose |
|-----------|---------|
| `src/Entity/` | Data classes: `ClassDef`, `FunctionDef`, `MethodDef`, `PropertyDef`, `ConstantDef`, `InterfaceDef` |
| `src/Generator/` | Codegen helpers: `ClosureGenerator`, `PlaceHolderGenerator`, `PropertyPromotion`, `Utils` |
| `src/Backend/` | Compiler abstraction: `CompilerBackend` (abstract) → `Gcc`, `Clang`, `Msvc`. Factory pattern via `CompilerFactory` |
| `src/Platform/` | OS abstraction: `PlatformBase``Linux`, `Macos`, `Windows`. Factory via `PlatformFactory` |
| `src/Context/` | `ScopeContext` and `FunctionContext` for variable scoping and type tracking |
| `src/Exception/` | `SyntaxError`, `Unsupported`, `DynamicCall`, `PlaceHolder`, `Skip`, `Redo`, `TestError` |
| `src/Parser/` | Special-purpose parsers like `StdContainerParser` (C++ std container foreach support) |
| `src/Resolver/Reflection.php` | Static helpers wrapping PHP reflection (internal class/function detection) |
| `src/Generator/Symbol.php` | Maps PHP operations to `phpx` C++ API symbol names |
| `src/Build/FileScanner.php` | Recursive file discovery with extension filtering (supports `.php`, `.cpp`, `.c`, `.s`, `.m`, `.mm`) |
| `src/Entity/ArgInfo.php` | Generates C function argument info structures for internal function registration |
| `src/Extractor.php` | Extracts interfaces from PHP classes |
| `src/Transform/Visitor.php` | Base `NodeVisitorAbstract` extension (skeleton for custom AST visitors) |
### Configuration
- `project.yml` — per-project build config (name, build-mode, C++ standard, compiler flags, sources, resources/icon)
- Command-line arguments and YAML config are merged in `Preprocessor`, with CLI taking highest priority
### Generated Output
Generated `.cc` and `.o` files land in `build/` directory. The compiled binary is named from `project.yml`'s `name` field (default: `app`).
### Test Infrastructure
- **PHPUnit tests** (`phpunit/src/`) — unit/integration tests for compiler internals. Bootstrap at `phpunit/bootstrap.php` defines a `BaseTest` class with an `exec()` helper that runs the compiler and expects a `TestError` exception containing a given string
- **PHPT tests** (`tests/compiler/`) — end-to-end tests using the standard PHPT format (`run-tests.php`). Each `.phpt` contains PHP source and expected output sections
When writing new compiler tests, use `CompilerTest::create(ROOT_PATH)` (in `src/CompilerTest.php`) which sets `forTest = true` to enable test-specific behavior without writing files to disk.

@ -0,0 +1,674 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2026 上海识沃网络科技有限公司. <https://www.swoole.com/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<https://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<https://www.gnu.org/licenses/why-not-lgpl.html>.

@ -1,53 +0,0 @@
## Swoole-Compiler Native AOT 编译器预览版使用须知
**请在使用本软件前仔细阅读以下条款:**
### 一、软件状态说明
1. 当前版本为 **预览版(Preview Version)**,仅供测试、评估和学习使用
2. 本版本可能存在未知的缺陷(BUG)、功能不完善、性能不稳定等问题
3. 软件的功能、API 接口、编译行为等可能在后续版本中发生重大变更,恕不另行通知
### 二、使用限制
**严禁将预览版用于以下场景:**
- ❌ 生产环境(Production Environment)
- ❌ 商业项目的正式部署
- ❌ 关键业务系统
- ❌ 涉及用户数据安全的应用
- ❌ 任何对稳定性、可靠性有严格要求的场景
**建议使用场景:**
- ✅ 本地开发测试
- ✅ 技术评估与验证
- ✅ 性能基准测试
- ✅ 学习研究目的
### 三、责任限制
1. **使用风险自负** - 用户明确知晓并接受使用预览版软件可能带来的所有风险
2. **无质量保证** - 开发团队不对软件的适用性、稳定性、安全性做任何明示或暗示的保证
3. **后果自行承担** - 因使用本软件导致的任何直接或间接损失,包括但不限于:
- 数据丢失或损坏
- 业务中断或服务不可用
- 经济损失
- 安全漏洞或数据泄露
- 第三方索赔
**均由用户自行承担全部责任**,开发团队不承担任何法律责任或赔偿义务
4. **技术支持** - 预览版仅提供有限的社区支持,不保证问题响应时间和解决方案
### 四、知识产权
本软件的所有知识产权归上海识沃网络科技有限公司所有。未经授权,不得用于商业用途或进行逆向工程。
### 五、条款接受
**下载、安装或使用本软件即表示您已完全理解并接受本免责声明的全部条款。如不同意,请立即停止使用并删除软件。**
---
> ⚠ **重要提示:** 正式稳定版发布前,请持续关注官方公告。我们将在软件达到生产就绪标准后正式发布 GA(General Availability)版本。

@ -0,0 +1,700 @@
[简体中文](README-CN.md) | [English](README.md)
<div align="center">
# TypePHP
**PHP 原生 AOT 编译器**
将 PHP 源码提前(AOT)编译为原生机器码,生成原生可执行文件、PHP 扩展和共享库,
同时保留你熟悉的 PHP 语法。
[![Linux x64](https://github.com/swoole/typephp/actions/workflows/linux-x64.yml/badge.svg)](https://github.com/swoole/typephp/actions/workflows/linux-x64.yml)
[![Linux ARM64](https://github.com/swoole/typephp/actions/workflows/linux-arm64.yml/badge.svg)](https://github.com/swoole/typephp/actions/workflows/linux-arm64.yml)
[![macOS ARM64](https://github.com/swoole/typephp/actions/workflows/macos-arm64.yml/badge.svg)](https://github.com/swoole/typephp/actions/workflows/macos-arm64.yml)
[![Windows x64](https://github.com/swoole/typephp/actions/workflows/windows-build.yml/badge.svg)](https://github.com/swoole/typephp/actions/workflows/windows-build.yml)
[![PHP 8.4–8.5](https://img.shields.io/badge/PHP-8.4--8.5-777bb4.svg)](https://www.php.net/)
[![License: GPL-3.0](https://img.shields.io/badge/License-GPL--3.0-blue.svg)](LICENSE)
</div>
---
## 什么是 TypePHP?
TypePHP 是一个 AOT(Ahead-Of-Time,提前编译)编译器,它把 PHP 源码翻译为 C++,
再编译为原生机器码。与字节码缓存或虚拟机不同,它不会在运行时解释 opcode,
而是直接生成在 CPU 上运行的原生二进制。
它保留熟悉的 PHP 语法,同时引入编译期类型信息,让编译器为性能热点生成快速、
静态类型的 C++ 代码。动态 PHP 值、内置函数、反射和对象元数据继续通过 PHPX
与 Zend runtime 互操作;用户函数编译完成后不再以 Zend opcode 方式执行。
TypePHP **完全由 PHP 语言编写**,并且**完全自举**:`tpc` 编译器二进制就是
用 TypePHP 编译编译器自身的 PHP 源码得到的。整个自举链路是纯 PHP——编译器
本身没有任何 C 或 C++ 胶水代码。
TypePHP 仍在积极开发中。它提供的是边界明确、可测试的 PHP 子集,而不是宣称可以
无修改替代所有高度动态的 PHP 程序。在将现有项目迁移到 TypePHP 前,请先阅读
[兼容性模型](#兼容性模型)和[不兼容特性清单](docs/INCOMPATIBLE_PHP_FEATURES.md)。
## 工作原理
```text
PHP 源码 + .stub.php 声明 + 可选 C/C++ 源码
解析、校验并收集全部声明
将函数实现和常量表达式降级为 C++17
原生编译器 + 可复用对象/PCH 缓存
可执行文件 | PHP 扩展 | 共享库 | WASI Component
```
prepare 阶段只建立完整符号模型,不分配运行时 Cache ID。常量和声明默认值只保留
AST,待全部项目符号就绪后再在 convert 阶段解析。这一两阶段设计保证多文件构建和
编译器自举过程具有确定性。
## 特性
- **完全自举、纯 PHP 实现** —— TypePHP 编译器完全由 PHP 语言编写,并能自举:
`tpc` 编译编译器自身的源码,即可生成原生二进制。
- **真正的 AOT 编译** —— PHP 先降级为 C++17,再编译为原生机器码。无解释器、
无 opcode 缓存、无 JIT 预热。
- **三种原生构建模式** —— 同一份代码可编译为原生 `bin` 可执行文件、可加载的
PHP `ext` 扩展,或可复用的 `lib` 共享库。
- **原生类型系统** —— `int`、`float`、`bool` 直接映射为 C++ 标量类型
(`int64_t`、`double`、`bool`),数值代码可获得数量级的性能提升。
- **高精度数值** —— `bigInt`(GMP)、`decimal`(libmpdec)、`bigFloat`(MPFR),
提供强类型运算符和方法 API。
- **强类型容器** —— `std::array`、`std::vector`、`std::map`、`std::ordered_map`,
元素类型在编译期确定;最高比 PHP 数组快 **10 倍**,性能与 C++ `std::vector` 相当。
- **通用方法(Universal Methods)** —— 在原生类型上直接调用方法
(`$s->upper()`、`$arr->contains()`、`$big->mul(2)`);静态类型已知时在编译期
直接解析调用。
- **混合 C++ / PHP 编程** —— 在性能关键内核中直接调用 C++ 函数(反之亦然)。
- **编译期函数与关键词** —— `any()`、`refval()`、`objval()`、`expected()`、
`unexpected()`,以及 `toInt()`、`toString()`、`toArray()` 等。
- **编译期安全检查** —— `#[Immutable]` 只读契约和 `#[ArrayDef]` 数组结构元数据,
在编译期检查,零运行时开销。
- **编译期代码生成** —— `#[Getter]`、`#[Setter]`、`#[With]`、`#[Constructor]`、
`#[Printer]``#[Arrayable]` 根据属性声明生成类型安全的方法。
- **现代 PHP 支持** —— PHP 8.4 property hooks、非对称可见性、PHP 8.5
`clone()`-with 以及 `(void)` 丢弃表达式。
- **跨平台与 WASM** —— 面向 x64 和 ARM64 的 Linux、Windows、macOS 目标,
以及 WASI 0.2 和浏览器(Jco)输出。
- **Python 桥接** —— 为 Python 模块生成 IDE helper,并将 Python 脚本转换为 TypePHP。
## 为什么选择 TypePHP?
| | TypePHP AOT | 字节码缓存(OPcache) | JIT(PHP 8+) |
|---|---|---|---|
| 编译目标 | 原生机器码 | 字节码 | 机器码(trace) |
| 启动 / 预热 | 无(已编译完成) | 每进程预热 | JIT 预热 |
| 类型驱动优化 | 编译期、全程序 | 无 | 有限,基于 trace |
| 生成原生可执行文件 | 支持 | 不支持 | 不支持 |
| 源码保护 | 编译为机器码 | 字节码(可还原) | 字节码(可还原) |
| 性能确定性 | 是 | 否 | 否 |
**相较原生 PHP 的优势:**
- **接近原生的性能。** 数值密集和容器密集的热点路径会编译为与 C++ 程序相同的机器码。
见下方[基准测试](#基准测试)。
- **源码保护。** 源码被编译掉——交付物是原生二进制,而不是可读的 PHP 文件。
- **原生进程入口。** 二进制模式直接启动原生可执行文件,不需要 PHP CLI 或独立的
解释器进程。可执行文件仍会嵌入或链接 PHPX、`libphp` 及项目配置的原生库,部署包
中必须提供这些运行时依赖。
- **渐进式类型,真正带来收益。** 只在性能关键处添加 `use native_types`、`std::`
容器和类型声明,其余保持普通 PHP。
- **Zend 生态互通。** 扩展模式以标准 PHP 扩展形式加载,项目可以调用受支持的
内置函数,并显式声明依赖的其他 Zend 扩展。
## 前置要求
- **PHP 8.4 – 8.5** CLI、开发头文件及 `php-config`
- 在类 Unix 系统构建二进制/共享库时,需要与 PHP 匹配的 **embed 库**
(`libphp.so` 或 `libphp.dylib`
- **GCC 9+**(或 Clang),支持 **C++17**
- **CMake 3.24+**
- **Composer 2**
- 高精度数学库:**GMP**、**MPFR**(libmpdec 已随 PHPX 内置)
```shell
# Ubuntu/Debian
sudo apt install build-essential cmake pkg-config libgmp-dev libmpfr-dev
# RHEL/CentOS/Fedora
sudo dnf install gcc gcc-c++ cmake pkgconf-pkg-config gmp-devel mpfr-devel
# Arch Linux
sudo pacman -S base-devel cmake pkgconf gmp mpfr
```
> GMP 用于 `bigInt`,MPFR 用于 `bigFloat`。`decimal` 底层是 libmpdec,
> 已随 PHPX 内置,无需单独安装。
Linux x64 是主要开发及全量测试 CI 平台。编译器也提供 Windows、macOS、ARM64 和
WASI 后端;具体主机能否构建某个目标,仍取决于 PHP embed、工具链和第三方库是否
可用。
原生 Release Assets 默认使用 PHP 8.5 ZTS 的最新版本构建,提供 Linux x64、Linux
ARM64、macOS ARM64 和 Windows x64 四个平台包;不提供原生 NTS 或 32 位 x86 包。
Linux 与 macOS 包包含编译器和 production Composer 依赖,Windows 包则包含完整且
匹配的 PHP/PHPX 运行时与 SDK。
## 安装
### 通过 Composer
```bash
composer require --dev swoole/typephp
```
然后编译你的项目:
```bash
vendor/bin/tpc.php project.yml
```
在 TypePHP 源码仓库中开发时,改用本地入口:
```bash
bin/tpc.php project.yml
```
### 从源码安装
```bash
git clone https://github.com/swoole/typephp.git
cd typephp
composer install
php bin/tpc.php --help
```
可以使用 `PHPX_HOME` 指向独立的 PHPX 源码或安装目录。`PHP_HOME` 可以指向 PHP
embed 安装前缀;在类 Unix 系统中,该目录应包含 `bin/php-config`、PHP 头文件和
`lib/libphp.so`
### 构建 `libphp.so`
二进制和共享库构建需要 PHP 的 `embed` SAPI。如果 Linux 上缺少 `libphp.so`
`tpc.php` 可以交互式下载 PHP 源码并自动构建。PHP 扩展构建从宿主 SAPI 解析 Zend
符号,不能再加载第二份 `libphp`。详见[自动构建 libphp.so](docs/LIBPHP_INSTALLER.md)。
## 快速开始
创建 `hello.php`
```php
<?php
function main(): void
{
echo "Hello World!\n";
var_dump(PHP_VERSION);
var_dump(php_uname());
}
```
编译并运行:
```bash
bin/tpc.php hello.php
./hello
```
输出示例(具体 PHP 版本和平台字符串取决于实际链接的运行时):
```
Hello World!
string(5) "8.x.x"
string(16) "Linux ..."
```
> 二进制模式需要全局 `main()` 函数。它可以声明为无参数,或
> `main(int $argc, array $argv)` 以接收命令行参数,且必须返回 `void`。全局作用域
> 不允许可执行语句;可执行代码必须位于函数或方法内。
## 编译模式
TypePHP 支持三种构建模式,通过 `-m` / `--mode` 选择:
| 模式 | 参数 | 输出 | 需要 `main()` | 典型用途 |
|---|---|---|---|---|
| 二进制 | `-m bin`(默认) | 可执行文件 | 是 | CLI 工具、常驻服务、独立应用 |
| 扩展 | `-m ext` | PHP `.so` / `.dll` | 否 | 将编译后的函数和类加载到 PHP SAPI |
| 库 | `-m lib` | 共享库及自动生成的 `.stub.php` | 否 | 在其他项目中复用编译后的 TypePHP API |
```bash
# 二进制(默认)
bin/tpc.php app.php -o myapp
# PHP 扩展
bin/tpc.php extension/ -m ext -o my_extension
# 共享库,同时生成 mylib.stub.php
bin/tpc.php lib/ -m lib -o mylib
```
详见[编译模式](docs/COMPILATION_MODES.md)。
## 项目配置
多文件项目建议使用 `project.yml` 固化可复用的构建配置:
```yaml
name: myapp
mode: bin
php-version: "8.5"
optimize: 2
job: 8
build-dir: build
cxx-std: c++17
sources:
- src
- cpp-src
- path: src/php85
if: PHP_VERSION_ID >= 80500
- path: src/windows
if: PHP_OS_FAMILY == "Windows"
ignore:
- src/experimental
include-paths:
- native/include
defines:
- FEATURE_FAST_PATH=1
link-paths:
- native/lib
link-libs:
- curl
# Zend 扩展依赖,不是原生链接库。
# `extension-dependencies` 是等价长名称,两者不能同时使用。
ext-deps:
- pdo_mysql
- curl
```
路径以 YAML 文件所在目录为基准。source 可以是文件或目录;条件 source 支持
`PHP_VERSION`、`PHP_VERSION_ID` 和 `PHP_OS_FAMILY`。命令行参数优先于 YAML
中的同名配置。原生链接依赖应写入 `link-libs`;`ext-deps` 会生成
`ZEND_MOD_REQUIRED`,缺少所需 PHP 扩展时由 Zend 拒绝加载模块。
构建目录保存生成的 C++、依赖对象和预编译头缓存。复用同一个构建目录可以显著加快
增量构建;仅在确实需要重编 PHPX 公共对象时使用 `--force`
全部项目配置项及命令行优先级详见[编译器命令行](docs/COMPILER_CLI.md)。
## 兼容性模型
TypePHP 会在适合 AOT 编译的范围内保持 PHP 语法和运行行为,同时有一些明确限制:
- 全局作用域只允许声明,可执行语句必须位于函数或方法内;
- 二进制模式对 `main()` 使用严格签名;
- `use native_types` 会让标量声明使用固定原生存储,之后不能改为不兼容类型;
- 静态可确定的调用和属性会直接编译,受支持的动态操作则通过 PHPX/Zend runtime
fallback 执行;
- `.stub.php` 用于声明 C++ 或外部库 API,函数体必须为空,stub 文件禁止声明
`#[Native]` 类;
- 部分高度动态的引用、声明、闭包和反射模式仍明确不支持。
兼容性边界属于公共契约,同时有正向和负向测试保护。请以
[不兼容 PHP 特性清单](docs/INCOMPATIBLE_PHP_FEATURES.md)为当前准确列表,不要把
README 未提及的行为默认理解为已支持。
## 编译期 Attribute 与代码生成
TypePHP 在 class lowering 阶段消费内置的代码生成 Attribute。生成的方法保留属性
声明的类型,并与显式声明的方法一样参与名称冲突、继承关系和 final 方法检查。
| Attribute | 目标 | 生成的 API |
|---|---|---|
| `#[Getter]` | 实例属性,包括构造器提升属性 | `public function getName(): T` |
| `#[Setter]` | 可变实例属性,包括构造器提升属性 | `public function setName(T $name): void` |
| `#[With]` | 可变实例属性,包括构造器提升属性 | `public function withName(T $name): static`;克隆对象、修改副本并返回副本 |
| `#[Constructor]` | 普通实例属性声明 | 将属性加入自动生成的 public `__construct()` |
| `#[Printer]` | 具名类 | `public function __toString(): string` |
| `#[Arrayable]` | 具名类 | `public function toArray(): array` |
```php
<?php
#[Printer(fields: ['id', 'name'])]
#[Arrayable(fields: ['id', 'name'])]
final class User
{
#[Constructor, Getter, With]
public int $id;
#[Constructor, Getter, Setter]
public string $name = 'guest';
}
function main(): void
{
$user = new User(7);
$user->setName('Alice');
$copy = $user->withId(8);
echo $user->getId(); // 7
echo $copy->getId(); // 8
echo $user; // User(id=7, name=Alice)
echo $user->toArray()['name'];
}
```
未指定 `fields` 时,`#[Printer]` 和 `#[Arrayable]` 使用当前类自身的 public 实例
属性。位置参数写法 `#[Arrayable(['id'])]` 等价于
`#[Arrayable(fields: ['id'])]`
`#[Getter]`、`#[Setter]` 和 `#[With]` 不能用于 static 属性或带 property hook 的
属性;`#[Setter]` 和 `#[With]` 还会拒绝 readonly 属性。类中已经显式声明
`__construct()` 时不能使用 `#[Constructor]`,必填的构造属性必须位于带默认值的
属性之前。生成的方法名若与已有方法冲突,或覆盖继承而来的 final 方法,编译期会
直接报错。
## 使用示例
### 1. 原生类型 —— 编译期数值加速
```php
<?php
use native_types;
function fib(int $n): int
{
if ($n == 1 || $n == 2) {
return 1;
}
return fib($n - 1) + fib($n - 2);
}
function main(int $argc, array $argv): void
{
$n = (int)$argv[1];
$begin = microtime(true);
echo fib($n) . "\n";
echo "Time: " . (microtime(true) - $begin) . "\n";
}
```
```bash
bin/tpc.php fib.php -O3 -o fib
./fib 30
```
使用 `use native_types` 后,`int` 变量变为 C++ `int64_t`,算术运算直接编译为
CPU 指令,而不是 ZendVM 调用。
### 2. 高精度数值
```php
<?php
declare(strict_types=1);
use native_types;
function main(): void
{
// 54 位整数 —— 自动识别并存储为 bigInt
$a = std::bigInt("123456789012345678901234567890123456789012345678901234");
$b = std::bigInt("987654321098765432109876543210987654321098765432109876");
echo $a->add($b)->toString() . "\n"; // 精确计算,不会溢出
// 精确的十进制运算 —— 无二进制浮点误差
$c = std::decimal("0.1")->add(std::decimal("0.2"));
echo $c->toString() . "\n"; // "0.3"
// 256 位浮点数
$pi = std::bigFloat("3.14159265358979323846264338327950288419716939937510");
echo $pi->mul(2)->toString() . "\n";
}
```
详见[高精度类型](docs/HIGH_PRECISION_TYPES.md)和[原生类型](docs/NATIVE_TYPES.md)。
### 3. 强类型容器
```php
<?php
use native_types;
function main(): void
{
$vector = std::vector(Type::Int);
$vector[] = 1;
$vector[] = 2;
$vector[] = 3;
$sum = 0;
foreach ($vector as $value) {
$sum += $value;
}
echo $sum . "\n"; // 6
echo $vector[1] . "\n"; // 2
// 固定 key/value 类型的映射
$map = std::ordered_map(Type::String, Type::Int);
$map["a"] = 1;
$map["b"] = 2;
}
```
详见 [Std 容器](docs/STD_CONTAINERS.md)。
### 4. 通用方法
```php
<?php
function main(): void
{
$s = "hello world";
echo $s->length() . "\n"; // strlen()
echo $s->upper() . "\n"; // strtoupper()
echo $s->substr(0, 5) . "\n"; // substr()
$arr = [1, 3, 5, 7, 9];
echo $arr->count() . "\n"; // count()
var_dump($arr->contains(3)); // in_array()
$big = std::bigInt("12345678901234567890");
echo $big->mul(2)->toString() . "\n";
}
```
原生类型上的方法调用在编译期被解析为直接的 C/C++ 函数调用——没有虚表查找、
没有反射、没有运行时派发。详见[通用方法](docs/UNIVERSAL_METHODS.md)。
### 5. 混合 C++ / PHP
用 C++ 编写性能关键内核,并在 PHP 中调用:
```cpp
// math.cpp
#include <phpx.h>
using namespace php;
Int php_fast_sum(Int a, Int b) {
return a + b;
}
```
```php
<?php
// math.stub.php —— 声明 C++ 函数签名
function fast_sum(int $a, int $b): int {}
```
```php
<?php
function main(): void
{
echo fast_sum(3, 4) . "\n"; // 7
}
```
需要将 `math.cpp`、`math.stub.php` 和调用它的 PHP 源码加入同一个项目配置。
C++ 符号的 `php_` 前缀属于 TypePHP callable ABI;stub 函数只提供类型元数据,
不能包含实际实现。
详见[混合 C++/PHP](docs/MIXED_CPP_PHP.md)。
## 基准测试
### PHP 语言基准(来自 php-src)
TypePHP 使用 `-O3` 运行 PHP 源码树自带的官方 `bench.php`
`micro_bench.php` 语言性能测试:
| 基准 | 解释执行 PHP | TypePHP AOT(`-O3`) | 加速比 |
|---|---|---|---|
| `bench.php`(总计) | 5.034 秒 | **0.603 秒** | 约 8× |
| `micro_bench.php`(总计) | 13.045 秒 | **2.021 秒** | 约 6.5× |
两项基准覆盖 PHP 语言核心性能——函数调用、对象属性访问、数组/哈希访问、
字符串处理、控制流等。仓库内的测试源码为
[`examples/bench.php`](examples/bench.php)和
[`examples/micro_bench.php`](examples/micro_bench.php)。
这些数字是项目测量快照,不是性能保证。PHP 版本、编译器、CPU、优化参数和已启用
扩展都会影响结果;在用于部署决策前,应在同一机器上使用相同 workload 自行对比。
### std::array 对比 PHP 数组
一个 10000×100000 的元素累加循环,对比 PHP 数组、TypePHP `std::array`
与原生 C++:
| 实现 | 耗时 |
|---|---|
| PHP 数组(JIT) | 67.6 秒 |
| `std::array`(TypePHP AOT) | **6.4 秒** |
| C++ `std::vector` | 6.2 秒 |
在该 workload 中,`std::array` 比 PHP 数组快约 **10 倍**,并接近手写 C++ 结果。
完整基准测试见 [Std 容器](docs/STD_CONTAINERS.md)。
## 命令行
```bash
bin/tpc.php <file|dir|project.yml> [options] [-- program-args...]
```
常用示例:
```bash
# 编译单个文件
bin/tpc.php app.php
# 优化并运行,`--` 后的参数传给生成的程序
bin/tpc.php app.php -O3 -r -- --flag value
# 编译 project.yml 定义的项目
bin/tpc.php project.yml -O2 -j 8
# 生成 PHP 扩展
bin/tpc.php extension/ -m ext -o my_extension
# 只生成 C++(跳过编译与链接)
bin/tpc.php app.php --dry --build-dir /tmp/typephp-build
# 编译为 WASI 0.2
bin/tpc.php --wasm app.php
# 编译为浏览器目标(需要 jco)
bin/tpc.php --wasm=browser app.php
```
主要选项:
| 选项 | 说明 |
|---|---|
| `-O <0-3>` | 优化级别(默认 `0`) |
| `-d`, `--debug` | 调试构建,带符号和源码跟踪 |
| `-o`, `--output <file>` | 输出文件名 |
| `-m`, `--mode <bin\|lib\|ext>` | 构建模式(默认 `bin`) |
| `-r`, `--run` | 构建成功后运行 |
| `-j`, `--job <num>` | 并行编译任务数(默认 `4`) |
| `-f`, `--force` | 不使用缓存,重新编译可复用 PHPX 对象 |
| `--build-dir <dir>` | 生成 C++ 与中间产物的目录 |
| `--dry` | 只生成 C++,跳过编译与链接 |
| `--php-version <8.4\|8.5>` | 接受的 PHP 语法版本 |
| `--cxx-std <ver>` | C++ 标准(如 `c++17`、`c++20`) |
| `--march <arch>` | 目标指令集(如 `native`) |
| `--target-platform <triple>` | 交叉编译目标 triple |
| `--lto` | 启用链接时优化 |
| `--sanitize <type>` | 启用 sanitizer(如 `address`) |
| `--profile` | 启用 Linux gperftools 性能分析 |
| `--format` | 使用 clang-format 格式化生成的 C++ |
| `--no-literal-strings` | 禁用字面量字符串表优化 |
| `--no-progress`, `--no-color` | 适合 CI 的输出控制 |
| `-I`, `-D`, `-L`, `-l` | 可重复指定的原生 include、define、库路径和链接库参数 |
运行 `bin/tpc.php --help` 查看权威的最新参数列表。详见
[编译器命令行](docs/COMPILER_CLI.md),包括 Bash 补全:
```bash
source <(./tpc --generate-completion=bash)
```
## 常见问题
- **缺少 `libphp.so` / `libphp.dylib`:** 安装或编译与当前 PHP 匹配的 embed SAPI,设置
`PHP_HOME`,或使用 `bin/tpc.php` 在 Linux 上提供的交互式安装流程。
- **找不到 PHPX:**`PHPX_HOME` 指向包含 `include/`
`lib/libphpx.so`(或对应平台文件)的 PHPX 安装目录,并在编译项目前先构建 PHPX。
- **启动崩溃或出现 ABI 错误:** PHP 头文件、`php-config`、`libphp` 和扩展 ABI
必须使用一致的 PHP 版本及 ZTS/NTS 模式,不能混用不同 PHP 构建产生的产物。
- **增量构建异常缓慢:** 固定使用同一个 `--build-dir`,以复用对象和 PCH 缓存。
当外层测试工具已经并行运行多个测试时,不要再设置过大的 `tpc -j`,避免并发数
相乘后造成 CPU 和内存争用。
- **使用 `bin/tpc.php` 可以编译,但自举 `tpc` 失败:** 必须用自举编译器复现。
自举执行可能暴露 PHP-hosted 编译器不会经过的动态调用或 ABI 路径。
## Python 桥接
TypePHP 内置一个 Python 工具子模块,复用 `tpc` 入口:
```shell
# 为 Python 模块生成 IDE helper
./tpc --gen-python-helper math
./tpc --gen-python-helper numpy --output-dir .ide-helper
# 将 Python 脚本转换为 TypePHP
./tpc --convert-python-to-php script.py > script.php
```
详见 [Python 工具子模块](docs/python/tools.md)。
## 开发与测试
安装开发依赖并运行编译器单元测试:
```bash
composer install
PHPX_HOME=/path/to/phpx vendor/bin/phpunit
```
PHPT 是端到端测试。必须先构建自举编译器,并显式传给测试工具;将 Zend PHP
可执行文件作为 `--compiler` 并不能验证实际交付的编译器:
```bash
PHPX_HOME=/path/to/phpx php bin/tpc.php project.yml --job 2 --no-progress
php run-tests.php -q -j8 --compiler ./tpc tests/compiler
```
静态分析与从测试源码生成的覆盖矩阵是两项独立检查:
```bash
composer analyse
php bin/analyze-test-coverage.php
php bin/analyze-test-coverage.php \
--format=markdown --output=build/test-coverage.md --strict
```
覆盖工具分别报告 PHP 版本 × 特性 × 正向编译 × 运行语义 × 负向诊断,并列出实际
出现的 php-parser AST 节点。它不会给出分母不明确的单一百分比。详见
[测试覆盖分析工具](docs/TEST_COVERAGE_ANALYZER.md)。
GitHub Actions 会在 PHP 8.4 和 8.5 上分别运行 PHPUnit 与自举 PHPT。修改编译器
内部规则或代码生成时应增加聚焦的 PHPUnit;运行输出或诊断可观察时还应增加 PHPT。
## 文档
- [快速入门](docs/QUICKSTART.md) —— 最小编译流程
- [编译模式](docs/COMPILATION_MODES.md) —— `bin`、`ext`、`lib`
- [编译器命令行](docs/COMPILER_CLI.md) —— CLI 参数与项目配置
- [不兼容 PHP 特性清单](docs/INCOMPATIBLE_PHP_FEATURES.md) —— 当前限制
- [原生类型](docs/NATIVE_TYPES.md) —— 原生标量类型
- [高精度类型](docs/HIGH_PRECISION_TYPES.md) —— BigInt / Decimal / BigFloat
- [Std 容器](docs/STD_CONTAINERS.md) —— 强类型容器
- [通用方法](docs/UNIVERSAL_METHODS.md) —— 编译期方法解析
- [编译期函数](docs/COMPILE_TIME_FUNCTIONS.md) —— `any()`、`refval()`、`objval()` 等
- [混合 C++/PHP](docs/MIXED_CPP_PHP.md) —— C++/PHP 互操作
- [`#[Immutable]`](docs/IMMUTABLE.md) —— 编译期只读契约
- [`#[ArrayDef]`](docs/ARRAY_DEF.md) —— 强类型数组属性契约
- [Property hooks](docs/PROPERTY_HOOKS.md) —— PHP 8.4 hook 降级和运行时元数据
- [对象存储模型](docs/OBJECT_STORAGE_AND_PASSING_MODELS.md) —— Zend object、Box 与 Native class 边界
- [Generator](docs/YIELD_GENERATOR.md) —— 生成器降级与生命周期
- [测试覆盖分析工具](docs/TEST_COVERAGE_ANALYZER.md) —— AST 与特性证据矩阵
- [WASI 构建](docs/WASI_BUILD.md) —— WASI 目标
## 授权协议
TypePHP 采用 [GNU General Public License v3.0](LICENSE) 授权。
## 社区
- 代码仓库:<https://github.com/swoole/typephp>
- 版权所有 © 2026 上海识沃网络科技有限公司(Swoole)

@ -1,84 +1,750 @@
# 依赖 [English](README.md) | [简体中文](README-CN.md)
- 编译器需要 PHP 8.4 以上版本;生成的扩展仍可面向 PHP 8.2~8.5
- 需要 GCC-9 以上版本,支持 C++17 标准
- 需要 CMake-3.24 以上版本
- 需要高精度数学库:`GMP`、`MPFR`、`libmpdec`
# Composer 安装 <div align="center">
在项目中安装 TypePHP: # TypePHP
**A native AOT compiler for PHP**
Compile PHP source code into native machine code ahead of time — producing
native executables, PHP extensions, and shared libraries — while keeping
the PHP syntax you already know.
[![Linux x64](https://github.com/swoole/typephp/actions/workflows/linux-x64.yml/badge.svg)](https://github.com/swoole/typephp/actions/workflows/linux-x64.yml)
[![Linux ARM64](https://github.com/swoole/typephp/actions/workflows/linux-arm64.yml/badge.svg)](https://github.com/swoole/typephp/actions/workflows/linux-arm64.yml)
[![macOS ARM64](https://github.com/swoole/typephp/actions/workflows/macos-arm64.yml/badge.svg)](https://github.com/swoole/typephp/actions/workflows/macos-arm64.yml)
[![Windows x64](https://github.com/swoole/typephp/actions/workflows/windows-build.yml/badge.svg)](https://github.com/swoole/typephp/actions/workflows/windows-build.yml)
[![PHP 8.4–8.5](https://img.shields.io/badge/PHP-8.4--8.5-777bb4.svg)](https://www.php.net/)
[![License: GPL-3.0](https://img.shields.io/badge/License-GPL--3.0-blue.svg)](LICENSE)
</div>
---
## What is TypePHP?
TypePHP is an Ahead-Of-Time (AOT) compiler that translates PHP source code into
C++ and then into native machine code. Unlike a bytecode cache or a VM, it does
not interpret opcodes at runtime: it generates optimized native binaries that
run directly on the CPU.
It keeps familiar PHP syntax and adds compile-time type information, so the
compiler can emit fast, statically-typed C++ for hot paths. Dynamic PHP values,
internal functions, reflection, and object metadata continue to interoperate
with the Zend runtime through PHPX; user functions are not executed as Zend
opcodes after they have been compiled.
TypePHP is **written entirely in PHP** and is **fully self-hosting**: the `tpc`
compiler binary is built by compiling the compiler's own PHP source code with
TypePHP. The bootstrap chain is pure PHP — no C or C++ glue in the compiler
itself.
TypePHP is under active development. It intentionally supports a defined,
testable subset of PHP rather than claiming drop-in compatibility with every
dynamic PHP program. Read [Compatibility model](#compatibility-model) and the
[incompatible-feature list](docs/INCOMPATIBLE_PHP_FEATURES.md) before adopting
it for an existing application.
## How it works
```text
PHP source + .stub.php declarations + optional C/C++ sources
parse, validate, and collect declarations
lower function bodies and constants to C++17
native compiler + reusable object/PCH caches
executable | PHP extension | shared library | WASI component
```
The prepare phase builds the complete symbol model without allocating runtime
cache IDs. Constants and declaration defaults retain their AST until the
convert phase, where they are lowered after all project symbols are known.
This two-phase design keeps multi-file and self-hosted builds deterministic.
## Features
- **Self-hosting, written in PHP** — the TypePHP compiler is implemented
entirely in PHP and bootstraps itself: `tpc` compiles the compiler's own
source into a native binary.
- **True AOT compilation** — PHP is lowered to C++17, then to native machine
code. No interpreter, no opcode cache, no JIT warm-up.
- **Three native build modes** — build a native `bin` executable, a loadable
PHP `ext` extension, or a reusable `lib` shared library from the same codebase.
- **Native type system**`int`, `float`, and `bool` map directly to C++
scalar types (`int64_t`, `double`, `bool`) for orders-of-magnitude speedups
on numeric code.
- **High-precision numerics**`bigInt` (GMP), `decimal` (libmpdec), and
`bigFloat` (MPFR), with typed operators and method APIs.
- **Strongly-typed containers**`std::array`, `std::vector`, `std::map`, and
`std::ordered_map` with compile-time element types; up to **10×** faster than
PHP arrays and on par with C++ `std::vector`.
- **Universal methods** — call methods directly on primitives
(`$s->upper()`, `$arr->contains()`, `$big->mul(2)`); statically-known calls
are resolved directly at compile time.
- **Mixed C++ / PHP** — call C++ functions from PHP (and vice versa) for
performance-critical kernels.
- **Compile-time functions & keywords**`any()`, `refval()`, `objval()`,
`expected()`, `unexpected()`, plus `toInt()`, `toString()`, `toArray()` and
friends.
- **Compile-time safety**`#[Immutable]` read-only contracts and `#[ArrayDef]`
array-shape metadata, checked at compile time with zero runtime cost.
- **Compile-time code generation**`#[Getter]`, `#[Setter]`, `#[With]`,
`#[Constructor]`, `#[Printer]`, and `#[Arrayable]` generate type-safe methods
from property declarations.
- **Modern PHP support** — PHP 8.4 property hooks, asymmetric visibility,
PHP 8.5 `clone()`-with, and `(void)` discard expressions.
- **Cross-platform & WASM** — Linux, Windows, and macOS targets for x64 and
ARM64, plus WASI 0.2 and browser (Jco) output.
- **Python bridge** — generate IDE helpers for Python modules and convert
Python scripts to TypePHP.
## Why TypePHP?
| | TypePHP AOT | Opcode cache (OPcache) | JIT (PHP 8+) |
|---|---|---|---|
| Compilation target | Native machine code | Bytecode | Machine code (trace) |
| Startup / warm-up | None (already compiled) | Per-process warm-up | JIT warm-up |
| Type-driven optimization | Compile-time, full-program | None | Limited, trace-based |
| Native executable output | Yes | No | No |
| Source code protection | Compiled to machine code | Bytecode (reversible) | Bytecode (reversible) |
| Deterministic performance | Yes | No | No |
**Strengths over plain PHP:**
- **Near-native performance.** Numeric and container-heavy hot paths compile
down to the same machine code a C++ program would produce. See the
[benchmark](#benchmark) below.
- **Source protection.** Your source is compiled away — shipped artifacts are
native binaries, not readable PHP files.
- **Native process entry.** Binary mode starts directly from a native
executable and does not require the PHP CLI or a separate interpreter
process. The executable still embeds/links PHPX, `libphp`, and any configured
native libraries, which must be available in the deployment package.
- **Gradual typing that actually pays off.** Add `use native_types`, `std::`
containers, and type declarations only where performance matters; the rest
stays ordinary PHP.
- **Zend ecosystem interop.** Extension mode loads as a standard PHP extension,
and projects can call supported internal functions and require other Zend
extensions explicitly.
## Requirements
- **PHP 8.4 – 8.5** CLI, development headers, and `php-config`
- The matching **PHP embed library** (`libphp.so` or `libphp.dylib`) for binary/shared-library
builds on Unix-like systems
- **GCC 9+** (or Clang) with **C++17**
- **CMake 3.24+**
- **Composer 2**
- High-precision math libraries: **GMP**, **MPFR** (libmpdec is bundled with PHPX)
```shell
# Ubuntu/Debian
sudo apt install build-essential cmake pkg-config libgmp-dev libmpfr-dev
# RHEL/CentOS/Fedora
sudo dnf install gcc gcc-c++ cmake pkgconf-pkg-config gmp-devel mpfr-devel
# Arch Linux
sudo pacman -S base-devel cmake pkgconf gmp mpfr
```
> GMP powers `bigInt` and MPFR powers `bigFloat`. The `decimal` type is backed
> by libmpdec, which is bundled with PHPX — no separate install required.
Linux x64 is the primary development and full-test CI platform. The compiler
also has Windows, macOS, ARM64, and WASI backends; availability of PHP embed,
toolchain, and third-party libraries still determines which target can be
built on a given host.
Native release assets are built with the latest PHP 8.5 ZTS release. TypePHP
publishes Linux x64, Linux ARM64, macOS ARM64, and Windows x64 packages. Native
NTS and 32-bit x86 packages are not provided. Linux and macOS archives contain
the compiler and production Composer dependencies, while the Windows archive
contains the complete matching PHP/PHPX runtime and SDK.
## Installation
### Via Composer
```bash ```bash
composer require --dev swoole/typephp composer require --dev swoole/typephp
``` ```
安装后可直接编译项目: Then compile your project:
```bash ```bash
vendor/bin/tpc.php project.yml vendor/bin/tpc.php project.yml
``` ```
在 TypePHP 源码仓库中则使用: When working inside the TypePHP source repository, use the local entry point
instead:
```bash ```bash
bin/tpc.php project.yml bin/tpc.php project.yml
``` ```
Linux 环境缺少 `libphp.so` 时,`tpc.php` 可以交互式下载 PHP 源码并自动构建,详见 [自动构建 libphp.so](docs/LIBPHP_INSTALLER.md)。 ### From source
```shell ```bash
# Ubuntu/Debian git clone https://github.com/swoole/typephp.git
sudo apt install libgmp-dev libmpfr-dev libmpdec-dev cd typephp
composer install
php bin/tpc.php --help
```
# RHEL/CentOS/Fedora `PHPX_HOME` may point to a separate PHPX checkout or installation. `PHP_HOME`
sudo dnf install gmp-devel mpfr-devel libmpdec-devel may point to the PHP embed prefix; it must contain `bin/php-config`, PHP headers,
and `lib/libphp.so` on Unix-like systems.
# Arch Linux ### Building `libphp.so`
sudo pacman -S gmp mpfr mpdecimal
Binary and shared-library builds require PHP's `embed` SAPI. If `libphp.so` is
missing on Linux, `tpc.php` can interactively download the PHP source and build
it for you. A PHP extension build resolves Zend symbols from the host SAPI and
must not load a second `libphp`. See
[Automatic libphp.so build](docs/LIBPHP_INSTALLER.md).
## Quick Start
Create `hello.php`:
```php
<?php
function main(): void
{
echo "Hello World!\n";
var_dump(PHP_VERSION);
var_dump(php_uname());
}
``` ```
> GMP 用于 `BigInt` 任意精度整数,MPFR 用于 `BigFloat` 高精度浮点数,libmpdec 用于 `Decimal` 十进制高精度小数。 Compile and run it:
> 预览版目前仅支持 `Linux` 系统,建议使用 `Ubuntu 22.04` ```bash
bin/tpc.php hello.php
./hello
```
## PHP Example output (the exact PHP version and platform strings depend on the linked
必须包含 embed 模块 runtime):
## PHPX ```
可使用 `composer install` 安装依赖。 Hello World!
进入 `vendor/swoole/phpx` 目录,编译 `phpx` string(5) "8.x.x"
string(16) "Linux ..."
```
```shell > Binary mode requires a global `main()` function. It may be declared with no
cd vendor/swoole/phpx > parameters, or as `main(int $argc, array $argv)` to receive command-line
cmake . > arguments, and must return `void`. Top-level executable statements are not
make -j32 > allowed; executable code belongs in a function or method.
## Compilation Modes
TypePHP supports three build modes, selected with `-m` / `--mode`:
| Mode | Flag | Output | Needs `main()` | Typical use |
|---|---|---|---|---|
| Binary | `-m bin` (default) | Executable | Yes | CLI tools, long-running services, standalone apps |
| Extension | `-m ext` | PHP `.so` / `.dll` | No | Loading compiled functions/classes into a PHP SAPI |
| Library | `-m lib` | Shared library plus generated `.stub.php` | No | Reusing a compiled TypePHP API from another project |
```bash
# Binary (default)
bin/tpc.php app.php -o myapp
# PHP extension
bin/tpc.php extension/ -m ext -o my_extension
# Shared library; also generates mylib.stub.php
bin/tpc.php lib/ -m lib -o mylib
``` ```
## 动态链接库 See [Compilation modes](docs/COMPILATION_MODES.md) for details.
```shell
sudo ldconfig -p | grep php ## Project configuration
For multi-file projects, keep repeatable build settings in `project.yml`:
```yaml
name: myapp
mode: bin
php-version: "8.5"
optimize: 2
job: 8
build-dir: build
cxx-std: c++17
sources:
- src
- cpp-src
- path: src/php85
if: PHP_VERSION_ID >= 80500
- path: src/windows
if: PHP_OS_FAMILY == "Windows"
ignore:
- src/experimental
include-paths:
- native/include
defines:
- FEATURE_FAST_PATH=1
link-paths:
- native/lib
link-libs:
- curl
# Zend extension requirements, not native linker libraries.
# `extension-dependencies` is the equivalent long name; do not use both.
ext-deps:
- pdo_mysql
- curl
``` ```
必须包含 `libphp.so``libphpx.so`
若编译完成,但找不到动态链接库,需要修改 Paths are resolved relative to the YAML file. A source entry may be a file or
```shell directory; conditional entries support `PHP_VERSION`, `PHP_VERSION_ID`, and
vim /etc/ld.so.conf.d/swoole.conf `PHP_OS_FAMILY`. CLI arguments override their YAML counterparts. Native linker
dependencies belong in `link-libs`; `ext-deps` writes `ZEND_MOD_REQUIRED`
entries so Zend can reject loading when a required PHP extension is missing.
The build directory contains generated C++, dependency objects, and the
precompiled-header cache. Reusing it makes incremental builds much faster;
use `--force` only when the reusable PHPX objects must be rebuilt.
See [Compiler CLI](docs/COMPILER_CLI.md) for all project keys and command-line
precedence rules.
## Compatibility model
TypePHP follows PHP syntax and runtime behavior where they are compatible with
ahead-of-time compilation, but it also makes several deliberate restrictions:
- global scope is declaration-only; executable statements must be inside a
function or method;
- binary mode has a strict `main()` signature;
- `use native_types` opts scalar declarations into fixed native storage, so a
value cannot later change to an incompatible type;
- statically-known calls and properties are compiled directly, while supported
dynamic operations use PHPX/Zend runtime fallbacks;
- `.stub.php` files declare C++ or imported-library APIs and must contain empty
bodies; `#[Native]` classes are not permitted in stub files;
- some highly dynamic reference, declaration, closure, and reflection patterns
remain intentionally unsupported.
The compatibility boundary is part of the public contract and has both
positive and negative tests. Consult
[Incompatible PHP features](docs/INCOMPATIBLE_PHP_FEATURES.md) for the current,
specific list instead of assuming that absence from this README means support.
## Compile-time attributes and code generation
TypePHP consumes its built-in code-generation attributes while lowering the
class. The generated methods retain the declared property types and take part
in the same conflict, inheritance, and final-method checks as explicitly
declared methods.
| Attribute | Target | Generated API |
|---|---|---|
| `#[Getter]` | Instance property, including a promoted property | `public function getName(): T` |
| `#[Setter]` | Mutable instance property, including a promoted property | `public function setName(T $name): void` |
| `#[With]` | Mutable instance property, including a promoted property | `public function withName(T $name): static`; clones the object, updates the clone, and returns it |
| `#[Constructor]` | Declared instance property | Adds the property to a generated public `__construct()` |
| `#[Printer]` | Named class | `public function __toString(): string` |
| `#[Arrayable]` | Named class | `public function toArray(): array` |
```php
<?php
#[Printer(fields: ['id', 'name'])]
#[Arrayable(fields: ['id', 'name'])]
final class User
{
#[Constructor, Getter, With]
public int $id;
#[Constructor, Getter, Setter]
public string $name = 'guest';
}
function main(): void
{
$user = new User(7);
$user->setName('Alice');
$copy = $user->withId(8);
echo $user->getId(); // 7
echo $copy->getId(); // 8
echo $user; // User(id=7, name=Alice)
echo $user->toArray()['name'];
}
```
Without `fields`, `#[Printer]` and `#[Arrayable]` use the class's own public
instance properties. The positional form, such as `#[Arrayable(['id'])]`, is
equivalent to `#[Arrayable(fields: ['id'])]`.
`#[Getter]`, `#[Setter]`, and `#[With]` cannot target static properties or
properties with hooks. `#[Setter]` and `#[With]` additionally reject readonly
properties. `#[Constructor]` cannot be used when the class already declares
`__construct()`, and required constructor properties must precede properties
with defaults. A generated method name that conflicts with a declared or
inherited final method is a compile-time error.
## Examples
### 1. Native types — compile-time numeric speedup
```php
<?php
use native_types;
function fib(int $n): int
{
if ($n == 1 || $n == 2) {
return 1;
}
return fib($n - 1) + fib($n - 2);
}
function main(int $argc, array $argv): void
{
$n = (int)$argv[1];
$begin = microtime(true);
echo fib($n) . "\n";
echo "Time: " . (microtime(true) - $begin) . "\n";
}
```
```bash
bin/tpc.php fib.php -O3 -o fib
./fib 30
```
With `use native_types`, `int` variables become C++ `int64_t` and arithmetic
compiles to plain CPU instructions instead of ZendVM calls.
### 2. High-precision numerics
```php
<?php
declare(strict_types=1);
use native_types;
function main(): void
{
// 54-digit integer — automatically detected and stored as bigInt
$a = std::bigInt("123456789012345678901234567890123456789012345678901234");
$b = std::bigInt("987654321098765432109876543210987654321098765432109876");
echo $a->add($b)->toString() . "\n"; // exact, no overflow
// Exact decimal arithmetic — no binary floating-point error
$c = std::decimal("0.1")->add(std::decimal("0.2"));
echo $c->toString() . "\n"; // "0.3"
// 256-bit floating point
$pi = std::bigFloat("3.14159265358979323846264338327950288419716939937510");
echo $pi->mul(2)->toString() . "\n";
}
```
See [High-precision types](docs/HIGH_PRECISION_TYPES.md) and
[Native types](docs/NATIVE_TYPES.md).
### 3. Strongly-typed containers
```php
<?php
use native_types;
function main(): void
{
$vector = std::vector(Type::Int);
$vector[] = 1;
$vector[] = 2;
$vector[] = 3;
$sum = 0;
foreach ($vector as $value) {
$sum += $value;
}
echo $sum . "\n"; // 6
echo $vector[1] . "\n"; // 2
// key-value map with fixed key/value types
$map = std::ordered_map(Type::String, Type::Int);
$map["a"] = 1;
$map["b"] = 2;
}
```
See [Std containers](docs/STD_CONTAINERS.md).
### 4. Universal methods
```php
<?php
function main(): void
{
$s = "hello world";
echo $s->length() . "\n"; // strlen()
echo $s->upper() . "\n"; // strtoupper()
echo $s->substr(0, 5) . "\n"; // substr()
$arr = [1, 3, 5, 7, 9];
echo $arr->count() . "\n"; // count()
var_dump($arr->contains(3)); // in_array()
$big = std::bigInt("12345678901234567890");
echo $big->mul(2)->toString() . "\n";
}
```
Method calls on primitives are resolved at compile time into direct C/C++
function calls — no vtable lookup, no reflection, no runtime dispatch. See
[Universal methods](docs/UNIVERSAL_METHODS.md).
### 5. Mixed C++ / PHP
Write performance-critical kernels in C++ and call them from PHP:
```cpp
// math.cpp
#include <phpx.h>
using namespace php;
Int php_fast_sum(Int a, Int b) {
return a + b;
}
```
```php
<?php
// math.stub.php — declares the C++ function signature
function fast_sum(int $a, int $b): int {}
```
```php
<?php
function main(): void
{
echo fast_sum(3, 4) . "\n"; // 7
}
```
Add `math.cpp`, `math.stub.php`, and the calling PHP source to the same project
configuration. The `php_` C++ symbol prefix is the TypePHP callable ABI; stub
functions provide type metadata only and must not contain an implementation.
See [Mixed C++/PHP](docs/MIXED_CPP_PHP.md).
## Benchmark
### PHP language benchmarks (from php-src)
TypePHP runs the official `bench.php` and `micro_bench.php` language
benchmarks that ship with the PHP source tree, compiled with `-O3`:
| Benchmark | Interpreted PHP | TypePHP AOT (`-O3`) | Speedup |
|---|---|---|---|
| `bench.php` (total) | 5.034 s | **0.603 s** | ~8× |
| `micro_bench.php` (total) | 13.045 s | **2.021 s** | ~6.5× |
Both benchmarks measure core PHP language performance — function calls, object
property access, array/hash access, string handling, control flow, and more.
The checked-in workloads are [`examples/bench.php`](examples/bench.php) and
[`examples/micro_bench.php`](examples/micro_bench.php).
These numbers are a project measurement snapshot, not a performance guarantee.
PHP version, compiler, CPU, optimization flags, and enabled extensions can all
change the result; compare on the same machine with the same workload before
making deployment decisions.
### std::array vs PHP array
A 10000×100000 element update loop, comparing PHP arrays against TypePHP's
`std::array` and native C++:
| Implementation | Time |
|---|---|
| PHP array (JIT) | 67.6 s |
| `std::array` (TypePHP AOT) | **6.4 s** |
| C++ `std::vector` | 6.2 s |
`std::array` is roughly **10× faster** than PHP arrays and performs
close to the hand-written C++ result in this workload. See the benchmark in
[Std containers](docs/STD_CONTAINERS.md).
## Command Line
```bash
bin/tpc.php <file|dir|project.yml> [options] [-- program-args...]
``` ```
添加路径 Common usage:
```bash
# Compile a single file
bin/tpc.php app.php
# Optimize and run, passing args to the program after `--`
bin/tpc.php app.php -O3 -r -- --flag value
# Compile a project defined in project.yml
bin/tpc.php project.yml -O2 -j 8
# Build a PHP extension
bin/tpc.php extension/ -m ext -o my_extension
# Only generate C++ (skip compile & link)
bin/tpc.php app.php --dry --build-dir /tmp/typephp-build
# Compile to WASI 0.2
bin/tpc.php --wasm app.php
# Compile for the browser (requires jco)
bin/tpc.php --wasm=browser app.php
``` ```
/home/swoole/workspace/projects/phpx/lib
/opt/php-8.4/lib/ Key options:
| Option | Description |
|---|---|
| `-O <0-3>` | Optimization level (default `0`) |
| `-d`, `--debug` | Debug build with symbols and source tracking |
| `-o`, `--output <file>` | Output file name |
| `-m`, `--mode <bin\|lib\|ext>` | Build mode (default `bin`) |
| `-r`, `--run` | Run after a successful build |
| `-j`, `--job <num>` | Parallel compile jobs (default `4`) |
| `-f`, `--force` | Rebuild reusable PHPX objects instead of using the cache |
| `--build-dir <dir>` | Directory for generated C++ and intermediates |
| `--dry` | Generate C++ only, skip compile and link |
| `--php-version <8.4\|8.5>` | PHP syntax version to accept |
| `--cxx-std <ver>` | C++ standard (e.g. `c++17`, `c++20`) |
| `--march <arch>` | Target instruction set (e.g. `native`) |
| `--target-platform <triple>` | Cross-compilation target triple |
| `--lto` | Enable link-time optimization |
| `--sanitize <type>` | Enable a sanitizer (e.g. `address`) |
| `--profile` | Enable Linux gperftools profiling |
| `--format` | Format generated C++ with clang-format |
| `--no-literal-strings` | Disable the literal-string table optimization |
| `--no-progress`, `--no-color` | CI-friendly output controls |
| `-I`, `-D`, `-L`, `-l` | Repeatable native include, define, library path, and library options |
Run `bin/tpc.php --help` for the authoritative, up-to-date list. See
[Compiler CLI](docs/COMPILER_CLI.md) for details, including Bash completion:
```bash
source <(./tpc --generate-completion=bash)
``` ```
## Release packaging ## Troubleshooting
- **`libphp.so` / `libphp.dylib` is missing:** install/build the matching PHP embed SAPI, set
`PHP_HOME`, or let `bin/tpc.php` offer the interactive Linux installer.
- **PHPX cannot be found:** set `PHPX_HOME` to a PHPX installation containing
`include/` and `lib/libphpx.so` (or the platform equivalent), then build PHPX
before compiling the project.
- **Startup crashes or ABI errors:** the PHP headers, `php-config`, `libphp`,
and loaded extension ABI must agree on the PHP version and ZTS/NTS mode. Do
not mix artifacts from different PHP builds.
- **Incremental builds are unexpectedly slow:** keep a stable `--build-dir` so
object and PCH caches can be reused. When an external test runner already
runs several tests concurrently, avoid multiplying that concurrency by an
unnecessarily large `tpc -j` value.
- **A project compiles with `bin/tpc.php` but fails with `tpc`:** reproduce with
the self-hosted compiler. Bootstrap execution can expose dynamic-call or ABI
paths that the PHP-hosted compiler does not exercise.
## Python bridge
Use the same PHP entry point on Windows, Linux, and macOS: TypePHP ships a Python tool submodule that shares the `tpc` entry point:
```shell ```shell
php package.php # Generate IDE helpers for Python modules
./tpc --gen-python-helper math
./tpc --gen-python-helper numpy --output-dir .ide-helper
# Convert a Python script to TypePHP
./tpc --convert-python-to-php script.py > script.php
```
See [Python tool submodule](docs/python/tools.md).
## Development and testing
Install development dependencies and run the compiler unit suite:
```bash
composer install
PHPX_HOME=/path/to/phpx vendor/bin/phpunit
```
PHPT is the end-to-end suite. Build the self-hosted compiler first and pass it
explicitly to the test runner; using the Zend PHP executable as `--compiler`
does not test the deployed compiler:
```bash
PHPX_HOME=/path/to/phpx php bin/tpc.php project.yml --job 2 --no-progress
php run-tests.php -q -j8 --compiler ./tpc tests/compiler
``` ```
Windows packaging requires `PHP_HOME` and `PHPX_HOME`; Linux packaging requires Static analysis and the source-derived coverage matrix are separate checks:
UPX; macOS uses `strip` when available. TypePHP rejects 32-bit targets and
supports common 64-bit CPU architectures, including x86-64 and ARM64. ```bash
composer analyse
php bin/analyze-test-coverage.php
php bin/analyze-test-coverage.php \
--format=markdown --output=build/test-coverage.md --strict
```
The coverage tool reports PHP version × feature × positive compilation ×
runtime semantics × negative diagnostics, plus concrete PHP-parser AST nodes.
It intentionally does not publish a single percentage without an explicit
denominator. See [Test coverage analyzer](docs/TEST_COVERAGE_ANALYZER.md).
GitHub Actions runs PHPUnit and self-hosted PHPT on PHP 8.4 and 8.5. Changes to
compiler behavior should add a focused PHPUnit test for internal/code-generation
rules and a PHPT whenever runtime output or diagnostics are observable.
## Documentation
- [Quick Start](docs/QUICKSTART.md) — minimal compilation flow
- [Compilation modes](docs/COMPILATION_MODES.md) — `bin`, `ext`, `lib`
- [Compiler CLI](docs/COMPILER_CLI.md) — CLI arguments and project config
- [Incompatible PHP features](docs/INCOMPATIBLE_PHP_FEATURES.md) — current limits
- [Native types](docs/NATIVE_TYPES.md) — native scalar types
- [High-precision types](docs/HIGH_PRECISION_TYPES.md) — BigInt / Decimal / BigFloat
- [Std containers](docs/STD_CONTAINERS.md) — strongly-typed containers
- [Universal methods](docs/UNIVERSAL_METHODS.md) — compile-time method resolution
- [Compile-time functions](docs/COMPILE_TIME_FUNCTIONS.md) — `any()`, `refval()`, `objval()`, …
- [Mixed C++/PHP](docs/MIXED_CPP_PHP.md) — C++/PHP interop
- [`#[Immutable]`](docs/IMMUTABLE.md) — compile-time read-only contracts
- [`#[ArrayDef]`](docs/ARRAY_DEF.md) — typed array-property contracts
- [Property hooks](docs/PROPERTY_HOOKS.md) — PHP 8.4 hook lowering and runtime metadata
- [Object storage models](docs/OBJECT_STORAGE_AND_PASSING_MODELS.md) — Zend object, Box, and Native class boundaries
- [Generators](docs/YIELD_GENERATOR.md) — generator lowering and lifecycle
- [Test coverage analyzer](docs/TEST_COVERAGE_ANALYZER.md) — AST and feature evidence matrix
- [WASI build](docs/WASI_BUILD.md) — WASI targets
## License
TypePHP is licensed under the [GNU General Public License v3.0](LICENSE).
## Community
- Repository: <https://github.com/swoole/typephp>
- Copyright © 2026 上海识沃网络科技有限公司 (Swoole)

@ -1,52 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<!-- 程序集标识 -->
<assemblyIdentity
type="win32"
name="AotCompiler.App"
version="1.0.0.0" />
<!-- 信任信息 / UAC 权限级别 -->
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<!--
asInvoker: 以当前用户权限运行(推荐默认值)
highestAvailable: 请求当前用户能获得的最高权限
requireAdministrator: 要求管理员权限
-->
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
</requestedPrivileges>
</security>
</trustInfo>
<!-- 应用程序兼容性 -->
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- Windows 10 / 11 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
<!-- Windows 8.1 -->
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />
<!-- Windows 8 -->
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />
<!-- Windows 7 -->
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />
</application>
</compatibility>
<!-- DPI 感知(Windows 10 版本 1607+) -->
<asmv3:application xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
<asmv3:windowsSettings>
<!--
PerMonitorV2: 逐显示器 DPI 感知 v2(推荐,支持混合模式 DPI 缩放)
PerMonitor: 逐显示器 DPI 感知 v1
System: 系统 DPI 感知
None / 不设置: DPI 不感知(系统会自动缩放)
-->
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
</asmv3:windowsSettings>
</asmv3:application>
</assembly>

495
ast.py

@ -1,495 +0,0 @@
#!/usr/bin/env python3
"""
使用 libclang 解析 C++ 代码并添加头文件路径
"""
import clang.cindex
from clang.cindex import Index, CursorKind, TypeKind, StorageClass
import json
import sys
import os
from pathlib import Path
import subprocess
import argparse
class PHPConfigHelper:
"""PHP 配置辅助类"""
def __init__(self, php_config_path='php-config'):
self.php_config = php_config_path
self._check_availability()
def _check_availability(self):
"""检查 php-config 是否可用"""
try:
result = subprocess.run(
[self.php_config, '--version'],
capture_output=True,
text=True,
check=True
)
print(f"✓ 找到 PHP {result.stdout.strip()}")
except (FileNotFoundError, subprocess.CalledProcessError) as e:
print(f"警告: php-config 不可用: {e}")
# 尝试使用常见路径
common_paths = [
'/usr/bin/php-config',
'/usr/local/bin/php-config',
'/opt/php/bin/php-config'
]
for path in common_paths:
if os.path.exists(path):
self.php_config = path
try:
result = subprocess.run(
[self.php_config, '--version'],
capture_output=True,
text=True,
check=True
)
print(f"✓ 找到 PHP {result.stdout.strip()}{path}")
return
except (FileNotFoundError, subprocess.CalledProcessError):
continue
print("警告: php-config 在任何常见路径都不可用,使用默认路径")
def get_includes(self):
"""
获取 include 路径列表
Returns:
list: 头文件路径列表不带 -I 前缀
"""
try:
result = subprocess.run(
[self.php_config, '--includes'],
capture_output=True,
text=True,
check=True
)
# 解析输出: "-I/path1 -I/path2" -> ['/path1', '/path2']
includes = []
for flag in result.stdout.strip().split():
if flag.startswith('-I'):
includes.append(flag[2:])
return includes
except (subprocess.CalledProcessError, FileNotFoundError):
print("警告: 无法获取 PHP includes,使用默认路径")
return ['/usr/include/php', '/usr/include/php/20210902'] # 默认路径
def get_include_dir(self):
"""获取主 include 目录"""
try:
result = subprocess.run(
[self.php_config, '--include-dir'],
capture_output=True,
text=True,
check=True
)
return result.stdout.strip()
except (subprocess.CalledProcessError, FileNotFoundError):
return '/usr/include/php'
def get_extension_dir(self):
"""获取扩展目录"""
try:
result = subprocess.run(
[self.php_config, '--extension-dir'],
capture_output=True,
text=True,
check=True
)
return result.stdout.strip()
except (subprocess.CalledProcessError, FileNotFoundError):
return '/usr/lib/php'
def get_version(self):
"""获取 PHP 版本"""
try:
result = subprocess.run(
[self.php_config, '--version'],
capture_output=True,
text=True,
check=True
)
return result.stdout.strip()
except (subprocess.CalledProcessError, FileNotFoundError):
return 'unknown'
def get_php_binary(self):
"""获取 PHP 二进制路径"""
try:
result = subprocess.run(
[self.php_config, '--php-binary'],
capture_output=True,
text=True,
check=True
)
return result.stdout.strip()
except (subprocess.CalledProcessError, FileNotFoundError):
return 'php'
def get_configure_options(self):
"""获取配置选项"""
try:
result = subprocess.run(
[self.php_config, '--configure-options'],
capture_output=True,
text=True,
check=True
)
return result.stdout.strip()
except (subprocess.CalledProcessError, FileNotFoundError):
return ''
def get_all_info(self):
"""获取所有配置信息"""
return {
'version': self.get_version(),
'includes': self.get_includes(),
'include_dir': self.get_include_dir(),
'extension_dir': self.get_extension_dir(),
'php_binary': self.get_php_binary(),
'configure_options': self.get_configure_options(),
}
class ClangParser:
def __init__(self, libclang_path=None):
"""
初始化 Clang 解析器
Args:
libclang_path: libclang 库的路径可选
"""
if libclang_path:
try:
clang.cindex.Config.set_library_file(libclang_path)
except Exception as e:
print(f"警告: 无法设置 libclang 路径 {libclang_path}: {e}")
print("尝试使用默认路径...")
try:
self.index = Index.create()
except Exception as e:
print(f"错误: 无法创建 Clang 索引: {e}")
print("请确保已安装 python3-clang 和 clang 库")
raise
def parse_file(self, filename, include_paths=None, defines=None,
compiler_args=None, language='c++'):
"""
解析 C++ 文件
Args:
filename: 要解析的文件路径
include_paths: 头文件搜索路径列表
defines: 宏定义列表 ['MACRO=value', 'DEBUG']
compiler_args: 额外的编译器参数
language: 语言类型 ('c', 'c++', 'objective-c')
Returns:
TranslationUnit 对象
"""
if not os.path.exists(filename):
raise FileNotFoundError(f"文件不存在: {filename}")
args = []
# 1. 设置语言标准
if language == 'c++':
args.extend([
'-x', 'c++',
'-std=c++14', # 更标准的 C++ 版本
])
elif language == 'c':
args.extend(['-x', 'c', '-std=c11'])
# 2. 添加头文件搜索路径
if include_paths:
for path in include_paths:
if os.path.exists(path): # 检查路径是否存在
args.append(f'-I{path}')
else:
print(f"警告: 包含路径不存在: {path}")
# 3. 添加宏定义
if defines:
for define in defines:
args.append(f'-D{define}')
# 4. 添加额外的编译器参数
if compiler_args:
args.extend(compiler_args)
# 5. 常用的编译选项
args.extend([
'-Wno-pragma-once-outside-header', # 忽略警告
'-ferror-limit=0', # 不限制错误数量
'-fno-delayed-template-parsing', # 避免某些 C++ 模板解析问题
'-w', # 禁用所有警告以减少输出
])
print(f"编译参数: {' '.join(args)}")
# 解析文件
try:
tu = self.index.parse(
filename,
args=args,
options=clang.cindex.TranslationUnit.PARSE_DETAILED_PROCESSING_RECORD
)
except Exception as e:
print(f"解析文件时出错: {e}")
print("尝试使用最小参数集...")
# 尝试使用最小参数集
minimal_args = ['-x', 'c++', '-std=c++14', '-w']
if include_paths:
for path in include_paths:
if os.path.exists(path):
minimal_args.append(f'-I{path}')
try:
tu = self.index.parse(
filename,
args=minimal_args,
options=clang.cindex.TranslationUnit.PARSE_DETAILED_PROCESSING_RECORD
)
print("使用最小参数集成功解析")
except Exception as e2:
print(f"使用最小参数集也失败: {e2}")
print("尝试解析不包含头文件的简化版本...")
# 创建一个临时文件,移除头文件包含行
temp_filename = filename + ".tmp"
with open(filename, 'r') as original:
lines = original.readlines()
# 移除 #include 行
filtered_lines = [line for line in lines if not line.strip().startswith('#include')]
with open(temp_filename, 'w') as temp:
temp.writelines(filtered_lines)
try:
tu = self.index.parse(
temp_filename,
args=minimal_args,
options=clang.cindex.TranslationUnit.PARSE_DETAILED_PROCESSING_RECORD
)
print("解析简化版本成功")
# 清理临时文件
os.remove(temp_filename)
except Exception as e3:
print(f"简化版本也失败: {e3}")
# 清理临时文件
if os.path.exists(temp_filename):
os.remove(temp_filename)
raise
# 检查诊断信息
if tu.diagnostics:
print(f"\n诊断信息 ({len(tu.diagnostics)} 个):")
error_count = 0
warning_count = 0
for diag in tu.diagnostics:
if diag.severity >= 3: # 错误级别
error_count += 1
else: # 警告级别
warning_count += 1
print(f"错误: {error_count}, 警告: {warning_count}")
# 只显示前几个诊断信息,避免输出过多
for i, diag in enumerate(tu.diagnostics):
if i >= 5: # 只显示前5个
print("... 还有更多诊断信息")
break
print(f" [{diag.severity}] {diag.spelling}")
if diag.location.file:
print(f" at {diag.location.file.name}:{diag.location.line}")
return tu
def extract_functions(self, tu, name_prefixes=None):
"""
提取函数定义
Args:
tu: TranslationUnit 对象
name_prefixes: 函数名前缀过滤列表
Returns:
函数信息列表
"""
functions = []
def visit_node(node, depth=0):
# 只处理函数声明/定义
if node.kind == CursorKind.FUNCTION_DECL:
try:
func_info = self.parse_function(node)
# 过滤函数名
if name_prefixes:
if any(func_info['name'].startswith(prefix)
for prefix in name_prefixes):
functions.append(func_info)
else:
functions.append(func_info)
except Exception as e:
print(f"解析函数时出错: {e}")
# 递归访问子节点
for child in node.get_children():
visit_node(child, depth + 1)
visit_node(tu.cursor)
return functions
def parse_function(self, cursor):
"""
解析函数详细信息
"""
# 检查方法是否存在
def safe_call(method, default_value=None):
try:
return method()
except AttributeError:
return default_value
# 基本信息
func_info = {
'name': cursor.spelling,
'displayName': cursor.displayname,
'mangledName': cursor.mangled_name,
'returnType': cursor.result_type.spelling,
'isStatic': cursor.storage_class == StorageClass.STATIC,
'isInline': safe_call(lambda: cursor.is_inline_function(), False),
'isVirtual': safe_call(lambda: cursor.is_virtual_method(), False),
'isConst': safe_call(lambda: cursor.is_const_method(), False),
'location': {
'file': str(cursor.location.file) if cursor.location.file else None,
'line': cursor.location.line,
'column': cursor.location.column,
},
'parameters': [],
'namespaces': self.get_namespaces(cursor),
}
# 解析参数
for arg in cursor.get_arguments():
param_info = {
'name': arg.spelling or f'arg{len(func_info["parameters"])}',
'type': arg.type.spelling,
'canonicalType': arg.type.get_canonical().spelling,
}
# 检查是否有默认值
try:
for token in arg.get_tokens():
if token.spelling == '=':
# 有默认值
param_info['hasDefault'] = True
break
except:
# 如果无法获取 tokens,跳过默认值检查
pass
func_info['parameters'].append(param_info)
return func_info
def get_namespaces(self, cursor):
"""
获取函数所在的命名空间
"""
namespaces = []
parent = cursor.semantic_parent
while parent and parent.kind != CursorKind.TRANSLATION_UNIT:
if parent.kind == CursorKind.NAMESPACE:
namespaces.insert(0, parent.spelling)
parent = parent.semantic_parent
return namespaces
def main():
parser = argparse.ArgumentParser(description='使用 libclang 解析 C++ 代码并提取函数信息')
parser.add_argument('filename', help='要解析的 C++ 文件路径')
parser.add_argument('--libclang-path', help='libclang 库路径')
parser.add_argument('--include-paths', nargs='*', help='额外的包含路径')
parser.add_argument('--function-prefixes', nargs='*', help='函数名前缀过滤器')
args = parser.parse_args()
if not os.path.exists(args.filename):
print(f"错误: 文件不存在: {args.filename}")
sys.exit(1)
try:
# 创建解析器
parser_obj = ClangParser(libclang_path=args.libclang_path)
# 配置头文件路径
include_paths = args.include_paths or [
"/usr/include/linux",
"/home/swoole/workspace/projects/phpx/include"
]
# 尝试获取 PHP 配置的头文件路径
try:
php_config = PHPConfigHelper()
php_includes = php_config.get_includes()
include_paths.extend(php_includes)
except Exception as e:
print(f"警告: 无法获取 PHP 配置: {e}")
print("继续使用默认路径...")
# 配置宏定义
defines = [
'HAVE_CONFIG_H',
'ZEND_ENABLE_STATIC_TSRMLS_CACHE=1',
]
# 额外的编译器参数
compiler_args = [
'-fparse-all-comments', # 解析所有注释
'-Wno-unknown-pragmas',
]
# 解析文件
tu = parser_obj.parse_file(
args.filename,
include_paths=include_paths,
defines=defines,
compiler_args=compiler_args,
language='c++'
)
# 提取函数
name_prefixes = args.function_prefixes or None
functions = parser_obj.extract_functions(tu, name_prefixes=name_prefixes)
# 输出结果
output = {
'file': args.filename,
'functions': functions,
'total': len(functions),
}
print(json.dumps(output, indent=2, ensure_ascii=False))
except clang.cindex.TranslationUnitLoadError as e:
print(f"翻译单元加载错误: {e}")
print("这通常意味着 C++ 代码包含语法错误或缺少必要的头文件")
sys.exit(1)
except Exception as e:
print(f"错误: {e}")
sys.exit(1)
if __name__ == '__main__':
main()

@ -0,0 +1,146 @@
#!/usr/bin/env php
<?php
/**
* This file is part of Swoole-Compiler(AOT).
*
* @link https://www.swoole.com/
* @contact service@swoole.com
*/
declare(strict_types=1);
use TypePhp\Testing\TestCoverageAnalyzer;
require __DIR__ . '/bootstrap.php';
$format = 'summary';
$output = null;
$includePhpUnit = true;
$strict = false;
$phpVersions = ['8.4', '8.5'];
$paths = [];
foreach (array_slice($argv, 1) as $argument) {
if ($argument === '--help' || $argument === '-h') {
printUsage($argv[0]);
exit(0);
}
if ($argument === '--no-phpunit') {
$includePhpUnit = false;
continue;
}
if ($argument === '--strict') {
$strict = true;
continue;
}
if (str_starts_with($argument, '--format=')) {
$format = substr($argument, strlen('--format='));
continue;
}
if (str_starts_with($argument, '--output=')) {
$output = substr($argument, strlen('--output='));
continue;
}
if (str_starts_with($argument, '--php-versions=')) {
$phpVersions = array_values(array_filter(array_map('trim', explode(',', substr($argument, strlen('--php-versions='))))));
continue;
}
if (str_starts_with($argument, '-')) {
fwrite(STDERR, 'Unknown option: ' . $argument . PHP_EOL);
exit(2);
}
$paths[] = $argument;
}
if (!in_array($format, ['summary', 'json', 'markdown'], true)) {
fwrite(STDERR, 'Invalid format. Expected summary, json or markdown.' . PHP_EOL);
exit(2);
}
if ($phpVersions === []) {
fwrite(STDERR, 'At least one target PHP version is required.' . PHP_EOL);
exit(2);
}
foreach ($phpVersions as $version) {
if (!preg_match('/^\d+\.\d+$/', $version)) {
fwrite(STDERR, 'Invalid PHP version: ' . $version . PHP_EOL);
exit(2);
}
}
if ($paths === []) {
$paths = ['tests/compiler'];
}
try {
$analyzer = new TestCoverageAnalyzer(TYPEPHP_ROOT_PATH, $phpVersions);
$report = $analyzer->analyze(
$paths,
$includePhpUnit ? TYPEPHP_ROOT_PATH . '/phpunit/src' : null,
$includePhpUnit ? TYPEPHP_ROOT_PATH . '/phpunit/code' : null,
);
} catch (Throwable $error) {
fwrite(STDERR, 'Coverage analysis failed: ' . $error->getMessage() . PHP_EOL);
exit(1);
}
$rendered = match ($format) {
'summary' => $analyzer->renderSummary($report),
'markdown' => $analyzer->renderMarkdown($report),
'json' => json_encode($report, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR) . PHP_EOL,
};
if ($output === null) {
echo $rendered;
} else {
$outputPath = isAbsolutePath($output) ? $output : TYPEPHP_ROOT_PATH . DIRECTORY_SEPARATOR . $output;
$directory = dirname($outputPath);
if (!is_dir($directory) && !mkdir($directory, 0777, true) && !is_dir($directory)) {
fwrite(STDERR, 'Unable to create output directory: ' . $directory . PHP_EOL);
exit(1);
}
if (file_put_contents($outputPath, $rendered) === false) {
fwrite(STDERR, 'Unable to write report: ' . $outputPath . PHP_EOL);
exit(1);
}
echo 'Wrote ', $format, ' coverage report: ', relativePath(TYPEPHP_ROOT_PATH, $outputPath), PHP_EOL;
}
if ($strict && ($report['parse_errors'] !== [] || $report['unresolved_phpunit_fixtures'] !== [])) {
exit(1);
}
function printUsage(string $script): void
{
echo <<<USAGE
Usage:
php {$script} [options] [PHPT path ...]
Options:
--format=summary|json|markdown Output format (default: summary)
--output=<file> Write the report to a file
--php-versions=8.4,8.5 Target PHP version columns
--no-phpunit Do not scan PHPUnit compiler fixtures
--strict Fail on parse issues or unresolved fixture links
-h, --help Show this help
Examples:
php {$script}
php {$script} --format=markdown --output=build/test-coverage.md
php {$script} --format=json tests/compiler/type_decl tests/compiler/basic
The tool reports separate, explicitly denominated AST-node, positive compile,
runtime semantic and negative diagnostic coverage. It never emits a combined
overall percentage.
USAGE;
}
function isAbsolutePath(string $path): bool
{
return $path !== '' && ($path[0] === '/' || preg_match('/^[A-Za-z]:[\\\\\/]/', $path) === 1);
}
function relativePath(string $root, string $path): string
{
$prefix = rtrim($root, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
return str_starts_with($path, $prefix) ? substr($path, strlen($prefix)) : $path;
}

@ -1,6 +1,17 @@
<?php <?php
define("ROOT_PATH", dirname(__DIR__)); /**
define('DEBUG', true); * This file is part of TypePHP(AOT).
*
* @link https://www.swoole.com/aot/
* @contact service@swoole.com
*/
require ROOT_PATH . '/vendor/autoload.php'; define('TYPEPHP_ROOT_PATH', dirname(__DIR__));
require ROOT_PATH . '/src/functions.php'; define('TYPEPHP_DEBUG', true);
// Composer bin proxies provide the consuming project's autoloader. A source
// checkout and a packaged compiler keep their own autoloader below TYPEPHP_ROOT_PATH.
$autoloadPath = $GLOBALS['_composer_autoload_path'] ?? TYPEPHP_ROOT_PATH . '/vendor/autoload.php';
unset($GLOBALS['_composer_autoload_path']);
require $autoloadPath;
unset($autoloadPath);

@ -8,7 +8,7 @@
* *
* 示例: * 示例:
* php bin/dump-ast.php examples/hello.php * php bin/dump-ast.php examples/hello.php
* php bin/dump-ast.php src/functions.php * php bin/dump-ast.php src/compiler.php
*/ */
require_once __DIR__ . '/../vendor/autoload.php'; require_once __DIR__ . '/../vendor/autoload.php';

@ -48,7 +48,7 @@ function showUsage(): void
USAGE; USAGE;
} }
function main(array $argv): void function extractorMain(array $argv): void
{ {
// 解析命令行参数 // 解析命令行参数
$options = [ $options = [
@ -153,5 +153,5 @@ function main(array $argv): void
// 运行主函数 // 运行主函数
if (php_sapi_name() === 'cli') { if (php_sapi_name() === 'cli') {
main($argv); extractorMain($argv);
} }

@ -1,9 +1,9 @@
#!/usr/bin/env php #!/usr/bin/env php
<?php <?php
require __DIR__ . '/bootstrap.php'; require __DIR__ . '/bootstrap.php';
require __DIR__ . '/../src/polyfills.php'; require TYPEPHP_ROOT_PATH . '/src/polyfills.php';
require __DIR__ . '/../src/gen_stub.php'; require TYPEPHP_ROOT_PATH . '/src/gen_stub.php';
require __DIR__ . '/../src/compiler.php'; require TYPEPHP_ROOT_PATH . '/src/compiler.php';
const TYPEPHP_PHP_SCRIPT_ENTRY = true; const TYPEPHP_PHP_SCRIPT_ENTRY = true;
main($argc, $argv); main($argc, $argv);

@ -0,0 +1,178 @@
#!/usr/bin/env bash
set -euo pipefail
readonly MIN_AGE_MINUTES=60
dry_run=false
tmp_root=/tmp
usage() {
cat <<'EOF'
Usage: ./cleanup-typephp-tmp.sh [options]
Remove inactive TypePHP temporary files and directories from /tmp.
An entry is skipped when it or any of its descendants was modified or
metadata-changed during the last 60 minutes.
Recognized prefixes: typephp-, typephp_, utils_test_, and phpx-windows.
Options:
-n, --dry-run Show what would be removed without deleting anything
--tmp-dir DIR Use another temporary directory (primarily for testing)
-h, --help Show this help
EOF
}
while (($# > 0)); do
case "$1" in
-n | --dry-run)
dry_run=true
;;
--tmp-dir)
if (($# < 2)); then
echo "Error: --tmp-dir requires a directory." >&2
exit 2
fi
tmp_root=$2
shift
;;
-h | --help)
usage
exit 0
;;
*)
echo "Error: unknown option: $1" >&2
usage >&2
exit 2
;;
esac
shift
done
if [[ ! -d "$tmp_root" ]]; then
echo "Error: temporary directory does not exist: $tmp_root" >&2
exit 1
fi
tmp_root=$(realpath -e -- "$tmp_root")
if [[ -z "$tmp_root" || "$tmp_root" == / ]]; then
echo "Error: refusing to use an unsafe temporary directory." >&2
exit 1
fi
readonly tmp_root
readonly owner_uid=${SUDO_UID:-$(id -u)}
format_size() {
local kib=$1
awk -v kib="$kib" 'BEGIN {
if (kib >= 1048576) {
printf "%.2f GiB", kib / 1048576
} else if (kib >= 1024) {
printf "%.2f MiB", kib / 1024
} else {
printf "%d KiB", kib
}
}'
}
entry_size_kib() {
local output
output=$(du -sk -- "$1" 2>/dev/null) || {
printf '0'
return
}
printf '%s' "${output%%$'\t'*}"
}
has_recent_entry() {
local candidate=$1
local recent
# Check the complete tree. Looking only at the top-level directory mtime
# would miss writes to an existing file in a nested build directory.
if ! recent=$(find -P "$candidate" -xdev \
\( -mmin "-${MIN_AGE_MINUTES}" -o -cmin "-${MIN_AGE_MINUTES}" \) \
-printf '1' -quit 2>/dev/null); then
return 0
fi
[[ -n "$recent" ]]
}
matched_count=0
removed_count=0
skipped_recent_count=0
skipped_error_count=0
total_kib=0
while IFS= read -r -d '' candidate; do
((matched_count += 1))
# Keep the target constrained to one direct child of the selected root.
if [[ "$candidate" != "$tmp_root"/* || "${candidate%/*}" != "$tmp_root" ]]; then
echo "[skip unsafe] $candidate" >&2
((skipped_error_count += 1))
continue
fi
if has_recent_entry "$candidate"; then
echo "[skip recent] $candidate"
((skipped_recent_count += 1))
continue
fi
size_kib=$(entry_size_kib "$candidate")
size=$(format_size "$size_kib")
# The size scan can take noticeable time for a large build tree. Recheck
# freshness immediately before acting in case a compiler started using it.
if has_recent_entry "$candidate"; then
echo "[skip recent] $candidate"
((skipped_recent_count += 1))
continue
fi
if $dry_run; then
echo "[would remove] $size $candidate"
((removed_count += 1))
((total_kib += size_kib))
continue
fi
if [[ -d "$candidate" && ! -L "$candidate" ]]; then
if rm -rf --one-file-system -- "$candidate"; then
echo "[removed] $size $candidate"
((removed_count += 1))
((total_kib += size_kib))
else
echo "[skip error] failed to remove: $candidate" >&2
((skipped_error_count += 1))
fi
elif rm -f -- "$candidate"; then
echo "[removed] $size $candidate"
((removed_count += 1))
((total_kib += size_kib))
else
echo "[skip error] failed to remove: $candidate" >&2
((skipped_error_count += 1))
fi
done < <(
find -P "$tmp_root" -mindepth 1 -maxdepth 1 -uid "$owner_uid" \
\( -name 'typephp-*' -o -name 'typephp_*' \
-o -name 'utils_test_*' -o -name 'phpx-windows*' \) -print0
)
if $dry_run; then
action='would remove'
else
action='removed'
fi
printf 'Summary: matched %d, %s %d (%s), skipped recent %d, errors %d.\n' \
"$matched_count" "$action" "$removed_count" "$(format_size "$total_kib")" \
"$skipped_recent_count" "$skipped_error_count"
if ((skipped_error_count > 0)); then
exit 1
fi

@ -0,0 +1,151 @@
# Bash completion for the TypePHP compiler.
# Generated by: tpc --generate-completion=bash
_typephp_tpc_complete_files()
{
local candidate
COMPREPLY=()
while IFS= read -r candidate; do
if [[ -d "$candidate" || "$candidate" == *.php || "$candidate" == *.yml || "$candidate" == *.yaml || "$candidate" == *.prof ]]; then
COMPREPLY+=("$candidate")
fi
done < <(compgen -f -- "$1")
}
_typephp_tpc_complete_python_files()
{
local candidate
COMPREPLY=()
while IFS= read -r candidate; do
if [[ -d "$candidate" || "$candidate" == *.py ]]; then
COMPREPLY+=("$candidate")
fi
done < <(compgen -f -- "$1")
}
_typephp_tpc_complete_paths()
{
local mode="$1" candidate
COMPREPLY=()
shift
while IFS= read -r candidate; do
COMPREPLY+=("$candidate")
done < <(compgen "$mode" -- "$1")
}
_typephp_tpc()
{
local current previous value candidate
current="${COMP_WORDS[COMP_CWORD]}"
previous=""
if (( COMP_CWORD > 0 )); then
previous="${COMP_WORDS[COMP_CWORD - 1]}"
fi
local index
for ((index = 1; index < COMP_CWORD; index++)); do
if [[ "${COMP_WORDS[index]}" == -- ]]; then
compopt -o default
return
fi
done
case "$previous" in
-O)
COMPREPLY=( $(compgen -W '0 1 2 3' -- "$current") )
return
;;
--optimize)
COMPREPLY=( $(compgen -W '0 1 2 3' -- "$current") )
return
;;
-m)
COMPREPLY=( $(compgen -W 'bin lib ext' -- "$current") )
return
;;
--mode)
COMPREPLY=( $(compgen -W 'bin lib ext' -- "$current") )
return
;;
--php-version)
COMPREPLY=( $(compgen -W '8.4 8.5' -- "$current") )
return
;;
--cxx-std)
COMPREPLY=( $(compgen -W 'c++17 c++20 c++23' -- "$current") )
return
;;
--sanitize)
COMPREPLY=( $(compgen -W 'address undefined' -- "$current") )
return
;;
--build-dir|--output-dir|-I|--include-path|-L|--link-path)
compopt -o filenames
_typephp_tpc_complete_paths -d "$current"
return
;;
--convert-python-to-php)
compopt -o filenames
_typephp_tpc_complete_python_files "$current"
return
;;
-o|--output)
compopt -o filenames
_typephp_tpc_complete_paths -f "$current"
return
;;
esac
case "$current" in
-O[0-3])
COMPREPLY=("$current")
return
;;
-O*)
value="${current#-O}"
COMPREPLY=( $(compgen -W '0 1 2 3' -P '-O' -- "$value") )
return
;;
--wasm=*)
value="${current#--wasm=}"
COMPREPLY=( $(compgen -W 'component browser' -P '--wasm=' -- "$value") )
return
;;
--generate-completion=*)
value="${current#--generate-completion=}"
COMPREPLY=( $(compgen -W 'bash' -P '--generate-completion=' -- "$value") )
return
;;
--build-dir=*)
value="${current#--build-dir=}"
compopt -o filenames
COMPREPLY=()
while IFS= read -r candidate; do
COMPREPLY+=("--build-dir=${candidate}")
done < <(compgen -d -- "$value")
return
;;
--output-dir=*)
value="${current#--output-dir=}"
compopt -o filenames
COMPREPLY=()
while IFS= read -r candidate; do
COMPREPLY+=("--output-dir=${candidate}")
done < <(compgen -d -- "$value")
return
;;
-* )
COMPREPLY=( $(compgen -W '-O --optimize -o --output -h --help -v --version --profile --no-literal-strings --php-version -f --force -m --mode -r --run --debug -j --job --no-console --sanitize --cxx-std --march --target-platform --no-color --build-dir --dry -I --include-path -D --define --no-progress --lto --format -l --link-lib -L --link-path --wasm --wasm= --gen-python-helper --convert-python-to-php --output-dir --output-dir= --build-dir= --generate-completion=' -- "$current") )
return
;;
esac
compopt -o filenames
_typephp_tpc_complete_files "$current"
}
complete -F _typephp_tpc tpc
complete -F _typephp_tpc ./tpc
complete -F _typephp_tpc tpc.php
complete -F _typephp_tpc bin/tpc.php
complete -F _typephp_tpc ./bin/tpc.php

@ -7,17 +7,20 @@
], ],
"require": { "require": {
"php": ">=8.4 <8.6", "php": ">=8.4 <8.6",
"nikic/php-parser": "5.6.1", "composer-runtime-api": "^2.2",
"nikic/php-parser": "^5.6",
"league/climate": "^3.10", "league/climate": "^3.10",
"marcj/topsort": "^2.0", "marcj/topsort": "^2.0",
"symfony/var-dumper": "^8.0", "symfony/var-dumper": "^8.0",
"symfony/yaml": "^8.0", "symfony/yaml": "^8.0",
"swoole/phpx": "~2.4.3", "swoole/phpx": "~2.6.4",
"ajaxray/ansikit": "^0.3.1" "ajaxray/ansikit": "^0.3",
"ext-dom": "*"
}, },
"require-dev": { "require-dev": {
"phpunit/phpunit": "^10.4", "phpunit/phpunit": "^10.4",
"friendsofphp/php-cs-fixer": "^3.40" "friendsofphp/php-cs-fixer": "^3.40",
"phpstan/phpstan": "^2.2"
}, },
"autoload": { "autoload": {
"psr-4": { "psr-4": {

90
composer.lock generated

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "230c2948c23aa6b500c006a3846c8acf", "content-hash": "2d4aeff65ded28c1339be3c4f55f55eb",
"packages": [ "packages": [
{ {
"name": "ajaxray/ansikit", "name": "ajaxray/ansikit",
@ -342,21 +342,22 @@
}, },
{ {
"name": "swoole/phpx", "name": "swoole/phpx",
"version": "v2.4.3", "version": "v2.6.4",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/swoole/phpx.git", "url": "https://github.com/swoole/phpx.git",
"reference": "6424c2b9bd4e910fc7e446b1ece5699818c08454" "reference": "87a532b30fad8ff7b0a4afac6cd8aacc0fd01cbd"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/swoole/phpx/zipball/6424c2b9bd4e910fc7e446b1ece5699818c08454", "url": "https://api.github.com/repos/swoole/phpx/zipball/87a532b30fad8ff7b0a4afac6cd8aacc0fd01cbd",
"reference": "6424c2b9bd4e910fc7e446b1ece5699818c08454", "reference": "87a532b30fad8ff7b0a4afac6cd8aacc0fd01cbd",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"league/climate": "^3.10", "league/climate": "^3.10",
"marcj/topsort": "^2.0" "marcj/topsort": "^2.0",
"php": ">=8.4 <8.6"
}, },
"require-dev": { "require-dev": {
"friendsofphp/php-cs-fixer": "^3.75", "friendsofphp/php-cs-fixer": "^3.75",
@ -381,9 +382,9 @@
], ],
"support": { "support": {
"issues": "https://github.com/swoole/phpx/issues", "issues": "https://github.com/swoole/phpx/issues",
"source": "https://github.com/swoole/phpx/tree/v2.4.3" "source": "https://github.com/swoole/phpx/tree/v2.6.4"
}, },
"time": "2026-07-31T09:43:58+00:00" "time": "2026-08-25T08:02:28+00:00"
}, },
{ {
"name": "symfony/polyfill-ctype", "name": "symfony/polyfill-ctype",
@ -1461,6 +1462,70 @@
}, },
"time": "2022-02-21T01:04:05+00:00" "time": "2022-02-21T01:04:05+00:00"
}, },
{
"name": "phpstan/phpstan",
"version": "2.2.9",
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/13d6b4f347bad222da436580c8304fa6f83e6bd0",
"reference": "13d6b4f347bad222da436580c8304fa6f83e6bd0",
"shasum": ""
},
"require": {
"php": "^7.4|^8.0"
},
"conflict": {
"phpstan/phpstan-shim": "*"
},
"bin": [
"phpstan",
"phpstan.phar"
],
"type": "library",
"autoload": {
"files": [
"bootstrap.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Ondřej Mirtes"
},
{
"name": "Markus Staab"
},
{
"name": "Vincent Langlet"
}
],
"description": "PHPStan - PHP Static Analysis Tool",
"keywords": [
"dev",
"static analysis"
],
"support": {
"docs": "https://phpstan.org/user-guide/getting-started",
"forum": "https://github.com/phpstan/phpstan/discussions",
"issues": "https://github.com/phpstan/phpstan/issues",
"security": "https://github.com/phpstan/phpstan/security/policy",
"source": "https://github.com/phpstan/phpstan-src"
},
"funding": [
{
"url": "https://github.com/ondrejmirtes",
"type": "github"
},
{
"url": "https://github.com/phpstan",
"type": "github"
}
],
"time": "2026-08-22T07:38:16+00:00"
},
{ {
"name": "phpunit/php-code-coverage", "name": "phpunit/php-code-coverage",
"version": "10.1.16", "version": "10.1.16",
@ -4856,12 +4921,13 @@
], ],
"aliases": [], "aliases": [],
"minimum-stability": "stable", "minimum-stability": "stable",
"stability-flags": [], "stability-flags": {},
"prefer-stable": false, "prefer-stable": false,
"prefer-lowest": false, "prefer-lowest": false,
"platform": { "platform": {
"php": ">=8.4 <8.6" "php": ">=8.4 <8.6",
"composer-runtime-api": "^2.2"
}, },
"platform-dev": [], "platform-dev": {},
"plugin-api-version": "2.1.0" "plugin-api-version": "2.9.0"
} }

@ -276,7 +276,7 @@ clang-format -i <file>
字面量数组与字面量字符串不同: 字面量数组与字面量字符串不同:
- **字面量字符串** 可以利用永久字符串,绕开 Zend request 生命周期 - **字面量字符串** 可以利用永久字符串,绕开 Zend request 生命周期
- **字面量数组** 必须存在于 `app_init()``app_clean()`,即 PHP 的 `RINIT/RSHUTDOWN` 之间 - **字面量数组** 必须存在于 `module_init()``module_clean()`,即 PHP 的 `RINIT/RSHUTDOWN` 之间
因此后续所有“数组初始化缓存”研究都必须遵守: 因此后续所有“数组初始化缓存”研究都必须遵守:

@ -0,0 +1,42 @@
# `#[ArrayDef]` compile-time array contracts
`#[ArrayDef]` attaches key/value type information to a property declared
exactly as `array`. It supports both Zend classes and `#[Native]` classes and
has no runtime metadata or per-read overhead.
```php
class Index
{
#[ArrayDef(Type::String)]
public array $names = []; // list<string>
#[ArrayDef(Type::Int, Type::String)]
public array $labels = []; // map<int, string>
}
```
One argument defines a list value type. Two arguments define a map key type
and value type. Map keys are restricted to `Type::Int` or `Type::String`.
`ClassName::class` is therefore valid only as a list element type or as the
second (value) argument of a map.
For direct writes whose expression types are known, the compiler either emits
the normal write unchanged or reports a fatal type error. An `any` key/value is
checked with PHPX exact-type helpers at runtime. No coercive `intval()` or
string conversion is performed.
List writes support `[]` and non-negative integer indexes up to PHP's current
append position. Indexed writes uniformly emit `php::safeArrayIndex(index,
array)`. The helper uses `zend_hash_next_free_element()` and follows the
initial-index rule of `zend_hash_next_index_insert()`, which remains correct
when `unset()` has created holes or removed the highest numeric key. An index
equal to that value behaves like the next `$array[]` append; earlier indexes
may update or refill an element. Negative indexes and indexes beyond the append
position fail at runtime. There is no AST special case for
`property[count(property)]`. Maps do not support `[]` append writes.
The contract intentionally applies only to direct element assignment lowered
by TypePHP. Reads and in-place operators are unchanged. Values passed through
dynamic functions, callbacks, Reflection, `eval()`, or other ZendVM escape
paths are outside the contract and have undefined behavior from ArrayDef's
perspective.

@ -1,224 +0,0 @@
# TypePHP 编译实现分析、提速方案与 mago 替代评估
> 生成日期:2026-08-03
> 适用范围:`swoole/typephp` v0.4.3(本仓库 `D:\git\php\aot-compiler`
> 说明:所有耗时数字均来自本仓库实际基准测试(`src/` 下 139 个 PHP 文件、约 1.65 MB 源码,PHP 8.4.16 CLI;端到端对比用 302 文件合成项目,Windows + MSVC)
---
## 0. 实施状态(2026-08-03 更新)
**S1(合并两次解析)与 S2(writeFile 内容比对 + 生成 .cc 增量缓存)已实施并验证。**
### 已落地的改动
| 文件 | 改动 |
|---|---|
| `src/CompilerBase.php` | 新增 `parseCachedAst()` / `cloneAst()`(AST 内存缓存 + CloningVisitor 深拷贝);`writeFile()` 增加内容比对(内容相同不覆盖,返回 bool 保持 mtime 稳定) |
| `src/Preprocessor.php` | `prepareFile()` 改用 `parseCachedAst()`,消除与 convert 的重复 parse |
| `src/Translator.php` | `doConvert()` 改用 `parseCachedAst()`;新增 `hasGeneratedObjectFileCache()` / `getGeneratedObjectCacheKey()` / `getGeneratedHeaderDependencies()` / `isGeneratedSourceFile()`;`compileFile()` 对生成的 .cc 走对象缓存;`save()`/`genExtension()` 仅在实际写入时 format |
### 实测收益(302 文件合成项目,dry 模式,3 次取均值)
| 场景 | 基线 | 改动后 | 提升 |
|---|---|---|---|
| 全量构建(冷) | 6011 ms | 5629 ms | **-6.4%** |
| 增量构建(第 3 次,不清理 build) | — | 5023 ms | **-16.4%**(相对基线冷构建) |
说明:dry 模式不包含 C++ 编译,S2 对象缓存的更大收益(跳过 g++/cl 编译)需在完整 build 场景体现;dry 耗时中固定开销(CLI 启动、YAML、符号表)占比高,稀释了 S1 的解析节省。
### 验证记录
- `parseCachedAst`:同一文件 prepare+convert 各调用一次,第二次命中缓存且返回深拷贝(不共享节点、无 resolvedName 残留污染)✓
- `writeFile` 内容比对:相同内容跳过写入,mtime 保持不变 ✓
- `isGeneratedSourceFile`:buildDir 前缀 + .cc 扩展名判断,与 `getCppFile` 路径一致 ✓
- PHPUnit:Entity 目录 85 测试、FileScanner 等 110 测试通过(1 个 Windows 路径分隔符既有失败与本次无关)
- `typephp-think`(554 文件):补丁后 prepare 通过,convert 阶段因「仓库源码类型系统比 v1095 正式版严格」(`Cannot re-assign`)受阻,属源码版本差异,非本次改动问题
---
## 1. 编译是如何实现的(管线剖析)
### 1.1 总体流程
入口为 `bin/tpc.php``src/compiler.php``main()``src/Translator.php`(继承 `Preprocessor``CompilerBase`)。编译是一条四阶段流水线:
```
┌────────────────────────────────────────────────────────────────────────┐
│ ① prepare() 扫描+解析+建符号表+依赖拓扑排序 │
│ src/Build/SourcePipelineTrait.php::prepare() │
│ → 每文件: Preprocessor::prepareFile() │
│ parser->parse() ←── AST 解析 #1
│ NodeTraverser(NameResolver + Visitor + 常量校验 + AttributeLower) │
│ 收集 symbolDeclInFile / symbolCallInFile / classMap / funcMap │
│ → getSortedFiles(): marcj/topsort 按文件间符号依赖拓扑排序 │
├────────────────────────────────────────────────────────────────────────┤
│ ② convert() 每文件: AST → C++ 源码 │
│ src/Build/SourcePipelineTrait.php::convert() │
│ → Translator::convertFile() → doConvert() │
│ loadFile() 读盘(第二次) │
│ parser->parse() ←── AST 解析 #2(同一文件解析两次!) │
│ NodeTraverser 再次遍历(与 prepare 相同的 4 个 visitor) │
│ 逐节点翻译生成 C++(CompilerBase 4000 行核心逻辑) │
│ → 生成 func_decl.h / data_decl.h / extension-<target>.cc │
├────────────────────────────────────────────────────────────────────────┤
│ ③ compile() C++ → .o(外部编译器) │
│ Translator::compile() │
│ → phpx misc 文件 + 生成的 .cc │
│ → PCH 预编译头(PrecompiledHeaderManager,内容指纹缓存) │
│ → Linux/macOS: pcntl fork 并行(默认 4 jobs,大文件优先调度) │
│ → Windows: 串行(supportsPcntlParallelCompile() = false) │
│ → phpx misc 有 .o 缓存(hasMiscObjectFileCache,命令+ABI 指纹) │
├────────────────────────────────────────────────────────────────────────┤
│ ④ build() 链接为可执行文件 / 扩展 / 库 │
│ Translator::build() → NativeBuilder::link() │
└────────────────────────────────────────────────────────────────────────┘
```
### 1.2 关键实现位置
| 职责 | 文件 |
|---|---|
| 四阶段编排 | `src/Build/SourcePipelineTrait.php`(prepare/convert)、`src/Translator.php`(compile/build) |
| PHP→C++ 逐节点翻译 | `src/CompilerBase.php`(4133 行,核心) |
| 扫描/符号表/依赖排序 | `src/Preprocessor.php` |
| 编译器抽象 | `src/Backend/`(CompilerBackend → GccLikeBackend → Gcc/Clang、Msvc) |
| 平台抽象 | `src/Platform/`(Linux/Macos/Windows) |
| 并行编译调度 | `src/Build/NativeBuilder.php::dispatchParallel()` + `SourceCompileQueue` |
| PCH 管理 | `src/Build/PrecompiledHeaderManager.php` |
| 生成物落盘 | `CompilerBase::writeFile()`(无内容比对,直接覆盖) |
### 1.3 当前已具备的优化
1. **PCH 预编译头**:`phpx.h` 等 8 个公共头打成 `.gch`,带 24 位 sha256 内容指纹缓存(`PrecompiledHeaderManager`)。
2. **phpx misc 对象缓存**:`phpx/src/misc/*.cc` 的 `.o` 用「编译命令 + PHP ABI」指纹缓存,避免每次全量重编 PHPX 运行时。
3. **编译并行**:Linux/macOS 用 pcntl fork 并行(`-j`,默认 4),且按「大文件优先占满槽位 + 小文件快速通道」调度。
4. **编译期常量展开、字面量字符串表**、`--dry` 只生成 C++ 等。
---
## 2. 性能瓶颈实测
### 2.1 基准方法
`nikic/php-parser 5.6.1`(与仓库 `composer.json` 一致)对 `src/` 全部 139 个 PHP 文件(约 1.65 MB)做多轮测量:
```
parse (nikic/php-parser) ×3 1455.5 ms → 单遍 ≈ 485 ms(3.18 MB/s)
parse + NameResolver ×3 1797.0 ms → 单遍 ≈ 599 ms
parse+resolve+serialize (1x) 701.1 ms
unserialize ×3 779.8 ms → 单遍 ≈ 260 ms
parse+resolve+prettyPrint ×3 2196.0 ms
serialized AST 体积 47.6 MB(源码的 28.8 倍)
```
### 2.2 结论性瓶颈清单(按影响排序)
| # | 瓶颈 | 证据 | 影响 |
|---|---|---|---|
| B1 | **每个 PHP 文件被解析两次** | `prepareFile()`(Preprocessor.php:130)与 `doConvert()`(Translator.php:2325)各自 `parser->parse()` 一次,且各自跑一遍完整 NodeTraverser | PHP 侧最大浪费:139 文件规模下纯解析+resolve ≈ 2×599ms ≈ **1.2s 起步**,大项目线性放大 |
| B2 | **生成的 .cc 无增量缓存** | `compileFile()` 只对 phpx misc 走 `hasMiscObjectFileCache`;用户代码生成的 `.cc` 每次全量重编 | 改 1 个 PHP 文件也要重编全部 .cc(C++ 编译是耗时的绝对大头) |
| B3 | **生成文件无内容比对** | `CompilerBase::writeFile()` 直接 `file_put_contents` 覆盖,mtime 必然变化 | 即使内容相同也触发下游重新依赖判断/重编,破坏增量可能性 |
| B4 | **prepare/convert 阶段完全串行** | 两阶段都是单进程 `foreach` | 多核只用于 compile;PHP 侧解析/翻译占用的核数为 1 |
| B5 | **Windows 无并行编译** | `Windows::supportsPcntlParallelCompile() = false`,直接走 `compileSourceFile()` 串行 | Windows 大项目编译时间 = 串行全量 |
| B6 | **PCH/缓存指纹每次全量计算** | `PrecompiledHeaderManager::buildFingerprint()` 每次构建都递归遍历 phpx 头文件并逐个 hash(实测 131 个头文件 ≈ 42ms,PHP 侧还有 misc 缓存同样的遍历) | 每次构建的固定开销,虽不大但在累积 |
| B7 | 转换阶段反复 `getType()` 字符串比较、NodeFinder 多次全树扫描(如 `findSymbolUsing`、`canOptimizeMultiReturn`) | CompilerBase/Preprocessor 中多次独立 `findInstanceOf` | 每文件多次 O(N) 全树扫描,可合并为一次遍历 |
### 2.3 一个反直觉的事实
**AST 序列化缓存是划算的**:unserialize 单遍 260ms vs parse+resolve 599ms(快 2.3 倍),即使加上 serialize 成本(首轮 ~700ms/139 文件),二次构建纯 PHP 解析阶段也能从 ~1.2s 降到 ~260ms。但注意序列化体积膨胀 28.8 倍(47.6MB/1.65MB),对磁盘 I/O 有压力——更优做法是**合并两阶段解析**(见方案 S1)。
---
## 3. 提速方案(按性价比排序)
### S1(P0)prepare/convert 合并为单次解析 —— 省一半 PHP 解析时间
- **问题**:同一文件 `prepare``convert` 各 parse+traverse 一次,逻辑完全重复(visitor 列表一致)。
- **做法**:让 `prepareFile()` 把 parse 好的 `$stmts`(NameResolver 等已跑完)挂到文件缓存(内存 `array<string, array<Node>>`,或按 `filemtime+内容hash` 落盘 `build/cache/ast/`),`doConvert()` 优先取缓存;取不到(如 `--force`、跨进程)才重新 parse。
- **收益**:139 文件规模 PHP 解析从 ~1.2s → ~0.6s;大项目直接减半。
- **风险**:低。`convert` 依赖的 `resolvedName` 等 attribute 由 NameResolver 在 traverse 时写入,缓存的是 traverse 之后的 AST,语义不变。需注意 `ConstantExpressionValidationVisitor` 的报错回调在 prepare 是 warning、convert 是 fatal,缓存后错误在 prepare 阶段就能暴露,行为只会更早更一致。
### S2(P0)生成 .cc 的增量对象缓存 —— 编译期最大收益
- **做法**:把 `hasMiscObjectFileCache()` 的机制推广到所有生成的 `.cc`:缓存 key = 编译命令 + PHP ABI + **生成物内容 hash(或源 PHP hash + 编译器版本)**。内容未变 → 跳过 g++/clang。
- **配套**:`writeFile()` 加「内容比对,相同则不落盘」逻辑(解决 B3),保证 mtime 稳定。
- **收益**:改 1 个文件后二次构建,C++ 编译从全量变为仅差异文件;这是肉眼可感知的最大提升。
- **风险**:中。生成 .cc 的内容由 `genFunctionDeclarations`/`genDataDeclarations` 等公共头影响——若公共头变了,所有 .cc 的 `#include` 语义可能变,但 .cc 内容 hash 不会变。**必须把公共声明头也纳入 key**(hash func_decl.h/data_decl.h/extension 文件内容),或采用「.cc 内容未变 且 公共头未变 且 .o 存在 且 命令指纹未变」四条件跳过。
### S3(P0)修复 PCH/缓存指纹的计算时机
- `buildFingerprint()` 每次构建全量扫描+hash 头文件(~42ms + misc 缓存同款遍历),应改为「mtime+size 列表指纹」先行判断,内容 hash 仅在前者变化时执行。
### S4(P1)prepare/convert 并行化
- parse+resolve 是纯函数,可用 pcntl fork 分片解析(子进程只做 parse+serialize,父进程归并反序列化),类似已有 `dispatchParallel` 的模式。转换阶段因共享状态(literalStrings、classMap 等)风险高,可先只并行「解析」,翻译仍串行。
### S5(P1)Windows 并行编译
- 两条路:(a) 给 `Msvc` 后端加 `/MP`(MSVC 原生多进程编译,单命令即可);(b) Windows 上用 `proc_open` 实现多进程调度替代 pcntl。收益对 Windows 用户显著。
### S6(P2)工具链层
- `ccache`/`sccache` 兜底(对 C++ 编译做内容缓存,配合 S2 效果更稳);链接器换 `mold`/`lld`;`-pipe`、`-fno-asynchronous-unwind-tables` 等编译 flag 微调。
### S7(P2)单次遍历替代多次 NodeFinder
- `findSymbolUsing()` 等每文件多次 `findInstanceOf` 全树扫描,可合并为一次 visit 收集函数调用/类引用/常量引用三类符号,减少 O(N) 重复。
---
## 4. AST 解析能否用 mago 替代?—— 结论:不建议(现阶段不可行)
### 4.1 mago 是什么
[mago](https://github.com/carthage-software/mago)(carthage-software/mago)是一个 **Rust 编写的 PHP 工具链**:lint、format、静态分析(对标 Clippy/OXC),v1.45.0(2026-07),MIT/Apache-2.0 双许可,开发活跃。其核心是自研高性能 PHP 解析器(crate:`mago-syntax` 词法/语法/遍历、`mago-names` 名称解析),官方基准:7M 行 WordPress 静态分析 1.46s(PHPStan 55.9s)、lint 0.88s、format 0.43s。**已支持 PHP 8.4 property hooks、asymmetric visibility(private(set) 等)。**
### 4.2 为什么不能直接替代
| 维度 | nikic/php-parser(现状) | mago | 兼容性判断 |
|---|---|---|---|
| 语言/进程 | PHP 类库,进程内对象 | Rust 二进制/crate,AST 是 Rust 结构 | 无法在 PHP 进程中直接消费 |
| AST 模型 | AST(抽象语法树),含 attribute 机制(`resolvedName`/`namespacedName` 等存于节点属性) | CST(具体语法树,含 trivia),`mago ast --json` 是调试输出 | 结构不同,需要完整映射层 |
| 下游依赖 | TypePHP 全库 296 处 `PhpParser\*` 引用、75 个文件;依赖 `NodeTraverser`/`NodeVisitor`/`NameResolver`/`NodeFinder`/`ConstExprEvaluator`/`PrettyPrinter`/`Modifiers` 等 | 无 PHP API | 替换 = 重写编译器前端 |
| 名称解析 | `NameResolver` 写入节点 attribute,后续代码生成大量读取 | `mago-names` 输出到 Rust 侧 | 需要桥接层搬运 |
| 代码生成 | `PrettyPrinter` 用于 stub 生成(`LibraryImportStubGenerator`、`gen_stub.php`) | 无等价物 | 缺失 |
| 错误语义 | `PhpParser\Error` + 行列号,被 `SyntaxError` 包装 | 错误模型不同 | 需要适配 |
**核心矛盾**:TypePHP 不是「解析完就丢」的 linter,而是「AST 必须留在 PHP 进程内、被 4000 行 CompilerBase 逐节点翻译」的编译器。走 mago 只有两条路,都不可行:
1. **mago CLI → JSON AST → PHP 重建对象**:JSON 序列化体积远大于源码(参考第 2 节:php-parser AST 序列化已膨胀 28.8 倍,mago CST 只多不少),反序列化 + 对象重建的 CPU/内存开销会**吃掉**解析省下的时间,且每文件一次进程启动(exec)成本极高;同时丢失 attribute 机制、注释/行列语义需重新映射。
2. **PHP FFI 绑定 mago-syntax crate**:需要为整个 Rust AST 写 FFI 边界 + 转 PHP 对象层,工程量接近重写前端,且 mago 的 CST 与 TypePHP 需要的「带 resolvedName 的 AST」不是一回事,仍要在 PHP 侧自建名称解析与代码生成数据模型。
### 4.3 mago 在本项目中的合理定位(可选)
- **前置快速语法校验**:编译前用 `mago`(或 `mago lint --quick`)做廉价的全项目语法/明显错误预检,给出比 php-parser 更快的反馈(适合 IDE/CI 场景,不进入编译流水线核心)。
- **性能参照物**:mago 证明了「Rust 原生解析」的量级(~百 MB/s vs php-parser 3 MB/s),可作为未来「把前端重写为原生编译器(如 Rust 侧做 parse+name-resolution,产物是自研序列化 AST)」的路线参考——但那是一个全新项目,而非对现有编译器的增量改造。
- **若仍想引入原生解析**:更现实的是保留 php-parser 语义、仅加速解析环节(如 `--php-parser``PhpParser\ParserFactory` 换用 tree-sitter 绑定并在 PHP 侧重建 php-parser 节点),但收益-成本比仍不如 S1/S2。
### 4.4 结论
> **不建议用 mago 替换 nikic/php-parser**。mago 是「解析即弃」的工具链,TypePHP 是「解析后深度消费 AST」的编译器,二者 AST 模型与进程模型不兼容,桥接成本会抹平解析速度优势。
> 真正的提速路径在 **S1(两阶段合并单次解析)+ S2(生成 .cc 增量缓存 + writeFile 内容比对)+ S4/S5(并行化)**,全部基于现有架构增量改造,风险可控、收益立竿见影。
---
## 5. 落地顺序建议
```
第一阶段(P0,改动小收益大,1-2 天)
1. writeFile 内容比对(S2 前置)
2. prepare/convert 共享 AST(S1)
3. 生成 .cc 增量对象缓存(S2,key 含公共头内容 hash)
第二阶段(P1,结构性收益)
4. PCH/misc 指纹轻量化(S3)
5. 解析阶段 pcntl 并行(S4 解析部分)
6. Windows 并行(/MP 或 proc_open)(S5)
第三阶段(P2,长期)
7. 单次遍历合并 NodeFinder(S7)
8. ccache / mold / lld(S6)
```
## 6. 附:基准脚本
分析所用基准脚本:`.workbuddy/bench_ast.php`(可重复运行:`php .workbuddy/bench_ast.php`),复现本节所有数字。

@ -1,5 +1,39 @@
# TypePHP 编译器命令行 # TypePHP 编译器命令行
## Bash 自动补全
TypePHP 提供与当前编译器参数同步的 Bash completion。当前终端临时启用:
```shell
source <(./tpc --generate-completion=bash)
```
从源码仓库开发时也可以直接执行 `source completions/tpc.bash`
安装到当前用户并在后续 Bash 会话自动加载:
```shell
mkdir -p "$HOME/.local/share/bash-completion/completions"
./tpc --generate-completion=bash \
> "$HOME/.local/share/bash-completion/completions/tpc"
```
如果系统没有自动扫描用户 completion 目录,可在 `~/.bashrc` 中加载:
```shell
source "$HOME/.local/share/bash-completion/completions/tpc"
```
系统级安装可将生成结果写入 `/usr/share/bash-completion/completions/tpc`。该操作通常
需要 root 权限。
补全支持编译选项、WASM profile、构建模式、PHP/C++ 版本、sanitizer、输入源码、
项目 YAML、Python 源文件以及目录参数。`--` 之后是被编译程序自身的参数,补全器
不会再把它们解释为 `tpc` 参数。
发布包携带预生成的 `completions/tpc.bash`。此文件由同一个生成器产生,并有单元
测试保证它与 `./tpc --generate-completion=bash` 的输出一致。
本文档与 `src/Translator.php::showUsage()` 保持同步。使用: 本文档与 `src/Translator.php::showUsage()` 保持同步。使用:
```bash ```bash
@ -48,7 +82,7 @@ bin/tpc.php app.php --dry --build-dir /tmp/typephp-build
| 选项 | 说明 | | 选项 | 说明 |
|---|---| |---|---|
| `--php-version <8.2|8.3|8.4|8.5>` | 限制接受的 PHP 语法版本,默认 `8.5`。 | | `--php-version <8.4|8.5>` | 限制接受的 PHP 语法版本,默认 `8.5`。 |
| `--cxx-std <ver>` | C++ 标准,例如 `c++17`、`c++20`。 | | `--cxx-std <ver>` | C++ 标准,例如 `c++17`、`c++20`。 |
| `--march <arch>` | 目标指令集,例如 `native`、`x86-64-v3`。 | | `--march <arch>` | 目标指令集,例如 `native`、`x86-64-v3`。 |
| `--target-platform <triple>` | 交叉编译目标 triple。 | | `--target-platform <triple>` | 交叉编译目标 triple。 |
@ -59,6 +93,8 @@ bin/tpc.php app.php --dry --build-dir /tmp/typephp-build
`--php-version` 控制解析器接受的源码语法,也用于 `project.yml` 中依据 `PHP_VERSION` / `PHP_VERSION_ID` 选择源文件。它不负责选择链接的 PHP 安装目录。 `--php-version` 控制解析器接受的源码语法,也用于 `project.yml` 中依据 `PHP_VERSION` / `PHP_VERSION_ID` 选择源文件。它不负责选择链接的 PHP 安装目录。
TypePHP 和 PHPX 的最低运行时版本均为 PHP 8.4。`--php-version` 与实际链接的 `libphp.so` 不要求小版本完全相同,但两者都必须为 PHP 8.4 或更高版本。
## C++ 编译和链接参数 ## C++ 编译和链接参数
这些参数均可重复: 这些参数均可重复:
@ -81,6 +117,20 @@ bin/tpc.php app.php --dry --build-dir /tmp/typephp-build
传入 `project.yml` 时,命令行参数优先于 YAML 中的同名配置。项目文件格式参见用户文档及代码中的项目配置解析器。 传入 `project.yml` 时,命令行参数优先于 YAML 中的同名配置。项目文件格式参见用户文档及代码中的项目配置解析器。
### PHP 扩展依赖
程序依赖其他 PHP 扩展时,可以将必需模块写入 Zend 模块依赖表:
```yaml
extension-dependencies:
- pdo_mysql
- curl
```
`ext-deps` 是等价的简写名称。一个项目中只能使用其中一个配置名;同时出现 `extension-dependencies``ext-deps` 会产生配置错误。
编译器会为每一项生成 `ZEND_MOD_REQUIRED`。Zend 在加载 TypePHP 模块时检查这些扩展是否已加载。该配置不表示原生链接库;C/C++ 链接依赖仍使用 `link-libs`
## 查看权威帮助 ## 查看权威帮助
命令行实现可能继续演进,发布版本的实际参数以以下命令为准: 命令行实现可能继续演进,发布版本的实际参数以以下命令为准:

@ -0,0 +1,356 @@
# C++ 命名空间、前缀与符号 ABI 规则
本文是 TypePHP、PHPX 以及 TypePHP 生成代码的内部 C++ 命名规范。它解决以下问题:
- 区分 TypePHP 运行时逻辑、PHPX ZendAPI 封装、项目私有实现和用户 PHP 符号;
- 防止框架 helper 与用户定义的 PHP 函数或类方法生成相同的 C++ 符号;
- 明确哪些名称属于稳定 ABI,哪些名称仅限单个生成项目内部使用;
- 为新增 helper、缓存、入口函数和生成符号提供统一的命名决策。
## 1. 总体规则
| 命名域 | 含义 | 典型形式 | 可见范围 | ABI 属性 |
| --- | --- | --- | --- | --- |
| `typephp_` | TypePHP 独有的运行时或编译产物支持逻辑 | `typephp_call_parent_constructor()` | TypePHP/PHPX 运行时 | 内部或显式导出 ABI |
| `php::` | 对 ZendAPI、zval、HashTable、call frame 等 PHP 运行时能力的 C++ 封装 | `php::deindirect()` | PHPX C++ API | PHPX API |
| `typephp_<project>` | 单个编译项目的私有 C++ 命名空间 | `namespace typephp_tpc` | 当前生成项目 | 非公共 ABI |
| `php_` | 用户 PHP 函数和类方法映射后的 C++ callable 符号 | `php_app__user__save()` | 链接器可见 | TypePHP/stub callable ABI |
核心约束:
1. 不得新增全局的框架 `php_*` helper。
2. 与 TypePHP 无关、只是包装 ZendAPI 的能力必须放入 `namespace php`
3. TypePHP 独有且需要跨生成文件调用的逻辑使用 `typephp_` 前缀。
4. 只服务于一个编译项目的数据和函数放入 `typephp_<project>` 命名空间。
5. 全局 `php_*` callable 名称保留给用户 PHP 声明的编译 ABI。
## 2. `typephp_`:TypePHP 独有逻辑
`typephp_` 表示该 API 的语义由 TypePHP 定义,不是 ZendAPI 的一般性 C++ 包装。常见场景包括:
- TypePHP 属性读写规则;
- TypePHP 构造、克隆和父方法调用链;
- TypePHP 编译期 Attribute 的运行时支持;
- TypePHP Native Class、Property Hook 等专属运行时逻辑;
- TypePHP embed runtime 的初始化和关闭入口。
示例:
```cpp
typephp_call_parent_constructor(object, constructor, args);
typephp_call_parent_clone(object, clone_method);
typephp_install_property_handlers(class_entry, handlers);
typephp_write_property_scoped(object, member, value, scope);
TYPEPHP_RUNTIME_INIT(project)(argc, argv);
```
### 2.1 使用边界
- 该前缀是 TypePHP 内部 C/C++ 名称空间,不代表 PHP 用户函数。
- 新增 API 时应使用完整、可识别的 snake_case 名称,不能使用含义过宽的名称,例如 `typephp_call()`
- 仅在一个 `.cc` 文件中使用的函数还应增加 `static` 或放入匿名命名空间。
- 需要跨动态库边界时,使用对应的导出宏;不需要导出的 helper 不应扩大符号可见性。
- 不要仅因为代码位于 `typephp_helper.h` 就使用 `typephp_`;判断依据是语义是否为 TypePHP 独有。
### 2.2 正反例
```cpp
// 正确:TypePHP 独有的构造链语义。
typephp_call_parent_constructor(object, constructor, args);
// 错误:只是将 INDIRECT zval 物化为普通值,并非 TypePHP 独有。
typephp_deindirect(value);
// 正确:通用 Zend 值包装属于 PHPX。
php::deindirect(value);
```
## 3. `php::`:ZendAPI 的 C++ 封装
`namespace php` 由 PHPX 提供,用于把 Zend 的 C API、宏、裸指针和手工资源管理封装为类型安全、RAII 友好的 C++ API。
这一命名域包含两类能力:
1. PHP 值和运行时对象,如 `php::Var`、`php::Str`、`php::Array`、`php::Object`;
2. ZendAPI 的安全包装,如符号查询、作用域管理、值转换、对象创建和调用。
示例:
```cpp
php::Var value;
php::Array arguments;
auto plain = php::deindirect(value);
auto called_ce = php::getCalledCe(this_);
auto scope = php::getCallableScope(function, this_);
auto create_object = php::getCreateObjectFn(class_entry);
auto globals = php::globalsArray();
```
### 3.1 何时使用 `php::`
满足以下条件时应放入 `namespace php`
- API 对任何 PHPX C++ 调用者都有意义;
- API 的行为可以完全用 Zend/PHP 运行时语义解释;
- API 不依赖 TypePHP AST、编译期 Attribute 或 TypePHP 特有语言规则;
- API 的主要作用是隐藏 Zend 宏、裸 `zval *`、引用计数或异常检查。
### 3.2 禁止全局 `php_*` helper
以下旧式写法是禁止的:
```cpp
php::Var php_deindirect(const php::Var &value);
php::Str php_get_called_class(php::Object &this_);
zend_class_entry *php_get_called_ce(php::Object &this_);
auto php_get_create_object_fn(zend_class_entry *ce);
```
它们必须写成:
```cpp
namespace php {
Var deindirect(const Var &value);
Str getCalledClass(Object &this_);
zend_class_entry *getCalledCe(Object &this_);
auto getCreateObjectFn(zend_class_entry *ce);
} // namespace php
```
原因是用户可以合法声明:
```php
function deindirect(mixed $value): mixed {}
function get_called_ce(): string {}
function get_create_object_fn(): string {}
```
这些 PHP 函数会生成 `php_deindirect`、`php_get_called_ce` 和
`php_get_create_object_fn`。如果 PHPX 也在全局定义同名 helper,可能在声明、重载解析或链接阶段发生冲突。
### 3.3 命名风格
PHPX C++ API 使用现有的 camelCase 风格:
```cpp
php::getCalledClass();
php::getClassEntrySafe();
php::getPersistentCache();
php::stdCreateObject();
```
不要把 Zend 的 snake_case 名称机械地保留为全局 C++ 名称。底层调用可以继续使用 Zend 原始 API,例如 `zend_objects_new()`,但对生成代码暴露的包装层应使用 `php::`
## 4. `typephp_<project>`:项目私有命名空间
每个 TypePHP 编译项目拥有独立的 C++ 命名空间:
```text
typephp_<target-name>
```
例如项目名为 `tpc`
```cpp
namespace typephp_tpc {
// Project-private generated state and helpers.
}
```
项目名中的 `-``*` 会转换为 `_`,其余字符必须满足编译器的 target identifier 校验。由于固定带有 `typephp_` 前缀,即使项目名以数字开头,最终 C++ namespace 仍是合法标识符。
### 4.1 应放入该命名空间的内容
- literal string 表和 `get_str()`
- class/function/property cache 表及其访问函数;
- 当前项目的全局变量存储;
- class entry、object handler 和默认属性模板;
- module entry、MINIT/RINIT/RSHUTDOWN 辅助状态;
- `module_init()`、`module_clean()` 等仅在生成 extension 文件内部调用的函数;
- Python module cache 等项目级生成状态。
示意:
```cpp
namespace typephp_demo {
static php::Str literal_strings[] = {
php::Str{"hello"},
};
php::Str &get_str(uint32_t index) {
return literal_strings[index];
}
static THREAD_LOCAL zend_class_entry *class_map[8];
zend_class_entry *get_class(int id, const php::Str &name) {
// Resolve and cache a symbol owned by this project.
}
static void module_init() {
// Initialize this project's generated state.
}
} // namespace typephp_demo
```
### 4.2 可见性与 ABI
- `typephp_<project>` 内的名称是实现细节,不是 library stub ABI。
- 可限制为 `static` 的对象和函数应继续标记为 `static`
- 生成头文件可以声明必须跨 translation unit 使用的项目内部 accessor,但不应暴露底层数组或缓存表。
- 外部手写 C++ 代码不得依赖 literal index、cache index 或项目内部 storage 名称。
- 不同 TypePHP 项目可以链接到同一进程,因为相同的内部短名称位于不同的项目 namespace 中。
### 4.3 作用域优先于名称拼写
项目 namespace 中仍可能出现历史生成名称,例如:
```cpp
typephp_demo::php_class_entry_App_User
```
虽然成员名以 `php_` 开头,但完整符号位于 `typephp_demo` 中,因此它属于项目私有实现,而不是第 5 节所述的全局用户 callable ABI。新增项目内部 helper 应优先使用不带 `php_` 的短名称,例如 `get_class()`、`get_func()` 和 `get_str()`
## 5. `php_`:用户 PHP callable 的 C++ ABI
全局 `php_` 前缀用于 TypePHP 将用户声明的 PHP 函数和类方法映射为 C++ callable 符号。这套命名同时被生成代码、library stub 和外部 C++ 实现使用,因此不能随意改变。
示例:
```php
namespace App;
function greet(string $name): string {}
class User
{
public function save(): bool {}
}
```
概念上的 C++ 符号为:
```cpp
php::Str php_app__greet(php::Str name);
php::Bool php_app__user__save(php::Object &this_);
```
规则包括:
- 使用 `php_` 标识“由 PHP 声明映射而来”;
- PHP namespace、class 和 method/function 名经过规范化后组合;
- `__` 是现有 ABI 的组合分隔符;
- 实例方法的第一个参数是对象 `this_`
- stub、library 和消费方必须使用完全相同的映射规则。
### 5.1 为什么内部 helper 不能使用 `php_`
`php_` 映射不是独立的保留关键字空间,而是用户 PHP 名称的机械 ABI。以下用户声明:
```php
function deindirect(mixed $value): mixed {}
```
会自然生成:
```cpp
php::Var php_deindirect(php::Var value);
```
因此框架若定义全局 `php_deindirect()`,就侵占了用户符号空间。正确做法是 `php::deindirect()`
### 5.2 组合冲突
由于当前 ABI 使用 `__` 组合 PHP namespace、class 和 callable 名,下列两个 PHP 声明可能映射到同一个 C++ 符号:
```php
function App\user__test(): void {}
namespace App;
class User
{
public function test(): void {}
}
```
编译器必须在预处理阶段检测这种情况并抛出 FatalError,不能通过覆盖、链接顺序或增加运行时分派来处理。修改映射分隔规则会破坏既有 stub/ABI,因此冲突必须由用户重命名解决。
### 5.3 入口符号例外
少量 C ABI/嵌入入口由生成器固定定义,不属于普通用户 callable。例如:
```cpp
php_<project>_embed_get_module();
typephp_<project>_runtime_init(argc, argv);
typephp_<project>_runtime_shutdown();
```
这些是 binary/library embed runtime 与当前项目 module entry 的连接点。定义和引用统一通过
`TYPEPHP_EMBED_GET_MODULE_FUNCTION()`、`TYPEPHP_RUNTIME_INIT_FUNCTION()`、
`TYPEPHP_RUNTIME_SHUTDOWN_FUNCTION()` 及对应的符号宏生成,风格与 Zend 的
`PHP_MINIT_FUNCTION()`/`PHP_MINIT()` 一致。最终符号包含项目名,不得作为通用 helper 命名模板。
### 5.4 多扩展进程中的公共运行时
TypePHP 扩展不得分别编译或静态链接包含进程级 Zend 状态的 PHPX 实现。Reflection handler、
`FiberGenerator` class entry、作用域和 Property Hook 运行时均由共享的 `libphpx` 唯一提供:
- host 模式的 extension/library 必须链接 `libphpx.so`、`libphpx.dylib` 或 `phpx.dll`,不能回退到 `libphpx.a`
- Unix PHP extension 不链接 Embed `libphp.so`,Zend/PHP 符号由加载它的 SAPI 提供;
- macOS extension 使用 `-undefined dynamic_lookup` 解析宿主符号;
- binary 和独立 WASI 程序仍可以静态链接,因为每个进程或 Wasm 实例只有一份运行时。
`src/core/typephp_*.cc` 只承载 TypePHP 专属的 `typephp_*` 运行时;`php::` ZendAPI 包装应放在不带
`typephp_` 前缀的 core 源文件中,例如 `src/core/scope.cc`
## 6. 名称选择流程
新增 C++ API 时按以下顺序判断:
1. **它是否是用户 PHP 函数或类方法的编译本体?**
- 是:使用既有 `php_` callable ABI 生成器,禁止手写另一套映射。
2. **它是否只服务于当前一个 TypePHP 项目?**
- 是:放入 `typephp_<project>`,并尽可能使用 `static` 或私有 accessor。
3. **它是否实现 TypePHP 独有语义?**
- 是:使用 `typephp_` 前缀。
4. **它是否只是对 Zend/PHP 运行时能力的 C++ 封装?**
- 是:放入 `namespace php`,使用 PHPX camelCase 风格。
5. **以上都不是?**
- 不应随意加入 `typephp_helper.h`;应重新确认所属模块和公共 API 边界。
## 7. 代码审查清单
新增或修改生成 helper 时必须检查:
- [ ] `typephp_helper.h` 中没有新增全局 `php_*` helper;
- [ ] ZendAPI 包装位于 `namespace php`
- [ ] TypePHP 独有逻辑使用 `typephp_`
- [ ] 项目缓存和 storage 位于 `typephp_<project>`
- [ ] 项目私有表没有通过生成头文件直接 `extern` 暴露;
- [ ] 用户 callable 仍使用统一的 `php_` ABI 生成器;
- [ ] 新名称不会与用户可声明的 PHP 函数或方法发生冲突;
- [ ] bin、lib、ext 和 WASM 构建使用相同的项目名推导规则;
- [ ] 修改公开 callable 映射时同步评估 stub 和既有 ABI;
- [ ] 至少增加一个用户同名函数的编译回归测试。
当前相关回归测试为:
```text
tests/compiler/basic/helper-symbol-collision.phpt
```
## 8. 主要实现位置
| 责任 | 文件 |
| --- | --- |
| `php_` callable 前缀与组合分隔符 | `src/CompilerBase.php` |
| callable 组合冲突检测 | `src/Preprocessor.php` |
| `typephp_<project>` 生成及项目私有表 | `src/Translator.php` |
| TypePHP extension 前缀常量 | `src/Metadata/Constants.php` |
| PHPX/TypePHP helper 分类 | `vendor/swoole/phpx/include/typephp_helper.h` |
| embed module accessor 拼接 | `vendor/swoole/phpx/src/misc/typephp_main.cc` |

@ -0,0 +1,114 @@
# `#[Immutable]` compile-time effect checking
## Purpose
`#[Immutable]` is a TypePHP compile-time annotation modelled after C++ `const`.
It prevents accidental mutation in statically compiled code without adding a
wrapper object, Zend metadata, runtime branch, or ABI change.
It is intentionally a best-effort static tool rather than a security boundary.
Calls whose target is deliberately made dynamic are an escape hatch and do not
receive a runtime guard.
## Supported targets
```php
#[Immutable]
public function name(): string
{
return $this->name;
}
function inspect(#[Immutable] User $user): string
{
return $user->name();
}
```
The attribute is valid on methods and on function, method, and closure
parameters. On an instance method it makes `$this` immutable. On a parameter it
makes the binding immutable and, when it can contain an object, treats the
referenced object as immutable as well.
## Rejected operations
For an immutable root such as `$this` or `$user`, the compiler rejects:
- assignment, destructuring, and array-element or object-property writes;
- compound assignment, `++`, `--`, `unset()`, taking a reference, and
`foreach (... as &$value)`;
- a statically named method call unless the resolved method is also marked
`#[Immutable]`;
- a mutating value extension such as `$array->sort()`; read-only array/string
methods remain available;
- passing an object to a statically resolved parameter that is not itself
`#[Immutable]`;
- passing any immutable value to a mutable by-reference parameter, including
extension functions such as `sort()`;
- storing an immutable object identity in an object property, array,
global/static variable, or returning/yielding it as a mutable value.
An immutable by-reference parameter is supported. It acts like a C++ `const &`:
the reference is accepted because the callee is checked against mutation.
`#[MethodsFor]` follows the same contract. An object extension is callable on
an immutable receiver only when its receiver parameter is marked
`#[Immutable]`.
## Aliases, closures, generators, and inheritance
Local aliases of immutable objects remain immutable:
```php
$alias = $user;
$alias->rename('new'); // compile-time error
```
`clone` creates a distinct mutable object and therefore intentionally drops the
annotation. Captured variables, arrow functions, closure `$this`, and Fiber
generator bodies carry immutable metadata into their generated function
contexts.
An overriding class or interface method may strengthen an ordinary contract by
adding `#[Immutable]`, but it cannot remove `#[Immutable]` from an inherited
method or parameter.
## Value versus object semantics
Scalar values and PHP copy-on-write values can be read and copied normally. For
example, `count($values)` and `$copy = $values` do not modify an immutable array.
The compiler propagates immutability through an expression only when object
identity is possible.
## Explicit escape hatches
The following intentionally bypass static method-effect checking:
```php
$method = 'rename';
$user->$method('new');
$callable = getRuntimeCallable();
$callable($user);
```
The same applies to other runtime-only mechanisms that hide the target from the
compiler, including reflection and dynamic ZendVM code. TypePHP neither inserts
a runtime read-only proxy nor attempts to recover the escaped value later.
This boundary is deliberate: `#[Immutable]` should cost nothing in generated
code and should not complicate PHPX/ZendVM object semantics.
## Property hooks and magic access
Property-hook reads are lowered to generated method calls. Consequently, a hook
used through an immutable receiver must itself carry an `#[Immutable]` method
contract; otherwise the generated call is rejected. Fully dynamic magic access
is covered by the same escape-hatch rule as other runtime-only behavior.
## Implementation boundaries
The implementation is isolated in `src/Immutable/ImmutableSupportTrait.php`.
`FunctionDef` and `ArgInfo` retain only compile-time effect bits, while each
`FunctionContext` stores the immutable roots and object aliases relevant to that
body. Checks run during AST lowering and emit no C++ code when successful.

@ -14,15 +14,21 @@
## 声明与类型 ## 声明与类型
- 不支持可变变量 `$$var` - 不支持可变变量 `$$var`
- PHP 8.4 property hooks 会降级为 AOT getter/setter;直接属性读写和动态对象读写均受支持。当前不支持对 hook 属性取引用。 - 暂不支持 PHP 8.5 `#[NoDiscard]`
- 支持 `private(set)``protected(set)` 非对称属性可见性;在 PHP 8.2~8.4 后端通过自定义属性写 handler 执行同等作用域检查。 - 支持 PHP 8.5 `(void)` 显式丢弃语句;操作数仍会求值并保留副作用,不能在赋值、返回、参数或条件等值上下文中使用。
- PHP 8.5 `clone()` / clone-with 依赖实际链接的 `libphp` 版本不低于 8.5。公开、动态、private/protected/readonly 和 property hook 属性,以及调用顺序、错误传播和 callable 路径均有 PHPT 覆盖。
- PHP 8.4 property hooks 会编译为 AOT getter/setter,并注册对应的 Zend hook 元数据;直接属性读写、Reflection 和对象遍历均受支持。当前不支持对 hook 属性取引用。
- PHP 8.4 Reflection Lazy Object 不能用于 TypePHP AOT 类。AOT 类以 persistent internal class 注册,而 Zend 的 `zend_object_make_lazy()` 明确拒绝 internal class;运行时动态加载的 ZendPHP user class 不受此限制。
- 支持 `private(set)``protected(set)` 非对称属性可见性,包括 constructor property promotion;Zend-backed 对象通过 PHP 8.4+ 类级 object handler 执行作用域检查,并保留 promoted/set visibility/implicit final 反射标志;Native 对象通过编译期访问检查执行同等作用域规则。
- 支持 final constructor property promotion,但 TypePHP 要求同时显式声明 `public`、`protected` 或 `private`;不接受 PHP 8.5 的 `final int $value` 隐式 public promotion 写法。该语法作为 TypePHP 扩展不受所链接 `libphp` 的源码语法版本限制,使用 PHP 8.4 `libphp.so` 时仍然可用。
- TypePHP 禁止在全局或命名空间常量声明上使用 attributes;PHP 8.5 global constant attributes 不在支持范围内。class constant attributes 不受此限制。
- 不支持闭包或箭头函数按引用返回。 - 不支持闭包或箭头函数按引用返回。
- 暂不支持 PHP 8.5 在全局常量、类常量、参数默认值或属性默认值中使用 `static function`;初始化表达式内嵌套的闭包同样会在编译期被拒绝。
- `__construct()` 不允许返回值。 - `__construct()` 不允许返回值。
- 参数默认值不允许出现在必填参数之前(`PHP`允许,但会直接丢弃此默认参数)。 - 参数默认值不允许出现在必填参数之前(`PHP`允许,但会直接丢弃此默认参数)。
- 不支持引用可变参数 `&...$args` - 不支持引用可变参数 `&...$args`
- 联合类型、交叉类型、`nullable` 类型仍以 `mixed/any` 作为 C++ 表示,但静态阶段会利用已知表达式类型提前拒绝确定不兼容的参数、返回值和属性赋值;动态值仍保留运行时 type check。 - 联合类型、交叉类型、`nullable` 类型仍以 `mixed/any` 作为 C++ 表示,但静态阶段会利用已知表达式类型提前拒绝确定不兼容的参数、返回值和属性赋值;动态值仍保留运行时 type check。
- 局部变量类型一旦被静态推断为具体 native 类型,不支持在同一作用域内重新赋值为不兼容类型。 - 局部变量类型一旦被静态推断为具体 native 类型,不支持在同一作用域内重新赋值为不兼容类型。
- attribute 参数不支持非空数组值和 `new` 表达式。
## declare ## declare
@ -33,6 +39,7 @@
## 调用与引用 ## 调用与引用
- `exit(message: $value)` 可作为 TypePHP named-argument 扩展使用;它与位置参数 `exit($value)` 进入同一退出路径。
- TypePHP 使用严格参数数量规则:非 variadic 函数不接受声明范围之外的额外参数;`func_get_args()` 不会隐式放宽签名。 - TypePHP 使用严格参数数量规则:非 variadic 函数不接受声明范围之外的额外参数;`func_get_args()` 不会隐式放宽签名。
- 已知签名的普通函数、普通方法和 native 直调支持引用参数及写回;不要把编译器内部跨 Trait 动态分派的限制误写成“TypePHP 不支持引用参数”。 - 已知签名的普通函数、普通方法和 native 直调支持引用参数及写回;不要把编译器内部跨 Trait 动态分派的限制误写成“TypePHP 不支持引用参数”。
- 闭包和箭头函数不支持引用参数。 - 闭包和箭头函数不支持引用参数。
@ -44,16 +51,16 @@
## 对象模型 ## 对象模型
- `toInt()`、`toString()`、`toArray()` 等保留关键词方法先于普通对象方法解析;需要参数的同名业务方法不按普通对象方法语义调用。 - `toInt()`、`toString()`、`toArray()` 等保留关键词方法先于普通对象方法解析;需要参数的同名业务方法不按普通对象方法语义调用。
- `toAny()``toRef()` 是不可覆盖的 TypePHP 关键词方法,普通 class-like 声明不得定义同名方法(方法名按 PHP 规则大小写不敏感)。Native class 仅可显式定义返回 `mixed/any``toAny()` 转换方法,不提供隐式转换;Native class 不支持 `toRef()`
- 固定值类型属性未显式初始化时使用类型零值,不保留 ZendPHP 的完整 uninitialized 状态;因此 `??` 等依赖 uninitialized 状态的表达式可能不同。 - 固定值类型属性未显式初始化时使用类型零值,不保留 ZendPHP 的完整 uninitialized 状态;因此 `??` 等依赖 uninitialized 状态的表达式可能不同。
- 禁止子类用同名 `private` 属性隐藏父类私有属性;`public` / `protected` 同名声明视为同一个继承 property slot,仍须满足类型、可见性和 `readonly` 兼容性要求。 - 禁止子类用同名 `private` 属性隐藏父类私有属性;`public` / `protected` 同名声明视为同一个继承 property slot,仍须满足类型、可见性和 `readonly` 兼容性要求。
- 为避免 typed property 写入路径引入额外动态检查,native typed property 在右值类型不确定或与属性类型不一致时会退化为 `setProperty()`;部分标量赋值可能遵循 Zend 弱类型转换,而不是 AOT 默认 strict 语义。 - 为避免 typed property 写入路径引入额外动态检查,native typed property 在右值类型不确定或与属性类型不一致时会退化为 `setProperty()`;部分标量赋值可能遵循 Zend 弱类型转换,而不是 AOT 默认 strict 语义。
- constructor property promotion 的运行时属性可用,但 `ReflectionProperty::isPromoted()` 目前不返回标准 PHP 结果。
## 表达式与控制流 ## 表达式与控制流
- `match` 的 arm condition 不能是 `match` 表达式。 - `match` 的 arm condition 不能是 `match` 表达式。
- `foreach` by reference 的 value 只能是变量。 - `foreach` by reference 的 value 只能是变量。
- `foreach` by reference 不支持 list destructuring。 - `foreach` list destructuring 不支持按引用绑定元素
- `std::vector`、`std::map`、`std::ordered_map` 在 `foreach` 期间禁止追加、插入、`unset()` 或整体替换;已有元素的非结构性更新仍可使用赋值运算符完成。 - `std::vector`、`std::map`、`std::ordered_map` 在 `foreach` 期间禁止追加、插入、`unset()` 或整体替换;已有元素的非结构性更新仍可使用赋值运算符完成。
- 固定 native typed object property 不允许按 PHP 未初始化语义自由 `unset()` - 固定 native typed object property 不允许按 PHP 未初始化语义自由 `unset()`
- native 类型变量执行 `unset()` 不会产生标准 PHP 的变量删除语义。 - native 类型变量执行 `unset()` 不会产生标准 PHP 的变量删除语义。

@ -0,0 +1,116 @@
# Interface Property Hooks 实现方案
本文记录 TP-AOT-010 的设计与实施计划。目标是支持 PHP 8.4 的 Interface Property Hook 契约,同时保持 TypePHP Native 调用的零成本抽象,并让 PHP 8.4 ZendVM 的 Reflection、动态类链接和继承检查获得完整元数据。
## 当前状态(2026-08-14)
第一阶段已经落地:Interface 契约模型、AOT 实现检查、get/set 方向方差、PHPX 抽象 Hook 元数据、Reflection、动态 PHP 实现类及回归测试均已接通。显式 setter 参数类型仍按下文约定在编译期拒绝;完成独立写入类型模型后再开放。
## 1. 设计结论
Interface 中的 Hooked Property 只表示属性契约:
```php
interface Named
{
public string $name { get; set; }
}
```
- Interface 不持有属性槽,不生成 getter/setter 实现,也不产生访问时的契约检查。
- TypePHP 在编译期验证已知 AOT 类是否满足属性的可见性、类型和 `get`/`set` 能力。
- PHP 8.4 目标在 MINIT 注册原生 Zend Hook 元数据,使 Reflection 和动态 PHP 类获得相同契约。
- TypePHP、PHPX 和最终目标运行时的最低版本均为 PHP 8.4,不提供旧版本降级路径。
## 2. 语法与诊断
支持三类契约:
```php
public string $readable { get; }
public string $writable { set; }
public string $readWrite { get; set; }
```
Interface Property Hook 必须是 `public`、非 `static`、无默认值且 Hook 不得包含函数体。普通 Interface Property、`private`/`protected`、`readonly`、重复或未知 Hook,以及带实现体的 Hook 均在 TypePHP 编译期抛出 FatalError。错误信息应尽可能与 PHP 8.4 一致。
第一阶段只接收隐式 setter 参数:
```php
public string $name { set; }
```
PHP 8.4 还允许 `set(string|Stringable $value)` 这类显式、可逆变的 setter 参数。该语法需要让编译期契约模型与 Zend Hook `arg_info` 同时保存独立于属性读取类型的写入类型;在这部分完成前,TypePHP 会给出明确的编译期错误,不生成可能错误的运行时元数据。
## 3. 编译器模型
Interface Property Hook 不应伪装成普通属性或 lowering 后的普通方法。为其建立独立契约模型,至少保存:
- 属性名和声明节点;
- 解析后的 TypePHP 类型与类类型;
- 是否要求 `get`
- 是否要求 `set`
- 可见性及其他用于诊断的标志。
契约存放在 `InterfaceDef` 中。AST/预处理阶段只收集和验证声明,不为 Interface 分配属性槽,不运行具体类使用的 `PropertyHookLowering`,也不生成隐藏方法。
所有类型完成预处理后再执行契约链接:展开父 Interface 契约,然后检查实现类自身或父类提供的属性。普通 public backed property 同时满足读写契约;Hooked Property 根据实际 Hook 能力判断。get-only 类型按读取方向协变,set-only 类型按写入方向逆变,同时包含 get/set 时保持不变。
## 4. PHPX 与 Zend 元数据
现有 `typephp_register_property_hooks()` 用于具有真实 AOT getter/setter 的具体类,不能复用于抽象 Interface Hook。
PHPX 增加独立 helper:
```cpp
typephp_register_abstract_property_hooks(
zend_class_entry *interface_ce,
zend_property_info *property_info,
bool readable,
bool writable
);
```
TypePHP/PHPX 已统一要求 PHP 8.4+,因此该 helper 直接访问 PHP 8.4 ABI,并负责:
- 持久化分配 `zend_property_info::hooks`
- 创建没有 handler 的 abstract `get`/`set` `zend_internal_function` 元数据;
- 设置 `ZEND_ACC_PUBLIC | ZEND_ACC_ABSTRACT`、正确的参数/返回类型及 `common.prop_info`
- 更新 `num_hooked_props`,使 Zend inheritance 和 Reflection 识别该契约;
- 保证所有字符串、Hook 表和函数描述具有 MINIT 级持久生命周期。
生成代码先注册 Interface,再以 `IS_UNDEF`、`ZEND_ACC_PUBLIC | ZEND_ACC_ABSTRACT | ZEND_ACC_VIRTUAL` 声明属性并挂载抽象 Hook,最后才注册和链接实现类。
## 5. PHP 版本边界
TypePHP 区分源码语言版本与链接运行时:
- `--php-version` 只允许 `8.4``8.5`,用于解析语法和处理项目条件;
- PHPX headers、`libphp` 与最终运行时必须为 PHP 8.4 或更高版本;
- `--php-version``libphp.so` 的小版本不要求完全一致,例如使用 8.5 语法模式并链接 PHP 8.4 时,最终能否构建仍由实际使用的 Zend API 决定;
- PHP 8.4 以下环境在 TypePHP/PHPX 构建入口直接拒绝。
## 6. TDD 覆盖
实现前先加入失败测试,覆盖:
1. get-only、set-only、get/set Interface 契约;
2. 普通 backed property、Hooked Property 和继承属性满足契约;
3. 缺失属性、缺少 get/set、非 public 和类型不兼容的编译错误;
4. Interface 继承、多个契约的合并与冲突;
5. Reflection 的 abstract、virtual、hasHook/getHook 元数据;
6. PHP 8.4 动态 PHP 类的成功与失败链接;
7. O0/O3 结果一致,Interface 不生成属性槽或 Native Hook 实现;
8. PHPX helper 在 NTS/ZTS 和 PHP 8.4/8.5 下的生命周期与 ABI 回归。
## 7. 实施顺序
1. 添加 TP-AOT-010 正常场景及语法错误 PHPT,确认当前失败。
2. 增加 Interface Property Contract 模型和预处理收集逻辑。
3. 实现 Interface 继承与实现类的编译期契约检查。
4. 在 PHPX 增加抽象 Hook 元数据 helper。
5. 修改 stub 生成和类注册顺序,接入 PHP 8.4 Zend 元数据。
6. 添加 Reflection、动态类链接、目标版本和生成代码测试。
7. 执行 Interface、Property Hook、Reflection 及全量编译器回归。
完成后的运行时属性访问仍直接进入实现类的普通属性或 Native Hook;Interface 契约本身只存在于编译期模型和 MINIT 元数据中,不进入请求热路径。

@ -654,6 +654,8 @@ php bin/tpc.php examples/prime -o prime
### 1. 函数命名规范 ### 1. 函数命名规范
这里的 `php_` 仅用于“用户 PHP 函数/类方法到 C++ callable”的 ABI 映射,不是 TypePHP 或 PHPX 内部 helper 的通用前缀。内部 ZendAPI 包装必须使用 `php::`,TypePHP 独有逻辑使用 `typephp_`。完整规则参见 [C++ 命名空间、前缀与符号 ABI](CPP_SYMBOL_NAMING.md)。
**正确**: **正确**:
```cpp ```cpp
bool php_is_prime(php::Int n); bool php_is_prime(php::Int n);
@ -713,7 +715,8 @@ class InternalService {}
`NoExport``ExtensionProvider` 都遵循 PHP 类名解析规则,支持完全限定名、`use` 和 `use ... as ...` 别名。只有解析结果严格指向根命名空间内建 Attribute 时,编译器才会消费它。 `NoExport``ExtensionProvider` 都遵循 PHP 类名解析规则,支持完全限定名、`use` 和 `use ... as ...` 别名。只有解析结果严格指向根命名空间内建 Attribute 时,编译器才会消费它。
`php_<target>_func_decl.h``php_<target>_data_decl.h` 都是 TypePHP 构建过程的内部生成文件,不是库的对外开发头文件。 `php_<target>_func_decl.h``php_<target>_data_decl.h` 都是 TypePHP 构建过程的内部生成文件,不是库的对外开发头文件。
`func_decl.h``-m lib` 构建时还会被强制包含,用于给当前 target 的 `php_*` C++ ABI 函数添加平台导出标记;`data_decl.h` 仅在 target 内部声明全局变量、字面量、常量对象和运行时映射等数据。 `func_decl.h``-m lib` 构建时还会被强制包含,用于给当前 target 的 `php_*` C++ ABI 函数添加平台导出标记;`data_decl.h` 仅在 target 内部声明全局变量、常量对象以及字面量/运行时映射 accessor。
这些项目数据声明位于 `typephp_<target>` C++ namespace;literal/cache 底层表保留在 `extension-<target>.cc` 中,其他 translation unit 只通过 `get_str()`、`get_class()`、`get_func()` 等 accessor 使用,不直接依赖 storage。
发布 TypePHP 库时,对外提供: 发布 TypePHP 库时,对外提供:

@ -0,0 +1,137 @@
# Native Class 实现验收矩阵
> 审计日期:2026-08-17
> 本文记录 `#[Native]` 对象模型的需求、实现入口和直接验证证据。它是
> [NATIVE_CLASS_OBJECT.md](NATIVE_CLASS_OBJECT.md) 的实现验收附件,不替代语义设计文档。
## 1. 验收原则
每一项能力必须同时具有:
1. 明确的语言边界;
2. 可定位的编译器或 PHPX 实现;
3. 正向 PHPT、负向 PHPUnit 或 PHPX C++ 单测中的直接证据。
仅有代码、仅有文档或“当前没有发现失败”均不视为完成。Native Object 没有 Zend
表示,因此任何不能静态证明安全的跨边界行为都必须在生成 C++ 前拒绝。
## 2. 对象模型与代码生成
| 要求 | 实现证据 | 测试证据 | 结论 |
|---|---|---|---|
| `#[Native]` 只用于具名 class | `NativeClassAttributeLowering`、`NativeClassSupportTrait` | `testRejectsNativeAttributeOnInterface/Trait/Enum/AnonymousClass` | 已验证 |
| 不注册 Zend class/object handlers | Native struct、descriptor 和自由函数生成路径 | `clone-and-zend-invisible.phpt`、Reflection 负向测试 | 已验证 |
| 方法保持 `php_*` 自由函数 ABI | Native method/virtual thunk 生成路径 | `basic.phpt`、`chained-call.phpt` | 已验证 |
| 静态可解析的 `new NativeClass()` 使用 Native Heap | `CompilerBase::parseNew()`、`php::nativeConstruct()` | `basic.phpt`、`construction-gc-roots.phpt` | 已验证 |
| `new (表达式)()` 保持普通 PHP 动态实例化 | `parseNew()` 只对 `Node\\Name` 进入 Native 分支 | `testLeavesDynamicClassExpressionsToTheOrdinaryPhpPath` | 已验证 |
| Native 对象本身不能充当动态 class target | `assertNotNativeObjectDynamicClassTarget()` | dynamic new/static call/class constant 负向测试 | 已验证 |
| 所有不支持的用法在编译期终止 | Native 边界检查、类型兼容检查 | 131 项 `NativeClassValidationTest` | 已验证 |
## 3. 属性与固定布局
| 要求 | 实现证据 | 测试证据 | 结论 |
|---|---|---|---|
| 所有属性必须声明类型 | Native field validation | `testRejectsUntypedProperty` | 已验证 |
| bool/int/float 使用固定值字段 | Native field C++ type mapping | `basic.phpt`、`numeric-properties.phpt` | 已验证 |
| string/array/object/typed object/Stream/mixed 可作为字段 | Native PHPX field mapping、写入检查 | `phpx-properties.phpt`、`stream-property.phpt`、`composite-property-types.phpt` | 已验证 |
| BigInt/BigFloat/Decimal 可作为字段 | 高精度字段映射与 trace/destroy | `high-precision-properties.phpt` | 已验证 |
| Native 类型字段保存裸指针,可形成循环类型 | struct 前置声明、descriptor trace | `mutual-reference-types.phpt`、`gc-cycle.phpt` | 已验证 |
| 未显式初始化字段使用确定零值 | Native field initializer | `zero-values.phpt` | 已验证 |
| 属性写入保持声明类型 | Native property assignment validation | composite、stream 及多项负向 PHPUnit | 已验证 |
| 仅 `any` 属性允许取 PHP 引用 | Native property reference lowering | `any-property-reference.phpt` 及 mixed/fixed property 负向测试 | 已验证 |
| Native 属性不支持 `unset()` | property unset validator | `testRejectsUnsetOnNativeObjectProperties` | 已验证 |
| readonly 属性不支持 | Native declaration validator | `testRejectsReadonlyPropertyUntilNativeWriteStateIsImplemented` | 已验证 |
| Box/Std Container 不能嵌入字段 | Native field validator | Box/Std Container property 负向测试 | 已验证 |
## 4. 身份、空值与调用 ABI
| 要求 | 实现证据 | 测试证据 | 结论 |
|---|---|---|---|
| `$a = $b` 只复制指针并共享对象身份 | Native pointer local representation | `parameter-semantics.phpt` | 已验证 |
| Native 参数和返回必须显式声明具体类 | call argument/return boundary validation | untyped/mixed/interface 参数与返回负向测试 | 已验证 |
| 普通 Native 参数非空,`?Class` 才可为空 | function entry/return checks | `non-null-parameter.phpt`、`nullable-signatures.phpt`、`return-nullability.phpt` | 已验证 |
| Native 参数、返回和变量禁止 `&` | reference boundary validation | reference parameter/return/assignment/function/method 负向测试 | 已验证 |
| Native variadic、union/intersection signature 不支持 | signature validation | variadic/union/null-union 负向测试 | 已验证 |
| `unset($object)`/`$object = null` 只清当前 pointer slot | Native root slot lowering | `unset-alias.phpt` | 已验证 |
| `===`/`!==` 与 `match` 使用指针身份 | Native identity lowering | `strict-identity.phpt`、`match-identity.phpt` | 已验证 |
| ternary/match/coalesce 为兄弟子类选择最近公共 Native 基类 | `getCommonNativeObjectClass()`、selection pointer cast | `value-selection.phpt`、跨文件 global discovery 测试 | 已验证 |
| 条件表达式检查非空指针,不调用 `toBool()` | Native condition lowering | `conditions.phpt` | 已验证 |
| 松散比较、算术、位运算、增减、复合写入和 switch 禁止 | operator validators | 对应 PHPUnit 负向测试 | 已验证 |
| `isset`/`empty`/`is_null`/nullsafe 保持 typed pointer | Native selection/nullsafe lowering | `isset-empty.phpt`、`is-null.phpt`、`nullsafe.phpt` | 已验证 |
| 调用参数严格从左到右求值并在 safe point 精确 rooting | Native call argument materialization | `call-argument-roots.phpt`、`constructor-argument-roots.phpt` | 已验证 |
## 5. 类语言能力
| 要求 | 实现证据 | 测试证据 | 结论 |
|---|---|---|---|
| 单继承、abstract 与有限虚分派 | Native C++ inheritance/virtual slot adapters | `abstract-method.phpt`、`polymorphic-clone.phpt`、`virtual-signature-variance.phpt` | 已验证 |
| public/private/protected 在编译期检查 | Native member resolution | `method-visibility.phpt` 及不可访问方法/常量负向测试 | 已验证 |
| Trait 在注入后按普通 Native member 编译 | 现有 Trait AST 注入 + Native member generation | `trait-inheritance-interface.phpt` | 已验证 |
| Interface 仅作编译期契约,不能成为值表示 | interface contract validator | `internal-interface.phpt`、`interface-property-hooks.phpt` 及 interface escape 负向测试 | 已验证 |
| 编译期可解析的 `instanceof` 折叠 | Native instanceof lowering | `instanceof.phpt`、dynamic instanceof 负向测试 | 已验证 |
| Getter/Setter 注解生成直接调用 | annotation lowering + Native method path | `generators.phpt` | 已验证 |
| Property Hook 只支持直接 get/set | Native hook lowering | `property-hooks.phpt`、`property-hook-native-object.phpt` 及间接操作负向测试 | 已验证 |
| `clone` 保持动态子类、PHPX COW 和浅对象语义 | Native clone descriptor/thunk、`php::nativeClone()` | clone 系列 PHPT、`clone-phpx-fields.phpt` | 已验证 |
| `__construct` 仅由 `new` 调用 | Native construction path、显式调用检查 | construction 系列 PHPT、explicit constructor 负向测试 | 已验证 |
| `__destruct` 由 GC 至多执行一次,继承链 derived-to-base | Native finalizer chain | destructor/finalizer/lifecycle 系列 PHPT | 已验证 |
| `__invoke``__toString` 使用确定 Native Call | Native magic method allow-list | `magic-methods.phpt` | 已验证 |
| 动态魔术方法、变量属性/方法名不支持 | Native magic/dynamic access deny-list | dynamic magic、variable method/property 负向测试 | 已验证 |
| `toArray/toString/toInt/toFloat/toBool/toObject` 要求实体方法、零参数和精确返回类型 | Native keyword method resolution | `keyword-conversions.phpt`、`testNativeObjectToObjectKeywordUsesDeclaredNativeMethod` 及签名负向测试 | 已验证 |
| `count($obj)` 仅在实现 Countable 时特化 | Native count optimizer | `keyword-conversions.phpt`、count-without-countable 负向测试 | 已验证 |
| `ArrayAccess` 直接语法映射到 Native `offset*()` 方法 | Native array access lowering | `array-access.phpt` | 已验证 |
| Native `ArrayAccess` 禁止间接修改和引用 | writable-chain/reference validators | ArrayAccess compound/increment/nested/property/reference/coalesce 负向测试 | 已验证 |
| Native `Iterator` foreach 映射到协议方法,保持 PHP 调用顺序 | Native foreach lowering | `iterator.phpt` | 已验证 |
| `IteratorAggregate` 分流 Native Iterator 与 PHP Traversable | aggregate return-type lowering | `iterator.phpt` | 已验证 |
| Native foreach 不枚举属性且禁止引用遍历 | interface/reference validators | foreach 负向 PHPUnit | 已验证 |
## 6. GC 与生命周期
| 要求 | 实现证据 | 测试证据 | 结论 |
|---|---|---|---|
| Wren 风格精确、非移动、STW mark-sweep | `phpx/thirdparty/wren-gc`、`native_gc.cc` | PHPX `wren_gc.*` | 已验证 |
| 裸指针写入无 RC、无 write barrier | Native pointer field/local codegen | 生成 C++ 审查、Native PHPT | 已验证 |
| 16 MiB 初始阈值、1 MiB 下限、50% headroom | Wren GC 配置 | `wren_gc.uses_stable_native_heap_defaults` | 已验证 |
| 精确 root frame 保持对象图存活 | `NativeRootFrame`、generated root slots | PHPX root tests、`gc-cycle.phpt` | 已验证 |
| Fiber 非 LIFO 生命周期安全 | root frame registry | `fiber-lifetime.phpt`、`fiber-shutdown.phpt`、PHPX Fiber root tests | 已验证 |
| global/static request roots 在 ZTS 下为 thread-local | generated globals/root registration | ZTS 环境下 `global-and-static.phpt`、PHPX request root tests | 已验证 |
| RSHUTDOWN 清空 root 并销毁 heap | `nativeGcRequestShutdown()` | PHPX shutdown tests | 已验证 |
| finalizer 可复活一次,之后不重复执行 | Wren/Native finalization state | `gc-cycle.phpt`、PHPX resurrection tests | 已验证 |
| finalizer 中分配、异常和 Zend 状态安全 | finalizer queue/exception cleanup | finalizer/lifecycle PHPT、PHPX finalizer tests | 已验证 |
| 构造或克隆失败不产生悬空对象,已逃逸对象保持有效 | `nativeConstruct()`、`nativeClone()` failure paths | `failed-lifecycle-escape.phpt`、`failed-clone-finalizer.phpt` | 已验证 |
## 7. ZendVM 边界与容器
| 要求 | 实现证据 | 测试证据 | 结论 |
|---|---|---|---|
| Native Object 不能进入 PHP array/object property/mixed | escape and boundary validators | 对应 PHPUnit 负向测试 | 已验证 |
| 不能传给 PHP/ZendVM 动态函数、Closure 或 constructor | call boundary validator | dynamic call、Closure、Zend constructor 负向测试 | 已验证 |
| Reflection/WeakReference/serialize/json_encode 不支持 | facility-specific diagnostics | 对应 PHPUnit 负向测试 | 已验证 |
| Generator 不能保存、接收或产出 Native pointer | generator boundary validator | generator 系列负向测试 | 已验证 |
| 普通函数跨 Fiber suspend 的 Native local 有精确 root | root frame lifecycle | Fiber PHPT | 已验证 |
| 局部 Std Container 可保存具体 Native pointer | Std Container Native value mapping/root frame | `std-containers.phpt` | 已验证 |
| Native Std Container 不能逃逸为 Zend 值、static/global 或 closure capture | container escape validation | Std Container 系列负向 PHPUnit | 已验证 |
| `include`/`eval` 不暴露 Native local 到 Zend symbol table | include scope filtering | `include-native-scope.phpt` | 已验证 |
## 8. 项目级分析
| 要求 | 实现证据 | 测试证据 | 结论 |
|---|---|---|---|
| Native class 前向声明不依赖文件顺序 | declaration discovery pre-pass | `testDiscoversNativeTypesBeforeCrossFileSignaturePreprocessing` | 已验证 |
| global Native slot ABI 在任一 C++ 文件生成前确定 | `NativeGlobalDiscovery`、`NativeGlobalTypeResolver` | `testDiscoversNativeGlobalSlotBeforeEarlierReaderIsConverted`,实际双文件构建 | 已验证 |
| `global $slot` 与静态可解析的 `$GLOBALS[...]` 使用同一 Native root slot | literal/constant global slot lowering、request root registration | `global-and-static.phpt`、跨文件 Closure/常量 `$GLOBALS` fixture | 已验证 |
| 动态 `$GLOBALS[$key]` 不得承载 Native Object | dynamic Zend boundary validation | `testRejectsNativeObjectStoredThroughDynamicGlobalsKey` | 已验证 |
| global slot 固定首个 Native 类型,只允许子类或 null | global registration/type validation | `global-and-static.phpt`、global type change 负向测试 | 已验证 |
| 未使用 Native Class 的项目跳过 Native global pre-pass | `discoverNativeGlobalObjects()` fast return | 源码检查、全量 PHPUnit | 已验证 |
## 9. 当前验证命令
```bash
./run-tests.php -j4 --compiler ./tpc tests/compiler/native-class/
vendor/bin/phpunit phpunit/src/NativeClass/NativeClassValidationTest.php
/home/swoole/workspace/aot/phpx/build/bin/phpx-tests \
--gtest_filter='wren_gc.*:native_gc.*'
```
本次 Iterator 专项结果为:`iterator.phpt` 1/1、Native Class PHPUnit 136/136,
普通 foreach 回归 14/14。Native Class PHPT 目录现有 71 项;按当前任务约定暂未重复执行
该目录及编译器 PHPT 全量测试,留待下一轮统一回归。

File diff suppressed because it is too large Load Diff

@ -0,0 +1,212 @@
# Zend Object 创建与属性默认值初始化
本文记录 TypePHP 生成的 Zend Class 在 MINIT 和对象创建阶段的初始化职责,重点说明何时需要自定义 `create_object`、其中允许执行哪些行为,以及对象创建热路径上的性能边界。
本文只讨论注册到 ZendVM 的普通 TypePHP Class。`#[Native]` Class 使用 Native Heap 与 GC,不走本文流程。
## 1. 两个初始化阶段必须分开
TypePHP Class 的属性初始化分为两个阶段:
1. `gen_stub.php` 在 MINIT 生成 `register_class_*()`,建立 `zend_class_entry`、属性元数据和默认属性表;
2. 只有默认属性表无法准确表达的值,才在每次创建对象时由自定义 `create_object` 补充。
这两个阶段不能重复执行相同的属性赋值。`register_class_*()` 已写入的值会由 Zend 的 `object_properties_init()` 复制到新对象;再次调用 `zend_update_property()` 不仅没有语义价值,还会进入属性名查找、类型检查、handler 分派和引用计数路径。
## 2. gen_stub.php 负责的默认值
以下值可以准确写入 Zend Class 的默认属性表:
| 源代码默认值 | 注册阶段表示 | 是否需要在 `create_object` 中再次写入 |
|---|---|---|
| `null` | `ZVAL_NULL` | 否 |
| `bool` | `ZVAL_TRUE/FALSE` | 否 |
| `int` | `ZVAL_LONG` | 否 |
| `float` | `ZVAL_DOUBLE` | 否 |
| `string` | 持久化 `zend_string` | 否 |
| 标量常量表达式 | 编译期求值后的标量 zval | 否 |
| `[]` | `ZVAL_EMPTY_ARRAY` | 否 |
| 没有显式默认值的 TypePHP typed property | TypePHP 规定的零值、空字符串、空数组、`null` 或 `UNDEF` | 否 |
例如:
```php
class Value
{
private const BASE = 20;
public int $id = self::BASE + 3;
public string $name = 'type' . 'php';
public array $items = [];
}
```
只要表达式能够在编译期安全求值,以上三个属性都应完全依赖 Zend Class 默认属性表。创建 `Value` 时不得再次调用 `zend_update_property()`
## 3. 默认值何时需要运行时补充
当前 `gen_stub.php` 不能在默认属性表中准确表示以下值。
### 3.1 非空数组
非空数组默认值当前在注册函数中使用 `ZVAL_EMPTY_ARRAY` 作为占位值。每个对象必须构造独立、语义正确的数组值:
```php
class Request
{
public array $options = ['timeout' => 10];
}
```
因此 `Request::$options` 需要在 `create_object` 中补充。多个对象仍遵守 PHP 数组的 copy-on-write 语义;修改一个对象的数组不得影响其他对象。
数组常量也遵守相同规则。若编译器只能确定它是数组、不能证明它为空,则保守地保留运行时初始化。
### 3.2 Enum case
Enum case 是对象,不是标量常量:
```php
enum State
{
case Ready;
}
class Task
{
public State $state = State::Ready;
}
```
类注册代码当前只能先生成占位值,`create_object` 再取得真正的 enum case 对象并写入属性。因此“只有非空数组才需要自定义 `create_object`”并不成立,enum case 是明确的第二类反例。
### 3.3 无法安全解析的常量表达式
若预处理阶段无法证明默认值可由 Zend 默认属性表准确表达,编译器必须保守地保留运行时初始化。优化只能删除已证明冗余的工作,不能根据表达式外形猜测其运行时类型。
## 4. handlers 与父类 allocator
### 4.1 Property Hook 与非对称 set 可见性不单独触发
PHP 8.4 Property Hook、`private(set)` 和 `protected(set)` 会安装 TypePHP 自定义 object handlers,但这本身不要求覆盖 `create_object`。Zend 8.4 的 `object_properties_init()` 直接复制 class default table,不调用 read/write handler;普通 `php::stdCreateObject()` 已能正确设置最终 handlers。
只有该类同时含有非空数组、enum case 等运行时默认值时,才需要自定义创建流程。补充初始化必须绕过 setter;即使使用 `zend_std_write_property()`,PHP 8.4 也会根据 Hook 元数据调用 setter。当前生成代码因此使用编译期已知的 property offset,经 PHPX `Object::attr(offset)` 直接更新 backing slot。
### 4.2 父类自定义对象分配器
若父类来自 PHP 内置扩展,或祖先类拥有自定义对象存储布局,子类不能绕过父类的 allocator。当前类因运行时默认值确实需要自定义创建流程时,必须先调用保存的父类 `create_object`,再补充当前类的值。
TypePHP 父类已经安装自定义 allocator 时,普通子类通常直接继承它。只有子类自身也需要补充初始化时,才生成新的委派层。
## 5. 自定义 create_object 的执行流程
生成代码通过 `typephp_create_object_with_defaults()` 完成以下步骤:
1. 保存类最终的 `default_object_handlers`
2. 若必须尊重父类对象布局,调用保存的父类 allocator;否则执行 `zend_objects_new()``object_properties_init()`
3. 临时把新对象切换到 Zend 标准 object handlers,确保异常路径和其他对象操作处于可控状态;
4. 只执行标记为 `requiresRuntimeDefaultInit` 的属性初始化,并通过缓存的 declared-property offset 直接写 backing slot;
5. 每次写入后检查 Zend 异常;
6. 无论正常返回还是发生 C++ 异常,都恢复最终 handlers;
7. 返回已完整初始化的 `zend_object *`
初始化器是模板参数和编译期 lambda,不使用 `std::function`,也不会为 lambda 动态分配内存。`delegate_to_base` 是调用点确定的布尔值,优化构建中通常可被 C++ 编译器折叠。
以下行为不属于 `create_object`
- PHP `__construct()` 的函数体;
- static property 默认值初始化;它在 `module_init()` 中完成;
- 已由默认属性表表达的标量、`null` 和空数组赋值;
- clone 后重新应用默认值;clone 应复制源对象当前状态,而不是重新创建默认状态。
## 6. 已修复的主要性能问题
旧生成逻辑只要类中存在任意显式非 static 默认值,就安装自定义 `create_object`,并在每次创建对象时重新 update 所有默认属性。这会产生两层重复成本:
1. 只含 `public int $value = 0` 的普通类也绕过标准快速创建路径;
2. 一个类只要含有一个非空数组,其他标量属性也会被逐个重复 update。
当前规则已经调整为:
- 只有确实需要运行时补充的属性才使 `requireCtor` 生效;
- 已由 `gen_stub.php` 准确注册的属性不会出现在运行时初始化 block 中;
- 只有 Hook/非对称可见性而没有运行时默认值的类不再生成空的自定义 allocator;
- Hook 与运行时默认值同时存在时,使用固定 property offset 更新 backing slot,不调用 setter。
在 micro benchmark 中,仅包含标量属性的 `new Foo()` 已从约 `1.8s` 降至约 `0.78s`,与同环境 ZendPHP 扣除空循环后的约 `0.83s` 接近。该数字只用于记录优化量级,不是跨机器性能承诺。
## 7. 已实现优化、剩余成本与后续方向
### 7.1 非空数组使用请求级模板与 copy-on-write
不能把非空数组放进 internal class 的默认属性表,但这不等于必须为每个对象重新构建数组。当前生成器已经使用请求级默认值模板:
1. 每个包含运行时数组默认值的类拥有一组 `THREAD_LOCAL php::Var` 模板和一个初始化状态,NTS 构建不引入锁;
2. 第一次创建该类对象时,通过 `UNEXPECTED(!initialized)` 惰性构建该类的全部模板;
3. 模板全部在局部临时值中成功构建后才提交并设置初始化标记,构造异常不会发布半初始化状态;
4. 模板初始化发生在对象分配之前,失败时不会遗留一个尚未返回的对象;
5. 后续创建对象时只把模板 zval 复制到目标 backing slot,即增加一次数组引用计数;
6. 某个对象第一次修改该属性时,由 Zend/PHPX 的 `SEPARATE_ARRAY` 执行 copy-on-write;
7. 在 `module_clean()` 中释放模板并重置初始化状态,request allocator 分配的 HashTable 不会跨越 RSHUTDOWN。
以如下默认值为例:
```php
class Request
{
public array $options = [
'timeout' => 10,
'headers' => ['Accept' => 'application/json'],
];
}
```
若创建一万个对象但不修改 `$options`,数组及嵌套数组只构建一次;每个对象只持有共享 zval。若其中一个对象执行 `$request->options['timeout'] = 30`,只有该对象在写入时分离,其他对象和模板保持不变。嵌套数组也继续使用 Zend 原有的逐层 copy-on-write 规则。
PHP 属性默认数组不能包含引用,允许出现在常量表达式中的对象主要是不可变的 enum case,因此共享模板符合默认属性语义。PHPT 已覆盖顶层写入、嵌套写入、`unset`、引用写入和动态对象写入,确认这些路径都会正确分离。
不能简单地在 MINIT 构造持久化数组并传给 `zend_declare_typed_property()`。TypePHP 注册的是 `ZEND_INTERNAL_CLASS`,Zend 8.4 明确禁止 internal property 使用 refcounted default zval;`_object_properties_init()` 的 internal-class 快速路径也不会增加默认值引用计数。非空 array 与 enum object 都属于 refcounted value。
因此,在不改变“TypePHP Class 注册为 internal class”这一基础设计、也不修改 Zend ABI 的前提下,非空数组仍不进入 class default table;表外请求级模板把数组构造成本从“每个对象一次”降为“每个请求、每个默认值一次”。未修改默认数组的对象只承担 zval 复制和引用计数成本,实际修改的对象才承担数组分离成本。
模板按类惰性初始化,而不是在 RINIT 无条件构建全部模板:大型项目中很多类在一次请求内不会实例化。每个对象只增加一个高度可预测的初始化状态分支;第一次之后该分支稳定为 false。
暂不生成模块生命周期的 persistent immutable template。该方案需要完整验证 persistent HashTable、interned string、嵌套数组、MSHUTDOWN 和 ZTS,并且包含运行时常量或 enum case 的数组仍要走请求级路径。在 ZendVM 对这些组合的约束得到充分验证前,请求级模板是安全边界。
### 7.2 已改为固定属性槽写入
运行时补充的属性在编译期已经知道 class、属性名、offset 和类型。当前实现复用 persistent property-offset cache,并通过 `php::Object::attr(offset)` 更新槽位,已经省去每个对象上的属性名 hash 查询、通用 write handler 和 Property Hook setter。
这里仍会为 initializer 建立一个短生命周期 `php::Object` carrier,并读取 offset cache。后续若 profiling 证明它是热点,可以在 MINIT 后直接保存最终 offset,或在 PHPX 增加不取得对象所有权的初始化 helper。任何进一步优化都必须继续处理旧值析构、引用计数、父类 private slot、Hook backing slot 和异常安全,不能退回裸指针的无保护赋值。
### 7.3 Enum case 可提前绑定
Enum case 同样是 refcounted object,不能直接作为 internal class 默认 zval。可考虑在 MINIT 缓存稳定的 enum case 指针或 zval,再在每次创建对象时执行正确的引用计数复制,从而省去重复 class/case 查找;仍不能省略对象属性写入本身。
### 7.4 继承链上的多层 allocator
父类和子类都拥有运行时默认值时,创建流程会逐层委派并执行各自初始化,成本随相关继承层数增长。未来可以对完全由 TypePHP 控制、且没有特殊对象布局的继承链合并初始化计划;内置扩展父类仍必须调用其 allocator。
### 7.5 保守常量可能产生不必要的 allocator
无法在预处理阶段解析的常量会保守进入运行时路径。可以在符号准备完成后增加一次统一的常量默认值分类,减少“实际是标量,但早期无法证明”的自定义 allocator。该优化必须保留 enum case 和数组常量的区别。
### 7.6 自定义 handlers 的动态访问成本
TypePHP 当前为普通 Zend Class 安装属性 handlers,以支持 typed property 的 unset 语义、Property Hook 和非对称写可见性。安装发生在 MINIT,不等同于安装自定义 `create_object`;但动态属性读写仍可能进入 handler。已被编译器解析为固定槽位的 Native 属性访问不应因此退化。
## 8. 回归测试要求
修改该流程至少应覆盖:
- 标量、标量常量表达式和空数组不生成自定义 allocator;
- 非空数组生成 allocator,且两个对象的数组修改互不影响;
- enum case 默认值在对象创建后是真正的 enum object;
- 仅含 Property Hook 或非对称 set 可见性的类不生成空 allocator,且 Reflection、动态读写行为不退化;
- Property Hook/非对称属性与运行时默认值组合时不触发 setter;
- 父子类分别声明运行时默认值时,父类和子类属性都正确;
- 继承内置扩展类时不破坏其对象布局;
- 异常路径恢复 object handlers;
- 自举编译和完整 PHPUnit/PHPT 回归通过。
当前针对代码生成的核心断言位于 `NewObjectCodegenTest`,运行语义由 `default-initialization-paths.phpt`、`default-expressions-inheritance.phpt` 和 Property Hook 测试组覆盖。

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save