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 8.4+ longest sequence matcher inspired by Python difflib. Compare strings or arrays to find matching blocks and similarities for diffing, change detection, and text analysis. Lightweight library extracted from php-diff with improvements.

View on GitHub
Deep Wiki
Context7

Getting Started

Install the package via Composer:

composer require jfcherng/php-sequence-matcher

First Use Case: Compare two strings or arrays to find similarity ratio (0-1):

use Jfcherng\SequenceMatcher\SequenceMatcher;

$matcher = new SequenceMatcher();
$ratio = $matcher->ratio("Laravel", "Laravul"); // Returns ~0.857

Key Methods to Explore:

  • ratio(): Returns similarity ratio (0 = completely different, 1 = identical).
  • getOpCodes(): Returns operation codes for detailed diff analysis.
  • findLongestMatch(): Finds the longest contiguous matching subsequence.

Where to Look First:


Implementation Patterns

Core Workflows

1. Basic String/Array Comparison

$matcher = new SequenceMatcher();
$similarity = $matcher->ratio($stringA, $stringB);

Laravel Integration:

// In a service class
public function compareContent($oldContent, $newContent)
{
    $matcher = app(SequenceMatcher::class);
    return $matcher->ratio($oldContent, $newContent);
}

2. Detailed Diff Analysis

$opCodes = $matcher->getOpCodes($a, $b);
foreach ($opCodes as $op) {
    // OP_EQ (1) = equal, OP_DEL (2) = delete, OP_INS (4) = insert, OP_REP (8) = replace
    if ($op[0] & SequenceMatcher::OP_REP) {
        // Handle replacements
    }
}

3. Eloquent Model Integration

// In a model observer
public function saved(Model $model)
{
    if ($model instanceof Post) {
        $similarity = app(SequenceMatcher::class)->ratio(
            $model->previous('content'),
            $model->content
        );
        $model->similarity_score = $similarity;
        $model->save();
    }
}

4. API Response Validation

// In a form request
public function passedValidation()
{
    $expected = $this->getExpectedResponse();
    $actual = $this->route()->getResponse();
    $matcher = app(SequenceMatcher::class);
    return $matcher->ratio($expected, $actual) >= config('api.min_similarity');
}

Advanced Patterns

Custom Scoring with Options

$options = new \Jfcherng\SequenceMatcher\Options();
$options->setAutoJump(true); // Optimize for speed
$options->setThreshold(0.5);  // Minimum ratio to consider a match

$matcher = new SequenceMatcher($options);
$isMatch = $matcher->ratio($a, $b) >= $options->getThreshold();

Queue-Based Batch Processing

// Dispatch a job for large comparisons
CompareContentJob::dispatch($oldContent, $newContent);

// In the job
public function handle()
{
    $matcher = app(SequenceMatcher::class);
    $result = $matcher->ratio($this->oldContent, $this->newContent);
    // Store or notify based on result
}

Caching Similarity Results

public function getCachedSimilarity($key, $a, $b)
{
    return cache()->remember("sequence_{$key}", now()->addHours(1), function() use ($a, $b) {
        return app(SequenceMatcher::class)->ratio($a, $b);
    });
}

Testing with PHPUnit

public function testSimilarityCalculation()
{
    $matcher = new SequenceMatcher();
    $this->assertEquals(1.0, $matcher->ratio("hello", "hello"));
    $this->assertEquals(0.0, $matcher->ratio("hello", "world"));
    $this->assertEquals(0.75, $matcher->ratio("abc", "abcd"));
}

Gotchas and Tips

