Сайт использует сookies для хранения данных. Продолжая использовать сайт, вы даёте согласие на работу с этими файлами.

ОК
🌐
Клиент-сервер
Опубликовано:
19.10.2026
•
Обновлено:
19.10.2026

Composer в PHP: разница json и lock, версионирование, PSR-4 и воспроизводимая сборка в CI/CD

Данил Мануйлов

Composer управляет зависимостями на уровне конкретного проекта, изолируя сторонние библиотеки в каталоге vendor/. Надежность деплоя и предсказуемость поведения приложения в продакшене зависят от понимания того, как менеджер пакетов разрешает граф зависимостей, фиксирует версии и генерирует карту автозагрузки.


composer.json vs composer.lock: фундаментальная разница

Два ключевых файла в корне проекта выполняют принципиально разные задачи: один декларирует правила, второй фиксирует точное состояние окружения.

Что хранит composer.json

composer.json — это манифест проекта. В нем описываются:

  • Прямые зависимости (require и require-dev) с допустимыми диапазонами версий.
  • Правила автозагрузки классов (PSR-4, PSR-0, classmap, files).
  • Системные требования (версия PHP, расширения).
  • Пользовательские скрипты и конфигурация поведения Composer.

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

Зачем нужен composer.lock

composer.lock — это снимок (snapshot) полностью разрешенного графа зависимостей. Когда Composer вычисляет дерево пакетов, он записывает в lock-файл:

  • Точные версии каждого установленного пакета (включая транзитивные зависимости).
  • Хеши коммитов (commit hash) для пакетов, загруженных из VCS.
  • URL-адреса дистрибутивов и контрольные суммы архивов.
  • Хеш содержимого composer.json (content-hash), гарантирующий, что lock-файл соответствует текущему манифесту.
{
    "_readme": [
        "This file locks the dependencies of your project to a known state"
    ],
    "content-hash": "a1b2c3d4e5f6...",
    "packages": [
        {
            "name": "monolog/monolog",
            "version": "3.5.0",
            "source": {
                "type": "git",
                "url": "https://github.com/Seldaek/monolog.git",
                "reference": "4118123e1b81a51c64a18ae52e79259de870b87b"
            }
        }
    ]
}

Почему composer.lock коммитят в приложениях, но игнорируют в библиотеках

  • В конечных приложениях (проектах): composer.lock обязательно фиксируется в Git. Это единственный способ гарантировать, что все разработчики, CI-раннеры и production-серверы запустят идентичный набор кода вплоть до последнего патча сторонней библиотеки.
  • В библиотеках (reusable packages): composer.lock добавляется в .gitignore. Если библиотека закоммитит свой lock-файл, Composer проигнорирует его при подключении этой библиотеки в родительский проект. Библиотека должна тестироваться на различных комбинациях версий зависимостей в пределах заданных диапазонов.

composer install против composer update: как не сломать прод

Непонимание разницы между этими двумя командами — основная причина внезапных сбоев при релизах.

                  ┌────────────────────────┐
                  │    composer install    │
                  └───────────┬────────────┘
                              │
                    composer.lock существует?
                     ├─── ДА ───► Устанавливает ТОЧНЫЕ версии из lock
                     └─── НЕТ ──► Разрешает зависимости -> Создает lock -> Устанавливает
                              
                  ┌────────────────────────┐
                  │    composer update     │
                  └───────────┬────────────┘
                              │
                    Игнорирует composer.lock
                              │
                    Вычисляет новые версии по composer.json
                              │
                    Перезаписывает composer.lock -> Устанавливает

Логика работы composer install

Команда предназначена для развертывания проекта:

  1. Если composer.lock присутствует, Composer читает только его и скачивает указанные там версии. Диапазоны из composer.json игнорируются.
  2. Если composer.lock отсутствует (например, при инициализации нового проекта), Composer вычисляет граф зависимостей по composer.json, устанавливает пакеты и создает новый composer.lock.

В CI/CD и на production-серверах используется исключительно composer install.

Что делает composer update

Команда предназначена для обновления зависимостей:

  1. Игнорирует существующий composer.lock.
  2. Заново обращается к Packagist/репозиториям и рассчитывает максимально свежие версии пакетов, удовлетворяющие ограничениям из composer.json.
  3. Перезаписывает composer.lock.
  4. Скачивает и устанавливает новые пакеты в vendor/.

Запуск composer update на рабочем сервере приводит к неконтролируемому обновлению сотен транзитивных пакетов прямо во время деплоя.

Безопасное обновление отдельного пакета

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

# Обновить только symfony/yaml
composer update symfony/yaml

# Обновить symfony/yaml и разрешить обновление его прямых зависимостей
composer update symfony/yaml --with-dependencies

# Обновить symfony/yaml и все связанные транзитивные зависимости
composer update symfony/yaml --with-all-dependencies

Ограничения версий (Version Constraints) и SemVer

