Strings7 min readExamples run on PHP 8.5.10

strnatcasecmp()

Learn how strnatcasecmp() compares strings case-insensitively in natural order, how to read its result, and how to use it with usort().

Signature

strnatcasecmp(string $string1, string $string2): int

Purpose

strnatcasecmp() compares two strings the way a person would read them, ignoring letter case. The "natural order" part matters when strings contain digits. A plain comparison goes character by character, so "file10" sorts before "file9" because 1 comes before 9. A natural comparison reads each run of digits as one number, so "file9" comes before "file10".

The function is the case-insensitive sibling of strnatcmp(). Use it for file names, version-like labels, product codes, or any list where people expect item2 to come before item10 whatever the capitalization.

Signature

Parameters and defaults

  • $string1: the first string to compare.
  • $string2: the second string to compare.

Both parameters are required and there are no optional parameters. In a file without strict types, scalar values such as integers are converted to strings. Passing an array or other non-stringable value throws a TypeError.

Return value and failure cases

The function returns an int:

  • a value less than 0 when $string1 sorts before $string2;
  • a value greater than 0 when $string1 sorts after $string2;
  • 0 when the strings are equal for this comparison.

Only the sign is meaningful. The manual notes that since PHP 8.2.0 the function is no longer guaranteed to return the length difference for strings of unequal length, and may return -1 or 1 instead. Compare the result with < 0, > 0 or === 0 rather than relying on a particular number.

There is no false return and no warning for ordinary strings. The only failure case is a type error for unsuitable arguments.

Example 1: reading the result

This is the basic behavior. Differences in case do not matter, so apple and Apple are equal.

Example
<?php

var_dump(strnatcasecmp('Apple', 'Banana'));
var_dump(strnatcasecmp('Banana', 'Apple'));
var_dump(strnatcasecmp('apple', 'Apple'));
Output
int(-1)
int(1)
int(0)

Example 2: natural order versus plain order

Here strcasecmp() stops at 1 versus 9, while strnatcasecmp() sees the numbers 10 and 9. The code reduces each result to its sign with <=> 0 so the output does not depend on the exact number returned.

Example
<?php

$a = 'File10.txt';
$b = 'file9.txt';

$result = [
    'natural' => strnatcasecmp($a, $b) <=> 0,
    'plain' => strcasecmp($a, $b) <=> 0,
];

echo json_encode($result, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
Output
{"natural":1,"plain":-1}

Example 3: sorting with usort()

Because the function takes two strings and returns a signed integer, it works directly as a usort() callback. The mixed capitalization below does not disturb the order.

Example
<?php

$files = ['Img12.png', 'img10.png', 'IMG2.png', 'img1.png'];

usort($files, 'strnatcasecmp');

echo json_encode($files, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
Output
["img1.png","IMG2.png","img10.png","Img12.png"]

Edge cases

  • Empty strings. An empty string sorts before any non-empty string.
  • Digits only at one position. Natural comparison only changes how runs of digits are treated. Text parts are still compared character by character, ignoring case.
  • Dotted numbers. A value such as 1.10 is not treated as a decimal number or a semantic version. The digit runs are compared separately. For real version numbers, version_compare() is the better tool.
  • Leading zeros and spaces. These can influence natural ordering in ways that are easy to overlook. If your data may contain them, test those cases or normalize the strings first.
  • Non-ASCII text. The function works on bytes and is not a multibyte-aware collation. Do not use it for language-specific alphabetical ordering of accented or non-Latin text.

Common mistakes

  1. Checking for -1 or 1 only. The sign is the contract. Write strnatcasecmp($a, $b) < 0 instead of comparing with a fixed number.
  2. Treating the result as a boolean. 0 means the strings are equal, and 0 is falsy. Using if (strnatcasecmp($a, $b)) actually tests for inequality.
  3. Using plain comparison for numbered names. strcasecmp() and sort() with default flags give item10 before item2 in many lists.
  4. Expecting numeric equality. "a1" and "a01" are different strings. Do not assume natural order makes them interchangeable.
  5. Using it for equality checks on secrets. It is an ordering helper, not a safe comparison for tokens or passwords.
  • strnatcmp(): the same natural-order comparison, but case-sensitive.
  • strcasecmp(): binary-safe case-insensitive comparison without natural number handling.
  • strcmp(): binary-safe, case-sensitive comparison.
  • strncasecmp() and strncmp(): compare only the first n characters.
  • natcasesort(): sorts an array in place using the same idea.
  • version_compare(): compares version strings.

Recap

strnatcasecmp() takes two strings and returns a negative number, zero, or a positive number according to a human-friendly, case-insensitive order. Treat only the sign as meaningful, remember that 0 means equal, and reach for it as a usort() callback when digits inside strings should count as numbers.

Tested examples

Reading the comparison result
<?php

var_dump(strnatcasecmp('Apple', 'Banana'));
var_dump(strnatcasecmp('Banana', 'Apple'));
var_dump(strnatcasecmp('apple', 'Apple'));
Output
int(-1)
int(1)
int(0)
Natural order versus plain case-insensitive order
<?php

$a = 'File10.txt';
$b = 'file9.txt';

$result = [
    'natural' => strnatcasecmp($a, $b) <=> 0,
    'plain' => strcasecmp($a, $b) <=> 0,
];

echo json_encode($result, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
Output
{"natural":1,"plain":-1}
Sorting file names with usort()
<?php

$files = ['Img12.png', 'img10.png', 'IMG2.png', 'img1.png'];

usort($files, 'strnatcasecmp');

echo json_encode($files, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
Output
["img1.png","IMG2.png","img10.png","Img12.png"]