Multibyte strings7 min readExamples run on PHP 8.5.10

mb_strtoupper()

Learn how mb_strtoupper() converts Unicode text to uppercase in PHP 8.4, how it differs from strtoupper(), and which mistakes to avoid.

Signature

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

Purpose

mb_strtoupper() converts the alphabetic characters of a string to uppercase using Unicode character properties. Unlike a byte-oriented function, it understands that a character such as é, ö or a Greek letter may occupy several bytes in UTF-8, and it can map those characters to their uppercase forms. Use it whenever your text may contain anything beyond plain ASCII, such as names, headings, user input or translated labels.

Signature

Parameters and defaults

  • $string is the text to convert.
  • $encoding names the character encoding of $string, for example 'UTF-8'. If you omit it or pass null, PHP uses the current internal encoding of the mbstring extension.

Relying on the internal encoding makes code depend on configuration. Passing 'UTF-8' explicitly makes the intent obvious and keeps the call predictable across environments.

Return value and failures

The function returns a new string in which every character with an uppercase mapping has been converted. Characters without such a mapping, like digits, spaces and punctuation, pass through unchanged. The original variable is not modified.

If $encoding is not a valid encoding name, PHP 8 throws a ValueError. Before PHP 8.0 this situation produced a warning and a false result, so older tutorials may show different failure handling.

Example 1: Unicode versus ASCII-only conversion

The byte-based strtoupper() only changes ASCII letters in PHP 8.4, while mb_strtoupper() also handles accented letters.

Example
<?php
$text = "héllo wörld";
echo json_encode([
    "mb" => mb_strtoupper($text, 'UTF-8'),
    "ascii_only" => strtoupper($text),
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
Output
{"mb":"HÉLLO WÖRLD","ascii_only":"HéLLO WöRLD"}

The mb value has É and Ö, whereas the ascii_only value leaves é and ö untouched.

Example 2: The result can have a different length

Uppercase mapping is not always one character to one character. The German sharp s expands to two letters, so the converted string can be longer than the original.

Example
<?php
$word = "Straße";
$upper = mb_strtoupper($word, 'UTF-8');
echo json_encode([
    "upper" => $upper,
    "before" => mb_strlen($word, 'UTF-8'),
    "after" => mb_strlen($upper, 'UTF-8'),
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
Output
{"upper":"STRASSE","before":6,"after":7}

Because of this, never assume that character positions in the input line up with positions in the output.

Example 3: Handling an invalid encoding name

An unknown encoding is a programmer error and raises an exception that you can catch when the name comes from outside your code.

Example
<?php
try {
    mb_strtoupper("abc", "not-an-encoding");
} catch (ValueError $e) {
    echo get_class($e), "\n";
}
Output
ValueError

Edge cases

  • Empty string: the result is an empty string.
  • Already uppercase or caseless text: the text is returned unchanged in content. Scripts without letter case, such as many East Asian scripts, are not altered.
  • Locale independence: the behavior follows Unicode properties and is not affected by locale settings, which is a key difference from locale-sensitive byte functions.
  • Mismatched encoding: if the bytes are not valid in the declared encoding, the output is not meaningful. Validate untrusted input with mb_check_encoding() before converting.
  • Length changes: as shown above, some characters map to several characters, so uppercase output may be longer.

Common mistakes

  1. Using strtoupper() for international text. It only handles ASCII letters, so accented and non-Latin characters stay lowercase.
  2. Omitting the encoding and trusting defaults. A different internal encoding in another environment can change results. Pass 'UTF-8' when your data is UTF-8.
  3. Comparing case-insensitively by uppercasing both sides without thinking. For some languages a simple uppercase comparison is not a perfect equality test. For comparisons, consider whether case folding via mb_convert_case() fits better.
  4. Assuming the original string changes. The function returns a new value; assign it to a variable or use it directly.
  5. Ignoring the exception. Invalid encoding names throw ValueError in PHP 8, rather than returning false.
  • mb_strtolower() makes a string lowercase with the same multibyte awareness.
  • mb_convert_case() performs several kinds of case conversion, including case folding.
  • strtoupper() is the byte-oriented counterpart for ASCII-only needs.
  • mb_strlen() counts characters in a given encoding and is useful when checking length changes.
  • mb_check_encoding() verifies that a string is valid in an encoding before you process it.

Recap

mb_strtoupper() returns an uppercase copy of a string based on Unicode alphabetic properties. Pass 'UTF-8' explicitly, expect that the result may be longer than the input, catch ValueError if the encoding name is dynamic, and choose strtoupper() only when ASCII behavior is really what you want.

Tested examples

Unicode versus ASCII-only conversion
<?php
$text = "héllo wörld";
echo json_encode([
    "mb" => mb_strtoupper($text, 'UTF-8'),
    "ascii_only" => strtoupper($text),
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
Output
{"mb":"HÉLLO WÖRLD","ascii_only":"HéLLO WöRLD"}
The result can have a different length
<?php
$word = "Straße";
$upper = mb_strtoupper($word, 'UTF-8');
echo json_encode([
    "upper" => $upper,
    "before" => mb_strlen($word, 'UTF-8'),
    "after" => mb_strlen($upper, 'UTF-8'),
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
Output
{"upper":"STRASSE","before":6,"after":7}
Handling an invalid encoding name
<?php
try {
    mb_strtoupper("abc", "not-an-encoding");
} catch (ValueError $e) {
    echo get_class($e), "\n";
}
Output
ValueError