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

diff.php

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