開発プロセス中、関数の戻り値の型は決定され、変更されなければなりませんが、PHP は型指定が弱い言語です。
そのため、PHP にはそのような構文検証がありません。そのため、多くの落とし穴が発生しました。
たとえば、次のコード:
<?php function getArticles(…){ $arrData = array(); if($exp1){ return $arrData; }else if($exp2){ return 1; }else{ return false; } } $arrData =getArticles(…); foreach($arrData as $record){ //do something. …. } ?>
関数 getArticles は、bool、int、配列など、さまざまな条件に従ってさまざまなタイプの値を返します。通常、このタイプの関数は配列を返し、それを使用します。
しかし、関数の戻り値の型が固定されていないため、呼び出し時にさまざまな予期せぬ落とし穴が発生する可能性があり、
それでは、標準化できないので、それを強制するだけだと思いました。
図に示すように、関数/メソッドの戻り値は強制型にすることができます。
戻り値が int、array、bool、object の 4 つの必須の型制限をサポートします。関数宣言を実行すると警告がスローされます。本来はエラーをスローしたかったのですが、
はエラーではなく例外としかみなせないと感じたので、代わりに warning を使用しました。
PHP 自体は int 関数のような構文をサポートしていないため、サポートしたい場合は、まず構文パーサーを入手する必要があります。構文パーサーについては、ここで
の詳細を確認できますが、ここでは説明しません。はい、
まず Zend/zend_ language_scanner.l ファイルをスキャンするように構文を変更します
次のコードを追加します:
<ST_IN_SCRIPTING>”int” { return T_FUNCTION_RETURN_INT; } <ST_IN_SCRIPTING>”bool” { return T_FUNCTION_RETURN_OBJECT; } <ST_IN_SCRIPTING>”object” { return T_FUNCTION_RETURN_OBJECT; } <ST_IN_SCRIPTING>”resource” { return T_FUNCTION_RETURN_RESOURCE; }
スキャナーがキーワード int、bool、object、resource、およびをスキャンするときの意味は非常に簡単です。配列の場合、対応する T_FUNCTION_* を返します。これはトークンです。
スキャナーは、最初に Zend/zend_ language_parser.y ファイルに定義する必要があります
………. %token T_FUNCTION_RETURN_INT %token T_FUNCTION_RETURN_BOOL %token T_FUNCTION_RETURN_STRING %token T_FUNCTION_RETURN_OBJECT %token T_FUNCTION_RETURN_RESOURCE 1 然后增加token处理逻辑: 1 function: T_FUNCTION { $$.u.opline_num = CG(zend_lineno);$$.u.EA.var = 0; } | T_FUNCTION_RETURN_INT T_FUNCTION { $$.u.opline_num = CG(zend_lineno); $$.u.EA.var = IS_LONG; } | T_FUNCTION_RETURN_BOOL T_FUNCTION { $$.u.opline_num = CG(zend_lineno); $$.u.EA.var = IS_BOOL; } | T_FUNCTION_RETURN_STRING T_FUNCTION { $$.u.opline_num = CG(zend_lineno); $$.u.EA.var = IS_STRING; } | T_FUNCTION_RETURN_OBJECT T_FUNCTION { $$.u.opline_num = CG(zend_lineno); $$.u.EA.var = IS_OBJECT; } | T_FUNCTION_RETURN_RESOURCE T_FUNCTION { $$.u.opline_num = CG(zend_lineno); $$.u.EA.var = IS_RESOURCE; } | T_ARRAY T_FUNCTION { $$.u.opline_num = CG(zend_lineno); $$.u.EA.var = IS_ARRAY; }
$$ .u.EA.var は関数の戻り値の型を保存し、最終的にそれを使用して戻り値の型
を照合し、構文インタープリターが新しい PHP 構文を処理できるようにします。
これだけでは十分ではありません。関数宣言で定義された処理ロジックも変更する必要があります
Zend/zend_compile.c ::zend_do_begin_function_declaration …… zend_op_array op_array; char *name = function_name->u.constant.value.str.val; int name_len = function_name->u.constant.value.str.len; int function_type = function_token->u.EA.var; //保存函数类型,在语法解释器中增加的: $$.u.EA.var = IS_LONG; int function_begin_line = function_token->u.opline_num; …… op_array.function_name = name; op_array.fn_type = function_type; //将类型保存到op_array中, op_array.return_reference = return_reference; op_array.fn_flags |= fn_flags; op_array.pass_rest_by_reference = 0; ……….
PHP はまず PHP 構文を解析して対応するオペコードを生成し、必要な環境とパラメーターの情報をexecute_data グローバル変数に保存し、最後に実行関数オペコードを通じてそれらを 1 つずつ実行します。
そのため、処理を行うには、関数タイプをオペコードに保存する必要があります: op_array.fn_type = function_type;
op_array には fn_type がありません。op_array の構造を変更するには、以下を追加します。 zend_uint fn_type;
(オペコードについては、C からアセンブリへの変換を想像してください。私のブログにも関連記事がありますので、参照してください)
最後に、オペコードの destroy 関数を変更する必要があります。この関数はトークン T_RETURN を生成し、T_RETURN は返された型に応じて異なるコールバック関数を呼び出します。 :
ZEND_RETURN_SPEC_CONST_HANDLER ZEND_RETURN_SPEC_TMP_HANDLER ZEND_RETURN_SPEC_VAR_HANDLER
戻り値が const 型データの場合、戻り値は一時データです。例: return 1、次に ZEND_RETURN_SPEC_TMP_HANDLER
戻り値は変数です。次のようなものです: return $a、次に ZEND_RETURN_SPEC_VAR_HANDLER
したがって、これら 3 つのコールバック関数に処理ロジックを追加する必要があります:
if((EG(active_op_array)->fn_type > 0) && Z_TYPE_P(retval_ptr) != EG(active_op_array)->fn_type){ php_error_docref0(NULL TSRMLS_DC,E_WARNING, “function name %s return a wrong type.”, EG(active_op_array)->function_name ); }