20 minutes
Leveling Up: The Power of Interfaces
This article was originally published in PHP|Architect magazine in the December 2015 issue. These articles are all copyright by David Stockton. You may be able to purchase the issue here.
Requirements:
- PHP 7+ (preferably)
- https://github.com/rlerdorf/php7dev
- PHP 5.5+
Interfaces are one of the most powerful and yet completely under-used features in PHP. It may be difficult to understand why a feature in PHP that technically doesn’t “do” anything can be one of the most powerful concepts in Object-Oriented Programming, however learning to love and embrace the power of interfaces in your code can help you bring your OOP skills to the next level.
Introduction
PHP is a single-inheritance language. This means that when we decide to use inheritance, each class can inherit from up to a single class. Some languages allow multiple inheritance, meaning a class can have more than one parent class. There are very good reasons to only allow single inheritance, including a complete avoidance of what’s known as the “Deadly Diamond of Death”.
Let’s talk quickly about multiple inheritance even though it’s not anything we can really do in PHP. Figure 1 shows how multiple inheritance can lead to issues. In this example, class B and C both inherit from and presumably override functionality from class A. Class D inherits from both B and C. If B and C both override some functionality of A, then what functionality would D use or override? The functionality from B or that from C.
Different languages each solve this problem in different ways. In PHP we only have single inheritance, but we can get around this to a certain extent by using traits. But again, this article isn’t really about traits or even inheritance, though we may talk about them a bit. Inheritance in OOP gives us an “is a” relationship. In other words, when you use inheritance, think about replacing the word “extends” with “is a”. If it makes sense, then inheritance might be the right tool. However, if that doesn’t make sense, then you’re probably not using inheritance correctly.
For example, class Volkswagen extends Automobile
makes sense, since we can replace “extends” with “is a” and see that Volkswagen is an automobile. However, an inheritance relationship such as class Dog extends Leg
would not make sense since a dog is not a leg. In the Figure 1 diagram, classes B and C are both of type A, that is, B “is an” A and C “is an” A. Class D “is a” B and it also “is a” C. We’ll come back to the “is a” relationship shortly.
What is an interface?
An interface in PHP defines the signatures of public methods that must be defined by a concrete PHP class. There are potentially several terms just in this sentence that we may need to define in order to completely understand this. Let’s do that now.
Firstly, let’s define the term “signature”. The signature of a function or method consists of the name of the function or method as well as the parameters. If the parameters are type-hinted, or if we’ve specified a return type (hello, PHP 7) then the signature includes those as well. Let’s look at some example signatures.
public function buildObject(ServiceLocator $serviceLocator);
protected function doInternalWork(StateObject $state, int $multiplier);
private function articulateSamoflange(float $pitch, float $roll, float $yaw) : Orientation;
In the examples above, I’ve only listed signatures of “methods”, that is functions that belong to class definitions, but standard functions have a signature as well. However, since interfaces only apply to objects, I’m only using OOP examples. So a signature tells PHP the name of the method, what to expect for parameters and potentially a return type.
The next part of our interfaces definition is that it defines public methods. As mentioned earlier, a method is just a function that belongs to a class definition. And public is referring to the visibility modifier of that method. PHP defines three different “visibility modifiers”. First, public methods (or properties) are those that can be accessed and used from anywhere. If you can see the object, you can call the public methods as well as change and access the public properties. If a method is marked as protected, the second visibility modifier, it means that the method is usable from within the object’s inheritance tree. This means a protected method can be called within instances of the object itself or from the parents or children of the class. The protected properties are also accessible from within these same classes in the inheritance tree, but not by anything outside of it. The final visibility modifier is private. It means that the method can only be called from within the individual instances of the object that defines the private method.
An interface can only define public methods. Public methods are the “API” or Application Programming Interface for the rest of your code. It’s not (necessarily) an API in the sense of Twitter, Facebook or Google APIs but defining a good API in your code is as important as building a good externally accessible API. Protected and private methods in a class are internal – they are specific to how you implement whatever functionality your class provides. You should be able to change these without breaking the code of anything that calls any of the public methods in your class.
The final term I want to be sure is understood is that of a “concrete” class. This means a class that is instantiable, or simply, any class that is not an abstract class. More specifically, parts of an interface may be implemented in an abstract class, but by the time we get to a concrete class, all the methods in the interface or interfaces that are implemented must be defined, whether all in the concrete class, in the abstract class(es) inherited by the concrete class or through some combination. If an abstract class is indicated to implement an interface, it must either define those methods or have them marked as abstract. Again, the same rules of inheritance apply here as well. Parent abstract classes of a given abstract class may be where some or all the methods of an interface are defined.
As a side note, you can create interfaces that do not contain any methods. This can be helpful in some situations.
Example Interface
To help illustrate the point, let’s take a look at a simple interface.
interface CalculatorInterface
{
public function add(int $x, int $y);
public function subtract(int $x, int $y);
public function multiply(int $x, int $y);
public function divide(int $x, int $y);
}
We’ve now got a simple interface that defines some standard and simple calculator based functionality. There is no implementation, and by itself, this code doesn’t do anything at all. It is defining methods that a (concrete) class that implements this interface must implement.
To use this interface, our class could be defined as such:
class Calculator implements CalculatorInterface
{
public function add(int $x, int $y) { return $x + $y; }
public function subtract(int $x, int $y) { return $x - $y; }
public function multiply(int $x, int $y) { return $x * $y; }
public function divide(int $x, int $y) { return $x / $y; }
}
Dependencies and Interfaces
Suppose now we’ve got another class that needs to do calculations in order to perform its job. We can use dependency injection to provide a class that does calculations:
class MathDoer2000
{
private $calculator;
public function __construct($calculator)
{
$this->calculator = $calculator;
}
public function doMaths($x, $y)
{
return $this->calculator->add($x, $y) +
$this->calculator->multiply($x, $y);
}
}
In the example above, I’ve not typehinted anything on the constructor, so PHP would allow literally anything to be passed in. If that parameter is an object that defines a public add
and multiply
method (or potentially a magic __call method) then the doMaths
method should work. But since there’s no typehinting on it, nothing would stop the caller from sending in an object that doesn’t have those methods, or a string, or an array, or literally anything else. In those cases, the code would bomb out with an error. In order to prevent these errors, we’d need to write a bunch of code to determine if the parameter passed in was an object and if that object defined those methods. If it failed any of those checks, we’d need to determine what the behavior should be.
However, if we redefine the constructor to typehint a Calculator parameter then we can be way more confident that things will work as expected.
<snip>...
public function __construct(Calculator $calculator)
{
$this->calculator = $calculator;
}
<snip>...
Redefining our constructor this way will give us certainty that whatever is provided to our object will be either a Calculator or a class that extends Calculator. This means that if a developer tries to pass in any value that is not a Calculator or derived from Calculator, PHP will throw an error. We don’t need to bother with all the various checks as PHP does it for us automatically.
However, we’re limited to only classes within that inheritance tree. If we instead use the interface we defined as the typehint, we can now use any class that implements the interface.
<snip>...
public function __construct(CalculatorInterface $calculator)
{
$this->calculator = $calculator;
}
<snip>...
It’s a very small change, but it gives our code a lot of flexibility. Instead of being tied to a particular implementation of a Calculator, this code can now use any implementation that provides those four methods and implements the CalculatorInterface
. Our MathDoer2000
class will work with any implementation.
Duck Typing
Interfaces are an explicit and powerful way of letting PHP, IDEs, and other developers know what methods our code will be using. It’s not the only way to compose classes though. In the first example, we didn’t type hint anything. In programming there’s a concept called “Duck Typing”. Essentially, this comes from the phrase, “if it looks like a duck and sounds like a duck, it’s a duck”, or in coding terms, “if it does what a duck does, we can use it like it’s a duck”. In the example, with the original constructor, the MathDoer2000
class would work with any object that provided the add
and multiply
methods, even if it didn’t explicitly implement our interface or extend from the Calculator class.
Defining an interface gives us a way to communicate intent and functionality as well as provide a mechanism that other developers can create new implementations that can be used as a drop-in replacement for a provided implementation. This is one place I feel interfaces are extremely powerful and protect us from problems that may arise from duck typing.
To give a silly example, suppose that we know that squeezing a duck can cause it to quack.
class Duck
{
public function squeeze() { echo 'quack'; }
}
We could imagine another class that works in a similar way that could potentially work in place of a duck.
class DuckCall
{
public function squeeze() { echo 'quack'; }
}
The expected functionality of a duck would be that we squeeze it and it will quack. If we need something to quack, then Duck Typing would infer that if something has a squeeze method, we can call it just like we would on the Duck and quacking would happen. However, if we were provided with an instance of the class below, duck typing could go very poorly.
class Badger
{
public function squeeze() { // bites face off of squeezer }
}
Despite having the same function name and signature, squeezing a badger is not going to result in a desirable outcome. With an interface, we can be more explicit and get an implementation that is more desirable. For instance:
namespace Duck;
interface Squeezable
{
public function squeeze() : Duck\Sound;
}
namespace Badger
interface Squeezable
{
public function squeeze() : Pain;
}
With the examples above, we’re now being explicit in a couple of ways. Firstly, the namespace lets us know about the intent. Additionally the return types means the function signatures are different because the resulting return will be quite different. If we omit the return types on those interfaces, we could end up with a class that implements both of those interfaces.
First, the interfaces with no return types:
namespace Duck;
interface Squeezable
{
public function squeeze();
}
namespace Badger
interface Squeezable
{
public function squeeze();
}
And now a class that can use this:
class Quacker
{
public function makeDuckSounds(\Duck\Squeezable $duck)
{
return $duck->squeeze();
}
}
In this case, we know that the implementation provided is implementing the Duck version of Squeezable. However, remember back at the beginning when I talked about multiple inheritance? In PHP you can implement multiple interfaces. These interfaces can define new and different functionality, but as long as the signatures are the same, multiple interfaces can define the same method and be implemented by the same class:
class RazorToothDuck implements Duck\Squeezable, Badger\Squeezable
{
public function squeeze()
{
$this->biteFaces();
return $this->quack();
}
}
Multiple Inheritance with Interfaces
As you’re already aware, we can extend classes, but only from one other class. Interfaces are allowed to use multiple inheritance however. And as long as the interfaces don’t provide two methods with the same name but different signatures, it will work.
interface KillerDuckInterface extends Duck\Squeezable, Badger\Squeezable {}
In the example above, I don’t even need to define any more methods since everything I need is defined in the two interfaces that I extended. If I create a class that implements the KillerDuck interface, PHP will ensure that I have to implement all the methods defined in both of the inherited interfaces. As a side note, you’re not limited to only two interfaces either. You can create interfaces that are extended from any number of composed interfaces as long as there are no signature discrepancies. If you do try to extend from two interfaces that have a conflicting definition though, you will see an error like this:
PHP Fatal error: Declaration of A::foo() must be compatible with B::foo(B $b)
Interestingly, implementing interfaces also gives us that “is a” relationship which is why you’ll often see interfaces named with the “-able” suffix. Since we can implement multiple interfaces, it means that we can test for and use our objects in different ways depending on what interfaces they have defined.
If my code was aware that it needs to squeeze something to get a quack, but also wanted to ensure that it wasn’t squeezing a badger, it could do something like this:
if ($myObj instanceof Duck\Squeezable
&& ! $myObj instanceof BadgerSqueezable) { ... }
You could also use an interface to define something like SafeToSqueezeInterface
which could be implemented by the Duck. If your code type hinted on that interface then it should be assured that whatever it was given was safe to squeeze, but it wouldn’t necessarily imply that it would quack when squeezed.
Real Ultimate Power
The real power of Object Oriented Programming doesn’t come from inheritance, even though that’s likely one of the first concepts you learned when introduced to OOP. The power comes from being able to build objects from other objects and have them delegate to each other.
This allows for concepts such as “Do One Thing and Do It Well”, otherwise known as the “Single-Responsibility Principle” or SRP. If our code follows this principle, and we’re using interfaces and typehints on those interfaces, we can easily swap out one implementation for another and none of the code that uses these interfaces needs to know or should care that it’s now interacting with another implementation. And swapping the implementations can be something that we do at configuration time (like changing out a caching system for a different implementation) or it can be done even at run-time.
When objects can be written to use the methods defined in the interface and not use any other methods, then swapping implementations possible and powerful. It’s the basis for the OOP concept of “polymorphism” which literally means “many shapes”. It allows simple code to interact with a whole selection of different objects in the exact same way even though those objects themselves may have completely different implementations and behavior. Polymorphism is why you can climb behind the wheel of a car and have a reasonable certainty of how to make the car stop, and go as well as turn left and right. Other parts of the car implementation may differ, but those four bits of functionality are pretty well common to all cars.
Now, imagine writing code for a graphical user interface of some kind. You’ll have components that represent buttons, inputs, shapes, scroll bars, etc. Each of these graphical elements will need to be positioned somewhere on the screen and will likely be responsible for how they draw themselves. If the code that manages all of these different components had to know about how to individually position each element and any special requirements each had when drawing, it would take a lot of time to build a GUI and it would be very error prone. Instead, we can use interfaces to define a common way to interact with the components.
interface Positionable
{
public function setCoordinates($x, $y);
}
interface Drawable
{
public function drawSelf(GraphicsContext $g);
}
Components that implement Positionable all provide similar functionality and an identical way to interact with them even though you may have different components which use that position in different ways. The code managing the shapes and components just needs to loop over them and call setCoordinates
in exactly the same way for each item. If the element can draw itself, the code that manages the shapes can call each element in turn, passing in some sort of graphics context that the component would be able to use to render itself.
Taking this further, imagine that GraphicsContext above is an interface itself. It would provide common functionality related to the drawing of shapes, setting colors, etc. There may be an implementation for drawing on the screen, as well as another implementation that is for dealing with printers, external displays, or perhaps even computer aided shape cutting machines. By providing a different implementation of the GraphicsContext interface to the elements we want to draw, the element is able to render for a screen, a printer or potentially a large vinyl cutout representation of the component. Each element’s drawSelf method would use only the publicly defined methods found within the GraphicsContext interface.
If we can build our code in this way, it allows for extremely powerful and flexible implementations that can be used in ways that we, as the original developer, hadn’t even thought of.
Using Methods Outside the Interface
Of course it’s possible to use methods outside of the interface. When we build our objects or send in concrete implementations, we will likely know what those are and what other methods have been provided. But as soon as we start calling methods outside of those provided on the interface we’ve typehinted on, we have now tied our code to a specific implementation and changing it out may be difficult. Suppose the GraphicsContext implementation for drawing on a screen also had public methods for setting the intensity of a pixel or changing the brightness of the screen. If our code in the elements use these methods, everything still works and we may be able to create some really neat effects. However, it’s unlikely that the implementation for the printer or the vinyl cutter would have a setPixelIntensity or setBrightness method which means if we tried to use that shape to print or cut out vinyl, the code would blow up.
By using methods that are in the implementation but not specified in the interface, rather than allowing us to do more, we’ve actually severely limited what we can actually do with the code beyond what we’ve thought of today.
Designing Interfaces
When designing interfaces, it’s important to consider how a class will be used and expose the bare minimum that is needed to effectively use the class. The interface is the API to callers of the code. If you later determine that you want to expose more functionality of a class publicly, you’re unlikely to hear any outcry. However, if you’ve exposed a method publicly, you’ve essentially committed to supporting it for the life of your project, or at least until you can be sure that there are no bits of code calling it. By limiting the public API, it leaves us options with how we want to implement it. As long as the public API hasn’t changed, we can refactor to our heart’s content any of the internal private methods and be sure we’ve not broken anything. Protected method refactoring may need a bit more care if any classes have extended the class we’re refactoring.
I recommend being as explicit as you can be or need to be in your interfaces and typehinting. In other words, if you can type hint on an interface, do so. If you can provide a return type for a method, do it. However, in some cases, you may determine that the amount of effort required to support a fully type-hinted API or a method may not be worth it and you want the caller to be able to provide a variety of different inputs. We’re writing PHP code. It’s a dynamically typed language and can deal with all sorts of different types being passed into methods and returned. But, if you can be explicit, it means your code, the PHP compiler, and your IDE can now prevent entire classes of errors from ever happening before your code is ever run. That’s a pretty incredible thing.
Strict Types
I know I’ve talked about Strict Types in the last two articles, but this is one of the biggest changes to PHP in nearly two decades. PHP is absolutely still the same dynamically typed, auto-coercing language we’ve all grown to love… and work around in some cases. The PHP 7 implementation of strict types allows the caller of the methods or functions to define whether they’d like PHP to coerce the values to those types or if it should be strict.
The declare(strict_types=1);
must be the first line in any file where you want the calls to use strict types. This means if you define a file that contains just your interface, the strict types statement will not have any affect whatsoever. Since there’s no implementation in an interface, by definition, there can be no calls made. However, defining your method parameters and return types means that in the actual implementation, you can use strict types and it will affect the calls made in your implementation.
If we break it down it means this:
In your interface file, the declare(strict_types=1);
declaration has no effect at all. In the classes that implements this interface, if you’ve specified declare(strict_types=1);
then the calls that are made within your classes from one method to another will be using strict types. If you use these classes in another file and you’d like your calls into the public methods of the objects instantiated from your classes to use strict types, then in those files, you’ll want to start with declare(strict_types=1);
.
Inside of your methods, regardless of whether you’ve called them with strict types or not, your methods can rely on receiving types that match the typehints you’ve defined. This means even if the caller isn’t using strict types, but your method is defined to take an int
, your method will only ever receive an int
. It doesn’t matter if the caller isn’t using strict mode and they pass in a string that can be turned into an integer, by the time it gets to your method, PHP will have already converted it to an integer. If the caller decides that they want to use strict types, then they must provide an integer to the method. A string that could be converted to an integer will not be automatically converted.
Conclusion
Interfaces and type-hinting (both against interfaces as well as scalars) provide PHP developers with a new and powerful tool that can be used to make systems of classes that are very powerful and flexible, and can be used in ways that we don’t have to anticipate when we first build them. While coding to an interface may at first seem limiting, the freedom and flexibility that comes with being able to swap out an implementation, or apply the same method calls to groups of code that use the same interface, is extremely powerful. It allows for expanding and enhancing the functionality of our code without the need to modify existing classes, and without the need to inherit classes. Inheritance in OOP, although often taught as one of the first OOP concepts is actually quite limiting in most cases. The rule of thumb I subscribe to is “Prefer Composition Over Inheritance”. In other words, use objects inside of your objects to expand and delegate functionality rather than relying on inheritance to solve a problem. Chances are the solution that uses composition will result in a better design that is much more flexible and powerful than can be created with inheritance.
Image Credits: The background image used in the "Twitter card" for this article is cropped from "badger" by Manchester-Monkey. It is licensed under CC BY-NC-SA 2.0.
David Stockton is a husband, father and Software Engineer. He builds software in Colorado, leading a few teams of software developers creating a very diverse array of web applications. His two daughters, age 11 and 9, are learning to code JavaScript, Python, Scratch, a bit of Java and PHP as well as building electrical circuits. His 4 year old son has been seen studying calculus, practicing linguistics with regards to human biology, and is excelling at annoying his sisters. David is a conference speaker and an active proponent of TDD, APIs and elegant PHP. He's on twitter as @dstockto and can be reached by email at levelingup@davidstockton.com.
4186 Words
2015-12-01 21:00 -0700