0Pricing
PHP Academy · Урок

Написание базового расширения PHP на C

Создайте и загрузите собственное нативное расширение

«Написание базового расширения PHP на C» — бесплатный урок PHP Academy на CoddyKit. Это урок 4 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения PHP Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс PHP Academy содержит 4 уроков всего.

Машинный код в PHP

Если чистый PHP работает слишком медленно или Вам нужно связать программу с библиотекой C, напишите расширение PHP на C с использованием программного интерфейса Zend. Расширение предоставляет машинные функции и классы, которые PHP вызывает напрямую, без накладных расходов VM.

В этом уроке создаётся минимальное расширение hello от начала до конца: каркас, функция, сборка, загрузка и проверка.

Цепочка инструментов сборки

Расширения собираются с помощью phpize из состава PHP: эта утилита подготавливает сборку autoconf, используя заголовочные файлы установленного PHP. Вам понадобятся php-dev/php-devel (они предоставляют phpize и php-config), а также компилятор C и make.

# Install build prerequisites (Debian/Ubuntu)
sudo apt install php-dev build-essential

# Confirm the tools exist
phpize --version
php-config --extension-dir   # where the .so will be installed

config.m4

Каждому расширению нужен файл config.m4, который регистрирует флаг сборки и объявляет файлы исходного кода. phpize использует его для создания скрипта настройки.

dnl config.m4 for the 'hello' extension
PHP_ARG_ENABLE([hello],
  [whether to enable hello support],
  [AS_HELP_STRING([--enable-hello], [Enable hello])],
  [no])

if test "$PHP_HELLO" != "no"; then
  PHP_NEW_EXTENSION(hello, hello.c, $ext_shared)
fi

Заголовочные файлы расширения

Исходный код на C подключает заголовочные файлы Zend/PHP и объявляет точку входа модуля. php.h подключает основное API, а ext/standard/info.h используется для вывода phpinfo(). Каждое расширение определяет zend_module_entry.

/* hello.c — includes */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "php.h"
#include "ext/standard/info.h"
#include "hello_arginfo.h"   /* generated from stub */

Заглушки arginfo

Современный PHP создаёт метаданные аргументов из файла .stub.php. Вы записываете сигнатуру функции в синтаксисе, похожем на PHP, а gen_stub.php создаёт hello_arginfo.h. Благодаря этому сведения для рефлексии и информация о типах остаются точными.

<?php
// hello.stub.php — describes the native function's signature
/** @generate-class-entries */

function hello_greet(string $name): string {}
?>

Реализация функции

Нативная функция — это функция C, помеченная с помощью PHP_FUNCTION. Вы разбираете входящие аргументы с помощью макросов ZEND_PARSE_PARAMETERS, а значения возвращаете через макросы RETURN_*. Здесь мы создаём строку приветствия.

/* hello.c — the native function */
PHP_FUNCTION(hello_greet)
{
    char *name;
    size_t name_len;

    ZEND_PARSE_PARAMETERS_START(1, 1)
        Z_PARAM_STRING(name, name_len)
    ZEND_PARSE_PARAMETERS_END();

    /* Build "Hello, <name>!" into a new zend_string */
    zend_string *result = strpprintf(0, "Hello, %s!", name);
    RETURN_STR(result);   /* hands ownership to the engine */
}

Точка входа модуля

Структура zend_module_entry связывает всё воедино: имя, версию, таблицу функций (из arginfo) и хуки жизненного цикла (MINIT, RINIT, MINFO). ZEND_GET_MODULE экспортирует символ точки входа, который ищет загрузчик.

/* hello.c — module wiring */
zend_module_entry hello_module_entry = {
    STANDARD_MODULE_HEADER,
    "hello",                 /* extension name */
    ext_functions,           /* function table from arginfo */
    NULL,                    /* MINIT  (module startup)  */
    NULL,                    /* MSHUTDOWN */
    NULL,                    /* RINIT  (per-request)     */
    NULL,                    /* RSHUTDOWN */
    PHP_MINFO(hello),        /* phpinfo section */
    "0.1.0",
    STANDARD_MODULE_PROPERTIES
};

