refactor(compiler): 重构PHP到C++编译器实现

- 修改编译流程,添加对象文件路径管理功能
- 实现缓存检查机制,跳过已存在的编译文件
- 重命名MethodDef中的def属性为functionDef
- 提取表达式类型转换逻辑到独立方法convertExprFromType
- 添加扩展模板文件extension.cc.php,使用模板渲染替代手动代码生成
- 修改main.cc文件结构,使用模块注册方式替代函数注册
- 更新编译参数,添加-Wall编译选项
- 实现方法调用参数传递的动态构建机制
- 优化全局变量初始化和清理函数的实现
pull/1/head
韩天峰 7 months ago
parent e4505c2ef1
commit f3944a5259
  1. 9
      bin/compiler.php
  2. 28
      src/Php/CompilerBase.php
  3. 11
      src/Php/MethodDef.php
  4. 129
      src/Php/Translator.php
  5. 30
      src/cpp/main.cc
  6. 120
      src/template/extension.cc.php

@ -34,6 +34,11 @@ $objectFiles = [];
// 分析 PHP 文件,预处理
foreach ($list as $k => $file) {
// if ($translator->hasCache($file)) {
// echo " skip: " . $file . ", cache exists\n";
// unset($list[$k]);
// continue;
// }
if (FileScanner::isPhpFile($file)) {
try {
$translator->prepare($file);
@ -83,8 +88,8 @@ $sourceFiles[] = ROOT_PATH . '/src/cpp/main.cc';
// 编译所有 C++ 文件
foreach ($sourceFiles as $cppFile) {
$translator->compileFile($cppFile);
$objectFile = $cppFile . '.o';
$objectFile = $translator->getObjectFile($cppFile);
$translator->compileFile($cppFile, $objectFile);
if (!is_file($objectFile)) {
throw new Exception("compile error");
}

@ -1046,9 +1046,10 @@ class CompilerBase extends \PhpAot\Core\Translator
protected function addCompilationOption(string &$cmd): void
{
$cmd .= $this->parseIncludes();
$cmd .= ' ' . $this->parseIncludes();
$cmd .= ' -O' . $this->optimizeLevel;
$cmd .= ' -g';
$cmd .= ' -Wall';
if ($this->climate->arguments->defined('profile')) {
$cmd .= ' -lprofiler';
$cmd .= ' -DPPROF_ON=1';
@ -2131,19 +2132,24 @@ class CompilerBase extends \PhpAot\Core\Translator
}
}
protected function convertExprFromType(string $type, string $expr): string
{
if ($type === self::TYPE_FLOAT) {
return $this->convertFloatExpr($expr);
}
if ($type === self::TYPE_INT) {
return $this->convertIntExpr($expr);
}
if ($type === self::TYPE_BOOL) {
return $this->convertBoolExpr($expr);
}
return $expr;
}
protected function convertVarType($var, $expr): string
{
if ($this->hasVar($var)) {
$type = $this->getVarType($var);
if ($type === self::TYPE_FLOAT) {
return $this->convertFloatExpr($expr);
}
if ($type === self::TYPE_INT) {
return $this->convertIntExpr($expr);
}
if ($type === self::TYPE_BOOL) {
return $this->convertBoolExpr($expr);
}
return $this->convertExprFromType($this->getVarType($var), $expr);
}
return $expr;
}

@ -6,22 +6,17 @@ class MethodDef
{
public int $flags;
public string $name;
public FunctionDef $def;
public FunctionDef $functionDef;
public function __construct(int $flags, string $name, FunctionDef $def)
{
$this->flags = $flags;
$this->name = $name;
$this->def = $def;
}
public function getFlags(): int
{
return $this->flags;
$this->functionDef = $def;
}
public function getReturnType(): string
{
return $this->def->returnType;
return $this->functionDef->returnType;
}
}

@ -88,6 +88,18 @@ class Translator extends Preprocessor
$climate->br();
}
public function getCppFile(string $file): string
{
$info = pathinfo($file);
return $this->buildDir . '/' . $this->removeCommonPrefix($this->buildDir, $info['dirname'] . '/' . $info['filename'] . '.cc');
}
public function getObjectFile(string $cppFile): string
{
$info = pathinfo($cppFile);
return $info['dirname'] . '/' . $info['filename'] . '.o';
}
public function convert(string $file): string
{
$phpCode = $this->loadFile($file);
@ -96,8 +108,7 @@ class Translator extends Preprocessor
while (true) {
try {
$cppCode = $this->doConvert($phpCode);
$info = pathinfo($file);
$cppFile = $this->buildDir . '/' . $this->removeCommonPrefix($this->buildDir, $info['dirname'] . '/' . $info['filename'] . '.cc');
$cppFile = $this->getCppFile($file);
$this->save($cppCode, $cppFile);
return $cppFile;
} catch (RedoException $e) {
@ -111,6 +122,17 @@ class Translator extends Preprocessor
return self::PREFIX . 'register_class_' . $name;
}
public function hasCache(string $file): bool
{
$cppFile = $this->getCppFile($file);
$objectFile = $this->getObjectFile($cppFile);
if ((file_exists($cppFile) and filemtime($cppFile) > filemtime($file))
and (file_exists($objectFile) and filemtime($objectFile) > filemtime($cppFile))) {
return true;
}
return false;
}
protected function getClassCe(ClassDef $classDef): string
{
return self::PREFIX . 'class_entry_' . $classDef->getNamespacedName();
@ -209,93 +231,24 @@ class Translator extends Preprocessor
$this->writeFile($file, $code);
}
private function render(string $template): string
{
ob_start();
include __DIR__ . '/../template/' . $template;
return ob_get_clean();
}
public function genGlobalVars(string $file): void
{
$this->localHeaders = [];
$code = $this->genIncludeHeaderFiles();
$lines = [];
// 全局变量只能是 var 类型
foreach ($this->globalVars as $name => $type) {
$lines[] = self::TYPE_VAR . ' ' . $name . ';';
}
// 类定义
$lines[] = $this->getIndent() . '// class entries';
foreach ($this->classes as $classDef) {
$lines[] = $this->getIndent() . 'zend_class_entry *' . $this->getClassCe($classDef) . ';';
}
foreach ($this->classes as $classDef) {
$name = $classDef->getNamespacedName();
if ($classDef->extends) {
$parentCe = 'zend_class_entry *parent_ce';
} else {
$parentCe = '';
}
$lines[] = 'extern zend_class_entry *' . $this->getRegisterClassFunction($name) . '(' . $parentCe . ');';
}
$code .= implode(PHP_EOL, $lines) . PHP_EOL;
$code .= PHP_EOL;
$literalStringsCount = count($this->literalStrings);
$code .= 'php::Var ' . self::LITERAL_STRINGS . '[' . $literalStringsCount . '] = {' . PHP_EOL;
$this->indentLevel++;
$code .= $this->getIndent() . '// literal strings' . PHP_EOL;
foreach ($this->literalStrings as $str => $index) {
$code .= $this->getIndent() . 'php::String{ZEND_STRL("' . $this->escapeString($str) . '"), true},' . PHP_EOL;
}
$this->indentLevel--;
$code .= '};' . PHP_EOL;
// 生成常量表
$this->indentLevel++;
foreach ($this->nativeConstants as $name => $constant) {
$code .= $constant->type . ' ' . $name . ';' . PHP_EOL;
}
$this->indentLevel--;
$code .= PHP_EOL;
$this->indentLevel++;
$lines = [];
$lines[] = $this->getIndent() . '// constants';
foreach ($this->nativeConstants as $name => $constant) {
$lines[] = $this->getIndent() . $name . ' = ' . $constant->value . ';';
$lines[] = $this->getIndent() . 'php::define("' . $name . '", ' . $name . ');';
}
$lines[] = $this->getIndent() . '// global vars';
foreach ($this->globalVars as $name => $type) {
$lines[] = $this->getIndent() . $name . ' = php::global("' . $name . '");';
}
$lines[] = $this->getIndent() . '// class entries';
foreach ($this->classes as $classDef) {
$name = $classDef->getNamespacedName();
$parentCe = $this->getParentClassCe($classDef);
$lines[] = $this->getIndent() . $this->getClassCe($classDef) . ' = ' . $this->getRegisterClassFunction($name) . '(' . $parentCe . ');';
}
$this->indentLevel--;
$code .= $this->genFunction(self::PREFIX . 'init_global_vars', 'void', [], $lines);
// 销毁全局变量
$code .= PHP_EOL;
$this->indentLevel++;
$lines = [];
foreach ($this->globalVars as $name => $type) {
$lines[] = $this->getIndent() . $name . '.unset();';
}
foreach ($this->nativeConstants as $name => $constant) {
if ($constant->type !== self::TYPE_VAR) {
continue;
}
$lines[] = $this->getIndent() . $name . '.unset();';
}
$this->indentLevel--;
$code .= $this->genFunction(self::PREFIX . 'unset_global_vars', 'void', [], $lines);
$code = $this->render('extension.cc.php');
$this->writeFile($file, $code);
$this->formatCppCode($file);
}
public function compileFile($file): void
public function compileFile(string $file, string $objectFile): void
{
$cmd = $this->cppCompiler . ' -c ' . $file . ' -o ' . $file . '.o ';
$cmd = $this->cppCompiler . ' -c ' . $file . ' -o ' . $objectFile;
$this->addCompilationOption($cmd);
$this->climate->comment($cmd);
shell_exec($cmd);
@ -475,11 +428,19 @@ class Translator extends Preprocessor
$cppCode .= $this->getIndent() . self::TYPE_OBJECT . ' this_(&execute_data->This);' . PHP_EOL;
$fn = self::PREFIX . $this->getNativeMethodName($classDef, $methodDef);
$callParams = '';
foreach ($methodDef->functionDef->argInfoList as $k => $argInfo) {
$expr = $this->convertExprFromType($argInfo->type, 'ZEND_CALL_ARG(EG(current_execute_data), ' . ($k + 1) . ')');
$cppCode .= $this->getIndent() . $argInfo->type . ' arg_' . $argInfo->name . ' = ' . $expr . ';' . PHP_EOL;
$callParams .= 'arg_' . $argInfo->name . ',';
}
$callParams = $methodDef->functionDef->argInfoList ? 'this_, ' . rtrim($callParams, ',') : 'this_';
if ($methodDef->getReturnType() !== self::TYPE_VOID) {
$cppCode .= $this->getIndent() . 'auto retval = ' . $fn . '(this_);' . PHP_EOL;
$cppCode .= $this->getIndent() . 'auto retval = ' . $fn . '(' . $callParams . ');' . PHP_EOL;
$cppCode .= $this->getIndent() . 'php::move(retval, return_value);' . PHP_EOL;
} else {
$cppCode .= $this->getIndent() . $fn . '(this_);' . PHP_EOL;
$cppCode .= $this->getIndent() . $fn . '(' . $callParams . ');' . PHP_EOL;
}
$cppCode .= '}' . PHP_EOL . PHP_EOL;
@ -500,7 +461,7 @@ class Translator extends Preprocessor
$param = '';
}
$cppCode .= 'zend_class_entry *' . $this->getRegisterClassFunction($name) . '(' . $parentName . ') {' . PHP_EOL;
$cppCode .= $this->getIndent() . 'return ' . $this->getRegisterClassFunction($name) . '(' . $param . ');' . PHP_EOL;
$cppCode .= $this->getIndent() . 'return register_class_' . $name . '(' . $param . ');' . PHP_EOL;
$cppCode .= '}' . PHP_EOL . PHP_EOL;
$methods = $classDef->methods;

@ -4,25 +4,14 @@
#endif
#include <phpx.h>
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_void, 0, 0, IS_VOID, 0)
ZEND_END_ARG_INFO()
void php_main();
extern php::Var argc;
extern php::Var argv;
extern void php_init_global_vars();
extern void php_unset_global_vars();
static ZEND_FUNCTION(main) {
php_main();
}
static const zend_function_entry ext_functions[] = {
ZEND_FE(main, arginfo_void)
ZEND_FE_END
};
extern void php_app_init();
extern void php_app_clean();
BEGIN_EXTERN_C()
extern zend_module_entry *get_module();
END_EXTERN_C()
static void throw_exception(zend_object *ex) {
zend_bailout();
@ -31,14 +20,17 @@ static void throw_exception(zend_object *ex) {
int main(int cpp_argc, char **cpp_argv) {
php_embed_init(cpp_argc, cpp_argv);
zend_throw_exception_hook = throw_exception;
zend_register_functions(nullptr, ext_functions, nullptr, 0);
if (zend_register_module_ex(get_module(), MODULE_TEMPORARY) == NULL) {
zend_error(E_ERROR, "Failed to register module");
}
int rc = 0;
#if PPROF_ON
ProfilerStart("myapp.prof");
#endif
zend_first_try {
php_init_global_vars();
php_app_init();
php::eval("main();");
}
zend_catch {
@ -52,7 +44,7 @@ int main(int cpp_argc, char **cpp_argv) {
#if PPROF_ON
ProfilerStop();
#endif
php_unset_global_vars();
php_app_clean();
php::request_shutdown();
php_embed_shutdown();

@ -0,0 +1,120 @@
<?php
/**
* @var $this Translator
*/
use PhpAot\Php\Translator;
echo $this->genIncludeHeaderFiles();
?>
// global vars
<?php
// 全局变量只能是 var 类型
foreach ($this->globalVars as $name => $type):
?>
<?= Translator::TYPE_VAR ?> <?= $name ?>;
<?php endforeach; ?>
// class entries
<?php
foreach ($this->classes as $classDef) :
?>
zend_class_entry * <?= $this->getClassCe($classDef) ?>;
<?php endforeach; ?>
// class register functions
<?php
foreach ($this->classes as $classDef):
$name = $classDef->getNamespacedName();
$parentCe = $classDef->extends ? 'zend_class_entry *parent_ce' : '';
?>
extern zend_class_entry *<?= $this->getRegisterClassFunction($name) ?>(<?= $parentCe ?>);
<?php endforeach; ?>
// literal strings
php::Var <?=Translator::LITERAL_STRINGS?>[] = {
<?php
foreach ($this->literalStrings as $str => $index):
?>
php::String{ZEND_STRL("<?=$this->escapeString($str)?>"), true},
<?php endforeach; ?>
};
// constants
<?php
foreach ($this->nativeConstants as $name => $constant):
?>
<?=$constant->type?> <?=$name?>;
<?php endforeach; ?>
static ZEND_FUNCTION(main) {
php_main();
}
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_void, 0, 0, IS_VOID, 0)
ZEND_END_ARG_INFO()
static const zend_function_entry ext_functions[] = {
ZEND_FE(main, arginfo_void)
ZEND_FE_END
};
static PHP_MINIT_FUNCTION(app) {
<?php
foreach ($this->classes as $classDef):
$name = $classDef->getNamespacedName();
$parentCe = $this->getParentClassCe($classDef);
$fn = $this->getRegisterClassFunction($name);
?>
<?=$this->getClassCe($classDef)?> = <?= $fn ?>(<?= $parentCe ?>);
<?php endforeach; ?>
return SUCCESS;
}
void php_app_init() {
// register constants
<?php
foreach ($this->nativeConstants as $name => $constant):
?>
<?=$name?> = <?= $constant->value ?>;
php::define("<?=$name?>", <?= $name ?>);
<?php endforeach; ?>
// global vars
<?php
foreach ($this->globalVars as $name => $type):
?>
<?= $name ?> = php::global("<?=$name?>");
<?php endforeach; ?>
}
void php_app_clean() {
<?php
foreach ($this->globalVars as $name => $type) :
?>
<?= $name ?>.unset();
<?php endforeach; ?>
<?php
foreach ($this->nativeConstants as $name => $constant):
if ($constant->type !== Translator::TYPE_VAR) {
continue;
}
?>
<?= $name ?>.unset();
<?php endforeach; ?>
}
static zend_module_entry app_module_entry = {
STANDARD_MODULE_HEADER,
"app",
ext_functions,
PHP_MINIT(app),
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
STANDARD_MODULE_PROPERTIES,
};
ZEND_GET_MODULE(app);
Loading…
Cancel
Save