>  기사  >  백엔드 개발  >  [번역][php 확장 개발 및 임베디드] 13장-php INI 설정

[번역][php 확장 개발 및 임베디드] 13장-php INI 설정

黄舟
黄舟원래의
2017-02-10 10:17:041059검색


INI 설정

및 이전 1장에서 본 슈퍼전역 변수 및 영구 상수와 마찬가지로 php.ini 값은 확장된 MINIT 블록에서 정의되어야 합니다. 그러나 다른 기능과 달리 INI 옵션의 정의는 간단한 시작/중지 줄로만 구성됩니다.

PHP_MINIT_FUNCTION(sample4)
{
    REGISTER_INI_ENTRIES();
    return SUCCESS;
}
PHP_MSHUTDOWN_FUNCTION(sample4)
{
    UNREGISTER_INI_ENTRIES();
    return SUCCESS;
}

INI 설정 정의 및 액세스

INI 명령 자체는 소스 코드 파일의 MINIT 함수 위에 있습니다. , use 다음 매크로는 이 두 매크로 사이에 완전히 독립적으로 정의될 수 있습니다.

PHP_INI_BEIGN()
PHP_INI_END()

이 두 매크로 함수는 ZEND_BEGIN_MODULE_GLOBALS()/ZEND_END_MODULE_GLOBALS()와 유사합니다. 그러나 이는 typdef 구조가 아니라 정적 데이터 인스턴스 정의를 위한 프레임워크 구성입니다.

static zend_ini_entry ini_entries[] = {
{0,0,NULL,0,NULL,NULL,NULL,NULL,NULL,0,NULL,0,0,NULL} };

보시다시피 이는 zend_ini_entry 값의 벡터를 정의하고 빈 레코드로 끝납니다. 이는 앞서 본 정적과 동일합니다. 벡터 function_entry의 정의는 동일합니다.

간단한 INI 설정

이제 It을 사용한 INI 구조가 생겼습니다. INI 명령어와 INI 설정의 엔진 등록/제거 메커니즘을 정의하는 데 사용되므로 실제로 확장에 대한 일부 INI 명령어를 정의할 수 있습니다. 5장 "첫 번째와 동일합니다." 확장명"을 사용하지만 인사하고 싶다면 이를 사용자 정의할 수 있습니다.

PHP_FUNCTION(sample4_hello_world)
{
    php_printf("Hello World!\n");
}

가장 간단하고 직접적인 방법은 INI 명령을 정의하고 기본값인 "Hello world! ":

#include "php_ini.h"
PHP_INI_BEGIN()
    PHP_INI_ENTRY("sample4.greeting", "Hello World",
                                    PHP_INI_ALL, NULL)
PHP_INI_END()

짐작하셨겠지만, 이 매크로의 처음 두 매개변수는 매크로의 이름을 나타냅니다. INI 명령 및 해당 기본값 세 번째 매개변수는 엔진이 이 INI 명령을 수정하도록 허용하는지 여부를 결정하는 데 사용됩니다(이 장의 뒷부분에서 소개되는 액세스 수준 문제와 관련됨). INI 명령어의 값이 변경될 때마다 호출됩니다. 수정 이벤트 섹션에서 이 매개변수의 세부정보를 볼 수 있습니다. 원본 작업의 예상 결과와 일치하지 않는 결과가 나타나면 "REGISTER_INI_ENTRIES();"를 추가하세요. 테스트 중에 MINIT() 함수를 호출하고 MINIT에 전역 공간을 할당한 후 호출이 실행되는지 확인하세요.

이제 INI 설정이 정의되었으므로,

PHP_FUNCTION(sample4_hello_world)
{
    const char *greeting = INI_STR("sample4.greeting");
    php_printf("%s\n", greeting);
}

꼭 사용해야 합니다. char * 값은 엔진이 소유하므로 수정해서는 안 됩니다. INI 설정 값을 const로 임시 저장하기 위해 로컬에서 사용하는 변수입니다. 물론 모든 INI 값이 문자열은 아닙니다. 정수, 부동 소수점 및 부울 값을 가져오는 다른 매크로가 있습니다. 🎜>

long lval = INI_INT("sample4.intval");
double dval = INI_FLT("sample4.fltval");
zend_bool bval = INI_BOOL("sample4.boolval");

