From 9465caea14f8f62c81828ce743570d3a5ca5ae94 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Thu, 29 Jan 2026 11:03:57 +0800 Subject: [PATCH] =?UTF-8?q?feat(php):=20=E6=B7=BB=E5=8A=A0=E5=B1=9E?= =?UTF-8?q?=E6=80=A7=E8=AE=BF=E9=97=AE=E6=8E=A7=E5=88=B6=E5=92=8C=E5=AF=B9?= =?UTF-8?q?=E8=B1=A1=E7=B1=BB=E5=9E=8B=E6=A3=80=E6=9F=A5=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在 ClassDef 中添加 hasProperty 和 getProperty 方法用于属性访问 - 在 CompilerBase 中添加 isTypedObject 方法检查对象类型 - 将 offsetGetIndirect 替换为 item 方法统一访问接口 - 实现 getPropertyIdentifier 方法支持属性标识符解析 - 添加对私有/受保护属性访问权限的检查和错误处理 - 将 getPropertyIndirect 替换为 attr 方法进行属性访问 - 添加属性引用访问支持通过 attrRef 方法实现 - 更新 identifierToStr 参数类型为 NodeAbstract - 在 PropertyDef 中添加访问修饰符检查方法(isPrivate/isProtected/isPublic) - 添加属性引用测试用例验证功能正确性 --- src/Php/ClassDef.php | 10 +++++++++ src/Php/CompilerBase.php | 45 +++++++++++++++++++++++++++++++--------- src/Php/PropertyDef.php | 21 +++++++++++++++++-- tests/aot/prop-ref.phpt | 17 +++++++++++++++ 4 files changed, 81 insertions(+), 12 deletions(-) create mode 100644 tests/aot/prop-ref.phpt diff --git a/src/Php/ClassDef.php b/src/Php/ClassDef.php index adaebf63..b4f63cd4 100644 --- a/src/Php/ClassDef.php +++ b/src/Php/ClassDef.php @@ -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]; + } } \ No newline at end of file diff --git a/src/Php/CompilerBase.php b/src/Php/CompilerBase.php index 85a434e4..53ffc085 100644 --- a/src/Php/CompilerBase.php +++ b/src/Php/CompilerBase.php @@ -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)) { diff --git a/src/Php/PropertyDef.php b/src/Php/PropertyDef.php index 1df41a5b..6f0f1d0c 100644 --- a/src/Php/PropertyDef.php +++ b/src/Php/PropertyDef.php @@ -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(); + } } \ No newline at end of file diff --git a/tests/aot/prop-ref.phpt b/tests/aot/prop-ref.phpt new file mode 100644 index 00000000..260df05e --- /dev/null +++ b/tests/aot/prop-ref.phpt @@ -0,0 +1,17 @@ +--TEST-- +object property ref +--FILE-- +a; + $ref = 2026; + var_dump($o->a); +} +?> +--EXPECT-- +int(2026) \ No newline at end of file