Skip to content

Instantly share code, notes, and snippets.

@1234ru
Last active February 28, 2024 15:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save 1234ru/7416702dad95891222259191c03c99ac to your computer and use it in GitHub Desktop.
Save 1234ru/7416702dad95891222259191c03c99ac to your computer and use it in GitHub Desktop.
Composer
# This script should be run from one of .git/hooks files.
# Current directory will be .git
# git commands don't need directory be changed to
# project root.
# composer command does need.
export composer_exec="php ~/composer"
export command="git diff --name-only $oldrev $newrev -- composer.lock"
read oldrev newrev refname
export was_changed=$($command)
if [ $was_changed ]
then
echo "Running composer install..."
cd .. # composer should be launched in the project root
$composer_exec
fi

Введение

https://getcomposer.org/

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

Даже если такой сторонний пакет всего один, использование Composer может быть удобным и полезным.

Пакеты Composer размещает в специальном каталоге (vendor). Добавлять пакеты в своё хранилище системы контроля версий не надо. Туда нужно занести только файл конфигурации composer.json, где и перечислены нужные пакеты, а при изменении их состава или обновлении версий запускать специальную команду.

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

Еще немного о Composer

Composer официально называют менеджером зависимостей для PHP, подобному npm для Node.js, подчеркивая отличие от менеджеров пакетов (таких как Apt или Yum в Linux). Отличие состоит в практике использования: установке пакетов внутри отдельных проектов, а не глобально на уровне операционной системы, хотя Composer поддерживает оба варианта.

Рабочий пакет самого Composer состоит из одного файла - composer.phar.

Все операции, проводимые Composer, требуют запуска этого файла (а точнее - передачи его интерпретатору PHP).

"Установка" Composer заключается в размещении этого файла тем или иным образом и бывает локальной (в рамках каталога отдельного проекта) - и глобальной (для всех проектов, на уровне ОС).

Установка: как начать пользоваться

Глобальная ручная установка Composer на Windows

Глобальную установку нужно провести один раз, и после этого можно будет использовать Composer во всех проектах без необходимости в каждый из них его устанавливать.

Установка вручную позволяет создать переносимый рабочий пакет, который не придется заново устанавливать при переносе на другие системы. Там нужно будет только прописать нужный каталог в системную переменную Path.

  1. Скачиваем файл composer.phar по адресу https://getcomposer.org/download/latest-stable/composer.phar. (Ссылка взята со страницы https://getcomposer.org/download/, раздел Manual Download, Latest Stable.)

  2. Создаем каталог, куда переносим файл composer.phar. Например, D:/portable/composer.

  3. Добавляем этот каталог в системную переменную Path. Win + R --> Дополнительные параметры системы --> Переменные среды --> Системные переменные --> Path --> Изменить --> Создать --> ... --> OK --> OK

  4. В этом же каталоге создаем файл composer.bat следующего содержания:

@php "%~dp0composer.phar" %*

(Например, так: echo @php "%~dp0composer.phar" %* > composer.bat.)

  1. Открываем командную строку (Win + R, cmd) и даем команду:
composer

Должна появиться распечатка опций composer.

Это запускается composer.bat.

Команду нужно дать какого-нибудь другого каталога, чтобы проверить изменения в Path.

Глобальная установка под Linux

Глобальная установка сводится к двум простым действиям:

  1. Скачать на сервер файл https://getcomposer.org/download/latest-stable/composer.phar.

Это можно сделать с помощью wget или другой аналогичной команды:

wget https://getcomposer.org/download/latest-stable/composer.phar

Если никакие утилиты для копирования на сервере не установлены, можно сделать это прямо с помощью php:

php -r "copy('https://getcomposer.org/download/latest-stable/composer.phar', 'composer.phar');"

Либо воспользоваться установщиком (раздел Command-line installation), который делает то же самое и проверяет контрольную сумму.

Важно также дать всем права на исполнение:

