[ Index ] |
PHP Cross Reference of Unnamed Project |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * 4 * @package diff 5 * @version $Id$ 6 * @copyright (c) 2006 phpBB Group 7 * @license http://opensource.org/licenses/gpl-license.php GNU Public License 8 * 9 */ 10 11 /** 12 * @ignore 13 */ 14 if (!defined('IN_PHPBB')) 15 { 16 exit; 17 } 18 19 /** 20 * Code from pear.php.net, Text_Diff-1.1.0 package 21 * http://pear.php.net/package/Text_Diff/ 22 * 23 * Modified by phpBB Group to meet our coding standards 24 * and being able to integrate into phpBB 25 * 26 * A class to render Diffs in different formats. 27 * 28 * This class renders the diff in classic diff format. It is intended that 29 * this class be customized via inheritance, to obtain fancier outputs. 30 * 31 * Copyright 2004-2008 The Horde Project (http://www.horde.org/) 32 * 33 * @package diff 34 */ 35 class diff_renderer 36 { 37 /** 38 * Number of leading context "lines" to preserve. 39 * 40 * This should be left at zero for this class, but subclasses may want to 41 * set this to other values. 42 */ 43 var $_leading_context_lines = 0; 44 45 /** 46 * Number of trailing context "lines" to preserve. 47 * 48 * This should be left at zero for this class, but subclasses may want to 49 * set this to other values. 50 */ 51 var $_trailing_context_lines = 0; 52 53 /** 54 * Constructor. 55 */ 56 function diff_renderer($params = array()) 57 { 58 foreach ($params as $param => $value) 59 { 60 $v = '_' . $param; 61 if (isset($this->$v)) 62 { 63 $this->$v = $value; 64 } 65 } 66 } 67 68 /** 69 * Get any renderer parameters. 70 * 71 * @return array All parameters of this renderer object. 72 */ 73 function get_params() 74 { 75 $params = array(); 76 foreach (get_object_vars($this) as $k => $v) 77 { 78 if ($k[0] == '_') 79 { 80 $params[substr($k, 1)] = $v; 81 } 82 } 83 84 return $params; 85 } 86 87 /** 88 * Renders a diff. 89 * 90 * @param diff &$diff A diff object. 91 * 92 * @return string The formatted output. 93 */ 94 function render(&$diff) 95 { 96 $xi = $yi = 1; 97 $block = false; 98 $context = array(); 99 100 // Create a new diff object if it is a 3-way diff 101 if (is_a($diff, 'diff3')) 102 { 103 $diff3 = &$diff; 104 105 $diff_1 = $diff3->get_original(); 106 $diff_2 = $diff3->merged_output(); 107 108 unset($diff3); 109 110 $diff = new diff($diff_1, $diff_2); 111 } 112 113 $nlead = $this->_leading_context_lines; 114 $ntrail = $this->_trailing_context_lines; 115 116 $output = $this->_start_diff(); 117 $diffs = $diff->get_diff(); 118 119 foreach ($diffs as $i => $edit) 120 { 121 // If these are unchanged (copied) lines, and we want to keep leading or trailing context lines, extract them from the copy block. 122 if (is_a($edit, 'diff_op_copy')) 123 { 124 // Do we have any diff blocks yet? 125 if (is_array($block)) 126 { 127 // How many lines to keep as context from the copy block. 128 $keep = ($i == sizeof($diffs) - 1) ? $ntrail : $nlead + $ntrail; 129 if (sizeof($edit->orig) <= $keep) 130 { 131 // We have less lines in the block than we want for context => keep the whole block. 132 $block[] = $edit; 133 } 134 else 135 { 136 if ($ntrail) 137 { 138 // Create a new block with as many lines as we need for the trailing context. 139 $context = array_slice($edit->orig, 0, $ntrail); 140 $block[] = new diff_op_copy($context); 141 } 142 143 $output .= $this->_block($x0, $ntrail + $xi - $x0, $y0, $ntrail + $yi - $y0, $block); 144 $block = false; 145 } 146 } 147 // Keep the copy block as the context for the next block. 148 $context = $edit->orig; 149 } 150 else 151 { 152 // Don't we have any diff blocks yet? 153 if (!is_array($block)) 154 { 155 // Extract context lines from the preceding copy block. 156 $context = array_slice($context, sizeof($context) - $nlead); 157 $x0 = $xi - sizeof($context); 158 $y0 = $yi - sizeof($context); 159 $block = array(); 160 161 if ($context) 162 { 163 $block[] = new diff_op_copy($context); 164 } 165 } 166 $block[] = $edit; 167 } 168 169 $xi += ($edit->orig) ? sizeof($edit->orig) : 0; 170 $yi += ($edit->final) ? sizeof($edit->final) : 0; 171 } 172 173 if (is_array($block)) 174 { 175 $output .= $this->_block($x0, $xi - $x0, $y0, $yi - $y0, $block); 176 } 177 178 return $output . $this->_end_diff(); 179 } 180 181 function _block($xbeg, $xlen, $ybeg, $ylen, &$edits) 182 { 183 $output = $this->_start_block($this->_block_header($xbeg, $xlen, $ybeg, $ylen)); 184 185 foreach ($edits as $edit) 186 { 187 switch (get_class($edit)) 188 { 189 case 'diff_op_copy': 190 $output .= $this->_context($edit->orig); 191 break; 192 193 case 'diff_op_add': 194 $output .= $this->_added($edit->final); 195 break; 196 197 case 'diff_op_delete': 198 $output .= $this->_deleted($edit->orig); 199 break; 200 201 case 'diff_op_change': 202 $output .= $this->_changed($edit->orig, $edit->final); 203 break; 204 } 205 } 206 207 return $output . $this->_end_block(); 208 } 209 210 function _start_diff() 211 { 212 return ''; 213 } 214 215 function _end_diff() 216 { 217 return ''; 218 } 219 220 function _block_header($xbeg, $xlen, $ybeg, $ylen) 221 { 222 if ($xlen > 1) 223 { 224 $xbeg .= ',' . ($xbeg + $xlen - 1); 225 } 226 227 if ($ylen > 1) 228 { 229 $ybeg .= ',' . ($ybeg + $ylen - 1); 230 } 231 232 // this matches the GNU Diff behaviour 233 if ($xlen && !$ylen) 234 { 235 $ybeg--; 236 } 237 else if (!$xlen) 238 { 239 $xbeg--; 240 } 241 242 return $xbeg . ($xlen ? ($ylen ? 'c' : 'd') : 'a') . $ybeg; 243 } 244 245 function _start_block($header) 246 { 247 return $header . "\n"; 248 } 249 250 function _end_block() 251 { 252 return ''; 253 } 254 255 function _lines($lines, $prefix = ' ') 256 { 257 return $prefix . implode("\n$prefix", $lines) . "\n"; 258 } 259 260 function _context($lines) 261 { 262 return $this->_lines($lines, ' '); 263 } 264 265 function _added($lines) 266 { 267 return $this->_lines($lines, '> '); 268 } 269 270 function _deleted($lines) 271 { 272 return $this->_lines($lines, '< '); 273 } 274 275 function _changed($orig, $final) 276 { 277 return $this->_deleted($orig) . "---\n" . $this->_added($final); 278 } 279 280 /** 281 * Our function to get the diff 282 */ 283 function get_diff_content($diff) 284 { 285 return $this->render($diff); 286 } 287 } 288 289 /** 290 * Renders a unified diff 291 * @package diff 292 */ 293 class diff_renderer_unified extends diff_renderer 294 { 295 var $_leading_context_lines = 4; 296 var $_trailing_context_lines = 4; 297 298 /** 299 * Our function to get the diff 300 */ 301 function get_diff_content($diff) 302 { 303 return nl2br($this->render($diff)); 304 } 305 306 function _block_header($xbeg, $xlen, $ybeg, $ylen) 307 { 308 if ($xlen != 1) 309 { 310 $xbeg .= ',' . $xlen; 311 } 312 313 if ($ylen != 1) 314 { 315 $ybeg .= ',' . $ylen; 316 } 317 return '<div class="diff"><big class="info">@@ -' . $xbeg . ' +' . $ybeg . ' @@</big></div>'; 318 } 319 320 function _context($lines) 321 { 322 return '<pre class="diff context">' . htmlspecialchars($this->_lines($lines, ' ')) . '<br /></pre>'; 323 } 324 325 function _added($lines) 326 { 327 return '<pre class="diff added">' . htmlspecialchars($this->_lines($lines, '+')) . '<br /></pre>'; 328 } 329 330 function _deleted($lines) 331 { 332 return '<pre class="diff removed">' . htmlspecialchars($this->_lines($lines, '-')) . '<br /></pre>'; 333 } 334 335 function _changed($orig, $final) 336 { 337 return $this->_deleted($orig) . $this->_added($final); 338 } 339 340 function _start_diff() 341 { 342 $start = '<div class="file">'; 343 344 return $start; 345 } 346 347 function _end_diff() 348 { 349 return '</div>'; 350 } 351 352 function _end_block() 353 { 354 return ''; 355 } 356 } 357 358 /** 359 * "Inline" diff renderer. 360 * 361 * This class renders diffs in the Wiki-style "inline" format. 362 * 363 * @author Ciprian Popovici 364 * @package diff 365 */ 366 class diff_renderer_inline extends diff_renderer 367 { 368 var $_leading_context_lines = 10000; 369 var $_trailing_context_lines = 10000; 370 371 // Prefix and suffix for inserted text 372 var $_ins_prefix = '<span class="ins">'; 373 var $_ins_suffix = '</span>'; 374 375 // Prefix and suffix for deleted text 376 var $_del_prefix = '<span class="del">'; 377 var $_del_suffix = '</span>'; 378 379 var $_block_head = ''; 380 381 // What are we currently splitting on? Used to recurse to show word-level 382 var $_split_level = 'lines'; 383 384 /** 385 * Our function to get the diff 386 */ 387 function get_diff_content($diff) 388 { 389 return '<pre>' . nl2br($this->render($diff)) . '<br /></pre>'; 390 } 391 392 function _start_diff() 393 { 394 return ''; 395 } 396 397 function _end_diff() 398 { 399 return ''; 400 } 401 402 function _block_header($xbeg, $xlen, $ybeg, $ylen) 403 { 404 return $this->_block_head; 405 } 406 407 function _start_block($header) 408 { 409 return $header; 410 } 411 412 function _lines($lines, $prefix = ' ', $encode = true) 413 { 414 if ($encode) 415 { 416 array_walk($lines, array(&$this, '_encode')); 417 } 418 419 if ($this->_split_level == 'words') 420 { 421 return implode('', $lines); 422 } 423 else 424 { 425 return implode("\n", $lines) . "\n"; 426 } 427 } 428 429 function _added($lines) 430 { 431 array_walk($lines, array(&$this, '_encode')); 432 $lines[0] = $this->_ins_prefix . $lines[0]; 433 $lines[sizeof($lines) - 1] .= $this->_ins_suffix; 434 return $this->_lines($lines, ' ', false); 435 } 436 437 function _deleted($lines, $words = false) 438 { 439 array_walk($lines, array(&$this, '_encode')); 440 $lines[0] = $this->_del_prefix . $lines[0]; 441 $lines[sizeof($lines) - 1] .= $this->_del_suffix; 442 return $this->_lines($lines, ' ', false); 443 } 444 445 function _changed($orig, $final) 446 { 447 // If we've already split on words, don't try to do so again - just display. 448 if ($this->_split_level == 'words') 449 { 450 $prefix = ''; 451 while ($orig[0] !== false && $final[0] !== false && substr($orig[0], 0, 1) == ' ' && substr($final[0], 0, 1) == ' ') 452 { 453 $prefix .= substr($orig[0], 0, 1); 454 $orig[0] = substr($orig[0], 1); 455 $final[0] = substr($final[0], 1); 456 } 457 458 return $prefix . $this->_deleted($orig) . $this->_added($final); 459 } 460 461 $text1 = implode("\n", $orig); 462 $text2 = implode("\n", $final); 463 464 // Non-printing newline marker. 465 $nl = "\0"; 466 467 // We want to split on word boundaries, but we need to preserve whitespace as well. 468 // Therefore we split on words, but include all blocks of whitespace in the wordlist. 469 $splitted_text_1 = $this->_split_on_words($text1, $nl); 470 $splitted_text_2 = $this->_split_on_words($text2, $nl); 471 472 $diff = new diff($splitted_text_1, $splitted_text_2); 473 unset($splitted_text_1, $splitted_text_2); 474 475 // Get the diff in inline format. 476 $renderer = new diff_renderer_inline(array_merge($this->get_params(), array('split_level' => 'words'))); 477 478 // Run the diff and get the output. 479 return str_replace($nl, "\n", $renderer->render($diff)) . "\n"; 480 } 481 482 function _split_on_words($string, $newline_escape = "\n") 483 { 484 // Ignore \0; otherwise the while loop will never finish. 485 $string = str_replace("\0", '', $string); 486 487 $words = array(); 488 $length = strlen($string); 489 $pos = 0; 490 491 $tab_there = true; 492 while ($pos < $length) 493 { 494 // Check for tabs... do not include them 495 if ($tab_there && substr($string, $pos, 1) === "\t") 496 { 497 $words[] = "\t"; 498 $pos++; 499 500 continue; 501 } 502 else 503 { 504 $tab_there = false; 505 } 506 507 // Eat a word with any preceding whitespace. 508 $spaces = strspn(substr($string, $pos), " \n"); 509 $nextpos = strcspn(substr($string, $pos + $spaces), " \n"); 510 $words[] = str_replace("\n", $newline_escape, substr($string, $pos, $spaces + $nextpos)); 511 $pos += $spaces + $nextpos; 512 } 513 514 return $words; 515 } 516 517 function _encode(&$string) 518 { 519 $string = htmlspecialchars($string); 520 } 521 } 522 523 /** 524 * "raw" diff renderer. 525 * This class could be used to output a raw unified patch file 526 * 527 * @package diff 528 */ 529 class diff_renderer_raw extends diff_renderer 530 { 531 var $_leading_context_lines = 4; 532 var $_trailing_context_lines = 4; 533 534 /** 535 * Our function to get the diff 536 */ 537 function get_diff_content($diff) 538 { 539 return '<textarea style="height: 290px;" rows="15" cols="76" class="full">' . htmlspecialchars($this->render($diff)) . '</textarea>'; 540 } 541 542 function _block_header($xbeg, $xlen, $ybeg, $ylen) 543 { 544 if ($xlen != 1) 545 { 546 $xbeg .= ',' . $xlen; 547 } 548 549 if ($ylen != 1) 550 { 551 $ybeg .= ',' . $ylen; 552 } 553 return '@@ -' . $xbeg . ' +' . $ybeg . ' @@'; 554 } 555 556 function _context($lines) 557 { 558 return $this->_lines($lines, ' '); 559 } 560 561 function _added($lines) 562 { 563 return $this->_lines($lines, '+'); 564 } 565 566 function _deleted($lines) 567 { 568 return $this->_lines($lines, '-'); 569 } 570 571 function _changed($orig, $final) 572 { 573 return $this->_deleted($orig) . $this->_added($final); 574 } 575 } 576 577 /** 578 * "chora (Horde)" diff renderer - similar style. 579 * This renderer class is a modified human_readable function from the Horde Framework. 580 * 581 * @package diff 582 */ 583 class diff_renderer_side_by_side extends diff_renderer 584 { 585 var $_leading_context_lines = 3; 586 var $_trailing_context_lines = 3; 587 588 var $lines = array(); 589 590 // Hold the left and right columns of lines for change blocks. 591 var $cols; 592 var $state; 593 594 var $data = false; 595 596 /** 597 * Our function to get the diff 598 */ 599 function get_diff_content($diff) 600 { 601 global $user; 602 603 $output = ''; 604 $output .= '<table cellspacing="0" class="hrdiff"> 605 <caption> 606 <span class="unmodified"> </span> ' . $user->lang['LINE_UNMODIFIED'] . ' 607 <span class="added"> </span> ' . $user->lang['LINE_ADDED'] . ' 608 <span class="modified"> </span> ' . $user->lang['LINE_MODIFIED'] . ' 609 <span class="removed"> </span> ' . $user->lang['LINE_REMOVED'] . ' 610 </caption> 611 <tbody> 612 '; 613 614 $this->render($diff); 615 616 // Is the diff empty? 617 if (!sizeof($this->lines)) 618 { 619 $output .= '<tr><th colspan="2">' . $user->lang['NO_VISIBLE_CHANGES'] . '</th></tr>'; 620 } 621 else 622 { 623 // Iterate through every header block of changes 624 foreach ($this->lines as $header) 625 { 626 $output .= '<tr><th>' . $user->lang['LINE'] . ' ' . $header['oldline'] . '</th><th>' . $user->lang['LINE'] . ' ' . $header['newline'] . '</th></tr>'; 627 628 // Each header block consists of a number of changes (add, remove, change). 629 $current_context = ''; 630 631 foreach ($header['contents'] as $change) 632 { 633 if (!empty($current_context) && $change['type'] != 'empty') 634 { 635 $line = $current_context; 636 $current_context = ''; 637 638 $output .= '<tr class="unmodified"><td><pre>' . ((strlen($line)) ? $line : ' ') . '<br /></pre></td> 639 <td><pre>' . ((strlen($line)) ? $line : ' ') . '<br /></pre></td></tr>'; 640 } 641 642 switch ($change['type']) 643 { 644 case 'add': 645 $line = ''; 646 647 foreach ($change['lines'] as $_line) 648 { 649 $line .= htmlspecialchars($_line) . '<br />'; 650 } 651 652 $output .= '<tr><td class="added_empty"> </td><td class="added"><pre>' . ((strlen($line)) ? $line : ' ') . '<br /></pre></td></tr>'; 653 break; 654 655 case 'remove': 656 $line = ''; 657 658 foreach ($change['lines'] as $_line) 659 { 660 $line .= htmlspecialchars($_line) . '<br />'; 661 } 662 663 $output .= '<tr><td class="removed"><pre>' . ((strlen($line)) ? $line : ' ') . '<br /></pre></td><td class="removed_empty"> </td></tr>'; 664 break; 665 666 case 'empty': 667 $current_context .= htmlspecialchars($change['line']) . '<br />'; 668 break; 669 670 case 'change': 671 // Pop the old/new stacks one by one, until both are empty. 672 $oldsize = sizeof($change['old']); 673 $newsize = sizeof($change['new']); 674 $left = $right = ''; 675 676 for ($row = 0, $row_max = max($oldsize, $newsize); $row < $row_max; ++$row) 677 { 678 $left .= isset($change['old'][$row]) ? htmlspecialchars($change['old'][$row]) : ''; 679 $left .= '<br />'; 680 $right .= isset($change['new'][$row]) ? htmlspecialchars($change['new'][$row]) : ''; 681 $right .= '<br />'; 682 } 683 684 $output .= '<tr>'; 685 686 if (!empty($left)) 687 { 688 $output .= '<td class="modified"><pre>' . $left . '<br /></pre></td>'; 689 } 690 else if ($row < $oldsize) 691 { 692 $output .= '<td class="modified"> </td>'; 693 } 694 else 695 { 696 $output .= '<td class="unmodified"> </td>'; 697 } 698 699 if (!empty($right)) 700 { 701 $output .= '<td class="modified"><pre>' . $right . '<br /></pre></td>'; 702 } 703 else if ($row < $newsize) 704 { 705 $output .= '<td class="modified"> </td>'; 706 } 707 else 708 { 709 $output .= '<td class="unmodified"> </td>'; 710 } 711 712 $output .= '</tr>'; 713 break; 714 } 715 } 716 717 if (!empty($current_context)) 718 { 719 $line = $current_context; 720 $current_context = ''; 721 722 $output .= '<tr class="unmodified"><td><pre>' . ((strlen($line)) ? $line : ' ') . '<br /></pre></td>'; 723 $output .= '<td><pre>' . ((strlen($line)) ? $line : ' ') . '<br /></pre></td></tr>'; 724 } 725 } 726 } 727 728 $output .= '</tbody></table>'; 729 730 return $output; 731 } 732 733 function _start_diff() 734 { 735 $this->lines = array(); 736 737 $this->data = false; 738 $this->cols = array(array(), array()); 739 $this->state = 'empty'; 740 741 return ''; 742 } 743 744 function _end_diff() 745 { 746 // Just flush any remaining entries in the columns stack. 747 switch ($this->state) 748 { 749 case 'add': 750 $this->data['contents'][] = array('type' => 'add', 'lines' => $this->cols[0]); 751 break; 752 753 case 'remove': 754 // We have some removal lines pending in our stack, so flush them. 755 $this->data['contents'][] = array('type' => 'remove', 'lines' => $this->cols[0]); 756 break; 757 758 case 'change': 759 // We have both remove and addition lines, so this is a change block. 760 $this->data['contents'][] = array('type' => 'change', 'old' => $this->cols[0], 'new' => $this->cols[1]); 761 break; 762 } 763 764 if ($this->data !== false) 765 { 766 $this->lines[] = $this->data; 767 } 768 769 return ''; 770 } 771 772 function _block_header($xbeg, $xlen, $ybeg, $ylen) 773 { 774 // Push any previous header information to the return stack. 775 if ($this->data !== false) 776 { 777 $this->lines[] = $this->data; 778 } 779 780 $this->data = array('type' => 'header', 'oldline' => $xbeg, 'newline' => $ybeg, 'contents' => array()); 781 $this->state = 'dump'; 782 } 783 784 function _added($lines) 785 { 786 array_walk($lines, array(&$this, '_perform_add')); 787 } 788 789 function _perform_add($line) 790 { 791 if ($this->state == 'empty') 792 { 793 return ''; 794 } 795 796 // This is just an addition line. 797 if ($this->state == 'dump' || $this->state == 'add') 798 { 799 // Start adding to the addition stack. 800 $this->cols[0][] = $line; 801 $this->state = 'add'; 802 } 803 else 804 { 805 // This is inside a change block, so start accumulating lines. 806 $this->state = 'change'; 807 $this->cols[1][] = $line; 808 } 809 } 810 811 function _deleted($lines) 812 { 813 array_walk($lines, array(&$this, '_perform_delete')); 814 } 815 816 function _perform_delete($line) 817 { 818 // This is a removal line. 819 $this->state = 'remove'; 820 $this->cols[0][] = $line; 821 } 822 823 function _context($lines) 824 { 825 array_walk($lines, array(&$this, '_perform_context')); 826 } 827 828 function _perform_context($line) 829 { 830 // An empty block with no action. 831 switch ($this->state) 832 { 833 case 'add': 834 $this->data['contents'][] = array('type' => 'add', 'lines' => $this->cols[0]); 835 break; 836 837 case 'remove': 838 // We have some removal lines pending in our stack, so flush them. 839 $this->data['contents'][] = array('type' => 'remove', 'lines' => $this->cols[0]); 840 break; 841 842 case 'change': 843 // We have both remove and addition lines, so this is a change block. 844 $this->data['contents'][] = array('type' => 'change', 'old' => $this->cols[0], 'new' => $this->cols[1]); 845 break; 846 } 847 848 $this->cols = array(array(), array()); 849 $this->data['contents'][] = array('type' => 'empty', 'line' => $line); 850 $this->state = 'dump'; 851 } 852 853 function _changed($orig, $final) 854 { 855 return $this->_deleted($orig) . $this->_added($final); 856 } 857 858 } 859 860 ?>
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Wed Oct 2 15:03:47 2013 | Cross-referenced by PHPXref 0.7.1 |