Как запускать на компьютере PHP, не устанавливая его (Linux)
Ответ прост и очевиден: я использую Docker 😃 Преимущества, которые я получаю от такого использования PHP:
- Не надо морочаться с настройкой локального окружения, переключением версии PHP, установкой дополнительных библиотек и разрешением конфликтов
- Достаточно одного текстового файла с алиасами командной строки, чтобы любая актуальная версия PHP работала на любом компьютере. А отсюда
- Легкость переноса данных между машинами
- Можно спокойно снести систему на своем основном компьютере, восстановление работы с PHP будет просто как
git pull
Заметка
Эта статья - про разбор использования докера в одном специфичном сценарии, так что если вы с этой технологией уже хорошо знакомы - можно смело пропускать. Остальных прошу под кат.
Что такое алиасы
Алиас - это дополнительная или переопределенная команда в терминале, при вызове которой на самом деле вызывается другая команда. Например, если вы для вывода списка файлов в папке всегда пишете ls -la
, имеет смысл сделать алиас alias ls='ls -la'
. Таким образом каждый раз, когда вы будете набирать в терминале команду ls
, фактически будет вызываться ls -la
.
Создание алиаса - это команда для терминала. Т.е. вы можете в терминале написать alias ls='ls -la'
, и это будет работать. Но только в этой сессии терминала. Чтобы это работало всегда - надо запускать такую команду во всех сессиях. Это делается по-разному в разных shell-оболочках. Я расскажу про то, как это делается в bash, который стоит по умолчанию в большинстве linux-дистрибутивов. Если вы используете ZShell или другую оболочку - будем считать, что вы в курсе, как в ней создаются алиасы.
Итак, bash. Любой пользователь баша может создать в своем домашнем каталоге файл .bashrc
, который выполняется всегда при старте терминала. Убунту создает его по умолчанию и по умолчанию же делает в нем такую запись:
if [ -f ~/.bash_aliases ]; then
. ~/.bash_aliases
fi
Т.е. подключает файл .bash_aliases
в домашнем каталоге, если он существует. Все свои алиасы я пишу именно в .bash_aliases
, чтобы держать их в отдельном файле. А теперь - приступим к изучению его содержимого.
PHP
Для PHP я создал набор вот таких алиасов:
alias php='docker run --rm -it -u `id -u`:`id -g` --volume `pwd`:/app -w /app ghcr.io/mileschou/xdebug:8.1 php $@'
alias php81='docker run --rm -it -u `id -u`:`id -g` --volume `pwd`:/app -w /app ghcr.io/mileschou/xdebug:8.1 php $@'
alias php8='docker run --rm -it -u `id -u`:`id -g` --volume `pwd`:/app -w /app ghcr.io/mileschou/xdebug:8.0 php $@'
alias php74='docker run --rm -it -u `id -u`:`id -g` --volume `pwd`:/app -w /app ghcr.io/mileschou/xdebug:7.4 php $@'
В итоге я пишу в терминале команды вроде php -a
для интерактивной работы или php vendor/bin/phpunit
для запуска тестов, и все работает так, будто php стоит локально на мой машине. А когда для какого-то проекта мне нужна другая версия php, я пишу, например, php74 vendor/bin/psalm
.
Итак, айда разбирать приведенные выше команды. Для каждой версии у меня написано одно и то же, меняется только собственно версия.
docker run
запускает новый контейнер на основе указанного образа (он - в конце команды). Документация поdocker run
. Все опции этой команды описаны в документации на той же странице.--rm
- этот параметр говорит о том, что контейнер должен быть удален после того, как закончит работу. Если его не указать, то при каждомdocker run
будет создаваться новый контейнер, после остановки он никуда не денется, и вскоре у вас закончится место на жестком диске.-it
- этот параметр нужен для работы в интерактивной сессии командной строки. Если его не указать, то не будут работать те команды php, которые ждут какой-то реакции от пользователя (например,php -a
или скрипты, просящие ввести данные).-u
запускает контейнер от имени определенного пользователя. По умолчанию контейнер запускается от имени суперпользователя root, что может привести к проблемам с правами доступа к файлам, которые монтируются с локальной машины и изменяются/создаются в контейнере.`id -u`
и`id -g`
возвращает id пользователя и его группы соответственно, и в большинстве случаев это будет1000
. Эти команды заключены в обратные кавычки для того, чтобы они выполнились до вызова алиаса. В итоге команда в алиасе вызовется с таким значением параметра пользователя:-u 1000:1000
, если ваши id пользователя и группы равны1000
.--volume `pwd`:/app
- этот параметр монтирует текущий путь в файловой системе (pwd
) в контейнер по пути/app
. Другими словами, ваши файлы из текущей папки будут доступны во контейнере по пути/app
.-w /app
- устанавливаем путь/app
в качестве рабочей папки в контейнере.ghcr.io/mileschou/xdebug:8.1
- название запускаемого образа контейнера и его тег (версия). Я использую этот образ вместо официального php, т.к. в официальном нет XDebug, а порой бывает нужна отладка или вычисление покрытия кода тестами. Тег всегда указывается после двоеточия, в этом примере - это8.1
php $@
- это команда, запускаемая внутри контейнера. При этом$@
- это переменная bash, указывающая на строку после алиаса. Покажу на примере. Если я напишу в терминалеphp vendor/bin/phpunit --foo --bar
, в переменной$@
окажетсяvendor/bin/phpunit --foo --bar
.
PhpStorm
Шторм прекрасно работает с контейнерами. Для того чтобы была возможность кнопкой запускать тесты, статический анализ или другие скрипты из Шторма, надо добавить контейнер в качестве интерпретатора. Документация.
Однако, на этом не приключения заканчиваются. Есть еще один частый кейс использования php в терминале, composer.
Composer
С композером все максимально похоже. Однако, команда тут значительно длиннее, т.к. добавляются переменные окружения и тома. Нужны они для двух целей: хранить настройки и кеш композера между запусками контейнера и прокидывать данные для ssh-авторизации с хост-машины в контейнер. У меня для него два алиаса на случай, если еще придется работать с проектом, где жестко вшита первая версия. Тут я уже использую официальные образы.
alias composer='docker run --rm -it --tty -u 1000:1000 -e COMPOSER_HOME=/composer-data/.composer -e COMPOSER_CACHE_DIR=/composer-data/.composer/cache -e SSH_AUTH_SOCK=/ssh-auth.sock --volume `pwd`:/app --volume /home/`whoami`/.composer:/composer-data/.composer --volume /run/user/1000/keyring/ssh:/ssh-auth.sock --volume /etc/passwd:/etc/passwd:ro --volume /etc/group:/etc/group:ro composer:2 $@'
alias composer1='docker run --rm -it --tty -u 1000:1000 -e COMPOSER_HOME=/composer-data/.composer -e COMPOSER_CACHE_DIR=/composer-data/.composer/cache -e SSH_AUTH_SOCK=/ssh-auth.sock --volume `pwd`:/app --volume /home/`whoami`/.composer:/composer-data/.composer --volume /run/user/1000/keyring/ssh:/ssh-auth.sock --volume /etc/passwd:/etc/passwd:ro --volume /etc/group:/etc/group:ro composer:1 $@'
Кроме параметров, которые я уже разобрал выше, тут используется еще несколько. Не буду повторяться и остановлюсь только на них.
-e
- этот параметр задает переменную окружения внутри контейнера. Я задаю их несколько:COMPOSER_HOME=/composer-data/.composer
- где композер будет искать и хранить свои данные (например, токены доступа на GitHub)COMPOSER_CACHE_DIR=/composer-data/.composer/cache
- где композер будет искать и хранить свой кеш. Это нужно для оптимизации. Если не хранить кеш на хосте, он будет каждый раз заново скачивать все пакеты. Если же в кеше есть пакет нужной версии - композер просто скопирует файлы из кеша в проект.SSH_AUTH_SOCK=/ssh-auth.sock
- путь к авторизационному сокету для ssh-агента
- Плюс я подключаю еще несколько томов с хоста. Это пары к указанным выше переменным окружения.
--volume /home/`whoami`/.composer:/composer-data/.composer
- сохраняю данные композера на хосте между запусками контейнеров--volume /run/user/`id -u`/keyring/ssh:/ssh-auth.sock
- прокидываю в контейнер авторизационный сокет для ssh-агента--volume /etc/passwd:/etc/passwd:ro
и--volume /etc/group:/etc/group:ro
прокидывают в контейнер учетные записи пользователей с хоста в режиме "только для чтения" (за это отвечает:ro
в конце). Стоит сразу заметить, что это небезопасно, и делать так стоит, только если вы понимаете, что и зачем делаете. Лично я считаю, что официальные образы композера достойны доверия, но это лишь мое личное мнение. А без этих томов не будет работать авторизация на гитхабе по SSH с ключами с хост-машины.
Стоит помнить, что в контейнере - минимально необходимая для работы композера сборка. Это значит, что порой вам придется дописывать флаг --ignore-platform-reqs
к вызываемой команде: composer i --ignore-platform-reqs
.
Если остались какие-то вопросы или есть предложения по улучшению алиасов - заходите в чат блога, пообщаемся.