What is an Interface?
An interface is a contract: a list of method signatures that an implementing class must provide. It has no method bodies and no state - just the shape.
Defining an Interface
<?php
interface Logger {
public function info(string $message): void;
public function error(string $message, ?Throwable $e = null): void;
public function debug(string $message): void;
}
All methods are implicitly public. You cannot declare properties or non-static method bodies.
Implementing an Interface
<?php
class FileLogger implements Logger {
public function __construct(private string $path) {}
public function info(string $message): void {
$this->write("INFO", $message);
}
public function error(string $message, ?Throwable $e = null): void {
$this->write("ERROR", $message . ($e ? " | " . $e->getMessage() : ""));
}
public function debug(string $message): void {
$this->write("DEBUG", $message);
}
private function write(string $level, string $msg): void {
file_put_contents($this->path, "[$level] $msg\n", FILE_APPEND);
}
}
class ConsoleLogger implements Logger {
public function info(string $message): void { echo "INFO: $message\n"; }
public function error(string $message, ?Throwable $e = null): void { echo "ERR: $message\n"; }
public function debug(string $message): void { echo "DBG: $message\n"; }
}
Multiple Interfaces
Unlike inheritance, a class can implement many interfaces:
<?php
interface Cacheable {
public function cacheKey(): string;
}
interface Serializable {
public function toArray(): array;
}
class Product implements Cacheable, Serializable {
public function __construct(
public int $id,
public string $name,
public float $price,
) {}
public function cacheKey(): string {
return "product_{$this->id}";
}
public function toArray(): array {
return ["id" => $this->id, "name" => $this->name, "price" => $this->price];
}
}
Interfaces Extending Interfaces
<?php
interface Readable {
public function read(): string;
}
interface Writable {
public function write(string $data): int;
}
// Interface inheritance - composite contract
interface ReadWrite extends Readable, Writable {
public function close(): void;
}
class MemoryStream implements ReadWrite {
public function read(): string { /* ... */ }
public function write(string $data): int { /* ... */ }
public function close(): void { /* ... */ }
}
Interface Constants
<?php
interface HttpStatus {
const OK = 200;
const NOT_FOUND = 404;
const SERVER_ERR = 500;
}
class Response implements HttpStatus {
public int $status = self::OK;
}
echo HttpStatus::OK; // 200
echo Response::NOT_FOUND; // 404 (inherited)
Type-Hinting Interfaces
The real power - code against the contract, not the implementation:
<?php
class OrderProcessor {
public function __construct(private Logger $logger) {}
public function process(Order $order): void {
$this->logger->info("Processing #{$order->id}");
// ...
}
}
// Swap implementations at the boundary
$processor = new OrderProcessor(new FileLogger("orders.log"));
// or
$processor = new OrderProcessor(new ConsoleLogger());
// In tests, inject a fake
$processor = new OrderProcessor(new InMemoryLogger());
Built-in PHP Interfaces
PHP ships with interfaces you can implement to plug into language features:
| Interface | Enables |
|---|---|
Countable | count($obj) |
Iterator / IteratorAggregate | foreach ($obj as ...) |
ArrayAccess | $obj["key"] syntax on objects |
Stringable | (string) $obj via __toString() |
JsonSerializable | json_encode($obj) control |
Throwable | Anything that can be thrown |
<?php
class Cart implements Countable, IteratorAggregate, JsonSerializable {
private array $items = [];
public function add(string $sku): void { $this->items[] = $sku; }
public function count(): int {
return count($this->items);
}
public function getIterator(): Iterator {
return new ArrayIterator($this->items);
}
public function jsonSerialize(): array {
return ["items" => $this->items, "count" => count($this)];
}
}
$c = new Cart();
$c->add("A"); $c->add("B");
echo count($c); // 2 - Countable
foreach ($c as $sku) echo $sku; // AB - Iterator
echo json_encode($c); // {"items":["A","B"],"count":2}
Interface vs Abstract Class
| Aspect | Interface | Abstract Class |
|---|---|---|
| Method bodies | No | Yes (concrete methods OK) |
| Properties | No | Yes |
| Constructor | No | Yes |
| How many can a class use? | Many (implements A, B, C) | Only one (extends X) |
| Constants | Yes | Yes |
| Use when... | You need a contract with no shared code | Subclasses share state or implementation |