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

Столкнулся на работе с занятной проблемой на 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 не будет опубликован. Обязательные поля помечены *