refactor(php): 重构属性访问解析器以使用只读上下文接口

- 将 PropertyAccessResolver 从依赖 CompilerBase 大类改为依赖 PropertyAccessContext 接口
- 在 CompilerBase 中实现 PropertyAccessContext 接口并提供 getClassDef 和 getParentClass 方法
- 移除 PropertyAccessResolver 构造函数中的闭包参数,改用接口方法调用
- 更新 Preprocessor 中的父类获取逻辑以直接使用命名空间类名解析
- 修改 Translator 中的父类解析方式以保持一致性
- 在重构计划文档中添加只读接口设计原则说明
pull/4/head
韩天峰 2 months ago
parent 87c54236a0
commit 59c9477bf5
  1. 2
      docs/REFACTORING_PLAN.md
  2. 32
      src/Php/CompilerBase.php
  3. 7
      src/Php/Preprocessor.php
  4. 16
      src/Php/Resolver/PropertyAccessResolver.php
  5. 2
      src/Php/Translator.php

@ -21,6 +21,7 @@
5. 优先使用小型 service/helper、明确 DTO、resolver、emitter;仅在边界稳定后再引入 Visitor、Strategy 等模式。
6. 所有错误信息保持 PHP 风格,不暴露实现细节,不使用 “AOT 禁止” 这类表述。
7. 每个阶段必须有 phpunit 或 phpt 回归验证,尤其是属性、类型、调用、继承、异常等高风险路径。
8. 面向 resolver/emitter 暴露的编译器接口优先保持只读。只读查询接口可以按需设为 public,写操作必须格外谨慎,避免绕过统一状态管理。
## 目标架构方向
@ -240,6 +241,7 @@
- 旧的 `CompilerBase::findNativeProperty()` 泛型入口已移除,避免后续继续扩散带 `$static` 布尔参数的访问模式。
- `CompilerBase::isSameClassName()`、`isSameOrSubclassOf()`、`canAccessProtectedProperty()` 已委托 resolver,避免规则继续扩散。
- 已添加 `prepare/convert/idle` 编译阶段状态;`PropertyAccessResolver` 只能在 convert 阶段创建和使用,避免预处理阶段误用不完整的类表状态。
- `PropertyAccessResolver` 已改为依赖 `PropertyAccessContext` 只读接口,而不是完整依赖 `CompilerBase` 大类。
- 当前迁移保持生成代码不变,后续阶段再统一 read/write emitter。
验证:

@ -43,6 +43,7 @@ use PhpAot\Php\Platform\Macos;
use PhpAot\Php\Platform\PlatformBase;
use PhpAot\Php\Platform\PlatformFactory;
use PhpAot\Php\Platform\Windows;
use PhpAot\Php\Resolver\PropertyAccessContext;
use PhpAot\Php\Resolver\PropertyAccessResult;
use PhpAot\Php\Resolver\PropertyAccessResolver;
use PhpParser\Modifiers;
@ -64,7 +65,7 @@ use PhpParser\ParserFactory;
use PhpParser\PhpVersion;
use PhpParser\PrettyPrinter;
class CompilerBase extends \PhpAot\Core\Translator
class CompilerBase extends \PhpAot\Core\Translator implements PropertyAccessContext
{
use AstNodeType;
use FuncCallOptimizer;
@ -1919,6 +1920,16 @@ class CompilerBase extends \PhpAot\Core\Translator
return $this->classes[$this->escapeClass($name)];
}
public function getClassDef(string $name): ?ClassDef
{
return $this->classes[$this->escapeClass($name)] ?? null;
}
public function getParentClass(string $class): string
{
return $this->classExtends[strtolower(ltrim($class, '\\'))] ?? '';
}
protected function hasClass(string $name): bool
{
return array_key_exists($this->escapeClass($name), $this->classes);
@ -2813,16 +2824,7 @@ class CompilerBase extends \PhpAot\Core\Translator
}
/**
* Expand a Big* compound-assignment into `$v = Type::method($v, $rhs)`.
*
* BigInt/BigDecimal/BigFloat are immutable Box types — Variant operator+=
* goes through ZendVM add_function which does not understand them, so we
* must emit the static-method form instead.
*/
/**
* Generate the C++ expression for a Big* binary operation.
*
* @param NodeAbstract $errorNode node to blame on unsupported operators
* Report a compiler fatal error.
*/
public function error(string $msg): never
{
@ -2837,7 +2839,7 @@ class CompilerBase extends \PhpAot\Core\Translator
}
}
protected function fatalError(Node $node, string $msg): never
public function fatalError(NodeAbstract $node, string $msg): never
{
$this->error("{$msg} in {$this->file}:{$node->getStartLine()}");
}
@ -5838,11 +5840,7 @@ class CompilerBase extends \PhpAot\Core\Translator
private function createPropertyAccessResolver(): PropertyAccessResolver
{
$this->assertCompilerPhase(self::PHASE_CONVERT, 'PropertyAccessResolver');
return new PropertyAccessResolver(
fn(string $class): ?ClassDef => $this->classes[$this->escapeClass($class)] ?? null,
fn(string $class): string => $this->classExtends[strtolower(ltrim($class, '\\'))] ?? '',
fn(NodeAbstract $expr, string $message) => $this->fatalError($expr, $message),
);
return new PropertyAccessResolver($this);
}
protected function isSameClassName(string $classA, string $classB): bool

