From edcbcf7cff544f84da1c63fd9638467f3d8c3114 Mon Sep 17 00:00:00 2001 From: Yurun Date: Thu, 23 Jul 2026 21:38:56 +0800 Subject: [PATCH 1/2] =?UTF-8?q?fix(stub):=20=E4=BF=AE=E5=A4=8D=20self/pare?= =?UTF-8?q?nt/static=20=E7=B1=BB=E5=B8=B8=E9=87=8F=E5=BC=95=E7=94=A8?= =?UTF-8?q?=E8=A7=A3=E6=9E=90=E5=A4=B1=E8=B4=A5=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/gen_stub.php | 29 +++++++++++---------- tests/compiler/self-class/003.phpt | 41 ++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 13 deletions(-) create mode 100644 tests/compiler/self-class/003.phpt diff --git a/src/gen_stub.php b/src/gen_stub.php index 5b65c0c5..9f09e78f 100755 --- a/src/gen_stub.php +++ b/src/gen_stub.php @@ -2368,20 +2368,23 @@ class EvaluatedValue return ltrim($className, '\\'); } $class = getClassConstFetchClassName($expr); - if ($class === 'self') { - $constName = ClassInfo::$currentClass . "::" . $constName; - if (isset($allConstInfos[$constName])) { - return $allConstInfos[$constName]->getValue($allConstInfos)->value; - } else { - return normalizeConstExprValue( - getTranslator()->getClassConstValue($expr, ClassInfo::$currentClass, $constName) - ); - } - } else { - return normalizeConstExprValue( - getTranslator()->getClassConstValue($expr, $class, $constName, ClassInfo::$currentClass) - ); + // 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); + } + $fqcnName = ltrim($class, '\\') . "::" . $constName; + if (isset($allConstInfos[$fqcnName])) { + return $allConstInfos[$fqcnName]->getValue($allConstInfos)->value; } + return normalizeConstExprValue( + getTranslator()->getClassConstValue($expr, $class, $constName, ClassInfo::$currentClass) + ); } else { $constName = $expr->name->__toString(); if (strtolower($constName) === "unknown") { diff --git a/tests/compiler/self-class/003.phpt b/tests/compiler/self-class/003.phpt new file mode 100644 index 00000000..86bd825e --- /dev/null +++ b/tests/compiler/self-class/003.phpt @@ -0,0 +1,41 @@ +--TEST-- +Class constant referenced via self:: / parent:: / ClassName:: as a property default value +--FILE-- +a); + var_dump($test->local); + var_dump($test->b); + var_dump($test->c); +} +?> +--EXPECT-- +int(1) +int(10) +int(1) +int(2) From 1527a44854613b9527d33894889f97e5f5aae4f7 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Fri, 24 Jul 2026 18:31:23 +0800 Subject: [PATCH 2/2] 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)