feat(php): 添加对动态属性赋值操作符和递增递减操作的支持

- 实现了动态属性的赋值操作符(如 +=、-=、.= 等)的解析和代码生成
- 添加了动态属性递增递减操作(++、--)的特殊处理逻辑
- 新增 genDynamicPropIncDec 方法用于生成动态属性 ++/-- 操作的 C++ 代码
- 为 clone 测试用例中的属性访问错误提供了更准确的错误消息
- 在属性获取逻辑中添加了静态与非静态属性访问的错误检查机制
pull/2/head
韩天峰 2 months ago
parent 386c6568f1
commit 5a7805b544
  1. 54
      src/Php/CompilerBase.php
  2. 17
      src/Php/Parser/AssignOpTrait.php
  3. 7
      tests/core/classes/clone_006.phpt

@ -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;

@ -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 . ')';
}

@ -6,15 +6,16 @@ error_reporting=2047
<?php
class MyCloneable {
static $id = 0;
static $nextId = 0;
public $id;
function __construct() {
$this->id = self::$id++;
$this->id = self::$nextId++;
}
function __clone() {
$this->address = "New York";
$this->id = self::$id++;
$this->id = self::$nextId++;
}
}
function main() {

Loading…
Cancel
Save