Wang Shuai: PHP カーネルの詳細 (1) - 弱く型付けされた変数の原理を探る

WBOY
リリース: 2016-08-08 09:21:11
オリジナル
1027 人が閲覧しました

PHP は、変数の弱い型指定など、Web に適した多くの言語機能を提供するシンプルで強力な言語です。弱い型指定メカニズムでは、変数に任意の型の値を割り当てることができます。
PHP は Zend Engine (以下 ZE と呼びます) を通じて実行され、ZE は C で書かれており、下部に一連の弱い型メカニズムを実装しています。 ZE のメモリ管理では、コピーオンライトや参照カウントなどの最適化戦略を使用して、変数を再割り当てする際のメモリ コピーを削減します。

以下では、PHP の弱い型付けの原理を探るだけでなく、PHP 拡張機能についても書き、PHP 変数の操作方法も紹介します。

1. PHP 変数の型

PHP には 8 つの変数型があります:

  • 標準型: ブール型、整数、浮動小数点浮動小数点、文字列
  • 複合型: 配列、オブジェクト
  • 特殊型: リソース

PHP は変数の型を厳密にチェックしません。変数は表示せずに型を宣言し、実行時に値を直接割り当てることができます。変数も自由に変換できます。次の例のように、実装宣言を行わなくても、$i には任意のタイプの値を割り当てることができます。

[php] ビュー 普通のコピー

  1. $i = 1; //int $i = 'お金を見せてください' //string $i = 0.02; //float $i = array( 1, 2, 3); // 配列 $i = new Exception('test', 123); // オブジェクト $i = fopen('/tmp/aaa.txt', 'a') // リソース ?>
弱い型付けの原理を深く理解していないと、変数を比較するときに「予想を超えた」驚きを感じることになります。

