Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Diff Laravel Package

sebastian/diff

Standalone PHP diff library extracted from PHPUnit. Generate textual diffs between strings with configurable output builders (unified, strict unified, diff-only) or custom formats, and parse unified diffs into an object model for further processing.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require sebastian/diff
    

    For development-only use (e.g., tests):

    composer require --dev sebastian/diff
    
  2. First Use Case: Generate a unified diff between two strings:

    use SebastianBergmann\Diff\Differ;
    use SebastianBergmann\Diff\Output\StrictUnifiedDiffOutputBuilder;
    
    $differ = new Differ(new StrictUnifiedDiffOutputBuilder);
    echo $differ->diff('old content', 'new content');
    
  3. Where to Look First:

    • Core Classes: Differ (for generating diffs) and Parser (for parsing diffs).
    • Output Builders: StrictUnifiedDiffOutputBuilder (default), DiffOnlyOutputBuilder (minimal output).
    • Documentation: GitHub README and PHPUnit’s usage (since this package is extracted from PHPUnit).

Implementation Patterns

Common Workflows

1. Generating Diffs for Debugging

  • Use Case: Log or display differences between expected/actual values in tests or API responses.
  • Pattern:
    $differ = new Differ(new StrictUnifiedDiffOutputBuilder(['header' => false]));
    $diff = $differ->diff($expectedJson, $actualJson);
    Log::error("JSON Mismatch:\n{$diff}");
    
  • Tip: Disable headers ('header' => false) for cleaner logs.

2. Parsing Git Diffs for CI/CD

  • Use Case: Process Git diffs in deployment pipelines (e.g., validate changes before applying).
  • Pattern:
    use SebastianBergmann\Diff\Parser;
    use Symfony\Component\Process\Process;
    
    $gitDiff = (new Process(['git', 'diff', 'HEAD~1']))->getOutput();
    $parser = new Parser();
    $diffObject = $parser->parse($gitDiff);
    
    foreach ($diffObject as $diff) {
        foreach ($diff->getChunks() as $chunk) {
            if ($chunk->getAddedLines() > 0) {
                echo "Added: " . $chunk->getAddedLines() . " lines\n";
            }
        }
    }
    

3. Custom Diff Output

  • Use Case: Format diffs for specific tools (e.g., Slack notifications, HTML reports).
  • Pattern:
    use SebastianBergmann\Diff\DiffOutputBuilderInterface;
    
    class SlackDiffOutputBuilder implements DiffOutputBuilderInterface {
        public function build($diff): string {
            return "```diff\n{$diff}```";
        }
    }
    
    $differ = new Differ(new SlackDiffOutputBuilder());
    

4. Comparing Arrays/Objects

  • Use Case: Diff serialized data (e.g., JSON, YAML) or database records.
  • Pattern:
    $expected = json_encode(['key' => 'value'], JSON_PRETTY_PRINT);
    $actual   = json_encode(['key' => 'updated'], JSON_PRETTY_PRINT);
    echo $differ->diff($expected, $actual);
    

5. Performance Optimization

  • Use Case: Handle large files (e.g., config files, logs) efficiently.
  • Pattern: Use StrictUnifiedDiffOutputBuilder with context lines reduced:
    $builder = new StrictUnifiedDiffOutputBuilder(['contextLines' => 2]);
    $differ  = new Differ($builder);
    

Integration Tips

  • Laravel Logging: Use diffs in App\Exceptions\Handler for exception debugging:
    report(function () use ($expected, $actual) {
        return [
            'diff' => (new Differ(new DiffOnlyOutputBuilder))->diff($expected, $actual),
        ];
    });
    
  • Artisan Commands: Add a diff:view command to compare database records:
    $model1 = Model::find(1);
    $model2 = Model::find(2);
    $this->info((new Differ(new StrictUnifiedDiffOutputBuilder))->diff(
        $model1->toJson(),
        $model2->toJson()
    ));
    
  • Testing: Replace assertEquals with custom assertions using diffs:
    public function assertJsonDiff(string $expected, string $actual, string $message = ''): void {
        $diff = (new Differ(new DiffOnlyOutputBuilder))->diff($expected, $actual);
        $this->assertEmpty($diff, $message . "\nDiff:\n{$diff}");
    }
    

