From 1527a44854613b9527d33894889f97e5f5aae4f7 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Fri, 24 Jul 2026 18:31:23 +0800 Subject: [PATCH] fix(stub): normalize special class constant references --- src/gen_stub.php | 42 +++++++------- tests/compiler/self-class/003.phpt | 90 +++++++++++++++++++++--------- 2 files changed, 87 insertions(+), 45 deletions(-) diff --git a/src/gen_stub.php b/src/gen_stub.php index 6e3bee06..9224f5fb 100755 --- a/src/gen_stub.php +++ b/src/gen_stub.php @@ -50,6 +50,18 @@ function getClassConstFetchClassName(Expr\ClassConstFetch $expr): string return $className; } +function resolveClassConstFetchClassName(Expr\ClassConstFetch $expr, string $currentClass): string +{ + $className = getClassConstFetchClassName($expr); + if (strcasecmp($className, 'self') === 0 || strcasecmp($className, 'static') === 0) { + return $currentClass; + } + if (strcasecmp($className, 'parent') === 0) { + return getTranslator()->getParentClass($currentClass); + } + return $className; +} + /** * @return FileInfo[] */ @@ -2325,7 +2337,11 @@ class EvaluatedValue } if ($expr instanceof Expr\ClassConstFetch) { - $originatingConstName = new ClassConstName($expr->class, $expr->name->toString()); + $className = resolveClassConstFetchClassName($expr, ClassInfo::$currentClass); + $originatingConstName = new ClassConstName( + new Name(ltrim($className, '\\')), + $expr->name->toString() + ); } else { $originatingConstName = new ConstName($expr->name->getAttribute('namespacedName'), $expr->name->toString()); } @@ -2361,26 +2377,12 @@ class EvaluatedValue if (strcasecmp($constName, 'class') === 0) { // `::class` is a compile-time magic constant that resolves to the // fully qualified class name of the referenced class. - $className = getClassConstFetchClassName($expr); - if (strcasecmp($className, 'self') === 0 || strcasecmp($className, 'static') === 0) { - return ClassInfo::$currentClass; - } - if (strcasecmp($className, 'parent') === 0) { - return getTranslator()->getParentClass(ClassInfo::$currentClass); - } - return ltrim($className, '\\'); - } - $class = getClassConstFetchClassName($expr); - // Resolve the special class-name keywords to concrete classes. - // Previously `self` was passed as both the class and a - // `ClassName::` name prefix (yielding `B::B::A`), and `parent` / - // `static` were passed verbatim (yielding `parent::A`), so the - // constant lookup always failed. - if (strcasecmp($class, 'self') === 0 || strcasecmp($class, 'static') === 0) { - $class = ClassInfo::$currentClass; - } elseif (strcasecmp($class, 'parent') === 0) { - $class = getTranslator()->getParentClass(ClassInfo::$currentClass); + return ltrim( + resolveClassConstFetchClassName($expr, ClassInfo::$currentClass), + '\\' + ); } + $class = resolveClassConstFetchClassName($expr, ClassInfo::$currentClass); $fqcnName = ltrim($class, '\\') . "::" . $constName; if (isset($allConstInfos[$fqcnName])) { return $allConstInfos[$fqcnName]->getValue($allConstInfos)->value; diff --git a/tests/compiler/self-class/003.phpt b/tests/compiler/self-class/003.phpt index 86bd825e..40300468 100644 --- a/tests/compiler/self-class/003.phpt +++ b/tests/compiler/self-class/003.phpt @@ -1,41 +1,81 @@ --TEST-- -Class constant referenced via self:: / parent:: / ClassName:: as a property default value +Class constants referenced through self, parent, explicit names and runtime static --FILE-- a); - var_dump($test->local); - var_dump($test->b); - var_dump($test->c); +namespace { + function main() + { + $test = new StubConstRef\Child; + var_dump( + StubConstRef\Child::SELF_LOCAL, + StubConstRef\Child::SELF_INHERITED, + StubConstRef\Child::PARENT_VALUE + ); + var_dump( + $test->selfLocal, + $test->selfInherited, + $test->parentValue, + $test->explicitValue + ); + var_dump($test->defaults()); + var_dump((new StubConstRef\GrandChild)->runtimeStatic()); + } } ?> --EXPECT-- +int(10) int(1) +int(2) int(10) int(1) int(2) +int(1) +array(2) { + [0]=> + int(10) + [1]=> + int(2) +} +int(30)