[ Index ] |
PHP Cross Reference of WordPress Trunk (Updated Daily) |
[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 https://opensource.org/license/lgpl-2-1/. 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 throw new Text_Exception("Reconstructed original does not match"); 264 } 265 if (serialize($to_lines) != serialize($this->getFinal())) { 266 throw new Text_Exception("Reconstructed final does not match"); 267 } 268 269 $rev = $this->reverse(); 270 if (serialize($to_lines) != serialize($rev->getOriginal())) { 271 throw new Text_Exception("Reversed original does not match"); 272 } 273 if (serialize($from_lines) != serialize($rev->getFinal())) { 274 throw new Text_Exception("Reversed final does not match"); 275 } 276 277 $prevtype = null; 278 foreach ($this->_edits as $edit) { 279 if ($prevtype !== null && $edit instanceof $prevtype) { 280 throw new Text_Exception("Edit sequence is non-optimal"); 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-insensitive 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 abstract class Text_Diff_Op { 354 355 var $orig; 356 var $final; 357 358 abstract function &reverse(); 359 360 function norig() 361 { 362 return $this->orig ? count($this->orig) : 0; 363 } 364 365 function nfinal() 366 { 367 return $this->final ? count($this->final) : 0; 368 } 369 370 } 371 372 /** 373 * @package Text_Diff 374 * @author Geoffrey T. Dairiki <dairiki@dairiki.org> 375 * 376 * @access private 377 */ 378 class Text_Diff_Op_copy extends Text_Diff_Op { 379 380 /** 381 * PHP5 constructor. 382 */ 383 function __construct( $orig, $final = false ) 384 { 385 if (!is_array($final)) { 386 $final = $orig; 387 } 388 $this->orig = $orig; 389 $this->final = $final; 390 } 391 392 /** 393 * PHP4 constructor. 394 */ 395 public function Text_Diff_Op_copy( $orig, $final = false ) { 396 self::__construct( $orig, $final ); 397 } 398 399 function &reverse() 400 { 401 $reverse = new Text_Diff_Op_copy($this->final, $this->orig); 402 return $reverse; 403 } 404 405 } 406 407 /** 408 * @package Text_Diff 409 * @author Geoffrey T. Dairiki <dairiki@dairiki.org> 410 * 411 * @access private 412 */ 413 class Text_Diff_Op_delete extends Text_Diff_Op { 414 415 /** 416 * PHP5 constructor. 417 */ 418 function __construct( $lines ) 419 { 420 $this->orig = $lines; 421 $this->final = false; 422 } 423 424 /** 425 * PHP4 constructor. 426 */ 427 public function Text_Diff_Op_delete( $lines ) { 428 self::__construct( $lines ); 429 } 430 431 function &reverse() 432 { 433 $reverse = new Text_Diff_Op_add($this->orig); 434 return $reverse; 435 } 436 437 } 438 439 /** 440 * @package Text_Diff 441 * @author Geoffrey T. Dairiki <dairiki@dairiki.org> 442 * 443 * @access private 444 */ 445 class Text_Diff_Op_add extends Text_Diff_Op { 446 447 /** 448 * PHP5 constructor. 449 */ 450 function __construct( $lines ) 451 { 452 $this->final = $lines; 453 $this->orig = false; 454 } 455 456 /** 457 * PHP4 constructor. 458 */ 459 public function Text_Diff_Op_add( $lines ) { 460 self::__construct( $lines ); 461 } 462 463 function &reverse() 464 { 465 $reverse = new Text_Diff_Op_delete($this->final); 466 return $reverse; 467 } 468 469 } 470 471 /** 472 * @package Text_Diff 473 * @author Geoffrey T. Dairiki <dairiki@dairiki.org> 474 * 475 * @access private 476 */ 477 class Text_Diff_Op_change extends Text_Diff_Op { 478 479 /** 480 * PHP5 constructor. 481 */ 482 function __construct( $orig, $final ) 483 { 484 $this->orig = $orig; 485 $this->final = $final; 486 } 487 488 /** 489 * PHP4 constructor. 490 */ 491 public function Text_Diff_Op_change( $orig, $final ) { 492 self::__construct( $orig, $final ); 493 } 494 495 function &reverse() 496 { 497 $reverse = new Text_Diff_Op_change($this->final, $this->orig); 498 return $reverse; 499 } 500 501 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated : Thu Nov 21 08:20:01 2024 | Cross-referenced by PHPXref |