Se você estiver escrevendo código orientado a objetos, um autoloader é obrigatório. Sem um autoloader, você precisaria adicionar uma linha incluindo o arquivo de classe, antes de poder inicializá-lo. Pode ser rapidamente complicado quando você trabalha com muitas classes. Um autoloader é uma função que dispara toda vez que uma nova classe é instanciada e inclui o arquivo de classe antes que a instanciação aconteça.
Os namespaces são uma forma de estruturar e encapsular seu código e ajudam a evitar colisões de nomes. Se você for escrever OOP, é recomendável usar namespaces também. Tenha em mente que você pode implementar um autoloader sem usar namespaces em seu código OOP.
Você pode usar este código para o seu tema ou plugin do WordPress, ou qualquer código PHP fora do WordPress – apenas modifique os caminhos de forma correspondente. Para este exemplo estou criando um autoloader para um tema WordPress.
Regras para namespace e estrutura de classe
A implementação de um autoloader exigiria algumas regras definidas para sua estrutura de código e onde encontrá-las. O uso de namespaces simplifica um pouco isso, pois seu namespace pode se referir a qual pasta as classes estão.
Primeiro, decida qual deve ser o nome do seu namespace. Normalmente é algo único para o seu código, por exemplo, o nome do seu tema. Por exemplo, o namespace para o tema deste site é AWhitePixelTheme
. Isso significa que para o autoloader funcionar, todas as classes devem estar dentro desse namespace.
namespace AWhitePixelTheme;
Minha primeira regra é que qualquer arquivo de classe sempre conterá apenas uma classe, e o nome da classe deve ser o mesmo que o nome do arquivo. Por exemplo; uma classe MyTest
deve ser definida dentro de um arquivo MyTest.php
.
Minha segunda regra é como estruturar as classes em pastas. Eu decido que todas as classes vão para dentro de uma pasta src
no meu tema. Posso colocar arquivos de classe diretamente nesta pasta, e para isso eles devem estar dentro do namespace "raiz" definido acima. Mas se eu quiser criar subpastas e colocar arquivos de classe nelas, seus namespaces devem incluir a estrutura de pastas. Por exemplo, uma classe arquivo MyTest.php
que reside na pasta src/Test/
, deve ter este namespace definido:
namespace AWhitePixelThemeTest;
Criando o carregador automático
Eu gosto de manter o autoloader em um arquivo separado e fora da src/
pasta que é definida apenas para arquivos de classe com namespace. Como exemplo vou criar um arquivo autoloader.php
na pasta inc/
do meu tema.
O PHP tem uma função de autoloader embutida: spl_autoload_register. Você fornece o nome da sua função de autoloader como parâmetro e nessa função você obtém a classe solicitada como argumento (o que você coloca depois new
ao instanciar a classe). Ao instanciar classes com namespaces, por exemplo new AWhitePixelThemeTestMyTest()
, a variável fornecida para esta função seria "AWhitePixelThemeTestMyTest"
.
Vamos adicionar a função autoloader, e nela definimos nosso namespace necessário para o autoloader:
Então precisamos incluir este arquivo para que nosso autoloader seja registrado. Como isso está em um tema, adicionarei o include no arquivo functions.php
. Se você estiver usando isso para um plug-in, coloque-o dentro de seus arquivos de plug-in. O arquivo autoloader precisa ser adicionado antecipadamente, antes de qualquer instanciação de classes. Estou colocando isso na primeira linha do meu functions.php
:
require_once(get_template_directory(). '/inc/autoloader.php');
Se você o estiver usando para um tema filho ou um plug-in, modifique o caminho para suas necessidades.
E é isso. Agora o autoloader está no lugar, mas não está fazendo nada. Vamos retornar à função autoloader e finalizá-la.
Escrevendo e testando a função de autoloader
Primeiro, precisamos garantir que o nome da classe solicitada esteja realmente dentro do nosso namespace. Simplesmente verificamos se o nome da classe do namespace fornecido contém a string do namespace e, caso contrário, saímos da função. Depois disso, removemos o nome do namespace da string, para que possamos resolver quaisquer subpastas e arquivos de classe.
Agora vamos transformar o namespace fornecido em um caminho real para o arquivo. Primeiro, substituiremos qualquer barra invertida ""
no namespace pelo caractere separador de pasta – para isso, usamos a constante PHP DIRECTORY_SEPARATOR
. No final adicionamos um ".php"
. E, finalmente, antes da string, adicionamos o caminho raiz completo. Como isso está dentro de um tema, estou usando get_template_directory()
. Se você estiver usando isso para um plug-in, use um método que retorne o caminho completo para seu plug-in.
...
$class = str_replace($namespace, '', $class);
$class = str_replace('', DIRECTORY_SEPARATOR, $class). '.php';
$directory = get_template_directory();
$path = $directory. DIRECTORY_SEPARATOR. 'src'. DIRECTORY_SEPARATOR. $class;
}
Tudo o que precisamos fazer agora é verificar se o arquivo existe e, se existir, exigi-lo.
...
$path = $directory. DIRECTORY_SEPARATOR. 'src'. DIRECTORY_SEPARATOR. $class;
if (file_exists($path)) {
require_once($path);
}
}
É isso!
Vamos testá-lo. Crie uma subpasta Test
na pasta do seu tema src/
e dentro dela coloque um arquivo php chamado MyTest.php
. Defina uma classe MyTest
nele, seguindo as regras para namespace: AWhitePixelThemeTest
. Vou apenas adicionar uma impressão de ‘Sucesso’ na função de construção para que possamos ver facilmente que ela realmente inicializa a classe.
Em nosso functions.php, após solicitar o autoloader, simplesmente instanciamos a classe:
$test = new AWhitePixelThemeTestMyTest();
Atualize seu site WordPress e veja se você obtém o ‘Sucesso!’ emitido.
O autoloader carregará automaticamente quaisquer arquivos de classe que estejam dentro do nosso namespace definido e siga as regras corretas. Você pode instanciar classes de qualquer lugar dentro do seu tema, até mesmo dentro das próprias classes.
A função de autoloader completa
Para referência, aqui está nossa função final de autoloader:
spl_autoload_register('awhitepixel_autoloader');
function awhitepixel_autoloader($class) {
$namespace = 'AWhitePixelTheme';
if (strpos($class, $namespace) !== 0) {
return;
}
$class = str_replace($namespace, '', $class);
$class = str_replace('', DIRECTORY_SEPARATOR, $class). '.php';
$directory = get_template_directory();
$path = $directory. DIRECTORY_SEPARATOR. 'src'. DIRECTORY_SEPARATOR. $class;
if (file_exists($path)) {
require_once($path);
}
}