Однажды на одном из проектов у меня возникла задача сделать централизованный автолоад классов, так как проект из "маленького" стал перерастать в "не совсем маленький". Проблема, казалось бы, банальна, но не всё оказалось так просто. Проект на битриксе, слой контроллера и модели, в большинстве своём, самописный - порядка 25 классов, которые активно юзают Zend Framework.
Кратенько о структуре папок. В папке /external проекта у нас хранится ZF, а в папке /php.inc/classes - наша библиотечка. Причём наши классы - не отельный namespace формата ZF, а просто куча файлов с префиксом, лежащие на одном уровне вложенности.
Прежде всего, регистрируем include_path'ы:
Теперь собственно решение. Самое простое решение - через функцию __autoload.
Кратенько о структуре папок. В папке /external проекта у нас хранится ZF, а в папке /php.inc/classes - наша библиотечка. Причём наши классы - не отельный namespace формата ZF, а просто куча файлов с префиксом, лежащие на одном уровне вложенности.
Прежде всего, регистрируем include_path'ы:
set_include_path(
$_SERVER['DOCUMENT_ROOT'] . '/../external' . PATH_SEPARATOR
. $_SERVER['DOCUMENT_ROOT'] . '/../php.inc/classes' . PATH_SEPARATOR
. get_include_path()
);
Теперь собственно решение. Самое простое решение - через функцию __autoload.
- Свои классы определяем просто - смотрим на первую часть имени класса - она содержит уникальный (в рамках проекта) префикс. Если есть совпадение - просто делаем require_once.
- Классы ZF тоже определяем по префиксу, но для подключения класса используем Zend_Loader::loadClass(). Он может кинуть исключение, если попытаться подключить класс, допустим, ZendTruLaLa. Поэтому оборачиваем в try/catch.
- С классами битрикса хитрее. Основное время при написании автолоадера ушло на поиск того, как битрикс автолоадит свои классы. В результате нашёл. Без подробностей - делает он это через свой автолоад и метод CModule::RequireAutoloadClass(). Он возвращает true/false - как раз то что нам нужно.
function __autoload($sClassName) {
if (strpos($sClassName, 'My') === 0) {
return require_once $sClassName . '.php';
}
if (strpos($sClassName, 'Zend') === 0) {
try {
Zend_Loader::loadClass($sClassName);
return true;
} catch (Exception $e) {
return false;
}
}
if (CModule::RequireAutoloadClass($sClassName)) {
return true;
}
return false;
}
Решение далеко не идеальное. Оно подходит в качестве первоначального "рабочего" варианта и даёт пространство для рефакторинга. Здесь отмечу, что если делать "по уму", то, конечно, это должен быть отдельный класс, что-то вроде этого:class MyAutoloader {
public function __construct() {
spl_autoload_register(array($this, 'loader'));
}
private function loader($sClassName) {
if (strpos($sClassName, 'My') === 0) {
return require_once $sClassName . '.php';
}
if (strpos($sClassName, 'Zend') === 0) {
try {
Zend_Loader::loadClass($sClassName);
return true;
} catch (Exception $e) {
return false;
}
}
if (CModule::RequireAutoloadClass($sClassName)) {
return true;
}
return false;
}
}
Поэтому ставим себе/** * @todo Разобраться, почему не подключаются классы * битрикса при использовании spl_autoload_register. * После этого можно будет вынести автолоад * в отдельный класс. */
Комментариев нет:
Отправить комментарий