Возможно ли злоупотреблять поздним статическим связыванием в PHP? - PullRequest
13 голосов
/ 29 ноября 2009

Начиная с версии 5.3, PHP поддерживает позднее связывание для статических методов. Хотя это, несомненно, полезная функция, есть только несколько случаев, когда ее использование действительно необходимо (например, шаблон 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 ]

15 голосов
/ 29 ноября 2009

Итак, чтобы закончить все это, являются ли эти (и подобные) способы позднего статического связывания излишним? Есть ли заметный удар по производительности? Кроме того, частое использование позднего связывания уменьшает общее повышение производительности, обеспечиваемое кэшами кода операции?

Введение позднего статического связывания исправляет недостаток в объектной модели PHP. Дело не в производительности, а в семантике.

Например, мне нравится использовать статические методы всякий раз, когда реализация метода не использует $this. То, что метод является статическим, не означает, что вы не хотите иногда его переопределять. До PHP 5.3 поведение было таким, что ошибка не была помечена, если вы переопределили статический метод, но PHP просто продолжал и молча использовал версию родителя. Например, код ниже печатает «A» до PHP 5.3. Это очень неожиданное поведение.

Позднее статическое связывание исправляет это, и теперь тот же код печатает «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();
?>
3 голосов
/ 29 ноября 2009

статические методы (с ранним или поздним ограничением) создают тесную связь и (таким образом) снижают тестируемость. Вы можете создавать большие программы на PHP без использования нескольких статических вызовов. для меня поздние статические методы не являются особенностью.

изменить , чтобы ответить на вопрос Марко Демайо, как статический метод снижает тестируемость?

Прошу прощения, если это все очевидно для вас, статические члены (и данные, и методы) полезны и не наносят вреда, если используются ответственно, я имел в виду их распространенное злоупотребление.

скажем, у вас есть веб-приложение, которое использует базу данных SQL. Ваши бизнес-объекты могут извлекать данные, используя статический интерфейс или полиморфизм. либо

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, и на самом деле проще протестировать без реальной базы данных. Кроме того, проще заглушить потребителя sql, если ваши тесты связаны с другим аспектом CUT (тестируемого кода).

тестирование всегда подразумевает ложь тестируемому коду о его соавторах и воздержание от статических интерфейсов («двойной столб» или «квадридот») означает, что ложь не должна быть массовой операцией, что является плюсом, поскольку чем дальше тестируемый код из производственного кода, тем менее значимы результаты теста.

1 голос
/ 24 ноября 2010

Где я нахожу необходимость использовать позднюю статическую привязку, это разрешить макетирование статических методов для модульного тестирования с помощью PHPUnit. У меня проблема в том, что я не люблю строго менять код, чтобы допустить насмешку, но я могу с этим справиться.

Однако, чтобы ответить на ваш вопрос, я бы поспорил, что независимо от того, сколько это будет стоить производительности, он побледнеет по сравнению с большинством времени выполнения программы. Другими словами, это не будет иметь заметного значения.

...