refactor(php): 优化PHP编译器类型检查和表达式处理

- 添加ClassConstFetch类型的检测方法
- 增加对VariadicPlaceholder的错误处理
- 移除静态属性获取的特殊处理逻辑
- 优化表达式解析的通用处理流程
- 修复最后行返回语句检测的边界情况
- 使用常量替换硬编码类型字符串
- 统一魔法方法参数类型验证方式
pull/1/head
韩天峰 6 months ago
parent 31a3275ecf
commit 43cb0948e1
  1. 5
      src/Php/AstNodeType.php
  2. 16
      src/Php/CompilerBase.php
  3. 14
      src/Php/MagicMethodDetector.php

@ -39,6 +39,11 @@ trait AstNodeType
return $expr instanceof Expr\StaticPropertyFetch; return $expr instanceof Expr\StaticPropertyFetch;
} }
protected function isClassConstFetch(NodeAbstract $expr): bool
{
return $expr instanceof Expr\ClassConstFetch;
}
protected function isNewExpr(NodeAbstract $expr): bool protected function isNewExpr(NodeAbstract $expr): bool
{ {
return $expr instanceof Expr\New_; return $expr instanceof Expr\New_;

@ -2066,6 +2066,9 @@ class CompilerBase extends \PhpAot\Core\Translator
$list_args = []; $list_args = [];
$last = array_key_last($args); $last = array_key_last($args);
foreach ($args as $i => $arg) { foreach ($args as $i => $arg) {
if ($arg instanceof Node\VariadicPlaceholder) {
$this->fatalError($arg, 'Variadic place holder are not supported');
}
if ($arg->name !== null) { if ($arg->name !== null) {
$this->fatalError($arg, 'Named arguments are not supported'); $this->fatalError($arg, 'Named arguments are not supported');
} }
@ -3127,16 +3130,14 @@ class CompilerBase extends \PhpAot\Core\Translator
} elseif ($this->isPropertyFetch($expr)) { } elseif ($this->isPropertyFetch($expr)) {
$name = $this->identifierToStr($expr->name); $name = $this->identifierToStr($expr->name);
$list[] = '{php::PropertyFetch, ' . self::TYPE_VAR . '(' . $name . ')}'; $list[] = '{php::PropertyFetch, ' . self::TYPE_VAR . '(' . $name . ')}';
} elseif ($this->isStaticPropertyFetch($expr)) {
$var = $this->genTmpVarName();
$this->addLocalVar($var, self::TYPE_VAR);
$this->beforeStmtLines[] = $var . '=' . $this->parseStaticPropertyFetch($expr) . ';';
break;
} elseif ($this->isVarExpr($expr)) { } elseif ($this->isVarExpr($expr)) {
$var = $this->parseIdentifier($expr); $var = $this->parseIdentifier($expr);
break; break;
} else { } else {
$this->fatalError($expr, 'The ' . $op . '() only supports variables, array fetch, and property read'); $var = $this->genTmpVarName();
$this->addLocalVar($var, self::TYPE_VAR);
$this->beforeStmtLines[] = $var . '=' . $this->parseExpr($expr) . ';';
break;
} }
$expr = $expr->var; $expr = $expr->var;
} }
@ -3987,6 +3988,9 @@ class CompilerBase extends \PhpAot\Core\Translator
protected function isReturnStmtInLastLine(array $stmts): bool protected function isReturnStmtInLastLine(array $stmts): bool
{ {
if (count($stmts) === 0) {
return false;
}
return $stmts[array_key_last($stmts)] instanceof Node\Stmt\Return_; return $stmts[array_key_last($stmts)] instanceof Node\Stmt\Return_;
} }
} }

