Advent of code 2021/19
Ajax Direct

Answer

Part 1 :
Part 2 :
// Parse scans
$scans = $input->split("\n\n")->map(fn ($scan) =>
    (array) $scan->lines->slice(1)->map(fn ($xyz) => (array) $xyz->numbers())
);

// Compute all manhattan distances between points
$man = $scans->map(function ($scan) {
    $m = [];
    for ($a = 0; $a < count($scan) - 1; $a++) for ($b = $a + 1; $b < count($scan); $b++) {
        $m[] = manhattan($scan[$a], $scan[$b]);
    }
    return $m;
});

// Link scans by common manhattan distances
$linked = [];
for ($a = 0; $a < count($man) - 1; $a++) for ($b = $a + 1; $b < count($man); $b++) {
    if (count(array_intersect($man[$a], $man[$b])) >= 72) { // (12**2)/2
        $linked[] = [$a, $b];
    }
}

// Find alignements between two scans
// Brute forced : Really slow, should be optimized
function align($a, $b) {
    foreach (get_3d_points_rotations((array) $b) as $rotation) {
        foreach ($a as $ap) foreach ($rotation as $bp) {
            [$dx, $dy, $dz] = [$ap[0] - $bp[0], $ap[1] - $bp[1], $ap[2] - $bp[2]];

            $b_aligned = array_map(fn ($p) => [$p[0] + $dx, $p[1] + $dy, $p[2] + $dz], $rotation);

            $common = 0;
            foreach ($a as $ca) foreach ($b_aligned as $cb) if ($ca == $cb) {
                if (++$common >= 12) return [[$dx, $dy, $dz], set($b_aligned)];
            };
        }
    }
}

// ==================================================
// > PART 1
// ==================================================
$all = [[[0, 0], $scans[0]]];

while ($linked) {
    foreach ($linked as $i=>[$a, $b]) {
        if (!isset($all[$a])) [$a, $b] = [$b, $a];
        if (!isset($all[$a])) continue;
        $all[$b] = align($all[$a][1], $scans[$b]);
        unset($linked[$i]);
    }
}

$solution_1 = set($all)->column(1)->merge()->callEach()->join(";")->unique()->count();

// ==================================================
// > PART 2
// ==================================================
$pos = set($all)->column(0);
$solution_2 = 0;
for ($a = 0; $a < $pos->count() - 1; $a++) for ($b = $a + 1; $b < $pos->count(); $b++) {
    $solution_2 = max($solution_2, manhattan($pos[$a], $pos[$b]));
}