このエントリーをはてなブックマークに追加

更新日: 2016年2月25日

実行時間: 0.0240

CI_Xmlrpc   CI_Xmlrpcs

 XML RPCは、主にサーバ間でデータの送受信を行うためのプロトコルです。
 データはXMLフォーマットでRPCプロトコルによって伝送します。

  • データの取得

 クライアントからリクエストを受けたWebサーバ(Webアプリケーション)は、別のデータリソースサーバ から「XML RPC」プロトコル通信によってデータ(XML)を取得し、そのデータを加工(HTML)して返します。

  • データの送信

 クライアントからリクエストを受けたWebサーバ(Webアプリケーション)は、データをXMLに加工して
 「 XML RPC サーバ 」に送信し、「 XML RPC サーバ 」は受信したデータをDBに保存するなどの処理を行います。


XML RPC

リクエスト・レスポンスデータ

 CI_Xmlrpc・CI_Xmlrpcsライブラリクラスを利用したデータの送受信方法を理解する前に、
まずは、これらのライブラリクラスで送受信するデータのセット方法を覚えておく必要があります。

 XML RPC では、クライアントからのリクエストデータは、複数のデータをサーバに渡すことができますが、 サーバからのレスポンスは1つのデータしか返すことができません。
 これは、複数のデータを引数として受け取り、1つのデータを返すプログラムの関数の動きと同様です。

 データ型

 XML RPC で扱えるデータの型には以下で示す8つがあり、複数のデータをまとめて送受信することができます。  また、これらのデータ型のデータを混在させることも可能です。

説明
int または i4 整数
double 倍精度浮動小数点数
boolean 論理値
string 文字列
base64 Base64エンコードデータ
dateTime.iso8601 日時(ISO 8601)
array 配列
struct 連想配列

 データの定義

 これらの型のデータは、2つの要素を持つ配列として定義していきます。
 配列の第一要素に「」、第二要素にその値の「」を指定します。
 以下がその例になります。

int または i4

 第一要素に「整数値」、第二要素に「int」または「i4」を指定します。

    array(123, 'int');

double

 第一要素に「浮動小数点数値」、第二要素に「double」を指定します。

    array(-123.092, 'double');

boolean

 第一要素に「0」または「1」、第二要素に「boolean」を指定します。

    array(0, 'boolean'); // FALSE
    array(1, 'boolean'); // TRUE

string

 第一要素に「文字列」、第二要素に「string」を指定します。

    array('ABC', 'string');

base64

 第一要素に「base64エンコード値」、第二要素に「base64」を指定します。

    array (
        'Q29kZUlnbml0ZXK5laykyQE=',
        'base64',
    );

dateTime.iso8601

 第一要素に「日時を表す文字列」、第二要素に「dateTime.iso8601」を指定します。
 日時は「年月日」と「時:分:秒」の間を「T」で区切る「ISO-8601」フォーマットです。

    array (
        '20120905T12:15:00',
        'dateTime.iso8601',
    );

array

 第一要素に「配列データ」、第二要素に「array」を指定します。

    array (
        array (
            array('ABC', 'string'),
            array('XYZ', 'string')
        ),
        'array'
    );

struct

 第一要素に「連想配列データ」、第二要素に「struct」を指定します。

    array (
        array (
            'name' => array('Ichiro', 'string'),
            'score' => array(89, 'int')
        ),
        'struct'
    );

XML RPC クライアント (CI_Xmlrpc)

 通常Webサーバが「XML RPC クライアント」となり、ブラウザ(HTTPクライアント)からのリクエストなどをトリガー として、「XML RPC サーバ」にリクエストを送り、そのレスポンスを取得します。

 XML RPC クライアントは、XMLフォーマットのデータをXML RPC サーバに送信し、 XML RPC サーバからXMLフォーマットのデータをレスポンスとして受信します。

 CodeIgniterでは、「 CI_Xmlrpc 」がXML RPC クライアントの機能を提供します。

