How to increment letters like numbers in PHP?
I would like to write a function that takes in 3 characters and increments it and returns the newly incremented characters as a string.
I know how to increase a single letter to the next one but how would I know when to increase the second letters and then stop and then increase the first letter again to have a sequential increase?
So if AAA is passed, return AAB. If AAZ is passed return ABA (hard part).
I would appreciate help with the logic and what php functions will be useful to use.
Even better, has some done this already or there is a class available to do this??
Thanks all for any help
Character/string increment works in PHP (though decrement doesn't)
$x = 'AAZ';
$x++;
echo $x; // 'ABA'
You can do it with the ++ operator.
$i = 'aaz';
$i++;
print $i;
aba
However this implementation has some strange things:
for($i = 'a'; $i < 'z'; $i++) print "$i ";
This will print out letters from a
to y
.
for($i = 'a'; $i <= 'z'; $i++) print "$i ";
This will print out lettes from a
to z
and it continues with aa
and ends with yz
.
As proposed in PHP RFC: Strict operators directive (currently Under Discussion):
Using the increment function on a string will throw a TypeError when strict_operators is enabled.
Whether or not the RFC gets merged, PHP will sooner or later go that direction of adding operator strictness. Therefore, you should not be incrementing strings.
a-z/A-Z ranges
If you know your letters will stay in range a-z/A-Z (not surpass z/Z), you can use the solution that converts letter to ASCII code, increments it, and converts back to letter.
Use ord()
a chr()
:
$letter = 'A';
$letterAscii = ord($letter);
$letterAscii++;
$letter = chr($letterAscii); // 'B'
-
ord()
converts the letter into ASCII num representation - that num representation is incremented
- using
chr()
the number gets converted back to the letter
As discovered in comments, be careful. This iterates ASCII table so from Z
(ASCII 90), it does not go to AA
, but to [
(ASCII 91).
Going beyond z/Z
If you dare to go further and want z
became aa
, this is what I came up with:
final class NextLetter
{
private const ASCII_UPPER_CASE_BOUNDARIES = [65, 91];
private const ASCII_LOWER_CASE_BOUNDARIES = [97, 123];
public static function get(string $previous) : string
{
$letters = str_split($previous);
$output = '';
$increase = true;
while (! empty($letters)) {
$letter = array_pop($letters);
if ($increase) {
$letterAscii = ord($letter);
$letterAscii++;
if ($letterAscii === self::ASCII_UPPER_CASE_BOUNDARIES[1]) {
$letterAscii = self::ASCII_UPPER_CASE_BOUNDARIES[0];
$increase = true;
} elseif ($letterAscii === self::ASCII_LOWER_CASE_BOUNDARIES[1]) {
$letterAscii = self::ASCII_LOWER_CASE_BOUNDARIES[0];
$increase = true;
} else {
$increase = false;
}
$letter = chr($letterAscii);
if ($increase && empty($letters)) {
$letter .= $letter;
}
}
$output = $letter . $output;
}
return $output;
}
}
I'm giving you also 100% coverage if you intend to work with it further. It tests against original string incrementation ++
:
/**
* @dataProvider letterProvider
*/
public function testIncrementLetter(string $givenLetter) : void
{
$expectedValue = $givenLetter;
self::assertSame(++$expectedValue, NextLetter::get($givenLetter));
}
/**
* @return iterable<array<string>>
*/
public function letterProvider() : iterable
{
yield ['A'];
yield ['a'];
yield ['z'];
yield ['Z'];
yield ['aaz'];
yield ['aaZ'];
yield ['abz'];
yield ['abZ'];
}