Last active
May 29, 2025 13:57
-
-
Save Konamiman/e081a5eb6811e92454b4837806657be4 to your computer and use it in GitHub Desktop.
PHP function to apply a diff
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* Apply a diff to a text composed of multiple lines. | |
* | |
* The diff is expected to be in unified format (https://www.gnu.org/software/diffutils/manual/html_node/Unified-Format.html) | |
* but for a single file (so there are no file names and the first line starts with "@@" already). | |
* | |
* The approach followed is probably not the most efficient possible, but the code is small and gets the job done. | |
* | |
* @param string $contents The contents to apply the diff to. | |
* @param string $diff The unified diff for the file. | |
* @return string The contents with the diff applied. | |
*/ | |
function apply_diff(string $contents, string $diff): string { | |
$diff_lines = explode("\n", $diff); | |
$added_lines = []; // line number => array of line contents | |
$removed_line_numbers = []; | |
foreach($diff_lines as $line) { | |
if($line === '') { | |
$line = ' '; | |
} | |
if($line[0] === '@') { | |
preg_match('/-(?<original_line_number>\d+),/m', $line, $matches); | |
$original_content_line_number = $matches['original_line_number']; | |
} | |
elseif($line[0] === '-') { | |
$removed_line_numbers[] = $original_content_line_number; | |
$original_content_line_number++; | |
} | |
elseif($line[0] === '+') { | |
$added_lines[$original_content_line_number][] = substr($line, 1); | |
} | |
else { | |
$original_content_line_number++; | |
} | |
} | |
$new_content_lines = []; | |
$current_line_number = 1; | |
$original_content_lines = explode("\n", $contents); | |
$max_original_content_line_number = count($original_content_lines); | |
$max_line_number = max(array_merge([$max_original_content_line_number], $removed_line_numbers, array_keys($added_lines))); | |
while($current_line_number <= $max_line_number) { | |
$to_add = $added_lines[$current_line_number] ?? []; | |
foreach($to_add as $line) { | |
$new_content_lines[] = $line; | |
} | |
if($current_line_number <= $max_original_content_line_number && !in_array($current_line_number, $removed_line_numbers)) { | |
$new_content_lines[] = $original_content_lines[$current_line_number-1]; | |
} | |
$current_line_number++; | |
} | |
return implode("\n", $new_content_lines) . "\n"; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
$text = <<<MSX | |
Line 1 | |
Line 2 | |
Line 3 | |
Line 4 | |
Line 5 | |
Line 6 | |
Line 7 | |
Line 8 | |
Line 9 | |
Line 10 | |
MSX; | |
$patch = <<<MSX | |
@@ -1,10 +1,15 @@ | |
+--- Title | |
Line 1 | |
-Line 2 | |
-Line 3 | |
-Line 4 | |
+Line 4 ! | |
+aaa | |
+bbb | |
+ccc | |
+ddd | |
+eee | |
+fff | |
+ggg | |
Line 5 | |
-Line 6 | |
-Line 7 | |
+Line 7 ? | |
Line 8 | |
-Line 9 | |
-Line 10 | |
+--- Aaand... | |
+--- The end! | |
MSX; | |
echo apply_diff($text, $patch); | |
/* | |
Output: | |
--- Title | |
Line 1 | |
Line 4 ! | |
aaa | |
bbb | |
ccc | |
ddd | |
eee | |
fff | |
ggg | |
Line 5 | |
Line 7 ? | |
Line 8 | |
--- Aaand... | |
--- The end! | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment