refactor(php): 重构变量声明和全局变量处理逻辑

- 将 localVarDecl 重命名为 scopeVarDecl 并更新所有相关调用
- 移除旧的 getStaticVarName 方法,使用新的 escapeStaticVar 方法
- 添加 parseSuperGlobalVar 方法来处理超全局变量
- 添加 hasArgument 和 addScopeGlobalVar 等辅助方法
- 更新 hasVar 方法以支持作用域全局变量检查
- 实现对 $GLOBALS 数组访问的支持,将其转换为相应变量
- 重构全局变量初始化和清理逻辑,使用新的命名规则
- 添加函数上下文中的全局变量数组存储
- 更新模板文件中的全局变量引用方式
pull/1/head
韩天峰 5 months ago
parent afbdf0fcdb
commit 27060d9b87
  1. 18
      examples/array_get.php
  2. 113
      src/Php/CompilerBase.php
  3. 1
      src/Php/Context/FunctionContext.php
  4. 4
      src/Php/Generator/ClosureGenerator.php
  5. 24
      src/Php/Generator/Utils.php
  6. 6
      src/Php/Translator.php
  7. 6
      src/template/extension.cc.php
  8. 29
      tests/aot/global-vars-002.phpt

@ -9,10 +9,20 @@
//var_dump($str[99]); //var_dump($str[99]);
//var_dump($str[-3]); //var_dump($str[-3]);
function foo()
{
global $gv;
$gv = 100;
}
function main() function main()
{ {
$arr = array( var_dump($_SERVER);
array(2, 2) foo();
);
var_dump(empty($arr[0][0][2][3]->prop[3])); global $gv;
var_dump($gv);
global $gv;
var_dump($gv);
} }

