Skip to content

Commit e474637

Browse files
Update ReplaceFunctionsDynamicReturnTypeExtension for lowercase-string
1 parent e45b637 commit e474637

File tree

4 files changed

+59
-7
lines changed

4 files changed

+59
-7
lines changed

src/Type/Php/ReplaceFunctionsDynamicReturnTypeExtension.php

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use PHPStan\Analyser\Scope;
77
use PHPStan\Reflection\FunctionReflection;
88
use PHPStan\Reflection\ParametersAcceptorSelector;
9+
use PHPStan\Type\Accessory\AccessoryLowercaseStringType;
910
use PHPStan\Type\Accessory\AccessoryNonEmptyStringType;
1011
use PHPStan\Type\Accessory\AccessoryNonFalsyStringType;
1112
use PHPStan\Type\Constant\ConstantArrayTypeBuilder;
@@ -82,17 +83,26 @@ private function getPreliminarilyResolvedTypeFromFunctionCall(
8283
return TypeUtils::toBenevolentUnion($defaultReturnType);
8384
}
8485

85-
if ($subjectArgumentType->isNonEmptyString()->yes() && array_key_exists($functionReflection->getName(), self::FUNCTIONS_REPLACE_POSITION)) {
86+
if (array_key_exists($functionReflection->getName(), self::FUNCTIONS_REPLACE_POSITION)) {
8687
$replaceArgumentPosition = self::FUNCTIONS_REPLACE_POSITION[$functionReflection->getName()];
8788

8889
if (count($functionCall->getArgs()) > $replaceArgumentPosition) {
8990
$replaceArgumentType = $scope->getType($functionCall->getArgs()[$replaceArgumentPosition]->value);
9091

92+
$accessories = [];
9193
if ($subjectArgumentType->isNonFalsyString()->yes() && $replaceArgumentType->isNonFalsyString()->yes()) {
92-
return new IntersectionType([new StringType(), new AccessoryNonFalsyStringType()]);
94+
$accessories[] = new AccessoryNonFalsyStringType();
95+
} elseif ($subjectArgumentType->isNonEmptyString()->yes() && $replaceArgumentType->isNonEmptyString()->yes()) {
96+
$accessories[] = new AccessoryNonEmptyStringType();
9397
}
94-
if ($replaceArgumentType->isNonEmptyString()->yes()) {
95-
return new IntersectionType([new StringType(), new AccessoryNonEmptyStringType()]);
98+
99+
if ($subjectArgumentType->isLowercaseString()->yes() && $replaceArgumentType->isLowercaseString()->yes()) {
100+
$accessories[] = new AccessoryLowercaseStringType();
101+
}
102+
103+
if (count($accessories) > 0) {
104+
$accessories[] = new StringType();
105+
return new IntersectionType($accessories);
96106
}
97107
}
98108
}

tests/PHPStan/Analyser/LegacyNodeScopeResolverTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7414,15 +7414,15 @@ public function dataReplaceFunctions(): array
74147414
{
74157415
return [
74167416
[
7417-
'non-falsy-string',
7417+
'lowercase-string&non-falsy-string',
74187418
'$expectedString',
74197419
],
74207420
[
74217421
'string|null',
74227422
'$expectedString2',
74237423
],
74247424
[
7425-
'non-falsy-string|null',
7425+
'(lowercase-string&non-falsy-string)|null',
74267426
'$anotherExpectedString',
74277427
],
74287428
[

tests/PHPStan/Analyser/nsrt/isset-coalesce-empty-type.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -472,7 +472,7 @@ function coalesce()
472472

473473
assertType('int<0, max>', rand() ?? false);
474474

475-
assertType('0|string', preg_replace('', '', '') ?? 0);
475+
assertType('0|lowercase-string', preg_replace('', '', '') ?? 0);
476476

477477
$foo = new FooCoalesce();
478478

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php
2+
3+
namespace LowercaseStringReplace;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
class ReplaceStrings
8+
{
9+
10+
/**
11+
* @param string $s
12+
* @param lowercase-string $ls
13+
*/
14+
public function doFoo(string $s, string $ls): void
15+
{
16+
assertType('lowercase-string', str_replace($s, $ls, $ls));
17+
assertType('string', str_replace($s, $s, $ls));
18+
assertType('string', str_replace($s, $ls, $s));
19+
assertType('lowercase-string', str_replace($ls, $ls, $ls));
20+
assertType('string', str_replace($ls, $s, $ls));
21+
assertType('string', str_replace($ls, $ls, $s));
22+
23+
assertType('lowercase-string', str_ireplace($s, $ls, $ls));
24+
assertType('string', str_ireplace($s, $s, $ls));
25+
assertType('string', str_ireplace($s, $ls, $s));
26+
assertType('lowercase-string', str_ireplace($ls, $ls, $ls));
27+
assertType('string', str_ireplace($ls, $s, $ls));
28+
assertType('string', str_ireplace($ls, $ls, $s));
29+
30+
assertType('lowercase-string|null', preg_replace($s, $ls, $ls));
31+
assertType('string|null', preg_replace($s, $s, $ls));
32+
assertType('string|null', preg_replace($s, $ls, $s));
33+
assertType('lowercase-string|null', preg_replace($ls, $ls, $ls));
34+
assertType('string|null', preg_replace($ls, $s, $ls));
35+
assertType('string|null', preg_replace($ls, $ls, $s));
36+
37+
assertType('lowercase-string', substr_replace($ls, $ls, 1));
38+
assertType('string', substr_replace($s, $ls, 1));
39+
assertType('string', substr_replace($ls, $s, 1));
40+
assertType('string', substr_replace($s, $s, 1));
41+
}
42+
}

0 commit comments

Comments
 (0)