Skip to content

[Bug] Установка 1.14.0-beta1: Class ComposerAutoloaderInit… not found #779

Closed

Description

@Ibochkarev

Описание проблемы

Установка пакета minishop3-1.14.0-beta1 падает на vendor/autoload.php:25: класс Composer autoloader не найден. Менеджер пишет, что пакет с этой подписью установить невозможно.

Шаги воспроизведения

  1. Скачать релиз v1.14.0-beta1, файл minishop3-1.14.0-beta1.transport.zip.
  2. Установить или обновить пакет через менеджер пакетов MODX.
  3. На шаге миграций resolver подключает core/components/minishop3/vendor/autoload.php.
  4. Установка обрывается с Class "ComposerAutoloaderInit541a2bb3a6d325164f8600968e830d0d" not found.

Ожидаемое поведение

Пакет ставится или обновляется. Phinx-миграции в resolver_02_migrations.php проходят на цельном vendor/.

Фактическое поведение

Установка падает. Менеджер: «Невозможно установить пакет с подписью: minishop3-1.14.0-beta1».

Скриншоты

Не приложены.

Окружение

  • MiniShop3: 1.14.0-beta1
  • MODX: уточнить у репортера
  • PHP: уточнить у репортера
  • MySQL: уточнить у репортера
  • Браузер: не применимо (установка из менеджера)

Логи ошибок

Логи из core/cache/logs/error.log
Error в /core/components/minishop3/vendor/autoload.php:25: Class "ComposerAutoloaderInit541a2bb3a6d325164f8600968e830d0d" not found
Невозможно установить пакет с подписью: minishop3-1.14.0-beta1

Дополнительный контекст

Официальный minishop3-1.14.0-beta1.transport.zip внутри согласован. И vendor/autoload.php, и vendor/composer/autoload_real.php объявляют один класс ComposerAutoloaderInit541a2bb3a6d325164f8600968e830d0d. Это тот же хэш, что в ошибке. ZIP как артефакт сборки не рассинхронен. Падение возникает на сайте в момент require из _build/resolvers/resolver_02_migrations.php через core/components/minishop3/phinx_vendor.php → vendor/autoload.php.

Типичный механизм при совпадающем имени класса в новом autoload.php:

  1. Upgrade / неполная замена vendor/. На диске новый autoload.php (хэш 541a2bb3…), а composer/autoload_real.php остался от прошлой установки (другой ComposerAutoloaderInit…) или битый. File vehicle MODX перезаписывает файлы, но не удаляет лишние и не гарантирует атомарную замену всего дерева vendor/.
  2. require_once + уже загруженный старый autoload_real.php в том же PHP-запросе. До копирования или из OPcache: require_once пропускает повторную загрузку файла по пути, класс из нового autoload.php не определён.
  3. OPcache после обновления пакета без сброса кэша опкодов.

Локальный vendor/ в working tree не обязан совпадать с релизом: каталог в .gitignore, сборка пакета берёт то, что было на машине релиза после composer install.

Отдельно, не как причина этого Class not found: в transport попал dev-vendor (в дереве есть пакеты вроде myclabs/deep-copy и PHPUnit-стек). composer.json кладёт phpunit в require-dev, а _build не вызывает composer install --no-dev. Это раздувает пакет и меняет хэш autoload. При цельном vendor/ само по себе Class not found не даёт. Follow-up для релизного процесса: собирать transport с composer install --no-dev.

Обходной путь

  1. Удалить каталог core/components/minishop3/vendor целиком.
  2. Переустановить пакет. Либо в каталоге компонента выполнить composer install --no-dev.
  3. Сбросить OPcache (перезапуск PHP-FPM / Apache) и очистить кэш MODX.

Связанные файлы: _build/resolvers/resolver_02_migrations.php, core/components/minishop3/phinx_vendor.php.

