Skip to content

6. Interfaces

An interface is a structure that defines method prototypes. A class that implements an interface must implement the code for all of the interface’s methods. An interface is a contract: it specifies what an object can do, without specifying how it does it.

6.1. The script directory structure

The scripts for this chapter are in the [scripts-console/interfaces] folder:

1
2
3
4
5
6
7
8
php8-oct-2026/scripts-console/interfaces
├── ICalculation.php
├── IExample.php
├── IExample2.php
├── interface-01.php
├── interface-02.php
├── interface-03.php
└── interface-04.php

6.2. A First Interface

We define an interface named [IExample] in a script named [IExample.php]. In the same script, we define two classes that implement the [IExample] interface:

<?php

// strict respect of the declared types of function parameters
declare(strict_types=1);

// namespace
namespace Examples;

// an interface
interface IExample
{
    // the methods of the interface
    public function add(int $i, int $j): int;

    public function subtract(int $i, int $j): int;
}

// implementation 1 of the IExample interface
class Class1 implements IExample
{
    public function add(int $a, int $b): int
    {
        return $a + $b + 10;
    }

    public function subtract(int $a, int $b): int
    {
        return $a - $b + 20;
    }
}

// implementation 2 of the IExample interface
class Class2 implements IExample
{
    public function add(int $a, int $b): int
    {
        return $a + $b + 100;
    }

    public function subtract(int $a, int $b): int
    {
        return $a - $b + 200;
    }
}

Comments

  • Lines 10–15: The [IExample] interface defines two methods: the [add] function (line 13) and the [subtract] function (line 15). The interface does not define the code for these methods. The classes that implement the interface will do so. The methods of an interface must be public;
  • line 19: the class [Class1] implements ([implements]) the interface [IExample]. It therefore defines code for the methods [add] (line 21) and [subtract] (line 26). The parameter names may differ from those in the interface ([$a] instead of [$i]), but their types must be compatible. Note, however: with the named arguments of PHP 8, a call to [add(i: 1, j: 2)] would not work with [Class1]. It is therefore preferable to keep the interface names;
  • Lines 33–44: Same applies to class [Class2].

We use the [IExample] interface in the following [interface-01.php] script:

<?php

// strict respect of the declared types of function parameters
declare(strict_types=1);

// inclusion and declaration of the classes and interfaces used by the script
require_once __DIR__ . "/IExample.php";
use Examples\Class1;
use Examples\Class2;
use Examples\IExample;

// function
function calculate(int $a, int $b, IExample $interface): void
{
    print $interface->add($a, $b) . "\n";
    print $interface->subtract($a, $b) . "\n";
}

// ------------------- main
// creating 2 objects of type Class1 and Class2
$c1 = new Class1();
$c2 = new Class2();
// calling the calculate function
calculate(4, 3, $c1);
calculate(14, 13, $c2);
// an interface cannot be instantiated
try {
    new IExample();
} catch (\Error $e) {
    print "Error: " . $e->getMessage() . "\n";
}

Results:

1
2
3
4
5
6
> php interface-01.php
17
21
127
201
Error: Cannot instantiate interface Examples\IExample

Comments

  • lines 7–10: inclusion and declaration of the classes and interfaces used by the script. The file [IExample.php] is included using the statement [require_once]: it will be included only once, even if multiple scripts in the project include it;
  • Lines 13–17: A function is defined;
  • Line 13: Here, we have declared the types of the three parameters. If, at runtime, the actual parameter is not of the expected type, a [TypeError] error is thrown. The third parameter is of type [IExample]. This means that the actual parameter must be an instance of a class that implements the [IExample] interface; therefore, in our example, an instance of the classes [Class1] or [Class2]. The function [calculate] is not aware of these classes; it only recognizes the interface. We can pass it objects of classes written later, provided they implement the [IExample] interface;
  • lines 15–16: we use the [add, subtract] methods of the [IExample] interface;
  • line 24: the third actual parameter is of type [Class1], which is compatible with the type [IExample] of the function’s third formal parameter;
  • line 25: same applies to the class [Class2];
  • line 28: an interface cannot be instantiated; an error is returned (line 6 of the results).

6.3. Deriving an Interface

Just as a child class can extend a parent class, a child interface can extend a parent interface.

Consider the interface [IExample2] defined in the file [IExample2.php]:

<?php

// strict respect of the declared types of function parameters
declare(strict_types=1);

// namespace
namespace Examples;

// inclusion of the definition of the IExample interface
require_once __DIR__ . "/IExample.php";

// an interface derived from IExample
interface IExample2 extends IExample
{
    // the method added by the interface
    public function multiply(int $i, int $j): int;
}

// implementation of the IExample2 interface
class Class3 extends Class2 implements IExample2
{
    // [Override] checks that the method does exist in the interface
    #[\Override]
    public function multiply(int $a, int $b): int
    {
        return $a * $b;
    }
}

