Asial Blog

Recruit! Asialで一緒に働きませんか?

flashとのAMF通信用CakePHPコンポーネント

カテゴリ :
バックエンド(プログラミング)
タグ :
Tech
PHP
Flash
こんにちは、中川です。
以前、「AMFPHPを試してみました」でphpとFlashの通信を試してみましたが、
今回はCakePHPを使ったFlashとAMFでのやり取りする方法を考えてみました。

CakePHPでAMF通信ができるものを探してみたところ、
CakeAMFPHP」、
CakeAMF」、
CakeSWXPHP
など、いろいろ見つかりました。

一通り試してみましたが、設置が面倒であったりうまく動かなかったり
(私のやり方が悪いと思いますが、)どうもしっくりきませんでした。

もっとCakePHPから使いやすそうなものがないか探してみたところ、
SabreAMF」というライブラリを見つけました。
これを通常の使用方法で試したところ、非常に簡単にやり取りができましたので、
CakePHPからも使いやすいようにCakePHP用のコンポーネントを作ってみました。

※動作イメージ


構成は以下のようになります。
  1. `-- myproject
  2.     `-- app
  3.         |-- controllers
  4.         |   |-- amfs_controller.php(サンプルファイル)
  5.         |   `-- components
  6.         |       `-- sabre_amf.php(コンポーネント)
  7.         `-- vendors
  8.             |-- SabreAMF(解凍した中身を配置)
  9.             `-- sabreamf_ini.php(PATH設定用ファイル)

まず、SabreAMFの最新ライブラリ(今回はSabreAMF-1.1.187)を http://code.google.com/p/sabreamf/downloads/list から
ダウンロードして、解凍しSabreAMFに改名して、
  1. /PROJECT_DIR/app/vendors/SabreAMF 
に配置します。

そして、パス設定用のファイル
  1. /PROJECT_DIR/app/vendors/sabreamf_ini.php 
を作成します。
  1. <?php
  2. //sabreamf_ini.php
  3. define('SABREAMF_PATH', dirname(__FILE__));
  4. set_include_path(SABREAMF_PATH . PATH_SEPARATOR . get_include_path());
  5. ?>
あとは今回作成したコンポーネント、sabre_amf.php を
  1. /PROJECT_DIR/app/controllers/components/sabre_amf.php
に設置します。
  1. <?php
  2. //sabre_amf.php
  3. vendor("sabreamf_ini");
  4. vendor('SabreAMF/CallbackServer');
  5.  
  6. $_cakeController = null;
  7.  
  8. function amfCallBack($service, $method, $data) {
  9.     global $_cakeController;
  10.     $res = null;
  11.     if ($_cakeController) {
  12.       if (strpos($method, "_") !== 0) {  // _(アンダーバー)で始まるmethodはエラー。
  13.         $_cakeController->amfData = $data;
  14.         if (method_exists($_cakeController, $method)) {
  15.           $res = $_cakeController->{$method}();
  16.         } else {
  17.           $res = "not found action.";
  18.         }
  19.       } else {
  20.         $res = "invalid method name.";
  21.       }
  22.     }  else {
  23.       $res = "not found controller.";
  24.     }
  25.     return $res;
  26. }
  27.  
  28.  
  29. class SabreAmfComponent extends Object {
  30.   
  31.   function initialize(&$controller) {
  32.     global $_cakeController;
  33.     $_cakeController = $controller;
  34.     $controller->isAmf = true;  // default true
  35.   }
  36.   
  37.   function startup(&$controller) {
  38.     if ($controller->isAmf) {
  39.       Configure::write('debug', 0);
  40.       $controller->autoRender = false;
  41.       $server = new SabreAMF_CallbackServer();
  42.       $server->onInvokeService = 'amfCallBack';
  43.       $server->exec();
  44.       exit;    //ここでexitしてるので、amf の 場合はほかのactionは呼べないはず。。。
  45.     }
  46.   }
  47.   
  48.   function beforeRender(&$controller) {
  49.     if ($controller->isAmf) {
  50.       exit;  //  念のため
  51.     }
  52.   }
  53. }
  54.  
  55. ?>

