Signature
ctype_xdigit(mixed $text): boolPurpose
ctype_xdigit() answers one narrow question: does every character in a string belong to the hexadecimal alphabet? That alphabet is the decimal digits 0 to 9 plus the letters A to F in either upper or lower case. It is a quick pre-check before you convert hash fragments, color codes, identifiers or escaped bytes, because it needs no regular expression and never raises an error for ordinary string input.
Signature
Parameters and defaults
There is a single parameter, $text, and it has no default value. You are expected to pass a string. The function checks every character of that string, so a single stray byte anywhere makes the whole result false.
The parameter is typed mixed for historical reasons. In older code you may see integers passed in. An integer between -128 and 255 used to be treated as the ASCII code of one character, and any other integer as the string of its decimal digits. Since PHP 8.1 passing a non-string argument is deprecated, and the documentation says a future version will treat the value as a string instead. The safe habit is to cast explicitly with (string) $value, or to call chr() if you really meant a character code.
Return value and failure cases
The function returns true when the string is non-empty and every character is in 0-9, A-F or a-f. It returns false otherwise. It does not throw for a bad string; the failure signal is simply false.
The most surprising failure case is the empty string. ctype_xdigit('') always returns false, so an empty value is never considered a valid hexadecimal string. This is usually what you want for validation, but it differs from the vacuous truth you might expect.
Example 1: basic checks and what fails
The first example runs several inputs through the function and prints the results as JSON, which makes the booleans easy to read.
$inputs = ['AB10BC99', 'AR1012', 'ab12bc99', '', '0x1F', ' 1f'];
$results = [];
foreach ($inputs as $input) {
$results[$input] = ctype_xdigit($input);
}
echo json_encode($results, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";{"AB10BC99":true,"AR1012":false,"ab12bc99":true,"":false,"0x1F":false," 1f":false}
Notice that 0x1F fails because of the x, and 1f fails because of the leading space. The function does not trim whitespace and does not understand prefixes.
Example 2: validating a CSS hex color
Because ctype_xdigit() only checks the character set, you combine it with other rules. Here a helper requires a leading # and exactly three or six hexadecimal digits.
function isHexColor(string $value): bool
{
if ($value === '' || $value[0] !== '#') {
return false;
}
$digits = substr($value, 1);
return in_array(strlen($digits), [3, 6], true) && ctype_xdigit($digits);
}
$tests = ['#fff', '#A1B2C3', '#12345', '#ggg', 'fff'];
foreach ($tests as $t) {
echo $t, ': ', isHexColor($t) ? 'yes' : 'no', "\n";
}#fff: yes #A1B2C3: yes #12345: no #ggg: no fff: no
The length check and the prefix check are your responsibility. The character check is the only part the function performs.
Example 3: guarding hexdec()
hexdec() silently ignores characters that are not valid hexadecimal digits, which can hide bad input. Validating first keeps the conversion honest.
$values = ['ff', '7FFF', 'xyz', '-1a'];
foreach ($values as $v) {
if (ctype_xdigit($v)) {
echo $v, ' => ', hexdec($v), "\n";
} else {
echo $v, ' => invalid', "\n";
}
}ff => 255 7FFF => 32767 xyz => invalid -1a => invalid
The value -1a is rejected because the minus sign is not a hexadecimal digit, even though it looks like a signed number.
Edge cases
- Empty string: always
false, as described above. - Whitespace and newlines: any space, tab or trailing newline makes the result
false. Trim the input first if you want to tolerate it. - Prefixes and separators:
0x,#, colons and dashes are not hexadecimal digits. Strip them before the check, for example when handling MAC addresses. - Odd length:
abcis valid even though it cannot represent whole bytes. If you need byte-aligned data, test thatstrlen($text) % 2 === 0yourself. - Integers:
ctype_xdigit(255)relies on the deprecated ASCII-code interpretation. Writectype_xdigit((string) 255)when you mean the digits of the number. - Non-ASCII text: characters outside the ASCII hex alphabet are not accepted, so a string with accented letters fails.
Common mistakes
- Passing numbers directly. Raw integers trigger a deprecation notice in PHP 8.1 and later, and they may be interpreted as a character code rather than a number. Cast to string first.
- Expecting it to validate a number. It checks characters only. It says nothing about range, sign, length or whether a value fits in an integer.
- Forgetting the empty string. Code such as
if (!ctype_xdigit($x))treats empty input as invalid, which is often desirable, but be aware that you cannot use the function to accept an optional blank value. - Assuming prefix support. Strings such as
0xFFor#FFneed the prefix removed before testing. - Confusing it with
ctype_digit(). The latter accepts only0to9, so hexadecimal letters make it fail.
Related functions
ctype_digit()checks for decimal digits only.ctype_alnum()checks for letters and digits.hexdec(),dechex()andbin2hex()convert between hexadecimal text and other representations.IntlChar::isxdigit()performs a similar check on a Unicode code point when the intl extension is available.preg_match()can express more complex formats, such as a fixed length plus a prefix, in one pattern.
Recap
ctype_xdigit() takes a string and returns true only when it is non-empty and made entirely of 0-9, a-f or A-F. It never trims, never accepts prefixes, and returns false for the empty string. Cast non-string input explicitly, add your own length and prefix rules, and validate before calling conversion functions such as hexdec().
Tested examples
$inputs = ['AB10BC99', 'AR1012', 'ab12bc99', '', '0x1F', ' 1f'];
$results = [];
foreach ($inputs as $input) {
$results[$input] = ctype_xdigit($input);
}
echo json_encode($results, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";{"AB10BC99":true,"AR1012":false,"ab12bc99":true,"":false,"0x1F":false," 1f":false}
function isHexColor(string $value): bool
{
if ($value === '' || $value[0] !== '#') {
return false;
}
$digits = substr($value, 1);
return in_array(strlen($digits), [3, 6], true) && ctype_xdigit($digits);
}
$tests = ['#fff', '#A1B2C3', '#12345', '#ggg', 'fff'];
foreach ($tests as $t) {
echo $t, ': ', isHexColor($t) ? 'yes' : 'no', "\n";
}#fff: yes #A1B2C3: yes #12345: no #ggg: no fff: no
$values = ['ff', '7FFF', 'xyz', '-1a'];
foreach ($values as $v) {
if (ctype_xdigit($v)) {
echo $v, ' => ', hexdec($v), "\n";
} else {
echo $v, ' => invalid', "\n";
}
}ff => 255 7FFF => 32767 xyz => invalid -1a => invalid