fix(php): 修复全局变量处理和代码格式化问题

- 将默认代码格式化功能从禁用改为启用
- 添加 GLOBALS 超全局变量到类型映射数组
- 在函数调用优化器中检查参数变量是否存在避免动态调用错误
- 为全局变量添加线程本地存储支持
- 实现 GLOBALS 超全局变量的特殊初始化逻辑
- 修复全局变量清理过程中的 GLOBALS 处理
pull/3/head
韩天峰 2 months ago
parent 99c3dec641
commit 129fcce880
  1. 3
      src/Php/CompilerBase.php
  2. 8
      src/Php/Optimizer/FuncCallOptimizer.php
  3. 20
      src/Php/Translator.php
  4. 16
      tests/aot/basic/global-003.phpt

@ -248,7 +248,7 @@ class CompilerBase extends \PhpAot\Core\Translator
protected array $linkPaths = []; // --link-path / -L: user-specified library search paths
protected int $floatPrecision = 17;
protected bool $debug = false;
protected bool $formatCode = false; // --format: enable clang-format (disabled by default)
protected bool $formatCode = true; // --format: enable clang-format (disabled by default)
protected bool $printBacktraceOnError = true;
protected bool $noLiteralStrings = false;
protected bool $noConsole = false; // Windows: hide console window
@ -334,6 +334,7 @@ class CompilerBase extends \PhpAot\Core\Translator
'_SESSION' => self::TYPE_ARRAY,
'_REQUEST' => self::TYPE_ARRAY,
'_ENV' => self::TYPE_ARRAY,
'GLOBALS' => self::TYPE_ARRAY,
];
protected array $globalVars = [];
protected bool $nativeTypes = false;

@ -200,6 +200,14 @@ trait FuncCallOptimizer
return false;
}
// 检测参数中使用的变量是否已定义,若变量不存在则回退到动态调用路径
// 动态路径中的 parseCallArgs() 会给出明确的错误信息
foreach ($expr->args as $arg) {
if ($this->isVarExpr($arg->value) && is_string($arg->value->name) && !$this->hasVar($arg->value->name)) {
return false;
}
}
if (is_string($config)) {
$targetName = $config;
$config = ['target' => $targetName];

@ -674,7 +674,7 @@ class Translator extends Preprocessor
$lines[] = '#include <phpx.h>';
$lines[] = PHP_EOL;
foreach ($this->globalVars as $name => $type) {
$lines[] = 'extern ' . self::TYPE_VAR . ' ' . $this->escapeGlobalVar($name) . ';';
$lines[] = 'extern THREAD_LOCAL ' . self::TYPE_VAR . ' ' . $this->escapeGlobalVar($name) . ';';
}
if ($this->literalStrings) {
@ -732,7 +732,7 @@ class Translator extends Preprocessor
$code .= "// global vars \n";
foreach ($this->globalVars as $name => $type) {
$code .= self::TYPE_VAR . ' ' . $this->escapeGlobalVar($name) . ';' . PHP_EOL;
$code .= 'THREAD_LOCAL ' . self::TYPE_VAR . ' ' . $this->escapeGlobalVar($name) . ';' . PHP_EOL;
}
$code .= "// class register functions \n";
@ -852,6 +852,8 @@ CODE;
$code .= '}' . PHP_EOL . PHP_EOL;
// minit end
$code .= 'THREAD_LOCAL zval globals_array;' . PHP_EOL;
// php_app_init begin
$code .= 'void php_app_init() {' . PHP_EOL;
$code .= '// register constants' . PHP_EOL;
@ -861,7 +863,13 @@ CODE;
}
$code .= '// global vars ' . PHP_EOL;
foreach ($this->globalVars as $name => $type) {
$code .= 'php::initGlobal(' . $this->genCharPtr($name) . ', ' . $this->escapeGlobalVar($name) . ');' . PHP_EOL;
if ($name == 'GLOBALS') {
$var = $this->escapeGlobalVar($name);
$code .= 'ZVAL_ARR(&globals_array, &EG(symbol_table));' . PHP_EOL;
$code .= 'ZVAL_INDIRECT(' . $var. '.ptr(), &globals_array);' . PHP_EOL;
} else {
$code .= 'php::initGlobal(' . $this->genCharPtr($name) . ', ' . $this->escapeGlobalVar($name) . ');' . PHP_EOL;
}
}
$code .= '// static property ' . PHP_EOL;
@ -886,8 +894,10 @@ CODE;
// php_app_clean begin
$code .= 'void php_app_clean() {' . PHP_EOL;
foreach ($this->globalVars as $name => $type) {
$code .= $this->escapeGlobalVar($name) . '.unset();' . PHP_EOL;
$code .= 'php::unsetGlobal("' . $name . '");' . PHP_EOL;
if ($name != 'GLOBALS') {
$code .= $this->escapeGlobalVar($name) . '.unset();' . PHP_EOL;
$code .= 'php::unsetGlobal("' . $name . '");' . PHP_EOL;
}
}
foreach ($this->constants as $name => $const) {
if ($const->type !== self::TYPE_VAR) {

@ -0,0 +1,16 @@
--TEST--
global vars
--FILE--
<?php
global $a;
$a = 100;
global $b;
$b = "rango";
require __DIR__ . '/../../../src/Assert.php';
Assert::eq(gettype($GLOBALS), 'array');
Assert::greaterThan(count($GLOBALS), 6);
?>
--EXPECTF--
Loading…
Cancel
Save