Skip to content

Commit c0750e4

Browse files
author
Julien Pauli
committed
extensions MINFO() additions
1 parent 4eacbdb commit c0750e4

File tree

4 files changed

+118
-11
lines changed

4 files changed

+118
-11
lines changed

Book/php7/extensions_design.rst

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@ Contents:
1313

1414
extensions_design/php_lifecycle.rst
1515
extensions_design/extension_skeleton.rst
16-
extensions_design/zend_extensions.rst
16+
extensions_design/php_functions.rst
1717
extensions_design/globals_management.rst
18+
extensions_design/extension_infos.rst
1819
extensions_design/ini_settings.rst
19-
extensions_design/php_functions.rst
20+
extensions_design/zend_extensions.rst
21+
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
Publishing extension informations
2+
=================================
3+
4+
Extensions can publish informations asked by ``phpinfo()`` or the Reflection API. Let's see that together.
5+
6+
This chapter won't be too long as there is really no difficulty.
7+
8+
MINFO() hook
9+
------------
10+
11+
Everything takes place in the ``MINFO()`` hook you declared, if you declared one. If you declared none, then the engine
12+
will run a default function to print informations about your extension. That function will only print the version of
13+
your extension and the :doc:`INI entries <ini_settings>` you eventually declared. If you want to hook into such
14+
process, you must declare an ``MINFO()`` :doc:`hook <php_lifecycle>` in your extension structure.
15+
16+
.. note:: Everything takes place in `ext/standard/info.c <https://github.com/php/php-src/blob/
17+
ce64b82ebb2ac87e53cb85c312eafc8c5c37b112/ext/standard/info.c>`_ , you may read that file. Printing
18+
information about PHP extensions is done by the engine by calling `php_info_print_module()
19+
<https://github.com/php/php-src/blob/ce64b82ebb2ac87e53cb85c312eafc8c5c37b112/ext/standard/info.c#L139>`_
20+
21+
Here is a simple ``MINFO()`` example::
22+
23+
#include "php/main/SAPI.h"
24+
#include "ext/standard/info.h"
25+
26+
PHP_MINFO_FUNCTION(pib)
27+
{
28+
time_t t;
29+
char cur_time[32];
30+
31+
time(&t);
32+
php_asctime_r(localtime(&t), cur_time);
33+
34+
php_info_print_table_start();
35+
php_info_print_table_colspan_header(2, "PHPInternalsBook");
36+
php_info_print_table_row(2, "Current time", cur_time);
37+
php_info_print_table_end();
38+
39+
php_info_print_box_start(0);
40+
if (!sapi_module.phpinfo_as_text) {
41+
php_write(PIB_HTML, strlen(PIB_HTML));
42+
} else {
43+
php_write(PIB_TXT, strlen(PIB_TXT));
44+
}
45+
php_info_print_box_end();
46+
}
47+
48+
zend_module_entry pib_module_entry = {
49+
STANDARD_MODULE_HEADER,
50+
"pib",
51+
NULL, /* Function entries */
52+
NULL, /* Module init */
53+
NULL, /* Module shutdown */
54+
NULL, /* Request init */
55+
NULL, /* Request shutdown */
56+
PHP_MINFO(pib), /* Module information */
57+
"0.1", /* Replace with version number for your extension */
58+
STANDARD_MODULE_PROPERTIES
59+
};
60+
61+
.. image:: ./images/php_minfo.png
62+
:align: center
63+
64+
What you basically have to do is to deal with ``php_info_print_*()`` API, that allows to print into the output stream
65+
that is generated. If you want to print some raw informations, a simple ``php_write()`` is enough. ``php_write()`` just
66+
writes what you pass as argument onto the SAPI output stream, whereas ``php_info_print_*()`` API does as well, but
67+
before formats the content using HTML *table-tr-td* tags if the output is expected to be HTML, or simple spaces if not.
68+
69+
Like you can see, you need to include *ext/standard/info.h* to access the ``php_info_print_*()`` API, and you will need
70+
*php/main/SAPI.h* to access the ``sapi_module`` symbol. That symbol is global, it represents the current *SAPI* used by
71+
the PHP process. The ``phpinfo_as_text`` field inform if you are going to write in a "Web" SAPI like *php-fpm* f.e, or
72+
in a "text" one, like *php-cli*.
73+
74+
What will trigger your ``MINFO()`` hook are :
75+
76+
* Calls to userland ``phpinfo()`` function
77+
* ``php -i``, ``php-cgi -i``, ``php-fpm -i``. More generaly ``<SAPI_binary> - i``
78+
* ``php --ri`` or userland ``ReflectionExtension::info()``
79+
80+
.. note:: Take care of the output formating. Probe for ``sapi_module.phpinfo_as_text`` if you need to change between
81+
text and HTML formatting. You don't know how your extensions' infos will be called by userland.
82+
83+
If you need to display your INI settings, just call for the ``DISPLAY_INI_ENTRIES()`` macro into your ``MINFO()``. This
84+
macro resolves to `display_ini_entries()
85+
<https://github.com/php/php-src/blob/4903f044d3a65de5b1c457d9eb618c9e247f7086/main/php_ini.c#L167>`_.
86+
87+
A note about the Reflection API
88+
-------------------------------
89+
90+
The Reflection heavilly uses your ``zend_module_entry`` structure. For example, when you call
91+
``ReflectionExtension::getVersion()``, the API just reads the version field of your ``zend_module_entry`` structure.
92+
93+
Same to discover functions, your ``zend_module_entry`` has got a ``const struct _zend_function_entry *functions`` member
94+
which is used to register PHP functions.
95+
96+
Basically, the PHP userland Reflection API just reads your ``zend_module_entry`` structure and publishes those
97+
informations. It may also use your ``module_number`` to gather back informations your extension registered at different
98+
locations against the engine. For example, ``ReflectionExtension::getINIentries()`` or
99+
``ReflectionExtension::getClasses()`` use this.

