Эффективная стратегия автоматической загрузки и именования PHP

Как и большинство веб-разработчиков в эти дни, я полностью наслаждаюсь преимуществами надежной архитектуры MVC для веб-приложений и сайтов. При выполнении MVC с PHP автозагрузка, очевидно, очень удобна.

Я стал поклонником spl_autoload_register, просто определяя одну функцию __autoload(), поскольку это, очевидно, более гибко, если вы включают в себя различные базовые модули, каждый из которых использует собственную автозагрузку. Тем не менее, я никогда не чувствовал себя прекрасно в функциях загрузки, которые я пишу. Они требуют много проверки строки и сканирования каталогов, чтобы искать возможные классы для загрузки.

Например, скажем, у меня есть приложение, которое имеет базовый путь, определенный как PATH_APP, и простую структуру с каталогами с именем models, views и controllers. Я часто использую структуру именования, в которой файлы называются IndexView.php и IndexController.php внутри соответствующего каталога, а модели по умолчанию не имеют никакой конкретной схемы по умолчанию. У меня может быть функция загрузчика для такой структуры, которая регистрируется с помощью 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;
}

Если после этого он не будет найден, у меня может быть другая функция для сканирования подкаталогов в каталоге моделей. Тем не менее, все if/else-ing, проверка строк и сканирование каталогов кажутся мне неэффективными, и я хотел бы улучшить его.

Мне очень любопытно, какие стратегии наложения имен и автозагрузки могут использовать другие разработчики. Я ищу конкретные методы для эффективной автозагрузки, а не альтернативы автозагрузке.

Ответ 1

Это то, что я использовал во всех моих проектах (поднял прямо из источника последнего):

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;
            }
        }
    }
}

Если я ищу SomeClass_SeperatedWith_Underscores, он будет искать SomeClass_SeperatedWith_Underscores.php, за которым следует SomeClass/SeperatedWith/Underscores.php, внедренный в каждую директорию в текущем пути include.

РЕДАКТИРОВАТЬ: Я просто хотел сказать, что я использую это для эффективности в разработке, а не для времени обработки. Если у вас есть PEAR на вашем пути, то с этим вы можете просто использовать классы и не включать их, когда они вам понадобятся.

Я стараюсь, чтобы мои классы были в иерархии каталогов, с подчеркиваниями, разбивающими пространства имен... Этот код позволяет мне сохранять файловую структуру красивой и аккуратной, если я хочу, или вводить быстрый файл класса без вложенных каталогов, если я want (для добавления одного или двух классов в библиотеку, на которую она ответила, но не часть проекта, над которым я сейчас работаю.)

Ответ 2

Я приземлился на это решение:

Я создал одиночный script, который перемещает мою библиотеку библиотеки классов (которая содержит вложенные папки для отдельных модулей/систем) и анализирует содержимое файла, ища определения классов. Если он находит определение класса в php файле (довольно простой шаблон регулярного выражения), он создает символическую ссылку:

class_name.php -> actual/source/file.php

Это позволяет мне использовать одну, простую функцию автозагрузки, которая требует только имя класса и путь к основной папке symlink и не требует каких-либо манипуляций по пути/строкам.

Лучшая часть заключается в том, что я могу полностью изменить исходный код или добавить новую подсистему и просто запустить ссылку, генерирующую script, чтобы иметь все автозагрузку.

Ответ 3

Если вам нужна эффективность, вы не должны использовать функцию автозагрузки вообще. Функция автозагрузки является ленивой. Вы должны предоставлять явный путь к вашим включенным файлам при их включении. Если ваша функция автозагрузки может найти эти файлы, вы можете запрограммировать их, чтобы найти их явно. Когда вы работаете над частью представления и загружаете новый класс представления, позволяя функции автозагрузки обрабатывать его, он сначала предполагает, что ваш класс является классом модели? Это неэффективно. Вместо этого ваш код должен быть только:

include_once $this->views_path . $class . '.php';

Если вам нужно несколько путей "просмотра", создайте функцию, которая загружает представления:

public function load_view($class) {
    // perhaps there a mapping here instead....
    foreach ($this->views_paths as $path) {
        $filename = $path . $class . '.php';
        if (file_exists($filename)) {
            include_once $filename;
        }
    }
    throw ....
}

В любом случае, в точке, где происходит включение, у вас есть самая большая/самая точная информация о классе, который вы хотите загрузить. Использование этой информации для полной загрузки класса является единственной эффективной стратегией загрузки классов. Да, вы можете получить больше переменных класса или (не дай бог) некоторых глобальных переменных. Но это лучший компромисс, а не просто ленивый и сканирующий части файловой системы для вашего класса.