From d90bf8a5fda4be14a2649d562d703b83bf07336f Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Fri, 16 Jan 2026 16:41:20 +0800 Subject: [PATCH] =?UTF-8?q?feat(compiler):=20=E6=B7=BB=E5=8A=A0=E7=B1=BB?= =?UTF-8?q?=E5=B8=B8=E9=87=8F=E6=94=AF=E6=8C=81=E5=92=8C=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=E7=94=9F=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 引入 NodeAbstract 类型用于对象转换 - 移除 Stmt_Nop 语句的空注释生成 - 为 Foreach 循环体添加前置语句解析 - 简化对象类型检查逻辑 - 添加多个类常量测试用例 - 在预处理器和翻译器中处理 Stmt_Nop 语句类型 - 完善类常量声明、继承和访问功能 --- src/Php/CompilerBase.php | 20 ++-- src/Php/Preprocessor.php | 1 + src/Php/Translator.php | 2 + tests/core/classes/constants_basic_001.phpt | 88 +++++++++++++++ tests/core/classes/constants_basic_002.phpt | 33 ++++++ tests/core/classes/constants_basic_003.inc | 5 + tests/core/classes/constants_basic_003.phpt | 33 ++++++ tests/core/classes/constants_basic_004.phpt | 102 ++++++++++++++++++ tests/core/classes/constants_basic_005.phpt | 19 ++++ tests/core/classes/constants_basic_006.phpt | 44 ++++++++ .../core/classes/constants_comments_001.phpt | 37 +++++++ 11 files changed, 373 insertions(+), 11 deletions(-) create mode 100644 tests/core/classes/constants_basic_001.phpt create mode 100644 tests/core/classes/constants_basic_002.phpt create mode 100644 tests/core/classes/constants_basic_003.inc create mode 100644 tests/core/classes/constants_basic_003.phpt create mode 100644 tests/core/classes/constants_basic_004.phpt create mode 100644 tests/core/classes/constants_basic_005.phpt create mode 100644 tests/core/classes/constants_basic_006.phpt create mode 100644 tests/core/classes/constants_comments_001.phpt diff --git a/src/Php/CompilerBase.php b/src/Php/CompilerBase.php index bd150e2d..5e002d75 100644 --- a/src/Php/CompilerBase.php +++ b/src/Php/CompilerBase.php @@ -12,6 +12,7 @@ use PhpParser\Error; use PhpParser\Node\NullableType; use PhpParser\Node\Scalar\MagicConst; use PhpParser\Node\Stmt\Foreach_; +use PhpParser\NodeAbstract; use PhpParser\NodeFinder; use PhpParser\NodeTraverser; use PhpParser\Parser; @@ -482,7 +483,7 @@ class CompilerBase extends \PhpAot\Core\Translator $result = $this->parseContinue($v); break; case 'Stmt_Nop': - $result = '// pass'; + $result = ''; break; case 'Stmt_Global': $result = $this->parseGlobal($v); @@ -2027,9 +2028,13 @@ class CompilerBase extends \PhpAot\Core\Translator $valueVar = $this->parseIdentifier($node->valueVar); $code .= self::TYPE_VAR . ' ' . $this->getIndent() . ' ' . $valueVar . ' = iter.value();' . PHP_EOL; } - $code .= $this->parseStmts($node->stmts); + + $body = $this->parseStmts($node->stmts); $this->indentLevel--; + $code .= $this->parseBeforeStmtLines() . PHP_EOL; + $code .= $body . PHP_EOL; + $code .= $this->getIndent() . '}'; return $code; @@ -2272,17 +2277,10 @@ class CompilerBase extends \PhpAot\Core\Translator return $expr; } - protected function convertToObject(Node $object): string + protected function convertToObject(NodeAbstract $object): string { $id = $this->parseIdentifier($object); - // TODO 这里的逻辑是不是错误 - if ($this->isVarExpr($object) and !$this->hasVar($id)) { - $this->addLocalVar($id, self::TYPE_OBJECT); - return $id; - } - - $type = $this->getVarType($id); - if ($type === self::TYPE_OBJECT) { + if ($this->isVarExpr($object) and $this->getVarType($id) === self::TYPE_OBJECT) { return $id; } diff --git a/src/Php/Preprocessor.php b/src/Php/Preprocessor.php index 337f9810..d5a31070 100644 --- a/src/Php/Preprocessor.php +++ b/src/Php/Preprocessor.php @@ -149,6 +149,7 @@ class Preprocessor extends CompilerBase switch ($type) { case 'Stmt_ClassConst': case 'Stmt_Property': + case 'Stmt_Nop': break; case 'Stmt_ClassMethod': $code .= $this->prepareFunction($v) . PHP_EOL; diff --git a/src/Php/Translator.php b/src/Php/Translator.php index 92049279..0bacbff6 100644 --- a/src/Php/Translator.php +++ b/src/Php/Translator.php @@ -542,6 +542,8 @@ class Translator extends Preprocessor case 'Stmt_ClassMethod': $this->parseClassMethod($v, $methodCodes); break; + case 'Stmt_Nop': + break; default: abort($v); } diff --git a/tests/core/classes/constants_basic_001.phpt b/tests/core/classes/constants_basic_001.phpt new file mode 100644 index 00000000..5f438078 --- /dev/null +++ b/tests/core/classes/constants_basic_001.phpt @@ -0,0 +1,88 @@ +--TEST-- +Class constant declarations +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +Warning: Undefined variable $undef in %s on line %d + +Attempt to access various kinds of class constants: +int(1) +float(1.5) +int(1) +float(1.5) +int(-1) +float(-1.5) +int(13) +string(%d) "%s" +string(1) "C" +string(0) "" +string(0) "" +int(1234) +int(456) +NULL +string(6) "hello1" +string(6) "hello2" +string(6) "hello2" +string(6) "hello2" + +Expecting fatal error: + +Fatal error: Uncaught Error: Undefined constant C::c19 in %s:%d +Stack trace: +#0 {main} + thrown in %s on line %d diff --git a/tests/core/classes/constants_basic_002.phpt b/tests/core/classes/constants_basic_002.phpt new file mode 100644 index 00000000..bbe33f8d --- /dev/null +++ b/tests/core/classes/constants_basic_002.phpt @@ -0,0 +1,33 @@ +--TEST-- +Basic class support - defining and reading a class constant. +--FILE-- +myConst); + + echo "\nClass constant not visible in object var_dump.\n"; + var_dump($myInstance); +} +?> +--EXPECTF-- +Read class constant. +string(5) "hello" + +Fail to read class constant from instance. + +Warning: Undefined property: aclass::$myConst in %s on line %d +NULL + +Class constant not visible in object var_dump. +object(aclass)#%d (0) { +} diff --git a/tests/core/classes/constants_basic_003.inc b/tests/core/classes/constants_basic_003.inc new file mode 100644 index 00000000..c7439f46 --- /dev/null +++ b/tests/core/classes/constants_basic_003.inc @@ -0,0 +1,5 @@ + diff --git a/tests/core/classes/constants_basic_003.phpt b/tests/core/classes/constants_basic_003.phpt new file mode 100644 index 00000000..bf33bbed --- /dev/null +++ b/tests/core/classes/constants_basic_003.phpt @@ -0,0 +1,33 @@ +--TEST-- +Ensure class properties and constants can be defined in terms of constants that are not known at compile time. +--SKIPIF-- + +--FILE-- + +--EXPECT-- +string(12) "hello from A" +string(12) "hello from C" +string(12) "hello from A" +string(12) "hello from C" diff --git a/tests/core/classes/constants_basic_004.phpt b/tests/core/classes/constants_basic_004.phpt new file mode 100644 index 00000000..1a678224 --- /dev/null +++ b/tests/core/classes/constants_basic_004.phpt @@ -0,0 +1,102 @@ +--TEST-- +Test properties with array default values using class constants as keys and values. +--SKIPIF-- + +--FILE-- + B::VALUE); + public $a_x = array(B::KEY => B::VALUE); + } + + class B + { + const KEY = "key"; + const VALUE = "value"; + + // Static and instance array using class constants with self + public static $sa_b = array(self::KEY => self::VALUE); + public $a_b = array(self::KEY => self::VALUE); + } + + class C extends B + { + // Static and instance array using class constants with parent + public static $sa_c_parent = array(parent::KEY => parent::VALUE); + public $a_c_parent = array(parent::KEY => parent::VALUE); + + // Static and instance array using class constants with self (constants should be inherited) + public static $sa_c_self = array(self::KEY => self::VALUE); + public $a_c_self = array(self::KEY => self::VALUE); + + // Should also include inherited properties from B. + } + +function main() { + echo "\nStatic properties:\n"; + var_dump(X::$sa_x, B::$sa_b, C::$sa_b, C::$sa_c_parent, C::$sa_c_self); + + echo "\nInstance properties:\n"; + $x = new x; + $b = new B; + $c = new C; + var_dump($x, $b, $c); +} +?> +--EXPECTF-- +Static properties: +array(1) { + ["key"]=> + string(5) "value" +} +array(1) { + ["key"]=> + string(5) "value" +} +array(1) { + ["key"]=> + string(5) "value" +} +array(1) { + ["key"]=> + string(5) "value" +} +array(1) { + ["key"]=> + string(5) "value" +} + +Instance properties: +object(X)#%d (1) { + ["a_x"]=> + array(1) { + ["key"]=> + string(5) "value" + } +} +object(B)#%d (1) { + ["a_b"]=> + array(1) { + ["key"]=> + string(5) "value" + } +} +object(C)#%d (3) { + ["a_b"]=> + array(1) { + ["key"]=> + string(5) "value" + } + ["a_c_parent"]=> + array(1) { + ["key"]=> + string(5) "value" + } + ["a_c_self"]=> + array(1) { + ["key"]=> + string(5) "value" + } +} diff --git a/tests/core/classes/constants_basic_005.phpt b/tests/core/classes/constants_basic_005.phpt new file mode 100644 index 00000000..d6d1cce1 --- /dev/null +++ b/tests/core/classes/constants_basic_005.phpt @@ -0,0 +1,19 @@ +--TEST-- +Test constants with default values based on other constants. +--SKIPIF-- + +--FILE-- + +--EXPECT-- +string(5) "hello" +string(5) "hello" diff --git a/tests/core/classes/constants_basic_006.phpt b/tests/core/classes/constants_basic_006.phpt new file mode 100644 index 00000000..eb2bcf98 --- /dev/null +++ b/tests/core/classes/constants_basic_006.phpt @@ -0,0 +1,44 @@ +--TEST-- +Ensure class constants are not evaluated when a class is looked up to resolve inheritance during runtime. +--SKIPIF-- + +--FILE-- + D::V, E::A => K); +} +class E extends D +{ + const A = "hello"; +} +function main() { + eval('class D extends C { const V = \'test\'; }'); + + var_dump(C::X, C::$a, D::X, D::$a, E::X, E::$a); +} +?> +--EXPECT-- +string(5) "hello" +array(2) { + ["nasty"]=> + string(4) "test" + ["hello"]=> + string(5) "nasty" +} +string(5) "hello" +array(2) { + ["nasty"]=> + string(4) "test" + ["hello"]=> + string(5) "nasty" +} +string(5) "hello" +array(2) { + ["nasty"]=> + string(4) "test" + ["hello"]=> + string(5) "nasty" +} diff --git a/tests/core/classes/constants_comments_001.phpt b/tests/core/classes/constants_comments_001.phpt new file mode 100644 index 00000000..3a5db3f7 --- /dev/null +++ b/tests/core/classes/constants_comments_001.phpt @@ -0,0 +1,37 @@ +--TEST-- +Class constants and doc comments +--INI-- +opcache.save_comments=1 +--FILE-- +getReflectionConstants() as $rc) { + echo $rc->getName() . " : " . $rc->getValue() . "\n"; + } +} +?> +--EXPECT-- +X1 : 1 +X2 : 2 +X3 : 3 +Y1 : 1 +Y2 : 2 +Y3 : 3 +