class-string rejects anonymous classes, though class_exists() accepts them.
Repro (v0.6.0, PHP 8.5.7):
require 'vendor/autoload.php';
$object = new class {};
var_dump(class_exists($object::class)); // true
var_dump(TypePHP\Internal\ClassNameValidator::isValid($object::class)); // false
Any @param class-string/@return class-string contract fed $anonymous::class throws:
TypeError: Registry::addService(): Argument $handlerClass must be of type class-string,
string 'App\Fixtures\EchoService@anonymous...' given
Cause. PHP mangles the name to Parent@anonymous\0/path/to/file.php:12$3. The identifier
regexes reject @, the NUL byte and the path:
src/Internal/ClassNameValidator.php:28
src/Validator/IdentifierValidator.php:54
Impact. Anonymous classes are the idiomatic test double in PHPUnit suites. Every one whose
::class crosses a class-string boundary fails, and the only workarounds are to weaken the
contract or @typephp-ignore the method.
Suggested fix. Accept a name whose prefix up to @anonymous is a valid identifier, or defer to
class_exists() when the name contains @anonymous.
class-stringrejects anonymous classes, thoughclass_exists()accepts them.Repro (v0.6.0, PHP 8.5.7):
Any
@param class-string/@return class-stringcontract fed$anonymous::classthrows:Cause. PHP mangles the name to
Parent@anonymous\0/path/to/file.php:12$3. The identifierregexes reject
@, the NUL byte and the path:src/Internal/ClassNameValidator.php:28src/Validator/IdentifierValidator.php:54Impact. Anonymous classes are the idiomatic test double in PHPUnit suites. Every one whose
::classcrosses aclass-stringboundary fails, and the only workarounds are to weaken thecontract or
@typephp-ignorethe method.Suggested fix. Accept a name whose prefix up to
@anonymousis a valid identifier, or defer toclass_exists()when the name contains@anonymous.