Something you probably don't know about interfaces

Le vendredi 15 avril 2011 dans Symfony, PHP / AFUP, Bonnes pratiques

Although I have a good knowledge of the PHP API and object oriented programming concepts, I learned something very interesting about interfaces in PHP. You probably know that an interface can extend another interface like a class does with another class. But, did you know that an interface can extend multiple interfaces in a row unlike classes? I didn't until today. I just discovered thanks to one of the best contributors of the Symfony2 framework: Bernhard Schussek aka WebMozart (love his nickname by the way :) ).

What are interfaces?

Interfaces are kind of abstract classes that only declare the public API. The public API is composed of constants and public methods. Unlike an abstract class that can contain abstract and concrete methods (private, protected and public altogether), an interface can only contain public methods signatures as shown below.

interface UserInterface
{
    const FLAG_SUPER_ADMIN = 0;
    const FLAG_ADMIN = 1;
    const FLAG_USER  = 2;
 
    public function getUsername();
 
    public function getPassword();
 
    public function getSalt();
}

In the above example, we declare a UserInterface interface that defines three constants and methods. If an abstract or concrete class implements this interface, it will have to implements the three methods with their body.

The role of an interface is to force a class to implement a certain public API provided by the interface. The interface can also give a type to a class. For example, if we define a concrete LdapUser class that implements the UserInterface, an object of the LdapUser class will be typed as with both LdapUser and UserInterface types.

var_dump($user instanceOf LdapUser); // returns true
var_dump($user instanceOf UserInterface); // returns true

An interface can also extend another interface as it's explained in the following section.

Single Interface Inheritance

Interfaces behave like real abstract and concrete classes when it comes to inheritance. An interface can extend another interface as you can see in the following example.

interface AdvancedUserInterface extends UserInterface
{
    function isActivated();
 
    function hasExpired();
}

If a LdapUser class implements the AdvancedUserInterface, an object of this class will be typed with LdapUser, UserInterface and AdvancedUserInterface.

var_dump($user instanceOf LdapUser); // returns true
var_dump($user instanceOf UserInterface); // returns true
var_dump($user instanceOf AvancedUserInterface); // returns true

Let's talk about the most unknown part of interfaces: multiple interfaces inheritance.

Multiple Interfaces Inheritance

Unlike classes, an interface can in fact extend several interfaces at a time. Suppose, we would like the AdvancedUserInterface to extend the UserInterface and also to have some ACL features like groups and roles. We can of course put ACL methods directly in the AdvancedUserInterface interface but we would probably like to attach the same ACL methods to another user object that belongs from another class, which doesn't implement the AdvancedUserInterface but only the UserInterface interface.

The best solution is to create a dedicated AccessControlListInterface and make the AdvancedUserInterface extend both UserInterface and AccessControlListInterface interfaces.

interface AccessControlListInterface
{
    function getGroups();
 
    function getRoles();
 
    function addGroup($group);
 
    function addRole($role);
 
    function hasRole($role);
}
 
interface AdvancedUserInterface extends UserInterface, AccessControlListInterface
{
    function isActivated();
 
    function hasExpired();
}

Finally, the LdapUser class can only implement the AdvancedUserInterface interface to take benefit from the UserInterface and AccessControlList APIs.

class LdapUser implements AdvancedUserInterface, Iterator
{
    // Implement UserInterface methods 
 
    // Implement AdvancedUserInterface methods
 
    // Implement AccessControlListInterface methods
 
    // Implement Iterator methods
}

Finally, the LdapUser class implements all methods from the three previous interfaces plus the Iterator interface methods.

Commentaires

Posté par Geoffrey - Il y'a environ about 1 year

makes sense, since you can implement several interfaces in a single class (as in class FooBar implements Foo, Bar {})

Posté par Timothée - Il y'a environ about 1 year

Good to know and well explained :)

What if two interfaces share the same method prototype ? Or worst, what if they share the declaration of the same constant with different values ?

I know it's tricky, but it's one of the reasons why we can't extend two classes, for the best, don't get me wrong.

Posté par Bernhard - Il y'a environ about 1 year

@Timothée: In this case PHP will complain.

"Fatal error: Cannot inherit previously-inherited or override constant FOO from interface B"

"Fatal error: Can't inherit abstract function B::foo() (previously declared abstract in A)"

Posté par Serge HARDY - Il y'a environ 11 months

Hello,

One of the main advantages of the use of interface is the "interface segregation principle" (the I in SOLID) which forces you to make dependencies at the highest level

Laisser un commentaire

Votre commentaire