Skip to content

Commit

Permalink
Renamed to viewdiff-ex.php
Browse files Browse the repository at this point in the history
  • Loading branch information
gorhill committed Feb 26, 2011
1 parent ff57ce1 commit 84162ab
Showing 1 changed file with 2 additions and 288 deletions.
290 changes: 2 additions & 288 deletions index.php
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,290 +1,4 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<style type="text/css">
body {
margin: 0;
border: 0;
padding: 0;
font-size: 11pt;
}
body > h1 {
margin:0 0 0.5em 0;
font: 2em sans-serif;
background-color: #def
}
body > div {
padding:2px;
}
p {
margin-top: 0;
}
ins {
color: green;
background: #dfd;
text-decoration: none;
}
del {
color: red;
background: #fdd;
text-decoration: none;
}
code {
font-size: smaller;
}
#params {
margin: 1em 0;
font: 14px sans-serif;
}
.code {
margin-left: 2em;
font: 12px monospace;
}
.ins {
background:#dfd;
}
.del {
background:#fdd;
}
.rep {
color: #008;
background: #eef;
}
.panecontainer {
display: inline-block;
width: 49.5%;
vertical-align: top;
}
.panecontainer > p {
margin: 0;
border: 1px solid #bcd;
border-bottom: none;
padding: 1px 3px;
background: #def;
font: 14px sans-serif
}
.panecontainer > p + div {
margin: 0;
padding: 2px 0 2px 2px;
border: 1px solid #bcd;
border-top: none;
}
.pane {
margin: 0;
padding: 0;
border: 0;
width: 100%;
min-height: 20em;
overflow:auto;
font: 12px monospace;
}
</style>
<title>PHP Fine Diff</title>
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-5586753-2']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script');
ga.type = 'text/javascript';
ga.async = true;
ga.src = 'http://www.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(ga, s);
})();
</script>
</head>
<body>
<h1>PHP Fine Diff</h1>
<div>
<p>This page demonstrate the <a href="finediff-code.php"><code>FineDiff</code></a> class (as in &ldquo;<b>fine</b> granularity <b>diff</b>&rdquo;) I wrote &ndash; starting from scratch &ndash; to generate a <u>lossless</u> (won't eat your line breaks), <u>compact</u> opcodes string listing the sequence of atomic actions (copy/delete/insert) necessary to transform one string into another (thereafter referred as the &ldquo;From&rdquo; and &ldquo;To&rdquo; string). The &ldquo;To&rdquo; string can be rebuilt by running the opcodes string on the &ldquo;From&rdquo; string. The <code>FineDiff</code> class allows to specify the granularity, and up to character-level granularity is possible, in order to generate the smallest diff possible (at the <i>potential</i> cost of increased CPU cycles.)</p>
<p>Typical usage:</p>
<p class="code">
include '<a href="finediff-code.php">finediff.php</a>';<br>
$opcodes = FineDiff::getDiffOpcodes($from_text, $to_text /* , default granularity is set to character */);<br>
// store opcodes for later use...</p>
<p>Later, <code>$to_text</code> can be re-created from <code>$from_text</code> using <code>$opcodes</code> as follow:</p>
<p class="code">
include '<a href="finediff-code.php">finediff.php</a>';<br>
$to_text = FineDiff::renderToTextFromOpcodes($from_text, $opcodes);
</p>
<p>Try it by inserting your own text, or <a href="index.php?sample=1">Use sample text</a>, or <a href="index.php">Start from scratch</a>, or just use the plain <a href="viewdiff.php">Online diff viewer</a>:</p>
<?php
// http://www.php.net/manual/en/function.get-magic-quotes-gpc.php#82524
function stripslashes_deep(&$value) {
$value = is_array($value) ? array_map('stripslashes_deep', $value) : stripslashes($value);
return $value;
}
if ( (function_exists("get_magic_quotes_gpc") && get_magic_quotes_gpc()) || (ini_get('magic_quotes_sybase') && strtolower(ini_get('magic_quotes_sybase'))!="off") ) {
stripslashes_deep($_GET);
stripslashes_deep($_POST);
stripslashes_deep($_REQUEST);
}

require_once 'finediff.php';

$from = '';
$to = '';
$granularity = 2;
if ( isset($_REQUEST['granularity']) && ctype_digit($_REQUEST['granularity']) ) {
$granularity = max(min(intval($_REQUEST['granularity']),3),0);
}
$rendered_diff = '';
if ( !empty($_REQUEST['from']) || !empty($_REQUEST['to'])) {
if ( !empty($_REQUEST['from']) ) {
$from = $_REQUEST['from'];
}
if ( !empty($_REQUEST['to']) ) {
$to = $_REQUEST['to'];
}
}
else if ( !empty($_REQUEST['sample']) ) { // use sample text?
// Sample text:
// http://en.wikipedia.org/w/index.php?title=Universe&action=historysubmit&diff=414830579&oldid=378547111
$from = file_get_contents('sample_from.txt');
$to = file_get_contents('sample_to.txt');
}
$from_len = strlen($from);
$to_len = strlen($to);
$use_stdlib_diff = !empty($_REQUEST['stdlib']) && ctype_digit($_REQUEST['stdlib']) && intval($_REQUEST['stdlib']) === 1;

require_once 'Text/Diff.php';

$start_time = gettimeofday(true);
if ( $use_stdlib_diff ) {
if ( $granularity < 3 ) {
$delimiters = array(
FineDiff::paragraphDelimiters,
FineDiff::sentenceDelimiters,
FineDiff::wordDelimiters,
FineDiff::characterDelimiters
);
function extractFragments($text, $delimiter) {
$text = str_replace(array("\n","\r"), array("\1","\2"), $text);
$delimiter = str_replace(array("\n","\r"), array("\1","\2"), $delimiter);
if ( empty($delimiter) ) {
return str_split($text, 1);
}
$fragments = array();
$start = $end = 0;
for (;;) {
$end += strcspn($text, $delimiter, $end);
$end += strspn($text, $delimiter, $end);
if ( $end === $start ) {
break;
}
$fragments[] = substr($text, $start, $end - $start);
$start = $end;
}
return $fragments;
}
$from_fragments = extractFragments($from, $delimiters[$granularity]);
$to_fragments = extractFragments($to, $delimiters[$granularity]);
$diff = new Text_Diff('native', array($from_fragments, $to_fragments));
$exec_time = sprintf('%.3f sec', gettimeofday(true) - $start_time);
$edits = array();
ob_start();
foreach ( $diff->getDiff() as $edit ) {
if ( $edit instanceof Text_Diff_Op_copy ) {
$orig = str_replace(array("\1","\2"), array("\n","\r"), implode('', $edit->orig));
$edits[] = new fineDiffCopyOp(strlen($orig));
echo htmlentities($orig);
}
else if ( $edit instanceof Text_Diff_Op_delete ) {
$orig = str_replace(array("\1","\2"), array("\n","\r"), implode('', $edit->orig));
$edits[] = new fineDiffDeleteOp(strlen($orig));
echo '<del>', htmlentities($orig), '</del>';
}
else if ( $edit instanceof Text_Diff_Op_add ) {
$final = str_replace(array("\1","\2"), array("\n","\r"), implode('', $edit->final));
$edits[] = new fineDiffInsertOp($final);
echo '<ins>', htmlentities($final), '</ins>';
}
else if ( $edit instanceof Text_Diff_Op_change ) {
$orig = str_replace(array("\1","\2"), array("\n","\r"), implode('', $edit->orig));
$final = str_replace(array("\1","\2"), array("\n","\r"), implode('', $edit->final));
$edits[] = new fineDiffReplaceOp(strlen($orig), $final);
echo '<del>', htmlentities($orig), '</del>';
echo '<ins>', htmlentities($final), '</ins>';
}
}
$rendered_diff = ob_get_clean();
$rendering_time = sprintf('%.3f sec', gettimeofday(true) - $start_time);
}
// character-level granularity not allowed
else {
$edits = false;
$exec_time = '?';
$rendering_time = '?';
$rendered_diff = '<span style="color:gray">Character-level granularity not allowed when using <code>Text_Diff</code>, due to performance issues.</span>';
}
}
else {
$granularityStacks = array(
FineDiff::$paragraphGranularity,
FineDiff::$sentenceGranularity,
FineDiff::$wordGranularity,
FineDiff::$characterGranularity
);

$diff = new FineDiff($from, $to, $granularityStacks[$granularity]);
$edits = $diff->getOps();
$exec_time = sprintf('%.3f sec', gettimeofday(true) - $start_time);
$rendered_diff = $diff->renderDiffToHTML();
$rendering_time = sprintf('%.3f sec', gettimeofday(true) - $start_time);
}

if ( $edits !== false ) {
$opcodes = array();
$opcodes_len = 0;
foreach ( $edits as $edit ) {
$opcode = $edit->getOpcode();
$opcodes_len += strlen($opcode);
$opcode = htmlentities($opcode);
if ( $edit instanceof FineDiffCopyOp ) {
$opcodes[] = "{$opcode}";
}
else if ( $edit instanceof FineDiffDeleteOp ) {
$opcodes[] = "<span class=\"del\">{$opcode}</span>";
}
else if ( $edit instanceof FineDiffInsertOp ) {
$opcodes[] = "<span class=\"ins\">{$opcode}</span>";
}
else /* if ( $edit instanceof FineDiffReplaceOp ) */ {
$opcodes[] = "<span class=\"rep\">{$opcode}</span>";
}
}
$opcodes = implode("", $opcodes);
$opcodes_len = sprintf('%d bytes (%.1f %% of &quot;To&quot;)', $opcodes_len, $to_len ? $opcodes_len * 100 / $to_len : 0);
}
else {
$opcodes = '?';
$opcodes_len = '?';
}
header("Location: viewdiff-ex.php");
exit;
?>
<form action="index.php" method="post">
<div class="panecontainer"><p>From:</p><div><textarea name="from" class="pane"><?php echo htmlentities($from); ?></textarea></div></div>
<div class="panecontainer"><p>To:</p><div><textarea name="to" class="pane"><?php echo htmlentities($to); ?></textarea></div></div>
<p id="params">Granularity:<input name="granularity" type="radio" value="0"<?php if ( $granularity === 0 ) { echo ' checked="checked"'; } ?>>&thinsp;Paragraph/lines&ensp;<input name="granularity" type="radio" value="1"<?php if ( $granularity === 1 ) { echo ' checked="checked"'; } ?>>&thinsp;Sentence&ensp;<input name="granularity" type="radio" value="2"<?php if ( $granularity === 2 ) { echo ' checked="checked"'; } ?>>&thinsp;Word&ensp;<input name="granularity" type="radio" value="3"<?php if ( $granularity === 3 ) { echo ' checked="checked"'; } ?>>&thinsp;Character&emsp;<!-- <input name="XDEBUG_PROFILE" type="hidden" value=""> --><input type="submit" value="Compute diff">&emsp;<input name="stdlib" type="checkbox" value="1"<?php if ( $use_stdlib_diff ) { echo ' checked="checked"'; } ?>><a href="http://pear.php.net/package/Text_Diff/"><code>Text_Diff</code></a> lib (for comparison purpose) <sup style="font-size:x-small"><a href="#notes">see notes</a></sup></p>
</form>
<div class="panecontainer"><p>Diff stats:</p><div><div class="pane">
<b>Diff execution time:</b> <?php echo $exec_time; ?><br>
<b>Diff execution + rendering time:</b> <?php echo $rendering_time; ?><br>
<b>&quot;From&quot; size:</b> <?php echo $from_len; ?> bytes<br>
<b>&quot;To&quot; size:</b> <?php echo $to_len; ?> bytes<br>
<b>Diff opcodes size:</b> <?php echo $opcodes_len; ?><br>
<b>Diff opcodes (<span style="border:1px solid #ccc;display:inline-block;width:16px">&nbsp;</span>=copy, <span class="del" style="display:inline-block;width:16px">&nbsp;</span>=delete, <span class="ins" style="display:inline-block;width:16px">&nbsp;</span>=insert, <span class="rep" style="display:inline-block;width:16px">&nbsp;</span>=replace):</b>
<div style="margin:2px 0 2px 0;border:0;border-top:1px dotted #aaa;padding-top:4px;word-wrap:break-word"><?php echo $opcodes; ?></div>
</div></div></div>
<div class="panecontainer"><p>Rendered Diff:</p><div><div class="pane" style="white-space:pre-line"><?php
echo $rendered_diff; ?></div></div>
</div>
</div>
<div style="margin:0.5em 0;border-top:1px solid #ccc;height:2px"></div>
<div id="notes" style="font:11px sans-serif"><h3 style="margin-top:0">Notes</h3>
<p>The PHP-based engine of <code><a href="http://pear.php.net/package/Text_Diff/">Text_Diff</a></code> is forced, in order to meaningfully compare results with PHP-based <code>FineDiff</code>. <code>Text_Diff</code> is naturally geared toward line-level granularity, and to compute diff for a higher granularity (sequences, words, characters), line break characters (\n, \r) are replaced in order to avoid having <code>Text_Diff</code> from eating our line breaks &mdash; so extra steps are required.</p>
<p><code>FineDiff</code> is natively better equipped to generate diff at granularity higher than line levels. An example of this is that using the above built-in sample text, for word and character-level granularity, <code>FineDiff</code> roughly executes in 25 ms and 30 ms, respectively, while <code>Text_Diff</code> roughly executes in 75 ms and 6.5 seconds, respectively (on my development computer, a run of the mill Intel i5 core desktop computer).</p>
<p>If you wish to comment on this page, head to the associated blog entry: <a href="http://www.raymondhill.net/blog/?p=441">FineDiff, a character-level diff algorithm in PHP</a></p>
</div>
</body>
</html>

0 comments on commit 84162ab

Please sign in to comment.