Advent of code 2018/8
Ajax Direct

Answer

Part 1 :
Part 2 :
$nodes = []; // [[metadata], parent_id, children_count, meta_length]
$current_id = null;
$state = 0; // 0: header children count, 1: header meta length, 2: meta

$nums = $input->numbers;
for ($i = 0; $i < $nums->count(); $i++) {
    $num = $nums[$i];

    switch ($state) {
        // Start new node, register node and children count
        case 0:
            if ($current_id !== null) $nodes[$current_id][2]--;
            $nodes[] = [[], $current_id, $num, 0];
            $current_id = count($nodes) - 1;
            $state = 1;
            break;

        // Register meta length
        case 1:
            $nodes[$current_id][3] = $num;
            $state = $nodes[$current_id][2] ? 0 : 2;
            break;

        // Register meta, go to next children or to parent
        case 2:
            $nodes[$current_id][0] = (array) $nums->slice($i, $nodes[$current_id][3]);
            $i += $nodes[$current_id][3] - 1;

            $current_id = $nodes[$current_id][1];
            if ($current_id === null) break 2; // End of tree

            $state = $nodes[$current_id][2] ? 0 : 2;
            break;
    }
}

// ==================================================
// > PART 1
// ==================================================
$solution_1 = set($nodes)->column(0)->merge()->sum();

// ==================================================
// > PART 2
// ==================================================
for ($i = count($nodes) - 1; $i >= 0; $i--) {
    $children = array_values(array_filter($nodes, fn ($node) => $node[1] === $i));

    if (!$children) {
        $nodes[$i][4] = array_sum($nodes[$i][0]);
        continue;
    }

    $nodes[$i][4] = 0;
    foreach ($nodes[$i][0] as $meta) {
        if (isset($children[$meta - 1])) {
            $nodes[$i][4] += $children[$meta - 1][4];
        }
    }
}

$solution_2 = $nodes[0][4];