Criando um plugin WordPress que usa APIs de serviço, “From Soup To Nuts”
Publicados: 2022-03-10Um número cada vez maior de APIs publicamente disponíveis fornece serviços poderosos para expandir a funcionalidade de nossos aplicativos. O WordPress é um CMS incrivelmente dinâmico e flexível que alimenta tudo, desde pequenos blogs pessoais até grandes sites de comércio eletrônico e tudo mais. Parte do que torna o WordPress tão versátil é seu poderoso sistema de plugins , que torna incrivelmente fácil adicionar funcionalidades.
Veremos como eu fiz o GitHub Pipeline, um plugin que permite exibir dados da API do GitHub em páginas do WordPress usando códigos de acesso. Darei exemplos específicos e trechos de código, mas considere a técnica descrita aqui como um modelo de como consumir qualquer API de serviço com um plug-in.
Leitura adicional no SmashingMag:
- WordPress Essentials: Como criar um plugin WordPress
- Como implantar plugins do WordPress com o GitHub usando transitórios
- Três abordagens para adicionar campos configuráveis ao seu plug-in
Começaremos do início, mas supõe-se um grau de familiaridade com o WordPress e o desenvolvimento de plugins, e não gastaremos tempo em tópicos para iniciantes, como instalar o WordPress ou o Composer.
Você precisará:
- um ambiente PHP, com uma nova instalação do WordPress;
- uma conta do GitHub (ou outro provedor de API, se você quiser improvisar);
- Compositor (recomendado).
Escolhendo uma API
O primeiro passo para escrever este tipo de plugin é escolher uma API. Neste tutorial, usaremos a API do GitHub. Se você está pensando em usar outra API, considere alguns fatores importantes que podem afetar a recompensa do seu projeto.
Uma das primeiras coisas que observo é o rigor e a qualidade da documentação da API. Se for escasso ou desatualizado, prepare-se para gastar tempo vasculhando o código-fonte para responder às suas próprias perguntas. Além disso, considere o grau de maturidade da API e a responsabilidade do provedor quanto à versão dela. Nada é pior do que investir tempo na criação de algo ótimo, apenas para quebrar por causa de alterações na API upstream. Procure um sistema de controle de versão nas URLs de endpoint.
// good https://api.stable.com/v1/user/ // danger https://api.dodgy.com/user/
Correndo o risco de afirmar o óbvio, a programação em uma API de terceiros envolve uma relação de confiança e nem todas as APIs são criadas da mesma forma.
Comprando uma biblioteca
Marcas estabelecidas geralmente distribuem bibliotecas ou SDKs para facilitar o trabalho com sua API. Se esse não for o caso, lembre-se de pesquisar se alguém já escreveu uma biblioteca antes de reinventar a roda. O Google e o GitHub são dois ótimos lugares para começar sua pesquisa. O número de estrelas ou bifurcações no GitHub é uma boa indicação da eficácia de uma biblioteca. A idade dos commits mais recentes e/ou o número de questões abertas são uma indicação de quão ativamente ele é mantido. No meu caso, tive a sorte de encontrar a adorável API PHP GitHub, da KNP Labs.
Escrevendo o seu próprio com Guzzle
Se não houver uma biblioteca satisfatória para o seu provedor, você ainda não deve começar do zero porque ferramentas como Guzzle facilitam muito o trabalho com solicitações HTTP. Guzzle envolve a biblioteca cURL do PHP e remove muitas das dores de cabeça normalmente associadas à configuração e realização de solicitações manualmente. Mesmo se você estiver fazendo apenas uma ou duas requisições, eu ainda recomendo usá-lo porque ele tornará seu código mais robusto, e instalá-lo com o Composer é muito fácil.
Configurando o plug-in
Começaremos com o esqueleto básico de um plugin WordPress mínimo, um diretório com dois arquivos. Escolher um nome de pasta descritivo e exclusivo é importante para evitar conflitos com outros plugins. Se o nome do seu plugin for um pouco genérico, considere adicionar um prefixo exclusivo.
github-api/ readme.txt github-api.php
O readme.txt
contém os metadados do seu plugin que aparecerão no wordpress.org
se você decidir publicá-lo lá. Leia sobre a publicação de plugins do WordPress na documentação ou confira a amostra abrangente readme.txt.
O arquivo PHP também contém alguns metadados no cabeçalho que serão usados para exibir informações sobre seu plugin no painel. Comece com um cabeçalho parecido com este:
<?php /** Plugin Name: GitHub API description: >- Add GitHub project information using shortcode Version: 1.0 Author: Your Name License: GPLv2 or later Text Domain: github-api */
Por motivos de segurança, também é uma boa ideia negar o acesso direto ao arquivo, assim:
defined( 'ABSPATH' ) or die( 'No script kiddies please!' );
Neste ponto, o plugin não fará nada, mas quando você copiar os arquivos para wp-content/plugins
, ele deverá aparecer na lista de plugins e você poderá ativá-lo.

