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

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. 12
      src/Php/ClassLikeDef.php
  7. 1444
      src/Php/CompilerBase.php
  8. 11
      src/Php/ConstantDef.php
  9. 8
      src/Php/Constants.php
  10. 87
      src/Php/Encryptor.php
  11. 345
      src/Php/Extractor.php
  12. 44
      src/Php/FileScanner.php
  13. 17
      src/Php/FileSorter.php
  14. 16
      src/Php/FuncCallOptimizer.php
  15. 13
      src/Php/FunctionDef.php
  16. 8
      src/Php/InterfaceDef.php
  17. 20
      src/Php/MagicMethodDetector.php
  18. 10
      src/Php/MethodDef.php
  19. 54
      src/Php/Preprocessor.php
  20. 11
      src/Php/PropertyDef.php
  21. 8
      src/Php/RedoException.php
  22. 8
      src/Php/Reflection.php
  23. 8
      src/Php/SyntaxError.php
  24. 720
      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()}");
}
if (!empty($conds)) {

@ -1,10 +1,20 @@
<?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;
class ArgInfo
{
public string $name;
public string $type;
public string $default = '';
}

@ -1,4 +1,12 @@
<?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;

@ -1,4 +1,12 @@
<?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;
@ -8,17 +16,23 @@ class ClassDef extends ClassLikeDef
* @var array<string, MethodDef>
*/
public array $methods = [];
/**
* @var array<string, PropertyDef>
*/
public array $properties = [];
/**
* @var array<string, ConstantDef>
*/
public array $constants = [];
public array $implements = [];
public string $extends = '';
public bool $requireCtor = false;
public int $flags;
public function __construct(string $name, int $flags, string $namespace = '')

@ -1,11 +1,21 @@
<?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;
class ClassLikeDef
{
public string $name;
public string $namespace;
public string $extends = '';
public function __construct(string $name, string $namespace = '')
@ -16,7 +26,7 @@ class ClassLikeDef
public function getNamespacedName(bool $symbolic = true): string
{
if ('' === $this->namespace) {
if ($this->namespace === '') {
return $this->name;
}
if ($symbolic) {

File diff suppressed because it is too large Load Diff

@ -1,12 +1,23 @@
<?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;
class ConstantDef
{
public string $name;
public string $type;
public string $flags;
public string $value;
public function __construct(string $name, string $flags, string $type, string $value)

@ -1,4 +1,12 @@
<?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;

@ -1,4 +1,12 @@
<?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;
@ -7,14 +15,21 @@ use PhpParser\PrettyPrinter\Standard;
class Encryptor extends \PhpAot\Core\Translator
{
protected array $stmts;
protected string $phpxDir = '~/workspace/phpx';
protected string $lang = 'PHP';
protected array $typeMap = [];
protected array $headers = [
'phpx.h',
];
protected array $encodeMap;
protected array $decodeMap;
protected array $constants;
public function __construct(array $stmts)
@ -64,34 +79,11 @@ class Encryptor extends \PhpAot\Core\Translator
return $node->getType();
}
private function parseFunctionDef($v)
public function compileFile($file)
{
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;
$cmd = 'g++ -c ' . $file . ' -o ' . $file . '.o ' . $this->parseIncludes() . $this->parseLdflags() . $this->parseLibs();
echo $cmd . PHP_EOL;
shell_exec($cmd);
}
protected function parseIdentifier($node)
@ -127,6 +119,36 @@ 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)
{
$list = [];
@ -284,7 +306,7 @@ class Encryptor extends \PhpAot\Core\Translator
{
$items = $node->items;
$list = [];
++$this->indentLevel;
$this->indentLevel++;
foreach ($items as $item) {
if ($item->key) {
$list[] = $this->getIndent() . '{ php::Variant(' . $this->parseIdentifier($item->key) . '), php::Variant(' . $this->parseIdentifier($item->value) . ') }';
@ -292,7 +314,7 @@ class Encryptor extends \PhpAot\Core\Translator
$list[] = $this->getIndent() . 'php::Variant(' . $this->parseIdentifier($item->value) . ')';
}
}
--$this->indentLevel;
$this->indentLevel--;
return '{' . PHP_EOL .
implode(', ' . PHP_EOL, $list) . PHP_EOL .
@ -356,13 +378,6 @@ class Encryptor extends \PhpAot\Core\Translator
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)
{
$left = $this->parseIdentifier($expr->left);

@ -1,10 +1,19 @@
<?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;
class Extractor
{
private string $ctagsPath = 'ctags';
private bool $isUniversalCtags = false;
public function __construct()
@ -12,24 +21,6 @@ class Extractor
$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 以获得更好的支持');
}
}
/**
* 提取函数定义.
*
@ -53,7 +44,7 @@ class Extractor
// 过滤和解析函数
$functions = [];
foreach ($tags as $tag) {
if ('function' !== $tag['kind']) {
if ($tag['kind'] !== 'function') {
continue;
}
@ -84,6 +75,151 @@ class Extractor
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 命令.
*/
@ -97,7 +233,7 @@ class Extractor
$output = shell_exec($cmd);
if (null === $output) {
if ($output === null) {
throw new RuntimeException('ctags 执行失败');
}
@ -111,7 +247,7 @@ class Extractor
}
$tag = json_decode($line, true);
if (null === $tag) {
if ($tag === null) {
continue;
}
@ -167,7 +303,7 @@ class Extractor
{
$lines = file($filename, FILE_IGNORE_NEW_LINES);
if (false === $lines || $lineNum > count($lines)) {
if ($lines === false || $lineNum > count($lines)) {
return '';
}
@ -175,12 +311,12 @@ class Extractor
$signatureLines = [];
$maxLines = min($lineNum + 20, count($lines));
for ($i = $lineNum - 1; $i < $maxLines; ++$i) {
for ($i = $lineNum - 1; $i < $maxLines; $i++) {
$line = $lines[$i];
$signatureLines[] = $line;
// 检查是否到达函数体或声明结束
if (false !== strpos($line, '{') || false !== strpos($line, ';')) {
if (strpos($line, '{') !== false || strpos($line, ';') !== false) {
break;
}
}
@ -195,9 +331,7 @@ class Extractor
$signature = preg_replace('/\s+/', ' ', $signature);
// 清理首尾空白
$signature = trim($signature);
return $signature;
return trim($signature);
}
/**
@ -237,7 +371,7 @@ class Extractor
$paramsStr = trim($matches[1]);
// 空参数或 void
if (empty($paramsStr) || 'void' === $paramsStr) {
if (empty($paramsStr) || $paramsStr === 'void') {
return [];
}
@ -271,16 +405,16 @@ class Extractor
$depth = 0;
$length = strlen($paramsStr);
for ($i = 0; $i < $length; ++$i) {
for ($i = 0; $i < $length; $i++) {
$char = $paramsStr[$i];
if ('<' === $char || '(' === $char || '[' === $char) {
++$depth;
if ($char === '<' || $char === '(' || $char === '[') {
$depth++;
$current .= $char;
} elseif ('>' === $char || ')' === $char || ']' === $char) {
--$depth;
} elseif ($char === '>' || $char === ')' || $char === ']') {
$depth--;
$current .= $char;
} elseif (',' === $char && 0 === $depth) {
} elseif ($char === ',' && $depth === 0) {
$params[] = $current;
$current = '';
} else {
@ -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,21 +480,6 @@ class Extractor
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;
}
/**
* 提取函数的元数据(注释、属性等).
*/
@ -405,8 +505,8 @@ class Extractor
// 检测修饰符
$signature = $func['signature'];
$metadata['isStatic'] = false !== strpos($signature, 'static');
$metadata['isInline'] = false !== strpos($signature, 'inline');
$metadata['isStatic'] = strpos($signature, 'static') !== false;
$metadata['isInline'] = strpos($signature, 'inline') !== false;
// 提取文档注释中的标签
$metadata['docTags'] = $this->parseDocTags($comments);
@ -428,21 +528,21 @@ class Extractor
// 空行
if (empty($line)) {
--$i;
$i--;
continue;
}
// C++ 风格注释
if (str_starts_with($line, '//')) {
array_unshift($comments, substr($line, 2));
--$i;
$i--;
continue;
}
// C 风格注释结束
if (str_ends_with($line, '*/')) {
$commentLines = [$line];
--$i;
$i--;
// 继续向上查找注释开始
while ($i >= 0) {
@ -452,7 +552,7 @@ class Extractor
if (str_starts_with($commentLine, '/*')) {
break;
}
--$i;
$i--;
}
// 解析多行注释
@ -462,7 +562,7 @@ class Extractor
$comment = preg_replace('#^\s*\*\s?#m', '', $comment);
array_unshift($comments, trim($comment));
--$i;
$i--;
continue;
}
@ -486,7 +586,7 @@ class Extractor
];
foreach ($phpMacros as $macro) {
if (false !== strpos($signature, $macro)) {
if (strpos($signature, $macro) !== false) {
return true;
}
}
@ -519,97 +619,4 @@ class Extractor
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,19 +1,29 @@
<?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;
class FileScanner
{
private string $directory;
private array $excludePatterns;
public const array PHP_EXT = ['php'];
public const array CPP_EXT = ['cpp', 'cxx', 'cc'];
private string $directory;
private array $excludePatterns;
public function __construct(string $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);
@ -59,19 +69,6 @@ class FileScanner
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
{
$files = [];
@ -97,6 +94,19 @@ class FileScanner
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
{
return fnmatch($pattern, $path, FNM_PATHNAME);

@ -1,10 +1,19 @@
<?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;
class FileSorter
{
private array $functionDeclInFile;
private array $functionCallInFile;
public function __construct(array $functionDeclInFile, array $functionCallInFile)
@ -22,13 +31,13 @@ class FileSorter
$inDegree = array_fill_keys($allFiles, 0);
foreach ($dependencies as $deps) {
foreach ($deps as $dep) {
++$inDegree[$dep];
$inDegree[$dep]++;
}
}
$queue = [];
foreach ($inDegree as $file => $degree) {
if (0 === $degree) {
if ($degree === 0) {
$queue[] = $file;
}
}
@ -40,8 +49,8 @@ class FileSorter
if (isset($dependencies[$current])) {
foreach ($dependencies[$current] as $dep) {
--$inDegree[$dep];
if (0 === $inDegree[$dep]) {
$inDegree[$dep]--;
if ($inDegree[$dep] === 0) {
$queue[] = $dep;
}
}

@ -1,4 +1,12 @@
<?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;
@ -8,10 +16,10 @@ trait FuncCallOptimizer
{
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) . ')';
}
if (1 == count($expr->args)) {
if (count($expr->args) == 1) {
switch ($name) {
case 'intval':
return $this->convertIntExpr($this->parseExpr($expr->args[0]->value));
@ -24,7 +32,7 @@ trait FuncCallOptimizer
default:
break;
}
} elseif (2 == count($expr->args)) {
} elseif (count($expr->args) == 2) {
switch ($name) {
case 'objval':
$arg1 = $expr->args[0]->value;
@ -35,7 +43,7 @@ trait FuncCallOptimizer
break;
}
}
if ('abs' === $name) {
if ($name === 'abs') {
return 'php::math::abs(' . $this->parseIdentifier($expr->args[0]->value) . ')';
}

@ -1,17 +1,30 @@
<?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;
class FunctionDef
{
public string $name;
public string $returnType;
/**
* @var array<ArgInfo>
*/
public array $argInfoList = [];
public int $argCountRequired = 0;
public string $params = '';
public bool $method = false;
public function __construct(string $name, string $returnType)

@ -1,4 +1,12 @@
<?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;

@ -1,4 +1,12 @@
<?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;
@ -8,13 +16,13 @@ trait MagicMethodDetector
{
public function checkRequiredArgNum(string $name, MethodDef $methodDef, NodeAbstract $v): void
{
if ('__call' == $name or '__callStatic' == $name or '__set' == $name) {
if (2 != count($methodDef->functionDef->argInfoList)) {
$this->fatalError($v, 'Method '.$this->class."::$name() must take exactly 2 arguments");
if ($name == '__call' or $name == '__callStatic' or $name == '__set') {
if (count($methodDef->functionDef->argInfoList) != 2) {
$this->fatalError($v, 'Method ' . $this->class . "::{$name}() must take exactly 2 arguments");
}
} elseif ('__get' == $name) {
if (1 != count($methodDef->functionDef->argInfoList)) {
$this->fatalError($v, 'Method '.$this->class."::$name() must take exactly 1 argument");
} elseif ($name == '__get') {
if (count($methodDef->functionDef->argInfoList) != 1) {
$this->fatalError($v, 'Method ' . $this->class . "::{$name}() must take exactly 1 argument");
}
}
}

@ -1,11 +1,21 @@
<?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;
class MethodDef
{
public int $flags;
public string $name;
public FunctionDef $functionDef;
public function __construct(int $flags, string $name, FunctionDef $def)

@ -1,4 +1,12 @@
<?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;
@ -28,29 +36,6 @@ 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 getCppFile(string $file): string
{
$info = pathinfo($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
{
$name = $this->getFunctionName($v);

@ -1,4 +1,12 @@
<?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;
@ -7,8 +15,11 @@ use PhpParser\Modifiers;
class PropertyDef
{
public string $name;
public string $type;
public int $flags;
public ?string $default = null;
public function __construct(string $name, int $flags, string $type, ?string $default = null)

@ -1,4 +1,12 @@
<?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;

@ -1,4 +1,12 @@
<?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;

@ -1,4 +1,12 @@
<?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;

@ -1,4 +1,12 @@
<?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;
@ -14,8 +22,11 @@ class Translator extends Preprocessor
use MagicMethodDetector;
protected string $targetName = 'app';
protected bool $verbose = false;
protected array $phpSrcFiles = [];
protected array $argInfoHeaderFiles = [];
protected array $unsupportedFunctions = [
@ -104,62 +115,246 @@ class Translator extends Preprocessor
}
}
protected function getRegisterClassFunction(string $name): string
public function getRegisterClassFunctionArgs(ClassDef|InterfaceDef $classDef): string
{
return self::PREFIX.'register_class_'.$name;
return implode(', ', $this->getRegisterClassFunctionCeList($classDef));
}
protected function getRegisterClassFunctionCeList(ClassDef|InterfaceDef $classDef): array
public function setTargetName(string $name): void
{
$list = [];
$parentCe = $this->getParentClassCe($classDef);
if ('' !== $parentCe) {
$list = [$parentCe];
if ($this->climate->arguments->defined('output')) {
$name = $this->climate->arguments->get('output');
}
// interface 没有 implements
if ($classDef instanceof InterfaceDef) {
$name = str_replace(['-', '*'], '_', $name);
if (!preg_match('/^[a-zA-Z0-9_]+$/', $name)) {
$this->climate->red('The target name must be a valid identifier');
exit(1);
}
if (in_array($name, Constants::CPP_RESERVED_NAMES)) {
$this->climate->red('The target name must not be a reserved keyword');
exit(1);
}
$this->targetName = $name;
}
public function getFiles(string $path): array
{
$realpath = realpath($path);
if ($realpath === false) {
exit("path not exists: {$path}\n");
}
$path = $realpath;
if (is_dir($path)) {
$list = $this->getFilesFromDir($path);
$targetName = basename($path);
$this->setTargetName($targetName);
} else {
$ext = pathinfo($path, PATHINFO_EXTENSION);
if ($ext === 'yml') {
$list = $this->parseProjectYaml($path);
} elseif ($ext === 'php') {
$list = [$path];
$targetName = FileScanner::getFileName($path);
$this->setTargetName($targetName);
} else {
$this->error('Unsupported file type: ' . $path);
}
}
return $list;
}
$implements = $this->getImplementCe($classDef);
return array_merge($list, $implements);
public function preprocessArgvAdvanced(): void
{
global $argv;
$processed = [$argv[0]];
for ($i = 1; $i < count($argv); $i++) {
$arg = $argv[$i];
if (preg_match('/^-([a-zA-Z])(.+)$/', $arg, $matches)) {
$option = $matches[1];
$value = $matches[2];
$processed[] = "-{$option}";
$processed[] = $value;
} elseif (preg_match('/^-([a-zA-Z]{2,})$/', $arg, $matches)) {
$options = str_split($matches[1]);
foreach ($options as $opt) {
$processed[] = "-{$opt}";
}
} else {
$processed[] = $arg;
}
}
$argv = $processed;
}
public function getRegisterClassFunctionArgs(ClassDef|InterfaceDef $classDef): string
public function genExternGlobalVars(string $file): void
{
return implode(', ', $this->getRegisterClassFunctionCeList($classDef));
$lines[] = '#include <phpx.h>';
$lines[] = PHP_EOL;
foreach ($this->globalVars as $name => $type) {
$lines[] = 'extern ' . self::TYPE_VAR . ' ' . $name . ';';
}
private function getRegisterClassFunctionArgDef(ClassDef|InterfaceDef $classDef): string
// property offset
foreach ($this->classes as $classDef) {
foreach ($classDef->properties as $propertyDef) {
$lines[] = 'extern uint32_t ' . self::PREFIX . $this->getPropertyOffset($propertyDef->name, $classDef->name, $classDef->namespace) . ';';
}
}
$literalStringsCount = count($this->literalStrings);
$lines[] = 'extern php::Var ' . self::LITERAL_STRINGS . '[' . $literalStringsCount . '];' . PHP_EOL;
$classEntryCount = count($this->classMap);
$lines[] = 'extern zend_class_entry *' . self::PREFIX . self::CLASS_ENTRY_MAP . '[' . $classEntryCount . '];' . PHP_EOL;
$code = implode(PHP_EOL, $lines) . PHP_EOL . PHP_EOL;
$this->writeFile($file, $code);
}
public function genExtension(string $file): void
{
$depsCeList = $this->getRegisterClassFunctionCeList($classDef);
if (empty($depsCeList)) {
return '';
if ($this->buildMode == 'bin') {
if (!isset($this->nativeFunctions['main'])) {
$this->climate->red('When the build mode is a binary executable file, the `main()` function must be defined');
exit(1);
}
}
$this->localHeaders = $this->argInfoHeaderFiles;
$this->genClassCeList();
$code = $this->render('extension.cc.php');
$this->writeFile($file, $code);
$this->formatCppCode($file);
$this->localHeaders = [];
}
return 'zend_class_entry *'.implode(', zend_class_entry *', $depsCeList);
public function hasObjectFileCache(string $cppFile): bool
{
if (!$this->enableCache or $this->climate->arguments->defined('force')) {
return false;
}
$objectFile = $this->getObjectFile($cppFile);
if (file_exists($objectFile) and filemtime($objectFile) > filemtime($cppFile)) {
return true;
}
protected function getClassCe(ClassLikeDef $classDef): string
return false;
}
public function compileFile(string $cppFile, string $objectFile): void
{
return self::PREFIX.'class_entry_'.$classDef->getNamespacedName();
if ($this->hasObjectFileCache($cppFile)) {
$this->climate->darkGray('skip: ' . $cppFile . ', cache exists');
return;
}
$cmd = $this->cppCompiler . ' -c ' . $cppFile . ' -o ' . $objectFile;
$this->addCompilationOption($cmd, false);
$this->climate->comment($cmd);
shell_exec($cmd);
}
public function setTargetName(string $name): void
public function build(array $objectFiles): void
{
if ($this->climate->arguments->defined('output')) {
$name = $this->climate->arguments->get('output');
$objectList = implode(' ', $objectFiles);
$targetFile = $this->targetName;
if ($this->buildMode == 'ext' and !str_ends_with($targetFile, '.so')) {
$targetFile .= '.so';
}
$name = str_replace(['-', '*'], '_', $name);
if (!preg_match('/^[a-zA-Z0-9_]+$/', $name)) {
$this->climate->red('The target name must be a valid identifier');
exit(1);
$linkCmd = $this->cppCompiler . ' ' . $objectList . ' -o ' . $targetFile;
$this->addCompilationOption($linkCmd, true);
$this->climate->comment($linkCmd);
shell_exec($linkCmd);
}
if (in_array($name, Constants::CPP_RESERVED_NAMES)) {
$this->climate->red('The target name must not be a reserved keyword');
exit(1);
public function genFunctionDeclaration(string $file): void
{
$code = '#include <phpx.h>' . PHP_EOL;
/**
* @var FunctionDef $func
*/
foreach ($this->nativeFunctions as $name => $func) {
$code .= 'extern ' . $func->returnType . ' ' . self::PREFIX . $name . '(';
$argInfoList = $func->argInfoList;
if ($argInfoList) {
$list = [];
if ($func->method) {
$list[] = 'php::Object &this_';
}
$this->targetName = $name;
foreach ($argInfoList as $argInfo) {
$arg = $argInfo->type . ' ' . $argInfo->name;
if ($argInfo->default) {
$arg .= ' = ' . $argInfo->default;
}
$list[] = $arg;
}
$code .= implode(', ', $list);
}
$code .= ');' . PHP_EOL;
}
$code .= PHP_EOL;
foreach ($this->nativeConstants as $name => $constant) {
$code .= 'extern ' . $constant->type . ' ' . $name . ';' . PHP_EOL;
}
$code .= 'extern zend_class_entry *php_get_class_entry(int class_id, const char *class_name);' . PHP_EOL;
$this->writeFile($file, $code);
}
public function getBuildMode(): string
{
return $this->buildMode;
}
public function getArgInfoHeaderFile(string $stubFilenameWithoutExtension, bool $relative = false): string
{
$basename = self::PREFIX . basename($stubFilenameWithoutExtension);
$absPath = $this->getIncludeDir() . "/{$basename}_arginfo.h";
if ($relative) {
return ltrim($this->removeCommonPrefix($this->getIncludeDir(), $absPath), '/');
}
return $absPath;
}
public function genIncludeHeaderFiles(): string
{
$headers = array_merge($this->globalHeaders, $this->localHeaders);
$lines = [];
foreach ($headers as $header) {
$lines[] = '#include <' . $header . '>';
}
return implode(PHP_EOL, $lines) . PHP_EOL . PHP_EOL;
}
protected function getRegisterClassFunction(string $name): string
{
return self::PREFIX . 'register_class_' . $name;
}
protected function getRegisterClassFunctionCeList(ClassDef|InterfaceDef $classDef): array
{
$list = [];
$parentCe = $this->getParentClassCe($classDef);
if ($parentCe !== '') {
$list = [$parentCe];
}
// interface 没有 implements
if ($classDef instanceof InterfaceDef) {
return $list;
}
$implements = $this->getImplementCe($classDef);
return array_merge($list, $implements);
}
protected function getClassCe(ClassLikeDef $classDef): string
{
return self::PREFIX . 'class_entry_' . $classDef->getNamespacedName();
}
protected function getFilesFromDir(string $path): array
@ -179,7 +374,7 @@ class Translator extends Preprocessor
$list = [];
foreach ($sources as $src) {
$src = trim($src);
if ('/' != $src[0]) {
if ($src[0] != '/') {
$absPath = $projectDir . '/' . $src;
} else {
$absPath = $src;
@ -219,34 +414,6 @@ class Translator extends Preprocessor
return $list;
}
public function getFiles(string $path): array
{
$realpath = realpath($path);
if (false === $realpath) {
exit("path not exists: $path\n");
}
$path = $realpath;
if (is_dir($path)) {
$list = $this->getFilesFromDir($path);
$targetName = basename($path);
$this->setTargetName($targetName);
} else {
$ext = pathinfo($path, PATHINFO_EXTENSION);
if ('yml' === $ext) {
$list = $this->parseProjectYaml($path);
} elseif ('php' === $ext) {
$list = [$path];
$targetName = FileScanner::getFileName($path);
$this->setTargetName($targetName);
} else {
$this->error('Unsupported file type: '.$path);
}
}
return $list;
}
protected function getInternalCeInfo(string $ce): array
{
return [
@ -264,16 +431,6 @@ class Translator extends Preprocessor
return self::PREFIX . 'class_entry_' . $classDef->extends;
}
private function getImplementCe(ClassDef $classDef): array
{
$list = [];
foreach ($classDef->implements as $interface) {
$list[] = self::PREFIX.'class_entry_'.$interface;
}
return $list;
}
protected function doConvert(string $phpCode): string
{
$this->climate->info('convert: ' . $this->file);
@ -337,63 +494,6 @@ class Translator extends Preprocessor
return $this->genIncludeHeaderFiles() . $cppCode;
}
public function preprocessArgvAdvanced(): void
{
global $argv;
$processed = [$argv[0]];
for ($i = 1; $i < count($argv); ++$i) {
$arg = $argv[$i];
if (preg_match('/^-([a-zA-Z])(.+)$/', $arg, $matches)) {
$option = $matches[1];
$value = $matches[2];
$processed[] = "-{$option}";
$processed[] = $value;
} elseif (preg_match('/^-([a-zA-Z]{2,})$/', $arg, $matches)) {
$options = str_split($matches[1]);
foreach ($options as $opt) {
$processed[] = "-{$opt}";
}
} else {
$processed[] = $arg;
}
}
$argv = $processed;
}
public function genExternGlobalVars(string $file): void
{
$lines[] = '#include <phpx.h>';
$lines[] = PHP_EOL;
foreach ($this->globalVars as $name => $type) {
$lines[] = 'extern '.self::TYPE_VAR.' '.$name.';';
}
// property offset
foreach ($this->classes as $classDef) {
foreach ($classDef->properties as $propertyDef) {
$lines[] = 'extern uint32_t '.self::PREFIX.$this->getPropertyOffset($propertyDef->name, $classDef->name, $classDef->namespace).';';
}
}
$literalStringsCount = count($this->literalStrings);
$lines[] = 'extern php::Var '.self::LITERAL_STRINGS.'['.$literalStringsCount.'];'.PHP_EOL;
$classEntryCount = count($this->classMap);
$lines[] = 'extern zend_class_entry *'.self::PREFIX.self::CLASS_ENTRY_MAP.'['.$classEntryCount.'];'.PHP_EOL;
$code = implode(PHP_EOL, $lines).PHP_EOL.PHP_EOL;
$this->writeFile($file, $code);
}
private function render(string $template): string
{
ob_start();
include __DIR__.'/../template/'.$template;
return ob_get_clean();
}
protected function genClassCeList(): void
{
if (empty($this->interfaces) and empty($this->classes)) {
@ -449,107 +549,16 @@ class Translator extends Preprocessor
}
}
$this->classCeInfo[$ce] = [
'deps' => $deps,
'func' => $this->getRegisterClassFunction($classDef->getNamespacedName()),
'args' => $this->getRegisterClassFunctionArgs($classDef),
'argDef' => $this->getRegisterClassFunctionArgDef($classDef),
];
$sorter->add($ce, $deps);
}
$this->classCeList = $sorter->sort();
}
public function genExtension(string $file): void
{
if ('bin' == $this->buildMode) {
if (!isset($this->nativeFunctions['main'])) {
$this->climate->red('When the build mode is a binary executable file, the `main()` function must be defined');
exit(1);
}
}
$this->localHeaders = $this->argInfoHeaderFiles;
$this->genClassCeList();
$code = $this->render('extension.cc.php');
$this->writeFile($file, $code);
$this->formatCppCode($file);
$this->localHeaders = [];
}
public function hasObjectFileCache(string $cppFile): bool
{
if (!$this->enableCache or $this->climate->arguments->defined('force')) {
return false;
}
$objectFile = $this->getObjectFile($cppFile);
if (file_exists($objectFile) and filemtime($objectFile) > filemtime($cppFile)) {
return true;
}
return false;
}
public function compileFile(string $cppFile, string $objectFile): void
{
if ($this->hasObjectFileCache($cppFile)) {
$this->climate->darkGray('skip: '.$cppFile.', cache exists');
return;
}
$cmd = $this->cppCompiler.' -c '.$cppFile.' -o '.$objectFile;
$this->addCompilationOption($cmd, false);
$this->climate->comment($cmd);
shell_exec($cmd);
}
public function build(array $objectFiles): void
{
$objectList = implode(' ', $objectFiles);
$targetFile = $this->targetName;
if ('ext' == $this->buildMode and !str_ends_with($targetFile, '.so')) {
$targetFile .= '.so';
}
$linkCmd = $this->cppCompiler.' '.$objectList.' -o '.$targetFile;
$this->addCompilationOption($linkCmd, true);
$this->climate->comment($linkCmd);
shell_exec($linkCmd);
}
public function genFunctionDeclaration(string $file): void
{
$code = '#include <phpx.h>'.PHP_EOL;
/**
* @var FunctionDef $func
*/
foreach ($this->nativeFunctions as $name => $func) {
$code .= 'extern '.$func->returnType.' '.self::PREFIX.$name.'(';
$argInfoList = $func->argInfoList;
if ($argInfoList) {
$list = [];
if ($func->method) {
$list[] = 'php::Object &this_';
}
foreach ($argInfoList as $argInfo) {
$arg = $argInfo->type.' '.$argInfo->name;
if ($argInfo->default) {
$arg .= ' = '.$argInfo->default;
}
$list[] = $arg;
}
$code .= implode(', ', $list);
}
$code .= ');'.PHP_EOL;
}
$code .= PHP_EOL;
foreach ($this->nativeConstants as $name => $constant) {
$code .= 'extern '.$constant->type.' '.$name.';'.PHP_EOL;
$this->classCeInfo[$ce] = [
'deps' => $deps,
'func' => $this->getRegisterClassFunction($classDef->getNamespacedName()),
'args' => $this->getRegisterClassFunctionArgs($classDef),
'argDef' => $this->getRegisterClassFunctionArgDef($classDef),
];
$sorter->add($ce, $deps);
}
$code .= 'extern zend_class_entry *php_get_class_entry(int class_id, const char *class_name);'.PHP_EOL;
$this->writeFile($file, $code);
$this->classCeList = $sorter->sort();
}
protected function getMethodName(Node\Stmt\ClassMethod $v): string
@ -572,7 +581,7 @@ class Translator extends Preprocessor
if ($this->useCppNamespace) {
$ns = explode('\\', $ns);
$ns = array_filter($ns, function ($v) {
return '' !== $v;
return $v !== '';
});
foreach ($ns as $name) {
$code .= 'namespace ' . $name . ' {' . PHP_EOL;
@ -618,7 +627,7 @@ class Translator extends Preprocessor
$stubFilenameWithoutExtension = str_replace(['.stub.php', '.php'], '', $file);
$headerFile = $this->getArgInfoHeaderFile($stubFilenameWithoutExtension, true);
if (!str_contains($output, 'Saved')) {
$this->error("failed to generate arginfo header file: `$headerFile`, output: $output");
$this->error("failed to generate arginfo header file: `{$headerFile}`, output: {$output}");
}
$this->argInfoHeaderFiles[] = $headerFile;
}
@ -671,22 +680,6 @@ class Translator extends Preprocessor
return $code;
}
public function getBuildMode(): string
{
return $this->buildMode;
}
public function getArgInfoHeaderFile(string $stubFilenameWithoutExtension, bool $relative = false): string
{
$basename = self::PREFIX.basename($stubFilenameWithoutExtension);
$absPath = $this->getIncludeDir()."/{$basename}_arginfo.h";
if ($relative) {
return ltrim($this->removeCommonPrefix($this->getIncludeDir(), $absPath), '/');
} else {
return $absPath;
}
}
protected function genNativeMethod($methodCodes): string
{
$code = '';
@ -720,7 +713,7 @@ class Translator extends Preprocessor
$callParams = $functionDef->argInfoList ? rtrim($callParams, ',') : '';
}
if (self::TYPE_VOID !== $functionDef->returnType) {
if ($functionDef->returnType !== self::TYPE_VOID) {
$cppCode .= $this->getIndent() . 'auto retval = ' . $fn . '(' . $callParams . ');' . PHP_EOL;
$cppCode .= $this->getIndent() . 'php::move(retval, return_value);' . PHP_EOL;
} else {
@ -738,7 +731,7 @@ class Translator extends Preprocessor
$cppCode .= $this->getIndent() . self::TYPE_OBJECT . ' this_(&execute_data->This);' . PHP_EOL;
foreach ($classDef->properties as $property) {
if (self::TYPE_ARRAY === $property->type and $property->default and $property->default !== self::TYPE_ARRAY.'{}') {
if ($property->type === self::TYPE_ARRAY and $property->default and $property->default !== self::TYPE_ARRAY . '{}') {
$propOffset = self::PREFIX . $this->getPropertyOffset($property->name, $classDef->name, $classDef->namespace);
$cppCode .= $this->getIndent() . 'this_.getPropertyIndirect(' . $propOffset . ') = ' . $property->default . ';' . PHP_EOL;
}
@ -750,16 +743,6 @@ class Translator extends Preprocessor
return $cppCode;
}
private function genFunctionWrapper(FunctionDef $functionDef): string
{
$name = $functionDef->name;
$cppCode = 'ZEND_FUNCTION('.$name.'){'.PHP_EOL;
$fn = self::PREFIX.$this->getNativeName($functionDef->name);
$cppCode .= $this->genWrapperFunctionArgs($fn, $functionDef);
return $cppCode;
}
protected function getClassRegisterCeFunc(ClassDef|InterfaceDef $classDef): void
{
$cppCode = '';
@ -786,100 +769,6 @@ class Translator extends Preprocessor
return $cppCode;
}
private function genClassNative(): string
{
$code = 'class '.$this->class.' { ';
$publicMethods = [];
$protectedMethods = [];
$privateMethods = [];
$publicConstants = [];
$protectedConstants = [];
$privateConstants = [];
$publicProperties = [];
$protectedProperties = [];
$privateProperties = [];
foreach ($this->classDef->constants as $const) {
if ($const->flags & Modifiers::PUBLIC) {
$publicConstants[] = $const;
}
if ($const->flags & Modifiers::PROTECTED) {
$protectedConstants[] = $const;
}
if ($const->flags & Modifiers::PRIVATE) {
$privateConstants[] = $const;
}
}
foreach ($this->classDef->methods as $method) {
if ($method->flags & Modifiers::PUBLIC) {
$publicMethods[] = $method;
}
if ($method->flags & Modifiers::PROTECTED) {
$protectedMethods[] = $method;
}
if ($method->flags & Modifiers::PRIVATE) {
$privateMethods[] = $method;
}
}
foreach ($this->classDef->properties as $property) {
if ($property->flags & Modifiers::PUBLIC) {
$publicProperties[] = $property;
}
if ($property->flags & Modifiers::PROTECTED) {
$protectedProperties[] = $property;
}
if ($property->flags & Modifiers::PRIVATE) {
$privateProperties[] = $property;
}
}
if ($privateConstants) {
$code .= 'private:'.PHP_EOL;
$code .= $this->genClassConstantList($privateConstants);
}
if ($protectedConstants) {
$code .= 'protected:'.PHP_EOL;
$code .= $this->genClassConstantList($protectedConstants);
}
if ($publicConstants) {
$code .= 'public:'.PHP_EOL;
$code .= $this->genClassConstantList($publicConstants);
}
if ($privateProperties) {
$code .= 'private:'.PHP_EOL;
$code .= $this->genClassPropertyList($privateProperties);
}
if ($protectedProperties) {
$code .= 'protected:'.PHP_EOL;
$code .= $this->genClassPropertyList($protectedProperties);
}
if ($publicProperties) {
$code .= 'public:'.PHP_EOL;
$code .= $this->genClassPropertyList($publicProperties);
}
$code .= '};'.PHP_EOL.PHP_EOL;
return $code;
}
public function genIncludeHeaderFiles(): string
{
$headers = array_merge($this->globalHeaders, $this->localHeaders);
$lines = [];
foreach ($headers as $header) {
$lines[] = '#include <'.$header.'>';
}
return implode(PHP_EOL, $lines).PHP_EOL.PHP_EOL;
}
/**
* @param array<ConstantDef> $list
*/
@ -956,7 +845,7 @@ class Translator extends Preprocessor
foreach ($v->props as $prop) {
$propDef = new PropertyDef($this->parseIdentifier($prop->name), $flags, $type);
if ($prop->default) {
if ('Expr_Array' == $prop->default->getType() and count($prop->default->items) > 0) {
if ($prop->default->getType() == 'Expr_Array' and count($prop->default->items) > 0) {
$this->classDef->requireCtor = true;
$propDef->type = self::TYPE_ARRAY;
}
@ -1017,10 +906,10 @@ class Translator extends Preprocessor
$code .= 'if (' . $tmpVar . ') {' . PHP_EOL;
++$this->indentLevel;
$this->indentLevel++;
$code .= $this->getIndent() . $tmpVar . '.exec("rewind");' . PHP_EOL;
$code .= $this->getIndent() . 'for (;' . $tmpVar . '.exec("valid"); ' . $tmpVar . '.exec("next")) {' . PHP_EOL;
++$this->indentLevel;
$this->indentLevel++;
$valueVar = $this->parseIdentifier($node->valueVar);
$this->checkVar($node, $valueVar);
@ -1033,13 +922,134 @@ class Translator extends Preprocessor
}
$code .= $this->parseStmts($node->stmts);
$code .= '}' . PHP_EOL;
--$this->indentLevel;
$this->indentLevel--;
$code .= $this->getIndent() . '} else {' . PHP_EOL;
$code .= $this->getIndent() . $tmpArrayVar . ' = php::call("get_object_vars", {' . $obj . '});' . PHP_EOL;
$code .= $this->parseForeachArray($node, $tmpArrayVar);
--$this->indentLevel;
$this->indentLevel--;
$code .= '}' . PHP_EOL;
return $code;
}
private function getRegisterClassFunctionArgDef(ClassDef|InterfaceDef $classDef): string
{
$depsCeList = $this->getRegisterClassFunctionCeList($classDef);
if (empty($depsCeList)) {
return '';
}
return 'zend_class_entry *' . implode(', zend_class_entry *', $depsCeList);
}
private function getImplementCe(ClassDef $classDef): array
{
$list = [];
foreach ($classDef->implements as $interface) {
$list[] = self::PREFIX . 'class_entry_' . $interface;
}
return $list;
}
private function render(string $template): string
{
ob_start();
include __DIR__ . '/../template/' . $template;
return ob_get_clean();
}
private function genFunctionWrapper(FunctionDef $functionDef): string
{
$name = $functionDef->name;
$cppCode = 'ZEND_FUNCTION(' . $name . '){' . PHP_EOL;
$fn = self::PREFIX . $this->getNativeName($functionDef->name);
$cppCode .= $this->genWrapperFunctionArgs($fn, $functionDef);
return $cppCode;
}
private function genClassNative(): string
{
$code = 'class ' . $this->class . ' { ';
$publicMethods = [];
$protectedMethods = [];
$privateMethods = [];
$publicConstants = [];
$protectedConstants = [];
$privateConstants = [];
$publicProperties = [];
$protectedProperties = [];
$privateProperties = [];
foreach ($this->classDef->constants as $const) {
if ($const->flags & Modifiers::PUBLIC) {
$publicConstants[] = $const;
}
if ($const->flags & Modifiers::PROTECTED) {
$protectedConstants[] = $const;
}
if ($const->flags & Modifiers::PRIVATE) {
$privateConstants[] = $const;
}
}
foreach ($this->classDef->methods as $method) {
if ($method->flags & Modifiers::PUBLIC) {
$publicMethods[] = $method;
}
if ($method->flags & Modifiers::PROTECTED) {
$protectedMethods[] = $method;
}
if ($method->flags & Modifiers::PRIVATE) {
$privateMethods[] = $method;
}
}
foreach ($this->classDef->properties as $property) {
if ($property->flags & Modifiers::PUBLIC) {
$publicProperties[] = $property;
}
if ($property->flags & Modifiers::PROTECTED) {
$protectedProperties[] = $property;
}
if ($property->flags & Modifiers::PRIVATE) {
$privateProperties[] = $property;
}
}
if ($privateConstants) {
$code .= 'private:' . PHP_EOL;
$code .= $this->genClassConstantList($privateConstants);
}
if ($protectedConstants) {
$code .= 'protected:' . PHP_EOL;
$code .= $this->genClassConstantList($protectedConstants);
}
if ($publicConstants) {
$code .= 'public:' . PHP_EOL;
$code .= $this->genClassConstantList($publicConstants);
}
if ($privateProperties) {
$code .= 'private:' . PHP_EOL;
$code .= $this->genClassPropertyList($privateProperties);
}
if ($protectedProperties) {
$code .= 'protected:' . PHP_EOL;
$code .= $this->genClassPropertyList($protectedProperties);
}
if ($publicProperties) {
$code .= 'public:' . PHP_EOL;
$code .= $this->genClassPropertyList($publicProperties);
}
$code .= '};' . PHP_EOL . PHP_EOL;
return $code;
}
}

@ -1,4 +1,12 @@
<?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;

@ -1,4 +1,12 @@
<?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;

Loading…
Cancel
Save