Написание базового расширения 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 installedconfig.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 — локальная установка не требуется.
Все уроки этого курса
- Как работает Zend Engine
- Управление памятью и сборка мусора
- OPcache и JIT-компиляция
- Написание базового расширения PHP на C