送信データ

 送信データは、配列で構成し、配列の要素数に制限はなく必要な数だけ定義することができます。

  • [ 文字列 ]
  • 1つの[ 文字列 ]データからなる送信データ
    $request_data = array(
        array('abc', 'string');
    );
    
  • [ 文字列 , 数値 , 配列 ]
  • [ 文字列 , 数値 , 配列 ]の3つのデータからなる送信データ
    $request_data = array(
        array('abc', 'string'),
        array(123, 'int'),
        array(
            array(
                array('XYZ', 'string'),
                array('ABC', 'string')
            )
        ), 'array'),
    );
    

リクエスト送信からレスポンス受信までの手順

 CI_Xmlrpc ライブラリクラスによるリクエスト送信からレスポンス受信までの大まかな手順は以下のようになります。

    1. CI_Xmlrpcライブラリクラスのロード
    2. XML RPC サーバURIの指定
    3. RPCメソッドの指定
    4. 送信データのセット
    5. リクエスト送信
    6. レスポンスデータの取得
    7. (エラーメッセージの取得)
    // 1. CI_Xmlrpcライブラリクラスのロード
    $this->load->library('xmlrpc');

    // 2. XML RPC サーバURIの指定
    $this->xmlrpc->server('http://your.site/hoge_class/foo_action/');

    // 3. RPCメソッドの指定
    $this->xmlrpc->method('get_user_data');

    // 4. 送信データのセット
    $user_ids = array (
        array('5012', 'string'),
        array('2520', 'string'),
        array('7133', 'string'),
    );
    $this->xmlrpc->request($user_ids);

    // 5. リクエスト送信
    if ($this->xmlrpc->send_request()) {
        // 6. レスポンスデータの取得
        $user_data = $this->xmlrpc->display_response();
    } else {
        // 7. エラーメッセージの取得
        $error_msg = $this->xmlrpc->display_error();
    }

XML RPC サーバ (CI_Xmlrpcs)

 XML RPC サーバは、XML RPC クライアントから「XML」データをリクエストとして受け取り、 レスポンスの「XML」データを生成してクライアントに返します。

XML RPC メソッド

 「XML RPC クライアント」(xmlrpc)の「server()」メソッドで指定したURIは、単にHTTPリクエストを受け付ける CodeIgniter上のコントローラとアクションメソッドです。

 若干、紛らわしいのですが、このアクションメソッドでは、「XML RPC サーバ」(xmlrpcs)のロードと
 初期化を行い、XMLデータを「XML RPC メソッド」に送ります。
 そして、「XML RPC メソッド」が「XMLリクエストデータ」の処理を行い、レスポンスデータを生成して CodeIgniter上のコントローラのアクションメソッドに返し、アクションメソッドがクライアントに出力します。

 XML RPC メソッドは、「リモートで呼び出す関数」として考えると理解しやすいでしょう。
 このように考えると、XML RPC メソッドもプログラムの関数と同様に、複数のデータを受け付け、 1つのデータを返すという動きも理解しやすいと思います。


XML RPC メソッドのマッピング

 「XML RPC クライント」(xmlrpc)の「method()」メソッドで指定した「XML RPC メソッド」名と、 実際に処理を行う「XML RPC クラス」「XML RPC メソッド」のマッピングを行う必要があります。

 例えば、「XML RPC クライアント」(xmlrpc)の「method()」メソッドで「putData」という
「XML RPC メソッド」名を指定した場合、それに対応する実際のクラスとメソッドを以下のように
指定します。

  • XML RPC クライアント
  • $this->xmlrpc->method('putData');
  • XML RPC サーバ
  • $config['functions']['putData'] = array('function' => 'UserApi.put');
  • XML RPC クラス・メソッド
  • /* application/controllers/UserApi.php */

    class UserApi extends CI_Controller {
        public function put($request)
        {
            // XMLデータ処理
        }
    }

 XML RPC メソッドのマッピング方法には3通りあります。

  1. xmlrpcsコンストラクタで指定
  2. $this->load->libarary('xmlrpcs', $config);
  3. xmlrpcsの「initialize()」メソッドで指定
  4. $this->xmlrpcs->initialize($config);
  5. config/xmlrpcs.php 設定ファイルで指定
  6. /* config/xmlrpcs.php */
    <?php

    $config['functions'] = array(
        'putData' => array('function' => 'UserApi.put');
        'getData' => array('function' => 'UserApi.get');
        'delData' => array('function' => 'UserApi.del');
    );

     config/xmlrpcs.php設定ファイルで指定した場合は、コンストラクタやinitialize()メソッド
    で指定する必要はありません。
     提供するAPIが多い場合などは、設定ファイルで指定するとよいでしょう。


