代码之家  ›  专栏  ›  技术社区  ›  Ignas R

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

  •  13
  • Ignas R  · 技术社区  · 15 年前

    从5.3版开始,PHP支持 late binding 对于静态方法。虽然这无疑是一个有用的特性,但只有在几个情况下才有必要使用它(例如,活动记录模式)。

    考虑这些例子:

    1。方便施工人员( ::create() )

    class SimpleObject
    {
        public function __construct() { /* ... */ }
    
        public static function create()
        {
            return new static; // or: return new self;
        }
    }
    

    如果这个类可以被扩展(但是,它不会被同一个包中的任何类扩展),那么应该使用延迟的静态绑定来简化扩展(而不必重写 ::创建() 方法,更重要的是,不必记住吗?

    注意:这个习语用于解决对刚构造的对象调用方法的不可能性: 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 回复  |  直到 8 年前
        1
  •  15
  •   Matt S    8 年前

    所以,要将其全部包装起来,后期静态绑定的这些用途(以及类似用途)是一种过度杀伤力吗?是否有明显的性能冲击?此外,频繁使用后期绑定是否会降低操作码缓存带来的总体性能提升?

    后期静态绑定的引入修复了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();
    ?>
    
        2
  •  3
  •   just somebody    13 年前

    静态方法(早期或后期绑定)创建紧密耦合,从而降低可测试性。您可以在PHP中创建大型程序,而不需要使用多个静态调用。对于我来说,最新的静态方法是非特性的。

    编辑 回答马尔科·德马约的问题, 静态方法如何降低可测试性?

    如果静态成员(数据和方法)对您来说都很明显,我很抱歉。 如果使用得当且无害,我指的是他们普遍滥用。

    假设您有一个使用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”)意味着谎言不需要是一个巨大的手术,这是一个优点,因为被测试的代码离生产代码越远,测试结果就越没有意义。

        3
  •  1
  •   David Harkness    14 年前

    我发现需要使用后期静态绑定的地方是允许对使用phpunit进行单元测试的静态方法进行模拟。我的问题是,我不喜欢严格地修改代码以允许模拟,但我可以克服这一点。

    不过,为了回答您的问题,我敢打赌,无论这带来多少性能成本,与大多数程序运行时相比,它都将是苍白的。换句话说,它不会产生显著的差异。