Ir para conteúdo

POWERED BY:

Arquivado

Este tópico foi arquivado e está fechado para novas respostas.

Felipe Rex

Autoload - Sem perder performance e desempenho

Recommended Posts

Estive vendo a função spl_autoload_register() e achei super interessante e útil. Mas levantou uma questão de performance quando eu implementei essa função em meus projetos.

 

 

Talvez, alguns achem que não há nenhum problema na questão que vou mencionar abaixo, porém, Eu dou grande valor à performance/desempenho de uma aplicação e não meço esforços para melhorar o código para alcançar o melhor resultado. Esse fascínio é por causa que uma aplicação de grande porte pode usar o seu código e o desempenho pode ser um fator importante para o sucesso da aplicação como um todo.

 

Então vamos lá...

Um exemplo simples de autoload:

 

Nome da aplicação: Imaster

 

Classes:

Arquivo: /forum/Imaster.class.php

class Imaster {}

 

Arquivo: /forum/Forum.class.php

class Forum {}

 

Arquivo: /forum/topico/Pergunta.class.php

class Pergunta {}

 

 

Diretórios dos arquivos

/forum/Imaster.class.php

/forum/Forum.class.php

/forum/topico/Pergunta.class.php

 

 

 

Agora nosso autoload para carregar a aplicação Imaster

 

Arquivo: /forum/_autoload.php

function autoload($className)

{

//carrega arquivos pesquisando nos diretórios da aplicação Imaster

}

 

Teste

new Imaster();

new Forum();

new Pergunta();

 

//output

OK, classes instanciadas.

 

----

Agora a questão da Performance/Desempenho

----

 

Agora imaginemos uma outra aplicação.

 

 

Nome da aplicação: Smile

 

 

Classes:

Arquivo: /emoticon/Smile.class.php

class Smile {}

 

Arquivo: /emoticon/icones/Carinhas.class.php

class Carinhas {}

 

 

Diretórios dos arquivos

/emoticon/Smiles.class.php

/emoticon/icones/Carinhas.class.php

 

 

 

Autoload para carregar a aplicação Smile

 

Arquivo: /emoticon/_autoload.php

function autoloadSmile($className)

{

//carrega arquivos pesquisando nos diretórios da aplicação Smile

}

 

 

 

-----------------------------

 

Usando as duas bibliotecas numa aplicação qualquer.

 

 

Arquivo: /Imaster.php

 

include_once('/forum/_autoload.php');

include_once('/emoticon/_autoload.php');

 

 

//registra os autoloader que farão o carregamento dos arquivos requisitados

spl_autoload_register('autoload');

spl_autoload_register('autoloadSmile');

 

 

//Instanciando classes

//aplicação: Imaster

new Imaster();

new Forum();

new Pergunta();

//aplicação: Smile

new Smile();

new Carinhas();

 

//output

OK, todas as classes foram instanciadas.

 

 

A QUESTÃO

 

 

No arquivo Imaster.php, rodaria sem problema. Porém, quando queremos instanciar as classes da aplicação Smile, o autoload que irá ser requisitado primeiro será o da aplicação Imaster, então, será percorrido todos os diretórios desta aplicação para ver se acha os arquivos da aplicação Smile, sem sucesso, irá para o próximo autoload registrado (no caso o autoloadSmile), agora sim, irá achar os arquivos da aplicação Smile.

 

Exemplo da questão citada acima:

ao executar new Smile(); a aplicação irá fazer isso:

 

Tenta pelo /forum/_autoload.php

Checando se existe: /forum/Smile.class.php [não encontrado]

Checando se existe: /forum/topico/Smile.class.php [não encontrado]

Tenta pelo /emoticon/_autoload.php

Checando se existe: /emoticon/Smile.class.php [encontrado]

 

 

Nesta verificação sem sucesso, a aplicação pode perder muito tempo e performance se o primeiro autoload tiver muitos diretórios para checar... a minha idéia seria indicar à função spl_autoload, o autoload a ser executado primeiro, ou seja, reorganizar a ordem dos autoloads registrados.

 

 

