ホームページ >バックエンド開発 >PHPチュートリアル >タイプヒントの実装_PHP チュートリアル

タイプヒントの実装_PHP チュートリアル

WBOY
WBOYオリジナル
2016-07-13 10:05:29752ブラウズ

型ヒントの実装

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 パラメーターに対して一連の操作が実行されます。存在しないクラスを使用した場合でも、プログラムはクラスが存在するかどうかを判断しません。クラス名。プログラムがエラーを報告すると、実パラメータで指定されたオブジェクトが指定されたクラスのインスタンスではないことも表示されます。
上記は型ヒントの宣言とその宣言過程でのパラメータのデフォルト値の判定の処理です。 次に、関数やメソッドを呼び出す際の型ヒントの実装を見てみましょう。
上記の宣言プロセスから、型ヒントに関連するコードをコンパイルするときに、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 *クラス名;
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)) {
return zend_verify_arg_error(zf, arg_num, info, need_msg, class_name, "のインスタンス", Z_OBJCE_P(arg)->name 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) {
ZEND_VERIFY_ARG_ERROR (ZF, ARG_NUM, CUR_ARG_INFO, "配列であること", "", "なし", "TSRMLS_CC)" を返します。
}
if (Z_TYPE_P(arg) != IS_ARRAY && (Z_TYPE_P(arg) != IS_NULL || !cur_arg_info->allow_null)) {
ZEND_VERIFY_ARG_ERROR (ZF, ARG_NUM, Cur_ARG_INFO, "配列であること", "", ZEND_ZVAL_TYPE_NAME (ARG), "TSRMLS_CC); を返します。
}
}
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);
*クラス名 = (*pce) ? (*pce)->名前: cur_arg_info->クラス名;
if (*pce && (*pce)->ce_flags & ZEND_ACC_INTERFACE) {
return "インターフェースを実装";
} 他 {
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;
} 他 {
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->ファイル名、ptr->opline->lineno);
} 他 {
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を返す;}
上のコードでは、前のブロックメッセージ内のいくつかの関連する引数、渡される、呼び出されるなどを見つけることができます。

http://www.bkjia.com/PHPjc/963684.htmlwww.bkjia.comtru​​ehttp://www.bkjia.com/PHPjc/963684.html技術記事タイプが提案する実装​​ PHP は弱いタイプの言葉であり、メソッドにパラメータを渡すときにも、データのタイプを検査する必要はなく、このために PHP で提供されます。
声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。