This is one of many weird PHP behaviors when it comes to classes and namespaces.
The standard solution is to create an Interface (let's call it FoodInterface') and implement it in the base class. Then use FoodInterface') and implement it in the base class. Then use FoodInterface as the type of the argument of method feed () `:
interface FoodInterface {} abstract class Food implements FoodInterface {} class Meat extends Food {} abstract class Animal { function feed(FoodInterface $food) {} } class Cat extends Animal { function feed(FoodInterface $meat) { parent::feed($meat); } }
The FoodInterface interface may be empty or you can declare in it the functions necessary to call Animal::feed() .
That way you can feed() your Cat (or any other Animal ) with any object that implements FoodInterface , whether it extends the Food class or not. As long as they implement the interface, they are good for submitting to any Animal .
class Toy implements FoodInterface {} $cat = new Cat(); $cat->feed(new Toy());
Since your base class is abstract, it can act as the aforementioned interface. Forget the interface and simply declare Cat::feed() with the same argument types as Animal::feed() .
Then, in the Cat::feed() implementation, you can use instanceof to check if the type of the received argument is the one you want ( Meat ):
abstract class Food {} class Meat extends Food {} abstract class Animal { function feed(Food $food) {} } class Cat extends Animal { function feed(Food $meat) { if (! $meat instanceof Meat) { throw new InvalidArgumentException("Cats don't eat any Food. Meat is required"); }
Comments
The first approach is the right way to do this. The second approach has its advantages, and I recommend using it only when, for some reason, the first approach is impossible.
axiac source share