代码格式化,命名空间处理

pull/1/head
韩天峰 7 months ago
parent 8c20fd18c8
commit 13a5849584
  1. 66
      .php-cs-fixer.dist.php
  2. 4
      bin/gen_stub.php
  3. 10
      src/Php/ArgInfo.php
  4. 8
      src/Php/AstNodeType.php
  5. 14
      src/Php/ClassDef.php
  6. 18
      src/Php/ClassLikeDef.php
  7. 2016
      src/Php/CompilerBase.php
  8. 15
      src/Php/ConstantDef.php
  9. 8
      src/Php/Constants.php
  10. 151
      src/Php/Encryptor.php
  11. 397
      src/Php/Extractor.php
  12. 48
      src/Php/FileScanner.php
  13. 21
      src/Php/FileSorter.php
  14. 20
      src/Php/FuncCallOptimizer.php
  15. 15
      src/Php/FunctionDef.php
  16. 8
      src/Php/InterfaceDef.php
  17. 20
      src/Php/MagicMethodDetector.php
  18. 16
      src/Php/MethodDef.php
  19. 76
      src/Php/Preprocessor.php
  20. 17
      src/Php/PropertyDef.php
  21. 8
      src/Php/RedoException.php
  22. 8
      src/Php/Reflection.php
  23. 8
      src/Php/SyntaxError.php
  24. 982
      src/Php/Translator.php
  25. 8
      src/Php/Unsupported.php
  26. 8
      src/Php/Visitor.php

@ -0,0 +1,66 @@
<?php
$header = <<<'EOF'
This file is part of Swoole-Compiler(AOT).
@link https://www.swoole.com/
@contact service@swoole.com
EOF;
return (new PhpCsFixer\Config())
->setRiskyAllowed(true)
->setRules([
'@DoctrineAnnotation' => true,
'@PhpCsFixer' => true,
'@PSR2' => true,
'@Symfony' => true,
'align_multiline_comment' => ['comment_type' => 'all_multiline'],
'array_syntax' => ['syntax' => 'short'],
'binary_operator_spaces' => ['operators' => ['=' => 'align', '=>' => 'align', ]],
'blank_line_after_namespace' => true,
'blank_line_before_statement' => ['statements' => ['declare']],
'class_attributes_separation' => true,
'concat_space' => ['spacing' => 'one'],
'constant_case' => ['case' => 'lower'],
'combine_consecutive_unsets' => true,
'declare_strict_types' => true,
'fully_qualified_strict_types' => ['phpdoc_tags' => []],
'general_phpdoc_annotation_remove' => ['annotations' => ['author']],
'header_comment' => ['comment_type' => 'PHPDoc', 'header' => $header, 'location' => 'after_open', 'separate' => 'bottom'],
'increment_style' => ['style' => 'post'],
'lambda_not_used_import' => false,
'linebreak_after_opening_tag' => true,
'list_syntax' => ['syntax' => 'short'],
'lowercase_static_reference' => true,
'multiline_comment_opening_closing' => true,
'multiline_whitespace_before_semicolons' => ['strategy' => 'new_line_for_chained_calls'],
'no_superfluous_phpdoc_tags' => ['allow_mixed' => true, 'allow_unused_params' => true, 'remove_inheritdoc' => false],
'no_unused_imports' => true,
'no_useless_else' => true,
'no_useless_return' => true,
'not_operator_with_space' => false,
'not_operator_with_successor_space' => false,
'php_unit_strict' => false,
'phpdoc_align' => ['align' => 'left'],
'phpdoc_annotation_without_dot' => false,
'phpdoc_no_empty_return' => false,
'phpdoc_types_order' => ['sort_algorithm' => 'none', 'null_adjustment' => 'always_last'],
'phpdoc_separation' => false,
'phpdoc_summary' => false,
'ordered_class_elements' => true,
'ordered_imports' => ['imports_order' => ['class', 'function', 'const'], 'sort_algorithm' => 'alpha'],
'ordered_types' => ['null_adjustment' => 'always_last', 'sort_algorithm' => 'none'],
'single_line_comment_style' => ['comment_types' => []],
'single_line_comment_spacing' => false,
'single_line_empty_body' => false,
'single_quote' => true,
'standardize_increment' => false,
'standardize_not_equals' => true,
'yoda_style' => ['always_move_variable' => false, 'equal' => false, 'identical' => false],
])
->setFinder(
PhpCsFixer\Finder::create()
->exclude(['html', 'vendor'])
->in(__DIR__)
)
->setUsingCache(false);

