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

  1. Use real interface names — check each entry with interface_exists(); replace any class entry with the interface you actually want to double
  2. Fix typos/imports; prefer A::class constants so the IDE and compiler resolve them
  3. 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

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


AI-assisted analysis of sebastianbergmann/phpunit@f123cdb2a2 (2026-08-23). Data as JSON: /api/errors/dd31041c2930195e. Report an issue: GitHub.