Interfaces

array get_declared_interfaces ( void )

Before you feel all warm and fuzzy about having mastered object-oriented programming in PHP, there is one further thing you need to understand. If you had a class for boats and a class for planes, how would you implement a boat plane class? The functions found in the boat class would be helpful to give you code such as sink(), scuttle(), dock(), etc, and the functions found in the plane() class would be helpful to give you code such as takeoff(), land(), and bailout(). What is really needed here is the ability inherit from both the boat class and the plane class, a technique known as multiple inheritance.

Sadly, PHP has no support for multiple inheritance, which means it is a struggle to implement this particular scenario. If you find yourself in this situation, there are two options: use two stages of single inheritance, or use an entirely new technique of interfacing . We will get to the latter option shortly, but first here is how it would look to have two stages of single inheritance:

<?php
    class boat {
        public function sink() { }
        public function scuttle() { }
        public function dock() { }
    }

    class plane extends boat {
        public function takeoff() { }
        public function land() { }
        public function bailout() { }
    }

    class boatplane extends plane {
    }

    $obj = new boatplane();
?>

In that example, our grandparent class is "boat", and we inherit all of its functions in class "plane". This in then inherited by class "boatplane", giving the child class all six functions and making it a valid boatplane class. However, it should be clear that the ends do not justify the means: the example above gives our plane class the dock() function, which is clearly incorrect.

So, the solution, such as it is, is to use interfaces. An interface can be though of as an abstract class where you can define sets of abstract functions that will be used elsewhere. If we were to use interfaces in the above example, both boat and plane would be an interface, and class boatplane would implement both of these interfaces. A class that implements an interface has to have concrete functions for each of the abstract functions defined in the interface, so by making a class implement an interface you are in fact saying, "this class is able to do everything the interface says it should". In essence, using interfaces is a way to form contracts with your classes - they must implement functions A, B, and C otherwise they will not work.

The above example could be rewritten using interfaces like this:

<?php
    interface boat {
        function sink();
        function scuttle();
        function dock();
    }

    interface plane {
        function takeoff();
        function land();
        function bailout();
    }

    class boatplane implements boat, plane {
        public function sink() { }
        public function scuttle() { }
        public function dock() { }
        public function takeoff() { }
        public function land() { }
        public function bailout() { }
    }

    $obj = new boatplane();
?>

I strongly recommend you type all that in and try it yourself - it will print out nothing, but we're going to play around with it now and you will learn the most if you see the results for yourself. Note that there are no access modifiers for the functions in the interface: they are all public by default, because it doesn't make sense to have them as anything else. Similarly you shouldn't try to use abstract or static modifiers with your interfaces - if you get an error like "PHP Fatal error: Access type for interface method boat::sink() must be omitted" you know you've gone wrong somewhere.

The first change I would like you to make is to comment out the bailout() function in the boatplane class, so that it only has five functions as opposed to six. Now run the script again - what do you get back? If you have done it correctly, PHP should quit with the following fatal error:

Fatal error: Class boatplane contains 1 abstract methods and must therefore be declared abstract (plane::bailout)

Our boatplane class, by implementing both the boat and plane interfaces, has essentially promised PHP it will have a function bailout(). Therefore, PHP gives it one by default - the bailout() function from the plane interface. However, as interfaces and their functions are entirely abstract, and by commenting out that one line we have not re-implemented bailout() in the boatplane class, the abstract function will be used and thereby make the entire boatplane class abstract - hence the error.

What this has proved is that when a class implements an interface, it makes an unbreakable contract with PHP that it will implement each function specified in that interface. Uncomment the bailout() function in the boatplane class, and try commenting out both the boat and plane interfaces, as well as rewriting the boatplane class so that you remove the "implements" part. Now what happens?

This time the script should run fine, just as it did the first time around. Essentially, there is nothing different - the boatplane class has all the same functions as it did before, so why bother with interfaces at all? The key is the "unbreakable contract" aspect, because by having a class implement an interface you know for a fact that it must implement all the functions specified in the interface and not just one or two.

The use of interfaces should be considered in the same light as the use of access modifiers - declaring a variable as private changes nothing, really, except that it forces other programmers (and perhaps yourself also) to live up to various expectations about objects of that class. The same applies to interfaces, and, although they are perhaps likely to remain one of the more niche aspects of PHP, they are certainly here to stay.

Author's Note: There is one situation in which interfaces actually make a concrete difference to your code, and that's with the Standard PHP Library (SPL). When trying to use functionality from the SPL you must always implement the appropriate interfaces - just implementing the functions isn't good enough.

The function get_declared_interfaces() will return an array of all the interfaces currently available to you, and it takes no parameters.

If you really want to delve deep into the world of interfaces, you can also have one interface inheriting from another using the same syntax as you would inherit classes.

As a result, this next script is the same as the previous one, as the plane interface inherits from the boat interface, and the boatplane class implements the plane interface:

<?php
    interface boat {
        function sink();
        function scuttle();
        function dock();
    }

    interface plane extends boat {
        function takeoff();
        function land();
        function bailout();
    }

    class boatplane implements plane {
        public function sink() { }
        public function scuttle() { }
        public function dock() { }
        public function takeoff() { }
        public function land() { }
        public function bailout() { }
    }

    $obj = new boatplane();
?>

The primary reason for inheriting interfaces is to avoid code like this:

class foo implements InterfaceA, InterfaceB, InterfaceC, InterfaceD, .... InterfaceK { }

Although it may well be a requirement for the foo class to implement all those interfaces, it does not make for easy reading. As a result, you should try to group interfaces together if possible.

Author's Note: It's important to note that although interfaces can extend other interfaces, and classes can implement interfaces, interfaces cannot extend classes. If you try this, you'll get an error along the lines of "Fatal error: boat cannot implement dog - it is not an interface".

 

Want to learn PHP 7?

Hacking with PHP has been fully updated for PHP 7, and is now available as a downloadable PDF. Get over 1200 pages of hands-on PHP learning today!

If this was helpful, please take a moment to tell others about Hacking with PHP by tweeting about it!

Next chapter: Deferencing object return values >>

Previous chapter: Helpful utility functions

Jump to:

 

Home: Table of Contents

Copyright ©2015 Paul Hudson. Follow me: @twostraws.