Сборка расширений PHP

Теперь, когда вы знаете, как собрать сам PHP, мы перейдём к сборке дополнительных расширений. Мы обсудим, как работает процесс сборки и какие опции для этого доступны.

Загрузка разделяемых расширений

Как вы уже знаете из предыдущего раздела, расширения PHP могут быть либо статически встроены в бинарный файл PHP, либо собраны в разделяемый объект (.so). Статическая линковка используется по умолчанию для большинства поставляемых в комплекте расширений, тогда как разделяемые объекты можно создать, явно передав --enable-EXTNAME=shared или --with-EXTNAME=shared скрипту ./configure.

В то время как статические расширения всегда доступны, разделяемые расширения необходимо загружать с помощью ini-опций extension или zend_extension. Обе опции принимают либо абсолютный путь к файлу .so, либо путь относительно настройки extension_dir.

В качестве примера рассмотрим сборку PHP, выполненную со следующей строкой configure:

~/php-src> ./configure --prefix=$HOME/myphp \
                       --enable-debug --enable-maintainer-zts \
                       --enable-opcache --with-gmp=shared

В этом случае и расширение opcache, и расширение GMP собираются в разделяемые объекты, расположенные в директории modules/. Вы можете загрузить оба, либо изменив extension_dir, либо передав абсолютные пути:

~/php-src> sapi/cli/php -dzend_extension=`pwd`/modules/opcache.so \
                        -dextension=`pwd`/modules/gmp.so
# or
~/php-src> sapi/cli/php -dextension_dir=`pwd`/modules \
                        -dzend_extension=opcache.so -dextension=gmp.so

# or (since PHP 7.2 the .so is optional)
~/php-src> sapi/cli/php -dextension_dir=`pwd`/modules \
                        -dzend_extension=opcache -dextension=gmp

На этапе make install оба файла .so будут перемещены в директорию расширений вашей установки PHP, которую можно найти с помощью команды php-config --extension-dir. Для указанных выше опций сборки это будет /home/myuser/myphp/lib/php/extensions/no-debug-non-zts-MODULE_API. Это значение также будет значением по умолчанию для ini-опции extension_dir, так что указывать её явно не придётся, и вы сможете загружать расширения напрямую:

~/myphp> bin/php -dzend_extension=opcache -dextension=gmp

Остаётся один вопрос: какой механизм следует использовать? Разделяемые объекты позволяют иметь базовый бинарный файл PHP и загружать дополнительные расширения через php.ini. Дистрибутивы используют это, предоставляя “голый” пакет PHP и распространяя расширения как отдельные пакеты. С другой стороны, если вы собираете собственный бинарный файл PHP, вам, вероятно, это не нужно, потому что вы уже знаете, какие расширения вам требуются.

Как правило, статическая линковка используется для расширений, поставляемых вместе с самим PHP, а разделяемые расширения — для всего остального. Причина проста: собрать внешнее расширение как разделяемый объект проще (или, по крайней мере, это менее навязчиво), как вы увидите далее. Другое преимущество в том, что вы можете обновить расширение без пересборки PHP.

Note

Если вам нужна информация о различии между расширениями и Zend-расширениями, вы можете заглянуть в соответствующую главу.

Установка расширений из PECL

PECL, PHP Extension Community Library, предлагает большое количество расширений для PHP. Когда расширения удаляются из основного дистрибутива PHP, они обычно продолжают существовать в PECL. Аналогично, многие расширения, которые сейчас поставляются в комплекте с PHP, ранее были расширениями PECL.

Если вы указали --with-pear на этапе конфигурации вашей сборки PHP, make install загрузит и установит PECL как часть PEAR. Вы найдёте скрипт pecl в директории $PREFIX/bin. Теперь установка расширений сводится к простому запуску pecl install EXTNAME, например:

~/myphp> bin/pecl install apcu

Эта команда загрузит, скомпилирует и установит расширение APCu. Результатом будет файл apcu.so в вашей директории расширений, который затем можно загрузить, передав ini-опцию extension=apcu.

Хотя pecl install очень удобен для конечного пользователя, разработчикам расширений он мало интересен. Далее мы опишем два способа собрать расширения вручную: либо путём импорта в основное дерево исходного кода PHP (это позволяет статическую линковку), либо путём внешней сборки (только разделяемая).

Добавление расширений в дерево исходного кода PHP

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

Прежде всего вам нужно поместить исходный код расширения в директорию ext/EXTNAME вашего дерева исходного кода PHP. Если расширение доступно через git, это делается так же просто, как клонирование репозитория внутри ext/:

~/php-src/ext> git clone https://github.com/krakjoe/apcu.git

В качестве альтернативы можно также загрузить исходный tar-архив и распаковать его:

/tmp> wget http://pecl.php.net/get/apcu-4.0.2.tgz
/tmp> tar xzf apcu-4.0.2.tgz
/tmp> mkdir ~/php-src/ext/apcu
/tmp> cp -r apcu-4.0.2/. ~/php-src/ext/apcu

Расширение будет содержать файл config.m4, который задаёт специфичные для расширения инструкции сборки для использования autoconf-ом. Чтобы включить их в скрипт ./configure, нужно снова запустить ./buildconf. Чтобы убедиться, что файл configure действительно будет пересоздан, рекомендуется предварительно удалить его:

