Signature
ctype_cntrl(mixed $text): boolPurpose
ctype_cntrl() answers one narrow question: is every character in a string a control character? Control characters are non-printing bytes that steer devices or text flow rather than show a glyph. Common ones are line feed, carriage return, tab and escape. The function is handy when you validate raw input, inspect protocol data or look for unexpected invisible bytes.
Signature
Parameters and defaults
There is a single parameter, $text, and it has no default value. It is meant to be a string. The check is applied to each byte, and the result is true only when all of them are control characters.
The manual describes a legacy behavior for integers. An integer from -128 to 255 is treated as the ASCII value of one character, and negative values have 256 added. Any other integer is treated as a string of its decimal digits. As of PHP 8.1.0, passing a non-string argument is deprecated. If you start from a number, cast it to a string or call chr() explicitly so your intent is clear.
Return value and failure cases
The function returns a bool:
trueif every character is a control character in the current locale.falseif at least one character is not a control character.falsefor an empty string, always.
The function does not throw for ordinary strings. The main way to get a surprising result is to pass a non-string value, which is deprecated.
Example 1: basic checks
This example tests a string of whitespace controls, a string of letters and digits, and an empty string.
$tests = [
'whitespace controls' => "\n\r\t",
'letters and digits' => 'arf12',
'empty string' => '',
];
$results = [];
foreach ($tests as $name => $value) {
$results[$name] = ctype_cntrl($value);
}
echo json_encode($results), "\n";{"whitespace controls":true,"letters and digits":false,"empty string":false}
The first string contains only control characters, so it passes. The second has printable characters. The third is empty, so it is rejected.
Example 2: what counts as a control character
A space is printable, so it is not a control character. Escape (\x1b) and delete (\x7f) are controls. One printable character is enough to make the whole string fail.
$samples = [
'escape' => "\x1b",
'delete' => "\x7f",
'space' => ' ',
'tab then text' => "\tA",
];
$out = [];
foreach ($samples as $label => $text) {
$out[$label] = ctype_cntrl($text);
}
echo json_encode($out), "\n";{"escape":true,"delete":true,"space":false,"tab then text":false}
Example 3: counting control bytes
Because the function requires all characters to match, it cannot count controls inside mixed text directly. Split the string into single-byte pieces and test each one.
function countControlCharacters(string $text): int
{
$count = 0;
foreach (str_split($text) as $char) {
if (ctype_cntrl($char)) {
$count++;
}
}
return $count;
}
echo json_encode([
countControlCharacters("Name:\tAda\n"),
countControlCharacters('Plain text'),
countControlCharacters(''),
]), "\n";[2,0,0]
The first string has a tab and a newline, so the count is 2. The plain string has none. The empty string has none either.
Edge cases
- Empty string: the result is always
false. If empty input should count as valid, handle it separately before calling the function. - All-or-nothing matching:
"\n"passes, but"\nA"fails. Do not readtrueas "contains a control character". - Locale: the manual says the test uses control characters from the current locale. For plain ASCII control bytes the result is stable, but bytes above 127 can depend on locale settings.
- Bytes, not Unicode characters: the function inspects bytes. For code-point-aware checks the manual points to
IntlChar::iscntrl()when the intl extension is available. - Integers: numeric input is deprecated and was historically ambiguous. For example, the integer
9meant the tab character, while the string"9"is a digit.
Common mistakes
- Expecting whitespace to always pass. Tab, line feed and carriage return are controls, but an ordinary space is not.
- Using it to find any control character. It checks that every character is a control. To detect a single control inside text, test characters one at a time or use a pattern-based approach.
- Forgetting the empty-string rule. An empty field returns
false, which may look like a failed validation even though no bad characters exist. - Passing numbers. Passing a number may either test an ASCII code or digits, and both are deprecated. Cast with
(string)or usechr(). - Assuming multibyte awareness. UTF-8 text is processed byte by byte, so do not use this function for Unicode classification.
Related functions
ctype_print()checks for printable characters.IntlChar::iscntrl()checks whether a Unicode code point is a control character.str_split()is useful for testing one byte at a time.chr()converts an ASCII code to a one-byte string.
Recap
ctype_cntrl() returns true only when a non-empty string consists entirely of control characters. Tabs, line feeds, carriage returns, escape and delete qualify, while spaces, letters and digits do not. Pass strings, expect false for an empty input, and split the text yourself if you need to count or locate control bytes.
Tested examples
$tests = [
'whitespace controls' => "\n\r\t",
'letters and digits' => 'arf12',
'empty string' => '',
];
$results = [];
foreach ($tests as $name => $value) {
$results[$name] = ctype_cntrl($value);
}
echo json_encode($results), "\n";
{"whitespace controls":true,"letters and digits":false,"empty string":false}
$samples = [
'escape' => "\x1b",
'delete' => "\x7f",
'space' => ' ',
'tab then text' => "\tA",
];
$out = [];
foreach ($samples as $label => $text) {
$out[$label] = ctype_cntrl($text);
}
echo json_encode($out), "\n";
{"escape":true,"delete":true,"space":false,"tab then text":false}
function countControlCharacters(string $text): int
{
$count = 0;
foreach (str_split($text) as $char) {
if (ctype_cntrl($char)) {
$count++;
}
}
return $count;
}
echo json_encode([
countControlCharacters("Name:\tAda\n"),
countControlCharacters('Plain text'),
countControlCharacters(''),
]), "\n";
[2,0,0]