Interfaces and Multiple Contracts

An interface is a promise of behaviour. A class can implement many at once, which is how PHP does 'is-a' without single-inheritance limits.

An interface says what a class can do, never how. Program against the interface and you can swap the concrete class freely — the essence of loose coupling. A class can only extend one parent, but it can implements any number of interfaces, separated by commas.

Easy example

<?php
interface Greetable {
    public function greet(): string;
}

class Robot implements Greetable {
    public function greet(): string {
        return 'BEEP BOOP';
    }
}

echo (new Robot())->greet();

Interfaces can also declare public constants and can themselves extend multiple other interfaces, letting you compose bigger contracts from small ones.

Example

Try it yourself
Loading editor…
Press Run to execute the code.

When to use it

  • Define a Renderable interface so blade templates and JSON responses share the same render() contract.
  • Implement multiple interfaces (Countable, Iterator) on a custom collection class to make it work with foreach and count().
  • Use interface type hints in a dependency-injection container to resolve concrete implementations at runtime.

More examples

Multiple interface implementation

A class can implement any number of interfaces, satisfying multiple unrelated contracts without multiple inheritance.

Example · php
<?php
interface Stringable {
    public function __toString(): string;
}
interface JsonSerializable {
    public function jsonSerialize(): mixed;
}

class Money implements Stringable, JsonSerializable {
    public function __construct(
        private int    $cents,
        private string $currency = 'USD'
    ) {}
    public function __toString(): string {
        return number_format($this->cents / 100, 2) . ' ' . $this->currency;
    }
    public function jsonSerialize(): mixed {
        return ['cents' => $this->cents, 'currency' => $this->currency];
    }
}

$m = new Money(1999);
echo $m;                         // 19.99 USD
echo json_encode($m->jsonSerialize()); // {"cents":1999,"currency":"USD"}

Interface as type hint

Type-hinting against Cache lets render() work with any cache implementation — Redis, Memcached, or a test stub.

Example · php
<?php
interface Cache {
    public function get(string $key): mixed;
    public function set(string $key, mixed $value, int $ttl = 60): void;
}

class ArrayCache implements Cache {
    private array $store = [];
    public function get(string $key): mixed { return $this->store[$key] ?? null; }
    public function set(string $key, mixed $value, int $ttl = 60): void {
        $this->store[$key] = $value;
    }
}

function render(string $template, Cache $cache): string {
    $cached = $cache->get($template);
    if ($cached !== null) return $cached;
    $result = "<html>$template</html>";
    $cache->set($template, $result);
    return $result;
}

echo render('home', new ArrayCache());

Interface extending interface

Interfaces can extend other interfaces, building a hierarchy of contracts without requiring a class hierarchy.

Example · php
<?php
interface Readable {
    public function read(): string;
}
interface ReadWritable extends Readable {
    public function write(string $data): void;
}

class FileStream implements ReadWritable {
    private string $buffer = '';
    public function read(): string  { return $this->buffer; }
    public function write(string $data): void { $this->buffer .= $data; }
}

$s = new FileStream();
$s->write('Hello');
echo $s->read(); // Hello

Discussion

  • Be the first to comment on this lesson.