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. |
|
|
(Beispiel Datei-Icons)
|
Auf das Icon klicken um den Quellcode anzuzeigen |
diff.php
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