Respostas:
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 self
palavra - chave não seguir as mesmas regras de herança. self
sempre 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, self
não fará referência ao filho como seria de esperar.
A ligação estática tardia introduz um novo uso para a static
palavra - 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
, parent
e static
funciona quando static
está 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á.
self
palavra - chave não segue as regras de herança. self
sempre é 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::
.
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
static
que 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 porself::
,parent::
,static::
ou, se a subir na hierarquia de classes,forward_static_call()
. A funçãoget_called_class()
pode ser usada para recuperar uma sequência com o nome da classe chamada estatic::
apresenta seu escopo.
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.
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 static
palavra-chave é usada em um padrão de design Singleton. Veja o link: https://refactoring.guru/design-patterns/singleton/php/example
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
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.
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;
}
}