Overview

Packages

  • Text
    • Diff

Classes

  • Horde_Text_Diff
  • Horde_Text_Diff_Engine_Native
  • Horde_Text_Diff_Engine_Shell
  • Horde_Text_Diff_Engine_String
  • Horde_Text_Diff_Engine_Xdiff
  • Horde_Text_Diff_Exception
  • Horde_Text_Diff_Mapped
  • Horde_Text_Diff_Op_Add
  • Horde_Text_Diff_Op_Base
  • Horde_Text_Diff_Op_Change
  • Horde_Text_Diff_Op_Copy
  • Horde_Text_Diff_Op_Delete
  • Horde_Text_Diff_Renderer
  • Horde_Text_Diff_Renderer_Context
  • Horde_Text_Diff_Renderer_Inline
  • Horde_Text_Diff_Renderer_Unified
  • Horde_Text_Diff_ThreeWay
  • Horde_Text_Diff_ThreeWay_BlockBuilder
  • Horde_Text_Diff_ThreeWay_Op_Base
  • Horde_Text_Diff_ThreeWay_Op_Copy
  • Overview
  • Package
  • Class
  • Tree
  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-2012 Horde LLC (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://www.horde.org/licenses/lgpl21.
 12:  *
 13:  * @author  Milian Wolff <mail@milianw.de>
 14:  * @package Text_Diff
 15:  */
 16: class Horde_Text_Diff_Engine_Shell
 17: {
 18:     /**
 19:      * Path to the diff executable
 20:      *
 21:      * @var string
 22:      */
 23:     protected $_diffCommand = 'diff';
 24: 
 25:     /**
 26:      * Returns the array of differences.
 27:      *
 28:      * @param array $from_lines lines of text from old file
 29:      * @param array $to_lines   lines of text from new file
 30:      *
 31:      * @return array all changes made (array with Horde_Text_Diff_Op_* objects)
 32:      */
 33:     public function diff($from_lines, $to_lines)
 34:     {
 35:         array_walk($from_lines, array('Horde_Text_Diff', 'trimNewlines'));
 36:         array_walk($to_lines, array('Horde_Text_Diff', 'trimNewlines'));
 37: 
 38:         // Execute gnu diff or similar to get a standard diff file.
 39:         $from_file = Horde_Util::getTempFile('Horde_Text_Diff');
 40:         $to_file = Horde_Util::getTempFile('Horde_Text_Diff');
 41:         $fp = fopen($from_file, 'w');
 42:         fwrite($fp, implode("\n", $from_lines));
 43:         fclose($fp);
 44:         $fp = fopen($to_file, 'w');
 45:         fwrite($fp, implode("\n", $to_lines));
 46:         fclose($fp);
 47:         $diff = shell_exec($this->_diffCommand . ' ' . $from_file . ' ' . $to_file);
 48:         unlink($from_file);
 49:         unlink($to_file);
 50: 
 51:         if (is_null($diff)) {
 52:             // No changes were made
 53:             return array(new Horde_Text_Diff_Op_Copy($from_lines));
 54:         }
 55: 
 56:         $from_line_no = 1;
 57:         $to_line_no = 1;
 58:         $edits = array();
 59: 
 60:         // Get changed lines by parsing something like:
 61:         // 0a1,2
 62:         // 1,2c4,6
 63:         // 1,5d6
 64:         preg_match_all('#^(\d+)(?:,(\d+))?([adc])(\d+)(?:,(\d+))?$#m', $diff,
 65:             $matches, PREG_SET_ORDER);
 66: 
 67:         foreach ($matches as $match) {
 68:             if (!isset($match[5])) {
 69:                 // This paren is not set every time (see regex).
 70:                 $match[5] = false;
 71:             }
 72: 
 73:             if ($match[3] == 'a') {
 74:                 $from_line_no--;
 75:             }
 76: 
 77:             if ($match[3] == 'd') {
 78:                 $to_line_no--;
 79:             }
 80: 
 81:             if ($from_line_no < $match[1] || $to_line_no < $match[4]) {
 82:                 // copied lines
 83:                 assert('$match[1] - $from_line_no == $match[4] - $to_line_no');
 84:                 array_push($edits,
 85:                     new Horde_Text_Diff_Op_Copy(
 86:                         $this->_getLines($from_lines, $from_line_no, $match[1] - 1),
 87:                         $this->_getLines($to_lines, $to_line_no, $match[4] - 1)));
 88:             }
 89: 
 90:             switch ($match[3]) {
 91:             case 'd':
 92:                 // deleted lines
 93:                 array_push($edits,
 94:                     new Horde_Text_Diff_Op_Delete(
 95:                         $this->_getLines($from_lines, $from_line_no, $match[2])));
 96:                 $to_line_no++;
 97:                 break;
 98: 
 99:             case 'c':
100:                 // changed lines
101:                 array_push($edits,
102:                     new Horde_Text_Diff_Op_Change(
103:                         $this->_getLines($from_lines, $from_line_no, $match[2]),
104:                         $this->_getLines($to_lines, $to_line_no, $match[5])));
105:                 break;
106: 
107:             case 'a':
108:                 // added lines
109:                 array_push($edits,
110:                     new Horde_Text_Diff_Op_Add(
111:                         $this->_getLines($to_lines, $to_line_no, $match[5])));
112:                 $from_line_no++;
113:                 break;
114:             }
115:         }
116: 
117:         if (!empty($from_lines)) {
118:             // Some lines might still be pending. Add them as copied
119:             array_push($edits,
120:                 new Horde_Text_Diff_Op_Copy(
121:                     $this->_getLines($from_lines, $from_line_no,
122:                                      $from_line_no + count($from_lines) - 1),
123:                     $this->_getLines($to_lines, $to_line_no,
124:                                      $to_line_no + count($to_lines) - 1)));
125:         }
126: 
127:         return $edits;
128:     }
129: 
130:     /**
131:      * Get lines from either the old or new text
132:      *
133:      * @access private
134:      *
135:      * @param array &$text_lines Either $from_lines or $to_lines
136:      * @param int   &$line_no    Current line number
137:      * @param int   $end         Optional end line, when we want to chop more
138:      *                           than one line.
139:      *
140:      * @return array The chopped lines
141:      */
142:     protected function _getLines(&$text_lines, &$line_no, $end = false)
143:     {
144:         if (!empty($end)) {
145:             $lines = array();
146:             // We can shift even more
147:             while ($line_no <= $end) {
148:                 array_push($lines, array_shift($text_lines));
149:                 $line_no++;
150:             }
151:         } else {
152:             $lines = array(array_shift($text_lines));
153:             $line_no++;
154:         }
155: 
156:         return $lines;
157:     }
158: }
159: 
API documentation generated by ApiGen