Home  >  Article  >  Backend Development  >  In-depth understanding of ini configuration in php (1), in-depth understanding of ini_PHP tutorial

In-depth understanding of ini configuration in php (1), in-depth understanding of ini_PHP tutorial

WBOY
WBOYOriginal
2016-07-13 10:17:25915browse

In-depth understanding of ini configuration in php (1), in-depth understanding of ini

This article will not describe the purpose of a certain ini configuration item in detail, these have been explained in the manual comprehensive. I just want to dig into the implementation mechanism of PHP from a specific perspective, which will involve some PHP kernel knowledge:-)

Students who use PHP know that the php.ini configuration will take effect throughout the entire SAPI life cycle. During the execution of a php script, if you manually modify the ini configuration, it will not take effect. If you cannot restart apache or nginx at this time, you can only explicitly call the ini_set interface in the php code. ini_set is a function provided by PHP to dynamically modify the configuration. It should be noted that the configuration set by ini_set and the configuration set in the ini file have different effective time ranges. After the php script is executed, the ini_set settings will become invalid immediately.

Therefore, this article is divided into two parts. The first part explains the principle of php.ini configuration, and the second part talks about dynamically modifying the php configuration.

The configuration of php.ini will roughly involve three pieces of data, configuration_hash, EG (ini_directives), and PG, BG, PCRE_G, JSON_G, XXX_G, etc. It doesn’t matter if you don’t know the meaning of these three types of data, they will be explained in detail below.

1, parse INI configuration file

Since php.ini needs to be in effect during the SAPI process, the work of parsing the ini file and constructing the php configuration accordingly must be the beginning of SAPI. In other words, it must occur during the startup process of PHP. PHP requires that these configurations have been generated internally before any actual request arrives.

Reflected into the core of php, which is the php_module_startup function.

php_module_startup is mainly responsible for starting php. It is usually called when SAPI starts. btw, another common function is php_request_startup, which is responsible for initializing each request when it arrives. php_module_startup and php_request_startup are two iconic actions, but their analysis is beyond the scope of this article.

For example, when php is hooked into a module under apache, then when apache starts, all these modules will be activated, including the php module. When activating the php module, php_module_startup will be called. The php_module_startup function completes a lot of work. Once the php_module_startup call ends, it means, OK, php has been started and can now accept requests and respond.

In the php_module_startup function, the implementation related to parsing the ini file is:

<span>/*</span><span> this will read in php.ini, set up the configuration parameters,
   load zend extensions and register php function extensions
   to be loaded later </span><span>*/</span>
<span>if</span> (php_init_config(TSRMLS_C) ==<span> FAILURE) {
    </span><span>return</span><span> FAILURE;
}</span>

As you can see, the php_init_config function is actually called to complete the parse of the ini file. The parse work mainly performs lex&grammar analysis, and extracts and saves the key and value pairs in the ini file. The format of php.ini is very simple, with key on the left side of the equal sign and value on the right side. Whenever a pair of kvs are extracted, where does php store them? The answer is the configuration_hash mentioned earlier.

<span>static</span> HashTable configuration_hash;

Configuration_hash is declared in php_ini.c, which is a HashTable type data structure. As the name suggests, it is actually a hash table. As an aside, configuration_hash cannot be obtained in versions before php5.3 because it is a static variable in the php_ini.c file. Later, php5.3 added the php_ini_get_configuration_hash interface, which directly returns &configuration_hash, so that each PHP extension can easily get a glimpse of the configuration_hash... What a great blessing...

Note four points:

First, php_init_config will not do any verification other than lexical and syntax. In other words, if we add a line hello=world to the ini file, as long as this is a correctly formatted configuration item, then the final configuration_hash will contain an element with the key hello and the value world, and the configuration_hash will reflect it to the maximum extent. ini file.

Second, the ini file allows us to configure in the form of an array. For example, write the following three lines in the ini file:

drift.arr<span>[]</span>=1<span>
drift.arr</span><span>[]</span>=2<span>
drift.arr</span><span>[]</span>=3

Then in the final generated configuration_hash table, there will be an element with the key drift.arr, and its value is an array containing three numbers: 1, 2, and 3. This is an extremely rare configuration method.

