wp/wp-includes/Text/Diff/Engine/shell.php
changeset 0 d970ebf37754
child 7 cf61fcea0001
equal deleted inserted replaced
-1:000000000000 0:d970ebf37754
       
     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     {
       
    36         array_walk($from_lines, array('Text_Diff', 'trimNewlines'));
       
    37         array_walk($to_lines, array('Text_Diff', 'trimNewlines'));
       
    38 
       
    39         $temp_dir = Text_Diff::_getTempDir();
       
    40 
       
    41         // Execute gnu diff or similar to get a standard diff file.
       
    42         $from_file = tempnam($temp_dir, 'Text_Diff');
       
    43         $to_file = tempnam($temp_dir, 'Text_Diff');
       
    44         $fp = fopen($from_file, 'w');
       
    45         fwrite($fp, implode("\n", $from_lines));
       
    46         fclose($fp);
       
    47         $fp = fopen($to_file, 'w');
       
    48         fwrite($fp, implode("\n", $to_lines));
       
    49         fclose($fp);
       
    50         $diff = shell_exec($this->_diffCommand . ' ' . $from_file . ' ' . $to_file);
       
    51         unlink($from_file);
       
    52         unlink($to_file);
       
    53 
       
    54         if (is_null($diff)) {
       
    55             // No changes were made
       
    56             return array(new Text_Diff_Op_copy($from_lines));
       
    57         }
       
    58 
       
    59         $from_line_no = 1;
       
    60         $to_line_no = 1;
       
    61         $edits = array();
       
    62 
       
    63         // Get changed lines by parsing something like:
       
    64         // 0a1,2
       
    65         // 1,2c4,6
       
    66         // 1,5d6
       
    67         preg_match_all('#^(\d+)(?:,(\d+))?([adc])(\d+)(?:,(\d+))?$#m', $diff,
       
    68             $matches, PREG_SET_ORDER);
       
    69 
       
    70         foreach ($matches as $match) {
       
    71             if (!isset($match[5])) {
       
    72                 // This paren is not set every time (see regex).
       
    73                 $match[5] = false;
       
    74             }
       
    75 
       
    76             if ($match[3] == 'a') {
       
    77                 $from_line_no--;
       
    78             }
       
    79 
       
    80             if ($match[3] == 'd') {
       
    81                 $to_line_no--;
       
    82             }
       
    83 
       
    84             if ($from_line_no < $match[1] || $to_line_no < $match[4]) {
       
    85                 // copied lines
       
    86                 assert('$match[1] - $from_line_no == $match[4] - $to_line_no');
       
    87                 array_push($edits,
       
    88                     new Text_Diff_Op_copy(
       
    89                         $this->_getLines($from_lines, $from_line_no, $match[1] - 1),
       
    90                         $this->_getLines($to_lines, $to_line_no, $match[4] - 1)));
       
    91             }
       
    92 
       
    93             switch ($match[3]) {
       
    94             case 'd':
       
    95                 // deleted lines
       
    96                 array_push($edits,
       
    97                     new Text_Diff_Op_delete(
       
    98                         $this->_getLines($from_lines, $from_line_no, $match[2])));
       
    99                 $to_line_no++;
       
   100                 break;
       
   101 
       
   102             case 'c':
       
   103                 // changed lines
       
   104                 array_push($edits,
       
   105                     new Text_Diff_Op_change(
       
   106                         $this->_getLines($from_lines, $from_line_no, $match[2]),
       
   107                         $this->_getLines($to_lines, $to_line_no, $match[5])));
       
   108                 break;
       
   109 
       
   110             case 'a':
       
   111                 // added lines
       
   112                 array_push($edits,
       
   113                     new Text_Diff_Op_add(
       
   114                         $this->_getLines($to_lines, $to_line_no, $match[5])));
       
   115                 $from_line_no++;
       
   116                 break;
       
   117             }
       
   118         }
       
   119 
       
   120         if (!empty($from_lines)) {
       
   121             // Some lines might still be pending. Add them as copied
       
   122             array_push($edits,
       
   123                 new Text_Diff_Op_copy(
       
   124                     $this->_getLines($from_lines, $from_line_no,
       
   125                                      $from_line_no + count($from_lines) - 1),
       
   126                     $this->_getLines($to_lines, $to_line_no,
       
   127                                      $to_line_no + count($to_lines) - 1)));
       
   128         }
       
   129 
       
   130         return $edits;
       
   131     }
       
   132 
       
   133     /**
       
   134      * Get lines from either the old or new text
       
   135      *
       
   136      * @access private
       
   137      *
       
   138      * @param array &$text_lines Either $from_lines or $to_lines
       
   139      * @param int   &$line_no    Current line number
       
   140      * @param int   $end         Optional end line, when we want to chop more
       
   141      *                           than one line.
       
   142      *
       
   143      * @return array The chopped lines
       
   144      */
       
   145     function _getLines(&$text_lines, &$line_no, $end = false)
       
   146     {
       
   147         if (!empty($end)) {
       
   148             $lines = array();
       
   149             // We can shift even more
       
   150             while ($line_no <= $end) {
       
   151                 array_push($lines, array_shift($text_lines));
       
   152                 $line_no++;
       
   153             }
       
   154         } else {
       
   155             $lines = array(array_shift($text_lines));
       
   156             $line_no++;
       
   157         }
       
   158 
       
   159         return $lines;
       
   160     }
       
   161 
       
   162 }