Gotchas and Tips

Pitfalls

  1. Breaking Changes in v9.0.0:

    • UnifiedDiffOutputBuilder was removed in favor of StrictUnifiedDiffOutputBuilder. Update all usages.
    • The $lcs parameter in Differ::diff() was removed. Use the default algorithm (Myers').
    • Fix: Replace:
      $differ->diff($old, $new, new TimeEfficientLongestCommonSubsequenceCalculator());
      
      With:
      $differ = new Differ(new StrictUnifiedDiffOutputBuilder());
      $differ->diff($old, $new);
      
  2. Line Number Offsets in Chunks:

    • If a chunk has getStartRange() === 0, getStart() returns the line after which the chunk should be inserted (not the first line of the range).
    • Example: A chunk with start: 5 and startRange: 0 means the change occurs after line 5.
  3. Empty Diffs:

    • StrictUnifiedDiffOutputBuilder returns an empty string if no differences exist (unlike older versions, which returned headers).
    • Fix: Check for empty output if you rely on headers:
      $diff = $differ->diff($old, $new);
      if (empty($diff)) {
          echo "No differences found.\n";
      }
      
  4. Newline Handling:

    • StrictUnifiedDiffOutputBuilder emits \ No newline at end of file warnings by default. Disable with:
      $builder = new StrictUnifiedDiffOutputBuilder([
          'emitNoLineEndEofWarning' => false,
      ]);
      
  5. Parser Limitations:

    • The Parser only supports unified diff format. Malformed diffs (e.g., from custom tools) may fail.
    • Fix: Validate input diffs before parsing:
      if (strpos($diff, '@@') === false) {
          throw new \InvalidArgumentException('Invalid unified diff format');
      }
      

Debugging Tips

  1. Inspect Diff Objects: Use print_r or var_export to debug parsed diffs:

    $diffObject = $parser->parse($gitDiff);
    var_export($diffObject);
    
  2. Log Raw Diffs: Log the raw diff output before parsing to identify formatting issues:

    Log::debug("Raw diff:\n" . $gitDiff);
    
  3. Context Lines: Adjust contextLines in StrictUnifiedDiffOutputBuilder to reduce noise in large diffs:

    $builder = new StrictUnifiedDiffOutputBuilder(['contextLines' => 1]);
    
  4. Performance Profiling: For large diffs (e.g., database dumps), profile the Differ class:

    $start = microtime(true);
    $diff = $differ->diff($largeString1, $largeString2);
    Log::info("Diff time: " . (microtime(true) - $start) . "s");
    

Extension Points

  1. Custom Output Builders: Implement DiffOutputBuilderInterface for unique formats:

    class HTMLDiffOutputBuilder implements DiffOutputBuilderInterface {
        public function build(Diff $diff): string {
            return "<pre>" . nl2br(htmlspecialchars($diff->render())) . "</pre>";
        }
    }
    
  2. Preprocessing Strings: Normalize strings before diffing (e.g., trim whitespace, sort arrays):

    $normalizedOld = preg_replace('/\s+/', ' ', $oldString);
    $normalizedNew = preg_replace('/\s+/', ' ', $newString);
    $diff = $differ->diff($normalizedOld, $normalizedNew);
    
  3. Post-Processing Diffs: Filter or highlight diffs using regex:

    $diff = $differ->diff($old, $new);
    $diff = preg_replace('/^\+\+\+ New$/', '<span class="highlight">+++ New</span>', $diff, 1);
    
  4. Integration with Laravel: Create a helper trait for diffing:

    trait Diffable
    
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
craftcms/url-validator
directorytree/privacy-filter-classifier
directorytree/privacy-filter
datacore/hub-sdk
develia/commons
cuci/prototurk-sdk
cuci/prototurk-sdk-symfony
develia/geo-bundle
dreamzy/livewire-charts
touchestate-sdk/php-sdk
22h/doctrine-garbage-collection-bundle
agtp/agtp-php
agtp/mod-php
splash/sonata-admin
splash/metadata
splash/openapi
splash/scopes
splash/toolkit
testo/output-teamcity
testo/bridge-symfony