Last Updated: November 21, 2018
·
33.47K
· piercelive

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:
Picture

7 Responses
Add your response

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 ...)

over 1 year ago ·

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.

over 1 year ago ·

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.

over 1 year ago ·

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?

over 1 year ago ·

You can find a simple, full diff implementation for both text and HTML here:

https://github.com/paulgb/simplediff/blob/master/php/simplediff.php

over 1 year ago ·

What would be more useful is both combined in one output, so "brown fox" striked out and directly behind have the green "white rabbit"

over 1 year ago ·

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 = preg
split('/\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 = array
search($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
array
push($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 :) .

over 1 year ago ·