タグ: PHP

Protocol Buffers を PHP から使う

Protocol Buffers とは

JSON, XML などは人間の目に比較的優しい形 [要出典] でデータを表現することができます。その反面、データが大きくなったり、データ解析が複雑だったりします。

Protocol Buffers は、通信や永続化などを目的に、データサイズの小ささやパース時のパフォーマンスなどを追及したシリアライズフォーマットです。データ構造はあらかじめプロトコル定義ファイル(.proto)に定義をしておき、Google が配布している protoc プログラムによって、各プラットフォーム上でのシリアライザ・デシリアライザを含めた関連コードが自動的に生成できるようになっています。

したがって利用する際の基本的な手順は大雑把に次のようになります

  1. データ構造を IDL で定義した proto ファイルを作成する
  2. proto ファイルからProtocol Buffersの利用に必要なコード(シリアライザ・デシリアライザなどが含まれる)を生成する
  3. 生成されたコードを利用したコードを書く

データ構造をはじめに定義し、そのデータ構造に応じたコードをあらかじめ用意しておくあたりが、JSON, XMLなどと異なります。

環境構築

さて、PHPから Protocol Buffers を使うために環境構築を行いましょう。まずは protoc プログラムをインストールします。

また今回利用するPHPライブラリは pear の Console_CommandLine を必要とするため、これも入れます。

最後に PHP ライブラリを composer で導入します。

インターフェース定義ファイルの作成

Google の proto3 Language Guide を見ようみまねで、簡単なものを定義してみましょう。次のように search_request.proto ファイルを作成してください。

インターフェース定義ファイルは直感的でわかりやすいので、きっと解説はいらないでしょう…。

Protocol Buffers 利用のためのコード生成

composer でひっぱってきたファイルのなかに必要なコードの生成を補助してくれるファイルがあるので、それを使いましょう。

今、ディレクトリ構造が以下のような形だったと仮定します。

vendor/bin 下 に protoc-gen-php.php というファイルが存在しますが、これを使うと簡単に必要なコードを生成することができます。さっそく SearchRequest データ構造を PHP で扱うためのコードを生成してみましょう。

すると src/Dist/Protobuf 下に SearchRequest.php というファイルが生成されるはずです。

これで準備が完了しました。

Protocol Buffers を利用する

まずは SearchRequest のインスタンスを取得し、各フィールドをセットして、シリアライズしてみます。

以上のようなコードを実行すると以下のようなバイナリが出力されます。

バイナリサイズは 30 byte でした。対応する標準的なJSONだと 77 byte あるのでおよそ半分になっていることがわかります。

またデシリアライズは次のようなコードで可能です

`

実行すると次のようになります

ちゃんともとのPOJOを復元できていますね

PHPのアレコレ

たまにしかPHPを触らないので色々問題が発生する

Java, Scala のお仕事依頼お待ちしております → @gomi_ningen

PHPの private なメソッドを無理やりテストする方法

Java の package プライベートとかほんと素晴らしい仕組みだと思うのですが、他言語はなかなかないですね…。最近だと Swift2 でようやく @testable import などという形で、モジュールプライベート的なメソッドをテストできるようになっていますが、まだまだ Java に比べると柔軟ではない感じはあります。そもそも、Swift1系はアクセス修飾子の設計普通にミスってるんじゃないかと思いますが、まあ言語を作っている方々は自分よりもはるかに知識がある方々だと思うので、いろいろあったのかな…という感想。自分はコードを読み書きするときにアクセス修飾子を基準にどこから読み進めるかとか、このメソッドは重要だなとかを判断するので、やっぱりこのあたりの機能が柔軟な言語のほうが好き。

本題。PHPの private メソッドを無理やりテストする方法。まあリフレクションしかないですよね。ということで、以下のような具合(自分のための備忘録)。

にゃ〜ん…って感じだ。

PHPのスコープについて

自分用メモ。PHPのblockはスコープの単位とならない。この特徴を示す単純なコードは以下のようなもの。

つまり以下のようなコードは冗長だということになる(ただ、個人的には上のコードわかりづらいと思う…)。

他言語を書きながら、ふと PHP を書くと混乱するのでメモ。

AWS SDK for PHP を用いた Amazon SNS の操作

AWS Lambda, SQS, HTTP/S, Email, SMS, モバイルデバイスなどに対して PUSH 通知を送ることができる Amazon SNS を PHP の SDK から操作する方法についてざっくりと見ていきます。

SNSクライアントのインスタンス化

Amazon SNS の操作をするためのクライアントクラスは、以下のように直感的にインスタンス化できます。

デバイストークンの登録

APNSやGCMなどから発行されたデバイストークンは SnsClient#createPlatformEndpoint を用いて対応するアプリケーションへエンドポイント登録ができます。戻り値の 'EndpointArn' にデバイスに紐づく EndpointArn が格納されているので、個別のデバイスへの通知が必要な場合は、これを永続化する必要があります。

なお、すでに登録されているデバイストークンを何度登録しても、正常に動作します(べき等性がある)。ただし、すでに登録されているデバイストークンが、異なる attribute(具体的にはUser Data)を持っている場合は例外が発生するので注意が必要です。こういったことは、AWSコンソールとコード両方からデバッグテストなどをしているときに発生しやすいと思います。

また、エンドポイントの Enabled が false になっていることをチェックしたければ、以下のように Attributes を指定してあげれば例外のほうに流れてくれます。

トピックの作成・購読

デバイストークンをアプリケーションに登録するとエンドポイントが発行されます。エンドポイントへ個別に通知を送るのも良いですが、一斉におなじ内容の通知を送りたいこともあると思います。そんなときにはトピックというものが便利です。トピックにエンドポイントを紐付けておけば、トピック宛に通知を送るだけで、紐付いているエンドポイントに一斉にメッセージを送ることができます。トピックを作る場合は SnsClient#createTopic を使います。

続いてエンドポイントが TopicArn を購読するように処理を走らせます。

このようにして、エンドポイントにトピックを購読させることができます。すでに無効になっているエンドポイントについても購読処理は正常に行うことができます。

プッシュ通知の送信

Amazon SNSを利用すれば、トピック、アプリケーション、各デバイスなど様々な粒度でPUSH通知を送ることができます。通知には SnsClient#publish を使います。

無効なエンドポイントに通知を行った場合、例外が発生します。ただし、トピックへ送った場合で、サブスクライバーの中に無効なエンドポイントがあっても正常に処理が行われます。JSONでメッセージを送る際には MessageStructure => 'json' をパラメタに追加してください。

エンドポイントの整理

デバイストークンは一定条件を満たすと失効します。失効したデバイストークンに対応するエンドポイントは Enabled = false となりますが、自動で消えてくれるわけではありません。Amazon SNSにはエンドポイントの状態変化を通知してくれる機能がありますので、それを利用して無効になったエンドポイントを削除する処理を走らせます。

まずは、エンドポイントの状態変化を通知するためのトピックを作成しましょう。そして、AWSコンソール SNS内の Applications で今回の処理を追加したい対象のアプリケーションを選択し、Actions -> Configure events へと進みます。そして「Endpoint updated」内に先ほど作成したトピックのARNを入力します。これで Endpoint の状態が変化したときに通知が飛ぶようになりました。データはJSONで飛んできますので、コンソールからよしなに処理先への購読処理を行ってください。

通知が来たら、エンドポイントの状態確認を行い、Enable = false だったらエンドポイントの削除処理を行います。