balmet.com

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

shell.php (5210B)


      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 (passed by reference).
    139      * @param int   $line_no    Current line number (passed by reference).
    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 }