php 包含严格错误

php strict error by including

如果 class 像这样在一个文件中,似乎不会发生严格错误:

abstract class Food{}

class Meat extends Food{}                    

abstract class Animal{          
    function feed(Food $food){ }
}

class Cat extends Animal{           
    function feed(Meat $meat){
        parent::feed($meat);
    }   
}    

但是如果您将 class 定义放在单独的文件中并像这样包含它们:

abstract class Food{}

class Meat extends Food{}                    

require 'Animal.php';
require 'Cat.php';

抛出严格标准错误消息:

Strict standards: Declaration of Cat::feed() should be compatible with Animal::feed(Food $food) in c:\path\to\Cat.php on line...

如果所有内容都在一个文件中,即使这样也没关系:

class Dog extends Animal{           
  function feed($qty = 1){ 
    for($i = 0; $i < $qty; $i++){
        $Meat = new Meat();
        parent::feed($Meat);
    }       
  } 
}

这是预期的行为吗?

因为MeatFood,本来就不该抱怨的吧? 所以解决方案简单明了:将所有内容放在一个文件中并满足严格的标准 ;)

感谢任何提示

当涉及到 classes 和名称空间时,这是 PHP 的许多奇怪行为之一。

标准解决方案是创建一个 Interface(我们将其命名为 FoodInterface') and implement it in the base class. Then useFoodInterfaceas the type of the argument of methodfeed()`:

interface FoodInterface {}

abstract class Food implements FoodInterface {}

class Meat extends Food {}

abstract class Animal {
    function feed(FoodInterface $food) {}
}

class Cat extends Animal {           
    function feed(FoodInterface $meat) {
        parent::feed($meat);
    }
}

FoodInterface接口可以为空,也可以在里面声明需要调用的函数Animal::feed()

通过这种方式,您可以 feed() 您的 Cat(或任何其他 Animal)与任何实现 FoodInterface 的对象,无论它们是否扩展了 Food class 与否。只要它们实现了接口,就可以很好地馈送到任何 Animal.

class Toy implements FoodInterface {}

$cat = new Cat();
$cat->feed(new Toy());    // He can't eat it but at least he will have some fun :-)

因为你的基础 class 是抽象的,它可以充当上述接口。忘掉接口,只用与 Animal::feed().

相同的参数类型声明 Cat::feed()

然后,在Cat::feed()的实现中你可以使用instanceof来检查接收到的参数类型是否是你想要的(Meat):

abstract class Food {}

class Meat extends Food {}

abstract class Animal {
    function feed(Food $food) {}
}

class Cat extends Animal {           
    function feed(Food $meat) {
        if (! $meat instanceof Meat) {
            throw new InvalidArgumentException("Cats don't eat any Food. Meat is required");
        }

        // Here you are sure the type of $meat is Meat
        // and you can safely invoke any method of class Meat
        parent::feed($meat);
    }
}

评论

第一种方法是正确的方法。第二种方法有其自身的优点,我建议仅在第一种方法由于某种原因无法实现时才使用它。

Is this the intended behavior?

不幸的是,是的。 class 声明的复杂性使得当它们都出现在同一个脚本中时并不总是应用严格的规则;每个文件一个 class 不会出现此问题。

Because Meat is a Food, there shouldn't be a complain in the first place, right?

错误的原因有二:

  1. Meat 是比 Food 小的类型,因此只允许后代使用较小的类型 class 你违反了 LSP;你不能用 Cat 代替 Animal.

  2. 在PHP中,重载方法时参数类型为invariant,即接受的类型必须与父类的类型完全匹配。虽然可以说逆变类型是有意义的,但由于技术原因,这是不可能的。

So the solution is plain and clear: Put everything in one file and strict standards are satisfied ;)

不,你绝对不应该依赖这种行为。