JSON「エンコードされた」PHPのこの動的な解析と実行を処理する主な機能は次のとおりです。基本的に、JSONを解析して、変数の指定を順番に識別します。PHPオブジェクトに出くわすと、その中にPHP関数呼び出しが存在することがわかります。関数のパラメーターの配列を反復処理して文字列「ポインター」を探し、それをそれらのポインター値に置き換えます。ポインターは、関数呼び出しへのパラメーターとして送信する必要がある、JSONで以前に定義された変数への単なる参照です。新しいパラメーター配列が作成されたら、それらのパラメーターを使用して要求された関数を呼び出し、その結果をその関数オブジェクト変数定義に戻します。ここで、別の関数呼び出しの引数として使用される場合と使用されない場合があります。
/**
* Iterates over an array containing PHP and handles calls to enabled functions and executes them.
* @param {phpObj} array A JSON decoded array of representational PHP.
* @return {*} Will return the results of the last function call passed in through phpObj.
*/
function parse_php_object( $arr, $config ) {
// We define a pointer array that contains reference names to parameter placeholders
// that will be replaced by real data.
$pointers = array();
foreach ( $arr as $k => $v ) {
// Create variable definition with our first level array keys
${$k} = $v;
// Populate our pointers index
$pointers[$k] = $k;
// When a value is an object we attempt to call functions defined within
if ( is_object( ${$k} ) ) {
// Convert our function object to an array
$funcArr = (Array)${$k};
// Use the first key of the function array as our function name to call
$func_name = array_keys($funcArr);
$func_name = $func_name[0];
// Get the array of arguments to parse to our arguments array
$func_args = $funcArr[$func_name];
// Create an array to store the arguments to pass to our function call
$args_arr = array();
// Now we iterate over our function arguments looking for reference strings
foreach ( $func_args as $arg ) {
// We compare against the keys in our pointers index which was created above
if ( array_key_exists( $arg, $pointers ) ) {
// This is now a reference to ${$k}, the originally defined definition, the returned
// result of the last sucessful function call
$p = ${$arg};
// We push our arguments onto the args_array which will be passed to our function call
array_push( $args_arr, $p );
} else {
// We push our arguments onto the args_array which will be passed to our function call
array_push( $args_arr, $arg );
}
}
// Based on the security mode selected, use either our blacklist or whitelist.
switch ( $config['SEC_MODE'] ) {
case 'blacklist' :
if ( function_exists( $func_name )
&& !in_array( $func_name, $config['LISTS']['blacklist'] ) ) {
$function_allowed = true;
} else {
$function_allowed = false;
}
break;
case 'whitelist' :
if ( function_exists( $func_name )
&& in_array( $func_name, $config['LISTS']['whitelist'] ) ) {
$function_allowed = true;
} else {
$function_allowed = false;
}
break;
}
// Call the requested function if permitted
if ( $function_allowed === true ) {
// Reassign our variable the returned value of a function call so that further function calls can
// search for the existence of pointers and then use the updated variable definitions. This logic
// takes advantage of the procedural nature of PHP and the order of the sub-blocks in the php object.
${$k} = call_user_func_array( $func_name, $args_arr );
} else {
return ("Function you requested $func_name has been disabled by backend configuration.");
}
}
// When we're not an object we're something else like an array, string, int, etc. If we're an array we need
// to recursively iterate over ourselves to convert any objects into arrays.
else {
if ( is_array( ${$k} ) ) {
array_walk_recursive( ${$k}, 'object_to_array' );
}
}
}
// Return the returned result from our final function call
return ${$k};
}
/**
* Converts PHP objects to arrays by typecasting.
* @param {object} Object A self referencing PHP object.
*/
function object_to_array( &$object ) {
if ( is_object( $object ) ) {
(Array)$object;
}
}
コマンドの決定は渡されたデータのタイプに基づいて行われるため、この方法では反射パターンが使用されていると思います。さらに、これは、関数の処理インターフェースを特別に定義する必要なしに、多数のAJAXベースのプロジェクトで使用できます。
もちろん、結果をクライアントに送り返す前に、型チェック、セキュリティテスト、JSONエンコーディングを実行するコードは他にもありますが、上記の関数が大部分の処理を実行します。