Skip to content

Commit b012bd4

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

File tree

4 files changed

+86
-1
lines changed

4 files changed

+86
-1
lines changed

src/Type/Php/MbConvertEncodingFunctionReturnTypeExtension.php

Lines changed: 34 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;
@@ -21,6 +22,10 @@
2122
final class MbConvertEncodingFunctionReturnTypeExtension implements DynamicFunctionReturnTypeExtension
2223
{
2324

25+
public function __construct(private PhpVersion $phpVersion)
26+
{
27+
}
28+
2429
public function isFunctionSupported(FunctionReflection $functionReflection): bool
2530
{
2631
return $functionReflection->getName() === 'mb_convert_encoding';
@@ -49,6 +54,35 @@ public function getTypeFromFunctionCall(
4954
return null;
5055
}
5156

57+
if ($this->phpVersion->throwsValueErrorForInternalFunctions()) {
58+
if (!isset($functionCall->getArgs()[2])) {
59+
return $result;
60+
}
61+
$fromEncodingArgType = $scope->getType($functionCall->getArgs()[2]->value);
62+
63+
$mayNotDetectEncoding = false;
64+
if (!$fromEncodingArgType->isArray()->no()) {
65+
foreach ($fromEncodingArgType->getConstantArrays() as $constantArray) {
66+
if (count($constantArray->getValueTypes()) > 1) {
67+
$mayNotDetectEncoding = true;
68+
break;
69+
}
70+
}
71+
}
72+
if (!$mayNotDetectEncoding && !$fromEncodingArgType->isString()->no()) {
73+
foreach ($fromEncodingArgType->getConstantStrings() as $constantString) {
74+
if (str_contains($constantString->getValue(), ',')) {
75+
$mayNotDetectEncoding = true;
76+
break;
77+
}
78+
}
79+
}
80+
81+
if (!$mayNotDetectEncoding) {
82+
return $result;
83+
}
84+
}
85+
5286
return TypeCombinator::union($result, new ConstantBooleanType(false));
5387
}
5488

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)