Image PHP Interface Blog
May 21, 2020

What Is an Interface In PHP?

PHP Development

PHP interfaces provide reusable types detailing specific behaviors that your application can consume. In particular, they allow you to create classes that have known, expected behaviors that fit within your own application architecture. PHP interfaces also allow you to substitute different implementations, regardless of normal class inheritance, giving you more flexibility in how your structure your applications.

In this blog, we dive in on PHP interfaces, including basic concepts and terminology, the role of PHP interfaces in object-oriented programming, and more.

Back to top

What Is a PHP Interface?

A PHP interface defines a contract which a class must fulfill. If a PHP class is a blueprint for objects, an interface is a blueprint for classes. Any class implementing a given interface can be expected to have the same behavior in terms of what can be called, how it can be called, and what will be returned.

Back to top

PHP Interface Basics

For an example of PHP Interface basics, we will consider "something that vocalizes". In the real world, this could be a bird ("tweets"), a dog ("barks"), a cat ("meows"), or a human ("sings"). The details of vocalization are specific to each type, but each can vocalize.

We could describe this as follows:

interface Vocalizer
{
    public function vocalize(string $message): string;
}

What we are saying above is: given a string $message, vocalize() will return what is heard as a string.

Now, an interface doesn't do anything on its own. It does, however, act as a PHP type. This means you can type hint on it as an argument, or even return something of that type from a function or method.

To create something of that type, we need to have an implementation available. Classes can implement an interface:

class Bird implements Vocalizer
{
    public function vocalize(string $message): string
    {
        return sprintf('<tweet>%s</tweet>', $message);
    }
}

Let's say we have a function as follows:

function prepareMessage(string $message, Vocalizer $vocalizer): string
{
    return $vocalizer->vocalize($message);
}

The above function can be called with any $vocalizer that implements Vocalizer:

$chickadee = new Bird();
echo prepareMessage('a song', $chickadee); // "<tweet>a song</tweet>"

Want to Expand Your PHP Skills?

Zend offers a variety of training courses, from basic to advanced. Explore our training options via the link below.

Explore Our Training Courses

Back to top

Inheritance and Substitution

In a nutshell, interfaces give us a way to provide features without requiring class inheritance. This can be really useful for adapting existing classes to work in other contexts.  
As an example, let's say we had a Bird class already that did not implement Vocalizer, but we had essentially equivalent functionality via a method tweet():

class Bird
{
    public function tweet(string $message): string
    {
        return sprintf('<tweet>%s</tweet>', $message);
    }
}

We could do one of two things to make this class a Vocalizer at this point, while keeping all existing functionality.

First, we could update the class to directly implement the Vocalizer interface:

class Bird implements Vocalizer
{
    public function vocalize(string $message): string
    {
        return $this->tweet($message);
    }
    public function tweet(string $message): string
    {
        return sprintf('<tweet>%s</tweet>', $message);
    }
}

Alternately, we could create an extension of Bird that implements Vocalizer:

class VocalizingBird extends Bird implements Vocalizer
{
    public function vocalize(string $message): string
    {
        return $this->tweet($message);
    }
}

Because this latter is an extension of Bird, it fulfills the Bird type for typehints; because it also implements Vocalizer, it also fulfills that type. You could also accomplish the above via an anonymous class implementation:

$bird = new class extends Bird implements Vocalizer {
    public function vocalize(string $message): string
    {
        return $this->tweet($message);
    }
};

This leads us to a key rationale for using interfaces: the ability to substitute one type for another. This ability ties directly to one of the five principles of object-oriented programming — the dependency inversion principle.

Back to top

Five Principles for Object-Oriented Programming

When performing object-oriented programming, there is a set of five core design principles that are recommended for creating flexible, maintainable architectures known as the SOLID principles:

  1. Single-responsibility principle
  2. Open-closed principle
  3. Liskov substitution principle
  4. Interface segregation principle
  5. Dependency inversion principle

Dependency Inversion Principle

While we won't talk about all the principles included in OOP, the dependency inversion principle is directly tied to interfaces.

The dependency inversion principle states that we should depend on abstractions, not implementations. What does that mean?

If we are only worried about vocalization, we should not need to worry about whether or not we have a Bird, a Human, or an Animal. We should worry about whether or not we have something capable of vocalization. 

Interfaces allow us to define these capabilities, and then allow our code to typehint against those capabilities, and not a more specific type. This in turn allows us to substitute different types when they fulfill the contract defined by the interface.

Common Mistake When Performing Object-Oriented Programming

