Bug #78818 [Opn->Nab]: Current Type variance changes breaks the Liskov substitution principle
[email protected] Fri, 15 Nov 2019 13:13:56 +0000
| Newsgroups | php.standards |
|---|---|
| Message-ID | <[email protected]> |
Edit report at https://bugs.php.net/bug.php?id=78818&edit=1 ID: 78818 Updated by: [email protected] Reported by: me at paveljanda dot com Summary: Current Type variance changes breaks the Liskov substitution principle -Status: Open +Status: Not a bug Type: Bug Package: PHP Language Specification Operating System: Linux PHP Version: 7.4.0RC6 Block user comment: N Private report: N New Comment: I'm sorry but this is the correct behaviour as per LSP. Your issue here is that your abstraction (interface/classes) is wrong and bending the rules of correct type safety just to make a wrong abstraction work is not correct. It seems you are confusing co and contravariance. Covariance is only safe in return types and contravariuance is only safe in argument types. Moreover you are not doing any of that you are doing flat out variance which is inherently incompatible with LSP. Previous Comments: ------------------------------------------------------------------------ [2019-11-15 13:03:35] me at paveljanda dot com Description: ------------ 1, Liskov substitution principle: interface FuelType { } class Gas implements FuelType { public function burn(int $litres) {} }; class Battery implements FuelType { public function discharge(int $kWh) {} }; class Vehicle { public function drive(Gas $fuel) { $battery->burn(10); } } class ElectricVehicle extends Vehicle { public function drive(FuelType $battery) { // do something } } (new ElectricVehicle)->drive(new Battery); -> This code works fine in 7.4 RC6. I don't think it should. That code would enable ElectricVehicle::drive() to accept "smaller" argument with less functionality that the "bigger" Gas class that is required by parent (!!) class method. 2. In the example above, when I change the ElectricVehicle into following form: class ElectricVehicle extends Vehicle { public function drive(Battery $battery) { $battery->discharge(10); } } , PHP tells me different result (Warning: Declaration of ElectricVehicle::drive(Battery $battery) should be compatible with Vehicle::drive(Gas $fuel)) event though Battery is a type of FuelType which worked fine in the previous example. It makes perfect sense to use Type variance in the opposite direction - classes that extend from Vehicle should be able to accept a class that extends Gas type. It should be OK to extends the successor behaviour. But it's not OK do make it both directions. Thank you all for spending some time with that report! Pavel Janda Test script: --------------- https://gist.github.com/paveljanda/cba6d31be920217c620289e54fed7c1c ------------------------------------------------------------------------ -- Edit this bug report at https://bugs.php.net/bug.php?id=78818&edit=1