← All results

Generic class-string and interface-string refinements

Group: Advanced PHPDoc types · Category: ecosystem · File: phpdoc_advanced_generic_class_string.php

Plain class-string is phpdoc_advanced_fallback_class_string. This file asks whether a type argument is applied (class-string<Contract>) and whether interface-string is a distinct spelling. References: - PHPStan class-string and interface-string types - Psalm class-string and interface-string types

Analyzer results

AnalyzerVersionResultDiagnostics
phan 6.0.7Phan 6.0.7 php-ast version 1.1.3 PHP version used to run Phan: 8.5.9 Unrecognized
phpdoc_advanced_generic_class_string.php:39: Saw possibly unextractable annotation for a fragment of comment '* @param interface-string<Contract> $interface': after interface, did not see an element name (will guess based on comment order) [PhanUnextractableAnnotationElementName]
phpdoc_advanced_generic_class_string.php:39: Saw a token Phan may have failed to parse after '* @param interface-string<Contract> $interface': after interface, saw '-' [PhanUnextractableAnnotationSuffix]
phpdoc_advanced_generic_class_string.php:41: Parameter $interface has undeclared type \Conformance\Tests\PhpdocAdvancedGenericClassString\interface [PhanUndeclaredTypeParameter]
phpdoc_advanced_generic_class_string.php:47: Argument 1 ($interface) is Contract::class of type string but \Conformance\Tests\PhpdocAdvancedGenericClassString\takesContractInterfaceString() takes \Conformance\Tests\PhpdocAdvancedGenericClassString\interface (no real type) defined at /Users/megurine/repo/php/php-typing-conformance/conformance/tests/phpdoc_advanced_generic_class_string.php:41 (the inferred real argument type has nothing in common with the parameter's phpdoc type) [PhanTypeMismatchArgumentProbablyReal]
phpdoc_advanced_generic_class_string.php:50: Argument 1 ($interface) is Implementation::class of type string but \Conformance\Tests\PhpdocAdvancedGenericClassString\takesContractInterfaceString() takes \Conformance\Tests\PhpdocAdvancedGenericClassString\interface (no real type) defined at /Users/megurine/repo/php/php-typing-conformance/conformance/tests/phpdoc_advanced_generic_class_string.php:41 (the inferred real argument type has nothing in common with the parameter's phpdoc type) [PhanTypeMismatchArgumentProbablyReal]
phpdoc_advanced_generic_class_string.php:51: Possible use of a classlike '\not a class name' with an invalid FQSEN. [PhanInvalidFQSENInClasslike]
phpdoc_advanced_generic_class_string.php:51: Argument 1 ($class) is 'not a class name' of type string but \Conformance\Tests\PhpdocAdvancedGenericClassString\takesContractClassString() takes class-string<\Conformance\Tests\PhpdocAdvancedGenericClassString\Contract> (no real type) defined at /Users/megurine/repo/php/php-typing-conformance/conformance/tests/phpdoc_advanced_generic_class_string.php:34 (the inferred real argument type has nothing in common with the parameter's phpdoc type) [PhanTypeMismatchArgumentProbablyReal]
  • Recognition: spelling not resolved — reported on declaration line(s) 39, 41
  • Enforcement: 2/3 of the expected violations reported — incidental, since the spelling was not resolved
Expectation diff
Line 47: Expected valid value to be accepted, got ["Argument 1 ($interface) is Contract::class of type string but \\Conformance\\Tests\\PhpdocAdvancedGenericClassString\\takesContractInterfaceString() takes \\Conformance\\Tests\\PhpdocAdvancedGenericClassString\\interface (no real type) defined at /Users/megurine/repo/php/php-typing-conformance/conformance/tests/phpdoc_advanced_generic_class_string.php:41 (the inferred real argument type has nothing in common with the parameter's phpdoc type) [PhanTypeMismatchArgumentProbablyReal]"]
phpstan 2.2.11PHPStan - PHP Static Analysis Tool 2.2.11 Partly enforced (2/3) reported Lv.5+
phpdoc_advanced_generic_class_string.php:49: Parameter #1 $class of function Conformance\Tests\PhpdocAdvancedGenericClassString\takesContractClassString expects class-string<Conformance\Tests\PhpdocAdvancedGenericClassString\Contract>, string given. [reported-from-level=5]
phpdoc_advanced_generic_class_string.php:51: Parameter #1 $class of function Conformance\Tests\PhpdocAdvancedGenericClassString\takesContractClassString expects class-string<Conformance\Tests\PhpdocAdvancedGenericClassString\Contract>, string given. [reported-from-level=5]
With strict-rules
phpdoc_advanced_generic_class_string.php:49: Parameter #1 $class of function Conformance\Tests\PhpdocAdvancedGenericClassString\takesContractClassString expects class-string<Conformance\Tests\PhpdocAdvancedGenericClassString\Contract>, string given.
phpdoc_advanced_generic_class_string.php:51: Parameter #1 $class of function Conformance\Tests\PhpdocAdvancedGenericClassString\takesContractClassString expects class-string<Conformance\Tests\PhpdocAdvancedGenericClassString\Contract>, string given.
  • Recognition: spelling resolved
  • Enforcement: 2/3 of the expected violations reported
psalm 6.16.1Psalm 6.16.1@f1f5de594dc76faf8784e02d3dc4716c91c6f6ac
next: 7.0.0-beta19
Partly enforced (2/3)
phpdoc_advanced_generic_class_string.php:49: Argument 1 of Conformance\Tests\PhpdocAdvancedGenericClassString\takesContractClassString expects class-string<Conformance\Tests\PhpdocAdvancedGenericClassString\Contract>, but Conformance\Tests\PhpdocAdvancedGenericClassString\Unrelated::class provided [InvalidArgument]
phpdoc_advanced_generic_class_string.php:51: Argument 1 of Conformance\Tests\PhpdocAdvancedGenericClassString\takesContractClassString expects class-string<Conformance\Tests\PhpdocAdvancedGenericClassString\Contract>, but parent type 'not a class name' provided [ArgumentTypeCoercion]
phpdoc_advanced_generic_class_string.php:51: Class, interface or enum named not a class name does not exist [UndefinedClass]
  • Recognition: spelling resolved
  • Enforcement: 2/3 of the expected violations reported
mago 1.47.4mago 1.47.4 Partly enforced (2/3)
phpdoc_advanced_generic_class_string.php:49: Invalid argument type for argument #1 of `Conformance\Tests\PhpdocAdvancedGenericClassString\takesContractClassString`: expected `class-string<Conformance\Tests\PhpdocAdvancedGenericClassString\Contract>`, but found `class-string('Conformance\Tests\PhpdocAdvancedGenericClassString\Unrelated')`. [invalid-argument]
phpdoc_advanced_generic_class_string.php:51: Possible argument type mismatch for argument #1 of `Conformance\Tests\PhpdocAdvancedGenericClassString\takesContractClassString`: expected `class-string<Conformance\Tests\PhpdocAdvancedGenericClassString\Contract>`, but possibly received `string('not a class name')`. [possibly-invalid-argument]
  • Recognition: spelling resolved
  • Enforcement: 2/3 of the expected violations reported
mir 0.72.1mir 0.72.1 Partly enforced (2/3)
phpdoc_advanced_generic_class_string.php:49: InvalidArgument: Argument $class of takesContractClassString() expects 'class-string<Conformance\Tests\PhpdocAdvancedGenericClassString\Contract>', got 'class-string<Conformance\Tests\PhpdocAdvancedGenericClassString\Unrelated>' [MIR0201]
phpdoc_advanced_generic_class_string.php:50: InvalidArgument: Argument $interface of takesContractInterfaceString() expects 'interface-string<Conformance\Tests\PhpdocAdvancedGenericClassString\Contract>', got 'class-string<Conformance\Tests\PhpdocAdvancedGenericClassString\Implementation>' [MIR0201]
  • Recognition: spelling resolved
  • Enforcement: 2/3 of the expected violations reported
phpantom 0.10.0phpantom_lsp 0.10.0 Partly enforced (2/3)
phpdoc_advanced_generic_class_string.php:49: Argument 1 ($class) expects class-string<Conformance\Tests\PhpdocAdvancedGenericClassString\Contract>, got class-string<Conformance\Tests\PhpdocAdvancedGenericClassString\Unrelated> [type_mismatch_argument]
phpdoc_advanced_generic_class_string.php:50: Argument 1 ($interface) expects interface-string<Conformance\Tests\PhpdocAdvancedGenericClassString\Contract>, got class-string<Conformance\Tests\PhpdocAdvancedGenericClassString\Implementation> [type_mismatch_argument]
  • Recognition: spelling resolved
  • Enforcement: 2/3 of the expected violations reported
intelephense 1.18.5intelephense 1.18.5 Not enforced

No diagnostics reported.

  • Recognition: spelling resolved
  • Enforcement: 0/3 of the expected violations reported
phpactor 2026.07.22.0Phpactor 2026.07.22.0 Not enforced

No diagnostics reported.

  • Recognition: spelling resolved
  • Enforcement: 0/3 of the expected violations reported
phpy 0.2.0phpy 0.2.0 Not enforced

No diagnostics reported.

  • Recognition: spelling resolved
  • Enforcement: 0/3 of the expected violations reported
qodana 262.9437.196Qodana 262.9437.196 Unrecognized
phpdoc_advanced_generic_class_string.php:39: Undefined class 'interface-string' [PhpUndefinedClassInspection]
phpdoc_advanced_generic_class_string.php:47: Parameter 'Contract::class' type is not compatible with declaration [PhpParamsInspection]
phpdoc_advanced_generic_class_string.php:50: Parameter 'Implementation::class' type is not compatible with declaration [PhpParamsInspection]
  • Recognition: spelling not resolved — reported on declaration line(s) 39
  • Enforcement: 1/3 of the expected violations reported — incidental, since the spelling was not resolved
Expectation diff
Line 47: Expected valid value to be accepted, got ["Parameter 'Contract::class' type is not compatible with declaration [PhpParamsInspection]"]
noverify 0.5.5NoVerify, version 0.5.5: built on: 2025.04.22 14:36:46 OS: r-kuchinskas arm64 Commit: 4774b82f8adc53fbef38f95e97af4b953b4d1529 Not enforced

No diagnostics reported.

  • Recognition: spelling resolved
  • Enforcement: 0/3 of the expected violations reported
steins 0.1.6steins 0.1.6 (2026-08-25 revision c13b4c7) Not enforced

No diagnostics reported.

  • Recognition: spelling resolved
  • Enforcement: 0/3 of the expected violations reported
phpstan-strict 2.2.11PHPStan - PHP Static Analysis Tool 2.2.11 Partly enforced (2/3)
phpdoc_advanced_generic_class_string.php:49: Parameter #1 $class of function Conformance\Tests\PhpdocAdvancedGenericClassString\takesContractClassString expects class-string<Conformance\Tests\PhpdocAdvancedGenericClassString\Contract>, string given.
phpdoc_advanced_generic_class_string.php:51: Parameter #1 $class of function Conformance\Tests\PhpdocAdvancedGenericClassString\takesContractClassString expects class-string<Conformance\Tests\PhpdocAdvancedGenericClassString\Contract>, string given.
  • Recognition: spelling resolved
  • Enforcement: 2/3 of the expected violations reported
psalm-next 7.0.0-beta19Psalm 7.0.0-beta19@7e751c06a756fa64dc4c759c09fe4a173afcb433 Partly enforced (2/3)
phpdoc_advanced_generic_class_string.php:49: Argument 1 of Conformance\Tests\PhpdocAdvancedGenericClassString\takesContractClassString expects class-string<Conformance\Tests\PhpdocAdvancedGenericClassString\Contract>, but Conformance\Tests\PhpdocAdvancedGenericClassString\Unrelated::class provided [InvalidArgument]
phpdoc_advanced_generic_class_string.php:51: Argument 1 of Conformance\Tests\PhpdocAdvancedGenericClassString\takesContractClassString expects class-string<Conformance\Tests\PhpdocAdvancedGenericClassString\Contract>, but parent type 'not a class name' provided [ArgumentTypeCoercion]
phpdoc_advanced_generic_class_string.php:51: Class, interface or enum named not a class name does not exist [UndefinedClass]
  • Recognition: spelling resolved
  • Enforcement: 2/3 of the expected violations reported

Source

<?php

declare(strict_types=1);

namespace Conformance\Tests\PhpdocAdvancedGenericClassString;

/**
 * Generic `class-string` and `interface-string` refinements.
 *
 * Plain `class-string` is `phpdoc_advanced_fallback_class_string`. This file
 * asks whether a type argument is applied (`class-string<Contract>`) and
 * whether `interface-string` is a distinct spelling.
 *
 * References:
 * - PHPStan class-string and interface-string types
 * - Psalm class-string and interface-string types
 */

interface Contract
{
}

final class Implementation implements Contract
{
}

final class Unrelated
{
}

/**
 * @param class-string<Contract> $class
 */
function takesContractClassString($class): void // T: class-string<Contract>
{
}

/**
 * @param interface-string<Contract> $interface
 */
function takesContractInterfaceString($interface): void // T: interface-string<Contract>
{
}

takesContractClassString(Contract::class); // V
takesContractClassString(Implementation::class); // V
takesContractInterfaceString(Contract::class); // V

takesContractClassString(Unrelated::class); // E?: the class does not implement Contract
takesContractInterfaceString(Implementation::class); // E?: the name denotes a class, not an interface
takesContractClassString('not a class name'); // E?: a non-class string is not a class-string