skip to content

In PHP, what may an interface declare, and what happens when a class implements several interfaces or an interface extends others?

level: middleimportance: should knowfreq 48%

answer

  1. public methods without bodies
  2. constants overridable since 8.1
  3. interface extends A, B
  4. one method must satisfy every interface
  5. inherits both ... which is ambiguous

basics

~20 s

A PHP interface declares public method signatures, public constants and, since 8.4, hooked property requirements. An interface may extend several interfaces, a class may implement several, and one method must satisfy every interface that declares it.

solid answer

~40 s

An interface body may contain **public method signatures** (instance or static, no bodies), **public constants** and, since PHP 8.4, **property requirements** written with hooks, such as `public string $name { get; }`. Interfaces combine in two ways. An interface may `extend` several others (`interface RefundableGateway extends PaymentGateway, Countable`), and a class may `implement` several with a comma list. When two interfaces declare the same method, the class writes one method that must be signature-compatible with both. When two interfaces bring the same **constant** name, the class fails with `inherits both I1::C and I2::C, which is ambiguous` unless it redeclares the constant itself; since PHP 8.1 interface constants are overridable unless declared `final`. Naming the same interface twice, or a class after `implements`, is a compile error.

code

php · 30 lines
php
<?php
declare(strict_types=1);

interface PaymentGateway
{
    public const CURRENCY = 'EUR';

    public function charge(int $amountCents, string $orderId): string;
}

interface AuditedGateway
{
    public const CURRENCY = 'EUR';

    public function charge(int|float $amountCents, string $orderId): string;
}

final class InvoiceGateway implements PaymentGateway, AuditedGateway
{
    // Redeclaring resolves "inherits both ... which is ambiguous".
    public const CURRENCY = 'EUR';

    // One method, compatible with both declarations.
    public function charge(int|float $amountCents, string $orderId): string
    {
        return 'invoice-' . $orderId;
    }
}

echo (new InvoiceGateway())->charge(4500, 'T-1001');

go deeper

for a junior

Recall what an interface may hold: public method signatures, public constants and, since 8.4, hooked property requirements, and that a class may implement several.

for a middle

Explain the combining rules: one method satisfying several declarations, the constant ambiguity error and its fix, interface extends with several parents, and constant overriding since 8.1.

for a senior

Show you can design small interfaces that compose with extends, and diagnose conflicts when a third-party interface collides with your own.

for a principal

Discuss how interface evolution works for published libraries: adding a method breaks every implementer, so plan contracts, versioning and extension interfaces accordingly.

## What an interface body may contain An **interface** is declared like a class with the `interface` keyword, and the engine restricts its members: - **Methods**: signatures only. A body fails with `Interface function X::m() cannot contain body`. Every method must be `public` (`Access type for interface method X::m() must be public`), and it cannot be `final`. Methods may be `static`, and magic methods may be declared to force implementers to provide them. - **Constants**: allowed and always `public` (`Access type for interface constant ... must be public`). Since **PHP 8.1** an implementing class or a child interface may redeclare an interface constant, unless the interface marks it `final`. - **Properties**: since **PHP 8.4**, only as hooked requirements such as `public string $name { get; }`. A plain `public string $name;` is rejected with `Interfaces may only include hooked properties`. - **Constructors**: allowed, but the manual strongly discourages them because they fix how every implementer is built. Interfaces share one name space with classes, traits and enums, so an interface cannot have the same name as a class. ## Implementing several interfaces A class lists interfaces after `implements`, separated by commas, and must implement every method of every interface (or be declared `abstract`). Rules the engine enforces: 1. **Same method in two interfaces.** The class writes one method, and it must satisfy the signature-compatibility rules against **each** declaration, so parameter contravariance and return covariance can reconcile them. 2. **Same constant in two interfaces.** If the class does not redeclare it, the declaration fails: `Class X inherits both I1::C and I2::C, which is ambiguous`. Redeclaring the constant in the class resolves it, because the class's own constant wins, provided neither interface declared it `final`. 3. **Same interface named twice.** `Class X cannot implement previously implemented interface I`. An interface already implemented by the parent class may be repeated harmlessly. 4. **Not an interface.** Naming a class after `implements` fails with `X cannot implement Y - it is not an interface`. ## Interfaces extending interfaces Unlike classes, an interface may `extend` **several** interfaces: ```php interface RefundableGateway extends PaymentGateway, Countable { public function refund(string $chargeId): bool; } ``` A class implementing `RefundableGateway` must provide `refund()`, every `PaymentGateway` method and `count()`, and it passes type checks for all three interface names. The same constant-ambiguity rule applies: an interface that extends two interfaces declaring the same constant fails with `Interface X inherits both ... which is ambiguous`. ## Working with the result | You write | The engine checks | |---|---| | `class CardGateway implements PaymentGateway, Stringable` | every method of both is present and compatible | | `abstract class HttpGateway implements PaymentGateway` | nothing yet: missing methods stay abstract | | `interface A extends B, C` | constant names from `B` and `C` do not collide | | `function checkout(PaymentGateway $g)` | at call time, the argument implements the interface | ## Static methods and constructors in interfaces An interface may declare `public static function fromConfig(array $config): static;`. Every implementer must then provide the static method, and code can call it on a class name held in a variable, such as `$gatewayClass::fromConfig($config)`. Calling it on the interface itself, `PaymentGateway::fromConfig(...)`, throws `Error: Cannot call abstract method`, because the interface has no body to run. A constructor in an interface is legal but the manual strongly discourages it: it forces every implementer to accept the same dependencies, which defeats the point of letting different classes satisfy one contract. A static factory method in the interface is usually the better way to standardise construction when it is needed at all. ## Design notes - Keep interfaces **small**: a checkout needing only `charge()` should not depend on an interface that also demands reporting methods. Combining small interfaces with `extends` is cheaper than splitting a large one later. - Put shared **values** such as a currency code in constants only when they truly belong to the contract; they become part of every implementer's public surface. - Keep parameter names identical to the interface's: since PHP 8.0 callers can pass arguments by name, so a renamed parameter in an implementation can break calls written against the interface.

  • Why is overriding an interface constant allowed since PHP 8.1, and how does an interface prevent it?
    PHP 8.1 made interface constants behave like class constants, which children may redeclare. An interface that needs a fixed value declares it `final public const`, and any implementer or child interface redeclaring it then fails with `cannot override final constant`.
  • Two interfaces declare charge() with an int parameter in one and a string parameter in the other. Can one class implement both?
    Yes. Parameters are contravariant, so the class declares `charge(int|string $amount, ...)`, which accepts everything either interface accepts and is compatible with both. Return types are the harder case: if one interface returns `int` and the other `string`, no useful single return type satisfies both, and the interfaces cannot sensibly be implemented together.

saying these in an interview costs you the question

  • An interface can extend only one other interface, just like classes
  • Two interfaces with the same method name can never be implemented together
  • Interface constants can never be redeclared by an implementing class
  • Interface methods may be protected if only implementers call them
  • Interfaces may declare ordinary stored properties since PHP 8.4