Find and format difference between two strings in PHP
This function takes two strings and formats them in a "diff" format that is familiar to most developers. This function can bu used to compare lines of files, or properties of objects. But the root is this function:
function get_decorated_diff($old, $new){
$from_start = strspn($old ^ $new, "\0");
$from_end = strspn(strrev($old) ^ strrev($new), "\0");
$old_end = strlen($old) - $from_end;
$new_end = strlen($new) - $from_end;
$start = substr($new, 0, $from_start);
$end = substr($new, $new_end);
$new_diff = substr($new, $from_start, $new_end - $from_start);
$old_diff = substr($old, $from_start, $old_end - $from_start);
$new = "$start<ins style='background-color:#ccffcc'>$new_diff</ins>$end";
$old = "$start<del style='background-color:#ffcccc'>$old_diff</del>$end";
return array("old"=>$old, "new"=>$new);
}
And use is this way:
$string_old = "The quick brown fox jumped over the lazy dog";
$string_new = "The quick white rabbit jumped over the lazy dog";
$diff = get_decorated_diff($string_old, $string_new);
echo "<table>
<tr>
<td>".$diff['old']."</td>
<td>".$diff['new']."</td>
</tr>
</table>";
Which will result in this output:
Written by Grant Pierce
Related protips
7 Responses
Not working if there are more than one different thing.
Eg: compare "I used to be pretty" and "I am pretty."
Notice the [dot] at the end. Everything after "I" will be marked as different.
This is not a real problem, as the code is short ... but you might wan't to split lines, and apply the function to each one, but you would have a diff per line (so, if a line is added, everything will look like edited ...)
That's true, very good point. This can be "good enough" in a lot of cases, but if you need a more fine-tuned diff, the code will have to be modified to fit your needs.
can anyone explain this code ..?? I mean it is for finding out the differences between two strings..??? If yes please explain the logic for that.
Sure, I'll explain.
I was working on a project where users could change their profile data, but the changes were emailed to administrators for approval.
This code highlights the changes so the administrators can easily see what was changed. Much like a "diff" view in most source control tools.
Does that help? Or did you mean for me to explain how the code works?
You can find a simple, full diff implementation for both text and HTML here:
https://github.com/paulgb/simplediff/blob/master/php/simplediff.php
What would be more useful is both combined in one output, so "brown fox" striked out and directly behind have the green "white rabbit"
Old but good post. I took the liberty to create another code, structural type, but you can easily make it OOP.
<?php
$old = 'Text scris manual direct intr-o ceva de altceva variabila !';
$new = 'Text scris de mana pentru comparatie, si plasat direct in variabila !';
$oldArr = pregsplit('/\s+/', $old);// old (initial) text splitted into words
$newArr = pregsplit('/\s+/', $new);// new text splitted into words
$resArr = array();
$oldCount = count($oldArr)-1;
$newCount = count($newArr)-1;
$tmpOld = 0;// marker position for old (initial) string
$tmpNew = 0;// marker position for new (modified) string
$end = 0;// do while variable
// endless do while loop untill specified otherwise
while($end == 0){
// if marker position is less or equal than max count for initial text
// to make sure we don't overshoot the max lenght
if($tmpOld <= $oldCount){
// we check if current words from both string match, at the current marker positions
if($oldArr[$tmpOld] === $newArr[$tmpNew]){
// if they match, nothing has been modified, we push the word into results and increment both markers
arraypush($resArr,$oldArr[$tmpOld]);
$tmpOld++;
$tmpNew++;
}else{
// fi the words don't match, we need to check for recurrence of the searched word in the entire new string
$foundKey = arraysearch($oldArr[$tmpOld],$newArr,TRUE);
// if we find it
if($foundKey != '' && $foundKey > $tmpNew){
// we get all the words from the new string between the current marker and the foundKey exclusive
// and we place them into results, marking them as new words
for($p=$tmpNew;$p<$foundKey;$p++){
arraypush($resArr,'<span class="new-word">'.$newArr[$p].'</span>');
}
// after that, we insert the found word as unmodified
arraypush($resArr,$oldArr[$tmpOld]);
// and we increment old marker position by 1
$tmpOld++;
// and set the new marker position at the found key position, plus one
$tmpNew = $foundKey+1;
}else{
// if the word wasn't found it means it has been deleted
// and we need to add ti to results, marked as deleted
array_push($resArr,'<span class="old-word">'.$oldArr[$tmpOld].'</span>');
// and increment the old marker by one
$tmpOld++;
}
}
}else{
$end = 1;
}
}
echo '<br><br>';
echo $old.'<br>';
echo $new.'<br><br>';
$textFinal = '';
foreach($resArr as $val){
$textFinal .= $val.' ';
}
echo $textFinal;
?>
<style>
.new-word{background:rgba(245,255,178,1.00)}
.new-word:after{content:' '; background:rgba(245,255,178,1.00)}
.old-word{text-decoration:none; position:relative}
.old-word:after{
content: ' ';
font-size: inherit;
display: block;
position: absolute;
right: 0;
left: 0;
top: 55%;
bottom: 30%;
border-top: 1px solid #000;
border-bottom: 1px solid #000;
}
</style>
Just run the script and you'll see the results :) .