Signature
similar_text(string $string1, string $string2, float &$percent = null): intPurpose
similar_text() measures how alike two strings are. It returns the number of characters the strings have in common, and it can also give you that number as a percentage through a by-reference argument. It is handy for rough "did you mean...?" suggestions, loose matching of short labels, and quick sanity checks on user input.
Signature
Parameters and defaults
$string1: the first string.$string2: the second string.$percent: optional. If you pass a variable, PHP stores the similarity as a float in it. The value is the match count divided by the average of the two string lengths, times 100. In effect it issim * 2 / (len1 + len2) * 100.
Because $percent is a reference, you must pass a variable, not a literal or an expression.
Return value and failure cases
The function returns an int: the total length of the matching pieces. It does not return false. If both strings are empty, the result is 0 and the percent is set to 0.
The algorithm works like this: find the longest common substring, then repeat the process on the parts to the left and to the right of that match, and add up the lengths of everything found. Comparison is case-sensitive and works on bytes, so it is not aware of multibyte characters.
In the default non-strict mode, scalar arguments are converted to strings. Passing an array, or an object that cannot be converted to a string, raises a TypeError.
Example 1: count and percent
World and Word share Wor, and then the leftover ld and d share d. That makes 4 matching characters. The percent is 4 * 2 / (5 + 4) * 100, about 88.89.
$sim = similar_text('World', 'Word', $percent);
echo json_encode(['sim' => $sim, 'percent' => round($percent, 2)]) . "\n";{"sim":4,"percent":88.89}
Rounding the percent keeps output stable and readable, because the raw float has many decimal places.
Example 2: argument order matters
The algorithm is not symmetric. Which string comes first can change which common substring is picked first, and that changes the recursion on each side.
$a = similar_text('bafoobar', 'barfoo', $p1);
$b = similar_text('barfoo', 'bafoobar', $p2);
echo json_encode([
'first' => ['similarity' => $a, 'percent' => round($p1, 2)],
'second' => ['similarity' => $b, 'percent' => round($p2, 2)],
]) . "\n";{"first":{"similarity":5,"percent":71.43},"second":{"similarity":3,"percent":42.86}}
The same two strings give 5 in one order and 3 in the other. If you need a stable score, define a fixed order, for example by sorting the two strings first.
Example 3: choosing the closest candidate
A common use is ranking candidates against a target. Here colour shares colo plus a trailing r with color, so it wins.
$target = 'color';
$scores = [];
foreach (['colour', 'collar', 'dolor'] as $candidate) {
$scores[$candidate] = similar_text($target, $candidate);
}
$best = array_search(max($scores), $scores);
echo json_encode(['scores' => $scores, 'best' => $best]) . "\n";{"scores":{"colour":5,"collar":4,"dolor":4},"best":"colour"}
When two candidates tie, array_search() returns the first one, so decide up front how ties should be handled.
Edge cases
- Case sensitivity:
'PHP'and'php'have no matching bytes. Normalize case first, for example withstrtolower(), if you want case-insensitive scoring. - Empty input: if one string is empty, the count is
0. If both are empty, the percent is0too, so you will not hit a division by zero. - Repeated characters: only non-overlapping pieces found by the recursive split are counted. A character in one string cannot be matched twice.
- Multibyte text: accented or non-Latin characters are compared as bytes, so results can look odd for UTF-8 text.
- Performance: the documented complexity is O(N**3) in the length of the longer string. Avoid running it over very long strings or very large candidate lists.
Common mistakes
- Assuming symmetry. Comparing
A, BandB, Amay give different scores, as Example 2 shows. - Passing a non-variable as
$percent. The argument is by reference, so it needs a real variable. - Treating the count as a distance. A bigger number means more similar, the opposite of
levenshtein(), where smaller means more similar. - Comparing raw counts across different lengths. A count of 4 means something different for two-word phrases than for long sentences. Use the percent for that.
- Printing the raw percent. The float has many digits. Round it before display or comparison.
- Using it for phonetic matching. It compares characters, not sounds. For sound-based comparison, look at
soundex()ormetaphone().
Related functions
levenshtein(): the number of edits needed to turn one string into another.soundex(): a phonetic key for English-like words.metaphone(): a more refined phonetic key.strcmp(): exact, ordered comparison.
Recap
similar_text() returns the total length of the matching pieces found by repeatedly taking the longest common substring and recursing on both sides. The optional by-reference $percent gives a relative score. The result depends on argument order, is case-sensitive and byte-based, and gets slow on long input. Use it for rough ranking of short strings, and pick another function when you need edit distance or phonetic matching.
Tested examples
$sim = similar_text('World', 'Word', $percent);
echo json_encode(['sim' => $sim, 'percent' => round($percent, 2)]) . "\n";
{"sim":4,"percent":88.89}
$a = similar_text('bafoobar', 'barfoo', $p1);
$b = similar_text('barfoo', 'bafoobar', $p2);
echo json_encode([
'first' => ['similarity' => $a, 'percent' => round($p1, 2)],
'second' => ['similarity' => $b, 'percent' => round($p2, 2)],
]) . "\n";
{"first":{"similarity":5,"percent":71.43},"second":{"similarity":3,"percent":42.86}}
$target = 'color';
$scores = [];
foreach (['colour', 'collar', 'dolor'] as $candidate) {
$scores[$candidate] = similar_text($target, $candidate);
}
$best = array_search(max($scores), $scores);
echo json_encode(['scores' => $scores, 'best' => $best]) . "\n";
{"scores":{"colour":5,"collar":4,"dolor":4},"best":"colour"}