XML RPC メソッドのシグネーチャ & ドキュメント

 XML RPC メソッドのマッピング定義では、各XML RPC メソッドの「リターン値の型」と 「期待するリクエストデータの型」を シグネーチャとして定義することができます。
 また、XML RPC メソッドの説明文(使い方や機能説明など)を定義することができます。

 シグネーチャを定義すると、リクエストを受け取ったXML RPC サーバは、リクエストデータの型を定義されているシグネーチャに マッチしているか検証します。
 もし、シグネーチャと異なる型のデータがリクエストされるとエラーを返します。

*注

 CodeIgniter 2.1.2以下のバージョンでは、シグネーチャの検証ロジックは不十分な実装となっているため利用しないほうがよいでしょう。

    $config['functions'] = array(
        'putData' => array(
            'function' => 'UserApi.put'
            'docstring' => 'insert new user data into user table.',
            // 'signature' => array(array('boolean', 'struct'))
        )
    );
                            

レスポンスデータ

 レスポンスデータは、2つの要素からなる配列となります。
 1つ目の要素はがデータで、2つ目の要素はデータの型となります。

 送信データは、複数のデータ構造を必要なだけ配列として定義できますが、レスポンスデータは 1つのデータ構造のみ定義することになるため、複数のデータをレスポンスしたい場合は、 「配列(array)」または、「連想配列(struct)」として複数のデータをまとめて定義します。

  • 1つのデータからなるレスポンスデータ
  • $response_data = array('ABC', 'string');
  • 複数のデータからなるレスポンスデータ
  • $response_data = array(
        array(
            array('ABC', 'string'),
            array('XYZ', 'string'),
            array(123, 'int'),
        ),
        'array'
    );
    
    $response_data = array(
        array(
            'param1' => array('ABC', 'string'),
            'param2' => array('XYZ', 'string'),
            'param3' => array(123, 'int'),
        ),
        'struct'
    );
    

リクエスト受信からレスポンス返信までの手順

 XML RPC サーバでは「CI_Xmlrpc」クライアントクラスと「CI_Xmlrpcs」サーバクラスの両方を ロードする必要があります。

    1. CI_Xmlrpcライブラリクラスのロード
    2. CI_Xmlrpcsライブラリクラスのロード
    3. xmlrpcsインスタンスの初期化(マッピング)
    4. RPCメソッドの実行
    /* application/controllers/hoge_class.php */
    
    class Hoge_class extends CI_Controller {
    
        public function foo_action()
        {
            // 1. CI_Xmlrpcライブラリクラスのロード
            $this->load->library('xmlrpc');
    
            // 2. CI_Xmlrpcsライブラリクラスのロード
            $this->load->library('xmlrpcs');
    
            // 3. xmlrpcsインスタンスの初期化(マッピング)
            $config['functions']['putData'] = array('function' => 'UserApi.put');
            $config['functions']['getData'] = array('function' => 'UserApi.get');
            $config['functions']['delData'] = array('function' => 'UserApi.del');
            $this->xmlrpcs->initialize($config);
    
            // 4. RPCメソッドの実行
            $this->xmlrpcs->serve();
        }
    }
    

    マッピングの指定をコンストラクタでした場合と、config/xmlrpcs.php設定ファイルでした場合は、
    initialize()メソッドでマッピングの指定をする必要はありません。


XML RPC メソッドでのXMLデータ処理

 xmlrpcsインスタンスの「serve()」メソッドがコールされるとマッピング指定で指定された
