refactor(foreach): 提取键值赋值与循环体解析为独立方法

pull/13/head
韩天峰 2 months ago
parent 7937f05282
commit 2e85940bff
  1. 78
      src/Php/CompilerBase.php
  2. 3
      src/Php/Parser/StdContainerTrait.php
  3. 27
      src/Php/Translator.php
  4. 57
      tests/aot/attribute/return-type-will-change.phpt

@ -5164,15 +5164,26 @@ class CompilerBase extends \PhpAot\Core\Translator implements PropertyAccessCont
return $code;
}
protected function parseForeachArray(Foreach_ $node, string $iteratorVar): string
protected function parseForeachBody(Foreach_ $node): string
{
$tmpVar = $this->genTmpVarName();
$code = "for (auto $tmpVar = $iteratorVar.begin(); $tmpVar != $iteratorVar.end(); ++$tmpVar) {" . PHP_EOL;
$this->indentLevel++;
if ($node->keyVar) {
$keyVar = $this->parseIdentifier($node->keyVar);
$this->checkVar($node, $keyVar);
$code .= $this->getIndent() . ' ' . $keyVar . ' = ' . $tmpVar . '.key();' . PHP_EOL;
return $this->parseStmts($node->stmts) . $this->genLoopEndFlagCheck();
}
protected function parseForeachKeyAssignment(Foreach_ $node, string $keyExpr, string $defaultType = self::TYPE_VAR): string
{
if (!$node->keyVar) {
return '';
}
$keyVar = $this->parseIdentifier($node->keyVar);
$this->checkVar($node, $keyVar, $defaultType);
return $this->getIndent() . ' ' . $keyVar . ' = ' . $keyExpr . ';' . PHP_EOL;
}
protected function parseForeachValueAssignment(Foreach_ $node, string $valueExpr, ?string $valueRefExpr = null): string
{
if ($node->byRef && $valueRefExpr === null) {
$this->fatalError($node, 'Cannot use & with foreach');
}
if ($node->byRef and !$this->isVarExpr($node->valueVar)) {
@ -5185,32 +5196,47 @@ class CompilerBase extends \PhpAot\Core\Translator implements PropertyAccessCont
}
$listTmpVar = $this->genTmpVarName();
$this->addLocalVar($listTmpVar, self::TYPE_VAR);
$code .= $this->getIndent() . ' ' . $listTmpVar . ' = ' . $tmpVar . '.value();' . PHP_EOL;
$code .= $this->parseForeachItemAsList($listTmpVar, $node->valueVar->items);
} elseif ($this->isArrayDimFetch($node->valueVar)) {
return $this->getIndent() . ' ' . $listTmpVar . ' = ' . $valueExpr . ';' . PHP_EOL
. $this->parseForeachItemAsList($listTmpVar, $node->valueVar->items);
}
if ($this->isArrayDimFetch($node->valueVar)) {
if ($node->byRef) {
$this->fatalError($node, 'Foreach by reference only supports variable as value');
}
$array = $this->parseIdentifier($node->valueVar->var);
if (!$this->hasVar($array) or $node->valueVar->dim === null) {
abort($node->valueVar);
}
$dim = $this->parseIdentifier($node->valueVar->dim);
$code .= $this->getIndent() . "{$array}.offsetSet({$dim}, {$tmpVar}.value());";
} else {
$valueVar = $this->parseIdentifier($node->valueVar);
if ($node->byRef) {
if (!$this->hasVar($valueVar)) {
$this->addLocalVar($valueVar, self::TYPE_REF);
} elseif ($this->getVarType($valueVar) !== self::TYPE_REF) {
$this->fatalError($node, 'Cannot assign value to reference of type');
}
$code .= $this->getIndent() . ' ' . $valueVar . ' = ' . $tmpVar . '.valueRef();' . PHP_EOL;
} else {
$this->checkVar($node, $valueVar);
$code .= $this->getIndent() . ' ' . $valueVar . ' = ' . $tmpVar . '.value();' . PHP_EOL;
return $this->getIndent() . "{$array}.offsetSet({$dim}, {$valueExpr});";
}
$valueVar = $this->parseIdentifier($node->valueVar);
if ($node->byRef) {
if (!$this->hasVar($valueVar)) {
$this->addLocalVar($valueVar, self::TYPE_REF);
} elseif ($this->getVarType($valueVar) !== self::TYPE_REF) {
$this->fatalError($node, 'Cannot assign value to reference of type');
}
return $this->getIndent() . ' ' . $valueVar . ' = ' . $valueRefExpr . ';' . PHP_EOL;
}
if ($this->isVarExpr($node->valueVar)) {
$this->checkVar($node, $valueVar);
}
return $this->getIndent() . ' ' . $valueVar . ' = ' . $valueExpr . ';' . PHP_EOL;
}
protected function parseForeachArray(Foreach_ $node, string $iteratorVar): string
{
$tmpVar = $this->genTmpVarName();
$code = "for (auto $tmpVar = $iteratorVar.begin(); $tmpVar != $iteratorVar.end(); ++$tmpVar) {" . PHP_EOL;
$this->indentLevel++;
$code .= $this->parseForeachKeyAssignment($node, $tmpVar . '.key()');
$code .= $this->parseForeachValueAssignment($node, $tmpVar . '.value()', $tmpVar . '.valueRef()');
$body = $this->parseStmts($node->stmts);
$body .= $this->genLoopEndFlagCheck();
$body = $this->parseForeachBody($node);
$this->indentLevel--;
$code .= $this->parseBeforeStmtLines() . PHP_EOL;

@ -383,8 +383,7 @@ trait StdContainerTrait
$code .= $this->getIndent() . "$valueVar = {$iterator}->second;" . PHP_EOL;
}
$body = $this->parseStmts($node->stmts);
$body .= $this->genLoopEndFlagCheck();
$body = $this->parseForeachBody($node);
$this->indentLevel--;
$code .= $this->parseBeforeStmtLines() . PHP_EOL;

@ -29,7 +29,6 @@ use PhpAot\Php\Platform\Windows;
use PhpParser\Modifiers;
use PhpParser\Node;
use PhpParser\Node\ArrayItem;
use PhpParser\Node\Expr\List_;
use PhpParser\Node\Stmt\Foreach_;
use PhpParser\NodeAbstract;
use PhpParser\NodeTraverser;
@ -3742,29 +3741,9 @@ CODE;
$code .= $this->getIndent() . 'for (;' . $tmpVar . '.call(' . $validStr . '); ' . $tmpVar . '.call(' . $nextStr . ')) {' . PHP_EOL;
$this->indentLevel++;
if ($node->valueVar instanceof List_) {
$listTmpVar = $this->genTmpVarName();
$this->addLocalVar($listTmpVar, self::TYPE_VAR);
$code .= $this->getIndent() . ' ' . $listTmpVar . ' = ' . $tmpVar . '.call(' . $currentStr . ');' . PHP_EOL;
if ($node->keyVar) {
$keyVar = $this->parseIdentifier($node->keyVar);
$this->checkVar($node, $keyVar);
$code .= $this->getIndent() . ' ' . $keyVar . ' = ' . $tmpVar . '.call(' . $keyStr . ');' . PHP_EOL;
}
$code .= $this->parseForeachItemAsList($listTmpVar, $node->valueVar->items);
} else {
$valueVar = $this->parseIdentifier($node->valueVar);
$this->checkVar($node, $valueVar);
$code .= $this->getIndent() . ' ' . $valueVar . ' = ' . $tmpVar . '.call(' . $currentStr . ');' . PHP_EOL;
if ($node->keyVar) {
$keyVar = $this->parseIdentifier($node->keyVar);
$this->checkVar($node, $keyVar);
$code .= $this->getIndent() . ' ' . $keyVar . ' = ' . $tmpVar . '.call(' . $keyStr . ');' . PHP_EOL;
}
}
$code .= $this->parseStmts($node->stmts);
$code .= $this->genLoopEndFlagCheck();
$code .= $this->parseForeachKeyAssignment($node, $tmpVar . '.call(' . $keyStr . ')');
$code .= $this->parseForeachValueAssignment($node, $tmpVar . '.call(' . $currentStr . ')');
$code .= $this->parseForeachBody($node);
$code .= '}' . PHP_EOL;
$this->indentLevel--;
$code .= $this->getIndent() . '} else {' . PHP_EOL;

@ -0,0 +1,57 @@
--TEST--
ReturnTypeWillChange attribute on internal interface methods
--FILE--
<?php
class ReturnTypeWillChangeBox implements ArrayAccess
{
private array $items = [];
#[\ReturnTypeWillChange]
public function offsetExists(mixed $offset)
{
return isset($this->items[$offset]);
}
#[\ReturnTypeWillChange]
public function offsetGet(mixed $offset)
{
return $this->items[$offset] ?? null;
}
#[\ReturnTypeWillChange]
public function offsetSet(mixed $offset, mixed $value)
{
if ($offset === null) {
$this->items[] = $value;
return;
}
$this->items[$offset] = $value;
}
#[\ReturnTypeWillChange]
public function offsetUnset(mixed $offset)
{
unset($this->items[$offset]);
}
}
function main(): void
{
$box = new ReturnTypeWillChangeBox();
$box['name'] = 'aot';
var_dump(isset($box['name']));
var_dump($box['name']);
unset($box['name']);
var_dump(isset($box['name']));
$method = new ReflectionMethod(ReturnTypeWillChangeBox::class, 'offsetGet');
$attrs = $method->getAttributes(ReturnTypeWillChange::class);
var_dump($attrs[0]->getName());
}
?>
--EXPECT--
bool(true)
string(3) "aot"
bool(false)
string(20) "ReturnTypeWillChange"
Loading…
Cancel
Save