Что такое поздние статические привязки в PHP?
Что такое поздние статические привязки в PHP?
7 ответов
Начиная с PHP 5.3.0, в PHP реализована функция, называемая поздним статическим связыванием, которая может использоваться для ссылки на вызываемый класс в контексте статического наследования.
Позднее статическое связывание пытается устранить это ограничение путем введения ключевого слова, которое ссылается на класс, который был первоначально вызван во время выполнения. Было решено не вводить новое ключевое слово, а использовать static
это уже было зарезервировано.
Давайте посмотрим на пример:
<?php
class Car
{
public static function run()
{
return static::getName();
}
private static function getName()
{
return 'Car';
}
}
class Toyota extends Car
{
public static function getName()
{
return 'Toyota';
}
}
echo Car::run(); // Output: Car
echo Toyota::run(); // Output: Toyota
?>
late static bindings
работать, сохраняя класс, указанный в последнем "вызове без переадресации". В случае статических вызовов методов это класс с явным именем (обычно слева от оператора::); в случае нестатических вызовов методов это класс объекта.
"Переадресация вызова" является статической, которая вводится self::
, parent::
, static::
или, если подняться в иерархию классов, forward_static_call()
,
Функция get_called_class()
может использоваться для получения строки с именем вызываемого класса и static::
вводит свою сферу.
Вам определенно необходимо прочитать Поздние статические привязки в руководстве по PHP. Тем не менее, я постараюсь дать вам краткое резюме.
В основном это сводится к тому, что self
Ключевое слово не соответствует тем же правилам наследования. self
всегда разрешается в классе, в котором он используется. Это означает, что если вы создадите метод в родительском классе и вызовете его из дочернего класса, self
не будет ссылаться на ребенка, как вы могли ожидать.
Позднее статическое связывание вводит новое использование для static
ключевое слово, которое устраняет этот конкретный недостаток. Когда вы используете static
, он представляет класс, где вы впервые используете его, т.е. он "привязывается" к классу времени выполнения.
Это две основные концепции, стоящие за этим. Путь self
, parent
а также static
работать, когда static
в игре может быть тонким, поэтому вместо того, чтобы вдаваться в подробности, я настоятельно рекомендую вам изучить примеры на странице руководства. Как только вы поймете основы каждого ключевого слова, примеры станут необходимыми, чтобы увидеть, какие результаты вы собираетесь получить.
Там не очень очевидное поведение:
Следующий код производит 'alphabeta'.
class alpha {
function classname(){
return __CLASS__;
}
function selfname(){
return self::classname();
}
function staticname(){
return static::classname();
}
}
class beta extends alpha {
function classname(){
return __CLASS__;
}
}
$beta = new beta();
echo $beta->selfname(); // Output: alpha
echo $beta->staticname(); // Output: beta
Однако, если мы удалим объявление функции classname из бета-класса, в результате мы получим "alphaalpha".
Самый простой пример, чтобы показать разницу.
Обратите внимание, self::$c
class A
{
static $c = 7;
public static function getVal()
{
return self::$c;
}
}
class B extends A
{
static $c = 8;
}
B::getVal(); // 7
Позднее статическое связывание, обратите внимание на статическое::$c
class A
{
static $c = 7;
public static function getVal()
{
return static::$c;
}
}
class B extends A
{
static $c = 8;
}
B::getVal(); // 8
Я цитирую из книги: "PHP Master написать передовой код".
Позднее статическое связывание было добавлено в php 5.3. Это позволяет нам наследовать статические методы от родительского класса и ссылаться на вызываемый дочерний класс.
Это означает, что вы можете иметь абстрактный класс со статическими методами и ссылаться на конкретные реализации дочернего класса, используя нотацию static::method() вместо self::method().
Не стесняйтесь взглянуть и на официальную документацию php: http://php.net/manual/en/language.oop5.late-static-bindings.php
Пример:
<?php
class Animal {
public static function StaticCall() {
// Parent object invokes its own getAnimalName()
// Child object invokes its own getAnimalName() instead of parent's getAnimalName()
return static::getAnimalName();
}
public static function SelfCall() {
return self::getWeight();
}
private static function getAnimalName(){
return 'Animal <br />';
}
private static function getWeight(){
return '10 kg <br />';
}
}
class Bird extends Animal {
public static function getAnimalName(){
return 'Bird <br />';
}
private static function getWeight(){
return '2 kg <br />';
}
}
echo Animal::StaticCall(); // Animal
echo Animal::SelfCall(); // 10 kg
echo Bird::StaticCall(); // Bird invokes method from own object
echo Bird::SelfCall(); // 10 kg invokes method from parent
В приведенном выше коде вы можете увидеть два класса Animal
который является родительским классом и Bird
который является дочерним классом. И то и другое Animal
а также Bird
иметь getAnimalName()
а также getWeight()
метод. Суперкласс Animal
имеет два метода: StaticCall()
а также SelfCall()
,
Метод StaticCall()
Запускает getAnimalName()
используя static
ключевое слово.
Метод SelfCall()
Запускает getWeight()
используя self
ключевое слово.
Теперь у нас возникает вопрос: в каком контексте getAnimalName()
казнят?
Ответ: static::getAnimalName()
идентифицирует контекст и вызывает метод в этом контексте.
Если вы вызываете Bird::StaticCall()
код выполнится StaticCall()
который в Animal
, затем static::getAnimalName()
будет вызывать и выполнять из Bird
метод getAnimalName()
,
Это отличается от self::
, так как self::
всегда вызывает метод внутри объекта self
определяется в. Так что если self::getWeight()
определяется в объекте Animal
в методе SelfCall()
а также Bird::SelfCall()
будет называться тогда self::getWeight()
Запускает getWeight()
в контексте Animal
объект.
Глядя на это с "почему я бы использовал это?" в перспективе это в основном способ изменить контекст, из которого интерпретируется / запускается статический метод.
С self
контекст - это тот, где вы определили метод изначально. С static
это тот, от кого ты звонишь.
Например:
abstract class Builder {
public static function build() {
return new static;
}
}
class Member extends Builder {
public function who_am_i() {
echo 'Member';
}
}
Member::build()->who_am_i();
Также смотрите, обновляете ли вы статические переменные в дочерних классах. Я нашел этот (несколько) неожиданный результат, когда дочерний B обновляет дочерний C:
class A{
protected static $things;
}
class B extends A {
public static function things(){
static::$things[1] = 'Thing B';
return static::$things;
}
}
class C extends A{
public static function things(){
static::$things[2] = 'Thing C';
return static::$things;
}
}
print_r(C::things());
// Array (
// [2] => Thing C
// )
B::things();
print_r(C::things());
// Array (
// [2] => Thing C
// [1] => Thing B
// )
Вы можете исправить это, объявив одну и ту же переменную в каждом дочернем классе, например:
class C extends A{
protected static $things; // add this and B will not interfere!
public static function things(){
static::$things[2] = 'Thing C';
return static::$things;
}
}