Вероятно, есть миллион способов сделать это, но в конечном итоге вам понадобится рекурсивный подход, который может фильтровать значение и путь к этому значению. Для этого одним из миллионов способов будет следующее.
Он использует предопределенные итераторы:
в сочетании с настраиваемым итератором PathAsKeyDecorator
.
Демо здесь
<?php
declare(strict_types=1);
final class PathAsKeyDecorator implements \Iterator
{
private RecursiveIteratorIterator $inner;
public function __construct(RecursiveIteratorIterator $inner)
{
$this->inner = $inner;
}
public function current()
{
return $this->inner->current();
}
public function next(): void
{
$this->inner->next();
}
public function key()
{
$path = [];
for ($i = 0, $depth = $this->inner->getDepth(); $i <= $depth; $i++) {
$path[] = $this->inner->getSubIterator($i)->key();
}
return $path;
}
public function valid(): bool
{
return $this->inner->valid();
}
public function rewind(): void
{
$this->inner->rewind();
}
}
$input = [
'steve' => [
'id' => [
'#text' => 1,
],
],
'albert' => [
'id' => [
'#text' => 2,
],
],
'john' => [
'profil' => [
'id' => [
'#text' => 3,
],
],
],
];
// this is the filter function that should be customized given your requirements
// or create a factory function which produces these types of filter functions
$filter = static function ($current, array $path): bool {
// with help from the PathAsKeyDecorator
// we can decide on the path to the current value
return ['id', '#text'] === array_slice($path, -2)
// and the current value
&& 2 === $current;
};
// configure the iterator
$it = new CallbackFilterIterator(
new PathAsKeyDecorator(new RecursiveIteratorIterator(new RecursiveArrayIterator($input))),
$filter,
);
// traverse the iterator
foreach ($it as $path => $val) {
print_r([
'path' => $path,
'val' => $val
]);
}
Альтернативным подходом может быть метод с простыми функциями, например:
Демо здесь
<?php
declare(strict_types=1);
function iterateWithPath(iterable $input, array $path = []): iterable {
foreach ($input as $key => $value) {
$pathToHere = [...$path, $key];
is_iterable($value)
? yield from iterateWithPath($value, $pathToHere)
: yield $pathToHere => $value;
}
}
function filterIterable(iterable $it, callable $filter): iterable {
foreach ($it as $key => $value) {
if ($filter($value, $key)) {
yield $key => $value;
}
}
}
$input = [
'steve' => [
'id' => [
'#text' => 1,
],
],
'albert' => [
'id' => [
'#text' => 2,
],
],
'john' => [
'profil' => [
'id' => [
'#text' => 3,
],
],
],
];
$it = filterIterable(iterateWithPath($input), static function ($current, array $path): bool {
return ['id', '#text'] === array_slice($path, -2)
&& 2 === $current;
});
foreach ($it as $path => $val) {
print_r([
'path' => $path,
'val' => $val
]);
}