ホームページ  >  記事  >  php教程  >  タイプヒントの実装

タイプヒントの実装

WBOY
WBOYオリジナル
2016-06-21 08:47:06983ブラウズ

PHP は型指定が弱い言語であり、メソッドにパラメーターを渡すときにデータ型が厳密にチェックされません。 ただし、メソッドに渡されるパラメーターを決定する必要がある場合があります。このため、PHP にはデータの種類を決定するための関数がいくつか用意されています。 たとえば、is_numeric() は、それが数値であるか、またはオブジェクト (instanceof) を決定するために使用される型演算子など、数値に変換できる文字列であるかを決定します。 instanceof は、特定のオブジェクトが指定されたオブジェクト クラスに由来するかどうかを判断するために使用されます。 instanceof 演算子は PHP 5 で導入されました。 これまでは is_a() が使用されていましたが、現在はその使用は推奨されていません。

非標準のオブジェクト型によって引き起こされる問題を回避するために、PHP5 では型ヒントの概念が導入されました。メソッドのパラメータを定義するときは、パラメータのオブジェクト タイプも定義します。 受信パラメータの型が、呼び出し時に定義されたパラメータの型と一致しない場合、エラーが報告されます。このようにして、オブジェクトの種類をフィルタリングしたり、データのセキュリティを保証したりできます。

PHP の型ヒント関数は、パラメーターがオブジェクトであるヒントにのみ使用できますが、整数、文字列、浮動小数点などの型ヒントには使用できません。 PHP5.1 以降、PHP は配列の型ヒントをサポートします。

型ヒントを使用するには、メソッド (または関数) のオブジェクト型パラメーターの前に既存のクラスの名前を追加するだけで、オブジェクト型だけでなく、抽象クラスやインターフェイスも指定できます。

配列の型ヒントの例:

関数 array_print(Array $arr) {

print_r($arr);}

array_print(1);

上記のコードには問題があり、今回導入した型ヒントがトリガーされます。このコードを PHP5.1 以降のバージョンで実行すると、次のエラーが報告されます。

キャッチ可能な致命的なエラー: array_print() に渡される引数 1 は配列である必要があります、

指定された整数、呼び出された ...

関数パラメータの整数変数を配列に変更すると、プログラムは通常どおり実行され、print_r 関数を呼び出して配列を出力します。 では、このタイプヒントはどのように実装されるのでしょうか? クラス内のメソッドであっても、呼び出す関数であっても、関数キーワードはその宣言のマークとして使用され、パラメーターの型は関数の宣言に関連しています。宣言の時刻ですが、呼び出されたときにのみ表示されます。 ここでは、タイプヒントの実装を 2 つの側面から説明します。

パラメータ宣言時の入力ヒント

関数またはメソッドを呼び出すときの入力ヒント

今の例を変更します:

関数 array_print(Array $arr = 1) {

print_r($arr);}

array_print(array(1));

前の例と比較すると、このコードは関数パラメータのデフォルト値を設定しますが、このデフォルト値は整数変数であり、パラメータによって与えられる型ヒント配列とは異なります。そのため、これを実行すると、コードを抜粋すると、プログラムが次のようにエラーを報告することがすぐにわかります:

致命的なエラー: 配列型ヒントを含むパラメーターのデフォルト値

配列または NULL のみを指定できます

なぜすぐにエラーが表示されるのですか? デフォルト値の検出処理は中間コード生成段階で発生するため、実行時のエラー報告とは異なり、まだ中間コードが生成されておらず、中間コードを実行する処理もありません。 Zend/zend_ language_parser.y ファイルでは、関数のパラメータ リストがコンパイル時に zend_do_receive_arg 関数を呼び出すことがわかります。 この関数のパラメーター リストの 5 番目のパラメーター (znode *class_type) は、このセクションで説明する型ヒントと密接に関連しています。 このパラメータの機能は、タイプ ヒントでタイプを宣言することです。ここには 3 つのタイプがあります:

空、つまり型ヒントがありません

クラス名、ユーザー定義または PHP でカスタマイズされたクラス、インターフェイスなど

配列。コンパイル中に対応するトークンは T_ARRAY で、これは配列文字列

です。

zend_do_receive_arg 関数では、基本的に上記の 3 つのタイプのクラス名に対して、class_type パラメーターに対して一連の操作が実行されます。プログラムは、別の For Existing を使用した場合でも、クラスが存在するかどうかを判断しません。クラス名を指定すると、プログラムがエラーを報告するときに、実際のパラメータで指定されたオブジェクトが指定されたクラスのインスタンスではないことも表示されます。

上記は型ヒントの宣言処理と、その宣言処理中にパラメータのデフォルト値を判定する処理です。関数やメソッドを呼び出す際の型ヒントの実装を見てみましょう。

上記の宣言プロセスから、型ヒントに関連するコードをコンパイルするときに、PHP が Zend/zend_complie.c ファイル内の zend_do_receive_arg 関数を呼び出すことがわかります。この関数では、型ヒント判定のオペコードに値 ZEND_RECV が割り当てられます。 。オペコードのマッピング計算規則によれば、実行中に ZEND_RECV_SPEC_HANDLER が呼び出されることがわかります。 コードは次のとおりです:

