Este artigo é a segunda parte da série Como Criar Plugins para WordPress. O WordPress é uma das ferramentas para gestão de sites que tem vindo a crescer mais rapidamente nos últimos anos. Uma das razões provém da sua arquitetura altamente extensível através de plugins a par com a sua API simples de implementar. Recentemente um dos nossos leitores contatou-nos indicando que estava tendo dificuldades em encontrar tutoriais WordPress sobre criar plugins, e sugeriu-nos então que publicássemos uma série de artigos sobre como criar plugins para WordPress, ideia essa que decidimos aceitar!

COMO É QUE OS PLUGINS INTERAGEM COM O WORDPRESS?

Na primeira parte fizemos uma introdução ao desenvolvimento de plugins para WordPress. Falámos sobre os tipos de plugins que existem, convenções usadas e funções base para começarmos o nosso desenvolvimento. No entanto, uma secção ficou em aberto: como é que os plugins interagem com o WordPress.

Como adiantado no artigo anterior, o WordPress apresenta várias APIs para que plugins e temas possam interagir facilmente e criar novas ou alterar funcionalidades:

  • Plugin – Providencia as hooks – um conjunto de acções e filtros – para que os plugins possam interagir em várias partes do WordPress de maneira muito simples;
  • Widgets – Permite colocar pedaços de código nas sidebars registadas do nosso tema, fazendo com que haja o display de várias informações na parte do site.
  • Shortcode – Permite que um editor chame uma função PHP definida no plugin de maneira simples no editor de posts.
  • HTTP – Esta API é um meio standard para fazer chamadas HTTP a servidores remotos sem que o autor do plugin se tenha que preocupar se existe cURL instalado. Basta chamar através desta API e ela preocupa-se em determinar qual o método de chamada mais apropriado para o servidor em que corre.
  • Settings – Permite a inserção de campos de opções personalizados na administração do WordPress para que estes possam ser usados pelos plugins facilmente. Com esta API o autor não se tem que preocupar com segurança nos ataques CRSF e XSS – ela faz tudo por si.
  • Dashboard Widgets – Permite criar e alterar widgets para a Dashboard da administração e com controlo de acessos.
  • Rewrite – Possibilita a criação de rewrite rules personalizadas para vários pontos do código dos plugins.
  • Transients – Sistema de cache básico persistente na base de dados com tempo de expiração. Pode ser usado para adicionar opções temporárias.
  • Database – Acesso rápido à base de dados sem haver necessidade de conhecimento de SQL. Permite inserir, atualizar, remover e selecionar registos.
  • Pluggable Functions – Funções providenciadas pelo WordPress que podem ser completamente redefinidas. É o caso da função wp_mail() que pode ser alterada para usar um servidor SMTP em vez do atual Sendmail.
Pelo facto destas APIs serem muito grandes iremos principalmente focar-nos em exemplos e dividir entre este e o próximo artigo as APIs.

PLUGIN ACÇÕES E FILTROS

Ao contrário da maior parte dos CMSes e Frameworks em PHP, o WordPress é construído em cima de uma estrutura de  procedimento, usando classes apenas dentro da Core como forma de organização e não de estrutura. Ao invés de usar uma estrutura MVC com a herança de classes, o WordPress é “montado” através de Hooks – acções e filtros – que são “disparados” em momentos específicos, chamando funções registadas dentro de cada uma das acções ou filtros. Este é um dos grandes entraves para developers que migram de outras plataformas, no entanto, na minha opinião, a forma como o WordPress está construído trás muito mais vantagens. Posso enumerar algumas:

  1. Classes em PHP são um meio dispendioso de recursos para estruturar códigos, pois de cada vez que se instância um novo objecto a carga de CPU é grande, além de que todos os métodos e propriedades de cada um dos objectos permanece na RAM até à sua remoção. Deve-se sempre avaliar se é realmente necessário criar uma classe ou usar apenas umas funções para fazer o nosso trabalho.
  2. Em quase todo o código do WordPress é possível filtrar dados e acionar acções, sem que haja um aumento do tempo de resposta e carregamento do WordPress.
  3. Ainda assim, é possível usar-se um modelo MVC nos seus plugins, basta que os métodos da classe pai (controller) implementem as acções e filtros apropriados ao nosso trabalho.

