[ Index ]

PHP Cross Reference of WordPress

title

Body

[close]

/wp-includes/Text/ -> Diff.php (source)

   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   * Copyright 2004 Geoffrey T. Dairiki <dairiki@dairiki.org>
  10   * Copyright 2004-2010 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   * @package Text_Diff
  16   * @author  Geoffrey T. Dairiki <dairiki@dairiki.org>
  17   */
  18  class Text_Diff {
  19  
  20      /**
  21       * Array of changes.
  22       *
  23       * @var array
  24       */
  25      var $_edits;
  26  
  27      /**
  28       * Computes diffs between sequences of strings.
  29       *
  30       * @param string $engine     Name of the diffing engine to use.  'auto'
  31       *                           will automatically select the best.
  32       * @param array $params      Parameters to pass to the diffing engine.
  33       *                           Normally an array of two arrays, each
  34       *                           containing the lines from a file.
  35       */
  36      function __construct( $engine, $params )
  37      {
  38          // Backward compatibility workaround.
  39          if (!is_string($engine)) {
  40              $params = array($engine, $params);
  41              $engine = 'auto';
  42          }
  43  
  44          if ($engine == 'auto') {
  45              $engine = extension_loaded('xdiff') ? 'xdiff' : 'native';
  46          } else {
  47              $engine = basename($engine);
  48          }
  49  
  50          // WP #7391
  51          require_once dirname(__FILE__).'/Diff/Engine/' . $engine . '.php';
  52          $class = 'Text_Diff_Engine_' . $engine;
  53          $diff_engine = new $class();
  54  
  55          $this->_edits = call_user_func_array(array($diff_engine, 'diff'), $params);
  56      }
  57  
  58      /**
  59       * PHP4 constructor.
  60       */
  61  	public function Text_Diff( $engine, $params ) {
  62          self::__construct( $engine, $params );
  63      }
  64  
  65      /**
  66       * Returns the array of differences.
  67       */
  68      function getDiff()
  69      {
  70          return $this->_edits;
  71      }
  72  
  73      /**
  74       * returns the number of new (added) lines in a given diff.
  75       *
  76       * @since Text_Diff 1.1.0
  77       *
  78       * @return int The number of new lines
  79       */
  80      function countAddedLines()
  81      {
  82          $count = 0;
  83          foreach ($this->_edits as $edit) {
  84              if (is_a($edit, 'Text_Diff_Op_add') ||
  85                  is_a($edit, 'Text_Diff_Op_change')) {
  86                  $count += $edit->nfinal();
  87              }
  88          }
  89          return $count;
  90      }
  91  
  92      /**
  93       * Returns the number of deleted (removed) lines in a given diff.
  94       *
  95       * @since Text_Diff 1.1.0
  96       *
  97       * @return int The number of deleted lines
  98       */
  99      function countDeletedLines()
 100      {
 101          $count = 0;
 102          foreach ($this->_edits as $edit) {
 103              if (is_a($edit, 'Text_Diff_Op_delete') ||
 104                  is_a($edit, 'Text_Diff_Op_change')) {
 105                  $count += $edit->norig();
 106              }
 107          }
 108          return $count;
 109      }
 110  
 111      /**
 112       * Computes a reversed diff.
 113       *
 114       * Example:
 115       * <code>
 116       * $diff = new Text_Diff($lines1, $lines2);
 117       * $rev = $diff->reverse();
 118       * </code>
 119       *
 120       * @return Text_Diff  A Diff object representing the inverse of the
 121       *                    original diff.  Note that we purposely don't return a
 122       *                    reference here, since this essentially is a clone()
 123       *                    method.
 124       */
 125      function reverse()
 126      {
 127          if (version_compare(zend_version(), '2', '>')) {
 128              $rev = clone($this);
 129          } else {
 130              $rev = $this;
 131          }
 132          $rev->_edits = array();
 133          foreach ($this->_edits as $edit) {
 134              $rev->_edits[] = $edit->reverse();
 135          }
 136          return $rev;
 137      }
 138  
 139      /**
 140       * Checks for an empty diff.
 141       *
 142       * @return bool True if two sequences were identical.
 143       */
 144      function isEmpty()
 145      {
 146          foreach ($this->_edits as $edit) {
 147              if (!is_a($edit, 'Text_Diff_Op_copy')) {
 148                  return false;
 149              }
 150          }
 151          return true;
 152      }
 153  
 154      /**
 155       * Computes the length of the Longest Common Subsequence (LCS).
 156       *
 157       * This is mostly for diagnostic purposes.
 158       *
 159       * @return int The length of the LCS.
 160       */
 161      function lcs()
 162      {
 163          $lcs = 0;
 164          foreach ($this->_edits as $edit) {
 165              if (is_a($edit, 'Text_Diff_Op_copy')) {
 166                  $lcs += count($edit->orig);
 167              }
 168          }
 169          return $lcs;
 170      }
 171  
 172      /**
 173       * Gets the original set of lines.
 174       *
 175       * This reconstructs the $from_lines parameter passed to the constructor.
 176       *
 177       * @return array  The original sequence of strings.
 178       */
 179      function getOriginal()
 180      {
 181          $lines = array();
 182          foreach ($this->_edits as $edit) {
 183              if ($edit->orig) {
 184                  array_splice($lines, count($lines), 0, $edit->orig);
 185              }
 186          }
 187          return $lines;
 188      }
 189  
 190      /**
 191       * Gets the final set of lines.
 192       *
 193       * This reconstructs the $to_lines parameter passed to the constructor.
 194       *
 195       * @return array  The sequence of strings.
 196       */
 197      function getFinal()
 198      {
 199          $lines = array();
 200          foreach ($this->_edits as $edit) {
 201              if ($edit->final) {
 202                  array_splice($lines, count($lines), 0, $edit->final);
 203              }
 204          }
 205          return $lines;
 206      }
 207  
 208      /**
 209       * Removes trailing newlines from a line of text. This is meant to be used
 210       * with array_walk().
 211       *
 212       * @param string $line  The line to trim.
 213       * @param int    $key   The index of the line in the array. Not used.
 214       */
 215      static function trimNewlines(&$line, $key)
 216      {
 217          $line = str_replace(array("\n", "\r"), '', $line);
 218      }
 219  
 220      /**
 221       * Determines the location of the system temporary directory.
 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      static 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 does not match", E_USER_ERROR);
 264          }
 265          if (serialize($to_lines) != serialize($this->getFinal())) {
 266              trigger_error("Reconstructed final does not match", E_USER_ERROR);
 267          }
 268  
 269          $rev = $this->reverse();
 270          if (serialize($to_lines) != serialize($rev->getOriginal())) {
 271              trigger_error("Reversed original does not match", E_USER_ERROR);
 272          }
 273          if (serialize($from_lines) != serialize($rev->getFinal())) {
 274              trigger_error("Reversed final does not match", E_USER_ERROR);
 275          }
 276  
 277          $prevtype = null;
 278          foreach ($this->_edits as $edit) {
 279              if ($edit instanceof $prevtype) {
 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 __construct($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       * PHP4 constructor.
 338       */
 339  	public function Text_MappedDiff( $from_lines, $to_lines,
 340                               $mapped_from_lines, $mapped_to_lines ) {
 341          self::__construct( $from_lines, $to_lines,
 342                               $mapped_from_lines, $mapped_to_lines );
 343      }
 344  
 345  }
 346  
 347  /**
 348   * @package Text_Diff
 349   * @author  Geoffrey T. Dairiki <dairiki@dairiki.org>
 350   *
 351   * @access private
 352   */
 353  class Text_Diff_Op {
 354  
 355      var $orig;
 356      var $final;
 357  
 358      function &reverse()
 359      {
 360          trigger_error('Abstract method', E_USER_ERROR);
 361      }
 362  
 363      function norig()
 364      {
 365          return $this->orig ? count($this->orig) : 0;
 366      }
 367  
 368      function nfinal()
 369      {
 370          return $this->final ? count($this->final) : 0;
 371      }
 372  
 373  }
 374  
 375  /**
 376   * @package Text_Diff
 377   * @author  Geoffrey T. Dairiki <dairiki@dairiki.org>
 378   *
 379   * @access private
 380   */
 381  class Text_Diff_Op_copy extends Text_Diff_Op {
 382  
 383      /**
 384       * PHP5 constructor.
 385       */
 386      function __construct( $orig, $final = false )
 387      {
 388          if (!is_array($final)) {
 389              $final = $orig;
 390          }
 391          $this->orig = $orig;
 392          $this->final = $final;
 393      }
 394  
 395      /**
 396       * PHP4 constructor.
 397       */
 398  	public function Text_Diff_Op_copy( $orig, $final = false ) {
 399          self::__construct( $orig, $final );
 400      }
 401  
 402      function &reverse()
 403      {
 404          $reverse = new Text_Diff_Op_copy($this->final, $this->orig);
 405          return $reverse;
 406      }
 407  
 408  }
 409  
 410  /**
 411   * @package Text_Diff
 412   * @author  Geoffrey T. Dairiki <dairiki@dairiki.org>
 413   *
 414   * @access private
 415   */
 416  class Text_Diff_Op_delete extends Text_Diff_Op {
 417  
 418      /**
 419       * PHP5 constructor.
 420       */
 421  	function __construct( $lines )
 422      {
 423          $this->orig = $lines;
 424          $this->final = false;
 425      }
 426  
 427      /**
 428       * PHP4 constructor.
 429       */
 430  	public function Text_Diff_Op_delete( $lines ) {
 431          self::__construct( $lines );
 432      }
 433  
 434      function &reverse()
 435      {
 436          $reverse = new Text_Diff_Op_add($this->orig);
 437          return $reverse;
 438      }
 439  
 440  }
 441  
 442  /**
 443   * @package Text_Diff
 444   * @author  Geoffrey T. Dairiki <dairiki@dairiki.org>
 445   *
 446   * @access private
 447   */
 448  class Text_Diff_Op_add extends Text_Diff_Op {
 449  
 450      /**
 451       * PHP5 constructor.
 452       */
 453      function __construct( $lines )
 454      {
 455          $this->final = $lines;
 456          $this->orig = false;
 457      }
 458  
 459      /**
 460       * PHP4 constructor.
 461       */
 462  	public function Text_Diff_Op_add( $lines ) {
 463          self::__construct( $lines );
 464      }
 465  
 466      function &reverse()
 467      {
 468          $reverse = new Text_Diff_Op_delete($this->final);
 469          return $reverse;
 470      }
 471  
 472  }
 473  
 474  /**
 475   * @package Text_Diff
 476   * @author  Geoffrey T. Dairiki <dairiki@dairiki.org>
 477   *
 478   * @access private
 479   */
 480  class Text_Diff_Op_change extends Text_Diff_Op {
 481  
 482      /**
 483       * PHP5 constructor.
 484       */
 485      function __construct( $orig, $final )
 486      {
 487          $this->orig = $orig;
 488          $this->final = $final;
 489      }
 490  
 491      /**
 492       * PHP4 constructor.
 493       */
 494  	public function Text_Diff_Op_change( $orig, $final ) {
 495          self::__construct( $orig, $final );
 496      }
 497  
 498      function &reverse()
 499      {
 500          $reverse = new Text_Diff_Op_change($this->final, $this->orig);
 501          return $reverse;
 502      }
 503  
 504  }


Generated: Thu Nov 21 01:00:03 2024 Cross-referenced by PHPXref 0.7.1