일반적으로 알고 싶은 것은 INI 설정의 현재 값입니다. 그러나 보충으로 사용할 수 있는 여러 매크로가 있습니다. INI 설정의 수정되지 않은 값 읽기:

const char *strval = INI_ORIG_STR("sample4.stringval");
long lval = INI_ORIG_INT("sample4.intval");
double dval = INI_ORIG_FLT("sample4.fltval");
zend_bool bval = INI_ORIG_BOOL("sample4.boolval");

이 예에서 INI 명령 "sample4.greeting"의 이름에는 다른 확장에 의해 노출되는 INI 명령 이름과 충돌하지 않도록 확장자가 붙습니다. 이 접두사는 필수는 아니지만

액세스 수준

을 위한 공개 확장에 권장됩니다.

INI 지침에는 항상 기본값이 있습니다. 대부분의 경우 기본값을 변경하지 않고 그대로 두는 것이 이상적이지만 일부 특수한 상황이나 스크립트 내의 특정 작업에서는 이러한 값을 사용합니다. ​may 수정이 필요합니다. 아래 표와 같이 INI 명령어의 값은


에서 수정될 수 있습니다.


某些设置如果可以在任何地方被修改就没有多大意义了, 比如safe_mode, 如果可以在任何地方去修改, 那么恶意脚本的作者就可以很简单的去禁用safe_mode, 接着去读或修改本不允许操作的文件.

类似的, 某些非安全相关的指令比如register_globals或magic_quotes_gpc, 在脚本中不能被修改, 因为, 在脚本执行时, 它所影响的事情已经发生过了.

这些指令的访问控制是通过PHP_INI_ENTRY()的第三个参数完成的. 在你前面例子中, 使用了PHP_INI_ALL, 它的定义是一个位域操作: PHP_INI_SYSTEM | PHP_INI_PERDIR | PHP_INI_USER.

对于register_globals和magic_quotes_gpc这样的指令, 定义的访问级别为PHP_INI_SYSTEM | PHP_INI_PERDIR. 排除了PHP_INI_USER将导致以这个名字调用ini_set()时最终会失败.

现在, 你可能已经猜到, safe_mode和open_basedir这样的指令应该仅被定义为PHP_INI_SYSTEM. 这样的设置就确保了只有系统管理员可以修改这些值, 因为只有它们可以访问修改php.ini或httpd.conf文件中的配置.

修改事件

当INI指令被修改时, 无论是通过ini_set()函数还是某个perdir指令的处理, 引擎都会为其测试OnModify回调. 修改处理器可以使用ZEND_INI_MH()宏定义, 并通过在OnModify参数上传递函数名附加到INI指令上:

ZEND_INI_MH(php_sample4_modify_greeting)
{
    if (new_value_length == 0) {
        return FAILURE;
    }
    return SUCCESS;
}
PHP_INI_BEGIN()
    PHP_INI_ENTRY("sample4.greeting", "Hello World",
            PHP_INI_ALL, php_sample4_modify_greeting)
PHP_INI_END()

通过在new_value_length为0时返回FAILURE, 这个修改处理器禁止将greeting设置为空字符串. ZEND_INI_MH()宏产生的整个原型如下:

int php_sample4_modify_greeting(zend_ini_entry *entry,
    char *new_value, uint new_value_length,
    void *mh_arg1, void *mh_arg2, void *mh_arg3,
    int stage TSRMLS_DC);

各个参数的含义见下表:


액세스 수준

의미

SYSTEM

php.ini에 있습니다. , 또는 apachehttpd.conf 구성 파일 7b799fe73e35dcfdc019b13f54de80e5017421ec4e1d87ae7f66fefe80084b1d 외부 지시문, 엔진 시동 단계에 영향 ,INI 세트"글로벌".

PERDIR

Apachehttpd.conf 구성 파일 7b799fe73e35dcfdc019b13f54de80e5017421ec4e1d87ae7f66fefe80084b1d 지시어 , 또는 디렉토리를 요청하거나 호스트 및 기타 apacheINI 아래에 스크립트가 .htaccess 파일이 있는 가상 디렉터리입니다. 요청을 처리하기 전에 다른 곳으로 설정 >COMMAND.

USER

스크립트 실행이 시작되면 , 은 사용자 공간 함수 ini_set( ) INI세트 .

