sebastianbergmann/phpunit · error · UnknownInterfaceException
Interface "%s" does not exist
Error message
Interface "%s" does not exist
What it means
While generating an intersection test double, PHPUnit verifies every entry in the interface list with interface_exists(). Any entry that names an existing CLASS (not interface), a misspelled/nonexistent name, or a not-yet-loaded interface triggers this UnknownInterfaceException.
Source
Thrown at src/Framework/MockObject/Generator/Generator.php:149
return $object;
}
/**
* @param list<class-string> $interfaces
*
* @throws RuntimeException
* @throws UnknownInterfaceException
*/
public function testDoubleForInterfaceIntersection(array $interfaces, bool $mockObject, bool $returnValueGeneration = true): MockObject|Stub
{
if (count($interfaces) < 2) {
throw new RuntimeException('At least two interfaces must be specified');
}
foreach ($interfaces as $interface) {
if (!interface_exists($interface)) {
throw new UnknownInterfaceException($interface);
}
}
sort($interfaces);
$methods = [];
foreach ($interfaces as $interface) {
$methods = array_merge($methods, $this->namesOfMethodsIn($interface));
}
if (count(array_unique($methods)) < count($methods)) {
throw new RuntimeException('Interfaces must not declare the same method');
}
$unqualifiedNames = [];
foreach ($interfaces as $interface) {View on GitHub (pinned to f123cdb2a2)
Solutions
- Use real interface names — check each entry with interface_exists(); replace any class entry with the interface you actually want to double
- Fix typos/imports; prefer A::class constants so the IDE and compiler resolve them
- composer dump-autoload / install the dependency that ships the interface
Example fix
// before
$db = $this->createMockForIntersectionOfInterfaces(
[UserRepository::class, LoggerInterface::class] // UserRepository is a class
);
// after
$db = $this->createMockForIntersectionOfInterfaces(
[UserRepositoryInterface::class, LoggerInterface::class]
); Defensive patterns
Strategy: validation
Validate before calling
foreach ($interfaces as $i => $name) {
if (!interface_exists($name)) {
throw new InvalidArgumentException("#$i is not an interface: $name");
}
}
$double = $this->createMockForIntersectionOfInterfaces($interfaces); Type guard
function allAreInterfaces(array $names): bool
{
foreach ($names as $n) {
if (!interface_exists($n)) {
return false;
}
}
return true;
} Prevention
- Reference interfaces with Iface::class constants so names are compiler-checked
- Watch for major-version dependency upgrades that rename PSR/framework interfaces
When it happens
Trigger: createMockForIntersectionOfInterfaces([A::class, B::class]) where B::class is a class or trait rather than an interface, or a string like 'Psr\Log\LoggerAwareInterfce' (typo), or an interface from a package whose autoloader is not available in the test runtime.
Common situations: Accidentally including a concrete class (e.g. a repository class instead of its interface) in the intersection list; missing use imports; renamed interfaces after a dependency major-version upgrade (e.g. PSR or Doctrine BC breaks) where the old interface name no longer exists.
Related errors
- At least two interfaces must be specified
- Interfaces must not declare the same method
- Subscriber "%s" does not exist or is not an interface
- Class "%s" is an anonymous class and cannot be doubled
- Class "%s" is an enumeration and cannot be doubled
AI-assisted analysis of sebastianbergmann/phpunit@f123cdb2a2 (2026-08-23).
Data as JSON: /api/errors/dd31041c2930195e.
Report an issue: GitHub.