ホームページ  >  記事  >  バックエンド開発  >  [翻訳][php拡張機能の開発と組み込み] 第13章-php INI設定

[翻訳][php拡張機能の開発と組み込み] 第13章-php INI設定

黄舟
黄舟オリジナル
2017-02-10 10:17:041007ブラウズ


INI 設定

前の章で見たスーパーグローバル変数と永続定数と同様に、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 関数の上にあります。次のマクロは完全に独立して定義できます。これら 2 つのマクロの間に 1 つ以上の INI 命令を定義できます:

PHP_INI_BEIGN()
PHP_INI_END()

これら 2 つのマクロ関数は、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 セットアップ

これで、INI 命令とエンジンの登録/アンインストール INI 設定メカニズムを定義するために使用される INI 構造体が完成しました。実際には、拡張機能の INI 命令をいくつか定義できます。ただし、第 5 章「最初の拡張機能」と同じように、拡張機能が挨拶関数を公開しているとします。挨拶したい場合は、次のようにカスタマイズできます。最も直接的な方法は、INI コマンドを定義してデフォルト値「Hello world!」を与えることです:

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

You ご想像のとおり、このマクロの最初の 2 つのパラメータは、 INI 命令の名前とそのデフォルト値。3 番目のパラメータは、エンジンがこの INI 命令の変更を許可するかどうかを決定するために使用されます (これには、この章の後半で説明するアクセス レベルが関係します)。 INI 命令の値が変更されるたびにこのパラメータが呼び出されます。このパラメータの詳細については、「イベントの変更」セクションを参照してください。テストするときは、MINIT() 関数に「REGISTER_INI_ENTRIES();」呼び出しを追加し、MINIT にグローバル領域を割り当てた後に呼び出しが実行されるようにしてください。

これで INI 設定が完了しました。が定義されているため、挨拶関数で使用するだけで済みます。

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

char * の値はエンジンによって所有されており、変更できないことに注意してください。このため、ローカル変数を定義してください。もちろん、すべての INI 値が文字列であるわけではありません。整数、浮動小数点、およびブール値を取得するためのマクロもあります:

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

通常、知りたいのは INI 設定の現在の値ですが、補足として、未変更の INI 設定値を読み取るために使用できるいくつかのマクロが存在します。

この例では、INI 命令「sample4.greeting」の名前には、他の拡張機能によって公開される INI 命令名と競合しないように、拡張子が接頭辞として付けられています。必要ですが、商用またはオープンソース リリースの公開拡張ではこれが推奨されます

INI 命令の場合、最初は常にデフォルト値があります。デフォルトを維持する場合、値は変更されません。ただし、一部の特殊な環境またはスクリプト内の特定のアクションでは、これらの値を変更する必要がある場合があります。以下の表に示すように、INI 命令の値を変更する必要があります。次の 3 つのポイント:

アクセスレベル


意味phpにあります.ini

システム

または
Apache

7b799fe73e35dcfdc019b13f54de80e5および017421ec4e1d87ae7f66fefe80084b1dのhttpd.conf設定ファイル内 外部ディレクティブ エンジンの始動段階に影響を与えるが考えられますINI"global"value.によって設定されますペルディル

は、httpd.conf 設定ファイル 7b799fe73e35dcfdc019b13f54de80e5 および 017421ec4e1d87ae7f66fefe80084b1d またはディレクトリにあります。リクエストスクリプトが存在するか、リクエスト.USERを処理する前に、仮想ホストの下の.htaccessファイルと他のapacheディレクティブINI

1回スクリプトの実行が開始されます

はユーザー空間関数
ini_set()を呼び出すことによってのみ

INIset.を変更できます


某些设置如果可以在任何地方被修改就没有多大意义了, 比如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);

各个参数的含义见下表:


価値

参数名

含义

entry

は、エンジンに実際に保存されているINIコマンド項目を指します。この構造体は、現在の値元の値をどのモジュールに提供しますかといくつかその他 次のコード(zend_ini_entry Structure Structure)は情報をリストします

new_value

設定される値 プロセッサーが戻ったら SUCCESS,この値はentry->value,に設定されます。同時に、entry->orig_valueが現在設定されていない場合、,は現在の値を設定しますvalue を ,entry->orig_value に設定し、entry->modified タグ . を設定します。この文字列の長さは、. 経由で渡されます。 new_value_length

mh_arg1、2、3

これらの3ポインタは、INI命令が定義されるときに与えられるデータポインタに対応します(zend_ini_entry内の同じ名前のメンバー3) 構造)。 実際、これらの値は、内部処理によって使用されます。これらを気にする必要はありません _

のいずれか。 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 までご連絡ください。