sebastianbergmann/phpunit · error · RuntimeException

Interfaces must not declare the same method

Error message

Interfaces must not declare the same method

What it means

When building an intersection test double, PHPUnit collects the method names of every listed interface and requires them to be disjoint (count(array_unique($methods)) === count($methods)). If two interfaces in the list declare a method with the same name — even with identical signatures — it refuses, because the generated combined interface cannot redeclare a method and PHP's own intersection semantics do not resolve which method wins.

Source

Thrown at src/Framework/MockObject/Generator/Generator.php:162

            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) {
            $parts              = explode('\\', $interface);
            $unqualifiedNames[] = array_pop($parts);
        }

        sort($unqualifiedNames);

        do {
            $intersectionName = sprintf(
                'Intersection_%s_%s',
                implode('_', $unqualifiedNames),
                substr(md5((string) mt_rand()), 0, 8),
            );
        } while (interface_exists($intersectionName, false));

View on GitHub (pinned to f123cdb2a2)

Solutions

  1. Rename the colliding method in one of your own interfaces (e.g. getDisplayName() vs getName())
  2. Drop one of the colliding interfaces from the list and mock it separately, or mock only their common subset
  3. If both interfaces come from third parties and cannot change, write a small named interface of your own combining the needed methods and mock that instead

Example fix

// before
interface HasName { public function getName(): string; }
interface NamedEntity { public function getName(): string; }
$mock = $this->createMockForIntersectionOfInterfaces([HasName::class, NamedEntity::class]); // collision

// after
interface HasName { public function getName(): string; }
interface NamedEntity { public function getEntityName(): string; }
$mock = $this->createMockForIntersectionOfInterfaces([HasName::class, NamedEntity::class]);
Defensive patterns

Strategy: validation

Validate before calling

// Reject colliding method names up front:
$methods = [];
foreach ($interfaces as $iface) {
    foreach (get_class_methods($iface) as $m) {
        if (isset($methods[$m])) {
            throw new InvalidArgumentException("Method '$m' declared by both {$methods[$m]} and $iface");
        }
        $methods[$m] = $iface;
    }
}
$double = $this->createMockForIntersectionOfInterfaces($interfaces);

Prevention

When it happens

Trigger: createMockForIntersectionOfInterfaces([A::class, B::class]) where both A and B declare e.g. getName(), reset(), or jsonSerialize() (common with JsonSerializable plus a custom HasToArray interface), including cases where both interfaces inherit the method from a shared parent — actually shared parents dedupe only by name; identical names from any source collide.

Common situations: Combining generic framework interfaces (SessionHandlerInterface, LoggerAwareInterface->setLogger, JsonSerializable::jsonSerialize) with domain interfaces that reuse the same short names; RFC-style value interfaces both declaring id(); interfaces designed before intersection doubles existed.

Related errors


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