Exemplo para demonstrar a idéia:

 

Arquivo: /Imaster.php

 

include_once('/forum/_autoload.php');

include_once('/emoticon/_autoload.php');

 

 

//registra os autoloader que farão o carregamento dos arquivos requisitados

spl_autoload_register('autoload');

spl_autoload_register('autoloadSmile');

 

 

//Instanciando classes

//aplicação: Imaster

new Imaster();

new Forum();

new Pergunta();

 

//aplicação: Smile

spl_autoload_primary('autoloadSmile'); //ESSA FUNÇÃO NÃO EXISTE, É APENAS PARA DEMONSTRAR A IDÉIA

new Smile();

new Carinhas();

 

//output

OK, todas as classes foram instanciadas.

 

 

No caso acima, a aplicação não perderia tempo em pesquisar diretórios errados, iria direto no seu.

 

 

Alguém tem sugestões de como pode ser implementado essa idéia?

Compartilhar este post


Link para o post
Compartilhar em outros sites

para os models prefiro não deixar no autoload

 

faço por include diretamente

 

no autoload deixo apenas as libraries nativas framework

 

Vejo muitos frameworks indepedentes onde o developer faz loops recursivos nos folders e subfolders desnecessariamente... a consequência é perda de performance, redundância..

 

exemplo,

se a url chama o módulo

http://foo.bar/?p=person/add

 

no controlador é feita a requisição e filtragem do parâmetro e montagem do path para a classe

 

$file = $_GET['p'];
$path = BASE_DIR . 'modules/' . $file;
if( file_exists($path) ){
   require_once( $path );
}else{
   // retorna um not found ou qq outra coisa..
}

*isso é apenas um escopo rápido, sem tratamento de dados, etc.. apenas para exemplo

 

 

sacou ?

 

não ha necessidade em ficar dando loop recursivo nas pastas dos módulos e outras que não são essenciais para o carregamento do sistema.

Compartilhar este post


Link para o post
Compartilhar em outros sites

para os models prefiro não deixar no autoload

 

faço por include diretamente

 

no autoload deixo apenas as libraries nativas framework

 

Vejo muitos frameworks indepedentes onde o developer faz loops recursivos nos folders e subfolders desnecessariamente... a consequência é perda de performance, redundância..

 

exemplo,

se a url chama o módulo

http://foo.bar/?p=person/add

 

no controlador é feita a requisição e filtragem do parâmetro e montagem do path para a classe

 

$file = $_GET['p'];
$path = BASE_DIR . 'modules/' . $file;
if( file_exists($path) ){
   require_once( $path );
}else{
   // retorna um not found ou qq outra coisa..
}

*isso é apenas um escopo rápido, sem tratamento de dados, etc.. apenas para exemplo

 

 

sacou ?

 

não ha necessidade em ficar dando loop recursivo nas pastas dos módulos e outras que não são essenciais para o carregamento do sistema.

 

 

Hinom, eu também fazia o carregamento das classes direto por meio de include. Porém, numa página que use vamos supor 3 bibliotecas diferentes, já pensou quantos include seria necessário deixar explicito para rodar a aplicação? E dessa forma a tendencia do programador é dar include em todas as classes da biblioteca (a fim de não dar erro de dependência), coisa que nem sempre é necessário, pois pode não precisar de todas as classes. É justamente este o papel do autoload, incluir somente as classes necessárias para a aplicação funcionar e sem dar ao programador o trabalho de incluir todas as classes de forma manual.

 

Sem dúvida, eu vejo uma notável vantagem em usar autoload em todas as bibliotecas e evitar o include manual. A única questão que estou em busca de uma solução é na questão da performance em não fazer uma aplicação pesquisar diretórios que não seja do autoload especifico dele.

 

 

Se pudesse dizer ao php qual autoload deve executar primeiro seria muito bom, pois não perderia tempo pesquisando em diretórios errados.

Compartilhar este post


Link para o post
Compartilhar em outros sites

no seu caso teria que criar um controle dos folders das classes que serão carregadas quando requisita um model

Compartilhar este post


Link para o post
Compartilhar em outros sites

