jfcherng/php-diff
Generate diffs between two strings in PHP with multiple renderers: unified/context text, JSON, and rich HTML (inline, side-by-side, combined). Includes configurable differ/renderer options and helper CSS for easy HTML diff styling.
Installation:
composer require jfcherng/php-diff
Add to composer.json if using Laravel:
"require": {
"jfcherng/php-diff": "^7.0"
}
First Use Case:
Compare two strings with the default Unified renderer:
use Jfcherng\Diff\DiffHelper;
$old = "Hello world!";
$new = "Hi there!";
$diff = DiffHelper::calculate($old, $new, 'Unified');
Where to Look First:
// Minimal usage (defaults for DifferOptions/RendererOptions)
$diff = DiffHelper::calculate($oldString, $newString, 'Inline');
// Custom options
$options = new RendererOptions(['detailLevel' => 'word']);
$diff = DiffHelper::calculate($oldString, $newString, 'Inline', null, $options);
$diff = DiffHelper::calculateFiles(
'/path/to/old.txt',
'/path/to/new.txt',
'SideBySide'
);
// Store JSON in DB
$jsonDiff = DiffHelper::calculate($old, $new, 'Json');
DB::table('diffs')->insert(['content' => $jsonDiff]);
// Later render as HTML
$htmlRenderer = RendererFactory::make('Inline', $rendererOptions);
$htmlDiff = $htmlRenderer->renderArray(json_decode($jsonDiff, true));
// Extend existing renderers (e.g., for a custom HTML template)
class CustomHtmlRenderer extends \Jfcherng\Diff\Renderer\InlineRenderer {
protected function renderLine(...): string {
// Override logic here
}
}
$renderer = new CustomHtmlRenderer($rendererOptions);
$differ = new Differ(explode("\n", $old), explode("\n", $new));
$diffLines = $differ->getDiffLines(); // Array of diff operations
// In a controller
public function showDiff() {
$diff = DiffHelper::calculate($old, $new, 'Inline');
return view('diff.view', ['diff' => $diff]);
}
// In Blade (diff.view.blade.php)
<div class="diff-container">
{!! $diff !!}
@include('diff.styles') <!-- Include DiffHelper::getStyleSheet() -->
</div>
return response()->json([
'diff' => DiffHelper::calculate($old, $new, 'Json'),
'html' => DiffHelper::calculate($old, $new, 'Inline')
]);
// app/Console/Commands/GenerateDiff.php
public function handle() {
$diff = DiffHelper::calculateFiles(
storage_path('old.txt'),
storage_path('new.txt'),
'Unified'
);
$this->info($diff);
}
public function testDiffGeneration() {
$diff = DiffHelper::calculate("old", "new", 'Unified');
$this->assertStringContainsString('-old', $diff);
$this->assertStringContainsString('+new', $diff);
}
HTML Renderer CSS Dependency:
Inline, SideBySide, etc.) require CSS for proper styling.DiffHelper::getStyleSheet() or include example/diff-table.css:
<style>{!! \Jfcherng\Diff\DiffHelper::getStyleSheet() !!}</style>
Line Ending Sensitivity:
ignoreLineEnding: false treats \n vs \r\n as differences.ignoreLineEnding: true in DifferOptions to normalize line endings.Performance with Large Files:
lengthLimit in DifferOptions caps input size (default: 2000 chars).Context or Unified renderers (faster than HTML renderers).JSON Renderer Quirks:
JsonText vs JsonHtml: JsonHtml escapes HTML tags (e.g., <p> becomes <p>).JsonText for raw diff operations, JsonHtml for safe HTML output.Word/Char-Level Diffs:
detailLevel: 'word' or 'char' splits changes granularly but slows processing.'line' for most use cases (balance of speed and readability).Case Sensitivity:
ignoreCase: true treats "Hello" and "hello" as identical.Inspect Raw Diff Operations:
$differ = new Differ(explode("\n", $old), explode("\n", $new));
$diffLines = $differ->getDiffLines();
dd($diffLines); // Debug the raw diff array
Validate JSON Output:
$jsonDiff = DiffHelper::calculate($old, $new, 'Json');
$decoded = json_decode($jsonDiff, true);
if (json_last_error() !== JSON_ERROR_NONE) {
throw new \Exception("Invalid JSON diff");
}
Check Renderer Compatibility:
detailLevel options (e.g., Combined ignores detailLevel).detailLevel: 'none' if rendering fails.Handle Empty Diffs:
fullContextIfIdentical: true in DifferOptions to avoid empty results for identical inputs.Custom Renderers:
\Jfcherng\Diff\Renderer\AbstractRenderer for new formats.MarkdownRenderer for GitHub-style diffs.Language Support:
language option in RendererOptions:
$options = new RendererOptions([
'language' => [
'added' => '✅ Added',
'removed' => '❌ Removed'
]
]);
Pre/Post-Processing:
Differ::getDiffLines() to modify diff operations before rendering:
$diffLines = $differ->getDiffLines();
$diffLines = array_map(function ($chunk) {
// Filter or transform chunks
return $chunk;
}, $diffLines);
CLI Colorization:
$options = new RendererOptions([
'cliColorization' => \Jfcherng\Diff\Renderer\RendererConstant::CLI_COLOR_ENABLE
]);
Merge Threshold (Combined Renderer):
mergeThreshold to control how aggressively adjacent changes are merged:
$options = new RendererOptions(['mergeThreshold' => 0.5]); // Merge if <=50% changed
SideBySide with detailLevel: 'word' to highlight inline changes.JsonText for programmatic diff parsing and Inline for user-facing views.diff-table.css with your app and override colors via CSS variables.How can I help you explore Laravel packages today?