One common mistake when beginning to perform object-oriented programming is to create a strict inheritance tree: a base class, and then subtypes of that base class, and then implementations of those subtypes, and so on. 

This can lead to creating a class that technically has a dozen or more different behaviors, but which is only used for one of them. By splitting these behaviors out into different interfaces, we can create classes that implement only specific behaviors, and use them wherever those behaviors are needed.

Back to top

What Can We Define In an PHP Interface?

Now that we have a basic idea of what an interface is, why we might use one, and how we can implement one? What exactly can we define in an interface?

Interfaces in PHP are limited to:  
•    Publicly visible methods.  
•    Publicly visible constants.

In our previous article on PHP classes, we noted that visibility is a more advanced topic. It still is, but we can cover some basics. In a nutshell, visibility helps detail what can use the functionality, and where. Something that has public visibility can be accessed both within class methods, as well as from instances. What is meant by the latter? In the following:

$chickadee = new Bird();

$chickadee is an instance.

Digression: Class Constants

In the article on classes we didn't cover class constants. A class constant is just like a normal PHP constant in that it details a value that, well, remains constant. It's a value that cannot be reassigned later. To declare a class constant, you use the const keyword within your class declaration, and optionally a visibility operator (like properties and methods, visibility defaults to public):

class Bird
{
    public const TAXONOMY_CLASS = 'aves';
}

You then refer to using the class name and constant name, separated by:

$taxonomy = Bird::TAXONOMY_CLASS;

A class constant can be any scalar type, or an array, as long as no members of the array are objects. They can even refer to other constants if needed.

By convention, constant names are usually in ALL UPPERCASE, using underscore separators.

Constants defined on an interface are inherited by all implementations.

When defining a method on an interface, you omit the body and its braces, and instead end the declaration with a semi-colon (;); you are defining a signature only. These indicate what the implementation must define in order to be valid.

We've already seen a method definition previously, when we defined a vocalize() method on our Vocalizer interface:

public function vocalize(string $message): string;

As such, any implementation must define this method, and be compatible. They can add more arguments — but only if those arguments are optional. That's the only way they can differ.

Back to top

Ability to Implement Multiple Inheritance

One feature some languages offer is multiple inheritance. This feature allows an object to extend multiple other types, and thus fulfill them all. As an example, a Horse class could extend both an Animal and a Vehicle class.

PHP does not offer multiple inheritance, at least not directly. What it does provide, however, is the ability to implement multiple interfaces. This can be useful particularly when you want to describe a subset of features a particular class provides within a given context. 

Example of Implementing Multiple Interfaces

As an example, the laminas/laminas-cache package defines a variety of interfaces describing storage adapter capabilities, including FlushableInterface, OptimizableInterface, ClearByPrefixInterface, TaggableInterface, etc. Individual adapters are all storage adapters, but they can indicate they have other capabilities by implementing these interfaces.

To implement multiple interfaces, you provide a comma-separated list of interfaces following the implements keyword when declaring your class:

class MyCustomStorageAdapter extends AbstractAdapter implements
    ClearByStorageInterface,
    FlushableInterface,
    OptimizableInterface,
    TaggableInterface
{
    // . . .
}

An instance of this class would then fulfill typehints against each of these interfaces.

Back to top

Naming PHP Interfaces

How should you name your interface? Generally speaking, name it based on the behaviors it describes. Per our example, we were defining something that vocalizes, so we named the interface Vocalizer.

This can be difficult to figure out, particularly if you extract an interface from a class you've previously defined, where the logical name is already the name of an existing class (e.g., you might want to define a Logger interface, but a Logger class already exists). 

Sometimes the naming difficulty is due to having a team of developers from different backgrounds or countries (e.g., some members of the team might not share the same native language). As such, many projects use an Interface suffix to simplify the naming decision, as well as to call out which class files in a project are contracts versus implementations.

You and your team should decide which approach makes most sense for you!

Back to top

Final Thoughts

In this blog, we've talked through the basics of PHP interfaces, the role they play in object-oriented programming, and general best practices teams can employ as they work with PHP interfaces. That said, this write-up is far from exhaustive. Zend by Perforce has a number of resources to help you learn more about principles of object-oriented programming in the PHP language, and emerging concepts that can help your team stay up to speed with PHP.

Need Help Modernizing Your PHP?

Zend offers a full suite of PHP products and services that can take your PHP applications to the next level -- including PHP LTS, migration services, consultative services, and more. Contact us today to learn more.

Contact Us

Additional Resources

 

 

Back to top