__call e __callStatic
Parte da seção Programação Orientada a Objetos do Journey de PHP da Coddy — lição 47 de 91.
Assim como __get() e __set() interceptam o acesso a propriedades, os métodos __call() e __callStatic() interceptam chamadas de métodos. Eles são acionados quando você chama um método que não existe ou está inacessível em um objeto ou classe.
O método __call() lida com chamadas de métodos de instância. Ele recebe o nome do método e um array de argumentos:
<?php
class Messenger {
public function __call(string $name, array $arguments): string {
return "Called '$name' with: " . implode(", ", $arguments);
}
}
$msg = new Messenger();
echo $msg->sendEmail("Hello", "World");
Saída:
Called 'sendEmail' with: Hello, WorldPara chamadas de métodos estáticos, use __callStatic(). Observe que ele deve ser declarado como static:
<?php
class QueryBuilder {
public static function __callStatic(string $name, array $arguments): string {
if (str_starts_with($name, "findBy")) {
$field = substr($name, 6);
return "Finding by $field: " . $arguments[0];
}
return "Unknown method: $name";
}
}
echo QueryBuilder::findByEmail("test@example.com");
Saída:
Finding by Email: test@example.comEstes métodos mágicos são poderosos para criar APIs fluentes, implementar o encaminhamento de métodos ou construir construtores de consultas dinâmicas (query builders) onde os nomes dos métodos se traduzem em operações de banco de dados. Eles permitem que seus objetos respondam de forma inteligente a qualquer chamada de método, mesmo aquelas que você não definiu explicitamente.
Desafio
FácilVamos construir um cliente de API dinâmico que utiliza métodos mágicos para lidar com chamadas de métodos de forma flexível. Você criará uma classe que intercepta chamadas de métodos de instância e estáticos, traduzindo-as em respostas significativas do tipo API sem definir cada método explicitamente.
Você organizará seu código em dois arquivos:
ApiClient.php— Crie uma classeApiClientque simula interações de API usando métodos mágicos. Sua classe deve ter uma propriedade privada$baseUrldefinida através do construtor. Implemente dois métodos mágicos:__call()— Lida com chamadas de métodos de instância. Quando um método que começa comgeté chamado (comogetUsersougetPosts), extraia o nome do recurso (a parte após "get") e retorne"GET [baseUrl]/[resource]: [arguments joined by comma]". O nome do recurso deve ser convertido para minúsculas. Se o método não começar com "get", retorne"Unknown action: [methodName]".__callStatic()— Lida com chamadas de métodos estáticos. Quando um método que começa comcreateé chamado (comocreateUser), extraia o nome do recurso e retorne"POST /[resource]: [arguments joined by comma]". O nome do recurso deve estar em minúsculas. Para métodos que começam comdelete, retorne"DELETE /[resource]: [first argument]". Para qualquer outro método, retorne"Static action not supported: [methodName]".
main.php— Inclua o arquivo ApiClient. Você receberá três entradas: uma URL base, um identificador de recurso e um valor. Crie uma instância deApiClientcom a URL base. Em seguida, imprima quatro linhas:- Chame um método
getdinâmico usando o identificador de recurso como sufixo do nome do método (por exemplo, se o recurso for "Users", chamegetUsers) com o valor como argumento - Chame
getProductscom dois argumentos:"electronics"e"active" - Chame o método estático
createOrdercom o valor como argumento - Chame o método estático
deleteItemcom o identificador de recurso como argumento
- Chame um método
Os métodos mágicos permitem que seu cliente de API responda a qualquer chamada de método dinamicamente. Em vez de definir getUsers(), getPosts(), getProducts() e dezenas de outros, um único método __call() lida com todos eles examinando o nome do método e extraindo a informação relevante.
Folha de consulta
O método __call() intercepta chamadas de métodos de instância que não existem ou estão inacessíveis. Ele recebe o nome do método e um array de argumentos:
<?php
class Messenger {
public function __call(string $name, array $arguments): string {
return "Called '$name' with: " . implode(", ", $arguments);
}
}
$msg = new Messenger();
echo $msg->sendEmail("Hello", "World");
// Saída: Called 'sendEmail' with: Hello, World
O método __callStatic() intercepta chamadas de métodos estáticos. Ele deve ser declarado como static:
<?php
class QueryBuilder {
public static function __callStatic(string $name, array $arguments): string {
if (str_starts_with($name, "findBy")) {
$field = substr($name, 6);
return "Finding by $field: " . $arguments[0];
}
return "Unknown method: $name";
}
}
echo QueryBuilder::findByEmail("test@example.com");
// Saída: Finding by Email: test@example.com
Esses métodos mágicos são úteis para criar APIs fluentes, implementar o encaminhamento de métodos ou construir construtores de consultas dinâmicas onde os nomes dos métodos se traduzem em operações.
Experimente você mesmo
<?php
require_once 'ApiClient.php';
// Ler entradas
$baseUrl = trim(fgets(STDIN));
$resource = trim(fgets(STDIN));
$value = trim(fgets(STDIN));
// TODO: Criar uma instância do ApiClient com a URL base
// TODO: Chamar um método get dinâmico usando o recurso como sufixo do nome do método
// Dica: Use chamadas de método variáveis como $api->{"get" . $resource}($value)
// TODO: Chamar getProducts com dois argumentos: "electronics" e "active"
// TODO: Chamar o método estático createOrder com o valor como argumento
// Dica: Use ApiClient::createOrder($value)
// TODO: Chamar o método estático deleteItem com o recurso como argumento
?>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
1Fundamentos de POO
Arquivos ExternosIntrodução à POOClasses vs ObjetosA Palavra-chave $thisMétodosPropriedadesConstrutor (__construct)Destrutor (__destruct)Recapitulação - Calculadora Simples4Herança
Herança BásicaA Palavra-chave parent::Sobrescrita de MétodoA Palavra-chave finalClasses AbstratasRecapitulação - Hierarquia de Funcionários7Encapsulamento
Público, Protegido, PrivadoModificadores de Acesso em DetalhesGetters e SettersOcultação de InformaçãoPromoção de Construtor (8.0)Recapitulação - Sistema de Registro de Alunos10Conceitos Avançados de POO
Composição vs HerançaInjeção de DependênciaClasses AnônimasEnums (PHP 8.1)Fibers (PHP 8.1)Mergulho Profundo em Clonagem de ObjetosGeradores e Iteradores13Padrões de Projeto Parte 2
Padrão CommandPadrão AdapterPadrão DecoratorPadrão Template MethodPadrão StatePadrão CompositePadrão Repository2Namespaces e Autoloading
Introdução a NamespacesA Palavra-chave usePadrão de Autoloading PSR-4Autoloader do ComposerRecapitulação - Projeto Organizado5Interfaces e Contratos
Introdução a InterfacesImplementando InterfacesImplementação de Múltiplas InterfacesInterface vs Classe AbstrataType Hinting com InterfacesRecapitulação - Calculadora de Formas8Métodos Mágicos
Introdução aos Métodos Mágicos__toString e __debugInfo__get, __set, __isset, __unset__call e __callStatic__clone e Clonagem de Objetos__serialize e __unserializeRecapitulação - Coleção Personalizada11Sistema de Tipos e Tratamento de Erros
Declarações de TiposTipos AnuláveisTipos de União e InterseçãoClasses de ExceçãoHierarquia de Exceções PersonalizadasTry, Catch, FinallyRecapitulação - Validador de Formulário14Projeto: Gerenciamento de Biblioteca
Visão Geral do ProjetoClasses Livro e Usuário3Propriedades de Classe
Propriedades de Instância vs EstáticasConstantes em ClassesMétodos e Propriedades EstáticosPropriedades Privadas e ProtegidasPropriedades Readonly (PHP 8.1)Recapitulação - Gerenciador de Conta Bancária6Polimorfismo
Sobrescrita de Métodos RevisitadaPolimorfismo via InterfacesType Hinting e Union TypesLate Static BindingRecapitulação - Processador de Pagamentos9Traits
Introdução a TraitsUsando Múltiplos TraitsResolução de Conflitos de TraitsMétodos Abstratos em TraitsTraits vs Herança12Padrões de Projeto Parte 1
Introdução aos Padrões de ProjetoPadrão SingletonPadrão FactoryPadrão ObserverPadrão Strategy