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


AWS Lambda, SQS, HTTP/S, Email, SMS, モバイルデバイスなどに対して PUSH 通知を送ることができる Amazon SNS を AWS SDK for PHP を用いて操作する方法についてざっくりと見ていきます。なお、雰囲気をつかんだのちに、実装する際には[ドキュメント](http://docs.aws.amazon.com/aws-sdk-php/v3/api/class-Aws.Sns.SnsClient.html)を見ることを強くお勧めします。

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

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

```php
<?php
require_once('./vendor/autoload.php');
use Aws\Sns\SnsClient;

$client = new SnsClient([
    'version' => 'latest',
    'region' => 'us-east-1', # 各自の利用しているregionを指定
    'profile' => 'default',  # ~/.aws/credentials 下に credential を置く
]);
```

## デバイストークンの登録

APNSやGCMなどから発行されたデバイストークンは [`SnsClient#createPlatformEndpoint`](http://docs.aws.amazon.com/aws-sdk-php/v3/api/api-sns-2010-03-31.html#createplatformendpoint) を用いて対応するアプリケーションへエンドポイント登録ができます。戻り値の `'EndpointArn'` にデバイスに紐づく EndpointArn が格納されているので、個別のデバイスへの通知が必要な場合は、これを永続化する必要があります。

```php
try {
    $platformApplicationArn = '<applicationのarn>';
    $token = '<デバイスから受け取ったデバイストークン>'
    $params = [
        'PlatformApplicationArn' => $platformApplicationArn,
        'Token' => $token,
    ];
    $result = $client->createPlatformEndpoint($params);

    $endpointArn = $result['EndpointArn'];  // 必要に応じて永続化
} catch (Aws\Sns\Exception\SnsException $e) {
    // 例外処理
}
```

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

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

```php
$params = [
    'PlatformApplicationArn' => $platformApplicationArn,
    'Token' => $token,
    'Attributes' => ['Enabled' => 'true'],
];
try {
    $result = $client->createPlatformEndpoint($params);
    
    $endpointArn = $result['EndpointArn'];  // 必要に応じて永続化
} catch (SnsException $e) {
    // トークンが登録済みで Enabled が false のときに例外に流れるようになる
}
```


## トピックの作成・購読

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

```php
$params = ['Name' => '<Topic名>'];
$result = $client->createTopic($params);

$topicArn = $result['TopicArn'];
```

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

```php
$params = [
   'Endpoint' => $endpointArn,
    'Protocol' => 'Application',
    'TopicArn' => $topicArn,
];

$result = $client->subscribe($params);
$subscriptionArn = $result['SubscriptionArn'];
```

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

## プッシュ通知の送信

Amazon SNSを利用すれば、トピックや各デバイスなど様々な粒度でPUSH通知を送ることができます。通知には [`SnsClient#publish`](http://docs.aws.amazon.com/aws-sdk-php/v3/api/api-sns-2010-03-31.html#publish) を使います。

```php
$msg = [
    'Message' => 'hogehoge',
    'TargetArn' => '<TopicArn | EndpointArn>',
];
```

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

```php
$msg = array(
    'MessageStructure' => 'json',
    'TargetArn' => $topicArn,
    'Message' => json_encode(array(
        'default' => '<string>...',
        'APNS_SANDBOX' => json_encode(array(
            'aps' => array(
                'key' => 'value'
            )
        ))
    ))
);
```