static int ZEND_FASTCALL ZEND_RECV_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS){

...//省略

if (param == NULL) {

char *space;

char *class_name = get_active_class_name(&space TSRMLS_CC);

zend_execute_data *ptr = EX(prev_execute_data);

if (zend_verify_arg_type((zend_function *) EG(active_op_array), arg_num, NULL, opline->extended_value TSRMLS_CC)) {

…//省略

}

…//省略

} else {

…//省略

zend_verify_arg_type((zend_function *) EG(active_op_array), arg_num, *param, opline->extended_value TSRMLS_CC);

…//省略

}

...//省略}

上に示すように、ZEND_RECV_SPEC_HANDLER の最後の呼び出しは zend_verify_arg_type です。コードは次のとおりです:

static inline int zend_verify_arg_type(zend_function *zf, zend_uint arg_num, zval *arg, ulong fetch_type TSRMLS_DC){

...//

を省略

if (cur_arg_info->class_name) {

const char *class_name;

if (!arg) {

need_msg = zend_verify_arg_class_kind(cur_arg_info, fetch_type, &class_name, &ce TSRMLS_CC);

zend_verify_arg_error(zf, arg_num, cur_arg_info, need_msg, class_name, "none", "" TSRMLS_CC) を返します。

}

if (Z_TYPE_P(arg) == IS_OBJECT) { // クラスオブジェクトパラメータであるため、渡されるパラメータはオブジェクトタイプである必要があります

// 次に、このオブジェクトがパラメータ プロンプト クラスのインスタンス オブジェクトであるかどうかを確認します。ここでは、サブクラス インスタンス オブジェクトを渡すことが許可されています。

need_msg = zend_verify_arg_class_kind(cur_arg_info, fetch_type, &class_name, &ce TSRMLS_CC);

if (!ce !instanceof_function(Z_OBJCE_P(arg), ce TSRMLS_CC)) {

}

} else if (Z_TYPE_P(arg) != IS_NULL !cur_arg_info->allow_null) { // パラメーターは NULL ですが、これもチェックに合格します。

need_msg = zend_verify_arg_class_kind(cur_arg_info, fetch_type, &class_name, &ce TSRMLS_CC);

zend_verify_arg_error(zf, arg_num, cur_arg_info, need_msg, class_name, zend_zval_type_name(arg), "" TSRMLS_CC) を返します

}

} else if (cur_arg_info->array_type_hint) { // 配列

if (!arg) {

;

}

if (Z_TYPE_P(arg) != IS_ARRAY && (Z_TYPE_P(arg) != IS_NULL !cur_arg_info->allow_null)) {

}

}

1 を返します;}

zend_verify_arg_type のプロセス全体を図 3.1 に示します。

図3.1 タイプ即時判定フローチャート

型ヒントがエラーを報告する場合、zend_verify_arg_type 関数は最終的に zend_verify_arg_class_kind を呼び出してエラー メッセージを生成し、zend_verify_arg_error を呼び出してエラーを報告します。コードは次のとおりです:

static inline char * zend_verify_arg_class_kind(const zend_arg_info *cur_arg_info, ulong fetch_type, const char **class_name, zend_class_entry **pce TSRMLS_DC){

*pce = zend_fetch_class(cur_arg_info->class_name, cur_arg_info->class_name_len, (fetch_type ZEND_FETCH_CLASS_AUTO ZEND_FETCH_CLASS_NO_AUTOLOAD) TSRMLS_CC);

*class_name = (*pce) ? (*pce)->name: cur_arg_info->class_name;

if (*pce && (*pce)->ce_flags & ZEND_ACC_INTERFACE) {

"実装インターフェイス" を返します;

} else {

return "のインスタンスになる;

}}

static inline int zend_verify_arg_error(const zend_function *zf, zend_uint arg_num, const zend_arg_info *cur_arg_info, const char *need_msg, const char *need_kind, const char *given_msg, char *given_kind TSRMLS_DC){

zend_execute_data *ptr = EG(current_execute_data)->prev_execute_data;

char *fname = zf->common.function_name;

char *fsep;

char *fclass;

if (zf->common.scope) {

fsep = "::";

fclass = zf->common.scope->name;

} else {

fsep = "";

fclass = "";

}

if (ptr && ptr->op_array) {

zend_error(E_RECOVERABLE_ERROR, "%s%s%s() に渡される引数 %d は %s%s でなければなりません。%s%s が指定され、%d 行目の %s で呼び出され、定義されています", arg_num, fclass, fsep 、fname、need_msg、need_kind、given_msg、given_kind、ptr->op_array->filename、ptr->opline->lineno);

} else {

zend_error(E_RECOVERABLE_ERROR, "%s%s%s() に渡される引数 %d は %s%s、%s%s が指定されている必要があります", arg_num, fclass, FSEP, fname, need_msg, need_kind, give_msg, Given_kind) ;

}

0 を返す;}

上のコードでは、前のメッセージの引数に渡されたり、呼び出されたりすることができます。これは、関数またはメソッドを使用するときに、メッセージの最終実行位置を表示する型です。 🎜>



声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。