Одна из достаточно распространенных проблем с которой можно столкнуться при работе с Сomposer — путаница с определением уровня стабильности stability зависимостей.

Типичная ситуация, когда приложение зависит от пакета A:dev-master, который в свою очередь зависит от пакета B:dev-master, приводит к тому, что Composer ругается, что не может найти подходящую версию пакета B.

Попробуем разобраться почему так происходит и как это исправить.

Корневой пакет

Есть такое понятие как root-only параметры в файле composer.json. Это параметры, которые учитываются только для корневого пакета. В файлах composer.json ваших зависимостей эти параметры игнорируются.

Корневой пакет это директория в которой находится файл composer.json и в которой вы выполняете composer install. Чаще всего это корневая директория вашего приложения.

Но корневой пакет это еще и контекст. Например, у вас в зависимостях указан пакет A. Пока вы находитесь в корневой директории вашего приложения, ваш пакет тоже является корневым. Но, когда вы перейдете cd в директорию пакета A, то в этом контексте корневым станет уже пакет A.

Так вот, уровень стабильности задается именно корневым пакетом и только им.

minimum-stability

Для указания желаемого уровня стабильности для всех зависимостей используется параметр minimum-stability. Это root-only параметр и работает он как ограничитель снизу.

Определение уровня стабильности

Вернемся к ситуации описанной в начале статьи: приложение зависит от пакета A:dev-master, который зависит от пакета B:dev-master.

Корневой composer.json выглядит следующим образом:

{
    "require": {
        "A": "dev-master"
    }
}

При запуске composer install Composer выполнит следующие шаги:

  1. Определит minimum-stability. Так как параметр не задан явно, то будет подставлено значение по умолчанию — stable.

  2. Зависимость A требуется в версии dev-master. Благодаря префиксу dev- Сomposer понимает, что уровень стабильности этого пакета dev. Так как зависимость описана в корневом пакете, пакет A неявно получает флаг @dev.

  3. Теперь когда пакет A имеет версию dev-master@dev, composer начинает его установку. Но пакет A зависит от пакета B, у которого требуемая версия тоже dev-master. Но так как зависимость описана не в корневом пакете, то пакет B флаг @dev не получает. Вместо этого B наследует значение minimum-stability, которое в нашем случае равно stable.

    Таким образом пакет B требуется в версии dev-master@stable.

    Здесь все и ломается, так как версии dev-master@stable в принципе существовать не может. Поэтому Composer и говорит, что не может найти пакет B с требуемым уровнем стабильности.

Флаги стабильности

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

Флаг является частью версии и учитывается только для корневого пакета. Флаги определенные в зависимых пакетах игнорируются, вместо них подставляется значение minimum-stability.

Чтобы установить нужный флаг на версии зависимостей второго порядка, можно просто добавить эти зависимости в корневой composer.json с указанием флага, но без указания версии.

В нашем случае, чтобы пропустить dev-версию пакета B добавляем соответствующий флаг:

{
    "require": {
        "A": "dev-master",
        "B": "@dev"
    }
}

Определение версии пакета B в этом случае мы делегируем пакету A, который непосредственно зависит от B.

prefer-stable

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

Гораздо проще задать minimum-stability как dev и не мучится, но и тут есть подводный камень. Пакеты, которые доступны в нескольких вариациях (stable, beta, dev) будут устанавливаться в dev-версии, а они могут не всегда надежно работать.

И вот тут как нельзя кстати пригодится параметр prefer-stable. Если указать prefer-stable как true, то Сomposer будет устанавливать самую стабильную версию пакета и опускать приемлемый уровень стабильности ниже в случае отсутствия таковой.

Вывод

Надеюсь, что вы теперь лучше ориентируетесь в том как Сomposer определяет уровни стабильности и как добиться от него желаемого результата.

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

Рассказать друзьям: