Is it possible to overuse late static binding in PHP?
Asked Answered
V

3

14

Starting with version 5.3, PHP supports late binding for static methods. While it's an undoubtedly useful feature, there are only several cases where its use is really necessary (e.g. the Active Record pattern).

Consider these examples:

1. Convenience constructors (::create())

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

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

If this class may be extended (however, it's not extended by any class in the same package), should late static binding be used just to make extending it easier (without having to rewrite the ::create() method, and, more importantly, without having to remember to do that)?

Note: this idiom is used to work around the impossibility to call methods on just constructed objects: new SimpleObject()->doStuff() is invalid in PHP.


2. Class constants

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

The reason to use static:: in this example is similar to the previous one. It's used just because this class can be made to match differently formed tags just by extending it and overriding the constant.


So, to wrap it all up, are these uses (and similar ones) of late static binding are an overkill? Is there any noticeable performance hit? Also, does frequent use of late binding reduce the overall performance boost given by opcode caches?

Viceregent answered 29/11, 2009 at 19:52 Comment(4)
Well, the second one could be achieved with overriding a private variable and using $this, too, right? Or am I misunderstanding something?Gilstrap
Yes, but I just couldn't come up to a better example... Having it as a const means avoiding copying it through all the instances. And even if PHP's copy-on-write would optimize this, it certainly looks better as a class constant, because it actually is a constant value class-wide.Viceregent
... and by all that, I actually meant that in this case using instance variables looks like a workaround, while static:: doesn't.Viceregent
You are right about the fact that new SimpleObject()->doStuff() will not call doStuff() on your newly constructed object. But you don't require late static binding to do that, as PHP does support that. You just have to write it as (new SimpleObject())->doStuff().Betweentimes
N
15

So, to wrap it all up, are these uses (and similar ones) of late static binding are an overkill? Is there any noticeable performance hit? Also, does frequent use of late binding reduce the overall performance boost given by opcode caches?

The introduction of late static binding fixes a flaw in PHP's object model. It's not about performance, it's about semantics.

For example, I like to use static methods whenever the implementation of the method doesn't use $this. Just because a method is static doesn't mean to say that you don't want to override it sometimes. Prior to PHP 5.3, the behavior was that no error was flagged if you overrode a static method, but PHP would just go ahead and silently use the parent's version. For example, the code below prints 'A' before PHP 5.3. That's highly unexpected behavior.

Late static binding fixes it, and now the same code prints '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();
?>
Neigh answered 29/11, 2009 at 20:23 Comment(2)
I know this is an old thread, but found it through google. Your example code should actually print 'A' regardless of PHP version. To activate late static bindings, use the static keyword. EG. replace self::who(); with static::who(); and it should print 'B'Trabzon
I concur with Ewan, if you aren't using $this, make it static.Pinnate
K
4

static methods (early- or late-bound) create tight coupling and (thus) reduce testability. you can create large programs in PHP without using more than a few static calls. for me, late static methods are a non-feature.

edit to answer Marco Demaio's question, how do static method reduce testability?

i'm sorry if this is all obvious to you, static members (both data and methods) are useful and do no harm if used responsibly, i was alluding to their prevalent misuse.

say you have a web application that uses an SQL database. your business objects may retrieve data using a static interface or through polymorphism. either

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

or

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

the latter is easier to test (as in: i want to test that the sql string constructed from such-and-such inputs is such-and-such) because it's easier to create another implementation of the dbconn interface than it is to change the meaning of db::. why you'd want either? because you don't need a real database to test the sql-composing behavior, and in fact it's easier to test for without a real database. also, it's easier to stub out the sql consumer if your tests are concerned with another aspect of the CUT (Code Under Test).

testing always implies lying to the tested code about its collaborators, and abstaining from static interfaces (the "doublecolon" or "quadridot") means the lie need not be a massive surgery, which is a plus, since the farther the tested code is from the production code, the less meaningful the test results are.

Kuban answered 29/11, 2009 at 20:20 Comment(5)
+1 Since I've started using instances over static methods, things got a lot easier all aroundTightwad
@just somebody: what do you mean when you are saying static methods create tight coupling reducing testability? I don't inderstand how they can reduce testability.Guildsman
@just somebody: +1 good clear example of why static is not good for decoupling during testsGuildsman
While the above remains true you may work around that issue using Facades and IoC-containers.Pannell
what is that issue?Kuban
S
1

Where I find a need to use late static binding is to allow mocking of static methods for unit testing with PHPUnit. The problem I have is that I don't like changing code strictly to allow mocking, but I can get over that.

To answer your question, however, I would bet that whatever performance cost this carries, it will pale in comparison to most program runtimes. In other words, it won't make a noticeable difference.

Seldon answered 24/11, 2010 at 0:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.