sugarcraft / candy-fuzzy
Fuzzy string matching with scored matched indices — Smith-Waterman + Sahilm algorithms. Unblocks filter-highlighting UI across the ecosystem.
Requires
- php: ^8.3
Requires (Dev)
- phpunit/phpunit: ^10.5
This package is not auto-updated.
Last update: 2026-06-02 12:56:08 UTC
README
Fuzzy string matching library with scored matched character indices — enables filter highlighting UI across the SugarCraft ecosystem.
Installation
composer require sugarcraft/candy-fuzzy
Role
Extracts the canonical Smith-Waterman fuzzy matcher from candy-forms and adds the key feature that was previously impossible: ranked matches WITH scored matched character indices, enabling UI filter highlighting.
Provides two algorithms:
- SmithWatermanMatcher — Smith-Waterman local alignment with adjacency bonus. Bit-equivalent to the original
candy-formsimplementation. - SahilmMatcher — Ports the
sahilm/fuzzyalgorithm used bycharmbracelet/gumfilter. Includes separator bonus, camelCase bonus, exact-prefix bonus.
Quickstart
use SugarCraft\Fuzzy\Matcher\SmithWatermanMatcher; use SugarCraft\Fuzzy\Highlighter; $matcher = new SmithWatermanMatcher(); // Match a single candidate $result = $matcher->match('foo', 'foobar'); // MatchResult(needle: 'foo', haystack: 'foobar', score: 16, matchedIndices: [0, 1, 2]) // Match against multiple candidates (sorted by score desc) $results = $matcher->matchAll('app', ['apple', 'applet', 'application', 'apricot']); // Returns list of MatchResult sorted by score // Highlight matched runs $highlighter = new Highlighter(); $styled = $highlighter->highlight($result, fn($matched) => "\033[1m$matched\033[0m"); // Returns 'foobar' with matched chars styled
MatchResult
final class MatchResult { public readonly string $needle; // Search query public readonly string $haystack; // Matched candidate public readonly int $score; // Higher = better match public readonly array $matchedIndices; // 0-based char indices of matched chars }
Interface
Swap matchers without touching call-sites:
use SugarCraft\Fuzzy\MatcherInterface; use SugarCraft\Fuzzy\FuzzyMatcher; function filter(MatcherInterface $matcher, string $query, array $candidates): array { return $matcher->matchAll($query, $candidates); }
Algorithm Differences
| Feature | SmithWaterman | Sahilm |
|---|---|---|
| Local alignment | ✅ | ❌ |
| Adjacent bonus | ✅ (5) | ✅ (consecutive: 5) |
| Separator bonus | ❌ | ✅ (10) |
| CamelCase bonus | ❌ | ✅ (10) |
| First-char bonus | ❌ | ✅ (15) |
| Case sensitive | ❌ | Optional |
Backward Compatibility
The existing SugarCraft\Forms\Fuzzy\FuzzyMatcher and SugarCraft\Lister\FuzzyMatch classes remain as deprecated shims that delegate to SugarCraft\Fuzzy\Matcher\SmithWatermanMatcher. Consumers will migrate in subsequent steps.