Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PHP 8 Method overriding with different types from the same BaseClass

Tags:

php

php-8

My project is running fine on PHP 7.X and after upgrading to PHP 8 I have the following problem and I don't know how to fix this.

I have a the following (simplified) situation:

<?php

class Vehicle
{
    //...
}

class Car extends Vehicle
{
    //...
}


class VehicleOutputMaker
{
    public function output(Vehicle $entity)
    {
        
    }
}


class CarOutputMaker extends VehicleOutputMaker
{
    //THROWS EXCEPTION
    public function output(Car $entity)
    {
        parent::output($entity);
    }
}

My whole project is running like this but since PHP 8 I get an exception for the line

public function output(Car $entity)

with "FATAL ERROR: Declaration of *** must be compatible with ***"

It worked perfectly well with PHP 7! Because "Car" is also a Vehicle.

Somebody has an idea how to fix this?

Thanks!

like image 254
Lumpy Avatar asked Jun 28 '26 12:06

Lumpy


1 Answers

The reason you got a warning and now a fatal error is due to the fact that your code doesn't respect the Liskov substitution principle (LSP) standard requirement of contravariance of method parameter types in the subtype.

With this in mind, the issue is that while a method in a sub-class can expand the range of parameter types, it must accept all parameter types that the parent class accepts.

Your sub-class CarOutputMaker breaks this rule, its method output() accepts a parameter of type Car (a sub-type of Vehicle), but not a parameter of super-type Vehicle as declared in its parent method VehicleOutputMaker::output().

So this would be valid in PHP7:

class VehicleOutputMaker  
{  
    public function output(Vehicle $entity)  
    {  
    }  
}  
  
class CarOutputMaker extends VehicleOutputMaker  
{  
    public function output(Vehicle $entity)  
    {  
        parent::output($entity);  
    }  
}

Noteworthy, since you're using PHP8, you can use union types to type-hint both types in your sub-class: Vehicle|Car.

class VehicleOutputMaker  
{  
    public function output(Vehicle $entity)  
    {  
    }  
}  
  
class CarOutputMaker extends VehicleOutputMaker  
{  
    public function output(Vehicle|Car $entity)  
    {  
        parent::output($entity);  
    }  
}
like image 111
berend Avatar answered Jun 30 '26 07:06

berend