Skip to content

Commit 9e7b557

Browse files
phpstan-botclaude
andcommitted
Drop the resource native type in PhpStormStubsSourceStubber instead of resolving it in ParserNodeTypeToPHPStanType
`resource` is a pseudo-type, PHP has no such native type - a `resource` typehint in source is read as a class name. So `ParserNodeTypeToPHPStanType` is the wrong place to special-case it: it resolves native types for all code, including a userland class named `Resource`. The pseudo-type only enters through the PhpStorm stubs, which describe pre-PHP 8 signatures with `#[LanguageLevelTypeAware(['8.1' => 'finfo'], default: 'resource')]`. `PhpStormStubsSourceStubber` turned that string into a native type, so every such signature ended up with `ObjectType('resource')` - a class type unrelated to `ResourceType`, hence "expects resource, resource given". Drop the native type there instead (the stubber already does this for other unrepresentable types such as `string[]|string|null`), which leaves the `@param resource`/`@return resource` PHPDoc in the same stub to describe the type. Unions containing `resource` (`resource|false`, `resource|array|string`) are handled too - those additionally made `is_resource()` narrow to `*NEVER*`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1 parent fc4f414 commit 9e7b557

4 files changed

Lines changed: 21 additions & 9 deletions

File tree

‎composer.json‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,9 @@
144144
"nikic/php-parser": [
145145
"patches/BuilderHelpers.patch"
146146
],
147+
"ondrejmirtes/better-reflection": [
148+
"patches/PhpStormStubsSourceStubber.patch"
149+
],
147150
"symfony/console": [
148151
"patches/OutputFormatter.patch",
149152
"patches/Application.patch"

‎composer.lock‎

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
--- a/src/SourceLocator/SourceStubber/PhpStormStubsSourceStubber.php
2+
+++ b/src/SourceLocator/SourceStubber/PhpStormStubsSourceStubber.php
3+
@@ -991,6 +991,14 @@
4+
return null;
5+
}
6+
7+
+ // `resource` is a pseudo-type, PHP has no such native type - a `resource`
8+
+ // typehint is read back as a class name. Stubs use it in
9+
+ // `#[LanguageLevelTypeAware]` to describe pre-PHP 8 signatures, so drop the
10+
+ // native type there and let the PHPDoc in the same stub describe the type.
11+
+ if (in_array('resource', explode('|', ltrim($type, '?')), true)) {
12+
+ return null;
13+
+ }
14+
+
15+
/** @psalm-suppress InternalClass, InternalMethod */
16+
return BuilderHelpers::normalizeType($type);
17+
}

‎src/Type/ParserNodeTypeToPHPStanType.php‎

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,6 @@ public static function resolve($type, ?ClassReflection $classReflection): Type
2626
} elseif ($type instanceof Name) {
2727
$typeClassName = (string) $type;
2828
$lowercasedClassName = strtolower($typeClassName);
29-
if ($typeClassName === 'resource') {
30-
// PhpStorm stubs describe pre-PHP 8 signatures with a `resource` native type.
31-
// Compared case-sensitively on purpose: PHP has no `resource` native type,
32-
// it reads such a typehint as a class name, so a userland class named
33-
// `Resource` has to keep resolving to an object type.
34-
return new ResourceType();
35-
}
36-
3729
if ($classReflection !== null && in_array($lowercasedClassName, ['self', 'static'], true)) {
3830
if ($lowercasedClassName === 'static') {
3931
return new StaticType($classReflection);

0 commit comments

Comments
 (0)