~/php-src> rm configure && ./buildconf

Теперь можно использовать скрипт ./config.nice, чтобы добавить APCu к существующей конфигурации, либо начать заново с совершенно новой строкой configure:

~/php-src> ./config.nice --enable-apcu
# or
~/php-src> ./configure --enable-apcu # --other-options

Наконец, выполните make -jN для непосредственной сборки. Поскольку мы не использовали --enable-apcu=shared, расширение статически линкуется в бинарный файл PHP, то есть никаких дополнительных действий для его использования не требуется. Разумеется, вы также можете воспользоваться make install для установки полученных бинарных файлов.

Сборка расширений с помощью phpize

Расширения также можно собирать отдельно от PHP, используя скрипт phpize, который уже упоминался в разделе Сборка PHP.

phpize играет роль, похожую на скрипт ./buildconf, используемый при сборке PHP: сначала он импортирует систему сборки PHP в ваше расширение, копируя файлы из $PREFIX/lib/php/build. Среди этих файлов — php.m4 (M4-макросы PHP), phpize.m4 (который будет переименован в configure.ac внутри вашего расширения и содержит основные инструкции сборки) и run-tests.php.

Затем phpize вызовет autoconf для генерации файла ./configure, который можно использовать для настройки сборки расширения. Обратите внимание, что передавать ему --enable-apcu не нужно, так как это подразумевается неявно. Вместо этого следует использовать --with-php-config, чтобы указать путь к вашему скрипту php-config:

/tmp/apcu-4.0.2> ~/myphp/bin/phpize
Configuring for:
PHP Api Version:         20121113
Zend Module Api No:      20121113
Zend Extension Api No:   220121113

/tmp/apcu-4.0.2> ./configure --with-php-config=$HOME/myphp/bin/php-config
/tmp/apcu-4.0.2> make -jN && make install

При сборке расширений всегда следует указывать опцию --with-php-config (если у вас не единственная глобальная установка PHP) — иначе ./configure не сможет правильно определить, под какую версию PHP и с какими флагами собирать. Указание скрипта php-config также гарантирует, что make install переместит сгенерированный файл .so (который можно найти в директории modules/) в правильную директорию расширений.

Поскольку файл run-tests.php также был скопирован на этапе phpize, вы можете запускать тесты расширения с помощью make test (либо явным вызовом run-tests.php).

Также доступна цель make clean для удаления скомпилированных объектов, которая позволяет принудительно выполнить полную пересборку расширения, если инкрементальная сборка не удалась после изменения. Кроме того, phpize предоставляет опцию очистки через phpize --clean. Она удалит все файлы, импортированные phpize, а также файлы, созданные скриптом ./configure.

Вывод информации о расширениях

Бинарный файл PHP CLI предоставляет несколько опций для отображения информации о расширениях. Вы уже знаете про -m, которая выводит список всех загруженных расширений. С её помощью можно убедиться, что расширение загрузилось корректно:

~/myphp/bin> ./php -dextension=apcu -m | grep apcu
apcu

Есть ещё несколько переключателей, начинающихся с --r, которые предоставляют доступ к функциональности Reflection. Например, можно использовать --ri, чтобы вывести конфигурацию расширения:

~/myphp/bin> ./php -dextension=apcu --ri apcu
apcu

APCu Support => disabled
Version => 4.0.2
APCu Debugging => Disabled
MMAP Support => Enabled
MMAP File Mask =>
Serialization Support => broken
Revision => $Revision: 328290 $
Build Date => Jan  1 2014 16:40:00

Directive => Local Value => Master Value
apc.enabled => On => On
apc.shm_segments => 1 => 1
apc.shm_size => 32M => 32M
apc.entries_hint => 4096 => 4096
apc.gc_ttl => 3600 => 3600
apc.ttl => 0 => 0
# ...

Переключатель --re выводит список всех ini-настроек, констант, функций и классов, добавленных расширением:

~/myphp/bin> ./php -dextension=apcu --re apcu
Extension [ <persistent> extension #27 apcu version 4.0.2 ] {
  - INI {
    Entry [ apc.enabled <SYSTEM> ]
      Current = '1'
    }
    Entry [ apc.shm_segments <SYSTEM> ]
      Current = '1'
    }
    # ...
  }

  - Constants [1] {
    Constant [ boolean APCU_APC_FULL_BC ] { 1 }
  }

  - Functions {
    Function [ <internal:apcu> function apcu_cache_info ] {

      - Parameters [2] {
        Parameter #0 [ <optional> $type ]
        Parameter #1 [ <optional> $limited ]
      }
    }
    # ...
  }
}

Переключатель --re работает только для обычных расширений, для Zend-расширений используется --rz. Это можно проверить на opcache:

~/myphp/bin> ./php -dzend_extension=opcache --rz "Zend OPcache"
Zend Extension [ Zend OPcache 7.0.3-dev Copyright (c) 1999-2013 by Zend Technologies <http://www.zend.com/> ]

Как видите, это не выводит никакой полезной информации. Причина в том, что opcache регистрирует и обычное расширение, и Zend-расширение, причём первое содержит все ini-настройки, константы и функции. Так что в этом конкретном случае всё равно нужно использовать --re. Другие Zend-расширения делают свою информацию доступной через --rz.