Answer ⏱  
                        
                                                             [VISUALISATION]
                                                        Part 1 : 
                            
                            Part 2 : 
                            
                         
                     
                    // Build map
$grid = [];
foreach ($input->lines as $line) {
    $nums = $line->numbers();
    foreach (range($nums[1], $nums[2]) as $num) {
        $line[0] == "x"
            ? ($grid[$num][$nums[0]] = "#")
            : ($grid[$nums[0]][$num] = "#");
    }
}
// Simulate waterfall
$ys      = array_keys($grid); sort($ys);
$floor   = max($ys);
$ceiling = $ys[0];
$drops   = [[500, 0]];
$sources = [];
$d = 0;
while ($drops) {
    $drop = array_shift($drops);
    $log[] = $drop;
    $d++;
    [$x, $y] = $drop;
    $grid[$y][$x] = 0;
    if (!empty($sources[$y][$x])) continue;
    $sources[$y][$x] = true;
    // Go down as far as possible
    while ((empty($grid[++$y][$x])) && $y <= $floor) $grid[$y][$x] = 0;
    if ($y > $floor) continue;
    $y--;
    // Go left and right as far as possible
    [$l, $r] = [$x, $x];
    $grid[$y][$x] = 0;
    while ((empty($grid[$y][--$l])) && !empty($grid[$y+1][$l])) $grid[$y][$l] = 0;
    while ((empty($grid[$y][++$r])) && !empty($grid[$y+1][$r])) $grid[$y][$r] = 0;
    // Add drop if we reached a border
    if (empty($grid[$y][$l])) $drops[] = [$l, $y];
    if (empty($grid[$y][$r])) $drops[] = [$r, $y];
    // Add drop above if water is traped, and change water state
    if (!empty($grid[$y][$l]) && !empty($grid[$y][$r])) {
        $sources[$y-1][$x] = false;
        $drops[] = [$x, $y-1];
        foreach (range($l + 1, $r - 1) as $x) {
            $grid[$y][$x] = "~";
        }
    }
}
$tiles = set($grid)->merge();
// ==================================================
// > PART 1
// ==================================================
$solution_1 = $tiles->remove("#")->count() - $ceiling;
// ==================================================
// > PART 2
// ==================================================
$solution_2 = $tiles->keep("~")->count();