【问题标题】:php strict error by includingphp通过包含严格错误
【发布时间】:2015-08-19 02:22:09
【问题描述】:

如果类在一个文件中,看起来不会发生严格的错误,如下所示:

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);
    }   
}    

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

abstract class Food{}

class Meat extends Food{}                    

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

抛出严格标准错误消息:

严格的标准:Cat::feed() 的声明应与在线 c:\path\to\Cat.php 中的Animal::feed(Food $food) 兼容...

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

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

这是预期的行为吗?

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

任何提示表示赞赏

【问题讨论】:

  • 您看到的完整信息是什么?
  • 显示错误信息以及代码的包含方式
  • Cat 可以转换为Animal,而Animal::feed 采用Food,但Cat::feed 没有,所以$animal = (Animal)$cat; $animal-&gt;feed($food) 将失败。
  • 如何包含代码:require 'Animal.php';需要'Cat.php';
  • @Siguza 谢谢你的解释,但是$Meat是一种食物,所以不意味着喂肉就是喂食吗?

标签: php strict


【解决方案1】:

这是 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类。只要他们实现了接口,就可以将它们提供给任何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 :-)

因为您的基类是抽象的,所以它可以充当上述接口。忘记接口,只需使用与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);
    }
}

评论

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

【讨论】:

  • 在 PHP 中,我的理解是,Interfaces 被视为Abstract 类。假定它们具有“空方法”
  • PHP 内部如何处理接口和抽象类并不重要。接口和抽象类之间的重要区别有两个:1)抽象类可以实现它的部分或全部方法,它可以有静态方法、类属性、静态属性和类常量(接口不能做任何这些); 2)一个类可以实现许多接口,但它只能扩展一个类(无论是否抽象)。除了领域建模的原因,这些是在特定情况下选择使用接口还是抽象类的技术原因。
【解决方案2】:

这是预期的行为吗?

很遗憾,是的。类声明的复杂性使得严格的规则在它们都出现在同一个脚本中时并不总是适用。每个文件一个类不会出现此问题。

因为肉是一种食物,所以一开始就不应该抱怨,对吧?

错误有两个原因:

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

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

所以解决方案很简单明了:将所有内容放在一个文件中并满足严格的标准;)

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

【讨论】:

猜你喜欢
  • 2011-08-09
  • 2017-07-24
  • 1970-01-01
  • 2013-12-10
  • 2010-12-28
  • 1970-01-01
  • 2015-09-24
  • 2015-01-10
相关资源
最近更新 更多