Verzeichnisstruktur phpBB-3.1.0


Veröffentlicht
27.10.2014

So funktioniert es


Auf das letzte Element klicken. Dies geht jeweils ein Schritt zurück

Auf das Icon klicken, dies öffnet das Verzeichnis. Nochmal klicken schließt das Verzeichnis.
Auf den Verzeichnisnamen klicken, dies zeigt nur das Verzeichnis mit Inhalt an

      (Beispiel Datei-Icons)

Auf das Icon klicken um den Quellcode anzuzeigen

renderer.php

Zuletzt modifiziert: 09.10.2024, 12:52 - Dateigröße: 18.52 KiB


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