Skip to content

Commit

Permalink
feature #453 Add str_increment and str_decrement functions (Fan2S…
Browse files Browse the repository at this point in the history
…hrek)

This PR was merged into the 1.x branch.

Discussion
----------

Add `str_increment` and `str_decrement` functions

Add implementations of `str_increment` and `str_decrement` functions

PS: It's my first PR to open source, feel free to give me feedback !

Fixes #452

Commits
-------

b551b38 Add str_increment and str_decrement functions
  • Loading branch information
nicolas-grekas committed Jan 10, 2024
2 parents 8bd6a32 + b551b38 commit 8a41c5a
Show file tree
Hide file tree
Showing 3 changed files with 240 additions and 0 deletions.
112 changes: 112 additions & 0 deletions src/Php83/Php83.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

/**
* @author Ion Bazan <ion.bazan@gmail.com>
* @author Pierre Ambroise <pierre27.ambroise@gmail.com>
*
* @internal
*/
Expand Down Expand Up @@ -82,4 +83,115 @@ public static function mb_str_pad(string $string, int $length, string $pad_strin
return mb_substr(str_repeat($pad_string, $leftPaddingLength), 0, $leftPaddingLength, $encoding).$string.mb_substr(str_repeat($pad_string, $rightPaddingLength), 0, $rightPaddingLength, $encoding);
}
}

public static function str_increment(string $string): string
{
if ('' === $string) {
throw new \ValueError('str_increment(): Argument #1 ($string) cannot be empty');
}

if (!\preg_match("/^[a-zA-Z0-9]+$/", $string)) {
throw new \ValueError('str_increment(): Argument #1 ($string) must be composed only of alphanumeric ASCII characters');
}

if (\is_numeric($string)) {
$offset = stripos($string, 'e');
if ($offset !== false) {
$char = $string[$offset];
$char++;
$string[$offset] = $char;
$string++;

switch ($string[$offset]) {
case 'f':
$string[$offset] = 'e';
break;
case 'F':
$string[$offset] = 'E';
break;
case 'g':
$string[$offset] = 'f';
break;
case 'G':
$string[$offset] = 'F';
break;
}

return $string;
}
}

return ++$string;
}

public static function str_decrement(string $string): string
{
if ('' === $string) {
throw new \ValueError('str_decrement(): Argument #1 ($string) cannot be empty');
}

if (!\preg_match("/^[a-zA-Z0-9]+$/", $string)) {
throw new \ValueError('str_decrement(): Argument #1 ($string) must be composed only of alphanumeric ASCII characters');
}

if (\preg_match('/\A(?:0[aA0]?|[aA])\z/', $string)) {
throw new \ValueError(sprintf('str_decrement(): Argument #1 ($string) "%s" is out of decrement range', $string));
}

if (!\in_array(substr($string, -1), ['A', 'a', '0'], true)) {
return join('', array_slice(str_split($string), 0, -1)) . chr(ord(substr($string, -1)) - 1);
}

$carry = '';
$decremented = '';

for ($i = strlen($string) - 1; $i >= 0; $i--) {
$char = $string[$i];

switch ($char) {
case 'A':
if ('' !== $carry) {
$decremented = $carry . $decremented;
$carry = '';
}
$carry = 'Z';

break;
case 'a':
if ('' !== $carry) {
$decremented = $carry . $decremented;
$carry = '';
}
$carry = 'z';

break;
case '0':
if ('' !== $carry) {
$decremented = $carry . $decremented;
$carry = '';
}
$carry = '9';

break;
case '1':
if ('' !== $carry) {
$decremented = $carry . $decremented;
$carry = '';
}

break;
default:
if ('' !== $carry) {
$decremented = $carry . $decremented;
$carry = '';
}

if (!\in_array($char, ['A', 'a', '0'], true)) {
$decremented = chr(ord($char) - 1) . $decremented;
}
}
}

return $decremented;
}
}
8 changes: 8 additions & 0 deletions src/Php83/bootstrap.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@ function mb_str_pad(string $string, int $length, string $pad_string = ' ', int $
function stream_context_set_options($context, array $options): bool { return stream_context_set_option($context, $options); }
}

if (!function_exists('str_increment')) {
function str_increment(string $string): string { return p\Php83::str_increment($string); }
}

if (!function_exists('str_decrement')) {
function str_decrement(string $string): string { return p\Php83::str_decrement($string); }
}

if (\PHP_VERSION_ID >= 80100) {
return require __DIR__.'/bootstrap81.php';
}
Expand Down
120 changes: 120 additions & 0 deletions tests/Php83/Php83Test.php
Original file line number Diff line number Diff line change
Expand Up @@ -202,4 +202,124 @@ public function testDateTimeExceptionClassesExist()
$this->assertTrue(class_exists(\DateMalformedIntervalStringException::class));
$this->assertTrue(class_exists(\DateMalformedPeriodStringException::class));
}

