@@ -2,9 +2,194 @@ Zend Extensions
2
2
===============
3
3
4
4
PHP knows two kinds of extensions :
5
+
5
6
* The PHP extensions, the most commonly used
6
7
* The Zend extensions, more uncommon, allows other hooks
7
8
8
9
This chapter will detail what are the main differences between Zend extensions and PHP extensions, when you should one
9
10
instead of the other, and how to build hybrid extensions, aka extensions being both PHP and Zend at the same time (and
10
11
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