Leia sobre a PSR-0, padronize sua estrutura de arquivos e diretórios e não tenha mais problemas com autoloads complexos, lentos ou problemáticos pois até a a mais simples das implementações funcionará.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Leia sobre a PSR-0, padronize sua estrutura de arquivos e diretórios e não tenha mais problemas com autoloads complexos, lentos ou problemáticos pois até a a mais simples das implementações funcionará.

 

 

 

Eu dei uma lida rápida sobre o padrão PSR-0 e tive uma noção das especificações, vou ler mais profundamente durante a próxima semana.

 

Eu notei que a Zend atualmente usa em todas as suas classes namespace para facilitar ao autoload carregar as classes, é isso mesmo? (pelo que entendi do PSR-0, é essa a idéia).

 

No caso, para solucionar o problema de autoload que é a questão do tópico, acredito que o que você sugeriu é ter um diretório padrão para todas as aplicações/classes. E usando as especificações da PSR-0, com o uso de namespace eu consigo dizer ao autoload qual o diretorio dentro do diretório padrão estão os arquivos...

 

 

exemplo:

 

Arquivo: classeXYZ.php

namespace AplicacaoXYZ\;

class XYZ {}

 

 

Arquivo: _autoload.php

function autoload($className)
{
  //lê o namespace, obtendo os diretórios da aplicação
  //testa o arquivo se existe
  //inclui o arquivo baseando-se no [b]diretório padrão[/b]
}

 

 

 

Seria mais ou menos isso, Bruno?

Compartilhar este post


Link para o post
Compartilhar em outros sites
Eu notei que a Zend atualmente usa em todas as suas classes namespace para facilitar ao autoload carregar as classes, é isso mesmo? (pelo que entendi do PSR-0, é essa a idéia).

Namespaces servem para duas coisas. A mais básica delas é encurtar os nomes das classes.

 

Enquanto o ZF antigo usava uma classe chamada, por exemplo, Zend_Db_Table_Abstract, sendo o arquivo da classe Abstract.php estando localizado em Zend/Db/Table, com namespaces a classe em questão PASSARIA a se chamar apenas algo como TableAbstract.

 

Note que com naemspaces você terá uma pequena chateação com classes abstratas em interfaces quando nomeando-as em inglês, haja vista que abstract e interface são palavras reservadas.

 

E veja que o verbo ali em cima está bem destacado pois, pelo visto, no ZF2 essa classe não existe mais.

 

Enfim... A segunda e mais importante é evitar colisões com nomes de classes, para que se reduzisse, para não dizer eliminar, erros do tipo Cannot redeclare class XXX

 

No caso, para solucionar o problema de autoload que é a questão do tópico, acredito que o que você sugeriu é ter um diretório padrão para todas as aplicações/classes.

Sim e não.

 

SIM porque quanto melhor e mais bem definida for sua hierarquia de diretórios, melhor será sua organização, mais bem definidos serão seus namespaces e mais simples será seu autoloader.

 

E NÃO porque isso é com você. Se você acha trabalhoso criar Um/Diretório/Para/Cada/Subclassificação/Das/Suas/Classes ( :upset: ), basta você aumentar seu include_path e fazer um autoloader mais elaborado.

 

O problema aqui é que quanto mais extenso for seu include_path maior será o tempo de carregamento das suas classes pois antes de fazê-lo todo o include_path será varrido atrás de uma combinação.

 

E um autoloader muito complexo, cheio de IF's, tratamentos de strings, ordenação e sei lá mais o quê, pesará na performance geral do sistema.

 

E como tempo é dinheiro... :rolleyes:

 

E usando as especificações da PSR-0, com o uso de namespace eu consigo dizer ao autoload qual o diretorio dentro do diretório padrão estão os arquivos...

Como eu disse você consegue fazer o que quiser com seu autoloader. A PSR-0 é um consenso a que chegaram diversos programadores de peso do mundo do PHP e visa padronizar a forma como se codifica um código.

 

Isso garante que a estrutura do Código A será similar à estrutura do Código B, como diretórios, nomes de classes etc.

 

