- Windows php zend extension
- Where to find an extension?
- Which extension to download?
- How to Use PHP Extensions
- What Is A PHP Extension?
- Why Use PHP Extensions?
- What Is a .PHP File Extension?
- Are PHP Extensions the Same Thing as .PHP File Extensions?
- Where Do I Find Shared PHP Extensions?
- PHP Extensions That Come With PHP
- Third-Party PHP Extensions
- How to Use PHP Extensions
- Develop Better PHP With Zend
- Два типа расширений PHP. Zend extension VS PHP module
- Какие расширения вообще бывают
- С точки зрения конечного пользователя.
- С точки зрения решаемых задач
- С точки зрения разработчика, который раньше не писал расширений для PHP и вдруг сподобился
- С точки зрения жизненного цикла
- Бонус. Гибридные расширения
- Создание расширений PHP: Введение в PHP и Zend
- Введение
- Что такое Расширение?
- Жизненный цикл
- Выделение памяти
- Настройка и сборка окружения
- Hello World
- Сборка расширения
- INI-настройки
- Глобальные переменные
- INI-настройки и глобальные переменные
- Что дальше?
Windows php zend extension
On Windows, you have two ways to load a PHP extension: either compile it into PHP, or load the DLL. Loading a pre-compiled extension is the easiest and preferred way.
To load an extension, you need to have it available as a «.dll» file on your system. All the extensions are automatically and periodically compiled by the PHP Group (see next section for the download).
To compile an extension into PHP, please refer to building from source documentation.
To compile a standalone extension (aka a DLL file), please refer to building from source documentation. If the DLL file is available neither with your PHP distribution nor in PECL, you may have to compile it before you can start using the extension.
Where to find an extension?
PHP extensions are usually called «php_*.dll» (where the star represents the name of the extension) and they are located under the «PHP\ext» folder.
PHP ships with the extensions most useful to the majority of developers. They are called «core» extensions.
However, if you need functionality not provided by any core extension, you may still be able to find one in » PECL. The PHP Extension Community Library (PECL) is a repository for PHP Extensions, providing a directory of all known extensions and hosting facilities for downloading and development of PHP extensions.
If you have developed an extension for your own uses, you might want to think about hosting it on PECL so that others with the same needs can benefit from your time. A nice side effect is that you give them a good chance to give you feedback, (hopefully) thanks, bug reports and even fixes/patches. Before you submit your extension for hosting on PECL, please read » PECL submit.
Which extension to download?
Many times, you will find several versions of each DLL:
- Different version numbers (at least the first two numbers should match)
- Different thread safety settings
- Different processor architecture (x86, x64, . )
- Different debugging settings
- etc.
You should keep in mind that your extension settings should match all the settings of the PHP executable you are using. The following PHP script will tell you all about your PHP settings:
How to Use PHP Extensions
In this blog, we give an introduction to PHP extensions for beginning PHP developers. Then we give more in-depth definitions for developers familiar with PHP extensions.
What Is A PHP Extension?
A PHP extension is a specially formed library or plug-in that provides a function that can be used by many applications.
Why Use PHP Extensions?
By using extensions, you can avoid having to recreate the same code for numerous applications. To use an extension, you simply connect it to your application’s PHP binary.
You can make use of a publicly available extension or create your own to:
- Provide functions and/or classes to work with data structures such as strings, arrays, objects, lists, and graphs.
- Implement bindings to some external libraries, such as POSIX regular expressions or simplexml.
- Connect to databases, such as MySQL or PostgreSQL.
- Increase security using Hashing or data encryption via technologies such as OpenSSL and Sodium.
- Facilitate processes with external services via technologies such as XML-RPC and SOAP.
- Add new capabilities to your application including caching, debugging, profiling, and testing.
PHP extensions are usually written in C and then compiled to native machine code. However, PHP 7.4 introduced Foreign Function Interface (PHP FFI), which enables PHP developers to write extensions and bindings to C libraries in pure PHP.
What Is a .PHP File Extension?
The term .php file extension refers to the name of a file with a PHP script or source code that has a «.PHP» extension at the end of it. It’s similar to a Word file with a .doc file extension.
Are PHP Extensions the Same Thing as .PHP File Extensions?
PHP extensions are not the same thing as a .php file extension or PHP class extension (inherited classes). A PHP extension is a specially formed library or plugin. But a .PHP file extension is the name of a file. And a PHP class extension refers to a class that extends another.
In other words, files that have a .php file extension are not the same thing as an extension a PHP developer uses in code to extend functionality.
Where Do I Find Shared PHP Extensions?
If you have developed any code in PHP, you have already used extensions. Almost all the functions and classes provided by PHP come from different extensions. One of the simplest examples is the function strlen() implemented in “standard” extension. Another example is “mysqli_connect,” which is implemented in the “mysqli” extension.
If your application needs some new functionality that is potentially useful for other applications, you should consider using a reusable library. You can add it by:
- Implementing this functionality in pure PHP, by writing one or more PHP classes.
- Using a ready-made extension that provides the functionality.
- Writing your own.
PHP Extensions That Come With PHP
If you are lucky, you will find an extension that already provides the feature. There is a rich set of extensions maintained and distributed with PHP. For example, PHP 7.4 provides about 70 different extensions. Some of them are always compiled and linked directly into the PHP binary, like “standard.” These extensions are available out of the box.
Other extensions are optional. Oftentimes, these are distributed as dynamic plug-ins that should be loaded though the php.ini extension directive — for example, extension=mysqli.so.
Third-Party PHP Extensions
Despite the extensions that come with the PHP core, you can find a lot of third-party extensions on the Internet. One of the biggest repositories — https://pecl.php.net/packages.php — contains almost 400 extensions. Not all of them are stable, portable among different PHP versions, and/or well supported. If you see an extension that could be useful, you need to try it to determine if it meets your requirements.
How to Use PHP Extensions
Because existing extensions don’t cover all the application domains, from time to time, you will need to develop a new extension. Companies that do business with PHP often develop extensions with very specific functionality. They also use extensions to re-implement mission-critical parts of code in C, to achieve better performance.
Learn more about PHP extensions in our technical guide, including:
- How to prepare a development environment.
- How to generate an extension skeleton.
- How to install PHP extensions.
- What basic PHP internals are.
- Using advanced techniques to create PHP object and classes with custom behavior.
Develop Better PHP With Zend
PHP extensions are just one aspect of PHP development. There’s a lot else to consider, like debugging, security, deployment, performance, and integrations. PHP training can help you learn more.
But using the right PHP development tools will help you development better applications. One of the best tools to use is Zend Server.
Zend Server is a PHP application server. You can use Zend Server to:
- Improve debugging.
- Boost security.
- Streamline deployments.
- Accelerate performance.
- Simplify integrations.
See for yourself how Zend Server will help you. Get started with a free 30-day trial.
Два типа расширений PHP. Zend extension VS PHP module
Какие расширения вообще бывают
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 )
Расширение Zend задается структурой _zend_extension (описывается в zend_extensions.h )
А вот теперь уже можно показывать картинку с жизненным циклом.
Бонус. Гибридные расширения
Да. Такая возможность есть.
Зачем оно может понадобиться?
- Вам нужен полный контроль, предоставляемый расширением zend и, помимо этого, хочется зарегистрировать новые функции.
- Вам, зачем-то, понадобилось использовать вообще все возможные хуки.
- Вам необходимо управлять порядком загрузки своего расширения. К примеру надо загрузиться не раньше загрузки OPCache .
Создание расширений PHP: Введение в PHP и Zend
Введение
Если вы читаете данную статью, скорее всего у вас есть некоторый интерес к созданию расширений для языка PHP. Если же нет… возможно, когда вы прочтёте эту статью, то обнаружите в себе этот интерес, не смотря на то, что вы о нём даже не подозревали!
Материал, изложенный в данной статье, подразумевает знакомство как с самим языком PHP, так и с языком, на котором написан интерпретатор PHP: C.
Начнём с того, что определим причины, по которым вы хотите написать расширение для PHP:
- Существование какой-нибудь библиотеки или специфичного вызова ОС, который не может быть сделан из PHP напрямую из-за уровня абстракции принятого в языке;
- Вы хотите заставить PHP работать нестандартным способом;
- У вас уже есть решение, написанное на PHP, но вы знаете, что оно может быть быстрее, компактней и потреблять меньше памяти в процессе работы;
- У вас есть особенный код, который вы хотели бы продать. Однако, важно, что бы покупатель мог запускать Ваш код, но не смотреть исходники.
Все перечисленные выше причины являются вполне адекватными, но, создавая расширение, прежде всего вам стоит понимать, что это в первую очередь расширение.
Что такое Расширение?
Если вам приходилось использовать PHP, то вы использовали и расширения. За небольшим исключением каждая доступная для использования функция в языке PHP сгруппирована в то или иное расширение. Основная часть функций (более 400) входит в состав стандартного расширения. Исходные коды PHP распространяются с порядка 86 расширениями, имеющими примерно по 30 функций в каждом. Посчитав, получим где-то 2500 функций в сумме. Если этого не достаточно, репозитарий PECL предлагает свыше 100 дополнительных расширений, ещё больше можно найти на бескрайних просторах интернета.
«Что же, с учётом всего этого множества функций, живущих в расширениях, тогда остаётся вне расширений?» — спросите вы. «Что расширения расширяют? Что такое ядро PHP?»
Ядро PHP реализовано в виде 2-х отдельных частей. Техническая часть языка представлена в виде Zend Engine (ZE). ZE отвечает за преобразование понятного для человека скрипта в понятные для компьютера токены (tokens), после чего выполняет их. Кроме того, ZE отвечает за управление памятью, область видимости переменных, обработку вызова функций.
Второй частью ядра является то, что непосредственно называется «ядром» (the PHP core). Оно отвечает за взаимодействие со слоем SAPI (Server Application Programming Interface, интерфейс взаимодействия PHP с другим серверным ПО — CLI, CGI, Apache и так далее). Кроме того, ядро реализует обобщённый слой контроля для проверок safe_mode и open_basedir (данные фичи объявлены depricated с версии 5.3), так же, как и слой потоков, который ассоциирует файловые и сетевые I/O операции с функциями fopen, fread и fwrite.
Жизненный цикл
Когда происходит запуск заданного SAPI (например, при запуске сервера Apache по команде /usr/local/apache/bin/apachectl start), PHP начинает свою работу с запуска подсистемы ядра. К концу процедуры запуска он загружает код каждого расширения и вызывает его функцию Module Initialization (MINIT). Это даёт каждому расширению возможность инициализировать внутренние переменные, выделить память под ресурсы, зарегистрировать обработчики ресурсов и свои функции в ZE, что бы при вызове каким-нибудь скриптом функции этого расширения ZE знал, какой код ему выполнять.
Далее PHP ждёт от слоя SAPI запроса на обработку страницы. В случае CGI или CLI SAPI это происходит незамедлительно и только один раз. В случае SAPI Apache, IIS или другого полноценного web-сервера запрос на обработку страницы происходит каждый раз при запросе (возможно конкурентном) страницы удалённым пользователем. Однако, вне зависимости от того, каким образом пришёл запрос, его обработка начинается с того, что ядро PHP просит ZE настроить окружающую среду для запуска скрипта, после чего вызывает функцию Request Initialization (RINIT) для каждого расширения. RINIT даёт расширениям возможность настроить специфичные переменные окружения, выделить память для специфичных ресурсов запроса и выполнить другие задания. Наглядным примером функции RINIT в действии может служить расширение session, в котором при включенной настройке session.auto_start функция RINIT автоматически вызывает исполнение функции session_start и инициализирует переменную $_SESSION.
После того, как запрос инициализирован, ZE транслирует PHP скрипт в токены, а затем в опкоды (opcodes), которые он может выполнить. Если какой-нибудь из этих опкодов запрашивает вызов функции из расширения, ZE формирует аргументы для вызова этой функции и временно передаёт ей управление до её завершения.
После того как скрипт завершил своё выполнение, PHP вызывает функцию Request Shutdown (RSHUTDOWN) для каждого расширения, что выполнить все необходимые для завершения чистки (например, сохранение сессионных переменных на диск). Следующим шагом ZE выполняет процесс чистки (также известный как сборка мусора), который фактически выполняет метод unset для каждой переменной, использованной в выполненном скрипте (начиная с PHP 5.3 механизм сборки мусора значительно улучшен).
Завершив обработку запроса, PHP ждёт от SAPI либо запроса на обработку другого скрипта, либо сигнала на завершение. В случае CGI или CLI SAPI «следующий запрос» невозможен, поэтому SAPI инициализирует завершение работы PHP незамедлительно. В процессе завершения PHP перебирает все расширения и для каждого вызывает функцию Module Shutdown (MSHUTDOWN), после чего завершает свою собственную подсистему ядра.
Этот процесс поначалу может показаться немного запутанным, но, погрузившись в работу над расширениями, вы постепенно прочувствуете его.
Выделение памяти
Для того, что бы избежать утечек памяти в плохо написанных расширениях, ZE использует свой собственный внутренний механизм управления памятью, основанный на дополнительном флаге для определения времени жизни данных. Постоянное (persistent) выделение памяти означает, что память будет выделена более, чем на время обработки запроса одной страницы. Непостоянное (non-persistent) выделение памяти означает освобождение памяти после обработки запроса вне зависимости от того, была ли вызвана функция освобождения памяти. К примеру, выделение памяти под пользовательские переменные носит непостоянный характер, так как по завершению обработки запроса они становятся бесполезными.
Несмотря на то, что расширение в теории может возложить на ZE освобождение непостоянной памяти автоматически по завершению каждого запроса, делать это не рекомендуется. Выделенная память будет оставаться долгое время невостребованной, ассоциированные с этой памятью ресурсы будут иметь меньше шансов быть закрытыми правильно, и, в конце концов, создание путаницы с освобождением памяти — плохая практика. Как вы увидите позже, убедиться, что все данные отчищены верно, достаточно легко.
Давайте кратко сравним традиционные функции выделения памяти (которые стоит использовать только при работе с внешними библиотеками) с функциями постоянного и непостоянного выделения памяти в PHP/ZE.
Традиционные | Непостоянные | Постоянные |
---|---|---|
malloc(count) | emalloc(count) | pemalloc(count, 1) * |
calloc(count, num) | ecalloc(count, 1) | pecalloc(count, num, 1) |
strdup(str) | estrdup(str) | pestrdup(str, 1) |
strndup(str, len) | estrndup(str, len) | pemalloc() & memcpy() |
free(ptr) | efree(ptr) | pefree(ptr, 1) |
realloc(ptr, newsize) | erealloc(ptr, newsize) | perealloc(ptr, newsize, 1) |
malloc(count * num + extr) ** | safe_emalloc(count, num, extr) | safe_pemalloc(count, num, extr) |
* Семейство функций pemalloc принимает в качестве параметра флаг «постоянности», который позволяет им вести себя как их непостоянные аналоги.
К примеру: emalloc(1234) тоже самое, что и pemalloc(1234, 0)
** safe_emalloc() и (в PHP 5) safe_pemalloc реализуют дополнительную проверку целочисленных переполнений.
Настройка и сборка окружения
Теперь, когда вы ознакомились с теорией работы PHP и Zend Engine, могу поспорить, вам не терпится погрузиться в работу и начать что-нибудь делать. Однако, перед этим вам нужно обзавестись кое-какими утилитами для сборки и настроить окружение для ваших целей.
Прежде всего, вам необходим сам PHP и набор средств сборки, необходимых для PHP. Если вам не приходилось собирать PHP из исходников, предлагаю взглянуть на эту статью. Несмотря на то, что использование бинарного пакета с исходниками PHP может показаться заманчивым, такие сборки зачастую лишены двух важных параметров программы ./configure, которые очень полезны во время процесса разработки. Первый из них это —enable-debug. Эта опция компилирует PHP с дополнительной отладочной информацией в исполняемых файлах, так что при возникновении ошибки сегментации (segfault) вы сможете получить дамп ядра и воспользоваться отладчиком gdb, что бы выяснить, где и почему произошла ошибка.
Название второй опции зависит от того, с какой версией PHP вы собираетесь работать. В PHP 4.3 она называется —enable-experimental-zts, начиная с PHP 5, она переименована в —enable-maintainer-zts. Эта опция заставит PHP думать, что он работает в многопотоковой (multithread) среде, и позволит вам отловить общие ошибки, которые незаметны в среде без потоков, но повлекут нестабильную работу вашего расширения в многопотоковой среде.
Скомпилировав PHP с дополнительными опциями и установив его на сервер разработки (или рабочую станцию), вы можете преступать к созданию своего первого расширения.
Hello World
PHP_ARG_ENABLE(hello, whether to enable Hello World support,
[ —enable-hello Enable Hello World support])
if test «$PHP_HELLO» = «yes»; then
AC_DEFINE(HAVE_HELLO, 1, [Whether you have Hello World])
PHP_NEW_EXTENSION(hello, hello.c, $ext_shared)
fi
#ifndef PHP_HELLO_H
#define PHP_HELLO_H 1
#define PHP_HELLO_WORLD_VERSION «1.0»
#define PHP_HELLO_WORLD_EXTNAME «hello»
extern zend_module_entry hello_module_entry;
#define phpext_hello_ptr &hello_module_entry
#include «php.h»
#include «php_hello.h»
static function_entry hello_functions[] = <
PHP_FE(hello_world, NULL)
>;
zend_module_entry hello_module_entry = <
#if ZEND_MODULE_API_NO >= 20010901
STANDARD_MODULE_HEADER,
#endif
PHP_HELLO_WORLD_EXTNAME,
hello_functions,
NULL,
NULL,
NULL,
NULL,
NULL,
#if ZEND_MODULE_API_NO >= 20010901
PHP_HELLO_WORLD_VERSION,
#endif
STANDARD_MODULE_PROPERTIES
>;
#ifdef COMPILE_DL_HELLO
ZEND_GET_MODULE(hello)
#endif
PHP_FUNCTION(hello_world)
<
RETURN_STRING( «Hello World» , 1);
>
str = estrdup( «Hello World» );
RETURN_STRING(str, 0);
>
В этом примере вы вручную выделили непостоянную память для строки «Hello World», которую передали обратно в вызывающий скрипт с помощьюфункции RETURN_STRING, указав ей 0 в качестве второго параметра, что заставляет функцию не делать копию строки, так как она может воспользоваться нашей.
Сборка расширения
Если всё пойдёт так, как должно, вы увидите строку Hello World в качестве результата работы скрипта, так как функция hello_world из загруженного расширения возвращает строку, а команда echo отображает то, что ей было передано на вход (в данном случае – результат работы функции hello_world).
В данном примере мы возвращали строку, другие скалярные типы данных могут быть возвращены по схожему принципу: RETURN_LONG для целочисленных значений, RETURN_DOUBLE для чисел с плавающей точкой, RETURN_BOOL для TRUE/FALSE и RETURN_NULL для, как вы догадались, NULL-значений. Давайте посмотрим на каждую из них в действии, добавив строки с макросом PHP_FE в структуру function_entity и соответствующие им макросы PHP_FUNCTION в файле hello.c:
static function_entry hello_functions[] = <
PHP_FE(hello_world, NULL)
PHP_FE(hello_long, NULL)
PHP_FE(hello_double, NULL)
PHP_FE(hello_bool, NULL)
PHP_FE(hello_null, NULL)
>;
PHP_FUNCTION(hello_world);
PHP_FUNCTION(hello_long);
PHP_FUNCTION(hello_double);
PHP_FUNCTION(hello_bool);
PHP_FUNCTION(hello_null);
Так как вы не вносили изменений в файл config.m4, технически нет необходимости повторять шаги phpize и ./configure, а можно сразу перейти к выполнению команды make. Тем не менее, на этот раз я попрошу вас пройти через все три шага сборки заново с целью убедиться, что никаких проблем не возникло. Кроме того, в качестве последнего шага вы можете использовать команду make clean all вместо make, что бы быть уверенным, что все файлы с исходниками будут пересобраны. Повторюсь, что это не необходимо ввиду типа изменений, которые вы сделали, но лучше перестраховаться, чем наткнуться на ошибку. После того, как модуль собран, вам остаётся скопировать его в каталог расширений, заменив им старую версию.
Сейчас вы можете снова вызвать интерпретатор PHP, передав ему нехитрые скрипты для того, что бы протестировать только что добавленные функции. На самом деле – почему бы ни сделать это сейчас? Я подожду вас здесь…
Готово? Хорошо. Если для просмотра результата работы каждой функции вы использовали var_dump, а не echo, то, возможно, обратили внимание, что hello_bool возвращает TRUE. Это результат равенства 1-це аргумента функции. Также как и в PHP-скриптах, целочисленное значение 0 эквивалентно FALSE, в то время как любое другое число эквивалентно TRUE. Авторы расширений зачастую используют соглашение, согласно которому этим числом является 1. Желательно, но не обязательно, что бы и вы придерживались этого соглашения. Кроме того, для большего удобства доступны макросы RETURN_TRUE и RETURN_FALSE. Ниже приведён пример функции hello_bool с использованием макроса RETURN_TRUE.
Обратите внимание, что никаких круглых скобок при вызове макроса не использовалось. В этом плане макросы RETURN_TRUE и RETURN_FALSE отличаются от остальных макросов семейства RETURN_*, так что будьте внимательны и не попадитесь на этом!
Вы, возможно, заметили, что во всех предыдущих примерах мы не передаём параметр, отвечающий за создание копии результата. Причина этого в том, что для таких простых скалярных величин не требуется выделение или освобождение дополнительной памяти.
Существуют ещё 3 дополнительных возвращаемых типа: RESOURCE (возвращаемый, к примеру, функциями mysql_connect, fsockopen или ftp_connect), ARRAY (также известный как HASH) и OBJECT (возвращаемый по ключевому слову new). Речь о них пойдёт позже.
INI-настройки
Zend Engine предоставляет два подхода для работы с INI-данными. Сейчас мы рассмотрим наиболее простой из них, а к более общему вернёмся после ознакомления с глобальными переменными.
Предположим, вы хотите объявить в файле php.ini настройку hello.greeting для вашего расширения, которая будет содержать значение для вывода функцией hello_world. Для этого нам придётся добавить несколько изменений в файлы hello.c и php_hello.h в рамках изменения структуры hello_module_entry. Начнём с добавления следующих прототипов перед списком прототипов пользовательских функций в файле php_hello.h:
PHP_FUNCTION(hello_world);
PHP_FUNCTION(hello_long);
PHP_FUNCTION(hello_double);
PHP_FUNCTION(hello_bool);
PHP_FUNCTION(hello_null);
zend_module_entry hello_module_entry = <
#if ZEND_MODULE_API_NO >= 20010901
STANDARD_MODULE_HEADER,
#endif
PHP_HELLO_WORLD_EXTNAME,
hello_functions,
PHP_MINIT(hello),
PHP_MSHUTDOWN(hello),
NULL,
NULL,
NULL,
#if ZEND_MODULE_API_NO >= 20010901
PHP_HELLO_WORLD_VERSION,
#endif
STANDARD_MODULE_PROPERTIES
>;
PHP_INI_BEGIN()
PHP_INI_ENTRY( «hello.greeting» , «Hello World» , PHP_INI_ALL, NULL)
PHP_INI_END()
#include «php.h»
#include «php_ini.h»
#include «php_hello.h»
PHP_FUNCTION(hello_world)
<
RETURN_STRING(INI_STR( «hello.greeting» ), 1);
>
Обратите внимание, что вы создаёте копию значения, полученного от INI_STR. Причина в том, что строка, возвращаемая INI_STR является статической. Другими словами, если вы попытаетесь её изменить рабочее окружение PHP может стать нестабильным или даже аварийно завершиться.
Первый набор внесённых в этой главе изменений добавляет два новых метода, с которыми вы должны быть уже знакомы: MINIT и MSHUTDOWN. Как упоминалось ранее, эти методы вызываются при первом запуске SAPI-слоя и в процессе завершения его работы соответственно. Они не вызываются в течение или между запросами. В этом примере они используются для регистрации записей из файла php.ini, объявленных в вашем расширении. Также в этих функциях могут быть зарегистрированы ресурсы, объекты и обработчики потоков.
В вашей функции hello_world вы используете макрос INI_STR для получения текущего значения записи hello.greating в качестве строки. Существует совокупность других функций для получения целочисленных значений, значений с плавающей точкой, булевых значений перечисленных ниже. В дополнение эти функции имеют дубликаты с суффиксом ORIG, позволяющие получить значение записи в том виде, в котором оно записано в файле php.ini (до того, как могло быть изменено посредствам файла .htaccess или функции ini_set).
Текущее значение | Оригинальное значение | Тип |
---|---|---|
INI_STR(name) | INI_ORIG_STR(name) | Char * (NULL terminated) |
INI_INT(name) | INI_ORIG_INT(name) | signed long |
INI_FLT(name) | INI_ORIG_FLT(name) | signed double |
INI_BOOL(name) | INI_ORIG_BOOL(name) | Zend_bool |
Перейдём к функции PHP_INI_ENTRY. Первым параметром ей передаётся строка, содержащая имя интересующей вас записи в файле php.ini. Для того, что бы избежать коллизий между именами записей в php.ini, вам следует использовать те же соглашения, что при наименовании функций: имя должно начинаться с префикса, совпадающего с именем расширения. Так же соглашение предусматривает, что разделителем имени расширения от оригинального имени настройки в INI-файлах должна служить точка. В данном случае имя настройки будет выглядеть как hello.greeting.
Вторым параметром является начальное значение настройки, которое всегда задаётся как char* вне зависимости от того, является ли значение числом или нет. Это является следствием того факта, что все настройки в INI-файлах по своей сути текстовые, так как сам файл текстовый. Только последующее использование в скрипте макросов INI_INT, INI_FLT или INI_BOOL вызывает преобразование их типов.
Третьим параметром является модификатор уровня доступа. Это битовая маска, которая определяет когда и как данная INI-настройка может быть модифицирована. Для некоторых настроек, таких как register_globals, просто-напросто не имеет смысла позволять изменение значения внутри скрипта с помощью функции ini_set, так как данная настройка имеет смысл только во время подготовки обработки запроса – до того как скрипту дана возможность отработать. Другие, такие как allow_url_fopen, являются административными настройками, которые пользователи не должны иметь права изменять ни через ini_set, ни через директивы .htaccess. По-умолчанию значением для этого параметра является значение PHP_INI_ALL, указывающее, что значение настройки может меняться где угодно. Также возможны значения PHP_INI_SYSTEM|PHP_INI_PERDIR, указывающие, что значение настройки может быть изменено через php.ini или директиву в файле .htaccess, но через функцию ini_set(). Или же возможно значение PHP_INI_SYSTEM, означающее, что настройку можно изменить только через файл php.ini и нигде больше.
Последний четвёртый параметр позволяет указать функцию обратного вызова (callback), вызываемую при изменении настройки с помощью функции ini_set. Это позволяет расширениям производить более полный контроль над условиями изменения настройки или вызывать соответствующую функцию в зависимости от нового значения настройки.
Глобальные переменные
Довольно часто расширению требуется обработать переменную в отдельном запросе, сохраняя её значение независимым от других запросов, которые могут обрабатываться в тоже самое время. В немногопоточном SAPI это можно сделать очень просто: всего лишь объявите глобальную переменную в файле с исходным кодом и обращайтесь к ней, когда вам нужно. Проблема в том, что, так как PHP спроектирован для работы с многопоточными web-серверами (такими как Apache 2 и ISS), ему необходимо хранить глобальные переменные, используемые одним потоком, отдельно от глобальных переменных другого. PHP значительно упрощает эту задачу благодаря использованию слоя абстракции TSRM (Thread Safe Resource Manager) иногда называемого ZTS (Zend Thread Safety). Фактически в данной статье уже использовались части TSRM, чего вы даже не заметили. (Не пытайтесь найти их, так как пока для вас это слишком сложно).
Первая часть создания потокобезопасной глобальной переменной, как и любой другой глобальной переменной, заключается в её объявлении. В качестве примера мы объявим одну глобальную переменную типа long, начальным значением которой будет 0. Каждый раз, когда функция hello_long будет вызываться, мы будем увеличивать значение глобальной переменной и возвращать её значение.
Добавьте следующий фрагмент кода в файл php_hello.h сразу после строки #define PHP_HELLO_H:
ZEND_BEGIN_MODULE_GLOBALS(hello)
long counter;
ZEND_END_MODULE_GLOBALS(hello)
#ifdef ZTS
#define HELLO_G(v) TSRMG(hello_globals_id, zend_hello_globals *, v)
#else
#define HELLO_G(v) (hello_globals.v)
#endif
PHP_MINIT_FUNCTION(hello);
PHP_MSHUTDOWN_FUNCTION(hello);
PHP_RINIT_FUNCTION(hello);
#include «php.h»
#include «php_ini.h»
#include «php_hello.h»
zend_module_entry hello_module_entry = <
#if ZEND_MODULE_API_NO >= 20010901
STANDARD_MODULE_HEADER,
#endif
PHP_HELLO_WORLD_EXTNAME,
hello_functions,
PHP_MINIT(hello),
PHP_MSHUTDOWN(hello),
PHP_RINIT(hello),
NULL,
NULL,
#if ZEND_MODULE_API_NO >= 20010901
PHP_HELLO_WORLD_VERSION,
#endif
STANDARD_MODULE_PROPERTIES
>;
И измените вашу функцию MINIT вместе с добавлением ещё нескольких функций для обработки инициализации при старте запроса:
static void php_hello_init_globals(zend_hello_globals *hello_globals)
<
>
PHP_RINIT_FUNCTION(hello)
<
HELLO_G(counter) = 0;
PHP_MINIT_FUNCTION(hello)
<
ZEND_INIT_MODULE_GLOBALS(hello, php_hello_init_globals, NULL);
В изменениях, которые вы внесли в файл php_hello.h, вы использовали пару макросов – ZEND_BEGIN_MODULE_GLOBALS и ZEND_END_MODULE_GLOBALS. С их помощью была определена структура zend_hello_globals, содержащая одну переменную типа long. После этого был определён макрос HELLO_G, позволяющий в зависимости от режима компиляции (с учётом или без многопоточности) получать значение из пула потоков или просто взять его из глобальной области видимости.
В файле hello.c с помощью макроса ZEND_DECLARE_MODULE_GLBALS вы объявили структуру zend_hello_globals либо как глобальную (при непотокобезопасной сборке), либо как член пула ресурсов потока. Нам как авторам расширения не нужно задумываться об этом механизме, так как всю работу берёт на себя ZE. И, наконец, вы используете функцию ZEND_INIT_MODULE_GLOBALS для выделения идентификатора потокобезопасного ресурса.
Возможно, вы заметили, что функция php_hello_init_globals на самом деле ничего не делает. Кроме того, инициализация счётчика значением 0 оказалась в функции RINIT. Почему?
Ключ к ответу на этот вопрос кроется в моменте, когда эти функции вызываются. Функция php_hello_init_globals вызывается только при запуске нового процесса или потока. Однако, каждый процесс может обслуживать более одного запроса, так что использование этой функции для инициализации нашего счётчика значением 0 будет верно только для первого запроса. Последующий запрос к этому же процессу по прежнему будет работать со старым значением счётчика и, следовательно, не будет начинать отчёт с нуля. Для инициализации счётчика значением 0 для каждого запроса мы использовали функция RINIT, которая, как вы уже читали, вызывается перед каждым запросом обработки скрипта. Мы включили функцию php_hello_init_globals в наш код как минимум из-за того, что передача NULL в качестве соответствующего параметра ZEND_INIT_MODULE_GLOBALS функции init приведёт к ошибке сегментации для платформ без поддержки потоков.
INI-настройки и глобальные переменные
ZEND_BEGIN_MODULE_GLOBALS(hello)
long counter;
zend_bool direction;
ZEND_END_MODULE_GLOBALS(hello)
PHP_INI_BEGIN()
PHP_INI_ENTRY( «hello.greeting» , «Hello World» , PHP_INI_ALL, NULL)
STD_PHP_INI_ENTRY( «hello.direction» , «1» , PHP_INI_ALL, OnUpdateBool, direction, zend_hello_globals, hello_globals)
PHP_INI_END()
static void php_hello_init_globals(zend_hello_globals *hello_globals)
<
hello_globals->direction = 1;
>
PHP_FUNCTION(hello_long)
<
if (HELLO_G(direction)) <
HELLO_G(counter)++;
> else <
HELLO_G(counter)—;
>
Вот и всё! С помощью метода OnUpdateBool (метод является часть ZE), переданного в качестве третьего параметра макроса STD_PHP_INI_ENTRY, будет производиться автоматическое приведение типа значения настройки, полученного из файла php.ini, .htaccess, или с помощью функции ini_set, к соответствующему значению TRUE/FALSE, которое вы можете получить прямо внутри скрипта. Последние три параметра функции STD_PHP_INI_ENTRY указывают PHP, какую глобальную переменную изменить, как выглядит структура глобальных переменных нашего расширения, и имя контейнера глобальных переменных, в котором они содержаться.
Что дальше?
В этой статье мы разобрали структуру простого PHP расширения, которое экспортирует функции, возвращающие значения, загружает и обрабатывает INI-настройки, сохраняя их на протяжении обработки запроса.
В следующей статье мы рассмотрим внутреннюю структуру переменных PHP, способы их хранения и обработки. Поработаем с функцией zend_parse_parameters, используемой для получения параметров из программы при вызове функции, и откроем способы возвращения более сложных результатов из функции, таких как массив, объект и ресурс.
В некоторых местах перевод имеет довольно вольный формат, что является следствием трудностей перевода, которые мне не удалось преодолеть в полной мере. Кроме того перевод содержит некоторое количество мелких дополнений, показавшихся мне актуальными.
Процесс сборки расширения согласно статье проверен на исходниках PHP 5.3.2.
У автора есть несколько статей по данной тематике: 1, 2.1, 2.2, 3. Четвёртая статья видимо так и не увидит свет ввиду публикации автором книги «Extending and Embedding PHP», посвящённой данной тематике.