ctype_cntrl
Checks whether every character in a string is a control character such as tab, newline or escape.
ctype_cntrl(mixed $text): bool- Added in PHP 4.0.4
Parameters
| Parameter | Type | Description |
|---|---|---|
$text | mixed | The string to test. Every byte must be a control character for the result to be true. An int between -128 and 255 is interpreted as an ASCII codepoint, which is why non-string arguments are deprecated as of PHP 8.1. |
Return value
booltrue if every character in text is a control character in the current locale, 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. Ints between -128 and 255 are still read as ASCII codepoints, so cast to string or use chr() explicitly.
ctype_cntrl() tests whether a string is made up entirely of control characters — ASCII 0 to 31 plus DEL (127). That covers the null byte, bell, backspace, tab, line feed, carriage return and escape. It is the exact complement of ctype_print() over the ASCII range.
When to reach for it
The whole-string form is rarely what you want, because real data is almost never only control characters. In practice you call it per character while scanning input that arrived from a file upload, a terminal, a scanner or a legacy feed, to find bytes that will corrupt a log line, break a CSV or slip an ANSI escape sequence into terminal output.
What trips people up
Tab, newline and carriage return are control characters, so a perfectly ordinary multi-line string will contain them. If you are hunting for unexpected control characters, whitelist \t, \n and \r before testing.
Note also that the check is per byte. A multi-byte UTF-8 character is never flagged, but an invalid byte sequence containing a stray \x00 is.
Example
When to use it
- Scanning uploaded CSV fields for null bytes or bell characters before importing
- Stripping ANSI escape sequences and other invisible bytes out of log lines
- Rejecting HTTP header values that contain CR or LF to prevent header injection
Frequently asked questions
Is a newline a control character in PHP?
Yes. Line feed, carriage return and tab are all ASCII control characters, so ctype_cntrl("\n") returns true. Exclude them explicitly if you only care about unusual control bytes.
How do I remove all control characters from a string?
Use preg_replace('/[\x00-\x1F\x7F]/', '', $text) for a one-liner, or loop with ctype_cntrl when you want to keep some characters such as newline and tab.
What is the difference between ctype_cntrl and ctype_print?
They are opposites over the ASCII range: ctype_print is true for characters that produce output including the space, and ctype_cntrl is true for the non-printing control codes 0-31 and 127.
More examples
Locating control bytes in a field
Prints "Control chars at: offset 5 (0x00), offset 8 (0x09)". bin2hex() is used because the characters themselves are invisible, so printing them directly would show nothing.
<?php
$input = "order\x00id\t42";
$found = [];
foreach (str_split($input) as $offset => $ch) {
if (ctype_cntrl($ch)) {
$found[] = "offset $offset (0x" . bin2hex($ch) . ')';
}
}
echo $found ? 'Control chars at: ' . implode(', ', $found) . "\n" : "Clean\n";Allowing tab and newline but nothing else
Prints string(16) "line one\nlinetwo" with the bell character (0x07) removed while the newline survives — nothing is put in the bell's place, so "line" and "two" end up joined. This whitelist pattern is what most sanitising code actually needs.
<?php
$text = "line one\nline\x07two";
$clean = '';
foreach (str_split($text) as $ch) {
if (!ctype_cntrl($ch) || $ch === "\n" || $ch === "\t") {
$clean .= $ch;
}
}
var_dump($clean);
Discussion