Advent of code 2015/11
Ajax Direct

Answer

Part 1 :
Part 2 :
class Password
{
    public function __construct($pass) {
        $this->pass = $pass;
        $this->incr = array_combine(range("a", "z"), array_merge(range("b", "z"), ["#"]));

        $range      = range("a", "z");
        $this->aa   = implode("|", array_map(fn ($a) => "$a$a", $range));
        $this->abc  = implode("|", array_map(fn ($i) => $range[$i] . $range[$i + 1] . $range[$i + 2], range(0, 23)));
    }

    /**
     * Increment the password by one.
     * Replace forbidden letters with the next one.
     *
     * @return void
     */
    public function incr()
    {
        $last = strlen($this->pass) - 1;
        $this->pass[$last] = $this->incr[$this->pass[$last]];
        while (($zero = strpos($this->pass, "#")) !== false) {
            $this->pass[$zero] = "a";
            $this->pass[$zero - 1] = $this->incr[$this->pass[$zero - 1]];
        }

        foreach (["i" => "j", "o" => "p", "l" => "m"] as $for=>$bid) {
            if (($pos = strpos($this->pass, $for)) !== false) {
                $this->pass[$pos] = $bid;
                $this->pass = substr($this->pass, 0, $pos + 1) . str_repeat("a", strlen($this->pass) - $pos - 1);
            }
        }
    }

    /**
     * Increment until the password is valid
     *
     * @return string
     */
    public function next()
    {
        while (true) {
            $this->incr();
            if (!preg_match("/{$this->abc}/", $this->pass)) continue;
            if (!preg_match("/({$this->aa}).*({$this->aa})/", $this->pass)) continue;
            return $this->pass;
        }
    }
}

// =============================================================================
// > SOLUTIONS
// =============================================================================
$password   = new Password((string) $input);
$solution_1 = $password->next();
$solution_2 = $password->next();