fix(php): 解决字符串连接操作符的void类型检查和引用参数处理问题

- 在parseBinaryOpConcat方法中添加了对左右操作数的类型检查,防止void类型的连接操作
- 为concat操作添加了专门的符号函数php::concat
- 修复了引用参数传递时的变量类型验证逻辑
- 更新了常量定义方法的返回类型从string改为void
- 修正了预处理器中函数声明的换行符处理
- 将Iterator方法调用从exec改为call以统一接口调用方式
- 添加了concat_void.php示例文件用于测试void类型连接错误
- 新增了ref相关的测试用例文件
pull/1/head
韩天峰 4 months ago
parent ec98d1045d
commit eaa598a48e
  1. 11
      examples/error/concat_void.php
  2. 24
      src/Php/CompilerBase.php
  3. 6
      src/Php/Preprocessor.php
  4. 5
      src/Php/Symbol.php
  5. 16
      src/Php/Translator.php
  6. 27
      tests/aot/ref/006.phpt

@ -0,0 +1,11 @@
<?php
function foo(): void
{
var_dump(__FUNCTION__);
}
function main()
{
$s = foo() . "\n";
echo $s;
}

@ -2074,10 +2074,15 @@ class CompilerBase extends \PhpAot\Core\Translator
protected function parseBinaryOpConcat(Expr\BinaryOp\Concat $expr): string
{
$left = $this->parseIdentifier($expr->left);
$left = $this->parseIdentifier($expr->left);
$right = $this->parseIdentifier($expr->right);
return 'php::concat(' . $left . ', ' . $right . ')';
$leftType = $this->detectTypeOfExpr($expr->left);
$rightType = $this->detectTypeOfExpr($expr->right);
if ($leftType === self::TYPE_VOID or $rightType === self::TYPE_VOID) {
$this->fatalError($expr, 'Cannot concat void');
}
return Symbol::concat() . '(' . $this->convertStringExpr($left) . ', ' . $this->convertStringExpr($right) . ')';
}
protected function parseFor(Node\Stmt\For_ $v): string
@ -2879,7 +2884,7 @@ class CompilerBase extends \PhpAot\Core\Translator
{
$expr = $this->parseExpr($expr->expr);
return '!' . $expr;
return '!(' . $expr . ')';
}
protected function parseWhile(Node\Stmt\While_ $v): string
@ -3305,6 +3310,15 @@ class CompilerBase extends \PhpAot\Core\Translator
$type = $this->detectTypeOfExpr($arg->value);
if ($argInfo->byRef) {
if ($this->isVarExpr($arg->value)) {
$var = $this->parseVariable($arg->value);
// 若参数是引用类型,可以传入未定义变量,将立即创建变量作为引用
if (!$this->hasLocalVar($var)) {
$this->addLocalVar($var, self::TYPE_VAR);
} elseif ($this->getVarType($var) != self::TYPE_REF) {
$this->fatalError($arg, "Argument `{$argInfo->name}` must be a reference, `{$type}` given");
}
}
return $this->convertToRef($arg->value);
}
@ -4407,7 +4421,7 @@ class CompilerBase extends \PhpAot\Core\Translator
return $flags;
}
protected function parseConstDef(mixed $v2): string
protected function parseConstDef(mixed $v2): void
{
foreach ($v2->consts as $const) {
$name = $this->parseIdentifier($const->name);
@ -4417,8 +4431,6 @@ class CompilerBase extends \PhpAot\Core\Translator
}
$this->addConstant($name, $value);
}
return '';
}
protected function addConstant(string $name, string $value): void

