Strings7 min readExamples run on PHP 8.5.10

str_word_count()

Learn how PHP 8.4 str_word_count() counts or lists words, how its three formats differ, and where its simple letter-based definition of a word surprises you.

Signature

str_word_count(string $string, int $format = 0, ?string $characters = null): array|int
Available since PHP 4.3.07.48.08.18.28.38.48.5All versions →

Purpose

str_word_count() inspects a string and reports on the words in it. Depending on the format argument it returns how many words it found, a plain list of the words, or a list keyed by the byte position where each word starts. It is a quick tool for rough statistics such as a reading-time estimate, a length hint or a short preview. It is not a full text-analysis tool, because its idea of a "word" is deliberately simple.

The manual defines a word as a locale-dependent run of alphabetic characters that may also contain, but not start with, an apostrophe (') or a hyphen (-). Multibyte locales are not supported.

Signature

Parameters and defaults

  • $string: the text to examine.
  • $format: selects the result shape. The default is 0.
    • 0 returns the number of words as an integer.
    • 1 returns a list array of the words in order.
    • 2 returns an associative array. Each key is the byte offset where the word begins and each value is the word.
  • $characters: an optional string of extra characters that should be treated as part of words. The default is null, meaning no extras. Since PHP 8.0 this parameter is explicitly nullable.

Return value and failure cases

The return type is array|int. With format 0 you get an int, otherwise an array. A string with no words gives 0 or an empty array. The function does not return false for ordinary input. In PHP 8, passing a $format other than 0, 1 or 2 throws a ValueError, so do not feed it unchecked user values.

Example: counting and listing

The simplest use is to count words, or to ask for the words themselves with format 1.

Example
<?php
$text = "The quick brown fox";
echo str_word_count($text), "\n";
echo json_encode(str_word_count($text, 1), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
Output
4
["The","quick","brown","fox"]

The count is 4, and the list contains the same four words in their original order.

Example: positions with format 2

Format 2 keeps track of where each word starts. Offsets are measured in bytes from the beginning of the string, starting at zero. Apostrophes and hyphens inside a word stay part of it.

Example
<?php
$text = "Don't stop: well-known rules!";
$words = str_word_count($text, 2);
echo json_encode($words, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
Output
{"0":"Don't","6":"stop","12":"well-known","23":"rules"}

Because the keys are not consecutive, json_encode() writes this array as an object. Punctuation such as the colon and the exclamation mark is never part of a word here.

Example: extra characters

By default, digits are not letters, so they split words. The third argument lets you declare extra characters that belong inside words.

Example
<?php
$text = "Order A1 and B22 shipped";
$plain = str_word_count($text, 1);
$withDigits = str_word_count($text, 1, "0123456789");
echo json_encode($plain, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
echo json_encode($withDigits, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
echo count($plain), " ", count($withDigits), "\n";
Output
["Order","A","and","B","shipped"]
["Order","A1","and","B22","shipped"]
5 5

Notice that both versions find five words, yet the contents differ. Without the extra characters, A1 becomes A, and B22 becomes B, so the number alone would not reveal the difference.

Edge cases

  • Digits split words. The manual's own example shows fri3nd becoming fri and nd unless 3 is passed in $characters.
  • Non-ASCII text. The function works on bytes and depends on the locale, and multibyte locales are not supported. Accented letters or non-Latin scripts may be treated as separators and split words apart. For such text consider preg_split() with the u modifier, or the mb_* functions.
  • Apostrophes and hyphens. The documented rule says words may contain but not start with them. The implementation is simple, so test your own punctuation-heavy input rather than assuming the rule is applied perfectly.
  • Empty or symbol-only strings. These produce 0 or an empty array, never an error.
  • Offsets are bytes. The keys from format 2 are byte positions, not character positions.

Common mistakes

  1. Treating the result as a universal word count. Numbers, emoji, URLs and email addresses are not counted the way a person would count them.
  2. Forgetting that the return type changes with $format. Calling count() on the default integer result is wrong, while count() on formats 1 or 2 is fine.
  3. Passing a variable $format without validating it, which can throw a ValueError.
  4. Using it on UTF-8 text in languages with accents and expecting correct splitting.
  5. Assuming format 2 keys are sequential indexes. They are offsets, so looping with for ($i = 0; ...) will miss entries.
  • explode() splits on a fixed separator and keeps punctuation attached.
  • preg_split() splits on a regular expression, giving full control, including Unicode.
  • count_chars() reports on the characters in a string rather than the words.
  • substr_count() counts occurrences of one specific substring.

Recap

str_word_count() returns a count, a list or an offset-keyed array depending on $format. Words are runs of letters, with apostrophes and hyphens allowed inside, and $characters lets you add more word characters such as digits. It is byte-oriented and locale-dependent, so use it for simple English-style text and choose a regex or multibyte approach for anything more demanding.

Tested examples

Count words and list them
<?php
$text = "The quick brown fox";
echo str_word_count($text), "\n";
echo json_encode(str_word_count($text, 1), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
Output
4
["The","quick","brown","fox"]
Word positions with format 2
<?php
$text = "Don't stop: well-known rules!";
$words = str_word_count($text, 2);
echo json_encode($words, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
Output
{"0":"Don't","6":"stop","12":"well-known","23":"rules"}
Extra word characters
<?php
$text = "Order A1 and B22 shipped";
$plain = str_word_count($text, 1);
$withDigits = str_word_count($text, 1, "0123456789");
echo json_encode($plain, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
echo json_encode($withDigits, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
echo count($plain), " ", count($withDigits), "\n";
Output
["Order","A","and","B","shipped"]
["Order","A1","and","B22","shipped"]
5 5