ctype_alnum
Checks whether every character in a string is a letter or a digit.
ctype_alnum(mixed $text): bool- Added in PHP 4.0.4
Parameters
| Parameter | Type | Description |
|---|---|---|
$text | mixed | The string to test. Every byte must be a letter or a digit for the result to be true. Passing an int between -128 and 255 is interpreted as an ASCII codepoint rather than as digits, which is why non-string arguments are deprecated as of PHP 8.1. |
Return value
booltrue if every character in text is a letter or a digit, false otherwise. An empty string always returns false.
Errors
Never throws. A non-string argument raises an E_DEPRECATED notice as of PHP 8.1; values that are neither string nor int simply return false.
Changelog
- PHP 8.1Passing a non-string argument is deprecated. Ints between -128 and 255 are still read as ASCII codepoints, so cast to string with (string) or convert deliberately with chr().
ctype_alnum() answers one question: is every byte in this string a letter or a digit? It is the cheapest way to enforce a "letters and numbers only" rule on usernames, coupon codes, slugs and table names, and it is noticeably faster than the equivalent preg_match('/^[A-Za-z0-9]+$/', $s) because no pattern has to be compiled.
When to reach for it
- Validating short identifiers before putting them into a URL, a filename or an SQL identifier.
- Rejecting input that should never contain spaces, dashes or underscores.
- Screening imported data for stray punctuation, one field at a time.
What trips people up
The check is byte-based, not Unicode-aware. Under the default C locale only ASCII A-Z, a-z and 0-9 count, so a UTF-8 string like café returns false. For accented or non-Latin text use preg_match('/^[\p{L}\p{N}]+$/u', $s) instead.
The result is also all-or-nothing: one hyphen anywhere makes the whole string fail. If you want to know which character broke the rule, loop over str_split() and test each byte.
Example
When to use it
- Rejecting usernames that contain spaces, dots or symbols before creating an account
- Validating a fixed-length promo code such as AB12CD34 without a regex
- Checking that a sort column name from a query string is safe to interpolate into SQL
Frequently asked questions
Why does ctype_alnum return false for an empty string?
By design: the documented behaviour is that an empty string always returns false, because there is no character that satisfies the test. Check for emptiness yourself if a blank value should be allowed.
Does ctype_alnum work with accented or Unicode characters?
No. It inspects raw bytes against the current locale, so under the default C locale only ASCII letters and digits pass and a UTF-8 string such as "café" returns false. Use preg_match with the /u modifier and \p{L}\p{N} for Unicode.
What is the difference between ctype_alnum and preg_match for validation?
They give the same answer for ASCII, but ctype_alnum is faster and more readable for a plain letters-and-digits check. Use a regex when you need length limits, anchors, optional characters or Unicode support.
More examples
A complete validator for a fixed-length code
Prints true, then false, then false. The length test comes first so the empty string is rejected on length rather than relying on the empty-string behaviour of ctype_alnum, which makes the intent explicit to the next reader.
<?php
function isValidCode(string $code): bool
{
return strlen($code) === 8 && ctype_alnum($code);
}
var_dump(isValidCode('AB12CD34'));
var_dump(isValidCode('AB12-CD3'));
var_dump(isValidCode(''));Finding the offending character
Prints "Bad character '#' at offset 5". ctype_alnum only tells you pass or fail for the whole string, so splitting into single characters is how you build a useful error message.
<?php
$input = 'order#42';
if (!ctype_alnum($input)) {
foreach (str_split($input) as $i => $ch) {
if (!ctype_alnum($ch)) {
echo "Bad character '$ch' at offset $i\n";
}
}
}
Discussion