ACÇÕES E FILTROS

A implementação de uma hook no WordPress é feita pela função do_action( $id, $args ) na parte do código onde se pretenda dar a possiblidade de inserir novas acções.

A título de exemplo, no ficheiro wp-includes/general-template.php, linha 1580 (versão 3.3.1), é definida a função wp_footer(), normalmente incluída no template footer.php dos temas:

/**
 * Fire the wp_footer action
 *
 * @since 1.5.1
 * @uses do_action() Calls 'wp_footer' hook.
 */
function wp_footer() {
    do_action('wp_footer');
}

Esta função apenas apresenta no seu conteúdo do_action(‘wp_footer’); . Este é um bom exemplo do poder modular que as hooks têm no WordPress, pois qualquer função registada na hook wp_footer, pode ser removida.

Registar Funções numa Hook

A função add_action( $id, $nome-da-funcao, $prioridade, $numero-de-argumentos ); regista numa hook uma função qualquer comum para ser “disparada” nesse momento.

Por exemplo:

add_action( 'wp_footer', 'funcao_copyright', 10, 0 );

function funcao_copyright() {
   echo 'Copyright (c) Escola WordPress';
}

Assim, quando for chamada a função wp_footer() no nosso template, a função que acabámos de registar também correrá mostrando o copyright da Escola WordPress.

Remover Funções duma Hook

A função remove_action( $tag, $function_to_remove ); permite remover qualquer função registada para qualquer hook.

Por exemplo, pegando no código anterior, iremos registar a função e voltar a removê-la da hook:

add_action( 'wp_footer', 'funcao_copyright', 10, 0 );

function funcao_copyright() {
   echo 'Copyright (c) Escola WordPress';
}

remove_action( 'wp_footer', 'funcao_copyright' );

Se corresse este código, o copyright não iria aparecer pois, o que acabámos de fazer foi registar a função para em seguida removê-la.

Claro que este código não faz sentido, pois seria um gasto de processamento desnecessário, porém serve para demonstrar o poder das hooks. Desta forma, damos ao developer a possibilidade de remover seja o que quiser, mesmo uma função que esteja na core do WordPress e poder implementar a sua própria função customizada sem ter que alterar o código da core.

O que são os filtros?

Ao contrário das acções, os filtros servem, nem mais nem menos, para filtrar informação passada pelo WordPress em determinados momentos. Funciona da mesma maneira que as acções, porém as funções registadas nessas hooks recebem como argumento um array de variáveis com informações para serem filtradas. Esse mesmo array, já com os filtros customizados serão retornados no final do procedimento da função.

Registar e Remover Funções de filtro

As funções add_filter() e remove_filter() aceitam os mesmos argumentos e comportam-se da mesma maneira, excepto no argumento das funções registadas:

// Acrescentar Bold nos títulos dos artigos via add_filter
add_filter( 'the_title', 'titulo_acrescenta_bold' );

function titulo_acrescenta_bold( $titulo ) {
   return '<b>'.$titulo.'</b>';
}

Neste caso simples, recebemos uma variável que, depois de modificada deve retornar a função.

Acrescentar Hooks customizadas nos plugins

Esta API pode ser (e deve ser) usada também nos plugins e temas para que outros developers possam também eles hackear os seus plugins sem ter que alterar o seu código.

A função do_action( ‘nome_da_hook’ ); pode ser colocado em qualquer lugar do código, basta ter em atenção para que o id da hook já não esteja registado algures no WordPress. Para isso, prefixe-o com as iniciais do seu plugin, por exemplo, por forma a evitar esse problema.

...
// Código do seu plugin
do_action( 'escolawp_rodape' );
A função apply_filter faz o mesmo, mas para filtrar informação, por exemplo um array:

