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

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