mb_substr_count

Counts how many non-overlapping times a substring occurs in a string.

Signature
mb_substr_count(string $haystack, string $needle, ?string $encoding = null): int
  • Added in PHP 4.3.0

Parameters

ParameterTypeDescription
$haystackstringThe string being searched.
$needlestringThe substring to count. Must not be empty.
$encodingoptional?string= nullCharacter encoding of both strings. Falls back to the internal encoding when null.

Return value

intThe number of times the needle substring occurs in the haystack string.

Errors

Throws a ValueError if needle is an empty string, or if encoding names an unknown character encoding.

Changelog

  • PHP 8.0.0encoding is nullable now.

mb_substr_count() returns how many times needle appears in haystack, counting non-overlapping matches: after a match it resumes searching from the end of that match, so 'ααα' contains 'αα' exactly once, not twice.

When to reach for it

Use it for quick tallies where a regular expression would be overkill — counting delimiters to guess a CSV column count, counting newlines to get a line count, checking how often a term appears in a body of text, or spotting a placeholder that appears more than once in a template.

Unlike the byte-based substr_count(), it cannot report a phantom match caused by a byte sequence that happens to straddle two characters. In UTF-8 that is not actually possible, so for UTF-8 both functions agree; the multibyte version matters in stateful and fixed-width encodings such as ISO-2022-JP and UTF-16.

What trips people up

There is no word-boundary logic. Counting 'το' in Greek text also matches the tail of 'τρίτο', and counting 'is' in English matches inside 'this'. Matching is also case-sensitive — lowercase both sides first if you want a case-insensitive tally. An empty needle throws a ValueError; it does not return 0.

Example

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

When to use it

  • Count the delimiters in a line to validate a CSV row's column count.
  • Check that a template placeholder appears exactly once before substituting it.
  • Tally how often a search term appears in an article for a relevance score.
  • Count newline characters to get the number of lines in a block of text.

Frequently asked questions

Does mb_substr_count count overlapping matches?

No. Searching resumes after the end of each match, so counting 'aa' in 'aaaa' returns 2, not 3. Use preg_match_all with a lookahead such as /(?=aa)/ if you need overlapping counts.

Is mb_substr_count case-insensitive?

No, it is case-sensitive. Apply mb_strtolower() to both the haystack and the needle first, or use preg_match_all with the i and u modifiers.

What happens if the needle is an empty string?

It throws a ValueError: 'mb_substr_count(): Argument #2 ($needle) must not be empty'. Guard against empty needles rather than relying on a zero return.

More examples

Case sensitivity and word boundaries

The first count is 3: two standalone "το" plus the ending of "τρίτο", while the capitalised "Το" is missed. Lowercasing first raises it to 4. Only preg_match_all with word boundaries gives the 3 real words, which is the number most people actually wanted.

Example · php
<?php
$text = "Το ένα και το άλλο και το τρίτο";

echo mb_substr_count($text, "το"), "\n";
echo mb_substr_count(mb_strtolower($text), "το"), "\n";
echo preg_match_all("/\\bτο\\b/u", mb_strtolower($text)), "\n";

Validating a template's placeholders

{nom} is reported twice and {date} zero times, which is enough to reject the template before rendering. The last line counts the accented é twice, showing that the needle can itself be a multibyte character.

Example · php
<?php
$template = "Bonjour {nom}, votre café {nom} est prêt.";

foreach (["{nom}", "{date}"] as $key) {
    $n = mb_substr_count($template, $key);
    echo $key, " appears ", $n, " time(s)\n";
}
echo mb_substr_count($template, "é"), "\n";

Discussion

  • Be the first to comment on this lesson.