Pitfalls

  1. Memory Usage with Large Sequences

    • The matcher loads both sequences into memory. For large arrays/strings (>1MB), consider:
      • Chunking the input (process in batches).
      • Using Laravel Queues to offload comparisons.
    • Tip: Monitor memory with memory_get_usage() and implement fallbacks for oversized inputs.
  2. Floating-Point Precision

    • Ratios like 0.9999999999999999 may appear due to floating-point arithmetic.
    • Fix: Round results or use number_format($ratio, 4).
  3. Case Sensitivity

    • The matcher is case-sensitive by default. Normalize inputs if needed:
      $matcher->ratio(strtolower($a), strtolower($b));
      
  4. Unicode Handling

    • Some Unicode characters (e.g., emojis, combining marks) may not compare as expected.
    • Tip: Use mb_strtolower() for Unicode-aware case folding.
  5. Performance with Arrays

    • Nested arrays or objects require serialization (e.g., json_encode()). Deeply nested structures may bloat memory.
    • Tip: Flatten arrays or limit depth before comparison.
  6. Breaking Changes in v5.0.0+

    • PHP 8.3+ required. Options are now object-based (not scalar values).
    • Migration: Update option handling:
      // Old (pre-5.0.0)
      $matcher->ratio($a, $b, SequenceMatcher::OP_NOP);
      
      // New
      $options = new Options();
      $matcher = new SequenceMatcher($options);
      

Debugging Tips

  1. Inspect Operation Codes

    • Use getOpCodes() to debug mismatches:
      $opCodes = $matcher->getOpCodes($a, $b);
      print_r($opCodes); // Analyze deletions/insertions/replacements
      
  2. Compare with Python

    • Validate results against Python’s difflib:
      from difflib import SequenceMatcher
      print(SequenceMatcher(None, "abc", "abcd").ratio())  # Should match PHP output
      
  3. Profile Performance

    • Use Laravel’s bench() helper or Xdebug to identify slow comparisons:
      $time = bench()->time(fn() => $matcher->ratio($largeA, $largeB));
      
  4. Handle Edge Cases

    • Test with:
      • Empty strings/arrays.
      • Identical sequences.
      • Sequences with mixed types (e.g., ["a", 1] vs. ["a", "1"]).

Extension Points

  1. Custom Matcher Logic

    • Extend SequenceMatcher to add domain-specific rules:
      class CustomMatcher extends SequenceMatcher
      {
          public function ratio($a, $b)
          {
              // Pre-process inputs (e.g., strip HTML tags)
              $a = strip_tags($a);
              $b = strip_tags($b);
              return parent::ratio($a, $b);
          }
      }
      
  2. Laravel Service Provider

    • Bind the matcher with custom options:
      $this->app->singleton(SequenceMatcher::class, function() {
          $options = new Options();
          $options->setAutoJump(true);
          return new SequenceMatcher($options);
      });
      
  3. Event-Based Notifications

    • Trigger events when similarity thresholds are crossed:
      event(new SequenceMismatch($model, $similarity));
      
  4. Queueable Comparisons

    • Create a job for async processing:
      class CompareContentJob implements ShouldQueue
      {
          use Dispatchable, InteractsWithQueue, Queueable;
      
          public function handle()
          {
              $matcher = app(SequenceMatcher::class);
              $result = $matcher->ratio($this->oldContent, $this->newContent);
              // Store or notify
          }
      }
      

Configuration Quirks

  1. Options Object

    • Always use Options class (not scalar values) in PHP 8.3+:
      // Correct
      $options = new Options();
      $options->setAutoJump(true);
      $matcher = new SequenceMatcher($options);
      
      // Incorrect (pre-5.0.0)
      $matcher->ratio($a, $b, true);
      
  2. Threshold Handling

    • The threshold option in Options is not used by ratio(). It’s for internal checks in findLongestMatch().
    • Tip: Manually check ratios against your threshold:
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.
emuniq/filament-browser-notifications
syriable/filament-translator
hungnm28/livewire-form
wenprise/eloquent
crudly/encrypted
fadion/bouncy
cuci/prototurk-sdk
gos/pubsub-router-bundle
cuci/prototurk-sdk-symfony
clementtalleu/easyadmin-markdown-bundle
codeflextech/permission-manager
karnoweb/livewire-datepicker
sayedenam/sayed-dashboard
milito/query-filter
apiboxsym/user-bundle
apiboxsym/health-check-bundle
jayeshmepani/jpl-moshier-ephemeris-php
elnasnato/laraliveui
labrodev/rest-sdk
sampaui/sampaui