Lesson 23 of 100
Classes12 min5 checksPHP 8.5

Interfaces: Program to a Contract

Learn how a PHP interface declares what a class must provide, so different classes can be used interchangeably through one shared contract.

01 · THE IDEAREAD → TRY → LEARN

What an interface is

A class describes both what an object can do and how it does it. An interface keeps only the first half. It lists the public members a class must provide and leaves every body empty. Code that depends on the interface does not need to know which concrete class it received. This is how you swap a real payment gateway for a test double, or one cache strategy for another, without editing the calling code.

You declare an interface with the interface keyword. Its methods have a signature and no body. Every interface method is public, so you never write anything else.

Implementing a contract

A class promises to honor an interface with implements. If it leaves out any required method, PHP stops with a fatal error. A class may implement several interfaces by separating their names with commas. When a class both extends a parent and implements interfaces, extends comes first.

The example below defines one contract and two implementations. The function welcome() accepts any Greeter, so it works with both.

Example
<?php
interface Greeter
{
    public function greet(string $name): string;
}

class EnglishGreeter implements Greeter
{
    public function greet(string $name): string
    {
        return "Hello, $name!";
    }
}

class SpanishGreeter implements Greeter
{
    public function greet(string $name): string
    {
        return "Hola, $name!";
    }
}

function welcome(Greeter $greeter, string $name): string
{
    return $greeter->greet($name);
}

echo welcome(new EnglishGreeter(), 'Ada'), "\n";
echo welcome(new SpanishGreeter(), 'Ada'), "\n";
Output
Hello, Ada!
Hola, Ada!

The type declaration Greeter $greeter is the important part. It says "give me anything that keeps the Greeter promise". Adding a third greeter later needs no change to welcome().

Keep the parameter names the same as the interface uses. Since PHP 8.0 callers may use named arguments, so a renamed parameter in an implementation can break code that relies on the interface's names.

Extending interfaces and testing for them

Interfaces can extend other interfaces, and an interface can extend more than one. A class implementing the child must satisfy every method from all parents. Interfaces can also hold constants, and the instanceof operator tells you whether an object satisfies a contract.

Example
<?php
interface Named
{
    public function name(): string;
}

interface Priced extends Named
{
    public const CURRENCY = 'USD';

    public function cents(): int;
}

class Book implements Priced
{
    public function name(): string
    {
        return 'PHP Book';
    }

    public function cents(): int
    {
        return 2500;
    }
}

$book = new Book();
echo json_encode([
    'named' => $book instanceof Named,
    'priced' => $book instanceof Priced,
    'currency' => Priced::CURRENCY,
    'label' => $book->name() . ' ' . $book->cents(),
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
Output
{"named":true,"priced":true,"currency":"USD","label":"PHP Book 2500"}

Interface properties in PHP 8.4

As of PHP 8.4, an interface may also declare a property. It must say whether the property is readable (get), writable (set), or both. An implementing class can satisfy a readable property with an ordinary public property, or with a virtual property that has a get hook.

Example
<?php
interface HasLabel
{
    public string $label { get; }
}

class Product implements HasLabel
{
    public function __construct(public string $label)
    {
    }
}

class Upper implements HasLabel
{
    public function __construct(private string $text)
    {
    }

    public string $label { get => strtoupper($this->text); }
}

function show(HasLabel $item): string
{
    return $item->label;
}

echo json_encode([show(new Product('tea')), show(new Upper('coffee'))]), "\n";
Output
["tea","COFFEE"]

Common mistakes

  • Putting a method body in an interface. Interfaces declare, classes define.
  • Forgetting that interface methods must be public in the implementing class.
  • Declaring a constructor in an interface. It is supported, but strongly discouraged because it limits implementers.
  • Type-hinting a concrete class when an interface would do, which blocks substitutes.
  • Using the same name for an interface and a class. They share one namespace.

Exercise contract

Write totalArea(array $specs): int. Each spec is a list: ["square", side] or ["rect", width, height]. Define a Shape interface with area(): int, create a Square and a Rectangle class that implement it, turn each spec into an object, and add up the areas by calling only area(). An empty list returns 0.

Recap

An interface is a contract of public members. Classes sign it with implements, and callers depend on the interface type rather than a concrete class. That lets you swap implementations freely while the calling code stays unchanged.

Before you start

Classes · Type declarations

You’ll practice
  • Declare an interface method
  • Use two implementations through the same contract
Your task

Make totalArea() pass all 5 checks. Run your code as often as you like, then check your answer.

exercise.phpSaved in this browserPHP 8.5
CONSOLE & CHECKS● Ready

Ready when you are

Your output will appear here.
Make it work. Then make it pass.

PHP loads on your first run. Each check starts fresh. Your code stays in this browser; clearing site data removes your progress. A check or the task looks wrong? Report a problem with this exercise.