$elementos = apply_filters( 'escolawp_filtro_array', array( 'a' => 'b', 'c' => 'd' ) );
foreach( $elementos as $elemento )
// Código qualquer

Neste caso estamos a dar a possibilidade de developers manipularem o conteúdo do array.

WIDGETS

Esta API já foi muito falada aqui na escola, dexamos aqui o link para um dos artigos que explica como criar widgets para WordPress. Essa implementação pode ser facilmente transportada para um plugin.

SHORTCODES

Esta API é muito útil e fácil de implementar. Permite que qualquer utilizador possa correr código PHP dentro de um post ou página recorrendo apenas à inserção uma tag do tipo [tag argumento=”valor”] no lugar onde pretende.

Se registar a função add_shortcode( ‘tagqualquer’, ‘funcao_qualquer’ ); permite que o utilizador insira a tag [tagqualquer] num post para que a funcao_qualquer possa ser chamada na posição onde o utilizador pretende.

add_shortcode( 'cumprimentos', 'escolawp_cumprimentos' );

function escolawp_cumprimentos() {
  echo '<p>Com os melhores cumprimentos,<br />Escola WordPress</p>';
}

A implementação acima dá a possibilidade ao autor do post de incluir [cumprimentos] no final do post e que no site apareça a string HTML que se encontra na função.

Para mais ideias sobre shortcodes, leia este artigo.

HTTP

Esta API pretende centralizar todas as chamadas HTTP via PHP a páginas externas sem que o developer se tenha que preocupar qual o método instalado no servidor.

Nesta API existem 3 funções importantes:

$url = 'http://www.escolawp.com';
$argumentos = array(
  'timeout' => 5, // Desiste da chamada após 5 segundos de inatividade
  'useragent' => 'Mozilla/5.0', // Permite alterar o "browser" de PHP para Firefox por exemplo
  'sslverify' => true // Verifica se os certificados SSL são válidos em ligações HTTPS
);

// Faz uma ligação à URL com uma chamada GET
$resultado_get = wp_remote_get( $url, $argumentos );

// Faz uma ligação à URL com uma chamada POST e acrescenta
// dados POST ao argumento em forma de array
$argumento['body'] = array( 'user' => 'Bob', 'pass' => 'qwerty' );
$resultado_post = wp_remote_post( $url, $argumentos );

// Para ligações onde é apenas necessário o cabeçalho,
// como por exemplo, se se pretende saber apenas se determinado
// URL encontra-se online, usa-se o método HEAD
$resultado_head = wp_remote_head( $url, $argumentos );

Qualquer uma desta funções irá retornar um array de arrays com o cabeçalho, o corpo e a resposta. A título de exemplo, aqui fica um retorno genérico de uma chamada por uma destas funções:

Array
(
    [headers] => Array
        (
            [date] => Thu, 30 Sep 2010 15:16:36 GMT
            [server] => Apache
            [x-powered-by] => PHP/5.3.3
            [x-server] => 10.90.6.243
            [expires] => Thu, 30 Sep 2010 03:16:36 GMT
            [cache-control] => Array
                (
                    [0] => no-store, no-cache, must-revalidate
                    [1] => post-check=0, pre-check=0
                )

            [vary] => Accept-Encoding
            [content-length] => 1641
            [connection] => close
            [content-type] => application/php
        )
    [body] => <html>Isto é um site!</html>
    [response] => Array
        (
            
 => 200
            [message] => OK
        )

    [cookies] => Array
        (
        )

)

Estas funções são portanto muito úteis para developers de WordPress e devem ser usadas em vez do uso de métodos nativos como o cURL ou a função PHP get_file_contents() que sofrem do problema de poderem ou não existir em diversas instalações PHP.

OPÇÕES

O WordPress implementa a tabela wp_options com uma estrutura que permite salvar qualquer valor na forma de par chave => valor, sendo que o valor pode ser qualquer tipo nativo do PHP, seja este uma string, array, objecto ou uma instância.

