Character checks7 min readExamples run on PHP 8.5.10

ctype_alpha()

Learn how PHP 8.4 ctype_alpha() tests whether every byte of a string is a letter, including its empty-string result, locale limits and common mistakes.

Signature

ctype_alpha(mixed $text): bool
Available since PHP 4.0.47.48.08.18.28.38.48.5All versions →

Purpose

ctype_alpha() answers one narrow question: is every character in a string a letter? It returns a plain boolean, so it works well as a quick guard for short tokens such as initials, country-style codes or simple identifiers. In the default C locale, which PHP uses unless you change it, the letters are just A-Z and a-z.

Signature

Parameters and defaults

$text is the value to test. It has no default and is required. Pass a string. The function examines the string byte by byte and requires each byte to be a letter in the current locale.

The manual also describes a legacy behavior for integers. An integer from -128 to 255 is treated as the ASCII code of one character, and any other integer is treated as its decimal digits. Passing a non-string argument has been deprecated since PHP 8.1. If you start from a number, convert it deliberately with a string cast, or use chr() if you really mean a character code.

Return value and failure cases

The function returns true only if every character is a letter. It returns false if any character is a digit, space, punctuation mark, newline or other non-letter. An empty string always gives false, which is different from what many people expect from an "all characters match" test. The function does not throw for ordinary string input; the result tells you whether the check passed.

Example 1: basic checks

The first example runs several strings through the function and prints the results as JSON. Note the empty string at the end.

Example
<?php
$inputs = ['KjgWZC', 'arf12', 'hello world', ''];
$results = [];
foreach ($inputs as $input) {
    $results[$input] = ctype_alpha($input);
}
echo json_encode($results, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
Output
{"KjgWZC":true,"arf12":false,"hello world":false,"":false}

The digits in arf12 and the space in hello world make those tests fail, and the empty string fails by definition.

Example 2: a small description helper

Wrapping the check in a function keeps calling code readable. This example also shows that an accented letter written in UTF-8 does not count in the default C locale, because it is stored as bytes above the ASCII range.

Example
<?php
function describe(string $value): string
{
    return ctype_alpha($value) ? 'letters only' : 'not letters only';
}

foreach (['Alpha', 'Beta2', 'snake_case', 'café'] as $value) {
    echo json_encode($value, JSON_UNESCAPED_UNICODE), ': ', describe($value), "\n";
}
Output
"Alpha": letters only
"Beta2": not letters only
"snake_case": not letters only
"café": not letters only

Underscores are not letters, so snake_case is rejected as well.

Example 3: comparing with case checks

The manual notes that, for a single character in the standard C locale, ctype_alpha() is equivalent to ctype_upper() || ctype_lower(). For longer strings the functions differ: mixed case is still alphabetic, but it is neither entirely upper nor entirely lower case.

Example
<?php
foreach (['PHP', 'php', 'Php'] as $word) {
    echo $word, ' ', json_encode([ctype_alpha($word), ctype_upper($word), ctype_lower($word)]), "\n";
}
Output
PHP [true,true,false]
php [true,false,true]
Php [true,false,false]

The three booleans are alpha, upper and lower, in that order. Php passes the alphabetic test but fails both case tests.

Edge cases

  • Empty string: always false. If an empty value should be allowed, test for it separately before calling the function.
  • Whitespace and line endings: a trailing newline or space makes the whole check fail. Trim user input first if surrounding whitespace should be ignored.
  • Non-ASCII text: a UTF-8 letter such as é is made of multiple bytes and is not a letter in the C locale. This function is not a Unicode-aware test. For Unicode letters, use a different tool, such as a PCRE pattern with the u modifier and \p{L}, or the Intl extension if it is available.
  • Locale: the manual says letters come from the current locale. Changing the locale with setlocale() can change the result for bytes outside ASCII, so avoid relying on it in portable code.
  • Non-string values: integers are interpreted in the legacy way described above and are deprecated as arguments. Do not depend on that behavior.

Common mistakes

  1. Expecting true for an empty string. Required form fields and optional fields need different handling.
  2. Validating human names with it. Names can contain spaces, hyphens, apostrophes and non-ASCII letters, so a strict ASCII-letter check will reject valid input.
  3. Passing numbers. ctype_alpha(65) relies on the deprecated ASCII-code interpretation. Cast to a string or call chr(65) to state your intention.
  4. Assuming it checks Unicode. It works with bytes and the current locale, not with Unicode code points.
  5. Forgetting that digits fail. Identifiers such as abc1 are not alphabetic; use ctype_alnum() when digits are acceptable.
  • ctype_upper() checks for uppercase letters.
  • ctype_lower() checks for lowercase letters.
  • ctype_alnum() allows letters and digits.
  • setlocale() sets locale information that can affect ctype functions.
  • IntlChar::isalpha() checks whether a code point is a letter, when the Intl extension is available.

Recap

Use ctype_alpha() for a fast ASCII-oriented test that a non-empty string contains only letters. Remember that an empty string returns false, whitespace and punctuation fail, non-ASCII UTF-8 text does not pass in the default C locale, and non-string arguments are deprecated.

Tested examples

Basic letter checks, including an empty string
<?php
$inputs = ['KjgWZC', 'arf12', 'hello world', ''];
$results = [];
foreach ($inputs as $input) {
    $results[$input] = ctype_alpha($input);
}
echo json_encode($results, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
Output
{"KjgWZC":true,"arf12":false,"hello world":false,"":false}
A small description helper
<?php
function describe(string $value): string
{
    return ctype_alpha($value) ? 'letters only' : 'not letters only';
}

foreach (['Alpha', 'Beta2', 'snake_case', 'café'] as $value) {
    echo json_encode($value, JSON_UNESCAPED_UNICODE), ': ', describe($value), "\n";
}
Output
"Alpha": letters only
"Beta2": not letters only
"snake_case": not letters only
"café": not letters only
Comparing alpha, upper and lower checks
<?php
foreach (['PHP', 'php', 'Php'] as $word) {
    echo $word, ' ', json_encode([ctype_alpha($word), ctype_upper($word), ctype_lower($word)]), "\n";
}
Output
PHP [true,true,false]
php [true,false,true]
Php [true,false,false]