このガイドでは、最新のAWS SDK for PHP を使うよう既存のコードを移行する方法、 および旧SDK Version 1 との違いを解説します。
PHPの言語やコミュニティはここ数年で著しい成長を遂げました。 AWS SDK for PHPの登場以降、PHPには大きな改版が2度 (PHP 5.3、PHP 5.4) あった他、 PHPコミュニティの多くが、 PHP Framework Interop Group の勧告のもとに統合されています。 そこで、PHPコミュニティで使われている、より現代的なパターンに沿って、 SDKにも大きな変更を施すことにしました。
今回のリリースでは、顧客から寄せられる多くの要望に応じるべく、SDKを白紙の状態から記述し直しました。 新しいSDKは Guzzle HTTPクライアントフレームワーク を基盤としているため、 処理性能が向上し、イベント駆動のカスタマイズも可能になっています。 さらに、高レベルの抽象化を施した結果、よく必要になるタスクのプログラミングが容易になりました。 SDKはPHP 5.3.3以降で動作し、名前空間やオートロードに関するPSR-0規約に従っています。
AWS SDK for PHPには、 Version 1にあったAWSサービスすべての他、 Amazon Route 53、Amazon Glacier、AWS Direct Connectなどが加わっています。 対応済みのサービス一覧が AWS SDK for PHPのウェブサイト に載っています。 最新の情報や変更事項については、 AWS SDK for PHP 2 GitHubリポジトリ を参照してください。
php://temp
ストリームに格納してメモリ消費量を削減build.xml
: 開発ツールのインストール、テストの実行、 .phar
ファイルの生成新しいSDKは Guzzle を基盤とし、その機能や規約を引き継いでいます。 AWSサービスクライアントはいずれも、サービス記述ファイルを使ってオペレーションを定義することにより、Guzzleクライアントを拡張したものです。 SDKは、デザインパターンの採用、イベントディスパッチ処理、依存性の注入など、 より頑健で適応性に優れたオブジェクト指向アーキテクチャを備えています。 その結果、旧SDKのクラスやメソッドの多くが変更されました。
新しいSDKは Version 1と違って、依存対象をあらかじめすべてリポジトリに収容してはいません。 Composer が依存関係を適切に解決し、オートロードするようになっています。 但し、pharをダウンロードしてSDKをインストールした場合は、依存関係が解決済みになっています。
SDKのディレクトリ構成や名前空間は、 PSR-0規約 に従って
編成されているため、自然とモジュール構成になっています。
Aws\Common
名前空間にはSDKの中核コードが収容されており、各サービスクライアントは
それぞれ独立した名前空間 (例: Aws\DynamoDb
) に入っています。
SDKはPHP Framework Interop Groupが制定したPSR規約に準拠しています。
すぐに気づくように、メソッド名はすべて小文字で始まるキャメルケース方式に
なりました (例えば put_object
ではなく putObject
)。
リージョン はクライアントのインスタンスを生成するために必要です (Amazon CloudFrontのように、サービスに単一のエンドポイントがある場合を除く)。 どのAWSリージョンを選択するかによって、処理性能やコストに影響があるかも知れません。
ファクトリメソッドはサービスクライアントのインスタンスを生成し、署名のセットアップ、 べき乗待機法に基づく再試行、例外ハンドラなどの処理を行います。 クライアントファクトリには少なくとも、アクセスキー、秘密鍵、リージョンを指定する必要がありますが、 他にもクライアントの動作を変更するための設定が多数あります。
$dynamodb = Aws\DynamoDb\DynamoDbClient::factory(array(
'key' => 'your-aws-access-key-id',
'secret' => 'your-aws-secret-access-key',
'region' => 'us-west-2',
));
グローバル設定ファイルを使って、サービスビルダ経由で自動的に、証明書をクライアントに注入することができます。 サービスビルダは、サービスクライアントに対する「依存性の注入」コンテナとして振る舞います。 (注: SDK第1版と違い、設定ファイルを自動的にロードすることはありません)
$aws = Aws\Common\Aws::factory('/path/to/custom/config.php');
$s3 = $aws->get('s3');
サービスクライアントのインスタンスを生成する方法としては、この技法が望ましいでしょう。
config.php
は次のような記述になります。
<?php
return array(
'includes' => array('_aws'),
'services' => array(
'default_settings' => array(
'params' => array(
'key' => 'your-aws-access-key-id',
'secret' => 'your-aws-secret-access-key',
'region' => 'us-west-2'
)
)
)
);
'includes' => array('_aws')
という記述により、SDKに付属するデフォルトの設定ファイルを取り込みます。
これでサービスクライアントのセットアップが終わり、
サービスビルダの get()
メソッドで名前による検索ができるようになります。
新版のSDKでもオペレーションの実行方法はほとんど同じですが、大きな違いが2つあります。
まず、オペレーション名は小文字で始まるキャメルケース方式になります。
次に、オプションを渡すためには常に、単一の配列パラメータを使います。
Amazon S3の PutObject
オペレーションの例を、新旧のSDKを対比して示します。
// Previous SDK - PutObject operation
$s3->create_object('bucket-name', 'object-key.txt', array(
'body' => 'lorem ipsum'
));
// New SDK - PutObject operation
$result = $s3->putObject(array(
'Bucket' => 'bucket-name',
'Key' => 'object-key.txt',
'Body' => 'lorem ipsum'
));
新しいSDKの putObject()
メソッドは、実際にはクライアント側に存在しません。
クライアントの __call()
マジックメソッドを使って実装されており、
コマンドのインスタンスを生成し、コマンドを実行し、結果を取得する、という一連の処理のショートカットとして働きます。
Command
オブジェクトは、AWSに対する呼び出しの要求と応答をカプセル化しています。
Command
オブジェクトから getResult()
メソッドで (先の例を参照) パース済みの結果を取得し、
あるいは getResponse()
メソッドで応答に関するデータ (状態コード、生の応答など) を取得することができます。
Command
オブジェクトは、実行前にコマンドを操作する場合、あるいはいくつかのコマンドを並列実行する場合にも有用です。
同じ PutObject
オペレーションを、コマンドの構文で実行する例を示します。
$command = $s3->getCommand('PutObject', array(
'Bucket' => 'bucket-name',
'Key' => 'object-key.txt',
'Body' => 'lorem ipsum'
));
$result = $command->getResult();
あるいは、 Command
オブジェクトの set()
メソッドを連結して記述する方式も可能です。
$result = $s3->getCommand('PutObject')
->set('Bucket', 'bucket-name')
->set('Key', 'object-key.txt')
->set('Body', 'lorem ipsum')
->getResult();
応答の形式が変わりました。応答は CFResponse
オブジェクトのインスタンスではなくなりました。
新しいSDKの Command
オブジェクトには (前節で説明したように) 要求と応答がカプセル化されているので、
ここから結果を取得できます。
// Previous SDK
// Execute the operation and get the CFResponse object
$response = $s3->list_tables();
// Get the parsed response body as a SimpleXMLElement
$result = $response->body;
// New SDK
// Executes the operation and gets the response in an array-like object
$result = $s3->listTables();
書き方はよく似ていますが、旧SDKにおける応答オブジェクトとの間には、根本的な違いがいくつかあります。
新しいSDKでは、パース済みの応答 (すなわち結果) を、Guzzleの Model
オブジェクトとして表します (旧版では CFSimpleXML
オブジェクト)。
このModelオブジェクトは配列と同じように振る舞うので扱いやすくなっています。
さらに、マッピング、フィルタリングなど、有用な組み込み機能もあります。
結果の中身も、新しいSDKでは違って見えるでしょう。
SDKは応答をマーシャル化 (直列化) してModelの形にし、サービス記述に基づき、より扱いやすい構造に変換します。
APIの資料では、すべてのオペレーションについて、応答の詳細を説明しています。
新しいSDKは、エラーや不正な応答があると、例外を投げて通知するようになりました。
旧SDKでは、オペレーションが成功したかどうか、 CFResponse::isOK()
メソッドで調べるようになっていました。
新しいSDKでは、オペレーションの結果が成功 でない 場合、例外を投げます。
したがって、例外が発生しなければ成功とみなしてよいことになりますが、アプリケーションコードには
try...catch
ロジックを記述して、エラーを適切に処理できるようにしなければなりません。
新しいSDKで、Amazon DynamoDBに対して DescribeTable
を呼び出したときの
応答を処理する例を示します。
$tableName = 'my-table';
try {
$result = $dynamoDb->describeTable(array('TableName' => $tableName));
printf('The provisioned throughput for table "%s" is %d RCUs and %d WCUs.',
$tableName,
$result->getPath('Table/ProvisionedThroughput/ReadCapacityUnits'),
$result->getPath('Table/ProvisionedThroughput/WriteCapacityUnits')
);
} catch (Aws\DynamoDb\Exception\DynamoDbException $e) {
echo "Error describing table {$tableName}";
}
コマンドから逆にGuzzle応答オブジェクトを取得することもできます。 これは、状態コード、ヘッダの追加データ、生の応答ボディーなどを取得したい場合に有用です。
$command = $dynamoDb->getCommand('DescribeTable', array('TableName' => $tableName));
$statusCode = $command->getResponse()->getStatusCode();
さらに、例外が発生した場合、ここから応答オブジェクトや状態コードを取得することも可能です。
try {
$command = $dynamoDb->getCommand('DescribeTable', array(
'TableName' => $tableName
));
$statusCode = $command->getResponse()->getStatusCode();
} catch (Aws\DynamoDb\Exception\DynamoDbException $e) {
$statusCode = $e->getResponse()->getStatusCode();
}
SDKにはイテレータクラスも付属しているので、リスト型や記述型のオペレーション結果について、容易に横断的な処理ができます。 複数の要求を実行し、トークンやマーカを追跡する処理は、イテレータクラスが適切に行うので、ループの形で記述する必要がありません。 イテレータを対象とするforeach構文で記述すればよいのです。
$objects = $s3->getIterator('ListObjects', array(
'Bucket' => 'my-bucket-name'
));
foreach ($objects as $object) {
echo $object['Key'] . PHP_EOL;
}
<?php
require '/path/to/sdk.class.php';
require '/path/to/config.inc.php';
$s3 = new AmazonS3();
$response = $s3->list_parts('my-bucket-name', 'my-object-key', 'my-upload-id', array(
'max-parts' => 10
));
if ($response->isOK())
{
// Loop through and display the part numbers
foreach ($response->body->Part as $part) {
echo "{$part->PartNumber}\n";
}
}
else
{
echo "Error during S3 ListParts operation.\n";
}
<?php
require '/path/to/vendor/autoload.php';
use Aws\Common\Aws;
use Aws\S3\Exception\S3Exception;
$aws = Aws::factory('/path/to/config.php');
$s3 = $aws->get('s3');
try {
$result = $s3->listParts(array(
'Bucket' => 'my-bucket-name',
'Key' => 'my-object-key',
'UploadId' => 'my-upload-id',
'MaxParts' => 10
));
// Loop through and display the part numbers
foreach ($result['Part'] as $part) {
echo "{$part[PartNumber]}\n";
}
} catch (S3Exception $e) {
echo "Error during S3 ListParts operation.\n";
}
<?php
require '/path/to/sdk.class.php';
require '/path/to/config.inc.php';
$dynamo_db = new AmazonDynamoDB();
$start_key = null;
$people = array();
// Perform as many Scan operations as needed to acquire all the names of people
// that are 16 or older
do
{
// Setup the parameters for the DynamoDB Scan operation
$params = array(
'TableName' => 'people',
'AttributesToGet' => array('id', 'age', 'name'),
'ScanFilter' => array(
'age' => array(
'ComparisonOperator' =>
AmazonDynamoDB::CONDITION_GREATER_THAN_OR_EQUAL,
'AttributeValueList' => array(
array(AmazonDynamoDB::TYPE_NUMBER => '16')
)
),
)
);
// Add the exclusive start key parameter if needed
if ($start_key)
{
$params['ExclusiveStartKey'] = array(
'HashKeyElement' => array(
AmazonDynamoDB::TYPE_STRING => $start_key
)
);
$start_key = null;
}
// Perform the Scan operation and get the response
$response = $dynamo_db->scan($params);
// If the response succeeded, get the results
if ($response->isOK())
{
foreach ($response->body->Items as $item)
{
$people[] = (string) $item->name->{AmazonDynamoDB::TYPE_STRING};
}
// Get the last evaluated key if it is provided
if ($response->body->LastEvaluatedKey)
{
$start_key = (string) $response->body
->LastEvaluatedKey
->HashKeyElement
->{AmazonDynamoDB::TYPE_STRING};
}
}
else
{
// Throw an exception if the response was not OK (200-level)
throw new DynamoDB_Exception('DynamoDB Scan operation failed.');
}
}
while ($start_key);
print_r($people);
<?php
require '/path/to/vendor/autoload.php';
use Aws\Common\Aws;
use Aws\DynamoDb\Enum\ComparisonOperator;
use Aws\DynamoDb\Enum\Type;
$aws = Aws::factory('/path/to/config.php');
$dynamodb = $aws->get('dynamodb');
// Create a ScanIterator and setup the parameters for the DynamoDB Scan operation
$scan = $dynamodb->getIterator('Scan', array(
'TableName' => 'people',
'AttributesToGet' => array('id', 'age', 'name'),
'ScanFilter' => array(
'age' => array(
'ComparisonOperator' => ComparisonOperator::GE,
'AttributeValueList' => array(
array(Type::NUMBER => '16')
)
),
)
));
// Perform as many Scan operations as needed to acquire all the names of people
// that are 16 or older
$people = array();
foreach ($scan as $item) {
$people[] = $item['name'][Type::STRING];
}
print_r($people);