param type can be a string but not class-string #10976
-
I was wondering if it's possible to annotate a method as accepting a class Foo
{
/**
* @param string & !class-string $entityName
*/
function getEntity(string $entityName) { /** .. */ }
}
$foo = new Foo();
$foo->getEntity(EntityName::class); // invalid
$foo->getEntity('EntityName'); // legal As far as I can see in the manual, phpstan 1.9 does not support this. Are there any workarounds? |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 3 replies
-
What's the use case for such a method? Right now you'd have to write a custom rule and check the argument type. |
Beta Was this translation helpful? Give feedback.
-
So the idea is that we're going to make a new custom PHPDoc type thanks to https://phpstan.org/developing-extensions/custom-phpdoc-types. Step 1: Implement NonClassStringTypenamespace App;
use PHPStan\Type\AcceptsResult;
use PHPStan\Type\StringType;
use PHPStan\Type\Type;
use PHPStan\Type\VerbosityLevel;
class NonClassStringType extends StringType
{
public function describe(VerbosityLevel $level): string
{
return 'non-class-string';
}
public function acceptsWithReason(Type $type, bool $strictTypes): AcceptsResult
{
if ($type->isClassStringType()->yes()) {
return AcceptsResult::createNo();
}
return parent::acceptsWithReason($type, $strictTypes);
}
} That's basically it. When implementing a custom type, we should make sure it plays well with other types in
These should be tested in a similar way PHPStan internals are tested here https://github.com/phpstan/phpstan-src/blob/1.11.x/tests/PHPStan/Type/TypeCombinatorTest.php Step 2: TypeNodeResolverExtensionThis makes sure that namespace App;
use PHPStan\Analyser\NameScope;
use PHPStan\PhpDoc\TypeNodeResolverExtension;
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
use PHPStan\Type\Type;
use function strtolower;
class NonClassStringTypeNodeResolverExtension implements TypeNodeResolverExtension
{
public function resolve(TypeNode $typeNode, NameScope $nameScope): ?Type
{
if (!$typeNode instanceof IdentifierTypeNode) {
return null;
}
$name = strtolower($typeNode->name);
if ($name !== 'non-class-string') {
return null;
}
return new NonClassStringType();
}
} And register this extension in phpstan.neon: services:
-
class: App\NonClassStringTypeNodeResolverExtension
tags:
- phpstan.phpDoc.typeNodeResolverExtension Step 3: Test that it worksWith all above, with a function marked with this type, we should observe the correct behaviour: /** @param non-class-string $name */
function sayHello(string $name): void
{
}
sayHello('blah blah'); // OK
sayHello(\stdClass::class); // Error reported Let me know how it turned out! |
Beta Was this translation helpful? Give feedback.
So the idea is that we're going to make a new custom PHPDoc type thanks to https://phpstan.org/developing-extensions/custom-phpdoc-types.
Step 1: Implement NonClassStringType
That's basically…