LOVE LOVE HEART

Apple Store(Japan)

Apple Store(Japan)

Apple Store(Japan)

 iTunes Store(Japan)

Return

Flash Remoting

Flash Remoting for PHP (AMFPHP)

PHP で Flash Remoting サービスを可能にした「AMFPHP」の紹介です。

このページで紹介している AMFPHP のバージョンは、0.9.0 です。

AMF とは?

AMF は、「Action Message Format」の略称です。
ActionScript で使用されるメッセージを交換するためのフォーマットということです。

AMF ではサーバーとクライアントの通信をバイナリデータで行なうため、従来のようなテキストデーターでのやり取りだけでなく、オブジェクトデーターの交換も可能になります。

AMF は HTTP プロトコル上を POST のデーターとして流すことも可能ですので、AMFPHP を使えば、通常の Web サーバー + PHP の環境で利用することが可能になります。

AMFPHP とは?

Macromedia 社で発売されている Flash Remoting は、PHP には対応していません。AMFPHP は Flash Remoting で使用されている AMF を実装することで、Flash Remoting を実現する PHP 用のクラスです。

AMFPHP は、PHP 用 AMF クラス「amfdata.php」をベースに、上位の Flash Remoting サービスを実現したものです。

AMFPHP では、ブーリアン、数値、文字列、オブジェクト、配列の入出力を扱うことが出来ます。
従来の変数または XML 形式でしかやり取り出来ないことと比べると、これは Flash と PHP でのやり取りの世界では、飛躍的な進歩です。

AMFPHP の特徴は?
  • Flash Remoting のサーバーサイドの仕組みを、Open Source で実現しています。

  • Flash Remoting コンポーネントが利用可能です。
    クライアント側では、純正の Flash Remoting の方法そのままで利用できます。

  • NetConnection デバッガが利用可能です。

AMFPHP の入手先

http://www.amfphp.org/ から入手可能です。

インストール方法
  1. amfphp-0.9.0.tar.gz をダウンロード&解凍

  2. source/flashservices をサーバーへコピー
    Web サーバーのルートに配置した場合の URL は「http://localhost/flashservices/」になります。

  3. flashservice フォルダに、app/Gateway.php を参照する gateway.php を作成
    これにより、ゲートウェイの URL は http://localhost/flashservices/services/gateway.php となります。

AMFPHP のマルチバイト対応

AMFPHP は UTF-8 には対応しているものの、標準ではマルチバイトに対応していません。

これは、内部コードと UTF-8 の変換に utf8_decode、utf8_encode が使用されているからです。
これらの関数は ISO-8859-1 しか考慮していませんので、マルチバイト文字列は壊れてしまい、文字化けしてしまいます。

Flash Remoting の入出力にはもともと UTF-8 が使われていますので、これを解消するためには、これらの関数を削除するだけです。

具体的には以下のようになります。

flashservices/io/AMFInputStream.php

[修正前]


    function readUTF()
    {
        // get the length of the string (1st 2 bytes)
        $length = $this->readInt();
        // grab the string
        $val = utf8_decode(substr($this->raw_data, $this->current_byte, $length));

[修正後]


    function readUTF()
    {
        // get the length of the string (1st 2 bytes)
        $length = $this->readInt();
        // grab the string
        $val = substr($this->raw_data, $this->current_byte, $length);

flashservices/io/AMFSerializer.php

[修正前]


	function writeString ($d)
	{
		$this->out->writeByte(2);
		$this->out->writeUTF(utf8_encode($d));
	}

	function writeXML($d)
	{
		$this->out->writeByte(15);
		$this->out->writeLongUTF(utf8_encode($d));
	}

[修正後]


	function writeString ($d)
	{
		$this->out->writeByte(2);
		$this->out->writeUTF($d);
	}

	function writeXML($d)
	{
		$this->out->writeByte(15);
		$this->out->writeLongUTF($d);
	}

flashservices/sql/adodbRecordSet.php

[修正前]


      function adodbRecordSet($d)
      {
          // create an initialData array
          $this->initialData = array();
          // grab all of the rows
         while ($line = $d->FetchRow()) {
              // decode each value ready for encoding when it goes through serialization
              foreach($line as $key => $value) {
                  $line[$key] = utf8_decode($value);
              }
              // add each row to the initial data array
              $this->initialData[] = $line;
          }
          // create the columnNames array
          $this->columnNames = array();
          // grab the number of fields
          $fieldcount = $d->FieldCount();
          // loop over all of the fields
          for($i=0; $i<$fieldcount; $i++){
              // decode each field name ready for encoding when it goes through serialization
              // and save each field name into the array
              $fld = $d->FetchField($i) ;
              $this->columnNames[$i] = utf8_decode($fld->name);
          }
          $this->numRows = $d->RecordCount();
      }

[修正後]


      function adodbRecordSet($d)
      {
          // create an initialData array
          $this->initialData = array();
          // grab all of the rows
         while ($line = $d->FetchRow()) {
              // decode each value ready for encoding when it goes through serialization
              foreach($line as $key => $value) {
                  $line[$key] = $value;
              }
              // add each row to the initial data array
              $this->initialData[] = $line;
          }
          // create the columnNames array
          $this->columnNames = array();
          // grab the number of fields
          $fieldcount = $d->FieldCount();
          // loop over all of the fields
          for($i=0; $i<$fieldcount; $i++){
              // decode each field name ready for encoding when it goes through serialization
              // and save each field name into the array
              $fld = $d->FetchField($i) ;
              $this->columnNames[$i] = $fld->name;
          }
          $this->numRows = $d->RecordCount();
      }

flashservices/sql/mssqlRecordSet.php

[修正前]


	function mssqlRecordSet($d)
	{
		// grab all of the rows
		while ($line = mssql_fetch_row($d)) {
			// decode each value ready for encoding when it goes through serialization
			foreach($line as $key => $value) {
				$line[$key] = utf8_decode($value);
			}
			// add each row to the initial data array
			$this->initialData[] = $line;
		}	
		// grab the number of fields
		$fieldcount = mssql_num_fields($d);
		// loop over all of the fields
		for($i=0; $i<$fieldcount; $i++)	{
			// decode each field name ready for encoding when it goes through serialization
			// and save each field name into the array
			$this->columnNames[$i] = utf8_decode(mssql_field_name($d, $i));
		}
		$this->numRows = mssql_num_rows($d);
	}

[修正後]


	function mssqlRecordSet($d)
	{
		// grab all of the rows
		while ($line = mssql_fetch_row($d)) {
			// decode each value ready for encoding when it goes through serialization
			foreach($line as $key => $value) {
				$line[$key] = $value;
			}
			// add each row to the initial data array
			$this->initialData[] = $line;
		}	
		// grab the number of fields
		$fieldcount = mssql_num_fields($d);
		// loop over all of the fields
		for($i=0; $i<$fieldcount; $i++)	{
			// decode each field name ready for encoding when it goes through serialization
			// and save each field name into the array
			$this->columnNames[$i] = mssql_field_name($d, $i);
		}
		$this->numRows = mssql_num_rows($d);
	}

flashservices/sql/mysqlRecordSet.php

[修正前]


	function mysqlRecordSet($d)
	{
		// grab all of the rows
		while ($line = mysql_fetch_row($d)) {
			// decode each value ready for encoding when it goes through serialization
			foreach($line as $key => $value) {
				$line[$key] = utf8_decode($value);
			}
			// add each row to the initial data array
			$this->initialData[] = $line;
		}	
		// grab the number of fields
		$fieldcount = mysql_num_fields($d);
		// loop over all of the fields
		for($i=0; $i<$fieldcount; $i++)	{
			// decode each field name ready for encoding when it goes through serialization
			// and save each field name into the array
			$this->columnNames[$i] = utf8_decode(mysql_field_name($d, $i));
		}
		$this->numRows = mysql_num_rows($d);
	}

[修正後]


	function mysqlRecordSet($d)
	{
		// grab all of the rows
		while ($line = mysql_fetch_row($d)) {
			// decode each value ready for encoding when it goes through serialization
			foreach($line as $key => $value) {
				$line[$key] = $value;
			}
			// add each row to the initial data array
			$this->initialData[] = $line;
		}	
		// grab the number of fields
		$fieldcount = mysql_num_fields($d);
		// loop over all of the fields
		for($i=0; $i<$fieldcount; $i++)	{
			// decode each field name ready for encoding when it goes through serialization
			// and save each field name into the array
			$this->columnNames[$i] = mysql_field_name($d, $i);
		}
		$this->numRows = mysql_num_rows($d);
	}

flashservices/sql/odbcRecordSet.php

[修正前]


	function odbcRecordSet($d)
	{
		// grab all of the rows
		while ($line = odbc_fetch_row($d)) {
			// decode each value ready for encoding when it goes through serialization
			foreach($line as $key => $value) {
				$line[$key] = utf8_decode($value);
			}
			// add each row to the initial data array
			$this->initialData[] = $line;
		}	
		// grab the number of fields
		$fieldcount = odbc_num_fields($d);
		// loop over all of the fields
		for($i=0; $i<$fieldcount; $i++)	{
			// decode each field name ready for encoding when it goes through serialization
			// and save each field name into the array
			$this->columnNames[$i] = utf8_decode(odbc_field_name($d, $i));
		}
		$this->numRows = odbc_num_rows($d);
	}

[修正後]


	function odbcRecordSet($d)
	{
		// grab all of the rows
		while ($line = odbc_fetch_row($d)) {
			// decode each value ready for encoding when it goes through serialization
			foreach($line as $key => $value) {
				$line[$key] = $value;
			}
			// add each row to the initial data array
			$this->initialData[] = $line;
		}	
		// grab the number of fields
		$fieldcount = odbc_num_fields($d);
		// loop over all of the fields
		for($i=0; $i<$fieldcount; $i++)	{
			// decode each field name ready for encoding when it goes through serialization
			// and save each field name into the array
			$this->columnNames[$i] = odbc_field_name($d, $i);
		}
		$this->numRows = odbc_num_rows($d);
	}

flashservices/sql/postgresRecordSet.php

[修正前]


	function postgresRecordSet($d)
	{
		// grab all of the rows
		while ($line = pg_fetch_row($d)) {
			// decode each value ready for encoding when it goes through serialization
			foreach($line as $key => $value) {
				$line[$key] = utf8_decode($value);
			}
			// add each row to the initial data array
			$this->initialData[] = $line;
		}	
		// grab the number of fields
		$fieldcount = pg_num_fields($d);
		// loop over all of the fields
		for($i=0; $i<$fieldcount; $i++)	{
			// decode each field name ready for encoding when it goes through serialization
			// and save each field name into the array
			$this->columnNames[$i] = utf8_decode(pg_field_name($d, $i));
		}
		$this->numRows = pg_num_rows($d);
	}

[修正後]


	function postgresRecordSet($d)
	{
		// grab all of the rows
		while ($line = pg_fetch_row($d)) {
			// decode each value ready for encoding when it goes through serialization
			foreach($line as $key => $value) {
				$line[$key] = $value;
			}
			// add each row to the initial data array
			$this->initialData[] = $line;
		}	
		// grab the number of fields
		$fieldcount = pg_num_fields($d);
		// loop over all of the fields
		for($i=0; $i<$fieldcount; $i++)	{
			// decode each field name ready for encoding when it goes through serialization
			// and save each field name into the array
			$this->columnNames[$i] = pg_field_name($d, $i);
		}
		$this->numRows = pg_num_rows($d);
	}

もし内部的に別の文字コードを使いたい場合は、utf8_decode、utf8_encode があった場所にマルチバイト文字列関数(mbstring)を入れてあげることで、SHIFT_JIS や EUC 等のデータベースにも対応出来るようになります。

使い方

まず、flashservices/services/ に、PHP で作った自分用のサービスを置きます。
その際、クラス名とファイル名は同じ名前にする必要があります。

次にそのサービスを、クライアント側から Flash Remoting で呼び出します。
この方法は、通常の Flash Remoting と同様です。
クライアント側からの違いは、ゲートウェイの URL が PHP のサーバーへ変わるだけです。

サンプル

準備中

注意点
  • Flash Player とサーバーとの通信には UTF-8 が使用されます。クライアントサイドの ActionScript 内では、System.useCodepage の状態に関わらず、サーバーからのデーターは受け取ることが出来ますが、サーバーサイドでは Flash にデーターを送信する際には必ず UTF-8 にする必要があります。
    混乱を避けるためには、クライアントサイドでも UTF-8 を使われた方が良いと思います。

  • AMFPHP は、AMF プロトコルを独自解析して実現されていますので、マクロメディアは動作保証していません。
    純正の製品とは違った挙動をする可能性がある点や、将来 Flash Remoting の仕様が変わった際に動作しなくなる可能性がある点には注意が必要です。

この情報は、Ryu!の独自調査などに基づいたものです。
間違い等がありましたら、ご連絡ください。

Return