feat(php): 添加命名空间支持和类继承功能

- 在 ClassDef 中添加 namespace、implements 和 extends 属性
- 实现 getNamespacedName 方法用于获取带命名空间的类名
- 在 CompilerBase 中添加 classes 属性存储类定义
- 修复函数名解析时的大小写转换问题
- 添加对 __CLASS__ 魔术常量的支持
- 移除废弃的 stub 文件常量初始化函数
- 更新命名空间定义的解析逻辑
- 在 Translator 中实现完整的命名空间解析功能
- 添加类继承关系的支持和注册机制
- 实现在全局变量生成中包含类定义入口
- 添加标识符列表解析辅助方法
- 更新示例文件以测试继承功能
pull/1/head
韩天峰 8 months ago
parent 64d18db7e3
commit d24c8be049
  1. 2
      examples/class/main.php
  2. 8
      examples/class/test.php
  3. 11
      src/Php/ClassDef.php
  4. 66
      src/Php/CompilerBase.php
  5. 45
      src/Php/Preprocessor.php
  6. 119
      src/Php/Translator.php
  7. 2
      src/cpp/main.cc

@ -1,5 +1,7 @@
<?php <?php
const VERSION = '1.0.1';
function main() function main()
{ {
var_dump("hello"); var_dump("hello");

@ -26,3 +26,11 @@ class Test
} }
} }
class Test2 extends Test{
function fun(string $name)
{
var_dump(__CLASS__);
var_dump($name);
}
}

@ -17,4 +17,15 @@ class ClassDef
* @var array<ConstantDef> * @var array<ConstantDef>
*/ */
public array $constants = []; 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);
}
} }

