|
1 | 1 | Declaring and using INI settings
|
2 | 2 | ================================
|
3 | 3 |
|
4 |
| -This chapter details how an extension is expected to hook into the main configuration step of PHP, by registering and |
5 |
| -making use of INI settings. |
| 4 | +This chapter details how PHP plays with its configuration and how an extension is expected to hook into the main |
| 5 | +configuration step of PHP, by registering and making use of INI settings. |
| 6 | + |
| 7 | +Reminders on INI settings |
| 8 | +------------------------- |
| 9 | + |
| 10 | +Before going further, you must remember how INI settings and PHP configuration work in PHP. Here are the steps, once |
| 11 | +more extracted as an interpretation of the source code. PHP INI file parsing steps happen in |
| 12 | +`php_init_config() <https://github.com/php/php-src/blob/4903f044d3a65de5b1c457d9eb618c9e247f7086/main/php_ini.c#L382>`_, |
| 13 | +and everything related to INI mainly takes place in |
| 14 | +`Zend/zend_ini.c <https://github.com/php/php-src/blob/4903f044d3a65de5b1c457d9eb618c9e247f7086/Zend/zend_ini.c>`_. |
| 15 | + |
| 16 | +First, PHP tries to parse one or several INI files. Those files may declare some settings, that may or may not be |
| 17 | +relevant in the future. At this very early stage (INI files parsing), PHP knows nothing about what to expect in such |
| 18 | +files. It just parses the content, and saves this one for later use. |
| 19 | + |
| 20 | +Then as a second step, PHP boots up its extensions, calling their ``MINIT()``. If you need to remember about the PHP |
| 21 | +lifecycle, :doc:`read the dedicated chapter <php_lifecycle>`. ``MINIT()`` may now register the current |
| 22 | +extension INI settings of interest. When registering the setting, the engine checks if it parsed its definition before, |
| 23 | +as part of the INI files parsing step. If that was the case, then the INI setting is registered into the engine and it |
| 24 | +gets the value that was parsed from INI files. If it had no definition in INI files parsed, then it gets registered with |
| 25 | +the default value the extension designer gives to the API. |
| 26 | + |
| 27 | +.. note:: The default value the setting will get is probed from INI files parsing. If none is found, then the default |
| 28 | + is the one given by the extension developer, not the other way around. |
| 29 | + |
| 30 | +The default value we are talking about here is called the **"master value"**. You may recall it from a ``phpinfo()`` |
| 31 | +output, right ?: |
| 32 | + |
| 33 | +.. image:: ./images/php_extensions_ini.png |
| 34 | + :align: center |
| 35 | + |
| 36 | +The master value cannot change. If during a request, the user wants to change the configuration, f.e using |
| 37 | +``ini_set()``, and if he's allowed to, then the changed value will be the **"local value"** , that is the current value |
| 38 | +for the current request. The engine will automaticaly restore the local value to the master value value at the end of |
| 39 | +the request. |
| 40 | + |
| 41 | +``ini_get()`` reads the current request-bound local value, whereas ``get_cfg_var()`` will read the master value |
| 42 | +whatever happens. |
| 43 | + |
| 44 | +.. note:: If you have understood correctly, ``get_cfg_var()`` will return false for any value asked that was not part of |
| 45 | + INI file parsing, even if the value exists and was declared by an extension. |
| 46 | + And the opposite is true: ``ini_get()`` will return false if asked for a setting that no extension has declared |
| 47 | + interest in, even if such a setting was part of an INI file parsing (like php.ini). |
| 48 | + |
| 49 | +Zoom on INI settings |
| 50 | +-------------------- |
| 51 | + |
| 52 | +Into the engine, an INI setting is represented by a ``zend_ini_entry`` structure:: |
| 53 | + |
| 54 | + struct _zend_ini_entry { |
| 55 | + zend_string *name; |
| 56 | + int (*on_modify)(zend_ini_entry *entry, zend_string *new_value, void *mh_arg1, void *mh_arg2, void *mh_arg3, |
| 57 | + int stage); |
| 58 | + void *mh_arg1; |
| 59 | + void *mh_arg2; |
| 60 | + void *mh_arg3; |
| 61 | + zend_string *value; |
| 62 | + zend_string *orig_value; |
| 63 | + void (*displayer)(zend_ini_entry *ini_entry, int type); |
| 64 | + int modifiable; |
| 65 | + |
| 66 | + int orig_modifiable; |
| 67 | + int modified; |
| 68 | + int module_number; |
| 69 | + }; |
| 70 | + |
| 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``. |
| 78 | + |
| 79 | +Then come two handlers: ``on_modify()`` is called whenever the current setting's value is modified, like f.e using a call |
| 80 | +to ``ini_set()`` (but not only). We'll focus deeper on ``on_modify()`` later, but think of it as being a |
| 81 | +*validator function* (f.e if the setting is expected to represent an integer, you may validate the values you'll be |
| 82 | +given against integers). It also serve as a memory bridge to update global values, we'll get back on that later as well. |
| 83 | + |
| 84 | +``diplayer()`` is less useful, and you usually don't pass any. ``displayer()`` is about how to display your setting. |
| 85 | +F.e, you may remember that PHP tend to display *On* for boolean values of *true*/*yes*/*on*/*1* etc. That's the |
| 86 | +``displayer()`` job. |
| 87 | + |
| 88 | +You will also need to deal with this structure ``zend_ini_entry_def``:: |
| 89 | + |
| 90 | + typedef struct _zend_ini_entry_def { |
| 91 | + const char *name; |
| 92 | + ZEND_INI_MH((*on_modify)); |
| 93 | + void *mh_arg1; |
| 94 | + void *mh_arg2; |
| 95 | + void *mh_arg3; |
| 96 | + const char *value; |
| 97 | + void (*displayer)(zend_ini_entry *ini_entry, int type); |
| 98 | + int modifiable; |
| 99 | + |
| 100 | + uint name_length; |
| 101 | + uint value_length; |
| 102 | + } zend_ini_entry_def; |
| 103 | + |
| 104 | +Pretty much similar to ``zend_ini_entry``, ``zend_ini_entry_def`` is used by the programmer (you) when he must register |
| 105 | +an INI setting against the engine. The engine reads a ``zend_ini_entry_def``, and creates internally a |
| 106 | +``zend_ini_entry`` for its own usage, based on the definition model you provide. Easy. |
| 107 | + |
| 108 | +Registering INI entries |
| 109 | +----------------------- |
| 110 | + |
| 111 | +INI settings are persistent through requests. They can change their value during a request (runtime), but they'll go back |
| 112 | +to original value at request shutdown. Thus, registering INI settings is done once for all, in ``MINIT()`` hook of your |
| 113 | +extension. |
| 114 | + |
| 115 | +What you must do is declare a vector of ``zend_ini_entry_def``, you'll be helped with dedicated macros for that. Then, |
| 116 | +you register your vector against the engine and you are done for the declaration. Let's see that:: |
| 117 | + |
| 118 | + |
| 119 | + |
| 120 | + |
0 commit comments