|
1 | | -Simple PHP diff |
2 | | -=============== |
| 1 | +# Simple PHP Diff |
3 | 2 |
|
4 | | -Find the quick difference between two text files in PHP. |
| 3 | +A lightweight PHP library for fast text comparison and difference visualization. Find the quick difference between two text files and render the results in plain text or HTML format. |
5 | 4 |
|
6 | | -Idea |
7 | | ----- |
| 5 | +## Key Principles |
8 | 6 |
|
9 | | -The library compares two text files very quickly and returns the object with the differences. |
| 7 | +- **Line-by-line comparison** - Compares texts line by line with numbered output for easy change tracking |
| 8 | +- **Immutable result object** - Returns a `Diff` object containing original, target, formatted diff, and changed line numbers |
| 9 | +- **Dual output modes** - Plain text diff output and styled HTML rendering with color-coded changes |
| 10 | +- **Strict mode support** - Optional strict comparison that preserves different line ending formats |
| 11 | +- **Whitespace visualization** - Pretty rendering shows tabs as arrows and spaces as dots for clarity |
| 12 | +- **Zero dependencies** - Pure PHP implementation requiring only PHP 8.0+ |
10 | 13 |
|
11 | | -The difference has numbered lines for easy display of changes to the user. You can also read an array of changed rows as an integer array from the `Diff` object as you browse for changes. |
| 14 | +## Architecture |
12 | 15 |
|
13 | | -📦 Installation |
14 | | ---------------- |
| 16 | +The library consists of two main components with a clean separation of concerns: |
| 17 | + |
| 18 | +``` |
| 19 | +┌─────────────────────────────────────────────────────────────┐ |
| 20 | +│ SimpleDiff │ |
| 21 | +│ ┌─────────────────────────────────────────────────────┐ │ |
| 22 | +│ │ compare($left, $right, $strict) │ │ |
| 23 | +│ │ - Normalizes line endings (non-strict mode) │ │ |
| 24 | +│ │ - Performs line-by-line comparison │ │ |
| 25 | +│ │ - Tracks changed line numbers │ │ |
| 26 | +│ │ - Formats output with line numbers │ │ |
| 27 | +│ └─────────────────────────────────────────────────────┘ │ |
| 28 | +│ │ │ |
| 29 | +│ ▼ │ |
| 30 | +│ ┌─────────────────────────────────────────────────────┐ │ |
| 31 | +│ │ renderDiff($diff) │ │ |
| 32 | +│ │ - Converts diff to styled HTML │ │ |
| 33 | +│ │ - Color-codes additions (green) / removals (red) │ │ |
| 34 | +│ └─────────────────────────────────────────────────────┘ │ |
| 35 | +└─────────────────────────────────────────────────────────────┘ |
| 36 | + │ |
| 37 | + ▼ |
| 38 | +┌─────────────────────────────────────────────────────────────┐ |
| 39 | +│ Diff │ |
| 40 | +│ Immutable Value Object │ |
| 41 | +│ ┌─────────────────────────────────────────────────────┐ │ |
| 42 | +│ │ - original: string (normalized left input) │ │ |
| 43 | +│ │ - target: string (normalized right input) │ │ |
| 44 | +│ │ - diff: string (formatted diff output) │ │ |
| 45 | +│ │ - changedLines: int[] (line numbers that changed) │ │ |
| 46 | +│ └─────────────────────────────────────────────────────┘ │ |
| 47 | +└─────────────────────────────────────────────────────────────┘ |
| 48 | +``` |
| 49 | + |
| 50 | +## Components |
| 51 | + |
| 52 | +### SimpleDiff |
| 53 | + |
| 54 | +The main comparison engine that processes two text inputs and generates a diff result. |
| 55 | + |
| 56 | +**Methods:** |
| 57 | + |
| 58 | +| Method | Description | |
| 59 | +|--------|-------------| |
| 60 | +| `compare(string $left, string $right, bool $strict = false): Diff` | Compares two strings and returns a `Diff` object | |
| 61 | +| `renderDiff(Diff\|string $diff): string` | Renders the diff as styled HTML | |
| 62 | + |
| 63 | +**Comparison Process:** |
| 64 | + |
| 65 | +1. **Input normalization** (non-strict mode): Converts all line endings (`\r\n`, `\r`) to `\n` and trims whitespace |
| 66 | +2. **Line splitting**: Splits both inputs into arrays by newline character |
| 67 | +3. **Line-by-line comparison**: Iterates through lines, comparing original vs target |
| 68 | +4. **Output formatting**: Prepends each line with status marker (`+`, `-`, or space) and line number |
| 69 | +5. **Change tracking**: Records line numbers where differences occur |
| 70 | + |
| 71 | +### Diff |
| 72 | + |
| 73 | +An immutable value object that encapsulates the comparison result. |
| 74 | + |
| 75 | +**Properties (via getters):** |
| 76 | + |
| 77 | +| Property | Type | Description | |
| 78 | +|----------|------|-------------| |
| 79 | +| `original` | `string` | The normalized left/original input text | |
| 80 | +| `target` | `string` | The normalized right/target input text | |
| 81 | +| `diff` | `string` | The formatted diff output with line markers | |
| 82 | +| `changedLines` | `int[]` | Array of line numbers (1-indexed) that differ | |
| 83 | + |
| 84 | +**String Conversion:** |
| 85 | + |
| 86 | +The `Diff` object implements `__toString()` which returns the formatted diff string, allowing direct string casting. |
| 87 | + |
| 88 | +## 📦 Installation |
15 | 89 |
|
16 | 90 | It's best to use [Composer](https://getcomposer.org) for installation, and you can also find the package on |
17 | 91 | [Packagist](https://packagist.org/packages/baraja-core/simple-php-diff) and |
18 | 92 | [GitHub](https://github.com/baraja-core/simple-php-diff). |
19 | 93 |
|
20 | 94 | To install, simply use the command: |
21 | 95 |
|
22 | | -``` |
| 96 | +```shell |
23 | 97 | $ composer require baraja-core/simple-php-diff |
24 | 98 | ``` |
25 | 99 |
|
26 | 100 | You can use the package manually by creating an instance of the internal classes, or register a DIC extension to link the services directly to the Nette Framework. |
27 | 101 |
|
28 | | -Example |
29 | | -------- |
| 102 | +### Requirements |
30 | 103 |
|
31 | | - |
| 104 | +- PHP 8.0 or higher |
32 | 105 |
|
33 | | -Diff can be rendered to HTML (with native method `SimpleDiff::renderDiff($diff)`: |
| 106 | +## Basic Usage |
34 | 107 |
|
35 | | - |
36 | | - |
37 | | -How to use |
38 | | ----------- |
39 | | - |
40 | | -Simply create a SimpleDiff instance and compare the two files: |
| 108 | +### Simple Text Comparison |
41 | 109 |
|
42 | 110 | ```php |
| 111 | +use Baraja\DiffGenerator\SimpleDiff; |
| 112 | + |
43 | 113 | $left = 'First text'; |
44 | 114 | $right = 'Second text'; |
45 | 115 |
|
46 | | -$diff = (new \Baraja\DiffGenerator\SimpleDiff)->compare($left, $right); |
| 116 | +$diff = (new SimpleDiff)->compare($left, $right); |
47 | 117 |
|
48 | | -// simple render diff |
49 | | -echo '<code><pre>' |
50 | | - . htmlspecialchars((string) $diff) |
51 | | - . '</pre></code>'; |
| 118 | +// Output the diff as plain text |
| 119 | +echo '<code><pre>' . htmlspecialchars((string) $diff) . '</pre></code>'; |
52 | 120 | ``` |
53 | 121 |
|
54 | | -The `compare()` method returns a complete object `Diff` with the results of the comparison, from which you can get much more. |
| 122 | +### Get Changed Line Numbers |
55 | 123 |
|
56 | | -For example, to get a list of changed rows: |
| 124 | +```php |
| 125 | +$diff = (new SimpleDiff)->compare($left, $right); |
| 126 | + |
| 127 | +echo 'Changed lines: ' . implode(', ', $diff->getChangedLines()); |
| 128 | +``` |
| 129 | + |
| 130 | +### Render Diff as HTML |
57 | 131 |
|
58 | 132 | ```php |
59 | | -echo 'Changed lines: '; |
60 | | -echo implode(', ', $diff->getChangedLines()); |
| 133 | +$simpleDiff = new SimpleDiff; |
| 134 | +$diff = $simpleDiff->compare($left, $right); |
| 135 | + |
| 136 | +// Returns styled HTML with color-coded changes |
| 137 | +echo $simpleDiff->renderDiff($diff); |
| 138 | +``` |
| 139 | + |
| 140 | +## Output Format |
| 141 | + |
| 142 | +### Plain Text Output |
| 143 | + |
| 144 | +The diff output uses a standardized format: |
| 145 | + |
| 146 | +``` |
| 147 | + 1| unchanged line |
| 148 | +- 2| removed·line·with·visible·spaces |
| 149 | ++ 2| added→→→→line·with·visible·tabs |
| 150 | + 3| another unchanged line |
61 | 151 | ``` |
62 | 152 |
|
63 | | -Display the Diff in HTML |
64 | | ------------------------- |
| 153 | +**Format explanation:** |
| 154 | + |
| 155 | +- Lines starting with ` ` (two spaces) are unchanged |
| 156 | +- Lines starting with `- ` indicate content from the original (left) text |
| 157 | +- Lines starting with `+ ` indicate content from the target (right) text |
| 158 | +- Line numbers are right-padded and followed by `| ` |
| 159 | +- Spaces are rendered as `·` (middle dot) |
| 160 | +- Tabs are rendered as `→→→→` (four arrows) |
| 161 | + |
| 162 | +### HTML Output |
| 163 | + |
| 164 | +The `renderDiff()` method generates HTML with inline styles: |
| 165 | + |
| 166 | +```html |
| 167 | +<pre class="code"> |
| 168 | +<div> 1| unchanged line</div> |
| 169 | +<div style="background:#e7acac">- 2| removed line</div> |
| 170 | +<div style="background:#a2f19c">+ 2| added line</div> |
| 171 | +<div> 3| another unchanged line</div> |
| 172 | +</pre> |
| 173 | +``` |
| 174 | + |
| 175 | +**Color coding:** |
| 176 | + |
| 177 | +- **Green background** (`#a2f19c`): Added lines (prefixed with `+`) |
| 178 | +- **Red background** (`#e7acac`): Removed lines (prefixed with `-`) |
| 179 | +- **No background**: Unchanged lines |
| 180 | + |
| 181 | +## Visual Examples |
| 182 | + |
| 183 | +### Plain Text Diff Output |
| 184 | + |
| 185 | + |
| 186 | + |
| 187 | +### HTML Rendered Diff |
65 | 188 |
|
66 | | -Very often we need to display the differences directly in the browser, for this the native method `renderDiff()` is suitable. |
| 189 | + |
| 190 | + |
| 191 | +## Comparison Modes |
| 192 | + |
| 193 | +### Non-Strict Mode (Default) |
| 194 | + |
| 195 | +In non-strict mode (default), the library normalizes line endings before comparison: |
| 196 | + |
| 197 | +- Converts `\r\n` (Windows) to `\n` |
| 198 | +- Converts `\r` (old Mac) to `\n` |
| 199 | +- Trims leading and trailing whitespace from both inputs |
| 200 | + |
| 201 | +This mode is ideal for comparing content where line ending differences should be ignored. |
67 | 202 |
|
68 | 203 | ```php |
69 | | -$left = 'First text'; |
70 | | -$right = 'Second text'; |
| 204 | +// Non-strict comparison (default) |
| 205 | +$diff = (new SimpleDiff)->compare($left, $right); |
| 206 | +$diff = (new SimpleDiff)->compare($left, $right, false); |
| 207 | +``` |
71 | 208 |
|
72 | | -$simpleDiff = new \Baraja\DiffGenerator\SimpleDiff; |
73 | | -$diff = $simpleDiff->compare($left, $right); |
| 209 | +### Strict Mode |
74 | 210 |
|
75 | | -echo $simpleDiff->renderDiff($diff); |
| 211 | +Strict mode preserves the original line endings and whitespace, useful when you need to detect differences in line termination characters. |
| 212 | + |
| 213 | +```php |
| 214 | +// Strict comparison - preserves line endings |
| 215 | +$diff = (new SimpleDiff)->compare($left, $right, true); |
| 216 | +``` |
| 217 | + |
| 218 | +## Working with the Diff Object |
| 219 | + |
| 220 | +### Accessing Original and Target Text |
| 221 | + |
| 222 | +```php |
| 223 | +$diff = (new SimpleDiff)->compare($left, $right); |
| 224 | + |
| 225 | +// Get the normalized original text |
| 226 | +$original = $diff->getOriginal(); |
| 227 | + |
| 228 | +// Get the normalized target text |
| 229 | +$target = $diff->getTarget(); |
| 230 | +``` |
| 231 | + |
| 232 | +### Getting the Raw Diff String |
| 233 | + |
| 234 | +```php |
| 235 | +$diff = (new SimpleDiff)->compare($left, $right); |
| 236 | + |
| 237 | +// Using getter method |
| 238 | +$diffString = $diff->getDiff(); |
| 239 | + |
| 240 | +// Using string casting (equivalent) |
| 241 | +$diffString = (string) $diff; |
| 242 | +``` |
| 243 | + |
| 244 | +### Working with Changed Lines |
| 245 | + |
| 246 | +```php |
| 247 | +$diff = (new SimpleDiff)->compare($left, $right); |
| 248 | +$changedLines = $diff->getChangedLines(); |
| 249 | + |
| 250 | +// Example output: [2, 5, 8] - lines 2, 5, and 8 were modified |
| 251 | +foreach ($changedLines as $lineNumber) { |
| 252 | + echo "Line {$lineNumber} was changed\n"; |
| 253 | +} |
| 254 | + |
| 255 | +// Check if any changes occurred |
| 256 | +if (count($changedLines) === 0) { |
| 257 | + echo "No differences found!"; |
| 258 | +} |
| 259 | +``` |
| 260 | + |
| 261 | +## Advanced Examples |
| 262 | + |
| 263 | +### Comparing Files |
| 264 | + |
| 265 | +```php |
| 266 | +$originalFile = file_get_contents('/path/to/original.txt'); |
| 267 | +$modifiedFile = file_get_contents('/path/to/modified.txt'); |
| 268 | + |
| 269 | +$simpleDiff = new SimpleDiff; |
| 270 | +$diff = $simpleDiff->compare($originalFile, $modifiedFile); |
| 271 | + |
| 272 | +// Check if files are identical |
| 273 | +if (empty($diff->getChangedLines())) { |
| 274 | + echo "Files are identical."; |
| 275 | +} else { |
| 276 | + echo "Files differ on lines: " . implode(', ', $diff->getChangedLines()); |
| 277 | + echo "\n\n"; |
| 278 | + echo $diff; |
| 279 | +} |
| 280 | +``` |
| 281 | + |
| 282 | +### Custom HTML Rendering |
| 283 | + |
| 284 | +If you need custom styling, you can process the diff string yourself: |
| 285 | + |
| 286 | +```php |
| 287 | +$diff = (new SimpleDiff)->compare($left, $right); |
| 288 | + |
| 289 | +$lines = explode("\n", $diff->getDiff()); |
| 290 | +$html = '<div class="my-diff-container">'; |
| 291 | + |
| 292 | +foreach ($lines as $line) { |
| 293 | + $firstChar = $line[0] ?? ''; |
| 294 | + $cssClass = match ($firstChar) { |
| 295 | + '+' => 'diff-added', |
| 296 | + '-' => 'diff-removed', |
| 297 | + default => 'diff-unchanged', |
| 298 | + }; |
| 299 | + $html .= sprintf('<div class="%s">%s</div>', $cssClass, htmlspecialchars($line)); |
| 300 | +} |
| 301 | + |
| 302 | +$html .= '</div>'; |
| 303 | +echo $html; |
76 | 304 | ``` |
77 | 305 |
|
78 | | -The method accepts Diff and returns valid treated HTML that can be displayed directly to the user. |
| 306 | +### Integration with Version Control Display |
79 | 307 |
|
80 | | -Comparison mode |
81 | | ---------------- |
| 308 | +```php |
| 309 | +function showCommitDiff(string $oldContent, string $newContent): string |
| 310 | +{ |
| 311 | + $simpleDiff = new SimpleDiff; |
| 312 | + $diff = $simpleDiff->compare($oldContent, $newContent); |
| 313 | + |
| 314 | + $changedCount = count($diff->getChangedLines()); |
| 315 | + |
| 316 | + $output = "<h3>Changes: {$changedCount} line(s) modified</h3>"; |
| 317 | + $output .= $simpleDiff->renderDiff($diff); |
| 318 | + |
| 319 | + return $output; |
| 320 | +} |
| 321 | +``` |
82 | 322 |
|
83 | | -This tool supports strict and basic comparison modes (strict mode is disabled by default). |
84 | | -Strict mode also allows you to compare changes in different line wrapping methods (for example, `"\n"` and so on). |
| 323 | +## Author |
85 | 324 |
|
| 325 | +**Jan Barášek** - [https://baraja.cz](https://baraja.cz) |
86 | 326 |
|
87 | | -📄 License |
88 | | ------------ |
| 327 | +## 📄 License |
89 | 328 |
|
90 | | -`baraja-core/simple-php-diff` is licensed under the MIT license. See the [LICENSE](https://github.com/baraja-core/template/blob/master/LICENSE) file for more details. |
| 329 | +`baraja-core/simple-php-diff` is licensed under the MIT license. See the [LICENSE](https://github.com/baraja-core/simple-php-diff/blob/master/LICENSE) file for more details. |
0 commit comments