[44] | 1 | <?php
|
---|
| 2 | /**
|
---|
| 3 | * Class used internally by Diff to actually compute the diffs.
|
---|
| 4 | *
|
---|
| 5 | * This class uses the Unix `diff` program via shell_exec to compute the
|
---|
| 6 | * differences between the two input arrays.
|
---|
| 7 | *
|
---|
| 8 | * $Horde: framework/Text_Diff/Diff/Engine/shell.php,v 1.8 2008/01/04 10:07:50 jan Exp $
|
---|
| 9 | *
|
---|
| 10 | * Copyright 2007-2008 The Horde Project (http://www.horde.org/)
|
---|
| 11 | *
|
---|
| 12 | * See the enclosed file COPYING for license information (LGPL). If you did
|
---|
| 13 | * not receive this file, see http://opensource.org/licenses/lgpl-license.php.
|
---|
| 14 | *
|
---|
| 15 | * @author Milian Wolff <mail@milianw.de>
|
---|
| 16 | * @package Text_Diff
|
---|
| 17 | * @since 0.3.0
|
---|
| 18 | */
|
---|
| 19 | class Text_Diff_Engine_shell {
|
---|
| 20 |
|
---|
| 21 | /**
|
---|
| 22 | * Path to the diff executable
|
---|
| 23 | *
|
---|
| 24 | * @var string
|
---|
| 25 | */
|
---|
| 26 | var $_diffCommand = 'diff';
|
---|
| 27 |
|
---|
| 28 | /**
|
---|
| 29 | * Returns the array of differences.
|
---|
| 30 | *
|
---|
| 31 | * @param array $from_lines lines of text from old file
|
---|
| 32 | * @param array $to_lines lines of text from new file
|
---|
| 33 | *
|
---|
| 34 | * @return array all changes made (array with Text_Diff_Op_* objects)
|
---|
| 35 | */
|
---|
| 36 | function diff($from_lines, $to_lines)
|
---|
| 37 | {
|
---|
| 38 | array_walk($from_lines, array('Text_Diff', 'trimNewlines'));
|
---|
| 39 | array_walk($to_lines, array('Text_Diff', 'trimNewlines'));
|
---|
| 40 |
|
---|
| 41 | $temp_dir = Text_Diff::_getTempDir();
|
---|
| 42 |
|
---|
| 43 | // Execute gnu diff or similar to get a standard diff file.
|
---|
| 44 | $from_file = tempnam($temp_dir, 'Text_Diff');
|
---|
| 45 | $to_file = tempnam($temp_dir, 'Text_Diff');
|
---|
| 46 | $fp = fopen($from_file, 'w');
|
---|
| 47 | fwrite($fp, implode("\n", $from_lines));
|
---|
| 48 | fclose($fp);
|
---|
| 49 | $fp = fopen($to_file, 'w');
|
---|
| 50 | fwrite($fp, implode("\n", $to_lines));
|
---|
| 51 | fclose($fp);
|
---|
| 52 | $diff = shell_exec($this->_diffCommand . ' ' . $from_file . ' ' . $to_file);
|
---|
| 53 | unlink($from_file);
|
---|
| 54 | unlink($to_file);
|
---|
| 55 |
|
---|
| 56 | if (is_null($diff)) {
|
---|
| 57 | // No changes were made
|
---|
| 58 | return array(new Text_Diff_Op_copy($from_lines));
|
---|
| 59 | }
|
---|
| 60 |
|
---|
| 61 | $from_line_no = 1;
|
---|
| 62 | $to_line_no = 1;
|
---|
| 63 | $edits = array();
|
---|
| 64 |
|
---|
| 65 | // Get changed lines by parsing something like:
|
---|
| 66 | // 0a1,2
|
---|
| 67 | // 1,2c4,6
|
---|
| 68 | // 1,5d6
|
---|
| 69 | preg_match_all('#^(\d+)(?:,(\d+))?([adc])(\d+)(?:,(\d+))?$#m', $diff,
|
---|
| 70 | $matches, PREG_SET_ORDER);
|
---|
| 71 |
|
---|
| 72 | foreach ($matches as $match) {
|
---|
| 73 | if (!isset($match[5])) {
|
---|
| 74 | // This paren is not set every time (see regex).
|
---|
| 75 | $match[5] = false;
|
---|
| 76 | }
|
---|
| 77 |
|
---|
| 78 | if ($match[3] == 'a') {
|
---|
| 79 | $from_line_no--;
|
---|
| 80 | }
|
---|
| 81 |
|
---|
| 82 | if ($match[3] == 'd') {
|
---|
| 83 | $to_line_no--;
|
---|
| 84 | }
|
---|
| 85 |
|
---|
| 86 | if ($from_line_no < $match[1] || $to_line_no < $match[4]) {
|
---|
| 87 | // copied lines
|
---|
| 88 | assert('$match[1] - $from_line_no == $match[4] - $to_line_no');
|
---|
| 89 | array_push($edits,
|
---|
| 90 | new Text_Diff_Op_copy(
|
---|
| 91 | $this->_getLines($from_lines, $from_line_no, $match[1] - 1),
|
---|
| 92 | $this->_getLines($to_lines, $to_line_no, $match[4] - 1)));
|
---|
| 93 | }
|
---|
| 94 |
|
---|
| 95 | switch ($match[3]) {
|
---|
| 96 | case 'd':
|
---|
| 97 | // deleted lines
|
---|
| 98 | array_push($edits,
|
---|
| 99 | new Text_Diff_Op_delete(
|
---|
| 100 | $this->_getLines($from_lines, $from_line_no, $match[2])));
|
---|
| 101 | $to_line_no++;
|
---|
| 102 | break;
|
---|
| 103 |
|
---|
| 104 | case 'c':
|
---|
| 105 | // changed lines
|
---|
| 106 | array_push($edits,
|
---|
| 107 | new Text_Diff_Op_change(
|
---|
| 108 | $this->_getLines($from_lines, $from_line_no, $match[2]),
|
---|
| 109 | $this->_getLines($to_lines, $to_line_no, $match[5])));
|
---|
| 110 | break;
|
---|
| 111 |
|
---|
| 112 | case 'a':
|
---|
| 113 | // added lines
|
---|
| 114 | array_push($edits,
|
---|
| 115 | new Text_Diff_Op_add(
|
---|
| 116 | $this->_getLines($to_lines, $to_line_no, $match[5])));
|
---|
| 117 | $from_line_no++;
|
---|
| 118 | break;
|
---|
| 119 | }
|
---|
| 120 | }
|
---|
| 121 |
|
---|
| 122 | if (!empty($from_lines)) {
|
---|
| 123 | // Some lines might still be pending. Add them as copied
|
---|
| 124 | array_push($edits,
|
---|
| 125 | new Text_Diff_Op_copy(
|
---|
| 126 | $this->_getLines($from_lines, $from_line_no,
|
---|
| 127 | $from_line_no + count($from_lines) - 1),
|
---|
| 128 | $this->_getLines($to_lines, $to_line_no,
|
---|
| 129 | $to_line_no + count($to_lines) - 1)));
|
---|
| 130 | }
|
---|
| 131 |
|
---|
| 132 | return $edits;
|
---|
| 133 | }
|
---|
| 134 |
|
---|
| 135 | /**
|
---|
| 136 | * Get lines from either the old or new text
|
---|
| 137 | *
|
---|
| 138 | * @access private
|
---|
| 139 | *
|
---|
| 140 | * @param array &$text_lines Either $from_lines or $to_lines
|
---|
| 141 | * @param int &$line_no Current line number
|
---|
| 142 | * @param int $end Optional end line, when we want to chop more
|
---|
| 143 | * than one line.
|
---|
| 144 | *
|
---|
| 145 | * @return array The chopped lines
|
---|
| 146 | */
|
---|
| 147 | function _getLines(&$text_lines, &$line_no, $end = false)
|
---|
| 148 | {
|
---|
| 149 | if (!empty($end)) {
|
---|
| 150 | $lines = array();
|
---|
| 151 | // We can shift even more
|
---|
| 152 | while ($line_no <= $end) {
|
---|
| 153 | array_push($lines, array_shift($text_lines));
|
---|
| 154 | $line_no++;
|
---|
| 155 | }
|
---|
| 156 | } else {
|
---|
| 157 | $lines = array(array_shift($text_lines));
|
---|
| 158 | $line_no++;
|
---|
| 159 | }
|
---|
| 160 |
|
---|
| 161 | return $lines;
|
---|
| 162 | }
|
---|
| 163 |
|
---|
| 164 | }
|
---|