From b3dc15a55f1fbc970e590fecb014baf13040a66a Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Tue, 10 Feb 2026 13:55:21 +0800 Subject: [PATCH] =?UTF-8?q?feat(closures):=20=E5=AE=9E=E7=8E=B0=E9=97=AD?= =?UTF-8?q?=E5=8C=85=E5=8A=9F=E8=83=BD=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加了闭包类型常量 php::Args - 实现了原生类型检查方法 isNativeType - 添加了引用转换方法 convertToRef 处理变量引用 - 修改闭包函数签名使用 php::Args 类型 - 实现了闭包 use 子句中引用传递的支持 - 添加了两个闭包测试用例验证基本功能和引用传递 --- src/Php/CompilerBase.php | 22 ++++++++++++++++++-- tests/zend/closures/closure_001.phpt | 30 ++++++++++++++++++++++++++++ tests/zend/closures/closure_002.phpt | 29 +++++++++++++++++++++++++++ 3 files changed, 79 insertions(+), 2 deletions(-) create mode 100644 tests/zend/closures/closure_001.phpt create mode 100644 tests/zend/closures/closure_002.phpt diff --git a/src/Php/CompilerBase.php b/src/Php/CompilerBase.php index 1a8adace..6349e17f 100644 --- a/src/Php/CompilerBase.php +++ b/src/Php/CompilerBase.php @@ -42,6 +42,7 @@ class CompilerBase extends \PhpAot\Core\Translator public const string TYPE_FLOAT = 'php::Float'; public const string TYPE_OBJECT = 'php::Object'; public const string TYPE_ARRAY = 'php::Array'; + public const string TYPE_ARGS = 'php::Args'; public const string TYPE_STR = 'php::Str'; public const string TYPE_REF = 'php::Ref'; public const string TYPE_VOID = 'void'; @@ -1164,6 +1165,11 @@ class CompilerBase extends \PhpAot\Core\Translator return $str === 'true' || $str === 'false'; } + protected function isNativeType(string $var): bool + { + return in_array($this->getVarType($var), [self::TYPE_INT, self::TYPE_FLOAT, self::TYPE_BOOL]); + } + protected function isInternalFunction(string $fname): bool { return array_key_exists($fname, $this->internalFunctions); @@ -2957,6 +2963,14 @@ class CompilerBase extends \PhpAot\Core\Translator return $tmpVar; } + protected function convertToRef(NodeAbstract $expr): string { + $this->checkLeftValue($expr); + if ($this->isVarExpr($expr) and $this->isNativeType($this->parseIdentifier($expr))) { + $this->fatalError($expr, 'Cannot convert variable of native type to reference'); + } + return $this->parseIdentifier($expr) . '.toReference()'; + } + protected function parseAssignRef(Node\Expr\AssignRef $expr): string { if ($this->isVarExpr($expr->var)) { @@ -3461,7 +3475,7 @@ class CompilerBase extends \PhpAot\Core\Translator { $tmpVar = $this->genTmpVarName(); - $fnCode = $this->getIndent() . 'php::ClosureFn ' . $tmpVar . ' = [](INTERNAL_FUNCTION_PARAMETERS, ' . self::TYPE_OBJECT . ' &this_, ' . self::TYPE_ARRAY . ' &vars_) {' . PHP_EOL; + $fnCode = $this->getIndent() . 'php::ClosureFn ' . $tmpVar . ' = [](INTERNAL_FUNCTION_PARAMETERS, ' . self::TYPE_OBJECT . ' &this_, ' . self::TYPE_ARGS . ' &vars_) {' . PHP_EOL; $oriLocalVars = $this->localVars; $this->localVars = []; $this->indentLevel++; @@ -3488,7 +3502,11 @@ class CompilerBase extends \PhpAot\Core\Translator if ($this->isVarExpr($useItem->var) and !$this->hasVar($var)) { $this->fatalError($expr, 'Variable `' . $var . '` is not defined'); } - $useVars [] = $var; + if ($useItem->byRef) { + $useVars [] = $this->convertToRef($useItem->var); + } else { + $useVars [] = $var; + } } return 'php::newClosure(' . $tmpVar . ', { ' . implode(', ', $useVars) . ' })'; diff --git a/tests/zend/closures/closure_001.phpt b/tests/zend/closures/closure_001.phpt new file mode 100644 index 00000000..1c379c8c --- /dev/null +++ b/tests/zend/closures/closure_001.phpt @@ -0,0 +1,30 @@ +--TEST-- +Closure 001: Lambda without lexical variables +--FILE-- + +--EXPECT-- +bool(true) +bool(true) +Hello World! +Hello Universe! +Hello World! +Hello Universe! +Done diff --git a/tests/zend/closures/closure_002.phpt b/tests/zend/closures/closure_002.phpt new file mode 100644 index 00000000..8cc1d681 --- /dev/null +++ b/tests/zend/closures/closure_002.phpt @@ -0,0 +1,29 @@ +--TEST-- +Closure 002: Lambda with lexical variables (global scope) +--FILE-- + +--EXPECT-- +4 +4 +4 +5 +Done