Compare commits

..

209 Commits

Author SHA1 Message Date
韩天峰 e44007846b perf: use native property handlers for ordinary classes 50 minutes ago
韩天峰 91f5daf457 test: align import conflict diagnostic 50 minutes ago
韩天峰 9b02dcd693 feat(compiler): implement independent import alias symbol domains 1 hour ago
韩天峰 c8148018f8 docs(features): update incompatible PHP features documentation 1 hour ago
韩天峰 3189d5bd7f docs: split into en/ and zh-cn/ with full English translation 2 hours ago
韩天峰 7f0a8181f5 fix(attribute): preserve enum case arguments 2 hours ago
韩天峰 fb79fb832c fix(parser): preserve runtime class in static calls 2 hours ago
韩天峰 019c626c37
Merge pull request #9 from FunkyOz/docs-english-translations --skip-tests 2 hours ago
Lorenzo Dessimoni a8f7c29ea0 Translate incompatible PHP features file to English 16 hours ago
韩天峰 a70a0ad078 refactor(parser): remove redundant null checks in binary operation and closure processing 18 hours ago
韩天峰 06023674fe remove(bin): delete PHP function and constant generation scripts 18 hours ago
韩天峰 d1d362d779 fix(static-analysis): resolve high-value type issues 18 hours ago
韩天峰 eccf5a0ef1 fix(compiler): enforce AST type boundaries 19 hours ago
韩天峰 1d1a50c3d3 chore: add Composer copyright metadata 20 hours ago
韩天峰 d19809af9b Reclassify phpt test files 20 hours ago
韩天峰 1488563a27 fix(platform): honor php-config library directory 20 hours ago
韩天峰 c9d9f58de0 Merge branch 'bug-fix-gh-5' 20 hours ago
韩天峰 8918fae3ba fix(parser): stabilize compound array access paths 21 hours ago
hafung ec251655ba Fix repeated array offset evaluation in compound assignments 21 hours ago
韩天峰 0f335a8135 fix(optimizer): fold is_null for native scalars 21 hours ago
韩天峰 d321666721 fix(parser): parse do-while body before condition 21 hours ago
韩天峰 98321138ce test(gen_stub): cover namespaced composite property types 22 hours ago
韩天峰 f2b0e9add9 chore(version): bump version from 0.1.0 to 0.6.6 22 hours ago
韩天峰 4a9b918e7c
Merge pull request #6 from yuan-dian/fix/union-type-arginfo-escape 22 hours ago
韩天峰 1b92ae6e7b fix(namespace): isolate explicit import aliases 22 hours ago
原点 6e593b3d46 fix(gen_stub): use toVarEscapedName for union type ZEND_TYPE_INIT_CLASS 23 hours ago
韩天峰 af2b03c111 fix(cli): support Composer entrypoint and isolate stub symbols 1 day ago
韩天峰 8ddd04e1b1 ci: restructure platform build and test checks 2 days ago
韩天峰 ee1d6e7b54 ci: add ZTS ARM64 and macOS release builds 2 days ago
韩天峰 fef462af33 build: exclude redundant archives from Windows package 2 days ago
韩天峰 875f123403 ci: enable zip for Windows release packaging 2 days ago
韩天峰 ed27572ca1 chore(version): bump version from 0.6.3 to 0.6.5 2 days ago
韩天峰 3d2933ec7f ci: package tagged compiler releases 2 days ago
韩天峰 87c328b3db ci: add Windows PHP ABI build matrix 2 days ago
韩天峰 063476360b fix(build): correct Windows mpdecimal DLL copying in CMake build 2 days ago
韩天峰 022ca55ff0 docs(readme): update documentation with comprehensive improvements 2 days ago
韩天峰 ced2ebb1ba docs(readme): update documentation with self-hosting details and benchmark data --skip-tests 2 days ago
韩天峰 7973f99f47 test: split PHP 8.5 numeric warnings 2 days ago
韩天峰 74274f8328 fix(compiler): defer cache IDs to convert phase 3 days ago
韩天峰 6693d95cd6 ci: restore full PHP test matrix 3 days ago
韩天峰 8f32497de7 refactor(compiler): strongly type symbol cache IDs 3 days ago
韩天峰 1b235d0769 test(compiler): reject mixed cache lifetime IDs 3 days ago
韩天峰 8c6ea1d14e fix(build): make source discovery deterministic 3 days ago
韩天峰 0ac6dfe499 fix(compiler): keep method cache lifetime aligned with class 3 days ago
韩天峰 a6cc11c6d7 ci: narrow class cache debugging workflow 3 days ago
韩天峰 2d14c9e432 ``` 3 days ago
韩天峰 7f587749f1 chore(ci): replace custom PHP setup action with community action 3 days ago
韩天峰 821f639e8f fix(ci): remove compiler flag from PHPT test runner 3 days ago
韩天峰 983333dc2e refactor(php): conditionally install and enable OPcache based on availability 3 days ago
韩天峰 978e553480 chore(ci): migrate PHP setup to custom action with Ubuntu 24.04 3 days ago
韩天峰 e4817819dc fix(ci): remove embed extension from PHP setup and add explicit embed library linking 3 days ago
韩天峰 8a30c5e58d rm composer.lock 3 days ago
韩天峰 0061d1595a refactor(examples): replace file reading with hardcoded rounds value 3 days ago
韩天峰 2c74be470e docs(readme): rewrite documentation with comprehensive TypePHP guide 3 days ago
韩天峰 2986f3db70 test(compiler): update test configuration and execution setup 3 days ago
韩天峰 e4bb7b2605 refactor(build): separate project-specific compile options from common pch 3 days ago
韩天峰 305d0d09d9 config(tests): update PHP configuration with operator overloading setting 3 days ago
韩天峰 a5700d24c9 chore(tests): update PHP configuration settings in workflow 3 days ago
韩天峰 642e9a7e4b fix(generator): use interned strings for persistent class metadata 3 days ago
韩天峰 422bdc4ce6 docs: remove documentation and plans for code reuse and encapsulation 3 days ago
韩天峰 208541d00b chore(build): add PHP header patch action and update dependencies 3 days ago
韩天峰 19a570ff3f feat(testing): add test coverage analyzer tool 3 days ago
韩天峰 178636a577 feat(config): add ext-deps alias for extension-dependencies configuration 3 days ago
韩天峰 1118c1e8ec feat(compiler): add PHP extension dependencies support 3 days ago
韩天峰 02fc0fb319 test(parser): add comprehensive tests for toArray method validation 3 days ago
韩天峰 3def25cc3c feat(compiler): add support for PHP 8.5 constant expression casts 3 days ago
韩天峰 44095252d2 feat(parser): add support for final promoted properties and exit named arguments 4 days ago
韩天峰 9f1cf07511 feat(parser): add support for promoted asymmetric visibility properties 4 days ago
韩天峰 36fd0228f0 feat(property): add property override attribute support and validation 4 days ago
韩天峰 4c59d9a21b feat(compiler): add DNF type support for method signatures and variance 4 days ago
韩天峰 b2246cd456 test(clone-with): add comprehensive tests for PHP 8.5 clone-with functionality 4 days ago
韩天峰 795a8e0a1d feat(compiler): add PHP 8.5 clone-with support and scope handling 4 days ago
韩天峰 9fc5755544 feat(parser): add support for PHP 8.5 void cast syntax 4 days ago
韩天峰 6dcafb652b test(compiler): add comprehensive negative compatibility tests and improve error handling 4 days ago
韩天峰 23a2bf44d7 test(compiler): add comprehensive test coverage for PHP language features 4 days ago
韩天峰 6d45c64b4d feat(parser): add validation for PHP 8.5 closure usage in constants and defaults 4 days ago
韩天峰 c5f478766e feat(compiler): add support for PHP 8.5 magic constants and namespace relative names 4 days ago
韩天峰 f64a1d5cb7 refactor(parser): remove unused methods and update call argument generation 4 days ago
韩天峰 00420215b3 feat(compiler): add native class support with export annotations 4 days ago
韩天峰 2471ed1db7 feat(compiler): add native class export validation for library stubs 4 days ago
韩天峰 0a97daa663 fix(compiler): simplify root slot registration in CompilerBase 6 days ago
韩天峰 4e00799c4a test(entry-script): add test for main line padding optimization 6 days ago
韩天峰 41e11a66e6 refactor(build): update runtime initialization and linking for native modules 6 days ago
韩天峰 0f6cc17450 chore(deps): update swoole/phpx dependency and project version 6 days ago
韩天峰 101b9f6261 fix(parser): handle array value write dereferences correctly 6 days ago
韩天峰 65a44c7f65 docs: add C++ naming convention documentation and update related references 7 days ago
韩天峰 e722501b3d refactor(generator): replace php prefixed helper functions with php namespace 7 days ago
韩天峰 d80f4a0d20 perf(parser): optimize array dimension fetch and selection operations 7 days ago
韩天峰 4713a58b06 feat(compiler): add local class entry hoisting control for parameter defaults 7 days ago
韩天峰 cb1e12f11f perf(compiler): optimize array statement writes and class constant lookups 7 days ago
韩天峰 7418b6247e feat(parser): optimize string concatenation assignment operations 7 days ago
韩天峰 65d3710a61 fix(parser): handle PHP array overflow in compound assignments and improve method name parsing 1 week ago
韩天峰 70d13a3601 修复 C++ 代码的缩进问题 1 week ago
韩天峰 4b8d0eb685 优化生成的 C++ 代码体积,减少无效行 1 week 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 支持 迭代器 2 weeks ago
韩天峰 464de23051 Native Class 支持 ArrayAccess 2 weeks ago
韩天峰 2ffd4ded19 Native Class 支持 [完结] 2 weeks ago
韩天峰 2e6ddf98f1 Native Class 支持 [第九阶段] 2 weeks ago
韩天峰 c9666664aa Native Class 支持 [第八阶段] 2 weeks ago
韩天峰 587818d5f9 Native Class 支持 [第七阶段] 2 weeks ago
韩天峰 7d1a90a340 Native Class 支持 [第六阶段] 2 weeks ago
韩天峰 13344ace72 Native Class 支持 [第五阶段] 2 weeks ago
韩天峰 7cf265cbc7 Native Class 支持 [第四阶段] 2 weeks ago
韩天峰 de8fdaf464 Native Class 支持 [第三阶段] 2 weeks ago
韩天峰 d5f0a113e6 Native Class 支持 [第二阶段] 2 weeks 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 3 weeks ago
韩天峰 997bbffb65 重构,拆分内置类、函数、属性表,这些是 MINIT 阶段就确定的,无需 thread_local,可预加载,request shutdown 阶段无需释放 3 weeks ago
韩天峰 8118fbd3dc feat(readonly): enforce direct assignment only for readonly properties 3 weeks ago
韩天峰 e5f61eabfe 重构 readonly:__construct() 和 __clone() 都属于初始化窗口 ,其他地方不允许写。与 PHP 不一致。 3 weeks ago
韩天峰 a3ea91918d feat(parser): add readonly property unset protection 3 weeks ago
韩天峰 3b51eaa6d0 test(json): add test case for JSON exception boundary behavior 3 weeks ago
韩天峰 0ba5079336 fix(parser): handle temporary variable cleanup for weak reference expressions 3 weeks ago
韩天峰 7d7fa75d6f feat(compiler): add dynamic scope tracking for runtime method calls 3 weeks ago
韩天峰 64afbc9656 feat(python): generate builtin helpers and convert native calls 3 weeks ago
韩天峰 6c97b4f301 feat(python): add Python IDE helper generation and update object conversion methods 3 weeks ago
韩天峰 f61a542255 refactor(python): update module access syntax from static to namespace notation 3 weeks ago
NathanFreeman 3b6a48ef74 修复编译后的程序通过 Zend VM 调用自身的函数,如果此时临时变量过多,会导致段错误的问题 (#47) 3 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 4 weeks ago
韩天峰 560ea46692 add phpt tests 4 weeks ago
yangweijie a92fc0dfd7 add onepiece-ddz game example 4 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. 755
      README.md
  78. 52
      app.manifest
  79. 495
      ast.py
  80. 17
      benchmark/README.md
  81. 60
      benchmark/bench.php
  82. 2
      benchmark/micro_bench.php
  83. 3
      benchmark/property-access/.gitignore
  84. 15
      benchmark/property-access/README.md
  85. 154
      benchmark/property-access/benchmark.php
  86. 7
      benchmark/property-access/project.yml
  87. 101
      benchmark/property-access/run.php
  88. 146
      bin/analyze-test-coverage.php
  89. 19
      bin/bootstrap.php
  90. 5
      bin/dump-ast.php
  91. 4
      bin/extractor.php
  92. 42
      bin/gen_rdata.php
  93. 40
      bin/ignore_constants.php
  94. 6
      bin/tpc.php
  95. 178
      cleanup-typephp-tmp.sh
  96. 151
      completions/tpc.bash
  97. 21
      composer.json
  98. 90
      composer.lock
  99. 224
      docs/COMPILATION_PERFORMANCE_ANALYSIS.md
  100. 81
      docs/INCOMPATIBLE_PHP_FEATURES.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/zh-cn/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/zh-cn/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/zh-cn/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/zh-cn/COMPILER_CLI.md)。
## 兼容性模型
TypePHP 会在适合 AOT 编译的范围内保持 PHP 语法和运行行为,同时有一些明确限制:
- 全局作用域只允许声明,可执行语句必须位于函数或方法内;
- 二进制模式对 `main()` 使用严格签名;
- `use native_types` 会让标量声明使用固定原生存储,之后不能改为不兼容类型;
- 静态可确定的调用和属性会直接编译,受支持的动态操作则通过 PHPX/Zend runtime
fallback 执行;
- `.stub.php` 用于声明 C++ 或外部库 API,函数体必须为空,stub 文件禁止声明
`#[Native]` 类;
- 部分高度动态的引用、声明、闭包和反射模式仍明确不支持。
兼容性边界属于公共契约,同时有正向和负向测试保护。请以
[不兼容 PHP 特性清单](docs/zh-cn/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/zh-cn/HIGH_PRECISION_TYPES.md)和[原生类型](docs/zh-cn/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/zh-cn/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/zh-cn/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/zh-cn/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 语言核心性能——函数调用、对象属性访问、数组/哈希访问、
字符串处理、控制流等。测试代码见 [`benchmark/bench.php`](benchmark/bench.php) 和
[`benchmark/micro_bench.php`](benchmark/micro_bench.php)。其他专项性能回归测试
统一放置在 [`benchmark/`](benchmark/) 目录中。
这些数字是项目测量快照,不是性能保证。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/zh-cn/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/zh-cn/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/zh-cn/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/zh-cn/TEST_COVERAGE_ANALYZER.md)。
GitHub Actions 会在 PHP 8.4 和 8.5 上分别运行 PHPUnit 与自举 PHPT。修改编译器
内部规则或代码生成时应增加聚焦的 PHPUnit;运行输出或诊断可观察时还应增加 PHPT。
## 文档
- [快速入门](docs/zh-cn/QUICKSTART.md) —— 最小编译流程
- [编译模式](docs/zh-cn/COMPILATION_MODES.md) —— `bin`、`ext`、`lib`
- [编译器命令行](docs/zh-cn/COMPILER_CLI.md) —— CLI 参数与项目配置
- [不兼容 PHP 特性清单](docs/zh-cn/INCOMPATIBLE_PHP_FEATURES.md) —— 当前限制
- [原生类型](docs/zh-cn/NATIVE_TYPES.md) —— 原生标量类型
- [高精度类型](docs/zh-cn/HIGH_PRECISION_TYPES.md) —— BigInt / Decimal / BigFloat
- [Std 容器](docs/zh-cn/STD_CONTAINERS.md) —— 强类型容器
- [通用方法](docs/zh-cn/UNIVERSAL_METHODS.md) —— 编译期方法解析
- [编译期函数](docs/zh-cn/COMPILE_TIME_FUNCTIONS.md) —— `any()`、`refval()`、`objval()` 等
- [混合 C++/PHP](docs/zh-cn/MIXED_CPP_PHP.md) —— C++/PHP 互操作
- [`#[Immutable]`](docs/zh-cn/IMMUTABLE.md) —— 编译期只读契约
- [`#[ArrayDef]`](docs/zh-cn/ARRAY_DEF.md) —— 强类型数组属性契约
- [Property hooks](docs/zh-cn/PROPERTY_HOOKS.md) —— PHP 8.4 hook 降级和运行时元数据
- [对象存储模型](docs/zh-cn/OBJECT_STORAGE_AND_PASSING_MODELS.md) —— Zend object、Box 与 Native class 边界
- [Generator](docs/zh-cn/YIELD_GENERATOR.md) —— 生成器降级与生命周期
- [测试覆盖分析工具](docs/zh-cn/TEST_COVERAGE_ANALYZER.md) —— AST 与特性证据矩阵
- [WASI 构建](docs/zh-cn/WASI_BUILD.md) —— WASI 目标
## 授权协议
TypePHP 采用 [GNU General Public License v3.0](LICENSE) 授权。
## 社区
- 代码仓库:<https://github.com/swoole/typephp>
- 版权所有 © 2026 上海识沃网络科技有限公司(Swoole)