Composer опирается на принципы семантического версионирования (SemVer: MAJOR.MINOR.PATCH):

  • MAJOR — обратно несовместимые изменения API;
  • MINOR — новая функциональность с сохранением обратной совместимости;
  • PATCH — исправление ошибок с сохранением обратной совместимости.

Синтаксис диапазонов

Ограничение Пример Допустимый диапазон Пояснение
Точная версия 1.2.3 1.2.3 Только указанный релиз.
Wildcard (маска) 1.2.* >=1.2.0 <1.3.0 Любые патчи в ветке 1.2.
Диапазон >=1.2 <2.0 >=1.2.0 <2.0.0 Явное указание границ.
Тильда (~) ~1.2.3<br>~1.2 >=1.2.3 <1.3.0<br>>=1.2.0 <2.0.0 Фиксирует последний указанный сегмент.
Карет (^) ^1.2.3<br>^0.3.2 >=1.2.3 <2.0.0<br>>=0.3.2 <0.4.0 Разрешает обновления, не ломающие обратную совместимость по SemVer.

Особенность карета (^) для версий до 1.0

По спецификации SemVer релизы 0.x.x считаются нестабильными, и любое минорное изменение может содержать ломающие правки. Поэтому:

  • ^0.3.2 эквивалентно >=0.3.2 <0.4.0;
  • ^0.0.4 эквивалентно 0.0.4.

Минимальная стабильность

По умолчанию Composer устанавливает только стабильные релизы (stable). Если проекту требуется альфа-, бета- или dev-версия:

{
    "minimum-stability": "stable",
    "prefer-stable": true,
    "require": {
        "vendor/package": "^2.1@RC"
    }
}

Флаг стабильности @RC или @dev рядом с конкретным пакетом позволяет точечно подключить нестабильный релиз, сохраняя общее требование стабильности для остальных библиотек.


Автозагрузка PSR-4: от composer.json до исполнения

Стандарт PSR-4 сопоставляет пространства имен (namespaces) PHP с физической структурой каталогов файловой системы.

Настройка секций autoload и autoload-dev

{
    "autoload": {
        "psr-4": {
            "App\\": "src/"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "App\\Tests\\": "tests/"
        }
    }
}
  • Префикс пространства имен обязательно оканчивается двойным обратным слешем \\. Это исключает ошибочное сопоставление похожих пространств имен (например, AppHelper\ не совпадет с правилом для App\).
  • Класс App\Services\PaymentService будет искаться по пути src/Services/PaymentService.php.
  • autoload-dev загружается только при обычной установке и полностью исключается при сборке с флагом --no-dev.

Оптимизация автозагрузчика для продакшена

В режиме разработки Composer проверяет файловую систему на наличие файлов при каждом обращении к новому классу. На продакшене это создает избыточные системные вызовы stat().

Для генерации статической карты классов (classmap) выполняется:

composer dump-autoload --optimize --no-dev --classmap-authoritative
  • --optimize (-o): конвертирует правила PSR-4/PSR-0 в плоский массив соответствий класс => файл в файле vendor/composer/autoload_classmap.php.
  • --classmap-authoritative (-a): запрещает автозагрузчику искать файлы на диске, если класс не найден в сгенерированной карте классов. Если класса нет в карте — сразу выбрасывается ошибка, минуя обращение к файловой системе.

Platform Requirements: синхронизация сред разработки и продакшена

Composer учитывает не только PHP-пакеты, но и саму среду исполнения: версию интерпретатора PHP и установленные расширения (ext-*).

Декларация требований среды в require

Фиксация расширений предотвращает установку проекта на сервер, где отсутствуют необходимые модули:

{
    "require": {
        "php": "^8.2",
        "ext-pdo": "*",
        "ext-mbstring": "*",
        "ext-intl": "*"
    }
}

Секция config.platform

Если разработчик локально использует PHP 8.3, а production-сервер работает на PHP 8.2, стандартный composer update может подтянуть версии пакетов, несовместимые с PHP 8.2.

Чтобы привязать расчет зависимостей к целевой среде, используется секция platform:

{
    "config": {
        "platform": {
            "php": "8.2.18",
            "ext-intl": "1.0.0"
        }
    }
}

Composer будет строить дерево зависимостей так, будто проект всегда запускается на PHP 8.2.18, независимо от реальной версии интерпретатора на машине сборки.


Воспроизводимая сборка в CI/CD: практический сценарий

Production-команда установки

В пайплайнах деплоя используется строго определенный набор флагов:

composer install \
    --no-dev \
    --no-interaction \
    --prefer-dist \
    --optimize-autoloader \
    --classmap-authoritative \
    --no-progress \
    --no-scripts

Разбор флагов:

  • --no-dev: исключает пакеты из секции require-dev (тестовые фреймворки, статические анализаторы).
  • --no-interaction: отключает любые интерактивные вопросы CLI.
  • --prefer-dist: скачивает архивы (zip), а не клонирует git-репозитории, что ускоряет сборку.
  • --optimize-autoloader и --classmap-authoritative: генерируют максимально производительную карту классов.
  • --no-progress: отключает анимацию прогресс-бара, снижая объем логов в CI.
  • --no-scripts: предотвращает автоматический запуск сторонних хуков во время сборки контейнера (скрипты миграций или прогрева кэша запускаются отдельными шагами).

