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

Php Sequence Matcher Laravel Package

jfcherng/php-sequence-matcher

PHP longest sequence matcher inspired by Python difflib. Compare arrays or strings to find matching blocks and measure similarity, useful for diffing and change detection. Lightweight, modern PHP (8.4+) package.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Verify PHP 8.3+ Compatibility:

    php -v  # Must be ≥8.3
    composer require jfcherng/php-sequence-matcher
    
  2. Basic Comparison (Arrays/Strings):

    use jfcherng\SequenceMatcher\SequenceMatcher;
    use jfcherng\SequenceMatcher\Options;
    
    $matcher = new SequenceMatcher(
        ['a', 'b', 'c'],  // Sequence A
        ['x', 'b', 'y'],  // Sequence B
        new Options()      // Object-based config (PHP 8.3+)
    );
    
  3. First Use Case: Generate Opcodes

    $opCodes = $matcher->getOpCodes();
    // Example output: [['replace', 0, 1, 0, 1], ['equal', 1, 2, 1, 2]]
    
  4. Key Methods to Explore:

    • ratio() → Similarity score (0.0–1.0)
    • getMatchingBlocks() → Identical subsequences
    • getOpCodes() → Edit instructions (insert/delete/replace/equal)

Implementation Patterns

Core Workflows

  1. Laravel Service Wrapper (Recommended)

    namespace App\Services;
    
    use jfcherng\SequenceMatcher\SequenceMatcher;
    use jfcherng\SequenceMatcher\Options;
    
    class SequenceMatcherService {
        public function __construct(
            private SequenceMatcher $matcher
        ) {}
    
        public static function create(array $a, array $b): self {
            return new self(
                new SequenceMatcher($a, $b, new Options())
            );
        }
    
        public function getChanges(): array {
            return $this->matcher->getOpCodes();
        }
    }
    

    Register in AppServiceProvider:

    $this->app->bind(SequenceMatcherService::class, function () {
        return SequenceMatcherService::create($oldData, $newData);
    });
    
  2. Diff Rendering for UI

    $changes = [];
    foreach ($matcher->getOpCodes() as $op) {
        [$tag, $i1, $i2, $j1, $j2] = $op;
        switch ($tag) {
            case SequenceMatcher::OP_REP:
                $changes[] = "Line {$i1}: '{$old[$i1]}' → '{$new[$j1]}'";
                break;
            case SequenceMatcher::OP_DEL:
                $changes[] = "Line {$i1}: Removed '{$old[$i1]}'";
                break;
            case SequenceMatcher::OP_INS:
                $changes[] = "Line {$j1}: Added '{$new[$j1]}'";
                break;
        }
    }
    return view('diff', ['changes' => $changes]);
    
  3. Schema Validation with Similarity Thresholds

    $similarity = $matcher->ratio();
    if ($similarity < 0.9) {
        throw new \RuntimeException(
            "Schema drift detected (similarity: {$similarity * 100}%)"
        );
    }
    
  4. Token-Level Diffing (e.g., for Code)

    $tokensOld = token_get_all(file_get_contents('old.php'));
    $tokensNew = token_get_all(file_get_contents('new.php'));
    $matcher = new SequenceMatcher($tokensOld, $tokensNew);
    $diffs = $matcher->getOpCodes();
    

Integration Tips

  • For Large Sequences (>10k elements): Use getMatchingBlocks() to identify identical chunks for partial processing.
  • Case-Insensitive Matching: Pre-process sequences (e.g., array_map('strtolower', $sequence)).
  • Laravel Caching: Cache SequenceMatcher instances for repeated comparisons:
    $matcher = Cache::remember(
        "diff_{$hashOld}_{$hashNew}",
        now()->addHours(1),
        fn() => new SequenceMatcher($old, $new, new Options())
    );
    

Gotchas and Tips

Pitfalls

  1. PHP Version Mismatch:

    • Error: Class 'jfcherng\SequenceMatcher\Options' not foundCause: PHP <8.3.
    • Fix: Upgrade PHP or use a fork (e.g., chrisboulton/php-diff for PHP 8.1).
  2. Opcode Confusion (v3.0+):

    • Gotcha: OP_EQ is now 1 (not 'eq' string). Use constants:
      if ($op[0] === SequenceMatcher::OP_REP) { ... }
      
    • Tip: Enable strict typing in Options for IDE autocompletion.
  3. Object-Based Options Overhead:

    • Issue: Serialization fails in Laravel cache/sessions.
    • Fix: Use serialize()-compatible wrappers or avoid caching SequenceMatcher directly.
  4. Non-Greedy Matching:

    • Gotcha: Default behavior may not match Python difflib's greedy algorithm.
    • Tip: Adjust Options for custom behavior (check Options class docs).
  5. Memory Spikes:

    • Warning: Large sequences (>50k elements) may exhaust memory.
    • Mitigation: Process in chunks or use getMatchingBlocks() for partial matches.

Debugging

  • Verify Opcodes:
    $opCodes = $matcher->getOpCodes();
    dump($opCodes); // Debug structure: [tag, i1, i2, j1, j2]
    
  • Check Similarity:
    $ratio = $matcher->ratio();
    dump($ratio); // Should be 0.0–1.0
    
  • Inspect Matching Blocks:
    $blocks = $matcher->getMatchingBlocks();
    dump($blocks); // [[i1, i2, j1, j2], ...]
    

Extension Points

  1. Custom Opcodes: Combine base opcodes (e.g., OP_INS | OP_DEL for complex edits).

    $op = SequenceMatcher::OP_INS | SequenceMatcher::OP_DEL;
    
  2. Subclass Options: Extend for project-specific defaults:

    class AppOptions extends Options {
        public function __construct() {
            parent::__construct();
            $this->setAutoJunk(false); // Disable junk filtering
        }
    }
    
  3. Laravel Facade: Create a facade for cleaner syntax:

    // app/Facades/SequenceMatcher.php
    namespace App\Facades;
    use Illuminate\Support\Facades\Facade;
    class SequenceMatcher extends Facade {
        protected static function getFacadeAccessor() {
            return 'sequenceMatcher';
        }
    }
    

    Bind in AppServiceProvider:

    $this->app->singleton('sequenceMatcher', function () {
        return new SequenceMatcher([], [], new Options());
    });
    

    Usage:

    $diffs = \App\Facades\SequenceMatcher::getOpCodes();
    

Laravel-Specific Quirks

  • Service Container Binding: Avoid binding SequenceMatcher directly—wrap it in a service class to handle dynamic sequences.
  • Queue Jobs: Serialize Options only, not the full SequenceMatcher:
    $job->handle(new Options($matcher->getOptions()));
    
  • Testing: Mock SequenceMatcher in unit tests:
    $this->partialMock(SequenceMatcher::class, function ($mock) {
        $mock->method('getOpCodes')->willReturn([['equal', 0, 1, 0, 1]]);
    });
    
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.
davejamesmiller/laravel-breadcrumbs
artisanry/parsedown
christhompsontldr/phpsdk
enqueue/dsn
bunny/bunny
enqueue/test
enqueue/null
enqueue/amqp-tools
milesj/emojibase
bower-asset/punycode
bower-asset/inputmask
bower-asset/jquery
bower-asset/yii2-pjax
laravel/nova
spatie/laravel-mailcoach
spatie/laravel-superseeder
laravel/liferaft
nst/json-test-suite
danielmiessler/sec-lists
jackalope/jackalope-transport