Para isso existem quatro funções que podem ser usadas em qualquer altura para acicionar, receber, remover ou atualizar valores dessa tabela.

$url = 'http://escolawp.com';

// Adicionamos um valor à tabela wp_options
add_option( 'escolawp_url', $url );

// Recebemos esse valor para uma variável
$url_get = get_option( 'escolawp_url' );

// Atualizamos o valor com novos dados
update_option( 'escolawp_url', $url . '/pagina-link' );

// e por fim removemos o valor da tabela
delete_option( 'escolawp_url' );

Estas funções são muito simples, fáceis de usar e essenciais para qualquer developer WordPress. Elas permitem de uma forma rápida guardar permanentemente e obter valores da base de dados para uso nos plugins.

O exemplo seguinte guarda um array na tabela:

add_option( 'escolawp_url', array(
  'url' => $url,
  'name' => 'Escola WordPress',
  )
);

Desta forma, se obtiver este valor através da função get_option( 'escolawp_url' ) irá também  retornar-lhe o array tal e qual como quando o guardou. Fantástico não é?

Acontece que estes registos são permanentes, ou seja, não podem ser usados como meio de guardar sessões, por exemplo, pois necessitaria de um tempo de expiração a partir do qual os valores seriam apagados da base de dados. Foi a pensar nisto que foi criado os transients.

TRANSIENTS

Tal como o próprio nome indica, são "coisas" que transitam de um estado para o outro. Um transiente não é mais que um valor que permanece guardado na base de dados por um certo período de tempo, sendo eliminado após ter expirado.

Esta API é muito útil para guardar, por exemplo, sessões em base de dados, em vez de nos cookies do browser.

Para se criar um transient na base de dados usamos a função set_tansient(). As funções get_transient() e delete_transient() recolhem e removem respetivamente os valores da base de dados:

// Guardar o valor MD5 do login do utilizador por 1 hora
set_transient( 'ewp_user_login_'.get_current_user_id(), md5( get_current_user_id() ), 3600 );

A função aceita 3 argumentos: um nome para o valor, o valor em si e o tempo em segundos até expirar.

// Verificar se o Utilizador atual ainda tem um login feito à menos de uma hora
$login = get_transient( 'ewp_user_login_'.get_current_user_id() );
if ( $login )
 // o utilizador tem login efetuado à menos de uma hora
else
 // considera-se que o utilizador não tem login efetuado

Se chamar a função delete_transient( 'ewp_user_login'.get_current_user_id() ); ela irá remover qualquer vestígio deste valor da base de dados independentemente se ainda não estiver expirada.

O uso de transients pode ser por vezes abusivo. Ao criar o seu plugin, tenha em atenção em reduzir o número de chamadas à base de dados pois poderá aumentar o tempo de resposta do seu site caso o número de chamadas à base de dados seja grande.

NOTAS FINAIS

No desenvolvimento de plugins é necessário ter em atenção a questão da economia de recursos. Para isso deixo umas breves notas finais:

Nas APIs de base de dados, é natural os developers "abusarem" no número de chamadas, ora dividindo várias opções separadamente, chamando sucessivas vezes o get_option(), ora usando muitos transients. A solução na maior parte das vezes está em agrupar dados.

  1. Opte por guardar todos os dados numa única chamada add_option() ou set_transient()
  2. Ao chamar get_option() faça-o apenas uma vez, guardando o seu valor numa variável que pode ser reutilizada
  3. Não guarde conteúdo gerado, como HTML de uma página inteira na base de dados. Ao contrário daquilo que seria suposto, estas funções comportam-se muito mal com uma grande quantidade de dados. Opte por guardar um array de dados e gerar o HTML on-the-fly. Use apenas cache para guardar páginas de HTML inteiras e nunca a base de dados.
Espero que tenha gostado deste artigo. Espero ve-lo para a próxima semana na terceira parte que falará sobre as restantes APIs e segurança ao nível de plugins. Ah, e não se esqueça de comentar. :)

Abraço,

Vitor