Cinco técnicas testadas em batalha que seu desenvolvedor de API do WordPress não está usando
Publicados: 2022-03-11Uma das melhores maneiras de elevar seu status como desenvolvedor WordPress, pelo menos aos olhos de seus clientes, é se tornar habilidoso no consumo de APIs. Aqui está um cenário comum para a implementação da API do WordPress: Seu cliente solicita que você adicione um widget ao site dele – digamos, por exemplo, um widget de assinatura de e-mail. Você pega algum código do serviço de e-mail de terceiros - talvez seja uma tag de script ou um iframe
- cola na página e responde ao seu cliente: "Entendi!"
Infelizmente, você está lidando com um cliente um pouco mais exigente e ele percebe as seguintes imperfeições:
- Embora o widget, como o resto do site, apresente uma fonte sans-serif, não é exatamente a correta. O widget está usando Helvetica em vez da fonte personalizada que você instalou.
- O formulário de inscrição do widget aciona um novo carregamento de página, que pode ser prejudicial se colocado no meio de um artigo.
- O widget parece demorar um pouco a mais para carregar depois do resto da página, o que parece chocante e barato.
- O cliente gostaria que os assinantes fossem marcados com metadados com base na postagem da qual se inscreveram, e o widget não oferece nada remotamente parecido com essa funcionalidade.
- O cliente acha irritante que agora eles tenham que gerenciar dois painéis (wp-admin e a área de administração do serviço de e-mail).
Neste ponto, uma de duas coisas pode razoavelmente acontecer. Você pode declarar esses itens como “agradáveis” e tranquilizar seu cliente sobre os méritos de uma solução 80/20, ou você pode atender a essas solicitações. Em minha experiência pessoal, descobri que atender a essas solicitações - ou seja, demonstrar domínio de serviços de terceiros - é uma maneira confiável de convencer o cliente de que você é um tipo de assistente do WordPress. Além disso, muitas vezes é divertido de fazer.
Na última década, usei o WordPress como plataforma para consumo de API contra provavelmente 50 APIs diferentes. Algumas das APIs mais comuns foram MailChimp, Google Analytics, Google Maps, CloudFlare e Bitbucket. Mas e se você precisar fazer mais, e se precisar de uma solução personalizada?
Como desenvolver um cliente de API do WordPress
Neste artigo, vou desenvolver uma API genérica de “serviço de e-mail”, tentando o meu melhor para manter as coisas o mais agnósticas possível. No entanto, acho razoável supor que estamos lidando com uma API REST JSON. Aqui estão alguns tópicos básicos que podem ajudá-lo a aproveitar os pontos técnicos deste artigo:
- A família de funções HTTP do WordPress
- JSON
- DESCANSO
Se você se encontra marginalmente familiarizado com esses tópicos e está interessado em se aprofundar, faça uma pausa agora e baixe o excelente aplicativo Postman. Ele permite que você se comunique com APIs sem escrever código.
No entanto, se você não estiver familiarizado com eles, continue lendo de qualquer maneira. Um público técnico com algum grau de experiência em WordPress tirará o máximo proveito deste artigo, mas terei o cuidado de explicar o valor de cada técnica de uma maneira menos técnica. Um leitor não técnico deixará este artigo capaz de avaliar o ROI de cada ponto antes de patrociná-lo e julgar a qualidade da implementação uma vez entregue.
Nota: Caso você precise de um curso de atualização rápido, você pode conferir nosso guia WordPress REST API.
Sem mais preâmbulos, permita-me compartilhar com você um punhado de técnicas diferentes que aprecio na maioria das APIs, projetos e equipes com as quais trabalho.
Transientes: quando segurá-los, quando dobrá-los
No meu parágrafo de abertura, observei que o cliente achava irritante ocupar duas áreas administrativas: wp-admin e o painel do serviço de e-mail. Uma boa maneira de resolver isso seria fornecer a eles um widget de painel no wp-admin, para mostrar um resumo da atividade recente do assinante.
Mas, novamente, isso pode exigir várias solicitações HTTP para a API remota (a API fornecida pelo serviço de e-mail), resultando em longos carregamentos de página. A solução para esse problema de desempenho é armazenar chamadas de API como transientes. Este artigo do Codex fornece uma ótima explicação que você definitivamente deveria ler, mas vou resumir assim:
- Obtenha os dados da API remota.
- Armazene-o usando
set_transient()
com um tempo de expiração de sua escolha com base em seu próprio julgamento sobre desempenho, limites de taxa e margem de erro na exibição de dados desatualizados neste aplicativo específico. - Prossiga em sua lógica de negócios – processando os dados, retornando um valor, seja qual for o caso.
- Quando você precisar dos dados novamente, como no próximo carregamento de página, verifique-os no cache transitório usando
get_transient()
antes de concluir que precisa obtê-los da API.
Eu considero isso uma base útil e viável, mas você pode dar um passo adiante se pensar por um momento sobre os verbos REST. Dos cinco métodos mais comuns (GET, POST, PATCH, PUT, DELETE), apenas um deles pertence ao seu cache transitório. Você consegue adivinhar qual? É GET. Em meus plugins, quase sempre tenho uma classe PHP dedicada a abstrair chamadas para a API remota em questão, e um argumento ao instanciar essa classe é o método HTTP. Se não for uma chamada GET, não invocarei nenhuma camada de cache.
Além disso, se não for uma chamada GET, é lógico que estou tomando alguma ação para alterar os dados remotos de alguma forma, talvez adicionando, editando ou removendo um assinante de e-mail. Este pode ser um bom momento para invalidar o cache existente para esse recurso, via delete_transient()
.
Para retornar ao nosso exemplo de uma API de assinatura de e-mail do WordPress, veja como isso funcionaria na prática:
- Um widget de painel para mostrar assinantes recentes chamará o endpoint da API para
/subscribers
por meio de uma solicitação GET. Por ser uma solicitação GET, ela é armazenada no meu cache transitório. - Um widget de barra lateral para se inscrever na lista de e-mail chamará o endpoint da API para
/subscribers
por meio de uma solicitação POST. Por ser uma solicitação POST, não apenas evitará meu cache transitório, mas também me provocará a excluir a parte relevante do meu cache transitório, para que o widget do painel reflita esse novo assinante. - Ao nomear transientes, geralmente os organizo nomeando-os literalmente após a URL da API remota que estou chamando. Essa é uma maneira prática de identificar o transiente correto a ser excluído. Se for um endpoint que recebe argumentos, vou concatená-los em uma string e adicioná-los ao nome transiente também.
Como cliente ou outra parte interessada menos técnica, você deve solicitar especificamente o cache temporário — ou pelo menos uma discussão sobre isso — sempre que o aplicativo estiver extraindo dados de um serviço remoto. Você deve se familiarizar com o excelente plugin Query Monitor para ter uma visão de como os transientes estão funcionando. Ele fornecerá uma interface para navegar em quais dados estão sendo armazenados como transitórios, com que frequência e por quanto tempo.
Às vezes, os transitórios não são bons o suficiente
Alguns serviços premium de hospedagem WordPress não permitem que você use transientes em produção. Eles têm código em execução, talvez na forma de um plug-in MU ou algum outro script, que interceptará sua tentativa de usar a API de transientes e armazenará essas informações por meio do cache do objeto. O WP-Engine, em sua configuração mais comum, é um excelente exemplo disso.
Se você está simplesmente armazenando e recuperando dados, na verdade você não precisa se preocupar com isso e pode nem perceber que está acontecendo. A família inteira de *_transient()
fornecerá o mesmo resultado final, apenas filtrado para usar o cache de objeto em vez do cache transitório. Onde você pode ter problemas, porém, é ao tentar excluir transientes. Aqui está o porquê.
Se sua integração de API for complexa o suficiente para merecer sua própria página de configurações, você pode querer incluir uma interface do usuário para permitir que o usuário administrador limpe todo o cache temporário do seu plug-in . O uso mais comum desse botão seria para quando o cliente altera alguns dados diretamente no serviço remoto, e deseja invalidar o cache que estamos armazenando no WordPress. Este botão também pode ser útil se o cliente alterar as credenciais da conta, chaves de API ou apenas como um botão de “redefinição de fábrica” para depuração.
Mesmo que você tenha sido inteligente o suficiente para namespace todas as suas chaves transitórias para que você tenha alguma esperança de identificar cada uma delas para delete_transient()
, o melhor cenário provavelmente ainda envolve SQL bruto, que eu sempre tento evitar no WordPress:
<?php // Purge all the transients associated with our plugin. function purge() { global $wpdb; $prefix = esc_sql( $this -> get_transient_prefix() ); $options = $wpdb -> options; $t = esc_sql( "_transient_timeout_$prefix%" ); $sql = $wpdb -> prepare ( " SELECT option_name FROM $options WHERE option_name LIKE '%s' ", $t ); $transients = $wpdb -> get_col( $sql ); // For each transient... foreach( $transients as $transient ) { // Strip away the WordPress prefix in order to arrive at the transient key. $key = str_replace( '_transient_timeout_', '', $transient ); // Now that we have the key, use WordPress core to the delete the transient. delete_transient( $key ); } } ?>
Não é conveniente, não é eficiente. Em vez disso, essa situação exige cache de objetos porque o cache de objetos nos dá uma maneira conveniente de agrupar valores armazenados em cache . Dessa forma, quando você precisar esvaziar todos os valores em cache relacionados ao seu plugin, é uma simples chamada de uma linha para wp_cache_delete( $key, $group )
.
Eu resumiria tudo isso dizendo: Você não pode ser um especialista em consumir APIs se ainda não for um especialista em gerenciar o cache desses dados.
Como cliente, a principal coisa a observar é o comportamento de cache aberrante entre os ambientes de preparação e produção. Em outras palavras, embora testar um novo lote de trabalho em teste seja sempre uma boa prática, o armazenamento em cache é algo que também deve ser testado em produção com igual cuidado.

A API remota pode ajudar a informar sua hierarquia de classes PHP
Ao apresentar as várias classes PHP para o meu plugin, muitas vezes acho útil imitar a forma como os endpoints da API são definidos – por exemplo, o que os endpoints a seguir parecem ter em comum?
- https://api.example-email-service.com/v1/subscribers.json
- https://api.example-email-service.com/v1/lists.json
- https://api.example-email-service.com/v1/campaigns.json
Todos eles retornam coleções , ou seja, o resultado de uma solicitação GET, retornando resultados zero-para-muitos onde cada resultado é membro de um array. Isso pode parecer bastante óbvio, mas acho que é um prompt útil para a seguinte estrutura de classes no meu código PHP:
-
class.collection.php
, uma classe abstrata -
class.subscribers.php
estende a classe abstrata,Collection
. -
class.lists.php
estende a classe abstrata,Collection
. -
class.campaigns.php
estende a classe abstrata,Collection
.
A classe abstrata teria como único argumento uma matriz de parâmetros de consulta: coisas como paginação, coluna de classificação, ordem de classificação e filtros de pesquisa. Ele teria métodos para tarefas comuns, como chamar a API remota, manipular erros e talvez transformar os resultados em um menu HTML <select>
ou uma sugestão automática de jQueryUI. As classes que instanciam a classe abstrata podem ser bem curtas, talvez fazendo pouco mais do que especificar a string a ser usada na URL do endpoint da API *.json
.
Da mesma forma, o que os pontos de extremidade a seguir têm em comum?
- https://api.example-email-service.com/v1/subscribers/104abyh4.json
- https://api.example-email-service.com/v1/lists/837dy1h2.json
- https://api.example-email-service.com/v1/campaigns/9i8udr43.json
Todos eles retornam um item , ou seja, exatamente um membro específico e exclusivo de uma coleção: coisas como um assinante de e-mail específico, uma lista de e-mail ou uma campanha de e-mail. Portanto, gosto de usar a seguinte estrutura no meu código PHP:
-
class.item.php
, uma classe abstrata -
class.subscriber.php
estende a classe abstrata,Item
. -
class.list.php
estende a classe abstrata,Item
. -
class.campaign.php
estende a classe abstrata,Item
.
A classe abstrata tomaria como único argumento uma string para identificar o item específico que está sendo solicitado. Mais uma vez, as classes que estão sendo instanciadas podem ser bem curtas, talvez fazendo pouco mais do que especificar a string a ser usada em */duy736td.json
.
Existem muitas abordagens para estruturar a herança de classes, mas mesmo que você adote uma abordagem diferente do que descrevi acima, aposto que há uma boa chance de que a estrutura da API remota possa ajudar a informar a estrutura do seu aplicativo.
Como cliente, um sintoma comum de arquitetura ruim é quando você precisa solicitar a mesma alteração várias vezes em um aplicativo. Por exemplo, se você solicitar que os relatórios retornem 100 resultados por página em vez de 10 e precisar repetir essa solicitação para relatórios de assinantes, relatórios de campanha, relatórios de cancelamento de assinatura etc., você pode estar detectando uma arquitetura de classe ruim. Nessa situação, vale a pena perguntar à sua equipe se eles se beneficiariam de um ciclo de refatoração: um corpo de trabalho em que o objetivo não é alterar o comportamento do produto, mas sim melhorar o código subjacente para que seja mais fácil alterar o comportamento do produto no futuro.
O caso de uso perfeito para WP_Error
Tenho vergonha de admitir que demorei anos a mais do que deveria para começar a usar corretamente a família de funções WP_Error
em meu código. Eu tendia a apenas codificar meu caminho, assumindo que nunca haveria erros com os quais valesse a pena se preocupar programaticamente ou lidando com eles caso a caso. Trabalhar com APIs remotas corta essa mentalidade como um raio laser, porque apresenta um caso de uso extremamente conveniente e poderoso para usar WP_Error
.
Lembre-se anteriormente que mencionei que muitas vezes tenho uma classe PHP cujo objetivo é fazer solicitações HTTP para a API remota. Quando você retira todo o clichê, toda a manipulação de dados, todas as preocupações secundárias, essa classe realmente se resume a chamar wp_remote_request()
para obter um objeto de resposta HTTP da API. Convenientemente, wp_remote_request()
retornará um WP_Error
se a chamada falhar na execução por algum motivo, mas e se a chamada obtiver sucesso em retornar uma resposta HTTP de um tipo desfavorável?
Como exemplo, talvez tenhamos feito uma chamada para o endpoint /lists.json
, mas essa conta específica ainda não tem nenhuma lista configurada. Isso retornaria uma resposta HTTP válida, mas com um código de status de 400. Embora isso não seja exatamente um erro fatal em si, da perspectiva de algum código de front-end que deseja transformar essa chamada de API em um menu suspenso, um 400 pode também ser um WSOD! Portanto, acho útil fazer alguma análise adicional no resultado de wp_remote_request()
, potencialmente retornando um WP_Error
afinal:
<?php function call() { $response = wp_remote_request( $this -> url, $this -> args ); $code = wp_remote_retrieve_response_code( $response ); $first_digit = $code[0]; $good_responses = array( 2, 3 ); if( ! in_array( $first_digit, $good_responses ) { $body = wp_remote_retrieve_body( $response ); $out = new WP_Error( $code, $body ); } else { $out = $response; } return $out; } ?>
Esse padrão pode ajudar a simplificar o código que invoca nossa classe chamadora, porque sabemos que podemos confiar com segurança em is_wp_error()
antes de prosseguir com nossa saída.
Como cliente, você deve ocasionalmente desempenhar o papel de um usuário mal-intencionado, um usuário confuso e um usuário impaciente. Use o aplicativo de maneiras que ele não deveria ser usado. Faça as coisas que seus desenvolvedores parecem não querer que você faça. Tome nota do que acontece. Você recebe mensagens de erro úteis? Você recebe alguma mensagem de erro? Caso contrário, pode valer a pena patrocinar um trabalho em torno de um melhor tratamento de erros.
O belo poder de depuração de ob_get_clean()
A web programável moderna, onde quase todos os sites consomem as APIs de outros sites e são consumidos por meio de sua própria API, tornou-se uma arena incrivelmente poderosa para o código. Mas é precisamente essa qualidade que também pode torná-lo bastante lento.
É comum que solicitações HTTP remotas sejam as partes mais demoradas de um determinado carregamento de página. Por esse motivo, muitos componentes orientados por API são executados via Ajax ou cron. Por exemplo, uma sugestão automática para pesquisar em uma lista de assinantes de e-mail provavelmente deve executar ping na fonte de dados remota sob demanda, a cada pressionamento de tecla, em vez de carregar todos os 100.000 assinantes no DOM à medida que a página é carregada. Se isso não for uma opção, talvez uma consulta grande possa ser sincronizada em uma tarefa cron noturna, para que os resultados possam ser obtidos de um espelho local em vez da API remota.
O problema com essa abordagem é que pode ser difícil de depurar. Em vez de simplesmente ativar o WP_DEBUG
e deixar as mensagens de erro rolarem na janela do seu navegador, você fica preso olhando no console de rede do navegador ou seguindo um arquivo de log enquanto uma tarefa cron (espero?) está sendo executada. Acho isso desconfortável.
Uma maneira de melhorar essa situação é fazer chamadas cuidadosas e estratégicas para error_log()
. Mas, novamente, um problema comum com o log é que, com aplicativos grandes ou ocupados, os logs de erros podem ficar muito grandes, ou crescer muito rapidamente, para serem úteis para monitoramento ou análise. Portanto, temos que ser seletivos com o que registramos, pensando nisso tanto quanto fazemos com nossa lógica de aplicativo real . É uma pena ter tido tempo para registrar algum erro de caso extremo exótico que só parece ocorrer intermitentemente em alguma tarefa cron infrequente apenas para perceber que a verdadeira natureza do bug escapou de você mais uma vez porque você falhou ao registrar algum membro específico da matriz , digamos, do valor ofensivo.
Portanto, minha filosofia se tornou, nem sempre logo, mas quando o faço, logo tudo . Em outras palavras, depois de identificar uma função particularmente preocupante, vou registrá-la com a maior rede possível:
<?php function debug( $bug ) { ob_start(); var_dump( $bug ); $out = ob_get_clean(); error_log( $out ); } ?>
Isso equivale a var_dump()
'colocando todo o valor do bug em uma única entrada no arquivo de log de erros.
Como cliente, vale a pena verificar periodicamente o uso total de memória de arquivo para seu aplicativo. Se você perceber que de repente está enfrentando os limites de armazenamento em sua conta de hospedagem, há uma boa chance de que um log de erros tenha sido o culpado. Seus desenvolvedores se beneficiarão de um ciclo de trabalho focado em um melhor registro – e seus clientes também!
Não exatamente clickbait, mas serve
Por favor, perdoe a estrutura da lista deste artigo. Não consegui forçar esses pontos em um tema de artigo mais unificador porque esses padrões são muito genéricos: eles se aplicam a qualquer endpoint JSON REST e a qualquer saída do WordPress .
Eles são os padrões que vejo ocorrendo repetidamente, independentemente de qual seja a API remota ou para que a estamos usando no WordPress. Cheguei a reunir todos esses tipos de princípios em um clichê de plugins que acelera muito meu trabalho. Você tem pontos semelhantes que você mantém para cada projeto? Por favor, compartilhe-os para que eu possa roubá-los e adicioná-los ao meu clichê!