Skip to content

Commit 5a43cc6

Browse files
committed
Extract and re-use StrIncrementDecrementHelper
1 parent 1c8e76c commit 5a43cc6

File tree

4 files changed

+89
-75
lines changed

4 files changed

+89
-75
lines changed

phpstan-baseline.neon

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ parameters:
3737
path: src/Analyser/MutatingScope.php
3838

3939
-
40-
message: '#^Only numeric types are allowed in pre\-increment, float\|int\|string\|null given\.$#'
40+
message: '#^Only numeric types are allowed in pre\-increment, float\|int\|null given\.$#'
4141
identifier: preInc.nonNumeric
4242
count: 1
4343
path: src/Analyser/MutatingScope.php
@@ -1681,7 +1681,7 @@ parameters:
16811681
message: '#^Cannot access offset int\<0, max\> on \(float\|int\)\.$#'
16821682
identifier: offsetAccess.nonOffsetAccessible
16831683
count: 2
1684-
path: src/Type/Php/StrIncrementDecrementFunctionReturnTypeExtension.php
1684+
path: src/Type/Php/StrIncrementDecrementHelper.php
16851685

16861686
-
16871687
message: '#^Doing instanceof PHPStan\\Type\\Constant\\ConstantStringType is error\-prone and deprecated\. Use Type\:\:getConstantStrings\(\) instead\.$#'