@ -85,6 +85,10 @@ class CompilerBase extends \PhpAot\Core\Translator
protected array $useNamespaces = []; protected array $useNamespaces = [];
protected array $useFunctions = []; protected array $useFunctions = [];
protected string $class = ''; protected string $class = '';
/**
* @var array<ClassDef>
*/
protected array $classes = [];
protected FunctionDef $functionDef; protected FunctionDef $functionDef;
protected ClassDef $classDef; protected ClassDef $classDef;
protected array $globalVars = [ protected array $globalVars = [
@ -197,8 +201,6 @@ class CompilerBase extends \PhpAot\Core\Translator
{ {
$this->indentLevel = 0; $this->indentLevel = 0;
$this->strictTypes = false; $this->strictTypes = false;
$this->stubFileIncluded = false;
$this->localHeaders = [];
} }
protected function resetNamespace(): void protected function resetNamespace(): void
@ -210,7 +212,7 @@ class CompilerBase extends \PhpAot\Core\Translator
protected function getFunctionName(Node $v): string protected function getFunctionName(Node $v): string
{ {
$names[] = strtolower($this->parseIdentifier($v->name)); $names[] = $this->escapeFunction($this->parseIdentifier($v->name));
if ($this->namespace) { if ($this->namespace) {
$names[] = $this->escapeNamespace($this->namespace); $names[] = $this->escapeNamespace($this->namespace);
} }
@ -602,6 +604,7 @@ class CompilerBase extends \PhpAot\Core\Translator
case 'Scalar_MagicConst_Dir': case 'Scalar_MagicConst_Dir':
case 'Scalar_MagicConst_Line': case 'Scalar_MagicConst_Line':
case 'Scalar_MagicConst_Function': case 'Scalar_MagicConst_Function':
case 'Scalar_MagicConst_Class':
return $this->parseMagicConst($expr); return $this->parseMagicConst($expr);
case 'Scalar_InterpolatedString': case 'Scalar_InterpolatedString':
return $this->parseInterpolatedString($expr); return $this->parseInterpolatedString($expr);
@ -1254,9 +1257,9 @@ class CompilerBase extends \PhpAot\Core\Translator
*/ */
protected function findNativeFunction(string $fname): string|false protected function findNativeFunction(string $fname): string|false
{ {
$possibleFunctionNames = [strtolower($fname),]; $possibleFunctionNames = [$this->escapeFunction($fname),];
if ($this->namespace) { if ($this->namespace) {
$possibleFunctionNames[] = $this->namespace . self::NAMESPACE_SEPARATOR . $fname; $possibleFunctionNames[] = $this->escapeNamespace($this->namespace) . self::NAMESPACE_SEPARATOR . $fname;
} }
if (isset($this->useFunctions[$fname])) { if (isset($this->useFunctions[$fname])) {
$possibleFunctionNames[] = $this->escapeNamespace($this->useFunctions[$fname]) . self::NAMESPACE_SEPARATOR . $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) . ')'; 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 protected function parseCastInt(Node $node): string
{ {
return $this->convertIntExpr($this->parseExpr($node->expr)); 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)); return str_replace('\\', self::NAMESPACE_SEPARATOR, strtolower($ns));
} }
protected function escapeFunction(string $fname): string
{
return strtolower($fname);
}
protected function escapeClass(string $class): string protected function escapeClass(string $class): string
{ {
return $class; return $class;
@ -1932,6 +1894,8 @@ class CompilerBase extends \PhpAot\Core\Translator
return (string)$expr->getStartLine(); return (string)$expr->getStartLine();
case 'Scalar_MagicConst_Function': case 'Scalar_MagicConst_Function':
return '"' . $this->escapeString($this->functionDef->name) . '"'; return '"' . $this->escapeString($this->functionDef->name) . '"';
case 'Scalar_MagicConst_Class':
return '"' . $this->escapeString($this->classDef->name) . '"';
default: default:
abort($expr); abort($expr);
} }

@ -26,6 +26,29 @@ class Preprocessor extends CompilerBase
$list = $sortedFiles; $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 public function prepare(string $file): void
{ {
$phpCode = $this->loadFile($file); $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 protected function prepareClass(Node\Stmt\Class_ $class): string
{ {

@ -91,6 +91,8 @@ class Translator extends Preprocessor
public function convert(string $file): string public function convert(string $file): string
{ {
$phpCode = $this->loadFile($file); $phpCode = $this->loadFile($file);
$this->stubFileIncluded = false;
$this->localHeaders = [];
while (true) { while (true) {
try { try {
$cppCode = $this->doConvert($phpCode); $cppCode = $this->doConvert($phpCode);
@ -125,7 +127,7 @@ class Translator extends Preprocessor
$this->parseDeclare($v); $this->parseDeclare($v);
break; break;
case 'Stmt_Namespace': case 'Stmt_Namespace':
$cppCode .= $this->parseNamespaceDef($v); $cppCode .= $this->parseNamespace($v);
break; break;
case 'Stmt_Class': case 'Stmt_Class':
$cppCode .= $this->parseClass($v); $cppCode .= $this->parseClass($v);
@ -187,18 +189,34 @@ class Translator extends Preprocessor
public function genGlobalVars(string $file): void public function genGlobalVars(string $file): void
{ {
$this->localHeaders = [];
$code = $this->genIncludeHeaderFiles(); $code = $this->genIncludeHeaderFiles();
$lines = []; $lines = [];
// 全局变量只能是 var 类型 // 全局变量只能是 var 类型
foreach ($this->globalVars as $name => $type) { foreach ($this->globalVars as $name => $type) {
$lines[] = self::TYPE_VAR . ' ' . $name . ';'; $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 .= implode(PHP_EOL, $lines) . PHP_EOL;
$code .= PHP_EOL; $code .= PHP_EOL;
$literalStringsCount = count($this->literalStrings); $literalStringsCount = count($this->literalStrings);
$code .= 'php::Var ' . self::LITERAL_STRINGS . '[' . $literalStringsCount . '] = {' . PHP_EOL; $code .= 'php::Var ' . self::LITERAL_STRINGS . '[' . $literalStringsCount . '] = {' . PHP_EOL;
$this->indentLevel++; $this->indentLevel++;
$code .= $this->getIndent() . '// literal strings' . PHP_EOL;
foreach ($this->literalStrings as $str => $index) { foreach ($this->literalStrings as $str => $index) {
$code .= $this->getIndent() . 'php::String{ZEND_STRL("' . $this->escapeString($str) . '"), true},' . PHP_EOL; $code .= $this->getIndent() . 'php::String{ZEND_STRL("' . $this->escapeString($str) . '"), true},' . PHP_EOL;
} }
@ -215,21 +233,25 @@ class Translator extends Preprocessor
$code .= PHP_EOL; $code .= PHP_EOL;
$this->indentLevel++; $this->indentLevel++;
$lines = []; $lines = [];
$lines[] = $this->getIndent() . '// constants';
foreach ($this->nativeConstants as $name => $constant) { foreach ($this->nativeConstants as $name => $constant) {
$lines[] = $this->getIndent() . $name . ' = ' . $constant->value . ';'; $lines[] = $this->getIndent() . $name . ' = ' . $constant->value . ';';
// 注册到 PHP
$lines[] = $this->getIndent() . 'php::define("' . $name . '", ' . $name . ');'; $lines[] = $this->getIndent() . 'php::define("' . $name . '", ' . $name . ');';
} }
$this->indentLevel--; $lines[] = $this->getIndent() . '// global vars';
$code .= $this->genFunction(self::PREFIX . 'init_constant_vars', 'void', [], $lines);
// 生成全局变量
$code .= PHP_EOL;
$this->indentLevel++;
$lines = [];
foreach ($this->globalVars as $name => $type) { foreach ($this->globalVars as $name => $type) {
$lines[] = $this->getIndent() . $name . ' = php::global("' . $name . '");'; $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--; $this->indentLevel--;
$code .= $this->genFunction(self::PREFIX . 'init_global_vars', 'void', [], $lines); $code .= $this->genFunction(self::PREFIX . 'init_global_vars', 'void', [], $lines);
@ -309,17 +331,77 @@ class Translator extends Preprocessor
return strtolower($this->parseIdentifier($v->name)); 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 protected function parseClass(Node\Stmt\Class_ $class): string
{ {
$this->class = $this->parseIdentifier($class->name); $this->class = $this->parseIdentifier($class->name);
if (!$this->stubFileIncluded) { 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); $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->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 = []; $methodCodes = [];
foreach ($class->stmts as $v) { foreach ($class->stmts as $v) {
@ -535,7 +617,16 @@ class Translator extends Preprocessor
$name = $this->getMethodName($v); $name = $this->getMethodName($v);
$flags = $v->flags; $flags = $v->flags;
$methodDef = new MethodDef($name, $flags); $methodDef = new MethodDef($name, $flags);
$this->classDef->methods[] = $methodDef; $this->classDef->methods[$name] = $methodDef;
$methodCodes[$name] = $this->parseFunction($v); $methodCodes[$name] = $this->parseFunction($v);
} }
private function parseIdentifierList(array $implements): array
{
$list = [];
foreach ($implements as $implement) {
$list[] = $this->parseIdentifier($implement);
}
return $list;
}
} }

@ -12,7 +12,6 @@ void php_main();
extern php::Var argc; extern php::Var argc;
extern php::Var argv; extern php::Var argv;
extern void php_init_constant_vars();
extern void php_init_global_vars(); extern void php_init_global_vars();
extern void php_unset_global_vars(); extern void php_unset_global_vars();
@ -40,7 +39,6 @@ int main(int cpp_argc, char **cpp_argv) {
#endif #endif
zend_first_try { zend_first_try {
php_init_global_vars(); php_init_global_vars();
php_init_constant_vars();
php::eval("main();"); php::eval("main();");
} }
zend_catch { zend_catch {

Loading…
Cancel
Save