@@ -10,7 +10,7 @@ sequence of its module which PHP is one. Starting up, is called internally **the
10
10
abbreviate it as the **MINIT ** step.
11
11
12
12
Once started, PHP waits to treat one/several requests. When we talk about PHP CLI, there will be only one request: the
13
- current script to run. However, when we talk about a web environment- should it be thought as PHP-FPM or webserver
13
+ current script to run. However, when we talk about a web environment- should it be PHP-FPM or webserver
14
14
module- PHP could serve several requests one after the other. It all depends on how you did configure you webserver:
15
15
you may tell it to serve an infinite number of requests, or a specific number before shutting down and recycling the
16
16
process. Everytime a new request shows in to be treated, PHP will run **a request startup step **. We call it the
@@ -41,9 +41,11 @@ To treat several requests at the same time, you need to run a parallelism model.
41
41
42
42
Using the process-based model, every PHP interpreter is isolated by the OS into its own process.
43
43
This model is very common under Unix. Every request leaves into its own process.
44
+ This model is used by PHP-CLI, PHP-FPM and PHP-CGI.
44
45
45
46
With the thread-based model, every PHP interpreter is isolated into a thread, using a thread library.
46
- This model is mainly used under Microsoft Windows OS, but can be used with most Unixes as well.
47
+ This model is mainly used under Microsoft Windows OS, but can be used with most Unixes as well. This requires PHP and
48
+ its extensions :doc: `to be built <../build_system/building_extensions >` in ZTS mode.
47
49
48
50
Here is the process-based model:
49
51
@@ -55,14 +57,18 @@ And here is the thread-based model:
55
57
.. image :: ./images/php_lifetime_thread.png
56
58
:align: center
57
59
60
+ .. note :: PHP's multi-processing module is not of your choice, as an extension developer. You will have to support it.
61
+ You will have to support the fact that your extension could run in a threaded environment, especially under
62
+ Windows platforms, and you'll have to program against it.
63
+
58
64
The PHP extensions hooks
59
65
************************
60
66
61
- As you could have guessed, the PHP engine will trigger your extension at several lifetime points. We call those hook
62
- functions. Your extension may declare interest into specific lifetime points by declaring function hooks while it
67
+ As you could have guessed, the PHP engine will trigger your extension at several lifetime points. We call those * hook
68
+ functions * . Your extension may declare interest into specific lifetime points by declaring function hooks while it
63
69
registers against the engine.
64
70
65
- Those hooks can clearly be felt once you analyze a PHP extension structure, the ``zend_module_entry `` structure::
71
+ Those hooks can clearly be noticed once you analyze a PHP extension structure, the ``zend_module_entry `` structure::
66
72
67
73
struct _zend_module_entry {
68
74
unsigned short size;
@@ -102,11 +108,12 @@ Module initialization: MINIT()
102
108
103
109
This is PHP process startup step. In your extension's ``MINIT() ``, you'll load and allocate any persistent object or
104
110
piece of information you'll need for every future request.
111
+ For the big part of them, those allocations will target read-only objects.
105
112
106
- In ``MINIT() ``, no thread has popped yet, so you may access global variables trully, with no protection at all. Also,
107
- you must not allocate memory that is request-bound, as a request has not started yet.
108
- You never use Zend Memory Manager allocations in ``MINIT() `` steps, but persistent allocations. No `` emalloc() ``, but
109
- ``pemalloc() ``. Failing to do that will lead to crashes.
113
+ In ``MINIT() ``, no thread or process has popped yet, so you may fully access global variables with no protection at
114
+ all. Also, you must not allocate memory that is request-bound, as a request has not started yet.
115
+ You never use :doc: ` Zend Memory Manager < ../memory_management/zend_memory_manager >` allocations in ``MINIT() `` steps,
116
+ but persistent allocations. No `` emalloc() ``, but ``pemalloc() ``. Failing to do that will lead to crashes.
110
117
111
118
At ``MINIT() ``, the execution engine is not started yet, so beware of not trying to access any of its structure without
112
119
special care.
@@ -115,7 +122,12 @@ If you need to register INI entries for your extension, ``MINIT()`` is the right
115
122
116
123
If you need to register read-only ``zend_strings `` for further usage, it is time to do so here (with persistent alloc).
117
124
118
- .. note :: Remember that ``MINIT()`` is called only once in PHP process lifetime: when this later starts.
125
+ If you need to allocate objects that well be written to while serving a request, then you'll need to duplicate their
126
+ memory allocation to a thread-specific pool for the request. Remember that you can only write safely to global space
127
+ while into ``MINIT() ``.
128
+
129
+ .. note :: Memory management, allocations, and debugging; are part of the :doc:`memory management<../memory_management>`
130
+ chapter.
119
131
120
132
``MINIT() `` is triggered by ``zend_startup_modules() `` in
121
133
`php_module_startup() <https://github.com/php/php-src/blob/3704947696fe0ee93e025fa85621d297ac7a1e4d/main/main.c#L2009 >`_
@@ -130,8 +142,9 @@ This is PHP process shutdown step. Easy enough, you basically perform here the e
130
142
Take care again here: the execution engine is shut down, so you should not access any of its variable (but you should
131
143
not need to here).
132
144
133
- As you don't live in a request here, you should not free resources using Zend Memory Manager ``efree() `` or alikes, but
134
- free for persistent allocations, aka ``pefree() ``.
145
+ As you don't live in a request here, you should not free resources using
146
+ :doc: `Zend Memory Manager <../memory_management/zend_memory_manager >` ``efree() `` or alikes, but free for persistent
147
+ allocations, aka ``pefree() ``.
135
148
136
149
``MSHUTDOWN() `` is triggered by ``zend_destroy_modules() `` from ``zend_shutdown() `` in
137
150
`php_module_shutdown() <https://github.com/php/php-src/blob/3704947696fe0ee93e025fa85621d297ac7a1e4d/main/main.c#L2335 >`_
@@ -141,7 +154,8 @@ Request initialization: RINIT()
141
154
-------------------------------
142
155
143
156
A request just showed in, and PHP is about to treat it here. In ``RINIT() ``, you bootstrap the resources you need to
144
- treat that precise request. PHP is a share-nothing architecture, and as-is, it provides memory management facilities.
157
+ treat that precise request. PHP is a share-nothing architecture, and as-is, it provides
158
+ :doc: `memory management <../memory_management >` facilities.
145
159
146
160
In ``RINIT() ``, if you need to allocate dynamic memory, you'll use Zend Memory Manager. You will call for ``emalloc() ``.
147
161
Zend Memory Manager tracks the memory you allocate through it, and when the request shuts down, it will attempt to free
@@ -151,6 +165,13 @@ You should not require persistent dynamic memory here, aka libc's ``malloc()`` o
151
165
persistent memory here, and forgets to free it, you'll create leaks that will stack as PHP treats more and more
152
166
requests, to finally crash the process (Kernel OOM) and starve the machine memory.
153
167
168
+ Also, take really care not to write to global space here. If PHP is run into a thread as chosen parallelism model, then
169
+ you'll modify the context for every thread of the pool (every other request treated in parallel to yours) and you could
170
+ also trigger race conditions if you don't lock the memory.
171
+ If you need globals, you'll need to protect them.
172
+
173
+ .. note :: Global scope management is explained into :doc:`a dedicated chapter <globals_management>`.
174
+
154
175
``RINIT() `` is triggered by ``zend_activate_module() `` in
155
176
`php_request_startup() <https://github.com/php/php-src/blob/3704947696fe0ee93e025fa85621d297ac7a1e4d/main/main.c#L1558 >`_
156
177
function.
@@ -205,7 +226,7 @@ Globals management will be covered in its dedicated chapter.
205
226
Remember that globals are not cleared after every request. If you need to reset them for every new request (likely),
206
227
then you need to put such a procedure into ``RINIT() ``.
207
228
208
- .. todo: Add a chapter about globals management and ZTS
229
+ .. note :: Global scope management is explained into :doc:`a dedicated chapter <globals_management>`.
209
230
210
231
Globals termination: GSHUTDOWN()
211
232
--------------------------------
@@ -220,7 +241,7 @@ Globals management will be covered in its dedicated chapter.
220
241
221
242
Remember that globals are not cleared after every request; aka ``GSHUTDOWN() `` is not called as part of ``RSHUTDOWN() ``.
222
243
223
- .. todo: Add a chapter about globals management and ZTS
244
+ .. note :: Global scope management is explained into :doc:`a dedicated chapter <globals_management>`.
224
245
225
246
Thoughts on PHP lifecycle
226
247
-------------------------
@@ -245,3 +266,54 @@ threads as multi-processing engine, then you crash every other thread with you,
245
266
Hooking by overwritting function pointers
246
267
*****************************************
247
268
269
+ Now you know when the engine will trigger your code, there exists also noticeable function pointers you may replace to
270
+ hook into the engine.
271
+ As those pointers are global variables, you may replace them into ``MINIT() `` step, and put them back into
272
+ ``MSHUTDOWN() ``.
273
+
274
+ Those of interest are:
275
+
276
+ .. +---------+-----------------+---------------------------------------------------------------------------+
277
+ .. | Subject | Definition file | function |
278
+ .. +---------+-----------------+---------------------------------------------------------------------------+
279
+ .. | Error | Zend/zend.h | `void (*zend_error_cb)(int type, const char *error_filename, |
280
+ .. | | | const uint error_lineno, const char *format, va_list args)` |
281
+ .. +---------+-----------------+---------------------------------------------------------------------------+
282
+ .. | | | |
283
+ .. +---------+-----------------+---------------------------------------------------------------------------+
284
+
285
+ * AST, Zend/zend_ast.h:
286
+ * `void (*zend_ast_process_t)(zend_ast *ast) `
287
+
288
+ * Compiler, Zend/zend_compile.h:
289
+ * `zend_op_array *(*zend_compile_file)(zend_file_handle *file_handle, int type) `
290
+ * `zend_op_array *(*zend_compile_string)(zval *source_string, char *filename) `
291
+
292
+ * Executor, Zend/zend_execute.h:
293
+ * `void (*zend_execute_ex)(zend_execute_data *execute_data) `
294
+ * `void (*zend_execute_internal)(zend_execute_data *execute_data, zval *return_value) `
295
+
296
+ * GC, Zend/zend_gc.h:
297
+ * `int (*gc_collect_cycles)(void) `
298
+
299
+ * TSRM, TSRM/TSRM.h:
300
+ * `void (*tsrm_thread_begin_func_t)(THREAD_T thread_id) `
301
+ * `void (*tsrm_thread_end_func_t)(THREAD_T thread_id) `
302
+
303
+ * Error, Zend/zend.h:
304
+ * `void (*zend_error_cb)(int type, const char *error_filename, const uint error_lineno, const char *format,
305
+ va_list args) `
306
+
307
+ * Exceptions, Zend/zend_exceptions.h:
308
+ * `void (*zend_throw_exception_hook)(zval *ex) `
309
+
310
+ * Lifetime, Zend/zend.h:
311
+ * `void (*zend_on_timeout)(int seconds) `
312
+ * `void (*zend_interrupt_function)(zend_execute_data *execute_data) `
313
+ * `void (*zend_ticks_function)(int ticks) `
314
+
315
+ Other exists but the above ones are the most important ones you could need while designing PHP extensions.
316
+ As their names are self explanatory, there is no need to detail every of them.
317
+
318
+ If you need some more informations, you can look for them into PHP source code, and discover when and how they get
319
+ triggered.
0 commit comments