118 lines
3.1 KiB
PHP
118 lines
3.1 KiB
PHP
<?php
|
|
|
|
namespace Phabel\Target\Php71;
|
|
|
|
use Phabel\Plugin;
|
|
use PhpParser\Node\Expr\Array_;
|
|
use PhpParser\Node\Expr\ArrayItem;
|
|
use PhpParser\Node\Expr\Assign;
|
|
use PhpParser\Node\Expr\List_;
|
|
use PhpParser\Node\Stmt\Foreach_;
|
|
|
|
/**
|
|
* Polyfills keyed list assignment.
|
|
*/
|
|
class ListKey extends Plugin
|
|
{
|
|
/**
|
|
* Parse list foreach with custom keys.
|
|
*
|
|
* @param Foreach_ $node Foreach
|
|
*
|
|
* @return void
|
|
*/
|
|
public function enterForeach(Foreach_ $node): void
|
|
{
|
|
if (!$node->valueVar instanceof List_ || !$this->shouldSplit($node->valueVar)) {
|
|
return;
|
|
}
|
|
[$node->valueVar, $array] = $this->splitList($node->valueVar);
|
|
$node->expr = self::callPoly('destructure', $array, $node->expr);
|
|
}
|
|
/**
|
|
* Parse list assignment with custom keys.
|
|
*
|
|
* @param Assign $node List assignment
|
|
*
|
|
* @return void
|
|
*/
|
|
public function enterAssign(Assign $node): void
|
|
{
|
|
if (!$node->var instanceof List_ || !$this->shouldSplit($node->var)) {
|
|
return;
|
|
}
|
|
[$node->var, $array] = $this->splitList($node->var);
|
|
$node->expr = self::callPoly('destructure', $array, $node->expr);
|
|
}
|
|
/**
|
|
* Whether this is a keyed list.
|
|
*
|
|
* @param List_ $list List
|
|
*
|
|
* @return boolean
|
|
*/
|
|
private function shouldSplit(List_ $list): bool
|
|
{
|
|
return isset($list->items[0]->key);
|
|
}
|
|
/**
|
|
* Split keyed list into new list assignment and array to pass to destructure function.
|
|
*
|
|
* @param List_ $list Keyed list
|
|
*
|
|
* @return array{0: List_, 1: Array_}
|
|
*/
|
|
private function splitList(List_ $list): array
|
|
{
|
|
$newList = [];
|
|
$keys = [];
|
|
$key = 0; // Technically list assignment does not support mixed keys, but we need this for nested assignments
|
|
foreach ($list->items as $item) {
|
|
if ($item) {
|
|
$curKey = $item->key ?? $key++;
|
|
$item->key = null;
|
|
if ($item->value instanceof List_) {
|
|
[$item->value, $keys[$curKey]] = $this->splitList($list);
|
|
} else {
|
|
$keys[$curKey] = null;
|
|
}
|
|
} else {
|
|
$newList []= null;
|
|
$keys[$key++] = null;
|
|
}
|
|
}
|
|
/** @var Array_ */
|
|
$keys = self::toLiteral($keys);
|
|
return [new List_($newList), $keys];
|
|
}
|
|
/**
|
|
* Destructure array.
|
|
*
|
|
* @param array $keys Custom keys
|
|
* @param array $array Array
|
|
*
|
|
* @psalm-param array<string, null|array> $keys Custom keys
|
|
*
|
|
* @return array
|
|
*/
|
|
public static function destructure(array $keys, array $array): array
|
|
{
|
|
$res = [];
|
|
foreach ($keys as $key => $type) {
|
|
if ($type === null) {
|
|
$res[] = $array[$key];
|
|
} else {
|
|
$res[] = self::destructure($type, $array[$key]);
|
|
}
|
|
}
|
|
return $res;
|
|
}
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
public function needs(): string
|
|
{
|
|
return ArrayList::class;
|
|
}
|
|
}
|