feat(closures): 实现闭包功能支持

- 添加了闭包类型常量 php::Args
- 实现了原生类型检查方法 isNativeType
- 添加了引用转换方法 convertToRef 处理变量引用
- 修改闭包函数签名使用 php::Args 类型
- 实现了闭包 use 子句中引用传递的支持
- 添加了两个闭包测试用例验证基本功能和引用传递
pull/1/head
韩天峰 7 months ago
parent 752767012f
commit b3dc15a55f
  1. 20
      src/Php/CompilerBase.php
  2. 30
      tests/zend/closures/closure_001.phpt
  3. 29
      tests/zend/closures/closure_002.phpt

@ -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,8 +3502,12 @@ class CompilerBase extends \PhpAot\Core\Translator
if ($this->isVarExpr($useItem->var) and !$this->hasVar($var)) {
$this->fatalError($expr, 'Variable `' . $var . '` is not defined');
}
if ($useItem->byRef) {
$useVars [] = $this->convertToRef($useItem->var);
} else {
$useVars [] = $var;
}
}
return 'php::newClosure(' . $tmpVar . ', { ' . implode(', ', $useVars) . ' })';
}

@ -0,0 +1,30 @@
--TEST--
Closure 001: Lambda without lexical variables
--FILE--
<?php
$lambda1 = function () {
echo "Hello World!\n";
};
$lambda2 = function ($x) {
echo "Hello $x!\n";
};
var_dump(is_callable($lambda1));
var_dump(is_callable($lambda2));
$lambda1();
$lambda2("Universe");
call_user_func($lambda1);
call_user_func($lambda2, "Universe");
echo "Done\n";
?>
--EXPECT--
bool(true)
bool(true)
Hello World!
Hello Universe!
Hello World!
Hello Universe!
Done

@ -0,0 +1,29 @@
--TEST--
Closure 002: Lambda with lexical variables (global scope)
--FILE--
<?php
$x = any(4);
$lambda1 = function () use ($x) {
echo "$x\n";
};
$lambda2 = function () use (&$x) {
echo "$x\n";
};
$lambda1();
$lambda2();
$x++;
$lambda1();
$lambda2();
echo "Done\n";
?>
--EXPECT--
4
4
4
5
Done
Loading…
Cancel
Save