angelovcom.net

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

string.php (8354B)


      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     /**
     26      * Parses a unified or context diff.
     27      *
     28      * First param contains the whole diff and the second can be used to force
     29      * a specific diff type. If the second parameter is 'autodetect', the
     30      * diff will be examined to find out which type of diff this is.
     31      *
     32      * @param string $diff  The diff content.
     33      * @param string $mode  The diff mode of the content in $diff. One of
     34      *                      'context', 'unified', or 'autodetect'.
     35      *
     36      * @return array  List of all diff operations.
     37      */
     38     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             return PEAR::raiseError('Type of diff is unsupported');
     55         }
     56 
     57         if ($mode == 'autodetect') {
     58             $context = strpos($diff, '***');
     59             $unified = strpos($diff, '---');
     60             if ($context === $unified) {
     61                 return PEAR::raiseError('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     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 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 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 Text_Diff_Op_delete($diff1);
    125                 } else {
    126                     $edits[] = new 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     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 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 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 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 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 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 Text_Diff_Op_delete($diff2);
    240                     break;
    241                 }
    242             }
    243         }
    244 
    245         return $edits;
    246     }
    247 
    248 }