Skip to content

Commit f81e0d2

Browse files
Improve mb_convert_encoding return type
1 parent 812d7da commit f81e0d2

File tree

4 files changed

+97
-1
lines changed

4 files changed

+97
-1
lines changed

src/Type/Php/MbConvertEncodingFunctionReturnTypeExtension.php

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use PhpParser\Node\Expr\FuncCall;
66
use PHPStan\Analyser\Scope;
7+
use PHPStan\Php\PhpVersion;
78
use PHPStan\Reflection\FunctionReflection;
89
use PHPStan\Reflection\ParametersAcceptorSelector;
910
use PHPStan\Type\Accessory\AccessoryArrayListType;
@@ -17,10 +18,15 @@
1718
use PHPStan\Type\TypeCombinator;
1819
use PHPStan\Type\UnionType;
1920
use function count;
21+
use function str_contains;
2022

2123
final class MbConvertEncodingFunctionReturnTypeExtension implements DynamicFunctionReturnTypeExtension
2224
{
2325

26+
public function __construct(private PhpVersion $phpVersion)
27+
{
28+
}
29+
2430
public function isFunctionSupported(FunctionReflection $functionReflection): bool
2531
{
2632
return $functionReflection->getName() === 'mb_convert_encoding';
@@ -49,6 +55,45 @@ public function getTypeFromFunctionCall(
4955
return null;
5056
}
5157

58+
if ($this->phpVersion->throwsValueErrorForInternalFunctions()) {
59+
if (!isset($functionCall->getArgs()[2])) {
60+
return $result;
61+
}
62+
$fromEncodingArgType = $scope->getType($functionCall->getArgs()[2]->value);
63+
64+
$mayNotDetectEncoding = false;
65+
if (!$fromEncodingArgType->isArray()->no()) {
66+
$constantArrays = $fromEncodingArgType->getConstantArrays();
67+
if (count($constantArrays) > 0) {
68+
foreach ($constantArrays as $constantArray) {
69+
if (count($constantArray->getValueTypes()) > 1) {
70+
$mayNotDetectEncoding = true;
71+
break;
72+
}
73+
}
74+
} else {
75+
$mayNotDetectEncoding = true;
76+
}
77+
}
78+
if (!$mayNotDetectEncoding && !$fromEncodingArgType->isString()->no()) {
79+
$constantStrings = $fromEncodingArgType->getConstantStrings();
80+
if (count($constantStrings) > 0) {
81+
foreach ($constantStrings as $constantString) {
82+
if (str_contains($constantString->getValue(), ',')) {
83+
$mayNotDetectEncoding = true;
84+
break;
85+
}
86+
}
87+
} else {
88+
$mayNotDetectEncoding = true;
89+
}
90+
}
91+
92+
if (!$mayNotDetectEncoding) {
93+
return $result;
94+
}
95+
}
96+
5297
return TypeCombinator::union($result, new ConstantBooleanType(false));
5398
}
5499

tests/PHPStan/Analyser/NodeScopeResolverTest.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,12 @@ private static function findTestFiles(): iterable
5757
yield __DIR__ . '/data/explode-php80.php';
5858
}
5959

60+
if (PHP_VERSION_ID < 80000) {
61+
yield __DIR__ . '/data/mb-convert-encoding-php7.php';
62+
} else {
63+
yield __DIR__ . '/data/mb-convert-encoding-php8.php';
64+
}
65+
6066
if (PHP_VERSION_ID >= 80000) {
6167
yield __DIR__ . '/../Reflection/data/unionTypes.php';
6268
yield __DIR__ . '/../Reflection/data/mixedType.php';

tests/PHPStan/Analyser/nsrt/mb_convert_encoding.php renamed to tests/PHPStan/Analyser/data/mb-convert-encoding-php7.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?php
22

3-
namespace MbConvertEncoding;
3+
namespace MbConvertEncodingPHP7;
44

55
/**
66
* @param 'foo'|'bar' $constantString
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?php
2+
3+
namespace MbConvertEncodingPHP8;
4+
5+
/**
6+
* @param 'foo'|'bar' $constantString
7+
* @param array{foo: string, bar: int, baz: 'foo'} $structuredArray
8+
* @param list<string> $stringList
9+
* @param list<int> $intList
10+
* @param 'foo'|'bar'|array{foo: string, bar: int, baz: 'foo'}|bool $union
11+
*/
12+
function test_mb_convert_encoding(
13+
mixed $mixed,
14+
string $constantString,
15+
string $string,
16+
array $mixedArray,
17+
array $structuredArray,
18+
array $stringList,
19+
array $intList,
20+
string|array|bool $union,
21+
int $int,
22+
): void {
23+
\PHPStan\Testing\assertType('array|string|false', mb_convert_encoding($mixed, 'UTF-8'));
24+
\PHPStan\Testing\assertType('string', mb_convert_encoding($constantString, 'UTF-8'));
25+
\PHPStan\Testing\assertType('string', mb_convert_encoding($string, 'UTF-8'));
26+
\PHPStan\Testing\assertType('array', mb_convert_encoding($mixedArray, 'UTF-8'));
27+
\PHPStan\Testing\assertType('array{foo: string, bar: int, baz: string}', mb_convert_encoding($structuredArray, 'UTF-8'));
28+
\PHPStan\Testing\assertType('list<string>', mb_convert_encoding($stringList, 'UTF-8'));
29+
\PHPStan\Testing\assertType('list<int>', mb_convert_encoding($intList, 'UTF-8'));
30+
\PHPStan\Testing\assertType('array{foo: string, bar: int, baz: string}|string|false', mb_convert_encoding($union, 'UTF-8'));
31+
\PHPStan\Testing\assertType('array|string|false', mb_convert_encoding($int, 'UTF-8'));
32+
33+
\PHPStan\Testing\assertType('string', mb_convert_encoding($string, 'UTF-8', 'FOO'));
34+
\PHPStan\Testing\assertType('string|false', mb_convert_encoding($string, 'UTF-8', $string));
35+
\PHPStan\Testing\assertType('string|false', mb_convert_encoding($string, 'UTF-8', 'FOO,BAR'));
36+
\PHPStan\Testing\assertType('string', mb_convert_encoding($string, 'UTF-8', ['FOO']));
37+
\PHPStan\Testing\assertType('string|false', mb_convert_encoding($string, 'UTF-8', ['FOO', 'BAR']));
38+
\PHPStan\Testing\assertType('string', mb_convert_encoding($string, 'UTF-8', ['FOO,BAR']));
39+
\PHPStan\Testing\assertType('list<string>', mb_convert_encoding($stringList, 'UTF-8', 'FOO'));
40+
\PHPStan\Testing\assertType('list<string>|false', mb_convert_encoding($stringList, 'UTF-8', $string));
41+
\PHPStan\Testing\assertType('list<string>|false', mb_convert_encoding($stringList, 'UTF-8', 'FOO,BAR'));
42+
\PHPStan\Testing\assertType('list<string>', mb_convert_encoding($stringList, 'UTF-8', ['FOO']));
43+
\PHPStan\Testing\assertType('list<string>|false', mb_convert_encoding($stringList, 'UTF-8', ['FOO', 'BAR']));
44+
\PHPStan\Testing\assertType('list<string>', mb_convert_encoding($stringList, 'UTF-8', ['FOO,BAR']));
45+
};

0 commit comments

Comments
 (0)