@ -129,7 +129,7 @@ class Preprocessor extends CompilerBase
$this->prepareClass($v);
break;
case 'Stmt_Function':
$this->prepareFunction($v) . PHP_EOL;
$this->prepareFunction($v);
break;
case 'Stmt_Use':
$this->parseUse($v);
@ -202,7 +202,7 @@ class Preprocessor extends CompilerBase
$this->prepareClass($v2);
break;
case 'Stmt_Function':
$this->prepareFunction($v2) . PHP_EOL;
$this->prepareFunction($v2);
break;
case 'Stmt_Use':
$this->parseUse($v2);
@ -579,7 +579,7 @@ class Preprocessor extends CompilerBase
if ($this->classDef->hasMethod($name)) {
$this->fatalError($v, "Duplicate method `{$this->method}`");
}
$this->prepareFunction($v) . PHP_EOL;
$this->prepareFunction($v);
$this->checkRequiredArgNum($name, $this->methodDef, $v);
$this->classDef->addMethod($this->methodDef);
} else {

@ -25,6 +25,11 @@ class Symbol
return 'php::instanceOf';
}
public static function concat(): string
{
return 'php::concat';
}
public static function getCalledCe(): string
{
return CompilerBase::PREFIX . 'get_called_ce(this_)';

@ -848,10 +848,10 @@ class Translator extends Preprocessor
$cppCode .= $this->parseFunction($v) . PHP_EOL;
break;
case 'Stmt_Const':
$this->parseConstDef($v) . PHP_EOL;
$this->parseConstDef($v);
break;
case 'Stmt_Interface':
$this->parseInterface($v) . PHP_EOL;
$this->parseInterface($v);
break;
case 'Stmt_Nop':
break;
@ -985,7 +985,7 @@ class Translator extends Preprocessor
$code .= $this->parseUse($v2) . PHP_EOL;
break;
case 'Stmt_Interface':
$code .= $this->parseInterface($v2) . PHP_EOL;
$this->parseInterface($v2);
break;
default:
abort($v2);
@ -1471,25 +1471,25 @@ class Translator extends Preprocessor
$this->addLocalVar($tmpArrayVar, self::TYPE_ARRAY);
$code = 'if (' . $obj . '.instanceOf("IteratorAggregate")) {' . PHP_EOL;
$code .= $this->getIndent() . $tmpVar . ' = ' . $obj . '.exec("getIterator");' . PHP_EOL . '}' . PHP_EOL;
$code .= $this->getIndent() . $tmpVar . ' = ' . $obj . '.call("getIterator");' . PHP_EOL . '}' . PHP_EOL;
$code .= 'else if (' . $obj . '.instanceOf("Iterator")) {' . PHP_EOL;
$code .= $this->getIndent() . $tmpVar . ' = ' . $obj . ';' . PHP_EOL . '}' . PHP_EOL;
$code .= 'if (' . $tmpVar . ') {' . PHP_EOL;
$this->indentLevel++;
$code .= $this->getIndent() . $tmpVar . '.exec("rewind");' . PHP_EOL;
$code .= $this->getIndent() . 'for (;' . $tmpVar . '.exec("valid"); ' . $tmpVar . '.exec("next")) {' . PHP_EOL;
$code .= $this->getIndent() . $tmpVar . '.call("rewind");' . PHP_EOL;
$code .= $this->getIndent() . 'for (;' . $tmpVar . '.call("valid"); ' . $tmpVar . '.call("next")) {' . PHP_EOL;
$this->indentLevel++;
$valueVar = $this->parseIdentifier($node->valueVar);
$this->checkVar($node, $valueVar);
$code .= $this->getIndent() . ' ' . $valueVar . ' = ' . $tmpVar . '.exec("current");' . PHP_EOL;
$code .= $this->getIndent() . ' ' . $valueVar . ' = ' . $tmpVar . '.call("current");' . PHP_EOL;
if ($node->keyVar) {
$keyVar = $this->parseIdentifier($node->keyVar);
$this->checkVar($node, $keyVar);
$code .= $this->getIndent() . ' ' . $keyVar . ' = ' . $tmpVar . '.exec("key");' . PHP_EOL;
$code .= $this->getIndent() . ' ' . $keyVar . ' = ' . $tmpVar . '.call("key");' . PHP_EOL;
}
$code .= $this->parseStmts($node->stmts);
$code .= '}' . PHP_EOL;

@ -0,0 +1,27 @@
--TEST--
ref
--FILE--
<?php
class Request {
public function bar() {
$this->foo('hello', $class);
$this->dump($class);
}
public function foo($name, ?string &$class) {
$class = __CLASS__;
}
public function dump(string $class) {
var_dump($class);
}
}
function main()
{
$req = new Request;
$req->bar();
}
?>
--EXPECT--
string(7) "Request"
Loading…
Cancel
Save