ホームページ > バックエンド開発 > PHPチュートリアル > phpの拡張と埋め込み--php内部変数_PHPチュートリアル

phpの拡張と埋め込み--php内部変数_PHPチュートリアル

WBOY
リリース: 2016-07-13 17:18:42
オリジナル
1039 人が閲覧しました

以前、PHP の内部ライフサイクルと Zend エンジンのスレッド セーフティ メカニズムについて紹介しましたが、この記事では主に PHP の内部変数がどのように実装されるかを紹介します。

これらの実装方法を理解すると、PHP の作成、特に PHP 拡張機能の開発に非常に役立つと感じます。


php は C に比べて型指定が比較的緩やかな言語で、変数を使用する前に型を指定する必要がなく、変数を直接使用できます。これを実現するために、PHP はデータ型の定義に関して何らかの作業を行う必要があります。

データ型:

最も基本的なタイプは zval または Zend Value と呼ばれ、Zend/zend.h ヘッダー ファイルで定義されます。 typedef struct _zval_struct {
zvalue_value 値;
zend_uint refcount;
zend_uchar タイプ;
zend_uchar is_ref;
zval; このうち、zvalue_value は次のように定義されます。 typedef Union _zvalue_value {
長い lval;
ダブル dval;
構造体{
char *val;
イントレン;
} str;
ハッシュテーブル *ht;
zend_object_value obj;
zvalue_value; これは共用体です。共用体を使用する場合は 2 つの可能性があります。1 つはすべての変数がメモリ空間を共有する場合、もう 1 つは n からいずれかの型を選択する必要がある場合です。
Zend では 8 つの基本的なデータ型が定義されています。これらの 8 つは基本的に他の言語でも見られるため、より特殊な型についてのみ説明します。 IS_NULL: 非値IS_BOOL: IS_LONGIS_DOUBLEIS_STRING: 割り当てられるスペースは長さ + 1 である必要がありますIS_ARRAY: PHP の配列は実際には、ラベルとデータを含むハッシュテーブルですIS_OBJECT: 配列に基づいて、メソッド、アクセス修飾子、ドメイン定数、特殊文字が追加されますイベント ハンドラー IS_RESOURCE: たとえば、ファイル ハンドルまたは mysql ハンドルは zval の型に格納され、zvalue_value と個別に対応します。 以下に 2 つの型判定関数の比較があることに注意してください。 リーリー そして れーれー
最初のコードは C で書かれ、2 番目のコードは PHP スタイルで書かれています。 Zend ヘッダー ファイルには zval 処理用のマクロが多数用意されていることに注意してください。ここでは Z_TYPE_P(foo) を使用します。それぞれ zval と zval** に対応する Z_TYPE() と Z_TYPE_PP() もあります php_printf() は printf に基づいており、SAPI および PHP 出力メカニズム用にいくつかの最適化を行います。


データ値

さまざまなタイプの zval の値は、いくつかのマクロを通じて取得できます。 BVAL(): ブールLVAL(): longDVAL(): double この関数は Z_TYPE を使用して、3 つの異なる zval 型の型判定を実行します。次に、対応する値抽出マクロを使用して値を取得します。 れーれー
文字列の処理は少し特殊です。値と長さをそれぞれ読み取るために 2 つのマクロ Z_STRVAZ_STRLEN が必要です。これは、文字と長さで構成される文字列タイプの定義からもわかります。 リーリー
配列へのアクセスには、ARRVAL シリーズ: Z_ARRVAL(zv)、Z_ARRVAL_P(pzv)、Z_ARRVAL_PP(ppzv) を使用します。 PHP ソース コードの一部のバージョンでは、HASH_OF() は Z_ARRVAL_P と同等ですが、このマクロは徐々に使用されなくなりました。
オブジェクトの場合: OBJ_HANDLE は、オブジェクト ハンドル識別子を返します。 OBJ_HT ハンドル テーブル OBJCE クラス定義 OBJPROP 属性ハッシュ テーブル OBJ_HANDLER は、OBJ_HT 内の特定の処理メソッドを操作します リソースの場合は、マクロ RESVAL を直接使用します。

データ作成:

想要创造一个变量并分配空间的malloc(sizeof(zval))在php这里并不可行。应该使用MAKE_STD_ZVAL(pzv), 它对空间的分配进行了优化,并且会自动的初始化refCount(表示这个变量被引用的次数)和is_ref(是否是强制引用)这两个性质。注意它的输入是一个指针.
ALLOC_INIT_ZVAL()也可以进行初始化,不同之处在于把zval*的值设为了NULL.
在设置不同类型的值的时候有很多形式,左边是比较简略的形式,右侧则是展开的形式: ZVAL_NULL(pvz); Z_TYPE_P(pzv) = IS_NULL;
ZVAL_BOOL(pzv, b); Z_TYPE_P(pzv) = IS_BOOL;
Z_BVAL_P(pzv) = b ? 1 : 0;
ZVAL_TRUE(pzv); ZVAL_BOOL(pzv, 1);
ZVAL_FALSE(pzv); ZVAL_BOOL(pzv, 0);
ZVAL_LONG(pzv, l); Z_TYPE_P(pzv) = IS_LONG;
Z_LVAL_P(pzv) = l;
ZVAL_DOUBLE(pzv, d); Z_TYPE_P(pzv) = IS_DOUBLE;
Z_DVAL_P(pzv) = d;
对于字符串的处理要特殊一些,提供了一个单独的参数dup. 这个参数决定了是否创建一个字符串的副本.举个例子 zval * pzva; ZVAL_STRING(pzval,"hello world",1); 由于“hello_world”是一个常量字符串,直接对它进行操作显然不合适,所以把dup设为1的话,会自动的给它创建一个副本,然后再赋给pzval. 这使得整个过程更加简洁。

ZVAL_STRINGL(pzv,str,len,dup); Z_TYPE_P(pzv) = IS_STRING;
Z_STRLEN_P(pzv) = len;
if (dup) {
Z_STRVAL_P(pzv) =
estrndup(str, len + 1);
} else {
Z_STRVAL_P(pzv) = str;
}
ZVAL_STRING(pzv, str, dup); ZVAL _STRINGL(pzv, str,
strlen(str), dup); 注意dup如果设为1的话就是申请新的空间并且拷贝内容,而不是一个shaddow copy。 ZVAL_RESOURCE(pzv, res); Z_TYPE_P(pzv) = IS_RESOURCE;
Z_RESVAL_P(pzv) = res;


数据的存储

数据的存储都在符号表中。 symbol table,每当创建一个新的变量的时候,Zend都保存这个值到这个内部的数组中去。 符号表在RINIT之前创建,在RSHUTDOWN之后销毁。
当用户空间的函数或对象方法被调用的时候,会创建一个新的符号表,生命与函数执行时间相同。 在Zend/zend_gblobals.h中定义了两个元素:
struct _zend_executor_globals {
    ...
    HashTable symbol_table;
    HashTable *active_symbol_table;
    ...
};
ログイン後にコピー

通过EG(symbol_table) 的方式可以访问符号表。感觉跟$GLOBALS似的。 注意到EG(symbol_table)这个宏返回的不是指针,必须加上&。 下面的这个对比非常的有趣: In PHP:
<?php $foo = 'bar'; ?>
ログイン後にコピー
针对这段php的代码,C中一共做了如下这些事:
In C:
{
    zval *fooval;
    MAKE_STD_ZVAL(fooval); //首先分配空间,设置变量
    ZVAL_STRING(fooval, "bar", 1); //然后赋值,创建一个copy,你不能直接操作常字符串
    ZEND_SET_SYMBOL(EG(active_symbol_table), "foo", fooval); // 在符号表中注册,foo是一个label
}
ログイン後にコピー
所谓active_symbol_table指的是程序执行当前的符号表,在进入一个函数之后,会有它自己对应的符号表,就类似于C中针对一个函数自己的栈空间。而当退出了函数之后,它的符号表会被销毁,这时候又回到了下面这个状态: EG(active_symbol_table) == &EG(symbol_table), 这个时候并没有进入函数。

数据的获取: 在获取数据的时候,比较多的是使用zend_hash_find()函数:

{
    zval **fooval;
    if (zend_hash_find(EG(active_symbol_table),
                       "foo", sizeof("foo"),
                       (void**)&fooval) == SUCCESS) {
        php_printf("Got the value of $foo!");
    } else {
        php_printf("$foo is not defined.");
    }
}
ログイン後にコピー
这个函数首先查找符号表,找到名字为“foo”的变量,然后返回到fooval中。下面着重解释两个问题:

为什么要声明一个zval ** fooval 然后还要通过&fooval并且转换为(void **)的形式?为什么要用sizeof("foo") 对第一个问题,要考虑到我们寻找的目标是一个zval*,所以要把它看作一个整体。利用这种写法可以避免编译告警。 第二个问题,使用sizeof(label)主要是为了表示字符串常量label的尾部,这里使用4也是可以的,但是通用性不够。

数据转换: 仅仅是说一下有这个功能,比如convert_to_string(zval *value)可以把zval转换为字符串。


以上就是php内部变量的一些介绍,为了能够区分不同的类型、设置获取变量值以及在符号表中增加和查找变量,这些知识必不可少。




www.bkjia.comtru​​ehttp://www.bkjia.com/PHPjc/621628.html技術記事以前に PHP の内部ライフサイクルと Zend エンジンのスレッド セーフティ メカニズムについて紹介しましたが、この記事では主に PHP の内部変数がどのように実装されるかを紹介します。 わかりました...
関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート