{"record":{"id":"ff93c861b850beec","repo":"sebastianbergmann/phpunit","slug":"cannot-double-method-with-invalid-name-s","errorCode":null,"errorMessage":"Cannot double method with invalid name \"%s\"","messagePattern":"Cannot double method with invalid name \"(.+?)\"","errorType":"exception","errorClass":"InvalidMethodNameException","httpStatus":null,"severity":"error","filePath":"src/Framework/MockObject/Generator/Generator.php","lineNumber":715,"sourceCode":"            throw new UnknownTypeException($type);\n        }\n    }\n\n    /**\n     * @param ?list<non-empty-string> $methods\n     *\n     * @throws DuplicateMethodException\n     * @throws InvalidMethodNameException\n     */\n    private function ensureValidMethods(?array $methods): void\n    {\n        if ($methods === null) {\n            return;\n        }\n\n        foreach ($methods as $method) {\n            if (preg_match('~\\A[a-zA-Z_\\x7f-\\xff][a-zA-Z0-9_\\x7f-\\xff]*\\z~', (string) $method) === 0) {\n                throw new InvalidMethodNameException((string) $method);\n            }\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) {","sourceCodeStart":697,"sourceCodeEnd":733,"githubUrl":"https://github.com/sebastianbergmann/phpunit/blob/f123cdb2a2d49f15025794166cfed8bda8627dd2/src/Framework/MockObject/Generator/Generator.php#L697-L733","documentation":"Thrown by Generator::ensureValidMethods() when a method name in the list of methods to double does not match the PHP identifier regex ^[a-zA-Z_\\x7f-\\xff][a-zA-Z0-9_\\x7f-\\xff]*$. PHPUnit validates every entry of the method list (e.g. onlyMethods()/addMethods()) before generating a test double class, so this always signals bad input from the caller: a typo, a name assembled from data, or a signature string pasted instead of a bare method name.","triggerScenarios":"Calling $this->getMockBuilder(C::class)->onlyMethods(['do-stuff']) or ->addMethods(['123abc', 'foo()', '$bar', '']) with any string that is not a valid PHP method identifier; calling Generator::testDouble()/getMock() directly with a $methods array containing such a value. Values are cast to string, so numbers pass but strings like 'method()' or ' method' fail.","commonSituations":"Method names built dynamically from configuration, CSV, or data providers; copy-pasting a full signature 'handleRequest($req)' instead of 'handleRequest'; kebab-case names from route names used as method names; refactored method names that no longer compile as identifiers.","solutions":["Correct the entry to the exact bare method name (no parentheses, no arguments, no $ prefix): 'doStuff' not 'doStuff()'.","If names come from a dynamic source, filter them first with the same regex PHPUnit uses: preg_match('~\\A[a-zA-Z_\\x7f-\\xff][a-zA-Z0-9_\\x7f-\\xff]*\\z~', $name).","If you meant an existing method of the mocked type, get real names from reflection instead of typing them: (new ReflectionClass(C::class))->getMethods().","For a method that does not exist yet on the class, add it to the class/interface or use addMethods() with a valid identifier."],"exampleFix":"// before\n$mock = $this->getMockBuilder(Greeter::class)\n    ->onlyMethods(['say-hello'])\n    ->getMock();\n\n// after\n$mock = $this->getMockBuilder(Greeter::class)\n    ->onlyMethods(['sayHello'])\n    ->getMock();","handlingStrategy":"validation","validationCode":"$methods = ['doStuff', 'save'];\n$valid = preg_filter('~\\A[a-zA-Z_\\x7f-\\xff][a-zA-Z0-9_\\x7f-\\xff]*\\z~', '', $methods) ?? [];\nif (count($valid) !== count($methods)) {\n    throw new InvalidArgumentException('method list contains invalid names');\n}","typeGuard":"function isPhpMethodName(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 = $this->getMockBuilder(C::class)->onlyMethods($methods)->getMock();\n} catch (PHPUnit\\Framework\\MockObject\\InvalidMethodNameException $e) {\n    // log which dynamic source produced the bad name and skip/fail gracefully\n}","preventionTips":["Never build method lists from unvalidated external data; validate with the identifier regex first.","When listing existing methods, derive names from ReflectionClass::getMethods() instead of hand-typing them.","Add static analysis (psalm/phpstan) so mistyped method names in test helpers surface early."],"tags":["phpunit","mock-object","method-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"}