从 5.3 版本开始,PHP 支持 后期绑定 对于静态方法。虽然它无疑是一个有用的功能,但只有少数情况下才真正需要使用它(例如活动记录模式)。

考虑这些例子:

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:: 此示例与前一个示例类似。使用它只是因为这个类可以通过扩展它并覆盖常量来匹配不同形式的标签。


那么,总而言之,后期静态绑定的这些使用(以及类似的使用)是否是一种过度杀伤力?是否有任何明显的性能影响?另外,频繁使用后期绑定是否会降低操作码缓存带来的整体性能提升?

有帮助吗?

解决方案

  

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

后期静态绑定的引入修复了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();
?>

其他提示

静态方法(早期或后期绑定)会产生紧密耦合并(从而)降低可测试性。您可以在 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 使用者。

测试总是意味着对有关其协作者的测试代码撒谎,并且放弃静态接口(“双冒号”或“四点”)意味着谎言不需要是一个大规模的手术,这是一个优点,因为测试的代码距离越远生产代码越多,测试结果的意义就越小。

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

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

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top