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