count_chars
Returns per-byte frequency information for a string, as an array of counts or a string of characters.
count_chars(string $string, int $mode = 0): array|string- Added in PHP 4.0
Parameters
| Parameter | Type | Description |
|---|---|---|
$string | string | The string being examined. |
$modeoptional | int= 0 | 0 = every byte value 0-255 with its frequency; 1 = only bytes that occur; 2 = only bytes that never occur; 3 = a string of all unique bytes used; 4 = a string of all unused bytes. |
Return value
array|stringFor modes 0-2 an array keyed by byte value; for modes 3 and 4 a string of characters.
Errors
Throws a ValueError if mode is not between 0 and 4 inclusive.
Changelog
- PHP 8.0.0Previously the function returned false on failure; it now throws a ValueError for an invalid mode.
count_chars builds a histogram of the bytes in a string. Mode 1 is the one you almost always want: an array whose keys are byte values and whose values are how often each occurred, with absent bytes omitted.
When to reach for it
It is the fastest way to answer "which characters appear in this string, and how many times?" without splitting it into an array first. Typical jobs: checking a password contains characters from several classes, spotting stray control bytes in imported data, finding a delimiter that does not appear anywhere in a payload (mode 4 hands you every unused byte directly), or building a quick letter-frequency table.
What trips people up
The keys are integers, not characters. Mode 1 gives you [97 => 3], not ['a' => 3] — run the keys through chr, or use array_count_values(str_split($s)) if you want character keys.
It also counts bytes, so on UTF-8 input a single é shows up as two separate entries, 0xC3 and 0xA9. Mode 0 always returns 256 entries, most of them zero, which is rarely what you want in a loop.
Example
When to use it
- Check that a password draws on several character classes before accepting it.
- Find a byte that never occurs in a payload so you can safely use it as a delimiter.
- Detect stray control characters or non-ASCII bytes in an imported CSV field.
Frequently asked questions
Why are the array keys numbers instead of letters?
count_chars keys the result by byte value, so 'a' appears as key 97. Convert with chr($key), or use array_count_values(str_split($string)) to get character keys directly.
Does count_chars work with UTF-8?
It counts bytes, so each multi-byte character is split into its individual bytes. For character-level counts use mb_str_split combined with array_count_values.
How do I count occurrences of one specific substring instead?
Use substr_count, which counts a whole substring rather than single bytes and is far more direct for that job.
More examples
Mode 0 versus mode 1
Mode 0 always returns all 256 byte slots, most with a count of zero. Mode 1 returns only the three bytes actually present, keyed 97, 98 and 99. Mode 2 is the complement: the 253 byte values that never appear.
<?php
$s = "aabbbc";
echo count(count_chars($s, 0)), "\n";
echo count(count_chars($s, 1)), "\n";
print_r(count_chars($s, 1));
echo count(count_chars($s, 2)), "\n";Picking an unused delimiter
Mode 4 returns every byte the payload does not use, ordered by value, so the first one is the lowest safe delimiter — here the NUL byte 0x00. The str_contains check confirms it cannot collide with the data.
<?php
$payload = "name=Ada;role=admin";
$unused = count_chars($payload, 4);
$delimiter = $unused[0];
echo "picked byte 0x", bin2hex($delimiter), "\n";
var_dump(str_contains($payload, $delimiter));
echo implode($delimiter, ["a", "b"]) === "a{$delimiter}b" ? "safe\n" : "clash\n";Spotting non-ASCII bytes
The four-character string reports five bytes: c, a, f and the pair c3 a9 that encodes é in UTF-8. The two flagged rows show why a byte histogram is not a character histogram for non-ASCII text.
<?php
$text = "caf\u{00E9}";
foreach (count_chars($text, 1) as $b => $n) {
printf("0x%02x x%d%s\n", $b, $n, $b > 127 ? " <- not ASCII" : "");
}
echo strlen($text), " bytes total\n";
Discussion