Não se repita: automatizando tarefas repetitivas com WP-CLI
Publicados: 2022-03-11Você já se viu entrando na área de administração do WordPress para atualizar temas, plugins e núcleo WP? Claro que você tem. Você foi perguntado: "Você pode criar/atualizar/excluir todos os usuários neste arquivo CSV?" Tenho certeza que você também já passou por isso. Você já tentou migrar um site e desejou que houvesse um plug-in ou ferramenta de terceiros que você pudesse usar para fazer o trabalho? Eu sei que tenho!
Existe uma ferramenta muito poderosa disponível para ajudá-lo com essas tarefas e muito mais. Antes de falar sobre isso, gostaria de criar uma anedota rápida.
O problema: em um projeto recente, havia várias tarefas programáticas que eu precisava repetir regularmente. Uma tarefa em particular envolvia a atualização de permissões em nível de usuário com base em evidências de compra ou assinatura em nível de associação. Se a empresa não conseguisse encontrar um pagamento do usuário para um determinado nível de associação, eles queriam que o nível de associação fosse removido do usuário. Por que isso era necessário? Talvez um membro tenha interrompido uma assinatura, mas um evento não foi disparado e, portanto, o membro ainda tem acesso, mesmo que não esteja pagando por isso (caramba!). Ou talvez alguém estivesse em uma oferta de avaliação, mas essa oferta expirou e o cliente ainda tem uma assinatura (também caramba!).
A Solução: Em vez de entrar no painel de administração e excluir manualmente centenas (talvez milhares) de assinaturas, optei por usar uma das minhas ferramentas favoritas do WordPress, WP-CLI, que corrigiu o problema com apenas algumas teclas.
Neste post, quero apresentá-lo ao WP-CLI (supondo que você ainda não seja um amigo próximo), guiá-lo por um comando personalizado simples que escrevi para essa situação específica e fornecer algumas idéias e recursos para usar o WP-CLI em seu próprio desenvolvimento.
O que é WP-CLI?
Se você nunca ouviu falar do WP-CLI antes, você não está sozinho. O projeto, embora com vários anos, pareceu voar sob o radar do WordPress por um tempo. Aqui está uma breve descrição do que WP-CLI é e faz no site oficial:
WP-CLI é um conjunto de ferramentas de linha de comando para gerenciar instalações do WordPress. Você pode atualizar plugins, configurar instalações em vários sites e muito mais, sem usar um navegador da web.
Os comandos a seguir mostram o poder do WP-CLI pronto para uso:
-
wp plugin update --all
atualiza todos os plugins atualizáveis. -
wp db export
exporta um dump SQL de seu banco de dados. -
wp media regenerate
regenera miniaturas para anexos (por exemplo, depois de alterar o tamanho do seu tema). -
wp checksum core
verifica se os arquivos principais do WordPress não foram adulterados. -
wp search-replace
procura e substitui strings no banco de dados.
Se você explorar mais comandos aqui, verá que há muitos comandos disponíveis para tarefas repetitivas que todo desenvolvedor WordPress ou mantenedor de site faz diariamente ou semanalmente. Esses comandos me pouparam incontáveis horas de apontar, clicar e esperar por recargas de página ao longo do ano.
Você está convencido? Pronto para começar? Excelente!
Você precisará ter o WP-CLI instalado com seu WordPress (ou globalmente em sua máquina local). Se você ainda não instalou o WP-CLI em seu ambiente de desenvolvimento local, as instruções de instalação podem ser encontradas no site aqui. Se você estiver usando Varying Vagrant Vagrants (VVV2), o WP-CLI está incluído. Muitos provedores de hospedagem também têm o WP-CLI incluído em sua plataforma. Vou assumir que você instalou com sucesso daqui para frente.
Usando WP-CLI para resolver o problema
Para resolver o problema das tarefas repetitivas, precisamos disponibilizar um comando WP-CLI personalizado para nossa instalação do WordPress. Uma das maneiras mais fáceis de adicionar funcionalidade a qualquer site é criar um plugin. Usaremos um plugin neste caso por três razões principais:
- Poderemos desativar o comando personalizado se não precisarmos dele
- Podemos facilmente estender nossos comandos e subcomandos enquanto mantemos as coisas modulares.
- Podemos manter a funcionalidade em todos os temas e até mesmo em outras instalações do WordPress.
Criando o plug-in
Para criar um plugin, precisamos adicionar um diretório ao nosso diretório /plugins
em nosso diretório wp-content
. Podemos chamar esse diretório toptal-wpcli
. Em seguida, crie dois arquivos nesse diretório:
-
index.php
, que deve ter apenas uma linha de código:<?php // Silence is golden
-
plugin.php
, que é para onde nosso código irá (você pode nomear este arquivo como quiser.)
Abra o arquivo plugin.php
e adicione o seguinte código:
<?php /** * Plugin Name: TOPTAL WP-CLI Commands * Version: 0.1 * Plugin URI: https://n8finch.com/ * Description: Some rando wp-cli commands to make life easier... * Author: Nate Finch * Author URI: https://n8finch.com/ * Text Domain: toptal-wpcli * Domain Path: /languages/ * License: GPL v3 */ /** * NOTE: THIS PLUGIN FILE WILL NOT WORK IN PRODUCTION AS IS AND IS ONLY FOR DEMONSTRATION PURPOSES! * You can of course take the code and repurpose it:-). */ if ( !defined( 'WP_CLI' ) && WP_CLI ) { //Then we don't want to load the plugin return; }
Há duas partes nessas primeiras linhas.
Primeiro, temos o cabeçalho do plugin. Esta informação é puxada para a página de administração do WordPress Plugins e nos permite registrar nosso plugin e ativá-lo. Apenas o nome do plug-in é obrigatório, mas devemos incluir o resto para quem quiser usar este código (assim como nós mesmos no futuro!).
Em segundo lugar, queremos verificar se WP-CLI está definido. Ou seja, estamos verificando se a constante WP-CLI está presente. Se não for, queremos resgatar e não executar o plugin. Se estiver presente, podemos executar o resto do nosso código.
Entre essas duas seções, adicionei uma observação de que esse código não deve ser usado “como está” em produção, pois algumas das funções são espaços reservados para funções reais. Se você alterar essas funções de espaço reservado para funções reais e ativas, sinta-se à vontade para excluir esta nota.
Adicionando o comando personalizado
Em seguida, queremos incluir o seguinte código:
class TOPTAL_WP_CLI_COMMANDS extends WP_CLI_Command { function remove_user() { echo "\n\n hello world \n\n"; } } WP_CLI::add_command( 'toptal', 'TOPTAL_WP_CLI_COMMANDS' );
Este bloco de código faz duas coisas para nós:
- Ele define a classe
TOPTAL_WP_CLI_COMMANDS
, para a qual podemos passar argumentos. - Ele atribui o comando
toptal
à classe, para que possamos executá-lo a partir da linha de comando.
Agora, se executarmos wp toptal remove_user
, veremos:
$ wp toptal hello hello world
Isso significa que nosso comando toptal
está registrado e nosso subcomando remove_user
está funcionando.
Configurando Variáveis
Como estamos processando a remoção de usuários em massa, queremos configurar as seguintes variáveis:
// Keep a tally of warnings and loops $total_warnings = 0; $total_users_removed = 0; // If it's a dry run, add this to the end of the success message $dry_suffix = ''; // Keep a list of emails for users we may want to double check $emails_not_existing = array(); $emails_without_level = array(); // Get the args $dry_run = $assoc_args['dry-run']; $level = $assoc_args['level']; $emails = explode( ',', $assoc_args['email'] );
A intenção de cada uma das variáveis é a seguinte:
-
total_warnings
: registraremos um aviso se o e-mail não existir ou se o e-mail não estiver associado ao nível de associação que estamos removendo. -
$total_users_removed
: Queremos calcular o número de usuários removidos no processo (veja a advertência abaixo). -
$dry_suffix
: Se esta for uma simulação, queremos adicionar palavras ao aviso final de sucesso. -
$emails_not_existing
: Armazena uma lista de emails que não existem. -
$emails_without_level
: Armazena uma lista de emails que não possuem o nível especificado. -
$dry_run
: Um booleano que armazena se o script está fazendo uma simulação (true) ou não (false). -
$level
: Um inteiro que representa o nível a ser verificado e possivelmente removido. -
$email
: Uma matriz de e-mails para verificar em relação ao nível fornecido. Vamos percorrer este array
Com nossas variáveis definidas, estamos prontos para realmente executar a função. No verdadeiro estilo WordPress, vamos executar um loop.
Escrevendo a própria função
Começamos criando um loop foreach
para percorrer todos os e-mails em nosso array $emails
:
// Loop through emails foreach ( $emails as $email ) { // code coming soon } // end foreach
Em seguida, adicionamos uma verificação condicional:
// Loop through emails foreach ( $emails as $email ) { //Get User ID $user_id = email_exists($email); if( !$user_id ) { WP_CLI::warning( "The user {$email} does not seem to exist." ); array_push( $emails_not_existing, $email ); $total_warnings++; continue; } } // end foreach
Essa verificação garante que temos um usuário registrado com o e-mail que estamos verificando. Ele usa a função email_exists()
para verificar se existe um usuário com esse email. Se não encontrar um usuário com esse e-mail, lança um aviso para que saibamos na tela do nosso terminal que o e-mail não foi encontrado:
$ wp toptal remove_user [email protected] --dry-run Warning: The user [email protected] does not seem to exist.
O email é então armazenado no array $emails_not_existing
para exibição posterior. Em seguida, incrementamos o aviso total em um e continuamos pelo loop até o próximo e-mail.
Se o email existir, usaremos as variáveis $user_id
e $level
para verificar se o usuário tem acesso ao nível. Armazenamos o valor booleano resultante na variável $has_level
:

// Loop through emails foreach ( $emails as $email ) { //Get User ID $user_id = email_exists($email); if( !$user_id ) { WP_CLI::warning( "The user {$email} does not seem to exist." ); array_push( $emails_not_existing, $email ); $total_warnings++; continue; } // Check membership level. This is a made up function, but you could write one or your membership plugin probably has one. $has_level = function_to_check_membership_level( $level, $user_id ); } // end foreach
Como a maioria das funções neste exemplo, esta function_to_check_membership_level()
é fabricada, mas a maioria dos plugins de associação devem ter funções auxiliares para obter essas informações.
Agora, vamos passar para a ação principal: remover o nível do usuário. Usaremos uma estrutura if/else
, que se parece com isso:
foreach ( $emails as $email ) { // Previous code here... // Check membership level. This is a made up function, but you could write one or your membership plugin probably has one. $has_level = function_to_check_membership_level( $level, $user_id ); if ( $has_level ) { if ( !$dry_run ) { // Deactivate membership level. This is a made up function, but you could write one or your membership plugin probably has one. function_to_deactivate_membership_level( $level, $user_id, 'inactive' ); } WP_CLI::success( "Membership canceled for {$email}, Level {$level} removed" . PHP_EOL ); $total_users_removed++; } else { WP_CLI::warning( "The user {$email} does not have Level = {$level} membership." ); array_push( $emails_without_level, $email ); $total_warnings++; } // We could echo something here to show that things are processing... } // end foreach
Se o valor de $has_level
for “truthy”, significando que o usuário tem acesso ao nível de associação, queremos executar uma função para remover esse nível. Neste exemplo, usaremos a function_to_deactivate_membership_level()
para realizar esta ação.
No entanto, antes de realmente removermos o nível do usuário, queremos incluir essa função em uma verificação condicional para ver se isso é realmente um dry-run
. Se for, não queremos remover nada, apenas relatar que fizemos. Se não for uma dry-run
, então iremos em frente e removeremos o nível do usuário, registraremos nossa mensagem de sucesso no terminal e continuaremos percorrendo os e-mails.
Se, por outro lado, o valor de $has_level
for “falsey”, significando que o usuário não tem acesso ao nível de associação, queremos registrar um aviso no terminal, enviar o email para o array $emails_without_level
e continuar percorrendo os e-mails.
Finalizando e Reportando
Quando o loop terminar, queremos registrar nossos resultados no console. Se isso foi uma simulação, queremos registrar uma mensagem extra no console:
if ( $dry_run ) { $dry_suffix = 'BUT, nothing really changed because this was a dry run:-).'; }
Este $dry-suffix
será anexado aos avisos e notificações de sucesso que registramos a seguir.
Finalizando, queremos registrar nossos resultados como uma mensagem de sucesso e nossos avisos como mensagens de aviso. Faremos assim:
WP_CLI::success( "{$total_users_removed} User/s been removed, with {$total_warnings} warnings. {$dry_suffix}" ); if ( $total_warnings ) { $emails_not_existing = implode(',', $emails_not_existing); $emails_without_level = implode(',', $emails_without_level); WP_CLI::warning( "These are the emails to double check and make sure things are on the up and up:" . PHP_EOL . "Non-existent emails: " . $emails_not_existing . PHP_EOL . "Emails without the associated level: " . $emails_without_level . PHP_EOL ); }
Observe que estamos usando os métodos auxiliares WP_CLI::success
e WP_CLI::warning
. Eles são fornecidos pelo WP-CLI para registrar informações no console. Você pode facilmente registrar strings, que é o que fazemos aqui, incluindo nossas $total_users_removed
, $total_warnings
e $dry_suffix
.
Por fim, se acumulamos quaisquer avisos durante o tempo de execução do script, queremos imprimir essas informações no console. Depois de executar uma verificação condicional, convertemos as variáveis de array $emails_not_existing
e $emails_without_level
em variáveis de string. Fazemos isso para que possamos imprimi-los no console usando o método auxiliar WP_CLI::warning
.
Adicionando uma Descrição
Todos nós sabemos que os comentários são úteis para os outros e para o nosso futuro, voltando ao nosso código semanas, meses ou até anos depois. WP-CLI fornece uma interface de descrições curtas (shortdesc) e descrições longas (longdesc) que nos permite anotar nosso comando. Vamos colocar no topo do nosso comando, após a definição da classe TOPTAL_WP_CLI_COMMANDS
:
/** * Remove a membership level from a user * * ## OPTIONS * --level=<number> * : Membership level to check for and remove * * --email=<email> * : Email of user to check against * * [--dry-run] * : Run the entire search/replace operation and show report, but don't save changes to the database. * * ## EXAMPLES * * wp toptal remove_user --level=5 [email protected],[email protected], [email protected] --dry-run * * @when after_wp_load */
No longdesc, definimos o que esperamos que nosso comando personalizado receba. A sintaxe para o shortdesc e o longdesc é Markdown Extra. Na seção ## OPTIONS
, definimos os argumentos que esperamos receber. Se um argumento for necessário, nós o envolvemos em < >
, e se for opcional, nós o envolvemos em [ ]
.
Essas opções são validadas quando o comando é executado; por exemplo, se deixarmos de fora o parâmetro de email obrigatório, obtemos o seguinte erro:
$ wp toptal remove_user --level=5 --dry-run Error: Parameter errors: missing --email parameter (Email of user to check against)
A seção ## EXAMPLES
inclui um exemplo de como o comando pode ficar ao ser chamado.
Nosso comando personalizado agora está completo. Você pode ver a essência final aqui.
Uma advertência e espaço para melhorias
É importante revisar o trabalho que fizemos aqui para ver como o código pode ser melhorado, expandido e refatorado. Há muitas áreas de melhoria para este script. Aqui estão algumas observações sobre melhorias que podem ser feitas.
Ocasionalmente, descobri que esse script não removerá todos os usuários registrados como "removidos". Isso provavelmente ocorre porque o script está sendo executado mais rápido do que as consultas podem ser executadas. Sua experiência pode variar, dependendo do ambiente e da configuração em que o script é executado. A maneira rápida de contornar isso é executar repetidamente com as mesmas entradas; ele acabará zerando e informando que nenhum usuário foi removido.
O script pode ser aprimorado para aguardar e validar que um usuário foi removido antes de registrá-lo como realmente removido. Isso retardaria a execução do script, mas seria mais preciso e você só teria que executá-lo uma vez.
Da mesma forma, se houver erros encontrados como esse, o script poderá gerar erros para alertar que um nível não foi removido de um usuário.
Outra área para melhorar o script é permitir que vários níveis sejam removidos de um endereço de e-mail ao mesmo tempo. O script pode detectar automaticamente se havia um ou mais níveis e um ou mais emails para remover. Recebi arquivos CSV por nível, então eu só precisava executar um nível de cada vez.
Também poderíamos refatorar parte do código para usar operadores ternários em vez das verificações condicionais mais detalhadas que temos atualmente. Optei por tornar isso mais fácil de ler para fins de demonstração, mas sinta-se à vontade para tornar o código seu.
Na etapa final, em vez de imprimir e-mails no console na etapa final, também podemos exportá-los automaticamente para um arquivo CSV ou de texto simples
Finalmente, não há verificações para garantir que estamos obtendo um inteiro para a variável $level
ou um email ou uma lista de emails separados por vírgula na variável $emails
. Atualmente, se alguém incluísse strings em vez de números inteiros, ou nomes de login de usuários em vez de emails, o script não funcionaria (e não geraria erros). Verificações de números inteiros e e-mails podem ser adicionados.
Ideias para Automação Adicional e Leitura Adicional
Como você pode ver, mesmo neste caso de uso específico, o WP-CLI é bastante flexível e poderoso o suficiente para ajudá-lo a realizar seu trabalho com rapidez e eficiência. Você pode estar se perguntando: “Como posso começar a implementar o WP-CLI no meu fluxo de desenvolvimento diário e semanal?”
Existem várias maneiras de usar o WP-CLI. Aqui estão alguns dos meus favoritos:
- Atualize temas, plugins e núcleo WP sem ter que entrar no painel de administração.
- Exporte bancos de dados para backup ou execute um dump SQL rápido se eu quiser testar uma consulta SQL.
- Migrar sites WordPress.
- Instale novos sites WordPress com dados fictícios ou configurações personalizadas do conjunto de plugins.
- Execute somas de verificação nos arquivos principais para garantir que eles não tenham sido comprometidos. (Na verdade, existe um projeto em andamento para expandir isso para temas e plugins no repositório WP.)
- Escreva seu próprio script para verificar, atualizar e manter os hosts do site (sobre os quais escrevi aqui).
As possibilidades com WP-CLI são praticamente ilimitadas. Aqui estão alguns recursos para mantê-lo avançando:
- O site principal do WP-CLI: http://wp-cli.org
- Comandos WP-CLI: https://developer.wordpress.org/cli/commands/
- Blog oficial do WP-CLI: https://make.wordpress.org/cli/
- Manual do WP-CLI: https://make.wordpress.org/cli/handbook/
- Em WooCommerce? Confira o WC-CLI: https://github.com/woocommerce/woocommerce/wiki/WC-CLI-Overview#woocommerce-commands
- Podcast Entrevista com Daniel Bachhuber, mantenedor do projeto: https://howibuilt.it/episode-28-daniel-bachhuber-wp-cli/