Thirdly, php also allows us to build some additional ini files in addition to the default php.ini file (php-%s.ini to be precise). These ini files will be placed in an additional directory. This directory is specified by the environment variable PHP_INI_SCAN_DIR. After php_init_config has parsed php.ini, it will scan this directory again and find all the .ini files in the directory for analysis. The kv key-value pairs generated in these additional ini files will also be added to the configuration_hash.

This is an occasionally useful feature. Suppose we develop a PHP extension ourselves but don’t want to mix the configuration into php.ini. We can write another ini and tell PHP where to find it through PHP_INI_SCAN_DIR. Of course, its disadvantages are also obvious, and it requires setting additional environment variables to support it. A better solution is for developers to call php_parse_user_ini_file or zend_parse_ini_file themselves in the extension to parse the corresponding ini file.

第四,在configuration_hash中,key是字符串,那么值的类型是什么?答案也是字符串(除了上述很特殊的数组)。具体来说,比如下面的配置:

display_errors =<span> On
log_errors </span>=<span> Off
log_errors_max_len </span>= 1024

 那么最后configuration_hash中实际存放的键值对为:

key: "display_errors"<span>
val : </span>"1"<span>

key: </span>"log_errors"<span>
val : </span>""<span>

key: </span>"log_errors_max_len"<span>
val : </span>"1024"

注意log_errors,其存放的值连"0"都不是,就是一个实实在在地空字符串。另外,log_errors_max_len也并非数字,而是字符串1024。

分析至此,基本上解析ini文件相关的内容都说清楚了。简单总结一下:

1,解析ini发生在php_module_startup阶段

2,解析结果存放在configuration_hash里。

2,配置作用到模块

php的大致结构可以看成是最下层有一个zend引擎,它负责与OS进行交互、编译php代码、提供内存托管等等,在zend引擎的上层,排列着很多很多的模块。其中最核心的就一个Core模块,其他还有比如Standard,PCRE,Date,Session等等...这些模块还有另一个名字叫php扩展。我们可以简单理解为,每个模块都会提供一组功能接口给开发者来调用,举例来说,常用的诸如explode,trim,array等内置函数,便是由Standard模块提供的。

为什么需要谈到这些,是因为在php.ini里除了针对php自身,也就是针对Core模块的一些配置(例如safe_mode,display_errors,max_execution_time等),还有相当多的配置是针对其他不同模块的。

例如,date模块,它提供了常见的date, time,strtotime等函数。在php.ini中,它的相关配置形如:

<span>[</span><span>Date</span><span>]</span><span>
;</span><span>date.timezone = 'Asia/Shanghai'</span><span>
;</span><span>date.default_latitude = 31.7667</span><span>
;</span><span>date.default_longitude = 35.2333<br />;date.sunrise_zenith = 90.583333<br />;date.sunset_zenith = 90.583333<br /></span>

除了这些模块拥有独立的配置,zend引擎也是可配的,只不过zend引擎的可配项非常少,只有error_reporting,zend.enable_gc和detect_unicode三项。

在上一小节中我们已经谈到,php_module_startup会调用php_init_config,其目的是解析ini文件并生成configuration_hash。那么接下来在php_module_startup中还会做什么事情呢?很显然,就是会将configuration_hash中的配置作用于Zend,Core,Standard,SPL等不同模块。当然这并非一个一蹴而就的过程,因为php通常会包含有很多模块,php启动的过程中这些模块也会依次进行启动。那么,对模块A进行配置的过程,便是发生在模块A的启动过程中。

有扩展开发经验的同学会直接指出,模块A的启动不就是在PHP_MINIT_FUNCTION(A)中么?

是的,如果模块A需要配置,那么在PHP_MINIT_FUNCTION中,可以调用REGISTER_INI_ENTRIES()来完成。REGISTER_INI_ENTRIES会根据当前模块所需要的配置项名称,去configuration_hash查找用户设置的配置值,并更新到模块自己的全局空间中。

2.1,模块的全局空间

要理解如何将ini配置从configuration_hash作用到各个模块之前,有必要先了解一下php模块的全局空间。对于不同的php模块,均可以开辟一块属于自己的存储空间,并且这块空间对于该模块来说,是全局可见的。一般而言,它会被用来存放该模块所需的ini配置。也就是说,configuration_hash中的配置项,最终会被存放到该全局空间中。在模块的执行过程中,只需要直接访问这块全局空间,就可以拿到用户针对该模块进行的设置。当然,它也经常被用来记录模块在执行过程中的中间数据。

