Как запускать на компьютере PHP, не устанавливая его (Linux)

August 19, 2022
Около 5 мин

Ответ прост и очевиден: я использую Docker 😃 Преимущества, которые я получаю от такого использования PHP:

  • Не надо морочаться с настройкой локального окружения, переключением версии PHP, установкой дополнительных библиотек и разрешением конфликтов
  • Достаточно одного текстового файла с алиасами командной строки, чтобы любая актуальная версия PHP работала на любом компьютере. А отсюда
    • Легкость переноса данных между машинами
    • Можно спокойно снести систему на своем основном компьютере, восстановление работы с PHP будет просто как git pull

Заметка

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

Как запускать на компьютере PHP, не устанавливая его

Что такое алиасы

Алиас - это дополнительная или переопределенная команда в терминале, при вызове которой на самом деле вызывается другая команда. Например, если вы для вывода списка файлов в папке всегда пишете 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 runopen in new window. Все опции этой команды описаны в документации на той же странице.
  • --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

Шторм прекрасно работает с контейнерами. Для того чтобы была возможность кнопкой запускать тесты, статический анализ или другие скрипты из Шторма, надо добавить контейнер в качестве интерпретатора. Документацияopen in new window. phpstorm-settings-php-interpreter-docker.png

Однако, на этом не приключения заканчиваются. Есть еще один частый кейс использования 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.

Если остались какие-то вопросы или есть предложения по улучшению алиасов - заходите в чат блогаopen in new window, пообщаемся.