Menu
Coddy logo textTech

Late Static Binding

Parte da seção Programação Orientada a Objetos do Journey de PHP da Coddy — lição 36 de 91.

Quando você usa self:: em uma classe pai, ele sempre se refere à classe onde foi escrito, não à classe que está sendo realmente utilizada. Isso pode causar comportamentos inesperados na herança. Late static binding resolve esse problema usando a palavra-chave static::.

Considere este exemplo onde self:: não funciona como esperado:

<?php
class ParentClass {
    public static function create() {
        return new self();
    }
    
    public static function getClassName() {
        return self::class;
    }
}

class ChildClass extends ParentClass {}

$obj = ChildClass::create();
echo get_class($obj) . "\n";
echo ChildClass::getClassName();

Saída:

ParentClass
ParentClass

Embora tenhamos chamado ChildClass::create(), obtivemos um objeto ParentClass porque self:: é resolvido em tempo de compilação. Agora vamos usar static:: em vez disso:

<?php
class ParentClass {
    public static function create() {
        return new static();
    }
    
    public static function getClassName() {
        return static::class;
    }
}

class ChildClass extends ParentClass {}

$obj = ChildClass::create();
echo get_class($obj) . "\n";
echo ChildClass::getClassName();

Saída:

ChildClass
ChildClass

Com static::, o PHP espera até o tempo de execução para determinar qual classe usar. Isso é chamado de ligação "tardia" (late binding) porque a decisão é atrasada. É essencial para métodos factory e qualquer método estático que deva respeitar a herança.

Ponto Chave: Use static:: em vez de self:: quando você deseja que as classes filhas herdem e personalizem corretamente o comportamento estático.

challenge icon

Desafio

Fácil

Vamos construir um sistema de registro de modelos que demonstra o poder do late static binding. Você criará uma classe base Model com um método factory que as classes filhas podem herdar — e graças ao static::, cada classe filha criará corretamente instâncias de si mesma em vez da classe pai.

Você organizará seu código em quatro arquivos:

  • Model.php — Crie uma classe base Model com uma propriedade estática protegida $tableName definida como "models". Adicione um método estático create() que usa new static() para retornar uma nova instância de qualquer classe que o chamou. Adicione outro método estático getTable() que retorna static::$tableName. Por fim, adicione um método describe() que retorna "[ClassName] from table [tableName]" usando static::class e static::$tableName.
  • User.php — Crie uma classe User que estende Model. Inclua o arquivo pai. Sobrescreva a propriedade estática $tableName para "users". A classe herda todos os métodos de Model sem quaisquer alterações.
  • Product.php — Crie uma classe Product que estende Model. Inclua o arquivo pai. Sobrescreva a propriedade estática $tableName para "products". Novamente, a classe herda todos os métodos do pai.
  • main.php — Inclua os arquivos User e Product. Use o método factory create() herdado para criar instâncias de User e Product. Para cada instância, chame describe() e imprima o resultado em sua própria linha. Em seguida, imprima o nome da tabela para cada classe usando User::getTable() e Product::getTable(), cada um em sua própria linha.

A mágica aqui é que os métodos create() e getTable() são definidos apenas na classe pai Model, mas funcionam corretamente com as classes filhas porque o static:: atrasa a vinculação até o tempo de execução. Quando você chama User::create(), o PHP cria uma instância de User — não uma instância de Model — porque static:: se refere à classe que foi realmente chamada.

Sua saída deve mostrar quatro linhas: a descrição para a instância de User, a descrição para a instância de Product, o nome da tabela User e o nome da tabela Product.

Folha de consulta

A palavra-chave self:: em uma classe pai sempre se refere à classe onde foi escrita, resolvida em tempo de compilação. O Late static binding (ligação estática tardia) com static:: resolve a referência em tempo de execução, permitindo que as classes filhas herdem e personalizem adequadamente o comportamento estático.

Usando self:: (ligação em tempo de compilação)

<?php
class ParentClass {
    public static function create() {
        return new self();  // Sempre cria ParentClass
    }
    
    public static function getClassName() {
        return self::class;  // Sempre retorna "ParentClass"
    }
}

class ChildClass extends ParentClass {}

$obj = ChildClass::create();  // Retorna instância de ParentClass
echo ChildClass::getClassName();  // Saída: ParentClass

Usando static:: (ligação em tempo de execução)

<?php
class ParentClass {
    public static function create() {
        return new static();  // Cria instância da classe que está chamando
    }
    
    public static function getClassName() {
        return static::class;  // Retorna o nome da classe que está chamando
    }
}

class ChildClass extends ParentClass {}

$obj = ChildClass::create();  // Retorna instância de ChildClass
echo ChildClass::getClassName();  // Saída: ChildClass

Ponto Chave: Use static:: em vez de self:: quando desejar que as classes filhas herdem e personalizem adequadamente o comportamento estático. Isso é essencial para métodos factory e qualquer método estático que deva respeitar a herança.

Experimente você mesmo

<?php

require_once 'User.php';
require_once 'Product.php';

// TODO: Use o método de fábrica herdado create() para criar uma instância de User

// TODO: Use o método de fábrica herdado create() para criar uma instância de Product

// TODO: Chame describe() na instância de User e imprima o resultado

// TODO: Chame describe() na instância de Product e imprima o resultado

// TODO: Imprima User::getTable()

// TODO: Imprima Product::getTable()

?>
quiz iconTeste seus conhecimentos

Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.

Todas as lições de Programação Orientada a Objetos