TypePHP 编译器
https://swoole.com/aot/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
25 KiB
25 KiB
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.h423KB、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 误编译)。
- 测试1:
- 用户实测测试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 避开该结构(如去掉 SPLRecursiveIteratorIterator、改用更简单 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()(含FilesystemIteratorSPL 迭代器)。这两类结构在 v1095 AOT 编译下最易被误编译成坏 size(2⁴⁷ 符号扩展)。hasMiscObjectFileCache原版也含RecursiveIteratorIterator/RecursiveDirectoryIterator。 - 已重写 S2 为保守版(src/Translator.php),消除全部高危结构:
getGeneratedObjectCacheKey:去掉hash_init/update/update_file/final流,改用hash('sha256', implode("\0", $parts))单次 + 每个依赖头hash_file('sha256', $header)单次。getGeneratedHeaderDependencies:去掉FilesystemIterator,改用scandir一层遍历;并把*_arginfo.h通配限定为当前 target 前缀(str_starts_with($name,'php_'.$targetName.'_')),顺手修了"扫描整个 build/include 含 tpc 工程残留头"的设计缺陷。hasMiscObjectFileCache:去掉RecursiveIteratorIterator,新增collectHeaderFiles()(scandir 递归,无 SPL 迭代器)替代。getMiscObjectCacheKey保持原hash('sha256', ...)单次调用(已验证安全)。- 恢复
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通过。 - 后续可选项(暂不阻塞):
- 用 v1095 重编当前源码(S1 开、S2 关)→ 应稳定,可正常编 hello.php 与 project.yml。
- 想保留 S2 加速的可试验:用更低优化级别(如
project.yml -O1而非-O2)重编 tpc.exe。若 v1095 的误编译是优化级别相关,低级别可能绕开 → 届时再恢复 compileFile 的 S2 调用测试。但 -O1 会让 tpc.exe 自身变慢,需权衡。 - 向 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/):
think\exception\ClassNotFoundException:framework vs think-container(两份字节一致,删 think-container 那份安全)。think\Exception:framework vs think-orm/stubs/Exception.php(stub,删 stub 安全)。think\Facade:think-container vs think-orm/stubs/Facade.php(stub,删 stub 安全)。think\route\Dispatch:framework 自身两份(route/Dispatch.php规范基类 vsroute/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 = <数组>;会是类型错误甚至静默误编译。正确做法是:被重新赋以不兼容类型的变量,从一开始(声明处)就声明为运行时 variantphp::Var。 - 修复(src 三处):
CompilerBase.php:新增protected array $variantVars = [];(resetFunction 里清空);新增areTypesAssignable($existing,$new): bool,与checkVarAssignExpr的兼容规则逐字一致(VAR/REF/同类型/双 native/big 类型 都兼容,其余冲突),仅返回 bool 不 fatal,供预扫描判定。Translator.php:新增computeVariantVars()+collectAssignTypes()/collectAssignTypesNode()(递归走函数体,跳过嵌套 function/closure/arrow,收集每个简单变量赋值 RHS 的 detectTypeOfExpr 类型,并以形参 argInfo->type 作种子);parseFunction()在 SSA 之后调用它,若某变量类型集合存在不兼容对 → 记入variantVars,并把这些形参的argInfo->type改为php::Var、重拼functionDef->params(用genArgumentDeclaration)。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 找到核心库——这是用户环境的常规布局操作。