Multibyte strings7 min readExamples run on PHP 8.5.10

mb_strtolower()

Learn how PHP 8.4 mb_strtolower() lowercases Unicode text, how its encoding argument works, when it throws ValueError and how it differs from strtolower().

Signature

mb_strtolower(string $string, ?string $encoding = null): string
Available since PHP 4.3.07.48.08.18.28.38.48.5All versions →

Purpose

mb_strtolower() converts the alphabetic characters of a string to lowercase using Unicode character properties. Use it for text that may contain letters beyond plain ASCII, such as Ä, É or Greek capitals. It belongs to the mbstring family, so it reads the string as characters in a given encoding rather than as single bytes.

The manual notes that, unlike strtolower(), this function is not affected by locale settings. Which characters count as alphabetic comes from Unicode, so the result does not depend on the server's locale.

Signature

Parameters and defaults

  • $string: the text to lowercase. It is required.
  • $encoding: the name of the character encoding, for example 'UTF-8'. The default is null. When it is omitted or null, PHP uses the internal character encoding.

Passing 'UTF-8' explicitly makes your code independent of the runtime's configured internal encoding. It also tells readers which encoding you expect.

Return value and failure cases

The function returns a string in which every alphabetic character has been converted to lowercase. Characters without a lowercase form, such as digits, punctuation and spaces, are left alone.

As of PHP 8.0.0, an invalid $encoding name throws a ValueError. Before PHP 8.0.0 it emitted an E_WARNING and returned false. In PHP 8.4 you never need to check for false, but you may need to catch the exception if the encoding name comes from outside your code.

The changelog also records that PHP 8.3.0 implemented conditional casing rules for the Greek letter sigma. A capital sigma may therefore lowercase differently depending on its position in a word. Do not assume that lowercasing is a simple one-to-one table lookup for every character.

Example 1: ASCII, accents and digits

The first example shows ordinary lowercasing. Accented capitals become accented lowercase letters, digits and punctuation stay as they are, and ß is already lowercase.

Example
<?php
echo mb_strtolower("HELLO World", 'UTF-8'), "\n";
echo mb_strtolower("ÄÖÜ ÉCOLE 123!", 'UTF-8'), "\n";
echo mb_strtolower("Straße", 'UTF-8'), "\n";
Output
hello world
äöü école 123!
straße

Example 2: Comparing with strtolower()

strtolower() works on bytes and only changes ASCII letters in PHP 8. A UTF-8 letter such as Ä spans several bytes, so strtolower() leaves it unchanged. mb_strtolower() converts it.

Example
<?php
$text = "ÄB";
echo json_encode([
    'strtolower' => strtolower($text),
    'mb_strtolower' => mb_strtolower($text, 'UTF-8'),
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
Output
{"strtolower":"Äb","mb_strtolower":"äb"}

Example 3: Handling an invalid encoding

If the encoding name is not recognized, PHP 8 throws a ValueError. Catch it when the encoding is configurable, for example when it comes from user settings.

Example
<?php
try {
    echo mb_strtolower("ABC", "NOT-A-CHARSET");
} catch (ValueError $e) {
    echo get_class($e), "\n";
}
Output
ValueError

Edge cases

  • Empty string: an empty string is returned unchanged.
  • Already lowercase text: the same characters come back.
  • Characters without case: digits, spaces and many scripts have no lowercase form and pass through untouched.
  • Encoding mismatch: the string must really be in the encoding you name. If you pass text in one encoding but declare another, the result is unreliable. Convert the text first with the appropriate mb_* tools.
  • Greek sigma: since PHP 8.3, the function applies conditional casing rules for the Greek capital sigma. Test words that end in sigma if the exact output matters to you.
  • Not a full language rule set: the function follows Unicode properties, not the conventions of a particular language. Language-specific rules, such as those for Turkish dotted and dotless i, need extra care.

Common mistakes

  1. Using strtolower() for user-facing Unicode text. It will leave non-ASCII capitals unchanged. Use mb_strtolower() for names, titles and other international input.
  2. Relying on the default encoding. Omitting $encoding is allowed, but it makes behavior depend on configuration. Pass 'UTF-8' when you know your data is UTF-8.
  3. Checking for false on failure. That was the behavior before PHP 8.0.0. Modern PHP throws ValueError.
  4. Treating lowercasing as a complete comparison strategy. Lowercasing two strings and comparing them handles many cases, but the manual lists mb_convert_case() for case folding, which is designed for case-insensitive matching. Consider it when comparison quality matters.
  5. Assuming string length stays the same. Case conversion can change the number of characters or bytes for some inputs. Do not cache lengths from before the conversion.
  • mb_strtoupper() makes a string uppercase.
  • mb_convert_case() converts case with modes such as case folding.
  • strtolower() is the byte-oriented lowercase function.

Recap

mb_strtolower() returns a lowercase version of a string using Unicode alphabetic properties and an optional encoding. It is independent of locale settings, it can convert letters like Ä that strtolower() leaves alone, and in PHP 8 an invalid encoding raises a ValueError. Pass 'UTF-8' explicitly, and consider mb_convert_case() for case-insensitive comparison.

Tested examples

ASCII, accents and digits
<?php
echo mb_strtolower("HELLO World", 'UTF-8'), "\n";
echo mb_strtolower("ÄÖÜ ÉCOLE 123!", 'UTF-8'), "\n";
echo mb_strtolower("Straße", 'UTF-8'), "\n";
Output
hello world
äöü école 123!
straße
Comparing with strtolower()
<?php
$text = "ÄB";
echo json_encode([
    'strtolower' => strtolower($text),
    'mb_strtolower' => mb_strtolower($text, 'UTF-8'),
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
Output
{"strtolower":"Äb","mb_strtolower":"äb"}
Handling an invalid encoding
<?php
try {
    echo mb_strtolower("ABC", "NOT-A-CHARSET");
} catch (ValueError $e) {
    echo get_class($e), "\n";
}
Output
ValueError