refactor(Php): 优化 MagicConst 和 Foreach 语句解析

- 修复 MagicConst 解析函数的类型声明
- 修复 Line 常量解析的返回值类型转换
- 修复 Foreach 语句解析函数的类型声明
- 更新 PhpParser 导入以支持 MagicConst 和 Foreach_ 节点类型
pull/1/head
韩天峰 8 months ago
parent 185c5a75dc
commit a085c33208
  1. 4
      examples/project/main.php
  2. 86
      src/Php/Translator.php

@ -3,5 +3,9 @@ function main()
{ {
fn1(); fn1();
$rs = fn_test(199, 189); $rs = fn_test(199, 189);
$array = [1, 3, 5];
foreach ($array as $r) {
echo $r;
}
var_dump(gettype($rs)); var_dump(gettype($rs));
} }

@ -1,4 +1,5 @@
<?php <?php
declare(strict_types=1); declare(strict_types=1);
namespace PhpAot\Php; namespace PhpAot\Php;
@ -10,6 +11,8 @@ use PhpParser\Node\Expr\Variable;
use PhpParser\Node\Identifier; use PhpParser\Node\Identifier;
use PhpParser\Error; use PhpParser\Error;
use PhpParser\Node\NullableType; use PhpParser\Node\NullableType;
use PhpParser\Node\Scalar\MagicConst;
use PhpParser\Node\Stmt\Foreach_;
use PhpParser\NodeFinder; use PhpParser\NodeFinder;
use PhpParser\NodeTraverser; use PhpParser\NodeTraverser;
use PhpParser\Parser; use PhpParser\Parser;
@ -20,22 +23,22 @@ use stdClass;
class Translator extends \PhpAot\Core\Translator class Translator extends \PhpAot\Core\Translator
{ {
const string TYPE_VAR = 'php::Var'; public const string TYPE_VAR = 'php::Var';
const string TYPE_BOOL = 'php::Bool'; public const string TYPE_BOOL = 'php::Bool';
const string TYPE_INT = 'php::Int'; public const string TYPE_INT = 'php::Int';
const string TYPE_FLOAT = 'php::Float'; public const string TYPE_FLOAT = 'php::Float';
const string TYPE_OBJECT = 'php::Object'; public const string TYPE_OBJECT = 'php::Object';
const string TYPE_ARRAY = 'php::Array'; public const string TYPE_ARRAY = 'php::Array';
const string TYPE_STR = 'php::Str'; public const string TYPE_STR = 'php::Str';
const string TYPE_REF = 'php::Var'; public const string TYPE_REF = 'php::Var';
const string VALUE_NAN = 'std::numeric_limits<double>::quiet_NaN()'; public const string VALUE_NAN = 'std::numeric_limits<double>::quiet_NaN()';
const string VALUE_INF = 'std::numeric_limits<double>::infinity()'; public const string VALUE_INF = 'std::numeric_limits<double>::infinity()';
const string LITERAL_STRINGS = '_literal_strings'; public const string LITERAL_STRINGS = '_literal_strings';
const string EXPR_VARIABLE = 'Expr_Variable'; public const string EXPR_VARIABLE = 'Expr_Variable';
const string EXPR_NEW = 'Expr_New'; public const string EXPR_NEW = 'Expr_New';
const string EXPR_ARRAY_DIM_FETCH = 'Expr_ArrayDimFetch'; public const string EXPR_ARRAY_DIM_FETCH = 'Expr_ArrayDimFetch';
const string NAMESPACE_SEPARATOR = '__'; public const string NAMESPACE_SEPARATOR = '__';
private string $phpxDir = '~/workspace/projects/phpx'; private string $phpxDir = '~/workspace/projects/phpx';
protected string $lang = 'PHP'; protected string $lang = 'PHP';
@ -137,7 +140,7 @@ class Translator extends \PhpAot\Core\Translator
private array $objectWrappers = []; private array $objectWrappers = [];
private bool $strictTypes = false; private bool $strictTypes = false;
const string PREFIX = 'php_'; public const string PREFIX = 'php_';
private string $rootPath; private string $rootPath;
private string $buildDir; private string $buildDir;
private int $debugLine = 0; private int $debugLine = 0;
@ -155,7 +158,7 @@ class Translator extends \PhpAot\Core\Translator
$this->parser = (new ParserFactory())->createForNewestSupportedVersion(); $this->parser = (new ParserFactory())->createForNewestSupportedVersion();
// $this->prettyPrinter = new PrettyPrinter\Standard; // $this->prettyPrinter = new PrettyPrinter\Standard;
$this->setBuildDir($rootPath . '/build'); $this->setBuildDir($rootPath . '/build');
$climate = new CLImate; $climate = new CLImate();
$this->climate = $climate; $this->climate = $climate;
$climate->arguments->add([ $climate->arguments->add([
'optimize' => [ 'optimize' => [
@ -197,13 +200,13 @@ class Translator extends \PhpAot\Core\Translator
$this->showUsage(); $this->showUsage();
exit(0); exit(0);
} }
// $this->noLiteralStrings = $climate->arguments->get('no-literal-strings'); // $this->noLiteralStrings = $climate->arguments->get('no-literal-strings');
$this->noLiteralStrings = true; $this->noLiteralStrings = true;
$this->optimizeLevel = $climate->arguments->get('optimize'); $this->optimizeLevel = $climate->arguments->get('optimize');
$this->internalFunctions = array_flip(get_defined_functions()['internal']); $this->internalFunctions = array_flip(get_defined_functions()['internal']);
} }
function showUsage(): void public function showUsage(): void
{ {
$climate = $this->climate; $climate = $this->climate;
$climate->bold()->green('PHP AOT Compiler v1.0.0'); $climate->bold()->green('PHP AOT Compiler v1.0.0');
@ -234,7 +237,7 @@ class Translator extends \PhpAot\Core\Translator
$climate->br(); $climate->br();
} }
function preprocessArgvAdvanced(): void public function preprocessArgvAdvanced(): void
{ {
global $argv; global $argv;
$processed = [$argv[0]]; $processed = [$argv[0]];
@ -289,7 +292,7 @@ class Translator extends \PhpAot\Core\Translator
$this->climate->info('convert: ' . $this->file); $this->climate->info('convert: ' . $this->file);
$ast = $this->parser->parse($phpCode); $ast = $this->parser->parse($phpCode);
$traverser = new NodeTraverser; $traverser = new NodeTraverser();
$traverser->addVisitor(new Visitor()); $traverser->addVisitor(new Visitor());
$stmts = $traverser->traverse($ast); $stmts = $traverser->traverse($ast);
@ -299,7 +302,7 @@ class Translator extends \PhpAot\Core\Translator
$this->resetNamespace(); $this->resetNamespace();
$cppCode = ''; $cppCode = '';
foreach($stmts as $v) { foreach ($stmts as $v) {
$type = $v->getType(); $type = $v->getType();
switch ($type) { switch ($type) {
case 'Stmt_Declare': case 'Stmt_Declare':
@ -451,7 +454,7 @@ class Translator extends \PhpAot\Core\Translator
if (isset($this->redoAfterDeclare[$name])) { if (isset($this->redoAfterDeclare[$name])) {
unset($this->redoAfterDeclare[$name]); unset($this->redoAfterDeclare[$name]);
$this->climate->cyan('Received redo request, retrying...'); $this->climate->cyan('Received redo request, retrying...');
throw new RedoException; throw new RedoException();
} }
} }
@ -510,6 +513,7 @@ class Translator extends \PhpAot\Core\Translator
$index = $this->literalStrings[$expr->value] ?? $this->addLiteralString($expr->value); $index = $this->literalStrings[$expr->value] ?? $this->addLiteralString($expr->value);
return self::LITERAL_STRINGS . '[' . $index . ']'; return self::LITERAL_STRINGS . '[' . $index . ']';
} }
// no break
default: default:
abort($expr); abort($expr);
} }
@ -933,10 +937,10 @@ class Translator extends \PhpAot\Core\Translator
{ {
if ($expr->getType() === 'Scalar_String') { if ($expr->getType() === 'Scalar_String') {
if ($this->isFloatStr($expr->value)) { if ($this->isFloatStr($expr->value)) {
return floatval($expr->value); return floatval($expr->value);
} else if ($this->isIntStr($expr->value)) { } elseif ($this->isIntStr($expr->value)) {
return intval($expr->value); return intval($expr->value);
} else if ($expr->value === '0') { } elseif ($expr->value === '0') {
return 0; return 0;
} }
} }
@ -1022,7 +1026,7 @@ class Translator extends \PhpAot\Core\Translator
$this->functionDef->returnType = $type; $this->functionDef->returnType = $type;
// 返回值变更,需要重新解析 // 返回值变更,需要重新解析
$this->climate->cyan('Return type changed, retrying...'); $this->climate->cyan('Return type changed, retrying...');
throw new RedoException; throw new RedoException();
} }
private function detectVarType($var): string private function detectVarType($var): string
@ -1447,7 +1451,7 @@ class Translator extends \PhpAot\Core\Translator
if (isset($this->useFunctions[$fname])) { if (isset($this->useFunctions[$fname])) {
$possibleFunctionNames[] = $this->escapeNamespace($this->useFunctions[$fname]) . self::NAMESPACE_SEPARATOR . $fname; $possibleFunctionNames[] = $this->escapeNamespace($this->useFunctions[$fname]) . self::NAMESPACE_SEPARATOR . $fname;
} }
foreach($possibleFunctionNames as $name) { foreach ($possibleFunctionNames as $name) {
// 在预处理阶段检测到函数声明,但是未定义,说明在当前文件,但是顺序错误 // 在预处理阶段检测到函数声明,但是未定义,说明在当前文件,但是顺序错误
if (isset($this->functionDeclInFile[$name]) if (isset($this->functionDeclInFile[$name])
and $this->functionDeclInFile[$name] === $this->file and $this->functionDeclInFile[$name] === $this->file
@ -1523,7 +1527,7 @@ class Translator extends \PhpAot\Core\Translator
if (!$this->hasVar($name)) { if (!$this->hasVar($name)) {
$this->addLocalVar($name, self::TYPE_REF); $this->addLocalVar($name, self::TYPE_REF);
$this->beforeStmtLines[] = $name . ' = php::newReference();'; $this->beforeStmtLines[] = $name . ' = php::newReference();';
} else if (!$nativeFunction and $funcName) { } elseif (!$nativeFunction and $funcName) {
$funcArg = Reflection::getFunctionParameter($funcName, $i); $funcArg = Reflection::getFunctionParameter($funcName, $i);
// 需要引用类型的参数,使用临时变量作为引用,并替换掉实际的参数 // 需要引用类型的参数,使用临时变量作为引用,并替换掉实际的参数
if ($funcArg and $funcArg->isPassedByReference()) { if ($funcArg and $funcArg->isPassedByReference()) {
@ -1692,7 +1696,7 @@ class Translator extends \PhpAot\Core\Translator
return $code; return $code;
} }
function isClosedCall($expr, $call): bool public function isClosedCall($expr, $call): bool
{ {
if ($call === '') { if ($call === '') {
if (!str_starts_with($expr, '(')) { if (!str_starts_with($expr, '(')) {
@ -1883,7 +1887,7 @@ class Translator extends \PhpAot\Core\Translator
$ns_end = ''; $ns_end = '';
} }
foreach($node->stmts as $v2) { foreach ($node->stmts as $v2) {
$type2 = $v2->getType(); $type2 = $v2->getType();
switch ($type2) { switch ($type2) {
case 'Stmt_Class': case 'Stmt_Class':
@ -1911,7 +1915,7 @@ class Translator extends \PhpAot\Core\Translator
{ {
$this->class = $this->parseIdentifier($v->name); $this->class = $this->parseIdentifier($v->name);
$code = ''; $code = '';
foreach($v->stmts as $v) { foreach ($v->stmts as $v) {
$type = $v->getType(); $type = $v->getType();
switch ($type) { switch ($type) {
case 'Stmt_ClassConst': case 'Stmt_ClassConst':
@ -2118,7 +2122,7 @@ class Translator extends \PhpAot\Core\Translator
return $var . ' ^= ' . $this->parseIdentifier($node->expr); return $var . ' ^= ' . $this->parseIdentifier($node->expr);
} }
private function parseMagicConst(mixed $expr): string private function parseMagicConst(MagicConst $expr): string
{ {
switch ($expr->getType()) { switch ($expr->getType()) {
case 'Scalar_MagicConst_Dir': case 'Scalar_MagicConst_Dir':
@ -2126,7 +2130,7 @@ class Translator extends \PhpAot\Core\Translator
case 'Scalar_MagicConst_File': case 'Scalar_MagicConst_File':
return '"' . $this->escapeString($this->file) . '"'; return '"' . $this->escapeString($this->file) . '"';
case 'Scalar_MagicConst_Line': case 'Scalar_MagicConst_Line':
return $expr->getStartLine(); return (string)$expr->getStartLine();
case 'Scalar_MagicConst_Function': case 'Scalar_MagicConst_Function':
return '"' . $this->escapeString($this->functionDef->name) . '"'; return '"' . $this->escapeString($this->functionDef->name) . '"';
default: default:
@ -2134,7 +2138,7 @@ class Translator extends \PhpAot\Core\Translator
} }
} }
private function parseForeach(Node $node): string private function parseForeach(Foreach_ $node): string
{ {
if ($node->byRef) { if ($node->byRef) {
$this->fatalError($node, 'Cannot use & with foreach'); $this->fatalError($node, 'Cannot use & with foreach');
@ -2321,7 +2325,7 @@ class Translator extends \PhpAot\Core\Translator
return self::VALUE_NAN; return self::VALUE_NAN;
} elseif (is_infinite($value)) { } elseif (is_infinite($value)) {
return $value > 0 ? self::VALUE_INF : '-' . self::VALUE_INF; return $value > 0 ? self::VALUE_INF : '-' . self::VALUE_INF;
} else if (floor($value) == $value && abs($value) < 1e15) { } elseif (floor($value) == $value && abs($value) < 1e15) {
return number_format($value, 1, '.', ''); return number_format($value, 1, '.', '');
} else { } else {
return sprintf('%.' . $this->floatPrecision . 'g', $value); return sprintf('%.' . $this->floatPrecision . 'g', $value);
@ -2331,7 +2335,7 @@ class Translator extends \PhpAot\Core\Translator
private function parseIsset(mixed $expr) private function parseIsset(mixed $expr)
{ {
$vars = $expr->vars; $vars = $expr->vars;
foreach($vars as $var) { foreach ($vars as $var) {
$type = $var->getType(); $type = $var->getType();
if ($type === self::EXPR_VARIABLE) { if ($type === self::EXPR_VARIABLE) {
return $this->hasVar($var->name) ? 'true' : 'false'; return $this->hasVar($var->name) ? 'true' : 'false';
@ -2675,7 +2679,7 @@ class Translator extends \PhpAot\Core\Translator
private function parseConstDef(mixed $v2): string private function parseConstDef(mixed $v2): string
{ {
foreach($v2->consts as $const) { foreach ($v2->consts as $const) {
$name = $this->parseIdentifier($const->name); $name = $this->parseIdentifier($const->name);
$value = $this->parseIdentifier($const->value); $value = $this->parseIdentifier($const->value);
$this->addConstant($name, $value); $this->addConstant($name, $value);
@ -2740,7 +2744,7 @@ class Translator extends \PhpAot\Core\Translator
$this->climate->info('prepare: ' . $this->file); $this->climate->info('prepare: ' . $this->file);
$ast = $this->parser->parse($phpCode); $ast = $this->parser->parse($phpCode);
$traverser = new NodeTraverser; $traverser = new NodeTraverser();
$traverser->addVisitor(new Visitor()); $traverser->addVisitor(new Visitor());
$stmts = $traverser->traverse($ast); $stmts = $traverser->traverse($ast);
@ -2818,7 +2822,7 @@ class Translator extends \PhpAot\Core\Translator
{ {
$this->class = $this->parseIdentifier($v->name); $this->class = $this->parseIdentifier($v->name);
$code = ''; $code = '';
foreach($v->stmts as $v) { foreach ($v->stmts as $v) {
$type = $v->getType(); $type = $v->getType();
switch ($type) { switch ($type) {
case 'Stmt_ClassConst': case 'Stmt_ClassConst':

Loading…
Cancel
Save