Activity

  1. biz87 commented on Sep 24, 2026

    @biz87
    Member

    Подтверждаю версию №2 из описания — она единственная верная, остальные две отпадают. Воспроизвёл на живом сайте и разобрал механизм построчно.

    Механизм

    core/components/minishop3/bootstrap.php:9 подключает автолоадер компонента на каждом запросе MODX:

    require_once $namespace['path'] . 'vendor/autoload.php';

    Дальше в одном запросе обновления:

    1. MODX стартует, bootstrap.php подключает установленный vendor/autoload.php, тот — require_once composer/autoload_real.php. В памяти оказывается класс предыдущей версии.
    2. File vehicle распаковывает новый vendor/ поверх.
    3. resolver_02_migrations.php → phinx_vendor.php → ms3PhinxResolveClassLoader() делает require vendor/autoload.php. Файл на диске уже новый, читается заново.
    4. Его строка require_once __DIR__ . '/composer/autoload_real.php' — no-op: путь не изменился и уже в списке подключённых, файл не перечитывается.
    5. Строка 25 зовёт ComposerAutoloaderInit541a2bb3…::getLoader() — этот класс никто не объявлял. Fatal.

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

    Версия №1 (частичная замена vendor/) отпадает: у пострадавших на диске оба файла новые. Версия №3 (OPcache) тоже — OPcache кэширует содержимое файла, а не карту require_once; без него ошибка воспроизводится ровно так же.

    Почему сломалось именно на 1.14.0

    Нужны два условия, и оба сошлись в этом релизе.

    Первое — сменилось имя класса. Composer выводит суффикс из состава пакетов, а в composer.json не было autoloader-suffix. Между тегами composer.lock изменился на 275 строк (добавился modxkit/testbench в require-dev) → новый хеш. Раньше состав между релизами не менялся, и имя класса совпадало случайно — шаг 5 тихо возвращал старый загрузчик.

    Второе — плоский require вместо require_once. Это наша регрессия из #726, она есть только в 1.14.0-beta1. До неё резолвер делал:

    if (!class_exists('Phinx\\Config\\Config')) {
        require_once $vendorAutoload;
    }

    require_once на уже подключённом пути — no-op целиком, до строки 25 дело не доходило. В #726 понадобилось возвращаемое значение (ClassLoader), require_once его на втором вызове не отдаёт — отсюда плоский require и открытая ловушка.

    Замечание про dev-vendor в пакете верное и, как выясняется, не такое уж побочное: именно изменение состава require-dev и сдвинуло хеш. Плюс 45 МБ вместо примерно семи.

    Воспроизведение

    Прогнал ту же последовательность на dev-сайте, где лежал старый хеш:

    1. установленный автолоадер в памяти: ComposerAutoloaderInit541a2bb3a6d325164f8600968e830d0d
    2. распакован новый vendor, его класс: ComposerAutoloaderInitMiniShop3
    3. phinx_vendor.php: как в 1.14.0-beta1
       → Error: Class "ComposerAutoloaderInitMiniShop3" not found
         в ...\vendor\autoload.php:25
    

    Тот же файл, та же строка 25.

    Исправление

    Важный момент: один autoloader-suffix текущих пострадавших не спасает. Они обновляются со старого хеша, и новый autoload.php всё равно позовёт класс, которого нет в памяти. Лечит именно защита в резолвере — она приезжает внутри пакета и исполняется уже из новых файлов.

    1. phinx_vendor.php — ms3PhinxEnsureAutoloaderInit() вычитывает имя класса из нового autoload.php и, если оно не объявлено, требует composer/autoload_real.php напрямую, в обход кэша require_once. Новое имя с прежним не столкнётся — это и есть условие, при котором мы сюда попали. Снимает отказ при обновлении с любой прошлой версии.
    2. composer.json — config.autoloader-suffix: "MiniShop3". Имя класса больше не зависит от состава пакетов. Проверено: одинаково с --no-dev и без.
    3. _build/vendor_guard.php + вызов в build.php — сборка отказывается паковать vendor/ с require-dev (читает dev и dev-package-names из installed.json) и печатает, что запустить.

    Тесты: tests/PhinxVendorAutoloadUpgradeTest.php воспроизводит ловушку на поддельном vendor/ и падает, если сценарий перестанет воспроизводиться; tests/BuildVendorGuardTest.php закрывает проверку сборки.

    Выпускаю 1.14.1-beta1. До него обходной путь из описания рабочий.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions