From 1c074d140e699d80733a74c9ffb99d1b52c227a1 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Thu, 29 Jan 2026 16:02:33 +0800 Subject: [PATCH] =?UTF-8?q?feat(php):=20=E5=AE=9E=E7=8E=B0PHP=E5=8F=B3?= =?UTF-8?q?=E7=BB=93=E5=90=88=E8=B5=8B=E5=80=BC=E6=93=8D=E4=BD=9C=E7=AC=A6?= =?UTF-8?q?=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加isArrayDimFetch方法用于检查数组维度获取表达式 - 实现parseRightAssociativeAssign方法处理右结合赋值链 - 重构parseAssign方法以支持嵌套赋值表达式解析 - 更新parseArrayDimStore方法处理空维度读取错误 - 添加右结合赋值功能的测试用例和示例文件 --- examples/right-assoc.php | 9 ++++ src/Php/AstNodeType.php | 4 ++ src/Php/CompilerBase.php | 73 +++++++++++++++++++++----------- tests/aot/right-associative.phpt | 65 ++++++++++++++++++++++++++++ 4 files changed, 126 insertions(+), 25 deletions(-) create mode 100644 examples/right-assoc.php create mode 100644 tests/aot/right-associative.phpt diff --git a/examples/right-assoc.php b/examples/right-assoc.php new file mode 100644 index 00000000..0c1cdd6a --- /dev/null +++ b/examples/right-assoc.php @@ -0,0 +1,9 @@ +val = $i->val = $j->val = 999; + +$h->val = 343; +var_dump($h); +var_dump($i); +var_dump($j); + diff --git a/src/Php/AstNodeType.php b/src/Php/AstNodeType.php index a81acf47..fee75bee 100644 --- a/src/Php/AstNodeType.php +++ b/src/Php/AstNodeType.php @@ -8,6 +8,10 @@ use PhpParser\Node; trait AstNodeType { + protected function isArrayDimFetch(NodeAbstract $expr): bool + { + return $expr instanceof Expr\ArrayDimFetch; + } protected function isVarExpr(NodeAbstract $expr): bool { return $expr instanceof Expr\Variable; diff --git a/src/Php/CompilerBase.php b/src/Php/CompilerBase.php index 53ffc085..c361135a 100644 --- a/src/Php/CompilerBase.php +++ b/src/Php/CompilerBase.php @@ -807,39 +807,56 @@ class CompilerBase extends \PhpAot\Core\Translator 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 { $left = $v->var; $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); } elseif ($left->getType() === 'Expr_StaticPropertyFetch') { $class = $this->identifierToStr($left->class); $propName = $this->identifierToStr($left->name); $value = $this->trimBrackets($this->parseExpr($right)); 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); } @@ -870,6 +887,7 @@ class CompilerBase extends \PhpAot\Core\Translator $this->indentLevel--; return $code . '}'; } + $this->inAssignExpr = true; $var = $this->parseIdentifier($left); $this->inAssignExpr = false; @@ -1472,10 +1490,15 @@ class CompilerBase extends \PhpAot\Core\Translator { $var = $this->parseIdentifier($node->var); 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 diff --git a/tests/aot/right-associative.phpt b/tests/aot/right-associative.phpt new file mode 100644 index 00000000..b6e12d8d --- /dev/null +++ b/tests/aot/right-associative.phpt @@ -0,0 +1,65 @@ +--TEST-- +right associative +--FILE-- +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) +} \ No newline at end of file