Verzeichnisstruktur phpBB-3.3.15


Veröffentlicht
28.08.2024

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

diff.php

Zuletzt modifiziert: 02.04.2025, 15:02 - Dateigröße: 24.03 KiB


0001  <?php
0002  /**
0003  *
0004  * This file is part of the phpBB Forum Software package.
0005  *
0006  * @copyright (c) phpBB Limited <https://www.phpbb.com>
0007  * @license GNU General Public License, version 2 (GPL-2.0)
0008  *
0009  * For full copyright and license information, please see
0010  * the docs/CREDITS.txt file.
0011  *
0012  */
0013   
0014  /**
0015  * @ignore
0016  */
0017  if (!defined('IN_PHPBB'))
0018  {
0019      exit;
0020  }
0021   
0022  /**
0023  * Code from pear.php.net, Text_Diff-1.1.0 package
0024  * http://pear.php.net/package/Text_Diff/
0025  *
0026  * Modified by phpBB Limited to meet our coding standards
0027  * and being able to integrate into phpBB
0028  *
0029  * General API for generating and formatting diffs - the differences between
0030  * two sequences of strings.
0031  *
0032  * Copyright 2004 Geoffrey T. Dairiki <dairiki@dairiki.org>
0033  * Copyright 2004-2008 The Horde Project (http://www.horde.org/)
0034  *
0035  * @package diff
0036  * @author  Geoffrey T. Dairiki <dairiki@dairiki.org>
0037  */
0038  class diff
0039  {
0040      /**
0041      * Array of changes.
0042      * @var array
0043      */
0044      var $_edits;
0045   
0046      /**
0047      * Computes diffs between sequences of strings.
0048      *
0049      * @param array    &$from_content    An array of strings. Typically these are lines from a file.
0050      * @param array    &$to_content    An array of strings.
0051      * @param bool    $preserve_cr    If true, \r is replaced by a new line in the diff output
0052      */
0053      function __construct(&$from_content, &$to_content, $preserve_cr = true)
0054      {
0055          $diff_engine = new diff_engine();
0056          $this->_edits = $diff_engine->diff($from_content, $to_content, $preserve_cr);
0057      }
0058   
0059      /**
0060      * Returns the array of differences.
0061      */
0062      function get_diff()
0063      {
0064          return $this->_edits;
0065      }
0066   
0067      /**
0068      * returns the number of new (added) lines in a given diff.
0069      *
0070      * @since Text_Diff 1.1.0
0071      *
0072      * @return integer The number of new lines
0073      */
0074      function count_added_lines()
0075      {
0076          $count = 0;
0077   
0078          for ($i = 0, $size = count($this->_edits); $i < $size; $i++)
0079          {
0080              $edit = $this->_edits[$i];
0081   
0082              if (is_a($edit, 'diff_op_add') || is_a($edit, 'diff_op_change'))
0083              {
0084                  $count += $edit->nfinal();
0085              }
0086          }
0087          return $count;
0088      }
0089   
0090      /**
0091      * Returns the number of deleted (removed) lines in a given diff.
0092      *
0093      * @since Text_Diff 1.1.0
0094      *
0095      * @return integer The number of deleted lines
0096      */
0097      function count_deleted_lines()
0098      {
0099          $count = 0;
0100   
0101          for ($i = 0, $size = count($this->_edits); $i < $size; $i++)
0102          {
0103              $edit = $this->_edits[$i];
0104   
0105              if (is_a($edit, 'diff_op_delete') || is_a($edit, 'diff_op_change'))
0106              {
0107                  $count += $edit->norig();
0108              }
0109          }
0110          return $count;
0111      }
0112   
0113      /**
0114      * Computes a reversed diff.
0115      *
0116      * Example:
0117      * <code>
0118      * $diff = new diff($lines1, $lines2);
0119      * $rev = $diff->reverse();
0120      * </code>
0121      *
0122      * @return diff  A Diff object representing the inverse of the original diff.
0123      *               Note that we purposely don't return a reference here, since
0124      *               this essentially is a clone() method.
0125      */
0126      function reverse()
0127      {
0128          if (version_compare(zend_version(), '2', '>'))
0129          {
0130              $rev = clone($this);
0131          }
0132          else
0133          {
0134              $rev = $this;
0135          }
0136   
0137          $rev->_edits = array();
0138   
0139          for ($i = 0, $size = count($this->_edits); $i < $size; $i++)
0140          {
0141              $edit = $this->_edits[$i];
0142              $rev->_edits[] = $edit->reverse();
0143          }
0144   
0145          return $rev;
0146      }
0147   
0148      /**
0149      * Checks for an empty diff.
0150      *
0151      * @return boolean  True if two sequences were identical.
0152      */
0153      function is_empty()
0154      {
0155          for ($i = 0, $size = count($this->_edits); $i < $size; $i++)
0156          {
0157              $edit = $this->_edits[$i];
0158   
0159              // skip diff_op_copy
0160              if (is_a($edit, 'diff_op_copy'))
0161              {
0162                  continue;
0163              }
0164   
0165              if (is_a($edit, 'diff_op_delete') || is_a($edit, 'diff_op_add'))
0166              {
0167                  $orig = $edit->orig;
0168                  $final = $edit->final;
0169   
0170                  // We can simplify one case where the array is usually supposed to be empty...
0171                  if (is_array($orig) && count($orig) == 1 && trim($orig[0]) === '')
0172                  {
0173                      $orig = array();
0174                  }
0175                  if (is_array($final) && count($final) == 1 && trim($final[0]) === '')
0176                  {
0177                      $final = array();
0178                  }
0179   
0180                  if (!$orig && !$final)
0181                  {
0182                      continue;
0183                  }
0184   
0185                  return false;
0186              }
0187   
0188              return false;
0189          }
0190   
0191          return true;
0192      }
0193   
0194      /**
0195      * Computes the length of the Longest Common Subsequence (LCS).
0196      *
0197      * This is mostly for diagnostic purposes.
0198      *
0199      * @return integer  The length of the LCS.
0200      */
0201      function lcs()
0202      {
0203          $lcs = 0;
0204   
0205          for ($i = 0, $size = count($this->_edits); $i < $size; $i++)
0206          {
0207              $edit = $this->_edits[$i];
0208   
0209              if (is_a($edit, 'diff_op_copy'))
0210              {
0211                  $lcs += count($edit->orig);
0212              }
0213          }
0214          return $lcs;
0215      }
0216   
0217      /**
0218      * Gets the original set of lines.
0219      *
0220      * This reconstructs the $from_lines parameter passed to the constructor.
0221      *
0222      * @return array  The original sequence of strings.
0223      */
0224      function get_original()
0225      {
0226          $lines = array();
0227   
0228          for ($i = 0, $size = count($this->_edits); $i < $size; $i++)
0229          {
0230              $edit = $this->_edits[$i];
0231   
0232              if ($edit->orig)
0233              {
0234                  array_splice($lines, count($lines), 0, $edit->orig);
0235              }
0236          }
0237          return $lines;
0238      }
0239   
0240      /**
0241      * Gets the final set of lines.
0242      *
0243      * This reconstructs the $to_lines parameter passed to the constructor.
0244      *
0245      * @return array  The sequence of strings.
0246      */
0247      function get_final()
0248      {
0249          $lines = array();
0250   
0251          for ($i = 0, $size = count($this->_edits); $i < $size; $i++)
0252          {
0253              $edit = $this->_edits[$i];
0254   
0255              if ($edit->final)
0256              {
0257                  array_splice($lines, count($lines), 0, $edit->final);
0258              }
0259          }
0260          return $lines;
0261      }
0262   
0263      /**
0264      * Removes trailing newlines from a line of text. This is meant to be used with array_walk().
0265      *
0266      * @param string &$line  The line to trim.
0267      * @param integer $key  The index of the line in the array. Not used.
0268      */
0269      function trim_newlines(&$line, $key)
0270      {
0271          $line = str_replace(array("\n", "\r"), '', $line);
0272      }
0273   
0274      /**
0275      * Checks a diff for validity.
0276      *
0277      * This is here only for debugging purposes.
0278      */
0279      function _check($from_lines, $to_lines)
0280      {
0281          if (serialize($from_lines) != serialize($this->get_original()))
0282          {
0283              trigger_error("[diff] Reconstructed original doesn't match", E_USER_ERROR);
0284          }
0285   
0286          if (serialize($to_lines) != serialize($this->get_final()))
0287          {
0288              trigger_error("[diff] Reconstructed final doesn't match", E_USER_ERROR);
0289          }
0290   
0291          $rev = $this->reverse();
0292   
0293          if (serialize($to_lines) != serialize($rev->get_original()))
0294          {
0295              trigger_error("[diff] Reversed original doesn't match", E_USER_ERROR);
0296          }
0297   
0298          if (serialize($from_lines) != serialize($rev->get_final()))
0299          {
0300              trigger_error("[diff] Reversed final doesn't match", E_USER_ERROR);
0301          }
0302   
0303          $prevtype = null;
0304   
0305          for ($i = 0, $size = count($this->_edits); $i < $size; $i++)
0306          {
0307              $edit = $this->_edits[$i];
0308   
0309              if ($prevtype == get_class($edit))
0310              {
0311                  trigger_error("[diff] Edit sequence is non-optimal", E_USER_ERROR);
0312              }
0313              $prevtype = get_class($edit);
0314          }
0315   
0316          return true;
0317      }
0318  }
0319   
0320  /**
0321  * @package diff
0322  * @author  Geoffrey T. Dairiki <dairiki@dairiki.org>
0323  */
0324  class mapped_diff extends diff
0325  {
0326      /**
0327      * Computes a diff between sequences of strings.
0328      *
0329      * This can be used to compute things like case-insensitve diffs, or diffs
0330      * which ignore changes in white-space.
0331      *
0332      * @param array $from_lines         An array of strings.
0333      * @param array $to_lines           An array of strings.
0334      * @param array $mapped_from_lines  This array should have the same size number of elements as $from_lines.
0335      *                                  The elements in $mapped_from_lines and $mapped_to_lines are what is actually
0336      *                                  compared when computing the diff.
0337      * @param array $mapped_to_lines    This array should have the same number of elements as $to_lines.
0338      */
0339      function __construct(&$from_lines, &$to_lines, &$mapped_from_lines, &$mapped_to_lines)
0340      {
0341          if (count($from_lines) != count($mapped_from_lines) || count($to_lines) != count($mapped_to_lines))
0342          {
0343              return false;
0344          }
0345   
0346          parent::__construct($mapped_from_lines, $mapped_to_lines);
0347   
0348          $xi = $yi = 0;
0349          for ($i = 0; $i < count($this->_edits); $i++)
0350          {
0351              $orig = &$this->_edits[$i]->orig;
0352              if (is_array($orig))
0353              {
0354                  $orig = array_slice($from_lines, $xi, count($orig));
0355                  $xi += count($orig);
0356              }
0357   
0358              $final = &$this->_edits[$i]->final;
0359              if (is_array($final))
0360              {
0361                  $final = array_slice($to_lines, $yi, count($final));
0362                  $yi += count($final);
0363              }
0364          }
0365      }
0366  }
0367   
0368  /**
0369  * @package diff
0370  * @author  Geoffrey T. Dairiki <dairiki@dairiki.org>
0371  *
0372  * @access private
0373  */
0374  class diff_op
0375  {
0376      var $orig;
0377      var $final;
0378   
0379      function &reverse()
0380      {
0381          trigger_error('[diff] Abstract method', E_USER_ERROR);
0382      }
0383   
0384      function norig()
0385      {
0386          return ($this->orig) ? count($this->orig) : 0;
0387      }
0388   
0389      function nfinal()
0390      {
0391          return ($this->final) ? count($this->final) : 0;
0392      }
0393  }
0394   
0395  /**
0396  * @package diff
0397  * @author  Geoffrey T. Dairiki <dairiki@dairiki.org>
0398  *
0399  * @access private
0400  */
0401  class diff_op_copy extends diff_op
0402  {
0403      function __construct($orig, $final = false)
0404      {
0405          if (!is_array($final))
0406          {
0407              $final = $orig;
0408          }
0409          $this->orig = $orig;
0410          $this->final = $final;
0411      }
0412   
0413      function &reverse()
0414      {
0415          $reverse = new diff_op_copy($this->final, $this->orig);
0416          return $reverse;
0417      }
0418  }
0419   
0420  /**
0421  * @package diff
0422  * @author  Geoffrey T. Dairiki <dairiki@dairiki.org>
0423  *
0424  * @access private
0425  */
0426  class diff_op_delete extends diff_op
0427  {
0428      function __construct($lines)
0429      {
0430          $this->orig = $lines;
0431          $this->final = false;
0432      }
0433   
0434      function &reverse()
0435      {
0436          $reverse = new diff_op_add($this->orig);
0437          return $reverse;
0438      }
0439  }
0440   
0441  /**
0442  * @package diff
0443  * @author  Geoffrey T. Dairiki <dairiki@dairiki.org>
0444  *
0445  * @access private
0446  */
0447  class diff_op_add extends diff_op
0448  {
0449      function __construct($lines)
0450      {
0451          $this->final = $lines;
0452          $this->orig = false;
0453      }
0454   
0455      function &reverse()
0456      {
0457          $reverse = new diff_op_delete($this->final);
0458          return $reverse;
0459      }
0460  }
0461   
0462  /**
0463  * @package diff
0464  * @author  Geoffrey T. Dairiki <dairiki@dairiki.org>
0465  *
0466  * @access private
0467  */
0468  class diff_op_change extends diff_op
0469  {
0470      function __construct($orig, $final)
0471      {
0472          $this->orig = $orig;
0473          $this->final = $final;
0474      }
0475   
0476      function &reverse()
0477      {
0478          $reverse = new diff_op_change($this->final, $this->orig);
0479          return $reverse;
0480      }
0481  }
0482   
0483   
0484  /**
0485  * A class for computing three way diffs.
0486  *
0487  * @package diff
0488  * @author  Geoffrey T. Dairiki <dairiki@dairiki.org>
0489  */
0490  class diff3 extends diff
0491  {
0492      /**
0493      * Conflict counter.
0494      * @var integer
0495      */
0496      var $_conflicting_blocks = 0;
0497   
0498      /**
0499      * Computes diff between 3 sequences of strings.
0500      *
0501      * @param array &$orig        The original lines to use.
0502      * @param array &$final1        The first version to compare to.
0503      * @param array &$final2        The second version to compare to.
0504      * @param bool $preserve_cr    If true, \r\n and bare \r are replaced by a new line
0505      *                            in the diff output
0506      */
0507      function __construct(&$orig, &$final1, &$final2, $preserve_cr = true)
0508      {
0509          $diff_engine = new diff_engine();
0510   
0511          $diff_1 = $diff_engine->diff($orig, $final1, $preserve_cr);
0512          $diff_2 = $diff_engine->diff($orig, $final2, $preserve_cr);
0513   
0514          unset($diff_engine);
0515   
0516          $this->_edits = $this->_diff3($diff_1, $diff_2);
0517      }
0518   
0519      /**
0520      * Return number of conflicts
0521      */
0522      function get_num_conflicts()
0523      {
0524          $conflicts = 0;
0525   
0526          for ($i = 0, $size = count($this->_edits); $i < $size; $i++)
0527          {
0528              $edit = $this->_edits[$i];
0529   
0530              if ($edit->is_conflict())
0531              {
0532                  $conflicts++;
0533              }
0534          }
0535   
0536          return $conflicts;
0537      }
0538   
0539      /**
0540      * Get conflicts content for download. This is generally a merged file, but preserving conflicts and adding explanations to it.
0541      * A user could then go through this file, search for the conflicts and changes the code accordingly.
0542      *
0543      * @param string $label1 the cvs file version/label from the original set of lines
0544      * @param string $label2 the cvs file version/label from the new set of lines
0545      * @param string $label_sep the explanation between label1 and label2 - more of a helper for the user
0546      *
0547      * @return mixed the merged output
0548      */
0549      function get_conflicts_content($label1 = 'CURRENT_FILE', $label2 = 'NEW_FILE', $label_sep = 'DIFF_SEP_EXPLAIN')
0550      {
0551          global $user;
0552   
0553          $label1 = (!empty($user->lang[$label1])) ? $user->lang[$label1] : $label1;
0554          $label2 = (!empty($user->lang[$label2])) ? $user->lang[$label2] : $label2;
0555          $label_sep = (!empty($user->lang[$label_sep])) ? $user->lang[$label_sep] : $label_sep;
0556   
0557          $lines = array();
0558   
0559          for ($i = 0, $size = count($this->_edits); $i < $size; $i++)
0560          {
0561              $edit = $this->_edits[$i];
0562   
0563              if ($edit->is_conflict())
0564              {
0565                  // Start conflict label
0566                  $label_start    = array('<<<<<<< ' . $label1);
0567                  $label_mid        = array('======= ' . $label_sep);
0568                  $label_end        = array('>>>>>>> ' . $label2);
0569   
0570                  $lines = array_merge($lines, $label_start, $edit->final1, $label_mid, $edit->final2, $label_end);
0571                  $this->_conflicting_blocks++;
0572              }
0573              else
0574              {
0575                  $lines = array_merge($lines, $edit->merged());
0576              }
0577          }
0578   
0579          return $lines;
0580      }
0581   
0582      /**
0583      * Return merged output (used by the renderer)
0584      *
0585      * @return mixed the merged output
0586      */
0587      function merged_output()
0588      {
0589          return $this->get_conflicts_content();
0590      }
0591   
0592      /**
0593      * Merge the output and use the new file code for conflicts
0594      */
0595      function merged_new_output()
0596      {
0597          $lines = array();
0598   
0599          for ($i = 0, $size = count($this->_edits); $i < $size; $i++)
0600          {
0601              $edit = $this->_edits[$i];
0602   
0603              if ($edit->is_conflict())
0604              {
0605                  $lines = array_merge($lines, $edit->final2);
0606              }
0607              else
0608              {
0609                  $lines = array_merge($lines, $edit->merged());
0610              }
0611          }
0612   
0613          return $lines;
0614      }
0615   
0616      /**
0617      * Merge the output and use the original file code for conflicts
0618      */
0619      function merged_orig_output()
0620      {
0621          $lines = array();
0622   
0623          for ($i = 0, $size = count($this->_edits); $i < $size; $i++)
0624          {
0625              $edit = $this->_edits[$i];
0626   
0627              if ($edit->is_conflict())
0628              {
0629                  $lines = array_merge($lines, $edit->final1);
0630              }
0631              else
0632              {
0633                  $lines = array_merge($lines, $edit->merged());
0634              }
0635          }
0636   
0637          return $lines;
0638      }
0639   
0640      /**
0641      * Get conflicting block(s)
0642      */
0643      function get_conflicts()
0644      {
0645          $conflicts = array();
0646   
0647          for ($i = 0, $size = count($this->_edits); $i < $size; $i++)
0648          {
0649              $edit = $this->_edits[$i];
0650   
0651              if ($edit->is_conflict())
0652              {
0653                  $conflicts[] = array($edit->final1, $edit->final2);
0654              }
0655          }
0656   
0657          return $conflicts;
0658      }
0659   
0660      /**
0661      * @access private
0662      */
0663      function _diff3(&$edits1, &$edits2)
0664      {
0665          $edits = array();
0666          $bb = new diff3_block_builder();
0667   
0668          $e1 = current($edits1);
0669          $e2 = current($edits2);
0670   
0671          while ($e1 || $e2)
0672          {
0673              if ($e1 && $e2 && is_a($e1, 'diff_op_copy') && is_a($e2, 'diff_op_copy'))
0674              {
0675                  // We have copy blocks from both diffs. This is the (only) time we want to emit a diff3 copy block.
0676                  // Flush current diff3 diff block, if any.
0677                  if ($edit = $bb->finish())
0678                  {
0679                      $edits[] = $edit;
0680                  }
0681   
0682                  $ncopy = min($e1->norig(), $e2->norig());
0683                  $edits[] = new diff3_op_copy(array_slice($e1->orig, 0, $ncopy));
0684   
0685                  if ($e1->norig() > $ncopy)
0686                  {
0687                      array_splice($e1->orig, 0, $ncopy);
0688                      array_splice($e1->final, 0, $ncopy);
0689                  }
0690                  else
0691                  {
0692                      $e1 = next($edits1);
0693                  }
0694   
0695                  if ($e2->norig() > $ncopy)
0696                  {
0697                      array_splice($e2->orig, 0, $ncopy);
0698                      array_splice($e2->final, 0, $ncopy);
0699                  }
0700                  else
0701                  {
0702                      $e2 = next($edits2);
0703                  }
0704              }
0705              else
0706              {
0707                  if ($e1 && $e2)
0708                  {
0709                      if ($e1->orig && $e2->orig)
0710                      {
0711                          $norig = min($e1->norig(), $e2->norig());
0712                          $orig = array_splice($e1->orig, 0, $norig);
0713                          array_splice($e2->orig, 0, $norig);
0714                          $bb->input($orig);
0715                      }
0716                      else
0717                      {
0718                          $norig = 0;
0719                      }
0720   
0721                      if (is_a($e1, 'diff_op_copy'))
0722                      {
0723                          $bb->out1(array_splice($e1->final, 0, $norig));
0724                      }
0725   
0726                      if (is_a($e2, 'diff_op_copy'))
0727                      {
0728                          $bb->out2(array_splice($e2->final, 0, $norig));
0729                      }
0730                  }
0731   
0732                  if ($e1 && ! $e1->orig)
0733                  {
0734                      $bb->out1($e1->final);
0735                      $e1 = next($edits1);
0736                  }
0737   
0738                  if ($e2 && ! $e2->orig)
0739                  {
0740                      $bb->out2($e2->final);
0741                      $e2 = next($edits2);
0742                  }
0743              }
0744          }
0745   
0746          if ($edit = $bb->finish())
0747          {
0748              $edits[] = $edit;
0749          }
0750   
0751          return $edits;
0752      }
0753  }
0754   
0755  /**
0756  * @package diff
0757  * @author  Geoffrey T. Dairiki <dairiki@dairiki.org>
0758  *
0759  * @access private
0760  */
0761  class diff3_op
0762  {
0763      function __construct($orig = false, $final1 = false, $final2 = false)
0764      {
0765          $this->orig = $orig ? $orig : array();
0766          $this->final1 = $final1 ? $final1 : array();
0767          $this->final2 = $final2 ? $final2 : array();
0768      }
0769   
0770      function merged()
0771      {
0772          if (!isset($this->_merged))
0773          {
0774              // Prepare the arrays before we compare them. ;)
0775              $this->solve_prepare();
0776   
0777              if ($this->final1 === $this->final2)
0778              {
0779                  $this->_merged = &$this->final1;
0780              }
0781              else if ($this->final1 === $this->orig)
0782              {
0783                  $this->_merged = &$this->final2;
0784              }
0785              else if ($this->final2 === $this->orig)
0786              {
0787                  $this->_merged = &$this->final1;
0788              }
0789              else
0790              {
0791                  // The following tries to aggressively solve conflicts...
0792                  $this->_merged = false;
0793                  $this->solve_conflict();
0794              }
0795          }
0796   
0797          return $this->_merged;
0798      }
0799   
0800      function is_conflict()
0801      {
0802          return ($this->merged() === false) ? true : false;
0803      }
0804   
0805      /**
0806      * Function to prepare the arrays for comparing - we want to skip over newline changes
0807      * @author acydburn
0808      */
0809      function solve_prepare()
0810      {
0811          // We can simplify one case where the array is usually supposed to be empty...
0812          if (count($this->orig) == 1 && trim($this->orig[0]) === '') $this->orig = array();
0813          if (count($this->final1) == 1 && trim($this->final1[0]) === '') $this->final1 = array();
0814          if (count($this->final2) == 1 && trim($this->final2[0]) === '') $this->final2 = array();
0815   
0816          // Now we only can have the case where the only difference between arrays are newlines, so compare all cases
0817   
0818          // First, some strings we can compare...
0819          $orig = $final1 = $final2 = '';
0820   
0821          foreach ($this->orig as $null => $line) $orig .= trim($line);
0822          foreach ($this->final1 as $null => $line) $final1 .= trim($line);
0823          foreach ($this->final2 as $null => $line) $final2 .= trim($line);
0824   
0825          // final1 === final2
0826          if ($final1 === $final2)
0827          {
0828              // We preserve the part which will be used in the merge later
0829              $this->final2 = $this->final1;
0830          }
0831          // final1 === orig
0832          else if ($final1 === $orig)
0833          {
0834              // Here it does not really matter what we choose, but we will use the new code
0835              $this->orig = $this->final1;
0836          }
0837          // final2 === orig
0838          else if ($final2 === $orig)
0839          {
0840              // Here it does not really matter too (final1 will be used), but we will use the new code
0841              $this->orig = $this->final2;
0842          }
0843      }
0844   
0845      /**
0846      * Find code portions from $orig in $final1 and use $final2 as merged instance if provided
0847      * @author acydburn
0848      */
0849      function _compare_conflict_seq($orig, $final1, $final2 = false)
0850      {
0851          $result = array('merge_found' => false, 'merge' => array());
0852   
0853          $_orig = &$this->$orig;
0854          $_final1 = &$this->$final1;
0855   
0856          // Ok, we basically search for $orig in $final1
0857          $compare_seq = count($_orig);
0858   
0859          // Go through the conflict code
0860          for ($i = 0, $j = 0, $size = count($_final1); $i < $size; $i++, $j = $i)
0861          {
0862              $line = $_final1[$i];
0863              $skip = 0;
0864   
0865              for ($x = 0; $x < $compare_seq; $x++)
0866              {
0867                  // Try to skip all matching lines
0868                  if (trim($line) === trim($_orig[$x]))
0869                  {
0870                      $line = (++$j < $size) ? $_final1[$j] : $line;
0871                      $skip++;
0872                  }
0873              }
0874   
0875              if ($skip === $compare_seq)
0876              {
0877                  $result['merge_found'] = true;
0878   
0879                  if ($final2 !== false)
0880                  {
0881                      $result['merge'] = array_merge($result['merge'], $this->$final2);
0882                  }
0883                  $i += ($skip - 1);
0884              }
0885              else if ($final2 !== false)
0886              {
0887                  $result['merge'][] = $line;
0888              }
0889          }
0890   
0891          return $result;
0892      }
0893   
0894      /**
0895      * Tries to solve conflicts aggressively based on typical "assumptions"
0896      * @author acydburn
0897      */
0898      function solve_conflict()
0899      {
0900          $this->_merged = false;
0901   
0902          // CASE ONE: orig changed into final2, but modified/unknown code in final1.
0903          // IF orig is found "as is" in final1 we replace the code directly in final1 and populate this as final2/merge
0904          if (count($this->orig) && count($this->final2))
0905          {
0906              $result = $this->_compare_conflict_seq('orig', 'final1', 'final2');
0907   
0908              if ($result['merge_found'])
0909              {
0910                  $this->final2 = $result['merge'];
0911                  $this->_merged = &$this->final2;
0912                  return;
0913              }
0914   
0915              $result = $this->_compare_conflict_seq('final2', 'final1');
0916   
0917              if ($result['merge_found'])
0918              {
0919                  $this->_merged = &$this->final1;
0920                  return;
0921              }
0922   
0923              // Try to solve $Id$ issues. ;)
0924              if (count($this->orig) == 1 && count($this->final1) == 1 && count($this->final2) == 1)
0925              {
0926                  $match = '#^' . preg_quote('* @version $Id: ', '#') . '[a-z\._\- ]+[0-9]+ [0-9]{4}-[0-9]{2}-[0-9]{2} [0-9\:Z]+ [a-z0-9_\- ]+\$$#';
0927   
0928                  if (preg_match($match, $this->orig[0]) && preg_match($match, $this->final1[0]) && preg_match($match, $this->final2[0]))
0929                  {
0930                      $this->_merged = &$this->final2;
0931                      return;
0932                  }
0933              }
0934   
0935              $second_run = false;
0936   
0937              // Try to solve issues where the only reason why the above did not work is a newline being removed in the final1 code but exist in the orig/final2 code
0938              if (trim($this->orig[0]) === '' && trim($this->final2[0]) === '')
0939              {
0940                  unset($this->orig[0], $this->final2[0]);
0941                  $this->orig = array_values($this->orig);
0942                  $this->final2 = array_values($this->final2);
0943   
0944                  $second_run = true;
0945              }
0946   
0947              // The same is true for a line at the end. ;)
0948              if (count($this->orig) && count($this->final2) && count($this->orig) === count($this->final2) && trim($this->orig[count($this->orig)-1]) === '' && trim($this->final2[count($this->final2)-1]) === '')
0949              {
0950                  unset($this->orig[count($this->orig)-1], $this->final2[count($this->final2)-1]);
0951                  $this->orig = array_values($this->orig);
0952                  $this->final2 = array_values($this->final2);
0953   
0954                  $second_run = true;
0955              }
0956   
0957              if ($second_run)
0958              {
0959                  $result = $this->_compare_conflict_seq('orig', 'final1', 'final2');
0960   
0961                  if ($result['merge_found'])
0962                  {
0963                      $this->final2 = $result['merge'];
0964                      $this->_merged = &$this->final2;
0965                      return;
0966                  }
0967   
0968                  $result = $this->_compare_conflict_seq('final2', 'final1');
0969   
0970                  if ($result['merge_found'])
0971                  {
0972                      $this->_merged = &$this->final1;
0973                      return;
0974                  }
0975              }
0976   
0977              return;
0978          }
0979   
0980          // CASE TWO: Added lines from orig to final2 but final1 had added lines too. Just merge them.
0981          if (!count($this->orig) && $this->final1 !== $this->final2 && count($this->final1) && count($this->final2))
0982          {
0983              $result = $this->_compare_conflict_seq('final2', 'final1');
0984   
0985              if ($result['merge_found'])
0986              {
0987                  $this->final2 = $this->final1;
0988                  $this->_merged = &$this->final1;
0989              }
0990              else
0991              {
0992                  $result = $this->_compare_conflict_seq('final1', 'final2');
0993   
0994                  if (!$result['merge_found'])
0995                  {
0996                      $this->final2 = array_merge($this->final1, $this->final2);
0997                      $this->_merged = &$this->final2;
0998                  }
0999                  else
1000                  {
1001                      $this->final2 = $this->final1;
1002                      $this->_merged = &$this->final1;
1003                  }
1004              }
1005   
1006              return;
1007          }
1008   
1009          // CASE THREE: Removed lines (orig has the to-remove line(s), but final1 has additional lines which does not need to be removed). Just remove orig from final1 and then use final1 as final2/merge
1010          if (!count($this->final2) && count($this->orig) && count($this->final1) && $this->orig !== $this->final1)
1011          {
1012              $result = $this->_compare_conflict_seq('orig', 'final1');
1013   
1014              if (!$result['merge_found'])
1015              {
1016                  return;
1017              }
1018   
1019              // First of all, try to find the code in orig in final1. ;)
1020              $compare_seq = count($this->orig);
1021              $begin = $end = -1;
1022              $j = 0;
1023   
1024              for ($i = 0, $size = count($this->final1); $i < $size; $i++)
1025              {
1026                  $line = $this->final1[$i];
1027   
1028                  if (trim($line) === trim($this->orig[$j]))
1029                  {
1030                      // Mark begin
1031                      if ($begin === -1)
1032                      {
1033                          $begin = $i;
1034                      }
1035   
1036                      // End is always $i, the last found line
1037                      $end = $i;
1038   
1039                      if (isset($this->orig[$j+1]))
1040                      {
1041                          $j++;
1042                      }
1043                  }
1044              }
1045   
1046              if ($begin !== -1 && $begin + ($compare_seq - 1) == $end)
1047              {
1048                  foreach ($this->final1 as $i => $line)
1049                  {
1050                      if ($i < $begin || $i > $end)
1051                      {
1052                          $merged[] = $line;
1053                      }
1054                  }
1055   
1056                  $this->final2 = $merged;
1057                  $this->_merged = &$this->final2;
1058              }
1059   
1060              return;
1061          }
1062   
1063          return;
1064      }
1065  }
1066   
1067  /**
1068  * @package diff
1069  * @author  Geoffrey T. Dairiki <dairiki@dairiki.org>
1070  *
1071  * @access private
1072  */
1073  class diff3_op_copy extends diff3_op
1074  {
1075      function __construct($lines = false)
1076      {
1077          $this->orig = $lines ? $lines : array();
1078          $this->final1 = &$this->orig;
1079          $this->final2 = &$this->orig;
1080      }
1081   
1082      function merged()
1083      {
1084          return $this->orig;
1085      }
1086   
1087      function is_conflict()
1088      {
1089          return false;
1090      }
1091  }
1092   
1093  /**
1094  * @package diff
1095  * @author  Geoffrey T. Dairiki <dairiki@dairiki.org>
1096  *
1097  * @access private
1098  */
1099  class diff3_block_builder
1100  {
1101      function __construct()
1102      {
1103          $this->_init();
1104      }
1105   
1106      function input($lines)
1107      {
1108          if ($lines)
1109          {
1110              $this->_append($this->orig, $lines);
1111          }
1112      }
1113   
1114      function out1($lines)
1115      {
1116          if ($lines)
1117          {
1118              $this->_append($this->final1, $lines);
1119          }
1120      }
1121   
1122      function out2($lines)
1123      {
1124          if ($lines)
1125          {
1126              $this->_append($this->final2, $lines);
1127          }
1128      }
1129   
1130      function is_empty()
1131      {
1132          return !$this->orig && !$this->final1 && !$this->final2;
1133      }
1134   
1135      function finish()
1136      {
1137          if ($this->is_empty())
1138          {
1139              return false;
1140          }
1141          else
1142          {
1143              $edit = new diff3_op($this->orig, $this->final1, $this->final2);
1144              $this->_init();
1145              return $edit;
1146          }
1147      }
1148   
1149      function _init()
1150      {
1151          $this->orig = $this->final1 = $this->final2 = array();
1152      }
1153   
1154      function _append(&$array, $lines)
1155      {
1156          array_splice($array, count($array), 0, $lines);
1157      }
1158  }
1159