アラタナエンジニアブログ

aratana Engineer's Blog

Flex, PHPのデータ通信(AMFPHP)

f:id:masuda-aratana:20141202195404j:plain

アラタナの増田です。

普段はSketchPageというAIRアプリを開発しているのですが、最近コードを書く機会が激減しています><
そこで、コードを書けないストレスをブログスペースで発散していきたいと思います!
今回は、AIRアプリ(Flexアプリ)とサーバ間のデータ通信についてまとめました。
「いまさらFlex・・・?」と思われたとしてもまとめました。

AMF

Flexは、サーバ間とのデータをやりとりするために、XMLJSONなどテキストベースのフォーマットを使用することができるのですが、加えて、より軽量で高速なバイナリフォーマットを使用することもできます。それがAMF(ActionScript Message Format)という形式です。

PHPでは、AMFを処理するためのライブラリとしてAMFPHPが用意されています。
これを利用することでアプリとサーバ間のデータ通信を手軽に実装することができます。

AMFPHPのインストール

インストールと言っても、ここからソースをダウンロードして展開したものを、サーバ上に設置するだけの簡単な作業です。zipファイルを解凍すると、中に「Amfphp」というフォルダが入っていますので、フォルダごとアップロードしてください。

下記のサンプルコードでは、http://xxxxx.jp/amfphpにアップロードしたと仮定しています。

サンプルコード

PHP Source
デフォルトの設定では、通信で使用するPHPクラスを/amfphp/Services配下に設置します。
ここでは、http://xxxxx.jp/amfphp/Services/SamplePHP.phpを作成したものとします。
Flexクライアントから送信されたデータを受け取って、加工したデータを返しています。

<?php
class SamplePHP {
    // String型、Number型、Array型のデータを受け取ってXML型で返す。
    public function test1($stringParam, $numberParam, $arrayParam) {
        $dom = new DomDocument('1.0', 'UTF-8');
        $dom->formatOutput = true;

        $root = $dom->appendChild($dom->createElement('root'));
        // String型データ用のノード作成
        $stringNode = $root->appendChild($dom->createElement('string_param'));
        $stringNode->appendChild($dom->createTextNode($stringParam));
        // Number型データ用のノード作成
        $numberNode = $root->appendChild($dom->createElement('number_param'));
        $numberNode->appendChild($dom->createTextNode($numberParam));
        // Array型データ用のノード作成
        $arrayNode = $root->appendChild($dom->createElement('array_param'));
        foreach ($arrayParam as $value) {
            $itemNode = $arrayNode->appendChild($dom->createElement('item'));
            $itemNode->appendChild($dom->createTextNode($value));
        }
        // XML型で返す
        return $dom->saveXML();
    }

    // XML型のデータを受け取ってString型で返す。
    public function test2($xmlParam) {
    	// XMLデータの読込
        $dom = new DomDocument('1.0', 'UTF-8');
        $dom->loadXML($xmlParam->data);
        // ノードの値を抽出
        $title = $dom->getElementsByTagName('title')->item(0)->nodeValue;
        $version = $dom->getElementsByTagName('version')->item(0)->nodeValue;
        $description = $dom->getElementsByTagName('description')->item(0)->nodeValue;
        // 各ノードの値を連結して文字列型で返す
        return implode(', ', array($title, $version, $description));
    }
}

Flex Source
Flex側のサンプルコードです。
ボタンクリックのイベントハンドラーの中で、SamplePHPの各テストメソッドを呼び出しています。

それぞれ、
①String型、Number型、Array型のデータを送信する。
XML型のデータを送信する。
の2ケースを実装しています。

<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
               xmlns:s="library://ns.adobe.com/flex/spark"
               xmlns:mx="library://ns.adobe.com/flex/mx"
               creationComplete="onCreationComplete()">

    <fx:Script>
        <![CDATA[
            private const INDEX_PHP:String = "http://xxx.jp/amfphp/index.php";

            private var _connection:NetConnection;
            private var _responder:Responder;

            private function onCreationComplete():void {
                _connection = new NetConnection();
                _connection.objectEncoding = ObjectEncoding.AMF3;
                _connection.connect(INDEX_PHP + "?time=" + new Date().getTime());

                _responder = new Responder(onResult, onFault)
            }

            private function onResult(result:*):void {
                resultText.text += result + "\n\n";
            }

            private function onFault(result:*):void {
                resultText.text += "fault\n\n";
            }

            private function onTest1ButtonClick():void {
                var stringParam:String = "test string";
                var numberParam:Number = 3.14;
                var arrayParam:Array = ["element1", "element2", "element3"];
                _connection.call("SamplePHP.test1", _responder, 
				    stringParam, numberParam, arrayParam);
            }

            private function onTest2ButtonClick():void {
                var xmlParam:XML = new XML(<root/>);
                xmlParam.appendChild(<title/>);
                xmlParam.appendChild(<version/>);
                xmlParam.appendChild(<description/>);
                xmlParam.title[0] = "TITLE STRING";
                xmlParam.version[0] = "1.0.0";
                xmlParam.description[0] = "description string.";
                _connection.call("SamplePHP.test2", _responder, xmlParam);
            }
        ]]>
    </fx:Script>

    <s:VGroup width="100%" height="100%" left="10" top="10" right="10" bottom="10">
        <s:HGroup width="100%" verticalAlign="middle" gap="10">
            <s:Button id="test1Button" label="test1" click="onTest1ButtonClick()"/>
            <s:Label text="'String', 'Number', 'Array'"/>
        </s:HGroup>
        <s:HGroup width="100%" verticalAlign="middle" gap="10">
            <s:Button id="test2Button" label="test2" click="onTest2ButtonClick()"/>
            <s:Label text="'XML'"/>
        </s:HGroup>
        <s:TextArea id="resultText" width="100%" height="100%"/>
    </s:VGroup>
</s:Application>

まとめ

FlexPHPで相互にデータをやり取りしていますが、AMFPHPのゲートウェイが互いのデータ形式シリアライズ/デシリアライズしてくれます。結果的に、Flex側は直接PHPメソッドを呼び出す感じで書けるので、実装がとても楽です。