php
53 lines · 7 steps
Building a ranked fuzzy search in PHP
A two-function approach that scores each candidate, filters out non-matches, and returns the best results in order.
Explained by
highlit
1function fuzzySearch(string $query, array $items, int $limit = 10): array
2{
3 $query = mb_strtolower(trim($query));
4
5 if ($query === '') {
6 return array_slice($items, 0, $limit);
7 }
8
9 $scored = [];
10
11 foreach ($items as $item) {
12 $candidate = mb_strtolower($item);
13 $score = scoreMatch($query, $candidate);
14
15 if ($score > 0) {
16 $scored[] = ['item' => $item, 'score' => $score];
17 }
18 }
19
20 usort($scored, static fn ($a, $b) => $b['score'] <=> $a['score']);
21
22 return array_map(
23 static fn ($entry) => $entry['item'],
24 array_slice($scored, 0, $limit)
25 );
26}
27
28function scoreMatch(string $query, string $candidate): float
29{
30 if ($candidate === $query) {
31 return 100.0;
32 }
33
34 if (str_starts_with($candidate, $query)) {
35 return 90.0 + (strlen($query) / max(strlen($candidate), 1)) * 5;
36 }
37
38 if (str_contains($candidate, $query)) {
39 return 70.0 + (strlen($query) / max(strlen($candidate), 1)) * 5;
40 }
41
42 $distance = levenshtein($query, $candidate);
43 $maxLen = max(strlen($query), strlen($candidate), 1);
44
45 if ($distance > $maxLen * 0.6) {
46 return 0.0;
47 }
48
49 similar_text($query, $candidate, $percent);
50 $editScore = (1 - $distance / $maxLen) * 40;
51
52 return round($editScore + $percent * 0.3, 2);
53}
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1Tiered scoring lets you prefer exact and prefix matches while still tolerating typos as a fallback.
- 2Normalizing case and whitespace once up front keeps every comparison consistent.
- 3Separating scoring from selection keeps the ranking logic testable and easy to tune.
Related explainers
php
<?php class NameParser {
Parsing a full name into components in PHP
string-parsing
arrays
normalization
Intermediate
8 steps
php
<?php namespace App\Services\Checkout;
Validating coupons with Laravel's Pipeline
pipeline
chain of responsibility
transactions
Intermediate
7 steps
php
<?php namespace App\Services;
How a password strength validator works in PHP
validation
regular-expressions
data-driven
Intermediate
8 steps
php
<?php namespace App\Services;
Building a cached daily leaderboard in Laravel
caching
aggregation
eager-loading
Intermediate
9 steps
php
<?php final class RotatingFileLogger {
How a rotating file logger works in PHP
logging
file-rotation
io
Intermediate
9 steps
python
import re from functools import total_ordering from typing import Optional
Parsing and comparing semantic versions
regex
operator-overloading
sorting
Intermediate
7 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/building-a-ranked-fuzzy-search-in-php-explained-php-836a/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.