Providing your interfaces match you don't need to use inheritance.
It's easier to ensure interfaces match and provide a guarantee by using a contract such as subclassing a base class that has abstract methods (that you have to provide an implementation for in your subclass) or implement a shared interface.
Interfaces don't contain any code, they're just what they say on the tin - a blueprint for a certain interface. Base classes can contain some implementation but abstract classes and methods must be derived / subclassed to be used.
Then you can apply type hints to check for a common Interface or base class (Observer):
public function register(Observer observer) {
observers.push(observer);
}
or check an Interface is implemented:
if (class instanceof MyInterface) {
// call a method that exists in your interface
class.myMethod();
}
or your object extends a base-class that has certain abstract methods:
if (class instanceof MyBaseClass) {
class.guaranteedToExist();
}