PageRenderTime 63ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 0ms

/wp-includes/Text/Diff/Engine/string.php

https://bitbucket.org/mrmustarde/manhattan-beach
PHP | 248 lines | 170 code | 23 blank | 55 comment | 92 complexity | 6df044c920f657487c0d04e69a66aeb8 MD5 | raw file
Possible License(s): BSD-3-Clause
  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. * Copyright 2005 Örjan Persson <o@42mm.org>
  14. * Copyright 2005-2010 The Horde Project (http://www.horde.org/)
  15. *
  16. * See the enclosed file COPYING for license information (LGPL). If you did
  17. * not receive this file, see http://opensource.org/licenses/lgpl-license.php.
  18. *
  19. * @author Örjan Persson <o@42mm.org>
  20. * @package Text_Diff
  21. * @since 0.2.0
  22. */
  23. class Text_Diff_Engine_string {
  24. /**
  25. * Parses a unified or context diff.
  26. *
  27. * First param contains the whole diff and the second can be used to force
  28. * a specific diff type. If the second parameter is 'autodetect', the
  29. * diff will be examined to find out which type of diff this is.
  30. *
  31. * @param string $diff The diff content.
  32. * @param string $mode The diff mode of the content in $diff. One of
  33. * 'context', 'unified', or 'autodetect'.
  34. *
  35. * @return array List of all diff operations.
  36. */
  37. function diff($diff, $mode = 'autodetect')
  38. {
  39. // Detect line breaks.
  40. $lnbr = "\n";
  41. if (strpos($diff, "\r\n") !== false) {
  42. $lnbr = "\r\n";
  43. } elseif (strpos($diff, "\r") !== false) {
  44. $lnbr = "\r";
  45. }
  46. // Make sure we have a line break at the EOF.
  47. if (substr($diff, -strlen($lnbr)) != $lnbr) {
  48. $diff .= $lnbr;
  49. }
  50. if ($mode != 'autodetect' && $mode != 'context' && $mode != 'unified') {
  51. return PEAR::raiseError('Type of diff is unsupported');
  52. }
  53. if ($mode == 'autodetect') {
  54. $context = strpos($diff, '***');
  55. $unified = strpos($diff, '---');
  56. if ($context === $unified) {
  57. return PEAR::raiseError('Type of diff could not be detected');
  58. } elseif ($context === false || $unified === false) {
  59. $mode = $context !== false ? 'context' : 'unified';
  60. } else {
  61. $mode = $context < $unified ? 'context' : 'unified';
  62. }
  63. }
  64. // Split by new line and remove the diff header, if there is one.
  65. $diff = explode($lnbr, $diff);
  66. if (($mode == 'context' && strpos($diff[0], '***') === 0) ||
  67. ($mode == 'unified' && strpos($diff[0], '---') === 0)) {
  68. array_shift($diff);
  69. array_shift($diff);
  70. }
  71. if ($mode == 'context') {
  72. return $this->parseContextDiff($diff);
  73. } else {
  74. return $this->parseUnifiedDiff($diff);
  75. }
  76. }
  77. /**
  78. * Parses an array containing the unified diff.
  79. *
  80. * @param array $diff Array of lines.
  81. *
  82. * @return array List of all diff operations.
  83. */
  84. function parseUnifiedDiff($diff)
  85. {
  86. $edits = array();
  87. $end = count($diff) - 1;
  88. for ($i = 0; $i < $end;) {
  89. $diff1 = array();
  90. switch (substr($diff[$i], 0, 1)) {
  91. case ' ':
  92. do {
  93. $diff1[] = substr($diff[$i], 1);
  94. } while (++$i < $end && substr($diff[$i], 0, 1) == ' ');
  95. $edits[] = &new Text_Diff_Op_copy($diff1);
  96. break;
  97. case '+':
  98. // get all new lines
  99. do {
  100. $diff1[] = substr($diff[$i], 1);
  101. } while (++$i < $end && substr($diff[$i], 0, 1) == '+');
  102. $edits[] = &new Text_Diff_Op_add($diff1);
  103. break;
  104. case '-':
  105. // get changed or removed lines
  106. $diff2 = array();
  107. do {
  108. $diff1[] = substr($diff[$i], 1);
  109. } while (++$i < $end && substr($diff[$i], 0, 1) == '-');
  110. while ($i < $end && substr($diff[$i], 0, 1) == '+') {
  111. $diff2[] = substr($diff[$i++], 1);
  112. }
  113. if (count($diff2) == 0) {
  114. $edits[] = &new Text_Diff_Op_delete($diff1);
  115. } else {
  116. $edits[] = &new Text_Diff_Op_change($diff1, $diff2);
  117. }
  118. break;
  119. default:
  120. $i++;
  121. break;
  122. }
  123. }
  124. return $edits;
  125. }
  126. /**
  127. * Parses an array containing the context diff.
  128. *
  129. * @param array $diff Array of lines.
  130. *
  131. * @return array List of all diff operations.
  132. */
  133. function parseContextDiff(&$diff)
  134. {
  135. $edits = array();
  136. $i = $max_i = $j = $max_j = 0;
  137. $end = count($diff) - 1;
  138. while ($i < $end && $j < $end) {
  139. while ($i >= $max_i && $j >= $max_j) {
  140. // Find the boundaries of the diff output of the two files
  141. for ($i = $j;
  142. $i < $end && substr($diff[$i], 0, 3) == '***';
  143. $i++);
  144. for ($max_i = $i;
  145. $max_i < $end && substr($diff[$max_i], 0, 3) != '---';
  146. $max_i++);
  147. for ($j = $max_i;
  148. $j < $end && substr($diff[$j], 0, 3) == '---';
  149. $j++);
  150. for ($max_j = $j;
  151. $max_j < $end && substr($diff[$max_j], 0, 3) != '***';
  152. $max_j++);
  153. }
  154. // find what hasn't been changed
  155. $array = array();
  156. while ($i < $max_i &&
  157. $j < $max_j &&
  158. strcmp($diff[$i], $diff[$j]) == 0) {
  159. $array[] = substr($diff[$i], 2);
  160. $i++;
  161. $j++;
  162. }
  163. while ($i < $max_i && ($max_j-$j) <= 1) {
  164. if ($diff[$i] != '' && substr($diff[$i], 0, 1) != ' ') {
  165. break;
  166. }
  167. $array[] = substr($diff[$i++], 2);
  168. }
  169. while ($j < $max_j && ($max_i-$i) <= 1) {
  170. if ($diff[$j] != '' && substr($diff[$j], 0, 1) != ' ') {
  171. break;
  172. }
  173. $array[] = substr($diff[$j++], 2);
  174. }
  175. if (count($array) > 0) {
  176. $edits[] = &new Text_Diff_Op_copy($array);
  177. }
  178. if ($i < $max_i) {
  179. $diff1 = array();
  180. switch (substr($diff[$i], 0, 1)) {
  181. case '!':
  182. $diff2 = array();
  183. do {
  184. $diff1[] = substr($diff[$i], 2);
  185. if ($j < $max_j && substr($diff[$j], 0, 1) == '!') {
  186. $diff2[] = substr($diff[$j++], 2);
  187. }
  188. } while (++$i < $max_i && substr($diff[$i], 0, 1) == '!');
  189. $edits[] = &new Text_Diff_Op_change($diff1, $diff2);
  190. break;
  191. case '+':
  192. do {
  193. $diff1[] = substr($diff[$i], 2);
  194. } while (++$i < $max_i && substr($diff[$i], 0, 1) == '+');
  195. $edits[] = &new Text_Diff_Op_add($diff1);
  196. break;
  197. case '-':
  198. do {
  199. $diff1[] = substr($diff[$i], 2);
  200. } while (++$i < $max_i && substr($diff[$i], 0, 1) == '-');
  201. $edits[] = &new Text_Diff_Op_delete($diff1);
  202. break;
  203. }
  204. }
  205. if ($j < $max_j) {
  206. $diff2 = array();
  207. switch (substr($diff[$j], 0, 1)) {
  208. case '+':
  209. do {
  210. $diff2[] = substr($diff[$j++], 2);
  211. } while ($j < $max_j && substr($diff[$j], 0, 1) == '+');
  212. $edits[] = &new Text_Diff_Op_add($diff2);
  213. break;
  214. case '-':
  215. do {
  216. $diff2[] = substr($diff[$j++], 2);
  217. } while ($j < $max_j && substr($diff[$j], 0, 1) == '-');
  218. $edits[] = &new Text_Diff_Op_delete($diff2);
  219. break;
  220. }
  221. }
  222. }
  223. return $edits;
  224. }
  225. }