src/Diff.php
protected function bisect($text1, $text2, $deadline)
    {
        // Cache the text lengths to prevent multiple calls.
        $text1Length = mb_strlen($text1);
        $text2Length = mb_strlen($text2);
        $maxD = (int)(($text1Length + $text2Length + 1) / 2);
        $vOffset = $maxD;
        $vLength = 2 * $maxD;
        $v1 = array_fill(0, $vLength, -1);
        $v1[$vOffset + 1] = 0;
        $v2 = $v1;
        $delta = $text1Length - $text2Length;

        // If the total number of characters is odd, then the front path will collide with the reverse path.
        $front = $delta % 2 != 0;

        // Offsets for start and end of k loop.
        // Prevents mapping of space beyond the grid.
        $k1Start = 0;
        $k1End = 0;
        $k2Start = 0;
        $k2End = 0;

        for ($d = 0; $d < $maxD; $d++) {
            // Bail out if deadline is reached.
            if (microtime(1) > $deadline) {
                break;
            }

            // Walk the front path one step.
            for ($k1 = -$d + $k1Start; $k1 < $d + 1 - $k1End; $k1 += 2) {
                $k1Offset = $vOffset + $k1;
                if ($k1 == -$d || ($k1 != $d && $v1[$k1Offset - 1] < $v1[$k1Offset + 1])) {
                    $x1 = $v1[$k1Offset + 1];
                } else {
                    $x1 = $v1[$k1Offset - 1] + 1;
                }
                $y1 = $x1 - $k1;
                while ($x1 < $text1Length && $y1 < $text2Length && mb_substr($text1, $x1, 1) == mb_substr($text2, $y1, 1)) {
                    $x1++;
                    $y1++;
                }
                $v1[$k1Offset] = $x1;
                if ($x1 > $text1Length) {
                    // Ran off the right of the graph.
                    $k1End += 2;
                } elseif ($y1 > $text2Length) {
                    // Ran off the bottom of the graph.
                    $k1Start += 2;
                } elseif ($front) {
                    $k2Offset = $vOffset + $delta - $k1;
                    if ($k2Offset >= 0 && $k2Offset < $vLength && $v2[$k2Offset] != -1) {
                        // Mirror x2 onto top-left coordinate system.
                        $x2 = $text1Length - $v2[$k2Offset];
                        if ($x1 >= $x2) {
                            // Overlap detected.
                            return $this->bisectSplit($text1, $text2, $x1, $y1, $deadline);
                        }
                    }
                }
            }

            // Walk the reverse path one step.
            for ($k2 = -$d + $k2Start; $k2 < $d + 1 - $k2End; $k2 += 2) {
                $k2Offset = $vOffset + $k2;
                if ($k2 == -$d || ($k2 != $d && $v2[$k2Offset - 1] < $v2[$k2Offset + 1])) {
                    $x2 = $v2[$k2Offset + 1];
                } else {
                    $x2 = $v2[$k2Offset - 1] + 1;
                }
                $y2 = $x2 - $k2;
                while ($x2 < $text1Length && $y2 < $text2Length && mb_substr($text1, -$x2 - 1, 1) == mb_substr($text2, -$y2 - 1, 1)) {
                    $x2++;
                    $y2++;
                }
                $v2[$k2Offset] = $x2;
                if ($x2 > $text1Length) {
                    // Ran off the right of the graph.
                    $k2End += 2;
                } elseif ($y2 > $text2Length) {
                    // Ran off the bottom of the graph.
                    $k2Start += 2;
                } elseif (!$front) {
                    $k1Offset = $vOffset + $delta - $k2;
                    if ($k1Offset >= 0 && $k1Offset < $vLength && $v1[$k1Offset] != -1) {
                        $x1 = $v1[$k1Offset];
                        $y1 = $vOffset + $x1 - $k1Offset;
                        // Mirror x2 onto top-left coordinate system.
                        $x2 = $text1Length - $x2;
                        if ($x1 >= $x2) {
                            // Overlap detected.
                            return $this->bisectSplit($text1, $text2, $x1, $y1, $deadline);
                        }
                    }
                }
            }
        }
        // Diff took too long and hit the deadline or
        // number of diffs equals number of characters, no commonality at all.
        return array(
          array(self::DELETE, $text1),
          array(self::INSERT, $text2),
        );
    }
