fix(stub): normalize special class constant references

pull/37/head
韩天峰 1 month ago
parent edb726efa6
commit 1527a44854
  1. 42
      src/gen_stub.php
  2. 90
      tests/compiler/self-class/003.phpt

@ -50,6 +50,18 @@ function getClassConstFetchClassName(Expr\ClassConstFetch $expr): string
return $className; 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[] * @return FileInfo[]
*/ */
@ -2325,7 +2337,11 @@ class EvaluatedValue
} }
if ($expr instanceof Expr\ClassConstFetch) { 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 { } else {
$originatingConstName = new ConstName($expr->name->getAttribute('namespacedName'), $expr->name->toString()); $originatingConstName = new ConstName($expr->name->getAttribute('namespacedName'), $expr->name->toString());
} }
@ -2361,26 +2377,12 @@ class EvaluatedValue
if (strcasecmp($constName, 'class') === 0) { if (strcasecmp($constName, 'class') === 0) {
// `::class` is a compile-time magic constant that resolves to the // `::class` is a compile-time magic constant that resolves to the
// fully qualified class name of the referenced class. // fully qualified class name of the referenced class.
$className = getClassConstFetchClassName($expr); return ltrim(
if (strcasecmp($className, 'self') === 0 || strcasecmp($className, 'static') === 0) { resolveClassConstFetchClassName($expr, ClassInfo::$currentClass),
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);
} }
$class = resolveClassConstFetchClassName($expr, ClassInfo::$currentClass);
$fqcnName = ltrim($class, '\\') . "::" . $constName; $fqcnName = ltrim($class, '\\') . "::" . $constName;
if (isset($allConstInfos[$fqcnName])) { if (isset($allConstInfos[$fqcnName])) {
return $allConstInfos[$fqcnName]->getValue($allConstInfos)->value; return $allConstInfos[$fqcnName]->getValue($allConstInfos)->value;

@ -1,41 +1,81 @@
--TEST-- --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-- --FILE--
<?php <?php
declare(strict_types=1); declare(strict_types=1);
class A namespace StubConstRef {
{ class Base
const A = 1; {
const B = 2; const INHERITED = 1;
} const OVERRIDDEN = 2;
}
class Child extends Base
{
const LOCAL = 10;
const OVERRIDDEN = 20;
const SELF_LOCAL = self::LOCAL;
const SELF_INHERITED = self::INHERITED;
const PARENT_VALUE = parent::OVERRIDDEN;
public int $selfLocal = self::LOCAL;
public int $selfInherited = self::INHERITED;
public int $parentValue = parent::OVERRIDDEN;
public int $explicitValue = \StubConstRef\Base::INHERITED;
public function defaults(
int $self = self::LOCAL,
int $parent = parent::OVERRIDDEN
): array {
return [$self, $parent];
}
public function runtimeStatic(): int
{
return static::OVERRIDDEN;
}
}
class B extends A class GrandChild extends Child
{ {
// self:: referencing an inherited constant. const OVERRIDDEN = 30;
// Bug: self::A was incorrectly resolved as B::B::A and the lookup failed. }
public $a = self::A;
// self:: referencing a constant declared in the same class.
const LOCAL = 10;
public $local = self::LOCAL;
// parent:: referencing a parent constant.
public $b = parent::A;
// explicit class name.
public $c = A::B;
} }
function main() namespace {
{ function main()
$test = new B; {
var_dump($test->a); $test = new StubConstRef\Child;
var_dump($test->local); var_dump(
var_dump($test->b); StubConstRef\Child::SELF_LOCAL,
var_dump($test->c); 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-- --EXPECT--
int(10)
int(1) int(1)
int(2)
int(10) int(10)
int(1) int(1)
int(2) int(2)
int(1)
array(2) {
[0]=>
int(10)
[1]=>
int(2)
}
int(30)

Loading…
Cancel
Save