/mod/wiki/inline-diff/Text/Diff.php

https://github.com/jarednipper/HSU-common-code · PHP · 372 lines · 192 code · 45 blank · 135 comment · 21 complexity · 7fac8f6fb35e689833f954fbdb95fdd1 MD5 · raw file

  1. <?php
  2. /**
  3. * Text_Diff
  4. *
  5. * General API for generating and formatting diffs - the differences between
  6. * two sequences of strings.
  7. *
  8. * The PHP diff code used in this package was originally written by Geoffrey
  9. * T. Dairiki and is used with his permission.
  10. *
  11. * $Horde: framework/Text_Diff/Diff.php,v 1.17 2006/02/06 00:16:09 jan Exp $
  12. *
  13. * @package Text_Diff
  14. * @author Geoffrey T. Dairiki <dairiki@dairiki.org>
  15. */
  16. class Text_Diff {
  17. /**
  18. * Array of changes.
  19. *
  20. * @var array
  21. */
  22. var $_edits;
  23. /**
  24. * Computes diffs between sequences of strings.
  25. *
  26. * @param array $from_lines An array of strings. Typically these are
  27. * lines from a file.
  28. * @param array $to_lines An array of strings.
  29. */
  30. function Text_Diff($engine, $params)
  31. {
  32. // Backward compatibility workaround.
  33. if (!is_string($engine)) {
  34. $params = array($engine, $params);
  35. $engine = 'auto';
  36. }
  37. if ($engine == 'auto') {
  38. $engine = extension_loaded('xdiff') ? 'xdiff' : 'native';
  39. }
  40. $engine = basename($engine);
  41. // require_once 'Text/Diff/Engine/' . $engine . '.php';
  42. require_once 'Diff/Engine/' . $engine . '.php';
  43. $class = 'Text_Diff_Engine_' . $engine;
  44. $diff_engine = &new $class();
  45. $this->_edits = call_user_func_array(array($diff_engine, 'diff'), $params);
  46. }
  47. /**
  48. * Returns the array of differences.
  49. */
  50. function getDiff()
  51. {
  52. return $this->_edits;
  53. }
  54. /**
  55. * Computes a reversed diff.
  56. *
  57. * Example:
  58. * <code>
  59. * $diff = &new Text_Diff($lines1, $lines2);
  60. * $rev = $diff->reverse();
  61. * </code>
  62. *
  63. * @return Text_Diff A Diff object representing the inverse of the
  64. * original diff. Note that we purposely don't return a
  65. * reference here, since this essentially is a clone()
  66. * method.
  67. */
  68. function reverse()
  69. {
  70. if (version_compare(zend_version(), '2', '>')) {
  71. $rev = clone($this);
  72. } else {
  73. $rev = $this;
  74. }
  75. $rev->_edits = array();
  76. foreach ($this->_edits as $edit) {
  77. $rev->_edits[] = $edit->reverse();
  78. }
  79. return $rev;
  80. }
  81. /**
  82. * Checks for an empty diff.
  83. *
  84. * @return boolean True if two sequences were identical.
  85. */
  86. function isEmpty()
  87. {
  88. foreach ($this->_edits as $edit) {
  89. if (!is_a($edit, 'Text_Diff_Op_copy')) {
  90. return false;
  91. }
  92. }
  93. return true;
  94. }
  95. /**
  96. * Computes the length of the Longest Common Subsequence (LCS).
  97. *
  98. * This is mostly for diagnostic purposes.
  99. *
  100. * @return integer The length of the LCS.
  101. */
  102. function lcs()
  103. {
  104. $lcs = 0;
  105. foreach ($this->_edits as $edit) {
  106. if (is_a($edit, 'Text_Diff_Op_copy')) {
  107. $lcs += count($edit->orig);
  108. }
  109. }
  110. return $lcs;
  111. }
  112. /**
  113. * Gets the original set of lines.
  114. *
  115. * This reconstructs the $from_lines parameter passed to the constructor.
  116. *
  117. * @return array The original sequence of strings.
  118. */
  119. function getOriginal()
  120. {
  121. $lines = array();
  122. foreach ($this->_edits as $edit) {
  123. if ($edit->orig) {
  124. array_splice($lines, count($lines), 0, $edit->orig);
  125. }
  126. }
  127. return $lines;
  128. }
  129. /**
  130. * Gets the final set of lines.
  131. *
  132. * This reconstructs the $to_lines parameter passed to the constructor.
  133. *
  134. * @return array The sequence of strings.
  135. */
  136. function getFinal()
  137. {
  138. $lines = array();
  139. foreach ($this->_edits as $edit) {
  140. if ($edit->final) {
  141. array_splice($lines, count($lines), 0, $edit->final);
  142. }
  143. }
  144. return $lines;
  145. }
  146. /**
  147. * Removes trailing newlines from a line of text. This is meant to be used
  148. * with array_walk().
  149. *
  150. * @param string $line The line to trim.
  151. * @param integer $key The index of the line in the array. Not used.
  152. */
  153. function trimNewlines(&$line, $key)
  154. {
  155. $line = str_replace(array("\n", "\r"), '', $line);
  156. }
  157. /**
  158. * Checks a diff for validity.
  159. *
  160. * This is here only for debugging purposes.
  161. */
  162. function _check($from_lines, $to_lines)
  163. {
  164. if (serialize($from_lines) != serialize($this->getOriginal())) {
  165. trigger_error("Reconstructed original doesn't match", E_USER_ERROR);
  166. }
  167. if (serialize($to_lines) != serialize($this->getFinal())) {
  168. trigger_error("Reconstructed final doesn't match", E_USER_ERROR);
  169. }
  170. $rev = $this->reverse();
  171. if (serialize($to_lines) != serialize($rev->getOriginal())) {
  172. trigger_error("Reversed original doesn't match", E_USER_ERROR);
  173. }
  174. if (serialize($from_lines) != serialize($rev->getFinal())) {
  175. trigger_error("Reversed final doesn't match", E_USER_ERROR);
  176. }
  177. $prevtype = null;
  178. foreach ($this->_edits as $edit) {
  179. if ($prevtype == get_class($edit)) {
  180. trigger_error("Edit sequence is non-optimal", E_USER_ERROR);
  181. }
  182. $prevtype = get_class($edit);
  183. }
  184. return true;
  185. }
  186. }
  187. /**
  188. * $Horde: framework/Text_Diff/Diff.php,v 1.17 2006/02/06 00:16:09 jan Exp $
  189. *
  190. * @package Text_Diff
  191. * @author Geoffrey T. Dairiki <dairiki@dairiki.org>
  192. */
  193. class Text_MappedDiff extends Text_Diff {
  194. /**
  195. * Computes a diff between sequences of strings.
  196. *
  197. * This can be used to compute things like case-insensitve diffs, or diffs
  198. * which ignore changes in white-space.
  199. *
  200. * @param array $from_lines An array of strings.
  201. * @param array $to_lines An array of strings.
  202. * @param array $mapped_from_lines This array should have the same size
  203. * number of elements as $from_lines. The
  204. * elements in $mapped_from_lines and
  205. * $mapped_to_lines are what is actually
  206. * compared when computing the diff.
  207. * @param array $mapped_to_lines This array should have the same number
  208. * of elements as $to_lines.
  209. */
  210. function Text_MappedDiff($from_lines, $to_lines,
  211. $mapped_from_lines, $mapped_to_lines)
  212. {
  213. assert(count($from_lines) == count($mapped_from_lines));
  214. assert(count($to_lines) == count($mapped_to_lines));
  215. parent::Text_Diff($mapped_from_lines, $mapped_to_lines);
  216. $xi = $yi = 0;
  217. for ($i = 0; $i < count($this->_edits); $i++) {
  218. $orig = &$this->_edits[$i]->orig;
  219. if (is_array($orig)) {
  220. $orig = array_slice($from_lines, $xi, count($orig));
  221. $xi += count($orig);
  222. }
  223. $final = &$this->_edits[$i]->final;
  224. if (is_array($final)) {
  225. $final = array_slice($to_lines, $yi, count($final));
  226. $yi += count($final);
  227. }
  228. }
  229. }
  230. }
  231. /**
  232. * @package Text_Diff
  233. * @author Geoffrey T. Dairiki <dairiki@dairiki.org>
  234. *
  235. * @access private
  236. */
  237. class Text_Diff_Op {
  238. var $orig;
  239. var $final;
  240. function reverse()
  241. {
  242. trigger_error('Abstract method', E_USER_ERROR);
  243. }
  244. function norig()
  245. {
  246. return $this->orig ? count($this->orig) : 0;
  247. }
  248. function nfinal()
  249. {
  250. return $this->final ? count($this->final) : 0;
  251. }
  252. }
  253. /**
  254. * @package Text_Diff
  255. * @author Geoffrey T. Dairiki <dairiki@dairiki.org>
  256. *
  257. * @access private
  258. */
  259. class Text_Diff_Op_copy extends Text_Diff_Op {
  260. function Text_Diff_Op_copy($orig, $final = false)
  261. {
  262. if (!is_array($final)) {
  263. $final = $orig;
  264. }
  265. $this->orig = $orig;
  266. $this->final = $final;
  267. }
  268. function &reverse()
  269. {
  270. $reverse = &new Text_Diff_Op_copy($this->final, $this->orig);
  271. return $reverse;
  272. }
  273. }
  274. /**
  275. * @package Text_Diff
  276. * @author Geoffrey T. Dairiki <dairiki@dairiki.org>
  277. *
  278. * @access private
  279. */
  280. class Text_Diff_Op_delete extends Text_Diff_Op {
  281. function Text_Diff_Op_delete($lines)
  282. {
  283. $this->orig = $lines;
  284. $this->final = false;
  285. }
  286. function &reverse()
  287. {
  288. $reverse = &new Text_Diff_Op_add($this->orig);
  289. return $reverse;
  290. }
  291. }
  292. /**
  293. * @package Text_Diff
  294. * @author Geoffrey T. Dairiki <dairiki@dairiki.org>
  295. *
  296. * @access private
  297. */
  298. class Text_Diff_Op_add extends Text_Diff_Op {
  299. function Text_Diff_Op_add($lines)
  300. {
  301. $this->final = $lines;
  302. $this->orig = false;
  303. }
  304. function &reverse()
  305. {
  306. $reverse = &new Text_Diff_Op_delete($this->final);
  307. return $reverse;
  308. }
  309. }
  310. /**
  311. * @package Text_Diff
  312. * @author Geoffrey T. Dairiki <dairiki@dairiki.org>
  313. *
  314. * @access private
  315. */
  316. class Text_Diff_Op_change extends Text_Diff_Op {
  317. function Text_Diff_Op_change($orig, $final)
  318. {
  319. $this->orig = $orig;
  320. $this->final = $final;
  321. }
  322. function &reverse()
  323. {
  324. $reverse = &new Text_Diff_Op_change($this->final, $this->orig);
  325. return $reverse;
  326. }
  327. }