str_ends_with

Checks whether a string ends with a given substring, case-sensitively.

Signature
str_ends_with(string $haystack, string $needle): bool
  • Added in PHP 8.0

Parameters

ParameterTypeDescription
$haystackstringThe string to search in.
$needlestringThe substring to look for at the end of haystack.

Return value

booltrue if haystack ends with needle, false otherwise.

Errors

Throws a TypeError if a non-string is passed under strict_types; otherwise no errors are raised.

str_ends_with replaces the old substr($s, -strlen($n)) === $n dance with a single readable call. It compares bytes from the end of the string and returns a boolean.

When to reach for it

The obvious use is suffix checks: does this filename end in .php, does this URL end in a trailing slash, does this hostname end in your domain. It is also handy for cheap format sniffing before a heavier parse — skip anything not ending in .json rather than decoding every file.

What trips people up

It is case-sensitive, so Report.CSV does not end with .csv. Filenames arriving from a filesystem or an upload routinely carry uppercase extensions; lowercase the haystack first, or use pathinfo($f, PATHINFO_EXTENSION) when you specifically want the extension. An empty needle always returns true.

For security checks, a suffix test is weaker than it looks. str_ends_with($host, 'example.com') also matches notexample.com; compare against '.example.com' or parse the host properly. The same applies to path checks, where .. segments make suffix matching unreliable.

Example

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

When to use it

  • Filter a directory listing down to files ending in .csv
  • Check whether a URL already has a trailing slash before appending one
  • Detect a specific file extension before choosing a parser
  • Verify a hostname belongs to a given domain suffix

Frequently asked questions

What is the difference between str_ends_with and substr?

str_ends_with returns a boolean and handles the length arithmetic for you; the substr equivalent, substr($s, -strlen($n)) === $n, is longer and breaks when the needle is empty. Prefer str_ends_with on PHP 8.0 and later.

Is str_ends_with case-insensitive?

No, it compares bytes exactly. Lowercase both values first, or use substr_compare with its case-insensitive flag, if case should not matter.

Does str_ends_with return true for an empty needle?

Yes. Every string ends with the empty string, so the result is always true. Guard against empty input if the needle comes from a user.

More examples

Normalising a trailing slash

Each path is given exactly one trailing slash. The middle input already ends in a slash so it is left alone, which is the point: a blind concatenation would have produced a double slash there.

Example · php
<?php
$paths = ['https://example.com/api', 'https://example.com/api/', '/tmp'];

foreach ($paths as $p) {
    $normalised = str_ends_with($p, '/') ? $p : $p . '/';
    echo $normalised, "\n";
}

Why a bare suffix check is not a domain check

The naive check accepts evilexample.com because that string genuinely ends with "example.com". The safe check requires either an exact match or a leading dot, which rejects the lookalike host. This is the difference between a string test and an ownership test.

Example · php
<?php
$hosts = ['app.example.com', 'example.com', 'evilexample.com'];

foreach ($hosts as $h) {
    $naive = str_ends_with($h, 'example.com');
    $safe = $h === 'example.com' || str_ends_with($h, '.example.com');
    printf("%-18s naive:%-5s safe:%s\n", $h, var_export($naive, true), var_export($safe, true));
}

Discussion

  • Be the first to comment on this lesson.