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

- 实现 addClass 方法用于添加类定义到编译器
- 优化 hasNativeClass 和 getClassDef 方法使用 escapeClass 处理类名
- 更新 escapeClass 方法支持反斜杠替换为下划线并去除首尾反斜杠
- 重构 getPropertyIdentifier 方法简化属性标识符获取逻辑
- 新增 findNativeProperty 方法实现原生属性查找功能
- 添加对私有、受保护和公共属性的访问权限检查
- 支持继承链中属性的向上查找功能
- 为 get_class_vars 函数添加完整的测试用例覆盖
pull/1/head
韩天峰 7 months ago
parent ac3351b5f4
commit 095f9e8c66
  1. 75
      src/Php/CompilerBase.php
  2. 2
      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; $this->globalVars[$name] = $type;
} }
protected function addClass(string $name, ClassDef $classDef): void
{
$this->classes[$this->escapeClass($name)] = $classDef;
}
protected function hasVar(string $name): bool protected function hasVar(string $name): bool
{ {
return $this->hasLocalVar($name) || $this->hasGlobalVar($name); return $this->hasLocalVar($name) || $this->hasGlobalVar($name);
@ -1368,8 +1373,7 @@ class CompilerBase extends \PhpAot\Core\Translator
protected function hasNativeClass(string $name): bool protected function hasNativeClass(string $name): bool
{ {
$name = trim($name, '\\'); return array_key_exists($this->escapeClass($name), $this->classes);
return array_key_exists($name, $this->classes);
} }
protected function getNativeStaticMethod(string $class, string $method): string|false 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 protected function getClassDef(string $name): ClassDef
{ {
$name = trim($name, '\\'); return $this->classes[$this->escapeClass($name)];
return $this->classes[$name];
} }
protected function resetReturnType(string $type): void protected function resetReturnType(string $type): void
@ -2613,7 +2616,7 @@ class CompilerBase extends \PhpAot\Core\Translator
protected function escapeClass(string $class): string protected function escapeClass(string $class): string
{ {
return strtolower($class); return str_replace('\\', '_', trim(strtolower($class), '\\'));
} }
protected function escapeFileName(string $file): string protected function escapeFileName(string $file): string
@ -2717,31 +2720,22 @@ class CompilerBase extends \PhpAot\Core\Translator
return implode(PHP_EOL . $this->getIndent(), $lines); 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)) { if ($this->isVarExpr($object) and $this->isIdExpr($property)) {
$objectName = $this->parseIdentifier($object); $objectName = $this->parseIdentifier($object);
$propertyName = $this->parseIdentifier($property); $propertyName = $this->parseIdentifier($property);
$nativeProperty = null;
if ($objectName === 'this_') { 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)) { } elseif ($this->isTypedObject($objectName)) {
$class = $this->objects[$objectName]; $nativeProperty = $this->findNativeProperty($propertyName, $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}`");
}
} }
if ($nativeProperty) {
return $nativeProperty;
} }
} }
} return $this->identifierToStr($property);
return $id;
} }
protected function parsePropertyFetch(Node\Expr\PropertyFetch $expr, bool $update = false): string protected function parsePropertyFetch(Node\Expr\PropertyFetch $expr, bool $update = false): string
@ -2749,7 +2743,6 @@ class CompilerBase extends \PhpAot\Core\Translator
$object = $expr->var; $object = $expr->var;
$property = $expr->name; $property = $expr->name;
$id = $this->getPropertyIdentifier($object, $property); $id = $this->getPropertyIdentifier($object, $property);
return $this->convertToObject($object) . '.attr(' . $id . ', ' . $this->escapeBool($update) . ')'; return $this->convertToObject($object) . '.attr(' . $id . ', ' . $this->escapeBool($update) . ')';
} }
@ -3318,6 +3311,44 @@ class CompilerBase extends \PhpAot\Core\Translator
return null; 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 protected function parseNativeStaticPropertyFetch(Node\Expr\StaticPropertyFetch $expr): string|bool
{ {
$nativeProp = $this->findNativeStaticProperty($expr, $class, $namespace); $nativeProp = $this->findNativeStaticProperty($expr, $class, $namespace);

@ -688,7 +688,7 @@ class Translator extends Preprocessor
$this->classDef->implements = $this->parseIdentifierList($class->implements); $this->classDef->implements = $this->parseIdentifierList($class->implements);
$className = $this->classDef->getNamespacedName(); $className = $this->classDef->getNamespacedName();
$this->classes[$className] = $this->classDef; $this->addClass($className, $this->classDef);
$this->classesDefineInFile[$className] = $this->classDef; $this->classesDefineInFile[$className] = $this->classDef;
$methodCodes = []; $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