chmod a+x composer
  1. Прописать расположение composer.phar в переменной $PATH, чтобы не приходилось каждый раз указывать полный путь. Как вариант - поместить файл в каталог, который уже есть в $PATH, например, /usr/local/bin.

Если прав на запись нет, можно сделать alias для личного пользования:

alias composer="php ~/composer"

Заодно убрать из имени файла расширение для более короткой записи (composer вместо composer.phar).

Скорее всего, команду нужно будет дать с помощью sudo:

sudo mv composer.phar /usr/bin/composer

Т.к. composer.phar содержит в начале фрагмент #!, в Linux его можно запускать из командной строки как исполняемый файл - писать просто composer ... вместо php composer ....

Можно, например, проверить успешность установки:

cd ~ 
composer -V
Composer version 2.5.5 2023-03-21 11:50:05

Кроме того, нужно назначить файлу разрешение на запуск (сразу после копирования его, как правило, нет из-за настроек umask):

chmod u+x,g+x,o+x composer

Короткая форма команд для установки

Установку можно провести всего двумя командами. В обеих фигурирует одинаковое имя файла, поэтому его предварительно удобно записать в переменную.

export destination=/usr/bin/composer
sudo wget -O $destination https://getcomposer.org/download/latest-stable/composer.phar
sudo chmod u+x,g+x,o+x $destination

composer.json, update, Packagist.org

Любой проект под управлением Composer должен иметь в своем корневом каталоге файл composer.json, в котором перечислены используемые пакеты и различные настройки. Выглядит он примерно так:

{
    "require": {
        "monolog/monolog": "^2.9",
        "phpoffice/phpspreadsheet": "^1.27"
    }
}

Чтобы установить пакеты (загрузить их файлы и поместить в нужные каталоги), нужно в командной строке из корневого каталога проекта дать команду:

# Если Composer установлен глобально:
composer update

# Если Composer установлен на уровне проекта 
php composer.phar update

(Примечание: при работе с git-bash под Windows нужно явно указывать расширение исполняемого файла - composer.bat update)

Подробнее см. на https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies.

Пути к пакетам указываются для Packagist.org - публичного хранилища, предназначенного для использования вместе с Composer. Любой желающий может не только использовать тамошние пакеты, но и публиковать свои собственные.

При желании, можно указать Composer забирать код из любых других хранилищ, в т.ч. собственноручно созданных.

Состоит путь из двух частей - поставщика (vendor) и собственно названия (package) и имеет вид поставщик/пакет. Часто названия постащика и пакета совпадают (например, monolog/monolog и др.).

При загрузке Composer создает в корне проекта каталог vendor и подкаталоги пакетов вида поставщик/пакет.

Устанавливать и обновлять пакеты можно также выборочно:

composer update monolog/monolog

composer.lock, install

Сразу после загрузки пакетов Composer создаст файл composer.lock. Там перечислены точные версии всех пакетов. Этот файл нужно добавить под контроль версий проекта, т.к. именно с его помощью Composer обеспечивает привязку к конкретным версиям пакетов на других копиях кода проекта (отсюда и "lock").

Этот файл использует команда composer install, которая, собственно, и загружает указанные версии пакетов. При запуске update эта команда вызывается неявно. Если файл composer.lock в проекте уже есть, для загрузки пакетов нужно вызывать её, а update - только для обновления пакетов до последних версий.

composer install, в частности, может создать и заполнить каталог vendor, если он еще не существует.

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

composer require

Можно одной командой добаить пакет в composer.json и загрузить его самую последнюю версию:

composer require phpoffice/phpspreadsheet

Также эта команда создает файл composer.json, если это не было сделано ранее.

Можно указывать сразу несколько пакетов:

composer require monolog/monolog phpoffice/phpspreadsheet

