Como abordar o desenvolvimento moderno do WordPress (Parte 2)
Publicados: 2022-03-11O WordPress é a tecnologia de sites mais usada no planeta e com razão. No entanto, o código legado em seu núcleo é uma bagunça, e esse problema se espalha para desenvolvedores de terceiros. Alguns desenvolvedores tomam isso como uma desculpa para cortar custos em seu próprio código PHP do WordPress, mas essa abordagem é mais cara a longo prazo para todas as mudanças, exceto as mais triviais.
Na Parte 1 de nossa série de duas partes, focamos nas ferramentas gerais de projeto e fluxo de trabalho, seguidas pelo desenvolvimento de front-end. Agora é hora de pegar o touro pelos chifres e lutar com PHP: Especificamente, como seguir as melhores práticas ao trabalhar com código WordPress de back-end. Você pode pensar nisso como um tutorial PHP/WordPress, mas um pouco mais avançado, supondo que você já tenha feito alguma personalização de back-end do WordPress.
Então, quais princípios modernos de design de software e recursos PHP lhe darão o melhor valor para o seu tempo? A seguir estão 10 práticas de desenvolvimento WordPress e PHP que eu recomendo.
Prática recomendada de desenvolvimento moderno do WordPress nº 1: siga “Separação de preocupações”
Separação de interesses significa que partes do código PHP do WordPress com diferentes funcionalidades ou propósitos não devem ser misturadas. Em vez disso, eles devem ser organizados em seções ou módulos distintos, passando dados entre si por meio de uma interface definida. (Uma interface é um conjunto de parâmetros definidos que um módulo recebe como entrada e o que ele retorna.) Um termo intimamente relacionado é o princípio de responsabilidade única : Cada módulo de código (ou função) deve ser responsável por apenas uma coisa.
O objetivo final de seguir esses princípios é produzir código que seja modular e, portanto, sustentável, extensível e reutilizável.
Isso foi um bocado, então vamos ver um exemplo de algum WordPress PHP (do núcleo do WordPress) que deixa tudo emaranhado. Esse estilo de codificação é frequentemente chamado de “código espaguete” porque entender seu funcionamento interno é quase impossível. O trecho abaixo foi redigido por brevidade; no entanto, o estilo e a formatação originais foram preservados.
$id = isset( $_REQUEST['id'] ) ? intval( $_REQUEST['id'] ) : 0; <table class="form-table"> <?php $blog_prefix = $wpdb->get_blog_prefix( $id ); $sql = "SELECT * FROM {$blog_prefix}options WHERE option_name NOT LIKE %s AND option_name NOT LIKE %s"; $query = $wpdb->prepare( $sql, $wpdb->esc_like( '_' ) . '%', '%' . $wpdb->esc_like( 'user_roles' ) ); $options = $wpdb->get_results( $query ); foreach ( $options as $option ) { if ( strpos( $option->option_value, "\n" ) === false ) { ?> <tr class="form-field"> <th scope="row"><label for="<?php echo esc_attr( $option->option_name ); ?>"><?php echo esc_html( ucwords( str_replace( '_', ' ', $option->option_name ) ) ); ?></label></th> <?php if ( $is_main_site && in_array( $option->option_name, array( 'siteurl', 'home' ) ) ) { ?> <td><code><?php echo esc_html( $option->option_value ); ?></code></td> <?php } else { ?> <td><input class="<?php echo $class; ?>" name="option[<?php echo esc_attr( $option->option_name ); ?>]" type="text" value="<?php echo esc_attr( $option->option_value ); ?>" size="40" <?php disabled( $disabled ); ?> /></td> <?php } ?> </tr> <?php } } // End foreach </table>
Em primeiro lugar, é totalmente incompreensível. E eu adoro que o único comentário seja End foreach
, que é completamente redundante. Temos consulta de banco de dados, processamento de resultados de consulta, processamento adicional embutido em HTML (há um if
/ else
aninhado lá, se você não percebeu), saída de escape e templates HTML todos misturados. Outro problema é o parâmetro $id
vindo direto do $_REQUEST
global ao invés de passar um parâmetro real para uma função.
Olhando para isso, é perfeitamente compreensível porque o núcleo do WordPress permaneceu praticamente o mesmo por anos. Refatorar esse tipo de código – especialmente mantendo o comportamento existente – é uma tarefa realmente épica que ninguém gostaria de fazer.
Então, como fazemos isso corretamente? Bem, uma coisa a lembrar é que não existe um caminho verdadeiro . Mencionamos acima as qualidades pelas quais devemos nos esforçar: Precisamos que o código PHP personalizado do WordPress seja sustentável e modular. Vamos ver como podemos dividir o código acima em módulos.
- As consultas SQL devem estar em um módulo separado, obviamente. O WordPress já possui uma classe
WP_Query
bem abstraída que deve ser usada como exemplo. - Todo o HTML vai para um modelo. Abordaremos a modelagem PHP mais abaixo.
- O código PHP restante deve ser encapsulado em uma função – várias funções se o código for muito longo ou complexo para uma função. Parâmetros como
$id
são passados por meio de argumentos de função.
Esta é uma reescrita bastante simplificada do exemplo acima:
function betterSiteSettings($args) { $data = WP_Settings_Query($args); // process $data here $context = array_merge([], $data_processed, $other_data); return Template::render('template.name', $context); }
Prática recomendada de desenvolvimento moderno do WordPress nº 2: evite variáveis globais
O WordPress tem muitas variáveis globais. Por que as variáveis globais são ruins? Eles tornam seu código PHP do WordPress difícil de seguir e tornam o estado do aplicativo não confiável. Qualquer pedaço de código PHP - e isso significa que qualquer plug-in instalado no WordPress - pode ler e gravar variáveis globais, portanto, não há garantia de que eles contenham dados válidos. Tentar entender quais variáveis globais são usadas em algo como o Loop também não é uma tarefa trivial.
Vejamos isso de um ângulo prático. Este exemplo vem do WooCommerce. Provavelmente todo desenvolvedor do WordPress sabe o que é – o Loop:
<?php while ( have_posts() ) : the_post(); ?> <?php wc_get_template_part( 'content', 'single-product' ); ?> <?php endwhile; // end of the loop. ?>
O snippet acima renderiza um modelo de produto. Como ele sabe qual produto mostrar, já que não há parâmetros passados para wc_get_template_part
? Observando o modelo, vemos que ele começa com global $product;
, então é onde o objeto do produto atual é armazenado.
Agora imagine que temos uma página de catálogo que pesquisa e filtra produtos e queremos mostrar um pop-up de “detalhes do produto” enquanto permanecemos na mesma página. Nos bastidores, o script de front-end faz uma solicitação AJAX para obter esse modelo de produto específico. Não podemos simplesmente chamar wc_get_template_part('content', 'single-product')
porque ele não usa parâmetros, então precisamos definir algumas variáveis globais para que isso funcione.
Casos de uso mais complexos envolveriam mais de um modelo, ganchos acionados nesses modelos e plug-ins de terceiros adicionando seus retornos de chamada a esses ganchos. Pode escalar rapidamente. Não temos como saber em qual estado global esses retornos de chamada dependem. Plugins de terceiros são livres para modificar qualquer variável global em seus retornos de chamada. Em vez de usar o sistema, começamos a lutar com o sistema, encontrando bugs estranhos que vêm de um estado global não confiável.
Não seria mais sensato passar esse ID do produto como parâmetro? Então, poderíamos reutilizar esse modelo sem nos preocupar em bagunçar as variáveis globais usadas pelo WordPress.
Prática recomendada de desenvolvimento moderno do WordPress nº 3: use programação orientada a objetos (OOP)
A modularidade leva ao conceito de objetos e programação orientada a objetos. No nível muito básico, POO é uma maneira de organizar o código. Funções e variáveis são agrupadas em classes e são chamadas de métodos e propriedades de classe, respectivamente. O WordPress Plugin Handbook recomenda o uso de OOP para organizar seu código PHP personalizado do WordPress.
Um princípio importante em OOP é limitar o acesso a métodos e propriedades – ou em termos de PHP, denotando-os como private
ou protected
– para que apenas outros métodos de classe possam acessá-los e alterá-los. Um termo OOP para isso é encapsulamento : os dados são encapsulados dentro da classe e a única maneira de alterar esses dados é usando os métodos de classe fornecidos.
Isso torna a depuração e a manutenção do código muito mais fácil do que ao usar variáveis globais que podem ser modificadas em qualquer lugar em toda a base de código. Considere a variável de post
global do WordPress. Você pode acessá-lo em qualquer lugar em seu código, e muitas funcionalidades dependem de seu uso. E se você pudesse restringir as modificações apenas às funções principais do WordPress, mas a leitura fosse permitida para qualquer pessoa? Ocultar ou encapsular a variável global post
em uma classe e construir uma interface em torno dela tornaria isso possível.
Esta é apenas uma descrição muito básica da OOP e como ela pode ser usada no desenvolvimento moderno do WordPress. Para um estudo mais aprofundado, recomendo completamente o e-book de Carl Alexander, Discover object-oriented programming using WordPress, que possui o conteúdo mais abrangente e útil disponível sobre o tema POO no WordPress.
É importante lembrar que a POO não é uma bala de prata: código ruim pode ser escrito com POO tão facilmente quanto com qualquer outro paradigma de programação.
Vamos mergulhar em alguns conselhos específicos sobre o uso do PHP para o desenvolvimento do WordPress.
Prática recomendada do PHP moderno nº 1: alvo PHP 7.0+
O uso de recursos modernos do PHP requer, bem, uma versão moderna do PHP. Simplesmente não há razão para suportar versões do PHP inferiores a 7.0. Mesmo o núcleo do WordPress exigirá o PHP 7.0 já no final de 2019.
No entanto, é uma boa prática verificar sua versão mínima para evitar a “tela branca da morte” em ambientes incompatíveis. O trecho abaixo mostra o uso de um cabeçalho de plug-in para declarar uma versão mínima do PHP com uma condição de guarda no código.
<?php /** * Plugin Name: My Awesome Plugin * Requires PHP: 7.0 */ // bails if PHP version is lower than required if (version_compare(PHP_VERSION, '7.0.0', '<')) { // add admin notice here return; } // the rest of the actual plugin here
Prática recomendada do PHP moderno nº 2: Adote os padrões da indústria do PHP (Guia de estilo de codificação PSR-2)
PSRs são recomendações publicadas pelo PHP Framework Interop Group. Eles são os padrões de fato da indústria em qualquer fluxo de trabalho PHP moderno, e pode-se dizer com segurança que a comunidade PHP como um todo segue esses padrões. PSR-2 é uma recomendação que descreve o estilo de codificação. Frameworks PHP populares como Symfony e Laravel seguem o PSR-2.
Por que você usaria o PSR-2 em vez do padrão de codificação do WordPress? Principalmente porque os padrões do WordPress são obsoletos e não usam nenhum dos recursos de linguagem mais recentes. Isso é compreensível porque o núcleo do WordPress tem que seguir seus próprios padrões. Ele tinha que suportar o PHP 5.2 até muito recentemente, e o PSR-2 não é compatível com o PHP 5.2.
Pode não ser óbvio, mas não há necessidade de usar os padrões de codificação do WordPress, a menos que você esteja se comprometendo com o núcleo. Não haveria problema em enviar um plugin que segue o padrão PSR-2 para o diretório de plugins do WordPress. Na verdade, existem alguns argumentos muito bons para fazê-lo.

Prática recomendada do PHP moderno nº 3: use um mecanismo de modelo PHP
PHP não é um mecanismo de modelo. Começou como um, mas depois evoluiu para uma linguagem de programação com todos os recursos, e não há motivo para continuar usando-o para modelagem. Os dois mecanismos de template mais populares para PHP são Twig e Blade, que são usados pelo Symfony e Laravel, respectivamente. Este artigo usará o Twig como um mecanismo de modelagem de exemplo; no entanto, o Blade possui recursos e funcionalidades comparáveis. Eu imploro que você olhe para ambos e decida por si mesmo o que melhor combina com você.
O exemplo abaixo compara um template PHP e seu template Twig correspondente. Exibir e escapar da saída é particularmente detalhado no exemplo do PHP:
foreach ( $options as $option ) { ?> <tr class="form-field"> <th scope="row"> <label for="<?php echo esc_attr( $option->option_name ); ?>"> <?php echo esc_html( strtolower( $option->option_name ) ); ?> </label> </th> </tr> <?php } // End foreach
No Twig, isso é mais conciso e legível:
{% for option in options %} <tr class="form-field"> <th scope="row"> <label for="{{ option.option_name }}"> {{ option.option_name }} </label> </th> </tr> {% endfor %}
As principais vantagens do Twig são:
- Sintaxe legível e concisa
- Saída automática de escape
- Extensão de modelo via herança e blocos
Em termos de desempenho, o Twig compila para modelos PHP e praticamente não tem sobrecarga. Twig tem apenas um subconjunto de construções de linguagem PHP limitadas apenas a modelagem. Isso força os desenvolvedores a remover a lógica de negócios dos modelos, reforçando assim a separação de interesses.
Existe até Twig para WordPress. Chama-se Timber e é uma ótima maneira de começar a criar modelos melhores. O tema inicial Timber é um exemplo perfeito de organização de temas da maneira OOP.
Prática recomendada do PHP moderno nº 4: use o Composer
O Composer é um gerenciador de dependências para PHP. É uma ferramenta que permite declarar bibliotecas que um projeto está usando e automatizar seus downloads, instalações e atualizações. Então você só precisa incluir o arquivo de carregamento automático vendor/autoload.php
do Composer em vez de exigir manualmente cada biblioteca.
Os plugins e temas do WordPress não costumam usar bibliotecas de terceiros. Isso ocorre em parte porque o WordPress possui uma API extensa que atende a quase todas as necessidades e em parte por causa de possíveis conflitos de versão. Considere dois plugins que requerem a mesma biblioteca PHP, mas versões diferentes dela. O plug-in que é executado primeiro obtém a versão correta e o segundo plug-in também obtém essa versão. Esta é possivelmente outra situação de tela branca da morte.
Para evitar conflitos, o gerenciamento de dependências deve ser usado no nível da aplicação, ou seja, o site WordPress como um todo. É isso que o Roots (Bedrock, mais especificamente) faz. Quando usado no nível do pacote (plugin ou tema), o Composer pode causar conflitos ao usar bibliotecas de terceiros. É um problema conhecido. A única solução que existe até agora é renomear os namespaces dessa biblioteca externa para algo único, e isso não é uma tarefa trivial.
No entanto, ainda há um uso para o Composer: o carregamento automático de suas próprias classes. Mas antes de prosseguirmos com o carregamento automático, precisamos nos familiarizar com os namespaces PHP.
Prática recomendada do PHP moderno nº 5: use namespaces
O núcleo do WordPress sendo um projeto legado, ele usa um namespace global ou, dito de forma diferente, nenhum namespace. Quaisquer classes ou funções declaradas globalmente (ou seja, não dentro de outra classe ou função) são visíveis em qualquer lugar em toda a base de código. Seus nomes devem ser exclusivos não apenas dentro de sua base de código, mas para todos os plugins e temas que são usados agora ou podem ser usados no futuro.
A colisão de nomes (por exemplo, declarar uma função com um nome que já existe) geralmente leva à tela branca da morte, e não queremos isso. O WordPress Codex aconselha prefixar todas as suas funções e classes com algo único. Então, em vez de ter nomes de classes simples como Order
, obtemos algo como Akrte_Awesome_Plugin_Order
onde “Akrte” é o prefixo único que acabei de criar.
Os namespaces podem ser vistos como grupos — ou pastas, se usarmos uma analogia do sistema de arquivos — que ajudam a organizar o código e evitar a colisão de nomes. Você pode ter namespaces complexos separados por uma barra, assim como pastas aninhadas. (Os namespaces PHP usam uma barra invertida em particular.)
Essas partes de namespace são chamadas de subnamespaces. Nossa classe de exemplo Akrte_Awesome_Plugin_Order
seria Akrte\Awesome_Plugin\Order
se feita usando namespaces. Aqui Akrte
e Awesome_Plugin
são partes do namespace (ou sub-namespaces) e Order
é o nome da classe. Em seguida, você pode adicionar uma instrução de use
e usar apenas o nome da classe posteriormente. Com certeza fica melhor:
use Akrte\Awesome_Plugin\Order; $a = new Order;
Obviamente, os namespaces devem ser exclusivos; assim, devemos dar ao primeiro sub-namespace “raiz” um nome exclusivo, que geralmente é um nome de fornecedor. Como exemplo, a classe WooCommerce WC_REST_Order_Notes_V2_Controller
pode ser refeita com namespaces como este:
namespace WooCommerce\RestApi\V2\Controllers; class OrderNotes {}
Atualmente, a base de código do WooCommerce usa namespaces; por exemplo, na API REST do WooCommerce versão 4.
Prática recomendada do PHP moderno nº 6: use um carregador automático
Na maioria dos fluxos de trabalho PHP, a maneira usual de vincular arquivos PHP é usando instruções require
ou include
. À medida que um projeto cresce, você obtém dezenas de declarações de require
em seu arquivo de plug-in principal. Um autoloader automatiza a inclusão de arquivos e o faz apenas quando necessário. Tecnicamente, é uma função que require
um arquivo sa contendo uma classe ou função quando é encontrado pela primeira vez no código. Não há mais necessidade de adicionar instruções require
manualmente.
Frequentemente, há também um ganho de desempenho significativo, pois um autoloader carrega apenas módulos que são usados em uma solicitação específica. Sem um autoloader, toda a sua base de código está incluída mesmo que uma solicitação use apenas, digamos, 10% do seu código.
Uma função de autoloader precisa saber em quais arquivos suas classes e funções estão. E existe um padrão PHP-FIG, PSR-4, para isso.
Ele diz que uma parte de um namespace, o prefixo, é designada para corresponder a uma pasta base. Os sub-namespaces que o seguem correspondem a pastas dentro da pasta base. Finalmente, o nome da classe corresponde ao nome do arquivo. Um exemplo de classe Akrte\AwesomePlugin\Models\Order
precisaria da estrutura de pastas abaixo:
/awesome-plugin awesome-plugin.php /includes /Models Order.php
O prefixo do namespace Akrte\AwesomePlugin\
corresponde à pasta includes
conforme especificado na configuração do autoloader discutida abaixo. O sub-namespace Models
tem uma pasta Models
correspondente e a classe Order
está contida em Order.php
.
Felizmente, não há necessidade de implementar uma função de autoloader por conta própria. O Composer pode criar um autoloader para você:
- Instalar o Compositor
- Crie um arquivo
composer.json
na pasta raiz do seu projeto. Deve conter estas linhas:
{ "name": "vendor-name/plugin-name", "require": {}, "autoload": { "psr-4": { "Akrte\\AwesomePlugin\\": "includes/" } } }
- Execute
composer install
. - Inclua
vendor/autoload.php
no topo do seu arquivo PHP de plugin principal assim:
<?php /** * Plugin Name: My Awesome Plugin */ defined('ABSPATH') || exit; require __DIR__ . '/vendor/autoload.php'; // do stuff
Além dos namespaces, a base de código mais recente do WooCommerce também usa um autoloader do Composer.
Com esses princípios de design PHP cobertos, é hora de vincular todas as nossas lições de PHP à personalização de back-end do WordPress com uma recomendação final.
Prática recomendada de desenvolvimento moderno do WordPress nº 4: considere usar a pilha de raízes
Roots é o fluxo de trabalho de desenvolvimento WordPress moderno mais abrangente que existe. No entanto, eu diria que não deve necessariamente ser usado em todos os projetos do WordPress, porque:
- Roots tem que ser usado desde o início. O motivo mais comum, na verdade. Refatorar um projeto existente seria muito caro.
- É opinativo. Bom quando você concorda, ruim quando você não concorda. Você pode preferir outras formas de organizar seu tema, por exemplo. Projetos opinativos também exigem tempo para aprender “do seu jeito”.
- Nem todo mundo sabe disso. O desenvolvedor que virá atrás de você para manter o site que você construiu com a pilha Roots pode não ter ideia do que é e se perguntar o que aconteceu com as pastas do WordPress. Devemos pensar em nossos colegas desenvolvedores do WordPress.
Em geral, você gostaria de entender completamente todos os benefícios e desvantagens de qualquer projeto fortemente opinativo antes de se comprometer a usá-lo.
Princípios modernos de PHP e software: tornando o desenvolvimento de back-end do WordPress robusto
É bastante óbvio que não existe uma maneira verdadeira de escrever software. Os conceitos, como separação de interesses, têm décadas; no entanto, o que significa praticamente sempre foi contestado. Tomemos, por exemplo, CSS. No início, nós o incluímos como style
em HTML, então decidimos que separar as folhas de CSS é o que é a separação de preocupações.
Avance uma década: os aplicativos JavaScript atuais usam componentes como uma implementação de separação de preocupações. Os desenvolvedores front-end gravitam em direção ao CSS-in-JS, e isso basicamente significa inserir CSS em HTML novamente (bem, não é tão simples, mas você entendeu). O círculo está completo!
As melhores práticas sempre foram sobre como melhorar a experiência do desenvolvedor:
Os programas devem ser escritos para que as pessoas leiam, e apenas incidentalmente para que as máquinas executem.
Abelson & Sussman, Estrutura e Interpretação de Programas de Computador
Algumas das práticas neste tutorial PHP WordPress são rápidas e fáceis de implementar em seu projeto. Por exemplo, autoloader: Faça isso uma vez por projeto e apenas aproveite. Por outro lado, novas ideias de arquitetura de software precisam de tempo, prática e inúmeras iterações para se tornarem úteis e confortáveis. As recompensas são muito maiores, no entanto. Você não só será mais eficiente no que faz, mas também desfrutará mais do que faz. E o prazer regular do trabalho que você faz para os clientes é talvez a única maneira de ser sustentável.