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:  * 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 Horde_Text_Diff('string', array($patch));
  9:  * $renderer = new Horde_Text_Diff_Renderer_inline();
 10:  * echo $renderer->render($diff);
 11:  * </code>
 12:  *
 13:  * Copyright 2005 Örjan Persson <o@42mm.org>
 14:  * Copyright 2005-2012 Horde LLC (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://www.horde.org/licenses/lgpl21.
 18:  *
 19:  * @author  Örjan Persson <o@42mm.org>
 20:  * @package Text_Diff
 21:  */
 22: class Horde_Text_Diff_Engine_String
 23: {
 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:      * @throws Horde_Text_Diff_Exception
 37:      */
 38:     public function diff($diff, $mode = 'autodetect')
 39:     {
 40:         // Detect line breaks.
 41:         $lnbr = "\n";
 42:         if (strpos($diff, "\r\n") !== false) {
 43:             $lnbr = "\r\n";
 44:         } elseif (strpos($diff, "\r") !== false) {
 45:             $lnbr = "\r";
 46:         }
 47: 
 48:         // Make sure we have a line break at the EOF.
 49:         if (substr($diff, -strlen($lnbr)) != $lnbr) {
 50:             $diff .= $lnbr;
 51:         }
 52: 
 53:         if ($mode != 'autodetect' && $mode != 'context' && $mode != 'unified') {
 54:             throw new Horde_Text_Diff_Exception('Type of diff is unsupported');
 55:         }
 56: 
 57:         if ($mode == 'autodetect') {
 58:             $context = strpos($diff, '***');
 59:             $unified = strpos($diff, '---');
 60:             if ($context === $unified) {
 61:                 throw new Horde_Text_Diff_Exception('Type of diff could not be detected');
 62:             } elseif ($context === false || $unified === false) {
 63:                 $mode = $context !== false ? 'context' : 'unified';
 64:             } else {
 65:                 $mode = $context < $unified ? 'context' : 'unified';
 66:             }
 67:         }
 68: 
 69:         // Split by new line and remove the diff header, if there is one.
 70:         $diff = explode($lnbr, $diff);
 71:         if (($mode == 'context' && strpos($diff[0], '***') === 0) ||
 72:             ($mode == 'unified' && strpos($diff[0], '---') === 0)) {
 73:             array_shift($diff);
 74:             array_shift($diff);
 75:         }
 76: 
 77:         if ($mode == 'context') {
 78:             return $this->parseContextDiff($diff);
 79:         } else {
 80:             return $this->parseUnifiedDiff($diff);
 81:         }
 82:     }
 83: 
 84:     /**
 85:      * Parses an array containing the unified diff.
 86:      *
 87:      * @param array $diff  Array of lines.
 88:      *
 89:      * @return array  List of all diff operations.
 90:      */
 91:     public function parseUnifiedDiff($diff)
 92:     {
 93:         $edits = array();
 94:         $end = count($diff) - 1;
 95:         for ($i = 0; $i < $end;) {
 96:             $diff1 = array();
 97:             switch (substr($diff[$i], 0, 1)) {
 98:             case ' ':
 99:                 do {
100:                     $diff1[] = substr($diff[$i], 1);
101:                 } while (++$i < $end && substr($diff[$i], 0, 1) == ' ');
102:                 $edits[] = new Horde_Text_Diff_Op_Copy($diff1);
103:                 break;
104: 
105:             case '+':
106:                 // get all new lines
107:                 do {
108:                     $diff1[] = substr($diff[$i], 1);
109:                 } while (++$i < $end && substr($diff[$i], 0, 1) == '+');
110:                 $edits[] = new Horde_Text_Diff_Op_Add($diff1);
111:                 break;
112: 
113:             case '-':
114:                 // get changed or removed lines
115:                 $diff2 = array();
116:                 do {
117:                     $diff1[] = substr($diff[$i], 1);
118:                 } while (++$i < $end && substr($diff[$i], 0, 1) == '-');
119: 
120:                 while ($i < $end && substr($diff[$i], 0, 1) == '+') {
121:                     $diff2[] = substr($diff[$i++], 1);
122:                 }
123:                 if (count($diff2) == 0) {
124:                     $edits[] = new Horde_Text_Diff_Op_Delete($diff1);
125:                 } else {
126:                     $edits[] = new Horde_Text_Diff_Op_Change($diff1, $diff2);
127:                 }
128:                 break;
129: 
130:             default:
131:                 $i++;
132:                 break;
133:             }
134:         }
135: 
136:         return $edits;
137:     }
138: 
139:     /**
140:      * Parses an array containing the context diff.
141:      *
142:      * @param array $diff  Array of lines.
143:      *
144:      * @return array  List of all diff operations.
145:      */
146:     public function parseContextDiff(&$diff)
147:     {
148:         $edits = array();
149:         $i = $max_i = $j = $max_j = 0;
150:         $end = count($diff) - 1;
151:         while ($i < $end && $j < $end) {
152:             while ($i >= $max_i && $j >= $max_j) {
153:                 // Find the boundaries of the diff output of the two files
154:                 for ($i = $j;
155:                      $i < $end && substr($diff[$i], 0, 3) == '***';
156:                      $i++);
157:                 for ($max_i = $i;
158:                      $max_i < $end && substr($diff[$max_i], 0, 3) != '---';
159:                      $max_i++);
160:                 for ($j = $max_i;
161:                      $j < $end && substr($diff[$j], 0, 3) == '---';
162:                      $j++);
163:                 for ($max_j = $j;
164:                      $max_j < $end && substr($diff[$max_j], 0, 3) != '***';
165:                      $max_j++);
166:             }
167: 
168:             // find what hasn't been changed
169:             $array = array();
170:             while ($i < $max_i &&
171:                    $j < $max_j &&
172:                    strcmp($diff[$i], $diff[$j]) == 0) {
173:                 $array[] = substr($diff[$i], 2);
174:                 $i++;
175:                 $j++;
176:             }
177: 
178:             while ($i < $max_i && ($max_j-$j) <= 1) {
179:                 if ($diff[$i] != '' && substr($diff[$i], 0, 1) != ' ') {
180:                     break;
181:                 }
182:                 $array[] = substr($diff[$i++], 2);
183:             }
184: 
185:             while ($j < $max_j && ($max_i-$i) <= 1) {
186:                 if ($diff[$j] != '' && substr($diff[$j], 0, 1) != ' ') {
187:                     break;
188:                 }
189:                 $array[] = substr($diff[$j++], 2);
190:             }
191:             if (count($array) > 0) {
192:                 $edits[] = new Horde_Text_Diff_Op_Copy($array);
193:             }
194: 
195:             if ($i < $max_i) {
196:                 $diff1 = array();
197:                 switch (substr($diff[$i], 0, 1)) {
198:                 case '!':
199:                     $diff2 = array();
200:                     do {
201:                         $diff1[] = substr($diff[$i], 2);
202:                         if ($j < $max_j && substr($diff[$j], 0, 1) == '!') {
203:                             $diff2[] = substr($diff[$j++], 2);
204:                         }
205:                     } while (++$i < $max_i && substr($diff[$i], 0, 1) == '!');
206:                     $edits[] = new Horde_Text_Diff_Op_Change($diff1, $diff2);
207:                     break;
208: 
209:                 case '+':
210:                     do {
211:                         $diff1[] = substr($diff[$i], 2);
212:                     } while (++$i < $max_i && substr($diff[$i], 0, 1) == '+');
213:                     $edits[] = new Horde_Text_Diff_Op_Add($diff1);
214:                     break;
215: 
216:                 case '-':
217:                     do {
218:                         $diff1[] = substr($diff[$i], 2);
219:                     } while (++$i < $max_i && substr($diff[$i], 0, 1) == '-');
220:                     $edits[] = new Horde_Text_Diff_Op_Delete($diff1);
221:                     break;
222:                 }
223:             }
224: 
225:             if ($j < $max_j) {
226:                 $diff2 = array();
227:                 switch (substr($diff[$j], 0, 1)) {
228:                 case '+':
229:                     do {
230:                         $diff2[] = substr($diff[$j++], 2);
231:                     } while ($j < $max_j && substr($diff[$j], 0, 1) == '+');
232:                     $edits[] = new Horde_Text_Diff_Op_Add($diff2);
233:                     break;
234: 
235:                 case '-':
236:                     do {
237:                         $diff2[] = substr($diff[$j++], 2);
238:                     } while ($j < $max_j && substr($diff[$j], 0, 1) == '-');
239:                     $edits[] = new Horde_Text_Diff_Op_Delete($diff2);
240:                     break;
241:                 }
242:             }
243:         }
244: 
245:         return $edits;
246:     }
247: }
248: 
API documentation generated by ApiGen