refactor(php): 重构属性访问和赋值类型检查逻辑

- 提取属性定义获取逻辑到 getNativePropertyDef 方法
- 添加原生属性值源设置和检查方法
- 创建 PropertyAssignTypeInfo 类处理属性赋值类型信息
- 统一属性访问类型检查的实现方式
- 简化静态属性访问和动态属性访问的处理流程
- 更新文档中的重构计划说明
pull/4/head
韩天峰 2 months ago
parent 59c9477bf5
commit c34d11c136
  1. 1
      docs/REFACTORING_PLAN.md
  2. 111
      src/Php/CompilerBase.php
  3. 10
      src/Php/Parser/BinaryOpTrait.php

@ -242,6 +242,7 @@
- `CompilerBase::isSameClassName()`、`isSameOrSubclassOf()`、`canAccessProtectedProperty()` 已委托 resolver,避免规则继续扩散。
- 已添加 `prepare/convert/idle` 编译阶段状态;`PropertyAccessResolver` 只能在 convert 阶段创建和使用,避免预处理阶段误用不完整的类表状态。
- `PropertyAccessResolver` 已改为依赖 `PropertyAccessContext` 只读接口,而不是完整依赖 `CompilerBase` 大类。
- 已建立 `PropertyAssignTypeInfo`,抽离 typed property 写入的纯 metadata 计算,包括固定类型属性判断、默认值、runtime typecheck 列表和类型字符串。
- 当前迁移保持生成代码不变,后续阶段再统一 read/write emitter。
验证:

