Server : Apache System : Linux indy02.toastserver.com 3.10.0-962.3.2.lve1.5.85.el7.x86_64 #1 SMP Thu Apr 18 15:18:36 UTC 2024 x86_64 User : palandch ( 1163) PHP Version : 7.1.33 Disable Function : NONE Directory : /home/palandch/www/core/xpdo/revision/ |
<?php /** * The xPDORevisionControl class provides utilities for content versioning. * * @package xpdo * @subpackage revision */ /** * Utility class for creating, merging, and managing diffs for versioning. * * BASED ON: * Implementation of a GNU diff alike function from scratch. * Copyright (C) 2003 Nils Knappmeier <nk@knappi.org> * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * @package xpdo * @subpackage revision * */ class xPDORevisionControl { /** * Computes the difference between two string linewise. * The output is the same format * as the GNU diff command without any parameters. * Note: Since GNU diff starts counting with 1, and arrays start counting with * 0, you have to subtract one from each line number to get the real one. */ function diff($text1, $text2) { $array1= $this->_split($text1); $array2= $this->_split($text2); /* Build up array with the line as key and a list of line numbers as value */ foreach ($array1 as $nr => $line) { $r_array1[$line][]= $nr; } foreach ($array2 as $nr => $line) { $r_array2[$line][]= $nr; } $result= ""; $a[1]= 0; /* counter for array1 */ $a[2]= 0; /* counter for array2 */ $actions= array (); while ($a[1] < sizeof($array1) && $a[2] < sizeof($array2)) { # print "$a[1]:$a[2]\n"; if ($array1[$a[1]] == $array2[$a[2]]) { $a[1]++; $a[2]++; $actions[]= 'copy'; } else { /* * $tmp1/2 is the next line in $array1/2 that equals * $array2/1[$a2/1+1]; */ $best[1]= count($array1); $best[2]= count($array2); $scan= $a; # echo "distBest = ".dist($a, $best)."\n"; # echo "distScan = ".dist($a, $scan)."\n"; while ($this->dist($a, $scan) < $this->dist($a, $best)) { # echo "A $a[1]:$a[2]\t"; # echo "Scan $scan[1]:$scan[2] "; $tmp[1]= $this->nextOccurence($array2[$scan[2]], $r_array1, $scan[1]); $tmp[2]= $scan[2]; if ($tmp[1] && $this->dist($a, $tmp) < $this->dist($a, $best)) $best= $tmp; # echo "Tmp1 $tmp[1]:$tmp[2]\t";; $tmp[1]= $scan[1]; $tmp[2]= $this->nextOccurence($array1[$scan[1]], $r_array2, $scan[2]); if ($tmp[2] && $this->dist($a, $tmp) < $this->dist($a, $best)) $best= $tmp; # echo "Tmp2 $tmp[1]:$tmp[2]\t"; # echo "Best $best[1]:$best[2]\n"; $scan[1]++; $scan[2]++; } for ($i= $a[1]; $i < $best[1]; $i++) { $actions[]= 'del'; } for ($i= $a[2]; $i < $best[2]; $i++) { $actions[]= 'add'; } $a= $best; } } # Here, we may still not be at the bottom left corner. # So we have to get there. (This case happens, when we just append something to the page) for ($i= $a[1]; $i < sizeof($array1); $i++) { $actions[]= 'del'; } for ($i= $a[2]; $i < sizeof($array2); $i++) { $actions[]= 'add'; } $actions[]= 'finish'; /* Now follow the way back and report connected (unequal) pieces */ $x= $xold= 0; $y= $yold= 0; $realAction= ""; /* the current action */ foreach ($actions as $action) { if ($action == 'del') { if ($realAction == "" || $realAction == "d") { $realAction= "d"; } else { $realAction= "c"; } $x++; } if ($action == 'add') { if ($realAction == "" || $realAction == "a") { $realAction= "a"; } else { $realAction= "c"; } $y++; } if ($action == 'copy' || $action == 'finish') { /* Prepare header for diff entry */ if ($xold +1 == $x) { $xstr= $x; } else { $xstr= ($xold +1) . ",$x"; } if ($yold +1 == $y) { $ystr= $y; } else { $ystr= ($yold +1) . ",$y"; } /* "Print" entry to result */ if ($realAction == "a") { $result .= ($x) . "a$ystr\n"; for ($i= $yold; $i < $y; $i++) { $result .= "> " . $array2[$i]; } } else if ($realAction == "d") { $result .= ($xstr) . "d" . ($y) . "\n"; for ($i= $xold; $i < $x; $i++) { $result .= "< " . $array1[$i]; } } else if ($realAction == "c") { $result .= "$xstr$realAction$ystr\n"; for ($i= $xold; $i < $x; $i++) { $result .= "< " . $array1[$i]; } $result .= "---\n"; for ($i= $yold; $i < $y; $i++) { $result .= "> " . $array2[$i]; } } $x++; $y++; $realAction= ""; $xold= $x; $yold= $y; } } return $result; } function cut_head(& $str, $key, $prefix) { if (strpos($str, $prefix) === 0) { $str= substr($str, strlen($prefix)); } else { print "Something is wrong in the patch: "; print "'$str' should begin with '$prefix'\n"; exit; } } function restore($revisions= array (), $restore= 0) { reset($revisions); $restored= current($revisions); if ($restore && next($revisions)) { while (list($k, $v)= each($revisions)) { if ($k > $restore) break; $restored= $this->patch($restored, $v); } } return $restored; } function patch($text, $patch) { $array= $this->_split($text); /* Modify patch to an array so that it * is compatible to the modification */ if ($patch == "") return $text; if (substr($patch, -1) == "\n") $patch= substr($patch, 0, strlen($patch) - 1); $patch_array= explode("\n", $patch); for ($i= 0; $i < count($patch_array); $i++) { $patch_array[$i]= $patch_array[$i] . "\n"; } $i= 0; $nlIndex= array_search("\\ No newline at end of file\n", $patch_array); while ($nlIndex != false && $i < 2) { /* This shouldn't be happening more than two times in a valid patch */ $newEntry= $patch_array[$nlIndex -1] . $patch_array[$nlIndex]; array_splice($patch_array, $nlIndex -1, 2, $newEntry); $nlIndex= array_search("\\ No newline at end of file\n", $patch_array); $i++; } /* Start computing */ $current= 0; do { if (preg_match("/^([\d,]+)([adc])([\d,]+)$/", $patch_array[$current], $matches) == 0) { print "<pre>Error in line $current: " . $patch_array[$current] . " not a command\n" . sizeof($patch_array); print "</pre>"; exit; } list ($full, $left, $action, $right)= $matches; /* Compute start and end of each side */ list ($left_start, $left_end)= explode(",", $left); list ($right_start, $right_end)= explode(",", $right); if ($left_end == "") { $left_end= $left_start; } if ($right_end == "") { $right_end= $right_start; } /* Perform action and switch to next patch */ if ($action == "a") { $replace= array_slice($patch_array, $current +1, $right_end - $right_start +1); array_walk($replace, array ($this, 'cut_head'), '> '); array_splice($array, $right_start -1, 0, $replace); $current += $right_end - $right_start +2; } else if ($action == "d") { /* Check whether lines in patch are like in file */ $should= array_slice($patch_array, $current +1, $left_end - $left_start +1); array_walk($should, array ($this, 'cut_head'), '< '); $is= array_splice($array, $right_start, $left_end - $left_start +1); if ($should !== $is) { print "<pre>According to the patch, in lines $left_start to "; print "$left_end there should be a\n"; print urlencode(implode("", $should)) . "\n"; print "but I only find a\n"; print urlencode(implode("", $is)) . "\n</pre>"; } $current += $left_end - $left_start +2; } else if ($action == "c") { $replace= array_slice($patch_array, $current +1 + $left_end - $left_start +2, $right_end - $right_start +1); array_walk($replace, array ($this, 'cut_head'), '> '); $is= array_splice($array, $right_start -1, $left_end - $left_start +1, $replace); /* Check whether lines in patch are like in text */ $should= array_slice($patch_array, $current +1, $left_end - $left_start +1); array_walk($should, array ($this, 'cut_head'), '< '); if ($should !== $is) { print "<pre>According to the patch, in lines $left_start to"; print "$left_end there should be a\n"; print implode("", $should); print "but I only find a\n"; print implode("", $is) . "</pre>"; } $current += 1 + $left_end - $left_start +1 + 1 + $right_end - $right_start +1; } } while ($current < count($patch_array)); $result= implode("", $array); $suffix= "\n\\ No newline at end of file\n"; if (substr($result, -strlen($suffix)) == $suffix) { $result= substr($result, 0, strlen($result) - strlen($suffix)); } return $result; } /** * Checks, if there is a line number-entry in $r_array for $line, * that is behind $where. * $where will be assigned the line-number, if true */ function nextOccurence($line, & $r_array, $where) { $tmp= $r_array[$line]; if (!$tmp) return false; foreach ($tmp as $nr) { if ($where <= $nr) { $where= $nr; return $nr; } } return false; } /** * Compute the manhatten distance of two points */ function dist($a, $b) { $d1= $b[1] - $a[1]; $d2= $b[2] - $a[2]; return $d2 + $d1; } function _split($text) { $array= explode("\n", $text); for ($i= 0; $i < count($array); $i++) { $array[$i]= $array[$i] . "\n"; } if ($array[count($array) - 1] == "\n") { array_pop($array); } else { $array[count($array) - 1]= $array[count($array) - 1] . "\\ No newline at end of file\n"; } return $array; } }