*/ final class PathMapping implements IteratorAggregate { /** @var array */ private array $source; /** * @param iterable $source * @param array $map */ public function __construct(iterable $source, array $map) { $this->source = $this->map($source, $this->prepareMappings($map)); } public function getIterator(): Traversable { yield from $this->source; } /** * @param iterable $source * @param array $mappings * @return array */ private function map(iterable $source, array $mappings, int $depth = 0): array { $out = []; foreach ($source as $key => $value) { /** @var int|string $key */ $newMappings = array_filter($mappings, fn (Mapping $mapping) => $mapping->matches($key, $depth)); $newKey = $this->findMapping($newMappings, $depth, $key); if (is_array($value)) { $out[$newKey] = $this->map($value, $newMappings, $depth + 1); continue; } $out[$newKey] = $value; } return $out; } /** * @param array $map * @return array */ private function prepareMappings(array $map): array { $mappings = []; foreach ($map as $from => $to) { $mappings[] = new Mapping(explode('.', (string)$from), $to); } return $mappings; } /** * @param array $mappings */ private function findMapping(array $mappings, int $atDepth, int|string $key): int|string { foreach ($mappings as $mapping) { $mappedKey = $mapping->findMappedKey($key, $atDepth); if (null !== $mappedKey) { return $mappedKey; } } return $key; } }