fix(compiler): 修复静态属性引用赋值

pull/17/head
Yurun 1 month ago
parent c7bdfd883d
commit 29fcf06482
  1. 3
      src/Parser/AssignOpTrait.php
  2. 30
      src/Parser/PropertyAccessTrait.php
  3. 34
      tests/compiler/static/static-prop-assign-ref-late-static-binding.phpt
  4. 41
      tests/compiler/static/static-prop-assign-ref-native.phpt
  5. 36
      tests/compiler/static/static-prop-assign-ref-parent.phpt
  6. 40
      tests/compiler/static/static-prop-assign-ref.phpt

@ -758,6 +758,9 @@ trait AssignOpTrait
} elseif ($this->isPropertyFetch($expr->expr)) {
$left = $this->parseIdentifier($expr->var);
$rightExpr = $tmpVar . ' = ' . $this->emitDynamicPropertyFetchRef($expr->expr, $expr);
} elseif ($this->isStaticPropertyFetch($expr->expr)) {
$left = $this->parseIdentifier($expr->var);
$rightExpr = $tmpVar . ' = ' . $this->emitStaticPropertyFetchRef($expr->expr, $expr);
} elseif ($this->isArrayDimFetch($expr->expr)) {
$left = $this->parseIdentifier($expr->var);
$array = $this->parseWritableIdentifier($expr->expr->var);

@ -201,6 +201,36 @@ trait PropertyAccessTrait
return $objectExpr . '.attrRef(' . $this->identifierToStr($expr->name) . ')';
}
/**
* Emit a reference (php::Ref) bound to a static property's underlying zval.
*
* `php::getStaticProperty(ce, offset)` returns a Variant that shares the
* static property's zval, so `.toReference()` yields a live reference whose
* writes propagate back to the static property.
*/
protected function emitStaticPropertyFetchRef(Expr\StaticPropertyFetch $expr, NodeAbstract $errorNode): string
{
if ($this->isIdExpr($expr->name)) {
$this->resolveNativeStaticPropertyFetch($expr);
$this->assertPropertySetVisibility($expr);
}
$resolution = $this->resolveNativeStaticPropertyFetch($expr);
if ($resolution !== null) {
if ($resolution->class !== null) {
$classPtr = $this->getClassEntryPtr($resolution->class);
return Symbol::getStaticProperty() . '(' . $classPtr . ', ' . $resolution->expression . ').toReference()';
}
if ($resolution->expression !== null) {
// Dynamic target, e.g. `self` resolved through the called class inside a trait.
return $resolution->expression . '.toReference()';
}
}
// Fully dynamic path: `static` keyword, dynamic class name, or dynamic property name.
return $this->parseDynamicStaticPropertyFetch($expr) . '.toReference()';
}
protected function resolveNativeStaticPropertyFetch(Expr\StaticPropertyFetch $expr): ?StaticPropertyFetchResolution
{

@ -0,0 +1,34 @@
--TEST--
Assign by reference to late-static-bound property resolves to called class
--FILE--
<?php
use native_types;
class Base {
protected static int $value = 1;
public static function bump(): void {
$ref = &static::$value;
$ref = 99;
}
public static function show(): void {
var_dump(static::$value);
}
}
class Child extends Base {
protected static int $value = 2;
}
function main(): void {
Base::show();
Child::bump();
Child::show();
Base::show();
}
?>
--EXPECT--
int(1)
int(99)
int(1)

@ -0,0 +1,41 @@
--TEST--
Assign by reference to native typed static property (self / static / class name)
--FILE--
<?php
use native_types;
class Test
{
private static int $value = 123;
public static function abc(): void
{
$a = &self::$value;
var_dump($a);
$a = 456;
var_dump(self::$value);
$b = &static::$value;
var_dump($b);
$b = 789;
var_dump(static::$value);
$c = &Test::$value;
var_dump($c);
$c = 1000;
var_dump(Test::$value);
}
}
function main(): void
{
Test::abc();
}
?>
--EXPECT--
int(123)
int(456)
int(456)
int(789)
int(789)
int(1000)

@ -0,0 +1,36 @@
--TEST--
Assign by reference to parent static property (parent::$value)
--FILE--
<?php
class Base
{
public static $value = 1;
}
class Child extends Base
{
public static function run()
{
$ref = &parent::$value;
var_dump(parent::$value);
var_dump(Child::$value);
$ref = 999;
var_dump(parent::$value);
var_dump(Child::$value);
var_dump(Base::$value);
}
}
function main()
{
Child::run();
}
?>
--EXPECT--
int(1)
int(1)
int(999)
int(999)
int(999)

@ -0,0 +1,40 @@
--TEST--
Assign by reference to static property (self / static / class name)
--FILE--
<?php
class Test
{
private static $value = 123;
public static function abc(): void
{
$a = &self::$value;
var_dump($a);
$a = 456;
var_dump(self::$value);
$b = &static::$value;
var_dump($b);
$b = 789;
var_dump(static::$value);
$c = &Test::$value;
var_dump($c);
$c = 1000;
var_dump(Test::$value);
}
}
function main(): void
{
Test::abc();
}
?>
--EXPECT--
int(123)
int(456)
int(456)
int(789)
int(789)
int(1000)
Loading…
Cancel
Save