@ -456,11 +456,6 @@ class Preprocessor extends CompilerBase
}
}
protected function getParentClass(NodeAbstract $extends): string
{
return $this->getNamespacedClassName($this->parseIdentifier($extends));
}
protected function prepareClass(Node\Stmt\Class_|Node\Stmt\Trait_|Node\Stmt\Enum_ $class): string
{
$this->resetClass();
@ -481,7 +476,7 @@ class Preprocessor extends CompilerBase
$this->addClass($fullClassName, $this->classDef);
if (!empty($class->extends)) {
$this->parentClass = $this->getParentClass($class->extends);
$this->parentClass = $this->getNamespacedClassName($this->parseIdentifier($class->extends));
$parentClassLower = strtolower($this->parentClass);
if ($parentClassLower === $fullClassNameLower) {
$this->fatalError($class, "Class {$fullClassName} cannot extend itself");

@ -8,20 +8,12 @@
namespace PhpAot\Php\Resolver;
use Closure;
use PhpAot\Php\Entity\ClassDef;
use PhpParser\NodeAbstract;
final class PropertyAccessResolver
{
/**
* @param Closure(string): ?ClassDef $getClassDef
* @param Closure(string): string $getParentClass
*/
public function __construct(
private readonly Closure $getClassDef,
private readonly Closure $getParentClass,
private readonly Closure $fatalError,
private readonly PropertyAccessContext $compiler,
) {
}
@ -38,7 +30,7 @@ final class PropertyAccessResolver
if ($class === $parent) {
return true;
}
$class = ($this->getParentClass)($class);
$class = $this->compiler->getParentClass($class);
}
return false;
}
@ -63,7 +55,7 @@ final class PropertyAccessResolver
$findClass = $class;
while (true) {
$classDef = ($this->getClassDef)($findClass);
$classDef = $this->compiler->getClassDef($findClass);
if ($classDef === null) {
break;
}
@ -155,6 +147,6 @@ final class PropertyAccessResolver
private function fatal(NodeAbstract $expr, string $message): never
{
($this->fatalError)($expr, $message);
$this->compiler->fatalError($expr, $message);
}
}

@ -2776,7 +2776,7 @@ CODE;
// 如果不是继承自内置类,需要检查父类是否存在,在预处理阶段只需检查了是否继承内置类
// 目前不允许继承自动态加载的自定义类
if ($this->classDef->extends and !$this->classDef->inheritedFromInternalClass) {
$parentClass = $this->getParentClass($class->extends);
$parentClass = $this->getNamespacedClassName($this->parseIdentifier($class->extends));
if ($this->hasClass($parentClass)) {
$parent = $this->getClass($parentClass);
// 父类是 final 无法继承

Loading…
Cancel
Save