Skip to content

Commit 09c5cc2

Browse files
author
Julien Pauli
committed
Ini Settings fixes
1 parent 1a5babd commit 09c5cc2

File tree

1 file changed

+71
-49
lines changed

1 file changed

+71
-49
lines changed

Book/php7/extensions_design/ini_settings.rst

Lines changed: 71 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ the default value the extension designer gives to the API.
2828
is the one given by the extension developer, not the other way around.
2929

3030
The default value we are talking about here is called the **"master value"**. You may recall it from a ``phpinfo()``
31-
output, right ?:
31+
output, right ?
3232

3333
.. image:: ./images/php_extensions_ini.png
3434
:align: center
@@ -68,23 +68,27 @@ Into the engine, an INI setting is represented by a ``zend_ini_entry`` structure
6868
int module_number;
6969
};
7070

71-
Nothing really strong in such a structure. Setting's ``name`` and ``value`` are the most commonly used fields. Note
72-
however that the value is a string (as :doc:`zend_string <../internal_types/strings/zend_strings>`) and nothing else.
73-
Then, like we detailed in the introduction chapter above, we find the ``orig_value``, ``orig_modified``, ``modifiable``
74-
and ``modified`` fields which are related to the modification of the setting's value. The setting must keep in memory
75-
its original value (as "master value"). ``modifiable`` tells if the setting is actually modifable, and must have one of
76-
the values you should be aware of from PHP userland : ``ZEND_INI_USER``, ``ZEND_INI_PERDIR``, ``ZEND_INI_SYSTEM`` or
77-
``ZEND_INI_ALL``. ``modified`` is set to one anytime the setting has been modified during a request so that the engine
78-
knows at request shutdown that it must restore that INI setting value to its master value for the next request to treat.
79-
80-
Then come two handlers: ``on_modify()`` is called whenever the current setting's value is modified, like f.e using a call
81-
to ``ini_set()`` (but not only). We'll focus deeper on ``on_modify()`` later, but think of it as being a
82-
*validator function* (f.e if the setting is expected to represent an integer, you may validate the values you'll be
83-
given against integers). It also serve as a memory bridge to update global values, we'll get back on that later as well.
84-
85-
``diplayer()`` is less useful, and you usually don't pass any. ``displayer()`` is about how to display your setting.
86-
F.e, you may remember that PHP tends to display *On* for boolean values of *true*/*yes*/*on*/*1* etc. That's the
87-
``displayer()`` job : turn the current value into a "displayed" value.
71+
Nothing really strong in such a structure.
72+
73+
* Setting's ``name`` and ``value`` are the most commonly used fields. Note
74+
however that the value is a string (as :doc:`zend_string *<../internal_types/strings/zend_strings>`) and nothing else.
75+
* Then, like we detailed in the introduction chapter above, we find the ``orig_value``, ``orig_modified``, ``modifiable``
76+
and ``modified`` fields which are related to the modification of the setting's value. The setting must keep in memory
77+
its original value (as "master value").
78+
* ``modifiable`` tells if the setting is actually modifable, and must pick some
79+
values from ``ZEND_INI_USER``, ``ZEND_INI_PERDIR``, ``ZEND_INI_SYSTEM`` or
80+
``ZEND_INI_ALL``, those may be flagued together and are detailed `in the PHP manual
81+
<http://php.net/configuration.changes.modes>`_.
82+
* ``modified`` is set to one anytime the setting has been modified during a request so that the engine knows at request
83+
shutdown that it must restore that INI setting value to its master value for the next request to treat.
84+
* ``on_modify()`` is a handler called whenever the current setting's value is modified, like f.e using a call
85+
to ``ini_set()`` (but not only). We'll focus deeper on ``on_modify()`` later, but think of it as being a
86+
*validator function* (f.e if the setting is expected to represent an integer, you may validate the values you'll be
87+
given against integers). It also serve as a memory bridge to update global values, we'll get back on that later as
88+
well.
89+
* ``diplayer()`` is less useful, and you usually don't pass any. ``displayer()`` is about how to display your setting.
90+
F.e, you may remember that PHP tends to display *On* for boolean values of *true*/*yes*/*on*/*1* etc. That's the
91+
``displayer()`` job : turn the current value into a "displayed" value.
8892

8993
You will also need to deal with this structure ``zend_ini_entry_def``::
9094

@@ -109,7 +113,10 @@ an INI setting against the engine. The engine reads a ``zend_ini_entry_def``, an
109113
Registering and using INI entries
110114
---------------------------------
111115

112-
INI settings are persistent through requests. They can change their value during a request (runtime), but they'll go back
116+
Registration
117+
************
118+
119+
INI settings are persistent through requests. They may change their value at runtime during a request, but they'll go back
113120
to original value at request shutdown. Thus, registering INI settings is done once for all, in ``MINIT()`` hook of your
114121
extension.
115122

@@ -132,6 +139,13 @@ for previous chapter about random number picking and guessing, once again only s
132139
{
133140
DISPLAY_INI_ENTRIES();
134141
}
142+
143+
PHP_MSHUTDOWN_FUNCTION(pib)
144+
{
145+
UNREGISTER_INI_ENTRIES();
146+
147+
return SUCCESS;
148+
}
135149

136150
That was the easiest INI declaration, we won't keep it as-is but the steps are trivial : you declare a
137151
``zend_ini_entry_def[]`` vector using ``PHP_INI_BEGIN`` and ``PHP_INI_END`` macros. In the middle, you add your
@@ -140,17 +154,24 @@ takes only four parameters : the name of the entry to register, its default valu
140154
file scanning (see above chapter for details), the modification level, ``PHP_INI_ALL`` says "everywhere". We did not
141155
play with validator yet, and passed NULL.
142156

143-
Now, the new *"pib.rnd_max"* INI setting is declared - as *PHP_INI_ALL* - that means that the user may play with its
144-
value using ``ini_set()`` and ``ini_get()`` mainly.
157+
In ``MINIT`` hook , we use the ``REGISTER_INI_ENTRIES`` macro which does its describing job whereas its counter-part
158+
``UNREGISTER_INI_ENTRIES`` is used at module shutdown to free the allocated resources.
145159

146-
We did not forget to display that INI setting as part of our extension informations, using ``DISPLAY_INI_ENTRIES()``.
160+
Now, the new *"pib.rnd_max"* INI setting is declared - as ``PHP_INI_ALL`` - that means that the user may modify its
161+
value using ``ini_set()`` (and read it back with ``ini_get()``).
162+
163+
We did not forget to display those INI settings as part of our extension informations, using ``DISPLAY_INI_ENTRIES()``.
147164
Forgetting this in the declaration of the ``MINFO()`` hook will lead to our INI settings being hidden from the user in
148165
the information page (``phpinfo()``). Have a look at the :doc:`extension informations chapter <extension_infos>` if
149166
you need.
150167

151-
On your part as extension developper, we may now need to read that value ourselves. The simplest way to do this is to
152-
use macros that will look for the value into the main array retaining all the INI settings, find it, and return it as
153-
the type we'll ask for. We are provided several macros depending on what C type we want to be given back.
168+
Usage
169+
*****
170+
171+
On your part as extension developper, we may now need to read INI settings values by yourself. The simplest way to do
172+
this into your extension is to use macros that will look for the value into the main array retaining all the INI
173+
settings, find it, and return it as the type you'll ask for. We are provided several macros depending on what C type
174+
we want to be given back.
154175

155176
``INI_INT(val)``, ``INI_FLT(val)``, ``INI_STR(val)``, ``INI_BOOL(val)`` all four macros will look for the provided
156177
value from the INI settings array and return it (if found) with a cast to the type you ask.
@@ -180,15 +201,15 @@ previous above lines is far from being optimal.
180201
There are two problems that will get solved at the same time by using the 'advanced' INI settings API :
181202

