PHP interfaces, what are they and when should you use them?

Photo of John Richardson

John Richardson

Fullstack PHP developer

Cover image for article: PHP interfaces, what are they and when should you use them?

Interfaces have been part of PHP since version 5.0 and are now a staple in most well-designed libraries and applications. With that in mind, it’s worth taking a moment to understand what an interface actually is - and when it makes sense to use one.

So, what is an interface?

Often described as a “contract” an interface ensures that any class implementing it provides a specific set of methods. In other words, if your class implements an interface, it’s promising to offer certain functionality. This is incredibly useful when other parts of your application rely on that class - they can trust that the required methods will always exist.

Interfaces also make implementing classes interchangeable. For example, suppose ClassA and ClassB both implement InterfaceZ, which requires a helloWorld() method. Another class, ClassC, can accept anything that implements InterfaceZ and call helloWorld() without caring which specific class it received.

Here’s what that looks like in practice:

interface InterfaceZ {
    public function helloWorld(): string;
}

class ClassA implements InterfaceZ {
    public function helloWorld(): string {
        return "Hello from ClassA!";
    }
}

class ClassB implements InterfaceZ {
    public function helloWorld(): string {
        return "Greetings from ClassB!";
    }
}

Now ClassC can work with either implementation:

class ClassC
{
    public function run(InterfaceZ $dependency): void
    {
        echo $dependency->helloWorld();
    }
}

How do interfaces differ from abstract classes?

If you are familiar with abstract classes, interfaces work in a vaguely similar way, providing a blueprint for classes which use them. This may sound confusing, as you are probably wondering why an interface is needed if an abstract class serves a similar purpose.

Well, abstract classes are normally used when you need to provide some basic, or common functionality which can then be extended or modified by derived classes. I like to think that an abstract class defines the core identity of a class. Any derived classes are of the same type. As an example, you may have an abstract ‘Animal’ class and a derived ‘Dog’ class. An interface on the other hand defines how the class will interact with the outside world, and can be used by a wide range of object types. As an example, you may have a ‘Moveable’ interface, which both ‘Animal’ and ‘Vehicle’ types can implement.

Additionally, it is not possible for a class to extend multiple abstract classes, whereas it is possible to implement multiple interfaces.

Recap/Notes:

  • An interface can be used to enforce a single method, or multiple methods. Also, just to reiterate, an interface defines what methods a class MUST have, but it is still possible to add additional methods outside of this enforcement.
  • In PHP an interface contains method declarations only – the method itself will be empty. This is in contrast to abstract classes, where it is possible to add some logic to methods which can then be overloaded/overridden in sub-classes.
  • An interface expresses a ‘HAS A’ relationship, while an abstract class expresses an ‘IS A’ relationship.

Example:

I find it’s often easier to understand these concepts when given something to look at, so here is an example which demonstrates usage of an interface.

This interface will enforce one method on the classes that implement it:

interface RendererInterface {
    public function render();
}

These classes implement the interface (and as such must contain a method named ‘render’):

class JsonRenderer implements RendererInterface {
    public function render() {
    }
}
class XMLRenderer implements RendererInterface {
    public function render() {
    }
}

Notice both classes contain the render method, if they did not, then an exception would be thrown. When using these classes throughout your application you can now be sure that each has a render method, and as such they become interchangeable. You could, as an example, do something similar to the below:

class blogPost {
    function __construct(RendererInterface $renderer) {
        $this->renderer = $renderer;
    }

    function render() {
        return $this->renderer->render();
    }
}

The above class now has the ability to render it’s contents in many different formats. This is made possible by injecting a ‘renderer’ class (which implements ‘RendererInterface’) into it. If you are unfamiliar with interfaces, you may also be unfamiliar with type-hinting.

In a nut-shell, type-hinting allows you to restrict a parameter to a specified type. This makes sense when the code used inside the function or class relies on the type of object passed in. Since we are assigning the object to a class property and then calling a specific method(render), our class is relying on the fact that the method exists. By typing-hinting the parameter to an object type of ‘RendererInterface’, it is possible to safely assume that the call to the render method(‘return $this->renderer->render()’) will be valid. As an added bonus, this now means that it’s possible to pass in absolutely any type of object, so long as it implements the interface.

Use of type hinting generally makes life easier for developers and those maintaining the code-base. Your IDE (if you are using a decent one) will have the ability to provide code completion, and it also self documents the parameter for developers working on the project in future; they instantly know that rendering the blogPost in a different format is as simple as creating a new class which implements the interface defined in the constructor.

In a dynamically-typed language are interfaces truly necessary?

Essentially the answer to that is no – they are not compulsory. You can remove the type-hint from the previous example to demonstrate this:

class blogPost {
    function __construct($renderer) {
        $this->renderer = $renderer;
    }

    function render() {
        return $this->renderer->render();
    }
}

PHP is dynamically typed (checks types at run-time) and as such allows ambiguous variables to be passed in such a way. Of course, if the ‘renderer’ did not have a ‘render’ method then our script would crash when executed. In a statically typed language however, providing a type would be compulsory (as types are required to be known at compile time). You could definitely argue that enforcing types in this way is the superior choice, as it means we catch errors upfront rather than once the code reaches production, for example.

So although it’s not a requirement, it’s still wise to use interfaces where possible. As an example, you may often find yourself spending a lot of time on manual validation, such as using the ‘is_object()’, or ‘method_exists()’ functions throughout your classes to verify that the correct type/object has been passed in. Using an interface with type-hinting removes the need for this manual work. It also makes the code easier to maintain (as mentioned earlier in the article).

© John Richardson Development. All rights reserved.