Перейти к содержимому

Сторонняя библиотека в проекте - composer, автозагрузка, выкладка

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

Что нужно знать заранее

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

Каталог ядра при обновлении перезаписывается целиком. Всё своё - описание зависимостей, установленные библиотеки и код проекта - держат в отдельном каталоге, который обновление не трогает.

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

Шаги

  1. Положить описание зависимостей в свой каталог, а не в каталог платформы.
  2. Установить библиотеку менеджером зависимостей и проверить появление каталога библиотек.
  3. Подключить автозагрузчик один раз, в файле инициализации своего каталога.
  4. Решить, что попадает в репозиторий: описание с замком версий или сами библиотеки.
  5. Добавить установку зависимостей в порядок выкладки на боевой сайт.

Решение

Кладём описание зависимостей в свой каталог:

Окно терминала
cd /home/site/www/local
composer require guzzlehttp/guzzle:^7.0 # библиотека приедет в local/vendor
ls -d /home/site/www/local/vendor # каталог ядра при этом не трогаем
cat /home/site/www/local/composer.json # описание зависимостей проекта
# то же самое подходит и для каталога /local на многосайтовой копии

Каталог платформы обновление перезаписывает целиком, вместе со всем, что туда положили. Описание зависимостей и установленные библиотеки живут в своём каталоге и переживают любое обновление продукта.

Подключаем автозагрузчик один раз:

// /local/php_interface/init.php - выполняется на каждом запросе
require_once $_SERVER['DOCUMENT_ROOT'] . '/local/vendor/autoload.php';
// подключение в шаблоне сайта не работает в админке, агентах и консоли

Файл инициализации выполняется в любом режиме работы платформы. Подключение из шаблона сайта выглядит рабочим до первого фонового задания, а потом даёт ошибку об отсутствующем классе.

Регистрируем своё пространство имён:

\Bitrix\Main\Loader::registerNamespace('Vendor\\Project', '/local/lib');
// свой код можно отдать и автозагрузчику платформы, а не только менеджеру
$service = new \Vendor\Project\Service\OrderExport();

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

Проверяем, что библиотека действительно загружается:

var_dump(class_exists(\GuzzleHttp\Client::class)); // true - автозагрузчик работает
var_dump(\Composer\InstalledVersions::getVersion('guzzlehttp/guzzle'));
// проверку удобно держать отдельным служебным скриптом в своём каталоге

Проверка занимает секунду и снимает вопрос «поставилось или нет» на любом стенде. Печать установленной версии дополнительно показывает, совпадает ли она с той, что записана в замке версий проекта.

Версионируем описание, а не библиотеки:

Окно терминала
git add local/composer.json local/composer.lock
echo 'local/vendor/' >> .gitignore # библиотеки ставит команда выкладки
git commit -m "библиотека для работы с HTTP"
git log --oneline -1 -- local/composer.lock # замок версий обязан быть в истории

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

Ставим зависимости при выкладке:

Окно терминала
composer install --no-dev --optimize-autoloader --working-dir=/home/site/www/local
# без этой команды на боевом сайте не окажется ни одной библиотеки

Установку зависимостей добавляют в порядок выкладки рядом со сборкой фронтенда. Флаг без разработческих пакетов убирает тесты и отладочные инструменты, которые на боевом сайте не нужны.

Типичные проблемы

После обновления платформы библиотеки исчезли.

Описание зависимостей и каталог библиотек лежали внутри каталога ядра. Обновление перезаписывает его целиком, поэтому своё держат в отдельном каталоге.

Класс не найден в админке или в фоновом задании.

Автозагрузчик подключён в шаблоне сайта, который в этих режимах не выполняется. Подключение переносят в файл инициализации своего каталога.

На боевом сайте нет ни одной библиотеки.

Каталог библиотек не версионируется, а установка зависимостей в порядок выкладки не добавлена. Команду установки ставят рядом со сборкой фронтенда.

Ошибка появляется только на сервере, а на стенде всё работает.

Различается регистр имён файлов, а файловая система сервера к нему чувствительна. Имена файлов приводят к одному виду и проверяют на копии боевого окружения.

Библиотека конфликтует с версией из платформы.

Та же библиотека уже поставляется вместе с продуктом и загружается первой. Версии сверяют до установки и при конфликте берут ту, что уже есть.

Частые вопросы

Класть ли каталог библиотек в репозиторий?

Обычно нет: в репозитории держат описание и замок версий. Исключение - выкладка без доступа к сети, когда библиотеки приходится нести с собой.

Где именно подключать автозагрузчик?

В файле инициализации своего каталога, до первого обращения к классам библиотеки. Так он работает и на витрине, и в админке, и в консольных скриптах.

Можно ли обойтись без менеджера зависимостей?

Можно, но обновлять и сверять версии придётся руками. Для одной маленькой библиотеки это допустимо, для нескольких - уже нет.

Что делать при конфликте версий?

Смотреть, кто требует конфликтующую версию, и по возможности брать ту, что уже есть в проекте. Две версии одной библиотеки в одном процессе не уживаются.

Ускоряет ли что-нибудь автозагрузку?

Да, сборка оптимизированной карты классов при установке зависимостей. На боевом сайте её включают всегда, на стенде она мешает частым правкам.

Смежное

Первоисточник