[ Index ] |
PHP Cross Reference of WordPress |
[Summary view] [Print] [Text view]
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 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Nov 21 01:00:03 2024 | Cross-referenced by PHPXref 0.7.1 |