diff --git a/src/Context.php b/src/Context.php index c7de82f..9ab4e46 100644 --- a/src/Context.php +++ b/src/Context.php @@ -17,7 +17,6 @@ use PhpParser\Node\Expr\Cast\Bool_; use PhpParser\Node\Expr\Closure; use PhpParser\Node\Expr\FuncCall; use PhpParser\Node\Expr\MethodCall; -use PhpParser\Node\Expr\New_; use PhpParser\Node\Expr\StaticCall; use PhpParser\Node\Expr\Ternary; use PhpParser\Node\Expr\Variable; @@ -181,13 +180,13 @@ class Context $parent->setAttribute('currentNodeIndex', $nodeKeyIndex - 1); return; // Done, inserted! } - + // Cannot insert, parent is not a statement $node = &$parent->{$nodeKey}; // If we insert before a conditional branch of a conditional expression, - // make sure the conditional branch has no side effects; + // make sure the conditional branch has no side effects; // if it does, turn the entire conditional expression into an if, and bubble it up - // + // // Unless we want to go crazy, do not consider side effect evaluation order for stuff like function call arguments, maths and so on. // if ($node instanceof BooleanOr && $nodeKey === 'right' && Tools::hasSideEffects($node->right)) { diff --git a/src/Target/Php73/ListReference.php b/src/Target/Php73/ListReference.php index 3d3cb0d..12076c0 100644 --- a/src/Target/Php73/ListReference.php +++ b/src/Target/Php73/ListReference.php @@ -6,6 +6,7 @@ use Phabel\Context; use Phabel\Plugin; use PhpParser\Node\Expr\Array_; use PhpParser\Node\Expr\ArrayDimFetch; +use PhpParser\Node\Expr\ArrayItem; use PhpParser\Node\Expr\Assign; use PhpParser\Node\Expr\AssignRef; use PhpParser\Node\Expr\List_; @@ -26,13 +27,13 @@ class ListReference extends Plugin */ public function enterForeach(Foreach_ $node, Context $ctx): void { - if (!($node->valueVar instanceof List_ || $node->valueVar instanceof Array_) || !$this->shouldSplit($node->valueVar)) { + if (!($node->valueVar instanceof List_ || $node->valueVar instanceof Array_) || !$this->shouldSplit($node->valueVar, true)) { return; } $list = $node->valueVar; $var = $node->valueVar = $ctx->getVariable(); $assignments = self::splitList($list, $var); - $node->stmts + $node->stmts = \array_merge($assignments, $node->stmts); } /** * Parse list assignment with custom keys. @@ -46,8 +47,19 @@ class ListReference extends Plugin if (!($node->var instanceof List_ || $node->var instanceof Array_) || !$this->shouldSplit($node->var)) { return; } - [$node->var, $array] = $this->splitList($node->var, $ctx); - $node->expr = self::callPoly('destructure', $array, $node->expr); + $isStmt = $ctx->parents[0]->getAttribute('currentNode') === 'stmts'; + $list = $node->var; + $var = $ctx->getVariable(); + $assignments = self::splitList($list, $var); + if ($isStmt) { + $last = \array_pop($assignments); + $ctx->insertBefore($node, new Assign($node->var, $node->expr), ...$assignments); + return $last; + } + + // On newer versions of php, list assignment returns the original array + $ctx->insertBefore($node, new Assign($var, $node->expr), ...$assignments); + return $var; } /** * Split referenced list into multiple assignments. @@ -66,9 +78,7 @@ class ListReference extends Plugin continue; } $curKey = $item->key ?? $key++; - if ($item->value instanceof List_ || $item->value instanceof Array_) { - // Do nothing, will re-split later if needed - } else if ($item->byRef) { + if ($item->byRef) { $assignments []= new AssignRef($item->value, new ArrayDimFetch($var, $curKey)); } else { $assignments []= new Assign($item->value, new ArrayDimFetch($var, $curKey)); @@ -79,15 +89,21 @@ class ListReference extends Plugin /** * Whether this is a referenced list. * - * @param List_|Array_ $list List + * @param List_|Array_ $list List + * @param bool $recurse Whether to recurse while checking * * @return boolean */ - private function shouldSplit($list): bool + private function shouldSplit($list, bool $recurse = false): bool { + /** @var ArrayItem $item */ foreach ($list->items ?? [] as $item) { if ($item->byRef) { return true; + } elseif ($recurse && ($item->value instanceof List_ || $item->value instanceof Array_)) { + if ($this->shouldSplit($item->value, $recurse)) { + return true; + } } } return false;