Otimizando código com __autoload
- PHP
- January 10, 2007
O advento do PHP5 trouxe consigo outra ótima ferramenta para otimizar seu código, se trata do __autoload. Esta função pode reduzir o tempo perdido na hora de se incluir arquivos de objetos e classes em seu código. Mas como otimizar e criar uma função __autoload?
Esta função é responsável por uma tarefa simples: ao identificar a chamada a uma nova classe (ex: new ObjetoX), o PHP procura a declaração desta classe, caso o mesmo não seja encontrado é então chamado o método __autoload() que deve incluir o arquivo desta classe com sua declaração, ai então é feita a instância do objeto.
Qual a vantagem?
Em sites de grande porte geralmente, caso o desenvolvedor seja organizado claro, classes são declaradas em arquivos separados, e podem acabar sendo muitos, especialmente se trabalhando de forma OO. Com isso o que geralmente acontece é que um arquivo central é responsável por incluir todos os arquivos de declaração, sem saber ao certo se todos objetos serão usados neste script. Isto causa o overhead, carregando arquivos de forma inútil, causando um tempo de processamento maior do que o necessário.
Utilizando o autoload é possível carregar arquivos externos apenas no caso de serem necessários, evitando este overhead e poupando o acesso a disco e processamento.
Qual a desvantagem?
Já dizia um velho sábio que toda moeda tem dois lados. Da mesma forma que se ganha tempo não carregando outros arquivos externos pode-se perder tempo no momento da instanciação do objeto, que podemos considerar muito pequeno para ser levado em conta, mas desvantagem é desvantagem.
Como implementar __autoload() no meu site?
Primeiramente devemos analisar a estrutura de arquivos de seu site, e desenhar a função autoload de acordo com isso, determinando um padrão de nomenclatura de objetos, para evitarmos problemas.
Na estrutura deste exemplo temos três situações: - Classe da Biblioteca ezComponents : localizados no include_path do PHP - Classes gerais: localizadas no diretório /lib - Classes de negócio: Localizadas no diretório /lib/gcc/sistema (onde sistema varia, ex: inet ou ind)
A função autoload deve saber em qual destas pastas deve buscar a classe específica que procura. Porém a classe autoload tem apenas um parâmetro: o nome da classe
void \_\_autoload(string $class\_name)
Desta forma a mágica de tudo esta na padronização dos nomes das Classes. Neste caso decido fazer três padrões: - Classes ezComponents sempre iniciam com “ezc” - Classes de negócio sao nomeadas gcc{cod_sistema}{Objeto}, por exemplo: gccInetAgenda - Outras classes podem ter qualquer tipo de nome.
Desta forma o PHP já saberia em qual pasta procurar o arquivo, mas como identificar o arquivo? Mais uma padronização era necessária: o nome do arquivo deve ser sempre {nome_da_classe}.class.php, respeitando letras maiúsculas e minúsculas.
Então com estes padrões a missão ficou simples, a função deve identificar o tipo de classe, e rodar o algoritmo específico de cada tipo, incluindo o arquivo com o nome certo. Apenas no caso das classes de negócio, onde decidi separar as classes por subsistema, mas uma expressão regular resolve rapidamente o problema.
function \_\_autoload( $className ) { if (strpos($className,'ezc') !== false){ //classe da ezComponents ezcBase::autoload( $className ); }elseif (strpos($className,'gcc') !== false){ //classe de negócio //Separar no nome, pasta, sistema e nome ereg("(gcc)(\[A-Z\]{1}\[^A-Z\]\*)(.\*)$",$className,$bits); //Incluir arquivo combinando dados include\_once("lib/".$bits\[1\]."/".strtolower($bits\[2\])."/".$className.".class.php"); }else{ //incluir classe comum include\_once("lib/".$className.".class.php"); } }
Pequeno porém, a função autoload é global, ou seja, não pode ser declarada duas vezes. Esta função deve ser declarada em um arquivo único e central. Mas em uma estrutura bem montada de sistema este problema dificilmente ocorrerá.
Qual o impacto final?
Se formos comparar a diferença de resultados em um arquivo, por carregamento, podemos não ficar muito impressionados, porém ao se pensar um um servidor com múltiplas conexões concorrentes, e milhares de carregamentos por dia, pense bem.
No exemplo que usei para fazer os testes, criei duas estruturas paralelas onde incluía arquivos, e instanciava um objeto. Num exemplo usei o autoload e no outro carreguei todos as classes normalmente.
Foram incluídas no total 8 classes em arquivos externos. Obviamente que com um numero destes de classes é raro que todas sejam usadas em um mesmo arquivo, mas imagine um sistema onde as regras de negócio sao baseadas em objetos, você teria muitos objetos e usaria poucos por cada vez, mas sem o autoload teria de sempre inserir todos, ou inserir objetos certos e cada arquivo que usa, um tanto quanto trabalhoso.
O resultado final foi o seguinte: Página com includes manuais: 0,0366659164429 segundos Página com uso do _autoload: 0,0239880084991 segundos
Conclusão
Em uma estrutura de sistema com ponto centralizado, e onde se deseja ter menos dor de cabeça ou trabalho incluindo cada classe, a função __autoload faz uma diferença significativa, e deve ser aproveitada. Ainda não encontrei desvantagens significativas da função que possam afetar o resultado final negativamente. Caso conheça alguma coisa deixe um comentário, ele será bem-vindo.