O que exatamente são as ligações estáticas tardias no PHP?

Respostas:

198

Você definitivamente precisa ler Ligações estáticas tardias no manual do PHP. No entanto, tentarei fornecer um resumo rápido.

Basicamente, tudo se resume ao fato de a selfpalavra - chave não seguir as mesmas regras de herança. selfsempre resolve para a classe em que é usado. Isso significa que, se você criar um método em uma classe pai e chamá-lo de uma classe filho, selfnão fará referência ao filho como seria de esperar.

A ligação estática tardia introduz um novo uso para a staticpalavra - chave, que trata dessa lacuna específica. Quando você usa static, representa a classe em que você a usa pela primeira vez, ou seja. 'liga' à classe de tempo de execução.

Esses são os dois conceitos básicos por trás disso. A maneira self, parente staticfunciona quando staticestá em jogo podem ser sutis, então ao invés de ir para mais detalhes, eu recomendo fortemente que você estudar os exemplos de página manual. Depois de entender o básico de cada palavra-chave, os exemplos são bastante necessários para ver que tipo de resultados você obterá.

zombat
fonte
Eu encontrei este artigo muito útil e descritivo, check it out [link] ( techflirt.com/tutorials/oop-in-php/late-static-binding.html )
Sadegh Shaikhi
"... a selfpalavra - chave não segue as regras de herança. selfsempre é resolvida para a classe em que é usada." - O que não significa que você não pode chamar o método estático de um pai a partir de um objeto filho self, assim como nos métodos não estáticos. Você talvez queira dizer a coisa certa, mas deve reformular isso. Tudo realmente importa apenas quando as crianças nomearem membros de forma idêntica, pois você poderá decidir a quem se referir usando static::.
DanMan
81

Do PHP: Ligações estáticas tardias - Manual :

A partir do PHP 5.3.0, o PHP implementa um recurso chamado ligação estática tardia, que pode ser usada para referenciar a classe chamada no contexto da herança estática.

A ligação estática tardia tenta resolver essa limitação introduzindo uma palavra-chave que faz referência à classe que foi chamada inicialmente no tempo de execução. ... Foi decidido não introduzir uma nova palavra-chave, mas usar a staticque já estava reservada.

Vamos ver um exemplo:

<?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
?>

As ligações estáticas tardias funcionam armazenando a classe nomeada na última "chamada de não encaminhamento". No caso de chamadas de método estático, essa é a classe explicitamente nomeada (geralmente a da esquerda do ::operador); no caso de chamadas de método não estático, é a classe do objeto. A "Encaminhamento de chamada" é um estático que é introduzido por self::, parent::, static::ou, se a subir na hierarquia de classes, forward_static_call(). A função get_called_class()pode ser usada para recuperar uma sequência com o nome da classe chamada e static::apresenta seu escopo.

Mrinmoy Ghoshal
fonte
1
Este post é para ~ 80% uma cópia literal do artigo php.net sem marcadores de citação.
WoodrowShigeru
22

Não há comportamento muito óbvio:

O código a seguir produz '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

No entanto, se removermos a declaração da função classname da classe beta, obteremos 'alphaalpha' como resultado.

Jokerius
fonte
1
Muito agradável. A mesma coisa é mostrada no manual do PHP, mas isso é muito mais claro. Para referência: php.net/manual/en/language.oop5.late-static-bindings.php (veja o ex. 4)
musicin3d
11

Estou citando o livro: "O PHP Master escreve código de ponta".

A ligação estática tardia foi um recurso introduzido no php 5.3. Ele nos permite herdar métodos estáticos de uma classe pai e fazer referência à classe filho que está sendo chamada.

Isso significa que você pode ter uma classe abstrata com métodos estáticos e referenciar as implementações concretas da classe filho usando a notação static :: method () em vez do self :: method ().

Sinta-se à vontade para dar uma olhada na documentação oficial do php também: http://php.net/manual/en/language.oop5.late-static-bindings.php


A maneira mais clara de explicar a ligação estática tardia é com um exemplo simples. Dê uma olhada nas duas definições de classe abaixo e continue a ler.

class Vehicle {
    public static function invokeDriveByStatic() {
        return static::drive(); // Late Static Binding
    }
    public static function invokeStopBySelf() {
        return self::stop(); // NOT Late Static Binding
    }
    private static function drive(){
        return "I'm driving a VEHICLE";
    }
    private static function stop(){
        return "I'm stopping a VEHICLE";
    }
}

class Car extends Vehicle  {
    protected static function drive(){
        return "I'm driving a CAR";
    }
    private static function stop(){
        return "I'm stopping a CAR";
    }
}

Vemos uma classe pai (veículo) e uma classe criança (carro). A Classe Pai possui 2 métodos públicos:

  • invokeDriveByStatic
  • invokeStopBySelf

A classe pai também possui 2 métodos particulares:

  • drive
  • stop

A classe Child substitui 2 métodos:

  • drive
  • stop

Agora vamos invocar os métodos públicos:

  • invokeDriveByStatic
  • invokeStopBySelf

Pergunte a si mesmo: qual classe chama invokeDriveByStatic/ invokeStopBySelf? A classe Pai ou Filho?

Dê uma olhada abaixo:

// This is NOT Late Static Binding
// Parent class invokes from Parent. In this case Vehicle.
echo Vehicle::invokeDriveByStatic(); // I'm driving a VEHICLE
echo Vehicle::invokeStopBySelf(); // I'm stopping a VEHICLE

// !!! This is Late Static Binding !!!!
// Child class invokes an inherited method from Parent.
// Child class = Car, Inherited method = invokeDriveByStatic().
// The inherited method invokes a method that is overridden by the Child class.
// Overridden method = drive()
echo Car::invokeDriveByStatic(); // I'm driving a CAR

// This is NOT Late Static Binding
// Child class invokes an inherited method from Parent.
// The inherited method invokes a method inside the Vehicle context.
echo Car::invokeStopBySelf(); // I'm stopping a VEHICLE

A staticpalavra-chave é usada em um padrão de design Singleton. Veja o link: https://refactoring.guru/design-patterns/singleton/php/example

juliano
fonte
7

O exemplo mais simples para mostrar a diferença.
Observe, self :: $ c

class A
{
    static $c = 7;

    public static function getVal()
    {
        return self::$c;
    }
}

class B extends A
{
    static $c = 8;
}

B::getVal(); // 7

Ligação estática tardia, note static :: $ c

class A
{
    static $c = 7;

    public static function getVal()
    {
        return static::$c;
    }
}

class B extends A
{
    static $c = 8;
}

B::getVal(); // 8
Sergey Onishchenko
fonte
4

Por exemplo:

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();
Petah
fonte
4

Olhando para isso de um "por que eu usaria isso?" perspectiva, é basicamente uma maneira de mudar o contexto no qual o método estático está sendo interpretado / executado.

Com self, o contexto é aquele em que você definiu o método originalmente. Com static, é de quem você está ligando.

DanMan
fonte
1

Além disso, observe se você atualiza variáveis ​​estáticas em classes filho. Encontrei este (um pouco) resultado inesperado em que o filho B atualiza o filho 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
// )

Você pode corrigi-lo declarando a mesma variável em cada classe filho, por exemplo:

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;        
    }
}
Frank Forte
fonte