Advent of code 2020/17
Ajax Direct

Answer

Part 1 :
Part 2 :
function solve($expand_4d = false) {
    global $input;
    $cube = [[array_map("str_split", explode("\n", (string) $input))]];

    // Range of each dimension
    [$fw, $tw] = [0, 0];
    [$fz, $tz] = [0, 0];
    [$fy, $ty] = [0, count($cube[0][0]) - 1];
    [$fx, $tx] = [0, count($cube[0][0][0]) - 1];

    $ncube = $cube;
    for ($i = 0; $i < 6; $i++) {

        // Expand the cube
        if ($expand_4d) { $fw--; $tw++; }
        $fz--; $tz++; $fy--; $ty++; $fx--; $tx++;

        // Consider each cell
        for ($w = $fw; $w <= $tw; $w++)
        for ($z = $fz; $z <= $tz; $z++)
        for ($y = $fy; $y <= $ty; $y++)
        for ($x = $fx; $x <= $tx; $x++) {

            // Count neighbors from -1 to +1 in each dimension
            $active_neighbors = 0;
            for ($nw = $w - 1; $nw <= $w + 1; $nw++)
            for ($nz = $z - 1; $nz <= $z + 1; $nz++)
            for ($ny = $y - 1; $ny <= $y + 1; $ny++)
            for ($nx = $x - 1; $nx <= $x + 1; $nx++) {
                if (($nw == $w && $nz == $z && $ny == $y && $nx == $x)) continue;
                if (($cube[$nw][$nz][$ny][$nx] ?? false) == "#") $active_neighbors++;
            }

            // Update cell acording to number of active neighbors
            if (($cube[$w][$z][$y][$x] ?? ".") == "#") {
                $ncube[$w][$z][$y][$x] = $active_neighbors == 2 || $active_neighbors == 3 ? "#" : ".";
            } else {
                $ncube[$w][$z][$y][$x] = $active_neighbors == 3 ? "#" : ".";
            }
        }
        $cube = $ncube;
    }

    // Merge everything, count active cells
    return set(array_merge(...array_merge(...array_merge(...$cube))))->remove(".")->count();
}

$solution_1 = solve();
$solution_2 = solve(true);