Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

Interfaces

Keep on Learning!

If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.

Start your All-Access Pass
Buy just this tutorial for $6.00

Notice, AbstractShipStorage unlike AbstractShip, doesn't actually have any logic in it:

... lines 1 - 2
abstract class AbstractShipStorage
{
abstract public function fetchAllShipsData();
abstract public function fetchSingleShipData($id);
}

All it does is have a contract that guarantees anything that extends this has these two functions. It turns out that when you have an abstract class like this that only contains abstract functions and no real code, well it's the perfect opportunity to use an Interface.

An interface works just like an abstract class and here's how it looks. To start, we need to rename our class to ShipStorageInterface since this more closely matches what it is. And instead of abstract class it's now labeled as an interface:

... lines 1 - 2
interface ShipStorageInterface
... lines 4 - 9

Get it?

As soon as you do that you no longer need abstract in front of all the functions, but these work the same:

... lines 1 - 2
interface ShipStorageInterface
{
public function fetchAllShipsData();
public function fetchSingleShipData($id);
}

On the AbstractShipStorage file in the tree, go to "Refactor" and click to "Rename" our file to ShipStorageInterface. I really like the consistency. And of course update our require line for this file in bootstrap.php:

20 lines bootstrap.php
... lines 1 - 14
require_once __DIR__.'/lib/Service/ShipStorageInterface.php';
... lines 16 - 20

Implment an Interface

Stepping back and looking at ShipStorageInterface. I want you to think of this as acting just like an abstract class with two functions that need to be filled in. An important difference is that you don't extend interfaces. Instead, we'll use a new keyword called implements and our updated class name ShipStorageInterface:

... lines 1 - 2
class JsonFileShipStorage implements ShipStorageInterface
... lines 4 - 32

This new line says that the JsonFileShipStorage must include the functions inside of ShipStorageInterface.

If I deleted fetchAllShipsData() you can see that immediately PhpStorm is telling me:

Hey buddy, you need to implement fetchAllShipsData().

So I'll comply and undelete that.

Update PdoShipStorage to implement ShipStorageInterface:

... lines 1 - 2
class PdoShipStorageInterface implements ShipStorageInterface
... lines 4 - 33

Time to head over to ShipLoader and change the AbstractShipStorage type hint to ShipStorageInterface which is our way of saying that we don't care what class is passed here as long as it has the two methods that are in ShipStorageInterface:

... lines 1 - 2
class ShipLoader
{
... lines 5 - 6
public function __construct(ShipStorageInterface $shipStorage)
... lines 8 - 58
}
... lines 60 - 61

That's the only thing we care about. Well, that and getting to dinner on time.

Over in the Container we can also update the @return statement. It doesn't affect anything really, but it's a good practice to keep it updated. Back to the browser and refresh! Everything still works perfectly.

Interfaces are just like abstract classes that don't have any functionality, they only contain abstract functions. If you try to add a real function inside of an interface you can see that PhpStorm highlights it with the message:

Interface method can't have body.

And it will freak out when we refresh.

What's so Great about an Interface?

The purpose of an interface is to allow you to make your code very generic since you're not requiring a concrete class just an interface. Why do interfaces exist? Sheesh you ask a lot of questions! Well, the answer is that in PHP you can only extend one base class but you can implement many interfaces. I'm not going to go into detail on ArrayAccess interface which comes from the core of PHP but this is what it looks like to implement multiple interfaces. Allowing multiple interfaces makes them a bit more flexible than abstract classes.

Another cool thing about interfaces and abstract classes is that they become directions on what all ship storage objects must look like. So if someone in the future needed to create a new ship storage object that loaded things from say XML, all they would need to do is created a class that implements this interface and boom you're being told exactly what methods that XML ship storage class has to have.

Interfaces Document what you need to do

This is also our opportunity to add really good documentation on these. We can label this one as an integer that should return an array of data. You could even go further and say "Returns an array of ship arrays, with keys id, name, weaponPower, defense.":