exemplo:

 

Arquivo: classeXYZ.php

namespace AplicacaoXYZ\;

class XYZ {}

 

 

Arquivo: _autoload.php

function autoload($className)
{
  //lê o namespace, obtendo os diretórios da aplicação
  //testa o arquivo se existe
  //inclui o arquivo baseando-se no [b]diretório padrão[/b]
}

Você precisa ler e entender o que são namespaces.

 

Não somente para que você os defina corretamente (essa barra invertida antes do ponto-e-vírgula está errado) como para que você saiba o que vai acontecer com os códigos que estiverem dentro um namespace quando usados em outro e como usá-los nessa circunstância (importando-os).

 

Quanto ao seu autoloader, sempre que você usar uma classe que não tenha sido declarada no arquivo ainda (via include/require), ele será invocado e o parâmetro $className virá com todo o namespace definido mais o nome da classe.

 

Ou seja:

 

Quando a classe Baaz pertencente ao namespace Foo/Bar (veja abaixo) for chamada, o argumento daquela função será Foo/Bar/Baaz e a partir dessa string que você localiza a classe.

 

Classe Hipotética

<?php

namespace Foo\Bar;

class Baaz {}

O meu autoloader (e o de muita gente) é bem simples:

 

function( $classname ) {

   $classname = stream_resolve_include_path(

       str_replace( '\\', DIRECTORY_SEPARATOR, $classname ) . '.php'
   );

   if( $classname !== FALSE ) {

       include $classname;
   }
}

Substituo as barras invertidas pelo separador de diretórios e adiciono um .php. Pronto, já tenho path completo até o arquivo.

 

Então verifico se esse path pode ser resolvido em um arquivo válido e, se positivo, incluo o arquivo.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Eu sei que o propósito principal dos namespace é possibilitar utilizar classes com nomes iguais num mesmo contexto. Só dei ênfase à utilizacao com autoload por ser esse o assunto do tópico. A barra junto com o ponto e virgula eu não tinha notado que digitei, acho que foi na hora de colar aqui...

 

Enfim... realmente você deixou claro que os namespace são úteis tbm para o carregamento das classes. Eu tinha notado isso no código da Zend no github. É algo muito interessante para mim!

 

 

Agradeço por ter ajudado com a explicação e principalmente por expressar como você usa o seu autoload.

 

Uma pergunta... o autoload fica dependente de namespace? E se a classe não tiver namespace, neste caso o autoload falha?

Compartilhar este post


Link para o post
Compartilhar em outros sites

Dependendo da implementação do autoloader pode sim falhar.

 

Não que ele não seja invocado, pois o autoloader é chamado sempre que uma classe não declarada for utilizada mas, a exemplo da simplicidade do meu autoloader acima, stream_resolve_include_path() não conseguirá resolver o path da classe, pois como o nome dela não terá as barras invertidas, aquele str_replace() não vai fazer nada.

 

Mas nada te impede de ter um sistema preparado para suportar classes com e sem namespaces ao mesmo tempo.

 

É aí que está a maior vantagem de spl_autoload_register() sobre __autoload(): Permitir mais de uma estratégia de carregamento sem sobrecarregar o próprio método de carregamento com N condicionais:

 

spl_autoload_register(

   function( $classname ) {

       $classname = str_replace( '\\', DIRECTORY_SEPARATOR, $classname ) . '.php';

       if( stream_resolve_include_path( $classname ) !== FALSE) {

           include $classname;
       }
   }
);

spl_autoload_register(

   function( $classname ) {

       $classname = str_replace( '_', DIRECTORY_SEPARATOR, $classname ) . '.php';

       if( stream_resolve_include_path( $classname ) !== FALSE) {

           include $classname;
       }
   }
);

Vai funcionar para tanto para a classe Baaz do namespace Foo\Bar, quanto para a classe Foo_Bar_Baaz, sem namespace algum.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Perfeito Bruno, entendi, muito bem explicado. Essa solução de autoload com namespace despertou meu interesse por eu ver que a Zend ta usando em toda as suas classes... com certeza essa forma é a mais indicada até o momento.

 

 

