Pergunta

Estou me perguntando qual é a melhor prática para lidar com o problema de ter que "incluir" tantos arquivos em meus scripts PHP para garantir que todas as classes que preciso usar estejam acessíveis ao meu script.

Atualmente, estou apenas usando include_once para incluir as aulas que acesso diretamente.Cada um deles seria include_once as aulas que eles acessam.

Eu procurei usar o __autoload função, mas isso não parece funcionar bem se você planeja organizar seus arquivos de classe em uma árvore de diretórios.Se você fizesse isso, parece que acabaria percorrendo a árvore de diretórios até encontrar a classe que estava procurando. Além disso, não tenho certeza de como isso afeta classes com o mesmo nome em namespaces diferentes.

Existe uma maneira mais fácil de lidar com isso?

Ou o PHP simplesmente não é adequado para "empreendedor" digite aplicativos com muitos objetos diferentes, todos localizados em arquivos separados que podem estar em muitos diretórios diferentes.

Foi útil?

Solução

Eu normalmente tenho meus aplicativos setup.php arquivo que inclui todas as classes principais (ou seja,estrutura e bibliotecas que a acompanham).Minhas classes personalizadas são carregadas usando o autoloader auxiliado pelo mapa de layout de diretório.

Cada vez que uma nova classe é adicionada, eu executo o script construtor de linha de comando que verifica toda a árvore de diretórios em busca de classes de modelo e, em seguida, cria uma matriz associativa com nomes de classes como chaves e caminhos como valores.Então, a função __autoload procura o nome da classe nesse array e obtém o caminho de inclusão.Aqui está o código:

autobuild.php

define('MAP', 'var/cache/autoload.map');
error_reporting(E_ALL);
require 'setup.php';
print(buildAutoloaderMap() . " classes mapped\n");

function buildAutoloaderMap() {
    $dirs = array('lib', 'view', 'model');
    $cache = array();
    $n = 0;
    foreach ($dirs as $dir) {
        foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir)) as $entry) {
            $fn = $entry->getFilename();
            if (!preg_match('/\.class\.php$/', $fn))
                continue;
            $c = str_replace('.class.php', '', $fn);
            if (!class_exists($c)) {
                $cache[$c] = ($pn = $entry->getPathname());
                ++$n;
            }
        }
    }
    ksort($cache);
    file_put_contents(MAP, serialize($cache));
    return $n;
}

carregamento automático.php

define('MAP', 'var/cache/autoload.map');

function __autoload($className) {
    static $map;
    $map or ($map = unserialize(file_get_contents(MAP)));
    $fn = array_key_exists($className, $map) ? $map[$className] : null;
    if ($fn and file_exists($fn)) {
        include $fn;
        unset($map[$className]);
    }
}

Observe que a convenção de nomenclatura de arquivo deve ser [class_name].class.php.Altere os diretórios em que as classes serão pesquisadas autobuild.php.Você também pode executar o autobuilder a partir da função autoload quando a classe não for encontrada, mas isso pode colocar seu programa em loop infinito.

Matrizes serializadas são extremamente rápidas.

@JasonMichael:PHP 4 está morto.Deixe isso para trás.

Outras dicas

Você pode definir várias funções de carregamento automático com spl_autoload_register:

spl_autoload_register('load_controllers');
spl_autoload_register('load_models');

function load_models($class){
    if( !file_exists("models/$class.php") )
        return false;

    include "models/$class.php";
    return true;
}
function load_controllers($class){
    if( !file_exists("controllers/$class.php") )
        return false;

    include "controllers/$class.php";
    return true;
}

Você também pode determinar programaticamente o local do arquivo de classe usando convenções de nomenclatura estruturadas que mapeiam para diretórios físicos.É assim que Zend faz isso Estrutura Zend.Então, quando você ligar Zend_Loader::loadClass("Zend_Db_Table"); ele explode o nome da classe em uma matriz de diretórios, dividindo-o nos sublinhados e, em seguida, a classe Zend_Loader carrega o arquivo necessário.

Como todos os módulos do Zend, eu esperaria que você pudesse usar apenas o carregador sozinho com suas próprias classes, mas eu só o usei como parte de um site usando o MVC do Zend.

Mas tem havido preocupações sobre o desempenho sob carga quando você usa qualquer tipo de carregamento dinâmico de classe, por exemplo, consulte esta postagem do blog comparando Zend_Loader com carregamento rígido de arquivos de classe.

Além da penalidade de desempenho de ter que pesquisar o caminho de inclusão do PHP, isso anula o cache do opcode.De um comentário nessa postagem:

Ao usar QUALQUER carregador de classe dinâmico, a APC não pode armazenar em cache esses arquivos totalmente, pois não tem certeza de quais arquivos serão carregados em uma única solicitação.Ao carregar os arquivos com força, a APC pode armazená-los em cache por completo.

__autoload funciona bem se você tiver uma convenção de nomenclatura consistente para suas classes que informe à função onde elas são encontradas na árvore de diretórios.O MVC é particularmente adequado para esse tipo de coisa porque você pode facilmente dividir as classes em modelos, visualizações e controladores.

Alternativamente, mantenha uma matriz associativa de nomes para arquivar locais para sua classe e deixe __autoload consulte esta matriz.

Das sugestões até agora, sou parcial com a de Kevin, mas não precisa ser absoluta.Vejo algumas opções diferentes para usar com __autoload.

  1. Coloque todos os arquivos de classe em um único diretório.Nomeie o arquivo com o nome da classe, ou seja, classes/User.php ou classes/User.class.php.
  2. A ideia de Kevin de colocar modelos em um diretório, controladores em outro, etc.Funciona bem se todas as suas classes se encaixarem perfeitamente na estrutura MVC, mas às vezes as coisas ficam confusas.
  3. Inclua o diretório no nome da classe.Por exemplo, uma classe chamada Model_User estaria localizada em classes/Model/User.php.Sua função __autoload saberia traduzir um sublinhado em um separador de diretório para localizar o arquivo.
  4. Basta analisar toda a estrutura de diretórios uma vez.Na função __autoload, ou mesmo no mesmo arquivo PHP onde está definido, faça um loop sobre o conteúdo do classes diretório e armazenar em cache quais arquivos estão onde.Então, se você tentar carregar o User classe, não importa se está em classes/User.php ou classes/Models/User.php ou classes/Utility/User.php.Uma vez que encontra User.php em algum lugar do classes diretório, ele saberá qual arquivo incluir quando o User a classe precisa ser carregada automaticamente.

@Kevin:

Eu estava apenas tentando salientar que spl_autoload_register é uma alternativa melhor para __autoload já que você pode definir vários carregadores e eles não entrarão em conflito entre si.Útil se você precisar incluir bibliotecas que também definam uma função __autoload.

Tem certeza?O documentação diz diferentemente:

Se o seu código tiver uma função __autoload existente, essa função deverá ser registrada explicitamente na pilha __autoload.Isso ocorre porque spl_autoload_register() substituirá efetivamente o cache do mecanismo para a função __autoload por spl_autoload() ou spl_autoload_call().

=> você deve registrar explicitamente qualquer biblioteca __autoload também.Mas, fora isso, você está certo, esta função é a melhor alternativa.

__autoload funcionará, mas apenas no PHP 5.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top