fix(aot): 修复trait常量继承和数组常量传播问题

- 将trait中的私有常量改为受保护,以便子类能够访问
- 添加数组常量在继承链中的清理机制
- 实现数组常量向子类的传播功能
- 新增测试用例验证trait方法和常量继承行为
pull/1/head
韩天峰 3 months ago
parent 6b65d23ffe
commit 4176e1f4c2
  1. 50
      src/Php/Translator.php
  2. 18
      src/Php/UniversalMethodCall.php
  3. 2
      tests/aot/trait/012.phpt
  4. 41
      tests/aot/trait/013.phpt

@ -628,6 +628,30 @@ CODE;
}
}
}
// Clean up inherited array constants from child classes
foreach ($this->classes as $className => $classDef) {
$ownConstNames = [];
foreach ($classDef->constants as $constant) {
if ($constant->type === self::TYPE_ARRAY) {
$ownConstNames[$constant->name] = true;
}
}
$parentName = $this->escapeClass($classDef->extends);
while ($parentName && isset($this->classes[$parentName])) {
$parentDef = $this->classes[$parentName];
foreach ($parentDef->constants as $constant) {
if ($constant->type === self::TYPE_ARRAY && !isset($ownConstNames[$constant->name])) {
$ownConstNames[$constant->name] = true;
$classNameStr = $this->genCharPtr($classDef->getNamespacedName(false), true);
$classConstStr = $this->genCharPtr($constant->name);
$code .= "php::updateConstant($classNameStr, $classConstStr, php::null);\n";
}
}
$parentName = $this->escapeClass($parentDef->extends);
}
}
$code .= '}' . PHP_EOL . PHP_EOL;
// php_app_clean end
@ -1376,6 +1400,32 @@ CODE;
}
}
}
// Propagate array constants to child classes that don't override them
foreach ($this->classes as $className => $classDef) {
$ownConstNames = [];
foreach ($classDef->constants as $constant) {
if ($constant->type === self::TYPE_ARRAY) {
$ownConstNames[$constant->name] = true;
}
}
$parentName = $this->escapeClass($classDef->extends);
while ($parentName && isset($this->classes[$parentName])) {
$parentDef = $this->classes[$parentName];
foreach ($parentDef->constants as $constant) {
if ($constant->type === self::TYPE_ARRAY && !isset($ownConstNames[$constant->name])) {
$ownConstNames[$constant->name] = true;
$constName = self::PREFIX . $this->getNativeName($constant->name, $parentDef->namespace, $parentDef->name);
$classNameStr = $this->genCharPtr($classDef->getNamespacedName(false), true);
$classConstStr = $this->genCharPtr($constant->name);
$code .= "php::updateConstant($classNameStr, $classConstStr, {$constName});\n";
}
}
$parentName = $this->escapeClass($parentDef->extends);
}
}
return $code;
}