これで準備完了です。
AMFを使用したいControllerで、
  1. var $components = array("SabreAmf");
とすれば、SabreAMFを利用してFlashとAMFでやりとりができるようになります。

以下、簡単な、文字列と、配列のやり取りのサンプル用Controllerです。
  1. <?php
  2. class AmfsController extends AppController {
  3.  
  4.   var $name = 'Amfs';
  5.   var $uses = array();
  6.   var $components = array("SabreAmf");
  7.   
  8.   function beforeFilter() {
  9.     // ここでamf出力ではないactionを設定できる。
  10.     // 全部amf出力の場合は何も設定いらない。
  11.     // この場合は「normal」のみ通常アクセスできる。
  12.     // ほかのactionは、URLからは実行されないはず。。。
  13.     if ($this->action == "normal") {  
  14.       $this->isAmf = false;
  15.       Configure::write('debug', 2);
  16.       $this->autoRender = true;
  17.     }
  18.   }
  19.   
  20.   // indexアクションは通常出力に使わないほうがいい。
  21.   // beforeFilterで除外すると、初期アクセスがamfから指定ない場合、
  22.   // リモートで呼び出されたときにエラーでちゃう
  23.   function index() {
  24.     return "index";
  25.   }
  26.   
  27.   //これは通常表示できるアクション
  28.   function normal() {
  29.     $this->set("hoge", date("Y-m-d H:i:s"));
  30.   }
  31.   
  32.   //文字列返却
  33.   function getstr() {
  34.     $testname = $this->amfData[0];  // flashから送られてきた値はamfDataにある。しかし、キーが消えてるよ。。。
  35.     return date("Y-m-d H:i:s") . " こんにちは " . $testname;  //各action で return した値が flashに返る。
  36.   }
  37.   
  38.   //配列返却
  39.   function getarr() {
  40.     $res_list = array();
  41.     for($i = 0; $i < 10; $i++) {
  42.       $res = array(
  43.         "col1" => $i,
  44.         "col2" => rand(1, 1000),
  45.       );
  46.       $res_list[] = $res;
  47.     }
  48.     $this->log(print_R($res_list, true), LOG_ERROR);
  49.     return $res_list;
  50.   }
  51.   
  52. }
  53. ?>

あとは、このコントローラにFlashからリクエストのサンプルです。

■RemoteTest.mxml(RemoteTest.swf)
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical">
  3.   <mx:Script>
  4.     <![CDATA[
  5.       import mx.rpc.events.FaultEvent;
  6.       import mx.rpc.events.ResultEvent;
  7.       import mx.controls.Alert;
  8.       import mx.utils.ArrayUtil;
  9.       
  10.       [Bindable]
  11.       private var myDataItem:Array = [];
  12.       
  13.       private function resultHandler(evt:ResultEvent):void {
  14.         myDataItem = ArrayUtil.toArray(evt.result);
  15.       }
  16.       
  17.       private function faultHandler(evt:FaultEvent):void {
  18.         Alert.show("Fault: " + evt.fault + " Msg: " + evt.message);
  19.       }
  20.       
  21.     ]]>
  22.   </mx:Script>
  23.   <!--
  24.   remoting-config.xmlをちゃんと設定したほうがいいのかな?
  25.   このサンプルでは、xmlを用意しないで、endpoint指定でやってます。
  26.   endpoint="{'http://{server.name}:{server.port}/amfs/'}" と書けば、
  27.   flash設置サーバの/amfs/と通信します。
  28.   以下のようにendpoint(cakeのcontroller呼び出し用URL)さえ指定しておけば
  29.   source は今回の場合は適当でいいです。というのも、sevice名として
  30.   サーバで受け取りますが、endpointのcakeのcontrollerの
  31.   メソッドしか呼べないようにしているので送信しているが使っていない。
  32.   また、destinationも適当でいいですが指定しないとエラーでます。
  33.   -->
  34.   
  35.   <!-- 文字列読み込み例 -->
  36.   <mx:RemoteObject id="ro" 
  37.     endpoint="{'http://{server.name}:{server.port}/amfs/'}" 
  38.     destination="MyAmfs"
  39.     source="Amfs"
  40.     result="myLabel.text = event.result.toString()" 
  41.     fault="faultHandler(event)">
  42.     <mx:method name="getstr">
  43.       <mx:arguments>
  44.         <testname>hoge</testname>
  45.       </mx:arguments>
  46.     </mx:method>
  47.   </mx:RemoteObject>
  48.   <mx:Label text="string_sample" id="myLabel" />
  49.   <mx:Button label="string load test" click="ro.getstr.send()"  x="24" y="42"/>
  50.   <!-- ro.●●.send() でcake側のアクションを呼べる -->
  51.   
  52.   <mx:Spacer height="2" width="200"/>
  53.   
  54.   <!-- 配列読み込み例 -->
  55.   <mx:RemoteObject id="ro2" 
  56.     endpoint="{'http://{server.name}:{server.port}/amfs/'}" 
  57.     destination="MyAmfs"
  58.     source="Amfs"
  59.     result="resultHandler(event)" 
  60.     fault="faultHandler(event)">
  61.   </mx:RemoteObject>
  62.   <mx:Button label="array load test" click="ro2.getarr.send()"  x="24" y="42"/>
  63.   <mx:DataGrid id="myData" dataProvider="{myDataItem}">
  64.     <mx:columns>
  65.       <mx:DataGridColumn headerText=" 1" dataField="col1"/>
  66.       <mx:DataGridColumn headerText="列 2" dataField="col2"/>
  67.     </mx:columns>
  68.   </mx:DataGrid>
  69.   
  70. </mx:Application>

