Interfaces — contracts, multiple interface implementation
Concept
Interfaces in PHP are the primary mechanism for dependency inversion and design by contract. A class that implements an interface guarantees it provides all declared methods with compatible signatures. Interfaces define the "what" — abstract classes define the "what with some how."
Multiple interface implementation: A class can implement any number of interfaces, which is PHP's answer to the limitations of single inheritance. This enables cross-cutting concerns to be expressed as interfaces without forcing them into the inheritance hierarchy.
Interface constants: PHP 8.0+ interfaces can declare constants; implementing classes cannot redefine them.
Interface extending interfaces: An interface can extend other interfaces (interface C extends A, B), combining multiple contracts into one.
PSR interfaces: The PHP-FIG defines standard interfaces (PSR-7 for HTTP messages, PSR-11 for containers, PSR-14 for events) that allow interoperability between frameworks. Laravel's core contracts (in Illuminate\Contracts\*) are interfaces that define the framework's internal API — enabling you to swap implementations in the container.
Why programming to interfaces matters in Laravel: Laravel's service container binds concrete implementations to interface abstractions. When you type-hint CacheInterface rather than RedisCache in a constructor, you can swap to FileCache in tests or staging without changing the consuming code.
Code Example
<?php
declare(strict_types=1);
// Defining interfaces
interface Hashable
{
public function hash(): string;
}
interface Equatable
{
public function equals(static $other): bool;
}
interface Serializable // PHP has a built-in Serializable, this is for example
{
public function serialize(): string;
public static function deserialize(string $data): static;
}
// Implementing multiple interfaces
class UserId implements Hashable, Equatable
{
public function __construct(private readonly int $value) {}
public function hash(): string
{
return hash('sha256', (string) $this->value);
}
public function equals(static $other): bool
{
return $this->value === $other->value;
}
public function getValue(): int { return $this->value; }
}
// Interface extending interfaces
interface FullyIdentifiable extends Hashable, Equatable
{
public function getId(): int;
}
// Dependency inversion — type-hint the interface, not the concrete class
interface CacheInterface
{
public function get(string $key): mixed;
public function set(string $key, mixed $value, int $ttl = 3600): void;
public function forget(string $key): void;
}
class RedisCache implements CacheInterface
{
public function get(string $key): mixed { /* Redis impl */ return null; }
public function set(string $key, mixed $value, int $ttl = 3600): void { /* */ }
public function forget(string $key): void { /* */ }
}
class ArrayCache implements CacheInterface
{
private array $store = [];
public function get(string $key): mixed { return $this->store[$key] ?? null; }
public function set(string $key, mixed $value, int $ttl = 3600): void { $this->store[$key] = $value; }
public function forget(string $key): void { unset($this->store[$key]); }
}
// Service depends on interface — works with either implementation
class UserService
{
public function __construct(private CacheInterface $cache) {}
public function findUser(int $id): ?array
{
return $this->cache->get("user:$id"); // works with Redis or Array
}
}
// Tests use ArrayCache, production uses RedisCache — same UserService
$testService = new UserService(new ArrayCache());
$prodService = new UserService(new RedisCache());