是否有可能在PHP中过度使用后期静态绑定?

时间:2009-11-29 19:52:49

标签: php late-binding late-static-binding

从版本5.3开始,PHP支持late binding用于静态方法。虽然它是一个无疑是有用的功能,但只有几种情况需要使用它(例如Active Record模式)。

请考虑以下示例:

1。便利构造函数(::create()

class SimpleObject
{
    public function __construct() { /* ... */ }

    public static function create()
    {
        return new static; // or: return new self;
    }
}

如果这个类可以扩展(但是,它没有被同一个包中的任何类扩展),那么是否应该使用后期静态绑定来使其更容易扩展(无需重写::create()方法,并且更重要的是,不必记得那样做?)

注意:这个习惯用于解决在构造对象上调用方法的不可能性:new SimpleObject()->doStuff()在PHP中无效。


2。类常量

class TagMatcher
{
    const TAG_PATTERN = '/\<([a-z\-]+?)\>/i';

    private $subject;

    public function construct($subject) { $this->subject = $subject; }

    public function getAllTags()
    {
        $pattern = static::TAG_PATTERN;
        preg_match_all($pattern, $this->subject);
        return $pattern[1];
    }
}

在此示例中使用static::的原因与前一个类似。它的使用只是因为这个类可以通过扩展它并覆盖常量来匹配不同形式的标记。


所以,要将它们全部包装起来,这些后期静态绑定的使用(以及类似的)是否过度?是否有明显的性能影响?此外,经常使用后期绑定会降低操作码缓存的整体性能提升吗?

3 个答案:

答案 0 :(得分:15)

  

所以,要将它们全部包装起来,这些后期静态绑定的使用(以及类似的)是否过度?是否有明显的性能影响?此外,经常使用后期绑定会降低操作码缓存的整体性能提升吗?

后期静态绑定的引入修复了PHP的对象模型中的一个缺陷。这不是关于性能,而是关于语义。

例如,每当方法的实现不使用$this时,我都喜欢使用静态方法。仅仅因为方法是静态的并不意味着你有时候不想覆盖它。在PHP 5.3之前,行为是如果你覆盖静态方法没有标记错误,但PHP会继续并静默使用父版本。例如,下面的代码在PHP 5.3之前打印'A'。这是非常意外的行为。

后期静态绑定修复它,现在相同的代码打印'B'。

<?php
class A {
  public static function who() {
    echo __CLASS__;
  }
  public static function test() {
    static::who();
  }
}

class B extends A {
  public static function who() {
    echo __CLASS__;
  }
}

B::test();
?>

答案 1 :(得分:3)

静态方法(早期或晚期)创建紧密耦合并(因此)降低可测试性。您可以在PHP中创建大型程序,而无需使用多个静态调用。对我来说,后期静态方法是一种非特征。

编辑以回答Marco Demaio的问题,静态方法如何降低可测试性?

我很抱歉,如果这对您来说显而易见,静态成员(数据和方法) 非常有用,如果负责任地使用,则不会造成伤害,我暗指他们普遍滥用。

假设您有一个使用SQL数据库的Web应用程序。您的业​​务对象可以使用静态接口或通过多态来检索数据。任

class MyBusinessObject
extends...
{
  public function doThisOrThat(...)
  {
    $results = db::query('sql string...');
    ...
  }
}

class MyBusinessObject
extends...
{
  public function __construct(dbconn $db)
  {
    $this->db = $db;
  }
  private $db;
  public function doThisOrThat(...)
  {
    $results = $this->db->query('sql string...');
    ...
  }
}

后者更容易测试(如:我想测试从这样的输入构造的sql字符串是这样的),因为它更容易创建dbconn的另一个实现接口比改变db::的含义。为什么你想要?因为你不需要真正的数据库来测试sql组合行为,事实上,没有真实数据库就可以更轻松地测试。另外,如果您的测试涉及CUT的另一个方面(测试代码),则更容易将sql使用者存根。

测试总是意味着对测试代码说谎其合作者,并且避免使用静态接口(“doublecolon”或“quadridot”)意味着谎言不一定是大手术,这是一个加分,因为测试的距离越远代码来自生产代码,测试结果的意义越小。

答案 2 :(得分:1)

我发现需要使用后期静态绑定是允许使用PHPUnit模拟静态方法进行单元测试。我遇到的问题是我不喜欢严格改变代码以允许嘲笑,但我可以克服它。

然而,为了回答你的问题,我敢打赌,无论性能成本如何,与大多数程序运行时相比,它都会变得苍白无力。换句话说,它不会产生明显的差异。