Back when I was a young programmer, I didn’t really understand the point of class interfaces.1 Frequently documentation talked about what an interface did, without hinting at where or why it would be used. The simplest explanation I could find was that interfaces provide a way for the programmer to swap one class for another, with some assurances that the classes are compatible - but how often would you actually do that?

As a quick technical refresher, an interface contains no code other than some method signatures.2 Any class implementing an interface will be guaranteed at run time to implement these methods. How the implementation works is up to the implementing class.

There’s some interesting points that documentation usually doesn’t mention, but for the purposes of this post, I’ll focus on the first one.

  1. When using type hinting in your methods, you can use the interface of a class, instead of the class.
  2. Classes can implement more methods than just those defined in the interface.
  3. Classes can implement more than one interface.
  4. Interfaces can extend other interfaces.

Almost every discussion I’ve seen on this subject uses the example of swapping out one SQL database like MySQL, for another, like Microsoft SQL. I dislike this example, because it makes interfaces sound like something reserved for the deep parts of the code, to be used sparingly in specific situations, rather than as common tool. Most of the classes I write now implement an interface to make unit testing easier. Rather than requiring the specific class of object you are passing to the method being tested, you can use a different class optimised for testing that implements the same interface.

<?php
class UserHelper {
  /**
   * Helper method to concatenate the first and last name together with a space.
   *
   * @param DatabaseUser $user
   *   A user object from the database.
   *
   * @return string
   *   The users full name.
   */
  public static function getFullName(DatabaseUser $user) {
    return $user->getFirstName() . ' ' . $user->getLastName();
  }
}

In the above code, testing getFullName() requires building a DatabaseUser object. Depending on how that class works, we might need to fire up an entire SQL database, just to test one method! Instead, if we change the signature of getFullName() to require the interface that DatabaseUser implements, we can build a mock class with only the interfaces we care about.

<?php
/**
 * Interface that must be implemented for all user objects.
 */
interface UserInterface {
  /**
   * Retrieve the first name of the user.
   *
   * @return string
   *   First name of user. This is never empty.
   */
  public function getFirstName();

  /**
   * Retrieve the last name of the user.
   *
   * @return string|null
   *   Last name of user, if known.
   */
  public function getLastName();
}

/**
 * User class that backs on to the default database.
 */
class DatabaseUser implements UserInterface {
  // ... 
}

/**
 * In memory user class. Used only for testing.
 */
class MockUser implements UserInterface {
  // ...
}

/**
 * The updated UserHelper class.
 */
class UserHelper {
   /**
    * The only difference is that the type hint is changed from DatabaseUser to UserInterface.
    */
   public static function getFullName(UserInterface $user) {
     return $user->getFirstName() . ' ' . $user->getLastName();
   }
}

Now we have two classes that are accepted by UserHelper::getFullName(). When we’re testing, we can pass in a MockUser object, and use a DatabaseUser object when running the actual program. The method getFullName() now accepts and works with any object implementing UserInterface.

Later, if we want another user class implementing the same interface, such as LdapUser, we can be assured that all our existing methods that accept UserInterface will work with the new class. In a later post, I’ll discuss where the other commonly undocumented points come in to play.


  1. Interface documentation for PHP, Java, C#, and C++ ↩︎

  2. A method signature is a short hand for saying the requirements of calling a method. This includes the types and number of arguments, and optionally as of PHP7, the returned type. ↩︎