Как создать расширение для PHP?php-46

Разработка PHP-расширений позволяет интегрировать в PHP высокопроизводительный C-код для решения специфических задач. Рассмотрим процесс создания расширения от начала до конца.

1. Подготовка окружения

Требуемые инструменты:

# Для Debian/Ubuntu
sudo apt install php-dev php-cli autoconf automake libtool re2c

# Для CentOS/RHEL
sudo yum install php-devel php-cli autoconf automake libtool re2c

2. Генерация скелета расширения

PHP предоставляет утилиту ext_skel для создания заготовки:

php ./ext_skel.php --extname=my_extension
cd my_extension

Структура сгенерированного проекта:

my_extension/
├── config.m4      # Конфигурация сборки
├── php_my_extension.h # Заголовочный файл
├── my_extension.c # Основной исходный код
└── tests/         # Тесты

3. Настройка config.m4

Пример модификации config.m4:

dnl Указываем, что расширение не зависит от внешних библиотек
PHP_ARG_ENABLE(my_extension, whether to enable My Extension support,
[  --enable-my-extension   Enable My Extension support])

if test "$PHP_MY_EXTENSION" != "no"; then
  dnl Указываем, что расширение должно быть скомпилировано
  PHP_NEW_EXTENSION(my_extension, my_extension.c, $ext_shared)
fi

4. Реализация базового функционала

4.1. Добавление функции в расширение

Модифицируем my_extension.c:

// Объявление функции
PHP_FUNCTION(my_extension_hello)
{
    char *name = NULL;
    size_t name_len;

    if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &name, &name_len) == FAILURE) {
        RETURN_NULL();
    }

    php_printf("Hello, %s!\n", name);
    RETURN_TRUE;
}

// Регистрация функции
static const zend_function_entry my_extension_functions[] = {
    PHP_FE(my_extension_hello, NULL)
    PHP_FE_END
};

4.2. Добавление класса

zend_class_entry *my_extension_ce;

PHP_METHOD(MyExtensionClass, __construct)
{
    zval *name;

    if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &name) == FAILURE) {
        RETURN_NULL();
    }

    zend_update_property(Z_OBJCE_P(getThis()), Z_OBJ_P(getThis()), "name", sizeof("name")-1, name);
}

// Регистрация методов класса
static const zend_function_entry my_extension_class_methods[] = {
    PHP_ME(MyExtensionClass, __construct, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR)
    PHP_FE_END
};

// В функции PHP_MINIT_FUNCTION
zend_class_entry ce;
INIT_CLASS_ENTRY(ce, "MyExtensionClass", my_extension_class_methods);
my_extension_ce = zend_register_internal_class(&ce);

5. Сборка и установка

phpize
./configure
make
sudo make install

Добавляем расширение в php.ini:

extension=my_extension.so

6. Тестирование расширения

Пример PHP-кода для тестирования:

<?php
// Тестирование функции
my_extension_hello("World");

// Тестирование класса
$obj = new MyExtensionClass("Test");
?>

7. Продвинутые техники

7.1. Работа с массивами

PHP_FUNCTION(my_extension_create_array)
{
    array_init(return_value);

    add_assoc_string(return_value, "key", "value");
    add_index_long(return_value, 0, 42);
}

7.2. Итераторы

// Реализация интерфейса Iterator
zend_class_implements(my_extension_ce, 1, zend_ce_iterator);

7.3. Поддержка PHP 8.x

Особенности для PHP 8:

  • Новая API для работы с типами
  • Поддержка атрибутов
  • Изменения в Zend Engine

8. Отладка расширений

Использование gdb:

gdb php
(gdb) run test_script.php

Логирование внутри расширения:

php_error_docref(NULL, E_NOTICE, "Debug message: %s", "value");

9. Оптимизация производительности

  1. Используйте zend_string вместо char*
  2. Минимизируйте аллокации памяти
  3. Используйте persistent ресурсы для долгоживущих объектов
  4. Кэшируйте zend_class_entry

10. Распространение расширения

  1. PECL: Официальный репозиторий PHP-расширений
  2. Composer: Для расширений с PHP-кодом
  3. GitHub: Сборка через docker

Резюмируем:

создание PHP-расширений требует глубокого понимания C и внутреннего устройства PHP, но открывает возможности для существенной оптимизации критического кода. Начинайте с простых функций, постепенно переходя к сложным структурам данных и интеграциям с системными библиотеками.