fix: stop relying on the deprecated ReflectionType::__toString() - #21
Merged
Merged
Conversation
`HasMethod` and `HasProperty` cast a `ReflectionType` to string to compare it against the configured type. `ReflectionType::__toString()` has been deprecated since PHP 8.0, and current PHPStan flags both call sites. Add `Support\TypeName`, which reproduces PHP's own rendering from the concrete reflection subclasses instead: - named types, with the `?` prefix that PHP omits for `null` and `mixed` - union types, including the `null` member of a nullable union - intersection types - DNF types, where the nested intersection is parenthesised: `(A&B)|C` Behaviour is unchanged. `TypeName::from()` was diffed against `(string) $type` across 18 type declarations covering every shape above, on PHP 8.4 and 8.5, with no mismatches. `TypeName::from(null)` returns `''`, which is what both call sites did for an absent type, so the null guards are no longer needed. The new fixtures are deliberately absent from the fake classmap, so discovery does not pick them up and existing expectations are untouched. DNF types are PHP 8.2+, so that fixture lives in its own file and its test is skipped below 8.2 — the file is then never autoloaded or parsed.
`$type && is_string($type)` narrows `$type` to a non-falsy string on the truthiness check, so PHPStan reports the `is_string()` that follows as always true. Swapping the two operands puts the type check first, where it is still meaningful. Semantics are identical, truthiness included: the argument `"0"` is falsy and still takes the else branch.
11 tasks
alright
force-pushed
the
fix/reflection-type-to-string
branch
2 times, most recently
from
August 25, 2026 13:36
deb830b to
d8ea640
Compare
teofanis
approved these changes
Aug 26, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What / Why
PHPStan currently fails on
main. The last green run was June 26; sincecomposer.lockis gitignored, CI resolves fresh PHPStan/larastan on every run, and newer releases flag two call sites:They're right on both counts.
ReflectionType::__toString()has been deprecated since PHP 8.0 — it emits no runtime notice on 8.4 or 8.5 (I checked), but it's a documented deprecation sitting on the package's only type-matching path.This PR makes
composer analysegreen again. It is split out from #20 (Laravel 13 / PHP 8.5) so neither diff carries the other's changes — #20 goes green once this lands.How
Support\TypeNamereproduces PHP's own rendering from the concreteReflectionTypesubclasses rather than calling the deprecated method:string,void?int— but never fornullormixed, which PHP leaves bareA&B''HasMethodandHasPropertynow call it. SinceTypeName::from(null)returns''— exactly what both sites produced for an absent type — the null guards fall away:ShowCommand:$type && is_string($type)→is_string($type) && $type. The truthiness check was narrowing$typeto a non-falsy string beforeis_string()ran. Semantics are identical,"0"included — it's falsy and still takes the else branch.Screenshots / Output (if applicable)
Behaviour is unchanged.
TypeName::from()was diffed against(string) $typeacross 18 type declarations covering every shape in the table, on both PHP 8.4 and 8.5:Type of change
Checklist
composer test) —tests/Unit/TypeNameTest.php, 6 cases./vendor/bin/pint) — every touched file passespint --testvendor/composer/autoload_classmap.php(tests usehookpress.composer.classmap_path)Notes for reviewers
DnfShowcaselives in its own fixture file and its test is->skip(PHP_VERSION_ID < 80200). Below 8.2 the class is never referenced, so the file is never autoloaded or parsed — the suite still runs on the 8.1 floor CONTRIBUTING sets.--no-verify, for two reasons worth knowing about:composer run rectorcan't run onmainat all —strictBooleans:was removed from Rector'swithPreparedSets()upstream, sorector.phpthrowsUnknown named parameter $strictBooleans. Fixed in feat: add support for Laravel 13 and PHP 8.5 #20; happy to split that out if you'd rather have it here.fully_qualified_strict_typesnow applies more broadly). That's independent of this change and belongs in its ownchore(style)commit.