Статья также доступна на украинском (перейти к просмотру).

Оглавление
- Что такое Composer
- Отдельное использование рабочих зависимостей и зависимостей разработки
- Автозагрузка
- Ограничение версии
- Корневые зависимости
- Воздействие среды
- Работа с системой контроля версий
- Внесение изменений в зависимости
- Вывод
Управление зависимостями в PHP стало гораздо более простым с появлением Composer. Давайте рассмотрим использование некоторых наиболее совершенных методов, применяемых указанной технологией.
Что такое Composer
Это менеджер зависимости для PHP, подобный NPM или Yarn для Javascript или pip для Python. Это упрощает управление пакетами, от которых зависит приложение. В качестве дополнительной опции Composer дает возможность для пакетов регистрировать автозагрузчик. И это сочетание делает Composer одним из важнейших утилит PHP-среды.
Чаще всего в качестве репозитория пакетов для Composer используется сервис Packagist. Он принимает только пакеты с открытым исходным кодом. Для использования всех функций Composer можно использовать Private Packagist. Это платная версия сервиса Packagist, созданная разработчиками Composer, позволяющая использовать пакеты с закрытым исходным кодом, включающей детальный контроль доступа. Недавно мы рассказывали о том, как наладить работу с частными репозиториями с помощью Gitlab. Подробнее о работе с частными репозиториями по ссылке.
Здесь не будет разъяснений относительно установки и настройки Composer. Разработчики сервиса подробно объясняют это в разделе «Начало работы» веб-сайта Composer. Для всех примеров, где будет использоваться Composer CLI, будем считать, что Composer установлен глобально и вызывается с помощью команды composer.
Отдельное использование рабочих зависимостей и зависимостей разработки
Composer различает два типа зависимостей – рабочие и разработки. Рабочие зависимости – это зависимости, которые всегда требуются коду, независимо от среды, в которой он выполняется. Зависимость разработки – это зависимости, которые необходимы только при разработке кода, например, в среде тестирования PHP Unit, статических анализаторах Psalm и анализаторах стиля PHP Code Sniffer. У них нет необходимости при запуске кода в рабочей среде. При добавлении зависимости при использовании команды composer require [package] она будет замечена как рабочая зависимость. При использовании приставки -dev пакет будет замечен как «зависимость для разработки».
При запуске команды composer install загружаются и инсталлируются для приложения как рабочие зависимости, так и разработки. Чтобы загрузить и установить только рабочие зависимости, необходимо добавить префикс --no-dev. Обычно это то, что нужно в конвейере развертывания, а зависимости разработки здесь не нужны.
Команда composer install загружает и устанавливает только зависимости разработки приложения или пакета, но это не касается зависимостей разработки второго уровня. Поэтому, если существует зависимость проекта от пакета А, а пакет А зависит от разработки от пакета В, то только пакет А будет загружен и установлен в проекте.
Зависимости разработки могут накапливаться в списке зависимостей и, соответственно, в большом количестве файлов. Все эти файлы автоматически загружаются автозагрузчиком. Это может сэкономить место, время передачи (во время развертывания) и время выполнения, чтобы минимизировать количество рабочих зависимостей в приложении. Экономия места и времени передачи могут быть незначительны при небольших объемах, но как только приложение увеличится в размерах или сопровождении, это может привести к более значимым цифрам. Еще одним преимуществом маркировки зависимостей разработки является то, что они не будут включены в конструкции Composer автозагрузчика при запуске с помощью команды composer install --no-dev.
Автозагрузка
Как уже отмечалось, дополнительная функция Composer заключается в том, что предлагает утилиты для подключения пакетов к автозагрузке PHP. С помощью этой функции пакеты могут указывать, каким образом Composer может сопоставлять имя класса с именем файла. Это освобождает разработчиков от загрязнения кода вызовами типа require_once и require. Все, что вам нужно сделать, это включить vendor/autoload.php.
Как и в случае с зависимостями, Composer предлагает два типа автозагрузчиков – рабочие директивы автозагрузки и директивы автозагрузки разработки. Пакеты могут использовать это, чтобы уменьшить свой размер и размер автозагрузчика, однако приложения также могут выиграть от этого. Если предположить, что проект хорошо протестирован, количество файлов, используемых только для тестирования, может составлять значительную часть кодовой базы, подобно любым утилитам разработки, таким как инструменты командной строки. Во время работы они не нужны. Отметив автозагрузчик для этих файлов в качестве автозагрузчика разработки, они не будут учитываться при запуске команды composer install --no-dev, что может повысить производительность приложения. Различие между рабочей автозагрузкой и автозагрузкой разработки указывается в файле composer.json:

