ctype_digit
Checks whether every character in a string is a decimal digit.
ctype_digit(mixed $text): bool- Added in PHP 4.0.4
Parameters
| Parameter | Type | Description |
|---|---|---|
$text | mixed | The string to test. Every byte must be one of 0-9 for the result to be true. An int between -128 and 255 is interpreted as an ASCII codepoint rather than as its digits, which is why non-string arguments are deprecated as of PHP 8.1. |
Return value
booltrue if every character in text is a decimal 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 return false.
Changelog
- PHP 8.1Passing a non-string argument is deprecated. An int between -128 and 255 is still read as an ASCII codepoint, so ctype_digit(5) tests chr(5) and returns false — cast to string first.
ctype_digit() is the strictest of PHP's numeric checks: it is true only when every byte is 0 through 9. No sign, no decimal point, no exponent, no leading or trailing whitespace, no thousands separator.
When to reach for it
Use it when the string must stay a string of digits — a ZIP code, an account number, a phone number segment, an ID from a URL. Unlike is_numeric(), it will not quietly accept 1e3, 0x1A, -5 or " 42", and unlike casting to int it does not destroy leading zeros.
What trips people up
The classic bug is passing an int. Because a small int is interpreted as an ASCII codepoint, ctype_digit(5) tests chr(5) and returns false, while ctype_digit(48) tests chr(48), which is the character 0, and returns true. Since PHP 8.1 this also emits a deprecation notice. If the value might be an int, use is_int($v) || ctype_digit((string) $v).
Remember too that a value passing this test may still be larger than PHP_INT_MAX, so validate the range separately before casting.
Example
When to use it
- Validating an ID taken from $_GET before using it in a query
- Checking a ZIP or postal code where leading zeros must survive
- Rejecting decimals and negative numbers on a quantity field that must be a whole count
Frequently asked questions
What is the difference between ctype_digit and is_numeric?
ctype_digit is true only for a string of 0-9 characters, while is_numeric also accepts signs, decimal points, exponents, hexadecimal-free floats and leading whitespace. Use ctype_digit when you mean unsigned whole digits and nothing else.
Why does ctype_digit return false for a number?
Passing an int between -128 and 255 makes PHP treat it as an ASCII codepoint, so ctype_digit(5) checks chr(5), a control character. Cast the value with (string) before calling; passing non-strings is deprecated since PHP 8.1 anyway.
Does ctype_digit accept negative numbers or decimals?
No. The minus sign and the decimal point are not digits, so '-5' and '1.5' both return false. Use filter_var with FILTER_VALIDATE_INT or FILTER_VALIDATE_FLOAT for those.
More examples
Safely handling a value that may be an int
Prints true, true, false. The is_int() branch exists because ctype_digit(5) would test ASCII codepoint 5 and return false while also raising a deprecation notice on PHP 8.1 and later.
<?php
function isWholeNumber(mixed $value): bool
{
return is_int($value) || (is_string($value) && ctype_digit($value));
}
var_dump(isWholeNumber(5));
var_dump(isWholeNumber('5'));
var_dump(isWholeNumber('5.0'));Keeping leading zeros intact
Prints "Valid ZIP: 01234" then "As int: 1234". This is why identifier-like numbers should be validated with ctype_digit and stored as strings rather than converted to integers.
<?php
$zip = '01234';
if (ctype_digit($zip) && strlen($zip) === 5) {
echo "Valid ZIP: $zip\n";
}
// Casting would silently lose the leading zero:
echo 'As int: ', (int) $zip, "\n";
Discussion