@ -46,6 +46,7 @@ use PhpAot\Php\Platform\Windows;
use PhpAot\Php\Resolver\PropertyAccessContext;
use PhpAot\Php\Resolver\PropertyAccessResult;
use PhpAot\Php\Resolver\PropertyAccessResolver;
use PhpAot\Php\Resolver\PropertyAssignTypeInfo;
use PhpParser\Modifiers;
use PhpParser\Node;
use PhpParser\Node\ArrayItem;
@ -99,6 +100,9 @@ class CompilerBase extends \PhpAot\Core\Translator implements PropertyAccessCont
public const string TYPE_BIGFLOAT = 'php::BigFloat';
public const string TYPE_BOX = 'php::Box';
protected const string NATIVE_PROPERTY_VALUE_VAR = 'var';
protected const string NATIVE_PROPERTY_VALUE_DYNAMIC = 'dynamic';
/**
* Keyword methods (to* builtins) with mandated return types.
* Use findKeywordMethod() for unified lookup including keyword extension methods.
@ -2416,13 +2420,12 @@ class CompilerBase extends \PhpAot\Core\Translator implements PropertyAccessCont
break;
case 'Expr_StaticPropertyFetch':
if ($this->isIdExpr($expr->name)) {
if (!$expr->hasAttribute('nativePropertyDef')) {
if (!$this->getNativePropertyDef($expr)) {
$class = null;
$this->findNativeStaticProperty($expr, $class);
}
if ($expr->hasAttribute('nativePropertyDef')) {
/** @var PropertyDef $def */
$def = $expr->getAttribute('nativePropertyDef');
$def = $this->getNativePropertyDef($expr);
if ($def) {
return $def->type;
}
}
@ -4650,25 +4653,12 @@ class CompilerBase extends \PhpAot\Core\Translator implements PropertyAccessCont
protected function getFixedObjectPropDefaultValue(PropertyDef $def): ?string
{
return match ($def->type) {
self::TYPE_INT => $def->default ?? '0',
self::TYPE_FLOAT => $def->default ?? '0.0',
self::TYPE_BOOL => $def->default ?? 'false',
self::TYPE_STR => $def->default ?? self::TYPE_STR . '()',
self::TYPE_ARRAY => $def->default ?? self::TYPE_ARRAY . '{}',
default => null,
};
return (new PropertyAssignTypeInfo())->getFixedDefaultValue($def);
}
protected function isFixedObjectProp(PropertyDef $def): bool
{
return in_array($def->type, [
self::TYPE_INT,
self::TYPE_FLOAT,
self::TYPE_BOOL,
self::TYPE_STR,
self::TYPE_ARRAY,
], true) && !$def->nullable;
return (new PropertyAssignTypeInfo())->isFixed($def);
}
protected function assertCanAssignObjectProp(Expr\PropertyFetch $left, Expr $right): void
@ -4683,12 +4673,11 @@ class CompilerBase extends \PhpAot\Core\Translator implements PropertyAccessCont
private function assertCanAssignObjectProperty(NodeAbstract $left, Expr $right, string $label): void
{
if (!$left->hasAttribute('nativePropertyDef')) {
$def = $this->getNativePropertyDef($left);
if (!$def) {
return;
}
/** @var PropertyDef $def */
$def = $left->getAttribute('nativePropertyDef');
$propName = $this->parseIdentifier($left->name);
if ($this->isNull($right)) {
@ -4732,12 +4721,11 @@ class CompilerBase extends \PhpAot\Core\Translator implements PropertyAccessCont
protected function wrapObjectPropertyAssignTypeCheck(NodeAbstract $left, Expr $right, string $rightExpr): string
{
if (!$left->hasAttribute('nativePropertyDef')) {
$def = $this->getNativePropertyDef($left);
if (!$def) {
return $rightExpr;
}
/** @var PropertyDef $def */
$def = $left->getAttribute('nativePropertyDef');
$typeCheck = $this->getObjectPropertyAssignTypeCheck($def);
if (empty($typeCheck)) {
return $rightExpr;
@ -4776,26 +4764,15 @@ class CompilerBase extends \PhpAot\Core\Translator implements PropertyAccessCont
private function getObjectPropertyAssignTypeCheck(PropertyDef $def): array
{
if (!empty($def->typeCheck)) {
return $def->typeCheck;
}
if ($def->type !== self::TYPE_OBJECT || $def->class === '') {
return [];
}
$check = [];
if ($def->nullable) {
$check[] = ['kind' => 'isNull'];
}
$check[] = ['kind' => 'instanceof', 'class' => $def->class];
return $check;
return (new PropertyAssignTypeInfo())->getRuntimeTypeCheck($def);
}
private function getObjectPropertyTypeCheckDisplayName(NodeAbstract $left): string
{
$propName = $this->parseIdentifier($left->name);
if ($left->hasAttribute('nativeClassDef')) {
$class = $left->getAttribute('nativeClassDef')->getNamespacedName(false);
$classDef = $this->getNativePropertyClassDef($left);
if ($classDef) {
$class = $classDef->getNamespacedName(false);
return $class . '::$' . $propName;
}
@ -4808,13 +4785,7 @@ class CompilerBase extends \PhpAot\Core\Translator implements PropertyAccessCont
private function getObjectPropertyTypeCheckTypeString(PropertyDef $def): string
{
if ($def->typeStr !== '') {
return $def->typeStr;
}
if ($def->class !== '') {
return ($def->nullable ? '?' : '') . $def->class;
}
return $def->type;
return (new PropertyAssignTypeInfo())->getTypeString($def);
}
protected function parseUnset(Node\Stmt\Unset_ $node): string
@ -4843,9 +4814,8 @@ class CompilerBase extends \PhpAot\Core\Translator implements PropertyAccessCont
$restoreDefault = null;
if ($this->isIdExpr($var->name)) {
$propertyId = $this->getPropertyIdentifier($var, $var->var, $var->name);
if ($var->hasAttribute('nativePropertyDef')) {
/** @var PropertyDef $def */
$def = $var->getAttribute('nativePropertyDef');
$def = $this->getNativePropertyDef($var);
if ($def) {
if ($this->isFixedObjectProp($def)) {
$restoreDefault = $this->getFixedObjectPropDefaultValue($def);
if ($restoreDefault === null) {
@ -4922,11 +4892,8 @@ class CompilerBase extends \PhpAot\Core\Translator implements PropertyAccessCont
}
$objectVar = $objectName;
$getProperty = $objectVar . '.attr(' . $id . ', ' . $this->escapeBool($update) . ')';
if ($expr->hasAttribute('nativePropertyDef') and $this->nativeTypes) {
/**
* @var PropertyDef $def
*/
$def = $expr->getAttribute('nativePropertyDef');
$def = $this->getNativePropertyDef($expr);
if ($def and $this->nativeTypes) {
$propName = $this->parseIdentifier($property);
$propVar = $this->getObjectPropVarName($objectVar, $propName);
if ($objectVar === 'this_') {
@ -4940,15 +4907,20 @@ class CompilerBase extends \PhpAot\Core\Translator implements PropertyAccessCont
];
}
$expr->setAttribute('nativePropertyVar', $propVar);
$this->setNativePropertyValueSource($expr, self::NATIVE_PROPERTY_VALUE_VAR);
return $propVar;
}
} elseif ($this->canHoistStableObjectProp($objectVar, $propName)) {
// SSA-stable object: lazily create reference at first access point
$result = $this->hoistStableObjectProp($objectVar, $propName, $id, $def->type);
$expr->setAttribute('nativePropertyVar', $result);
$this->setNativePropertyValueSource($expr, self::NATIVE_PROPERTY_VALUE_VAR);
return $result;
}
}
if ($def) {
$this->setNativePropertyValueSource($expr, self::NATIVE_PROPERTY_VALUE_DYNAMIC);
}
return $getProperty;
}
@ -5884,14 +5856,33 @@ class CompilerBase extends \PhpAot\Core\Translator implements PropertyAccessCont
return $expr->hasAttribute('nativeProperty');
}
protected function getNativePropertyDef(NodeAbstract $expr): ?PropertyDef
{
return $expr->hasAttribute('nativePropertyDef') ? $expr->getAttribute('nativePropertyDef') : null;
}
protected function getNativePropertyClassDef(NodeAbstract $expr): ?ClassDef
{
return $expr->hasAttribute('nativeClassDef') ? $expr->getAttribute('nativeClassDef') : null;
}
protected function setNativePropertyValueSource(NodeAbstract $expr, string $source): void
{
$expr->setAttribute('nativePropertyValueSource', $source);
}
protected function isNativePropertyTypedValue(NodeAbstract $expr): bool
{
return $expr->getAttribute('nativePropertyValueSource') === self::NATIVE_PROPERTY_VALUE_VAR;
}
protected function parseNativeStaticPropertyFetch(Expr\StaticPropertyFetch $expr): string|bool
{
$class = null;
$nativeProp = $this->findNativeStaticProperty($expr, $class);
if ($nativeProp) {
if ($this->nativeTypes && $expr->hasAttribute('nativePropertyDef')) {
/** @var PropertyDef $def */
$def = $expr->getAttribute('nativePropertyDef');
$def = $this->getNativePropertyDef($expr);
if ($this->nativeTypes && $def) {
$info = $this->getHoistedObjectPropInfo($def->type);
$propName = $this->parseIdentifier($expr->name);
$refVar = '_static_' . str_replace('\\', '_', $class) . '_' . $propName;
@ -5907,6 +5898,7 @@ class CompilerBase extends \PhpAot\Core\Translator implements PropertyAccessCont
];
}
$helper = $def->type === self::TYPE_FLOAT ? 'php_aot_static_float_ref' : 'php_aot_static_int_ref';
$this->setNativePropertyValueSource($expr, self::NATIVE_PROPERTY_VALUE_VAR);
return $helper . '(' . $refVar . ')';
}
@ -5919,13 +5911,16 @@ class CompilerBase extends \PhpAot\Core\Translator implements PropertyAccessCont
'kind' => $info['kind'],
];
}
$this->setNativePropertyValueSource($expr, self::NATIVE_PROPERTY_VALUE_VAR);
return $refVar;
}
if ($this->isNativePropertyAccess($expr)) {
$classPtr = $this->getClassEntryPtr($class);
$this->setNativePropertyValueSource($expr, self::NATIVE_PROPERTY_VALUE_DYNAMIC);
return Symbol::getStaticProperty() . '(' . $classPtr . ', ' . $nativeProp . ')';
} else {
$this->setNativePropertyValueSource($expr, self::NATIVE_PROPERTY_VALUE_DYNAMIC);
return $nativeProp;
}
}

@ -209,16 +209,18 @@ trait BinaryOpTrait
if (isset($this->context->objectProps[$nativePropertyVar])) {
return $this->context->objectProps[$nativePropertyVar]['type'];
}
if (!str_contains($nativePropertyVar, '.attr(') && $expr->hasAttribute('nativePropertyDef')) {
return $expr->getAttribute('nativePropertyDef')->type;
$def = $this->getNativePropertyDef($expr);
if ($def && $this->isNativePropertyTypedValue($expr)) {
return $def->type;
}
}
return self::TYPE_VAR;
}
if ($expr instanceof Expr\StaticPropertyFetch) {
if ($expr->hasAttribute('nativePropertyDef') && !str_contains($value, 'getStaticProperty')) {
return $expr->getAttribute('nativePropertyDef')->type;
$def = $this->getNativePropertyDef($expr);
if ($def && $this->isNativePropertyTypedValue($expr)) {
return $def->type;
}
return self::TYPE_VAR;
}

Loading…
Cancel
Save