feat(php): 实现PHP右结合赋值操作符支持

- 添加isArrayDimFetch方法用于检查数组维度获取表达式
- 实现parseRightAssociativeAssign方法处理右结合赋值链
- 重构parseAssign方法以支持嵌套赋值表达式解析
- 更新parseArrayDimStore方法处理空维度读取错误
- 添加右结合赋值功能的测试用例和示例文件
pull/1/head
韩天峰 7 months ago
parent 9465caea14
commit 1c074d140e
  1. 9
      examples/right-assoc.php
  2. 4
      src/Php/AstNodeType.php
  3. 73
      src/Php/CompilerBase.php
  4. 65
      tests/aot/right-associative.phpt

@ -0,0 +1,9 @@
<?php
$h = $i = $j = new stdClass();
$h->val = $i->val = $j->val = 999;
$h->val = 343;
var_dump($h);
var_dump($i);
var_dump($j);

@ -8,6 +8,10 @@ use PhpParser\Node;
trait AstNodeType trait AstNodeType
{ {
protected function isArrayDimFetch(NodeAbstract $expr): bool
{
return $expr instanceof Expr\ArrayDimFetch;
}
protected function isVarExpr(NodeAbstract $expr): bool protected function isVarExpr(NodeAbstract $expr): bool
{ {
return $expr instanceof Expr\Variable; return $expr instanceof Expr\Variable;

@ -807,39 +807,56 @@ class CompilerBase extends \PhpAot\Core\Translator
return "$array.setProperty($propName, " . $this->trimBrackets($this->parseExpr($right)) . ")"; return "$array.setProperty($propName, " . $this->trimBrackets($this->parseExpr($right)) . ")";
} }
protected function parseRightAssociativeAssign(NodeAbstract $left, Node\Expr\Assign $right): string
{
$checkVarFn = function ($var) {
if ($this->isArrayDimFetch($var)) {
$array = $this->parseIdentifier($var->var);
if (!$this->hasVar($array)) {
$this->addLocalVar($array, self::TYPE_ARRAY);
}
}
};
$checkVarFn($left);
$chain[] = $left;
$next = $right;
while ($next->getType() === 'Expr_Assign') {
$var = $next->var;
$checkVarFn($var);
$chain[] = $var;
$next = $next->expr;
}
$tmpVar = $this->genTmpVarName();
$this->addLocalVar($tmpVar, self::TYPE_VAR);
// 翻转赋值链
$chain = array_reverse($chain);
$list = [];
$list[] = $this->getIndent() . $tmpVar . ' = ' . $this->parseExpr($next);
$right = new Variable($tmpVar);
foreach ($chain as $var) {
$list[] = $this->getIndent() . $this->parseFinallyAssign($var, $right);
}
return implode(";\n" . $this->getIndent(), $list);
}
protected function parseAssign(Node\Expr\Assign $v): string protected function parseAssign(Node\Expr\Assign $v): string
{ {
$left = $v->var; $left = $v->var;
$right = $v->expr; $right = $v->expr;
if ($left->getType() === self::EXPR_ARRAY_DIM_FETCH) { if ($right->getType() === 'Expr_Assign') {
return $this->parseRightAssociativeAssign($left, $right);
} elseif ($left->getType() === self::EXPR_ARRAY_DIM_FETCH) {
return $this->parseAssignArrayDim($left, $right); return $this->parseAssignArrayDim($left, $right);
} elseif ($left->getType() === 'Expr_StaticPropertyFetch') { } elseif ($left->getType() === 'Expr_StaticPropertyFetch') {
$class = $this->identifierToStr($left->class); $class = $this->identifierToStr($left->class);
$propName = $this->identifierToStr($left->name); $propName = $this->identifierToStr($left->name);
$value = $this->trimBrackets($this->parseExpr($right)); $value = $this->trimBrackets($this->parseExpr($right));
return "php::setStaticProperty($class, $propName, $value)"; return "php::setStaticProperty($class, $propName, $value)";
} elseif ($right->getType() === 'Expr_Assign') {
$chain[] = $left;
while ($right->getType() === 'Expr_Assign') {
$chain[] = $right->var;
$right = $right->expr;
}
// 翻转赋值链
$chain = array_reverse($chain);
// 取最后一个变量作为第一行的 left,右值为表达式
$left = array_shift($chain);
$list[] = $this->parseFinallyAssign($left, $right);
/**
* 构造赋值链
* a = b = c = d = (expr) -> d = (expr); c = d; b = c; a = b;
*/
$right = $left;
foreach ($chain as $left) {
$list[] = $this->getIndent() . $this->parseFinallyAssign($left, $right);
$right = $left;
}
return implode(";\n" . $this->getIndent(), $list);
} }
return $this->parseFinallyAssign($left, $right); return $this->parseFinallyAssign($left, $right);
} }
@ -870,6 +887,7 @@ class CompilerBase extends \PhpAot\Core\Translator
$this->indentLevel--; $this->indentLevel--;
return $code . '}'; return $code . '}';
} }
$this->inAssignExpr = true; $this->inAssignExpr = true;
$var = $this->parseIdentifier($left); $var = $this->parseIdentifier($left);
$this->inAssignExpr = false; $this->inAssignExpr = false;
@ -1472,10 +1490,15 @@ class CompilerBase extends \PhpAot\Core\Translator
{ {
$var = $this->parseIdentifier($node->var); $var = $this->parseIdentifier($node->var);
if ($node->dim === null) { if ($node->dim === null) {
$this->fatalError($node, 'Unsupported operand types: null + array'); if (!$write) {
$this->fatalError($node, 'Cannot use [] for reading');
} else {
return $var . '.newItem()';
}
} else {
$dim = $this->trimBrackets($this->parseIdentifier($node->dim));
return $var . '.item(' . $dim . ', ' . $this->escapeBool($write) . ')';
} }
$dim = $this->parseIdentifier($node->dim);
return $var . '.item(' . $this->trimBrackets($dim) . ', ' . $this->escapeBool($write) . ')';
} }
protected function parseArrayDimStore($array, $dim, $var): string protected function parseArrayDimStore($array, $dim, $var): string

@ -0,0 +1,65 @@
--TEST--
right associative
--FILE--
<?php
$a = $b = $c = 100;
var_dump($a);
var_dump($b);
var_dump($c);
$d[] = $e[] = $f[] = 23;
var_dump($d);
var_dump($e);
var_dump($f);
$h = $i = $j = new stdClass();
$h->val = $i->val = $j->val = 999;
var_dump($h);
var_dump($i);
var_dump($j);
$k['val'] = $l['val'] = $m['val'] = 888;
var_dump($k);
var_dump($l);
var_dump($m);
?>
--EXPECTF--
int(100)
int(100)
int(100)
array(1) {
[0]=>
int(23)
}
array(1) {
[0]=>
int(23)
}
array(1) {
[0]=>
int(23)
}
object(stdClass)#1 (1) {
["val"]=>
int(999)
}
object(stdClass)#1 (1) {
["val"]=>
int(999)
}
object(stdClass)#1 (1) {
["val"]=>
int(999)
}
array(1) {
["val"]=>
int(888)
}
array(1) {
["val"]=>
int(888)
}
array(1) {
["val"]=>
int(888)
}
Loading…
Cancel
Save