Otimizando código com __autoload

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.

This post is also available in: Inglês

6 comentários sobre “Otimizando código com __autoload

  1. Muito boa a sua apresentação sobre a __autoload(). Descobri ela a um tempo atrás enquanto lia o manual do php. Desde então não faço mais nenhum projeto sem ela. Eu geralmente coloco as classes em duas pastas: uma para regra de negócios e outra para a estrutura do sistema. A __autoload() é perfeita para isso.

  2. Olá Rafael,

    Há uma desvantagem que deve ser considerada. Você não pode por exemplo verificar se uma classe está incluida, caso ela não esteja na pasta das classes. Se você fizer isso, a função __autoload automaticamente tentará incluir a classe e dará erro pois não encontrará o arquivo.

    if(class_exists(“foo”)) echo “Ok”;
    else echo “erro”;

    Claro que em um sistema organizado você deve deixar todas as classes na mesma pasta, mas nem sempre é assim.

    Abraços

  3. Eu trabalho com uma idéia muito parecida com a sua, entretanto ainda tenho uma classeFachada que seria pra chamar todos os métodos, entretanto como temos vários módulos cada módulo tem um arquivo deste tipo, nesse ponto complicou um pouco o seu artigo pra mim, mas dá uma excelente ajuda.
    Em outros sites sempre é o mesmo exemplo.

    Parabéns você partiu para uma situação real.

  4. function __autoload($class){
    global $classAlert;
    $arq = “classes/”.strtolower($class).”.class.php”;
    if(file_exists($arq)){
    require_once($arq);
    if(!class_exists($class)){
    $classAlert = “A classe não foi encontrada no arquivo {$arq}”;
    }
    }else{
    $classAlert = “Houve um erro no script, o arquivo {$arq} não foi encontrada”;
    //die();
    }
    echo $classAlert;
    }

Os comentários estão desativados.