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

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

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

Loading…
Cancel
Save