0

Позднее статическое связывание в PHP

В PHP 5.3 появилась такая интересная возможность, как позднее статическое связывание (late static binding). Дальше немного вольный перевод описания из официального мануала.

Начиная с PHP 5.3.0 в языке реализована возможность, называемая поздним статическим связыванием, которая может использоваться для ссылки на вызываемый класс в контексте статического наследования.

Эту возможность назвали «позднее статическое связывание». «Позднее связывание» говорит о том, что static:: будет разрешаться не относительно класса, где определен метод, но будет вычисляться во время выполнения. «Статическое связывание» означает, что оно может быть использовано в вызовах статических методов (но не ограничивается только ими).

Ограничения self::

Статические ссылки на текущий класс в виде self:: или __CLASS__ разрешаются относительно класса, к которому относится функция, то есть относительно класса, где ссылка была определена:

Пример № 1: использование self::

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

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

B::test();
?>

Пример выведет:

A

Использование позднего статического связывания

Позднее статическое связывание пытается решить это ограничение, вводя ключевое слово, ссылающееся на класс, первоначально вызванный в процессе выполнения. То есть, ключевое слово, которое позволит сослаться на B из test() в предыдущем примере. Было решено не вводить новое слово, а использовать уже зарезервированное static.

Пример № 2: простое использование static::

<?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();
?>

Пример выведет:

B

Замечание: static:: не работает как $this для статических методов! $this-> следует правилам наследования, а static:: нет. Это различие уточняется ниже.

Пример № 3: использование static:: в нестатическом контексте

<?php
class TestChild extends TestParent {
  public function __construct() {
    static::who();
  }

  public function test() {
    $o = new TestParent();
  }

  public static function who() {
    echo __CLASS__."\n";
  }
}

class TestParent {
  public function __construct() {
    static::who();
  }

  public static function who() {
    echo __CLASS__."\n";
  }
}
$o = new TestChild;
$o->test();
?>

Пример выведет:

TestChild
TestParent

Замечание: Позднее статическое связывание останавливает процесс разрешения вызова. Статические вызовы с использованием ключевых слов parent:: или self:: передают информацию о вызове дальше.

Пример № 4: Передача и непередача вызовов

<?php
class A {
  public static function foo() {
    static::who();
  }

  public static function who() {
    echo __CLASS__."\n";
  }
}

class B extends A {
  public static function test() {
    A::foo();
    parent::foo();
    self::foo();
  }

  public static function who() {
    echo __CLASS__."\n";
  }
}
class C extends B {
  public static function who() {
    echo __CLASS__."\n";
  }
}

C::test();
?>

Пример выведет

A
C
C

Крайние случаи

Существует множество различных способов вызвать метод в PHP, такие как коллбэки или магически методы. Поскольку позднее статическое связывание разрешается во время выполнения, это может привести к неожиданным результатам в так называемых крайних случаях.

Пример № 5 Позднее статическое связывание в магических методах

<?php
class A {
  protected static function who() {
    echo __CLASS__."\n";
  }

  public function __get($var) {
    return static::who();
  }
}

class B extends A {
  protected static function who() {
    echo __CLASS__."\n";
  }
}

$b = new B;
$b->foo;
?>

Пример выведет:

B

Пару статей по теме на хабре: Singleton и Late static binding и Позднее статическое связывание в PHP.

Copyright © 2017 — dec5e | Site design by Trevor Fitzgerald