In PHP, what is the difference between an interface and an abstract class, and when would you choose each?
answer
- implements many, extends one
- no method bodies, public only
- abstract class can hold state
- type against the interface
- abstract base implements the interface
basics
~20 sA PHP interface is a pure contract: public method signatures, constants and, since 8.4, hooked property declarations, and a class may implement many. An abstract class can also carry state and shared code, but a class extends only one.
solid answer
~40 sAn **interface** lists what a class must offer: public method signatures with no bodies, constants and, since PHP 8.4, properties declared with `{ get; }` or `{ set; }` requirements. A class can `implement` any number of interfaces. An **abstract class** is a real class that cannot be instantiated: it can hold properties, a constructor, concrete methods, `protected` members and abstract methods, but a class can `extend` only one. Choose an interface for the **type** that callers depend on, such as a `PaymentGateway` parameter, because any class from any hierarchy can satisfy it. Choose an abstract class when several implementations share **code or state**, and usually let it implement the interface so callers still type against the interface. Both `new PaymentGateway()` and `new AbstractGateway()` throw an `Error`.
code
php · 41 lines<?php
declare(strict_types=1);
interface PaymentGateway
{
public function charge(int $amountCents, string $orderId): string;
}
abstract class HttpGateway implements PaymentGateway
{
public function charge(int $amountCents, string $orderId): string
{
// shared retry and logging would live here
return $this->send(['amount' => $amountCents, 'order' => $orderId]);
}
abstract protected function send(array $payload): string;
}
final class CardGateway extends HttpGateway
{
protected function send(array $payload): string
{
return 'card-' . $payload['order'];
}
}
final class VoucherGateway implements PaymentGateway
{
public function charge(int $amountCents, string $orderId): string
{
return 'voucher-' . $orderId;
}
}
function checkout(PaymentGateway $gateway): string
{
return $gateway->charge(4500, 'T-1001');
}
echo checkout(new CardGateway()), ' ', checkout(new VoucherGateway());go deeper
Recall the core differences: implements many vs extends one, no bodies and public-only methods on interfaces, state and shared code allowed in abstract classes.
Explain the interface-plus-abstract-base shape, why callers should type against the interface, and what PHP 8.4 interface properties do and do not add.
Justify choosing an interface for anything consumed across module boundaries and keeping abstract bases as optional internal conveniences, and show how that keeps tests and new implementations cheap.
Discuss the long-term cost of publishing an abstract base that outside teams extend versus publishing only an interface, and how you would evolve either without breaking implementers.
## Two tools that look similar Both an **interface** and an **abstract class** describe methods that a concrete class must provide, and neither can be instantiated on its own: `new` on one throws `Error: Cannot instantiate interface ...` or `Error: Cannot instantiate abstract class ...`. They differ in what else they may contain and in how many a class may use. ## Side by side in PHP 8.5 | | interface | abstract class | |---|---|---| | how a class uses it | `implements`, any number | `extends`, exactly one | | method bodies | none; every method is abstract | abstract **and** concrete methods | | method visibility | `public` only | `public`, `protected`, `private` for concrete methods | | stored properties | none; since 8.4 only hooked property **requirements** | any properties, typed, readonly, static | | constructor | allowed but discouraged by the manual | common, shared through `parent::__construct()` | | constants | allowed, always `public` | allowed, any visibility | | can extend | several interfaces at once | one parent class | The key asymmetry is **multiplicity**. A class can implement `PaymentGateway`, `Countable` and `Stringable` together, but it has only one parent class slot. Spending that slot on a base class is a bigger commitment than adding an interface. ## Choosing in a ticketing site Suppose the checkout of a ticketing site can charge a card, issue an invoice or redeem a voucher. 1. Define `interface PaymentGateway` with `charge()` and `refund()`. The checkout service takes a `PaymentGateway` parameter and never names a concrete class. 2. If the card and invoice gateways both talk to a remote HTTP API with the same retry and logging code, put that code in `abstract class HttpGateway implements PaymentGateway`, leaving the vendor-specific request building as an `abstract protected` method. 3. The voucher gateway has no HTTP at all; it implements `PaymentGateway` directly and ignores the base class. This **interface plus optional abstract base** pairing is the common PHP shape: the interface is the type, the abstract class is a convenience for some implementers. ## When an interface is the better choice - The contract will be implemented by classes that already have a parent, including third-party classes. - Tests need a substitute: any small class, or an anonymous class, can implement the interface without inheriting behaviour. - The type is a **capability** such as `Countable` or `Stringable`, which unrelated classes share. ## When an abstract class is the better choice - Implementations share real code or state, and duplicating it would be the alternative. - You need `protected` hook methods that only subclasses may call or override. - You want to fix an algorithm in a `final` method and let subclasses vary one step. ## A quick decision checklist When a new abstraction appears in a code review, these questions settle most cases: 1. **Who depends on it?** If other modules, services or tests will receive it as a parameter, it needs an interface, because that is the cheapest thing for them to depend on. 2. **Is there shared code or state?** If not, stop at the interface. An abstract class with only abstract methods is an interface that wastes the parent slot. 3. **Will every implementer want that shared code?** If only some do, keep the base class optional: implement the interface in the base, and let other classes implement the interface directly. 4. **Does the base need `protected` members?** Interfaces cannot express them, which is a legitimate reason for an abstract class. The answer is often both, with the interface as the public type and the abstract class as an internal helper that is free to change. ## Pitfalls - Typing parameters against the abstract class instead of the interface ties every caller to one hierarchy. - Putting a constructor in an interface: the manual discourages it because it removes the implementer's freedom over dependencies. - Forgetting that an interface cannot hold state: a stored property on an interface is rejected with `Interfaces may only include hooked properties`. - Assuming an abstract class must implement every interface method: it may leave some abstract, and the first concrete subclass must supply them.
- Why type the checkout parameter as PaymentGateway rather than HttpGateway?Typing against `HttpGateway` would reject the voucher gateway, which has no HTTP code and no reason to extend that base. The interface names only what checkout needs, `charge()`, so any class that provides it qualifies, including test doubles and classes with a different parent.
- Can an abstract class implement an interface without implementing all of its methods?Yes. An abstract class may leave interface methods unimplemented; they stay abstract. The first concrete subclass must implement them, otherwise its declaration fails with `Class X contains N abstract method(s) and must therefore be declared abstract or implement the remaining methods`.
saying these in an interview costs you the question
- An interface can contain default method bodies in PHP
- A PHP class can extend several abstract classes at once
- Interface methods may be protected to hide them from callers
- An abstract class cannot have a constructor or concrete methods
- Since PHP 8.4 interfaces can store property values like classes