From d24c8be04958a9044a1c65a20204eb60e7ba69fc Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Mon, 12 Jan 2026 17:11:31 +0800 Subject: [PATCH] =?UTF-8?q?feat(php):=20=E6=B7=BB=E5=8A=A0=E5=91=BD?= =?UTF-8?q?=E5=90=8D=E7=A9=BA=E9=97=B4=E6=94=AF=E6=8C=81=E5=92=8C=E7=B1=BB?= =?UTF-8?q?=E7=BB=A7=E6=89=BF=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在 ClassDef 中添加 namespace、implements 和 extends 属性 - 实现 getNamespacedName 方法用于获取带命名空间的类名 - 在 CompilerBase 中添加 classes 属性存储类定义 - 修复函数名解析时的大小写转换问题 - 添加对 __CLASS__ 魔术常量的支持 - 移除废弃的 stub 文件常量初始化函数 - 更新命名空间定义的解析逻辑 - 在 Translator 中实现完整的命名空间解析功能 - 添加类继承关系的支持和注册机制 - 实现在全局变量生成中包含类定义入口 - 添加标识符列表解析辅助方法 - 更新示例文件以测试继承功能 --- examples/class/main.php | 2 + examples/class/test.php | 8 +++ src/Php/ClassDef.php | 11 ++++ src/Php/CompilerBase.php | 66 +++++----------------- src/Php/Preprocessor.php | 45 +++++++-------- src/Php/Translator.php | 119 ++++++++++++++++++++++++++++++++++----- src/cpp/main.cc | 2 - 7 files changed, 164 insertions(+), 89 deletions(-) diff --git a/examples/class/main.php b/examples/class/main.php index 04e5dfb7..9538b4b9 100644 --- a/examples/class/main.php +++ b/examples/class/main.php @@ -1,5 +1,7 @@ */ public array $constants = []; + public string $namespace = ''; + public array $implements = []; + public string $extends = ''; + + public function getNamespacedName(): string + { + if ($this->namespace === '') { + return $this->name; + } + return str_replace('\\', '_', $this->namespace . '_' . $this->name); + } } \ No newline at end of file diff --git a/src/Php/CompilerBase.php b/src/Php/CompilerBase.php index e6b1da62..0b80803b 100644 --- a/src/Php/CompilerBase.php +++ b/src/Php/CompilerBase.php @@ -85,6 +85,10 @@ class CompilerBase extends \PhpAot\Core\Translator protected array $useNamespaces = []; protected array $useFunctions = []; protected string $class = ''; + /** + * @var array + */ + protected array $classes = []; protected FunctionDef $functionDef; protected ClassDef $classDef; protected array $globalVars = [ @@ -197,8 +201,6 @@ class CompilerBase extends \PhpAot\Core\Translator { $this->indentLevel = 0; $this->strictTypes = false; - $this->stubFileIncluded = false; - $this->localHeaders = []; } protected function resetNamespace(): void @@ -210,7 +212,7 @@ class CompilerBase extends \PhpAot\Core\Translator protected function getFunctionName(Node $v): string { - $names[] = strtolower($this->parseIdentifier($v->name)); + $names[] = $this->escapeFunction($this->parseIdentifier($v->name)); if ($this->namespace) { $names[] = $this->escapeNamespace($this->namespace); } @@ -602,6 +604,7 @@ class CompilerBase extends \PhpAot\Core\Translator case 'Scalar_MagicConst_Dir': case 'Scalar_MagicConst_Line': case 'Scalar_MagicConst_Function': + case 'Scalar_MagicConst_Class': return $this->parseMagicConst($expr); case 'Scalar_InterpolatedString': return $this->parseInterpolatedString($expr); @@ -1254,9 +1257,9 @@ class CompilerBase extends \PhpAot\Core\Translator */ protected function findNativeFunction(string $fname): string|false { - $possibleFunctionNames = [strtolower($fname),]; + $possibleFunctionNames = [$this->escapeFunction($fname),]; if ($this->namespace) { - $possibleFunctionNames[] = $this->namespace . self::NAMESPACE_SEPARATOR . $fname; + $possibleFunctionNames[] = $this->escapeNamespace($this->namespace) . self::NAMESPACE_SEPARATOR . $fname; } if (isset($this->useFunctions[$fname])) { $possibleFunctionNames[] = $this->escapeNamespace($this->useFunctions[$fname]) . self::NAMESPACE_SEPARATOR . $fname; @@ -1676,52 +1679,6 @@ class CompilerBase extends \PhpAot\Core\Translator return $var . '.instanceOf(' . $this->identifierToStr($expr->class) . ')'; } - protected function parseNamespaceDef(Node $node): string - { - $ns = $this->parseIdentifier($node->name); - $code = ''; - - $this->resetNamespace(); - - if ($this->useCppNamespace) { - $ns = explode('\\', $ns); - $ns = array_filter($ns, function ($v) { - return $v !== ''; - }); - foreach ($ns as $name) { - $code .= 'namespace ' . $name . ' {' . PHP_EOL; - } - $ns_end = str_repeat('}', count($ns)); - $this->namespace = implode('::', $ns); - } else { - $this->namespace = $this->escapeNamespace($node->name->toString()); - $ns_end = ''; - } - - foreach ($node->stmts as $v2) { - $type2 = $v2->getType(); - switch ($type2) { - case 'Stmt_Class': - $code .= $this->parseClass($v2); - break; - case 'Stmt_Const': - $this->parseConstDef($v2) . PHP_EOL; - break; - case 'Stmt_Function': - $code .= $this->parseFunction($v2) . PHP_EOL; - break; - case 'Stmt_Use': - $code .= $this->parseUse($v2) . PHP_EOL; - break; - default: - abort($v2); - } - } - $code .= $ns_end; - $this->resetNamespace(); - return $code; - } - protected function parseCastInt(Node $node): string { return $this->convertIntExpr($this->parseExpr($node->expr)); @@ -1817,6 +1774,11 @@ class CompilerBase extends \PhpAot\Core\Translator return str_replace('\\', self::NAMESPACE_SEPARATOR, strtolower($ns)); } + protected function escapeFunction(string $fname): string + { + return strtolower($fname); + } + protected function escapeClass(string $class): string { return $class; @@ -1932,6 +1894,8 @@ class CompilerBase extends \PhpAot\Core\Translator return (string)$expr->getStartLine(); case 'Scalar_MagicConst_Function': return '"' . $this->escapeString($this->functionDef->name) . '"'; + case 'Scalar_MagicConst_Class': + return '"' . $this->escapeString($this->classDef->name) . '"'; default: abort($expr); } diff --git a/src/Php/Preprocessor.php b/src/Php/Preprocessor.php index f958b8f9..6aac8b3e 100644 --- a/src/Php/Preprocessor.php +++ b/src/Php/Preprocessor.php @@ -26,6 +26,29 @@ class Preprocessor extends CompilerBase $list = $sortedFiles; } + protected function prepareNamespaceDef(Node\Stmt\Namespace_ $node): void + { + $this->resetNamespace(); + $this->namespace = $this->parseIdentifier($node->name); + foreach ($node->stmts as $v2) { + $type2 = $v2->getType(); + switch ($type2) { + case 'Stmt_Class': + $this->prepareClass($v2); + break; + case 'Stmt_Function': + $this->prepareFunction($v2) . PHP_EOL; + break; + case 'Stmt_Use': + case 'Stmt_Const': + break; + default: + abort($v2); + } + } + $this->resetNamespace(); + } + public function prepare(string $file): void { $phpCode = $this->loadFile($file); @@ -84,28 +107,6 @@ class Preprocessor extends CompilerBase } } - protected function prepareNamespaceDef(Node\Stmt\Namespace_ $node): void - { - $this->resetNamespace(); - $this->namespace = $this->escapeNamespace($this->parseIdentifier($node->name)); - foreach ($node->stmts as $v2) { - $type2 = $v2->getType(); - switch ($type2) { - case 'Stmt_Class': - $this->prepareClass($v2); - break; - case 'Stmt_Function': - $this->prepareFunction($v2) . PHP_EOL; - break; - case 'Stmt_Use': - case 'Stmt_Const': - break; - default: - abort($v2); - } - } - $this->resetNamespace(); - } protected function prepareClass(Node\Stmt\Class_ $class): string { diff --git a/src/Php/Translator.php b/src/Php/Translator.php index 837b0354..7308ea7d 100644 --- a/src/Php/Translator.php +++ b/src/Php/Translator.php @@ -91,6 +91,8 @@ class Translator extends Preprocessor public function convert(string $file): string { $phpCode = $this->loadFile($file); + $this->stubFileIncluded = false; + $this->localHeaders = []; while (true) { try { $cppCode = $this->doConvert($phpCode); @@ -125,7 +127,7 @@ class Translator extends Preprocessor $this->parseDeclare($v); break; case 'Stmt_Namespace': - $cppCode .= $this->parseNamespaceDef($v); + $cppCode .= $this->parseNamespace($v); break; case 'Stmt_Class': $cppCode .= $this->parseClass($v); @@ -187,18 +189,34 @@ class Translator extends Preprocessor public function genGlobalVars(string $file): void { + $this->localHeaders = []; $code = $this->genIncludeHeaderFiles(); $lines = []; // 全局变量只能是 var 类型 foreach ($this->globalVars as $name => $type) { $lines[] = self::TYPE_VAR . ' ' . $name . ';'; } + // 类定义 + $lines[] = $this->getIndent() . '// class entries'; + foreach ($this->classes as $classDef) { + $lines[] = $this->getIndent() . 'zend_class_entry *' . self::PREFIX . 'class_entry_' . $classDef->getNamespacedName() . ';'; + } + foreach ($this->classes as $classDef) { + $name = $classDef->getNamespacedName(); + if ($classDef->extends) { + $parentName = 'zend_class_entry *parent_ce'; + } else { + $parentName = ''; + } + $lines[] = 'extern zend_class_entry *' . self::PREFIX . 'register_class_' . $name . '(' . $parentName . ');'; + } $code .= implode(PHP_EOL, $lines) . PHP_EOL; $code .= PHP_EOL; $literalStringsCount = count($this->literalStrings); $code .= 'php::Var ' . self::LITERAL_STRINGS . '[' . $literalStringsCount . '] = {' . PHP_EOL; $this->indentLevel++; + $code .= $this->getIndent() . '// literal strings' . PHP_EOL; foreach ($this->literalStrings as $str => $index) { $code .= $this->getIndent() . 'php::String{ZEND_STRL("' . $this->escapeString($str) . '"), true},' . PHP_EOL; } @@ -215,21 +233,25 @@ class Translator extends Preprocessor $code .= PHP_EOL; $this->indentLevel++; $lines = []; + $lines[] = $this->getIndent() . '// constants'; foreach ($this->nativeConstants as $name => $constant) { $lines[] = $this->getIndent() . $name . ' = ' . $constant->value . ';'; - // 注册到 PHP $lines[] = $this->getIndent() . 'php::define("' . $name . '", ' . $name . ');'; } - $this->indentLevel--; - $code .= $this->genFunction(self::PREFIX . 'init_constant_vars', 'void', [], $lines); - - // 生成全局变量 - $code .= PHP_EOL; - $this->indentLevel++; - $lines = []; + $lines[] = $this->getIndent() . '// global vars'; foreach ($this->globalVars as $name => $type) { $lines[] = $this->getIndent() . $name . ' = php::global("' . $name . '");'; } + $lines[] = $this->getIndent() . '// class entries'; + foreach ($this->classes as $classDef) { + $name = $classDef->getNamespacedName(); + if ($classDef->extends) { + $parentName = self::PREFIX . 'class_entry_' . $classDef->extends; + } else { + $parentName = ''; + } + $lines[] = $this->getIndent() . self::PREFIX . 'class_entry_' . $name . ' = ' . self::PREFIX . 'register_class_' . $name . '(' . $parentName . ');'; + } $this->indentLevel--; $code .= $this->genFunction(self::PREFIX . 'init_global_vars', 'void', [], $lines); @@ -309,17 +331,77 @@ class Translator extends Preprocessor return strtolower($this->parseIdentifier($v->name)); } + protected function parseNamespace(Node\Stmt\Namespace_ $node): string + { + $ns = $this->parseIdentifier($node->name); + $code = ''; + + $this->resetNamespace(); + + if ($this->useCppNamespace) { + $ns = explode('\\', $ns); + $ns = array_filter($ns, function ($v) { + return $v !== ''; + }); + foreach ($ns as $name) { + $code .= 'namespace ' . $name . ' {' . PHP_EOL; + } + $ns_end = str_repeat('}', count($ns)); + $this->namespace = implode('::', $ns); + } else { + $this->namespace = $ns; + $ns_end = ''; + } + + foreach ($node->stmts as $v2) { + $type2 = $v2->getType(); + switch ($type2) { + case 'Stmt_Class': + $code .= $this->parseClass($v2); + break; + case 'Stmt_Const': + $this->parseConstDef($v2) . PHP_EOL; + break; + case 'Stmt_Function': + $code .= $this->parseFunction($v2) . PHP_EOL; + break; + case 'Stmt_Use': + $code .= $this->parseUse($v2) . PHP_EOL; + break; + default: + abort($v2); + } + } + $code .= $ns_end; + $this->resetNamespace(); + return $code; + } + protected function parseClass(Node\Stmt\Class_ $class): string { $this->class = $this->parseIdentifier($class->name); if (!$this->stubFileIncluded) { - shell_exec('php ' . $this->rootPath . '/bin/gen_stub.php -f' . $this->file); + $genStubCmd = PHP_BINARY. ' ' . $this->rootPath . '/bin/gen_stub.php -f ' . $this->file; + shell_exec($genStubCmd); + $this->climate->comment($genStubCmd); $stubFilenameWithoutExtension = str_replace([".stub.php", '.php'], "", $this->file); - $this->localHeaders[] = $this->getArgInfoHeaderFile($stubFilenameWithoutExtension, true); + $headerFile = $this->getArgInfoHeaderFile($stubFilenameWithoutExtension, true); + $this->localHeaders[] = $headerFile; $this->stubFileIncluded = true; } - $this->classDef = new ClassDef(); - $this->classDef->name = $this->class; + + if (!isset($this->classes[$this->class])) { + $this->classDef = new ClassDef(); + $this->classDef->name = $this->class; + if ($class->extends) { + $this->classDef->extends = $this->parseIdentifier($class->extends); + } + $this->classDef->implements = $this->parseIdentifierList($class->implements); + $this->classDef->namespace = $this->namespace; + + $this->classes[$this->class] = $this->classDef; + } + $methodCodes = []; foreach ($class->stmts as $v) { @@ -535,7 +617,16 @@ class Translator extends Preprocessor $name = $this->getMethodName($v); $flags = $v->flags; $methodDef = new MethodDef($name, $flags); - $this->classDef->methods[] = $methodDef; + $this->classDef->methods[$name] = $methodDef; $methodCodes[$name] = $this->parseFunction($v); } + + private function parseIdentifierList(array $implements): array + { + $list = []; + foreach ($implements as $implement) { + $list[] = $this->parseIdentifier($implement); + } + return $list; + } } \ No newline at end of file diff --git a/src/cpp/main.cc b/src/cpp/main.cc index d9bd1a40..3278c9be 100644 --- a/src/cpp/main.cc +++ b/src/cpp/main.cc @@ -12,7 +12,6 @@ void php_main(); extern php::Var argc; extern php::Var argv; -extern void php_init_constant_vars(); extern void php_init_global_vars(); extern void php_unset_global_vars(); @@ -40,7 +39,6 @@ int main(int cpp_argc, char **cpp_argv) { #endif zend_first_try { php_init_global_vars(); - php_init_constant_vars(); php::eval("main();"); } zend_catch {