163 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			163 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
<?php
 | 
						|
/**
 | 
						|
 * Class used internally by Diff to actually compute the diffs.
 | 
						|
 *
 | 
						|
 * This class uses the Unix `diff` program via shell_exec to compute the
 | 
						|
 * differences between the two input arrays.
 | 
						|
 *
 | 
						|
 * Copyright 2007-2010 The Horde Project (http://www.horde.org/)
 | 
						|
 *
 | 
						|
 * See the enclosed file COPYING for license information (LGPL). If you did
 | 
						|
 * not receive this file, see https://opensource.org/license/lgpl-2-1/.
 | 
						|
 *
 | 
						|
 * @author  Milian Wolff <mail@milianw.de>
 | 
						|
 * @package Text_Diff
 | 
						|
 * @since   0.3.0
 | 
						|
 */
 | 
						|
class Text_Diff_Engine_shell {
 | 
						|
 | 
						|
    /**
 | 
						|
     * Path to the diff executable
 | 
						|
     *
 | 
						|
     * @var string
 | 
						|
     */
 | 
						|
    var $_diffCommand = 'diff';
 | 
						|
 | 
						|
    /**
 | 
						|
     * Returns the array of differences.
 | 
						|
     *
 | 
						|
     * @param array $from_lines lines of text from old file
 | 
						|
     * @param array $to_lines   lines of text from new file
 | 
						|
     *
 | 
						|
     * @return array all changes made (array with Text_Diff_Op_* objects)
 | 
						|
     */
 | 
						|
    function diff($from_lines, $to_lines)
 | 
						|
    {
 | 
						|
        array_walk($from_lines, array('Text_Diff', 'trimNewlines'));
 | 
						|
        array_walk($to_lines, array('Text_Diff', 'trimNewlines'));
 | 
						|
 | 
						|
        $temp_dir = Text_Diff::_getTempDir();
 | 
						|
 | 
						|
        // Execute gnu diff or similar to get a standard diff file.
 | 
						|
        $from_file = tempnam($temp_dir, 'Text_Diff');
 | 
						|
        $to_file = tempnam($temp_dir, 'Text_Diff');
 | 
						|
        $fp = fopen($from_file, 'w');
 | 
						|
        fwrite($fp, implode("\n", $from_lines));
 | 
						|
        fclose($fp);
 | 
						|
        $fp = fopen($to_file, 'w');
 | 
						|
        fwrite($fp, implode("\n", $to_lines));
 | 
						|
        fclose($fp);
 | 
						|
        $diff = shell_exec($this->_diffCommand . ' ' . $from_file . ' ' . $to_file);
 | 
						|
        unlink($from_file);
 | 
						|
        unlink($to_file);
 | 
						|
 | 
						|
        if (is_null($diff)) {
 | 
						|
            // No changes were made
 | 
						|
            return array(new Text_Diff_Op_copy($from_lines));
 | 
						|
        }
 | 
						|
 | 
						|
        $from_line_no = 1;
 | 
						|
        $to_line_no = 1;
 | 
						|
        $edits = array();
 | 
						|
 | 
						|
        // Get changed lines by parsing something like:
 | 
						|
        // 0a1,2
 | 
						|
        // 1,2c4,6
 | 
						|
        // 1,5d6
 | 
						|
        preg_match_all('#^(\d+)(?:,(\d+))?([adc])(\d+)(?:,(\d+))?$#m', $diff,
 | 
						|
            $matches, PREG_SET_ORDER);
 | 
						|
 | 
						|
        foreach ($matches as $match) {
 | 
						|
            if (!isset($match[5])) {
 | 
						|
                // This paren is not set every time (see regex).
 | 
						|
                $match[5] = false;
 | 
						|
            }
 | 
						|
 | 
						|
            if ($match[3] == 'a') {
 | 
						|
                $from_line_no--;
 | 
						|
            }
 | 
						|
 | 
						|
            if ($match[3] == 'd') {
 | 
						|
                $to_line_no--;
 | 
						|
            }
 | 
						|
 | 
						|
            if ($from_line_no < $match[1] || $to_line_no < $match[4]) {
 | 
						|
                // copied lines
 | 
						|
                assert($match[1] - $from_line_no == $match[4] - $to_line_no);
 | 
						|
                array_push($edits,
 | 
						|
                    new Text_Diff_Op_copy(
 | 
						|
                        $this->_getLines($from_lines, $from_line_no, $match[1] - 1),
 | 
						|
                        $this->_getLines($to_lines, $to_line_no, $match[4] - 1)));
 | 
						|
            }
 | 
						|
 | 
						|
            switch ($match[3]) {
 | 
						|
            case 'd':
 | 
						|
                // deleted lines
 | 
						|
                array_push($edits,
 | 
						|
                    new Text_Diff_Op_delete(
 | 
						|
                        $this->_getLines($from_lines, $from_line_no, $match[2])));
 | 
						|
                $to_line_no++;
 | 
						|
                break;
 | 
						|
 | 
						|
            case 'c':
 | 
						|
                // changed lines
 | 
						|
                array_push($edits,
 | 
						|
                    new Text_Diff_Op_change(
 | 
						|
                        $this->_getLines($from_lines, $from_line_no, $match[2]),
 | 
						|
                        $this->_getLines($to_lines, $to_line_no, $match[5])));
 | 
						|
                break;
 | 
						|
 | 
						|
            case 'a':
 | 
						|
                // added lines
 | 
						|
                array_push($edits,
 | 
						|
                    new Text_Diff_Op_add(
 | 
						|
                        $this->_getLines($to_lines, $to_line_no, $match[5])));
 | 
						|
                $from_line_no++;
 | 
						|
                break;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        if (!empty($from_lines)) {
 | 
						|
            // Some lines might still be pending. Add them as copied
 | 
						|
            array_push($edits,
 | 
						|
                new Text_Diff_Op_copy(
 | 
						|
                    $this->_getLines($from_lines, $from_line_no,
 | 
						|
                                     $from_line_no + count($from_lines) - 1),
 | 
						|
                    $this->_getLines($to_lines, $to_line_no,
 | 
						|
                                     $to_line_no + count($to_lines) - 1)));
 | 
						|
        }
 | 
						|
 | 
						|
        return $edits;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Get lines from either the old or new text
 | 
						|
     *
 | 
						|
     * @access private
 | 
						|
     *
 | 
						|
     * @param array $text_lines Either $from_lines or $to_lines (passed by reference).
 | 
						|
     * @param int   $line_no    Current line number (passed by reference).
 | 
						|
     * @param int   $end        Optional end line, when we want to chop more
 | 
						|
     *                          than one line.
 | 
						|
     *
 | 
						|
     * @return array The chopped lines
 | 
						|
     */
 | 
						|
    function _getLines(&$text_lines, &$line_no, $end = false)
 | 
						|
    {
 | 
						|
        if (!empty($end)) {
 | 
						|
            $lines = array();
 | 
						|
            // We can shift even more
 | 
						|
            while ($line_no <= $end) {
 | 
						|
                array_push($lines, array_shift($text_lines));
 | 
						|
                $line_no++;
 | 
						|
            }
 | 
						|
        } else {
 | 
						|
            $lines = array(array_shift($text_lines));
 | 
						|
            $line_no++;
 | 
						|
        }
 | 
						|
 | 
						|
        return $lines;
 | 
						|
    }
 | 
						|
 | 
						|
}
 |