Já vou implementar um autoload dessa forma, valeu a dica dos dois autoload distintos.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Todas as classes não. Como que você aplica o autoload nele mesmo? O ZF, assim como uma boa maioria de frameworks que querem abraçar o mundo (o/) tem as diferentes estratégias de autoload também em classes.

 

Mas são classes sem namespaces senão ficaríamos num loop de carregamento infinito. :lol:

 

Mas enfim, eu não penso assim "se o ZF usa, tá certo" porque apesar de ser um framework sólido e parrudo, eu não curto todo o peso dele para coisa simples. É preferível levar em conta a adoção da PSR-0 como referencial.

 

Mesmo porque, xeretando no código você vê umas gafes sutis ou umas besteiras sendo feitas aqui e ali.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Aproveitando o tópico, quando uma classe possui um namespace como posso instanciar a classe sem ser pelo uso do nome do namespace? Estou com essa dificuldade...

 

 

Por exemplo:

 

Arquivo ferramentaA.php

namespace testeNamespace\ferramentas;
{

   /**
    * Representação de classe com uso de namespace
    *
    * @author Felipe
    */
   class FerramentaA 
   {
       public function __construct()
       {
           echo "FerramentaA instanciada<br />";
       }
   }
}

 

 

Arquivo objetoFerramenta.php

require_once('ferramentaA.php');

//chama classe pelo seu nome, sem usar o namespace.
new FerramentaA();

 

 

 

A chamada pelo nome sem namespace gera um fatal error. Por que?

Compartilhar este post


Link para o post
Compartilhar em outros sites

Existem várias pequenas diferenças, vou cobrir as principais em dois exemplos.

 

Exemplo #1 - Dois arquivos sob mesmo namespace

 

Nesse exemplo dispensa-se o uso de use ou de barras invertidas para qualificação completa do(s) namespace(s):

 

<?php

namespace Foo/Bar;

class Baaz {}

<?php
 
namespace Foo\Bar;
 
class Baaz2 {
 
    public function __construct() {
 
        $baaz = new Baaz;
    }
}
 
[b]Exemplo #2[/b] - Dois arquivos em namespaces diferentes
 
Você pode qualificar completamente, usando as barras invertidas ou importá-lo com [b]use[/b]:
 
[code]<?php

namespace Foo/Bar;

class Baaz {}

<?php

namespace FooDois\BarDois;

use \Foo\Bar\Baaz;

class Baaz2 {

   public function __construct() {

       $baaz = new Baaz; // Com o 'use' acima

       $baaz = new \Foo\Bar\Baaz; // Sem o 'use' acima
   }
}

Perceba que o use foi colocado FORA da definição da classe.

 

Algumas informações adicionais:

 

Podem haver casos em que mesmo utilizando um use o nome fique grande -OU- o nome da classe importada conflite com o nome da classe atual.

 

Nesses casos você utiliza um alias definido pelo uso de as após a qualificação feito com use:

 

<?php

namespace Foo\Bar;

use \My\Class\Inside\Too\Much\Subdirectories\AndWithAVeryLongAndUselessName as MyClass;

class Baaz {

   public function __construct() {

       $myClass = MyClass;
   }
}

Existem ainda as classes nativas do PHP como ReflectionClass, DateTime e etc. Dentro de um namespace elas não vão funcionar pois ao fazer:

 

<?php

namespace Foo\Bar;

class Baaz2 {

   public function __construct() {

       $dateTime = new DateTime;
   }
}

A classe DateTime será buscada como Foo\Bar\DateTime e como ela potencialmente não existirá, produzirá um erro.

 

Para esses casos, vale a mesma regra do Exemplo #2, isto é, prefixar com uma barra invertida:

 

<?php

namespace Foo\Bar;

class Baaz2 {

   public function __construct() {

       $dateTime = new \DateTime;
   }
}

Você também pode importar com use, mas eu pessoalmente acho, hoje, inútil.

Compartilhar este post


Link para o post
Compartilhar em outros sites

×

Informação importante

Ao usar o fórum, você concorda com nossos Termos e condições.