182203
* Everytime we want to read our value, a lookup into the main INI settings table is needed, as well as a cast to the
183-
right type, often.
204+
right type (often). Those operations cost some CPU cycles.
184205
* We did not provide any validator, thus the user could alter our setting and put anything he wants to as a value.
185206

186-
Welcome globals update memory bridge.
207+
The solution is to use an ``on_modify()`` validator and a memory bridge to update a global variable.
187208

188209
By using the advanced INI settings management API, we can tell the engine to register our settings just normally, but
189210
we can also instruct it to update a global of our taste everytime the INI setting value is changed. Hence, whenever we
190211
will want to read back our value, we'll just need to read our global. This will provide a boost in performance in the
191-
case we need to read the INI setting value often, as a hashtable lookup won't be needed anymore.
212+
case we need to read the INI setting value often, as a hashtable lookup and a cast operation won't be needed anymore.
192213

193214
.. note:: You will need to feel comfortable with globals to continue reading the chapter. Global space management is
194215
treated :doc:`into its own chapter <globals_management>`.
@@ -227,25 +248,25 @@ declares a ``pib_globals`` symbol of such a type.
227248
``max_rnd`` member into the ``zend_pib_globals`` structure, to be able to update that part of memory whenever the
228249
*'pib.rnd_max'* will get changed.
229250

230-
The validator used here, ``onUpdateLongGEZero()``, is a default validator that exists in PHP and validates the value
231-
against a long greater than or equal to zero. The validator is needed for the global to be updated, as such a job is
232-
done into the validator.
251+
The ``on_modify()`` validator used here, ``onUpdateLongGEZero()``, is a default validator that exists in PHP and
252+
validates the value against a long greater than or equal to zero. The validator is needed for the global to be updated,
253+
as such a job is done into the validator.
233254

234255
Now, to read back our INI setting value, we just need to read the value of our ``max_rnd`` global::
235256

236257
php_printf("The value is : %lu", PIB_G(max_rnd));
237258

238259
And we are done.
239260

240-
Let's go to see the validator now. The validator has two goals :
261+
Let's go to see the validator now (``on_modify()`` handler). The validator has two goals :
241262

242263
* Validate the passed value
243264
* Update the global if the validation succeeds
244265

245-
The validator is only called when the INI setting is set or modified, whenever this step happens.
266+
The validator is only called when the INI setting is set or modified (written to), whenever this step happens.
246267

247-
.. warning:: If you want the global to be updated with the INI setting value, you'll need a validator. Such a mechanism
248-
is not magicaly performed by the engine, but must be done explicitly into the validator
268+
.. warning:: If you want the global variable to be updated with the INI setting value, you'll need a validator. Such a
269+
mechanism is not magicaly performed by the engine, but must be done explicitly into the validator.
249270

250271
Let's see the ``onUpdateLongGEZero()`` source code::
251272

@@ -276,8 +297,8 @@ Let's see the ``onUpdateLongGEZero()`` source code::
276297

277298
Like you can see, there is nothing complex. Your validator is given the ``new_value`` and must validate against it.
278299
Remember that ``new_value`` is of type :doc:`zend_string * <../internal_types/strings/zend_strings>`. The
279-
``onUpdateLongGEZero()`` takes the value as a long and checks it is >=0. One must return SUCCESS from the validator if
280-
OK and FAILURE if not.
300+
``onUpdateLongGEZero()`` takes the value as a long and checks if it is a positive integer. One must return ``SUCCESS``
301+
from the validator if everything went right and ``FAILURE`` if not.
281302

282303
Then comes the part to update the global. ``mh_arg`` variables are used to carry any type of information to your
283304
validator.
@@ -290,7 +311,7 @@ is accessed differently if you are using ZTS mode or not. More informations abou
290311
:doc:`can be found here <globals_management>`.
291312