Проверка консистентности файлов

Перед установкой зависимостей в CI рекомендуется валидировать файлы проекта:

# Проверка синтаксиса и синхронизации composer.json и composer.lock
composer validate --strict --no-check-all

Если разработчик изменил composer.json, но забыл обновить composer.lock, флаг --strict вернет ненулевой код возврата и остановит пайплайн.

Пример конфигурации GitHub Actions с кэшированием

name: Build and Test

on: [push, pull_request]

jobs:
  composer:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Setup PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: '8.2'
          extensions: mbstring, pdo, intl
          coverage: none

      - name: Get Composer Cache Directory
        id: composer-cache
        run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT

      - name: Cache dependencies
        uses: actions/cache@v4
        with:
          path: ${{ steps.composer-cache.outputs.dir }}
          key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
          restore-keys: ${{ runner.os }}-composer-

      - name: Validate composer files
        run: composer validate --strict

      - name: Install dependencies
        run: |
          composer install --no-interaction --prefer-dist --optimize-autoloader

Чек-лист: диагностика и устранение конфликтов зависимостей

Когда composer update или добавление нового пакета завершается ошибкой Your requirements could not be resolved to an installable set of packages, используйте следующий алгоритм:

  1. Определите инициатора конфликта. Выполните проверку причин установки конкретной версии:
    # Почему установлен данный пакет
    composer why symfony/console
    
    # Почему нельзя установить желаемую версию
    composer why-not symfony/console 6.4.0
    
  2. Проверьте ограничения платформы. Убедитесь, что требуемый пакет не конфликтует с локальной версией PHP или расширениями:
    composer check-platform-reqs
    
  3. Проанализируйте транзитивные зависимости. Если пакет A требует C: ^1.0, а пакет B требует C: ^2.0, они не могут сосуществовать. Проверьте репозитории библиотек на предмет наличия обновлений, поддерживающих более новые версии общего пакета C.
  4. Устраните устаревшие блокировки. Не удаляйте composer.lock целиком. Попробуйте обновить конфликтующие ветки точечно:
    composer update vendor/package-a vendor/package-b --with-all-dependencies
    

Частые вопросы (FAQ)

Что делать, если composer.lock вызывает конфликт при git merge?

Не разрешайте конфликт в composer.lock вручную в текстовом редакторе. Правильный порядок действий:

  1. Разрешите конфликт в composer.json вручную, выбрав нужные диапазоны зависимостей.
  2. Зафиксируйте composer.json.
  3. Запустите composer update --lock — эта команда пересчитает composer.lock на основе разрешенного composer.json, обновляя только метаданные без неконтролируемого обновления остальных пакетов.
  4. Добавьте обновленный composer.lock в коммит.

Почему нельзя запускать composer update на продакшене?

composer update рассчитывает дерево зависимостей заново. Если один из авторов ваших транзитивных зависимостей выпустил минорный релиз с ошибкой, composer update скачает его прямо на сервер. Кроме того, процесс разрешения зависимостей требует значительного объема оперативной памяти и процессорного времени, что может вызвать сбой рабочего сервера из-за Out Of Memory (OOM).

В чём разница между autoload и autoload-dev?

Секция autoload содержит правила загрузки для боевого кода приложения (src/). Секция autoload-dev предназначена для тестового окружения, фикстур и инструментов разработки (tests/). При запуске composer install --no-dev правила из autoload-dev не попадают в итоговую карту классов в vendor/autoload.php.

Зачем нужен флаг --optimize-autoloader (-o)?

Он создает единый массив соответствий всех классов их физическим путям (autoload_classmap.php). Интерпретатору не приходится проверять наличие файла на диске через правила PSR-4 — файл подключается по готовому пути из памяти, что снижает время отклика приложения.

Как обойти несовпадение локальной версии PHP без поломки сборки?

Используйте секцию config.platform.php в composer.json. Не используйте флаг --ignore-platform-reqs при повседневной разработке, так как он полностью отключает проверки и позволяет установить пакеты, код которых физически не сможет выполниться на целевом интерпретаторе.


Заключение

Стабильность инфраструктуры на базе PHP строится на разделении манифеста composer.json и фиксатора состояния composer.lock. Использование предсказуемых ограничений версий, настройка config.platform под целевой сервер и вызов composer install с оптимизацией автозагрузки в CI/CD обеспечивают детерминированную сборку, исключая расхождения между средами разработки и продакшена.

Источники

Это авторская статья, основанная на личном опыте и субъективном взгляде автора. Заметили ошибку или битую ссылку? Сообщите нам: info@codesrc.ru - мы оперативно исправим. Спасибо, что помогаете делать блог лучше.
Следите за нами в соцсетях:

Читайте также