@ -4467,6 +4467,10 @@ class FileInfo {
} }
} }
if ($stmt instanceof Stmt\Use_) {
continue;
}
throw new Exception("Unexpected node {$stmt->getType()}"); throw new Exception("Unexpected node {$stmt->getType()}");
} }
if (!empty($conds)) { if (!empty($conds)) {

@ -1,10 +1,20 @@
<?php <?php
/**
* This file is part of Swoole-Compiler(AOT).
*
* @link https://www.swoole.com/
* @contact service@swoole.com
*/
declare(strict_types=1);
namespace PhpAot\Php; namespace PhpAot\Php;
class ArgInfo class ArgInfo
{ {
public string $name; public string $name;
public string $type; public string $type;
public string $default = ''; public string $default = '';
} }

@ -1,4 +1,12 @@
<?php <?php
/**
* This file is part of Swoole-Compiler(AOT).
*
* @link https://www.swoole.com/
* @contact service@swoole.com
*/
declare(strict_types=1);
namespace PhpAot\Php; namespace PhpAot\Php;

@ -1,4 +1,12 @@
<?php <?php
/**
* This file is part of Swoole-Compiler(AOT).
*
* @link https://www.swoole.com/
* @contact service@swoole.com
*/
declare(strict_types=1);
namespace PhpAot\Php; namespace PhpAot\Php;
@ -8,17 +16,23 @@ class ClassDef extends ClassLikeDef
* @var array<string, MethodDef> * @var array<string, MethodDef>
*/ */
public array $methods = []; public array $methods = [];
/** /**
* @var array<string, PropertyDef> * @var array<string, PropertyDef>
*/ */
public array $properties = []; public array $properties = [];
/** /**
* @var array<string, ConstantDef> * @var array<string, ConstantDef>
*/ */
public array $constants = []; public array $constants = [];
public array $implements = []; public array $implements = [];
public string $extends = ''; public string $extends = '';
public bool $requireCtor = false; public bool $requireCtor = false;
public int $flags; public int $flags;
public function __construct(string $name, int $flags, string $namespace = '') public function __construct(string $name, int $flags, string $namespace = '')

@ -1,28 +1,38 @@
<?php <?php
/**
* This file is part of Swoole-Compiler(AOT).
*
* @link https://www.swoole.com/
* @contact service@swoole.com
*/
declare(strict_types=1);
namespace PhpAot\Php; namespace PhpAot\Php;
class ClassLikeDef class ClassLikeDef
{ {
public string $name; public string $name;
public string $namespace; public string $namespace;
public string $extends = ''; public string $extends = '';
public function __construct(string $name, string $namespace = '') public function __construct(string $name, string $namespace = '')
{ {
$this->name = $name; $this->name = $name;
$this->namespace = $namespace; $this->namespace = $namespace;
} }
public function getNamespacedName(bool $symbolic = true): string public function getNamespacedName(bool $symbolic = true): string
{ {
if ('' === $this->namespace) { if ($this->namespace === '') {
return $this->name; return $this->name;
} }
if ($symbolic) { if ($symbolic) {
return str_replace('\\', '_', $this->namespace.'_'.$this->name); return str_replace('\\', '_', $this->namespace . '_' . $this->name);
} }
return $this->namespace.'\\\\'.$this->name; return $this->namespace . '\\\\' . $this->name;
} }
} }

File diff suppressed because it is too large Load Diff

@ -1,18 +1,29 @@
<?php <?php
/**
* This file is part of Swoole-Compiler(AOT).
*
* @link https://www.swoole.com/
* @contact service@swoole.com
*/
declare(strict_types=1);
namespace PhpAot\Php; namespace PhpAot\Php;
class ConstantDef class ConstantDef
{ {
public string $name; public string $name;
public string $type; public string $type;
public string $flags; public string $flags;
public string $value; public string $value;
public function __construct(string $name, string $flags, string $type, string $value) public function __construct(string $name, string $flags, string $type, string $value)
{ {
$this->name = $name; $this->name = $name;
$this->type = $type; $this->type = $type;
$this->flags = $flags; $this->flags = $flags;
$this->value = $value; $this->value = $value;
} }

@ -1,4 +1,12 @@
<?php <?php
/**
* This file is part of Swoole-Compiler(AOT).
*
* @link https://www.swoole.com/
* @contact service@swoole.com
*/
declare(strict_types=1);
namespace PhpAot\Php; namespace PhpAot\Php;

@ -1,4 +1,12 @@
<?php <?php
/**
* This file is part of Swoole-Compiler(AOT).
*
* @link https://www.swoole.com/
* @contact service@swoole.com
*/
declare(strict_types=1);
namespace PhpAot\Php; namespace PhpAot\Php;
@ -7,33 +15,40 @@ use PhpParser\PrettyPrinter\Standard;
class Encryptor extends \PhpAot\Core\Translator class Encryptor extends \PhpAot\Core\Translator
{ {
protected array $stmts; protected array $stmts;
protected string $phpxDir = '~/workspace/phpx'; protected string $phpxDir = '~/workspace/phpx';
protected string $lang = 'PHP'; protected string $lang = 'PHP';
protected array $typeMap = []; protected array $typeMap = [];
protected array $headers = [ protected array $headers = [
'phpx.h', 'phpx.h',
]; ];
protected array $encodeMap; protected array $encodeMap;
protected array $decodeMap; protected array $decodeMap;
protected array $constants; protected array $constants;
public function __construct(array $stmts) public function __construct(array $stmts)
{ {
$confDir = __DIR__.'/../../config'; $confDir = __DIR__ . '/../../config';
$this->stmts = $stmts; $this->stmts = $stmts;
$this->encodeMap = require $confDir.'/functions.php'; $this->encodeMap = require $confDir . '/functions.php';
$this->decodeMap = array_flip($this->encodeMap); $this->decodeMap = array_flip($this->encodeMap);
$this->constants = require $confDir.'/constants.php'; $this->constants = require $confDir . '/constants.php';
} }
public function parseHeaders(): string public function parseHeaders(): string
{ {
$lines = []; $lines = [];
foreach ($this->headers as $header) { foreach ($this->headers as $header) {
$lines[] = '#include <'.$header.'>'; $lines[] = '#include <' . $header . '>';
} }
return implode(PHP_EOL, $lines).PHP_EOL.PHP_EOL; return implode(PHP_EOL, $lines) . PHP_EOL . PHP_EOL;
} }
public function setPhpxDir($dir): void public function setPhpxDir($dir): void
@ -64,34 +79,11 @@ class Encryptor extends \PhpAot\Core\Translator
return $node->getType(); return $node->getType();
} }
private function parseFunctionDef($v) public function compileFile($file)
{ {
if ($v->name) { $cmd = 'g++ -c ' . $file . ' -o ' . $file . '.o ' . $this->parseIncludes() . $this->parseLdflags() . $this->parseLibs();
$name = $this->parseIdentifier($v->name); echo $cmd . PHP_EOL;
} else { shell_exec($cmd);
$name = '';
}
if ($v->returnType) {
$return = $this->parseIdentifier($v->returnType);
} else {
$return = '';
}
if ($v->params) {
$params = $this->parseParams($v->params);
} else {
$params = '';
}
$code = $return.' '.$name.'('.$params.') {'.PHP_EOL;
++$this->indentLevel;
$stmts = $this->parseStmts($v->stmts);
--$this->indentLevel;
$code .= $stmts;
$code .= '}';
return $code;
} }
protected function parseIdentifier($node) protected function parseIdentifier($node)
@ -105,7 +97,7 @@ class Encryptor extends \PhpAot\Core\Translator
case 'Scalar_Float': case 'Scalar_Float':
return $node->value; return $node->value;
case 'Scalar_String': case 'Scalar_String':
return '"'.$node->value.'"'; return '"' . $node->value . '"';
case 'Expr_Array': case 'Expr_Array':
return $this->parseArray($node); return $this->parseArray($node);
case 'Expr_FuncCall': case 'Expr_FuncCall':
@ -127,13 +119,43 @@ class Encryptor extends \PhpAot\Core\Translator
} }
} }
private function parseFunctionDef($v)
{
if ($v->name) {
$name = $this->parseIdentifier($v->name);
} else {
$name = '';
}
if ($v->returnType) {
$return = $this->parseIdentifier($v->returnType);
} else {
$return = '';
}
if ($v->params) {
$params = $this->parseParams($v->params);
} else {
$params = '';
}
$code = $return . ' ' . $name . '(' . $params . ') {' . PHP_EOL;
$this->indentLevel++;
$stmts = $this->parseStmts($v->stmts);
$this->indentLevel--;
$code .= $stmts;
$code .= '}';
return $code;
}
private function parseParams($params) private function parseParams($params)
{ {
$list = []; $list = [];
foreach ($params as $param) { foreach ($params as $param) {
$type = $param->type ? $this->parseType($param->type) : ''; $type = $param->type ? $this->parseType($param->type) : '';
$name = $param->var ? $this->parseIdentifier($param->var) : ''; $name = $param->var ? $this->parseIdentifier($param->var) : '';
$list[] = $type.' '.$name; $list[] = $type . ' ' . $name;
$this->typeMap[$name] = $type; $this->typeMap[$name] = $type;
} }
@ -150,10 +172,10 @@ class Encryptor extends \PhpAot\Core\Translator
$lines[] = $this->parseFunctionDef($v); $lines[] = $this->parseFunctionDef($v);
break; break;
case 'Stmt_Expression': case 'Stmt_Expression':
$lines[] = $this->parseExpr($v->expr).';'; $lines[] = $this->parseExpr($v->expr) . ';';
break; break;
case 'Stmt_Echo': case 'Stmt_Echo':
$lines[] = $this->parseEcho($v).';'; $lines[] = $this->parseEcho($v) . ';';
break; break;
case 'Stmt_Return': case 'Stmt_Return':
$this->parseReturn($v); $this->parseReturn($v);
@ -175,7 +197,7 @@ class Encryptor extends \PhpAot\Core\Translator
} }
$code = ''; $code = '';
foreach ($lines as $line) { foreach ($lines as $line) {
$code .= $this->getIndent().$line.PHP_EOL; $code .= $this->getIndent() . $line . PHP_EOL;
} }
return $code; return $code;
@ -229,7 +251,7 @@ class Encryptor extends \PhpAot\Core\Translator
private function parseEcho(mixed $v) private function parseEcho(mixed $v)
{ {
return 'php::echo('.$this->parseExprs($v->exprs).')'; return 'php::echo(' . $this->parseExprs($v->exprs) . ')';
} }
private function parseExprs($exprs) private function parseExprs($exprs)
@ -244,23 +266,23 @@ class Encryptor extends \PhpAot\Core\Translator
private function parseBinaryOpPlus(mixed $expr) private function parseBinaryOpPlus(mixed $expr)
{ {
$left = $this->parseIdentifier($expr->left); $left = $this->parseIdentifier($expr->left);
$right = $this->parseIdentifier($expr->right); $right = $this->parseIdentifier($expr->right);
return $left.' + '.$right; return $left . ' + ' . $right;
} }
private function parseReturn(mixed $v) private function parseReturn(mixed $v)
{ {
return 'return '.$this->parseExpr($v->expr); return 'return ' . $this->parseExpr($v->expr);
} }
private function parseBinaryOpMul(mixed $expr) private function parseBinaryOpMul(mixed $expr)
{ {
$left = $this->parseIdentifier($expr->left); $left = $this->parseIdentifier($expr->left);
$right = $this->parseIdentifier($expr->right); $right = $this->parseIdentifier($expr->right);
return $left.' * '.$right; return $left . ' * ' . $right;
} }
private function detectType($var, $expr) private function detectType($var, $expr)
@ -283,20 +305,20 @@ class Encryptor extends \PhpAot\Core\Translator
private function parseArray($node) private function parseArray($node)
{ {
$items = $node->items; $items = $node->items;
$list = []; $list = [];
++$this->indentLevel; $this->indentLevel++;
foreach ($items as $item) { foreach ($items as $item) {
if ($item->key) { if ($item->key) {
$list[] = $this->getIndent().'{ php::Variant('.$this->parseIdentifier($item->key).'), php::Variant('.$this->parseIdentifier($item->value).') }'; $list[] = $this->getIndent() . '{ php::Variant(' . $this->parseIdentifier($item->key) . '), php::Variant(' . $this->parseIdentifier($item->value) . ') }';
} else { } else {
$list[] = $this->getIndent().'php::Variant('.$this->parseIdentifier($item->value).')'; $list[] = $this->getIndent() . 'php::Variant(' . $this->parseIdentifier($item->value) . ')';
} }
} }
--$this->indentLevel; $this->indentLevel--;
return '{'.PHP_EOL. return '{' . PHP_EOL .
implode(', '.PHP_EOL, $list).PHP_EOL. implode(', ' . PHP_EOL, $list) . PHP_EOL .
$this->getIndent(). $this->getIndent() .
'}'; '}';
} }
@ -318,11 +340,11 @@ class Encryptor extends \PhpAot\Core\Translator
private function parseIncludes() private function parseIncludes()
{ {
$list = [ $list = [
$this->phpxDir.'/include', $this->phpxDir . '/include',
]; ];
$out = '$(php-config --includes) '; $out = '$(php-config --includes) ';
foreach ($list as $li) { foreach ($list as $li) {
$out .= '-I '.$li.' '; $out .= '-I ' . $li . ' ';
} }
return $out; return $out;
@ -332,11 +354,11 @@ class Encryptor extends \PhpAot\Core\Translator
{ {
$list = [ $list = [
'$(php-config --prefix)/lib', '$(php-config --prefix)/lib',
$this->phpxDir.'/lib', $this->phpxDir . '/lib',
]; ];
$out = ''; $out = '';
foreach ($list as $li) { foreach ($list as $li) {
$out .= '-L '.$li.' '; $out .= '-L ' . $li . ' ';
} }
return $out; return $out;
@ -350,25 +372,18 @@ class Encryptor extends \PhpAot\Core\Translator
]; ];
$out = ''; $out = '';
foreach ($list as $li) { foreach ($list as $li) {
$out .= '-l'.$li.' '; $out .= '-l' . $li . ' ';
} }
return $out; return $out;
} }
public function compileFile($file)
{
$cmd = 'g++ -c '.$file.' -o '.$file.'.o '.$this->parseIncludes().$this->parseLdflags().$this->parseLibs();
echo $cmd.PHP_EOL;
shell_exec($cmd);
}
private function parseBinaryOpConcat(mixed $expr) private function parseBinaryOpConcat(mixed $expr)
{ {
$left = $this->parseIdentifier($expr->left); $left = $this->parseIdentifier($expr->left);
$right = $this->parseIdentifier($expr->right); $right = $this->parseIdentifier($expr->right);
return $left.' + '.$right; return $left . ' + ' . $right;
} }
private function parseFuncCall($expr) private function parseFuncCall($expr)

