levenshtein

Calculates the edit distance between two strings — the number of changes needed to turn one into the other.

Signature
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

ParameterTypeDescription
$string1stringOne of the strings being evaluated for Levenshtein distance.
$string2stringOne of the strings being evaluated for Levenshtein distance.
$insertion_costoptionalint= 1Cost charged for inserting a character.
$replacement_costoptionalint= 1Cost charged for replacing a character.
$deletion_costoptionalint= 1Cost 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

Try it yourself
Loading editor…
Press Run to execute the code.

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.

Example · php
<?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.

Example · php
<?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

  • Be the first to comment on this lesson.