Character checks7 min readExamples run on PHP 8.5.10

ctype_punct()

Learn how PHP 8.4 ctype_punct() tests whether every character is printable punctuation, with examples, empty-string and integer pitfalls, and related functions.

Signature

ctype_punct(mixed $text): bool
Available since PHP 4.0.47.48.08.18.28.38.48.5All versions →

Purpose

ctype_punct() answers one narrow question: is every character in a string a printable character that is neither whitespace nor a letter nor a digit? In the default C locale, that means ASCII symbols such as !, #, $, -, _ and ~. It is useful when you want to check that a token consists purely of symbols, for example a separator line made of dashes or a placeholder made of punctuation marks.

The check is made on the whole string. A single letter, digit, space, tab or newline anywhere in the text makes the result false.

Signature

Parameters and defaults

$text is the value to test. It has no default, so you must always pass it. You should pass a string.

The manual describes special handling for integers. An integer from -128 to 255 is treated as the ASCII value of one character, with negative values having 256 added. Any other integer is treated as a string made of its decimal digits. Since PHP 8.1, passing a non-string argument is deprecated, and the manual says a future version will treat such values as strings. If you have a number, convert it deliberately: use (string) $value when you mean its digits, or chr($code) when you mean a character code.

Return value and failure cases

The function returns a bool:

  • true when the string is non-empty and every character is punctuation.
  • false when at least one character is a letter, digit, blank or other non-punctuation character.
  • false for an empty string. This differs from what you might expect from an "all characters match" check on an empty set.

The function does not throw for ordinary strings. It reports mismatches only through its boolean result.

Example 1: strings that pass and fail

The first example runs several inputs through the function. json_encode() shows each input and result clearly, including the empty string.

Example
<?php
$inputs = ['!?.', 'abc!', '!@ #', '', '*&$()'];
foreach ($inputs as $input) {
    echo json_encode($input), ': ', json_encode(ctype_punct($input)), "\n";
}
Output
"!?.": true
"abc!": false
"!@ #": false
"": false
"*&$()": true

The string 'abc!' fails because of the letters, and '!@ #' fails because of the space. The empty string also fails.

Example 2: validating separator tokens

A small parser might accept tokens that consist only of symbols, such as --- or .... Mapping over a list of tokens shows how underscores and tildes count as punctuation, while whitespace and digits do not.

Example
<?php
$tokens = ['---', '...', '--a', '_', '~', ' ', "\t", '1'];
$results = array_map(fn(string $token): bool => ctype_punct($token), $tokens);
echo json_encode($results), "\n";
Output
[true,true,false,true,true,false,false,false]

Note that the tab character is whitespace, so it is not punctuation either.

Example 3: discovering the ASCII punctuation set

To see exactly what the function accepts in the C locale, test each printable ASCII character. The example sets the character-type locale to C explicitly so that the result does not depend on the environment.

Example
<?php
setlocale(LC_CTYPE, 'C');
$found = '';
for ($code = 32; $code <= 126; $code++) {
    $char = chr($code);
    if (ctype_punct($char)) {
        $found .= $char;
    }
}
echo strlen($found), "\n";
echo $found[0], $found[-1], "\n";
Output
32
!~

There are 32 matches among the 95 printable ASCII characters. The first is ! and the last is ~. The 95 minus the 32 matches leaves the space, 26 uppercase letters, 26 lowercase letters and 10 digits.

Edge cases

  • Empty input: ctype_punct('') is false. If an empty string is allowed in your application, handle that case separately.
  • Whitespace: spaces, tabs and newlines are not punctuation, so a string containing them fails.
  • Digits: '123' is not punctuation. This also applies to the integer 123, which is treated as the string of its digits, although passing integers is deprecated.
  • Locale and non-ASCII text: the ctype functions work on bytes and follow the current character-type locale. Do not use this function to judge Unicode punctuation in UTF-8 text. The manual lists IntlChar::ispunct() for code point checks, which requires the intl extension.

Common mistakes

  1. Expecting a mixed string to pass if it contains some punctuation. The check requires every character to be punctuation. To find whether any punctuation is present, test characters individually or use a regular expression.
  2. Passing integers. An integer such as 33 may be read as an ASCII code, while 1000 is read as digits. Since PHP 8.1 either form is deprecated. Cast to string or call chr() explicitly.
  3. Assuming an empty string is valid. The documented result is always false.
  4. Treating it as a Unicode-aware validator. The function is byte and locale oriented, so accented characters and non-Latin symbols are not reliably classified.
  5. Confusing it with ctype_graph(). ctype_graph() accepts any printable character except space, which includes letters and digits. ctype_punct() excludes letters and digits too.
  • ctype_cntrl() checks for control characters.
  • ctype_graph() checks for printable characters other than space.
  • ctype_alnum() checks for letters and digits, the group that punctuation excludes.
  • IntlChar::ispunct() checks one Unicode code point when the intl extension is available.

Recap

Use ctype_punct() with a real string when every byte must be a printable non-space, non-alphanumeric character. It returns false for empty input and for any mixed content. Avoid integer arguments, and use a Unicode-aware tool when your text is not simple ASCII.

Tested examples

Strings that pass and fail the punctuation check
<?php
$inputs = ['!?.', 'abc!', '!@ #', '', '*&$()'];
foreach ($inputs as $input) {
    echo json_encode($input), ': ', json_encode(ctype_punct($input)), "\n";
}
Output
"!?.": true
"abc!": false
"!@ #": false
"": false
"*&$()": true
Validating separator tokens with array_map
<?php
$tokens = ['---', '...', '--a', '_', '~', ' ', "\t", '1'];
$results = array_map(fn(string $token): bool => ctype_punct($token), $tokens);
echo json_encode($results), "\n";
Output
[true,true,false,true,true,false,false,false]
Discovering the ASCII punctuation set in the C locale
<?php
setlocale(LC_CTYPE, 'C');
$found = '';
for ($code = 32; $code <= 126; $code++) {
    $char = chr($code);
    if (ctype_punct($char)) {
        $found .= $char;
    }
}
echo strlen($found), "\n";
echo $found[0], $found[-1], "\n";
Output
32
!~