feat(php): 添加类定义管理和属性访问优化功能

- 实现 addClass 方法用于添加类定义到编译器
- 优化 hasNativeClass 和 getClassDef 方法使用 escapeClass 处理类名
- 更新 escapeClass 方法支持反斜杠替换为下划线并去除首尾反斜杠
- 重构 getPropertyIdentifier 方法简化属性标识符获取逻辑
- 新增 findNativeProperty 方法实现原生属性查找功能
- 添加对私有、受保护和公共属性的访问权限检查
- 支持继承链中属性的向上查找功能
- 为 get_class_vars 函数添加完整的测试用例覆盖
pull/1/head
韩天峰 6 months ago
parent ac3351b5f4
commit 095f9e8c66
  1. 83
      src/Php/CompilerBase.php
  2. 4
      src/Php/Translator.php
  3. 40
      tests/zend/get_class_vars/get_class_vars_001.phpt
  4. 48
      tests/zend/get_class_vars/get_class_vars_002.phpt

@ -1356,6 +1356,11 @@ class CompilerBase extends \PhpAot\Core\Translator
$this->globalVars[$name] = $type;
}
protected function addClass(string $name, ClassDef $classDef): void
{
$this->classes[$this->escapeClass($name)] = $classDef;
}
protected function hasVar(string $name): bool
{
return $this->hasLocalVar($name) || $this->hasGlobalVar($name);
@ -1368,8 +1373,7 @@ class CompilerBase extends \PhpAot\Core\Translator
protected function hasNativeClass(string $name): bool
{
$name = trim($name, '\\');
return array_key_exists($name, $this->classes);
return array_key_exists($this->escapeClass($name), $this->classes);
}
protected function getNativeStaticMethod(string $class, string $method): string|false
@ -1386,8 +1390,7 @@ class CompilerBase extends \PhpAot\Core\Translator
protected function getClassDef(string $name): ClassDef
{
$name = trim($name, '\\');
return $this->classes[$name];
return $this->classes[$this->escapeClass($name)];
}
protected function resetReturnType(string $type): void
@ -2613,7 +2616,7 @@ class CompilerBase extends \PhpAot\Core\Translator
protected function escapeClass(string $class): string
{
return strtolower($class);
return str_replace('\\', '_', trim(strtolower($class), '\\'));
}
protected function escapeFileName(string $file): string
@ -2717,39 +2720,29 @@ class CompilerBase extends \PhpAot\Core\Translator
return implode(PHP_EOL . $this->getIndent(), $lines);
}
protected function getPropertyIdentifier(NodeAbstract $object, NodeAbstract $property): string
protected function getPropertyIdentifier(NodeAbstract $object, NodeAbstract $property): ?string
{
$id = $this->identifierToStr($property);
if ($this->isVarExpr($object) and $this->isIdExpr($property)) {
$objectName = $this->parseIdentifier($object);
$objectName = $this->parseIdentifier($object);
$propertyName = $this->parseIdentifier($property);
$nativeProperty = null;
if ($objectName === 'this_') {
$id = self::PREFIX . $this->getPropertyOffset($propertyName, $this->class, $this->namespace);
$nativeProperty = $this->findNativeProperty($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}`");
}
}
}
$nativeProperty = $this->findNativeProperty($propertyName, $this->objects[$objectName]);
}
if ($nativeProperty) {
return $nativeProperty;
}
}
return $id;
return $this->identifierToStr($property);
}
protected function parsePropertyFetch(Node\Expr\PropertyFetch $expr, bool $update = false): string
{
$object = $expr->var;
$object = $expr->var;
$property = $expr->name;
$id = $this->getPropertyIdentifier($object, $property);
$id = $this->getPropertyIdentifier($object, $property);
return $this->convertToObject($object) . '.attr(' . $id . ', ' . $this->escapeBool($update) . ')';
}
@ -3318,6 +3311,44 @@ class CompilerBase extends \PhpAot\Core\Translator
return null;
}
protected function findNativeProperty($property, string $class, string $namespace = ''): ?string
{
$findClass = $class;
if ($namespace) {
$findClass = $namespace . '\\' . $class;
}
$scope = $this->class ? $namespace . '\\' . $class : '';
while (true) {
if ($this->hasNativeClass($findClass)) {
$classDef = $this->getClassDef($findClass);
if ($classDef->hasProperty($property)) {
$propertyDef = $classDef->getProperty($property);
if ($propertyDef->isPublic()) {
return self::PREFIX . $this->getPropertyOffset($property, $classDef->name, $classDef->namespace);
} elseif ($propertyDef->isProtected()) {
if ($scope) {
return self::PREFIX . $this->getPropertyOffset($property, $classDef->name, $classDef->namespace);
}
$this->fatalError($property, "Cannot access protected property `{$property}` of class `{$class}`");
} else {
if ($scope === $findClass) {
return self::PREFIX . $this->getPropertyOffset($property, $classDef->name, $classDef->namespace);
}
$this->error("Cannot access private property `{$property}` of class `{$class}`");
}
} elseif ($classDef->extends) {
$findClass = $classDef->namespace . '\\' . $classDef->extends;
continue;
} else {
$this->error("Property `{$property}` does not exist in class `{$class}`");
}
}
break;
}
return null;
}
protected function parseNativeStaticPropertyFetch(Node\Expr\StaticPropertyFetch $expr): string|bool
{
$nativeProp = $this->findNativeStaticProperty($expr, $class, $namespace);

@ -687,8 +687,8 @@ class Translator extends Preprocessor
}
$this->classDef->implements = $this->parseIdentifierList($class->implements);
$className = $this->classDef->getNamespacedName();
$this->classes[$className] = $this->classDef;
$className = $this->classDef->getNamespacedName();
$this->addClass($className, $this->classDef);
$this->classesDefineInFile[$className] = $this->classDef;
$methodCodes = [];

@ -0,0 +1,40 @@
--TEST--
get_class_vars(): Simple test
--FILE--
<?php
class A {
public $a = 1;
private $b = 2;
private $c = 3;
}
class B extends A {
static public $aa = 4;
static private $bb = 5;
static protected $cc = 6;
}
function main() {
var_dump(get_class_vars('A'));
var_dump(get_class_vars('B'));
try {
get_class_vars("Unknown");
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
}
?>
--EXPECT--
array(1) {
["a"]=>
int(1)
}
array(2) {
["a"]=>
int(1)
["aa"]=>
int(4)
}
get_class_vars(): Argument #1 ($class) must be a valid class name, Unknown given

@ -0,0 +1,48 @@
--TEST--
get_class_vars(): Testing the scope
--FILE--
<?php
class A {
public $a = 1;
protected $b = 2;
}
class B extends A {
static public $aa = 4;
static private $bb = 5;
static protected $cc = 6;
}
class C extends B {
public function __construct() {
var_dump(get_class_vars('A'));
var_dump(get_class_vars('B'));
var_dump($this->a, $this->b);
}
}
function main() {
new C;
}
?>
--EXPECTF--
array(2) {
["a"]=>
int(1)
["b"]=>
int(2)
}
array(4) {
["a"]=>
int(1)
["b"]=>
int(2)
["aa"]=>
int(4)
["cc"]=>
int(6)
}
int(1)
int(2)
Loading…
Cancel
Save