From 3d8eb272a6057122e38ac47179754acada6afd11 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Fri, 10 Jul 2026 14:51:08 +0800 Subject: [PATCH] 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 --- src/CompilerBase.php | 32 ++++++++++-- .../class/dynamic-class-constant-fetch.phpt | 50 +++++++++++++++++++ 2 files changed, 78 insertions(+), 4 deletions(-) create mode 100644 tests/aot/class/dynamic-class-constant-fetch.phpt diff --git a/src/CompilerBase.php b/src/CompilerBase.php index bc8feb77..f0cdf3c6 100644 --- a/src/CompilerBase.php +++ b/src/CompilerBase.php @@ -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') { diff --git a/tests/aot/class/dynamic-class-constant-fetch.phpt b/tests/aot/class/dynamic-class-constant-fetch.phpt new file mode 100644 index 00000000..c4ba19b0 --- /dev/null +++ b/tests/aot/class/dynamic-class-constant-fetch.phpt @@ -0,0 +1,50 @@ +--TEST-- +dynamic class constant fetch and object ::class +--FILE-- + +--EXPECT-- +string(5) "first" +pick:child +string(6) "second" +pick:base +string(5) "first" +string(6) "second" +object +string(23) "DynamicClassConstSecond"