こんにちは。伊藤です。
今回はFacebookが開発したRPCフレームワークであるThriftについて書きます。
Thriftとは?
 http://ja.wikipedia.org/wiki/Apache_Thrift
つまりこれを使うと、Server側はJavaで開発、クライアント側はPHPで開発といった事が簡単にできるワケです。
今回はServer側をJava、クライアント側をPHPとして動かす方法について紹介します。
1. Thriftのインストール
弊社のt_ishidaによるこの記事を参考にどうぞ http://qiita.com/items/ca2eeb813249a9a09a58
2. .thriftファイルの作成
この辺を参考にして、.thriftファイルを作成します。
使用できる型は、bool/byte/short/int/long/double/stringの他に、Map/List/Enumも可能です。
test.thrift
| 
					 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34  | 
						namespace java jp.co.aainc.thrift // Namespaceの定義  // Listの定義 typedef list<map<string, bool>> ListMap1 // Enumの定義 enum Enum1 {     YES,     NO } // 構造の定義 struct StructTest {     1: required bool boolTest;     2: required byte byteTest;     3: required i16 i16Test; // Javaではshort     4: optional i32 i32Test; // Javaではint     5: optional i64 i64Test; // Javaではlong     6: required double doubleTest;      7: required string stringTest;     8: optional ListMap1 listMapTest; // 上で定義したList     9: optional Enum1 enumTest; // 上で定義したEnum } struct ResultTest {     1: bool result; } // サービスを定義 service ServiceTest {     ResultTest executeTest1(1:StructTest parameter) // 引数はStructTestで結果はResultTestで返す }  | 
					
3. .thriftファイルを基にソースコードを生成
まずJavaのソースコードを生成します。
| 
					 1  | 
						thrift --gen java test.thrift  | 
					
このコマンドを叩く事で、gen-java配下の指定したNamespace(パッケージ)に次のファイル群が生成されます。
Enum(Enum1.java)
| 
					 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45  | 
						/**  * Autogenerated by Thrift Compiler (0.8.0)  *  * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING  *  @generated  */ package jp.co.aainc.thrift; import java.util.Map; import java.util.HashMap; import org.apache.thrift.TEnum; public enum Enum1 implements org.apache.thrift.TEnum {   YES(0),   NO(1);   private final int value;   private Enum1(int value) {     this.value = value;   }   /**    * Get the integer value of this enum value, as defined in the Thrift IDL.    */   public int getValue() {     return value;   }   /**    * Find a the enum type by its integer value, as defined in the Thrift IDL.    * @return null if the value is not found.    */   public static Enum1 findByValue(int value) {      switch (value) {       case 0:         return YES;       case 1:         return NO;       default:         return null;     }   } }  | 
					
