feat(php): 添加属性访问控制和对象类型检查功能

- 在 ClassDef 中添加 hasProperty 和 getProperty 方法用于属性访问
- 在 CompilerBase 中添加 isTypedObject 方法检查对象类型
- 将 offsetGetIndirect 替换为 item 方法统一访问接口
- 实现 getPropertyIdentifier 方法支持属性标识符解析
- 添加对私有/受保护属性访问权限的检查和错误处理
- 将 getPropertyIndirect 替换为 attr 方法进行属性访问
- 添加属性引用访问支持通过 attrRef 方法实现
- 更新 identifierToStr 参数类型为 NodeAbstract
- 在 PropertyDef 中添加访问修饰符检查方法(isPrivate/isProtected/isPublic)
- 添加属性引用测试用例验证功能正确性
pull/1/head
韩天峰 7 months ago
parent 04fdbcc0e4
commit 9465caea14
  1. 10
      src/Php/ClassDef.php
  2. 45
      src/Php/CompilerBase.php
  3. 21
      src/Php/PropertyDef.php
  4. 17
      tests/aot/prop-ref.phpt

@ -31,4 +31,14 @@ class ClassDef extends ClassLikeDef
{
return isset($this->methods[$method]);
}
public function hasProperty(string $property): bool
{
return isset($this->properties[$property]);
}
public function getProperty($property): PropertyDef
{
return $this->properties[$property];
}
}

@ -238,6 +238,11 @@ class CompilerBase extends \PhpAot\Core\Translator
return isset($this->nativeFunctions[$name]);
}
public function isTypedObject(string $object): bool
{
return isset($this->objects[$object]);
}
protected function resetFunction(): void
{
$this->localVars = [];
@ -857,7 +862,7 @@ class CompilerBase extends \PhpAot\Core\Translator
if (!$this->hasVar($var)) {
$this->addLocalVar($var, self::TYPE_VAR);
}
$code .= "$var = $tmpVar.offsetGetIndirect($k); ";
$code .= "$var = $tmpVar.item($k); ";
} else {
abort($item);
}
@ -1470,7 +1475,7 @@ class CompilerBase extends \PhpAot\Core\Translator
$this->fatalError($node, 'Unsupported operand types: null + array');
}
$dim = $this->parseIdentifier($node->dim);
return $var . '.offsetGetIndirect(' . $this->trimBrackets($dim) . ', ' . $this->escapeBool($write) . ')';
return $var . '.item(' . $this->trimBrackets($dim) . ', ' . $this->escapeBool($write) . ')';
}
protected function parseArrayDimStore($array, $dim, $var): string
@ -2151,18 +2156,38 @@ class CompilerBase extends \PhpAot\Core\Translator
return implode(PHP_EOL . $this->getIndent(), $lines);
}
protected function parsePropertyFetch(Node\Expr\PropertyFetch $expr, bool $assign = false): string
protected function getPropertyIdentifier(NodeAbstract $object, NodeAbstract $property): string
{
$object = $expr->var;
$property = $expr->name;
$id = $this->identifierToStr($property);
if ($this->isVarExpr($object) and $this->isIdExpr($property)) {
if ($this->parseIdentifier($object) === 'this_') {
$id = self::PREFIX . $this->getPropertyOffset($property, $this->class, $this->namespace);
$objectName = $this->parseIdentifier($object);
$propertyName = $this->parseIdentifier($property);
if ($objectName === 'this_') {
$id = self::PREFIX . $this->getPropertyOffset($propertyName, $this->class, $this->namespace);
} elseif ($this->isTypedObject($objectName)) {
$class = $this->objects[$objectName];
if (isset($this->classes[$class])) {
$classDef = $this->classes[$class];
if ($classDef->hasProperty($propertyName)) {
$propertyDef = $classDef->getProperty($propertyName);
if ($propertyDef->isPublic() or $this->class === $class) {
$id = self::PREFIX . $this->getPropertyOffset($propertyName, $class, $classDef->namespace);
} else {
$this->fatalError($property, "Cannot access private/protected property `$propertyName` of class `$class`");
}
}
}
}
}
return $id;
}
return $this->convertToObject($object) . '.getPropertyIndirect(' . $id . ', ' . $this->escapeBool($assign) . ')';
protected function parsePropertyFetch(Node\Expr\PropertyFetch $expr, bool $update = false): string
{
$object = $expr->var;
$property = $expr->name;
$id = $this->getPropertyIdentifier($object, $property);
return $this->convertToObject($object) . '.attr(' . $id . ', ' . $this->escapeBool($update) . ')';
}
protected function parseAssignOpShiftRight(Node $node): string
@ -2525,7 +2550,7 @@ class CompilerBase extends \PhpAot\Core\Translator
$left = $this->parseIdentifier($expr->var);
$object = $this->convertToObject($expr->expr->var);
$prop = $this->identifierToStr($expr->expr->name);
return $left . ' = ' . $object . '.getPropertyReference(' . $prop . ')';
return $left . ' = ' . $object . '.attrRef(' . $prop . ')';
}
}
abort($expr);
@ -2546,7 +2571,7 @@ class CompilerBase extends \PhpAot\Core\Translator
}
}
protected function identifierToStr(Node $node, bool $require = true): string
protected function identifierToStr(NodeAbstract $node, bool $require = true): string
{
$id = $this->parseIdentifier($node);
if ($this->isVarExpr($node)) {

@ -2,18 +2,35 @@
namespace PhpAot\Php;
use PhpParser\Modifiers;
class PropertyDef
{
public string $name;
public string $type;
public string $flags;
public int $flags;
public ?string $default = null;
public function __construct(string $name, string $flags, string $type, ?string $default = null)
public function __construct(string $name, int $flags, string $type, ?string $default = null)
{
$this->flags = $flags;
$this->name = $name;
$this->type = $type;
$this->default = $default;
}
public function isPrivate(): bool
{
return $this->flags & Modifiers::PRIVATE;
}
public function isProtected(): bool
{
return $this->flags & Modifiers::PROTECTED;
}
public function isPublic(): bool
{
return !$this->isPrivate() && !$this->isProtected();
}
}

@ -0,0 +1,17 @@
--TEST--
object property ref
--FILE--
<?php
class TestRef {
public int $a = 999;
}
function main()
{
$o = new TestRef();
$ref = &$o->a;
$ref = 2026;
var_dump($o->a);
}
?>
--EXPECT--
int(2026)
Loading…
Cancel
Save