@ -1,10 +1,19 @@
<?php <?php
/**
* This file is part of Swoole-Compiler(AOT).
*
* @link https://www.swoole.com/
* @contact service@swoole.com
*/
declare(strict_types=1);
namespace PhpAot\Php; namespace PhpAot\Php;
class Extractor class Extractor
{ {
private string $ctagsPath = 'ctags'; private string $ctagsPath = 'ctags';
private bool $isUniversalCtags = false; private bool $isUniversalCtags = false;
public function __construct() public function __construct()
@ -12,29 +21,11 @@ class Extractor
$this->checkCtags(); $this->checkCtags();
} }
/**
* 检查 ctags 是否可用.
*/
private function checkCtags(): void
{
$output = shell_exec("{$this->ctagsPath} --version 2>&1");
if (null === $output) {
$this->error("未找到 ctags 命令\n安装: sudo apt install universal-ctags");
}
$this->isUniversalCtags = false !== stripos($output, 'Universal Ctags');
if (!$this->isUniversalCtags) {
$this->warn('建议使用 Universal Ctags 以获得更好的支持');
}
}
/** /**
* 提取函数定义. * 提取函数定义.
* *
* @param string $filename 文件路径 * @param string $filename 文件路径
* @param array $prefixes 函数名前缀列表 * @param array $prefixes 函数名前缀列表
* *
* @return array 函数列表 * @return array 函数列表
*/ */
@ -45,7 +36,7 @@ class Extractor
} }
$this->info("分析文件: {$filename}"); $this->info("分析文件: {$filename}");
$this->info('函数前缀: '.implode(', ', $prefixes)); $this->info('函数前缀: ' . implode(', ', $prefixes));
// 运行 ctags // 运行 ctags
$tags = $this->runCtags($filename); $tags = $this->runCtags($filename);
@ -53,7 +44,7 @@ class Extractor
// 过滤和解析函数 // 过滤和解析函数
$functions = []; $functions = [];
foreach ($tags as $tag) { foreach ($tags as $tag) {
if ('function' !== $tag['kind']) { if ($tag['kind'] !== 'function') {
continue; continue;
} }
@ -79,11 +70,156 @@ class Extractor
} }
} }
$this->info('找到 '.count($functions).' 个函数'); $this->info('找到 ' . count($functions) . ' 个函数');
return $functions; return $functions;
} }
/**
* 批量提取多个文件.
*/
public function extractFromFiles(array $files, array $prefixes = ['php_']): array
{
$allFunctions = [];
foreach ($files as $file) {
try {
$functions = $this->extractFunctions($file, $prefixes);
$allFunctions = array_merge($allFunctions, $functions);
} catch (Exception $e) {
$this->error("处理文件 {$file} 失败: " . $e->getMessage());
}
}
return $allFunctions;
}
/**
* 提取函数并添加额外信息.
*/
public function extractWithMetadata(string $filename, array $prefixes = ['php_']): array
{
$functions = $this->extractFunctions($filename, $prefixes);
// 添加额外的元数据
foreach ($functions as &$func) {
$func['metadata'] = $this->extractMetadata($filename, $func);
}
return $functions;
}
/**
* 生成函数统计信息.
*/
public function generateStatistics(array $functions): array
{
$stats = [
'total' => count($functions),
'byReturnType' => [],
'byParameterCount' => [],
'byPrefix' => [],
'withComments' => 0,
'isPHPFunction' => 0,
];
foreach ($functions as $func) {
// 按返回类型统计
$returnType = $func['returnType'];
$stats['byReturnType'][$returnType] =
($stats['byReturnType'][$returnType] ?? 0) + 1;
// 按参数数量统计
$paramCount = count($func['parameters']);
$stats['byParameterCount'][$paramCount] =
($stats['byParameterCount'][$paramCount] ?? 0) + 1;
// 按前缀统计
$name = $func['name'];
$prefix = preg_match('/^([a-z_]+_)/i', $name, $m) ? $m[1] : 'other';
$stats['byPrefix'][$prefix] =
($stats['byPrefix'][$prefix] ?? 0) + 1;
// 有注释的函数
if (!empty($func['metadata']['comments'])) {
$stats['withComments']++;
}
// PHP 函数宏
if ($func['metadata']['isPHPFunction'] ?? false) {
$stats['isPHPFunction']++;
}
}
return $stats;
}
/**
* 导出为 Markdown 文档.
*/
public function exportToMarkdown(array $functions, string $title = 'API 文档'): string
{
$md = "# {$title}\n\n";
$md .= '生成时间: ' . date('Y-m-d H:i:s') . "\n\n";
$md .= '总计: ' . count($functions) . " 个函数\n\n";
$md .= "---\n\n";
foreach ($functions as $func) {
$md .= "## {$func['name']}\n\n";
// 签名
$md .= "```c\n{$func['signature']}\n```\n\n";
// 返回类型
$md .= "**返回类型**: `{$func['returnType']}`\n\n";
// 参数
if (!empty($func['parameters'])) {
$md .= "**参数**:\n\n";
foreach ($func['parameters'] as $param) {
$name = $param['name'] ?: '(unnamed)';
$md .= "- `{$param['type']}` **{$name}**\n";
}
$md .= "\n";
} else {
$md .= "**参数**: 无\n\n";
}
// 注释
if (!empty($func['metadata']['comments'])) {
$md .= "**说明**:\n\n";
foreach ($func['metadata']['comments'] as $comment) {
$md .= $comment . "\n\n";
}
}
// 位置
$md .= "**位置**: {$func['location']['file']}:{$func['location']['line']}\n\n";
$md .= "---\n\n";
}
return $md;
}
/**
* 检查 ctags 是否可用.
*/
private function checkCtags(): void
{
$output = shell_exec("{$this->ctagsPath} --version 2>&1");
if ($output === null) {
$this->error("未找到 ctags 命令\n安装: sudo apt install universal-ctags");
}
$this->isUniversalCtags = stripos($output, 'Universal Ctags') !== false;
if (!$this->isUniversalCtags) {
$this->warn('建议使用 Universal Ctags 以获得更好的支持');
}
}
/** /**
* 运行 ctags 命令. * 运行 ctags 命令.
*/ */
@ -97,12 +233,12 @@ class Extractor
$output = shell_exec($cmd); $output = shell_exec($cmd);
if (null === $output) { if ($output === null) {
throw new RuntimeException('ctags 执行失败'); throw new RuntimeException('ctags 执行失败');
} }
// 解析 JSON 输出 // 解析 JSON 输出
$tags = []; $tags = [];
$lines = explode("\n", trim($output)); $lines = explode("\n", trim($output));
foreach ($lines as $line) { foreach ($lines as $line) {
@ -111,7 +247,7 @@ class Extractor
} }
$tag = json_decode($line, true); $tag = json_decode($line, true);
if (null === $tag) { if ($tag === null) {
continue; continue;
} }
@ -127,7 +263,7 @@ class Extractor
private function parseFunction(string $filename, array $tag): ?array private function parseFunction(string $filename, array $tag): ?array
{ {
$funcName = $tag['name'] ?? ''; $funcName = $tag['name'] ?? '';
$lineNum = $tag['line'] ?? 0; $lineNum = $tag['line'] ?? 0;
if (empty($funcName) || $lineNum < 1) { if (empty($funcName) || $lineNum < 1) {
return null; return null;
@ -147,15 +283,15 @@ class Extractor
$parameters = $this->parseParameters($signature, $funcName); $parameters = $this->parseParameters($signature, $funcName);
return [ return [
'name' => $funcName, 'name' => $funcName,
'returnType' => $returnType, 'returnType' => $returnType,
'signature' => $signature, 'signature' => $signature,
'parameters' => $parameters, 'parameters' => $parameters,
'location' => [ 'location' => [
'file' => $filename, 'file' => $filename,
'line' => $lineNum, 'line' => $lineNum,
], ],
'scope' => $tag['scope'] ?? null, 'scope' => $tag['scope'] ?? null,
'scopeKind' => $tag['scopeKind'] ?? null, 'scopeKind' => $tag['scopeKind'] ?? null,
]; ];
} }
@ -167,20 +303,20 @@ class Extractor
{ {
$lines = file($filename, FILE_IGNORE_NEW_LINES); $lines = file($filename, FILE_IGNORE_NEW_LINES);
if (false === $lines || $lineNum > count($lines)) { if ($lines === false || $lineNum > count($lines)) {
return ''; return '';
} }
// 从函数声明行开始收集,直到遇到 { 或 ; // 从函数声明行开始收集,直到遇到 { 或 ;
$signatureLines = []; $signatureLines = [];
$maxLines = min($lineNum + 20, count($lines)); $maxLines = min($lineNum + 20, count($lines));
for ($i = $lineNum - 1; $i < $maxLines; ++$i) { for ($i = $lineNum - 1; $i < $maxLines; $i++) {
$line = $lines[$i]; $line = $lines[$i];
$signatureLines[] = $line; $signatureLines[] = $line;
// 检查是否到达函数体或声明结束 // 检查是否到达函数体或声明结束
if (false !== strpos($line, '{') || false !== strpos($line, ';')) { if (strpos($line, '{') !== false || strpos($line, ';') !== false) {
break; break;
} }
} }
@ -195,9 +331,7 @@ class Extractor
$signature = preg_replace('/\s+/', ' ', $signature); $signature = preg_replace('/\s+/', ' ', $signature);
// 清理首尾空白 // 清理首尾空白
$signature = trim($signature); return trim($signature);
return $signature;
} }
/** /**
@ -206,7 +340,7 @@ class Extractor
private function parseReturnType(string $signature, string $funcName): string private function parseReturnType(string $signature, string $funcName): string
{ {
// 匹配: <返回类型> <函数名>( // 匹配: <返回类型> <函数名>(
$pattern = '/^(.+?)\s+'.preg_quote($funcName, '/').'\s*\(/'; $pattern = '/^(.+?)\s+' . preg_quote($funcName, '/') . '\s*\(/';
if (preg_match($pattern, $signature, $matches)) { if (preg_match($pattern, $signature, $matches)) {
$returnType = trim($matches[1]); $returnType = trim($matches[1]);
@ -228,7 +362,7 @@ class Extractor
private function parseParameters(string $signature, string $funcName): array private function parseParameters(string $signature, string $funcName): array
{ {
// 提取括号内的参数 // 提取括号内的参数
$pattern = '/'.preg_quote($funcName, '/').'\s*\((.*?)\)/s'; $pattern = '/' . preg_quote($funcName, '/') . '\s*\((.*?)\)/s';
if (!preg_match($pattern, $signature, $matches)) { if (!preg_match($pattern, $signature, $matches)) {
return []; return [];
@ -237,7 +371,7 @@ class Extractor
$paramsStr = trim($matches[1]); $paramsStr = trim($matches[1]);
// 空参数或 void // 空参数或 void
if (empty($paramsStr) || 'void' === $paramsStr) { if (empty($paramsStr) || $paramsStr === 'void') {
return []; return [];
} }
@ -266,23 +400,23 @@ class Extractor
*/ */
private function splitParameters(string $paramsStr): array private function splitParameters(string $paramsStr): array
{ {
$params = []; $params = [];
$current = ''; $current = '';
$depth = 0; $depth = 0;
$length = strlen($paramsStr); $length = strlen($paramsStr);
for ($i = 0; $i < $length; ++$i) { for ($i = 0; $i < $length; $i++) {
$char = $paramsStr[$i]; $char = $paramsStr[$i];
if ('<' === $char || '(' === $char || '[' === $char) { if ($char === '<' || $char === '(' || $char === '[') {
++$depth; $depth++;
$current .= $char; $current .= $char;
} elseif ('>' === $char || ')' === $char || ']' === $char) { } elseif ($char === '>' || $char === ')' || $char === ']') {
--$depth; $depth--;
$current .= $char; $current .= $char;
} elseif (',' === $char && 0 === $depth) { } elseif ($char === ',' && $depth === 0) {
$params[] = $current; $params[] = $current;
$current = ''; $current = '';
} else { } else {
$current .= $char; $current .= $char;
} }
@ -321,25 +455,6 @@ class Extractor
]; ];
} }
/**
* 批量提取多个文件.
*/
public function extractFromFiles(array $files, array $prefixes = ['php_']): array
{
$allFunctions = [];
foreach ($files as $file) {
try {
$functions = $this->extractFunctions($file, $prefixes);
$allFunctions = array_merge($allFunctions, $functions);
} catch (Exception $e) {
$this->error("处理文件 {$file} 失败: ".$e->getMessage());
}
}
return $allFunctions;
}
/** /**
* 输出信息. * 输出信息.
*/ */
@ -365,48 +480,33 @@ class Extractor
exit(1); exit(1);
} }
/**
* 提取函数并添加额外信息.
*/
public function extractWithMetadata(string $filename, array $prefixes = ['php_']): array
{
$functions = $this->extractFunctions($filename, $prefixes);
// 添加额外的元数据
foreach ($functions as &$func) {
$func['metadata'] = $this->extractMetadata($filename, $func);
}
return $functions;
}
/** /**
* 提取函数的元数据(注释、属性等). * 提取函数的元数据(注释、属性等).
*/ */
private function extractMetadata(string $filename, array $func): array private function extractMetadata(string $filename, array $func): array
{ {
$lineNum = $func['location']['line']; $lineNum = $func['location']['line'];
$lines = file($filename, FILE_IGNORE_NEW_LINES); $lines = file($filename, FILE_IGNORE_NEW_LINES);
$metadata = [ $metadata = [
'comments' => [], 'comments' => [],
'attributes' => [], 'attributes' => [],
'isPHPFunction' => false, 'isPHPFunction' => false,
'isStatic' => false, 'isStatic' => false,
'isInline' => false, 'isInline' => false,
]; ];
// 提取函数前的注释 // 提取函数前的注释
$comments = $this->extractComments($lines, $lineNum); $comments = $this->extractComments($lines, $lineNum);
$metadata['comments'] = $comments; $metadata['comments'] = $comments;
// 检测 PHP 函数宏 // 检测 PHP 函数宏
$metadata['isPHPFunction'] = $this->isPHPFunction($func['signature']); $metadata['isPHPFunction'] = $this->isPHPFunction($func['signature']);
// 检测修饰符 // 检测修饰符
$signature = $func['signature']; $signature = $func['signature'];
$metadata['isStatic'] = false !== strpos($signature, 'static'); $metadata['isStatic'] = strpos($signature, 'static') !== false;
$metadata['isInline'] = false !== strpos($signature, 'inline'); $metadata['isInline'] = strpos($signature, 'inline') !== false;
// 提取文档注释中的标签 // 提取文档注释中的标签
$metadata['docTags'] = $this->parseDocTags($comments); $metadata['docTags'] = $this->parseDocTags($comments);
@ -420,7 +520,7 @@ class Extractor
private function extractComments(array $lines, int $lineNum): array private function extractComments(array $lines, int $lineNum): array
{ {
$comments = []; $comments = [];
$i = $lineNum - 2; // 从函数声明的前一行开始 $i = $lineNum - 2; // 从函数声明的前一行开始
// 向上查找注释 // 向上查找注释
while ($i >= 0) { while ($i >= 0) {
@ -428,21 +528,21 @@ class Extractor
// 空行 // 空行
if (empty($line)) { if (empty($line)) {
--$i; $i--;
continue; continue;
} }
// C++ 风格注释 // C++ 风格注释
if (str_starts_with($line, '//')) { if (str_starts_with($line, '//')) {
array_unshift($comments, substr($line, 2)); array_unshift($comments, substr($line, 2));
--$i; $i--;
continue; continue;
} }
// C 风格注释结束 // C 风格注释结束
if (str_ends_with($line, '*/')) { if (str_ends_with($line, '*/')) {
$commentLines = [$line]; $commentLines = [$line];
--$i; $i--;
// 继续向上查找注释开始 // 继续向上查找注释开始
while ($i >= 0) { while ($i >= 0) {
@ -452,7 +552,7 @@ class Extractor
if (str_starts_with($commentLine, '/*')) { if (str_starts_with($commentLine, '/*')) {
break; break;
} }
--$i; $i--;
} }
// 解析多行注释 // 解析多行注释
@ -462,7 +562,7 @@ class Extractor
$comment = preg_replace('#^\s*\*\s?#m', '', $comment); $comment = preg_replace('#^\s*\*\s?#m', '', $comment);
array_unshift($comments, trim($comment)); array_unshift($comments, trim($comment));
--$i; $i--;
continue; continue;
} }
@ -486,7 +586,7 @@ class Extractor
]; ];
foreach ($phpMacros as $macro) { foreach ($phpMacros as $macro) {
if (false !== strpos($signature, $macro)) { if (strpos($signature, $macro) !== false) {
return true; return true;
} }
} }
@ -505,7 +605,7 @@ class Extractor
// 匹配 @tag 格式 // 匹配 @tag 格式
if (preg_match_all('/@(\w+)\s+(.*)$/m', $comment, $matches, PREG_SET_ORDER)) { if (preg_match_all('/@(\w+)\s+(.*)$/m', $comment, $matches, PREG_SET_ORDER)) {
foreach ($matches as $match) { foreach ($matches as $match) {
$tagName = $match[1]; $tagName = $match[1];
$tagValue = trim($match[2]); $tagValue = trim($match[2]);
if (!isset($tags[$tagName])) { if (!isset($tags[$tagName])) {
@ -519,97 +619,4 @@ class Extractor
return $tags; return $tags;
} }
/**
* 生成函数统计信息.
*/
public function generateStatistics(array $functions): array
{
$stats = [
'total' => count($functions),
'byReturnType' => [],
'byParameterCount' => [],
'byPrefix' => [],
'withComments' => 0,
'isPHPFunction' => 0,
];
foreach ($functions as $func) {
// 按返回类型统计
$returnType = $func['returnType'];
$stats['byReturnType'][$returnType] =
($stats['byReturnType'][$returnType] ?? 0) + 1;
// 按参数数量统计
$paramCount = count($func['parameters']);
$stats['byParameterCount'][$paramCount] =
($stats['byParameterCount'][$paramCount] ?? 0) + 1;
// 按前缀统计
$name = $func['name'];
$prefix = preg_match('/^([a-z_]+_)/i', $name, $m) ? $m[1] : 'other';
$stats['byPrefix'][$prefix] =
($stats['byPrefix'][$prefix] ?? 0) + 1;
// 有注释的函数
if (!empty($func['metadata']['comments'])) {
++$stats['withComments'];
}
// PHP 函数宏
if ($func['metadata']['isPHPFunction'] ?? false) {
++$stats['isPHPFunction'];
}
}
return $stats;
}
/**
* 导出为 Markdown 文档.
*/
public function exportToMarkdown(array $functions, string $title = 'API 文档'): string
{
$md = "# {$title}\n\n";
$md .= '生成时间: '.date('Y-m-d H:i:s')."\n\n";
$md .= '总计: '.count($functions)." 个函数\n\n";
$md .= "---\n\n";
foreach ($functions as $func) {
$md .= "## {$func['name']}\n\n";
// 签名
$md .= "```c\n{$func['signature']}\n```\n\n";
// 返回类型
$md .= "**返回类型**: `{$func['returnType']}`\n\n";
// 参数
if (!empty($func['parameters'])) {
$md .= "**参数**:\n\n";
foreach ($func['parameters'] as $param) {
$name = $param['name'] ?: '(unnamed)';
$md .= "- `{$param['type']}` **{$name}**\n";
}
$md .= "\n";
} else {
$md .= "**参数**: 无\n\n";
}
// 注释
if (!empty($func['metadata']['comments'])) {
$md .= "**说明**:\n\n";
foreach ($func['metadata']['comments'] as $comment) {
$md .= $comment."\n\n";
}
}
// 位置
$md .= "**位置**: {$func['location']['file']}:{$func['location']['line']}\n\n";
$md .= "---\n\n";
}
return $md;
}
} }

@ -1,22 +1,32 @@
<?php <?php
/**
* This file is part of Swoole-Compiler(AOT).
*
* @link https://www.swoole.com/
* @contact service@swoole.com
*/
declare(strict_types=1);
namespace PhpAot\Php; namespace PhpAot\Php;
class FileScanner class FileScanner
{ {
private string $directory;
private array $excludePatterns;
public const array PHP_EXT = ['php']; public const array PHP_EXT = ['php'];
public const array CPP_EXT = ['cpp', 'cxx', 'cc']; public const array CPP_EXT = ['cpp', 'cxx', 'cc'];
private string $directory;
private array $excludePatterns;
public function __construct(string $directory) public function __construct(string $directory)
{ {
if (!is_dir($directory)) { if (!is_dir($directory)) {
throw new \InvalidArgumentException("Directory does not exist: $directory"); throw new \InvalidArgumentException("Directory does not exist: {$directory}");
} }
$this->directory = rtrim($directory, DIRECTORY_SEPARATOR); $this->directory = rtrim($directory, DIRECTORY_SEPARATOR);
$this->excludePatterns = []; $this->excludePatterns = [];
} }
@ -59,22 +69,9 @@ class FileScanner
return $this->directory; return $this->directory;
} }
private function isExcluded(string $filePath): bool
{
$excluded = false;
foreach ($this->excludePatterns as $pattern) {
if ($this->matchPattern($pattern, $filePath)) {
$excluded = true;
break;
}
}
return $excluded;
}
public function scan(): array public function scan(): array
{ {
$files = []; $files = [];
$iterator = new \RecursiveIteratorIterator( $iterator = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($this->directory, \FilesystemIterator::SKIP_DOTS) new \RecursiveDirectoryIterator($this->directory, \FilesystemIterator::SKIP_DOTS)
); );
@ -97,6 +94,19 @@ class FileScanner
return $files; return $files;
} }
private function isExcluded(string $filePath): bool
{
$excluded = false;
foreach ($this->excludePatterns as $pattern) {
if ($this->matchPattern($pattern, $filePath)) {
$excluded = true;
break;
}
}
return $excluded;
}
private function matchPattern(string $pattern, string $path): bool private function matchPattern(string $pattern, string $path): bool
{ {
return fnmatch($pattern, $path, FNM_PATHNAME); return fnmatch($pattern, $path, FNM_PATHNAME);

@ -1,10 +1,19 @@
<?php <?php
/**
* This file is part of Swoole-Compiler(AOT).
*
* @link https://www.swoole.com/
* @contact service@swoole.com
*/
declare(strict_types=1);
namespace PhpAot\Php; namespace PhpAot\Php;
class FileSorter class FileSorter
{ {
private array $functionDeclInFile; private array $functionDeclInFile;
private array $functionCallInFile; private array $functionCallInFile;
public function __construct(array $functionDeclInFile, array $functionCallInFile) public function __construct(array $functionDeclInFile, array $functionCallInFile)
@ -22,26 +31,26 @@ class FileSorter
$inDegree = array_fill_keys($allFiles, 0); $inDegree = array_fill_keys($allFiles, 0);
foreach ($dependencies as $deps) { foreach ($dependencies as $deps) {
foreach ($deps as $dep) { foreach ($deps as $dep) {
++$inDegree[$dep]; $inDegree[$dep]++;
} }
} }
$queue = []; $queue = [];
foreach ($inDegree as $file => $degree) { foreach ($inDegree as $file => $degree) {
if (0 === $degree) { if ($degree === 0) {
$queue[] = $file; $queue[] = $file;
} }
} }
$sorted = []; $sorted = [];
while (!empty($queue)) { while (!empty($queue)) {
$current = array_shift($queue); $current = array_shift($queue);
$sorted[] = $current; $sorted[] = $current;
if (isset($dependencies[$current])) { if (isset($dependencies[$current])) {
foreach ($dependencies[$current] as $dep) { foreach ($dependencies[$current] as $dep) {
--$inDegree[$dep]; $inDegree[$dep]--;
if (0 === $inDegree[$dep]) { if ($inDegree[$dep] === 0) {
$queue[] = $dep; $queue[] = $dep;
} }
} }
@ -60,7 +69,7 @@ class FileSorter
$dependencies = []; $dependencies = [];
foreach ($this->functionCallInFile as $call) { foreach ($this->functionCallInFile as $call) {
$callerFile = $call['file']; $callerFile = $call['file'];
$functionName = $call['name']; $functionName = $call['name'];
if (isset($this->functionDeclInFile[$functionName])) { if (isset($this->functionDeclInFile[$functionName])) {

@ -1,4 +1,12 @@
<?php <?php
/**
* This file is part of Swoole-Compiler(AOT).
*
* @link https://www.swoole.com/
* @contact service@swoole.com
*/
declare(strict_types=1);
namespace PhpAot\Php; namespace PhpAot\Php;
@ -8,10 +16,10 @@ trait FuncCallOptimizer
{ {
protected function parseFuncCallWithOptimizer(string $name, Node\Expr\FuncCall $expr): string|false protected function parseFuncCallWithOptimizer(string $name, Node\Expr\FuncCall $expr): string|false
{ {
if ('strlen' === $name or 'sizeof' === $name or 'count' === $name) { if ($name === 'strlen' or $name === 'sizeof' or $name === 'count') {
return 'php::len('.$this->parseIdentifier($expr->args[0]->value).')'; return 'php::len(' . $this->parseIdentifier($expr->args[0]->value) . ')';
} }
if (1 == count($expr->args)) { if (count($expr->args) == 1) {
switch ($name) { switch ($name) {
case 'intval': case 'intval':
return $this->convertIntExpr($this->parseExpr($expr->args[0]->value)); return $this->convertIntExpr($this->parseExpr($expr->args[0]->value));
@ -24,7 +32,7 @@ trait FuncCallOptimizer
default: default:
break; break;
} }
} elseif (2 == count($expr->args)) { } elseif (count($expr->args) == 2) {
switch ($name) { switch ($name) {
case 'objval': case 'objval':
$arg1 = $expr->args[0]->value; $arg1 = $expr->args[0]->value;
@ -35,8 +43,8 @@ trait FuncCallOptimizer
break; break;
} }
} }
if ('abs' === $name) { if ($name === 'abs') {
return 'php::math::abs('.$this->parseIdentifier($expr->args[0]->value).')'; return 'php::math::abs(' . $this->parseIdentifier($expr->args[0]->value) . ')';
} }
return false; return false;

@ -1,22 +1,35 @@
<?php <?php
/**
* This file is part of Swoole-Compiler(AOT).
*
* @link https://www.swoole.com/
* @contact service@swoole.com
*/
declare(strict_types=1);
namespace PhpAot\Php; namespace PhpAot\Php;
class FunctionDef class FunctionDef
{ {
public string $name; public string $name;
public string $returnType; public string $returnType;
/** /**
* @var array<ArgInfo> * @var array<ArgInfo>
*/ */
public array $argInfoList = []; public array $argInfoList = [];
public int $argCountRequired = 0; public int $argCountRequired = 0;
public string $params = ''; public string $params = '';
public bool $method = false; public bool $method = false;
public function __construct(string $name, string $returnType) public function __construct(string $name, string $returnType)
{ {
$this->name = $name; $this->name = $name;
$this->returnType = $returnType; $this->returnType = $returnType;
} }
} }

@ -1,4 +1,12 @@
<?php <?php
/**
* This file is part of Swoole-Compiler(AOT).
*
* @link https://www.swoole.com/
* @contact service@swoole.com
*/
declare(strict_types=1);
namespace PhpAot\Php; namespace PhpAot\Php;

@ -1,4 +1,12 @@
<?php <?php
/**
* This file is part of Swoole-Compiler(AOT).
*
* @link https://www.swoole.com/
* @contact service@swoole.com
*/
declare(strict_types=1);
namespace PhpAot\Php; namespace PhpAot\Php;
@ -8,13 +16,13 @@ trait MagicMethodDetector
{ {
public function checkRequiredArgNum(string $name, MethodDef $methodDef, NodeAbstract $v): void public function checkRequiredArgNum(string $name, MethodDef $methodDef, NodeAbstract $v): void
{ {
if ('__call' == $name or '__callStatic' == $name or '__set' == $name) { if ($name == '__call' or $name == '__callStatic' or $name == '__set') {
if (2 != count($methodDef->functionDef->argInfoList)) { if (count($methodDef->functionDef->argInfoList) != 2) {
$this->fatalError($v, 'Method '.$this->class."::$name() must take exactly 2 arguments"); $this->fatalError($v, 'Method ' . $this->class . "::{$name}() must take exactly 2 arguments");
} }
} elseif ('__get' == $name) { } elseif ($name == '__get') {
if (1 != count($methodDef->functionDef->argInfoList)) { if (count($methodDef->functionDef->argInfoList) != 1) {
$this->fatalError($v, 'Method '.$this->class."::$name() must take exactly 1 argument"); $this->fatalError($v, 'Method ' . $this->class . "::{$name}() must take exactly 1 argument");
} }
} }
} }

@ -1,18 +1,28 @@
<?php <?php
/**
* This file is part of Swoole-Compiler(AOT).
*
* @link https://www.swoole.com/
* @contact service@swoole.com
*/
declare(strict_types=1);
namespace PhpAot\Php; namespace PhpAot\Php;
class MethodDef class MethodDef
{ {
public int $flags; public int $flags;
public string $name; public string $name;
public FunctionDef $functionDef; public FunctionDef $functionDef;
public function __construct(int $flags, string $name, FunctionDef $def) public function __construct(int $flags, string $name, FunctionDef $def)
{ {
$this->flags = $flags; $this->flags = $flags;
$this->name = $name; $this->name = $name;
$this->functionDef = $def; $this->functionDef = $def;
$this->functionDef->method = true; $this->functionDef->method = true;
} }

@ -1,4 +1,12 @@
<?php <?php
/**
* This file is part of Swoole-Compiler(AOT).
*
* @link https://www.swoole.com/
* @contact service@swoole.com
*/
declare(strict_types=1);
namespace PhpAot\Php; namespace PhpAot\Php;
@ -17,7 +25,7 @@ class Preprocessor extends CompilerBase
unset($this->functionCallInFile[$k]); unset($this->functionCallInFile[$k]);
} }
} }
$sorter = new FileSorter($this->functionDeclInFile, $this->functionCallInFile); $sorter = new FileSorter($this->functionDeclInFile, $this->functionCallInFile);
$sortedFiles = $sorter->sort(); $sortedFiles = $sorter->sort();
foreach ($list as $file) { foreach ($list as $file) {
@ -28,41 +36,18 @@ 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 getCppFile(string $file): string public function getCppFile(string $file): string
{ {
$info = pathinfo($file); $info = pathinfo($file);
return $this->buildDir.'/'.$this->removeCommonPrefix($this->buildDir, $info['dirname'].'/'.$info['filename'].'.cc'); return $this->buildDir . '/' . $this->removeCommonPrefix($this->buildDir, $info['dirname'] . '/' . $info['filename'] . '.cc');
} }
public function getObjectFile(string $cppFile): string public function getObjectFile(string $cppFile): string
{ {
$info = pathinfo($cppFile); $info = pathinfo($cppFile);
return $info['dirname'].'/'.$info['filename'].'.o'; return $info['dirname'] . '/' . $info['filename'] . '.o';
} }
public function hasCppFileCache(string $file): bool public function hasCppFileCache(string $file): bool
@ -81,14 +66,14 @@ class Preprocessor extends CompilerBase
public function prepare(string $file): void public function prepare(string $file): void
{ {
if ($this->hasCppFileCache($file)) { if ($this->hasCppFileCache($file)) {
$this->climate->darkGray('skip: '.$file.', cache exists'); $this->climate->darkGray('skip: ' . $file . ', cache exists');
return; return;
} }
$phpCode = $this->loadFile($file); $phpCode = $this->loadFile($file);
$this->climate->info('prepare: '.$this->file); $this->climate->info('prepare: ' . $this->file);
try { try {
$ast = $this->parser->parse($phpCode); $ast = $this->parser->parse($phpCode);
} catch (\PhpParser\Error $e) { } catch (\PhpParser\Error $e) {
@ -111,7 +96,7 @@ class Preprocessor extends CompilerBase
$this->prepareClass($v); $this->prepareClass($v);
break; break;
case 'Stmt_Function': case 'Stmt_Function':
$this->prepareFunction($v).PHP_EOL; $this->prepareFunction($v) . PHP_EOL;
break; break;
case 'Stmt_Declare': case 'Stmt_Declare':
case 'Stmt_Use': case 'Stmt_Use':
@ -120,17 +105,17 @@ class Preprocessor extends CompilerBase
case 'Stmt_Nop': case 'Stmt_Nop':
break; break;
default: default:
$this->fatalError($v, 'Unsupported statement: '.$type); $this->fatalError($v, 'Unsupported statement: ' . $type);
break; break;
} }
} }
$nodeFinder = new NodeFinder(); $nodeFinder = new NodeFinder();
$functionCalls = $nodeFinder->findInstanceOf($ast, Node\Expr\FuncCall::class); $functionCalls = $nodeFinder->findInstanceOf($ast, Node\Expr\FuncCall::class);
foreach ($functionCalls as $call) { foreach ($functionCalls as $call) {
if ($call->name instanceof Node\Name) { if ($call->name instanceof Node\Name) {
$name = $call->name->toString(); $name = $call->name->toString();
$this->functionCallInFile[] = [ $this->functionCallInFile[] = [
'name' => $name, 'name' => $name,
'file' => $this->file, 'file' => $this->file,
@ -140,6 +125,29 @@ class Preprocessor extends CompilerBase
} }
} }
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();
}
protected function prepareFunction(Node $v): void protected function prepareFunction(Node $v): void
{ {
$name = $this->getFunctionName($v); $name = $this->getFunctionName($v);
@ -153,7 +161,7 @@ class Preprocessor extends CompilerBase
protected function prepareClass(Node\Stmt\Class_|Node\Stmt\Trait_ $class): string protected function prepareClass(Node\Stmt\Class_|Node\Stmt\Trait_ $class): string
{ {
$this->class = $this->parseIdentifier($class->name); $this->class = $this->parseIdentifier($class->name);
$code = ''; $code = '';
foreach ($class->stmts as $v) { foreach ($class->stmts as $v) {
$type = $v->getType(); $type = $v->getType();
switch ($type) { switch ($type) {
@ -163,7 +171,7 @@ class Preprocessor extends CompilerBase
case 'Stmt_TraitUse': case 'Stmt_TraitUse':
break; break;
case 'Stmt_ClassMethod': case 'Stmt_ClassMethod':
$code .= $this->prepareFunction($v).PHP_EOL; $code .= $this->prepareFunction($v) . PHP_EOL;
break; break;
default: default:
abort($v); abort($v);

@ -1,4 +1,12 @@
<?php <?php
/**
* This file is part of Swoole-Compiler(AOT).
*
* @link https://www.swoole.com/
* @contact service@swoole.com
*/
declare(strict_types=1);
namespace PhpAot\Php; namespace PhpAot\Php;
@ -7,15 +15,18 @@ use PhpParser\Modifiers;
class PropertyDef class PropertyDef
{ {
public string $name; public string $name;
public string $type; public string $type;
public int $flags; public int $flags;
public ?string $default = null; public ?string $default = null;
public function __construct(string $name, int $flags, string $type, ?string $default = null) public function __construct(string $name, int $flags, string $type, ?string $default = null)
{ {
$this->flags = $flags; $this->flags = $flags;
$this->name = $name; $this->name = $name;
$this->type = $type; $this->type = $type;
$this->default = $default; $this->default = $default;
} }

@ -1,4 +1,12 @@
<?php <?php
/**
* This file is part of Swoole-Compiler(AOT).
*
* @link https://www.swoole.com/
* @contact service@swoole.com
*/
declare(strict_types=1);
namespace PhpAot\Php; namespace PhpAot\Php;

@ -1,4 +1,12 @@
<?php <?php
/**
* This file is part of Swoole-Compiler(AOT).
*
* @link https://www.swoole.com/
* @contact service@swoole.com
*/
declare(strict_types=1);
namespace PhpAot\Php; namespace PhpAot\Php;

@ -1,4 +1,12 @@
<?php <?php
/**
* This file is part of Swoole-Compiler(AOT).
*
* @link https://www.swoole.com/
* @contact service@swoole.com
*/
declare(strict_types=1);
namespace PhpAot\Php; namespace PhpAot\Php;

File diff suppressed because it is too large Load Diff

@ -1,4 +1,12 @@
<?php <?php
/**
* This file is part of Swoole-Compiler(AOT).
*
* @link https://www.swoole.com/
* @contact service@swoole.com
*/
declare(strict_types=1);
namespace PhpAot\Php; namespace PhpAot\Php;

@ -1,4 +1,12 @@
<?php <?php
/**
* This file is part of Swoole-Compiler(AOT).
*
* @link https://www.swoole.com/
* @contact service@swoole.com
*/
declare(strict_types=1);
namespace PhpAot\Php; namespace PhpAot\Php;

Loading…
Cancel
Save