292313
``mh_arg1`` is passed the computed offset of your global member (``max_rnd`` for us), and you must slice the memory
293-
yourself to get a pointer to it. Taht's why we stored ``mh_arg2`` as a generic ``char *`` pointer and casted
314+
yourself to get a pointer to it. That's why we stored ``mh_arg2`` as a generic ``char *`` pointer and casted
294315
``mh_arg1`` to ``size_t``.
295316

296317
Then, you simply update the content with the validated value by writing into the pointer. ``mh_arg3`` is unused actually.
@@ -332,19 +353,19 @@ for example::
332353
STD_PHP_INI_ENTRY("pib.rnd_max", "100", PHP_INI_ALL, onUpdateMaxRnd, max_rnd, zend_pib_globals, pib_globals)
333354
PHP_INI_END()
334355

335-
.. note:: It is safe to write to an unsigned long from a long, as soon as the ranges are checked, which the validator
356+
.. note:: It is safe to write to an *unsigned long* from a *long*, as soon as the ranges are checked, which the validator
336357
does.
337358

338359
Now, if the user wants to modify the setting and pass a wrong value that does not validate, ``ini_set()`` simply will
339-
return FALSE to the userland, and will not modify the value:
360+
return false to the userland, and will not modify the value:
340361

341362
.. code-block:: php
342363
343-
ini_set('pib.rnd_max', 2048);
364+
ini_set('pib.rnd_max', 2048); /* returns false as 2048 is > 1000 */
344365
345-
In the opposite case, ``ini_set()`` returns the old value and modifies the value. The new provided value becomes the
346-
current "local" value whereas the default preceding value stays as the "master value". ``phpinfo()`` or ``ini_get_all()``
347-
detail such values. Example:
366+
In the opposite case, ``ini_set()`` returns the old value and modifies current the value. The new provided value becomes
367+
the current "local" value whereas the default preceding value stays as the "master value". ``phpinfo()`` or
368+
``ini_get_all()`` detail such values. Example:
348369

349370
.. code-block:: php
350371
@@ -376,17 +397,18 @@ For example, for our tiny example, the validator we designed is called three tim
376397

377398
Keep also in mind that the value accessor is checked against by ``ini_set()``. If we would have designed a
378399
``PHP_INI_SYSTEM`` setting, then the user would not have been able to modify it using ``ini_set()``, as ``ini_set()``
379-
uses ``PHP_INI_USER`` as accessor. The mismatch would then have benn detected soon and the validator would not have
380-
been called.
400+
uses ``PHP_INI_USER`` as accessor. The mismatch would then have been detected and the validator would not have
401+
been called by the engine in such a case.
381402

382403
If you need to change the INI setting value into your extension at runtime, the internal call is
383404
``zend_alter_ini_entry()``, this is what userland ``ini_set()`` uses.
384405

385406
Using a displayer
386407
-----------------
387408

388-
The last thing you need to know about INI settings is the ``displayer()`` callback. That one is triggered any time the
389-
userland asks to "see" your INI setting value, that is through the usage of ``phpinfo()`` or ``php --ri``.
409+
The last thing you need to know about INI settings is the ``displayer()`` callback. It is less used in practice, it is
410+
triggered any time the userland asks to "print" your INI setting value, that is through the usage of
411+
``phpinfo()`` or ``php --ri``.
390412

391413
If you provide no displayer, a default one will be used. See it::
392414

@@ -395,7 +417,7 @@ If you provide no displayer, a default one will be used. See it::
395417
Directive => Local Value => Master Value
396418
pib.rnd_max => 120 => 120
397419

398-
The default displayer takes the INI setting value (which, as a reminder, is of type ``zend_string \*``), and simply
420+
The default displayer takes the INI setting value (which, as a reminder, is of type ``zend_string *``), and simply
399421
displays it. If no value were found or the value were the empty string, then it displays the string "no value".
400422

401423
To take hand on such a process, we must declare a ``displayer()`` callback that will be called. Let's try to represent

0 commit comments

Comments
 (0)