Eficiente PHP auto-carregamento e estratégias de nomeação
-
16-09-2019 - |
Pergunta
Como a maioria dos desenvolvedores web estes dias, estou gostando os benefícios da arquitetura MVC sólida para aplicações web e sites. Ao fazer MVC com PHP, auotocarregáveis, obviamente, vem em muito útil.
Eu me tornei um fã do spl_autoload_register
mais simplesmente definindo uma única função __autoload()
, pois este é, obviamente, mais flexível, se você está incorporando diferentes módulos básicos que cada usar o seu próprio carregamento automático. No entanto, eu nunca me senti muito bem sobre as funções de carregamento que eu escrevo. Elas envolvem um monte de verificação de corda e digitalização diretório, a fim de olhar para possíveis classes para carregar.
Por exemplo, digamos que eu tenho um aplicativo que tenha um caminho de base definida como PATH_APP
, e uma estrutura simples, com diretórios nomeados models
, views
e controllers
. Eu muitas vezes empregam uma estrutura de nomeação pelo qual arquivos são nomeados IndexView.php
e IndexController.php
dentro do diretório apropriado, e os modelos geralmente têm nenhum esquema especial por padrão. Eu poderia ter uma função de carregador para esta estrutura como esta que fica registrado com spl_autoload_register
:
public function MVCLoader($class)
{
if (file_exists(PATH_APP.'/models/'.$class.'.php')) {
require_once(PATH_APP.'/models/'.$class.'.php');
return true;
}
else if (strpos($class,'View') !== false) {
if (file_exists(PATH_APP.'/views/'.$class.'.php')) {
require_once(PATH_APP.'/views/'.$class.'.php');
return true;
}
}
else if (strpos($class,'Controller') !== false) {
if (file_exists(PATH_APP.'/controllers/'.$class.'.php')) {
require_once(PATH_APP.'/controllers/'.$class.'.php');
return true;
}
}
return false;
}
Se não for encontrado depois disso, eu poderia ter outra função para sub-diretórios varredura no diretório modelos. No entanto, todo o if / else-ing, verificação de corda e digitalização diretório parece ineficiente para mim, e eu gostaria de melhorá-lo.
Estou muito curioso que nomeação e auotocarregáveis ??estratégias arquivo de outros desenvolvedores pode empregar. Eu estou olhando especificamente para boas técnicas a empregar para carregamento automático eficiente, e não alternativas para carregamento automático.
Solução
Isto é o que tenho vindo a utilizar em todos os meus projetos (levantou direto da fonte do último):
public static function loadClass($class)
{
$files = array(
$class . '.php',
str_replace('_', '/', $class) . '.php',
);
foreach (explode(PATH_SEPARATOR, ini_get('include_path')) as $base_path)
{
foreach ($files as $file)
{
$path = "$base_path/$file";
if (file_exists($path) && is_readable($path))
{
include_once $path;
return;
}
}
}
}
Se eu olhar para SomeClass_SeperatedWith_Underscores ele vai olhar para SomeClass_SeperatedWith_Underscores.php seguido por SomeClass / SeperatedWith / Underscores.php enraizada em cada diretório no atual caminho de inclusão.
EDIT: Eu só queria colocar lá fora que eu uso isso para a eficiência no desenvolvimento, e não necessariamente o tempo de processamento. Se você tem PEAR em seu caminho, em seguida, com isso, você pode apenas usar as classes e não tem que incluí-los quando você precisar deles.
Eu tendem a manter minhas aulas em uma hierarquia de diretórios, com sublinhados quebrando namespaces ... Este código permite-me manter a boa estrutura do arquivo e arrumado, se eu quiser, ou para injetar um arquivo de classe rápida, sem diretórios aninhados se eu quer (para a adição de uma única classe ou dois para uma biblioteca que é acusado, mas não faz parte do projeto que eu estou trabalhando atualmente.)
Outras dicas
Eu aterrou nesta solução:
Eu criei um único script que atravessa minha pasta biblioteca de classes (que contém subpastas para módulos / sistemas separados), e analisa o conteúdo do arquivo em busca de definições de classe. Se ele encontrar uma definição de classe em um arquivo php (padrão de regex bastante simples), ele cria um link simbólico:
class_name.php -> actual/source/file.php
Isto deixa-me usar uma única função autoload, simples que as necessidades apenas o nome da classe e o caminho para a pasta symlink principal, e não tem que fazer qualquer manipulação path / string.
A melhor parte é que eu posso reorganizar meu código fonte completo ou adicionar um novo subsistema e apenas executar o script de geração de link para ter tudo carregados automaticamente.
Se você quiser eficiência, então você não deve estar usando o recurso de carregamento automático em tudo. O recurso autoload é para ser preguiçoso. Você deve fornecer um caminho explícito para o seu incluir arquivos quando você incluí-los. Se a sua função autoload pode encontrar esses arquivos, então você pode codificar para encontrá-los explicitamente. Quando estiver a trabalhar na vista parte do código e sobre para carregar uma nova classe vista, deixando a função autoload lidar com isso, primeiro ele assume sua classe é uma classe de modelo? Isso é ineficiente. Em vez disso o seu código deve ser apenas:
include_once $this->views_path . $class . '.php';
Se precisar de múltiplos caminhos "vista", fazer uma função que as opiniões cargas:
public function load_view($class) {
// perhaps there's a mapping here instead....
foreach ($this->views_paths as $path) {
$filename = $path . $class . '.php';
if (file_exists($filename)) {
include_once $filename;
}
}
throw ....
}
Em qualquer caso, no ponto onde o incluem ocorre, você tem o maior mais informações / precisas sobre a classe que você deseja carregar. Usando essa informação para carregar a classe é plenamente a estratégia de carregamento de classe apenas eficiente. Sim, você pode acabar com mais variáveis ??de classe ou (Proibir céu) algumas variáveis ??globais. Mas isso é uma compensação melhor do que apenas ser preguiçoso e digitalização de partes do sistema de arquivos para a sua classe.