Skip to content

Commit 423c1bc

Browse files
author
Julien Pauli
committed
Zend extensions are in now
1 parent 3592382 commit 423c1bc

File tree

2 files changed

+185
-0
lines changed

2 files changed

+185
-0
lines changed
200 KB
Loading

Book/php7/extensions_design/zend_extensions.rst

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,194 @@ Zend Extensions
22
===============
33

44
PHP knows two kinds of extensions :
5+
56
* The PHP extensions, the most commonly used
67
* The Zend extensions, more uncommon, allows other hooks
78

89
This chapter will detail what are the main differences between Zend extensions and PHP extensions, when you should one
910
instead of the other, and how to build hybrid extensions, aka extensions being both PHP and Zend at the same time (and
1011
why do that)
12+
13+
On differences between PHP and Zend extensions
14+
**********************************************
15+
16+
Just to say. Into PHP's source code, PHP extensions are named as **"PHP modules"**, whereas Zend extensions are called
17+
**"Zend extensions"**.
18+
19+
So into PHP's heart, if you read the "extension" keyword, you should first think about a Zend extension. And if you
20+
read the "module" keyword, you may think about a PHP extension.
21+
22+
In traditionnal life, we talk about *"PHP extensions"* versus *"Zend extensions"*.
23+
24+
The thing that differentiate them is the way they are loaded :
25+
26+
* PHP extensions (aka PHP "modules") are loaded in INI files as a *"extension=pib.so"* line
27+
* Zend extensions are loaded in INI files as a *"zend_extension=pib.so"* line
28+
29+
That's the only visible difference we see from PHP userland.
30+
31+
But that's a different story from internal point of view.
32+
33+
What is a Zend extension ?
34+
**************************
35+
36+
First of all, Zend extensions are compiled and loaded the same way as PHP extensions. Thus, if you haven't yet read the
37+
:doc:`building PHP extensions <../build_system/building_extensions>` chapter, you should have a look as it is valid
38+
also for Zend extensions.
39+
40+
.. note:: If not done, :doc:`get some informations about PHP extensions <../extensions_design>` as we will compare
41+
against them here.
42+
43+
Here is a Zend extension. Note that you need to publish not one but two structures for the engine to load your Zend
44+
extension::
45+
46+
/* Main Zend extension structure */
47+
struct _zend_extension {
48+
char *name; /*
49+
char *version; * Some infos
50+
char *author; *
51+
char *URL; *
52+
char *copyright; */
53+
54+
startup_func_t startup; /*
55+
shutdown_func_t shutdown; * Specific branching lifetime points
56+
activate_func_t activate; * ( Hooks )
57+
deactivate_func_t deactivate; */
58+
59+
message_handler_func_t message_handler; /* Hook called on zend_extension registration */
60+
61+
op_array_handler_func_t op_array_handler; /* Hook called just after Zend compilation */
62+
63+
statement_handler_func_t statement_handler; /*
64+
fcall_begin_handler_func_t fcall_begin_handler; * Hooks called through the Zend VM as specific OPCodes
65+
fcall_end_handler_func_t fcall_end_handler; */
66+
67+
op_array_ctor_func_t op_array_ctor; /* Hook called on OPArray construction */
68+
op_array_dtor_func_t op_array_dtor; /* Hook called on OPArray destruction */
69+
70+
int (*api_no_check)(int api_no); /* Checks against zend_extension incompatibilities
71+
int (*build_id_check)(const char* build_id); */
72+
73+
op_array_persist_calc_func_t op_array_persist_calc; /* Hooks called if the zend_extension extended the
74+
op_array_persist_func_t op_array_persist; * OPArray structure and has some SHM data to declare
75+
*/
76+
77+
void *reserved5; /*
78+
void *reserved6; * Do what you want with those free pointers
79+
void *reserved7; *
80+
void *reserved8; */
81+
82+
DL_HANDLE handle; /* dlopen() returned handle */
83+
int resource_number; /* internal number used to manage that extension */
84+
};
85+
86+
/* Structure used when the Zend extension get loaded into the engine */
87+
typedef struct _zend_extension_version_info {
88+
int zend_extension_api_no;
89+
char *build_id;
90+
} zend_extension_version_info;
91+
92+
.. note:: As always, read the source. Zend extensions are managed into
93+
`Zend/zend_extension.c <https://github.com/php/php-src/blob/57dba0e2f5e39f6b05031317048e39d463243cc3/Zend/
94+
zend_extensions.c>`_ (and .h)
95+
96+
Like you can notice, Zend extensions are more complex than PHP extensions, as they got more hooks, and those are much
97+
closer to the Zend engine and its Virtual Machine (The most complex parts of the whole PHP source code).
98+
99+
Let us warn you : until you have very advanced knowledge on PHP internal's Vritual Machine, and until you need to hook
100+
deep into it, you shouldn't need a Zend extension, but a PHP extension will be enough.
101+
102+
Today's most commonly known Zend extensions into PHP's world are OPCache, XDebug, phpdbg and Blackfire. But you know
103+
dozens of PHP extensions next to that don't you ?! That's a clear sign that :
104+
105+
* You should not need a Zend extension for a very big part of your problematics
106+
* Zend extensions can also be used as PHP extensions (more on that later)
107+
* A PHP extension still can do a lot of things.
108+
109+
.. note:: There is no :doc:`skeleton generator <extension_skeleton>` for Zend extensions, like for PHP extensions.
110+
111+
.. warning:: With Zend extensions, no generator, no help. Zend extensions are reserved to advanced programmers, they
112+
are more complex to understand, they got deeper-engine behaviors and usually require an advanced knowledge
113+
of PHP's internal machinery.
114+
115+
API versions and conflicts management
116+
*************************************
117+
118+
You know that PHP extensions check against several rules before loading, to know if they are compatible with the PHP
119+
version you try to load them on. This has been detailed into
120+
:doc:`the chapter about building PHP extensions <../build_system/building_extensions>`.
121+
122+
For Zend extension, the same rules apply, but a little bit differently : Instead of the engine trashing you away in
123+
case of mismatch in numbers, it will use the ``zend_extension_version_info`` structure you published to know what to do.
124+
125+
The ``ZEND_EXTENSION_API_NO`` is checked when your Zend extension is loaded. But the difference is that if this number
126+
doesn't match your Zend extension's, you still have a chance to get loaded. The engine will call for your
127+
``api_no_check()``hook, if you declared one, and will pass it the ``ZEND_EXTENSION_API_NO``. Here, you must tell if you
128+
support that API number, or not.
129+
130+
The same applies to the other ABI settings, such as ``ZEND_DEBUG``, or ``ZTS``. Where PHP extensions will refuse to
131+
load if there is a mismatch, Zend extensions are given a chance to load as the engine checks against
132+
``build_id_check()`` hook and pass it the ``ZEND_EXTENSION_BUILD_ID``. Here again, you say if you are compatible or not.
133+
134+
Those abilities to force things against the engine are rarely used in practice.
135+
136+
.. note:: You see how more complex Zend extensions are compared to PHP extensions ? The engine is less restrictive, and
137+
it suppose that you know what you do, for the best or the worst.
138+
139+
.. warning:: Zend extensions should really be developped by experienced and advanced programmers, as the engine is
140+
weaker about its checks. It clearly supposes that you master what you do.
141+
142+
To sum things up about API compatibility, well, every step is detailed in
143+
`zend_load_extension() <https://github.com/php/php-src/blob/57dba0e2f5e39f6b05031317048e39d463243cc3/Zend/
144+
zend_extensions.c#L67>`_.
145+
146+
Then comes the problem of Zend extension conflicts. One may be incompatible with an other, and to master that, every
147+
Zend extension has got a hook called ``message_handler``. If declared, this hook is triggered on every already loaded
148+
extension when another Zend extension gets loaded. You are passed a pointer to its ``zend_extension`` structure, and you
149+
may then detect which one it is, and abort if you think you'll confict with it. This is something rarely used.
150+
151+
Zend extensions lifetime hooks
152+
******************************
153+
154+
If you remember about :doc:`the PHP lifecycle <php_lifecycle>` (you should read the dedicated chapter), well, Zend
155+
extensions plug into that lifecycle this way:
156+
157+
.. image:: ./images/php_extensions_lifecycle_full.png
158+
:align: center
159+
160+
We can notice that our ``api_no_check()``, ``build_id_check()`` and ``message_handler()`` check hooks are only triggered
161+
when PHP starts up. Those later three hooks are detailed in the preceding part (above).
162+
163+
Then the **important** thing to remember :
164+
165+
* ``MINIT()`` is triggered on PHP extensions **before** Zend extensions (``startup()``).
166+
* ``RINIT()`` is triggered of Zend extensions (``activate()``) **before** PHP extensions.
167+
* Zend extensions request shutdown procedure (``deactivate()``) is called **in between** ``RSHUTDOWN()`` and
168+
``PRSHUTDOWN()`` for PHP extensions.
169+
* ``MSHUTDOWN()`` is called on PHP extensions **first**, then on Zend extensions **after** (``shutdown()``).
170+
171+
.. warning:: Like for every hook, there is a precise defined order and you must master it and remember it for complex
172+
use-case extensions.
173+
174+
In *practice*, what we can say about it is that :
175+
176+
* Zend extensions are started **after** PHP extensions. That allows Zend extensions to be sure that every PHP extension
177+
is already loaded when they start. They are then able to replace-and-hook into PHP extensions. For example, if you need
178+
to replace the ``session_start()`` function handler by yours, it will be easier to do so in a Zend extension. If you do
179+
it in a PHP extension, you must be sure you get loaded after the session extension, and that can be tricky to check and
180+
to master (You still can specify a dependency using a `zend_module_dep <https://github.com/php/php-src/blob/
181+
c18ba686cdf2d937475eb3d5c239e4ef8e733fa6/Zend/zend_modules.h#L118>`_).
182+
However, :doc:`remember <extension_skeleton>` that statically compiled extensions are always started before
183+
dynamically compiled ones. Thus, for the session use-case, this is not a problem as *ext/session* is loaded as static.
184+
Until some distributions (FreeBSD hear us) change that ...
185+
* Zend extensions are triggered before PHP extensions when a request shows in. That means they got a chance to modify
186+
the engine about the current request to come, so that PHP extensions use that modified context. OPCache uses such a
187+
trick so that it can perform its complex tasks before any extension had a chance to prevent it to.
188+
* Same for request shutdown : Zend extensions can assume every PHP extension has shut down the request.
189+
190+
My very first simple Zend extension
191+
***********************************
192+
193+
Here we'll detail some hook Zend extensions can use, and what to do with them, in some very simple scenario. Remember
194+
that Zend extension usually require that you master the Zend engine deeply, so here we'll have a simple starter that
195+
doesn't make such an assumption.

0 commit comments

Comments
 (0)