1
+ − 1
<?php
+ − 2
/**
+ − 3
* Parses unified or context diffs output from eg. the diff utility.
+ − 4
*
+ − 5
* Example:
+ − 6
* <code>
+ − 7
* $patch = file_get_contents('example.patch');
+ − 8
* $diff = &new Text_Diff('string', array($patch));
+ − 9
* $renderer = &new Text_Diff_Renderer_inline();
+ − 10
* echo $renderer->render($diff);
+ − 11
* </code>
+ − 12
*
+ − 13
* @author �rjan Persson <o@42mm.org>
+ − 14
* @copyright Copyright 2005 �rjan Persson
+ − 15
* @package Text_Diff
+ − 16
* @since 0.2.0
+ − 17
* @access private
+ − 18
*/
+ − 19
class Text_Diff_Engine_string {
+ − 20
+ − 21
/**
+ − 22
* Parses a unified or context diff.
+ − 23
*
+ − 24
* First param contains the whole diff and the second can be used to force
+ − 25
* a specific diff type. If the second parameter is 'autodetect', the
+ − 26
* diff will be examined to find out which type of diff this is.
+ − 27
*
+ − 28
* @param string $diff The diff content.
+ − 29
* @param string $mode The diff mode of the content in $diff. One of
+ − 30
* 'context', 'unified', or 'autodetect'.
+ − 31
*
+ − 32
* @return array List of all diff operations.
+ − 33
*/
+ − 34
function diff($diff, $mode = 'autodetect')
+ − 35
{
+ − 36
if ($mode != 'autodetect' && $mode != 'context' && $mode != 'unified') {
+ − 37
die_friendly('Text_Diff', '<p>Type of diff is unsupported</p>');
+ − 38
}
+ − 39
+ − 40
if ($mode == 'autodetect') {
+ − 41
$context = strpos($diff, '***');
+ − 42
$unified = strpos($diff, '---');
+ − 43
if ($context === $unified) {
+ − 44
die_friendly('Text_Diff', '<p>Type of diff could not be detected</p>');
+ − 45
} elseif ($context === false || $context === false) {
+ − 46
$mode = $context !== false ? 'context' : 'unified';
+ − 47
} else {
+ − 48
$mode = $context < $unified ? 'context' : 'unified';
+ − 49
}
+ − 50
}
+ − 51
+ − 52
// split by new line and remove the diff header
+ − 53
$diff = explode("\n", $diff);
+ − 54
array_shift($diff);
+ − 55
array_shift($diff);
+ − 56
+ − 57
if ($mode == 'context') {
+ − 58
return $this->parseContextDiff($diff);
+ − 59
} else {
+ − 60
return $this->parseUnifiedDiff($diff);
+ − 61
}
+ − 62
}
+ − 63
+ − 64
/**
+ − 65
* Parses an array containing the unified diff.
+ − 66
*
+ − 67
* @param array $diff Array of lines.
+ − 68
*
+ − 69
* @return array List of all diff operations.
+ − 70
*/
+ − 71
function parseUnifiedDiff($diff)
+ − 72
{
+ − 73
$edits = array();
+ − 74
$end = count($diff) - 1;
+ − 75
for ($i = 0; $i < $end;) {
+ − 76
$diff1 = array();
+ − 77
switch (substr($diff[$i], 0, 1)) {
+ − 78
case ' ':
+ − 79
do {
+ − 80
$diff1[] = substr($diff[$i], 1);
+ − 81
} while (++$i < $end && substr($diff[$i], 0, 1) == ' ');
+ − 82
$edits[] = &new Text_Diff_Op_copy($diff1);
+ − 83
break;
+ − 84
case '+':
+ − 85
// get all new lines
+ − 86
do {
+ − 87
$diff1[] = substr($diff[$i], 1);
+ − 88
} while (++$i < $end && substr($diff[$i], 0, 1) == '+');
+ − 89
$edits[] = &new Text_Diff_Op_add($diff1);
+ − 90
break;
+ − 91
case '-':
+ − 92
// get changed or removed lines
+ − 93
$diff2 = array();
+ − 94
do {
+ − 95
$diff1[] = substr($diff[$i], 1);
+ − 96
} while (++$i < $end && substr($diff[$i], 0, 1) == '-');
+ − 97
while ($i < $end && substr($diff[$i], 0, 1) == '+') {
+ − 98
$diff2[] = substr($diff[$i++], 1);
+ − 99
}
+ − 100
if (count($diff2) == 0) {
+ − 101
$edits[] = &new Text_Diff_Op_delete($diff1);
+ − 102
} else {
+ − 103
$edits[] = &new Text_Diff_Op_change($diff1, $diff2);
+ − 104
}
+ − 105
break;
+ − 106
default:
+ − 107
$i++;
+ − 108
break;
+ − 109
}
+ − 110
}
+ − 111
return $edits;
+ − 112
}
+ − 113
+ − 114
/**
+ − 115
* Parses an array containing the context diff.
+ − 116
*
+ − 117
* @param array $diff Array of lines.
+ − 118
*
+ − 119
* @return array List of all diff operations.
+ − 120
*/
+ − 121
function parseContextDiff(&$diff)
+ − 122
{
+ − 123
$edits = array();
+ − 124
$i = $max_i = $j = $max_j = 0;
+ − 125
$end = count($diff) - 1;
+ − 126
while ($i < $end && $j < $end) {
+ − 127
while ($i >= $max_i && $j >= $max_j) {
+ − 128
// find the boundaries of the diff output of the two files
+ − 129
for ($i = $j;
+ − 130
$i < $end && substr($diff[$i], 0, 3) == '***';
+ − 131
$i++);
+ − 132
for ($max_i = $i;
+ − 133
$max_i < $end && substr($diff[$max_i], 0, 3) != '---';
+ − 134
$max_i++);
+ − 135
for ($j = $max_i;
+ − 136
$j < $end && substr($diff[$j], 0, 3) == '---';
+ − 137
$j++);
+ − 138
for ($max_j = $j;
+ − 139
$max_j < $end && substr($diff[$max_j], 0, 3) != '***';
+ − 140
$max_j++);
+ − 141
}
+ − 142
+ − 143
// find what hasn't been changed
+ − 144
$array = array();
+ − 145
while ($i < $max_i &&
+ − 146
$j < $max_j &&
+ − 147
strcmp($diff[$i], $diff[$j]) == 0) {
+ − 148
$array[] = substr($diff[$i], 2);
+ − 149
$i++;
+ − 150
$j++;
+ − 151
}
+ − 152
while ($i < $max_i && ($max_j-$j) <= 1) {
+ − 153
if ($diff[$i] != '' && substr($diff[$i], 0, 1) != ' ') {
+ − 154
break;
+ − 155
}
+ − 156
$array[] = substr($diff[$i++], 2);
+ − 157
}
+ − 158
while ($j < $max_j && ($max_i-$i) <= 1) {
+ − 159
if ($diff[$j] != '' && substr($diff[$j], 0, 1) != ' ') {
+ − 160
break;
+ − 161
}
+ − 162
$array[] = substr($diff[$j++], 2);
+ − 163
}
+ − 164
if (count($array) > 0) {
+ − 165
$edits[] = &new Text_Diff_Op_copy($array);
+ − 166
}
+ − 167
+ − 168
if ($i < $max_i) {
+ − 169
$diff1 = array();
+ − 170
switch (substr($diff[$i], 0, 1)) {
+ − 171
case '!':
+ − 172
$diff2 = array();
+ − 173
do {
+ − 174
$diff1[] = substr($diff[$i], 2);
+ − 175
if ($j < $max_j && substr($diff[$j], 0, 1) == '!') {
+ − 176
$diff2[] = substr($diff[$j++], 2);
+ − 177
}
+ − 178
} while (++$i < $max_i && substr($diff[$i], 0, 1) == '!');
+ − 179
$edits[] = &new Text_Diff_Op_change($diff1, $diff2);
+ − 180
break;
+ − 181
case '+':
+ − 182
do {
+ − 183
$diff1[] = substr($diff[$i], 2);
+ − 184
} while (++$i < $max_i && substr($diff[$i], 0, 1) == '+');
+ − 185
$edits[] = &new Text_Diff_Op_add($diff1);
+ − 186
break;
+ − 187
case '-':
+ − 188
do {
+ − 189
$diff1[] = substr($diff[$i], 2);
+ − 190
} while (++$i < $max_i && substr($diff[$i], 0, 1) == '-');
+ − 191
$edits[] = &new Text_Diff_Op_delete($diff1);
+ − 192
break;
+ − 193
}
+ − 194
}
+ − 195
+ − 196
if ($j < $max_j) {
+ − 197
$diff2 = array();
+ − 198
switch (substr($diff[$j], 0, 1)) {
+ − 199
case '+':
+ − 200
do {
+ − 201
$diff2[] = substr($diff[$j++], 2);
+ − 202
} while ($j < $max_j && substr($diff[$j], 0, 1) == '+');
+ − 203
$edits[] = &new Text_Diff_Op_add($diff2);
+ − 204
break;
+ − 205
case '-':
+ − 206
do {
+ − 207
$diff2[] = substr($diff[$j++], 2);
+ − 208
} while ($j < $max_j && substr($diff[$j], 0, 1) == '-');
+ − 209
$edits[] = &new Text_Diff_Op_delete($diff2);
+ − 210
break;
+ − 211
}
+ − 212
}
+ − 213
}
+ − 214
return $edits;
+ − 215
}
+ − 216
}