File transfer
This commit is contained in:
116
app/Addons/AdvancedAdmin/Services/Revisions/DiffService.php
Normal file
116
app/Addons/AdvancedAdmin/Services/Revisions/DiffService.php
Normal file
@@ -0,0 +1,116 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Diff Service — generates unified diffs between two text contents.
|
||||
*
|
||||
* Uses SebastianBergmann\Diff if available, falls back to simple line diff.
|
||||
*
|
||||
* @author Ball Studios <https://git.balls.studio>
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
namespace Pterodactyl\Addons\AdvancedAdmin\Services\Revisions;
|
||||
|
||||
class DiffService
|
||||
{
|
||||
/**
|
||||
* Generate a unified diff between old and new content.
|
||||
* Returns array of lines with type: 'context' | 'added' | 'removed' | 'header'
|
||||
*/
|
||||
public function diff(string $oldContent, string $newContent, string $fileName = 'file'): array
|
||||
{
|
||||
if (class_exists(\SebastianBergmann\Diff\Differ::class)) {
|
||||
return $this->diffViaSebastian($oldContent, $newContent, $fileName);
|
||||
}
|
||||
return $this->diffNative($oldContent, $newContent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute a structured diff using SebastianBergmann\Diff.
|
||||
*/
|
||||
private function diffViaSebastian(string $old, string $new, string $fileName): array
|
||||
{
|
||||
$differ = new \SebastianBergmann\Diff\Differ(
|
||||
new \SebastianBergmann\Diff\Output\UnifiedDiffOutputBuilder("--- a/{$fileName}\n+++ b/{$fileName}\n")
|
||||
);
|
||||
$rawDiff = $differ->diff($old, $new);
|
||||
return $this->parseUnifiedDiff($rawDiff);
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple pure-PHP line-level diff (fallback).
|
||||
*/
|
||||
private function diffNative(string $old, string $new): array
|
||||
{
|
||||
$oldLines = explode("\n", $old);
|
||||
$newLines = explode("\n", $new);
|
||||
$result = [];
|
||||
|
||||
// LCS-based diff
|
||||
$lcs = $this->lcs($oldLines, $newLines);
|
||||
$i = 0; $j = 0; $k = 0;
|
||||
|
||||
while ($i < count($oldLines) || $j < count($newLines)) {
|
||||
if ($k < count($lcs) && $i < count($oldLines) && $oldLines[$i] === $lcs[$k] && $j < count($newLines) && $newLines[$j] === $lcs[$k]) {
|
||||
$result[] = ['type' => 'context', 'content' => $oldLines[$i], 'line_old' => $i + 1, 'line_new' => $j + 1];
|
||||
$i++; $j++; $k++;
|
||||
} elseif ($j < count($newLines) && ($k >= count($lcs) || $newLines[$j] !== $lcs[$k])) {
|
||||
$result[] = ['type' => 'added', 'content' => $newLines[$j], 'line_new' => $j + 1];
|
||||
$j++;
|
||||
} else {
|
||||
$result[] = ['type' => 'removed', 'content' => $oldLines[$i], 'line_old' => $i + 1];
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function parseUnifiedDiff(string $diff): array
|
||||
{
|
||||
$lines = explode("\n", $diff);
|
||||
$result = [];
|
||||
$oldLine = 0; $newLine = 0;
|
||||
|
||||
foreach ($lines as $line) {
|
||||
if (str_starts_with($line, '---') || str_starts_with($line, '+++')) {
|
||||
$result[] = ['type' => 'header', 'content' => $line];
|
||||
} elseif (str_starts_with($line, '@@')) {
|
||||
// Parse @@ -old +new @@
|
||||
preg_match('/@@ -(\d+).*\+(\d+)/', $line, $m);
|
||||
$oldLine = (int) ($m[1] ?? 0);
|
||||
$newLine = (int) ($m[2] ?? 0);
|
||||
$result[] = ['type' => 'header', 'content' => $line];
|
||||
} elseif (str_starts_with($line, '-')) {
|
||||
$result[] = ['type' => 'removed', 'content' => substr($line, 1), 'line_old' => $oldLine++];
|
||||
} elseif (str_starts_with($line, '+')) {
|
||||
$result[] = ['type' => 'added', 'content' => substr($line, 1), 'line_new' => $newLine++];
|
||||
} elseif (str_starts_with($line, ' ')) {
|
||||
$result[] = ['type' => 'context', 'content' => substr($line, 1), 'line_old' => $oldLine++, 'line_new' => $newLine++];
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Longest Common Subsequence (for native diff).
|
||||
*/
|
||||
private function lcs(array $a, array $b): array
|
||||
{
|
||||
$m = count($a); $n = count($b);
|
||||
$dp = array_fill(0, $m + 1, array_fill(0, $n + 1, 0));
|
||||
for ($i = 1; $i <= $m; $i++) {
|
||||
for ($j = 1; $j <= $n; $j++) {
|
||||
$dp[$i][$j] = $a[$i-1] === $b[$j-1]
|
||||
? $dp[$i-1][$j-1] + 1
|
||||
: max($dp[$i-1][$j], $dp[$i][$j-1]);
|
||||
}
|
||||
}
|
||||
$result = []; $i = $m; $j = $n;
|
||||
while ($i > 0 && $j > 0) {
|
||||
if ($a[$i-1] === $b[$j-1]) { array_unshift($result, $a[$i-1]); $i--; $j--; }
|
||||
elseif ($dp[$i-1][$j] > $dp[$i][$j-1]) { $i--; }
|
||||
else { $j--; }
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user