Разработка расширений php: hello world

Вступление

По работе мне приходится перерабатывать и улучшать производительность нашего студийного продукта Simple CMS — CMF ориентированная на быструю разработку модулей, админской части этих модулей, а так же быстрого создания конечных продуктов — сайтов. В данном продукте, собственно, как и в любом другом, есть критические места которые работают не так быстро как хотелось бы, и очень хочется их ускорить. Но как ускорять то, что уже просто невозможно ускорить на уровне PHP? Правильно — сделать аналогичные функции и методы в php расширении, а написанный код который работает не так быстро как хотелось бы — оставить для совместимости с теми интерпретаторами, где не будет данного модуля.

Хочу заметить, на данный момент расширение не написано даже на 1%, но я надеюсь что я дойду до того этапа, когда оно будет работать хотя бы с багами.

Далее будет описано как создать hello world за пять минут.

Опять оговорюсь: все действия проводятся под linux‐системой, и адаптировать действия под иные системы придется вам самим. А так же, расширение будет ориентированно под PHP 5.4, и не будет работать под PHP версии <5.4.

Подготовка

Для начала нам нужно установить сам PHP, пакет PHP с dev‐инструментами и make:

apt-get install php5 php5-cli php5-dev make

После того как пакеты будут установлены, создаем в произвольном месте папку hello‐world, переходим в нее и создаем два файла:

mkdir hello-word && cd hello-world
touch config.m4
touch hello_world.c

Все готово, переходим к написанию самого расширения.

Пишем…

Для начала нам необходимо написать в файл config.m4 следующее:

PHP_ARG_ENABLE(hello_world, whether to enable my helloworld extension,
[ --enable-hello-world Enable hello world])

if test "$PHP_HELLO_WORLD" = "yes"; then
AC_DEFINE(HAVE_HELLO_WORLD, 1, [Whether you have hello world])
PHP_NEW_EXTENSION(hello_world, hello_world.c, $ext_shared)
fi

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

После этого можно перейти к написанию кода расширения:

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "php.h"
 
#define PHP_HELLO_WORLD_VERSION "1.0"
#define PHP_HELLO_WORLD_EXTNAME "hello_world"
 
extern zend_module_entry hello_world_module_entry;
#define phpext_hello_ptr &hello_world_module_entry
 
PHP_FUNCTION(hello_php);
 
// list of custom PHP functions provided by this extension
// set {NULL, NULL, NULL} as the last record to mark the end of list
static zend_function_entry my_functions[] = {
    PHP_FE(hello_php, NULL)
    {NULL, NULL, NULL}
};
 
// the following code creates an entry for the module and registers it with Zend.
zend_module_entry hello_world_module_entry = {
    STANDARD_MODULE_HEADER,
    PHP_HELLO_WORLD_EXTNAME,
    my_functions,
    NULL, // name of the MINIT function or NULL if not applicable
    NULL, // name of the MSHUTDOWN function or NULL if not applicable
    NULL, // name of the RINIT function or NULL if not applicable
    NULL, // name of the RSHUTDOWN function or NULL if not applicable
    NULL, // name of the MINFO function or NULL if not applicable
    PHP_HELLO_WORLD_VERSION,
    STANDARD_MODULE_PROPERTIES
};
 
ZEND_GET_MODULE(hello_world)
 
// implementation of a custom hello_php()
PHP_FUNCTION(hello_php)
{
    RETURN_STRING("Hello World", 1);
}

Рассмотрим ключевые моменты в данном куске кода:

#define PHP_HELLO_WORLD_VERSION "1.0"
#define PHP_HELLO_WORLD_EXTNAME "hello_world"

Тут мы объявляем версию расширения, а так же его название, для последующей регистрации модуля.

Тут мы объявляем саму функцию которую нам будет предоставлять модуль — hello_php ();

PHP_FUNCTION(hello_php);

И объявляем массив со всеми функциями которые будет предоставлять модуль

static zend_function_entry my_functions[] = {
    PHP_FE(hello_php, NULL)
    {NULL, NULL, NULL}
};

И заполняем структуру с описанием модуля, в которую передаем ранее объявленные — версию, название модуля, массив с функциями которые предоставляет расширение, а так же дополнительные значения по умолчанию (о них мы может быть поговорим в следующий раз):

zend_module_entry hello_world_module_entry = {
    STANDARD_MODULE_HEADER,
    PHP_HELLO_WORLD_EXTNAME,
    my_functions,
    NULL, // name of the MINIT function or NULL if not applicable
    NULL, // name of the MSHUTDOWN function or NULL if not applicable
    NULL, // name of the RINIT function or NULL if not applicable
    NULL, // name of the RSHUTDOWN function or NULL if not applicable
    NULL, // name of the MINFO function or NULL if not applicable
    PHP_HELLO_WORLD_VERSION,
    STANDARD_MODULE_PROPERTIES
};

Макрос ZEND_GET_MODULE необходим в случае если расширение динамически подключаемое (shared), какое мы собственно и делаем. включает необходимый код для динамического расширения.

Далее, самое вкусное — реализация функции hello_php ():

PHP_FUNCTION(hello_php)
{
    RETURN_STRING("Hello World", 1);
}

Тут в принципе все очевидно — описываем функцию hello_php которая возвращает строку «Hello World ». У макроса RETURN_STRING два аргумента, первый — это собственно само возвращаемое значение, а второй — как его вернуть, в виде копии, или вернуть ссылку на него, ну и значения этого аргумента 1 и 0 соответственно для копии и ссылки.

Вроде бы тут все понятно, можно перейти к сборке.

Сборка

Тут так же ничего сложного, в консоли поочереди выполняем команды:

phpize
./configure
make
sudo make install

Теперь чуть‐чуть подробностей. phpize (или же phpize5) — утилита которая генерирует различные файлики вроде configure, config.h.in и так далее. configure — создаст makefile необходимый для сборки, а так же файл config.h с различными параметрами. make — соберет расширение, а make install — скопирует расширение в папку с расширениями php (ее можно узнать выполнив php‐config --extension‐dir).

В случае удачной сборки можно переходить к запуску.

Запуск

Мы собрали модуль и установили, осталось его только подключить — для этого в php.ini добавляем строку

extension=hello_world.so

и сохраняем файл. Для пущей надежности выполняем

php -m | grep hello_world

и если видим там свой модуль — выполняем код

php -r "echo hello_php();"

И в случае успеха вы увидете желанную фразу — Hello World:)

Заключение

В качестве источников информации был использован http://google.com, http://php.net, http://php.webtutor.pl, http://habrahabr.ru.

Весь код представленный в данном посте распространяется «As Is», и я не несу никакой ответственности за него :)

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

P.P. S. Код не подсвеченный потому что форматировалка кода сломалась…

comments powered by Disqus