diff --git a/README.md b/README.md
index b68d450..b982796 100644
--- a/README.md
+++ b/README.md
@@ -4,6 +4,8 @@
# SparkPost PHP Library
+## This fork is maintained internally due to inactivity in the upstream repository. Only used within Lightfoot projects.
+
[](https://travis-ci.org/SparkPost/php-sparkpost)
[](https://coveralls.io/github/SparkPost/php-sparkpost?branch=master)
[](https://packagist.org/packages/sparkpost/sparkpost)
diff --git a/composer.json b/composer.json
index 1736421..61ede6d 100644
--- a/composer.json
+++ b/composer.json
@@ -8,24 +8,33 @@
}
],
"minimum-stability": "stable",
+ "config": {
+ "allow-plugins": {
+ "php-http/discovery": true
+ }
+ },
"scripts": {
"test": "XDEBUG_MODE=coverage ./vendor/bin/phpunit",
"fix-style": "php-cs-fixer fix ."
},
"require": {
- "php": "^7.1 || ^8.0",
+ "php": "^7.4 || ^8.0",
"php-http/httplug": "^1.0 || ^2.0",
+ "psr/http-factory": "^1.0",
+ "nyholm/psr7": "^1.0",
"php-http/message": "^1.0",
"php-http/client-implementation": "^1.0",
- "php-http/discovery": "^1.0"
+ "php-http/discovery": "^1.0",
+ "ext-json": "*"
},
"require-dev": {
"phpunit/phpunit": "^8.0 || ^9.0",
- "php-http/guzzle6-adapter": "^1.0",
+ "rector/rector": "^1.0",
+ "php-http/guzzle7-adapter": "^1.0",
"mockery/mockery": "^1.3",
"nyholm/nsa": "^1.0",
"php-coveralls/php-coveralls": "^2.4",
- "friendsofphp/php-cs-fixer": "^2.18"
+ "friendsofphp/php-cs-fixer": "^3.9"
},
"autoload": {
"psr-4": {
diff --git a/examples/bootstrap.php b/examples/bootstrap.php
index 688d53e..6c8c4f5 100644
--- a/examples/bootstrap.php
+++ b/examples/bootstrap.php
@@ -1,3 +1,3 @@
getRequest());
echo "Response:\n";
- echo $response->getStatusCode()."\n";
- print_r($response->getBody())."\n";
+ echo $response->getStatusCode() . "\n";
+ print_r($response->getBody()) . "\n";
} catch (\Exception $e) {
echo "Request:\n";
print_r($e->getRequest());
echo "Exception:\n";
- echo $e->getCode()."\n";
- echo $e->getMessage()."\n";
+ echo $e->getCode() . "\n";
+ echo $e->getMessage() . "\n";
}
diff --git a/examples/message-events/get_message_events.php b/examples/message-events/get_message_events.php
index 92bc70b..40ca911 100644
--- a/examples/message-events/get_message_events.php
+++ b/examples/message-events/get_message_events.php
@@ -2,7 +2,7 @@
namespace Examples\Templates;
-require dirname(__FILE__).'/../bootstrap.php';
+require __DIR__ . '/../bootstrap.php';
use SparkPost\SparkPost;
use GuzzleHttp\Client;
@@ -20,9 +20,9 @@
try {
$response = $promise->wait();
- echo $response->getStatusCode()."\n";
- print_r($response->getBody())."\n";
+ echo $response->getStatusCode() . "\n";
+ print_r($response->getBody()) . "\n";
} catch (\Exception $e) {
- echo $e->getCode()."\n";
- echo $e->getMessage()."\n";
+ echo $e->getCode() . "\n";
+ echo $e->getMessage() . "\n";
}
diff --git a/examples/message-events/get_message_events_with_retry_logic.php b/examples/message-events/get_message_events_with_retry_logic.php
index 82efd2a..a4d7ee2 100644
--- a/examples/message-events/get_message_events_with_retry_logic.php
+++ b/examples/message-events/get_message_events_with_retry_logic.php
@@ -2,7 +2,7 @@
namespace Examples\Templates;
-require dirname(__FILE__).'/../bootstrap.php';
+require __DIR__ . '/../bootstrap.php';
use SparkPost\SparkPost;
use GuzzleHttp\Client;
@@ -23,11 +23,11 @@
*/
try {
$response = $promise->wait();
- echo $response->getStatusCode()."\n";
- print_r($response->getBody())."\n";
+ echo $response->getStatusCode() . "\n";
+ print_r($response->getBody()) . "\n";
} catch (\Exception $e) {
- echo $e->getCode()."\n";
- echo $e->getMessage()."\n";
+ echo $e->getCode() . "\n";
+ echo $e->getMessage() . "\n";
if ($e->getCode() >= 500 && $e->getCode() <= 599) {
echo "Wow, this failed epically";
diff --git a/examples/templates/create_template.php b/examples/templates/create_template.php
index 1d697d5..0ce0d0b 100644
--- a/examples/templates/create_template.php
+++ b/examples/templates/create_template.php
@@ -2,7 +2,7 @@
namespace Examples\Templates;
-require dirname(__FILE__).'/../bootstrap.php';
+require __DIR__ . '/../bootstrap.php';
use SparkPost\SparkPost;
use GuzzleHttp\Client;
@@ -22,16 +22,16 @@
// Valid short template content examples
$plain_text = 'Write your text message part here.';
-$html = <<
Write your HTML message part here
-HTML;
+HTML_WRAP;
-$amp_html = <<
@@ -43,25 +43,25 @@
Hello World! Let's get started using AMP HTML together!
-HTML;
+HTML_WRAP;
$promise = $sparky->request('POST', 'templates', [
- 'name' => $template_name,
- 'id' => $template_id,
- 'content' => [
- 'from' => "from@$sending_domain",
- 'subject' => 'Your Subject',
- 'text' => $plain_text,
- 'html' => $html,
- 'amp_html' => $amp_html,
- ],
+ 'name' => $template_name,
+ 'id' => $template_id,
+ 'content' => [
+ 'from' => "from@$sending_domain",
+ 'subject' => 'Your Subject',
+ 'text' => $plain_text,
+ 'html' => $html,
+ 'amp_html' => $amp_html,
+ ],
]);
try {
$response = $promise->wait();
- echo $response->getStatusCode()."\n";
- print_r($response->getBody())."\n";
+ echo $response->getStatusCode() . "\n";
+ print_r($response->getBody()) . "\n";
} catch (\Exception $e) {
- echo $e->getCode()."\n";
- echo $e->getMessage()."\n";
+ echo $e->getCode() . "\n";
+ echo $e->getMessage() . "\n";
}
diff --git a/examples/templates/delete_template.php b/examples/templates/delete_template.php
index 609e67e..34e66e7 100644
--- a/examples/templates/delete_template.php
+++ b/examples/templates/delete_template.php
@@ -2,7 +2,7 @@
namespace Examples\Templates;
-require dirname(__FILE__).'/../bootstrap.php';
+require __DIR__ . '/../bootstrap.php';
use SparkPost\SparkPost;
use GuzzleHttp\Client;
@@ -19,9 +19,9 @@
try {
$response = $promise->wait();
- echo $response->getStatusCode()."\n";
- print_r($response->getBody())."\n";
+ echo $response->getStatusCode() . "\n";
+ print_r($response->getBody()) . "\n";
} catch (\Exception $e) {
- echo $e->getCode()."\n";
- echo $e->getMessage()."\n";
+ echo $e->getCode() . "\n";
+ echo $e->getMessage() . "\n";
}
diff --git a/examples/templates/get_all_templates.php b/examples/templates/get_all_templates.php
index 87a5b4d..722126d 100644
--- a/examples/templates/get_all_templates.php
+++ b/examples/templates/get_all_templates.php
@@ -2,7 +2,7 @@
namespace Examples\Templates;
-require dirname(__FILE__).'/../bootstrap.php';
+require __DIR__ . '/../bootstrap.php';
use SparkPost\SparkPost;
use GuzzleHttp\Client;
@@ -17,9 +17,9 @@
try {
$response = $promise->wait();
- echo $response->getStatusCode()."\n";
- print_r($response->getBody())."\n";
+ echo $response->getStatusCode() . "\n";
+ print_r($response->getBody()) . "\n";
} catch (\Exception $e) {
- echo $e->getCode()."\n";
- echo $e->getMessage()."\n";
+ echo $e->getCode() . "\n";
+ echo $e->getMessage() . "\n";
}
diff --git a/examples/templates/get_template.php b/examples/templates/get_template.php
index 3dd8849..647e022 100644
--- a/examples/templates/get_template.php
+++ b/examples/templates/get_template.php
@@ -2,7 +2,7 @@
namespace Examples\Templates;
-require dirname(__FILE__).'/../bootstrap.php';
+require __DIR__ . '/../bootstrap.php';
use SparkPost\SparkPost;
use GuzzleHttp\Client;
@@ -19,9 +19,9 @@
try {
$response = $promise->wait();
- echo $response->getStatusCode()."\n";
- print_r($response->getBody())."\n";
+ echo $response->getStatusCode() . "\n";
+ print_r($response->getBody()) . "\n";
} catch (\Exception $e) {
- echo $e->getCode()."\n";
- echo $e->getMessage()."\n";
+ echo $e->getCode() . "\n";
+ echo $e->getMessage() . "\n";
}
diff --git a/examples/templates/preview_template.php b/examples/templates/preview_template.php
index 51675a2..8af61d9 100644
--- a/examples/templates/preview_template.php
+++ b/examples/templates/preview_template.php
@@ -2,7 +2,7 @@
namespace Examples\Templates;
-require dirname(__FILE__).'/../bootstrap.php';
+require __DIR__ . '/../bootstrap.php';
use SparkPost\SparkPost;
use GuzzleHttp\Client;
@@ -23,9 +23,9 @@
try {
$response = $promise->wait();
- echo $response->getStatusCode()."\n";
- print_r($response->getBody())."\n";
+ echo $response->getStatusCode() . "\n";
+ print_r($response->getBody()) . "\n";
} catch (\Exception $e) {
- echo $e->getCode()."\n";
- echo $e->getMessage()."\n";
+ echo $e->getCode() . "\n";
+ echo $e->getMessage() . "\n";
}
diff --git a/examples/templates/update_template.php b/examples/templates/update_template.php
index 2f79cfd..83a1c05 100644
--- a/examples/templates/update_template.php
+++ b/examples/templates/update_template.php
@@ -2,7 +2,7 @@
namespace Examples\Templates;
-require dirname(__FILE__).'/../bootstrap.php';
+require __DIR__ . '/../bootstrap.php';
use SparkPost\SparkPost;
use GuzzleHttp\Client;
@@ -23,9 +23,9 @@
try {
$response = $promise->wait();
- echo $response->getStatusCode()."\n";
- print_r($response->getBody())."\n";
+ echo $response->getStatusCode() . "\n";
+ print_r($response->getBody()) . "\n";
} catch (\Exception $e) {
- echo $e->getCode()."\n";
- echo $e->getMessage()."\n";
+ echo $e->getCode() . "\n";
+ echo $e->getMessage() . "\n";
}
diff --git a/examples/transmissions/create_transmission.php b/examples/transmissions/create_transmission.php
index 6204ff8..a137f9e 100644
--- a/examples/transmissions/create_transmission.php
+++ b/examples/transmissions/create_transmission.php
@@ -2,7 +2,7 @@
namespace Examples\Transmissions;
-require dirname(__FILE__).'/../bootstrap.php';
+require __DIR__ . '/../bootstrap.php';
use SparkPost\SparkPost;
use GuzzleHttp\Client;
@@ -40,9 +40,9 @@
try {
$response = $promise->wait();
- echo $response->getStatusCode()."\n";
- print_r($response->getBody())."\n";
+ echo $response->getStatusCode() . "\n";
+ print_r($response->getBody()) . "\n";
} catch (\Exception $e) {
- echo $e->getCode()."\n";
- echo $e->getMessage()."\n";
+ echo $e->getCode() . "\n";
+ echo $e->getMessage() . "\n";
}
diff --git a/examples/transmissions/create_transmission_with_attachment.php b/examples/transmissions/create_transmission_with_attachment.php
index b97dd1d..2d85e5d 100644
--- a/examples/transmissions/create_transmission_with_attachment.php
+++ b/examples/transmissions/create_transmission_with_attachment.php
@@ -2,7 +2,7 @@
namespace Examples\Transmissions;
-require dirname(__FILE__).'/../bootstrap.php';
+require __DIR__ . '/../bootstrap.php';
use SparkPost\SparkPost;
use GuzzleHttp\Client;
@@ -13,10 +13,10 @@
// In these examples, fetch API key from environment variable
$sparky = new SparkPost($httpClient, ["key" => getenv('SPARKPOST_API_KEY')]);
-$filePath = dirname(__FILE__).'/';
+$filePath = __DIR__ . '/';
$fileName = 'sparkpost.png';
-$fileType = mime_content_type($filePath.$fileName);
-$fileData = base64_encode(file_get_contents($filePath.$fileName));
+$fileType = mime_content_type($filePath . $fileName);
+$fileData = base64_encode(file_get_contents($filePath . $fileName));
// put your own sending domain and test recipient address here
$sending_domain = "steve2-test.trymsys.net";
@@ -52,9 +52,9 @@
try {
$response = $promise->wait();
- echo $response->getStatusCode()."\n";
- print_r($response->getBody())."\n";
+ echo $response->getStatusCode() . "\n";
+ print_r($response->getBody()) . "\n";
} catch (\Exception $e) {
- echo $e->getCode()."\n";
- echo $e->getMessage()."\n";
+ echo $e->getCode() . "\n";
+ echo $e->getMessage() . "\n";
}
diff --git a/examples/transmissions/create_transmission_with_cc_and_bcc.php b/examples/transmissions/create_transmission_with_cc_and_bcc.php
index c49ab0c..d7bdb11 100644
--- a/examples/transmissions/create_transmission_with_cc_and_bcc.php
+++ b/examples/transmissions/create_transmission_with_cc_and_bcc.php
@@ -2,7 +2,7 @@
namespace Examples\Transmissions;
-require dirname(__FILE__).'/../bootstrap.php';
+require __DIR__ . '/../bootstrap.php';
use SparkPost\SparkPost;
use GuzzleHttp\Client;
@@ -58,9 +58,9 @@
try {
$response = $promise->wait();
- echo $response->getStatusCode()."\n";
- print_r($response->getBody())."\n";
+ echo $response->getStatusCode() . "\n";
+ print_r($response->getBody()) . "\n";
} catch (\Exception $e) {
- echo $e->getCode()."\n";
- echo $e->getMessage()."\n";
+ echo $e->getCode() . "\n";
+ echo $e->getMessage() . "\n";
}
diff --git a/examples/transmissions/create_transmission_with_recipient_list.php b/examples/transmissions/create_transmission_with_recipient_list.php
index b95a994..5b8eec3 100644
--- a/examples/transmissions/create_transmission_with_recipient_list.php
+++ b/examples/transmissions/create_transmission_with_recipient_list.php
@@ -2,7 +2,7 @@
namespace Examples\Transmissions;
-require dirname(__FILE__).'/../bootstrap.php';
+require __DIR__ . '/../bootstrap.php';
use SparkPost\SparkPost;
use GuzzleHttp\Client;
@@ -35,9 +35,9 @@
try {
$response = $promise->wait();
- echo $response->getStatusCode()."\n";
- print_r($response->getBody())."\n";
+ echo $response->getStatusCode() . "\n";
+ print_r($response->getBody()) . "\n";
} catch (\Exception $e) {
- echo $e->getCode()."\n";
- echo $e->getMessage()."\n";
+ echo $e->getCode() . "\n";
+ echo $e->getMessage() . "\n";
}
diff --git a/examples/transmissions/create_transmission_with_template.php b/examples/transmissions/create_transmission_with_template.php
index 66de0b9..60a8d5a 100644
--- a/examples/transmissions/create_transmission_with_template.php
+++ b/examples/transmissions/create_transmission_with_template.php
@@ -2,7 +2,7 @@
namespace Examples\Transmissions;
-require dirname(__FILE__).'/../bootstrap.php';
+require __DIR__ . '/../bootstrap.php';
use SparkPost\SparkPost;
use GuzzleHttp\Client;
@@ -34,9 +34,9 @@
try {
$response = $promise->wait();
- echo $response->getStatusCode()."\n";
- print_r($response->getBody())."\n";
+ echo $response->getStatusCode() . "\n";
+ print_r($response->getBody()) . "\n";
} catch (\Exception $e) {
- echo $e->getCode()."\n";
- echo $e->getMessage()."\n";
+ echo $e->getCode() . "\n";
+ echo $e->getMessage() . "\n";
}
diff --git a/examples/transmissions/delete_transmission.php b/examples/transmissions/delete_transmission.php
index e7fb65a..da51da9 100644
--- a/examples/transmissions/delete_transmission.php
+++ b/examples/transmissions/delete_transmission.php
@@ -2,7 +2,7 @@
namespace Examples\Transmissions;
-require dirname(__FILE__).'/../bootstrap.php';
+require __DIR__ . '/../bootstrap.php';
use SparkPost\SparkPost;
use GuzzleHttp\Client;
@@ -20,9 +20,9 @@
try {
$response = $promise->wait();
- echo $response->getStatusCode()."\n";
- print_r($response->getBody())."\n";
+ echo $response->getStatusCode() . "\n";
+ print_r($response->getBody()) . "\n";
} catch (\Exception $e) {
- echo $e->getCode()."\n";
- echo $e->getMessage()."\n";
+ echo $e->getCode() . "\n";
+ echo $e->getMessage() . "\n";
}
diff --git a/lib/SparkPost/ResourceBase.php b/lib/SparkPost/ResourceBase.php
index 3620192..9197935 100644
--- a/lib/SparkPost/ResourceBase.php
+++ b/lib/SparkPost/ResourceBase.php
@@ -2,6 +2,9 @@
namespace SparkPost;
+use Http\Client\Exception;
+use Psr\Http\Client\ClientExceptionInterface;
+
/**
* Class ResourceBase.
*/
@@ -10,20 +13,20 @@ class ResourceBase
/**
* SparkPost object used to make requests.
*/
- protected $sparkpost;
+ protected SparkPost $sparkpost;
/**
* The api endpoint that gets prepended to all requests send through this resource.
*/
- protected $endpoint;
+ protected string $endpoint;
/**
* Sets up the Resource.
*
* @param SparkPost $sparkpost - the sparkpost instance that this resource is attached to
- * @param string $endpoint - the endpoint that this resource wraps
+ * @param string $endpoint - the endpoint that this resource wraps
*/
- public function __construct(SparkPost $sparkpost, $endpoint)
+ public function __construct(SparkPost $sparkpost, string $endpoint)
{
$this->sparkpost = $sparkpost;
$this->endpoint = $endpoint;
@@ -32,9 +35,11 @@ public function __construct(SparkPost $sparkpost, $endpoint)
/**
* Sends get request to API at the set endpoint.
*
+ * @return SparkPostPromise|SparkPostResponse
+ * @throws SparkPostException|Exception|ClientExceptionInterface
* @see SparkPost->request()
*/
- public function get($uri = '', $payload = [], $headers = [])
+ public function get($uri = '', array $payload = [], array $headers = [])
{
return $this->request('GET', $uri, $payload, $headers);
}
@@ -42,9 +47,11 @@ public function get($uri = '', $payload = [], $headers = [])
/**
* Sends put request to API at the set endpoint.
*
+ * @return SparkPostPromise|SparkPostResponse
+ * @throws SparkPostException|Exception|ClientExceptionInterface
* @see SparkPost->request()
*/
- public function put($uri = '', $payload = [], $headers = [])
+ public function put($uri = '', array $payload = [], array $headers = [])
{
return $this->request('PUT', $uri, $payload, $headers);
}
@@ -52,9 +59,12 @@ public function put($uri = '', $payload = [], $headers = [])
/**
* Sends post request to API at the set endpoint.
*
+ * @return SparkPostPromise|SparkPostResponse
+ * @throws SparkPostException
+ * @throws Exception|ClientExceptionInterface
* @see SparkPost->request()
*/
- public function post($payload = [], $headers = [])
+ public function post(array $payload = [], array $headers = [])
{
return $this->request('POST', '', $payload, $headers);
}
@@ -62,9 +72,11 @@ public function post($payload = [], $headers = [])
/**
* Sends delete request to API at the set endpoint.
*
+ * @return SparkPostPromise|SparkPostResponse
+ * @throws SparkPostException|Exception|ClientExceptionInterface
* @see SparkPost->request()
*/
- public function delete($uri = '', $payload = [], $headers = [])
+ public function delete($uri = '', array $payload = [], array $headers = [])
{
return $this->request('DELETE', $uri, $payload, $headers);
}
@@ -72,11 +84,13 @@ public function delete($uri = '', $payload = [], $headers = [])
/**
* Sends requests to SparkPost object to the resource endpoint.
*
+ * @return SparkPostPromise|SparkPostResponse depending on sync or async request
+ * @throws SparkPostException
+ * @throws Exception|ClientExceptionInterface
* @see SparkPost->request()
*
- * @return SparkPostPromise or SparkPostResponse depending on sync or async request
*/
- public function request($method = 'GET', $uri = '', $payload = [], $headers = [])
+ public function request(string $method = 'GET', $uri = '', array $payload = [], array $headers = [])
{
if (is_array($uri)) {
$headers = $payload;
@@ -84,7 +98,7 @@ public function request($method = 'GET', $uri = '', $payload = [], $headers = []
$uri = '';
}
- $uri = $this->endpoint.'/'.$uri;
+ $uri = $this->endpoint . '/' . $uri;
return $this->sparkpost->request($method, $uri, $payload, $headers);
}
diff --git a/lib/SparkPost/SparkPost.php b/lib/SparkPost/SparkPost.php
index 140caf7..aa04c4b 100644
--- a/lib/SparkPost/SparkPost.php
+++ b/lib/SparkPost/SparkPost.php
@@ -2,38 +2,36 @@
namespace SparkPost;
-use Http\Client\HttpClient;
+use Http\Client\Exception;
use Http\Client\HttpAsyncClient;
-use Http\Discovery\MessageFactoryDiscovery;
-use Http\Message\RequestFactory;
+use Http\Discovery\Psr17FactoryDiscovery;
+use Psr\Http\Client\ClientExceptionInterface;
+use Psr\Http\Client\ClientInterface;
+use Psr\Http\Message\RequestFactoryInterface;
use Psr\Http\Message\RequestInterface;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\StreamFactoryInterface;
class SparkPost
{
- /**
- * @var string Library version, used for setting User-Agent
- */
- private $version = '2.3.0';
+
+ private string $version = '2.3.0';
/**
- * @var HttpClient|HttpAsyncClient used to make requests
+ * @var ClientInterface|HttpAsyncClient used to make requests
*/
private $httpClient;
- /**
- * @var RequestFactory
- */
- private $messageFactory;
+ private RequestFactoryInterface $requestFactory;
- /**
- * @var array Options for requests
- */
- private $options;
+ private StreamFactoryInterface $streamFactory;
+
+ private array $options;
/**
* Default options for requests that can be overridden with the setOptions function.
*/
- private static $defaultOptions = [
+ private static array $defaultOptions = [
'host' => 'api.sparkpost.com',
'protocol' => 'https',
'port' => 443,
@@ -44,18 +42,16 @@ class SparkPost
'retries' => 0
];
- /**
- * @var Transmission Instance of Transmission class
- */
- public $transmissions;
+ public Transmission $transmissions;
/**
* Sets up the SparkPost instance.
*
- * @param HttpClient $httpClient - An httplug client or adapter
- * @param array $options - An array to overide default options or a string to be used as an API key
+ * @param ClientInterface $httpClient - An httplug client or adapter
+ * @param array $options - An array to overide default options or a string to be used as an API key
+ * @throws \Exception
*/
- public function __construct($httpClient, array $options)
+ public function __construct(ClientInterface $httpClient, array $options)
{
$this->setOptions($options);
$this->setHttpClient($httpClient);
@@ -67,12 +63,14 @@ public function __construct($httpClient, array $options)
*
* @param string $method
* @param string $uri
- * @param array $payload - either used as the request body or url query params
- * @param array $headers
+ * @param array $payload - either used as the request body or url query params
+ * @param array $headers
*
* @return SparkPostPromise|SparkPostResponse Promise or Response depending on sync or async request
+ * @throws SparkPostException
+ * @throws \Exception
*/
- public function request($method = 'GET', $uri = '', $payload = [], $headers = [])
+ public function request(string $method = 'GET', string $uri = '', array $payload = [], array $headers = [])
{
if ($this->options['async'] === true) {
return $this->asyncRequest($method, $uri, $payload, $headers);
@@ -86,37 +84,41 @@ public function request($method = 'GET', $uri = '', $payload = [], $headers = []
*
* @param string $method
* @param string $uri
- * @param array $payload
- * @param array $headers
+ * @param array $payload
+ * @param array $headers
*
* @return SparkPostResponse
*
* @throws SparkPostException
+ * @throws \Exception
*/
- public function syncRequest($method = 'GET', $uri = '', $payload = [], $headers = [])
+ public function syncRequest(string $method = 'GET', string $uri = '', array $payload = [], array $headers = []): SparkPostResponse
{
$requestValues = $this->buildRequestValues($method, $uri, $payload, $headers);
- $request = call_user_func_array(array($this, 'buildRequestInstance'), $requestValues);
+ $request = call_user_func_array([$this, 'buildRequestInstance'], $requestValues);
$retries = $this->options['retries'];
try {
- if ($retries > 0) {
- $resp = $this->syncReqWithRetry($request, $retries);
- } else {
- $resp = $this->httpClient->sendRequest($request);
- }
+ $resp = $retries > 0
+ ? $this->syncReqWithRetry($request, $retries)
+ : $this->httpClient->sendRequest(
+ $request
+ );
return new SparkPostResponse($resp, $this->ifDebug($requestValues));
- } catch (\Exception $exception) {
+ } catch (\Throwable $exception) {
throw new SparkPostException($exception, $this->ifDebug($requestValues));
}
}
- private function syncReqWithRetry($request, $retries)
+ /**
+ * @throws Exception|ClientExceptionInterface
+ */
+ private function syncReqWithRetry( RequestInterface $request, int $retries): ResponseInterface
{
$resp = $this->httpClient->sendRequest($request);
$status = $resp->getStatusCode();
if ($status >= 500 && $status <= 599 && $retries > 0) {
- return $this->syncReqWithRetry($request, $retries-1);
+ return $this->syncReqWithRetry($request, $retries - 1);
}
return $resp;
}
@@ -126,34 +128,49 @@ private function syncReqWithRetry($request, $retries)
*
* @param string $method
* @param string $uri
- * @param array $payload
- * @param array $headers
+ * @param array $payload
+ * @param array $headers
*
* @return SparkPostPromise
+ * @throws \Exception
*/
- public function asyncRequest($method = 'GET', $uri = '', $payload = [], $headers = [])
- {
+ public function asyncRequest(
+ string $method = 'GET',
+ string $uri = '',
+ array $payload = [],
+ array $headers = []
+ ): SparkPostPromise {
if ($this->httpClient instanceof HttpAsyncClient) {
$requestValues = $this->buildRequestValues($method, $uri, $payload, $headers);
- $request = call_user_func_array(array($this, 'buildRequestInstance'), $requestValues);
+ $request = call_user_func_array([$this, 'buildRequestInstance'], $requestValues);
$retries = $this->options['retries'];
if ($retries > 0) {
- return new SparkPostPromise($this->asyncReqWithRetry($request, $retries), $this->ifDebug($requestValues));
+ return new SparkPostPromise(
+ $this->asyncReqWithRetry($request, $retries), $this->ifDebug($requestValues)
+ );
} else {
- return new SparkPostPromise($this->httpClient->sendAsyncRequest($request), $this->ifDebug($requestValues));
+ return new SparkPostPromise(
+ $this->httpClient->sendAsyncRequest($request),
+ $this->ifDebug($requestValues)
+ );
}
} else {
- throw new \Exception('Your http client does not support asynchronous requests. Please use a different client or use synchronous requests.');
+ throw new \Exception(
+ 'Your http client does not support asynchronous requests. Please use a different client or use synchronous requests.'
+ );
}
}
- private function asyncReqWithRetry($request, $retries)
+ /**
+ * @throws \Exception
+ */
+ private function asyncReqWithRetry(RequestInterface $request, int $retries)
{
- return $this->httpClient->sendAsyncRequest($request)->then(function($response) use ($request, $retries) {
+ return $this->httpClient->sendAsyncRequest($request)->then(function ($response) use ($request, $retries) {
$status = $response->getStatusCode();
if ($status >= 500 && $status <= 599 && $retries > 0) {
- return $this->asyncReqWithRetry($request, $retries-1);
+ return $this->asyncReqWithRetry($request, $retries - 1);
}
return $response;
});
@@ -164,12 +181,13 @@ private function asyncReqWithRetry($request, $retries)
*
* @param string $method
* @param string $uri
- * @param array $payload
- * @param array $headers
+ * @param array $payload
+ * @param array $headers
*
* @return array $requestValues
+ * @throws \Exception
*/
- public function buildRequestValues($method, $uri, $payload, $headers)
+ public function buildRequestValues(string $method, string $uri, array $payload, array $headers): array
{
$method = trim(strtoupper($method));
@@ -186,6 +204,11 @@ public function buildRequestValues($method, $uri, $payload, $headers)
// old form-feed workaround now removed
$body = json_encode($body);
+
+ if ($body === false) {
+ throw new \Exception('JSON encoding error: ' . json_last_error_msg());
+ }
+
return [
'method' => $method,
'url' => $url,
@@ -197,26 +220,41 @@ public function buildRequestValues($method, $uri, $payload, $headers)
/**
* Build RequestInterface from given params.
*
- * @param array $requestValues
- *
+ * @param string $method
+ * @param string $url
+ * @param array $headers
+ * @param array|string $body
* @return RequestInterface
*/
- public function buildRequestInstance($method, $url, $headers, $body)
+ public function buildRequestInstance(string $method, string $url, array $headers, $body): RequestInterface
{
- return $this->getMessageFactory()->createRequest($method, $url, $headers, $body);
+ $request = $this->getRequestFactory()->createRequest($method, $url);
+
+ foreach ($headers as $name => $value) {
+ $request = $request->withHeader($name, $value);
+ }
+
+ if (isset($body) && $body !== '' && $body !== []) {
+ $request = $request->withBody($this->getStreamFactory()->createStream($body));
+ }
+
+ return $request;
}
/**
* Build RequestInterface from given params.
*
- * @param array $requestValues
- *
+ * @param string $method
+ * @param string $uri
+ * @param array $payload
+ * @param array $headers
* @return RequestInterface
+ * @throws \Exception
*/
- public function buildRequest($method, $uri, $payload, $headers)
+ public function buildRequest(string $method, string $uri, array $payload, array $headers): RequestInterface
{
$requestValues = $this->buildRequestValues($method, $uri, $payload, $headers);
- return call_user_func_array(array($this, 'buildRequestInstance'), $requestValues);
+ return call_user_func_array([$this, 'buildRequestInstance'], $requestValues);
}
/**
@@ -226,12 +264,12 @@ public function buildRequest($method, $uri, $payload, $headers)
*
* @return array $headers - headers for the request
*/
- public function getHttpHeaders($headers = [])
+ public function getHttpHeaders(array $headers = []): array
{
$constantHeaders = [
'Authorization' => $this->options['key'],
'Content-Type' => 'application/json',
- 'User-Agent' => 'php-sparkpost/'.$this->version,
+ 'User-Agent' => 'php-sparkpost/' . $this->version,
];
foreach ($constantHeaders as $key => $value) {
@@ -244,12 +282,12 @@ public function getHttpHeaders($headers = [])
/**
* Builds the request url from the options and given params.
*
- * @param string $path - the path in the url to hit
- * @param array $params - query parameters to be encoded into the url
+ * @param string $path - the path in the url to hit
+ * @param array $params - query parameters to be encoded into the url
*
* @return string $url - the url to send the desired request to
*/
- public function getUrl($path, $params = [])
+ public function getUrl(string $path, array $params = []): string
{
$options = $this->options;
@@ -259,25 +297,31 @@ public function getUrl($path, $params = [])
$value = implode(',', $value);
}
- array_push($paramsArray, $key.'='.$value);
+ $paramsArray[] = $key . '=' . $value;
}
$paramsString = implode('&', $paramsArray);
- return $options['protocol'].'://'.$options['host'].($options['port'] ? ':'.$options['port'] : '').'/api/'.$options['version'].'/'.$path.($paramsString ? '?'.$paramsString : '');
+ return $options['protocol'] . '://' . $options['host'] . ($options['port'] ? ':' . $options['port'] : '') . '/api/' . $options['version'] . '/' . $path . ($paramsString !== '' && $paramsString !== '0' ? '?' . $paramsString : '');
}
/**
* Sets $httpClient to be used for request.
*
- * @param HttpClient|HttpAsyncClient $httpClient - the client to be used for request
+ * @param ClientInterface|HttpAsyncClient $httpClient - the client to be used for request
*
* @return SparkPost
*/
- public function setHttpClient($httpClient)
+ public function setHttpClient($httpClient): self
{
- if (!($httpClient instanceof HttpAsyncClient || $httpClient instanceof HttpClient)) {
- throw new \LogicException(sprintf('Parameter to SparkPost::setHttpClient must be instance of "%s" or "%s"', HttpClient::class, HttpAsyncClient::class));
+ if (!$httpClient instanceof ClientInterface && !$httpClient instanceof HttpAsyncClient) {
+ throw new \LogicException(
+ sprintf(
+ 'Parameter to SparkPost::setHttpClient must be instance of "%s" or "%s"',
+ ClientInterface::class,
+ HttpAsyncClient::class
+ )
+ );
}
$this->httpClient = $httpClient;
@@ -288,27 +332,31 @@ public function setHttpClient($httpClient)
/**
* Sets the options from the param and defaults for the SparkPost object.
*
- * @param array $options - either an string API key or an array of options
+ * @param string|array $options - either an string API key or an array of options
*
* @return SparkPost
+ * @throws \Exception
*/
- public function setOptions($options)
+ public function setOptions($options): self
{
// if the options map is a string we should assume that its an api key
if (is_string($options)) {
$options = ['key' => $options];
}
+ if (!isset($this->options)) {
+ $this->options = self::$defaultOptions;
+ }
+
// Validate API key because its required
- if (!isset($this->options['key']) && (!isset($options['key']) || !preg_match('/\S/', $options['key']))) {
+ $keyToValidate = $options['key'] ?? $this->options['key'] ?? '';
+ if (!preg_match('/\S/', $keyToValidate)) {
throw new \Exception('You must provide an API key');
}
- $this->options = isset($this->options) ? $this->options : self::$defaultOptions;
-
// set options, overriding defaults
foreach ($options as $option => $value) {
- if (key_exists($option, $this->options)) {
+ if (array_key_exists($option, $this->options)) {
$this->options[$option] = $value;
}
}
@@ -319,11 +367,11 @@ public function setOptions($options)
/**
* Returns the given value if debugging, an empty instance otherwise.
*
- * @param any $param
+ * @param array $param
*
- * @return any $param
+ * @return array|null $param
*/
- private function ifDebug($param)
+ private function ifDebug(array $param): ?array
{
return $this->options['debug'] ? $param : null;
}
@@ -331,31 +379,55 @@ private function ifDebug($param)
/**
* Sets up any endpoints to custom classes e.g. $this->transmissions.
*/
- private function setupEndpoints()
+ private function setupEndpoints(): void
{
$this->transmissions = new Transmission($this);
}
/**
- * @return RequestFactory
+ * @return RequestFactoryInterface
*/
- private function getMessageFactory()
+ private function getRequestFactory(): RequestFactoryInterface
{
- if (!$this->messageFactory) {
- $this->messageFactory = MessageFactoryDiscovery::find();
+ if (!isset($this->requestFactory)) {
+ $this->requestFactory = Psr17FactoryDiscovery::findRequestFactory();
}
- return $this->messageFactory;
+ return $this->requestFactory;
+ }
+
+ /**
+ * @return StreamFactoryInterface
+ */
+ private function getStreamFactory(): StreamFactoryInterface
+ {
+ if (!isset($this->streamFactory)) {
+ $this->streamFactory = Psr17FactoryDiscovery::findStreamFactory();
+ }
+
+ return $this->streamFactory;
+ }
+
+ /**
+ * @param RequestFactoryInterface $requestFactory
+ *
+ * @return SparkPost
+ */
+ public function setRequestFactory(RequestFactoryInterface $requestFactory): self
+ {
+ $this->requestFactory = $requestFactory;
+
+ return $this;
}
/**
- * @param RequestFactory $messageFactory
+ * @param StreamFactoryInterface $streamFactory
*
* @return SparkPost
*/
- public function setMessageFactory(RequestFactory $messageFactory)
+ public function setStreamFactory(StreamFactoryInterface $streamFactory): self
{
- $this->messageFactory = $messageFactory;
+ $this->streamFactory = $streamFactory;
return $this;
}
diff --git a/lib/SparkPost/SparkPostException.php b/lib/SparkPost/SparkPostException.php
index bde2e5e..bbec4cb 100644
--- a/lib/SparkPost/SparkPostException.php
+++ b/lib/SparkPost/SparkPostException.php
@@ -9,19 +9,20 @@ class SparkPostException extends \Exception
/**
* Variable to hold json decoded body from http response.
*/
- private $body = null;
+ private ?array $body = null;
/**
* Array with the request values sent.
*/
- private $request;
+ private ?array $request;
/**
* Sets up the custom exception and copies over original exception values.
*
- * @param Exception $exception - the exception to be wrapped
+ * @param \Throwable $exception - the exception to be wrapped
+ * @param null $request
*/
- public function __construct(\Exception $exception, $request = null)
+ public function __construct(\Throwable $exception, $request = null)
{
$this->request = $request;
@@ -33,7 +34,7 @@ public function __construct(\Exception $exception, $request = null)
$code = $exception->getResponse()->getStatusCode();
}
- parent::__construct($message, $code, $exception->getPrevious());
+ parent::__construct($message, $code, $exception);
}
/**
@@ -41,7 +42,7 @@ public function __construct(\Exception $exception, $request = null)
*
* @return array $request
*/
- public function getRequest()
+ public function getRequest(): ?array
{
return $this->request;
}
@@ -51,7 +52,7 @@ public function getRequest()
*
* @return array $body - the json decoded body from the http response
*/
- public function getBody()
+ public function getBody(): ?array
{
return $this->body;
}
diff --git a/lib/SparkPost/SparkPostPromise.php b/lib/SparkPost/SparkPostPromise.php
index b7ded0c..6d671bd 100644
--- a/lib/SparkPost/SparkPostPromise.php
+++ b/lib/SparkPost/SparkPostPromise.php
@@ -9,19 +9,20 @@ class SparkPostPromise implements HttpPromise
/**
* HttpPromise to be wrapped by SparkPostPromise.
*/
- private $promise;
+ private HttpPromise $promise;
/**
* Array with the request values sent.
*/
- private $request;
+ private ?array $request;
/**
* set the promise to be wrapped.
*
* @param HttpPromise $promise
+ * @param array|null $request
*/
- public function __construct(HttpPromise $promise, $request = null)
+ public function __construct(HttpPromise $promise, ?array $request = null)
{
$this->promise = $promise;
$this->request = $request;
@@ -30,18 +31,19 @@ public function __construct(HttpPromise $promise, $request = null)
/**
* Hand off the response functions to the original promise and return a custom response or exception.
*
- * @param callable $onFulfilled - function to be called if the promise is fulfilled
- * @param callable $onRejected - function to be called if the promise is rejected
+ * @param callable|null $onFulfilled - function to be called if the promise is fulfilled
+ * @param callable|null $onRejected - function to be called if the promise is rejected
+ * @return HttpPromise
*/
- public function then(callable $onFulfilled = null, callable $onRejected = null)
+ public function then(?callable $onFulfilled = null, ?callable $onRejected = null): HttpPromise
{
$request = $this->request;
- return $this->promise->then(function ($response) use ($onFulfilled, $request) {
+ return $this->promise->then(function ($response) use ($onFulfilled, $request): void {
if (isset($onFulfilled)) {
$onFulfilled(new SparkPostResponse($response, $request));
}
- }, function ($exception) use ($onRejected, $request) {
+ }, function ($exception) use ($onRejected, $request): void {
if (isset($onRejected)) {
$onRejected(new SparkPostException($exception, $request));
}
@@ -51,9 +53,9 @@ public function then(callable $onFulfilled = null, callable $onRejected = null)
/**
* Hand back the state.
*
- * @return $state - returns the state of the promise
+ * @return string $state - returns the state of the promise
*/
- public function getState()
+ public function getState(): string
{
return $this->promise->getState();
}
@@ -67,13 +69,13 @@ public function getState()
*
* @throws SparkPostException
*/
- public function wait($unwrap = true)
+ public function wait($unwrap = true): SparkPostResponse
{
try {
$response = $this->promise->wait($unwrap);
return $response ? new SparkPostResponse($response, $this->request) : $response;
- } catch (\Exception $exception) {
+ } catch (\Throwable $exception) {
throw new SparkPostException($exception, $this->request);
}
}
diff --git a/lib/SparkPost/SparkPostResponse.php b/lib/SparkPost/SparkPostResponse.php
index 402a2b2..a7ba1c3 100644
--- a/lib/SparkPost/SparkPostResponse.php
+++ b/lib/SparkPost/SparkPostResponse.php
@@ -2,6 +2,7 @@
namespace SparkPost;
+use Psr\Http\Message\MessageInterface as MessageInterface;
use Psr\Http\Message\ResponseInterface as ResponseInterface;
use Psr\Http\Message\StreamInterface as StreamInterface;
@@ -10,17 +11,18 @@ class SparkPostResponse implements ResponseInterface
/**
* ResponseInterface to be wrapped by SparkPostResponse.
*/
- private $response;
+ private ResponseInterface $response;
/**
* Array with the request values sent.
*/
- private $request;
+ private ?array $request;
/**
* set the response to be wrapped.
*
* @param ResponseInterface $response
+ * @param null $request
*/
public function __construct(ResponseInterface $response, $request = null)
{
@@ -33,90 +35,96 @@ public function __construct(ResponseInterface $response, $request = null)
*
* @return array $request
*/
- public function getRequest()
+ public function getRequest(): ?array
{
return $this->request;
}
/**
- * Returns the body.
- *
- * @return array $body - the json decoded body from the http response
+ * Returns the body as StreamInterface (PSR-7).
*/
- public function getBody()
+ public function getBody(): StreamInterface
{
- $body = $this->response->getBody();
- $body_string = $body->__toString();
+ return $this->response->getBody();
+ }
+ /**
+ * Returns the json decoded body from the http response.
+ *
+ * @return array $body
+ */
+ public function getBodyDecoded(): array
+ {
+ $body_string = $this->response->getBody()->__toString();
$json = json_decode($body_string, true);
- return $json;
+ return $json ?? [];
}
/**
* pass these down to the response given in the constructor.
*/
- public function getProtocolVersion()
+ public function getProtocolVersion(): string
{
return $this->response->getProtocolVersion();
}
- public function withProtocolVersion($version)
+ public function withProtocolVersion(string $version): MessageInterface
{
return $this->response->withProtocolVersion($version);
}
- public function getHeaders()
+ public function getHeaders(): array
{
return $this->response->getHeaders();
}
- public function hasHeader($name)
+ public function hasHeader(string $name): bool
{
return $this->response->hasHeader($name);
}
- public function getHeader($name)
+ public function getHeader(string $name): array
{
return $this->response->getHeader($name);
}
- public function getHeaderLine($name)
+ public function getHeaderLine(string $name): string
{
return $this->response->getHeaderLine($name);
}
- public function withHeader($name, $value)
+ public function withHeader(string $name, $value): MessageInterface
{
return $this->response->withHeader($name, $value);
}
- public function withAddedHeader($name, $value)
+ public function withAddedHeader(string $name, $value): MessageInterface
{
return $this->response->withAddedHeader($name, $value);
}
- public function withoutHeader($name)
+ public function withoutHeader(string $name): MessageInterface
{
return $this->response->withoutHeader($name);
}
- public function withBody(StreamInterface $body)
+ public function withBody(StreamInterface $body): MessageInterface
{
return $this->response->withBody($body);
}
- public function getStatusCode()
+ public function getStatusCode(): int
{
return $this->response->getStatusCode();
}
- public function withStatus($code, $reasonPhrase = '')
+ public function withStatus(int $code, string $reasonPhrase = ''): ResponseInterface
{
return $this->response->withStatus($code, $reasonPhrase);
}
- public function getReasonPhrase()
+ public function getReasonPhrase(): string
{
return $this->response->getReasonPhrase();
}
diff --git a/lib/SparkPost/Transmission.php b/lib/SparkPost/Transmission.php
index 8efc934..8b1205d 100644
--- a/lib/SparkPost/Transmission.php
+++ b/lib/SparkPost/Transmission.php
@@ -30,13 +30,13 @@ public function post($payload = [], $headers = [])
*
* @return array - the modified request body
*/
- public function formatPayload($payload)
+ public function formatPayload(array $payload): array
{
$payload = $this->formatBlindCarbonCopy($payload); //Fixes BCCs into payload
$payload = $this->formatCarbonCopy($payload); //Fixes CCs into payload
- $payload = $this->formatShorthandRecipients($payload); //Fixes shorthand recipients format
+ //Fixes shorthand recipients format
- return $payload;
+ return $this->formatShorthandRecipients($payload);
}
/**
@@ -46,9 +46,8 @@ public function formatPayload($payload)
*
* @return array - the modified request body
*/
- private function formatBlindCarbonCopy($payload)
+ private function formatBlindCarbonCopy(array $payload): array
{
-
//If there's a list of BCC recipients, move them into the correct format
if (isset($payload['bcc'])) {
$payload = $this->addListToRecipients($payload, 'bcc');
@@ -64,16 +63,17 @@ private function formatBlindCarbonCopy($payload)
*
* @return array - the modified request body
*/
- private function formatCarbonCopy($payload)
+ private function formatCarbonCopy(array $payload): array
{
if (isset($payload['cc'])) {
$ccAddresses = [];
- for ($i = 0; $i < count($payload['cc']); ++$i) {
- array_push($ccAddresses, $this->toAddressString($payload['cc'][$i]['address']));
+ $counter = count($payload['cc']);
+ for ($i = 0; $i < $counter; ++$i) {
+ $ccAddresses[] = $this->toAddressString($payload['cc'][$i]['address']);
}
// set up the content headers as either what it was before or an empty array
- $payload['content']['headers'] = isset($payload['content']['headers']) ? $payload['content']['headers'] : [];
+ $payload['content']['headers'] ??= [];
// add cc header
$payload['content']['headers']['CC'] = implode(',', $ccAddresses);
@@ -89,14 +89,16 @@ private function formatCarbonCopy($payload)
* @param array $payload - the request body
*
* @return array - the modified request body
+ * @throws \Exception
*/
- private function formatShorthandRecipients($payload)
+ private function formatShorthandRecipients(array $payload): array
{
if (isset($payload['content']['from'])) {
$payload['content']['from'] = $this->toAddressObject($payload['content']['from']);
}
+ $counter = count($payload['recipients']);
- for ($i = 0; $i < count($payload['recipients']); ++$i) {
+ for ($i = 0; $i < $counter; ++$i) {
$payload['recipients'][$i]['address'] = $this->toAddressObject($payload['recipients'][$i]['address']);
}
@@ -106,12 +108,13 @@ private function formatShorthandRecipients($payload)
/**
* Loops through the given listName in the payload and adds all the recipients to the recipients list after removing their names.
*
- * @param array $payload - the request body
- * @param array $listName - the name of the array in the payload to be moved to the recipients list
+ * @param array $payload - the request body
+ * @param string $listName - the name of the array in the payload to be moved to the recipients list
*
* @return array - the modified request body
+ * @throws \Exception
*/
- private function addListToRecipients($payload, $listName)
+ private function addListToRecipients(array $payload, string $listName): array
{
$originalAddress = $this->toAddressString($payload['recipients'][0]['address']);
foreach ($payload[$listName] as $recipient) {
@@ -123,7 +126,7 @@ private function addListToRecipients($payload, $listName)
unset($recipient['address']['name']);
}
- array_push($payload['recipients'], $recipient);
+ $payload['recipients'][] = $recipient;
}
//Delete the original object from the payload.
@@ -138,8 +141,9 @@ private function addListToRecipients($payload, $listName)
* @param $address - the shorthand form of an email address "Name "
*
* @return array - the longhand form of an email address [ "name" => "John", "email" => "john@exmmple.com" ]
+ * @throws \Exception
*/
- private function toAddressObject($address)
+ private function toAddressObject($address): array
{
$formatted = $address;
if (is_string($formatted)) {
@@ -152,7 +156,7 @@ private function toAddressObject($address)
$formatted['name'] = $matches[1];
$formatted['email'] = $matches[2];
} else {
- throw new \Exception('Invalid address format: '.$address);
+ throw new \Exception('Invalid address format: ' . $address);
}
}
@@ -162,18 +166,13 @@ private function toAddressObject($address)
/**
* Takes the longhand form of an email address and converts it to the shorthand form.
*
- * @param $address - the longhand form of an email address [ "name" => "John", "email" => "john@exmmple.com" ]
- * @param string - the shorthand form of an email address "Name "
+ * @param string|array $address - the longhand form of an email address [ "name" => "John", "email" => "john@exmmple.com" ]
*/
- private function toAddressString($address)
+ private function toAddressString($address): string
{
// convert object to string
if (!is_string($address)) {
- if (isset($address['name'])) {
- $address = '"'.$address['name'].'" <'.$address['email'].'>';
- } else {
- $address = $address['email'];
- }
+ $address = isset($address['name']) ? '"' . $address['name'] . '" <' . $address['email'] . '>' : $address['email'];
}
return $address;
@@ -183,9 +182,9 @@ private function toAddressString($address)
* Checks if a string is an email.
*
* @param string $email - a string that might be an email address
- * @param bool - true if the given string is an email
+ * @return bool - true if the given string is an email
*/
- private function isEmail($email)
+ private function isEmail(string $email): bool
{
if (filter_var($email, FILTER_VALIDATE_EMAIL)) {
return true;
diff --git a/rector.php b/rector.php
new file mode 100644
index 0000000..55b2ebe
--- /dev/null
+++ b/rector.php
@@ -0,0 +1,26 @@
+paths([
+ __DIR__ . '/examples',
+ __DIR__ . '/lib',
+ __DIR__ . '/test',
+ ]);
+
+ // register a single rule
+ $rectorConfig->rule(InlineConstructorDefaultToPropertyRector::class);
+
+ // define sets of rules
+ $rectorConfig->sets([
+ LevelSetList::UP_TO_PHP_74,
+ SetList::TYPE_DECLARATION,
+ SetList::CODE_QUALITY,
+ ]);
+};
diff --git a/test/unit/IssueReproductionTest.php b/test/unit/IssueReproductionTest.php
new file mode 100644
index 0000000..48533c9
--- /dev/null
+++ b/test/unit/IssueReproductionTest.php
@@ -0,0 +1,28 @@
+ 'test-key']);
+
+ // Invalid UTF-8 sequence to make json_encode return false
+ $payload = ['invalid' => "\xB1\x31"];
+
+ // We expect an Exception because json_encode fails
+ try {
+ $sparkpost->buildRequest('POST', 'test', $payload, []);
+ $this->fail('Expected \Exception was not thrown');
+ } catch (\Exception $e) {
+ $this->assertStringContainsString('JSON encoding error', $e->getMessage());
+ }
+ }
+}
diff --git a/test/unit/SparkPostResponseTest.php b/test/unit/SparkPostResponseTest.php
index 90f16be..03485d6 100644
--- a/test/unit/SparkPostResponseTest.php
+++ b/test/unit/SparkPostResponseTest.php
@@ -3,46 +3,77 @@
namespace SparkPost\Test;
use PHPUnit\Framework\TestCase;
+use Psr\Http\Message\MessageInterface;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\StreamInterface;
use SparkPost\SparkPostResponse;
use Mockery;
class SparkPostResponseTest extends TestCase
{
- /** @var Mockery\MockInterface|\Psr\Http\Message\ResponseInterface */
+ /** @var Mockery\MockInterface|ResponseInterface */
private $responseMock;
/** @var string */
- private $returnValue;
+ private string $returnValue;
public function setUp(): void
{
$this->returnValue = 'some_value_to_return';
- $this->responseMock = Mockery::mock('Psr\Http\Message\ResponseInterface');
+ $this->responseMock = Mockery::mock(ResponseInterface::class);
}
- public function testGetProtocolVersion()
+ /**
+ * Test that getProtocolVersion returns the protocol version from the wrapped response.
+ *
+ * Why: Ensures that the SparkPostResponse correctly delegates the call to the underlying PSR-7 response object.
+ * How: Mocks ResponseInterface, sets expectation for getProtocolVersion, and verifies the wrapper returns the same value.
+ */
+ public function testGetProtocolVersion(): void
{
$this->responseMock->shouldReceive('getProtocolVersion')->andReturn($this->returnValue);
$sparkpostResponse = new SparkPostResponse($this->responseMock);
$this->assertEquals($this->responseMock->getProtocolVersion(), $sparkpostResponse->getProtocolVersion());
}
- public function testWithProtocolVersion()
+ /**
+ * Test that withProtocolVersion returns a new instance with the specified protocol version.
+ *
+ * Why: Ensures that the SparkPostResponse correctly delegates the call to the underlying PSR-7 response object.
+ * How: Mocks ResponseInterface, sets expectation for withProtocolVersion, and verifies the returned object matches the mock result.
+ */
+ public function testWithProtocolVersion(): void
{
$param = 'protocol version';
+ $messageMock = Mockery::mock(MessageInterface::class);
- $this->responseMock->shouldReceive('withProtocolVersion')->andReturn($this->returnValue);
+ $this->responseMock->shouldReceive('withProtocolVersion')->andReturn($messageMock);
$sparkpostResponse = new SparkPostResponse($this->responseMock);
- $this->assertEquals($this->responseMock->withProtocolVersion($param), $sparkpostResponse->withProtocolVersion($param));
+ $this->assertEquals(
+ $this->responseMock->withProtocolVersion($param),
+ $sparkpostResponse->withProtocolVersion($param)
+ );
}
- public function testGetHeaders()
+ /**
+ * Test that getHeaders returns the headers from the wrapped response.
+ *
+ * Why: Ensures that the SparkPostResponse correctly delegates the call to the underlying PSR-7 response object.
+ * How: Mocks ResponseInterface, sets expectation for getHeaders, and verifies the wrapper returns the same array.
+ */
+ public function testGetHeaders(): void
{
- $this->responseMock->shouldReceive('getHeaders')->andReturn($this->returnValue);
+ $this->responseMock->shouldReceive('getHeaders')->andReturn([]);
$sparkpostResponse = new SparkPostResponse($this->responseMock);
$this->assertEquals($this->responseMock->getHeaders(), $sparkpostResponse->getHeaders());
}
- public function testHasHeader()
+ /**
+ * Test that hasHeader returns whether a header exists in the wrapped response.
+ *
+ * Why: Ensures that the SparkPostResponse correctly delegates the call to the underlying PSR-7 response object.
+ * How: Mocks ResponseInterface, sets expectation for hasHeader, and verifies the wrapper returns the expected boolean/value.
+ */
+ public function testHasHeader(): void
{
$param = 'header';
@@ -51,16 +82,28 @@ public function testHasHeader()
$this->assertEquals($this->responseMock->hasHeader($param), $sparkpostResponse->hasHeader($param));
}
- public function testGetHeader()
+ /**
+ * Test that getHeader returns the specified header from the wrapped response.
+ *
+ * Why: Ensures that the SparkPostResponse correctly delegates the call to the underlying PSR-7 response object.
+ * How: Mocks ResponseInterface, sets expectation for getHeader, and verifies the wrapper returns the same array.
+ */
+ public function testGetHeader(): void
{
$param = 'header';
- $this->responseMock->shouldReceive('getHeader')->andReturn($this->returnValue);
+ $this->responseMock->shouldReceive('getHeader')->andReturn([]);
$sparkpostResponse = new SparkPostResponse($this->responseMock);
$this->assertEquals($this->responseMock->getHeader($param), $sparkpostResponse->getHeader($param));
}
- public function testGetHeaderLine()
+ /**
+ * Test that getHeaderLine returns the specified header line from the wrapped response.
+ *
+ * Why: Ensures that the SparkPostResponse correctly delegates the call to the underlying PSR-7 response object.
+ * How: Mocks ResponseInterface, sets expectation for getHeaderLine, and verifies the wrapper returns the same string.
+ */
+ public function testGetHeaderLine(): void
{
$param = 'header';
@@ -69,36 +112,69 @@ public function testGetHeaderLine()
$this->assertEquals($this->responseMock->getHeaderLine($param), $sparkpostResponse->getHeaderLine($param));
}
- public function testWithHeader()
+ /**
+ * Test that withHeader returns a new instance with the specified header.
+ *
+ * Why: Ensures that the SparkPostResponse correctly delegates the call to the underlying PSR-7 response object.
+ * How: Mocks ResponseInterface, sets expectation for withHeader, and verifies the returned object matches the mock result.
+ */
+ public function testWithHeader(): void
{
$param = 'header';
$param2 = 'value';
+ $messageMock = Mockery::mock(MessageInterface::class);
- $this->responseMock->shouldReceive('withHeader')->andReturn($this->returnValue);
+ $this->responseMock->shouldReceive('withHeader')->andReturn($messageMock);
$sparkpostResponse = new SparkPostResponse($this->responseMock);
- $this->assertEquals($this->responseMock->withHeader($param, $param2), $sparkpostResponse->withHeader($param, $param2));
+ $this->assertEquals(
+ $this->responseMock->withHeader($param, $param2),
+ $sparkpostResponse->withHeader($param, $param2)
+ );
}
- public function testWithAddedHeader()
+ /**
+ * Test that withAddedHeader returns a new instance with the specified header added.
+ *
+ * Why: Ensures that the SparkPostResponse correctly delegates the call to the underlying PSR-7 response object.
+ * How: Mocks ResponseInterface, sets expectation for withAddedHeader, and verifies the returned object matches the mock result.
+ */
+ public function testWithAddedHeader(): void
{
$param = 'header';
$param2 = 'value';
+ $messageMock = Mockery::mock(MessageInterface::class);
- $this->responseMock->shouldReceive('withAddedHeader')->andReturn($this->returnValue);
+ $this->responseMock->shouldReceive('withAddedHeader')->andReturn($messageMock);
$sparkpostResponse = new SparkPostResponse($this->responseMock);
- $this->assertEquals($this->responseMock->withAddedHeader($param, $param2), $sparkpostResponse->withAddedHeader($param, $param2));
+ $this->assertEquals(
+ $this->responseMock->withAddedHeader($param, $param2),
+ $sparkpostResponse->withAddedHeader($param, $param2)
+ );
}
- public function testWithoutHeader()
+ /**
+ * Test that withoutHeader returns a new instance without the specified header.
+ *
+ * Why: Ensures that the SparkPostResponse correctly delegates the call to the underlying PSR-7 response object.
+ * How: Mocks ResponseInterface, sets expectation for withoutHeader, and verifies the returned object matches the mock result.
+ */
+ public function testWithoutHeader(): void
{
$param = 'header';
+ $messageMock = Mockery::mock(MessageInterface::class);
- $this->responseMock->shouldReceive('withoutHeader')->andReturn($this->returnValue);
+ $this->responseMock->shouldReceive('withoutHeader')->andReturn($messageMock);
$sparkpostResponse = new SparkPostResponse($this->responseMock);
$this->assertEquals($this->responseMock->withoutHeader($param), $sparkpostResponse->withoutHeader($param));
}
- public function testGetRequest()
+ /**
+ * Test that getRequest returns the request data passed during construction.
+ *
+ * Why: Ensures that the SparkPostResponse correctly stores and retrieves the request metadata used for the API call.
+ * How: Initializes SparkPostResponse with a request array and verifies getRequest() returns that same array.
+ */
+ public function testGetRequest(): void
{
$request = ['some' => 'request'];
$this->responseMock->shouldReceive('getRequest')->andReturn($request);
@@ -106,32 +182,57 @@ public function testGetRequest()
$this->assertEquals($sparkpostResponse->getRequest(), $request);
}
- public function testWithBody()
+ /**
+ * Test that withBody returns a new instance with the specified body.
+ *
+ * Why: Ensures that the SparkPostResponse correctly delegates the call to the underlying PSR-7 response object.
+ * How: Mocks ResponseInterface, sets expectation for withBody, and verifies the returned object matches the mock result.
+ */
+ public function testWithBody(): void
{
- $param = Mockery::mock('Psr\Http\Message\StreamInterface');
+ $param = Mockery::mock(StreamInterface::class);
+ $messageMock = Mockery::mock(MessageInterface::class);
- $this->responseMock->shouldReceive('withBody')->andReturn($this->returnValue);
+ $this->responseMock->shouldReceive('withBody')->andReturn($messageMock);
$sparkpostResponse = new SparkPostResponse($this->responseMock);
$this->assertEquals($this->responseMock->withBody($param), $sparkpostResponse->withBody($param));
}
- public function testGetStatusCode()
+ /**
+ * Test that getStatusCode returns the status code from the wrapped response.
+ *
+ * Why: Ensures that the SparkPostResponse correctly delegates the call to the underlying PSR-7 response object.
+ * How: Mocks ResponseInterface, sets expectation for getStatusCode, and verifies the wrapper returns the same integer.
+ */
+ public function testGetStatusCode(): void
{
- $this->responseMock->shouldReceive('getStatusCode')->andReturn($this->returnValue);
+ $this->responseMock->shouldReceive('getStatusCode')->andReturn(200);
$sparkpostResponse = new SparkPostResponse($this->responseMock);
$this->assertEquals($this->responseMock->getStatusCode(), $sparkpostResponse->getStatusCode());
}
- public function testWithStatus()
+ /**
+ * Test that withStatus returns a new instance with the specified status code.
+ *
+ * Why: Ensures that the SparkPostResponse correctly delegates the call to the underlying PSR-7 response object.
+ * How: Mocks ResponseInterface, sets expectation for withStatus, and verifies the returned object matches the mock result.
+ */
+ public function testWithStatus(): void
{
- $param = 'status';
+ $param = 200;
- $this->responseMock->shouldReceive('withStatus')->andReturn($this->returnValue);
+ $this->responseMock->shouldReceive('withStatus')->andReturn($this->responseMock);
$sparkpostResponse = new SparkPostResponse($this->responseMock);
$this->assertEquals($this->responseMock->withStatus($param), $sparkpostResponse->withStatus($param));
}
- public function testGetReasonPhrase()
+ /**
+ * Test that getReasonPhrase returns the reason phrase from the wrapped response.
+ *
+ * Why: Ensures that the SparkPostResponse correctly delegates the call to the underlying PSR-7 response object.
+ * How: Mocks ResponseInterface, sets expectation for getReasonPhrase, and verifies the wrapper returns the same string.
+ */
+ public function testGetReasonPhrase(): void
{
$this->responseMock->shouldReceive('getReasonPhrase')->andReturn($this->returnValue);
$sparkpostResponse = new SparkPostResponse($this->responseMock);
diff --git a/test/unit/SparkPostTest.php b/test/unit/SparkPostTest.php
index d707ced..a07bc1a 100644
--- a/test/unit/SparkPostTest.php
+++ b/test/unit/SparkPostTest.php
@@ -2,33 +2,43 @@
namespace SparkPost\Test;
+use Http\Client\Exception;
+use Http\Client\Exception\HttpException;
use Http\Client\HttpAsyncClient;
-use Http\Client\HttpClient;
-use Http\Message\MessageFactory;
+use Http\Promise\Promise;
+use Psr\Http\Client\ClientInterface;
+use Psr\Http\Message\RequestFactoryInterface;
+use Psr\Http\Message\RequestInterface;
use Nyholm\NSA;
use PHPUnit\Framework\TestCase;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\StreamInterface;
use SparkPost\SparkPost;
+use SparkPost\SparkPostException;
use SparkPost\SparkPostPromise;
use GuzzleHttp\Promise\FulfilledPromise as GuzzleFulfilledPromise;
use GuzzleHttp\Promise\RejectedPromise as GuzzleRejectedPromise;
-use Http\Adapter\Guzzle6\Promise as GuzzleAdapterPromise;
+use Http\Adapter\Guzzle7\Promise as GuzzleAdapterPromise;
use Mockery;
+use SparkPost\SparkPostResponse;
class SparkPostTest extends TestCase
{
+ public array $badResponseBody;
+ public $badResponseMock;
private $clientMock;
/** @var SparkPost */
- private $resource;
+ private SparkPost $resource;
private $exceptionMock;
- private $exceptionBody;
+ private array $exceptionBody;
private $responseMock;
- private $responseBody;
+ private array $responseBody;
private $promiseMock;
- private $postTransmissionPayload = [
+ private array $postTransmissionPayload = [
'content' => [
'from' => ['name' => 'Sparkpost Team', 'email' => 'postmaster@sendmailfor.me'],
'subject' => 'First Mailing From PHP',
@@ -40,43 +50,48 @@ class SparkPostTest extends TestCase
],
];
- private $getTransmissionPayload = [
+ private array $getTransmissionPayload = [
'campaign_id' => 'thanksgiving',
];
+ /**
+ * @throws \Exception
+ */
public function setUp(): void
{
// response mock up
- $responseBodyMock = Mockery::mock();
+ $responseBodyMock = Mockery::mock(StreamInterface::class);
$this->responseBody = ['results' => 'yay'];
- $this->responseMock = Mockery::mock('Psr\Http\Message\ResponseInterface');
+ $this->responseMock = Mockery::mock(ResponseInterface::class);
$this->responseMock->shouldReceive('getStatusCode')->andReturn(200);
$this->responseMock->shouldReceive('getBody')->andReturn($responseBodyMock);
$responseBodyMock->shouldReceive('__toString')->andReturn(json_encode($this->responseBody));
- $errorBodyMock = Mockery::mock();
+ $errorBodyMock = Mockery::mock(StreamInterface::class);
$this->badResponseBody = ['errors' => []];
- $this->badResponseMock = Mockery::mock('Psr\Http\Message\ResponseInterface');
+ $this->badResponseMock = Mockery::mock(ResponseInterface::class);
$this->badResponseMock->shouldReceive('getStatusCode')->andReturn(503);
$this->badResponseMock->shouldReceive('getBody')->andReturn($errorBodyMock);
$errorBodyMock->shouldReceive('__toString')->andReturn(json_encode($this->badResponseBody));
// exception mock up
- $exceptionResponseMock = Mockery::mock();
+ $exceptionResponseMock = Mockery::mock(ResponseInterface::class);
+ $exceptionResponseBodyMock = Mockery::mock(StreamInterface::class);
$this->exceptionBody = ['results' => 'failed'];
- $this->exceptionMock = Mockery::mock('Http\Client\Exception\HttpException');
+ $this->exceptionMock = Mockery::mock(HttpException::class);
$this->exceptionMock->shouldReceive('getResponse')->andReturn($exceptionResponseMock);
$exceptionResponseMock->shouldReceive('getStatusCode')->andReturn(500);
- $exceptionResponseMock->shouldReceive('getBody->__toString')->andReturn(json_encode($this->exceptionBody));
+ $exceptionResponseMock->shouldReceive('getBody')->andReturn($exceptionResponseBodyMock);
+ $exceptionResponseBodyMock->shouldReceive('__toString')->andReturn(json_encode($this->exceptionBody));
// promise mock up
- $this->promiseMock = Mockery::mock('Http\Promise\Promise');
+ $this->promiseMock = Mockery::mock(Promise::class);
//setup mock for the adapter
- $this->clientMock = Mockery::mock('Http\Adapter\Guzzle6\Client');
- $this->clientMock->shouldReceive('sendAsyncRequest')->
- with(Mockery::type('GuzzleHttp\Psr7\Request'))->
- andReturn($this->promiseMock);
+ $this->clientMock = Mockery::mock(ClientInterface::class, HttpAsyncClient::class);
+ $this->clientMock->shouldReceive('sendAsyncRequest')
+ ->with(Mockery::type(RequestInterface::class))
+ ->andReturn($this->promiseMock);
$this->resource = new SparkPost($this->clientMock, ['key' => 'SPARKPOST_API_KEY']);
}
@@ -86,33 +101,77 @@ public function tearDown(): void
Mockery::close();
}
- public function testRequestSync()
+ /**
+ * Test that request() returns a SparkPostResponse when async option is false.
+ *
+ * Why: Ensures the SparkPost client correctly handles synchronous request mode.
+ * How: Sets 'async' to false, mocks the client's sendRequest method, and asserts the returned object is a SparkPostResponse.
+ *
+ * @throws SparkPostException
+ * @throws \Exception
+ */
+ public function testRequestSync(): void
{
$this->resource->setOptions(['async' => false]);
$this->clientMock->shouldReceive('sendRequest')->andReturn($this->responseMock);
- $this->assertInstanceOf('SparkPost\SparkPostResponse', $this->resource->request('POST', 'transmissions', $this->postTransmissionPayload));
+ $this->assertInstanceOf(
+ SparkPostResponse::class,
+ $this->resource->request('POST', 'transmissions', $this->postTransmissionPayload)
+ );
}
- public function testRequestAsync()
+ /**
+ * Test that request() returns a SparkPostPromise when async option is true.
+ *
+ * Why: Ensures the SparkPost client correctly handles asynchronous request mode.
+ * How: Sets 'async' to true, mocks the client's sendAsyncRequest method, and asserts the returned object is a SparkPostPromise.
+ *
+ * @throws SparkPostException
+ * @throws \Exception
+ */
+ public function testRequestAsync(): void
{
- $promiseMock = Mockery::mock('Http\Promise\Promise');
+ $promiseMock = Mockery::mock(Promise::class);
$this->resource->setOptions(['async' => true]);
$this->clientMock->shouldReceive('sendAsyncRequest')->andReturn($promiseMock);
- $this->assertInstanceOf('SparkPost\SparkPostPromise', $this->resource->request('GET', 'transmissions', $this->getTransmissionPayload));
+ $this->assertInstanceOf(
+ SparkPostPromise::class,
+ $this->resource->request('GET', 'transmissions', $this->getTransmissionPayload)
+ );
}
- public function testDebugOptionWhenFalse() {
+ /**
+ * Test that the debug option, when false, does not include request data in the response.
+ *
+ * Why: Verifies that sensitive or large request data is not attached to the response object by default.
+ * How: Sets 'debug' to false, executes a request, and asserts that getRequest() on the resulting response returns null.
+ *
+ * @throws SparkPostException
+ * @throws \Exception
+ */
+ public function testDebugOptionWhenFalse(): void
+ {
$this->resource->setOptions(['async' => false, 'debug' => false]);
$this->clientMock->shouldReceive('sendRequest')->andReturn($this->responseMock);
$response = $this->resource->request('POST', 'transmissions', $this->postTransmissionPayload);
- $this->assertEquals($response->getRequest(), null);
+ $this->assertEquals(null, $response->getRequest());
}
- public function testDebugOptionWhenTrue() {
+ /**
+ * Test that the debug option, when true, includes request data in successful and failed responses.
+ *
+ * Why: Ensures developers can access original request parameters from response/exception objects for debugging.
+ * How: Sets 'debug' to true, executes requests (one success, one failure), and verifies that request data is correctly attached.
+ *
+ * @throws SparkPostException
+ * @throws \Exception
+ */
+ public function testDebugOptionWhenTrue(): void
+ {
// setup
$this->resource->setOptions(['async' => false, 'debug' => true]);
@@ -126,59 +185,92 @@ public function testDebugOptionWhenTrue() {
try {
$response = $this->resource->request('POST', 'transmissions', $this->postTransmissionPayload);
- }
- catch (\Exception $e) {
+ } catch (\Exception $e) {
$this->assertEquals(json_decode($e->getRequest()['body'], true), $this->postTransmissionPayload);
}
}
- public function testSuccessfulSyncRequest()
+ /**
+ * Test a successful synchronous request.
+ *
+ * Why: Verifies that syncRequest() correctly processes a successful API call.
+ * How: Mocks a successful sendRequest call and asserts the SparkPostResponse contains the expected body and status code.
+ *
+ * @throws Exception
+ * @throws SparkPostException
+ */
+ public function testSuccessfulSyncRequest(): void
{
$this->clientMock->shouldReceive('sendRequest')->
- once()->
- with(Mockery::type('GuzzleHttp\Psr7\Request'))->
- andReturn($this->responseMock);
+ once()->
+ with(Mockery::type(RequestInterface::class))->
+ andReturn($this->responseMock);
$response = $this->resource->syncRequest('POST', 'transmissions', $this->postTransmissionPayload);
- $this->assertEquals($this->responseBody, $response->getBody());
+ $this->assertEquals($this->responseBody, $response->getBodyDecoded());
$this->assertEquals(200, $response->getStatusCode());
}
- public function testUnsuccessfulSyncRequest()
+ /**
+ * Test an unsuccessful synchronous request.
+ *
+ * Why: Ensures that HTTP client exceptions are properly wrapped and re-thrown as SparkPostExceptions.
+ * How: Mocks a client exception during sendRequest and verifies that a SparkPostException is caught with correct error details.
+ */
+ public function testUnsuccessfulSyncRequest(): void
{
$this->clientMock->shouldReceive('sendRequest')->
- once()->
- with(Mockery::type('GuzzleHttp\Psr7\Request'))->
- andThrow($this->exceptionMock);
+ once()->
+ with(Mockery::type(RequestInterface::class))->
+ andThrow($this->exceptionMock);
try {
$this->resource->syncRequest('POST', 'transmissions', $this->postTransmissionPayload);
- } catch (\Exception $e) {
+ } catch (\Exception|Exception $e) {
$this->assertEquals($this->exceptionBody, $e->getBody());
$this->assertEquals(500, $e->getCode());
}
}
- public function testSuccessfulSyncRequestWithRetries()
+ /**
+ * Test synchronous request with automatic retries on 5xx errors.
+ *
+ * Why: Verifies that the client correctly retries transient server errors.
+ * How: Mocks the client to return multiple 503 responses followed by a 200, and verifies the final success.
+ *
+ * @throws Exception
+ * @throws SparkPostException
+ * @throws \Exception
+ */
+ public function testSuccessfulSyncRequestWithRetries(): void
{
$this->clientMock->shouldReceive('sendRequest')->
- with(Mockery::type('GuzzleHttp\Psr7\Request'))->
- andReturn($this->badResponseMock, $this->badResponseMock, $this->responseMock);
+ with(Mockery::type(RequestInterface::class))->
+ andReturn($this->badResponseMock, $this->badResponseMock, $this->responseMock);
$this->resource->setOptions(['retries' => 2]);
$response = $this->resource->syncRequest('POST', 'transmissions', $this->postTransmissionPayload);
- $this->assertEquals($this->responseBody, $response->getBody());
+ $this->assertEquals($this->responseBody, $response->getBodyDecoded());
$this->assertEquals(200, $response->getStatusCode());
}
- public function testUnsuccessfulSyncRequestWithRetries()
+ /**
+ * Test that synchronous retries are exhausted after the specified number of attempts.
+ *
+ * Why: Ensures the client doesn't retry indefinitely and eventually reports the failure.
+ * How: Mocks a persistent client exception and verifies that it is thrown after the configured retry limit is reached.
+ *
+ * @throws Exception
+ * @throws \Exception
+ */
+ public function testUnsuccessfulSyncRequestWithRetries(): void
{
$this->clientMock->shouldReceive('sendRequest')->
- once()->
- with(Mockery::type('GuzzleHttp\Psr7\Request'))->
- andThrow($this->exceptionMock);
+ once()->
+ with(Mockery::type(RequestInterface::class))->
+ andThrow($this->exceptionMock);
$this->resource->setOptions(['retries' => 2]);
try {
@@ -189,18 +281,35 @@ public function testUnsuccessfulSyncRequestWithRetries()
}
}
- public function testSuccessfulAsyncRequestWithWait()
+ /**
+ * Test a successful asynchronous request followed by wait().
+ *
+ * Why: Verifies the basic async request-response flow using promise wait().
+ * How: Mocks an async request that returns a promise, and asserts that wait() on the returned SparkPostPromise yield correct data.
+ *
+ * @throws SparkPostException
+ * @throws \Exception
+ */
+ public function testSuccessfulAsyncRequestWithWait(): void
{
$this->promiseMock->shouldReceive('wait')->andReturn($this->responseMock);
$promise = $this->resource->asyncRequest('POST', 'transmissions', $this->postTransmissionPayload);
$response = $promise->wait();
- $this->assertEquals($this->responseBody, $response->getBody());
+ $this->assertEquals($this->responseBody, $response->getBodyDecoded());
$this->assertEquals(200, $response->getStatusCode());
}
- public function testUnsuccessfulAsyncRequestWithWait()
+ /**
+ * Test an unsuccessful asynchronous request followed by wait().
+ *
+ * Why: Ensures that async failures are correctly reported when wait() is called.
+ * How: Mocks an async request that returns a rejected promise and verifies wait() throws a SparkPostException.
+ *
+ * @throws \Exception
+ */
+ public function testUnsuccessfulAsyncRequestWithWait(): void
{
$this->promiseMock->shouldReceive('wait')->andThrow($this->exceptionMock);
@@ -214,7 +323,15 @@ public function testUnsuccessfulAsyncRequestWithWait()
}
}
- public function testSuccessfulAsyncRequestWithThen()
+ /**
+ * Test a successful asynchronous request using then() callbacks.
+ *
+ * Why: Verifies that the onFulfilled callback is correctly triggered with a SparkPostResponse.
+ * How: Uses a Guzzle fulfilled promise and verifies the then() callback receives the expected status and body.
+ *
+ * @throws \Throwable
+ */
+ public function testSuccessfulAsyncRequestWithThen(): void
{
$guzzlePromise = new GuzzleFulfilledPromise($this->responseMock);
$result = $this->resource->buildRequest('POST', 'transmissions', $this->postTransmissionPayload, []);
@@ -222,13 +339,21 @@ public function testSuccessfulAsyncRequestWithThen()
$promise = new SparkPostPromise(new GuzzleAdapterPromise($guzzlePromise, $result));
$responseBody = $this->responseBody;
- $promise->then(function ($response) use ($responseBody) {
+ $promise->then(function ($response) use ($responseBody): void {
$this->assertEquals(200, $response->getStatusCode());
- $this->assertEquals($responseBody, $response->getBody());
- }, null)->wait();
+ $this->assertEquals($responseBody, $response->getBodyDecoded());
+ })->wait();
}
- public function testUnsuccessfulAsyncRequestWithThen()
+ /**
+ * Test an unsuccessful asynchronous request using then() callbacks.
+ *
+ * Why: Verifies that the onRejected callback is correctly triggered with a SparkPostException.
+ * How: Uses a Guzzle rejected promise and verifies the then() callback receives the expected error code and body.
+ *
+ * @throws \Throwable
+ */
+ public function testUnsuccessfulAsyncRequestWithThen(): void
{
$guzzlePromise = new GuzzleRejectedPromise($this->exceptionMock);
$result = $this->resource->buildRequest('POST', 'transmissions', $this->postTransmissionPayload, []);
@@ -236,19 +361,27 @@ public function testUnsuccessfulAsyncRequestWithThen()
$promise = new SparkPostPromise(new GuzzleAdapterPromise($guzzlePromise, $result));
$exceptionBody = $this->exceptionBody;
- $promise->then(null, function ($exception) use ($exceptionBody) {
+ $promise->then(null, function ($exception) use ($exceptionBody): void {
$this->assertEquals(500, $exception->getCode());
$this->assertEquals($exceptionBody, $exception->getBody());
})->wait();
}
- public function testSuccessfulAsyncRequestWithRetries()
+ /**
+ * Test asynchronous request with automatic retries on 5xx errors.
+ *
+ * Why: Verifies that the async promise chain correctly handles transient errors via retries.
+ * How: Mocks multiple 503 responses in the async client and verifies the final successful response is delivered to the then() callback.
+ *
+ * @throws \Throwable
+ */
+ public function testSuccessfulAsyncRequestWithRetries(): void
{
$testReq = $this->resource->buildRequest('POST', 'transmissions', $this->postTransmissionPayload, []);
- $clientMock = Mockery::mock('Http\Adapter\Guzzle6\Client');
- $clientMock->shouldReceive('sendAsyncRequest')->
- with(Mockery::type('GuzzleHttp\Psr7\Request'))->
- andReturn(
+ $clientMock = Mockery::mock(ClientInterface::class, HttpAsyncClient::class);
+ $clientMock->shouldReceive('sendAsyncRequest')
+ ->with(Mockery::type(RequestInterface::class))
+ ->andReturn(
new GuzzleAdapterPromise(new GuzzleFulfilledPromise($this->badResponseMock), $testReq),
new GuzzleAdapterPromise(new GuzzleFulfilledPromise($this->badResponseMock), $testReq),
new GuzzleAdapterPromise(new GuzzleFulfilledPromise($this->responseMock), $testReq)
@@ -258,31 +391,47 @@ public function testSuccessfulAsyncRequestWithRetries()
$resource->setOptions(['async' => true, 'retries' => 2]);
$promise = $resource->asyncRequest('POST', 'transmissions', $this->postTransmissionPayload);
- $promise->then(function($resp) {
+ $promise->then(function ($resp): void {
$this->assertEquals(200, $resp->getStatusCode());
})->wait();
}
- public function testUnsuccessfulAsyncRequestWithRetries()
+ /**
+ * Test that asynchronous retries are exhausted after the specified number of attempts.
+ *
+ * Why: Ensures the async chain eventually fails if the server is persistently broken.
+ * How: Mocks a rejected promise and verifies the final error is propagated to the then() rejection callback.
+ *
+ * @throws \Throwable
+ */
+ public function testUnsuccessfulAsyncRequestWithRetries(): void
{
$testReq = $this->resource->buildRequest('POST', 'transmissions', $this->postTransmissionPayload, []);
$rejectedPromise = new GuzzleRejectedPromise($this->exceptionMock);
- $clientMock = Mockery::mock('Http\Adapter\Guzzle6\Client');
- $clientMock->shouldReceive('sendAsyncRequest')->
- with(Mockery::type('GuzzleHttp\Psr7\Request'))->
- andReturn(new GuzzleAdapterPromise($rejectedPromise, $testReq));
+ $clientMock = Mockery::mock(ClientInterface::class, HttpAsyncClient::class);
+ $clientMock->shouldReceive('sendAsyncRequest')
+ ->with(Mockery::type(RequestInterface::class))
+ ->andReturn(new GuzzleAdapterPromise($rejectedPromise, $testReq));
$resource = new SparkPost($clientMock, ['key' => 'SPARKPOST_API_KEY']);
$resource->setOptions(['async' => true, 'retries' => 2]);
$promise = $resource->asyncRequest('POST', 'transmissions', $this->postTransmissionPayload);
- $promise->then(null, function($exception) {
+ $promise->then(null, function ($exception): void {
$this->assertEquals(500, $exception->getCode());
$this->assertEquals($this->exceptionBody, $exception->getBody());
})->wait();
}
- public function testPromise()
+ /**
+ * Test that the SparkPostPromise correctly reports its state.
+ *
+ * Why: Verifies that state checks are correctly delegated to the underlying promise.
+ * How: Mocks the underlying promise's getState method and asserts the SparkPostPromise wrapper returns the same states.
+ *
+ * @throws \Exception
+ */
+ public function testPromise(): void
{
$promise = $this->resource->asyncRequest('POST', 'transmissions', $this->postTransmissionPayload);
@@ -293,16 +442,28 @@ public function testPromise()
$this->assertEquals($this->promiseMock->getState(), $promise->getState());
}
- public function testUnsupportedAsyncRequest()
+ /**
+ * Test that an exception is thrown when attempting an async request with a non-async client.
+ *
+ * Why: Prevents runtime errors by validating client capabilities early.
+ * How: Sets a synchronous-only mock client and verifies that calling asyncRequest throws an exception.
+ */
+ public function testUnsupportedAsyncRequest(): void
{
$this->expectException(\Exception::class);
- $this->resource->setHttpClient(Mockery::mock('Http\Client\HttpClient'));
+ $this->resource->setHttpClient(Mockery::mock(ClientInterface::class));
$this->resource->asyncRequest('POST', 'transmissions', $this->postTransmissionPayload);
}
- public function testGetHttpHeaders()
+ /**
+ * Test that HTTP headers are correctly constructed, including defaults and custom headers.
+ *
+ * Why: Ensures the API receives mandatory headers (Auth, Content-Type, User-Agent) correctly.
+ * How: Calls getHttpHeaders with a custom header and asserts all expected headers (default and custom) are present and correct.
+ */
+ public function testGetHttpHeaders(): void
{
$headers = $this->resource->getHttpHeaders([
'Custom-Header' => 'testing',
@@ -313,45 +474,83 @@ public function testGetHttpHeaders()
$this->assertEquals('SPARKPOST_API_KEY', $headers['Authorization']);
$this->assertEquals('application/json', $headers['Content-Type']);
$this->assertEquals('testing', $headers['Custom-Header']);
- $this->assertEquals('php-sparkpost/'.$version, $headers['User-Agent']);
+ $this->assertEquals('php-sparkpost/' . $version, $headers['User-Agent']);
}
- public function testGetUrl()
+ /**
+ * Test that the API URL is correctly constructed with path and query parameters.
+ *
+ * Why: Ensures requests are sent to the correct API endpoint with properly formatted query strings.
+ * How: Calls getUrl with a path and array of parameters, and asserts the generated URL matches the expected format.
+ */
+ public function testGetUrl(): void
{
$url = 'https://api.sparkpost.com:443/api/v1/transmissions?key=value 1,value 2,value 3';
$testUrl = $this->resource->getUrl('transmissions', ['key' => ['value 1', 'value 2', 'value 3']]);
$this->assertEquals($url, $testUrl);
}
- public function testSetHttpClient()
+ /**
+ * Test that a synchronous HTTP client can be set.
+ *
+ * Why: Ensures flexibility in choosing HTTP client implementations.
+ * How: Sets a mock ClientInterface and uses reflection to verify the private property was updated.
+ */
+ public function testSetHttpClient(): void
{
- $mock = Mockery::mock(HttpClient::class);
+ $mock = Mockery::mock(ClientInterface::class);
$this->resource->setHttpClient($mock);
$this->assertEquals($mock, NSA::getProperty($this->resource, 'httpClient'));
}
- public function testSetHttpAsyncClient()
+ /**
+ * Test that an asynchronous HTTP client can be set.
+ *
+ * Why: Ensures flexibility in choosing async-capable HTTP client implementations.
+ * How: Sets a mock HttpAsyncClient and uses reflection to verify the private property was updated.
+ */
+ public function testSetHttpAsyncClient(): void
{
$mock = Mockery::mock(HttpAsyncClient::class);
$this->resource->setHttpClient($mock);
$this->assertEquals($mock, NSA::getProperty($this->resource, 'httpClient'));
}
- public function testSetHttpClientException()
+ /**
+ * Test that an exception is thrown when setting an invalid HTTP client.
+ *
+ * Why: Ensures type safety and prevents runtime errors from incompatible client objects.
+ * How: Attempts to set an invalid object (stdClass) as the HTTP client and asserts an exception is thrown.
+ */
+ public function testSetHttpClientException(): void
{
$this->expectException(\Exception::class);
$this->resource->setHttpClient(new \stdClass());
}
- public function testSetOptionsStringKey()
+ /**
+ * Test that options can be initialized using just a string as the API key.
+ *
+ * Why: Provides a convenient shorthand for common client initialization.
+ * How: Passes a string to setOptions and verifies the 'key' option is correctly set while others remain at defaults.
+ *
+ * @throws \Exception
+ */
+ public function testSetOptionsStringKey(): void
{
$this->resource->setOptions('SPARKPOST_API_KEY');
$options = NSA::getProperty($this->resource, 'options');
$this->assertEquals('SPARKPOST_API_KEY', $options['key']);
}
- public function testSetBadOptions()
+ /**
+ * Test that an exception is thrown if no API key is provided in the options.
+ *
+ * Why: Ensures the client cannot be misconfigured without a mandatory API key.
+ * How: Attempts to set options without a 'key' and asserts that an exception is thrown.
+ */
+ public function testSetBadOptions(): void
{
$this->expectException(\Exception::class);
@@ -359,11 +558,17 @@ public function testSetBadOptions()
$this->resource->setOptions(['not' => 'SPARKPOST_API_KEY']);
}
- public function testSetMessageFactory()
+ /**
+ * Test that a custom PSR-17 Request Factory can be set.
+ *
+ * Why: Allows developers to use their preferred PSR-17 implementation.
+ * How: Sets a mock RequestFactoryInterface and verifies it is retrieved by the internal factory getter.
+ */
+ public function testSetRequestFactory(): void
{
- $messageFactory = Mockery::mock(MessageFactory::class);
- $this->resource->setMessageFactory($messageFactory);
+ $messageFactory = Mockery::mock(RequestFactoryInterface::class);
+ $this->resource->setRequestFactory($messageFactory);
- $this->assertEquals($messageFactory, NSA::invokeMethod($this->resource, 'getMessageFactory'));
+ $this->assertEquals($messageFactory, NSA::invokeMethod($this->resource, 'getRequestFactory'));
}
}
diff --git a/test/unit/TransmissionTest.php b/test/unit/TransmissionTest.php
index 09bd1cb..4a77f51 100644
--- a/test/unit/TransmissionTest.php
+++ b/test/unit/TransmissionTest.php
@@ -3,16 +3,22 @@
namespace SparkPost\Test;
use PHPUnit\Framework\TestCase;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\StreamInterface;
use SparkPost\SparkPost;
use Mockery;
+use Psr\Http\Message\RequestInterface;
+use Http\Client\HttpAsyncClient;
+use Psr\Http\Client\ClientInterface;
+use SparkPost\SparkPostException;
class TransmissionTest extends TestCase
{
private $clientMock;
/** @var SparkPost */
- private $resource;
+ private SparkPost $resource;
- private $postTransmissionPayload = [
+ private array $postTransmissionPayload = [
'content' => [
'from' => ['name' => 'Sparkpost Team', 'email' => 'postmaster@sendmailfor.me'],
'subject' => 'First Mailing From PHP',
@@ -41,7 +47,7 @@ class TransmissionTest extends TestCase
];
- private $getTransmissionPayload = [
+ private array $getTransmissionPayload = [
'campaign_id' => 'thanksgiving',
];
@@ -50,12 +56,13 @@ class TransmissionTest extends TestCase
*
* @before
*
+ * @throws \Exception
* @see PHPUnit_Framework_TestCase::setUp()
*/
public function setUp(): void
{
//setup mock for the adapter
- $this->clientMock = Mockery::mock('Http\Adapter\Guzzle6\Client');
+ $this->clientMock = Mockery::mock(ClientInterface::class);
$this->resource = new SparkPost($this->clientMock, ['key' => 'SPARKPOST_API_KEY', 'async' => false]);
}
@@ -65,7 +72,13 @@ public function tearDown(): void
Mockery::close();
}
- public function testInvalidEmailFormat()
+ /**
+ * Test that an invalid email format in recipients throws an exception.
+ *
+ * Why: Verifies that the Transmission resource validates email addresses before sending the request to the API.
+ * How: Adds an invalid email to the payload and asserts that calling post() throws an Exception.
+ */
+ public function testInvalidEmailFormat(): void
{
$this->expectException(\Exception::class);
@@ -76,17 +89,25 @@ public function testInvalidEmailFormat()
$response = $this->resource->transmissions->post($this->postTransmissionPayload);
}
- public function testGet()
+ /**
+ * Test a GET request to the transmissions endpoint.
+ *
+ * Why: Verifies that the Transmission resource correctly delegates GET requests to the SparkPost client.
+ * How: Mocks a successful GET request and verifies the response matches the expected mock data.
+ *
+ * @throws SparkPostException
+ */
+ public function testGet(): void
{
- $responseMock = Mockery::mock('Psr\Http\Message\ResponseInterface');
- $responseBodyMock = Mockery::mock();
+ $responseMock = Mockery::mock(ResponseInterface::class);
+ $responseBodyMock = Mockery::mock(StreamInterface::class);
$responseBody = ['results' => 'yay'];
$this->clientMock->shouldReceive('sendRequest')->
- once()->
- with(Mockery::type('GuzzleHttp\Psr7\Request'))->
- andReturn($responseMock);
+ once()->
+ with(Mockery::type(RequestInterface::class))->
+ andReturn($responseMock);
$responseMock->shouldReceive('getStatusCode')->andReturn(200);
$responseMock->shouldReceive('getBody')->andReturn($responseBodyMock);
@@ -94,21 +115,29 @@ public function testGet()
$response = $this->resource->transmissions->get($this->getTransmissionPayload);
- $this->assertEquals($responseBody, $response->getBody());
+ $this->assertEquals($responseBody, $response->getBodyDecoded());
$this->assertEquals(200, $response->getStatusCode());
}
- public function testPut()
+ /**
+ * Test a PUT request to the transmissions endpoint.
+ *
+ * Why: Verifies that the Transmission resource correctly delegates PUT requests to the SparkPost client.
+ * How: Mocks a successful PUT request and verifies the response matches the expected mock data.
+ *
+ * @throws SparkPostException
+ */
+ public function testPut(): void
{
- $responseMock = Mockery::mock('Psr\Http\Message\ResponseInterface');
- $responseBodyMock = Mockery::mock();
+ $responseMock = Mockery::mock(ResponseInterface::class);
+ $responseBodyMock = Mockery::mock(StreamInterface::class);
$responseBody = ['results' => 'yay'];
$this->clientMock->shouldReceive('sendRequest')->
- once()->
- with(Mockery::type('GuzzleHttp\Psr7\Request'))->
- andReturn($responseMock);
+ once()->
+ with(Mockery::type(RequestInterface::class))->
+ andReturn($responseMock);
$responseMock->shouldReceive('getStatusCode')->andReturn(200);
$responseMock->shouldReceive('getBody')->andReturn($responseBodyMock);
@@ -116,21 +145,27 @@ public function testPut()
$response = $this->resource->transmissions->put($this->getTransmissionPayload);
- $this->assertEquals($responseBody, $response->getBody());
+ $this->assertEquals($responseBody, $response->getBodyDecoded());
$this->assertEquals(200, $response->getStatusCode());
}
- public function testPost()
+ /**
+ * Test a POST request to the transmissions endpoint.
+ *
+ * Why: Verifies that the Transmission resource correctly formats payloads and sends POST requests.
+ * How: Mocks a successful POST request and verifies the response matches the expected mock data.
+ */
+ public function testPost(): void
{
- $responseMock = Mockery::mock('Psr\Http\Message\ResponseInterface');
- $responseBodyMock = Mockery::mock();
+ $responseMock = Mockery::mock(ResponseInterface::class);
+ $responseBodyMock = Mockery::mock(StreamInterface::class);
$responseBody = ['results' => 'yay'];
$this->clientMock->shouldReceive('sendRequest')->
- once()->
- with(Mockery::type('GuzzleHttp\Psr7\Request'))->
- andReturn($responseMock);
+ once()->
+ with(Mockery::type(RequestInterface::class))->
+ andReturn($responseMock);
$responseMock->shouldReceive('getStatusCode')->andReturn(200);
$responseMock->shouldReceive('getBody')->andReturn($responseBodyMock);
@@ -138,46 +173,60 @@ public function testPost()
$response = $this->resource->transmissions->post($this->postTransmissionPayload);
- $this->assertEquals($responseBody, $response->getBody());
+ $this->assertEquals($responseBody, $response->getBodyDecoded());
$this->assertEquals(200, $response->getStatusCode());
}
- public function testPostWithRecipientList()
+ /**
+ * Test a POST request using a stored recipient list ID.
+ *
+ * Why: Ensures that when a list_id is used, individual recipient formatting is bypassed.
+ * How: Provides a list_id in the payload and verifies the POST request is successful without recipient validation errors.
+ */
+ public function testPostWithRecipientList(): void
{
$postTransmissionPayload = $this->postTransmissionPayload;
$postTransmissionPayload['recipients'] = ['list_id' => 'SOME_LIST_ID'];
- $responseMock = Mockery::mock('Psr\Http\Message\ResponseInterface');
- $responseBodyMock = Mockery::mock();
+ $responseMock = Mockery::mock(ResponseInterface::class);
+ $responseBodyMock = Mockery::mock(StreamInterface::class);
$responseBody = ['results' => 'yay'];
$this->clientMock->shouldReceive('sendRequest')->
- once()->
- with(Mockery::type('GuzzleHttp\Psr7\Request'))->
- andReturn($responseMock);
+ once()->
+ with(Mockery::type(RequestInterface::class))->
+ andReturn($responseMock);
$responseMock->shouldReceive('getStatusCode')->andReturn(200);
$responseMock->shouldReceive('getBody')->andReturn($responseBodyMock);
$responseBodyMock->shouldReceive('__toString')->andReturn(json_encode($responseBody));
- $response = $this->resource->transmissions->post();
+ $response = $this->resource->transmissions->post($postTransmissionPayload);
- $this->assertEquals($responseBody, $response->getBody());
+ $this->assertEquals($responseBody, $response->getBodyDecoded());
$this->assertEquals(200, $response->getStatusCode());
}
- public function testDelete()
+ /**
+ * Test a DELETE request to the transmissions endpoint.
+ *
+ * Why: Verifies that the Transmission resource correctly delegates DELETE requests to the SparkPost client.
+ * How: Mocks a successful DELETE request and verifies the response matches the expected mock data.
+ *
+ * @throws SparkPostException
+ */
+ public function testDelete(): void
{
- $responseMock = Mockery::mock('Psr\Http\Message\ResponseInterface');
- $responseBodyMock = Mockery::mock();
+ $responseMock = Mockery::mock(ResponseInterface::class);
+ $responseBodyMock = Mockery::mock(StreamInterface::class);
$responseBody = ['results' => 'yay'];
$this->clientMock->shouldReceive('sendRequest')->
- once()->
- with(Mockery::type('GuzzleHttp\Psr7\Request'))->
- andReturn($responseMock);
+ once()->
+ with(Mockery::type(RequestInterface::class))->
+ andReturn($responseMock);
$responseMock->shouldReceive('getStatusCode')->andReturn(200);
$responseMock->shouldReceive('getBody')->andReturn($responseBodyMock);
@@ -185,13 +234,22 @@ public function testDelete()
$response = $this->resource->transmissions->delete($this->getTransmissionPayload);
- $this->assertEquals($responseBody, $response->getBody());
+ $this->assertEquals($responseBody, $response->getBodyDecoded());
$this->assertEquals(200, $response->getStatusCode());
}
- public function testFormatPayload()
+ /**
+ * Test the payload formatting logic.
+ *
+ * Why: Verifies the complex transformation of transmission payloads into the format required by the API.
+ * How: Passes a raw payload to formatPayload() and asserts the output matches a predefined correctly formatted JSON structure.
+ */
+ public function testFormatPayload(): void
{
- $correctFormattedPayload = json_decode('{"content":{"from":{"name":"Sparkpost Team","email":"postmaster@sendmailfor.me"},"subject":"First Mailing From PHP","text":"Congratulations, {{name}}!! You just sent your very first mailing!","headers":{"CC":"avi.goldman@sparkpost.com"}},"substitution_data":{"name":"Avi"},"recipients":[{"address":{"name":"Vincent","email":"vincent.song@sparkpost.com"}},{"address":{"email":"test@example.com"}},{"address":{"email":"emely.giraldo@sparkpost.com","header_to":"\"Vincent\" "}},{"address":{"email":"avi.goldman@sparkpost.com","header_to":"\"Vincent\" "}}]}', true);
+ $correctFormattedPayload = json_decode(
+ '{"content":{"from":{"name":"Sparkpost Team","email":"postmaster@sendmailfor.me"},"subject":"First Mailing From PHP","text":"Congratulations, {{name}}!! You just sent your very first mailing!","headers":{"CC":"avi.goldman@sparkpost.com"}},"substitution_data":{"name":"Avi"},"recipients":[{"address":{"name":"Vincent","email":"vincent.song@sparkpost.com"}},{"address":{"email":"test@example.com"}},{"address":{"email":"emely.giraldo@sparkpost.com","header_to":"\"Vincent\" "}},{"address":{"email":"avi.goldman@sparkpost.com","header_to":"\"Vincent\" "}}]}',
+ true
+ );
$formattedPayload = $this->resource->transmissions->formatPayload($this->postTransmissionPayload);
$this->assertEquals($correctFormattedPayload, $formattedPayload);