substr_count

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

Signature
substr_count(string $haystack, string $needle, int $offset = 0, ?int $length = null): int
  • Added in PHP 4.0

Parameters

ParameterTypeDescription
$haystackstringThe string to search in.
$needlestringThe substring to count. May not be an empty string.
$offsetoptionalint= 0The position at which counting starts. Negative values count back from the end of the string.
$lengthoptional?int= nullThe maximum number of bytes to search after offset. A negative value stops that many bytes before the end of haystack. null searches to the end.

Return value

intThe number of times needle occurs in the searched portion of haystack.

Errors

Throws a ValueError if needle is an empty string, or if offset or length falls outside the haystack.

Changelog

  • PHP 8.0.0length is nullable now.
  • PHP 7.1.0Negative offset and length values are now supported, and length may be 0.

substr_count counts occurrences of needle in haystack, moving past each match before looking for the next. That means matches never overlap: substr_count('aaa', 'aa') is 1, not 2.

When to reach for it

It is the fastest way to answer "how many times" for a literal substring — counting newlines to get a line number, counting a delimiter to validate a CSV row, or checking that a template has exactly one placeholder. Compared with preg_match_all it does no pattern compilation, and compared with count(explode(...)) it builds no array at all.

What trips people up

Overlapping matches are the number one surprise; if you need them, use preg_match_all with a lookahead. An empty needle throws a ValueError rather than returning zero, so validate any needle that comes from user input.

The offset and length arguments are measured in bytes and are validated strictly: an offset past the end of the string throws instead of returning 0. And, like the rest of the str* family, this counts byte sequences — counting a multibyte character works because you are matching whole sequences, but a length limit can cut one in half.

Example

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

When to use it

  • Count newlines in a buffer to report the line number of a parse error.
  • Verify that every row of a CSV chunk has the expected number of separators before splitting it.
  • Check that a message template contains exactly one occurrence of a required placeholder.
  • Measure how often a search term appears in a document for a simple relevance score.

Frequently asked questions

Does substr_count count overlapping occurrences?

No. It resumes searching after the end of each match, so substr_count('aaaa', 'aa') is 2. Use preg_match_all with a lookahead such as /(?=aa)/ to count overlaps.

Is substr_count case-sensitive?

Yes. Lowercase both strings first, or use preg_match_all with the i modifier, if case should be ignored.

What happens if the needle is an empty string?

It throws a ValueError. Check that the needle is non-empty before calling substr_count with a value that came from input.

More examples

Reporting the line a match is on

Prints 'found on line 3' and 'total lines: 4'. Counting newlines only up to the match position — using the length argument — is the cheapest way to turn a byte offset into a line number.

Example · php
<?php
$doc = "alpha\nbeta\ngamma\ndelta\n";
$pos = strpos($doc, 'gamma');

$line = substr_count($doc, "\n", 0, $pos) + 1;
echo "found on line $line\n";
echo 'total lines: ', substr_count($doc, "\n"), "\n";

Counting overlapping matches instead

substr_count reports 2 because it resumes after each match, while the zero-width lookahead pattern reports 3, catching the overlap at offset 2. Use the regex form whenever overlaps count.

Example · php
<?php
$s = 'abababa';

echo substr_count($s, 'aba'), "\n";                  // 2
preg_match_all('/(?=aba)/', $s, $m);
echo count($m[0]), "\n";                             // 3

Discussion

  • Be the first to comment on this lesson.