- Bypassing a Slow Composer Repository
- Slow Composer Followup
- Getting Started with PHP Composer and Magento
- PHP: The Right Way; A Review
- PSR and Code Style Guidelines
- Sellvana: Developer’s Alpha Review
- PHP Primer: Type Hints
- PHP Primer: Namespaces
- PHP Primer: Interfaces
- Design Patterns, PHP, and Magento
- PHP-FPM and File Permissions
- Why did the Elephant join the Guild?
On a “what they do level”, interfaces are pretty simple. Consider the following PHP class
<?php
class Foo extends Bar
{
}
We’re all (I hope!) familiar with the idea of one class extend
ing another. Interfaces allows a PHP class to do something else. With interfaces, a PHP class may also implement something
<?php
class Foo extends Bar implements BazInterface
{
}
In the above code sample, the class Foo
implements the interface named BazInterface
.
As to what it means to implement an interface, consider this simple program
#File: interface-example.php
<?php
interface AlohaInterface
{
}
class Hello implements AlohaInterface
{
public function sayHello()
{
echo 'Hello',"\n";
}
}
$cmd = new Hello;
$cmd->sayHello();
You’ll notice in this program we’ve defined an interface named AlohaInterface
(interfaces don’t need the word Interface
in their name, it’s just a common convention), and our class Hello
implements this interface. If we run this program from the command line or a browser, we’ll see the following output
$ php interface-example.php
Hello
So far, the interface hasn’t had any measurable impact on our program. However, if we change the interface
#File: interface-example.php
//...
interface AlohaInterface
{
public function sayHello();
public function sayGoodbye();
}
//...
and try running our program again, we’ll get the following error
PHP Fatal error: Class Hello contains 1 abstract method and must therefore
be declared abstract or implement the remaining methods
(AlohaInterface::sayGoodbye) in
interface-example.php on line 15
If this is gobbledygook to you, don’t worry, it’s gobbledygook to everyone at first. The key to understanding these sorts of error messages is to understand what an interface is for.
What is an Interface
Think of an interface like a stencil for your class. When you implement an interface, it’s sort of like telling PHP
Hey, I’m making this sort of class, so make sure I actually do the job right
When you define an interface, you’re making the stencil for your class. When you said
#File: interface-example.php
//...
interface AlohaInterface
{
public function sayHello();
public function sayGoodbye();
}
//...
You were telling PHP that any class that implements the AlohaInterface
must define both a sayHello
method, and a sayGoodbye
method. You’ll notice the methods above don’t have any body. That’s because they’re not real methods, they’re just us telling PHP what a class should look like. Because they’re “not real”, we call these abstract methods.
So, going back to our error message
PHP Fatal error: Class Hello contains 1 abstract method and must therefore
be declared abstract or implement the remaining methods
(AlohaInterface::sayGoodbye) in
interface-example.php on line 15
PHP was telling us that, because our Class Hello
has an interface with an abstract method
, that we need to define (the error message uses the word implement
) the abstract method (AlohaInterface::sayGoodbye
).
So lets define it!
#File: interface-example.php
//...
class Hello implements AlohaInterface
{
public function sayHello()
{
echo 'Hello',"\n";
}
public function sayGoodbye()
{
echo 'Goodbye',"\n";
}
}
//...
Try running the program with the above class definition in place, and everything should be back to normal.
$ php interface-example.php
Hello
That’s all an interface is — it’s a way to ensure a class has a certain number of methods. As usual, this is a quick primer: The PHP manual has the full story on interfaces, as well as their close cousins, abstract classes.
Why Interfaces
We started this article saying that the “what they do” of interfaces was relatively simple. The “why” of interfaces is a harder story to tell. If you’re coming from shell scripting, or even certain sorts of application development, you’re probably wracking your brain for a reason to use interfaces.
Hint: The reason you’re having so much trouble thinking of a reason is — there isn’t one.
Interfaces only make sense if you’re building programmatic systems for other people to use. They allow a system developer to specify exactly how certain classes need to behave. Magento 1 contained numerous examples of this. Consider the shipping carrier classes
class Mage_Shipping_Model_Carrier_Flatrate
extends Mage_Shipping_Model_Carrier_Abstract
implements Mage_Shipping_Model_Carrier_Interface
Whenever you wanted to implement a new shipping carrier in Magento 1, you’d start with a class that implemented the Mage_Shipping_Model_Carrier_Interface
. This forced you to define the same methods that the other shipping carrier classes had. This, in turn, meant the Magento systems code for fetching carrier rates could safely assume your carrier object had the same methods as the other carrier objects.
Interfaces are a way for programmers to communicate indirectly. By looking at the interfaces defined in a system, you’ll start to get an idea of what you’ll need to do to customize a system, without needing to talk to the original developer or going through a tutorial.
Java Legacy
Interfaces are another one of those features we get from Java, and that play a slightly different role in a dynamic language like PHP. That said, I think they’re one of the features that makes sense in PHP and can help avoid a certain class of bugs.
Consider the following pseudo-code
function someFunction ($object, $flag)
{
$object->doTheThing();
if($flag)
{
$object->doTheOtherThing();
}
}
In the above code, if $object
doesn’t implement a doTheThing
method — calling someFunction
will always fail. However, if $object
does implement doTheThing
but doesn’t implement doTheOtherThing
, the program might not fail, depending on the value of $flag
. This mean it’s possible that a developer will ship this code to production with a lurking bug. You might say tests could catch this, and you’d be right, but I could also say there are large swaths of the industry where tests don’t catch this. This is where interfaces, combined with PHP type hints, can save us
interface SomeInterface
{
public function doTheThing();
public function doTheOtherThing();
}
@highlightsyntax@php
function someFunction (SomeInterface $object, $flag)
{
$object->doTheThing();
if($flag)
{
$object->doTheOtherThing();
}
}
By using an interface as a type hint, we can ensure no one calls someFunction
with an object that doesn’t have all the needed methods.
Regardless of what you think of interfaces and programming by contract, they’re part of the PHP world now. Knowing how to read code that uses interfaces will be a vital skill for anyone working with modern PHP systems.