FlexとPHPでチャットを作る

いまFlexをいじっているんだけど、サーバサイドはJavaなので手軽じゃない(レンタルサーバで動かない)
で、サーバサイドをPHPにしてチャットを作りたいなー、と思ったんだけど、ぐぐってもこれぞというのがひっかからないので自分で作ることにした
表題の通り、クライアントサイドをFlexで、サーバサイドをPHPで実装する

サーバ構築

PHPは初めていじるので、Apacheのインストール 初心者用PHP入門を参考にまずはサーバを用意する
ほぼ手順通りでできたけど、一カ所だけ手順通りにできなかった

次に、c:\WINDOWSディレクトリ内にあるphp.iniをメモ帳などで開き「doc_root = 」を次のように追加して修正します。

自分の環境がVistaだから?かもしれないけど、c:\windowsにはphp.iniはなかったので、phpのインストールディレクトリにあるphp.ini-distをコピーして作成した

PHPが動作する環境ができた!\(=ω=.)

Flex実装

適当に作る

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
  <mx:VBox>
    <mx:Label text="Chat" width="1024"/>
    
    <mx:TabNavigator width="1024">
      <mx:VBox label="CHAT">
        <mx:HBox>
          <mx:TextInput id="handleName" width="120"/>
          <mx:TextInput id="content" width="640"/>
          <mx:Button id="submit" label="投稿"/>
        </mx:HBox>
        <mx:DataGrid id="chatLog">
          <mx:columns>
            <mx:DataGridColumn headerText="投稿者" dataField="handleName" width="120"/>
            <mx:DataGridColumn headerText="投稿日時" dataField="time" width="120"/>
            <mx:DataGridColumn headerText="内容" dataField="content" width="640"/>
          </mx:columns>
        </mx:DataGrid>
      </mx:VBox>
    </mx:TabNavigator>
    
  </mx:VBox>
</mx:Application>


できた\(=ω=.)

FlexPHPのデータ連係

FlexはできるけどPHPは触ったことすらないのでここからが怖い
そもそもFlexPHPのデータのやりとりはどうやるんだろう、とぐぐっていたらAdobe公式のドキュメントを見つけた
JSONを使用したFlexおよびPHP間のデータ送信
ここに習ってFlexPHPの連携にはJSONを使うことにした

Flexの修正

上記URLに従ってcorelibをダウンロードする
解凍すると出てくるswcファイルをFlexプロジェクトのライブラリパスに設定する
これでFlexからJSONが扱える
というわけで修正

<?xml version="1.0" encoding="utf-8"?>
<mx:Application
  xmlns:mx="http://www.adobe.com/2006/mxml"
  paddingTop="5"
  paddingBottom="5"
  paddingRight="5"
  paddingLeft="5"
  horizontalAlign="left"
  verticalAlign="top"
  >
  <mx:Script>
    <![CDATA[
      import mx.collections.ArrayCollection;
      import mx.controls.Alert;
      import mx.rpc.events.ResultEvent;
      import com.adobe.serialization.json.JSON;
      
      private function chatResult(event:ResultEvent):void {
        var rawData:String = String(event.result);
        var post:Object =JSON.decode(rawData);
        
        var posts:Array = post as Array;
        var postCollection:ArrayCollection = new ArrayCollection(posts);
        
        chatLog.dataProvider = postCollection;
        
      }
    ]]>
  </mx:Script>
  
  <mx:HTTPService id="chatRequest"
    url="./chat.php"
    useProxy="false"
    method="GET"
    resultFormat="text"
    result="chatResult(event)">
    <mx:request xmlns="">
      <getPost>true</getPost>
    </mx:request>
  </mx:HTTPService>

  <mx:VBox>
    <mx:Label text="\(=ω=.)/"/>
    
    <mx:TabNavigator>
      <mx:VBox label="CHAT" 
        height="600" width="800"
        paddingLeft="5" paddingRight="5">
        <mx:HBox>
          <mx:TextInput id="handleName" width="120"/>
          <mx:TextInput id="content" width="600"/>
          <mx:Button id="submit" label="投稿" click="chatRequest.send()"/>
        </mx:HBox>
        <mx:DataGrid id="chatLog" height="540" width="780">
          <mx:columns>
            <mx:DataGridColumn headerText="投稿者" dataField="handleName" width="120"/>
            <mx:DataGridColumn headerText="投稿日時" dataField="time" width="160"/>
            <mx:DataGridColumn headerText="内容" dataField="content" width="500"/>
          </mx:columns>
        </mx:DataGrid>
      </mx:VBox>
    </mx:TabNavigator>
  </mx:VBox>
  
</mx:Application>

HTTPServiceとmx:Scriptを追加
投稿ボタンを押したときにHTTPServiceのインスタンスの関数を呼び出すようにする
あとデザインを少し修正
とりあえず投稿ボタンを押すと、PHPでログファイルを読み込んできて、DataGridに出す感じで作る

PHPの実装

まずはログのモックを作る

konata#2008/08/02 20:47:12#test1
kagami#2008/08/02 20:47:13#test2
tsukasa#2008/08/02 20:47:14#test3
miwiki#2008/08/02 20:47:15#test4

これをPHPに読み込みたい
本題のPHPを作る

<?php
  class Post {
    public $handleName;
    public $time;
    public $content;
  }
  
  if(isset($_GET['getPost'])) {
    // ファイルをオープン.
    $file = fopen('log.txt', 'r');
    
    $posts = array();
    // ログファイルを読み込む.
    while(!feof($file)) {
     $line = fgets($file);
     
     $post = new Post();
     $items = explode("#", $line);
     $post->handleName = $items[0];
     $post->time = $items[1];
     $post->content = $items[2];
     
     array_push($posts, $post);
    }
    
    echo json_encode( $posts );
  }
?>

動作確認

投稿ボタンを押すとログファイルを読み込んで、Flexに表示する

できた\(=ω=.)

メモ1

  • JavaScriptで言うsplit()関数は、PHPではexplode()関数
  • オブジェクト指向が分かれば普通にいけそう
  • 配列にオブジェクトを追加する関数を調べるのに5分位かかった自分は低脳

PHPの修正

読みこみにしか対応してなかったので書き込みに対応する

<?php
  class Post {
    public $handleName;
    public $time;
    public $content;
  }
  
  // 投稿.
  if(isset($_POST['doPost'])) {
    // readwriteでファイルをオープン.
    $file = fopen('log.txt', 'a');
    
    $asOfDate = date("Y/m/d H:i:s");
    $line = $_POST['handleName']."#".$asOfDate."#".$_POST['content']."\n";
    fputs($file, $line);
    
    fclose(file);
  }
  
  // 更新.
  if(isset($_GET['doGet'])) {
    // ファイルをオープン.
    $file = fopen('log.txt', 'r');
    
    $posts = array();
    // ログファイルを読み込む.
    while(!feof($file)) {
     $line = fgets($file);
     
     $post = new Post();
     $items = explode("#", $line);
     $post->handleName = $items[0];
     $post->time = $items[1];
     $post->content = $items[2];
     
     array_push($posts, $post);
    }
    echo json_encode( $posts );
  }  
?>

リクエストがあったらサーバ側で時間を取得、フォーマットしてログに出力する

Flexの修正

リロードと投稿で動作を分ける

<?xml version="1.0" encoding="utf-8"?>
<mx:Application
  xmlns:mx="http://www.adobe.com/2006/mxml"
  paddingTop="5"
  paddingBottom="5"
  paddingRight="5"
  paddingLeft="5"
  horizontalAlign="left"
  verticalAlign="top"
  >
  <mx:Script>
    <![CDATA[
      import mx.collections.ArrayCollection;
      import mx.controls.Alert;
      import mx.rpc.events.ResultEvent;
      import com.adobe.serialization.json.JSON;
      
      private function chatResult(event:ResultEvent):void {
      	
        var rawData:String = String(event.result);
        var post:Object =JSON.decode(rawData);
        
        var posts:Array = post as Array;
        var postCollection:ArrayCollection = new ArrayCollection(posts);
        
        chatLog.dataProvider = postCollection;
      }
      
      private function post(event:Event = null):void {
      	// 内容が入力されている場合投稿、空の場合は更新する.
        if(handleName.text != "" && content.text != "") {
          // 投稿
          postRequest.send();
          reloadRequest.send();
        } else {
          // 更新
          reloadRequest.send();
        }
      }
    ]]>
  </mx:Script>
  
  <mx:HTTPService id="postRequest"
    url="./chat.php"
    useProxy="false"
    method="POST"
    resultFormat="text">
    <mx:request xmlns="">
      <doPost>true</doPost>
      <handleName>{handleName.text}</handleName>
      <content>{content.text}</content>
    </mx:request>
  </mx:HTTPService>

  <mx:HTTPService id="reloadRequest"
    url="./chat.php"
    useProxy="false"
    method="GET"
    resultFormat="text"
    result="chatResult(event)">
    <mx:request xmlns="">
      <doGet>true</doGet>
    </mx:request>
  </mx:HTTPService>

  <mx:VBox>
    <mx:Label text="\(=ω=.)/"/>
    
    <mx:TabNavigator>
      <mx:VBox label="CHAT" 
        height="600" width="800"
        paddingLeft="5" paddingRight="5">
        <mx:HBox>
          <mx:TextInput id="handleName" width="120"/>
          <mx:TextInput id="content" width="600"/>
          <mx:Button id="submit" label="投稿" click="post()"/>
        </mx:HBox>
        <mx:DataGrid id="chatLog" height="540" width="780">
          <mx:columns>
            <mx:DataGridColumn headerText="投稿者" dataField="handleName" width="120"/>
            <mx:DataGridColumn headerText="投稿日時" dataField="time" width="160"/>
            <mx:DataGridColumn headerText="内容" dataField="content" width="500"/>
          </mx:columns>
        </mx:DataGrid>
      </mx:VBox>
    </mx:TabNavigator>
  </mx:VBox>
  
</mx:Application>

とりあえず

できた、嘘だけど

  • 投稿後にリロードされない
  • 連打するとリロードされないので連続でPOSTされる
  • セキュリティのことを何も考えてない
  • デザインがひどい

位は気が向いたらなおしたい\(=ω=.)