我们以bcmath模块来举例说明,bcmath是一个提供数学计算方面接口的php模块,首先我们来看看它有哪些ini配置:

<span>PHP_INI_BEGIN()
    STD_PHP_INI_ENTRY(</span><span>"</span><span>bcmath.scale</span><span>"</span>, <span>"</span><span>0</span><span>"</span><span>, PHP_INI_ALL, OnUpdateLongGEZero, bc_precision, zend_bcmath_globals, bcmath_globals)
PHP_INI_END()</span>

bcmath只有一个配置项,我们可以在php.ini中用bcmath.scale来配置bcmath模块。

接下来继续看看bcmatch模块的全局空间定义。在php_bcmath.h中有如下声明:

<span>ZEND_BEGIN_MODULE_GLOBALS(bcmath)<br />    bc_num _zero_;<br />    bc_num _one_;<br />    bc_num _two_;<br />    long bc_precision;<br />ZEND_END_MODULE_GLOBALS(bcmath)</span>

 宏展开之后,即为:

typedef struct _zend_bcmath_globals {
    bc_num _zero_;
    bc_num _one_;
    bc_num _two_;
    long bc_precision;
} zend_bcmath_globals;

其实,zend_bcmath_globals类型就是bcmath模块中的全局空间类型。这里仅仅声明了zend_bcmath_globals结构体,在bcmath.c中还有具体的实例化定义:

// 展开后即为zend_bcmath_globals bcmath_globals;<br />ZEND_DECLARE_MODULE_GLOBALS(bcmath) 

可以看出,用ZEND_DECLARE_MODULE_GLOBALS完成了对变量bcmath_globals的定义。

bcmath_globals是一块真正的全局空间,它包含有四个字段。其最后一个字段bc_precision,对应于ini配置中的bcmath.scale。我们在php.ini中设置了bcmath.scale的值,随后在启动bcmath模块的时候,bcmath.scale的值被更新到bcmath_globals.bc_precision中去。

把configuration_hash中的值,更新到各个模块自己定义的xxx_globals变量中,就是所谓的将ini配置作用到模块。一旦模块启动完成,那么这些配置也都作用到位。所以在随后的执行阶段,php模块无需再次访问configuration_hash,模块仅需要访问自己的XXX_globals,就可以拿到用户设定的配置。

bcmath_globals,除了有一个字段为ini配置项,其他还有三个字段为何意?这就是模块全局空间的第二个作用,它除了用于ini配置,还可以存储模块执行过程中的一些数据。

再例如json模块,也是php中一个很常用的模块:

<span>ZEND_BEGIN_MODULE_GLOBALS(json)
    </span><span>int</span><span> error_code;
ZEND_END_MODULE_GLOBALS(json)</span>

可以看到json模块并不需要ini配置,它的全局空间只有一个字段error_code。error_code记录了上一次执行json_decode或者json_encode中发生的错误。json_last_error函数便是返回这个error_code,来帮助用户定位错误原因。

为了能够很便捷的访问模块全局空间变量,php约定俗成的提出了一些宏。比如我们想访问json_globals中的error_code,当然可以直接写做json_globals.error_code(多线程环境下不行),不过更通用的写法是定义JSON_G宏:

<span>#define</span> JSON_G(v) (json_globals.v)

我们使用JSON_G(error_code)来访问json_globals.error_code。本文刚开始的时候,曾提到PG、BG、JSON_G、PCRE_G,XXX_G等等,这些宏在php源代码中也是很常见的。现在我们可以很轻松的理解它们,PG宏可以访问Core模块的全局变量,BG访问Standard模块的全局变量,PCRE_G则访问PCRE模块的全局变量。

<span>#define</span> PG(v) (core_globals.v)
<span>#define</span> BG(v) (basic_globals.v)

2.2,如何确定一个模块需要哪些配置呢?

模块需要什么样的INI配置,都是在各个模块中自己定义的。举例来说,对于Core模块,有如下的配置项定义:

