Какие расширения вообще бывают
PHP module – оно же обычное расширение PHP
К этому типу относится подавляющее число расширений в PHP. Все то, что подключается в php.ini
с помощью инструкции extension=some_library.so
— это они и есть.
Zend extension
Расширений такого типа крайне мало, однако они ничуть не менее востребованы.
В статье я обзорно, совсем по верхам, расскажу, чем же эти два типа расширений отличаются.
С точки зрения конечного пользователя.
Отличаются только способом подключения.
Обычные расширения подключаются через php.ini
с помощью инструкции:
extension=some_extension.so
Расширения zend с помощью:
zend_extension=some_extension.so
.
Если хочется подключить через аргумент командной строки, то, для обычных:
php -d extension=/path/extension.so
А для расширений zend:
php -z /path/zend_extension.so
Однако под капотом они очень разные.
Тут очень подходит аналогия с бензиновым и дизельным двигателем. Для пользователя вся разница заключается только в типе топлива, которое он заливает в бак, но по факту это две совершенно разных конструкции, с разными принципами работы и со своими плюсами и минусами.
С точки зрения решаемых задач
Стандартные расширения, в подавляющем числе случаев, используются для расширения функциональных возможностей языка, таких как добавления новых классов, функций, констант и т.д. Крайне редко используются для решения других задач. Например, PECL расширение Vulcan Logic Disassembler(vld) позволяет посмотреть сгенерированный opcode для скрипта.
Расширения zend используются в случаях, когда нужно максимально глубоко залезть внутрь виртуальной машины. Например для отладки или профилирования скрипта, либо для изменения логики работы PHP.
С точки зрения разработчика, который раньше не писал расширений для PHP и вдруг сподобился
Написание обычных расширений хорошо документировано и описано во множестве статей. Для них даже есть инструмент генерации скелета проекта, включенный в исходные коды PHP.
В случае с Zend extension ничего этого нет. Хороших статей практически нет. Плохих тоже. Будьте готовы к длительному и вдумчивому изучению исходных кодов как самого PHP, так и немногих существующих расширений данного типа.
С точки зрения жизненного цикла
К сожалению, тут не обойтись без кода на С, поскольку жизненный цикл расширения целиком и полностью является отражением определяющей его структуры. (Структуры привожу в сокращенном виде. Только то, что необходимо в рамках статьи)
Стандартное расширение задается структурой _zend_module_entry
(описывается в zend_module.h
)
struct _zend_module_entry {
/* skipped */
int (*module_startup_func)(INIT_FUNC_ARGS); /* MINIT() */
int (*module_shutdown_func)(SHUTDOWN_FUNC_ARGS); /* MSHUTDOWN() */
int (*request_startup_func)(INIT_FUNC_ARGS); /* RINIT() */
int (*request_shutdown_func)(SHUTDOWN_FUNC_ARGS); /* RSHUTDOWN() */
void (*info_func)(ZEND_MODULE_INFO_FUNC_ARGS); /* PHPINFO() */
/* skipped */
void (*globals_ctor)(void *global); /* GINIT() */
void (*globals_dtor)(void *global); /* GSHUTDOWN */
int (*post_deactivate_func)(void); /* PRSHUTDOWN() */
/* skipped */
};
Расширение Zend задается структурой _zend_extension
(описывается в zend_extensions.h
)
struct _zend_extension {
/* skipped */
startup_func_t startup; /* STARTUP() */
shutdown_func_t shutdown; /* SHUTDOWN() */
activate_func_t activate; /* ACTIVATE() */
deactivate_func_t deactivate; /* DEACTIVATE() */
message_handler_func_t message_handler; /* MESSAGE_HANDLER() Вызывается при регистрации расширения*/
op_array_handler_func_t op_array_handler; /* Вызывается после компиляции корневого пространства имен скрипта и для каждой функции. Принимает массив опкодов */
/* Данные хуки, если заданы, приводят к добавлению специфичных опкодов, которые
* вызывают эти функции в процессе работы скрипта
*/
statement_handler_func_t statement_handler; /* Перед каждым оператором */
fcall_begin_handler_func_t fcall_begin_handler; /* Перед вызовом функции */
fcall_end_handler_func_t fcall_end_handler; /* После вызова функции */
op_array_ctor_func_t op_array_ctor; /* Вызывается при создании OPArray */
op_array_dtor_func_t op_array_dtor; /* Вызывается при уничтожении OPArray */
int (*api_no_check)(int api_no); /* API_NO_CHECK() */
int (*build_id_check)(const char* build_id); /* BUILD_ID_CHECK() */
/* skipped */
};
А вот теперь уже можно показывать картинку с жизненным циклом.
Бонус. Гибридные расширения
Да. Такая возможность есть.
Зачем оно может понадобиться?
- Вам нужен полный контроль, предоставляемый расширением zend и, помимо этого, хочется зарегистрировать новые функции.
- Вам, зачем-то, понадобилось использовать вообще все возможные хуки.
- Вам необходимо управлять порядком загрузки своего расширения. К примеру надо загрузиться не раньше загрузки
OPCache
.
Полезные ссылки
Пример написания простого расширения Zend
Крайне полезный ресурс по внутреннему устройству PHP
Исходники PHP