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