<span>PHP_INI_BEGIN()
    ......
    STD_PHP_INI_ENTRY_EX(</span>"display_errors", "1", PHP_INI_ALL,    OnUpdateDisplayErrors, display_errors, php_core_globals, core_globals,<span> display_errors_mode)
    STD_PHP_INI_BOOLEAN(</span>"enable_dl",       "1", PHP_INI_SYSTEM, OnUpdateBool,          enable_dl,      php_core_globals,<span> core_globals)
    STD_PHP_INI_BOOLEAN(</span>"expose_php",      "1", PHP_INI_SYSTEM, OnUpdateBool,          expose_php,     php_core_globals,<span> core_globals)
    STD_PHP_INI_BOOLEAN(</span>"safe_mode",       "0", PHP_INI_SYSTEM, OnUpdateBool,          safe_mode,      php_core_globals,<span> core_globals)
    ......
PHP_INI_END()</span>

可以在php-src\main\main.c文件大概450+行找到上述代码。其中涉及的宏比较多,有ZEND_INI_BEGIN 、ZEND_INI_END、PHP_INI_ENTRY_EX、STD_PHP_INI_BOOLEAN等等,本文不一一赘述,感兴趣的读者可自行分析。

上述代码进行宏展开后得到:

<span>static</span> <span>const</span> zend_ini_entry ini_entries[] =<span> {
    ..
    { </span><span>0</span>, PHP_INI_ALL,    <span>"</span><span>display_errors</span><span>"</span>,<span>sizeof</span>(<span>"</span><span>display_errors</span><span>"</span>),OnUpdateDisplayErrors,(<span>void</span> *)XtOffsetOf(php_core_globals, display_errors), (<span>void</span> *)&core_globals, NULL, <span>"</span><span>1</span><span>"</span>, <span>sizeof</span>(<span>"</span><span>1</span><span>"</span>)-<span>1</span>, NULL, <span>0</span>, <span>0</span>, <span>0</span><span>, display_errors_mode },
    { </span><span>0</span>, PHP_INI_SYSTEM, <span>"</span><span>enable_dl</span><span>"</span>,     <span>sizeof</span>(<span>"</span><span>enable_dl</span><span>"</span>),     OnUpdateBool,         (<span>void</span> *)XtOffsetOf(php_core_globals, enable_dl),      (<span>void</span> *)&core_globals, NULL, <span>"</span><span>1</span><span>"</span>, <span>sizeof</span>(<span>"</span><span>1</span><span>"</span>)-<span>1</span>, NULL, <span>0</span>, <span>0</span>, <span>0</span><span>, zend_ini_boolean_displayer_cb },
    { </span><span>0</span>, PHP_INI_SYSTEM, <span>"</span><span>expose_php</span><span>"</span>,    <span>sizeof</span>(<span>"</span><span>expose_php</span><span>"</span>),    OnUpdateBool,         (<span>void</span> *)XtOffsetOf(php_core_globals, expose_php),     (<span>void</span> *)&core_globals, NULL, <span>"</span><span>1</span><span>"</span>, <span>sizeof</span>(<span>"</span><span>1</span><span>"</span>)-<span>1</span>, NULL, <span>0</span>, <span>0</span>, <span>0</span><span>, zend_ini_boolean_displayer_cb },
    { </span><span>0</span>, PHP_INI_SYSTEM, <span>"</span><span>safe_mode</span><span>"</span>,     <span>sizeof</span>(<span>"</span><span>safe_mode</span><span>"</span>),     OnUpdateBool,         (<span>void</span> *)XtOffsetOf(php_core_globals, safe_mode),      (<span>void</span> *)&core_globals, NULL, <span>"</span><span>0</span><span>"</span>, <span>sizeof</span>(<span>"</span><span>0</span><span>"</span>)-<span>1</span>, NULL, <span>0</span>, <span>0</span>, <span>0</span><span>, zend_ini_boolean_displayer_cb },
    ...
    { </span><span>0</span>, <span>0</span>, NULL, <span>0</span>, NULL, NULL, NULL, NULL, NULL, <span>0</span>, NULL, <span>0</span>, <span>0</span>, <span>0</span><span>, NULL }
};</span>

我们看到,配置项的定义,其本质上就是定义了一个zend_ini_entry类型的数组。zend_ini_entry结构体的字段具体含义为:

