substr_compare()
The substr_compare() function in PHP is used to compare two strings from a specified start position up to a specified length. This function is useful when
Introduction
The substr_compare() function compares a portion of one string with another string, starting at a given offset. It works like comparing a substring of $main_str against $str — but without you having to call substr() first to slice the string out. That makes it the idiomatic, allocation-free way to answer questions such as "does this string start with X?" or "does this string end with Y?" in older PHP versions.
This chapter explains the parameters, the meaning of the return value, and the common patterns (prefix checks, suffix checks, case-insensitive comparison) where substr_compare() shines.
Syntax
substr_compare(
string $haystack,
string $needle,
int $offset,
?int $length = null,
bool $case_insensitive = false
): intNote: The
$lengthparameter is optional. In PHP 8+ it is explicitly typed as nullable (?int); passingnull(or omitting it) compares to the end of$haystack.
| Parameter | Description |
|---|---|
$haystack | The main string. The comparison reads a slice of this string. |
$needle | The string compared against the slice of $haystack. |
$offset | Where in $haystack the comparison starts. A negative value counts back from the end of $haystack. |
$length | How many characters to compare. If null, the longer of the remaining $haystack and the whole $needle is used. |
$case_insensitive | If true, letter case is ignored ("A" equals "a"). Defaults to false. |
Return value
substr_compare() returns an int:
0— the compared portions are equal.- a negative number — the slice of
$haystackis less than$needle(sorts earlier). - a positive number — the slice of
$haystackis greater than$needle(sorts later).
The sign is what matters; the exact magnitude is implementation-defined and should not be relied on. To test for equality, compare against 0 explicitly: substr_compare(...) === 0.
Basic example
Here we compare the first strlen($string2) (6) characters of "Hello World!" against "Hello!". The sixth character is a space (" ") in the first string but an exclamation mark ("!") in the second; a space (ASCII 32) sorts before ! (ASCII 33), so the function returns a negative integer (-1).
Checking if a string starts with a prefix
Comparing at offset 0 with $length equal to the prefix length is a clean "starts with" test:
<?php
function startsWith(string $haystack, string $prefix): bool
{
return substr_compare($haystack, $prefix, 0, strlen($prefix)) === 0;
}
var_dump(startsWith("php-fpm", "php")); // bool(true)
var_dump(startsWith("python", "php")); // bool(false)On PHP 8.0+ you can use the built-in
str_starts_with()instead.substr_compare()remains useful on older runtimes and when you also need case-insensitivity.
Checking if a string ends with a suffix
A negative offset counts from the end of the string, which makes "ends with" checks effortless — you don't need to compute the position yourself:
<?php
function endsWith(string $haystack, string $suffix): bool
{
return substr_compare($haystack, $suffix, -strlen($suffix)) === 0;
}
var_dump(endsWith("report.pdf", ".pdf")); // bool(true)
var_dump(endsWith("report.txt", ".pdf")); // bool(false)Case-insensitive comparison
Set the fifth argument to true to ignore letter case:
<?php
$sensitive = substr_compare("Hello", "hello", 0);
$insensitive = substr_compare("Hello", "hello", 0, null, true);
echo $sensitive; // non-zero: 'H' and 'h' differ
echo PHP_EOL;
echo $insensitive; // 0: equal when case is ignoredGotchas
- Offset out of range. A positive
$offsetgreater than the length of$haystackraises aValueErrorin PHP 8+ (a warning returningfalsein PHP 7). Validate offsets againststrlen($haystack)when they come from user input. $lengthlonger than what remains. If$lengthexceeds the available characters, only the available characters are compared — the function does not error, it simply compares less.- Byte-based, not multibyte-aware.
substr_compare()works on bytes. For UTF-8 text where one character may span several bytes, offsets and lengths are byte offsets, not character offsets.
Related functions
strcmp()— binary-safe comparison of two whole strings.strncmp()— compares only the first n characters of two strings.strcasecmp()— case-insensitive comparison of two whole strings.substr()— extract a portion of a string.
Conclusion
substr_compare() compares a slice of one string against another without first extracting it, which makes it efficient for prefix and suffix checks. Remember to test the result against 0 for equality, use a negative $offset for "ends with" tests, and pass true as the last argument when case should be ignored.