Signature
str_increment(string $string): stringPurpose
str_increment() returns the "next" value of an alphanumeric ASCII string. It works like an odometer that uses digits and letters: the last character moves forward by one, and when it wraps around, the carry moves to the character on its left. This is handy for generating sequential labels such as INV009 -> INV010, spreadsheet-style column names, or short codes.
The function is available since PHP 8.3.0 and is part of PHP 8.4. Its companion is str_decrement(), which moves in the opposite direction.
Signature
Parameters and defaults
$string: the string to increment. It is required and has no default. It must be non-empty and contain only the ASCII characters0-9,a-zandA-Z.
Return value and failure cases
The function returns a new string; the original variable is never modified. The result is the same length as the input, or one character longer when the carry runs off the left end.
It signals failure by throwing a ValueError, not by returning false:
- when the string is empty;
- when the string contains anything other than ASCII letters and digits (spaces, hyphens, underscores, accented letters and so on).
How the carry works
Each character has its own cycle: digits go 0 to 9, lowercase letters go a to z, uppercase letters go A to Z. When a character wraps, it becomes the first value of its own kind (9 -> 0, z -> a, Z -> A) and the next character to the left is incremented. If the leftmost character wraps, a new character is added at the front: 1 for digits, a for lowercase and A for uppercase. That is why ZZ becomes AAA and 99 becomes 100.
Example 1: letters and carries
The first example shows a simple increment, a carry in the middle, a full wrap, and mixed case.
$values = ['ABC', 'DZ', 'ZZ', 'az', 'Zz'];
$result = [];
foreach ($values as $value) {
$result[$value] = str_increment($value);
}
echo json_encode($result, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";{"ABC":"ABD","DZ":"EA","ZZ":"AAA","az":"ba","Zz":"AAa"}
Notice Zz: the lowercase z wraps to a, then the uppercase Z wraps to A, and because the carry leaves the string, a new A is added at the front, giving AAa.
Example 2: digits and leading zeros
Digits are treated as characters, not as numbers. Leading zeros are therefore preserved, which is often exactly what you want for padded identifiers.
$values = ['9', '99', 'a9', '009', 'Az9'];
$next = array_map('str_increment', $values);
echo json_encode($next, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";["10","100","b0","010","Ba0"]
The string 009 becomes 010, not 10, and the result is always a string. In Az9, the digit wraps, then z wraps, and finally A moves to B, so the result is Ba0.
Example 3: safe code generation
Because invalid input throws, wrap calls in try/catch whenever the string comes from users or imported data.
function nextCode(string $code): string
{
try {
return str_increment($code);
} catch (ValueError $e) {
return 'invalid';
}
}
echo json_encode([nextCode('INV009'), nextCode('INV-9'), nextCode('')], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";["INV010","invalid","invalid"]
INV009 increments normally. INV-9 is rejected because of the hyphen, and the empty string is rejected because there is nothing to increment.
Edge cases
- A single character wraps into two:
z->aa,Z->AA,9->10. - Only the final character is incremented first; characters to the left change only when a carry reaches them.
- Prefixes that contain separators must be split off before calling the function, then re-attached.
- The function only understands ASCII. Multibyte or accented letters cause a
ValueError. - Input such as
" 7"(with whitespace) is not trimmed for you; it is invalid.
Common mistakes
- Expecting a numeric result.
str_increment('9')returns the string"10". If you need integer arithmetic, cast and use+ 1instead. - Forgetting error handling. Unlike some string functions, there is no
falsereturn. An unhandledValueErrorstops the script. - Passing codes with punctuation.
INV-0009must be split into prefix and counter first. - Assuming it mutates the argument. The function returns a value; assign it yourself.
- Confusing it with the
++operator. The increment operator on a string follows looser rules (for example, numeric strings are treated as numbers), whilestr_increment()is explicit and strict about its input. - Relying on fixed length. The result can grow by one character, which matters if the value is stored in a fixed-width column.
Related functions
str_decrement()goes backward through the same alphanumeric sequence.str_pad()can restore fixed-width numeric counters.ctype_alnum()can pre-check that a string is alphanumeric, although it also accepts only ASCII in the default locale, so test your intended inputs.sprintf()is better for formatted numeric counters when you only need numbers.
Recap
str_increment() advances a non-empty ASCII alphanumeric string by one step, carrying across characters and growing the string when necessary. It returns a string, preserves leading zeros, and throws ValueError for empty or non-alphanumeric input. Validate or catch errors for untrusted data, and separate any prefix or punctuation before incrementing.
Tested examples
$values = ['ABC', 'DZ', 'ZZ', 'az', 'Zz'];
$result = [];
foreach ($values as $value) {
$result[$value] = str_increment($value);
}
echo json_encode($result, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
{"ABC":"ABD","DZ":"EA","ZZ":"AAA","az":"ba","Zz":"AAa"}
$values = ['9', '99', 'a9', '009', 'Az9'];
$next = array_map('str_increment', $values);
echo json_encode($next, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
["10","100","b0","010","Ba0"]
function nextCode(string $code): string
{
try {
return str_increment($code);
} catch (ValueError $e) {
return 'invalid';
}
}
echo json_encode([nextCode('INV009'), nextCode('INV-9'), nextCode('')], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
["INV010","invalid","invalid"]