「XML RPC メソッド」が実行されます。

    /* application/controllers/UserApi.php */
    
    class UserApi extends CI_Controller {
    
        public function __construct()
        {
            parent::__construct();
    
            $this->load->library('xmlrpc');
            $this->load->model('user_model');
        }
    
        // ------------------------------------------------------------
    
        public function put($request)
        {
            $request_data = $request->output_parameters();
    
            if ($this->user_model->put($request_data)) {
                // success
                $response_data = array(1, 'boolean');
            } else {
                // failure
                $response = $this->xmlrpc->send_error_message(
                    'E001',
                    'Could not set user data.'
                );
            }
    
            $response = $this->xmlrpc->send_response($response_data);
    
            return $response;
        }
    
        // ------------------------------------------------------------
    
        public function get($request)
        {
            $request_data = $request->output_parameters();
    
            if ($user_info = $this->user_model->get($request_data)) {
                // success
                $response_data = array(
                    array(
                        'name' => array($user_info['name'], 'string'),
                        'age' => array($user_info['age'], 'int'),
                        'score' => array($user_info['score'], 'int'),
                    ),
                    'struct');
            } else {
                // failure
                $response = $this->xmlrpc->send_error_message(
                    'E002',
                    'User data is not found.'
                );
            }
    
            $response = $this->xmlrpc->send_response($response_data);
    
            return $response;
        }
    
        // ------------------------------------------------------------
    
        public function del($request)
        {
            $request_data = $request->output_parameters();
    
            if ($this->user_model->del($request_data)) {
                // success
                $response_data = array(1, 'boolean');
                $response = $this->xmlrpc->send_response($response_data);
            } else {
                // failure
                $response = $this->xmlrpc->send_error_message(
                    'E003',
                    'Could not delete user data.'
                );
            }
    
            return $response;
        }
    
    }
    

エラーレスポンス

 XML RPC メソッドのデータ処理でエラーが起きた場合にクライアントでエラーをレスポンスするには 「send_error_message()」メソッドでエラー情報を生成します。

 第一引数に、エラーコードなどエラーを識別する簡単な文字列を指定します。
 第二引数には、詳細なエラーメッセージを指定します。

    return $this->xmlrpc->send_error_message('ErrCode-002', 'Error Message.');

XML RPC システムメソッド (イントロスペクションメソッド)

 CodeIgniterのXML RPC サーバは、システムメソッドとしてデフォルトで下記の「XML RPC メソッド」を提供しています。

