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
const VERSION = '1.0.1';
function main()
{
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>
*/
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 $useFunctions = [];
protected string $class = '';
/**
* @var array<ClassDef>
*/
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);
}

@ -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
{

@ -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;
}
}

@ -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 {

Loading…
Cancel
Save