Multibyte strings7 min readExamples run on PHP 8.5.10

mb_ucfirst()

Learn how PHP 8.4 mb_ucfirst() title-cases the first character of a UTF-8 string, how it differs from ucfirst(), and which edge cases to expect.

Signature

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

Purpose

mb_ucfirst() is the multibyte-safe counterpart of ucfirst(). It takes a string and returns a copy in which the first character is title-cased. The rest of the string is left alone. It is part of the mbstring extension and is documented as available from PHP 8.4.0.

The point of the function is that it works on characters, not bytes. In UTF-8, a letter such as é or ñ takes more than one byte, so a byte-oriented function cannot reliably recognise it as a lowercase letter. mb_ucfirst() decodes the first character according to the encoding and uses Unicode character properties to decide what its title-case form is.

Signature

Parameters and defaults

  • $string is the input text. It is not modified; a new string is returned.
  • $encoding names the character encoding of the input, for example "UTF-8". The default is null, which means PHP uses the configured internal encoding. Passing the encoding explicitly makes your code independent of that setting, so it is a good habit in code that must behave the same everywhere.

Return value and failure cases

The function always returns a string. If the first character has no title-case form, for instance a digit, a space or an uppercase letter, the string comes back unchanged. An empty string returns an empty string.

An unknown encoding name is a caller error: mbstring functions reject it by throwing a ValueError. Because the encoding argument is normally a literal or a configuration value, you rarely need to catch this, but do not build it from untrusted input without validating it.

The manual also notes that case conversion here follows Unicode character properties and is not affected by locale settings. That is a major difference from the old behaviour of byte-based case functions.

Example 1: capitalizing a name

The first example capitalizes a lowercase name that starts with an accented letter.

Example
<?php
$name = "élodie";
echo json_encode(mb_ucfirst($name, "UTF-8"), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
Output
"Élodie"

The leading é becomes É, and the remaining letters are untouched.

Example 2: comparing with ucfirst()

ucfirst() works on single bytes and only changes ASCII letters, so it does nothing to a word that starts with é. mb_ucfirst() handles it.

Example
<?php
$word = "élan";
echo json_encode([
    "ucfirst" => ucfirst($word),
    "mb_ucfirst" => mb_ucfirst($word, "UTF-8"),
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
Output
{"ucfirst":"élan","mb_ucfirst":"Élan"}

The result shows the unchanged word from ucfirst() next to the capitalized word from mb_ucfirst().

Example 3: mixed inputs in a list

Real data is messy. This example runs several inputs through the function to show what changes and what does not.

Example
<?php
$items = ["", "123abc", " ñandú", "ñandú", "Ärger"];
$result = array_map(fn(string $s): string => mb_ucfirst($s, "UTF-8"), $items);
echo json_encode($result, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
Output
["","123abc"," ñandú","Ñandú","Ärger"]

Only ñandú changes. The empty string stays empty, 123abc begins with a digit, the third item begins with a space, and Ärger is already capitalized.

Edge cases

  • The first character is not a letter. Leading spaces, digits, quotes or punctuation are the first character, so nothing is capitalized. If your input might have leading whitespace, call trim() or ltrim() first.
  • Title case is not always the same as upper case. The function title-cases the first character. For most letters this equals the uppercase form, but Unicode has a few characters, such as some digraph letters, where the two differ. If you need strict uppercase for the first character, check which conversion your use case requires.
  • Only one character is changed. The function does not lowercase the rest of the string. To normalise a shout-case input such as "ÉLAN" to "Élan", lowercase it first with mb_strtolower() and then call mb_ucfirst().
  • Only the start of the whole string. Words after the first are not touched. For capitalizing every word, mb_convert_case() with a title mode is the better tool.

Common mistakes

  1. Using ucfirst() on UTF-8 text. It appears to work for English words and silently fails for names that begin with accented letters.
  2. Passing the wrong encoding. Telling the function that UTF-8 text is in another encoding can produce garbled output. Use the encoding your data really has.
  3. Relying on the default encoding. The default depends on configuration. Passing "UTF-8" explicitly removes that dependency.
  4. Expecting word-by-word capitalization. mb_ucfirst() only touches the very first character.
  5. Forgetting that it is a PHP 8.4 function. Code that must run on older PHP versions cannot call it without a fallback of your own.
  • ucfirst() is the byte-based version for ASCII text.
  • mb_lcfirst() lowercases the first character instead.
  • mb_convert_case() converts the case of a whole string, including title-casing every word.
  • mb_strtolower() and mb_strtoupper() change the case of the entire string.

Recap

mb_ucfirst() returns a new string whose first character is title-cased using Unicode properties, independent of locale. Pass the encoding explicitly, remember that it changes only the first character, and use ucfirst() only when you know the text is plain ASCII.

Tested examples

Capitalizing a name that starts with an accented letter
<?php
$name = "élodie";
echo json_encode(mb_ucfirst($name, "UTF-8"), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
Output
"Élodie"
Comparing ucfirst() with mb_ucfirst()
<?php
$word = "élan";
echo json_encode([
    "ucfirst" => ucfirst($word),
    "mb_ucfirst" => mb_ucfirst($word, "UTF-8"),
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
Output
{"ucfirst":"élan","mb_ucfirst":"Élan"}
Mixed inputs in a list
<?php
$items = ["", "123abc", " ñandú", "ñandú", "Ärger"];
$result = array_map(fn(string $s): string => mb_ucfirst($s, "UTF-8"), $items);
echo json_encode($result, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
Output
["","123abc"," ñandú","Ñandú","Ärger"]