Hello guys I was creating a package and i was trying to implement a dependency injection on my class without success. I followed all the instruction for do it work. I'm getting crazy on it.
When i try to call
Player::team_players(2);
it throw me an error:
Argument 1 passed to Team\Player\Player::__construct() must be an
instance of Team\Player\StatusPlayerInterface, none given, called in
C:\wamp\www\ultima\workbench\team\player\src\Team\Player\PlayerServiceProvider.php
on line 35 and defined
I created my class Player.php
<?php namespace Team\Player;
use Team\Player\Models\User;
use Team\Player\Models\Team;
use Team\Player\Models\Fighter;
use Team\Player\StatusPlayerInterface;
use DB;
class Player {
protected $player;
function __construct(StatusPlayerInterface $player) {
$this->player = $player;
}
public function team_players($team_id) {
return $player->team($team_id);
}
}
StatusPlayerInterface.php
<?php namespace Team\Player;
interface StatusPlayerInterface {
public function team($team_id); // active - retired - injured by team id
}
Active.php
<?php namespace Team\Player;
use Team\Player\Models\User;
use Team\Player\Models\Team;
use Team\Player\Models\Fighter;
/**
*
*/
class Active implements StatusPlayerInterface
{
protected $user;
protected $team;
protected $fighter;
function __construct(User $user,Team $team,Fighter $fighter)
{
$this->user = $user;
$this->team = $team;
$this->fighter = $fighter;
}
public function team($team_id)
{
return $fighters = $this->fighter->with('user')->where('team_id',$team_id)->active()->Confirmed()->get();
}
}
PlayerServiceProvider.php
public function register()
{
$this->app->bind('Team\Player\StatusPlayerInterface','Team\Player\Player'); // bind the interface
$this->app['player'] = $this->app->share(function($app)
{
return new Player; // line 35
});
$this->app->booting(function()
{
$loader = \Illuminate\Foundation\AliasLoader::getInstance();
$loader->alias('Player', 'Team\Player\Facades\Player');
});
}
EDIT:
What i'm trying to do is to follow a principle that Jeffrey Way suggested to follow. It say
Entities should be open for extension but close for modification.
I 2 others classes that implements StatusPlayerInterface and of course change only the query on the function team()
Active // on the example
Retired
Injured
Then i have the main class Player and with the method team_players it should automatically call the function team of the instance called. this method is used for don't do
class Player {
....
function team_player($team_id,$status) {
if (is_a($status) == "Active") {
$fighters = $this->fighter->with('user')->where('team_id',$team_id)->active()->Confirmed()->get();
} elseif(is_a($status) == "Retired") {
$fighters = $this->fighter->with('user')->where('team_id',$team_id)->retired()->Confirmed()->get();
}
// ecc
}
}
but i can throw pass the interface to the constructor and return just the function team of the interface, because the interface is like a contract so it can trust that exist that function. But the problem is that i cannot find a way for pass that interface on the constructor.
Your constructor here is waiting for a $player:
class Player {
...
function __construct(StatusPlayerInterface $player) {
$this->player = $player;
}
}
So your ServiceProvider should be passing one to it in line 35:
return new Player; // line 35
I can see you tried to use IoC to do that for you:
$this->app->bind('Team\Player\StatusPlayerInterface','Team\Player\Player');
But you have two problems,
1) Team\Player\Player doesn't implements Team\Player\StatusPlayerInterface and it must. But Active class does implements, shouldn't you be using it?
2) I'm not sure that the IoC will be effective at this point of the code, would have to ask Taylor Otwell himself.
But this is something you can do:
public function register()
{
$this->app['player'] = $this->app->share(function($app)
{
return new Player(new Team\Player\Player);
//// OR
return new Player(new Team\Player\Active);
});
$this->app->booting(function()
{
$loader = \Illuminate\Foundation\AliasLoader::getInstance();
$loader->alias('Player', 'Team\Player\Facades\Player');
});
}
Your Player class would have to implement the StatusPlayerInterface:
class Player implements StatusPlayerInterface {
}
But I'm not sure if it is supposed to, so, look, those are suggestions, I not aware of what you're doing exactly with your package, so I'm just pointing what I'm seeing is wrong on it, okay?
EDIT
For example, you are constructing your Player class already passing a Player Status, right? But how would you swap different statuses if the constructor, the way you're building, will only receive the one you are passing via your ServiceProvider? In this case the IoC container will not help you, because you should be able to instantiate that same class with 3 differents statuses: active, retired and injured.
You can create a setPlayerStatus() method, to change it in the during a request, of course, but as I hope you can see, before building the whole package, you have first to think a lot about your architecture and then write your code based on it, always remembering that the IoC container has its boundaries and there are some resolutions it will not solve, just because they are problems on your architecture.
EDIT 2
You don't really pass an interface to a constructor. You pass a concrete object of a concrete class that implemented that interface.
Look at the error again, it says 3 important things
Argument 1 passed to Team\Player\Player::__construct()
must be an instance of Team\Player\StatusPlayerInterface,
none given
So you need to instantiate Player
return new Player;
with something:
return new Player(new Active);
That's all you need to make it work, really. The error will go away. But you need this package to work too and I'm afraid this is not enough.
As I said before, if the IoC could work here, how could you make it send the correct implementation of Active, Retired or Injured, at the time you need? I see two options:
1) Call
$this->app->bind('Team\Player\StatusPlayerInterface','Team\Player\Active');
$this->app->bind('Team\Player\StatusPlayerInterface','Team\Player\Retired');
$this->app->bind('Team\Player\StatusPlayerInterface','Team\Player\Injured');
every time you need one of them, which is bad.
2) Change the architecture to keep you in the SOLID track, in the case the Open Closed Principle.
Take a read on the Factory Design Pattern, it might help you out with this achitecture. This is an answer about it: What is a Factory Design Pattern in PHP?.
Related
First of all sorry for not explaining this correctly I have hard time finding my words.
I been following a laracast php tutorial about The Decorator Pattern. It explain how you can build up an object at run time.
From the tutorial
interface CarService{
public function getCost();
}
class Service implements CarService {
public function getCost()
{
return 20;
}
}
class OilChange implements CarService{
protected $carService;
public function __construct(CarService $carService)
{
$this->carService = $carService;
}
public function getCost()
{
return 10 + $this->carService->getCost();
}
}
class Tyre implements CarService {
protected $carService;
public function __construct(CarService $carService)
{
$this->carService = $carService;
}
public function getCost()
{
return 20 + $this->carService->getCost();
}
}
The main bit im focus on is this
$carService = new Tyre(new OilChange(new Service()));
echo $carService->getCost();
//output 50
As the tutorial describes having this code helps minimize repetition of code in the classes by introducing an interface. I understand why its a good approach but there is a bit that wasnt explained in the tutorial. if you have 100 types of services there is a possibility of 10,000 different class initiation such as this one $carService = new Tyre(new OilChange(new Service())); or this one $carService = new OilChange(new Service());
As you can see this will quickly add up if you have to initiate classes base on each possible outcome of car service (10,000).
how could I solve this so that it knows exactly what class/type of service to initialize? clearly having a long if statement for every single condition which would be 10,000 is not correct.
I would like to test some of my services, but I can not find any example on Laravel's website:
https://laravel.com/docs/5.1/testing
They show how to test simple classes, entities, controllers, but I have no idea how to test services. How is it possible to instantiate a service with complex dependencies?
Example service:
<?php
namespace App\Services;
// Dependencies
use App\Services\FooService;
use App\Services\BarService;
class DemoService {
private $foo_srv;
private $bar_srv;
function __construct(
FooService $foo_srv,
BarService $bar_srv
) {
$this->foo_srv = $foo_srv;
$this->bar_srv = $bar_srv;
}
// I would like to test these two functions
public function demoFunctionOne() {
// ...
}
public function demoFunctionTwo() {
// ...
}
}
The quickest thought that might come to the mind is to create a copy of those Service classes, but that could grow so big. This is why there's MockObject in PhpUnit.
To achieve this mock and use it as a replacement to the service class you may need to resolve it in Laravel service container.
Here is how it will look:
class DemoServiceTest extends TestCase
{
// Dependencies
use App\Services\FooService;
use App\Services\BarService;
use App\Services\DemoService;
public function testDemoFunctionOne()
{
$foo_srv = $this->getMockBuilder(FooService::class)
//->setMethods(['...']) // array of methods to set initially or they return null
->disableOriginalConstructor() //disable __construct
->getMock();
/**
* Set methods and value to return
**/
// $foo_srv->expects($this->any())
// ->method('myMethod') //method needed by DemoService?
// ->will($this->returnValue('some value')); // return value expected
$bar_srv = $this->getMockBuilder(BarService::class)
// ->setMethods(['...']) // array of methods to set initially or they return null
->disableOriginalConstructor()
->getMock();
/**
* Set methods and value to return
**/
// $bar_srv->expects($this->any())
// ->method('myMethod') //method needed by DemoService?
// ->will($this->returnValue('some value')); // return value expected
$demo_service = new DemoService($foo_srv, $bar_srv);
$result = $demo_service->demoFunctionOne(); //run demo function
$this->assertNotEmpty($result); //an assertion
}
}
We are creating new mock for both FooService and BarService classes, and then passing it when instantiating DemoService.
As you can see without uncommenting the commented chunk of code, then we set a return value, otherwise when setMethods is not used, all methods are default to return null.
Let's say you want to resolve these classes in Laravel Service Container for example then you can after creating the mocks, call:
$this->app->instance(FooService::class, $foo_srv);
$this->app->instance(BarService::class, $bar_srv);
Laravel resolves both classes, feeding the mock classes to any caller of the classes So that you just call your class this way:
$demoService = $this->app->make(DemoService::class);
There are lots of things to watch out for when mocking classes for tests, see sources: https://matthiasnoback.nl/2014/07/test-doubles/, https://phpunit.de/manual/6.5/en/test-doubles.html
I have an example using the service you gave below. The basic idea is that for dependencies you just assume that they'll work as expected and create mocks for them.
Mocks are special classes that pretend to be a different class, but don't really do anything unless you tell them to. For example, say we have a class called UserCreationService that takes a few arguments required to create a new user. This class depends on some things like a Mailer class to send a registration mail, and a UserRepository class to save the user to the database. It also does a lot of validation of the user, and we'd like to check lots and lots of edge cases for all the different possible arguments to create a user.
In this example do we really want to check that the user was saved to the database? Do we want to check for sure that the mail was really sent? We could do that, but it would take a very long time to run all our test cases. Instead we just assume that the classes our UserCreationService depends on will just do their job and we create mock classes for the dependencies. We would create mocks for the Mailer and UserRepository and just tell the test that we expect some methods to be called (like sendRegistrationMail for the Mailer) and concentrate on the logic contained in our class.
// This is the class we want to test
class UserCreationService {
// The dependencies
private $userRepository;
private $mailer;
public function __construct($userRepository, $mailer)
{
$this->userRepository = $userRepository;
$this->mailer = $mailer;
}
public function create($name, $email, $location, $age)
{
$user = new User();
// do some complex validation here. This is what we want to test
$this->validateName($name);
$this->validateEmail($email);
$this->validateLocation($location);
$this->validateAge($age);
// then call our external services
$this->userRepository->save($user);
$this->mailer->sendRegistrationMail($user);
}
}
// This is a sample test for the above class using mocking
class UserCreationServiceTest extends TestCase
{
public function testValidUserWillBeSaved() {
// The framework allows using argument tokens.
// This just means an instance of *any* user class is expected
$anyUserToken = Argument::type(User::class);
// The prophesize method creates our mock for us
// We then define its behavior
$mailer = $this->prophesize(Mailer::class);
// Let our test know we expect this method to be called
// And that we expect an instance of a user class to be passed to it
$mailer->sendRegistrationMail($anyUserToken)->shouldBeCalled();
$userRepo = $this->prophesize(UserRepository::class);
$userRepo->save($anyUserToken)->shouldBeCalled();
// Create our service with the mocked dependencies
$service = new UserCreationService(
$userRepo->reveal(), $mailer->reveal()
);
// Try calling our method as part of the test
$result = $service->create('Tom', 'tom#test.com', 'Ireland', 24);
// Do some check to see that the result we got is what we expected
$this->assertEquals('Tom', $result->getName());
}
}
This is something similar, but specific to the example you gave above:
use PHPUnit\Framework\TestCase;
class DemoServiceTest extends TestCase
{
public function testDemoFunctionOne()
{
// These are the variables that will be passed around the service
$sampleId = 1;
$myModel = new MyModelClass();
// Set up a mock FooService instance
$fooMock = $this->prophesize(FooService::class);
// Tell the test that we expect "findFoo" to be called and what to return
$fooMock->findFoo($sampleId)->willReturn($myModel);
// Set up a mock BarService instance
$barMock = $this->prophesize(BarService::class);
// Tell the test we expect "save" to be called and what argument to expect
$barMock->save($myModel)->shouldBeCalled();
// Create an instance of the service you want to test with the mocks
$demoService = new DemoService($fooMock->reveal(), $barMock->reveal());
// Call your method, get a result
$result = $demoService->demoFunctionOne($sampleId);
// Check that the result is what you want
$this->assertEquals($myModel, $result);
}
}
I'd have a look at Laravel specific stuff here and prophecy here
i am not sure about the context you have there, but I will try to have an answer for you based on an example.
Imagine that you want to test a payment gateway from a payment provider.
My approach is to make 2 payment gateways extend something like this interface:
<?php
namespace App\Payment;
interface PaymentGateway
{
public function charge($amount, $token);
public function getTestToken();
....
}
then i will create the real payment gateway that is used in the controllers or where ever you need it and for the tests the 'fake' payment and this is an exact copy of the real one but with dummy data. This you can use on the tests because it is faster and it is a 1to1 copy of the real. I think if the service it self works or not via the internet it is outside of the testing scope, at least for now. So you will end up with something like this:
<?php
namespace App\Payment;
class FakePaymentGateway implements PaymentGateway
{
private $tokens;
const TEST_CARD_NUMBER = '1234123412341234';
public function __construct()
{
$this->tokens = collect();
}
public function getTestToken()
{
return 'fake-tok_'.str_random(15);
}
....
}
and the real one:
<?php
namespace App\Payment;
class PaypalPaymentGateway implements PaymentGateway
{
...
public function __construct(PayPal $PaypalClient)
{
...
}
public function charge($amount, $token)
{
...
}
....
}
so i think in your case, when you have complex dependencies, you have to fake all of that, depending on the case, into the fake service.
the tests will look like this for the fake service:
<?php namespace Tests\Unit\Payment;
use App\Payment\FakePaymentGateway;
use Tests\TestCase;
class FakePaymentGatewayTest extends TestCase
{
use PaymentGatewayContractTests;
protected function getPaymentGateway()
{
return new FakePaymentGateway;
}
...
}
for the real one like this:
<?php namespace Tests\Unit\Payment;
use App\Payment\PaypalPaymentGateway;
use Tests\TestCase;
/**
* #group integration
*
* ./vendor/phpunit/phpunit/phpunit --exclude-group integration
*/
class PaypalPaymentGatewayTest extends TestCase
{
use PaymentGatewayContractTests;
protected function getPaymentGateway()
{
return new PaypalPaymentGateway(....);
}
...
}
for the real one you should ignore it in the phpunit when running to make the tests faster and not depending of the internet connection and so on. It is also nice to have in the tests suite, when changes from the service occur.
You will end up having a better understanding of the dependencies and maybe you will also do some refactoring. Anyway i guess it is a lot to work but when the real implemanation will change you can see that also from the tests and change it faster.
I hope my answer will help you in your service tests :).
Maybe you can use Mockery to mock the Dependencies.
We are doing this for our cases and it does the work, yet. :)
Especially the partial Mock is doing fine here.
https://laravel.com/docs/5.8/mocking#mocking-objects
Reading about laravel bindings,I understand $this->app->bind,$this->app->singleton and $this->app->instance because they are almost the same.
But $this->app->when is a little bit tricky for me.
In laravel example
$this->app->when('App\Http\Controllers\UserController')
->needs('$variableName')
->give($value);
In my understanding it injects some primitive value and the App\Http\Controllers\UserController is the alias of the object that will be binded.But where is the object?
Can anyone explain? Thank you for your help.
Contextual binding does not work on variable names, but on types. It is used to provide different implementations for interfaces to consuming classes or functions, depending on the context. In fact, you can actually read the method calls and it does exactly what you'd expect. To elaborate on this, I'll take the example of the documentation and adapt it slightly:
$this->app->when(Worker::class)
->needs(Money::class)
->give(function () {
return new Dollar();
});
$this->app->when(Boss::class)
->needs(Money::class)
->give(function () {
return new Cheque();
});
In this example, Money is an interface and Dollar as well as Cheque are implementations of the interface. The example literally means that if you typehint Money on a Worker class, it will resolve to an instance of Dollar while it will resolve to Cheque on a Boss class. To illustrate, here the implementations and the results:
interface Money
{
public function getAmount();
}
class Dollar implements Money
{
public function getAmount()
{
return 1;
}
}
class Cheque implements Money
{
public function getAmount()
{
return 100000;
}
}
And now we typehint the Money interface to see what we'll get:
class Worker
{
public function __construct(Money $money)
{
echo $money->getAmount(); // prints '1'
}
}
class Boss
{
public function __construct(Money $money)
{
echo $money->getAmount(); // prints '100000'
}
}
It means that if a class of UserController is instantiated and it needs a variable with the name $variableName Larvel will automatically resolve this variable with the given value and you don't have to provide it.
For example:
$value = "Sven"
$this->app->when('App\Http\Controllers\UserController')
->needs('$userName')
->give($value);
This would insert the value 'Sven' into the UserController whenever it needs the variable with the name $userName
In other words, if you had a function like public function __construct(Request $request) Laravel knows what to insert because it knows that a Request object is expected. When you use a function like public function __construct($name) Laravel has no clue what to insert here, essentially you tell Laravel how to resolve this variable with the bindings.
This is an example of primitive binding, for Contextual binding see the answer of #Namoshek
In case anyone finds this thread, trying to achieve contextual binding on an app()->make() or similar situation:
use Closure;
use Illuminate\Container\Container;
class FooBar {
public function doSomething(): void
{
//...do your thing
$this->getFooObject();
}
private function getFooObject(): FooAbstract
{
$class = FooAbstract::class;
$buildStack = static::class;
app()->beforeResolving(
$class,
Closure::bind(
function () use ($buildStack) {
$this->buildStack[] = $buildStack;
},
app(),
Container::class,
)
);
return app($class);
}
}
In the above example laravel's app container is bind as $this to this closure. Since the buildStack property, that's used to identify the object that "needs" the object that is being created, is protected, we have access to it, and we can add the classname to the stack.
for example: if $buildStack = Bar::class; you can do the following
app()->when(FooBar::class)->needs(FooAbstract::class)->give(FooImplementation::class);
In an application I'm building there's a CLI entry point class:
class CLIEntryPoint {
protected $factory;
public function __construct(ApplicationObjectFactoryInterface $factory) {
$this->factory = $factory;
}
public function run(...$args) {
$choice = $args[1];
$appObject = $this->factory->makeApplicationObject($choice);
$appObject->doApplicationRelatedStuff();
}
}
This entry point is created using Dependency Injection in my "front controller" script and it receives an ApplicationObjectFactoryInterface implementation (actually the current implementation of ApplicationObjectFactoryInterface is injected by the DI container, which in turn reads it from its configuration file, but that's not the point).
The current implementation of ApplicationObjectFactoryInterface also uses DI and depends on other factories which help it building the resulting application object:
class CurrentImplementationOfApplicationObjectFactory implements ApplicationObjectFactoryInterface {
protected $someComponentFactory;
protected $anotherComponentFactory;
public function __construct(SomeComponentFactoryInterface $someComponentFactory, AnotherComponentFactoryInterface $anotherComponentFactory) {
$this->someComponentFactory = $someComponentFactory;
$this->anotherComponentFactory = $anotherComponentFactory;
}
/**
* Interface's method
*
* #return ApplicationObjectInterface
*/
public function makeApplicationObject($choice) {
$component = $this->someComponentFactory->makeSomeComponent();
$anotherComponent = $this->anotherComponent->makeAnotherComponent();
switch ($choice) {
case 1:
return new CurrentImplementationOfApplicationObject1($component, $anotherComponent);
case 2:
return new CurrentImplementationOfApplicationObject2($component, $anotherComponent);
default:
return new DefaultImplementationOfApplicationObject($component, $anotherComponent);
}
}
}
Here CurrentImplementationOfApplicationObject1, CurrentImplementationOfApplicationObject2 and DefaultImplementationOfApplicationObject all implement the ApplicationObjectInterface interface and therefore they all have the doApplicationRelatedStuff method.
I would like to know whether it's good practice or not to write code like I did and if not how can I improve it.
Basically here I am creating a component which depends on other components in order to function properly using a factory which in turn needs inner factories to build the component which implements the ApplicationObjectInterface interface.
Is it considered good practice?
Thanks for the attention, as always!
EDIT: I looked at the article of Steven and tried to refactor CLIEntryPoint. The only problem now seems to be how to pass the $choice parameter to the factory which now is inside of the proxy when the run() method is called. Is this code structure better than the one I posted above? Of course, SomeComponentFactoryInterface and AnotherComponentFactoryInterface should follow the same behaviour (the factory that uses them should not use them directly, but through two proxies which implement, in order, SomeComponentInterface and AnotherComponentInterface). I hope I get it right, anyway, here is the code:
class CLIEntryPoint {
protected $applicationObject;
public function __construct(ApplicationObjectInterface $applicationObject) {
$this->applicationObject = $applicationObject;
}
public function run(...$args) {
$choice = $args[1]; // How do I deal with different choices when I am using a Proxy? I should have different application objects depending on input.
$this->applicationObject->doApplicationRelatedStuff();
}
}
interface ApplicationObjectInterface {
public function doApplicationRelatedStuff();
}
class ApplicationObjectProxy implements ApplicationObjectInterface {
protected $applicationObjectFactory;
protected $applicationObjectImplementation = NULL;
public function __construct(ApplicationObjectFactoryInterface $factory) {
$this->applicationObjectFactory = $factory;
}
public function __call($method, $args) {
// Calling interface's
$implementation = $this->getImplementation();
$methodOfInterfaceToCall = preg_replace('/Proxy$/', '', $method);
return $implementation->{$methodOfInterfaceToCall}(...$args);
}
/**
* Laxy loading method.
*/
protected function getImplementation() {
if (is_null($this->applicationObjectImplementation)) {
$this->applicationObjectImplementation = $this->applicationObjectFactory->makeApplicationObject(); // Choice should go here somehow...
}
return $this->applicationObjectImplementation;
}
public function doApplicationRelatedStuff() {
// This will call the PHP's magic `__call` method, which in turn will forward the call to the application object's
// implementation returned by the factory.
return $this->doApplicationRelatedStuffProxy();
}
}
Actually yes, this is a pattern called the Abstract Factory Pattern. So an example that I used to present it in front of my class during my undergrad:
So if you are building a video game first person shooter, you might want to create three concrete factories like:
FlyingMonsterFactory
SwimmingMonsterFactory
WalkingMonsterFactory.
All these factories would implement an abstract MonsterFactory.
With this, you can have your video game create a level in which you want waves of the same type of monsters, so you can have a randomWaveMonsterGenerator method return a MonsterFactory which might have returned a concrete SwimmingMonsterFactory. So then you will have a wave of SwimmingMonster(s) generated by the SwimmingMonsterFactory.
So answer your description more directly, looking at your code above, you asked the question on choice for Dependency Injection. With Dependency Injection, I believe for this type of pattern, you will have to inject every concrete class before your code even attempts to get the implementation class.
So for example:
Your code above says the run method gives an argument called
choice.
With this choice, you will have to use it as a parameter into a getImplementation method.
All the concrete objects that the getImplementation method that rely upon Dependency
Injection have to be created BEFORE you call the getImplementation method.
But since you don't know which implementation class will be called, I believe you have to inject ALL the implementation classes before hand.
Then you can use the choice variable as a parameter to get the correct implemented factory class.
Hope this helps!
So I have understood how interfaces and abstraction work in PHP, I just don't see the point for example, of having a interface if it just sets a guide and requires implemented objects to have certain methods. Especially since the interface is not even getting instantiated.
This also goes with abstraction, I just can't apply it to my code and see it as such a great thing. When I am trying to create objects on a bigger scale to interact with each other in order to figure out interfaces, each class ends up passing information back and forth, but never is the interface touched.
So what I'm asking is if you guys have any advice or links to outside sources that is good at explaining this kind of thing.
Here's one simple example. Creating interfaces and abstract classes allows you to ensure an object adhears to a common API. See the example below.
interface iCar
{
function drive();
}
abstract class Car implements iCar
{
public $make = 'Generic';
public function drive()
{
printf("I'm driving in my %s%s", $this->make, PHP_EOL);
}
}
class FordTruck extends Car
{
public $make = "Ford";
}
class Porsche extends Car
{
public $make = 'Porsche';
public function drive()
{
printf("I'm speeding around in my %s%s", $this->make, PHP_EOL);
}
}
class Yugo extends Car
{
public $make = 'Yugo';
public function drive()
{
printf("I'm pushing my %s around town%s", $this->make, PHP_EOL);
}
}
function drive(iCar $car)
{
$car->drive();
}
$car1 = new FordTruck;
$car2 = new Porsche;
$car3 = new Yugo;
drive($car1);
drive($car2);
drive($car3);
Even if you don't specify the type of input parameter on the drive() function, you can check if the input is an instanceof an iCar
function drive($car)
{
if ($car instanceof iCar)
$car->drive();
}
Another example would be building a caching interface in your application. You can specify that all cache engines support the same methods for reading/writing/invalidating objects in the cache without knowing (or caring) about the actual implementation of a particular cache engine.
I could give you the simplest as possible example.
Assume you want a feature that allow your site to login with Facebook/Twitter
# here's your interface/abstract class
interface Auth_Adapter {
public function auth();
}
# now your Facebook
class Auth_Adapter_Facebook implements Auth_Adapter {
public function login() {
# include facebook-sdk and auth
}
}
# Twitter
class Auth_Adapter_Twitter implements Auth_Adapter {
public function login() {
# include twitter-oauth and auth
}
}
Imagine when someone try to use Facebook/Twitter thing They can simply call
$adapter = new Auth_Adapter_Facebook;
$adapter->login();
$adapter = new Auth_Adapter_Twitter;
$adapter->login();
As you can see both adapters use the same login interface. What's happen if in the future you have to include 'Pinterest' login? Your code still work as long as you implement the same interface.
EDIT: More explanations
Here's the reason why you have to use interface or abstract
# I use `type-hinting` here. So I can ensure that only object that implements `Auth_Adapter` will allow. Without this implementation someone might pass some other object that doesn't have `login` method in. But in our case we don't have to worry about that.
public function perform_login(Auth_Adapter $adapter) {
$adapter->login();
}