feat(compiler): add support for dynamic class constant fetch expressions

- Implement parseDynamicClassConstFetch method to handle non-name expressions in class constant fetch
- Add materializeDynamicClassConstTarget method to properly evaluate dynamic class targets
- Modify parseClassConstFetch to delegate dynamic cases to new handler
- Remove outdated conditional logic for variable expressions in class
pull/16/head
韩天峰 2 months ago
parent 330d7eb655
commit 3d8eb272a6
  1. 32
      src/CompilerBase.php
  2. 50
      tests/aot/class/dynamic-class-constant-fetch.phpt

@ -7045,6 +7045,10 @@ class CompilerBase implements PropertyAccessContext
protected function parseClassConstFetch(Expr\ClassConstFetch $expr): string
{
if (!$this->isNameExpr($expr->class)) {
return $this->parseDynamicClassConstFetch($expr);
}
$class = $this->parseIdentifier($expr->class);
$self = false;
if ($class === 'self' or $class === 'this_') {
@ -7076,10 +7080,6 @@ class CompilerBase implements PropertyAccessContext
if ($self or $this->isNameExpr($expr->class)) {
return $this->getLiteralString($class);
}
if ($this->isVarExpr($expr->class) and $this->isTypedObject($expr->class->name)) {
return $this->getLiteralString($this->getObjectType($expr->class->name));
}
return 'php::fn::get_class(' . $class . ')';
}
if (($self or $this->isNameExpr($expr->class)) and $this->isIdExpr($expr->name)) {
if ($this->hasClass($class)) {
@ -7101,6 +7101,30 @@ class CompilerBase implements PropertyAccessContext
return Symbol::constant() . '(' . $name . ')';
}
protected function parseDynamicClassConstFetch(Expr\ClassConstFetch $expr): string
{
$const = $this->escapeString($this->parseIdentifier($expr->name));
$target = $this->materializeDynamicClassConstTarget($expr->class);
if ($const === 'class') {
return 'php::fn::get_class(' . $target . ')';
}
$className = '(' . $target . '.isObject() ? php::fn::get_class(' . $target . ') : ' . $target . ')';
return Symbol::constant() . '(php::concat({' . $className . ', "::", ' . $this->getLiteralString($const) . '}))';
}
protected function materializeDynamicClassConstTarget(NodeAbstract $expr): string
{
$this->assertExprCanBeUsedAsValue($expr, 'class constant target');
[$value, $beforeStmts, $afterStmts] = $this->parseExprWithCapturedStmts($expr);
$tmpVar = $this->addTmpVar(self::TYPE_VAR);
$this->appendCapturedStmtLinesToContext($beforeStmts);
$this->context->beforeStmtLines[] = $tmpVar . ' = ' . $value . ';';
$this->appendCapturedStmtLinesToContext($afterStmts);
return $tmpVar;
}
protected function parseThrow(mixed $expr): string
{
if ($this->method === '__destruct') {

@ -0,0 +1,50 @@
--TEST--
dynamic class constant fetch and object ::class
--FILE--
<?php
class DynamicClassConstFirst
{
public const NAME = 'first';
}
class DynamicClassConstSecond
{
public const NAME = 'second';
}
function pick_class(bool $child): string
{
echo 'pick:', $child ? 'child' : 'base', "\n";
return $child ? DynamicClassConstSecond::class : DynamicClassConstFirst::class;
}
function make_dynamic_object(): object
{
echo "object\n";
return new DynamicClassConstSecond();
}
function main(): void
{
$class = DynamicClassConstFirst::class;
var_dump($class::NAME);
var_dump(pick_class(true)::NAME);
var_dump(pick_class(false)::NAME);
$object = new DynamicClassConstSecond();
var_dump($object::NAME);
var_dump(make_dynamic_object()::class);
}
?>
--EXPECT--
string(5) "first"
pick:child
string(6) "second"
pick:base
string(5) "first"
string(6) "second"
object
string(23) "DynamicClassConstSecond"
Loading…
Cancel
Save