From 5a7805b544177c6904ae3a3f347fce07fe4b3152 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Wed, 17 Jun 2026 19:42:25 +0800 Subject: [PATCH] =?UTF-8?q?feat(php):=20=E6=B7=BB=E5=8A=A0=E5=AF=B9?= =?UTF-8?q?=E5=8A=A8=E6=80=81=E5=B1=9E=E6=80=A7=E8=B5=8B=E5=80=BC=E6=93=8D?= =?UTF-8?q?=E4=BD=9C=E7=AC=A6=E5=92=8C=E9=80=92=E5=A2=9E=E9=80=92=E5=87=8F?= =?UTF-8?q?=E6=93=8D=E4=BD=9C=E7=9A=84=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 实现了动态属性的赋值操作符(如 +=、-=、.= 等)的解析和代码生成 - 添加了动态属性递增递减操作(++、--)的特殊处理逻辑 - 新增 genDynamicPropIncDec 方法用于生成动态属性 ++/-- 操作的 C++ 代码 - 为 clone 测试用例中的属性访问错误提供了更准确的错误消息 - 在属性获取逻辑中添加了静态与非静态属性访问的错误检查机制 --- src/Php/CompilerBase.php | 54 +++++++++++++++++++++++++++++-- src/Php/Parser/AssignOpTrait.php | 17 ++++++++++ tests/core/classes/clone_006.phpt | 7 ++-- 3 files changed, 72 insertions(+), 6 deletions(-) diff --git a/src/Php/CompilerBase.php b/src/Php/CompilerBase.php index 6edaf650..0172422f 100644 --- a/src/Php/CompilerBase.php +++ b/src/Php/CompilerBase.php @@ -2431,10 +2431,44 @@ class CompilerBase extends \PhpAot\Core\Translator return $code; } + /** + * Generate C++ code for dynamic property ++/-- operations. + * + * Returns null if $var is not a dynamic property fetch, so callers can + * fall through to their normal codegen path. + */ + protected function genDynamicPropIncDec($var, string $op, bool $isPre): ?string + { + if (!$this->isPropertyFetch($var) || $var->getAttribute('nativeProperty')) { + return null; + } + + $obj = $this->parseIdentifier($var->var); + $propName = $this->identifierToStr($var->name, literal: true); + $tmpVar = $this->genTmpVarName(); + $this->addLocalVar($tmpVar, self::TYPE_VAR); + + if ($isPre) { + $this->context->beforeStmtLines[] = "{$tmpVar} = {$obj}.getProperty({$propName}) {$op} 1; {$obj}.setProperty({$propName}, {$tmpVar});"; + } else { + $this->context->beforeStmtLines[] = "{$tmpVar} = {$obj}.getProperty({$propName});"; + $this->context->afterStmtLines[] = "{$obj}.setProperty({$propName}, {$tmpVar} {$op} 1);"; + } + + return $tmpVar; + } + protected function parsePreInc(Expr\PreInc $expr): string { $oriInAssignExpr = $this->context->inAssignExpr; $this->context->inAssignExpr = true; + + $result = $this->genDynamicPropIncDec($expr->var, '+', true); + if ($result !== null) { + $this->context->inAssignExpr = $oriInAssignExpr; + return $result; + } + $type = $this->detectVarType($expr->var); if ($type === self::TYPE_BIGINT || $type === self::TYPE_DECIMAL || $type === self::TYPE_BIGFLOAT) { $this->fatalError($expr, 'Cannot use ++ on ' . $type . '. Use += 1 instead (Big* types are immutable).'); @@ -3061,6 +3095,11 @@ class CompilerBase extends \PhpAot\Core\Translator protected function parsePostOp(Expr\PostDec|Expr\PostInc $expr, string $op): string { + $result = $this->genDynamicPropIncDec($expr->var, $op, false); + if ($result !== null) { + return $result; + } + if ($this->isVarExpr($expr->var) or $this->isPropertyFetch($expr->var) or $this->isArrayDimFetch($expr->var)) { $oriInAssignExpr = $this->context->inAssignExpr; $this->context->inAssignExpr = true; @@ -3167,6 +3206,13 @@ class CompilerBase extends \PhpAot\Core\Translator { $oriInAssignExpr = $this->context->inAssignExpr; $this->context->inAssignExpr = true; + + $result = $this->genDynamicPropIncDec($expr->var, '-', true); + if ($result !== null) { + $this->context->inAssignExpr = $oriInAssignExpr; + return $result; + } + $type = $this->detectVarType($expr->var); if ($type === self::TYPE_BIGINT || $type === self::TYPE_DECIMAL || $type === self::TYPE_BIGFLOAT) { $this->fatalError($expr, 'Cannot use -- on ' . $type . '. Use -= 1 instead (Big* types are immutable).'); @@ -4713,9 +4759,11 @@ class CompilerBase extends \PhpAot\Core\Translator $classDef = $this->getClass($findClass); if ($classDef->hasProperty($property)) { $propertyDef = $classDef->getProperty($property); - // 获取动态属性,但找到了静态属性,或者获取静态属性,但是是动态属性,直接返回 null - if ((!$static and $propertyDef->isStatic()) or ($static and !$propertyDef->isStatic())) { - return null; + if (!$static and $propertyDef->isStatic()) { + $this->fatalError($expr, "Cannot access static property `{$class}::\${$property}` as non-static instance property."); + } + if ($static and !$propertyDef->isStatic()) { + $this->fatalError($expr, "Cannot access non-static property `{$class}::\${$property}` as static property."); } if ($propertyDef->isPublic()) { break; diff --git a/src/Php/Parser/AssignOpTrait.php b/src/Php/Parser/AssignOpTrait.php index 99fffb03..4ffae7e7 100644 --- a/src/Php/Parser/AssignOpTrait.php +++ b/src/Php/Parser/AssignOpTrait.php @@ -387,6 +387,23 @@ trait AssignOpTrait return $this->parseArrayDimStore($node->var->var, $dim, $tmpVar); } + if ($this->isPropertyFetch($node->var) and !$node->var->getAttribute('nativeProperty')) { + $obj = $this->parseIdentifier($node->var->var); + $propName = $this->identifierToStr($node->var->name, literal: true); + $binaryOp = $this->removeAssignOp($op); + $tmpVar = $this->genTmpVarName(); + $this->addLocalVar($tmpVar, self::TYPE_VAR); + if ($this->isAssignOpConcat($op)) { + $this->context->beforeStmtLines[] = "{$tmpVar} = php::concat({$obj}.getProperty({$propName}), {$expr});"; + } elseif ($this->isAssignOpPow($op)) { + $this->context->beforeStmtLines[] = "{$tmpVar} = php::fn::pow({$obj}.getProperty({$propName}), {$expr});"; + } else { + $this->context->beforeStmtLines[] = "{$tmpVar} = {$obj}.getProperty({$propName}) {$binaryOp} ({$expr});"; + } + $this->context->afterStmtLines[] = "{$obj}.setProperty({$propName}, {$tmpVar});"; + return $tmpVar; + } + if ($this->isAssignOpConcat($op)) { return $var . '.append(' . $expr . ')'; } diff --git a/tests/core/classes/clone_006.phpt b/tests/core/classes/clone_006.phpt index 4ba87b1c..c81b8cce 100644 --- a/tests/core/classes/clone_006.phpt +++ b/tests/core/classes/clone_006.phpt @@ -6,15 +6,16 @@ error_reporting=2047 id = self::$id++; + $this->id = self::$nextId++; } function __clone() { $this->address = "New York"; - $this->id = self::$id++; + $this->id = self::$nextId++; } } function main() {