Почему Laravel пытается подключиться к БД при инициализации?

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

assorted electric cables
Photo by John Barkiple on Unsplash

Столкнулся на работе с занятной проблемой на Laravel 9.

Однажды, я, прогоняя тесты phpunit, никак не мог добиться их удачного выполнения: они постоянно падали, но написаны гарантированно верно, вчера их запускал. Через какое-то всё прошло. Я не был удивлён: на БД-песочнице проводились работы, вскоре она была перезапущена и я таки прогнал тесты с успехом.

Но осознав произошедшее меня обдало ледяным потом — стоп, а какого хуя? В моих тестах нет подключения к БД!


Начав работу над новым сервисом, я подключил статический анализатор vimeo/psalm с плагином psalm/plugin-laravel. Нацепил на pre-commit хук гита, и, настроив самые жестокие (но подходящие) проверки, больше не вспоминал. Работает складно, не мешает, носом тычет, булшит коммитить не даёт.

Начал гонять какие-то свои тесты. Они были унаследованы от штатного класса Test/Testcase, в котором подключен трейт CreatesApplication. В setUp() методе теста вызывается единственый метод этого трейта $this->createApplication(). Так на время выполнения тестов создаётся полное окружение бекенда, с контейнерами зависимостей, роутами и прочим. Полезно, однако ошибка происходит именно из-за этого, где-то при инициализации классов.

Расчехлив отладчик, вскоре я внезапно очутился на вызове DB::listen(). Это метод фасада DB, который после подключения к БД начинает следить за выполненяемыми запросами (для логирования, например). Этой фигни точно быть не должно.

Тем не менее, компания spatie так не считает. Их пакет spatie/laravel-ray, предназначенный для помощи в отладке проекта, безусловно мешает попыткой подключиться к БД. Разумеется, подключение не удаётся и тесты падают.

Я начал размышлять как можно переопределить это поведение:

  1. перегрузить/заmockать классы/методы laravel-ray хотя бы в рамках теста — безрезультатно;
  2. настроить конфиг ray.php, где явно выключил вообще всё — не помогло (DB::listen() отрабатывает раньше, чем этот конфиг вообще прочитается);
  3. настроить конфиг с отдельным драйвером для подключения к БД — и сработало.

Сработало в таком виде:

// config/database.php
'connections' => [
    ...
    'testing' => [
        'driver'   => 'sqlite',
        'database' => ':memory:',
        'prefix'   => ''
    ],
    ...
]

// phpunit.xml
<phpunit ...>
    ...
    <php>
        ...
        <server name="DB_CONNECTION" value="testing"/>
    </php>
</phpunit>

Хак классический, в общем, известный, но я подумал — а может его вообще снести нахуй, этот рей? В composer.json его, ожидаемо, нет — я ведь его не подключал! Значит и удалить штатно не получится.

Попытавшись развязать клубок зависимостей в composer.lock я понял, что от этого пакета нам просто так не избавиться — это сквозная зависимость вплоть до ядра Laravel (где-то в require, где-то в require-dev). Крушить я на тот момент не был готов, погружаться не стал; в конце концов, тесты стали отрабатывать и это меня успокоило.


Через время я наткнулся на похожие ошибки подключения к БД на этапе auto-discovery пакетов после composer require/update/remove/dumpautoload.

И знаешь что? Это был другой бекенд. Никакой БД в нём никак не участвует, не подключается. Вообще. Меня прошибло дежавю.

Auto-discovery — механизм Laravel (начиная с 5.5), который позволяет обнаруживать composer-пакеты с классами сервис-провайдеров, которые подключаются самостоятельно, без необходимости вручную прописывать их имена в конфигах проекта. И в этот момент, разумеется, отрабатывает знакомый нам spatie/laravel-ray и его DB::listen().

Я снова озадачился этой проблемой. Пошёл к людям, мол, чё делать-то? И в конце концов меня ткнули в composer why.

Помнишь, в начале поста я упоминал psalm/plugin-laravel? Так вот, он тащит за собой orchestra/testbench. А эта сука тянет за собой что? Рей. И обе зависимости в секции require, а не в require-dev.

Таким образом получаем ситуацию, когда сам плагин псалма, будучи dev-зависимостью в dev-окружении, по цепочке триггерит сквозные не-dev-зависимости, будто так и надо. В итоге всё либо падает, либо зря подключается к БД. Может быть сейчас это уже кажется логичным, но не перестаёт быть контринтуитивным поведением, причём не только композера, но и самих пакетов.

На время работы над проектом я подключен к корпоративному VPN, за которым находится и БД. В конечном счёте, корректное состояние сети всегда перекрывало некорректное поведение проекта.


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

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *