Skip to content

Instantly share code, notes, and snippets.

@Konamiman
Last active May 29, 2025 13:57
Show Gist options
  • Save Konamiman/e081a5eb6811e92454b4837806657be4 to your computer and use it in GitHub Desktop.
Save Konamiman/e081a5eb6811e92454b4837806657be4 to your computer and use it in GitHub Desktop.
PHP function to apply a diff
<?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";
}
<?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