参数名

含义

entry

은 엔진의 실제 저장된 INI 명령 항목을 가리킵니다.이 구조는 현재 value , 원래 값 , 해당 모듈이 속한 모듈 , 및 기타 아래코드(zend_ini_entry구조구조)등재정보

new_value

설정할 값.프로세서가 반환되는 경우SUCCESS,이 값은 entry->value,로 설정되며 entry->orig_value 에는 현재 이 설정되어 있지 않습니다. 에서 현재 값을 entry->orig_value로 설정합니다. , entry->modified 태그 를 설정합니다.이 문자열의 길이는 다음과 같습니다. 통과 new_value_length통과.

mh_arg1, 2, 3

3 포인터는 INI 명령이 다음과 같을 때 제공되는 데이터 포인터에 해당합니다. 정의된 (zend_ini_entry3명의 동일한 이름).실제로는 ,이 값은 엔진에서 내부 처리를 위해 사용됩니다,신경쓰지 않아도 됩니다.

스테이지

ZEND_INI_STAGE_ 시리즈 5개 값 중 하나: STARTUP, SHUTDOWN, ACTIVATE, DEACTIVATE, RUNTIME 이 상수는 MINIT, MSHUTDOWN, RINIT, RSHUTDOWN, 및 활성 스크립트 실행 에 해당합니다. .


核心结构体: zend_ini_entry

struct _zend_ini_entry {
    int module_number;
    int modifiable;
    char *name;
    uint name_length;
    ZEND_INI_MH((*on_modify));
    void *mh_arg1;
    void *mh_arg2;
    void *mh_arg3;

    char *value;
    uint value_length;

    char *orig_value;
    uint orig_value_length;
    int modified;

    void ZEND_INI_DISP(*displayer);
};

展示INI设置

在上一章, 你看到了MINFO函数以及相关的指令用于展示扩展的信息. 由于扩展暴露INI指令是很常见的, 因此引擎提供了一个公共的宏可以放置到PHP_MINFO_FUNCTION()中用于展示INI指令信息.

PHP_MINFO_FUNCTION(sample4)
{
    DISPLAY_INI_ENTRIES();
}

这个宏将迭代PHP_INI_BEGIN()和PHP_INI_END()宏之间定义的INI指令集和, 在一个3列的表格中展示它们的INI指令名, 原始值(全局的), 以及当前值(经过PERDIR指令或ini_set()调用修改后)

默认情况下, 所有的指令都直接以其字符串形式输出. 对于某些指令, 比如布尔值以及用于语法高亮的颜色值, 则在展示处理时应用了其他格式. 这些格式是通过每个INI设置的显示处理器处理的, 它和你看到的OnModify一样是一个动态的回调函数指针.

显示处理器可以使用PHP_INI_ENTRY()宏的扩展版指定, 它接受一个额外的参数. 如果设置为NULL, 则使用展示字符串值的处理器作为默认处理器:

PHP_INI_ENTRY_EX("sample4.greeting", "Hello World", PHP_INI_ALL,
    php_sample4_modify_greeting, php_sample4_display_greeting)

显然, 需要在INI设置定义之前声明这个函数. 和OnModify回调函数一样, 这可以通过一个包装宏以及少量编码完成:

#include "SAPI.h" /* needed for sapi_module */
PHP_INI_DISP(php_sample4_display_greeting)
{
    const char *value = ini_entry->value;

    /* 选择合适的当前值或原始值 */
    if (type == ZEND_INI_DISPLAY_ORIG &&
        ini_entry->modified) {
        value = ini_entry->orig_value;
    }

    /* 使得打招呼的字符串粗体显示(当以HTML方式输出时) */
    if (sapi_module.phpinfo_as_text) {
        php_printf("%s", value);
    } else {
        php_printf("<b>%s</b>", value);
    }
}

绑定到扩展的全局空间

所有的INI指令都在Zend引擎内有一块存储空间, 可以用以跟踪脚本内的变更并进行请求外部的全局设置维护. 在这块存储空间中, 所有的INI指令都以字符串值存储. 你已经知道了, 这些值可以使用INI_INT(), INI_FLT(), INI_BOOL()等宏函数, 很简单的翻译成其他的标量类型.

