Signature
ctype_upper(mixed $text): boolPurpose
ctype_upper() answers one narrow question: is every character in a string an uppercase letter? It is a quick validation helper for short, strict inputs such as region codes, grade labels or tokens that must be all capitals. It does not convert case and it does not look inside the string for a pattern. It only reports true or false.
Signature
The function belongs to the ctype extension, which provides small character-class checks that work byte by byte.
Parameters and defaults
$text is the value to test. There are no other parameters and no defaults. You should pass a string.
The manual documents a legacy behaviour for integers. An int from -128 to 255 is treated as the ASCII code of a single character, and any other int is treated as a string of its decimal digits. Since PHP 8.1 passing a non-string argument is deprecated, and the manual says the argument will be read as a string in the future. If you have a number, cast it with (string) when you mean digits, or use chr() when you mean a character code. Being explicit keeps your code stable and avoids deprecation notices.
Return value and failure cases
The function returns true only when every character is an uppercase letter in the current locale. Otherwise it returns false. It does not throw for ordinary strings, and an empty string always gives false, which is different from what some "all items match" checks would suggest.
Because the check is per character, one bad character is enough to fail. Digits, spaces, punctuation, underscores and line breaks all make the result false.
Example 1: basic strings
The first example runs several ASCII strings through the function and prints the results as JSON.
$inputs = ['LMNSDO', 'AKLWC139', 'akwSKWsm', 'HELLO WORLD'];
$results = [];
foreach ($inputs as $input) {
$results[$input] = ctype_upper($input);
}
echo json_encode($results), "\n";{"LMNSDO":true,"AKLWC139":false,"akwSKWsm":false,"HELLO WORLD":false}
Only LMNSDO passes. AKLWC139 contains digits, akwSKWsm mixes cases, and HELLO WORLD contains a space.
Example 2: validating a fixed-length code
ctype_upper() checks the character class but not the length, so combine it with strlen() when the format has a fixed size.
function isRegionCode(string $value): bool
{
return strlen($value) === 2 && ctype_upper($value);
}
$values = ['US', 'us', 'U1', 'USA', ''];
echo json_encode(array_map('isRegionCode', $values)), "\n";[true,false,false,false,false]
Only US is accepted. Lowercase, a digit, a third letter and an empty string are all rejected.
Example 3: edge cases and explicit conversions
This example shows the empty string, trailing whitespace and the two explicit ways to handle numbers.
$raw = "ABC\n";
$checks = [
'empty' => ctype_upper(''),
'newline' => ctype_upper($raw),
'trimmed' => ctype_upper(trim($raw)),
'digits string' => ctype_upper((string) 65),
'chr' => ctype_upper(chr(65)),
];
echo json_encode($checks), "\n";{"empty":false,"newline":false,"trimmed":true,"digits string":false,"chr":true}
The newline at the end of $raw is a character that is not an uppercase letter, so the first check fails until the value is trimmed. Casting 65 to a string produces the digits 6 and 5, which are not letters. chr(65) produces A, which is.
Edge cases
- Empty string: always
false. If empty input should be allowed, test for it separately. - Whitespace: spaces, tabs and newlines are not uppercase letters. Trim user input first if surrounding whitespace is acceptable.
- Locale: the definition of an uppercase letter depends on the current locale. With the default setup, stick to ASCII
AtoZfor predictable results. Changing it withsetlocale()can alter how bytes above 127 are classified, so such code is harder to reproduce across servers. - Multibyte text: the check is byte-oriented. Do not rely on it for UTF-8 text with accents or non-Latin scripts. For code point properties, the manual points to
IntlChar::isupper()when the intl extension is available. - Non-string input: integers keep their legacy ASCII-code interpretation but are deprecated as of PHP 8.1. Convert them explicitly.
Common mistakes
- Expecting an empty string to pass. It returns
false. - Forgetting digits and separators. A code like
AB-12is not all uppercase letters. If digits are allowed, use a different check or a regular expression. - Passing integers on purpose.
ctype_upper(65)relies on deprecated behaviour. Usechr(65)or a string instead. - Using it as a case-conversion test for all languages. It is not a Unicode-aware validator.
- Skipping the length check.
ctype_upper('A')istrue, so enforce size separately when a format requires it. - Not trimming input. A trailing newline from a pasted value causes a surprising
false.
Related functions
ctype_alpha()checks for letters of either case.ctype_lower()checks for lowercase letters.setlocale()changes locale information that can affect ctype checks.IntlChar::isupper()checks the Unicode general category "Lu" for a code point.
Recap
ctype_upper(mixed $text): bool returns true only for a non-empty string whose characters are all uppercase letters in the current locale. Pass real strings, trim input when whitespace is not meaningful, add your own length rule when needed, and remember that digits, spaces and the empty string all produce false.
Tested examples
$inputs = ['LMNSDO', 'AKLWC139', 'akwSKWsm', 'HELLO WORLD'];
$results = [];
foreach ($inputs as $input) {
$results[$input] = ctype_upper($input);
}
echo json_encode($results), "\n";{"LMNSDO":true,"AKLWC139":false,"akwSKWsm":false,"HELLO WORLD":false}
function isRegionCode(string $value): bool
{
return strlen($value) === 2 && ctype_upper($value);
}
$values = ['US', 'us', 'U1', 'USA', ''];
echo json_encode(array_map('isRegionCode', $values)), "\n";[true,false,false,false,false]
$raw = "ABC\n";
$checks = [
'empty' => ctype_upper(''),
'newline' => ctype_upper($raw),
'trimmed' => ctype_upper(trim($raw)),
'digits string' => ctype_upper((string) 65),
'chr' => ctype_upper(chr(65)),
];
echo json_encode($checks), "\n";{"empty":false,"newline":false,"trimmed":true,"digits string":false,"chr":true}