From f2e7b70043c8e0d7f1fba0eede300008dd6cea28 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Mon, 9 Feb 2026 20:11:35 +0800 Subject: [PATCH] =?UTF-8?q?feat(compiler):=20=E6=B7=BB=E5=8A=A0=E9=97=AD?= =?UTF-8?q?=E5=8C=85=E8=A1=A8=E8=BE=BE=E5=BC=8F=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在 CompilerBase.php 中添加对 Expr_Closure 节点类型的解析处理 - 实现 parseClosure 方法来编译 PHP 闭包为 C++ 代码 - 添加临时变量生成和缩进级别管理逻辑 - 处理闭包参数和 use 子句中的变量捕获 - 验证闭包中使用的变量是否已定义 - 添加闭包示例文件 closure.php 和对应测试文件 - 修复 fcall.php 中的调试输出注释问题 --- examples/closure.php | 14 ++++++++++++++ examples/fcall.php | 3 +++ src/Php/CompilerBase.php | 39 ++++++++++++++++++++++++++++++++++++++ tests/aot/closure-001.phpt | 28 +++++++++++++++++++++++++++ 4 files changed, 84 insertions(+) create mode 100644 examples/closure.php create mode 100644 tests/aot/closure-001.phpt diff --git a/examples/closure.php b/examples/closure.php new file mode 100644 index 00000000..853c4599 --- /dev/null +++ b/examples/closure.php @@ -0,0 +1,14 @@ +foo(1, 2); + var_dump($xxx); +// var_dump($arr2['hello']); +// var_dump($o2->null); } diff --git a/src/Php/CompilerBase.php b/src/Php/CompilerBase.php index 60c4beee..843b9277 100644 --- a/src/Php/CompilerBase.php +++ b/src/Php/CompilerBase.php @@ -375,6 +375,8 @@ class CompilerBase extends \PhpAot\Core\Translator return $this->parseThrow($expr); case 'Expr_ShellExec': return $this->parseShellExec($expr); + case 'Expr_Closure': + return $this->parseClosure($expr); case 'Name_FullyQualified': return $expr->name; case 'Scalar_Int': @@ -3427,4 +3429,41 @@ class CompilerBase extends \PhpAot\Core\Translator } return $code; } + + protected function parseClosure(Node\Expr\Closure $expr): string + { + $tmpVar = $this->genTmpVarName(); + + $fnCode = $this->getIndent() . 'php::ClosureFn ' . $tmpVar . ' = [](INTERNAL_FUNCTION_PARAMETERS, ' . self::TYPE_OBJECT . ' &this_, ' . self::TYPE_ARRAY . ' &vars_) {' . PHP_EOL; + $oriLocalVars = $this->localVars; + $this->localVars = []; + $this->indentLevel++; + foreach ($expr->params as $i => $param) { + $var = $this->parseIdentifier($param->var); + $fnCode .= 'auto ' . $var . ' = php::getCallArg(' . $i . ');' . PHP_EOL; + $this->addLocalVar($var, self::TYPE_VAR); + } + foreach ($expr->uses as $i => $useItem) { + $var = $this->parseIdentifier($useItem->var); + $fnCode .= 'auto ' . $var . ' = vars_.get(' . $i . ');' . PHP_EOL; + $this->addLocalVar($var, self::TYPE_VAR); + } + $fnCode .= $this->parseStmts($expr->stmts); + $this->indentLevel--; + $fnCode .= '};' . PHP_EOL; + + $this->beforeStmtLines[] = $fnCode; + $this->localVars = $oriLocalVars; + + $useVars = []; + foreach ($expr->uses as $useItem) { + $var = $this->parseIdentifier($useItem->var); + if ($this->isVarExpr($useItem->var) and !$this->hasVar($var)) { + $this->fatalError($expr, 'Variable `' . $var . '` is not defined'); + } + $useVars [] = $var; + } + + return 'php::newClosure(' . $tmpVar . ', { ' . implode(', ', $useVars) . ' })'; + } } diff --git a/tests/aot/closure-001.phpt b/tests/aot/closure-001.phpt new file mode 100644 index 00000000..3fcaba99 --- /dev/null +++ b/tests/aot/closure-001.phpt @@ -0,0 +1,28 @@ +--TEST-- +closure 001 +--FILE-- + +--EXPECT-- +int(100) +array(3) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) +} +int(1000) +