src/Analyser/MutatingScope.php

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@
125125
use PHPStan\Type\ObjectShapeType;
126126
use PHPStan\Type\ObjectType;
127127
use PHPStan\Type\ParserNodeTypeToPHPStanType;
128+
use PHPStan\Type\Php\StrIncrementDecrementHelper;
128129
use PHPStan\Type\StaticType;
129130
use PHPStan\Type\StringType;
130131
use PHPStan\Type\ThisType;
@@ -150,7 +151,6 @@
150151
use function array_values;
151152
use function count;
152153
use function explode;
153-
use function function_exists;
154154
use function get_class;
155155
use function implode;
156156
use function in_array;
@@ -161,7 +161,6 @@
161161
use function ltrim;
162162
use function md5;
163163
use function sprintf;
164-
use function str_increment;
165164
use function str_starts_with;
166165
use function strlen;
167166
use function strtolower;
@@ -1735,8 +1734,8 @@ static function (Node $node, Scope $scope) use ($arrowScope, &$arrowFunctionImpu
17351734
foreach ($varScalars as $varValue) {
17361735
if ($node instanceof Expr\PreInc) {
17371736
if (!is_bool($varValue)) {
1738-
if (function_exists('str_increment') && is_string($varValue)) {
1739-
$varValue = str_increment($varValue);
1737+
if (is_string($varValue)) {
1738+
$varValue = StrIncrementDecrementHelper::increment($varValue);
17401739
} else {
17411740
++$varValue;
17421741
}

src/Type/Php/StrIncrementDecrementFunctionReturnTypeExtension.php

Lines changed: 2 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,12 @@
1111
use PHPStan\Type\ErrorType;
1212
use PHPStan\Type\Type;
1313
use PHPStan\Type\TypeCombinator;
14-
use function chr;
1514
use function count;
16-
use function implode;
1715
use function in_array;
1816
use function is_float;
1917
use function is_int;
20-
use function is_numeric;
2118
use function is_string;
22-
use function ord;
2319
use function preg_match;
24-
use function str_split;
25-
use function stripos;
2620

2721
#[AutowiredService]
2822
final class StrIncrementDecrementFunctionReturnTypeExtension implements DynamicFunctionReturnTypeExtension
@@ -64,9 +58,9 @@ public function getTypeFromFunctionCall(
6458

6559
$result = null;
6660
if ($fnName === 'str_increment') {
67-
$result = $this->increment($string);
61+
$result = StrIncrementDecrementHelper::increment($string);
6862
} elseif ($fnName === 'str_decrement') {
69-
$result = $this->decrement($string);
63+
$result = StrIncrementDecrementHelper::decrement($string);
7064
}
7165

7266
if ($result === null) {
@@ -81,65 +75,4 @@ public function getTypeFromFunctionCall(
8175
: TypeCombinator::union(...$types);
8276
}
8377

84-
private function increment(string $s): string
85-
{
86-
if (is_numeric($s)) {
87-
$offset = stripos($s, 'e');
88-
if ($offset !== false) {
89-
// Using increment operator would cast the string to float
90-
// Therefore we manually increment it to convert it to an "f"/"F" that doesn't get affected
91-
$c = $s[$offset];
92-
$c++;
93-
$s[$offset] = $c;
94-
$s++;
95-
$s[$offset] = [
96-
'f' => 'e',
97-
'F' => 'E',
98-
'g' => 'f',
99-
'G' => 'F',
100-
][$s[$offset]];
101-
102-
return $s;
103-
}
104-
}
105-
106-
return (string) ++$s;
107-
}
108-
109-
private function decrement(string $s): ?string
110-
{
111-
if (in_array($s, ['a', 'A', '0'], true)) {
112-
return null;
113-
}
114-
115-
$decremented = str_split($s, 1);
116-
$position = count($decremented) - 1;
117-
$carry = false;
118-
$map = [
119-
'0' => '9',
120-
'A' => 'Z',
121-
'a' => 'z',
122-
];
123-
do {
124-
$c = $decremented[$position];
125-
if (!in_array($c, ['a', 'A', '0'], true)) {
126-
$carry = false;
127-
$decremented[$position] = chr(ord($c) - 1);
128-
} else {
129-
$carry = true;
130-
$decremented[$position] = $map[$c];
131-
}
132-
} while ($carry && $position-- > 0);
133-
134-
if ($carry || count($decremented) > 1 && $decremented[0] === '0') {
135-
if (count($decremented) === 1) {
136-
return null;
137-
}
138-
139-
unset($decremented[0]);
140-
}
141-
142-
return implode($decremented);
143-
}
144-
14578
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Type\Php;
4+
5+
use function chr;
6+
use function count;
7+
use function implode;
8+
use function in_array;
9+
use function is_numeric;
10+
use function ord;
11+
use function str_split;
12+
use function stripos;
13+
14+
final class StrIncrementDecrementHelper
15+
{
16+
17+
public static function increment(string $s): string
18+
{
19+
if ($s === '') {
20+
return '1';
21+
}
22+
23+
if (is_numeric($s)) {
24+
$offset = stripos($s, 'e');
25+
if ($offset !== false) {
26+
// Using increment operator would cast the string to float
27+
// Therefore we manually increment it to convert it to an "f"/"F" that doesn't get affected
28+
$c = $s[$offset];
29+
$c++;
30+
$s[$offset] = $c;
31+
$s++;
32+
$s[$offset] = [
33+
'f' => 'e',
34+
'F' => 'E',
35+
'g' => 'f',
36+
'G' => 'F',
37+
][$s[$offset]];
38+
39+
return $s;
40+
}
41+
}
42+
43+
return (string) ++$s;
44+
}
45+
46+
public static function decrement(string $s): ?string
47+
{
48+
if (in_array($s, ['a', 'A', '0'], true)) {
49+
return null;
50+
}
51+
52+
$decremented = str_split($s, 1);
53+
$position = count($decremented) - 1;
54+
$carry = false;
55+
$map = [
56+
'0' => '9',
57+
'A' => 'Z',
58+
'a' => 'z',
59+
];
60+
do {
61+
$c = $decremented[$position];
62+
if (!in_array($c, ['a', 'A', '0'], true)) {
63+
$carry = false;
64+
$decremented[$position] = chr(ord($c) - 1);
65+
} else {
66+
$carry = true;
67+
$decremented[$position] = $map[$c];
68+
}
69+
} while ($carry && $position-- > 0);
70+
71+
if ($carry || count($decremented) > 1 && $decremented[0] === '0') {
72+
if (count($decremented) === 1) {
73+
return null;
74+
}
75+
76+
unset($decremented[0]);
77+
}
78+
79+
return implode($decremented);
80+
}
81+
82+
}

0 commit comments

Comments
 (0)