上記サンプルでは、RemoteObjectを使用していますが、
NetConnectionを使った場合は以下のような感じで通信できると思います。
(以前のAMFPHPの記事も参照)
  1. var con:NetConnection = new NetConnection();
  2. con.connect(GATEWAY_URL);
  3. con.objectEncoding = ObjectEncoding.AMF3;
  4. con.call("Amfs.getstr", new Responder(resultHandler, faultHandler));

※しっかりした、動作確認はできておりません。
使用される場合は自己責任でご自由にお使いください。

-----------------------------------------------
最近CakePHPの人気はすごいですねー。今週水曜CakePHPの勉強会ですが、
申し込みに間に合わず、満員で参加できませんでしたorz
いきたかったなぁ~。。。

コメント

  • 参考にしてやってみたけどやり方が悪いのか動かないorz

    このコード公開してくれればありがたいのに・・・。

  • ryuring

    こんにちは。
    このページコードを参考にsabreAMFとCakePHPでやってみましたがうまくいきませんでした。
    以下環境です。
    ━━━━━━━━━━━━━━
    SabreAMF-1.2.203.tgz
    CakePHP1.2 RC3
    Flex Buider 3
    AIR 1.5
    ━━━━━━━━━━━━━━
    SWFはデータ送信時、内部的に初回だけ2回POSTするらしく、
    sableAMFのCallbackServer.phpのexecメソッドで
    振り分け処理を行っているようです。

    1回目:CommandMessage(PING)
    2回目:RemotingMessage(データ)

    1回目のCommandMessageは受け取ってデータを返すところまで、
    PHP側にログを出力させて確認できました。

    ただ、apacheのログを見ても、2回目のPOSTが走らないようです。

    1回目のPINGでの返信データがおかしいんですかね??
    何か情報をお持ちでしたら教えて下さいm(_ _)m

  • ryuring

    自己レスです。
    なんだかんだ手こずったのですが、ソースコードをコピペする際に、phpタグの前にスペースが混入してしまっていたため、失敗してました。
    気づくまでに丸2日かかりました。。情けない。。
    ご存知のとおり、CakePHP1.2の場合は、vendorタグをApp::importに書き換えるとうまくいきました。

  • Mimosa

    確認を取扱う方法?
    How to deal with authentication?

コメントフォーム



captcha_key

アシアルの会社情報

アシアル株式会社はPHP、HTML5、JavaScriptに特化したWebエンジニアリング企業です。ユーザーエクスペリエンス設計から大規模システム構築まで、アシアルメンバーが各々の専門性を通じてインターネットの進化に貢献します。

会社情報詳細

最近の記事