W3docs

insteadof

A palavra-chave "insteadof" é usada em PHP para especificar qual trait deve ser utilizada em vez de outra para um determinado método. Neste artigo, exploramos seu uso.

A palavra-chave insteadof do PHP

insteadof é uma palavra-chave do PHP usada dentro de um bloco use para resolver um conflito de métodos entre dois ou mais traits. Quando uma classe usa vários traits que definem um método com o mesmo nome, o PHP não consegue decidir qual prevalece e lança um erro fatal. insteadof informa ao PHP qual método do trait deve ser mantido e qual deve ser descartado.

Esta página explica o conflito que ele resolve, sua sintaxe exata, como ele se combina com a palavra-chave as para preservar o método "perdedor" e as armadilhas que vale conhecer.

O problema que insteadof resolve

Um trait é um bloco reutilizável de métodos que é copiado para uma classe em tempo de compilação. Se dois traits definem um método com o mesmo nome e uma classe usa ambos, o PHP não tem como escolher um deles. O resultado é um erro fatal de colisão:

<?php

trait FileLogger {
  function log() { echo "Writing to a file."; }
}
trait DatabaseLogger {
  function log() { echo "Writing to the database."; }
}

class Service {
  use FileLogger, DatabaseLogger; // No conflict resolution
}

// PHP Fatal error: Trait method DatabaseLogger::log has not been
// applied as Service::log, because of collision with FileLogger::log

A classe nunca chega a ser criada — este é um erro em tempo de compilação, não em tempo de execução. insteadof é a forma de indicar ao PHP qual método deve ser aplicado.

Sintaxe

insteadof é escrito dentro do bloco de chaves de uma instrução use:

use TraitWeKeep, TraitWeDrop {
    TraitWeKeep::methodName insteadof TraitWeDrop;
}

Leia como: "use o methodName de TraitWeKeep em vez de o de TraitWeDrop." O trait nomeado após insteadof tem sua versão desse método removida da classe.

<?php

trait FileLogger {
  function log() { echo "Writing to a file."; }
}
trait DatabaseLogger {
  function log() { echo "Writing to the database."; }
}

class Service {
  use FileLogger, DatabaseLogger {
    FileLogger::log insteadof DatabaseLogger;
  }
}

$service = new Service();
$service->log();
// Output: Writing to a file.

Resolvendo conflitos com mais de um trait

Se três ou mais traits colidem, liste todos os traits que deseja excluir após insteadof, separados por vírgulas:

<?php

trait Json    { function format() { echo "JSON output"; } }
trait Xml      { function format() { echo "XML output"; } }
trait Csv      { function format() { echo "CSV output"; } }

class Report {
  use Json, Xml, Csv {
    Json::format insteadof Xml, Csv;
  }
}

$report = new Report();
$report->format();
// Output: JSON output

Mantendo o outro método com as

insteadof descarta um método — mas muitas vezes você não quer perdê-lo, apenas quer disponibilizá-lo com outro nome para que ambos estejam acessíveis. É isso que a palavra-chave as faz: ela cria um alias para o método excluído com um novo nome, permitindo que ele sobreviva à resolução do conflito.

<?php

trait FileLogger {
  function log($msg) { return "[file] $msg"; }
}
trait DatabaseLogger {
  function log($msg) { return "[db] $msg"; }
}

class Service {
  use FileLogger, DatabaseLogger {
    FileLogger::log insteadof DatabaseLogger;  // FileLogger::log becomes log()
    DatabaseLogger::log as logToDb;            // DatabaseLogger::log survives as logToDb()
  }
}

$service = new Service();
echo $service->log("started"), "\n";      // [file] started
echo $service->logToDb("started"), "\n";  // [db] started

insteadof e as são quase sempre usados juntos: insteadof escolhe o vencedor, as resgata o perdedor sob um alias. A palavra-chave as também pode alterar a visibilidade de um método (por exemplo, FileLogger::log as protected;).

Armadilhas

  • insteadof resolve apenas colisões de nomes. Se os dois métodos não compartilham um nome, não há conflito e insteadof é desnecessário.
  • Você deve referenciar um método que realmente existe no trait nomeado, usando a sintaxe Trait::method. Um erro de digitação gera um erro fatal.
  • Ele não mescla comportamentos. O método excluído simplesmente não é aplicado; insteadof nunca chama ambos os métodos.
  • Um método de classe filha ainda prevalece sobre um trait. A ordem de resolução de métodos é: métodos definidos na própria classe substituem os métodos de trait, e os métodos de trait substituem os métodos herdados (da classe pai).

Quando usar

Recorra a insteadof sempre que você compuser uma classe a partir de múltiplos traits e dois deles expuserem o mesmo nome de método — situação comum ao misturar traits de terceiros que você não controla. Combinado com as, permite compor comportamentos de traits independentes sem renomear o código-fonte deles, mantendo suas classes modulares e livres de conflitos.

Prática

Prática
O que a palavra-chave 'insteadof' faz em PHP?
O que a palavra-chave 'insteadof' faz em PHP?
Was this page helpful?