02 Как PHP загружает свои расширения?
Вероятно, вы знаете, что родные расширения PHP скомпилированы в .so-файлы в unix-подобных системах и DLL-файлах в средах Windows и что глобальный файл php.ini содержит список всех расширений, доступных в вашей системе. Это означает, что если вы создаете собственное расширение, вы также создадите такой файл .so или .dll, и вам придется обновить файл конфигурации PHP, чтобы ваше собственное расширение было загружено PHP.
Функция запуска get_module()
Прежде чем мы объясним, как вы можете создать собственное расширение, мы сначала объясним, что PHP делает для загрузки расширения. Когда PHP запускается, он загружает файлы конфигурации * .ini из своего каталога конфигурации и для каждой строки «extension = name.so» в этих файлах конфигурации, он открывает соответствующую библиотеку и вызывает функцию get_module() из него. Поэтому каждая библиотека расширений (также как и ваше расширение) должно определять и реализовывать эту функцию «get_module()». Эта функция вызывается PHP сразу после загрузки библиотеки (и, таким образом, перед обработкой страниц), и она должна возвращать адрес памяти, который указывает на структуру, которая содержит информацию обо всех функциях, классах, переменных и константах, которые становятся доступными расширению.
Структура, возвращаемая get_module(), определяется в файлах заголовков движка Zend, но это довольно сложная структура без хорошей документации. К счастью, библиотека PHP-CPP облегчает вам жизнь и предлагает класс Extension, который можно использовать вместо этого.
#include <phpcpp.h> /** * сообщить компилятору, что get_module - это чистая функция C */ extern "C" { /** * Функция, которая вызывается PHP сразу после начала процесса PHP, * и возвращает адрес внутренней структуры PHP со всеми подробностями * и функциями вашего расширения * * @return void* указатель на адрес, который понимается PHP */ PHPCPP_EXPORT void *get_module() { // static(!)Php::Extension объект, который должен оставаться в памяти // на протяжении всего процесса (поэтому он статический) static Php::Extension myExtension("my_extension", "1.0"); // @todo добавить свои собственные функции, классы, пространства имен в расширение // return the extension return myExtension; } }
В приведенном выше примере вы видите очень простую реализацию функции get_module (). Каждое расширение PHP, использующее библиотеку PHP-CPP, реализует эту функцию более или менее аналогично, и это отправная точка каждого расширения. Ряд элементов требует особого внимания. Для начала единственным заголовочным файлом, который вы видите, является заголовочный файл phpcpp.h. Если вы используете PHP-CPP-библиотеку для создания собственных расширений, вам не нужно включать сложные, неструктурированные и в основном недокументированные файлы заголовков движка Zend - все, что вам нужно, это один заголовочный файл phpcpp.h Библиотека PHP-CPP. Если вы настаиваете, вы, конечно, можете также включать заголовочные файлы основного PHP-движка, но вам этого не нужно. PHP-CPP заботится о работе с внутренними механизмами PHP и предлагает вам простой в использовании API.
Следующее, что вы заметите, это то, что мы поместили функцию get_module () внутри кодового блока extern "C". Поскольку имя библиотеки уже отдается, PHP-CPP является библиотекой C ++. Однако PHP ожидает, что ваша библиотека и особенно ваша функция get_module () будут реализованы в C, а не на C ++. Вот почему мы завершили функцию get_module () в блоке extern "C". Это даст указание компилятору C ++, что get_module () является регулярной функцией C и что он не должен применять к нему какое-либо имя C ++.
Библиотека PHP-CPP определяет макрос «PHPCPP_EXPORT», который должен быть помещен перед функцией get_module (). Этот макрос гарантирует, что функция get_module () будет публично экспортироваться и, следовательно, вызвана PHP. Макрос имеет другую реализацию, основанную на компиляторе и операционной системе.
Это, кстати, также является единственным макросом, который предлагает PHP-CPP. PHP-CPP намеревается быть простой библиотекой C ++, не используя магии или трюки от пре-процессоров. То, что вы видите, это то, что вы получаете: если что-то похоже на функцию, вы можете быть уверены, что на самом деле это функция, и когда что-то похоже на переменную, вы можете быть уверены, что она также является переменной.
Давайте двигаться дальше. Внутри функции get_module () создается объект Php :: Extension, и он возвращается. Крайне важно создать статический экземпляр этого класса Php :: Extension, потому что объект должен существовать для всего жизненного цикла процесса PHP, а не только для продолжительности вызова get_module (). Конструктор принимает два аргумента: имя вашего расширения и его номер версии.
Последним шагом в функции get_module () является то, что возвращается объект расширения. Сначала это может показаться странным, потому что функция get_module () должна возвращать указатель-на-void, а не полный объект Php :: Extension. Почему компилятор не жалуется на это? Ну, класс Php :: Extension имеет оператор cast-to-void-pointer-operator. Поэтому, хотя кажется, что вы возвращаете полный объект расширения, на самом деле вы возвращаете только адрес памяти, который указывает на структуру данных, которая понимается основным движком PHP, и которая содержит все детали вашего расширения.
Обратите внимание, что приведенный выше пример еще не экспортирует никаких родных функций или родных классов в PHP - он создает только расширение. Это будет следующий шаг.