... lines 1 - 2
interface ShipStorageInterface
{
/**
* Returns an array of ship arrays, each with the following keys:
... lines 7 - 11
*
* @return array
*/
public function fetchAllShipsData();
/**
* Returns the single ship array for this id (see fetchAllShipsData)
*
* @param integer $id
* @return array
*/
public function fetchSingleShipData($id);
}

Adding as many details as possible here is good, that way if someone implements this interface later they'll know exactly what to put in their classes.

Interfaces in third-party Libraries

One last note about interfaces, they are a bit more advanced. It's not that they are difficult, but in your code you may not find many reasons to create these. How often is it that you need to make a class like ShipLoader and make it so flexible to work with a PDO ship storage or a Json file ship storage? In most apps you know which one way you are loading data. So it's actually ok to hardcode the implementation here with a concrete class like PdoShipStorage.

If you're creating a reusable library that you are going to share with the world then you will need a lot of flexibility and interfaces would be a good thing to use.

You may not create very many interfaces, but there is a very good chance that you will use a lot of them. For example, you might want to use a third party library in your project and their documentation will say:

"If you want to create a custom ship storage object, then you will need to implement this interface that comes with the library."

So you will create your own custom class, implement the library's interface which then tells you which methods to fill in.

Understanding interfaces is really important because you will probably be implementing a lot of them.

Alright, that's it and I hope you find abstract classes, interfaces and inheritance as cool as I do!

See ya next time!

Leave a comment!

11
Login or Register to join the conversation

Hi,

there is a problem with the last challenge...you can't finish the 3rd course because it does redirect you to https://symfonycasts.com/sc... after you finish.

Reply

Hey g0spon

That's a bit odd. It's working fine for me. What happens if you go to the main page of the course and then click directly on the challenge you need to accomplish? (in the chapters list)
https://symfonycasts.com/sc...

Cheers!

Reply

Hi, sorry for the late reply. I didn't explain the problem well. I watched all chapters and did all challenges right, but after the last challenge in chapter 10 after I finish and press "Finish the Course" it redirects me to the course 4 without course 3 badge and certificate. When I go to course 3 is says 93% complete even all challenges are green and i watched all videos. I'm obviously missing something.

Reply

g0spon I was wrong. I was checking episode by episode and realised that video for chapter 8 and 9 had no tick, probably i skiped videos before the end. Now it's all right. Sorry for bothering you.

Reply

No problem man, you're welcome

Reply
Jose carlos C. Avatar
Jose carlos C. Avatar Jose carlos C. | posted 4 years ago

Awesome course, the best I've seen of PHP OOP. I don't know if it would be possible to do similar with Javascript with ES7

Reply

Hey Jose carlos C.!

Woohoo! Glad you loved it - thanks for the super nice note :).

About the JavaScript & ES7 stuff... I think (hope) that we've already done it - https://symfonycasts.com/tr... - specifically the second tutorial - https://symfonycasts.com/sc.... There's actually not a TON of new features in ES7 and ES8 - so the ES6 tutorial is really the big, important stuff (then you can quickly google ES7 and ES8 to learn about a few nice, handy new functions).

I hope that's what you're looking for. Because, honestly, I LOVE that entire series :).

Cheers!

1 Reply
Jose carlos C. Avatar
Jose carlos C. Avatar Jose carlos C. | weaverryan | posted 4 years ago

Perfect, I'll see the course in a while, thank you.

Reply

This is a fantastic course about OO, as an old procedural PHP developer, even if I've touched Symfony, and OO, I find a lot a good things and practices in here !
Simple question : In ShipStorageInterface, we define the return type in the PHPDoc for the fetchSingleShipData as "array()". What does the parenthesis mean exactly ?

Reply

Hey abyssweb

Thanks for your kind words, we love to hear that our tutorials are useful and fun for our users :)

About the extra parenthesis, nice catch! Actually they are not needed, I believe it came from the habit of defining empty arrays

Cheers!

Reply

Hey Matt!

Ah, awesome! Thanks for the nice words and dropping the real-world example! When you start thinking like this, then yea, you've definitely "grasped" the purpose of interfaces :)

Cheers!

Reply
Cat in space

"Houston: no signs of life"
Start the conversation!

userVoice