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 $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; } }