1

I've found what appears to be an inconsistency in PHP when overriding methods in child classes, using subtypes as the parameter for the methods. It's easiest if I explain in code:

interface IngredientInterface {}
interface TomatoInterface extends IngredientInterface {}

class Tomato implements TomatoInterface {}

class Soup {
    public function __construct(IngredientInterface $ingredient) {}
    public function foo(IngredientInterface $ingredient) {}
}

class TomatoSoup extends Soup {
    public function __construct(TomatoInterface $tomato) { parent::__construct($tomato); }
    public function foo(TomatoInterface $ingredient) { parent::foo($ingredient); }
}

One would expect that the error reporting behaviour would be identical between the overriden __construct() and foo() methods, but they are not.

The __construct() method generates no errors in PHP 5.5.38, 5.6.19, and 7.0.4

The foo() method generates the following error in 5.5.38 and 5.6.19:

Strict Standards: Declaration of TomatoSoup::foo() should be compatible with Soup::foo(IngredientInterface $ingredient) in H:\webroot\test.php on line 16

and in 7.0.4:

Warning: Declaration of TomatoSoup::foo(TomatoInterface $ingredient) should be compatible with Soup::foo(IngredientInterface $ingredient) in H:\webroot\test.php on line 16

Now I'm not concerned that the error type has changed from E_STRICT to E_WARNING, I'm more concerned at the inconsistency of the constructor parsing fine, but the foo() method not.

Is this a bug in PHP? Should I report it to bugs.php.net?

e_i_pi
  • 4,590
  • 4
  • 27
  • 45

1 Answers1

0

Why do you want to have subtype if you are extending a class, if you have a supertype IngredientInterface you can pass every object that is subtype of IngredienteInterface to the constructor and to the method foo (remember the good practices Liskov substitution principle), and, ¿why do you want to extend an interface? (remember the Interface Segregation principle)

The result is the same if you use the supertype in the children class, but I think you have to review a little more your code, SOLID principles are a good point of start

<?php

interface IngredientInterface {}
interface TomatoInterface extends IngredientInterface {}

class Ingredient implements IngredientInterface {}
class Tomato implements TomatoInterface {}

class Soup {
    public function __construct(IngredientInterface $ingredient) {}
    public function foo(IngredientInterface $ingredient) {}
}

class TomatoSoup extends Soup {
    public function __construct(IngredientInterface $tomato) { parent::__construct($tomato); }
    public function foo(IngredientInterface $ingredient) { parent::foo($ingredient); }
}

$tom = new TomatoSoup(new Tomato());
$tom->foo(new Tomato());

$tom2 = new TomatoSoup(new Ingredient());
$tom2->foo(new Ingredient());
  • Thanks for your reply, and yes I agree your code works fine. In response, there's no problem with extending interfaces, that doesn't violate ISP. Also, I don't necessarily want to subtype method arguments, there are other ways to do this that don't violate SOLID. I'm concerned that PHP prevents violation of LSP in methods, but allows it in the constructor. @DeveloperChris explains it in this question... looks like the PHP crew got lazy :( http://stackoverflow.com/questions/13423494/why-is-overriding-method-parameters-a-violation-of-strict-standards-in-php – e_i_pi Dec 08 '16 at 03:40