@ -72,6 +72,9 @@ class CompilerBase extends \PhpAot\Core\Translator
public const string VALUE_INF = 'std::numeric_limits<double>::infinity()'; public const string VALUE_INF = 'std::numeric_limits<double>::infinity()';
public const string VALUE_NULL = 'php::null'; public const string VALUE_NULL = 'php::null';
public const string LITERAL_STRINGS = '_literal_strings'; public const string LITERAL_STRINGS = '_literal_strings';
public const string ANON_CLASS = '_anon_class_';
public const string STATIC_VAR = '_static_var_';
public const string GLOBAL_VAR = '_global_var_';
public const string CLASS_MAP = 'class_map'; public const string CLASS_MAP = 'class_map';
public const string FUNC_MAP = 'func_map'; public const string FUNC_MAP = 'func_map';
public const string PROP_MAP = 'property_map'; public const string PROP_MAP = 'property_map';
@ -85,8 +88,6 @@ class CompilerBase extends \PhpAot\Core\Translator
public const string NAMESPACE_SEPARATOR = '__'; public const string NAMESPACE_SEPARATOR = '__';
public const string PREFIX = 'php_'; public const string PREFIX = 'php_';
public const string ANON_CLASS = '_anon_class_';
public const string STATIC_VAR = '_static_var_';
public const string OP_ISSET = 'isset'; public const string OP_ISSET = 'isset';
public const string OP_EMPTY = 'empty'; public const string OP_EMPTY = 'empty';
public const string OP_NOT_EMPTY = 'notEmpty'; public const string OP_NOT_EMPTY = 'notEmpty';
@ -633,25 +634,6 @@ class CompilerBase extends \PhpAot\Core\Translator
return $this->getNativeName($this->parseIdentifier($v->name), $this->namespace, $this->class); return $this->getNativeName($this->parseIdentifier($v->name), $this->namespace, $this->class);
} }
protected function getStaticVarName(string $name): string
{
$prefix = self::STATIC_VAR;
if ($this->namespace) {
$prefix .= $this->escapeNamespace($this->namespace) . '_';
}
if ($this->class) {
$prefix .= $this->escapeClass($this->class) . '_';
if ($this->method) {
$prefix .= $this->method . '_';
}
} else {
if ($this->function) {
$prefix .= $this->function . '_';
}
}
return $this->escapeName($prefix . $name);
}
protected function getFullClassName(): string protected function getFullClassName(): string
{ {
return ltrim($this->namespace . '\\' . $this->class, '\\'); return ltrim($this->namespace . '\\' . $this->class, '\\');
@ -912,7 +894,7 @@ class CompilerBase extends \PhpAot\Core\Translator
$code = $functionDeclCode . ' {' . PHP_EOL; $code = $functionDeclCode . ' {' . PHP_EOL;
$this->indentLevel++; $this->indentLevel++;
$code .= $this->genLocalVarDecl(); $code .= $this->genScopeVarDecl();
$code .= "\n"; $code .= "\n";
// Constructor Property Promotion // Constructor Property Promotion
foreach ($this->functionDef->argInfoList as $argInfo) { foreach ($this->functionDef->argInfoList as $argInfo) {
@ -968,16 +950,27 @@ class CompilerBase extends \PhpAot\Core\Translator
} }
} }
protected function parseSuperGlobalVar(string $name): string
{
if (!$this->hasGlobalVar($name)) {
$this->addGlobalVar($name, $this->superGlobalVars[$name]);
}
if (!$this->hasScopeGlobalVar($name)) {
$this->addScopeGlobalVar($name, $this->superGlobalVars[$name]);
}
return $name;
}
protected function parseVariable(Variable $expr): string protected function parseVariable(Variable $expr): string
{ {
if (is_object($expr->name) and $this->isVarExpr($expr->name)) { if (is_object($expr->name) and $this->isVarExpr($expr->name)) {
$this->fatalError($expr, 'The `$$` syntax is not supported'); $this->fatalError($expr, 'The `$$` syntax is not supported');
} }
if ($this->isSuperGlobal($expr->name) and !$this->hasGlobalVar($expr->name)) { if ($this->isSuperGlobal($expr->name)) {
$this->addGlobalVar($expr->name, $this->superGlobalVars[$expr->name]); return $this->parseSuperGlobalVar($expr->name);
} }
if ($this->hasStaticVar($expr->name)) { if ($this->hasStaticVar($expr->name)) {
return $this->getStaticVarName($expr->name); return $this->escapeStaticVar($expr->name);
} }
return $this->escapeVarName($expr->name); return $this->escapeVarName($expr->name);
} }
@ -1639,7 +1632,12 @@ class CompilerBase extends \PhpAot\Core\Translator
$this->error('Duplicate static variable `$' . $name . '`'); $this->error('Duplicate static variable `$' . $name . '`');
} }
$this->context->staticVars[$name] = $type; $this->context->staticVars[$name] = $type;
$this->addGlobalVar($this->getStaticVarName($name), $type); $this->addGlobalVar($this->escapeStaticVar($name), $type);
}
protected function hasArgument(string $name): bool
{
return isset($this->context->arguments[$name]);
} }
protected function addArgument(string $name, string $type): void protected function addArgument(string $name, string $type): void
@ -1661,6 +1659,11 @@ class CompilerBase extends \PhpAot\Core\Translator
$this->globalVars[$name] = $type; $this->globalVars[$name] = $type;
} }
protected function addScopeGlobalVar(string $name, string $type): void
{
$this->context->globalVars[$name] = $type;
}
protected function addObject(string $name, string $class): void protected function addObject(string $name, string $class): void
{ {
$this->context->objects[$name] = $class; $this->context->objects[$name] = $class;
@ -1668,7 +1671,7 @@ class CompilerBase extends \PhpAot\Core\Translator
protected function hasVar(string $name): bool protected function hasVar(string $name): bool
{ {
return $this->hasLocalVar($name) || $this->hasGlobalVar($name) || $this->hasStaticVar($name); return $this->hasLocalVar($name) || $this->hasStaticVar($name) || $this->hasScopeGlobalVar($name) || $this->isSuperGlobal($name);
} }
protected function hasLocalVar(string $name): bool protected function hasLocalVar(string $name): bool
@ -2276,11 +2279,14 @@ class CompilerBase extends \PhpAot\Core\Translator
} }
} }
protected function parseArrayDimFetch(Expr\ArrayDimFetch $node, bool $write): string /**
* $GLOBALS['var'] 等价于 global $var; $var ,将字符串常量转为变量名称即可
* 仅限于字面量字符串可以转为变量名称,其他则使用 php::global() 函数获取
* @param Expr\ArrayDimFetch $node
* @return string
*/
protected function parseGlobalsArrayDimFetch(Expr\ArrayDimFetch $node): string
{ {
$var = $this->parseIdentifier($node->var);
if ($this->isVarExpr($node->var)) {
if ($var === 'GLOBALS') {
if ($node->dim === null) { if ($node->dim === null) {
$this->fatalError($node, 'Cannot use [] for GLOBALS'); $this->fatalError($node, 'Cannot use [] for GLOBALS');
} }
@ -2289,10 +2295,21 @@ class CompilerBase extends \PhpAot\Core\Translator
if (!$this->hasGlobalVar($name)) { if (!$this->hasGlobalVar($name)) {
$this->addGlobalVar($name, self::TYPE_VAR); $this->addGlobalVar($name, self::TYPE_VAR);
} }
if (!$this->hasScopeGlobalVar($name)) {
$this->addScopeGlobalVar($name, self::TYPE_VAR);
}
return $name; return $name;
} }
return 'php::global(' . $this->parseIdentifier($node->dim) . ')'; return 'php::global(' . $this->parseIdentifier($node->dim) . ')';
} }
protected function parseArrayDimFetch(Expr\ArrayDimFetch $node, bool $write): string
{
$var = $this->parseIdentifier($node->var);
if ($this->isVarExpr($node->var)) {
if ($var === 'GLOBALS') {
return $this->parseGlobalsArrayDimFetch($node);
}
if (!$this->hasVar($var)) { if (!$this->hasVar($var)) {
if ($write) { if ($write) {
$this->addLocalVar($var, self::TYPE_ARRAY); $this->addLocalVar($var, self::TYPE_ARRAY);
@ -2573,19 +2590,7 @@ class CompilerBase extends \PhpAot\Core\Translator
} elseif ($this->isArrayDimFetch($arg->value) and $this->isVarExpr($arg->value->var)) { } elseif ($this->isArrayDimFetch($arg->value) and $this->isVarExpr($arg->value->var)) {
$array = $this->parseIdentifier($arg->value->var); $array = $this->parseIdentifier($arg->value->var);
if ($array === 'GLOBALS') { if ($array === 'GLOBALS') {
if ($arg->value->dim === null) { $globalVar = $this->parseGlobalsArrayDimFetch($arg->value);
$this->fatalError($arg, 'GLOBALS array dimension must be a constant expression');
}
// $GLOBALS['var'] 等价于 global $var; $var ,将字符串常量转为变量名称即可
// 仅限于字面量字符串可以转为变量名称,其他则使用 php::global 函数获取
if ($arg->value->dim instanceof Node\Scalar\String_) {
$globalVar = $arg->value->dim->value;
if (!$this->hasGlobalVar($globalVar)) {
$this->addGlobalVar($globalVar, self::TYPE_VAR);
}
} else {
$globalVar = 'php::global(' . $this->parseExpr($arg->value->dim) . ')';
}
// 全局变量作为引用参数 // 全局变量作为引用参数
if ($byRef) { if ($byRef) {
$ref = $this->addTmpVar(self::TYPE_REF); $ref = $this->addTmpVar(self::TYPE_REF);
@ -3196,12 +3201,14 @@ class CompilerBase extends \PhpAot\Core\Translator
protected function parseGlobal(Node\Stmt\Global_ $v): string protected function parseGlobal(Node\Stmt\Global_ $v): string
{ {
foreach ($v->vars as $v) { foreach ($v->vars as $v) {
$name = $this->escapeVarName($this->parseVariable($v)); $name = $this->parseVariable($v);
if (!$this->hasGlobalVar($name)) { if (!$this->hasGlobalVar($name)) {
$this->addGlobalVar($name, self::TYPE_VAR); $this->addGlobalVar($name, self::TYPE_VAR);
} }
if (!$this->hasScopeGlobalVar($name)) {
$this->addScopeGlobalVar($name, self::TYPE_VAR);
}
} }
return ''; return '';
} }
@ -3572,7 +3579,7 @@ class CompilerBase extends \PhpAot\Core\Translator
$this->indentLevel++; $this->indentLevel++;
$initCode .= $this->getIndent() . "{$initState} = true;\n"; $initCode .= $this->getIndent() . "{$initState} = true;\n";
$initCode .= $this->genLambdaCall(function () use ($var, $varName) { $initCode .= $this->genLambdaCall(function () use ($var, $varName) {
return $this->getIndent() . $this->getStaticVarName($varName) . ' = ' . $this->parseExpr($var->default) . ';'; return $this->getIndent() . $this->escapeStaticVar($varName) . ' = ' . $this->parseExpr($var->default) . ';';
}); });
$this->indentLevel--; $this->indentLevel--;
$initCode .= $this->getIndent() . '}'; $initCode .= $this->getIndent() . '}';
@ -3762,6 +3769,11 @@ class CompilerBase extends \PhpAot\Core\Translator
return array_key_exists($name, $this->globalVars); return array_key_exists($name, $this->globalVars);
} }
protected function hasScopeGlobalVar(string $name): bool
{
return array_key_exists($name, $this->context->globalVars);
}
protected function hasStaticVar(string $name): bool protected function hasStaticVar(string $name): bool
{ {
return array_key_exists($name, $this->context->staticVars); return array_key_exists($name, $this->context->staticVars);
@ -4555,7 +4567,7 @@ class CompilerBase extends \PhpAot\Core\Translator
return $code; return $code;
} }
protected function genLocalVarDecl(): string protected function genScopeVarDecl(): string
{ {
$code = ''; $code = '';
foreach ($this->context->localVars as $name => $type) { foreach ($this->context->localVars as $name => $type) {
@ -4568,6 +4580,9 @@ class CompilerBase extends \PhpAot\Core\Translator
} }
$code .= ';' . PHP_EOL; $code .= ';' . PHP_EOL;
} }
foreach ($this->context->globalVars as $name => $type) {
$code .= $this->getIndent() . self::TYPE_VAR . ' &' . $name . ' = ' . $this->escapeGlobalVar($name) . ';' . PHP_EOL;
}
return $code; return $code;
} }
@ -4608,7 +4623,7 @@ class CompilerBase extends \PhpAot\Core\Translator
foreach ($vars as $var) { foreach ($vars as $var) {
$varName = $this->escapeVarName($this->parseVariable($var)); $varName = $this->escapeVarName($this->parseVariable($var));
if ($varName === 'this' if ($varName === 'this_'
or !$this->hasLocalVar($varName) or !$this->hasLocalVar($varName)
or isset($params[$var->name]) or isset($params[$var->name])
or isset($uses[$varName])) { or isset($uses[$varName])) {

@ -16,6 +16,7 @@ class FunctionContext
public array $objects = []; public array $objects = [];
public array $localVars = []; public array $localVars = [];
public array $staticVars = []; public array $staticVars = [];
public array $globalVars = [];
/** /**
* @var array<string, string> * @var array<string, string>

@ -58,7 +58,7 @@ trait ClosureGenerator
} }
$body = $bodyGenCb(); $body = $bodyGenCb();
$code .= $this->genLocalVarDecl() . $body; $code .= $this->genScopeVarDecl() . $body;
$this->indentLevel--; $this->indentLevel--;
$this->context->inClosure = false; $this->context->inClosure = false;
@ -106,7 +106,7 @@ trait ClosureGenerator
$code .= '([&](){' . PHP_EOL; $code .= '([&](){' . PHP_EOL;
$body = $cb(); $body = $cb();
$code .= $this->genLocalVarDecl(); $code .= $this->genScopeVarDecl();
$code .= $this->parseBeforeStmtLines(); $code .= $this->parseBeforeStmtLines();
$code .= $body; $code .= $body;
$code .= $this->parseAfterStmtLines(); $code .= $this->parseAfterStmtLines();

@ -56,6 +56,30 @@ trait Utils
return $name; return $name;
} }
protected function escapeStaticVar(string $name): string
{
$prefix = self::STATIC_VAR;
if ($this->namespace) {
$prefix .= $this->escapeNamespace($this->namespace) . '_';
}
if ($this->class) {
$prefix .= $this->escapeClass($this->class) . '_';
if ($this->method) {
$prefix .= $this->method . '_';
}
} else {
if ($this->function) {
$prefix .= $this->function . '_';
}
}
return $prefix . $name;
}
protected function escapeGlobalVar(string $name): string
{
return self::GLOBAL_VAR . $name;
}
protected function escapeNamespace(string $ns): string protected function escapeNamespace(string $ns): string
{ {
return str_replace('\\', self::NAMESPACE_SEPARATOR, strtolower($ns)); return str_replace('\\', self::NAMESPACE_SEPARATOR, strtolower($ns));

@ -212,7 +212,7 @@ class Translator extends Preprocessor
$lines[] = '#include <phpx.h>'; $lines[] = '#include <phpx.h>';
$lines[] = PHP_EOL; $lines[] = PHP_EOL;
foreach ($this->globalVars as $name => $type) { foreach ($this->globalVars as $name => $type) {
$lines[] = 'extern ' . self::TYPE_VAR . ' ' . $name . ';'; $lines[] = 'extern ' . self::TYPE_VAR . ' ' . $this->escapeGlobalVar($name) . ';';
} }
$literalStringsCount = count($this->literalStrings); $literalStringsCount = count($this->literalStrings);
@ -943,7 +943,7 @@ class Translator extends Preprocessor
$oriCtx = $this->context; $oriCtx = $this->context;
$this->context = $this->classDef->propertyContext; $this->context = $this->classDef->propertyContext;
$this->classDef->ctorInit .= $this->genLocalVarDecl() . $this->parseBeforeStmtLines(); $this->classDef->ctorInit .= $this->genScopeVarDecl() . $this->parseBeforeStmtLines();
$this->classDef->ctorClean .= $this->parseAfterStmtLines(); $this->classDef->ctorClean .= $this->parseAfterStmtLines();
$this->context = $oriCtx; $this->context = $oriCtx;
@ -1150,7 +1150,7 @@ class Translator extends Preprocessor
$arrayExpr = ''; $arrayExpr = '';
if ($this->context->beforeStmtLines) { if ($this->context->beforeStmtLines) {
if ($this->context->localVars) { if ($this->context->localVars) {
$arrayExpr .= $this->genLocalVarDecl(); $arrayExpr .= $this->genScopeVarDecl();
} }
$arrayExpr .= $this->parseBeforeStmtLines(); $arrayExpr .= $this->parseBeforeStmtLines();
} }

@ -15,7 +15,7 @@ echo $this->genIncludeHeaderFiles();
// 全局变量只能是 var 类型 // 全局变量只能是 var 类型
foreach ($this->globalVars as $name => $type): foreach ($this->globalVars as $name => $type):
?> ?>
<?= Translator::TYPE_VAR ?> <?= $name ?>; <?= Translator::TYPE_VAR ?> <?= $this->escapeGlobalVar($name) ?>;
<?php endforeach; ?> <?php endforeach; ?>
// class register functions // class register functions
@ -145,7 +145,7 @@ foreach ($this->constants as $name => $const):
<?php <?php
foreach ($this->globalVars as $name => $type): foreach ($this->globalVars as $name => $type):
?> ?>
php::initGlobal("<?=$name?>", <?= $name ?>); php::initGlobal("<?=$name?>", <?= $this->escapeGlobalVar($name) ?>);
<?php endforeach; ?> <?php endforeach; ?>
// static property // static property
@ -165,7 +165,7 @@ void php_app_clean() {
<?php <?php
foreach ($this->globalVars as $name => $type) : foreach ($this->globalVars as $name => $type) :
?> ?>
<?= $name ?>.unset(); <?= $this->escapeGlobalVar($name) ?>.unset();
php::unsetGlobal("<?=$name?>"); php::unsetGlobal("<?=$name?>");
<?php endforeach; ?> <?php endforeach; ?>
<?php <?php

@ -0,0 +1,29 @@
--TEST--
global vars (002)
--FILE--
<?php
function foo()
{
global $gv;
$gv = 100;
}
function main()
{
var_dump(is_array($_SERVER));
var_dump(is_array($GLOBALS['_SERVER']));
foo();
global $gv;
var_dump($gv);
global $gv;
var_dump($gv);
}
?>
--EXPECTF--
bool(true)
bool(true)
int(100)
int(100)
Loading…
Cancel
Save