@ -319,7 +319,7 @@ trait UniversalMethodCall
],
];
private const array MUTATING_HANDLERS = ['direct_method_mutate', 'php_fn_ref'];
protected const array MUTATING_HANDLERS = ['direct_method_mutate', 'php_fn_ref'];
protected function detectUniversalMethodReturnType(string $type, string $method): ?string
{
@ -331,7 +331,7 @@ trait UniversalMethodCall
return $ext ? $ext['return_type'] : null;
}
private const array TYPE_EXTENSION_PREFIX = [
protected const array TYPE_EXTENSION_PREFIX = [
CompilerBase::TYPE_INT => 'int',
CompilerBase::TYPE_FLOAT => 'float',
CompilerBase::TYPE_BOOL => 'bool',
@ -349,7 +349,7 @@ trait UniversalMethodCall
return strtolower(preg_replace('/([a-z])([A-Z])/', '$1_$2', $name));
}
private const array TO_CONVERT_FN = [
protected const array TO_CONVERT_FN = [
CompilerBase::TYPE_BIGINT => ['toInt' => 'php::BigInt::toInt', 'toFloat' => 'php::BigInt::toFloat', 'toString' => 'php::BigInt::toString'],
CompilerBase::TYPE_BIGFLOAT => ['toInt' => 'php::BigFloat::toInt', 'toFloat' => 'php::BigFloat::toFloat', 'toString' => 'php::BigFloat::toString'],
CompilerBase::TYPE_DECIMAL => ['toInt' => 'php::Decimal::toInt', 'toFloat' => 'php::Decimal::toFloat', 'toString' => 'php::Decimal::toString'],
@ -485,7 +485,7 @@ trait UniversalMethodCall
return $kwExt ? $kwExt['return_type'] : null;
}
private function buildInternalExtensionMethod(string $type, string $funcName): ?array
protected function buildInternalExtensionMethod(string $type, string $funcName): ?array
{
$ref = Reflection::getFunction($funcName);
if ($ref === null) {
@ -525,7 +525,7 @@ trait UniversalMethodCall
];
}
private function validateExtensionFirstParam(string $type, \PhpAot\Php\Entity\FunctionDef $funcDef): bool
protected function validateExtensionFirstParam(string $type, \PhpAot\Php\Entity\FunctionDef $funcDef): bool
{
if (empty($funcDef->argInfoList)) {
return false;
@ -545,7 +545,7 @@ trait UniversalMethodCall
return $paramType === $type;
}
private function validateInternalExtensionFirstParam(string $type, string $funcName): bool
protected function validateInternalExtensionFirstParam(string $type, string $funcName): bool
{
$param = Reflection::getFunctionParameter($funcName, 0);
if ($param === null) {
@ -628,7 +628,7 @@ trait UniversalMethodCall
* Wrap a non-variable receiver expression in the appropriate type conversion
* so that direct C++ method calls (e.g. .get(), .set()) work on the correct type.
*/
private function wrapUniversalReceiver(string $type, string $expr): string
protected function wrapUniversalReceiver(string $type, string $expr): string
{
$convFns = [
self::TYPE_ARRAY => 'toArray',
@ -643,7 +643,7 @@ trait UniversalMethodCall
return $expr;
}
private function validateUniversalMethodArgs(Node\Expr\MethodCall $expr, string $method, array $def, bool $isVar): void
protected function validateUniversalMethodArgs(Node\Expr\MethodCall $expr, string $method, array $def, bool $isVar): void
{
$argCount = count($expr->args);
$maxArgs = $def['max_args'];
@ -727,7 +727,7 @@ trait UniversalMethodCall
return $cppFunc . '(' . implode(', ', $argExprs) . ')';
}
private function buildReceiverArgs(string $receiver, array $args, int $receiverPos): array
protected function buildReceiverArgs(string $receiver, array $args, int $receiverPos): array
{
$userArgs = [];
foreach ($args as $arg) {

@ -3,7 +3,7 @@ Single Trait with simple trait method
--FILE--
<?php
trait THello {
private const array CONST_ARRAY = [
protected const array CONST_ARRAY = [
'test_fn_1' => ['toInt' => 123,],
'test_fn_2' => ['toInt' => 234, ],
'test_fn_3' => ['toInt' => 333,],

@ -0,0 +1,41 @@
--TEST--
Single Trait with simple trait method
--FILE--
<?php
trait THello {
public const array CONST_ARRAY = [ 'foo' ];
}
class TraitsTest {
use THello;
}
class Test2 extends TraitsTest {
}
class Test3 extends Test2 {
public function foo() {
var_dump(static::CONST_ARRAY);
}
}
function main() {
$o2 = new Test3();
eval("var_dump(Test3::CONST_ARRAY);");
var_dump(Test3::CONST_ARRAY);
$o2->foo();
}
?>
--EXPECT--
array(1) {
[0]=>
string(3) "foo"
}
array(1) {
[0]=>
string(3) "foo"
}
array(1) {
[0]=>
string(3) "foo"
}
Loading…
Cancel
Save