Если дать команду, указав вместо имени пакета звездочку - composer require phpoffice/*, она запросит и выдаст список всех пактов поставщика.

composer remove

composer remove phpoffice/phpspreadsheet

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

Можно давать её с маской:

composer remove monolog/*

Указание требованиЙ к версии PHP, установленных расширений и библиотек.

В секции require файла composer.json можно перечислять и такие требования. Например:

{
   "require": {
      "php": ">=7.4",
      "ext-gd": "*",
      "lib-curl": "*",
      "another-vendor/package": "1.*"
   }
}

Критика Composer

PHP Composer is an anti-pattern.

Пример избыточности зависимостей

Пакет phpoffice/phpspreadsheet среди требований имеет "ext-mbstring": "*".

При этом среди зависимостей этот пакет имеет maennchen/zipstream-php, который в свою очередь требует symfony/polyfill-mbstring.

Получается, что последний присутствует в проекте без надобности.

Так получается, потому что в composer.json нет возможности указания зависимостей в случае отсутствия библиотек/расширений в системе.

Как дорабатывать подключенные пакеты

Composer может установить пакет в виде репозитория git.

Для этого команды install и require нужно запускать с ключом --prefer-install=source (значение по умолчанию - dist, см. install).

Для дальнейшей работы с репозиторием пакета нужно дать в ее каталоге команду git checkout master, т.к. Composer при загрузке отсоединяет ветку.

Если пакет уже был установлен в обычном виде, а нужно получить его репозиторий, нужно использовать команду reinstall с вышеукзанным ключом. Она удалит пакет и установит его заново. ВНИМАНИЕ! При этом нужно закрыть все программы, в которых открыт каталога пакета внутри vendor; в противном случае Composer не сможет его пересоздать, т.к. он занят другой программой, и операция завершится ошибкой, исправить которую можно будет только откатом файлов (git checkout .) и последующим повторным запуском composer install.

Размещение пакетов вне packagist.org

Можно дать Composer инструкцию искать пакеты и в других местах. Это может потребоваться, если они пока еще не готовы к тому, чтобы представить их широкой публике.

Пакеты при этом могут храниться в репозиториях под котролем git, в т.ч. на github, или просто на каком-то сервере с досутпом по SSH (в таком случае нужно предварительно наладить и проверить соединение).

Единственное к ним требование - наличие в корневом каталоге собственного файла composer.json с полем name в виде vendor/package:

{
   "name": "someone/some-package"
}

Этот должен находиться под контролем версий, т.е. его нужно добавить под управление git и сделать коммит. Только после этого он станет действовать.

Далее такой пакет нужно указать как один из элементов поля repositories исходного composer.json:

"repositories": [
   {
      "type": "git",
      "url": "ssh://username@host:port/path-to-repo"
   }
]

Можно указывать пути к репозиториям на локальной машине, например

"url": "D:\\somedir\\somepkg"

После этого можно давать команду на установку.

Если проекту не назначаются версии в виде меток git (tags), обычная команда выдаст ошибку:

Could not find a version of package one234ru/somepkg matching your minimum-stability (stable). Require it with an explicit version constraint allowing its desired stability.

Команде нужно явно указать ветку в виде названия с префиксом dev-. Например, для ветки master команда будет выглядить так:

composer require someone/some-package:dev-master

В этом случае пакет всегда устанавливается с исходным кодом - в режиме --prefer-install=source. Явно указанное значение dist никакого эффекта не даёт.

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

(Как вариант, можно организовать свой packagist-заглушку прям в каталоге prod-площадки, указав packages.json смотреть прямо в vendor там.)

ВНИМАНИЕ! При изменении путей в repositories нужно дать команду composer update для обновления файла composer.lock. Если этого не сделать, отправка изменений в вышестоящий репозиторий и запуск там хуков git будет обращаться к старому источнику пакетов.

Кроме того, нужно не забывать делать composer update при каждом случае доработки пакета и только потом делать commit и push. (Например, если работа проходила в локальном каталоге vendor и затем отправляется в вышестоящий репозиторий пакета.) В противном случае ссылки на версию git в composer.lock не обновятся, и на всех других копиях будет использована старая версия, без обновлений. Буквально:

  • вносим изменения в пакет, git commit
  • git push
  • возвращаемся в корневой каталог проекта
  • делаем composer update; изменения будут получены из вышестоящего хранилища пакета, обновится composer.lock; делаем commit уже самого проекта, а не библиотеки;
    если не хочется гонять содержимое библиотеки туда-обратно, можно просто обновить поля reference и time у соотв. пакета в composer.lock (поле packages)

Также надо отметить, что Composer сразу прописывает подходящий URL для push. Например:

composer        https://github.com/vendor/package (fetch)
composer        https://github.com/vendor/package (push)
origin  https://github.com/vendor/package (fetch)
origin  git@github.com:vendor/package.git (push)

autoload

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

Можно добавить код в В автозагрузку можно , а также вручную указанных пользователем инструкций.

Выглядит эти инструкции примерно так:

"autoload": {
  "psr-4": {
    "SomeSpace\\": "."
  }
}

Такая конфигурация подходит для случая, когда в корневом каталоге пакета находится файл SomeClass.php следующего содержания:

<?php
namespace SomeSpace;
class SomeClass {}

Если бы файл находился, к примеру, в подкаталоге src (как это бывает во многих общедоступных пакетах), то конфигурация автозагрузки выглядела бы так:

"SomeSpace\\": "src/"

Включение автозагрузки

Чтобы автозагрузчик подействовал, нужно где-то его подключить:

require __DIR__ . '/vendor/autoload.php';

Файл vendor/autoload.php возвращает объект автозагрузчика. С его помощью можно дополнять правила автозагрузки. Подробнее см. тут.

Дополнение автозагрузки в composer.json

В composer.json можно сделать раздел autoload.

Если, например, весь код располагать в пространстве имён _Local и складывать его в папку lib в корневом каталоге проекта, инструкции будут выглядеть так:

{
  "autoload": {
    "psr-4": {
      "_Local\\": "lib/"
    }
  }
}

Указание искать все пространства имён в каталоге обозначается так:

{
    "autoload": {
        "psr-4": { "": "src/" }
    }
}

После внесения изменений нужно выполнить команду composer dumpautoload.

Также можно проинструктировать composer искать классы в нескольких местах. Причем эта инструкция будет выполняться вместе со стандартной автозагрузкой из каталога vendor:

{
  "autoload": {
    "psr-4": {
      "_Local\\": "lib/_local",
      "SomeVendor\\": "lib/somevendor-dev",
      "OtherVendoer\\": [ "lib/othervendor", "tmp/othervendor"]
    }
  }
}

composer vs git submodule update

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

Composer более универсален, т.к. он может загружать пакеты прямо из хранилища git, без регистрации их на Packagist.org и где-либо еще.

Composer лучше подходит для поддержания зависимостей, т.к. именно для этого он и создавался. У подмодуля git тоже могут быть подмодули, с --recursive они загрузятся. Однако проверки версий PHP и пр. не будет.

composer --prefer-install=source - это, по сути, и есть git submodule

Хуки git для composer

При использовании Git composer install нужно выполнять при инициализации рабочей копии - git checkout (можно использовать хук вида post-checkout) и при получении изменений - git pull (post-merge) в случае, если изменился composer.lock.

При публикации изменений на основной площадке по типу push-to-deploy, когда главный репозиторий находится непосредственно в рабочем каталоге, нужно иметь аналогичный хук post-receive.

Для реализации такой схемы понадобятся два файла - непосредственно сам хук и скрипт, где сосредоточена собственно рабочая логика:

Хук - файл .git/hooks/post-receive:

#/bin/sh

echo "This is post-receive hook"
# current dir is .git
sh ../../composer-install-if-necessary.sh

Во всех хуках можно использовать одну и ту же команду.

Скрипт composer-install-if-necessary.sh нужно поместить в каталог выше рабочий копии (.. по отношению к ней). Это логично, если рабочих копий несколько - например, prod и несколько dev, и все они будут его использовать.

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