levenshtein
Calculates the edit distance between two strings — the number of changes needed to turn one into the other.
levenshtein(string $string1, string $string2, int $insertion_cost = 1, int $replacement_cost = 1, int $deletion_cost = 1): int- Added in PHP 4.0.1
Parameters
| Parameter | Type | Description |
|---|---|---|
$string1 | string | One of the strings being evaluated for Levenshtein distance. |
$string2 | string | One of the strings being evaluated for Levenshtein distance. |
$insertion_costoptional | int= 1 | Cost charged for inserting a character. |
$replacement_costoptional | int= 1 | Cost charged for replacing a character. |
$deletion_costoptional | int= 1 | Cost charged for deleting a character. |
Return value
intThe Levenshtein distance between the two strings — the minimum total cost of insertions, replacements and deletions needed to turn string1 into string2.
Changelog
- PHP 8.0.0Prior to this version, levenshtein() had to be called with either two or five arguments — three or four was not accepted.
- PHP 8.0.0Prior to this version, levenshtein() returned -1 if one of the argument strings was longer than 255 characters. The limit is gone.
levenshtein answers one question: how many single-character insertions, deletions and replacements does it take to turn the first string into the second? A distance of 0 means the strings are identical; a distance of 1 means one typo apart. This makes it the workhorse behind "did you mean …?" suggestions, fuzzy command matching in CLI tools, and de-duplicating slightly different records.
Reading the number
The result is an absolute count, not a percentage, so it is only meaningful next to the length of the strings. A distance of 3 is close for a 40-character sentence and hopeless for a 4-letter word. Normalise it yourself when you need a ratio: 1 - $distance / max(strlen($a), strlen($b)). If you want a percentage handed to you directly, use similar_text instead.
Weighting the operations
The three cost parameters let you bias the algorithm. Charging more for replacement than for insertion plus deletion (for example replacement_cost = 5) makes the function prefer to model a change as "remove then add", which is useful when you are treating substitutions as more suspicious than length differences. Since PHP 8.0 you may pass three or four arguments; older versions demanded exactly two or five.
The comparison is case-sensitive and byte-based — lowercase both sides first if case should not count.
Example
When to use it
- Suggest the closest valid subcommand when a CLI user mistypes one.
- Flag likely duplicate customer records whose names differ by one or two characters.
- Match a user-supplied form field name against a list of known fields before rejecting it.
Frequently asked questions
What is the difference between levenshtein and similar_text?
levenshtein returns an edit count (lower is better) and runs in O(m·n) time; similar_text returns the number of matching characters plus an optional percentage (higher is better) but is O(n³), so it is far slower on long strings. Use levenshtein for typo detection and similar_text when you want a similarity percentage.
Does levenshtein work with UTF-8 strings?
Not correctly. It compares bytes, so one accented or emoji character counts as two to four edits. For real Unicode edit distance, convert both strings with mb_str_split and implement the comparison yourself, or normalise the text to ASCII first.
Is levenshtein still limited to 255 characters?
No. That limit — which made the function return -1 — was removed in PHP 8.0. Long strings now work, though the cost grows with the product of the two lengths.
More examples
Building a "did you mean?" suggestion
Prints `Did you mean 'username'? (distance 1)`. Only one character is missing from `usrname`, so `username` scores 1 while every other candidate scores much higher. In production you would also reject the suggestion when the best distance is above a threshold, otherwise total nonsense still matches something.
<?php
$known = ['username', 'password', 'email', 'timezone'];
$input = 'usrname';
$best = null;
$bestDistance = PHP_INT_MAX;
foreach ($known as $field) {
$d = levenshtein($input, $field);
if ($d < $bestDistance) {
$bestDistance = $d;
$best = $field;
}
}
echo "Did you mean '$best'? (distance $bestDistance)", PHP_EOL;Bytes, not characters — and case matters
Outputs 2, 5, 5 and 0. The é occupies two bytes in UTF-8, so replacing it with `e` costs two edits rather than one, and `strlen` confirms the four-character string is five bytes long. The last two lines show the comparison is case-sensitive: five differing letters until both sides are lowercased.
<?php
echo levenshtein('Café', 'Cafe'), PHP_EOL;
echo strlen('Café'), PHP_EOL;
echo levenshtein('HELLO', 'hello'), PHP_EOL;
echo levenshtein(strtolower('HELLO'), 'hello'), PHP_EOL;
Discussion