Signature
ctype_digit(mixed $text): boolPurpose
ctype_digit() answers one narrow question: does every character in a string fall in the range 0 to 9? It is a quick character-class test, handy for validating things like numeric IDs, PIN codes, postal codes in some countries, or the digit-only part of a token before you convert it with (int).
It does not parse numbers. It does not know about signs, decimal points, exponents or whitespace. If you need to decide whether a value can be treated as a number in a broader sense, look at is_numeric() or filter_var() instead.
Signature
Parameters and defaults
$text is the only parameter and has no default. The function is meant to receive a string, and you should always pass one.
The parameter is typed mixed for historical reasons. Integers are handled in a surprising way: an integer from -128 to 255 is treated as the ASCII code of a single character, with negative values having 256 added. Any other integer is treated as a string of its decimal digits. Because of this, ctype_digit(42) tests the character * (ASCII 42) and gives false, whereas ctype_digit('42') gives true. Since PHP 8.1 passing a non-string argument is deprecated, and the manual says it will eventually be interpreted as a string. Cast explicitly with (string) when you really have an integer, or avoid the situation by validating input as a string from the start.
Return value and failure cases
The function returns a bool:
truewhen the string is non-empty and every character is a decimal digit.falsewhen any character is not a digit.falsefor an empty string, always. This differs from the intuitive "all zero characters are digits" logic, so it is convenient for validation but worth remembering.
With string input it does not throw exceptions and emits no warnings. Bad input simply produces false.
Example 1: basic checks
This example runs several strings through the function and collects the results as pairs, so the output is easy to read. Note that leading zeros are fine, because '007' is still made only of digits.
$inputs = ['10002', '1820.20', 'wsl!12', '', '007'];
$result = [];
foreach ($inputs as $input) {
$result[] = [$input, ctype_digit($input)];
}
echo json_encode($result, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);[["10002",true],["1820.20",false],["wsl!12",false],["",false],["007",true]]
Example 2: things that look numeric but are not
Signs, decimal points, exponent notation and whitespace are all non-digit characters. That includes a trailing newline, which commonly sneaks in from text input.
$inputs = ['-5', '+5', ' 5', "5\n", '5.0', '1e3'];
$result = [];
foreach ($inputs as $input) {
$result[] = [$input, ctype_digit($input)];
}
echo json_encode($result, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);[["-5",false],["+5",false],[" 5",false],["5\n",false],["5.0",false],["1e3",false]]
Every result is false. If you want to allow surrounding whitespace, call trim() first. If you want to allow a sign, strip it deliberately and check the rest.
Example 3: safe conversion to an integer ID
A typical use is guarding a cast. The check prevents junk from silently becoming 0, and the length limit keeps the value comfortably inside the integer range.
function parseId(string $value): ?int
{
if (ctype_digit($value) && strlen($value) <= 9) {
return (int) $value;
}
return null;
}
$values = ['0042', 'abc', '', '12345678901'];
echo json_encode(array_map('parseId', $values), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);[42,null,null,null]
The leading zeros in '0042' are accepted and dropped by the cast, giving 42. The empty string is rejected because ctype_digit('') is false, and the long value is rejected by the length limit.
Edge cases
- Empty string: always
false. - Negative numbers: the minus sign is not a digit, so
'-1'isfalse. - Decimals:
'3.14'isfalsebecause of the dot. - Leading zeros: accepted. If zeros matter (for example, a ZIP code), keep the value as a string rather than casting it.
- Non-ASCII digits: the check is a byte-oriented character class test, so do not rely on it to recognise digits from other scripts. For Unicode-aware classification consider
IntlChar::isdigit()or apreg_match()pattern with theumodifier, if those tools are available to you. - Overflow: a long string of digits passes the test but may not fit in an integer. Check the length or compare as strings before casting.
Common mistakes
- Passing an integer.
ctype_digit(42)does not mean "is 42 a number". It examines the ASCII character with that code, and the behaviour is deprecated from PHP 8.1. Cast to a string first if the value really is an integer. - Expecting it to validate numbers. Values such as
'-12','1.5'and'1e3'are all numeric tois_numeric()but not toctype_digit(). - Forgetting to trim. Form or file input often has extra spaces or a newline, which will make the check fail.
- Assuming a passing check means a safe integer. Very long digit strings pass but overflow when cast.
- Treating an empty field as valid. The
falseresult for''helps here, but make sure your logic does not take a separate path for empty values that skips the check.
Related functions
ctype_alnum()checks for letters and digits.ctype_xdigit()checks for hexadecimal digit characters.is_numeric()accepts a wider range of numeric strings, including signs, decimals and exponents.is_int()andis_string()test the type of a variable rather than its content.IntlChar::isdigit()classifies Unicode code points.
Recap
Use ctype_digit() on strings when you need to know that the input is made only of the characters 0 to 9. It returns false for empty strings, signs, decimal points and whitespace. Do not pass integers, since they are interpreted as character codes (deprecated since PHP 8.1), and combine the check with a length limit before casting to int.
Tested examples
$inputs = ['10002', '1820.20', 'wsl!12', '', '007'];
$result = [];
foreach ($inputs as $input) {
$result[] = [$input, ctype_digit($input)];
}
echo json_encode($result, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);[["10002",true],["1820.20",false],["wsl!12",false],["",false],["007",true]]
$inputs = ['-5', '+5', ' 5', "5\n", '5.0', '1e3'];
$result = [];
foreach ($inputs as $input) {
$result[] = [$input, ctype_digit($input)];
}
echo json_encode($result, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);[["-5",false],["+5",false],[" 5",false],["5\n",false],["5.0",false],["1e3",false]]
function parseId(string $value): ?int
{
if (ctype_digit($value) && strlen($value) <= 9) {
return (int) $value;
}
return null;
}
$values = ['0042', 'abc', '', '12345678901'];
echo json_encode(array_map('parseId', $values), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);[42,null,null,null]