Em seguida, desejaremos incluir a biblioteca que lidará com as solicitações da API. No exemplo de código a seguir, estamos incluindo a API PHP GitHub do KNP Labs, mas você pode incluir qualquer dependência substituindo knplabs/github-api
pelo pacote que está usando.
$ cd wp-content/plugins/github-api $ composer require knplabs/github-api
Agora, sua estrutura de arquivos deve ter a seguinte aparência:
github-api/ composer.json composer.lock github-api.php readme.txt vendor/
Agora que os arquivos estão no lugar, precisamos exigir o vendor/autoload.php
que foi criado quando você o instalou.
require_once 'vendor/autoload.php';
Se tudo foi configurado corretamente, você poderá instanciar uma classe da biblioteca sem que um erro fatal seja gerado.
$testing = new \Github\Client();
Mas esta é apenas uma maneira rápida de testar se suas dependências estão disponíveis. Eu recomendo definir uma nova classe que estenda a biblioteca.
class MyGithub extends \Github\Client {};
Isso não é crítico, mas torna seu código mais flexível de algumas maneiras. A maneira mais óbvia é que você pode alterar ou adicionar novas funcionalidades, se necessário. Mas também facilita a vida caso você queira mudar de biblioteca no futuro, porque você terá que fazer as alterações em apenas um lugar, em vez de em todo o seu código.
Códigos de acesso do WordPress
Agora é hora de configurar nosso primeiro shortcode, que é um snippet especial que permite gerar conteúdo colocando-o em um post ou página. Se você é completamente novo em códigos de acesso ou deseja uma compreensão mais profunda de como eles funcionam, confira a documentação oficial de códigos de acesso. No meu exemplo, vou exibir uma lista de problemas do GitHub onde quer que o autor coloque este shortcode: [github_issues]
Como fizemos antes, vamos começar com um exemplo mínimo para garantir que o shortcode esteja registrado corretamente antes de trabalharmos na chamada da API.
function github_issues_func( $atts ) { return "Hello world!"; } add_shortcode( "github_issues", "github_issues_func" );
Agora, se você publicar uma página contendo [github_issues]
, deverá ver “Hello world” quando visitar a página. Agora que isso está funcionando, vamos configurar a chamada da API.
Na seção anterior, configuramos o carregamento automático para nossa biblioteca do GitHub e definimos nossa própria classe para estendê-la. Agora, podemos começar a usá-lo para fazer chamadas de API. Então, vamos adicioná-lo à função de retorno de chamada do código de acesso. Como prova de conceito, vamos buscar todos os problemas no repositório do GitHub Pipeline. A documentação para este método está disponível.
function github_issues_func( $atts ) { // Instantiate our class $gh = new MyGithub(); // Make the API call to get issues, passing in the GitHub owner and repository $issues = $gh->api('issue')->all('TransitScreen', 'wp-github-pipeline'); // Handle the case when there are no issues if ( empty($issues) ) return "<strong>" . __("No issues to show", 'githup-api') . "</strong>"; // We're going to return a string. First, we open a list. $return = "<ul>"; // Loop over the returned issues foreach( $issues as $issue ) { // Add a list item for each issue to the string // (Feel free to get fancier here) // Maybe make each one a link to the issue issuing $issue['url] ) $return .= "<li>{$issue['title']}</li>"; } // Don't forget to close the list $return .= "</ul>"; return $return; } add_shortcode( 'github_issues', 'github_issues_func' );
Agora você poderá visualizar a mesma página que usamos acima para testar o código de acesso e, desta vez, você verá uma lista não ordenada de problemas. Mas espere! Antes de prosseguir, vamos fazer uma otimização para que nosso código seja mais fácil de testar. (Você está testando, certo?!) Em vez de usar new
em nossa função para instanciar a classe da biblioteca do GitHub, vamos passá-la como parâmetro e instanciar apenas se for necessário.
function github_issues_func( $atts, $gh=null ) { // Conditionally instantiate our class $gh = ( $gh ) ? $gh : new MyGithub(); …
Essa modificação se assemelha ao padrão de injeção de dependência, o que torna nossa função muito mais fácil de testar. O teste de unidade do WordPress está além do escopo deste tutorial, mas é fácil ver como essa nova versão facilita a passagem de dados de API “falsos” para a função para que as asserções do nosso teste saibam exatamente o que esperar.
O que fizemos até agora é muito bom para um projeto interno que será usado apenas em um único repositório, mas seria muito mais útil se os usuários pudessem configurar de qual repositório buscar os problemas. Vamos configurar isso.
Primeiro, usaremos um gancho do WordPress para registrar nossa nova página de configurações, e a função de retorno de chamada definirá os rótulos e títulos do menu. Usaremos a mesma abordagem incremental para fazê-lo funcionar.
// Register the menu. add_action( "admin_menu", "gh_plugin_menu_func" ); function gh_plugin_menu_func() { add_submenu_page( "options-general.php", // Which menu parent "GitHub", // Page title "GitHub", // Menu title "manage_options", // Minimum capability (manage_options is an easy way to target administrators) "github", // Menu slug "gh_plugin_options" // Callback that prints the markup ); } // Print the markup for the page function gh_plugin_options() { if ( !current_user_can( "manage_options" ) ) { wp_die( __( "You do not have sufficient permissions to access this page." ) ); } echo "Hello world!"; }
Agora você deve conseguir fazer login no painel e ver o novo menu do GitHub em “Configurações” e clicar nele para ver uma página de configurações em branco com “Hello world!”


Em seguida, substituiremos a Hello word
pela marcação real do formulário. Vou dar um exemplo básico que você pode estilizar o quanto quiser.
?> <form method="post" action="<?php echo admin_url( 'admin-post.php'); ?>"> <input type="hidden" name="action" value="update_github_settings" /> <h3><?php _e("GitHub Repository Info", "github-api"); ?></h3> <p> <label><?php _e("GitHub Organization:", "github-api"); ?></label> <input class="" type="text" name="gh_org" value="<?php echo get_option('gh_org'); ?>" /> </p> <p> <label><?php _e("GitHub repository (slug):", "github-api"); ?></label> <input class="" type="text" name="gh_repo" value="<?php echo get_option('gh_repo'); ?>" /> </p> <input class="button button-primary" type="submit" value="<?php _e("Save", "github-api"); ?>" /> </form> <?php
As classes CSS que incluí correspondem às usadas pelo núcleo do WordPress, que é uma boa maneira de fazer com que sua página de opções personalizadas pareça decente sem nenhum esforço adicional.

Há duas coisas importantes para entender sobre este formulário. Primeiro, o endpoint para o qual ele envia deve ser /wp-admin/admin-post.php
. Você pode codificar esse caminho, mas se o site estiver instalado em um subdiretório, ele não funcionará. Então, usamos o admin_url()
para criá-lo dinamicamente.
Em segundo lugar, observe a entrada oculta com o nome action
. Este campo é como o WordPress sabe o que fazer com a solicitação de postagem enviada pelo formulário. O value
corresponde ao nome do gancho de ação que usaremos para definir a função de retorno de chamada. O nome da ação à qual nos conectaremos será esse valor, prefixado com admin post
. Então, no nosso caso, precisamos adicionar isso:
add_action( 'admin_post_update_github_settings', 'github_handle_save' );
Acho que esse é um dos aspectos mais peculiares e menos intuitivos da criação de menus de administração personalizados, mas depois que você se acostuma, é bastante indolor. Como você deve ter adivinhado, o segundo parâmetro de add_action()
é o nome da nossa função de retorno de chamada que realmente salvará os valores no banco de dados.
function github_handle_save() { // Get the options that were sent $org = (!empty($_POST["gh_org"])) ? $_POST["gh_org"] : NULL; $repo = (!empty($_POST["gh_repo"])) ? $_POST["gh_repo"] : NULL; // Validation would go here // Update the values update_option( "gh_repo", $repo, TRUE ); update_option("gh_org", $org, TRUE); // Redirect back to settings page // The ?page=github corresponds to the "slug" // set in the fourth parameter of add_submenu_page() above. $redirect_url = get_bloginfo("url") . "/wp-admin/options-general.php?page=github&status=success"; header("Location: ".$redirect_url); exit; }
O exemplo também é bastante mínimo. Em uma configuração de produção, você provavelmente gostaria de adicionar alguma validação. Além disso, neste exemplo, estamos retornando automaticamente status=success
para a URL. A marcação do formulário ainda não o usa. Para mostrar condicionalmente uma mensagem de sucesso, adicione algo como o seguinte acima do formulário em gh_plugin_options()
:
if ( isset($_GET['status']) && $_GET['status']=='success') { ?> <div class="updated notice is-dismissible"> <p><?php _e("Settings updated!", "github-api"); ?></p> <button type="button" class="notice-dismiss"> <span class="screen-reader-text"><?php _e("Dismiss this notice.", "github-api"); ?></span> </button> </div> <?php }
Se você adicionar validação, use esse mesmo padrão para retornar diferentes status e mensagens para que o usuário saiba se e por que o envio do formulário falhou.
Agora você deve poder salvar novos valores de proprietário e repositório. Teste-o inserindo o proprietário e o repositório de qualquer projeto público no GitHub. A etapa final é voltar para a função de retorno de chamada de código de acesso github_issues_func()
e substituir o proprietário codificado e os valores do repositório.
… $issues = $gh->api("issue")->all(get_option("gh_org"), get_option("gh_repo")); …
Uma vez que isso esteja no lugar, revisite a página onde você adicionou o shortcode. Agora você deve ver os problemas de qualquer projeto definido.
Rodada de bônus: autenticação OAuth 2.0
A abordagem que usamos acima funciona muito bem para repositórios públicos, mas e se quisermos usar este plugin com um repositório privado que requer autenticação? A API do GitHub é autenticada usando o protocolo OAuth 2.0, que é o que você encontrará trabalhando com as APIs mais populares atualmente. O fluxo de trabalho básico do OAuth 2.0 é assim:
- Você registra um aplicativo (às vezes chamado de “cliente”) com o provedor e recebe um ID exclusivo e uma chave secreta.
- Seu aplicativo faz uma solicitação ao endpoint de autenticação do provedor, passando as credenciais acima, bem como uma URL de redirecionamento que o provedor usa para redirecionar a solicitação de volta para um endpoint do seu aplicativo.
- O usuário é solicitado a aceitar sua solicitação de acesso. Se o fizerem, o provedor usará a URL que você enviou com a solicitação para redirecionar o usuário de volta ao seu aplicativo, juntamente com um código temporário.
- Seu aplicativo captura esse código e, em seguida, faz uma segunda solicitação, passando esse código de volta ao provedor. O provedor responde com um token de acesso, que seu aplicativo usa para autenticar com o provedor.
Começaremos registrando um aplicativo no GitHub. Tal como acontece com as APIs mais populares, esta seção está localizada na área do desenvolvedor do site.

A coisa mais importante aqui é a URL de retorno de chamada de autorização. O URL de redirecionamento passado na etapa três deve corresponder ao que você inserir aqui ou incluí-lo. Então, durante o desenvolvimento, costumo entrar na página inicial do site. Dessa forma, meu aplicativo pode redirecionar para qualquer caminho.
Em seguida, adicionaremos um segundo formulário à nossa página de configurações, em gh_plugin_options()
, para enviar o ID do cliente e o segredo do aplicativo.
<form method="post" action="<?php echo admin_url( 'admin-post.php'); ?>"> <input type="hidden" name="action" value="oauth_submit" /> <h3>Oauth 2.0</h3> <p> <label><?php _e("GitHub Application Client ID:", "github-api"); ?></label> <input class="" type="text" name="client_id" value="<?php echo get_option('client_id')?>" /> </p> <p> <label><?php _e("GitHub Application Client Secret:", "github-api"); ?></label> <input class="" type="password" name="client_secret" value="<?php echo get_option('client_secret')?>" /> </p> <input class="button button-primary" type="submit" value="<?php _e("Authorize", "github-api"); ?>" /> </form>
Para facilitar a vida, usarei o GitHub Provider for OAuth 2.0 Client da The League of Extraordinary Packages. Então, primeiro, vamos usar o Composer novamente para adicionar a dependência:
composer require league/oauth2-github
Observe que a biblioteca GitHub do KNP Labs também possui suporte ao OAuth 2.0 integrado. Portanto, no meu exemplo específico, isso é um pouco redundante. Mas eu queria apresentar essa biblioteca porque ela pertence a um conjunto de bibliotecas cliente OAuth 2.0 específicas do provedor que estendem a mesma estrutura mantida pela poderosa League of Extraordinary Packages. Você pode visualizar uma lista completa de provedores com suporte ou ler as instruções sobre como estender a estrutura para oferecer suporte a um novo provedor.
Vamos criar uma função para solicitar e salvar o token quando o usuário enviar o formulário. Como o GitHub é um dos provedores já suportados, posso copiar o exemplo em sua documentação com apenas algumas modificações.
function handle_oauth() { // If the form was just submitted, save the values // (Step 1 above) if ( isset($_POST["client_id"]) && isset($_POST["client_secret"]) ) { update_option( "client_id", $_POST["client_id"], TRUE ); update_option("client_secret", $_POST["client_secret"], TRUE); } // Get the saved application info $client_id = get_option("client_id"); $client_secret = get_option("client_secret"); if ($client_id && $client_secret) { $provider = new League\OAuth2\Client\Provider\Github([ "clientId" => $client_id, "clientSecret" => $client_secret, "redirectUri" => admin_url("options-general.php?page=github"), ]); } // If this is a form submission, start the workflow // (Step 2) if (!isset($_GET["code"]) && $_SERVER["REQUEST_METHOD"] === "POST") { // If we don't have an authorization code, then get one $authUrl = $provider->getAuthorizationUrl(); $_SESSION["oauth2state"] = $provider->getState(); header("Location: ".$authUrl); exit; // Check given state against previously stored one to mitigate CSRF attack // (Step 3 just happened and the user was redirected back) } elseif (empty($_GET["state"]) || ($_GET["state"] !== $_SESSION["oauth2state"])) { unset($_SESSION["oauth2state"]); exit("Invalid state"); } else { // Try to get an access token (using the authorization code grant) // (Step 4) $token = $provider->getAccessToken("authorization_code", [ "code" => $_GET["code"] ]); // Save the token for future use update_option( "github_token", $token->getToken(), TRUE ); } }
E, assim como fizemos com o outro formulário, precisamos adicionar o gancho de ação para que a função seja chamada quando o formulário for salvo.
add_action( "admin_post_oauth_submit", "handle_oauth" );
Salvar o formulário agora deve enviar você para a página de autorização do provedor de API. Após a autorização, seu aplicativo pode usar o token salvo para solicitar dados de repositórios privados aos quais você tem acesso. A biblioteca que estou usando pelo KNP Labs tem um método útil para isso.
$gh = new MyGithub(); $gh->authenticate( get_option("github_token"), NULL, Github\Client::AUTH_HTTP_TOKEN);
As bibliotecas diferem precisamente em como lidam com a autenticação, mas de uma forma ou de outra, você passará o token, que fará solicitações autenticadas em nome de um usuário.
Conclusão
Cobrimos muito terreno e tentei manter os exemplos o mínimo possível para que o fluxo de trabalho geral permaneça claro. O código-fonte completo deste tutorial está disponível. Espero que agora você tenha uma compreensão clara das partes móveis envolvidas na criação de um plugin WordPress que consome APIs de serviços de terceiros, e espero que você esteja inspirado para escrever seu próprio plugin WordPress API.
Notas Finais
- WordPress Codex (documentação)
- API do GitHub (documentação)
- OAuth 2.0 (documentação)
- Compositor (documentação)
- A Liga dos Pacotes Extraordinários
- Documentação do Guzzle