構造(StructTest.java)
| 
					 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35  | 
						public class StructTest implements org.apache.thrift.TBase<StructTest, StructTest._Fields>, java.io.Serializable, Cloneable {   private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("StructTest");   private static final org.apache.thrift.protocol.TField BOOL_TEST_FIELD_DESC = new org.apache.thrift.protocol.TField("boolTest", org.apache.thrift.protocol.TType.BOOL, (short)1);   private static final org.apache.thrift.protocol.TField BYTE_TEST_FIELD_DESC = new org.apache.thrift.protocol.TField("byteTest", org.apache.thrift.protocol.TType.BYTE, (short)2);   private static final org.apache.thrift.protocol.TField I16_TEST_FIELD_DESC = new org.apache.thrift.protocol.TField("i16Test", org.apache.thrift.protocol.TType.I16, (short)3);   private static final org.apache.thrift.protocol.TField I32_TEST_FIELD_DESC = new org.apache.thrift.protocol.TField("i32Test", org.apache.thrift.protocol.TType.I32, (short)4);   private static final org.apache.thrift.protocol.TField I64_TEST_FIELD_DESC = new org.apache.thrift.protocol.TField("i64Test", org.apache.thrift.protocol.TType.I64, (short)5);   private static final org.apache.thrift.protocol.TField DOUBLE_TEST_FIELD_DESC = new org.apache.thrift.protocol.TField("doubleTest", org.apache.thrift.protocol.TType.DOUBLE, (short)6);   private static final org.apache.thrift.protocol.TField STRING_TEST_FIELD_DESC = new org.apache.thrift.protocol.TField("stringTest", org.apache.thrift.protocol.TType.STRING, (short)7);   private static final org.apache.thrift.protocol.TField LIST_MAP_TEST_FIELD_DESC = new org.apache.thrift.protocol.TField("listMapTest", org.apache.thrift.protocol.TType.LIST, (short)8);   private static final org.apache.thrift.protocol.TField ENUM_TEST_FIELD_DESC = new org.apache.thrift.protocol.TField("enumTest", org.apache.thrift.protocol.TType.I32, (short)9);   private static final Map<Class<? extends IScheme>, SchemeFactory> schemes = new HashMap<Class<? extends IScheme>, SchemeFactory>();   static {     schemes.put(StandardScheme.class, new StructTestStandardSchemeFactory());     schemes.put(TupleScheme.class, new StructTestTupleSchemeFactory());   }   public boolean boolTest; // required   public byte byteTest; // required   public short i16Test; // required   public int i32Test; // optional   public long i64Test; // optional   public double doubleTest; // required   public String stringTest; // required   public List<Map<String,Boolean>> listMapTest; // optional   /**    *     * @see Enum1    */   public Enum1 enumTest; // optional  以下略。。。 }  | 
					
構造(ResultTest.java)
| 
					 1 2 3 4 5 6 7 8 9 10 11 12 13 14  | 
						public class ResultTest implements org.apache.thrift.TBase<ResultTest, ResultTest._Fields>, java.io.Serializable, Cloneable {   private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("ResultTest");   private static final org.apache.thrift.protocol.TField RESULT_FIELD_DESC = new org.apache.thrift.protocol.TField("result", org.apache.thrift.protocol.TType.BOOL, (short)1);   private static final Map<Class<? extends IScheme>, SchemeFactory> schemes = new HashMap<Class<? extends IScheme>, SchemeFactory>();   static {     schemes.put(StandardScheme.class, new ResultTestStandardSchemeFactory());     schemes.put(TupleScheme.class, new ResultTestTupleSchemeFactory());   }   public boolean result; // required  以下略。。。 }  | 
					
サービスクラス(ServiceTest.java)
| 
					 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26  | 
						public class ServiceTest {   public interface Iface {     public ResultTest executeTest1(StructTest parameter) throws org.apache.thrift.TException;   }   public interface AsyncIface {     public void executeTest1(StructTest parameter, org.apache.thrift.async.AsyncMethodCallback<AsyncClient.executeTest1_call> resultHandler) throws org.apache.thrift.TException;   }   public static class Client extends org.apache.thrift.TServiceClient implements Iface {     public static class Factory implements org.apache.thrift.TServiceClientFactory<Client> {       public Factory() {}       public Client getClient(org.apache.thrift.protocol.TProtocol prot) {         return new Client(prot);       }       public Client getClient(org.apache.thrift.protocol.TProtocol iprot, org.apache.thrift.protocol.TProtocol oprot) {         return new Client(iprot, oprot);       }     }  以下略。。。 }  | 
					
次にPHPのソースコードを生成します。
| 
					 1  | 
						thrift --gen java test.thrift  | 
					
このコマンドを叩く事で、gen-php配下に次のファイル群が生成されます。
定義などをarrayにしたもの(test_types.php)
※ソースは割愛
サービスクラス(ServiceTest.php)
※ソースは割愛
また、PHPについてはThriftのソースコードをコピーしてきて、このような階層にしておく必要があります。
http://wiki.apache.org/thrift/ThriftUsagePhp
packagesの中に今回生成した2ファイルを配置して下さい。
さて、ここまででやっと準備が整いました。
4. サーバ側実装(Java)
Server.java
| 
					 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44  | 
						/**  * PHP側からパラメータ「StructTest」を受け取って、結果「ResultTest」を返す  */ public class Server {     public static void main(String[] args) {         try {             TServerTransport serverTransport = new TServerSocket(9090);             final TServer server = new TThreadPoolServer(new TThreadPoolServer.Args(serverTransport).processor(                     new ServiceTest.Processor(new ServiceTest.Iface() { // 自動生成されたServiceTestを使用する                         @Override                         public ResultTest executeTest1(StructTest parameter) throws TException {                             // test.thriftで定義したパラメータたちが渡ってくる                             boolean boolTest = parameter.boolTest;                             byte byteTest = parameter.byteTest;                             short i16Test = parameter.i16Test;                             int i32Test = parameter.i32Test;                             long i64Test = parameter.i64Test;                             double doubleTest = parameter.doubleTest;                             String stringTest = parameter.stringTest;                             List<Map<String,Boolean>> listMapTest = parameter.listMapTest;                             Enum1 enumTest = parameter.enumTest;                             // ここで何らかの処理実行 //                             doSomething();                             // 結果返す。test.thriftファイルで定義した結果用オブジェクトを作る                             ResultTest resultTest = new ResultTest();                             resultTest.result = true; // booleanのresultにTrueをセット                             return resultTest;                         }                     }))             );             new Thread(new Runnable() {                 public void run() {                     System.out.println("Starting the server...");                     server.serve();                     System.out.println("Stopped the server...");                 }             }).start();         } catch (Exception e) {             e.printStackTrace();         }     } }  | 
					
このクラスを実行して、localhostの9090ポートで待ち受け状態にしておきます。
5. クライアント側実装(PHP)
ThriftTest.php
| 
					 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27  | 
						<?php require_once 'thrift/Thrift.php'; require_once 'thrift/packages/ServiceTest.php'; $socket    = new TSocket ( 'localhost', '9090' ) // Serverが起動しているアドレスとポートを指定; $socket->setSendTimeout(600 * 1000); $socket->setRecvTimeout(600 * 1000); $transport = new TBufferedTransport ( $socket ); $protocol  = new TBinaryProtocol($transport); $client = new ServiceTestClient($protocol); $transport->open(); // Serverへ渡すパラメータを作成 // ※test.thriftでrequiredにしているパラメータがNULLだと怒られます $struct = new StructTest(); $struct->boolTest = true; $struct->byteTest = 1; $struct->i16Test = 123; $struct->i32Test = 1234; $struct->i64Test = 12345; $struct->doubleTest = 123.45; $struct->stringTest = 'hoge'; $struct->listMapTest = array(array('fuga' => false)); $struct->enumTest = 'YES'; $result = $client->executeTest1($struct); // 処理を実行 echo $result->result; // 結果取得 trueが返ってきます。  | 
					
ThriftTest.phpを実行すると、PHP側のexecuteTest1()を経由して、Server側のexecuteTest1()が呼ばれ、結果が返ってきます。この辺の呼びだしをThriftがよしなにやってくれるのです。
何かダラダラと長くなってしまいましが、このようにThriftは簡単に言語間の差を吸収してくれます。
色々と使い道がありそうですね。
元Javaプログラマ。現在はScala/PlayでWeb開発と、SwiftでiOSアプリ開発をしています。 Unitテストとか書いてる時が一番楽しかったりします。
                
            


        