Advent of code 2016/24
Ajax Direct

Answer 1390ms

Part 1 : 464 Part 2 : 652
function solve($input, $return = false) {
    $grid   = grid($input)->map(fn ($c) => $c->string);
    $points = $input->numbers()->mapAssoc(fn ($i, $v) => [$v => $grid->search($v)]);
    $walls  = $grid->cells()->keep("#");

    // Define graph
    $graph = new Graph(function ($graph) use ($walls) {
        $neighbors = array_map(fn ($xy) => implode(";", $xy), neighbors(explode(";", $graph->current)));
        $neighbors = array_filter($neighbors, fn ($xy) => empty($walls[$xy]));
        return array_fill_keys($neighbors, 1);
    });

    // Compute all shortest paths between each point
    $paths = [];
    foreach ($points->keys()->combinations(2, 2) as $comb) {
        $path = $graph->explore(implode(";", $points[$comb[0]]), implode(";", $points[$comb[1]]));
        $paths[$comb[0]][$comb[1]] = $path[1];
        $paths[$comb[1]][$comb[0]] = $path[1];
    }

    // Look for the shortest permulation of all points
    $shortest = INF;
    foreach ($points->keys()->remove(0)->permutations() as $perm) {

        array_unshift($perm, 0); // Start at 0
        if ($return) array_push($perm, 0); // Return to 0

        $dist = 0;
        for ($i = 0; $i < count($perm) - 1; $i++) {
            $dist += $paths[$perm[$i]][$perm[$i + 1]];
        }

        if ($dist < $shortest) {
            $shortest = $dist;
        }
    }

    return $shortest;
}

// ==================================================
// > SOLUTIONS
// ==================================================
$solution_1 = solve($input);
$solution_2 = solve($input, true);