b2evolution PHP Cross Reference Blogging Systems

Source: /inc/_ext/phpsvnclient/ext/Diff/Diff.php - 456 lines - 12139 bytes - Summary - Text - Print

   1  <?php
   2  /**
   3   * General API for generating and formatting diffs - the differences between
   4   * two sequences of strings.
   5   *
   6   * The original PHP version of this code was written by Geoffrey T. Dairiki
   7   * <dairiki@dairiki.org>, and is used/adapted with his permission.
   8   *
   9   * $Horde: framework/Text_Diff/Diff.php,v 1.11.2.12 2009/01/06 15:23:41 jan Exp $
  10   *
  11   * Copyright 2004 Geoffrey T. Dairiki <dairiki@dairiki.org>
  12   * Copyright 2004-2009 The Horde Project (http://www.horde.org/)
  13   *
  14   * See the enclosed file COPYING for license information (LGPL). If you did
  15   * not receive this file, see http://opensource.org/licenses/lgpl-license.php.
  16   *
  17   * @package Text_Diff
  18   * @author  Geoffrey T. Dairiki <dairiki@dairiki.org>
  19   */
  20  if( ! defined( 'EVO_MAIN_INIT' ) ) die( 'Please, do not access this page directly.' );
  21  
  22  class Text_Diff {
  23  
  24      /**
  25       * Array of changes.
  26       *
  27       * @var array
  28       */
  29      var $_edits;
  30  
  31      /**
  32       * Computes diffs between sequences of strings.
  33       *
  34       * @param string $engine     Name of the diffing engine to use.  'auto'
  35       *                           will automatically select the best.
  36       * @param array $params      Parameters to pass to the diffing engine.
  37       *                           Normally an array of two arrays, each
  38       *                           containing the lines from a file.
  39       */
  40      function Text_Diff($engine, $params)
  41      {
  42          // Backward compatibility workaround.
  43          if (!is_string($engine)) {
  44              $params = array($engine, $params);
  45              $engine = 'auto';
  46          }
  47  
  48          if ($engine == 'auto') {
  49              $engine = extension_loaded('xdiff') ? 'xdiff' : 'native';
  50          } else {
  51              $engine = basename($engine);
  52          }
  53  
  54          require_once 'Text/Diff/Engine/' . $engine . '.php';
  55          $class = 'Text_Diff_Engine_' . $engine;
  56          $diff_engine = new $class();
  57  
  58          $this->_edits = call_user_func_array(array($diff_engine, 'diff'), $params);
  59      }
  60  
  61      /**
  62       * Returns the array of differences.
  63       */
  64      function getDiff()
  65      {
  66          return $this->_edits;
  67      }
  68      
  69      /**
  70       * returns the number of new (added) lines in a given diff.
  71       *
  72       * @since Text_Diff 1.1.0
  73       * @since Horde 3.2
  74       *
  75       * @return integer The number of new lines
  76       */
  77      function countAddedLines()
  78      {
  79          $count = 0;
  80          foreach ($this->_edits as $edit) {
  81              if (is_a($edit, 'Text_Diff_Op_add') ||
  82                  is_a($edit, 'Text_Diff_Op_change')) {
  83                  $count += $edit->nfinal();
  84              }
  85          }
  86          return $count;
  87      }
  88      
  89      /**
  90       * Returns the number of deleted (removed) lines in a given diff.
  91       *
  92       * @since Text_Diff 1.1.0
  93       * @since Horde 3.2
  94       *
  95       * @return integer The number of deleted lines
  96       */
  97      function countDeletedLines()
  98      {
  99          $count = 0;
 100          foreach ($this->_edits as $edit) {
 101              if (is_a($edit, 'Text_Diff_Op_delete') ||
 102                  is_a($edit, 'Text_Diff_Op_change')) {
 103                  $count += $edit->norig();
 104              }
 105          }
 106          return $count;
 107      }
 108  
 109      /**
 110       * Computes a reversed diff.
 111       *
 112       * Example:
 113       * <code>
 114       * $diff = new Text_Diff($lines1, $lines2);
 115       * $rev = $diff->reverse();
 116       * </code>
 117       *
 118       * @return Text_Diff  A Diff object representing the inverse of the
 119       *                    original diff.  Note that we purposely don't return a
 120       *                    reference here, since this essentially is a clone()
 121       *                    method.
 122       */
 123      function reverse()
 124      {
 125          if (version_compare(zend_version(), '2', '>')) {
 126              $rev = clone($this);
 127          } else {
 128              $rev = $this;
 129          }
 130          $rev->_edits = array();
 131          foreach ($this->_edits as $edit) {
 132              $rev->_edits[] = $edit->reverse();
 133          }
 134          return $rev;
 135      }
 136  
 137      /**
 138       * Checks for an empty diff.
 139       *
 140       * @return boolean  True if two sequences were identical.
 141       */
 142      function isEmpty()
 143      {
 144          foreach ($this->_edits as $edit) {
 145              if (!is_a($edit, 'Text_Diff_Op_copy')) {
 146                  return false;
 147              }
 148          }
 149          return true;
 150      }
 151  
 152      /**
 153       * Computes the length of the Longest Common Subsequence (LCS).
 154       *
 155       * This is mostly for diagnostic purposes.
 156       *
 157       * @return integer  The length of the LCS.
 158       */
 159      function lcs()
 160      {
 161          $lcs = 0;
 162          foreach ($this->_edits as $edit) {
 163              if (is_a($edit, 'Text_Diff_Op_copy')) {
 164                  $lcs += count($edit->orig);
 165              }
 166          }
 167          return $lcs;
 168      }
 169  
 170      /**
 171       * Gets the original set of lines.
 172       *
 173       * This reconstructs the $from_lines parameter passed to the constructor.
 174       *
 175       * @return array  The original sequence of strings.
 176       */
 177      function getOriginal()
 178      {
 179          $lines = array();
 180          foreach ($this->_edits as $edit) {
 181              if ($edit->orig) {
 182                  array_splice($lines, count($lines), 0, $edit->orig);
 183              }
 184          }
 185          return $lines;
 186      }
 187  
 188      /**
 189       * Gets the final set of lines.
 190       *
 191       * This reconstructs the $to_lines parameter passed to the constructor.
 192       *
 193       * @return array  The sequence of strings.
 194       */
 195      function getFinal()
 196      {
 197          $lines = array();
 198          foreach ($this->_edits as $edit) {
 199              if ($edit->final) {
 200                  array_splice($lines, count($lines), 0, $edit->final);
 201              }
 202          }
 203          return $lines;
 204      }
 205  
 206      /**
 207       * Removes trailing newlines from a line of text. This is meant to be used
 208       * with array_walk().
 209       *
 210       * @param string $line  The line to trim.
 211       * @param integer $key  The index of the line in the array. Not used.
 212       */
 213      function trimNewlines(&$line, $key)
 214      {
 215          $line = str_replace(array("\n", "\r"), '', $line);
 216      }
 217  
 218      /**
 219       * Determines the location of the system temporary directory.
 220       *
 221       * @static
 222       *
 223       * @access protected
 224       *
 225       * @return string  A directory name which can be used for temp files.
 226       *                 Returns false if one could not be found.
 227       */
 228      function _getTempDir()
 229      {
 230          $tmp_locations = array('/tmp', '/var/tmp', 'c:\WUTemp', 'c:\temp',
 231                                 'c:\windows\temp', 'c:\winnt\temp');
 232  
 233          /* Try PHP's upload_tmp_dir directive. */
 234          $tmp = ini_get('upload_tmp_dir');
 235  
 236          /* Otherwise, try to determine the TMPDIR environment variable. */
 237          if (!strlen($tmp)) {
 238              $tmp = getenv('TMPDIR');
 239          }
 240  
 241          /* If we still cannot determine a value, then cycle through a list of
 242           * preset possibilities. */
 243          while (!strlen($tmp) && count($tmp_locations)) {
 244              $tmp_check = array_shift($tmp_locations);
 245              if (@is_dir($tmp_check)) {
 246                  $tmp = $tmp_check;
 247              }
 248          }
 249  
 250          /* If it is still empty, we have failed, so return false; otherwise
 251           * return the directory determined. */
 252          return strlen($tmp) ? $tmp : false;
 253      }
 254  
 255      /**
 256       * Checks a diff for validity.
 257       *
 258       * This is here only for debugging purposes.
 259       */
 260      function _check($from_lines, $to_lines)
 261      {
 262          if (serialize($from_lines) != serialize($this->getOriginal())) {
 263              trigger_error("Reconstructed original doesn't match", E_USER_ERROR);
 264          }
 265          if (serialize($to_lines) != serialize($this->getFinal())) {
 266              trigger_error("Reconstructed final doesn't match", E_USER_ERROR);
 267          }
 268  
 269          $rev = $this->reverse();
 270          if (serialize($to_lines) != serialize($rev->getOriginal())) {
 271              trigger_error("Reversed original doesn't match", E_USER_ERROR);
 272          }
 273          if (serialize($from_lines) != serialize($rev->getFinal())) {
 274              trigger_error("Reversed final doesn't match", E_USER_ERROR);
 275          }
 276  
 277          $prevtype = null;
 278          foreach ($this->_edits as $edit) {
 279              if ($prevtype == get_class($edit)) {
 280                  trigger_error("Edit sequence is non-optimal", E_USER_ERROR);
 281              }
 282              $prevtype = get_class($edit);
 283          }
 284  
 285          return true;
 286      }
 287  
 288  }
 289  
 290  /**
 291   * @package Text_Diff
 292   * @author  Geoffrey T. Dairiki <dairiki@dairiki.org>
 293   */
 294  class Text_MappedDiff extends Text_Diff {
 295  
 296      /**
 297       * Computes a diff between sequences of strings.
 298       *
 299       * This can be used to compute things like case-insensitve diffs, or diffs
 300       * which ignore changes in white-space.
 301       *
 302       * @param array $from_lines         An array of strings.
 303       * @param array $to_lines           An array of strings.
 304       * @param array $mapped_from_lines  This array should have the same size
 305       *                                  number of elements as $from_lines.  The
 306       *                                  elements in $mapped_from_lines and
 307       *                                  $mapped_to_lines are what is actually
 308       *                                  compared when computing the diff.
 309       * @param array $mapped_to_lines    This array should have the same number
 310       *                                  of elements as $to_lines.
 311       */
 312      function Text_MappedDiff($from_lines, $to_lines,
 313                               $mapped_from_lines, $mapped_to_lines)
 314      {
 315          assert(count($from_lines) == count($mapped_from_lines));
 316          assert(count($to_lines) == count($mapped_to_lines));
 317  
 318          parent::Text_Diff($mapped_from_lines, $mapped_to_lines);
 319  
 320          $xi = $yi = 0;
 321          for ($i = 0; $i < count($this->_edits); $i++) {
 322              $orig = &$this->_edits[$i]->orig;
 323              if (is_array($orig)) {
 324                  $orig = array_slice($from_lines, $xi, count($orig));
 325                  $xi += count($orig);
 326              }
 327  
 328              $final = &$this->_edits[$i]->final;
 329              if (is_array($final)) {
 330                  $final = array_slice($to_lines, $yi, count($final));
 331                  $yi += count($final);
 332              }
 333          }
 334      }
 335  
 336  }
 337  
 338  /**
 339   * @package Text_Diff
 340   * @author  Geoffrey T. Dairiki <dairiki@dairiki.org>
 341   *
 342   * @access private
 343   */
 344  class Text_Diff_Op {
 345  
 346      var $orig;
 347      var $final;
 348  
 349      function &reverse()
 350      {
 351          trigger_error('Abstract method', E_USER_ERROR);
 352      }
 353  
 354      function norig()
 355      {
 356          return $this->orig ? count($this->orig) : 0;
 357      }
 358  
 359      function nfinal()
 360      {
 361          return $this->final ? count($this->final) : 0;
 362      }
 363  
 364  }
 365  
 366  /**
 367   * @package Text_Diff
 368   * @author  Geoffrey T. Dairiki <dairiki@dairiki.org>
 369   *
 370   * @access private
 371   */
 372  class Text_Diff_Op_copy extends Text_Diff_Op {
 373  
 374      function Text_Diff_Op_copy($orig, $final = false)
 375      {
 376          if (!is_array($final)) {
 377              $final = $orig;
 378          }
 379          $this->orig = $orig;
 380          $this->final = $final;
 381      }
 382  
 383      function &reverse()
 384      {
 385          $reverse = &new Text_Diff_Op_copy($this->final, $this->orig);
 386          return $reverse;
 387      }
 388  
 389  }
 390  
 391  /**
 392   * @package Text_Diff
 393   * @author  Geoffrey T. Dairiki <dairiki@dairiki.org>
 394   *
 395   * @access private
 396   */
 397  class Text_Diff_Op_delete extends Text_Diff_Op {
 398  
 399      function Text_Diff_Op_delete($lines)
 400      {
 401          $this->orig = $lines;
 402          $this->final = false;
 403      }
 404  
 405      function &reverse()
 406      {
 407          $reverse = &new Text_Diff_Op_add($this->orig);
 408          return $reverse;
 409      }
 410  
 411  }
 412  
 413  /**
 414   * @package Text_Diff
 415   * @author  Geoffrey T. Dairiki <dairiki@dairiki.org>
 416   *
 417   * @access private
 418   */
 419  class Text_Diff_Op_add extends Text_Diff_Op {
 420  
 421      function Text_Diff_Op_add($lines)
 422      {
 423          $this->final = $lines;
 424          $this->orig = false;
 425      }
 426  
 427      function &reverse()
 428      {
 429          $reverse = &new Text_Diff_Op_delete($this->final);
 430          return $reverse;
 431      }
 432  
 433  }
 434  
 435  /**
 436   * @package Text_Diff
 437   * @author  Geoffrey T. Dairiki <dairiki@dairiki.org>
 438   *
 439   * @access private
 440   */
 441  class Text_Diff_Op_change extends Text_Diff_Op {
 442  
 443      function Text_Diff_Op_change($orig, $final)
 444      {
 445          $this->orig = $orig;
 446          $this->final = $final;
 447      }
 448  
 449      function &reverse()
 450      {
 451          $reverse = &new Text_Diff_Op_change($this->final, $this->orig);
 452          return $reverse;
 453      }
 454  
 455  }
 456  ?>

title

Description

title

Description

title

Description

title

title

Body