<span>struct</span><span> _zend_ini_entry {
    </span><span>int</span> module_number;                <span>//</span><span> 模块的id</span>
    <span>int</span> modifiable;                   <span>//</span><span> 可被修改的范围,例如php.ini,ini_set</span>
    <span>char</span> *name;                       <span>//</span><span> 配置项的名称</span>
    <span>uint</span><span> name_length;
    ZEND_INI_MH((</span>*on_modify));        <span>//</span><span> 回调函数,配置项注册或修改的时候会调用</span>
    <span>void</span> *mh_arg1;                    <span>//</span><span> 通常为配置项字段在XXX_G中的偏移量</span>
    <span>void</span> *mh_arg2;                    <span>//</span><span> 通常为XXX_G</span>
    <span>void</span> *mh_arg3;                    <span>//</span><span> 通常为保留字段,极少用到</span>

    <span>char</span> *value;                      <span>//</span><span> 配置项的值</span>
    <span>uint</span><span> value_length;

    </span><span>char</span> *orig_value;                 <span>//</span><span> 配置项的原始值</span>
    <span>uint</span><span> orig_value_length;
    </span><span>int</span> orig_modifiable;              <span>//</span><span> 配置项的原始modifiable</span>
    <span>int</span> modified;                     <span>//</span><span> 是否发生过修改,如果有修改,则orig_value会保存修改前的值</span>

    <span>void</span> (*displayer)(zend_ini_entry *ini_entry, <span>int</span><span> type);
};</span>

2.3,将配置作用到模块——REGISTER_INI_ENTRIES

经常能够在不同扩展的PHP_MINIT_FUNCTION里看到REGISTER_INI_ENTRIES。REGISTER_INI_ENTRIES主要负责完成两件事情,第一,对模块的全局空间XXX_G进行填充,同步configuration_hash中的值到XXX_G中去。其次,它还生成了EG(ini_directives)。

REGISTER_INI_ENTRIES也是一个宏,展开之后实则为zend_register_ini_entries方法。具体来看下zend_register_ini_entries的实现:

ZEND_API <span>int</span> zend_register_ini_entries(<span>const</span> zend_ini_entry *ini_entry, <span>int</span> module_number TSRMLS_DC) <span>/*</span><span> {{{ </span><span>*/</span><span>
{
    </span><span>//</span><span> ini_entry为zend_ini_entry类型数组,p为数组中每一项的指针</span>
    <span>const</span> zend_ini_entry *p =<span> ini_entry;
    zend_ini_entry </span>*<span>hashed_ini_entry;
    zval default_value;
    
    </span><span>//</span><span> EG(ini_directives)就是registered_zend_ini_directives</span>
    HashTable *directives =<span> registered_zend_ini_directives;
    zend_bool config_directive_success </span>= <span>0</span><span>;
    
    </span><span>//</span><span> 还记得ini_entry最后一项固定为{ 0, 0, NULL, ... }么</span>
    <span>while</span> (p-><span>name) {
        config_directive_success </span>= <span>0</span><span>;
        
        </span><span>//</span><span> 将p指向的zend_ini_entry加入EG(ini_directives)</span>
        <span>if</span> (zend_hash_add(directives, p->name, p->name_length, (<span>void</span>*)p, <span>sizeof</span>(zend_ini_entry), (<span>void</span> **) &hashed_ini_entry) ==<span> FAILURE) {
            zend_unregister_ini_entries(module_number TSRMLS_CC);
            </span><span>return</span><span> FAILURE;
        }
        hashed_ini_entry</span>->module_number =<span> module_number;
        
        </span><span>//</span><span> 根据name去configuration_hash中查询,取出来的结果放在default_value中
        </span><span>//</span><span> 注意default_value的值比较原始,一般是数字、字符串、数组等,具体取决于php.ini中的写法</span>
        <span>if</span> ((zend_get_configuration_directive(p->name, p->name_length, &default_value)) ==<span> SUCCESS) {
            </span><span>//</span><span> 调用on_modify更新到模块的全局空间XXX_G中</span>
            <span>if</span> (!hashed_ini_entry->on_modify || hashed_ini_entry->on_modify(hashed_ini_entry, Z_STRVAL(default_value), Z_STRLEN(default_value), hashed_ini_entry->mh_arg1, hashed_ini_entry->mh_arg2, hashed_ini_entry->mh_arg3, ZEND_INI_STAGE_STARTUP TSRMLS_CC) ==<span> SUCCESS) {
                hashed_ini_entry</span>->value =<span> Z_STRVAL(default_value);
                hashed_ini_entry</span>->value_length =<span> Z_STRLEN(default_value);
                config_directive_success </span>= <span>1</span><span>;
            }
        }

        </span><span>//</span><span> 如果configuration_hash中没有找到,则采用默认值</span>
        <span>if</span> (!config_directive_success && hashed_ini_entry-><span>on_modify) {
            hashed_ini_entry</span>->on_modify(hashed_ini_entry, hashed_ini_entry->value, hashed_ini_entry->value_length, hashed_ini_entry->mh_arg1, hashed_ini_entry->mh_arg2, hashed_ini_entry-><span>mh_arg3, ZEND_INI_STAGE_STARTUP TSRMLS_CC);
        }
        p</span>++<span>;
    }
    </span><span>return</span><span> SUCCESS;
}</span>