@ -1,84 +1,751 @@
# 依赖 [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/en/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/en/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/en/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/en/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/en/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/en/HIGH_PRECISION_TYPES.md) and
[Native types](docs/en/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/en/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/en/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/en/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 [`benchmark/bench.php`](benchmark/bench.php) and
[`benchmark/micro_bench.php`](benchmark/micro_bench.php). Additional focused
performance regressions live in the same [`benchmark/`](benchmark/) directory.
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/en/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/en/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/en/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/en/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/en/QUICKSTART.md) — minimal compilation flow
- [Compilation modes](docs/en/COMPILATION_MODES.md) — `bin`, `ext`, `lib`
- [Compiler CLI](docs/en/COMPILER_CLI.md) — CLI arguments and project config
- [Incompatible PHP features](docs/en/INCOMPATIBLE_PHP_FEATURES.md) — current limits
- [Native types](docs/en/NATIVE_TYPES.md) — native scalar types
- [High-precision types](docs/en/HIGH_PRECISION_TYPES.md) — BigInt / Decimal / BigFloat
- [Std containers](docs/en/STD_CONTAINERS.md) — strongly-typed containers
- [Universal methods](docs/en/UNIVERSAL_METHODS.md) — compile-time method resolution
- [Compile-time functions](docs/en/COMPILE_TIME_FUNCTIONS.md) — `any()`, `refval()`, `objval()`, …
- [Mixed C++/PHP](docs/en/MIXED_CPP_PHP.md) — C++/PHP interop
- [`#[Immutable]`](docs/en/IMMUTABLE.md) — compile-time read-only contracts
- [`#[ArrayDef]`](docs/en/ARRAY_DEF.md) — typed array-property contracts
- [Property hooks](docs/en/PROPERTY_HOOKS.md) — PHP 8.4 hook lowering and runtime metadata
- [Object storage models](docs/en/OBJECT_STORAGE_AND_PASSING_MODELS.md) — Zend object, Box, and Native class boundaries
- [Generators](docs/en/YIELD_GENERATOR.md) — generator lowering and lifecycle
- [Test coverage analyzer](docs/en/TEST_COVERAGE_ANALYZER.md) — AST and feature evidence matrix
- [WASI build](docs/en/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,17 @@
# TypePHP benchmarks
This directory contains repeatable performance workloads used to guide and
verify compiler/runtime optimizations. Benchmark results depend on the CPU,
PHP build, compiler, and system load, so compare PHP and TypePHP on the same
machine instead of committing absolute timing expectations.
- `bench.php` and `micro_bench.php` are the original general workloads moved
from `examples/`.
- `property-access/` builds and compares dynamic/static property access under
Zend PHP and TypePHP.
Run the property benchmark from the repository root:
```bash
php benchmark/property-access/run.php
```

@ -1,7 +1,7 @@
<?php <?php
use native_types; use native_types;
function simple() function simple(): void
{ {
$a = 0; $a = 0;
$total_count = 10000000; $total_count = 10000000;
@ -19,7 +19,7 @@ function simple()
/****/ /****/
function simplecall() function simplecall(): void
{ {
$total = 0; $total = 0;
for ($i = 0; $i < 1000000; $i++) for ($i = 0; $i < 1000000; $i++)
@ -29,28 +29,28 @@ function simplecall()
/****/ /****/
function hallo($a) { function hallo(string $a): void {
} }
function simpleucall() { function simpleucall(): void {
for ($i = 0; $i < 1000000; $i++) for ($i = 0; $i < 1000000; $i++)
hallo("hallo"); hallo("hallo");
} }
/****/ /****/
function hallo2($a) { function hallo2(string $a): void {
} }
function simpleudcall() { function simpleudcall(): void {
for ($i = 0; $i < 1000000; $i++) for ($i = 0; $i < 1000000; $i++)
hallo2("hallo"); hallo2("hallo");
} }
/****/ /****/
function mandel() { function mandel(): void {
$w1=50; $w1=50;
$h1=150; $h1=150;
$recen=-.45; $recen=-.45;
@ -90,7 +90,7 @@ function mandel() {
/****/ /****/
function mandel2() { function mandel2(): void {
$b = " .:,;!/>)|&IH%*#"; $b = " .:,;!/>)|&IH%*#";
//float r, i, z, Z, t, c, C; //float r, i, z, Z, t, c, C;
for ($y=30; printf("\n"), $C = $y*0.1 - 1.5, $y--;){ for ($y=30; printf("\n"), $C = $y*0.1 - 1.5, $y--;){
@ -104,20 +104,20 @@ function mandel2() {
/****/ /****/
function Ack(int $m, int $n){ function Ack(int $m, int $n): int {
if($m == 0) return $n+1; if($m == 0) return $n+1;
if($n == 0) return Ack($m-1, 1); if($n == 0) return Ack($m-1, 1);
return Ack($m - 1, Ack($m, ($n - 1))); return Ack($m - 1, Ack($m, ($n - 1)));
} }
function ackermann(int $n) { function ackermann(int $n): void {
$r = Ack(3, $n); $r = Ack(3, $n);
print "Ack(3,$n): $r\n"; print "Ack(3,$n): $r\n";
} }
/****/ /****/
function ary($n) { function ary(int $n): void {
for ($i=0; $i<$n; $i++) { for ($i=0; $i<$n; $i++) {
$X[$i] = $i; $X[$i] = $i;
} }
@ -130,7 +130,7 @@ function ary($n) {
/****/ /****/
function ary2($n) { function ary2(int $n): void {
for ($i=0; $i<$n;) { for ($i=0; $i<$n;) {
$X[$i] = $i; ++$i; $X[$i] = $i; ++$i;
$X[$i] = $i; ++$i; $X[$i] = $i; ++$i;
@ -163,7 +163,7 @@ function ary2($n) {
/****/ /****/
function ary3(int $n) { function ary3(int $n): void {
for ($i=0; $i<$n; $i++) { for ($i=0; $i<$n; $i++) {
$X[$i] = $i + 1; $X[$i] = $i + 1;
$Y[$i] = 0; $Y[$i] = 0;
@ -179,18 +179,18 @@ function ary3(int $n) {
/****/ /****/
function fibo_r(int $n){ function fibo_r(int $n): int {
return(($n < 2) ? 1 : fibo_r($n - 2) + fibo_r($n - 1)); return(($n < 2) ? 1 : fibo_r($n - 2) + fibo_r($n - 1));
} }
function fibo(int $n) { function fibo(int $n): void {
$r = fibo_r($n); $r = fibo_r($n);
print "$r\n"; print "$r\n";
} }
/****/ /****/
function hash1(int $n) { function hash1(int $n): void {
for ($i = 1; $i <= $n; $i++) { for ($i = 1; $i <= $n; $i++) {
$X[dechex($i)] = $i; $X[dechex($i)] = $i;
} }
@ -203,7 +203,7 @@ function hash1(int $n) {
/****/ /****/
function hash2(int $n) { function hash2(int $n): void {
for ($i = 0; $i < $n; $i++) { for ($i = 0; $i < $n; $i++) {
$hash1["foo_$i"] = $i; $hash1["foo_$i"] = $i;
$hash2["foo_$i"] = 0; $hash2["foo_$i"] = 0;
@ -218,12 +218,12 @@ function hash2(int $n) {
/****/ /****/
function gen_random (int $n) { function gen_random(int $n): float {
global $LAST; global $LAST;
return( ($n * ($LAST = ($LAST * IA + IC) % IM)) / IM ); return( ($n * ($LAST = ($LAST * IA + IC) % IM)) / IM );
} }
function heapsort_r(int $n, &$ra) { function heapsort_r(int $n, array &$ra): void {
$l = ($n >> 1) + 1; $l = ($n >> 1) + 1;
$ir = $n; $ir = $n;
@ -255,7 +255,7 @@ function heapsort_r(int $n, &$ra) {
} }
} }
function heapsort(int $N) { function heapsort(int $N): void {
global $LAST; global $LAST;
define("IM", 139968); define("IM", 139968);
@ -272,7 +272,7 @@ function heapsort(int $N) {
/****/ /****/
function mkmatrix ($rows, $cols) { function mkmatrix(int $rows, int $cols): array {
$count = 1; $count = 1;
$mx = array(); $mx = array();
for ($i=0; $i<$rows; $i++) { for ($i=0; $i<$rows; $i++) {
@ -283,7 +283,7 @@ function mkmatrix ($rows, $cols) {
return ($mx); return ($mx);
} }
function mmult ($rows, $cols, $m1, $m2) { function mmult(int $rows, int $cols, array $m1, array $m2): array {
$m3 = array(); $m3 = array();
for ($i=0; $i<$rows; $i++) { for ($i=0; $i<$rows; $i++) {
for ($j=0; $j<$cols; $j++) { for ($j=0; $j<$cols; $j++) {
@ -297,7 +297,7 @@ function mmult ($rows, $cols, $m1, $m2) {
return($m3); return($m3);
} }
function matrix(int $n) { function matrix(int $n): void {
$SIZE = 30; $SIZE = 30;
$m1 = mkmatrix($SIZE, $SIZE); $m1 = mkmatrix($SIZE, $SIZE);
$m2 = mkmatrix($SIZE, $SIZE); $m2 = mkmatrix($SIZE, $SIZE);
@ -309,7 +309,7 @@ function matrix(int $n) {
/****/ /****/
function nestedloop($n) { function nestedloop(int $n): void {
$x = 0; $x = 0;
for ($a=0; $a<$n; $a++) for ($a=0; $a<$n; $a++)
for ($b=0; $b<$n; $b++) for ($b=0; $b<$n; $b++)
@ -323,7 +323,7 @@ function nestedloop($n) {
/****/ /****/
function sieve(int $n) { function sieve(int $n): void {
$count = 0; $count = 0;
while ($n-- > 0) { while ($n-- > 0) {
$count = 0; $count = 0;
@ -342,7 +342,7 @@ function sieve(int $n) {
/****/ /****/
function strcat($n) { function strcat(int $n): void {
$str = ""; $str = "";
while ($n-- > 0) { while ($n-- > 0) {
$str .= "hello\n"; $str .= "hello\n";
@ -359,13 +359,13 @@ function gethrtime(): float
return (($hrtime[0] * 1000000000.0 + $hrtime[1]) / 1000000000.0); return (($hrtime[0] * 1000000000.0 + $hrtime[1]) / 1000000000.0);
} }
function start_test() function start_test(): float
{ {
ob_start(); ob_start();
return gethrtime(); return gethrtime();
} }
function end_test($start, $name) function end_test(float $start, string $name): float
{ {
global $total; global $total;
$end = gethrtime(); $end = gethrtime();
@ -379,7 +379,7 @@ function end_test($start, $name)
return gethrtime(); return gethrtime();
} }
function total() function total(): void
{ {
global $total; global $total;
$pad = str_repeat("-", 24); $pad = str_repeat("-", 24);
@ -389,7 +389,7 @@ function total()
echo "Total" . $pad . $num . "\n"; echo "Total" . $pad . $num . "\n";
} }
function main() function main(): void
{ {
if (function_exists("date_default_timezone_set")) { if (function_exists("date_default_timezone_set")) {
date_default_timezone_set("UTC"); date_default_timezone_set("UTC");

@ -348,7 +348,7 @@ function main()
$x->call(N); $x->call(N);
$t = end_test($t, '$this->f()', $overhead); $t = end_test($t, '$this->f()', $overhead);
$x->read_const(N); $x->read_const(N);
$t = end_test($t, '$x = Foo::TEST', $overhead); $t = end_test($t, '$x = $this::TEST', $overhead);
create_object(N); create_object(N);
$t = end_test($t, 'new Foo()', $overhead); $t = end_test($t, 'new Foo()', $overhead);
read_const(N); read_const(N);

@ -0,0 +1,3 @@
/build/
/property_access
/*.rsp

@ -0,0 +1,15 @@
# Dynamic property benchmark
This benchmark compares the same dynamic and static property operations under
Zend PHP and a TypePHP `-O2` binary. Each metric is the best of seven rounds
after three warm-up rounds and is reported in nanoseconds per property access.
Run it from the repository root:
```bash
php benchmark/property-access/run.php
```
To reuse an existing binary, add `--skip-build`. For local regression checks,
`--max-ratio=1.5` exits unsuccessfully when a dynamic read or write takes more
than 1.5 times the corresponding Zend PHP result.

@ -0,0 +1,154 @@
<?php
declare(strict_types=1);
final class DynamicPropertyEntity
{
public int $first = 0;
public int $second = 0;
public int $third = 0;
public int $fourth = 0;
public int $fifth = 0;
public function hydrate(array $data): void
{
foreach ($data as $property => $value) {
$this->$property = $value;
}
}
public function sum(array $properties): int
{
$sum = 0;
foreach ($properties as $property) {
$sum += $this->$property;
}
return $sum;
}
}
final class StaticPropertyEntity
{
public int $first = 0;
public int $second = 0;
public int $third = 0;
public int $fourth = 0;
public int $fifth = 0;
public function hydrate(array $data): void
{
$this->first = $data['first'];
$this->second = $data['second'];
$this->third = $data['third'];
$this->fourth = $data['fourth'];
$this->fifth = $data['fifth'];
}
public function sum(): int
{
return $this->first + $this->second + $this->third + $this->fourth + $this->fifth;
}
}
function runDynamicWrite(DynamicPropertyEntity $entity, array $data, int $iterations): int
{
for ($i = 0; $i < $iterations; $i++) {
$entity->hydrate($data);
}
return $entity->first;
}
function runStaticWrite(StaticPropertyEntity $entity, array $data, int $iterations): int
{
for ($i = 0; $i < $iterations; $i++) {
$entity->hydrate($data);
}
return $entity->first;
}
function runDynamicRead(DynamicPropertyEntity $entity, array $properties, int $iterations): int
{
$sum = 0;
for ($i = 0; $i < $iterations; $i++) {
$sum += $entity->sum($properties);
}
return $sum;
}
function runStaticRead(StaticPropertyEntity $entity, int $iterations): int
{
$sum = 0;
for ($i = 0; $i < $iterations; $i++) {
$sum += $entity->sum();
}
return $sum;
}
function measure(callable $callback, int $operations): float
{
global $benchmarkSink;
for ($warmup = 0; $warmup < 3; $warmup++) {
$benchmarkSink += $callback();
}
$best = 1.0e30;
for ($round = 0; $round < 7; $round++) {
$start = hrtime(true);
$result = $callback();
$elapsed = hrtime(true) - $start;
$benchmarkSink += $result;
if ($elapsed < $best) {
$best = $elapsed;
}
}
return $best / $operations;
}
function main(): void
{
global $benchmarkSink;
$benchmarkSink = 0;
$iterations = 200000;
$data = [
'first' => 1,
'second' => 2,
'third' => 3,
'fourth' => 4,
'fifth' => 5,
];
$properties = ['first', 'second', 'third', 'fourth', 'fifth'];
$dynamic = new DynamicPropertyEntity();
$static = new StaticPropertyEntity();
$operations = $iterations * 5;
$dynamicWrite = measure(
function () use ($dynamic, $data, $iterations): int {
return runDynamicWrite($dynamic, $data, $iterations);
},
$operations,
);
$staticWrite = measure(
function () use ($static, $data, $iterations): int {
return runStaticWrite($static, $data, $iterations);
},
$operations,
);
$dynamicRead = measure(
function () use ($dynamic, $properties, $iterations): int {
return runDynamicRead($dynamic, $properties, $iterations);
},
$operations,
);
$staticRead = measure(
function () use ($static, $iterations): int {
return runStaticRead($static, $iterations);
},
$operations,
);
printf("dynamic_write_ns=%.3f\n", $dynamicWrite);
printf("static_write_ns=%.3f\n", $staticWrite);
printf("dynamic_read_ns=%.3f\n", $dynamicRead);
printf("static_read_ns=%.3f\n", $staticRead);
echo 'checksum=', $benchmarkSink + $dynamic->sum($properties) + $static->sum(), "\n";
}

@ -0,0 +1,7 @@
name: property_access_benchmark
mode: bin
optimize: 2
build-dir: build
output: property_access
sources:
- benchmark.php

@ -0,0 +1,101 @@
<?php
declare(strict_types=1);
$root = dirname(__DIR__, 2);
$source = __DIR__ . '/benchmark.php';
$project = __DIR__ . '/project.yml';
$binary = __DIR__ . '/property_access';
$skipBuild = in_array('--skip-build', $argv, true);
$maximumRatio = null;
foreach ($argv as $argument) {
if (str_starts_with($argument, '--max-ratio=')) {
$maximumRatio = (float) substr($argument, strlen('--max-ratio='));
}
}
/** @param list<string> $command */
function runCommand(array $command, string $cwd, bool $capture): string
{
$stdout = $capture ? ['pipe', 'w'] : STDOUT;
$stderr = $capture ? ['pipe', 'w'] : STDERR;
$process = proc_open($command, [STDIN, $stdout, $stderr], $pipes, $cwd, null, ['bypass_shell' => true]);
if (!is_resource($process)) {
throw new RuntimeException('Failed to start: ' . implode(' ', $command));
}
$output = '';
$error = '';
if ($capture) {
$output = stream_get_contents($pipes[1]);
$error = stream_get_contents($pipes[2]);
fclose($pipes[1]);
fclose($pipes[2]);
}
$status = proc_close($process);
if ($status !== 0) {
throw new RuntimeException(
'Command failed (' . $status . '): ' . implode(' ', $command) . "\n" . $output . $error,
);
}
return $output;
}
/** @return array<string, float> */
function parseResults(string $output): array
{
$results = [];
foreach (explode("\n", trim($output)) as $line) {
if (!str_contains($line, '=')) {
continue;
}
[$name, $value] = explode('=', $line, 2);
if ($name !== 'checksum') {
$results[$name] = (float) $value;
}
}
return $results;
}
if (!$skipBuild) {
runCommand([
PHP_BINARY,
$root . '/bin/tpc.php',
$project,
'-j',
'8',
'--no-color',
'--no-progress',
], $root, false);
}
if (!is_file($binary)) {
throw new RuntimeException('Benchmark binary does not exist: ' . $binary);
}
$php = parseResults(runCommand([
PHP_BINARY,
'-d',
'opcache.enable_cli=0',
'-r',
'require ' . var_export($source, true) . '; main();',
], $root, true));
$typephp = parseResults(runCommand([$binary], $root, true));
echo "Metric PHP ns/op TypePHP ns/op TypePHP/PHP\n";
echo "------------------------------------------------------------\n";
$failed = false;
foreach (['dynamic_write_ns', 'dynamic_read_ns', 'static_write_ns', 'static_read_ns'] as $metric) {
if (!isset($php[$metric], $typephp[$metric])) {
throw new RuntimeException('Missing benchmark metric: ' . $metric);
}
$ratio = $typephp[$metric] / $php[$metric];
printf("%-22s %10.2f %14.2f %12.2fx\n", $metric, $php[$metric], $typephp[$metric], $ratio);
if ($maximumRatio !== null && str_starts_with($metric, 'dynamic_') && $ratio > $maximumRatio) {
$failed = true;
}
}
if ($failed) {
fwrite(STDERR, "Dynamic property ratio exceeded --max-ratio={$maximumRatio}\n");
exit(1);
}

@ -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';
@ -189,11 +189,12 @@ function printNodeArray(array $arr, string $indent, int|string $key): void
function printNodeAttrs(array $arr, string $indent): void function printNodeAttrs(array $arr, string $indent): void
{ {
$type = $arr['type'];
$hasAttrs = !empty($arr['attributes']); $hasAttrs = !empty($arr['attributes']);
$attrs = $arr['attributes'] ?? []; $attrs = $arr['attributes'] ?? [];
unset($arr['type'], $arr['attributes']); unset($arr['type'], $arr['attributes']);
echo $indent . "type: " . $arr['type'] . "\n"; echo $indent . "type: " . $type . "\n";
echo $indent . "lines: " . $attrs['startLine'] . '-' . $attrs['endLine'] . "\n"; echo $indent . "lines: " . $attrs['startLine'] . '-' . $attrs['endLine'] . "\n";
if (isset($attrs['comments'])) { if (isset($attrs['comments'])) {

@ -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,42 +0,0 @@
<?php
$dir = '/home/htf/workspace/c/php-8.0.12/ext/standard/';
$outDir = __DIR__ . '/../config/';
$funcs = [];
$files = glob($dir . '*.c');
$index = 0;
foreach ($files as $file) {
$content = file_get_contents($file);
preg_match_all('/PHP_FUNCTION\(([a-z0-9_]+)\)/i', $content, $match);
if (empty($match[1])) {
continue;
}
foreach ($match[1] as $fn) {
$funcs[] = $fn;
}
}
shuffle($funcs);
$_funcs = [];
foreach ($funcs as $fn) {
$_funcs['fn_' . random_int(100000, 999999) . str_pad($index++, 3, '0') . random_int(100, 999)] = $fn;
}
function dumpVar($file, $var)
{
file_put_contents($file, "<?php\nreturn " . var_export($var, 1) . ";\n");
}
dumpVar($outDir . 'functions.php', $_funcs);
$constants = get_defined_constants();
$ignore_constants = array_flip(require __DIR__ . '/ignore_constants.php');
foreach ($constants as $k => $v) {
if (isset($ignore_constants[$k])) {
unset($constants[$k]);
}
}
dumpVar($outDir . 'constants.php', $constants);

@ -1,40 +0,0 @@
<?php
return [
"PHP_SAPI",
"PHP_OS",
"PHP_OS_FAMILY",
"PHP_ZTS",
"PHP_MAXPATHLEN",
"PHP_EOL",
"PATH_SEPARATOR",
"DIRECTORY_SEPARATOR",
"PHP_BINDIR",
"DEFAULT_INCLUDE_PATH",
"PHP_LIBDIR",
"PHP_VERSION",
"PHP_VERSION_ID",
'PEAR_INSTALL_DIR',
'PEAR_EXTENSION_DIR',
'PHP_EXTENSION_DIR',
'PHP_PREFIX',
'PHP_MANDIR',
'PHP_DATADIR',
'PHP_SYSCONFDIR',
'PHP_LOCALSTATEDIR',
'PHP_CONFIG_FILE_PATH',
'PHP_CONFIG_FILE_SCAN_DIR',
'PHP_BINARY',
'LIBXML_VERSION',
'LIBXML_DOTTED_VERSION',
'LIBXML_LOADED_VERSION',
'OPENSSL_VERSION_TEXT',
'OPENSSL_VERSION_NUMBER',
'OPENSSL_DEFAULT_STREAM_CIPHERS',
'PCRE_VERSION',
'PCRE_VERSION_MAJOR',
'PCRE_VERSION_MINOR',
'PCRE_JIT_SUPPORT',
'STDIN',
'STDOUT',
'STDERR',
];

@ -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

@ -2,22 +2,33 @@
"name": "swoole/typephp", "name": "swoole/typephp",
"description": "TypePHP native AOT compiler", "description": "TypePHP native AOT compiler",
"type": "library", "type": "library",
"license": "GPL-3.0-only",
"authors": [
{
"name": "上海识沃网络科技有限公司",
"homepage": "https://www.swoole.com/",
"role": "Copyright holder"
}
],
"bin": [ "bin": [
"bin/tpc.php" "bin/tpc.php"
], ],
"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": {
@ -26,7 +37,7 @@
}, },
"scripts": { "scripts": {
"cs-fix": "php-cs-fixer fix $1", "cs-fix": "php-cs-fixer fix $1",
"analyse": "phpstan analyse --memory-limit 512M -l 5 -c phpstan.neon", "analyse": "phpstan analyse --memory-limit 1G -c phpstan.neon",
"rector": "rector process --clear-cache" "rector": "rector process --clear-cache"
}, },
"suggest": { "suggest": {

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"
} }

@ -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,81 +0,0 @@
# AOT 与 PHP 不兼容特性清单
本文档只记录当前 AOT 编译器与标准 PHP 不兼容或受限的关键特性。
## 程序结构
- 全局作用域不允许可执行语句;只允许声明、`use`、`declare`、常量定义等静态结构。
- 函数和方法内部不允许声明函数。
- 函数和方法内部不允许声明具名类。
- 二进制模式必须定义全局 `main()`
- `main()` 只允许无参数,或 `(int $argc, array $argv)`
- `main()` 必须返回 `void`
## 声明与类型
- 不支持可变变量 `$$var`
- PHP 8.4 property hooks 会降级为 AOT getter/setter;直接属性读写和动态对象读写均受支持。当前不支持对 hook 属性取引用。
- 支持 `private(set)``protected(set)` 非对称属性可见性;在 PHP 8.2~8.4 后端通过自定义属性写 handler 执行同等作用域检查。
- 不支持闭包或箭头函数按引用返回。
- `__construct()` 不允许返回值。
- 参数默认值不允许出现在必填参数之前(`PHP`允许,但会直接丢弃此默认参数)。
- 不支持引用可变参数 `&...$args`
- 联合类型、交叉类型、`nullable` 类型仍以 `mixed/any` 作为 C++ 表示,但静态阶段会利用已知表达式类型提前拒绝确定不兼容的参数、返回值和属性赋值;动态值仍保留运行时 type check。
- 局部变量类型一旦被静态推断为具体 native 类型,不支持在同一作用域内重新赋值为不兼容类型。
- attribute 参数不支持非空数组值和 `new` 表达式。
## declare
- 不支持 `declare(ticks=...)`
- `declare(encoding=...)` 只允许 `UTF-8`
- `declare(strict_types=...)` 只允许 `strict_types=1`
- 不支持其他 `declare` 指令。
## 调用与引用
- TypePHP 使用严格参数数量规则:非 variadic 函数不接受声明范围之外的额外参数;`func_get_args()` 不会隐式放宽签名。
- 已知签名的普通函数、普通方法和 native 直调支持引用参数及写回;不要把编译器内部跨 Trait 动态分派的限制误写成“TypePHP 不支持引用参数”。
- 闭包和箭头函数不支持引用参数。
- 引用赋值不支持从复杂静态属性表达式建立引用。
- 动态调用、闭包调用等编译期无法确定参数签名的调用,不能自动转换引用参数;需要显式使用 `refval()` 或等价关键词方法 `toRef()`
- `refval()` / `toRef()` 只接受变量、数组元素或对象属性。
- 带 unpack 且尾部追加 named arguments 的调用会退化为动态调用,不能使用 native call。
## 对象模型
- `toInt()`、`toString()`、`toArray()` 等保留关键词方法先于普通对象方法解析;需要参数的同名业务方法不按普通对象方法语义调用。
- 固定值类型属性未显式初始化时使用类型零值,不保留 ZendPHP 的完整 uninitialized 状态;因此 `??` 等依赖 uninitialized 状态的表达式可能不同。
- 禁止子类用同名 `private` 属性隐藏父类私有属性;`public` / `protected` 同名声明视为同一个继承 property slot,仍须满足类型、可见性和 `readonly` 兼容性要求。
- 为避免 typed property 写入路径引入额外动态检查,native typed property 在右值类型不确定或与属性类型不一致时会退化为 `setProperty()`;部分标量赋值可能遵循 Zend 弱类型转换,而不是 AOT 默认 strict 语义。
- constructor property promotion 的运行时属性可用,但 `ReflectionProperty::isPromoted()` 目前不返回标准 PHP 结果。
## 表达式与控制流
- `match` 的 arm condition 不能是 `match` 表达式。
- `foreach` by reference 的 value 只能是变量。
- `foreach` by reference 不支持 list destructuring。
- `std::vector`、`std::map`、`std::ordered_map` 在 `foreach` 期间禁止追加、插入、`unset()` 或整体替换;已有元素的非结构性更新仍可使用赋值运算符完成。
- 固定 native typed object property 不允许按 PHP 未初始化语义自由 `unset()`
- native 类型变量执行 `unset()` 不会产生标准 PHP 的变量删除语义。
## 运行时动态能力
- `ClassName::class` 只支持字符串字面量或可静态解析的类名。
- `static::class` 在需要编译期常量类名的位置不支持。
- `__CLASS__` 只允许在 `class` 定义的代码段中使用(`PHP`允许,返回空字符串)。
- `__TRAIT__` 只允许在 `trait` 定义的代码段中使用(`PHP`允许,返回空字符串)。
- 动态属性链、动态类名、动态函数名和动态回调会统一走 Zend runtime fallback,不保证 native 优化;动态调用的引用参数仍需显式使用 `refval()``toRef()`
- `Closure::bind()` 绑定静态闭包访问私有成员时,当前行为与标准 PHP 不完全一致。
- first-class callable 存入 typed nullable `Closure` 属性后,当前存在运行时稳定性限制。
- 所有源文件必须是 `UTF-8` 编码。
## 编译器自举与内部重构约束
本节描述编译器自身使用 TypePHP 编译时的约束,不是面向用户代码新增的 PHP 语义差异。
- 重构前,同一核心类内可静态解析的 `$this->method()` 会生成 native C++ 直调。引用参数会直接映射为 `php::Ref` 或 C++ 引用,写回语义正常。
- 将调用方和被调用方拆到不同 Trait 后,单独编译 Trait 本体时无法从 Trait 的 `$this` 确定最终宿主类。当前方法解析器可能将跨 Trait 调用降级为 Zend method call,例如生成 `this_.call(..., php::ArgList{value})`
- 动态 method call 的 `ArgList` 不会仅凭被调 wrapper 的 arginfo 自动把普通实参升级为引用。若被调方法声明 `&$value`,wrapper 会通过 `getCallArgByRef()` 取参;调用方传入的却是普通值,结果是 `must be passed by reference` 警告,并且被调方修改无法写回调用方。
- 因此,编译器内部跨 Trait API 禁止使用引用输出参数和“修改传入标量/数组后由调用方读取”的协议。应返回结果值、元组数组或 DTO,例如用 `[$type, $class] = resolveTypeDecl(...)` 代替 `parseTypeDecl(..., &$class)`
- 对字符串累加、数组排序、解析结果输出等内部 helper,优先设计为纯返回值:`$code .= format(...)`、`$files = sort(...)`。只有确认调用会保持 native 直调时,才允许依赖引用写回。
- 每次移动方法到 Trait、父类或独立组件后,必须使用自举产物重新编译至少一个覆盖该调用的测试;仅使用 `bin/tpc.php` 运行测试不能发现“源编译器正常、自举编译器退化”的问题。

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

Loading…
Cancel
Save