@ -19,7 +19,7 @@ trait MagicMethodDetector
if (count($methodDef->functionDef->argInfoList) != 2) { if (count($methodDef->functionDef->argInfoList) != 2) {
$this->fatalError($v, 'Method ' . $this->class . "::{$name}() must take exactly 2 arguments"); $this->fatalError($v, 'Method ' . $this->class . "::{$name}() must take exactly 2 arguments");
} }
if (!$methodDef->functionDef->argInfoList[0]->type or $methodDef->functionDef->argInfoList[0]->type != 'string') { if ($methodDef->functionDef->argInfoList[0]->type and $methodDef->functionDef->argInfoList[0]->type != self::TYPE_STR) {
$this->fatalError($v, 'Method ' . $this->class . "::{$name}() must take string as argument"); $this->fatalError($v, 'Method ' . $this->class . "::{$name}() must take string as argument");
} }
} elseif ($name == '__get') { } elseif ($name == '__get') {
@ -27,33 +27,33 @@ trait MagicMethodDetector
$this->fatalError($v, 'Method ' . $this->class . "::{$name}() must take exactly 1 argument"); $this->fatalError($v, 'Method ' . $this->class . "::{$name}() must take exactly 1 argument");
} }
} elseif ($name == '__toString') { } elseif ($name == '__toString') {
if (!$methodDef->functionDef->returnType or $methodDef->functionDef->returnType != 'string') { if (!$methodDef->functionDef->returnType or $methodDef->functionDef->returnType != self::TYPE_STR) {
$this->fatalError($v, 'Method ' . $this->class . "::{$name}() must return string"); $this->fatalError($v, 'Method ' . $this->class . "::{$name}() must return string");
} }
} elseif ($name == '__serialize') { } elseif ($name == '__serialize') {
if (!$methodDef->functionDef->returnType or $methodDef->functionDef->returnType != 'array') { if (!$methodDef->functionDef->returnType or $methodDef->functionDef->returnType != self::TYPE_ARRAY) {
$this->fatalError($v, 'Method ' . $this->class . "::{$name}() must return array"); $this->fatalError($v, 'Method ' . $this->class . "::{$name}() must return array");
} }
} elseif ($name == '__unserialize') { } elseif ($name == '__unserialize') {
if (count($methodDef->functionDef->argInfoList) != 1) { if (count($methodDef->functionDef->argInfoList) != 1) {
$this->fatalError($v, 'Method ' . $this->class . "::{$name}() must take exactly 1 argument"); $this->fatalError($v, 'Method ' . $this->class . "::{$name}() must take exactly 1 argument");
} elseif (!$methodDef->functionDef->argInfoList[0]->type or $methodDef->functionDef->argInfoList[0]->type != 'array') { } elseif (!$methodDef->functionDef->argInfoList[0]->type or $methodDef->functionDef->argInfoList[0]->type != self::TYPE_ARRAY) {
$this->fatalError($v, 'Method ' . $this->class . "::{$name}() must take array as argument"); $this->fatalError($v, 'Method ' . $this->class . "::{$name}() must take array as argument");
} }
} elseif ($name == '__isset' or $name == '__unset' or $name == '__set_state') { } elseif ($name == '__isset' or $name == '__unset' or $name == '__set_state') {
if (count($methodDef->functionDef->argInfoList) != 1) { if (count($methodDef->functionDef->argInfoList) != 1) {
$this->fatalError($v, 'Method ' . $this->class . "::{$name}() must take exactly 1 argument"); $this->fatalError($v, 'Method ' . $this->class . "::{$name}() must take exactly 1 argument");
} }
if (!$methodDef->functionDef->argInfoList[0]->type or $methodDef->functionDef->argInfoList[0]->type != 'string') { if (!$methodDef->functionDef->argInfoList[0]->type or $methodDef->functionDef->argInfoList[0]->type != self::TYPE_STR) {
$this->fatalError($v, 'Method ' . $this->class . "::{$name}() must take string as argument"); $this->fatalError($v, 'Method ' . $this->class . "::{$name}() must take string as argument");
} }
if ($name == '__set_state') { if ($name == '__set_state') {
if (!$methodDef->functionDef->returnType or $methodDef->functionDef->returnType != 'array') { if (!$methodDef->functionDef->returnType or $methodDef->functionDef->returnType != self::TYPE_ARRAY) {
$this->fatalError($v, 'Method ' . $this->class . "::{$name}() must return array"); $this->fatalError($v, 'Method ' . $this->class . "::{$name}() must return array");
} }
} }
} elseif ($name == '__debugInfo') { } elseif ($name == '__debugInfo') {
if (!$methodDef->functionDef->returnType or $methodDef->functionDef->returnType != 'array') { if (!$methodDef->functionDef->returnType or $methodDef->functionDef->returnType != self::TYPE_ARRAY) {
$this->fatalError($v, 'Method ' . $this->class . "::{$name}() must return array"); $this->fatalError($v, 'Method ' . $this->class . "::{$name}() must return array");
} }
} }

Loading…
Cancel
Save