Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Declaration not compatible with interface (PHP)

Let's say I have one parent class with two child classes like this

abstract class Vehicle {
    public function setManufacturer(string $manufacturer) { ... }
}

class Bicycle extends Vehicle {
    public function addSaddle() { ... }
}

class Car extends Vehicle {
    public function setSpeedLimit(float $limit) { ... }
}

Now I want to use these objects with an interface like this

interface VehicleInterface {
    public function create(Vehicle $vehicle);
}

class BicycleService implements VehicleInterface {
    public function create(Bicycle $bicycle) {
        $bicycle->setManufacturer('Some company'); // Common for all Vehicle objects
        $bicycle->addSaddle(); // Common only for Bicycle objects
    }
}

It seems that this is not possible since BicycleService create(...) is not compatible with the interface. Is there any way around this other than removing type hints?

like image 861
charliekelly Avatar asked Feb 13 '26 07:02

charliekelly


1 Answers

Consider this:

class NotBicycle implements Vehicle { ... }

interface VehicleInterface {
    public function create(Vehicle $vehicle);
}

class BicycleService implements VehicleInterface {
    public function create(Bicycle $bicycle) {
        $bicycle->setManufacturer('Some company'); // Common for all Vehicle objects
        $bicycle->addSaddle(); // Common only for Bicycle objects
    }
}

function createVehicle(VehicleInterface $service, Vehicle $vehicle) {
    $service->create($vehicle);
}
$service = new BicycleService();
$vehicle = new NotBicycle();
createVehicle($service, $vehicle);

Even if you were somehow able to only accept a Bicycle in the BicycleService, createVehicle($service, $vehicle) will still work because VehicleInterface has a method create(Vehicle $vehicle). Therefore in order to get this to work the way you want it, you need to basically break what an interface is.

Your only real option is to add a runtime type check. Like e.g.

class BicycleService implements VehicleInterface {
    public function create(Vehicle $bicycle) {
        if (!$bicycle instance of Bicycle) { 
           throw new TypeError('Expected Bicycle but got '.get_class($bicycle)); 
        }
        $bicycle->setManufacturer('Some company'); // Common for all Vehicle objects
        $bicycle->addSaddle(); // Common only for Bicycle objects
    }
}

What you are trying to do is called a covariant method parameter type and is not allowed in most object-oriented programming languages because it breaks the Liskov substitution principle

Read more on parameter covariance and contravariance

like image 154
apokryfos Avatar answered Feb 14 '26 19:02

apokryfos



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!