Composer предлагает некоторые дополнительные инструменты для повышения производительности функций и классов автозагрузки посредством оптимизации автозагрузчика.
Ограничение версии
До появления Composer разработчик приложений загружал определенную версию зависимости и помещал ее в систему управления версиями. Composer позволяет отойти от конкретной версии зависимости и берет на себя задачу решить, какую версию зависимости установить. Чтобы определить это, Composer в значительной степени опирается на концепцию семантического управления версиями. Он не только лишь делает это для приложения, да и расширяет эту функциональность для зависимостей. Это позволяет пакетам иметь свои собственные зависимости, которые в свою очередь тоже могут иметь свои зависимости и так далее. Это приводит к уменьшению размера пакетов, которые могут сосредоточиться на одной функции и тем самым обеспечить ее централизацию.
В сравнении с NPM, Composer использует только одну версию пакета для каждого приложения. Это означает, что необходимо установить меньше пакетов, хотя это создает дополнительный риск конфликта версий. Это причина того, чтобы не использовать конкретную версию зависимости, а делать запрос на диапазон версий. Обычно это делается с помощью оператора каретки типа ^1.0. Это означает, что любая версия принимается в диапазоне >= 1.0.0 и < 2.0.0. Учитывая семантическое управление версиями, это означает, что будут гарантированы необходимые функции и не вносятся изменения, нарушающие обратную совместимость.
Указав диапазон вместо конкретной версии, можно предупредить конфликты версий в случае, если другая зависимость использует тот же пакет. Например, рассмотрим распространенную ситуацию.
Содержимое файла composer.json приложения выглядит следующим образом:

Файл composer.json для vendor/package:

При установке зависимостей Composer обнаружит, что имеется несколько запросов для other_vendor/package, но поскольку они имеют ограничение диапазона, он может решить, что ему не стоит устанавливать версию 1.1.5, а вместо этого лучше установить версию 1.2.8.
Composer имеет несколько операторов ограничений и даже позволяет их комбинировать. Это означает, что часто используемое ограничение типа "php": "^7.2" не будет включать PHP 8. Чтобы указать, что поддерживаются как PHP 7.2 и выше, так и PHP 8, можно объединить два оператора: "php ": "^7.2 | | ^8,0".
Другим вариантом было бы использовать подход Symfony и выбрать оператор "php": >= 7.2". Но мы не рекомендуем применять этот подход, поскольку он предусматривает, что код также будет работать с PHP 9 и выше. Но неизвестно, какие нарушающие изменения обратную совместимость будут внесены в PHP 9. И потому, на наш взгляд, лучше использовать оператор типа "php": "^7.2|| ^8.0".
Корневые зависимости
Глядя на содержимое файла composer.json, можно обнаружить различия между зависимостями, требуемыми непосредственно из приложения («корневой пакет» в терминах Composer) и зависимостями, которые необходимы косвенно, как зависимость от корневой зависимости.
Например, возьмем базовый файл composer.json:

Запуск команды composer install для указанного файла покажет, что загружен будет не только пакет laminas/laminas-diactoros, но и laminas/laminas-zendframework-bridge, psr/http-factory и psr-http-message. В этом примере laminas/laminas-diactoros является корневым требованием.
Разница между корневым и производным требованием заключается в количестве прямых зависимостей в проекте. В целом, меньше корневых зависимостей означает меньшее количество пакетов и соответственно меньше возникающих конфликтов при их обновлении. Меньше пакетов, так как новая версия пакета может иметь другие зависимости, которые будут автоматически поддерживаться Composer. Меньше конфликтов, потому что дерево зависимостей имеет меньше ограничений.
Но что делает пакет таким, чтобы он отвечал корневому требованию? Эмпирическое правило заключается в том, что когда класс или интерфейс пакета используется в доменном коде, это должно быть корневым требованием. Теперь снова посмотрим на наш пример:

Глядя на пример, где мы полагаемся только на laminas/laminas-diactoros, Diactoros предлагает реализацию интерфейсов, указанных в PSR-7 и PSR-15. Если в коде мы ссылаемся только на объекты с laminas/laminas-diactoros, это будет допустима конфигурация Composer. Но если мы будем ссылаться на интерфейсы, определенные в PSR-7 и PSR-15, то это было бы причиной сделать psr-http-message и psr/http-factory корневыми требованиями.
Для примера:

Это эмпирическое правило означает, что пакет, выполняющий действия на основе объектов PSR-7, должен только отметить psr-http-message как зависимость, что делает его независимым от фактической реализации интерфейса. В этом, в сущности, и состоит идея PSR.
Воздействие среды
Приложения или пакеты создаются для определенной версии PHP. Это гарантирует правильное функционирование кода только для этой версии. Возможно также установка расширения PHP. Поэтому рекомендуется указывать, какие версии PHP необходимы или поддерживаются, а также какие расширения.
Когда пакет разрабатывается, версии и расширения PHP можно использовать так, как будто это обычные пакеты:

Поскольку расширение привязано к PHP, указание версии не требуется, и поэтому мы можем для cURL расширения использовать знак «*».
Этот подход также можно использовать при создании приложения и даже указать эти ограничения в качестве конфигурации платформы:

Преимущество здесь в том, что Composer будет использовать версию PHP 7.4.4 в качестве версии PHP, даже если в системе, где выполняются команды Composer, установлена ??другая версия. С точки зрения использования решений виртуализации, таких как Docker и Vagrant, это позволит пользователям запускать команды Composer локально и запускать приложение в виртуализированной среде. Это также исключает возможность воздействия на рабочую среду для установки одной из пользователей версий зависимостей, не поддерживаемых рабочей средой.
Работа с системой контроля версий
Обычно в проекте Composer будет иметь три объекта файловой системы – два файла с именем composer.json и composer.lock, а также каталог с именем vendor*. Однако не все из них должны быть включены в систему управления версиями.
В файле composer.json находятся все пакеты, требуемые с ограничениями версий – дополнительную информацию см. в разделе «Ограничение версий». Этот файл всегда должен быть добавлен в систему контроля версий. Без него пользователь, желающий использовать код, даже не представляет, какие пакеты требуются.
В файле composer.lock содержатся точные версии всех пакетов, включая контрольные суммы. Если этот файл присутствует при запуске команды composer install, он установит те же версии, которые указаны в composer.lock. Благодаря этой функции гарантируется, что зависимости во всех средах одинаковы. При создании приложения мы обязательно добавим этот файл в систему контроля версий, однако при создании библиотеки мы этого не сделаем. Это связано с тем, что приложения (или другие пакеты), использующие пакет, не будут учитывать этот файл, а локальная среда разработки учитывается. Это может привести к разработке или тестированию устаревших версий зависимостей, в то время как потребители пакета действительно используют последние версии.
В каталоге vendor содержится код пакетов. Содержимое этого каталога не следует добавлять в систему управления версиями. Этот каталог заполняется при запуске процесса установки сервиса Composer.
Можно изменить имя и расположение каталога с помощью параметра конфигурации vendor-dir, однако для этой статьи используется значение по умолчанию.
Внесение изменений в зависимости
Предположим, что мы хотим обновить зависимости нашего приложения. В этом случае мы запустим несколько команд обновления сервиса Composer и увидим изменения в файле composer.lock. Мы создадим коммит. При последующем обновлении появится еще один коммит. Потом, через несколько дней, мы снова продолжим с того места, на котором остановились. При этом мы вынуждены будем работать с перегруженным репозиторием, и поэтому, прежде чем продолжить, необходимо выполнить перебазирование, чтобы убедиться, что внесенные изменения совместимы с последней версией кода. Есть конфликт слияния! Конфликт слияния можно разрешить у composer.json, но у composer.lock это сделать сложнее. Способ разрешения конфликтов слияния в файле composer.lock состоит в том, чтобы принять входящие изменения – это последние изменения – и запустить команды Composer, чтобы снова внести изменения в первую очередь. Это гарантирует, что внесенные изменения будут применены к последней версии кодовой базы. Затем нужно зафиксировать новый composer.lock и продолжить перебазирование. Если выполнить еще какую-либо работу с Composer в следующем коммите, необходимо будет повторить эти шаги еще раз. Для того чтобы этого избежать, необходимо:
- Вносить изменения в зависимости в одном коммите;
- Использовать для запуска список команд Composer;
- Дважды использовать эти команды, если у composer.lock возникает конфликт слияния.
Вывод
Composer стал жизненно важной частью разработки PHP. Как и в случае с любым менеджером пакетов, здесь могут потребоваться дополнительные пояснения. Ниже приведем некоторые рекомендации по его использованию:
- Выделите зависимости рабочей среды и зависимости разработки, чтобы уменьшить размер приложения;
- Разделить автозагрузчик на рабочий и разработки, это уменьшит его размер и повысит производительность;
- Не следует полагаться на фиксированные версии зависимостей, а только на их диапазон, это сделает обновление пакетов менее болезненным;
- Использовать только прямые зависимости, которые фактически вызываются внутри приложения, это упростит обновление или переход на альтернативное решение;
- Всегда помещать composer.json в систему управления версиями, но composer.lock в систему управления версиями для приложений, чтобы пользователи пакета не смогли обновлять другие пакеты;
- Надо пытаться иметь максимум один фиксированный запрос или запрос на слияние, вносящий изменения в composer.json и composer.lock, это сведет к минимуму конфликты слияний в этих файлах.
Виртуальный хостинг FREEhost.UA предоставляет возможность работы с Composer с помощью SSH-доступа. Это крайне необходимо при современной разработке сайтов. Приглашаем Вас бесплатно протестировать работу нашего хостинга.
Подписывайтесь на наш телеграмм-канал https://t.me/freehostua, чтобы быть в курсе новых полезных материалов.
Смотрите наш канал Youtube на https://www.youtube.com/freehostua.
Мы в чем ошиблись, или что-то пропустили?
Напишите Об этом в комментариях, мы с удовольствием ответим и обсуждаем Ваши замечания и предложения.
|
Дата: 13.12.2022 Автор: Евгений
|
|

Авторам статьи важно Ваше мнение. Будем рады его обсудить с Вами:
comments powered by Disqus