Skip to content

Commit e40cef4

Browse files
ondrejmirtesclaude
andcommitted
Treat unsealed from-encoding arrays as multi-candidate in mb_convert_encoding()
The PHP 8 "can this return false?" check counted only the explicit `getValueTypes()` of the from-encoding array, so a one-explicit-key unsealed array — `mb_convert_encoding($s, 'UTF-8', array{'FOO', ...<int, string>})` — was treated as a single guaranteed encoding and the `false` branch was dropped. The unsealed extras can supply further candidates, making it an auto-detect list that may fail. Extend the `count(...) > 1` gate with `|| isUnsealed()->yes()` so the result keeps `false` as a possible outcome. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 3699e95 commit e40cef4

2 files changed

Lines changed: 12 additions & 1 deletion

File tree

src/Type/Php/MbConvertEncodingFunctionReturnTypeExtension.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,11 @@ public function getTypeFromFunctionCall(
7171
$constantArrays = $fromEncodingArgType->getConstantArrays();
7272
if (count($constantArrays) > 0) {
7373
foreach ($constantArrays as $constantArray) {
74-
if (count($constantArray->getValueTypes()) > 1) {
74+
// Unsealed extras can add further encoding candidates
75+
// on top of the explicit ones, so the list may hold
76+
// 2+ entries (auto-detect → may return false) even when
77+
// only one explicit value is present.
78+
if (count($constantArray->getValueTypes()) > 1 || $constantArray->isUnsealed()->yes()) {
7579
$returnFalseIfCannotDetectEncoding = true;
7680
break;
7781
}

tests/PHPStan/Analyser/nsrt/mb-convert-encoding-php8.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
* @param list<string> $stringList
99
* @param list<int> $intList
1010
* @param 'foo'|'bar'|array{foo: string, bar: int, baz: 'foo'}|bool $union
11+
* @param array{'FOO', ...<int, string>} $unsealedEncodings
1112
*/
1213
function test_mb_convert_encoding(
1314
mixed $mixed,
@@ -19,6 +20,7 @@ function test_mb_convert_encoding(
1920
array $intList,
2021
string|array|bool $union,
2122
int $int,
23+
array $unsealedEncodings,
2224
): void {
2325
\PHPStan\Testing\assertType('array|string', mb_convert_encoding($mixed, 'UTF-8'));
2426
\PHPStan\Testing\assertType('string', mb_convert_encoding($constantString, 'UTF-8'));
@@ -45,4 +47,9 @@ function test_mb_convert_encoding(
4547

4648
\PHPStan\Testing\assertType('string|false', mb_convert_encoding($string, 'UTF-8', 'auto'));
4749
\PHPStan\Testing\assertType('string|false', mb_convert_encoding($string, 'UTF-8', ' AUTO '));
50+
51+
// One explicit encoding, but the unsealed extras can add more, so the
52+
// from-encoding list may hold 2+ candidates → auto-detect → false is
53+
// possible.
54+
\PHPStan\Testing\assertType('string|false', mb_convert_encoding($string, 'UTF-8', $unsealedEncodings));
4855
};

0 commit comments

Comments
 (0)