system.listMethods XML RPC サーバが提供する「XML RPC メソッド」名の一覧を返します。
system.methodHelp 指定された「XML RPC メソッド」の説明テキストを返します。
説明テキストは「XML RPC メソッド」のマッピング定義時に「docstring」としてセットします。
system.methodSignature XML RPC メソッドが返すレスポンスデータ型と、XML RPC メソッドが期待する引数データの型を配列で返します。 「XML RPC メソッド」のマッピング定義時に「signature」としてセットします。
  • system.listMethods
    • XML RPC サーバ
    • $this->load->library('xmlrpc');
      $this->load->library('xmlrpcs');
      
      $config['functions'] = array(
          'putData' => array('function' => 'UserApi.put'),
          'getData' => array('function' => 'UserApi.get'),
          'delData' => array('function' => 'UserApi.del'),
          'updateData' => array('function' => 'UserApi.update')
      ); 
      
      $this->xmlrpcs->initialize($config);
      
      $this->xmlrpcs->serve();
      
    • XML RPC クライアント
    • $this->load->library('xmlrpc');
      $this->xmlrpc->server('http://your.site/hoge_class/foo_action/', 80);
      $this->xmlrpc->method('system.listMethods');
      
      if ($this->xmlrpc->send_request()) {
          print_r($this->xmlrpc->display_response());
      }
      
      Array
      (
          [0] => system.listMethods
          [1] => system.methodHelp
          [2] => system.methodSignature
          [3] => system.multicall
          [4] => putData
          [5] => getData
          [6] => delData
          [7] => updateData
      )
      

  • system.methodHelp
    • XML RPC サーバ
    • $this->load->library('xmlrpc');
      $this->load->library('xmlrpcs');
      
      $config['functions'] = array(
          'putData' => array(
                          'function' => 'UserApi.put',
                          'docstring' => 'This is method to put Data.'
                       ),
          'getData' => array(
                          'function' => 'UserApi.get',
                          'docstring' => 'This is method to get Data.'
                       ),
          'delData' => array(
                          'function' => 'UserApi.del',
                          'docstring' => 'This is method to delete Data.'
                       ),
          'updateData' => array(
                          'function' => 'UserApi.update',
                          'docstring' => 'This is method to update Data.'
                       )
      ); 
      
      $this->xmlrpcs->initialize($config);
      
      $this->xmlrpcs->serve();
      
    • XML RPC クライアント
    • $this->load->library('xmlrpc');
      $this->xmlrpc->server('http://your.site/hoge_class/foo_action/', 80);
      
      $req = array('delData', 'string');
      $this->xmlrpc->request($req);
      $this->xmlrpc->method('system.methodHelp');
      
      if ($this->xmlrpc->send_request()) {
          echo $this->xmlrpc->display_response();
      }
      
      This is method to delete Data.

  • system.methodSignature
  •  XML RPC メソッドのシグネーチャは、配列で表します。
     第一要素にリターン値の型、第二要素以降は期待する引数データ(リクエストデータ)の型を必要な数だけ定義します。

      array('リターン値の型'[, '期待する引数データ型'[, …]])

     しかし、同じXML RPC メソッドでもシグネーチャ(リターン値の型、と引数データの型)は一つとは限りません。

     例えば、あるデータを取得するXML RPC メソッドでは、一つの引数データとして文字列を受け取ると そのデータをキーにして 一つの文字列データを返すが、配列で文字列データを受け取るとそのデータ全てに関するデータを配列で返す機能を持つかもしれません。

     この例を通常のメソッドで表すと以下のようになると思います。

    • 通常メソッドの例
    • public function getHoge($key)
      {
          $this->load->model('hoge_model');
      
          if (is_array($key)) {
              $ret_val = array();
              foreach ($key as $k) {
                  $ret_val[] = $this->hoge_model->getInfo($k);
              }
              return $ret_val;
          }
          elseif (is_string($key)) {
              return $this->hoge_model->getInfo($key);
          }
      }
      

     この例の場合、シグネーチャは2種類あります。これを一つずつシグネーチャとして定義すると以下のようになります。

      // 配列を受け取り、配列を返す
      array('array', 'array')
      // 文字列を受け取り、文字列を返す
      array('string', 'string')
      

     XML RPC メソッドのマッピング定義時に「signature」として定義する際は、全てのパターンのシグネーチャを配列で定義します。

      'signature' => array(
                          array('array', 'array'),
                          array('string', 'string')
                      )
      
    • XML RPC サーバ
    • $this->load->library('xmlrpc');
      $this->load->library('xmlrpcs');
      
      $config['functions'] = array(
          'putData' => array(
                          'function' => 'UserApi.put',
                          'signature' => array(
                              array('boolean', 'struct')
                          )
                       ),
          'getData' => array(
                          'function' => 'UserApi.get',
                          'signature' => array(
                              array('array', 'string'), // return single data in array
                              array('array', 'array'), // return multiple data in array
                              array('array') // return all data in array
                          )
                       ),
          'delData' => array(
                          'function' => 'UserApi.del',
                          'signature' => array(
                              array('boolean', 'string'), // delete sigle data
                              array('boolean', 'array'), // delete multiple data
                          )
                       ),
          'updateData' => array(
                          'function' => 'UserApi.update',
                          'signature' => array(
                              array('boolean', 'string', 'struct')
                          ),
                          'docstring' =>
                              'update data for ID.' .
                              'require request as [ ID, UpdateData ],' .
                              'then return boolean as the result.'
                       );
      ); 
      
      $this->xmlrpcs->initialize($config);
      
      $this->xmlrpcs->serve();
                                  
    • XML RPC クライアント
    • $this->load->library('xmlrpc');
      $this->xmlrpc->server('http://your.site/hoge_class/foo_action/', 80);
      
      $req = array('getData', 'string');
      $this->xmlrpc->request($req);
      $this->xmlrpc->method('system.methodSignature');
      
      if ($this->xmlrpc->send_request()) {
          print_r($this->xmlrpc->display_response());
      }
      
      Array
      (
          [0] => Array
              (
                  [0] => array
                  [1] => string
              )
      
          [1] => Array
              (
                  [0] => array
                  [1] => array
              )
          [2] => Array
              (
                  [0] => array
              )
      )