

本文為英文版的機器翻譯版本，如內容有任何歧義或不一致之處，概以英文版為準。

# 從 第 2 版遷移 適用於 PHP 的 AWS SDK
<a name="getting-started_migration"></a>

本主題說明如何遷移程式碼以使用 適用於 PHP 的 AWS SDK 第 3 版，並說明新版本與開發套件第 2 版的差異。

**注意**  
開發套件第 2 版的基本使用模式 (如 `$result = $client->operation($params);`) 在第 3 版中並沒有變化，能讓使用者順利遷移版本。

## 簡介
<a name="introduction"></a>

第 3 版 適用於 PHP 的 AWS SDK 代表了改善 SDK 功能、納入超過兩年的客戶意見回饋、升級相依性、改善效能，以及採用最新 PHP 標準的重大努力。

## 第 3 版有哪些新功能？
<a name="what-s-new-in-version-3"></a>

第 3 版 適用於 PHP 的 AWS SDK 遵循 [PSR-4 和 PSR-7 標準](http://php-fig.org)，並將遵循 [SemVer](http://semver.org/) 標準。

其他新功能包括：
+ 推出中介軟體系統，可用來自訂服務用戶端行為
+ 彈性化的*分頁程式*，可讓使用者逐一查看分頁結果
+ 能夠使用 *JMESPath*，從*結果*與*分頁程式*物件中查詢資料 
+ 藉由 `'debug'` 組態選項，即可輕鬆偵錯

### 分離式 HTTP 層級
<a name="decoupled-http-layer"></a>
+  依據預設，系統會使用 [Guzzle 6](http://guzzlephp.org) 傳送請求，但亦支援 Guzzle 5。
+ 系統將在無法使用 cURL 的環境中運作此開發套件。
+ 亦支援自訂 HTTP 處理常式。

### 非同步請求
<a name="asynchronous-requests"></a>
+ 使用者亦可採用非同步的方式來執行*等待程式*與*分段上傳程式*等功能。
+ 可以透過 *promises* 與 *coroutines*，建立非同步的工作流程。
+ 並行請求或批次請求的效能有所改善。

## 與第 2 版有何不同？
<a name="what-s-different-from-version-2"></a>

### 專案相依性已更新
<a name="project-dependencies-are-updated"></a>

此版本的開發套件相依性有所變更。
+ 開發套件現在需要 PHP 8.1\$1。除此之外，開發套件程式碼中亦大量地使用 [generators](http://php.net/manual/en/language.generators.overview.php) (產生器)。
+ 我們已升級軟體開發套件以使用 [Guzzle 6](http://guzzlephp.org) （或 5)，提供軟體開發套件用來將請求傳送至 AWS 服務的基礎 HTTP 用戶端實作。Guzzle 的最新版本推出許多改良功能，包括非同步請求、可切換的 HTTP 處理常式、PSR-7 規範，以及更優異的效能等等。
+ 來自 PHP-FIG (`psr/http-message`) 的 PSR-7 套件，成功定義了用來代表 HTTP 請求、HTTP 回應、URL 與串流的界面。這些界面可供開發套件與 Guzzle 使用，亦可與其他 PSR-7 合規套件保持互通性。
+ Guzzle 的 PSR-7 實作套件 (`guzzlehttp/psr7`) 提供了 PSR-7 中界面的實作，以及數種頗具助益的類別與函數。因此，不論是開發套件或是 Guzzle 6，都極度倚賴此套件。
+ 軟體開發套件與 Guzzle 皆使用 Guzzle 推出的 [Promises/A\$1](https://promisesaplus.com) 實作 (`guzzlehttp/promises`)，以提供管理非同步請求和 coroutine 的界面。最終，Guzzle 的多重 cURL HTTP 處理器會實作非封鎖式 I/O 模型，允許非同步請求，且此套件可讓使用者在該模式中進行程式設計。如需詳細資訊，請參閱第 [3 適用於 PHP 的 AWS SDK 版中的 Promises](guide_promises.md)。
+ 開發套件會採用 PHP 推出的 [JMESPath](http://jmespath.org/) 實作 (`mtdowling/jmespath.php`)，以供使用者查詢 `Aws\Result::search()` 與 `Aws\ResultPaginator::search()` 方法的資料。如需詳細資訊，請參閱第 [3 適用於 PHP 的 AWS SDK 版中的 JMESPath 表達式](guide_jmespath.md)。

### 現在需要區域和版本選項
<a name="region-and-version-options-are-now-required"></a>

當您將任何服務的用戶端執行個體化時，請指定 `'region'` 與 `'version'` 選項。在 第 2 版中 適用於 PHP 的 AWS SDK， `'version'` 是完全選用的，有時`'region'`是選用的。然而在第 3 版中，上述兩個選項一律為必要選項。明確說明這兩個選項可讓您鎖定要為其編碼的 API 版本和 AWS 區域。建立新的 API 版本或新的 AWS 區域可用時，系統會將您與可能中斷的變更隔離，直到您準備好明確更新組態為止。

**注意**  
如果您對使用中的 API 版本沒有疑慮，則僅需將 `'version'` 選項設為 `'latest'`。不過，我們建議您明確地設定生產程式碼的 API 版本編號。  
並非所有 服務都適用於所有 AWS 區域。如需查詢可用區域的清單，請參考[區域與端點](https://docs.aws.amazon.com/general/latest/gr/rande.html)。  
對於只能透過單一全域端點 （例如 Amazon Route 53、 AWS Identity and Access Management和 Amazon CloudFront) 提供的服務，請將用戶端的已設定區域設定為 來執行個體化`us-east-1`。

**重要**  
SDK 也包含多區域用戶端，可根據做為命令參數提供的參數 (`@region`)，將請求分派至不同的 AWS 區域。這些用戶端預設使用的區域是經由提供給用戶端建構函式的 `region` 選項加以指定。

### 用戶端執行個體化使用 建構函數
<a name="client-instantiation-uses-the-constructor"></a>

在 第 3 版中 適用於 PHP 的 AWS SDK，您執行個體化用戶端的方式已變更。您僅需使用 `factory` 關鍵字，即可將用戶端執行個體化，不需再使用第 2 版的 `new` 方法。

```
use Aws\DynamoDb\DynamoDbClient;

// Version 2 style
$client = DynamoDbClient::factory([
    'region'  => 'us-east-2'
]);

// Version 3 style
$client = new DynamoDbClient([
    'region'  => 'us-east-2',
    'version' => '2012-08-10'
]);
```

**注意**  
您依然可以使用 `factory()` 方法來執行個體化用戶端。不過，系統會將該方式視為已遭取代。

### 用戶端組態已變更
<a name="client-configuration-has-changed"></a>

第 3 版中的用戶端組態選項 適用於 PHP 的 AWS SDK 已從第 2 版稍微變更。如需所有支援選項的說明，請參閱第 [3 適用於 PHP 的 AWS SDK 版的組態](guide_configuration.md)頁面。

**重要**  
在第 3 版的根層級中，`'key'` 與 `'secret'` 不再是有效的選項，但您可以將這兩個選項傳入 `'credentials'` 選項。我們這麼做的其中一個原因是阻止開發人員將 AWS 登入資料硬式編碼到專案中。

#### Sdk 物件
<a name="the-sdk-object"></a>

第 3 版會將 `Aws\Sdk` 物件做為替代項目 適用於 PHP 的 AWS SDK 引入 `Aws\Common\Aws`。系統會將 `Sdk` 物件做為用戶端 factory 使用，並採用該物件來管理多個用戶端的共用組態選項。

雖然軟體開發套件第 2 版的 `Aws` 類別是以類似服務定位器的方式進行運作 (該類別會一律傳回相同的用戶端執行個體)，但第 3 版的 `Sdk` 類別每次都會傳回新的用戶端執行個體。

`Sdk` 物件亦不支援與軟體開發套件第 2 版相同的組態檔案格式。該組態格式為 Guzzle 3 專屬格式，目前已淘汰。使用基本陣列將能更輕鬆地進行組態，其做法詳載於[使用 Sdk 類別](configuring-service-clients-code.md#sdk-class)。

### 某些 API 結果已變更
<a name="some-api-results-have-changed"></a>

為了提供 SDK 如何剖析 API 操作結果的一致性，Amazon ElastiCache、Amazon RDS 和 Amazon Redshift 現在在某些 API 回應上具有額外的包裝元素。

例如，呼叫 Amazon RDS [DescribeEngineDefaultParameters](https://docs.aws.amazon.com/AmazonRDS/latest/APIReference/API_DescribeEngineDefaultParameters.html) 會導致版本 3 現在包含包裝 “EngineDefaults” 元素。然而在第 2 版中，此元素並不存在。

```
$client = new Aws\Rds\RdsClient([
    'region'  => 'us-west-1',
    'version' => '2014-09-01'
]);

// Version 2
$result = $client->describeEngineDefaultParameters();
$family = $result['DBParameterGroupFamily'];
$marker = $result['Marker'];

// Version 3
$result = $client->describeEngineDefaultParameters();
$family = $result['EngineDefaults']['DBParameterGroupFamily'];
$marker = $result['EngineDefaults']['Marker'];
```

下列操作在此版本中亦受到影響，因此這些操作目前在輸出結果中會納入包裝元素，如括號中所示：
+ Amazon ElastiCache
  + AuthorizeCacheSecurityGroupIngress (CacheSecurityGroup)
  + CopySnapshot (Snapshot)
  + CreateCacheCluster (CacheCluster)
  + CreateCacheParameterGroup (CacheParameterGroup)
  + CreateCacheSecurityGroup (CacheSecurityGroup)
  + CreateCacheSubnetGroup (CacheSubnetGroup)
  + CreateReplicationGroup (ReplicationGroup)
  + CreateSnapshot (Snapshot)
  + DeleteCacheCluster (CacheCluster)
  + DeleteReplicationGroup (ReplicationGroup)
  + DeleteSnapshot (Snapshot)
  + DescribeEngineDefaultParameters (EngineDefaults)
  + ModifyCacheCluster (CacheCluster)
  + ModifyCacheSubnetGroup (CacheSubnetGroup)
  + ModifyReplicationGroup (ReplicationGroup)
  + PurchaseReservedCacheNodesOffering (ReservedCacheNode)
  + RebootCacheCluster (CacheCluster)
  + RevokeCacheSecurityGroupIngress (CacheSecurityGroup)
+ Amazon RDS
  + AddSourceIdentifierToSubscription (EventSubscription)
  + AuthorizeDBSecurityGroupIngress (DBSecurityGroup)
  + CopyDBParameterGroup (DBParameterGroup)
  + CopyDBSnapshot (DBSnapshot)
  + CopyOptionGroup (OptionGroup)
  + CreateDBInstance (DBInstance)
  + CreateDBInstanceReadReplica (DBInstance)
  + CreateDBParameterGroup (DBParameterGroup)
  + CreateDBSecurityGroup (DBSecurityGroup)
  + CreateDBSnapshot (DBSnapshot)
  + CreateDBSubnetGroup (DBSubnetGroup)
  + CreateEventSubscription (EventSubscription)
  + CreateOptionGroup (OptionGroup)
  + DeleteDBInstance (DBInstance)
  + DeleteDBSnapshot (DBSnapshot)
  + DeleteEventSubscription (EventSubscription)
  + DescribeEngineDefaultParameters (EngineDefaults)
  + ModifyDBInstance (DBInstance)
  + ModifyDBSubnetGroup (DBSubnetGroup)
  + ModifyEventSubscription (EventSubscription)
  + ModifyOptionGroup (OptionGroup)
  + PromoteReadReplica (DBInstance)
  + PurchaseReservedDBInstancesOffering (ReservedDBInstance)
  + RebootDBInstance (DBInstance)
  + RemoveSourceIdentifierFromSubscription (EventSubscription)
  + RestoreDBInstanceFromDBSnapshot (DBInstance)
  + RestoreDBInstanceToPointInTime (DBInstance)
  + RevokeDBSecurityGroupIngress (DBSecurityGroup)
+ Amazon Redshift
  + AuthorizeClusterSecurityGroupIngress (ClusterSecurityGroup)
  + AuthorizeSnapshotAccess (Snapshot)
  + CopyClusterSnapshot (Snapshot)
  + CreateCluster (Cluster)
  + CreateClusterParameterGroup (ClusterParameterGroup)
  + CreateClusterSecurityGroup (ClusterSecurityGroup)
  + CreateClusterSnapshot (Snapshot)
  + CreateClusterSubnetGroup (ClusterSubnetGroup)
  + CreateEventSubscription (EventSubscription)
  + CreateHsmClientCertificate (HsmClientCertificate)
  + CreateHsmConfiguration (HsmConfiguration)
  + DeleteCluster (Cluster)
  + DeleteClusterSnapshot (Snapshot)
  + DescribeDefaultClusterParameters (DefaultClusterParameters)
  + DisableSnapshotCopy (Cluster)
  + EnableSnapshotCopy (Cluster)
  + ModifyCluster (Cluster)
  + ModifyClusterSubnetGroup (ClusterSubnetGroup)
  + ModifyEventSubscription (EventSubscription)
  + ModifySnapshotCopyRetentionPeriod (Cluster)
  + PurchaseReservedNodeOffering (ReservedNode)
  + RebootCluster (Cluster)
  + RestoreFromClusterSnapshot (Cluster)
  + RevokeClusterSecurityGroupIngress (ClusterSecurityGroup)
  + RevokeSnapshotAccess (Snapshot)
  + RotateEncryptionKey (Cluster)

### 列舉類別已移除
<a name="enum-classes-have-been-removed"></a>

我們已經移除 適用於 PHP 的 AWS SDK第 2 版現存的 `Enum` 類別 (如 `Aws\S3\Enum\CannedAcl`)。Enum 屬於開發套件公有 API 中的具體類別，而該類別所含的常數可用來表示有效參數值群組。由於這些 enum 是 API 版本特有的類別，可能會隨著時間改變，或是與 PHP 保留字衝突，致使降低效能且沒有任何助益；因此，我們決定在第 3 版中移除這些類別。如此一來，這項改變即可支援第 3 版的資料導向與 API 版本適用性。

您不該使用來自 `Enum` 物件的值，而是直接使用常值。例如：使用 `CannedAcl::PUBLIC_READ` 取代 `'public-read'`。

### 已移除精細的例外類別
<a name="fine-grained-exception-classes-have-been-removed"></a>

出於與移除 Enums 類別相似的考量，我們也一併移除了各服務命名空間中的精細例外狀況類別 (例如 `Aws\Rds\Exception\{SpecificError}Exception`)。由於服務或操作會依據使用的 API 版本來擲出例外狀況，因此例外狀況會隨版本而異。不僅如此，系統亦無法使用指定操作所拋出的例外狀況完整清單，導致第 2 版的精細例外狀況類別無法完整呈現。

為了處理錯誤，您應該取得各項服務的例外根類別 (例如：`Aws\Rds\Exception\RdsException`)。您可以使用例外狀況的 `getAwsErrorCode()` 方法，藉此檢查特定的錯誤碼。此功能相當於取得不同的例外類別，但本版本所提供的功能不會增加開發套件的膨脹速度。

### 靜態外觀類別已移除
<a name="static-facade-classes-have-been-removed"></a>

在 第 2 版中 適用於 PHP 的 AWS SDK，有一個受到 Laravel 啟發的隱蔽功能，可讓您在 `Aws`類別`enableFacades()`上呼叫 ，以啟用對各種服務用戶端的靜態存取。然而，這項功能並不符合 PHP 最佳實務。因此，我們在一年前便已經停止記錄該功能，而我們更是在第 3 版中完全將之移除。從 `Aws\Sdk` 物件中擷取用戶端物件，並將這些物件做為物件執行個體來使用，而非靜態類別。

### 分頁程式取代疊代運算
<a name="paginators-supersede-iterators"></a>

第 2 版 適用於 PHP 的 AWS SDK 具有名為 \$1 迭代器\$1 的功能。系統會使用疊代運算物件來逐一查看分頁結果。使用者對這些物件感到不滿的原因之一便是「缺乏彈性」，因為疊代運算僅會發送每個結果的特定值。您只能透過事件接聽程式，才能擷取其他所需的結果值。

在第 3 版中，疊代運算已由[分頁程式](guide_paginators.md)取代。兩種功能的用途十分相似，但分頁程式提供更多使用彈性。這是因為分頁程式會產生結果物件，而非擷取回應中的數值。

以下範例會示範第 2 版和第 3 版如何擷取 `S3 ListObjects` 操作的分頁結果，以便說明分頁程式與疊代運算之間的差異。

```
// Version 2
$objects = $s3Client->getIterator('ListObjects', ['Bucket' => 'amzn-s3-demo-bucket']);
foreach ($objects as $object) {
    echo $object['Key'] . "\n";
}
```

```
// Version 3
$results = $s3Client->getPaginator('ListObjects', ['Bucket' => 'amzn-s3-demo-bucket']);
foreach ($results as $result) {
    // You can extract any data that you want from the result.
    foreach ($result['Contents'] as $object) {
        echo $object['Key'] . "\n";
    }
}
```

分頁程式物件的 `search()` 方法讓您能夠使用 [JMESPath](guide_jmespath.md) 表達式，更輕鬆地從結果集擷取資料。

```
$results = $s3Client->getPaginator('ListObjects', ['Bucket' => 'amzn-s3-demo-bucket']);
foreach ($results->search('Contents[].Key') as $key) {
    echo $key . "\n";
}
```

**注意**  
為了讓使用者順利轉移至第 3 版，該版本仍然支援 `getIterator()` 方法，但我們建議您遷移程式碼以使用分頁程式。

### 許多更高層級的抽象概念已變更
<a name="many-higher-level-abstractions-have-changed"></a>

整體而言，此版本改良或更新了許多高階抽象概念 (除用戶端之外的服務特定 helper 物件)；甚至移除部分高階抽象概念。
+   
**已更新:**  
  + [Amazon S3 分段上傳](s3-multipart-upload.md)的使用方式有所變更，Amazon Glacier 分段上傳以類似方式變更。
  + [Amazon S3 預先簽章的 URL](s3-presigned-url.md) 建立方式有所變更。
  + 採用 `Aws\S3\Sync` 類別來取代 `Aws\S3\Transfer` 命名空間。您仍可使用 `S3Client::uploadDirectory()` 與 `S3Client::downloadBucket()` 方法，但選項會有所不同。請參閱 [Amazon S3 Transfer Manager 第 3 適用於 PHP 的 AWS SDK 版](s3-transfer.md)的文件。
  +  採用 `Aws\S3\Model\ClearBucket` 與 `Aws\S3\Model\DeleteObjectsBatch` 來取代 `Aws\S3\BatchDelete` 和 `S3Client::deleteMatchingObjects()`。
  + 搭配第 [3 適用於 PHP 的 AWS SDK 版使用 DynamoDB 工作階段處理常式](service_dynamodb-session-handler.md)的選項和行為已稍微變更。
  + 採用 `Aws\DynamoDb\Model\BatchRequest` 來取代 `Aws\DynamoDb\WriteRequestBatch` 命名空間。請參閱 [DynamoDB WriteRequestBatch](https://docs.aws.amazon.com/aws-sdk-php/v3/api/class-Aws.DynamoDb.WriteRequestBatch.html) 文件。
  + `Aws\Ses\SesClient` 現可在使用 `SendRawEmail` 操作時處理 base64 以對 `RawMessage` 進行編碼。
+   
**已移除：**  
  + Amazon DynamoDB`Item``Attribute`、 和 `ItemIterator`類別 - 這些先前已在 [2.7.0 版](https://github.com/aws/aws-sdk-php/blob/3.0.0/CHANGELOG.md#270===2014-10-08)中棄用。
  + Amazon SNS 訊息驗證程式 - 這是[獨立的輕量型專案，](https://github.com/aws/aws-php-sns-message-validator)不需要 SDK 做為相依性。然而，此專案會包含在開發套件的 Phar 與 ZIP 分發中。您可以在 [AWS PHP Development 部落格中找到](https://aws.amazon.com/blogs/developer/receiving-amazon-sns-messages-in-php/)入門指南。
  + Amazon S3`AcpBuilder` 和相關物件已移除。

## 比較兩個 SDK 版本的程式碼範例
<a name="comparing-code-samples-from-both-versions-of-the-sdk"></a>

下列範例顯示使用 第 3 適用於 PHP 的 AWS SDK 版與第 2 版的一些不同之處。

### 範例：Amazon S3 `ListObjects`操作
<a name="example-s3-listobjects-operation"></a>

#### 從 SDK 第 2 版開始
<a name="from-version-2-of-the-sdk"></a>

```
<?php

require '/path/to/vendor/autoload.php';

use Aws\S3\S3Client;
use Aws\S3\Exception\S3Exception;

$s3 = S3Client::factory([
    'profile' => 'my-credential-profile',
    'region'  => 'us-east-1'
]);

try {
    $result = $s3->listObjects([
        'Bucket' => 'amzn-s3-demo-bucket',
        'Key'    => 'my-object-key'
    ]);

    foreach ($result['Contents'] as $object) {
        echo $object['Key'] . "\n";
    }
} catch (S3Exception $e) {
    echo $e->getMessage() . "\n";
}
```

#### 從 SDK 第 3 版
<a name="from-version-3-of-the-sdk"></a>

主要差異：
+ 使用 `new` 將用戶端執行個體化，而非使用 `factory()`。
+ 需要指定 `'version'` 與 `'region'` 選項，才能執行個體化。

```
<?php

require '/path/to/vendor/autoload.php';

use Aws\S3\S3Client;
use Aws\S3\Exception\S3Exception;

$s3 = new S3Client([
    'profile' => 'my-credential-profile',
    'region'  => 'us-east-1',
    'version' => '2006-03-01'
]);

try {
    $result = $s3->listObjects([
        'Bucket' => 'amzn-s3-demo-bucket',
        'Key'    => 'my-object-key'
    ]);

    foreach ($result['Contents'] as $object) {
        echo $object['Key'] . "\n";
    }
} catch (S3Exception $e) {
    echo $e->getMessage() . "\n";
}
```

### 範例：使用全域組態執行個體化用戶端
<a name="example-instantiating-a-client-with-global-configuration"></a>

#### 從 SDK 第 2 版開始
<a name="id2"></a>

```
<?php return array(
    'includes' => array('_aws'),
    'services' => array(
        'default_settings' => array(
            'params' => array(
                'profile' => 'my_profile',
                'region'  => 'us-east-1'
            )
        ),
        'dynamodb' => array(
            'extends' => 'dynamodb',
            'params' => array(
                'region'  => 'us-west-2'
            )
        ),
    )
);
```

```
<?php

require '/path/to/vendor/autoload.php';

use Aws\Common\Aws;

$aws = Aws::factory('path/to/my/config.php');

$sqs = $aws->get('sqs');
// Note: SQS client will be configured for us-east-1.

$dynamodb = $aws->get('dynamodb');
// Note: DynamoDB client will be configured for us-west-2.
```

#### 從 SDK 第 3 版
<a name="id3"></a>

主要差異：
+ 使用 `Aws\Sdk` 類別，而不是 `Aws\Common\Aws`。
+ 沒有組態檔案。但會使用組態陣列取而代之。
+ 需要指定 `'version'` 選項，才能執行個體化。
+ 使用 `create<Service>()` 方法，而不是 `get('<service>')`。

```
<?php

require '/path/to/vendor/autoload.php';

$sdk = new Aws\Sdk([
    'profile' => 'my_profile',
    'region' => 'us-east-1',
    'version' => 'latest',
    'DynamoDb' => [
        'region' => 'us-west-2',
    ],
]);

$sqs = $sdk->createSqs();
// Note: Amazon SQS client will be configured for us-east-1.

$dynamodb = $sdk->createDynamoDb();
// Note: DynamoDB client will be configured for us-west-2.
```