简单来说,可以把上述代码的逻辑表述为:

1,将模块声明的ini配置项添加到EG(ini_directives)中。注意,ini配置项的值可能在随后被修改。

2,尝试去configuration_hash中寻找各个模块需要的ini。

  • 如果能够找到,说明用户叜ini文件中配置了该值,那么采用用户的配置。
  • 如果没有找到,OK,没有关系,因为模块在声明ini的时候,会带上默认值。

3,将ini的值同步到XX_G里面。毕竟在php的执行过程中,起作用的还是这些XXX_globals。具体的过程是调用每条ini配置对应的on_modify方法完成,on_modify由模块在声明ini的时候进行指定。

我们来具体看下on_modify,它其实是一个函数指针,来看两个具体的Core模块的配置声明:

STD_PHP_INI_BOOLEAN(<span>"</span><span>log_errors</span><span>",</span>      <span>"</span><span>0</span><span>"</span><span>,    PHP_INI_ALL, OnUpdateBool, log_errors,         php_core_globals, core_globals)
STD_PHP_INI_ENTRY(</span><span>"</span><span>log_errors_max_len</span><span>"</span>,<span>"</span><span>1024</span><span>"</span>, PHP_INI_ALL, OnUpdateLong, log_errors_max_len, php_core_globals, core_globals)

对于log_errors,它的on_modify被设置为OnUpdateBool,对于log_errors_max_len,则on_modify被设置为OnUpdateLong。

进一步假设我们在php.ini中的配置为:

log_errors =<span> On
log_errors_max_len </span>= 1024

具体来看下OnUpdateBool函数:

<span>ZEND_API ZEND_INI_MH(OnUpdateBool) 
{
    zend_bool </span>*<span>p;
    
    </span><span>//</span><span> base表示core_globals的地址</span>
    <span>char</span> *<span>base</span> = (<span>char</span> *<span>) mh_arg2;

    </span><span>//</span><span> p表示core_globals的地址加上log_errors字段的偏移量
    </span><span>//</span><span> 得到的即为log_errors字段的地址</span>
    p = (zend_bool *) (<span>base</span>+<span>(size_t) mh_arg1);  

    </span><span>if</span> (new_value_length == <span>2</span> && strcasecmp(<span>"</span><span>on</span><span>"</span>, new_value) == <span>0</span><span>) {
        </span>*p = (zend_bool) <span>1</span><span>;
    }
    </span><span>else</span> <span>if</span> (new_value_length == <span>3</span> && strcasecmp(<span>"</span><span>yes</span><span>"</span>, new_value) == <span>0</span><span>) {
        </span>*p = (zend_bool) <span>1</span><span>;
    }
    </span><span>else</span> <span>if</span> (new_value_length == <span>4</span> && strcasecmp(<span>"</span><span>true</span><span>"</span>, new_value) == <span>0</span><span>) {
        </span>*p = (zend_bool) <span>1</span><span>;
    }
    </span><span>else</span><span> {
        </span><span>//</span><span> configuration_hash中存放的value是字符串"1",而非"On"
        </span><span>//</span><span> 因此这里用atoi转化成数字1</span>
        *p =<span> (zend_bool) atoi(new_value);
    }
    </span><span>return</span><span> SUCCESS;
}</span>

最令人费解的估计就是mh_arg1和mh_arg2了,其实对照前面所述的zend_ini_entry定义,mh_arg1,mh_arg2还是很容易参透的。mh_arg1表示字节偏移量,mh_arg2表示XXX_globals的地址。因此,(char *)mh_arg2 + mh_arg1的结果即为XXX_globals中某个字段的地址。具体到本case中,就是计算core_globals中log_errors的地址。因此,当OnUpdateBool最后执行到

*p = (zend_bool) atoi(new_value);

其作用就相当于

core_globals.log_errors(zend_bool) atoi("1");

分析完了OnUpdateBool,我们再来看OnUpdateLong便觉得一目了然:

<span>ZEND_API ZEND_INI_MH(OnUpdateLong)
{
    </span><span>long</span> *<span>p;
    </span><span>char</span> *<span>base</span> = (<span>char</span> *<span>) mh_arg2;

    </span><span>//</span><span> 获得log_errors_max_len的地址</span>
    p = (<span>long</span> *) (<span>base</span>+<span>(size_t) mh_arg1);

    </span><span>//</span><span> 将"1024"转化成long型,并赋值给core_globals.log_errors_max_len</span>
    *p =<span> zend_atol(new_value, new_value_length);
    </span><span>return</span><span> SUCCESS;
}</span>

最后需要注意的是,zend_register_ini_entries函数中,如果configuration_hash中存在配置,则当调用on_modify结束后,hashed_ini_entry中的value和value_length会被更新。也就是说,如果用户在php.ini中配置过,则EG(ini_directives)存放的就是实际配置的值。如果用户没配,EG(ini_directives)中存放的是声明zend_ini_entry时给出的默认值。

zend_register_ini_entries中的default_value变量命名比较糟糕,相当容易造成误解。其实default_value并非表示默认值,而是表示用户实际配置的值。

3,总结

至此,三块数据configuration_hash,EG(ini_directives)以及PG、BG、PCRE_G、JSON_G、XXX_G...已经都交代清楚了。

总结一下:

1,configuration_hash,存放php.ini文件里的配置,不做校验,其值为字符串。
2,EG(ini_directives),存放的是各个模块中定义的zend_ini_entry,如果用户在php.ini配置过(configuration_hash中存在),则值被替换为configuration_hash中的值,类型依然是字符串。
3,XXX_G,该宏用于访问模块的全局空间,这块内存空间可用来存放ini配置,并通过on_modify指定的函数进行更新,其数据类型由XXX_G中的字段声明来决定。

 

php中ini的配置是怎做的

直接编辑php目录下的php.ini文件即可

有一个“register_globals = Off”值,这个值是用来打开全局变量的,比如表单送过来的值,如果这个值设为“Off”,就只能用“$_POST['变量名']、$_GET['变量名 ']”等来取得送过来的值,如果设为“On”,就可以直接使用“$变量名”来获取送过来的值,当然,设为“Off”就比较安全,不会让人轻易将网页间传送 的数据截取。这个值是否改成“On”就看自己感觉了,是安全重要还是方便重要?
要用mysql,就要把“;extension= php_mysql.dll”前的“;”去掉。所有的模块文件都放在php解压缩目录的“ext”之下,用什么就把前面的“;”去掉就行了。
 

php读取ini配置文件属性

ini的内容格式如下,请根据自己的INI,格式修改下段程序.
autostart = false
font_size = 12
font_color = red
===================
function get_ini_file($file_name = "demo.ini"){
$str=file_get_contents($file_name);//读取ini文件存到一个字符串中.
$ini_list = explode("\r\n",$str);//按换行拆开,放到数组中.
$ini_items = array();
foreach($ini_list as $item){
$one_item = explode("=",$item);
if(isset($one_item[0])&&isset($one_item[1])) $ini_items[trim($one_item[0])] = trim($one_item[1]); //存成key=>value的形式.
}
return $ini_items;
}

function get_ini_item($ini_items = null,$item_name = ''){//获得INI条目的值.
if(empty($ini_items)) return "";
else return $ini_items[$item_name];
}

$ini_items = get_ini_file("demo.ini");

echo get_ini_item($ini_items,'font_size'); //输出获得的值.
 

www.bkjia.comtruehttp://www.bkjia.com/PHPjc/892527.htmlTechArticle深入理解php中的ini配置(1),深入理解ini 这篇文章不会详细叙述某个ini配置项的用途,这些在手册上已经讲解的面面俱到。我只是想从某个特...
Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn