{"record":{"id":"75e7538256ba7f21","repo":"sebastianbergmann/phpunit","slug":"cannot-use-s-as-the-name-of-a-test-double-class","errorCode":null,"errorMessage":"Cannot use \"%s\" as the name of a test double class because it is not a valid PHP class name","messagePattern":"Cannot use \"(.+?)\" as the name of a test double class because it is not a valid PHP class name","errorType":"exception","errorClass":"InvalidClassNameException","httpStatus":null,"severity":"error","filePath":"src/Framework/MockObject/Generator/Generator.php","lineNumber":734,"sourceCode":"            }\n        }\n\n        if ($methods !== array_unique($methods)) {\n            throw new DuplicateMethodException($methods);\n        }\n    }\n\n    /**\n     * @throws InvalidClassNameException\n     */\n    private function ensureValidNameForTestDoubleClass(string $className): void\n    {\n        if ($className === '') {\n            return;\n        }\n\n        if (preg_match('~\\A[a-zA-Z_\\x7f-\\xff][a-zA-Z0-9_\\x7f-\\xff]*\\z~', $className) === 0) {\n            throw new InvalidClassNameException($className);\n        }\n    }\n\n    /**\n     * @throws NameAlreadyInUseException\n     * @throws ReflectionException\n     */\n    private function ensureNameForTestDoubleClassIsAvailable(string $className): void\n    {\n        if ($className === '') {\n            return;\n        }\n\n        if (class_exists($className, false) ||\n            interface_exists($className, false) ||\n            trait_exists($className, false)) {\n            throw new NameAlreadyInUseException($className);\n        }","sourceCodeStart":716,"sourceCodeEnd":752,"githubUrl":"https://github.com/sebastianbergmann/phpunit/blob/f123cdb2a2d49f15025794166cfed8bda8627dd2/src/Framework/MockObject/Generator/Generator.php#L716-L752","documentation":"Thrown by Generator::ensureValidNameForTestDoubleClass() when the requested class name for the generated test double is not a valid PHP class name (same identifier regex as method names; empty string is allowed and skips validation). This name comes from the mockClassName argument, e.g. MockBuilder::setMockClassName(). Note the regex has no backslash, so fully-qualified namespaced names are rejected too.","triggerScenarios":"$this->getMockBuilder(C::class)->setMockClassName('My-Mock') with hyphens, digits first ('Mock123' is fine, '123Mock' is not), spaces, or a namespaced string 'App\\\\Mocks\\\\Foo'; calling Generator::testDouble(..., mockClassName: 'not valid') directly.","commonSituations":"Passing an existing FQCN as the new mock's class name (expecting namespacing support); building mock class names from test data or slugs that contain dashes; copying a class basename with stray characters; migrating tests that used dynamic class names in old PHPUnit versions.","solutions":["Use a bare identifier: letters, digits, underscore, starting with a letter or underscore (setMockClassName('FooMock')).","Do not include a namespace in the mock class name; PHPUnit generates the class in the global mock namespace.","If you do not need a specific name, drop setMockClassName() entirely and let PHPUnit pick a unique name.","Sanitize dynamic names: preg_replace('/[^A-Za-z0-9_]/', '', $name) and prefix with a letter."],"exampleFix":"// before\n$mock = $this->getMockBuilder(UserRepo::class)\n    ->setMockClassName('App\\\\Tests\\\\User-Repo-Mock')\n    ->getMock();\n\n// after\n$mock = $this->getMockBuilder(UserRepo::class)\n    ->setMockClassName('UserRepoMock')\n    ->getMock();","handlingStrategy":"validation","validationCode":"$mockClassName = 'RepoMock';\nif (!preg_match('~\\A[a-zA-Z_\\x7f-\\xff][a-zA-Z0-9_\\x7f-\\xff]*\\z~', $mockClassName)) {\n    $mockClassName = preg_replace('/[^A-Za-z0-9_]/', '', $mockClassName);\n}","typeGuard":"function isPhpClassName(string $name): bool\n{\n    return (bool) preg_match('~\\A[a-zA-Z_\\x7f-\\xff][a-zA-Z0-9_\\x7f-\\xff]*\\z~', $name);\n}","tryCatchPattern":"try {\n    $mock = $builder->setMockClassName($name)->getMock();\n} catch (PHPUnit\\Framework\\MockObject\\InvalidClassNameException $e) {\n    // fall back to an auto-generated name: omit setMockClassName()\n}","preventionTips":["Prefer omitting setMockClassName() unless a specific name is required.","Remember the mock class name must be a single identifier — no namespace separators, no dashes, no leading digit.","Sanitize data-derived names down to [A-Za-z0-9_] before use."],"tags":["phpunit","mock-object","class-name","input-validation","identifier"],"backgroundTag":"invalid-identifier-name","analyzedSha":"f123cdb2a2d49f15025794166cfed8bda8627dd2","analyzedAt":"2026-08-23T01:20:58.058Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}