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() {