protected function bisect($text1, $text2, $deadline)
    {
        // Cache the text lengths to prevent multiple calls.
        $text1Length = mb_strlen($text1);
        $text2Length = mb_strlen($text2);
        $maxD = (int)(($text1Length + $text2Length + 1) / 2);
        $vOffset = $maxD;
        $vLength = 2 * $maxD;
        $v1 = array_fill(0, $vLength, -1);
        $v1[$vOffset + 1] = 0;
        $v2 = $v1;
        $delta = $text1Length - $text2Length;

        // If the total number of characters is odd, then the front path will collide with the reverse path.
        $front = $delta % 2 != 0;

        // Offsets for start and end of k loop.
        // Prevents mapping of space beyond the grid.
        $k1Start = 0;
        $k1End = 0;
        $k2Start = 0;
        $k2End = 0;

        for ($d = 0; $d < $maxD; $d++) {
            // Bail out if deadline is reached.
            if (microtime(1) > $deadline) {
                break;
            }

            // Walk the front path one step.
            for ($k1 = -$d + $k1Start; $k1 < $d + 1 - $k1End; $k1 += 2) {
                $k1Offset = $vOffset + $k1;
                if ($k1 == -$d || ($k1 != $d && $v1[$k1Offset - 1] < $v1[$k1Offset + 1])) {
                    $x1 = $v1[$k1Offset + 1];
                } else {
                    $x1 = $v1[$k1Offset - 1] + 1;
                }
                $y1 = $x1 - $k1;
                while ($x1 < $text1Length && $y1 < $text2Length && mb_substr($text1, $x1, 1) == mb_substr($text2, $y1, 1)) {
                    $x1++;
                    $y1++;
                }
                $v1[$k1Offset] = $x1;
                if ($x1 > $text1Length) {
                    // Ran off the right of the graph.
                    $k1End += 2;
                } elseif ($y1 > $text2Length) {
                    // Ran off the bottom of the graph.
                    $k1Start += 2;
                } elseif ($front) {
                    $k2Offset = $vOffset + $delta - $k1;
                    if ($k2Offset >= 0 && $k2Offset < $vLength && $v2[$k2Offset] != -1) {
                        // Mirror x2 onto top-left coordinate system.
                        $x2 = $text1Length - $v2[$k2Offset];
                        if ($x1 >= $x2) {
                            // Overlap detected.
                            return $this->bisectSplit($text1, $text2, $x1, $y1, $deadline);
                        }
                    }
                }
            }

            // Walk the reverse path one step.
            for ($k2 = -$d + $k2Start; $k2 < $d + 1 - $k2End; $k2 += 2) {
                $k2Offset = $vOffset + $k2;
                if ($k2 == -$d || ($k2 != $d && $v2[$k2Offset - 1] < $v2[$k2Offset + 1])) {
                    $x2 = $v2[$k2Offset + 1];
                } else {
                    $x2 = $v2[$k2Offset - 1] + 1;
                }
                $y2 = $x2 - $k2;
                while ($x2 < $text1Length && $y2 < $text2Length && mb_substr($text1, -$x2 - 1, 1) == mb_substr($text2, -$y2 - 1, 1)) {
                    $x2++;
                    $y2++;
                }
                $v2[$k2Offset] = $x2;
                if ($x2 > $text1Length) {
                    // Ran off the right of the graph.
                    $k2End += 2;
                } elseif ($y2 > $text2Length) {
                    // Ran off the bottom of the graph.
                    $k2Start += 2;
                } elseif (!$front) {
                    $k1Offset = $vOffset + $delta - $k2;
                    if ($k1Offset >= 0 && $k1Offset < $vLength && $v1[$k1Offset] != -1) {
                        $x1 = $v1[$k1Offset];
                        $y1 = $vOffset + $x1 - $k1Offset;
                        // Mirror x2 onto top-left coordinate system.
                        $x2 = $text1Length - $x2;
                        if ($x1 >= $x2) {
                            // Overlap detected.
                            return $this->bisectSplit($text1, $text2, $x1, $y1, $deadline);
                        }
                    }
                }
            }
        }
        // Diff took too long and hit the deadline or
        // number of diffs equals number of characters, no commonality at all.
        return array(
          array(self::DELETE, $text1),
          array(self::INSERT, $text2),
        );
    }
Find the 'middle snake' of a diff, split the problem in two
and return the recursively constructed diff.
See Myers 1986 paper: An O(ND) Difference Algorithm and Its Variations.

@param string $text1    Old string to be diffed.
@param string $text2    New string to be diffed.
@param int    $deadline Time at which to bail if not yet complete.

@return array Array of diff arrays.