[php] ビュー 普通のコピー

    $str1 = null; echo$str1 == $str2 ? '等しい': '等しくない' = ''$str4; $str3= =$str4 ? '等しい' : $str5 = 0; 「0」; echo $str5==$str6 : 「等しくない」; 上記 3 つの結果すべてPHP は変数を比較するときに内部で変数変換を実行するため、それらは等しいです。値と型を同時に決定したい場合は、= を 3 つ使用して (例: $a===0) 決定してください。もしかしたら、それがありふれたことだと思うかもしれませんし、驚くべきことだと思うかもしれません。ぜひ私と一緒に PHP カーネルを詳しく調べ、PHP 変数の原理を探求してください。 2. 変数ストレージと標準型の概要 PHP のすべての変数は、zval 構造体で実装されています。zval の定義は次のとおりです。 普通のコピー
    1. typedef Union _zvalue_value { long lval; /* long value */ double value */ struct { char *val;常に文字列に設定されます */ } str; /* 文字列 (常に長さがあります) */ HashTable *ht; /* 配列 */ zend_object_value obj; /* オブジェクトを格納しますストアハンドルとハンドラ */} zvalue_value; は参照カウントを意味します1is_ref__gc
    それが参照であるかどうかを示します

    0type

    このうち、refcount__gcとis_ref__gcは、変数が参照かどうかを示します。 type フィールドは、変数のタイプを識別します。type の値は、IS_NULL、IS_BOOL、IS_LONG、IS_FLOAT、IS_STRING、IS_ARRAY、IS_OBJECT、IS_RESOURCE です。 PHP は、タイプに基づいて zvalue_value を格納する方法を選択します。
    zvalue_value は、次のように定義された変数の弱い型のコアを実現できます:

    [php] ビュー 普通のコピー

    1. typedef Union _zvalue_value {long lval; /* double dval; /* double value */ struct { char *val;常に文字列に設定されます */ } str; /* 文字列 (常に長さがあります) */ HashTable *ht; /* 配列 */ zend_object_value obj;ストア ハンドルとハンドラー */ } zvalue_value; ブール型 zval.type=IS_BOOL は、zval.value.lval フィールドを読み取ります。値は 1/0 です。文字列の場合、zval.type=IS_STRING は、文字列ポインタと長さを格納する構造体である zval.value.str を読み取ります。 C言語では「」を使用します。 普通のコピー
    2. typedefstruct_zend_rsrc_list_entry { void *ptr; int type; int refcount; }zend_rsrc_list_entry; 実装への最終ポインタ。 type は、さまざまなリソース タイプを区別するために使用される type タグです。 refcount はリソースの参照カウントに使用されます。

    カーネルでは、リソースタイプは関数 ZEND_FETCH_RESOURCE を通じて取得されます。

    [php]ビュー 普通のコピー

    1. ZEND_FETCH_RESOURCE(con, type, zval *, default, resource_name, resource_type);

    5. 変数の型の変換

    PHP言語、変数 型は zval.type フィールドの指示に依存し、変数の内容は zval.type に従って zval.value に格納されます。 PHP で変数が必要な場合、必要な手順は 2 つだけです。zval.value の値またはポインターを変更し、次に zval.type の型を変更します。ただし、PHP の一部の高度な変数 (配列/オブジェクト/リソース) では、変数の変換にさらに多くの操作が必要です。

    変数の変換原理は 3 つのタイプに分けられます:

    5.1 標準タイプの相互変換

    は比較的単純で、上記の手順に従って変換するだけです。

    5.2 標準型とリソース型の変換

    リソース型は int として理解できるため、標準型を変換するのに便利です。変換後、リソースは閉鎖またはリサイクルされます。

    [php] ビュー 普通のコピー

    1. $var = fopen('/tmp/aaa.txt', 'a' ); // リソース#1 $var = (int) $var($var); // 出力 1 ?>

    5.3 標準型と複合型の変換

    Float は、要素の数を返します。ブール値に変換すると、配列内に要素があるかどうかが返されます。文字列に変換すると、「配列」が返され、警告がスローされます。

    詳細は経験に依存します。PHPマニュアルを読んでください: http://php.net/manual/en/ language.types.type-juggling.php

    5.4 複合型変換

    配列とオブジェクトをそれぞれに変換できます他の。他のタイプの値がオブジェクトに変換される場合、組み込みクラス

    stdClass のインスタンスが作成されます。

    PHP 拡張機能を作成するとき、PHP カーネルは型変換のための一連の関数を提供します:

    value変数
    変数の特定の型
    void Convert_to_long(zval* pzval)void Convert_to_double(zval* pzval) void Convert_to _ロングベース(zval* pzval, int Base)void Convert_to_null(zval* pzval)void Convert_to_boolean(zval* pzval)void Convert_to_array(zval* pzval)void Convert_to_object( zval* pzval)void Convert_object_to_type(zval* pzval, Convert_func_t Converter)
    zval に簡単にアクセスし、より詳細な方法で zval の値を取得するために PHP カーネルによって提供されるマクロのセット:

    zval コンテナ API へのカーネルアクセス マクロ 変数へのアクセス (zval).value.lval (zval).value.dval(zval).value.str.val(zval).value.str len(zval).value.ht(zval).type( *zval).value.lval(*zval).value.dval(*zval).value.str。 val(*zval).value.str.len(*zval).value.ht(*zval).value.obj.handlers(**zval).value.lval(* *zval ).value.dval(**zval).value.str.val(**zval).value。 str.lenZ_ARRVAL_PP(zval_pp)
    Z_LVAL(zval)
    Z_DVAL(zval)
    Z_STRVAL(zval)
    Z_STRLEN(zval)
    Z_ARRVAL(zval)
    Z_TYPE(zval)
    Z_ L VAL_P(ズヴァル)
    Z_DVAL_P(zval)
    Z_STRVAL_P(zval_p)
    Z_STRLEN_P(zval_p)
    Z_ARRVAL_P(zval_p)
    Z_OBJ_HT_P( zval_p)
    Z_LVAL_PP(zval_pp)
    Z_DVAL_PP(zval_pp)
    Z_STRVAL_PP(zval_pp)
    Z_STRLEN_PP(zval_pp)
    (**zval)。

    6. 変数シンボル テーブルとスコープ

    PHP の変数シンボル テーブルと zval 値のマッピングは、HashTable (ハッシュ テーブルとも呼ばれます。以下、HashTable と呼びます) を介して行われ、次のような言語機能が含まれます。定数、変数、関数は HT によって編成され、PHP の配列型も HashTable を通じて実装されます。
    例:

    [php] ビュー 普通のコピー $var の変数名は変数シンボル テーブルに格納され、$ を表します。var の型と値の zval 構造はハッシュ テーブルに格納されます。カーネルは、変数シンボル テーブルと zval アドレスのハッシュ マッピングを通じて PHP 変数へのアクセスを実装します。

    なぜ範囲について言及する必要があるのですか?関数の内部変数が保護されているためです。スコープに応じて、PHP 変数はグローバル変数とローカル変数に分割されます。各スコープの PHP はシンボル テーブルの HashTable を保持します。 PHP で関数またはクラスを作成するとき、ZE は関数またはクラス内の変数がローカル変数であることを示す新しいシンボル テーブルを作成します。これにより、関数内の変数は外部からアクセスできなくなります。 PHP 変数を作成するとき、ZE は zval を割り当て、対応する型と初期値を設定し、ユーザーが変数を使用できるように現在のスコープのシンボル テーブルに変数を追加します。
      ZEND_SET_SYMBOL は変数を設定するためにカーネルで使用されます:
    1. [php] ビュー 普通のコピー ZEND_SET_SYMBOL(EG(active_symbol_table), "foo"
    2. , foo); _zend_executor_globals 構造体を表示します

    [php]ビュー 普通のコピー


    Zend/zend_globals.h

    1. struct _zend_executor_globals { HashTable *active_symbol_table;//ローカル変数のシンボルテーブル //省略 }; PHP 拡張機能を作成するとき、 EG マクロを通じて PHP の変数シンボル テーブルにアクセスできます。 EG (symbol_table) はグローバル スコープの変数シンボル テーブルにアクセスし、EG (active_symbol_table) は現在のスコープの変数シンボル テーブルにアクセスします。ローカル変数には、HashTable を操作するときに対応する関数に渡されるポインターが格納されます。 ハッシュ テーブルと変数のスコープをよりよく理解するために、簡単な例を見てみましょう:
    [php] ビュー 普通のコピー

    $temp
    1. = 'グローバル' test() {
    2. $temp = 'アクティブ'; test(); var_dump(
    $temp

    );

    変数 $temp を関数の外に作成し、グローバル シンボル テーブルに追加し、グローバル シンボルの HashTable に割り当てます。 table 値「global」を持つ文字 zval。関数 test の内部変数 $temp を作成し、それを関数 test に属するシンボル テーブルに追加し、文字タイプ zval に値 'active' を割り当てます。

    7. PHP 拡張機能での変数操作

      PHP 変数を作成する
    1. 拡張機能で関数 MAKE_STD_ZVAL (pzv) を呼び出して、PHP 呼び出し可能な変数を作成できます。 MAKE_STD_ZVAL に適用されるマクロは次のとおりです。 普通のコピー
      1. #define MAKE_STD_ZVAL(zv) ALLOC_ZVAL(zv);INIT_PZVAL(zv) #define ALLOC_ZVAL(z) ZEND_FAST_ALLOC (z, zval, ZVAL_CACHE_LIST) #define ZEND_FAST_ALLOC(p, type, fc_type) (p) = (type * ) emalloc(sizeof(type)) #define INIT_PZVAL(z) (z)->refcount__gc = 1;(z)->is_ref__gc = 0;

      MAKE_ STD_ZVAL(foo) が展開された後、取得:

      [php] ビュー 普通のコピー

      1. (foo) = (zval *) emalloc(sizeof(zval)); (foo)->is_ref__gc = 0; MAKE_STD_ZVAL は、メモリの割り当て、zval 構造体の refcount と is_ref の初期化の 3 つのことを行います。 カーネルには、操作を簡素化するためのいくつかのマクロが用意されており、zval の型と値を 1 ステップで設定できます。

      zvalにアクセスするためのAPIマクロ

      マクロ実装方法Z_TYPE_P(pzv) = Z_TYPE_P(pzv) = IS_BOOL; Z_BVAL_P(pzv) = b ? VAL_FALSE(pzv, 0);ZVAL_LONG(l は値) Z_TYPE_P(pzv) = IS_LONG;Z_LVAL_P(pzv) = l;Z_TYPE_P(pzv) = IS_DOUBLE;Z_LVAL_P(pzv) = d; Z_TYPE_P(pzv) = IS_STRING;Z_STRLEN_P (pzv) = len; if (dup) { {Z_STRVAL_P(pzv) =estrndup(str, len + 1); }zval_stringl(pzv、str、strlen(str)、dup); ZVAL_RESOURCE(pvz, 解像度)


      ZVAL_STRINGL(pzv,str,len,dup) の dup パラメーター

      まず ZVAL_STRINGL(pzv,str,len,dup) について説明します。str パラメーターと len パラメーターは次の場所に保存されていることがわかっているため、理解しやすいです。カーネル 文字列のアドレスとその長さ。これは実際には文字列をコピーする必要があるかどうかを示します。値が 1 の場合、最初に新しいメモリが割り当てられ、文字列が割り当てられます。その後、新しいメモリのアドレスが pzv にコピーされます。値が 0 の場合、str のアドレスが zval に直接割り当てられます。

      ZVAL_STRINGLとZVAL_STRINGの違い

      特定の位置で文字列をインターセプトしたい場合、または文字列の長さがすでにわかっている場合は、明示的に指定されたマクロ ZVAL_STRINGL(zval, string, length, Duplicate) を使用できます。 strlen() を使用する代わりに文字列の長さを指定します。このマクロは文字列の長さを引数として受け取ります。ただし、strlen が 1 つ少ないため、バイナリ セーフであり、ZVAL_STRING よりも高速です。
      ZVAL_RESOURCE は ZVAL_LONG とほぼ等しいです

      第 4 章では、PHP のリソースタイプの値は整数であるため、ZVAL_RESOURCE は、zval のタイプを IS_RESOURCE に設定することを除いて ZVAL_LONG と同様に機能すると述べました。

      8. 概要

      PHP の弱い型付けは、ZE の zval コンテナー変換によって完了しますが、これには操作効率がある程度犠牲になります。また、変数の型が暗黙的に変換されるため、開発プロセス中の変数の型の検出が不十分であると問題が発生する可能性があります。

      ただし、弱い型付け、配列、メモリ ホスティング、拡張機能などの PHP の言語機能は、Web 開発シナリオに非常に適しており、開発効率が非常に高く、製品のイテレーション サイクルを高速化できます。大規模なサービスでは、多くの場合、ボトルネックは言語自体ではなく、データ アクセス層にあります。実際の運用では、PHP はロジック層やプレゼンテーション層として機能するだけでなく、データやキャッシュの中間層としても PHP が開発した UDPServer/TCPServer を使用します。

      上記は、Wang Shuai: In- Depth PHP Core (1) - Exploring the Principle of Weakly Typed Variables をさまざまな側面を含めて紹介したもので、PHP チュートリアルに興味のある友人にとって役立つことを願っています。

      ZVAL_NULL(pvz)
      ZVAL_BOOL(pvz)

      ZVAL_DOU BLE(pvz, d )
      ZVAL_STRINGL(pvz, str, len, dup)
      } else {{z_strval_p(pzv)= str;}}zval_string(pvz、str、len)
      Z_TYPE_P(pzv) = IS_RESOURCE;Z_RESVAL_P(pzv) =
      関連ラベル:
      ソース:php.cn
      このウェブサイトの声明
      この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
      人気のチュートリアル
      詳細>
      最新のダウンロード
      詳細>
      ウェブエフェクト
      公式サイト
      サイト素材
      フロントエンドテンプレート