这个查找和转换过程由于两个原因非常低效: 首先, 每次一个INI的值在获取时, 它必须通过名字在一个HashTable中进行定位. 这种查找方式对于仅在运行时编译的用户空间脚本而言是没有问题的, 但是对于已编译的机器代码源, 运行时做这个工作就毫无意义.

每次请求标量值的时候都需要将底层的字符串值转换到标量值是非常低效的. 因此我们使用你已经学习过的线程安全全局空间作为存储媒介, 每次INI指令值变更时更新它即可. 这样, 所有访问INI指令的代码都只需要查找你的线程安全全局空间结构体中的某个指针即可, 这样就获得了编译期优化的优点.

在你的php_sample4.h文件MODULE_GLOBALS结构体中增加const char *greeting; 接着更新sample4.c中的下面两个方法:

ZEND_INI_MH(php_sample4_modify_greeting)
{
    /* Disallow empty greetings */
    if (new_value_length == 0) {
        return FAILURE;
    }
    SAMPLE4_G(greeting) = new_value;
    return SUCCESS;
}
PHP_FUNCTION(sample4_hello_world)
{
    php_printf("%s\n", SAMPLE4_G(greeting));
}

由于这是对INI访问的一种非常常见的优化方式, 因此引擎暴露了一组专门处理INI指令到全局变量的绑定宏:

STD_PHP_INI_ENTRY_EX("sample4.greeting", "Hello World",
    PHP_INI_ALL, OnUpdateStringUnempty, greeting,
    zend_sample4_globals, sample4_globals,
    php_sample4_display_greeting)


这个宏执行和上面你自己的php_sample4_modify_greeting相同的工作, 但它不需要OnModify回调. 取而代之的是, 它使用了一个泛化的修改回调OnUpdateStringUnempty, 以及信息应该存储的空间. 如果要允许空的greeting指令值, 你可以直接指定OnUpdateString替代OnUpdateStringUnempty.

类似的, INI指令也可以绑定long, double, zend_bool的标量值. 在你的php_sample4.h中MODULE_GLOBALS结构体上增加几个字段:

long mylong;
double mydouble;
zend_bool mybool;

现在在你的PHP_INI_BEGIN()/PHP_INI_END()代码块中使用STD_PHP_INI_ENTRY()宏创建新的INI指令, 它和对应的_EX版本的宏的区别只是显示处理器以及绑定到的值不同.

STD_PHP_INI_ENTRY("sample4.longval", "123",
    PHP_INI_ALL, OnUpdateLong, mylong,
    zend_sample4_globals, sample4_globals)
STD_PHP_INI_ENTRY("sample4.doubleval", "123.456",
    PHP_INI_ALL, OnUpdateDouble, mydouble,
    zend_sample4_globals, sample4_globals)
STD_PHP_INI_ENTRY("sample4.boolval", "1",
    PHP_INI_ALL, OnUpdateBool, mybool,
    zend_sample4_globals, sample4_globals)

这里要注意, 如果调用了DISPLAY_INI_ENTRIES(), 布尔类型的INI指令"sample4.boolval"将和其他设置一样, 被显示为它的字符串值; 然而, 首选的布尔值指令应该被显示为"on"或"off". 要使用这些更加表意的显示, 你可以使用STD_PHP_INI_ENTRY_EX()宏并创建显示处理器, 或者使用另外一个宏:

STD_PHP_INI_BOOLEAN("sample4.boolval", "1",
    PHP_INI_ALL, OnUpdateBool, mybool,
    zend_sample4_globals *, sample4_globals)

这个特定类型的宏是布尔类型特有的, 它提供的是将布尔值转换为"on"/"off"值的显示处理器.

小结

在本章, 你了解了php语言中最古老的特性之一的实现, 它也是阻碍php可移植的罪魁. 对于每个新的INI设置, 都会使得编写可移植代码变得更加复杂. 使用这些特性要非常慎重, 因为扩展以后时钟都要使用它了. 并且, 在使用时要注意不同系统间的行为一致性, 以免在维护时出现不可预期的状况.

接下来的三张, 我们将深入到流API, 开始使用流的实现层和包装操作, 上下文, 过滤器等.

以上就是[翻译][php扩展开发和嵌入式]第13章-php的INI设置 的内容,更多相关内容请关注PHP中文网(www.php.cn)!


성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.