Вступление
По работе мне приходится перерабатывать и улучшать производительность нашего студийного продукта 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. Код не подсвеченный потому что форматировалка кода сломалась…