Book/php7/extensions_design/globals_management.rst

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ Let's zero our random value::
198198
PHP_MODULE_GLOBALS(pib),
199199
PHP_GINIT(pib),
200200
PHP_GSHUTDOWN(pib),
201-
NULL, /* MINFO() */
201+
NULL, /* PRSHUTDOWN() */
202202
STANDARD_MODULE_PROPERTIES_EX
203203
};
204204

@@ -367,15 +367,20 @@ Here is the patched example introducing true globals, we just show the diff abou
367367
static zend_string *more, *less;
368368
static zend_ulong max = 100;
369369

370+
static void register_persistent_string(char *str, zend_string **result)
371+
{
372+
*result = zend_string_init(str, strlen(str), 1);
373+
zend_string_hash_val(*result);
374+
375+
GC_FLAGS(*result) |= IS_INTERNED;
376+
}
377+
370378
PHP_MINIT_FUNCTION(pib)
371379
{
372380
char *pib_max;
373381

374-
more = zend_string_init("more", strlen("more"), 1);
375-
less = zend_string_init("less", strlen("less"), 1);
376-
377-
GC_FLAGS(more) |= IS_STR_INTERNED;
378-
GC_FLAGS(less) |= IS_STR_INTERNED;
382+
register_persistent_string("more", &more);
383+
register_persistent_string("less", &less);
379384

380385
if (pib_max = getenv("PIB_RAND_MAX")) {
381386
if (!strchr(pib_max, '-')) {
@@ -421,9 +426,10 @@ What happened here is that we created two :doc:`zend_string <../internal_types/s
421426
and ``less``. Those strings don't need to be created and destroyed anytime they are used like it was done before. Those
422427
are immutable strings that can be allocated once and reused anytime needed, as soon as they stay immutable
423428
(aka : read-only). We initialize those two strings in ``MINIT()`` using a persistent allocation in
424-
``zend_string_init()``, and we tell the zval garbage collector those strings are interned so that it will never ever
425-
try to destroy them (however it could need to copy them if they were used as part of a write operation, such as a
426-
concatenation). Obviously we don't forget to destroy such strings in ``MSHUTDOWN()``.
429+
``zend_string_init()``, we precompute their hash now (instead of having the very first request doing it), and we tell
430+
the zval garbage collector those strings are interned so that it will never ever try to destroy them (however it could
431+
need to copy them if they were used as part of a write operation, such as a concatenation). Obviously we don't forget
432+
to destroy such strings in ``MSHUTDOWN()``.
427433

428434
Then in ``MINIT()`` we probe for a ``PIB_RAND_MAX`` environment and use it as the maximum range value for our random
429435
number pick. As we use an unsigned integer and we know ``strtoull()`` won't complain about negative numbers (and thus
7.38 KB
Loading

0 commit comments

Comments
 (0)