Comments

  • line 7: the [IExample2] interface will be in the same namespace as the [IExample] interface;
  • line 10: the definition file for the [IExample] interface is included;
  • Lines 13–17: define the [IExample2] interface, which extends ([extends]) the [IExample] interface and adds a new method to it (line 16). An interface can even extend multiple interfaces: [interface I3 extends I1, I2];
  • lines 20–28: The class [Class3] extends the class [Class2]. Since the latter implements the [IExample] interface, the [Class3] class will also do so. In fact, [Class3] inherits all the public and protected methods of the [Class2] class, including the [add] and [subtract] methods. Furthermore, it is specified that [Class3] implements the [IExample2] interface. As such, it must also implement the [multiply] method, which [Class2] does not do. This is what lines 24–27 do;
  • line 23: Starting with PHP 8.3, the attribute [#[\Override]], discussed in the previous chapter, also applies to a method that implements an interface method. If the method [multiply] were to disappear from the interface [IExample2], PHP would signal this.

The [IExample2] interface is used by the following [interface-02.php] script:

<?php

// strict respect of the declared types of function parameters
declare(strict_types=1);

// inclusion and declaration of the classes and interfaces needed by the script
require_once __DIR__ . "/IExample2.php";
use Examples\IExample2;
use Examples\Class3;

// function
function calculate(int $a, int $b, IExample2 $interface): void
{
    print $interface->add($a, $b) . "\n";
    print $interface->subtract($a, $b) . "\n";
    print $interface->multiply($a, $b) . "\n";
}

// ------------------- main
// creating 1 object of type Class3 that implements IExample2
$c3 = new Class3();
// calling the calculate function
calculate(4, 3, $c3);

Results:

1
2
3
4
> php interface-02.php
107
201
12

Comments

  • Line 12: The third parameter of the [calculate] function is of type [IExample2];
  • Lines 14–16: The three methods of the [IExample2] interface are used: the two inherited from [IExample] and its own;
  • line 23: the function [calculate] is called with, as its third parameter, an object of type [Class3] that implements [IExample2] (line 21).

6.4. Passing an Interface as a Function Parameter

We saw in the previous chapter (in the section “Passing an Object as a Function Parameter”) that when the expected type for a function parameter is a class, the actual parameter can be an object of the expected type or a derived type. The same applies to interfaces, as shown in the following script, [interface-03.php]:

<?php

// strict respect of the declared types of function parameters
declare(strict_types=1);

// inclusion and declaration of the classes and interfaces needed by the script
require_once __DIR__ . "/IExample.php";
require_once __DIR__ . "/IExample2.php";
use Examples\IExample;
use Examples\IExample2;
use Examples\Class1;
use Examples\Class3;

// function
function calculate(int $a, int $b, IExample $interface): void
{
    print $interface->add($a, $b) . "\n";
    print $interface->subtract($a, $b) . "\n";
}

// ------------------- main
// creating 1 object of type Class3 that implements IExample2 and therefore IExample
$c3 = new Class3();
// calling the calculate function
calculate(4, 3, $c3);
// the [instanceof] operator tests whether an object implements an interface
foreach ([new Class1(), $c3] as $object) {
    print $object::class . ": IExample=" . var_export($object instanceof IExample, true)
        . ", IExample2=" . var_export($object instanceof IExample2, true) . "\n";
}
// the interfaces implemented by the Class3 class
print_r(class_implements($c3));

Results:

> php interface-03.php
107
201
Examples\Class1: IExample=true, IExample2=false
Examples\Class3: IExample=true, IExample2=true
Array
(
    [Examples\IExample] => Examples\IExample
    [Examples\IExample2] => Examples\IExample2
)

Comments

  • Line 15: The function [calculate] expects a type [IExample] as its third parameter. This means that the actual parameter can be of type [IExample] or a derived type;
  • Line 23: An object of type [$c3] is instantiated from the class [Class3], which implements the interface [IExample2], which in turn extends the interface [IExample]. So ultimately, [$c3] implements the [IExample] interface;
  • Line 25: The function [calculate] is called with the object [$c3] (of type [Class3]) as its third parameter. Through class inheritance and implementations, we saw that the object [$c3] implements the type [IExample]. We can therefore use it as the third parameter;
  • lines 27–30: The [instanceof] operator checks whether an object implements an interface. An object of type [Class1] implements [IExample] but not [IExample2] (line 4 of the results). An object of type [Class3] implements both (line 5);
  • line 32: The [class_implements] function returns the list of interfaces implemented by an object or class, either directly or through inheritance (lines 6–10 of the results).

6.5. Interface constants, multiple interfaces, and intersection types

An interface can also declare constants, and a class can implement multiple interfaces. We define two interfaces, [ICalculation] and [INameable], and two classes that implement them in the file [ICalculation.php]:

<?php

// strict respect of the declared types of function parameters
declare(strict_types=1);

// namespace
namespace Examples;

// an interface can declare constants
interface ICalculation
{
    // typed constant (PHP 8.3)
    public const int PRECISION = 2;

    public function calculate(float $a, float $b): float;
}

// a second interface
interface INameable
{
    public function getName(): string;
}

// a class can implement several interfaces
final class Addition implements ICalculation, INameable
{
    #[\Override]
    public function calculate(float $a, float $b): float
    {
        return round($a + $b, self::PRECISION);
    }

    #[\Override]
    public function getName(): string
    {
        return "addition";
    }
}

final class Division implements ICalculation, INameable
{
    #[\Override]
    public function calculate(float $a, float $b): float
    {
        // [ICalculation::PRECISION] and [self::PRECISION] refer to the same constant
        return round($a / $b, ICalculation::PRECISION);
    }

    #[\Override]
    public function getName(): string
    {
        return "division";
    }
}

Comments

  • line 13: The [ICalculation] interface declares a constant. All classes that implement the interface inherit it. Starting with PHP 8.3, a class or interface constant can be typed (here, [int]);
  • Lines 19–22: A second interface, with a single method;
  • line 25: the class [Addition] implements both interfaces: the interface names are separated by commas. It must define the methods of both;
  • line 30: the constant inherited from the interface is accessed by [self::PRECISION]. The function [round] rounds a number to the specified number of decimal places;
  • line 46: The constant can also be referred to by the interface name: [ICalculation::PRECISION].

These interfaces are used by the script [interface-04.php]:

<?php

// strict respect of the declared types of function parameters
declare(strict_types=1);

// inclusion and declaration of the classes and interfaces needed by the script
require_once __DIR__ . "/ICalculation.php";
use Examples\ICalculation;
use Examples\INameable;
use Examples\Addition;
use Examples\Division;

// the [$calculation] parameter has a default value: an object created by [new] (PHP 8.1)
function execute(float $a, float $b, ICalculation $calculation = new Addition()): float
{
    return $calculation->calculate($a, $b);
}

// intersection type (PHP 8.1): [$operation] must implement both interfaces
function display(float $a, float $b, ICalculation&INameable $operation): void
{
    print $operation->getName() . "($a, $b)=" . $operation->calculate($a, $b) . "\n";
}

// ------------------- main
// constant of the interface
print "precision: " . ICalculation::PRECISION . " decimal places\n";
// default value of the 3rd parameter
print "execute(1.234, 2.345)=" . execute(1.234, 2.345) . "\n";
// explicit parameter
print "execute(10, 3, new Division())=" . execute(10, 3, new Division()) . "\n";
// intersection type
display(10, 4, new Division());
display(10, 4, new Addition());
// the classes implement both interfaces
print_r(class_implements(Division::class));

Results:

> php interface-04.php
precision: 2 decimal places
execute(1.234, 2.345)=3.58
execute(10, 3, new Division())=3.33
division(10, 4)=2.5
addition(10, 4)=14
Array
(
    [Examples\ICalculation] => Examples\ICalculation
    [Examples\INameable] => Examples\INameable
)

Comments

  • Line 14: The third parameter of the [execute] function is of type [ICalculation] and has a default value—a [Addition] object created with [new]. Up to PHP 8.0, the default value of a parameter could only be a constant. Starting with PHP 8.1, the expression [new] is accepted in parameter default values (“new in initializers”), as well as in the arguments of PHP attributes ([#[...]]), static variables, and global constants. It remains prohibited in the default value of a class attribute. The object is created on every call that omits the parameter;
  • line 20: the type [ICalculation&INameable] is an intersection type (PHP 8.1): the actual parameter must implement both [ICalculation] and [INameable]. The function can then call methods from both interfaces. This should not be confused with the union type [ICalculation|INameable] (either one);
  • line 27: the constant is accessible from outside via the interface name (line 2 of the results);
  • line 29: the third parameter is omitted; it is a [Addition] object that performs the calculation (line 3);
  • line 31: a [Division] object is explicitly passed (line 4). Note that the integers 10 and 3 are accepted for parameters of type [float] despite the declaration [strict_types=1]: the conversion of a ***[int] to a [float]*** is the only automatic conversion allowed in strict mode;
  • Lines 33–34: Both classes implement both interfaces; they are accepted by the [display] function (lines 5–6);
  • line 36: the notation [Division::class] represents the full class name, in this case [Examples\Division]. The function [class_implements] accepts both a class name and an object.

Interfaces are ubiquitous in PHP itself. We have already encountered two in the previous chapter: [Stringable], automatically implemented by any class with a [__toString] method, and [JsonSerializable], which allows you to control an object’s JSON encoding. In the next chapter, we’ll look at the [Throwable] interface, which is implemented by all errors and exceptions. An enumeration can also implement an interface. Finally, we’ll see later in this course that interfaces form the basis of our applications’ layered architecture: each layer knows only the interface of the adjacent layer.