/**
* @covers \Symfony\Polyfill\Php83\Php83::str_increment
*
* @dataProvider strIncrementProvider
*/
public function testStrIncrement(string $result, string $string)
{
$this->assertSame($result, str_increment($string));
}

/**
* @covers \Symfony\Polyfill\Php83\Php83::str_decrement
*
* @dataProvider strDecrementProvider
*/
public function testStrDecrement(string $result, string $string)
{
$this->assertSame($result, str_decrement($string));
}

public static function strIncrementProvider(): iterable
{
yield ['ABD', 'ABC'];
yield ['EB', 'EA'];
yield ['AAA', 'ZZ'];
yield ['Ba', 'Az'];
yield ['bA', 'aZ'];
yield ['B0', 'A9'];
yield ['b0', 'a9'];
yield ['AAa', 'Zz'];
yield ['aaA', 'zZ'];
yield ['10a', '9z'];
yield ['10A', '9Z'];
yield ['5e7', '5e6'];
yield ['e', 'd'];
yield ['E', 'D'];
yield ['5', '4'];
}

public static function strDecrementProvider(): iterable
{
yield ['Ay', 'Az'];
yield ['aY', 'aZ'];
yield ['A8', 'A9'];
yield ['a8', 'a9'];
yield ['Yz', 'Za'];
yield ['yZ', 'zA'];
yield ['Y9', 'Z0'];
yield ['y9', 'z0'];
yield ['Z', 'aA'];
yield ['9', 'A0'];
yield ['9', 'a0'];
yield ['9', '10'];
yield ['Z', '1A'];
yield ['z', '1a'];
yield ['9z', '10a'];
yield ['5e5', '5e6'];
yield ['C', 'D'];
yield ['c', 'd'];
yield ['3', '4'];
}

/**
* @covers \Symfony\Polyfill\Php83\Php83::str_increment
*
* @dataProvider strInvalidIncrementProvider
*/
public function testInvalidStrIncrement(string $errorMessage, string $string)
{
$this->expectException(\ValueError::class);
$this->expectExceptionMessage($errorMessage);

str_increment($string);
}


public static function strInvalidIncrementProvider(): iterable
{
yield ['str_increment(): Argument #1 ($string) cannot be empty', ""];
yield ['str_increment(): Argument #1 ($string) must be composed only of alphanumeric ASCII characters', "-cc"];
yield ['str_increment(): Argument #1 ($string) must be composed only of alphanumeric ASCII characters', "Z "];
yield ['str_increment(): Argument #1 ($string) must be composed only of alphanumeric ASCII characters', " Z"];
yield ['str_increment(): Argument #1 ($string) must be composed only of alphanumeric ASCII characters', "é"];
yield ['str_increment(): Argument #1 ($string) must be composed only of alphanumeric ASCII characters', '我喜歡雞肉'];
yield ['str_increment(): Argument #1 ($string) must be composed only of alphanumeric ASCII characters', 'α'];
yield ['str_increment(): Argument #1 ($string) must be composed only of alphanumeric ASCII characters', 'ω'];
yield ['str_increment(): Argument #1 ($string) must be composed only of alphanumeric ASCII characters', 'Α'];
yield ['str_increment(): Argument #1 ($string) must be composed only of alphanumeric ASCII characters', 'Ω'];
yield ['str_increment(): Argument #1 ($string) must be composed only of alphanumeric ASCII characters', 'foo1.txt'];
yield ['str_increment(): Argument #1 ($string) must be composed only of alphanumeric ASCII characters', '1f.5'];
yield ['str_increment(): Argument #1 ($string) must be composed only of alphanumeric ASCII characters', 'foo.1.txt'];
yield ['str_increment(): Argument #1 ($string) must be composed only of alphanumeric ASCII characters', '1.f.5'];
}


/**
* @covers \Symfony\Polyfill\Php83\Php83::str_decrement
*
* @dataProvider strInvalidDecrementProvider
*/
public function testInvalidStrDecrement(string $errorMessage, string $string)
{
$this->expectException(\ValueError::class);
$this->expectExceptionMessage($errorMessage);

str_decrement($string);
}

public static function strInvalidDecrementProvider(): iterable
{
yield ['str_decrement(): Argument #1 ($string) cannot be empty', ''];
yield ['str_decrement(): Argument #1 ($string) must be composed only of alphanumeric ASCII characters', '我喜歡雞肉'];
yield ['str_decrement(): Argument #1 ($string) "0" is out of decrement range', '0'];
yield ['str_decrement(): Argument #1 ($string) "a" is out of decrement range', 'a'];
yield ['str_decrement(): Argument #1 ($string) "A" is out of decrement range', 'A'];
yield ['str_decrement(): Argument #1 ($string) "00" is out of decrement range', '00'];
yield ['str_decrement(): Argument #1 ($string) "0a" is out of decrement range', '0a'];
yield ['str_decrement(): Argument #1 ($string) "0A" is out of decrement range', '0A'];
}
}

0 comments on commit 8a41c5a

Please sign in to comment.