7 * LICENSE: This source file is licensed under the terms of the GNU General Public License.
9 * @package Magic3 Framework
10 * @author 平田直毅(Naoki Hirata) <naoki@aplo.co.jp>
11 * @copyright Copyright 2006-2010 Magic3 Project.
12 * @license http://www.gnu.org/copyleft/gpl.html GPL License
13 * @version SVN: $Id: diff.php 3474 2010-08-13 10:36:48Z fishbone $
14 * @link http://www.magic3.org
17 // 2003-2005 PukiWiki Developers Team
18 // 2001-2002 Originally written by yu-ji
19 // License: GPL v2 or (at your option) any later version
22 // Show more information when it conflicts
23 define('PKWK_DIFF_SHOW_CONFLICT_DETAIL', 1);
25 // Create diff-style data between arrays
26 function do_diff($strlines1, $strlines2)
28 $obj = new line_diff();
29 $str = $obj->str_compare($strlines1, $strlines2);
33 // Visualize diff-style-text to text-with-CSS
34 // '+Added' => '<span added>Added</span>'
35 // '-Removed' => '<span removed>Removed</span>'
36 // ' Nothing' => 'Nothing'
37 function diff_style_to_css($str = '')
39 // Cut diff markers ('+' or '-' or ' ')
40 $str = preg_replace('/^\-(.*)$/m', '<span class="diff_removed">$1</span>', $str);
41 $str = preg_replace('/^\+(.*)$/m', '<span class="diff_added" >$1</span>', $str);
42 return preg_replace('/^ (.*)$/m', '$1', $str);
45 // Merge helper (when it conflicts)
46 function do_update_diff($pagestr, $poststr, $original)
48 $obj = new line_diff();
50 $obj->set_str('left', $original, $pagestr);
52 $diff1 = $obj->toArray();
54 $obj->set_str('right', $original, $poststr);
56 $diff2 = $obj->toArray();
58 $arr = $obj->arr_compare('all', $diff1, $diff2);
60 if (PKWK_DIFF_SHOW_CONFLICT_DETAIL) {
61 global $do_update_diff_table;
63 $do_update_diff_table = <<<EOD
64 <p>l : between backup data and stored page data.<br />
65 r : between backup data and your post data.</p>
66 <table class="style_table">
73 $tags = array('th', 'th', 'td');
74 foreach ($arr as $_obj) {
75 $do_update_diff_table .= '<tr>';
76 $params = array($_obj->get('left'), $_obj->get('right'), $_obj->text());
77 foreach ($params as $key=>$text) {
78 $text = htmlspecialchars($text);
79 if (trim($text) == '') $text = ' ';
80 $do_update_diff_table .= '<' . $tags[$key] .
81 ' class="style_' . $tags[$key] . '">' . $text .
82 '</' . $tags[$key] . '>';
84 $do_update_diff_table .= '</tr>' . "\n";
86 $do_update_diff_table .= '</table>' . "\n";
90 foreach ($arr as $_obj) {
91 if ($_obj->get('left') != '-' && $_obj->get('right') != '-')
92 $body .= $_obj->text();
97 return array(rtrim($body) . "\n", $auto);
101 // References of this class:
102 // S. Wu, <A HREF="http://www.cs.arizona.edu/people/gene/vita.html">
103 // E. Myers,</A> U. Manber, and W. Miller,
104 // <A HREF="http://www.cs.arizona.edu/people/gene/PAPERS/np_diff.ps">
105 // "An O(NP) Sequence Comparison Algorithm,"</A>
106 // Information Processing Letters 35, 6 (1990), 317-323.
109 var $arr1, $arr2, $m, $n, $pos, $key, $plus, $minus, $equal, $reverse;
111 function line_diff($plus = '+', $minus = '-', $equal = ' ')
114 $this->minus = $minus;
115 $this->equal = $equal;
118 function arr_compare($key, $arr1, $arr2)
124 $arr = $this->toArray();
128 function set_str($key, $str1, $str2)
131 $this->arr1 = array();
132 $this->arr2 = array();
133 $str1 = str_replace("\r", '', $str1);
134 $str2 = str_replace("\r", '', $str2);
135 foreach (explode("\n", $str1) as $line) {
136 $this->arr1[] = new DiffLine($line);
138 foreach (explode("\n", $str2) as $line) {
139 $this->arr2[] = new DiffLine($line);
143 function str_compare($str1, $str2)
145 $this->set_str('diff', $str1, $str2);
149 foreach ($this->toArray() as $obj) {
150 $str .= $obj->get('diff') . $obj->text();
157 $this->m = count($this->arr1);
158 $this->n = count($this->arr2);
160 if ($this->m == 0 || $this->n == 0) { // No need to compare
161 $this->result = array(array('x'=>0, 'y'=>0));
166 array_unshift($this->arr1, new DiffLine(''));
168 array_unshift($this->arr2, new DiffLine(''));
171 $this->reverse = ($this->n < $this->m);
172 if ($this->reverse) {
174 $tmp = $this->m; $this->m = $this->n; $this->n = $tmp;
175 $tmp = $this->arr1; $this->arr1 = $this->arr2; $this->arr2 = $tmp;
179 $delta = $this->n - $this->m; // Must be >=0;
182 $this->path = array();
184 for ($p = -($this->m + 1); $p <= ($this->n + 1); $p++) {
186 $this->path[$p] = array();
189 for ($p = 0;; $p++) {
190 for ($k = -$p; $k <= $delta - 1; $k++) {
191 $fp[$k] = $this->snake($k, $fp[$k - 1], $fp[$k + 1]);
193 for ($k = $delta + $p; $k >= $delta + 1; $k--) {
194 $fp[$k] = $this->snake($k, $fp[$k - 1], $fp[$k + 1]);
196 $fp[$delta] = $this->snake($delta, $fp[$delta - 1], $fp[$delta + 1]);
197 if ($fp[$delta] >= $this->n) {
198 $this->pos = $this->path[$delta]; // 経路を決定
204 function snake($k, $y1, $y2)
213 $this->path[$k] = $this->path[$_k];// ここまでの経路をコピー
215 while ((($x + 1) < $this->m) && (($y + 1) < $this->n)
216 and $this->arr1[$x + 1]->compare($this->arr2[$y + 1]))
219 $this->path[$k][] = array('x'=>$x, 'y'=>$y); // 経路を追加
227 if ($this->reverse) { // 姑息な…
228 $_x = 'y'; $_y = 'x'; $_m = $this->n; $arr1 = $this->arr2; $arr2 = $this->arr1;
230 $_x = 'x'; $_y = 'y'; $_m = $this->m; $arr1 = $this->arr1; $arr2 = $this->arr2;
234 $this->add_count = $this->delete_count = 0;
235 $this->pos[] = array('x'=>$this->m, 'y'=>$this->n); // Sentinel
236 foreach ($this->pos as $pos) {
237 $this->delete_count += ($pos[$_x] - $x);
238 $this->add_count += ($pos[$_y] - $y);
240 while ($pos[$_x] > $x) {
241 $arr1[$x]->set($this->key, $this->minus);
242 $arr[] = $arr1[$x++];
245 while ($pos[$_y] > $y) {
246 $arr2[$y]->set($this->key, $this->plus);
247 $arr[] = $arr2[$y++];
251 $arr1[$x]->merge($arr2[$y]);
252 $arr1[$x]->set($this->key, $this->equal);
266 function DiffLine($text)
268 $this->text = $text . "\n";
269 $this->status = array();
272 function compare($obj)
274 return $this->text == $obj->text;
277 function set($key, $status)
279 $this->status[$key] = $status;
284 return isset($this->status[$key]) ? $this->status[$key] : '';
289 $this->status += $obj->status;