Fix phpstan/phpstan#3250: \settype() is not handled properly#5404
Conversation
- When settype()'s second argument is a non-constant string, broaden the variable type to all possible settype outcomes (bool|int|float|string|array|object|null) - Previously, non-constant type strings caused the extension to return empty SpecifiedTypes, leaving the variable type unchanged - New regression test in tests/PHPStan/Analyser/nsrt/bug-3250.php
| $constantStrings = $castType->getConstantStrings(); | ||
| if (count($constantStrings) < 1) { | ||
| return new SpecifiedTypes(); | ||
| return $this->typeSpecifier->create( |
There was a problem hiding this comment.
Instead of returning here, just write
$constantStrings = [...];
with all the valid values and let the foreach do the job.
Also, in the foreach
case 'object':
$types[] = new ObjectType(stdClass::class);
is not correct as
$a = 1; settype($a, 'object');
gives stdClass but
$a = new \DateTime; settype($a, 'object');
gives a DateTime.
So fix the object case.
There was a problem hiding this comment.
Done. The changes address both review comments:
-
Reuse foreach for non-constant types: Instead of returning a hardcoded union type, the non-constant fallback now populates
$constantStringswith all valid settype values (bool,int,float,string,array,object,null) and lets the existing foreach do the work. This avoids duplicating the type mapping logic. -
Fix the
objectcase: Theobjectcase now correctly handles three scenarios:- Value is definitely an object → keeps original type (e.g.
DateTimestaysDateTime) - Value is definitely not an object → produces
stdClass - Value might be an object → union of the object part of the type with
stdClass(e.g.DateTime|string→DateTime|stdClass)
- Value is definitely an object → keeps original type (e.g.
…ix object case Instead of returning a hardcoded union for non-constant type strings, populate $constantStrings with all valid values and let the existing foreach handle them. This avoids duplicating the type mapping logic. Fix the 'object' case to preserve the original type when the value is already an object (e.g. settype($dateTime, 'object') keeps DateTime), instead of always returning stdClass. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
| break; | ||
| case 'object': | ||
| $types[] = new ObjectType(stdClass::class); | ||
| if ($valueType->isObject()->yes()) { |
There was a problem hiding this comment.
Use a constant for $valueType->isObject() to avoid computing it two times.
There was a problem hiding this comment.
The background make phpstan task completed successfully (already saw the result earlier — no errors). Everything is done and pushed.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Summary
When
settype()is called with a non-constant string as the type argument (e.g. a parameterstring $castTo), PHPStan did not update the variable's type at all, leading to false positives about impossible return types.Changes
src/Type/Php/SetTypeFunctionTypeSpecifyingExtension.phpto handle non-constant type strings by setting the variable type to the union of all possiblesettype()outcomes:array|bool|float|int|stdClass|string|nulltests/PHPStan/Analyser/nsrt/bug-3250.phpcovering non-constant, constant, and union-of-constants type argumentsRoot cause
The
SetTypeFunctionTypeSpecifyingExtensionreturned an emptySpecifiedTypeswhen the second argument had no constant string values, which meant the variable's type was left unchanged after thesettype()call. The fix returns a union of all possible result types when the cast target is unknown.Test
The NSRT test verifies:
'int'producesint'int'|'float'producesfloat|intFixes phpstan/phpstan#3250