#ifdef COMPILE_DL_HELLO
ZEND_GET_MODULE(hello)
#endif

Память: emalloc и malloc

Внутри расширения выделяйте память со временем жизни запроса с помощью emalloc/efree (её отслеживает диспетчер памяти Zend, а в конце запроса она освобождается), а не с помощью обычного malloc. Для постоянных выделений памяти, сохраняющихся между запросами, используйте pemalloc. Возврат zend_string через RETURN_STR передаёт владение движку, который освобождает эту строку.

/* Request-scoped buffer the engine will clean up on error/shutdown */
char *buf = emalloc(64);
/* ... use buf ... */
efree(buf);

/* Persistent allocation surviving the request (rare) */
/* char *cfg = pemalloc(128, 1);  ...  pefree(cfg, 1); */

Сборка

Классическая сборка в три шага: phpize создаёт каркас, затем ./configure запускается с вашим флагом включения, после чего выполняется make. Не забудьте сначала запустить gen_stub.php, чтобы создать заголовок arginfo.

# Generate arginfo from the stub
php /path/to/php-src/build/gen_stub.php hello.stub.php

# Scaffold + configure + compile
phpize
./configure --enable-hello
make

# Result lands in modules/hello.so
ls -la modules/hello.so

Загрузка и проверка

Загрузите скомпилированный файл .so с помощью -d extension=... (или добавьте его в ini-файл). Затем вызывайте нативную функцию из PHP точно так же, как встроенную. Так выглядит ваш скрипт проверки после установки расширения.

<?php
// After:  php -d extension=./modules/hello.so test.php
if (!extension_loaded('hello')) {
    fwrite(STDERR, "hello extension not loaded\n");
    exit(1);
}

echo hello_greet('Zend') . PHP_EOL;   // Hello, Zend!
var_dump(extension_loaded('hello'));  // bool(true)
?>

Когда (не) стоит его создавать

Нативные расширения требуют обслуживания: это ошибки управления памятью в C, пересборка для каждой минорной версии PHP и нарушение совместимости ABI. Прежде чем переходить к нативному коду, рассмотрите FFI (вызов библиотек C из PHP без компиляции расширения) или оптимизацию на чистом PHP. Выбирайте расширение C, когда нужны максимальная скорость, глубокая интеграция с движком или аккуратная обёртка над сложной библиотекой C.

<?php
// FFI alternative: call a C library directly, no extension build
$ffi = FFI::cdef(
    "int abs(int);",   // declare the symbol
    "libc.so.6"
);
echo $ffi->abs(-42) . PHP_EOL;   // 42
?>

Быстрая проверка

Какой распределитель памяти следует использовать внутри расширения для памяти со временем жизни запроса?

Итоги

Вы создали минимальное расширение C: config.m4 регистрирует сборку, файл .stub.php создаёт arginfo, PHP_FUNCTION реализует логику, разбирая аргументы с помощью ZEND_PARSE_PARAMETERS и возвращая результат через RETURN_STR, а zend_module_entry связывает хуки жизненного цикла. Выполните сборку с помощью phpize → configure → make, загрузите файл .so и проверьте работу из PHP. Используйте emalloc для памяти на время запроса и рассмотрите FFI, прежде чем переходить к нативному коду.

Часто задаваемые вопросы

Урок «Написание базового расширения PHP на C» бесплатный?

Да — полный текст урока «Написание базового расширения PHP на C» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс PHP Academy, подпишись на CoddyKit PRO. Курс PHP Academy содержит 4 уроков всего.

Чему я научусь в уроке «Написание базового расширения PHP на C»?

Создайте и загрузите собственное нативное расширение Ты практикуешь PHP Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать PHP Academy?

Предыдущий опыт не требуется. PHP Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 4 из 4.

Сколько времени занимает урок «Написание базового расширения PHP на C»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке PHP Academy?

Да. Каждый урок PHP Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Как работает Zend Engine
  2. Управление памятью и сборка мусора
  3. OPcache и JIT-компиляция
  4. Написание базового расширения PHP на C
← Назад к PHP Academy