Source of file SparkPostApiClient.php
Size: 32,166 Bytes - Last Modified: 2021-12-23T10:01:31+00:00
/var/www/docs.ssmods.com/process/src/src/api/SparkPostApiClient.php
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138 | <?php namespace LeKoala\SparkPost\Api; use Exception; use InvalidArgumentException; use DateTime; use LeKoala\SparkPost\EmailUtils; /** * A really simple SparkPost api client * * @author LeKoala <thomas@lekoala.be> */ class SparkPostApiClient { // CLIENT SETTINGS const CLIENT_VERSION = '0.2'; const API_ENDPOINT = 'https://api.sparkpost.com/api/v1'; const API_ENDPOINT_EU = 'https://api.eu.sparkpost.com/api/v1'; const METHOD_GET = "GET"; const METHOD_POST = "POST"; const METHOD_PUT = "PUT"; const METHOD_DELETE = "DELETE"; const DATETIME_FORMAT = 'Y-m-d\TH:i'; // SPARKPOST TYPES const TYPE_MESSAGE = 'message_event'; // Bounce, Delivery, Injection, SMS Status, Spam Complaint, Out of Band, Policy Rejection, Delay const TYPE_ENGAGEMENT = 'track_event'; // Click, Open const TYPE_GENERATION = 'gen_event'; // Generation Failure, Generation Rejection const TYPE_UNSUBSCRIBE = 'unsubscribe_event'; // List Unsubscribe, Link Unsubscribe const TYPE_RELAY = 'relay_event'; // Relay Injection, Relay Rejection, Relay Delivery, Relay Temporary Failure, Relay Permanent Failure // SPARKPOST EVENTS const EVENT_DELIVERY = 'delivery'; const EVENT_BOUNCE = 'bounce'; const EVENT_INJECTION = 'injection'; const EVENT_SMS_STATUS = 'sms_status'; const EVENT_SPAM_COMPLAINT = 'spam_complaint'; const EVENT_OUT_OF_BAND = 'out_of_band'; const EVENT_POLICY_REJECTION = 'policy_rejection'; const EVENT_DELAY = 'delay'; const EVENT_OPEN = 'open'; const EVENT_CLICK = 'click'; const EVENT_GEN_FAILURE = 'generation_failure'; const EVENT_GEN_REJECTION = 'generation_rejection'; const EVENT_LIST_UNSUB = 'list_unsubscribe'; const EVENT_LINK_UNSUB = 'link_unsubscribe'; const EVENT_RELAY_INJECTION = 'relay_injection'; const EVENT_RELAY_REJECTION = 'relay_rejection'; const EVENT_RELAY_DELIVERY = 'relay_delivery'; const EVENT_RELAY_TEMPFAIL = 'relay_tempfail'; const EVENT_RELAY_PERMFAIL = 'relay_permfail'; /** * Your api key * * @var string */ protected $key; /** * Is eu endpoint ? * * @var boolean */ protected $euEndpoint = false; /** * Curl verbose log * * @var string */ protected $verboseLog = ''; /** * A callback to log results * * @var callable */ protected $logger; /** * Results from the api * * @var array */ protected $results = []; /** * The ID of the subaccount to use * * @var int */ protected $subaccount; /** * Client options * * @var array */ protected $curlOpts = []; /** * Create a new instance of the SparkPostApiClient * * @param string $key Specify the string, or it will read env SPARKPOST_API_KEY or constant SPARKPOST_API_KEY * @param int $subaccount Specify a subaccount to limit data sent by the API * @param array $curlOpts Additionnal options to configure the curl client */ public function __construct($key = null, $subaccount = null, $curlOpts = []) { if ($key) { $this->key = $key; } else { $this->key = getenv('SPARKPOST_API_KEY'); } if (getenv('SPARKPOST_EU')) { $this->euEndpoint = getenv('SPARKPOST_EU'); } elseif (defined('SPARKPOST_EU')) { $this->euEndpoint = true; } $this->subaccount = $subaccount; $this->curlOpts = array_merge($this->getDefaultCurlOptions(), $curlOpts); } /** * Get default options * * @return array */ public function getDefaultCurlOptions() { return [ 'connect_timeout' => 10, 'timeout' => 10, 'verbose' => false, ]; } /** * Get an option * * @param string $name * @return mixed */ public function getCurlOption($name) { if (!isset($this->curlOpts[$name])) { throw new InvalidArgumentException("$name is not a valid option. Valid options are : " . implode(', ', array_keys($this->curlOpts))); } return $this->curlOpts[$name]; } /** * Set an option * * @param string $name * @return mixed */ public function setCurlOption($name, $value) { $this->curlOpts[$name] = $value; } /** * Get the current api key * * @return string */ public function getKey() { return $this->key; } /** * Set the current api key * * @param string $key */ public function setKey($key) { $this->key = $key; } /** * Get the use of eu endpoint * * @return string */ public function getEuEndpoint() { return $this->euEndpoint; } /** * Set the use of eu endpoint * * @param string $euEndpoint */ public function setEuEndpoint($euEndpoint) { $this->euEndpoint = $euEndpoint; } /** * Get verbose log * * @return string */ public function getVerboseLog() { return $this->verboseLog; } /** * Get the logger * * @return type */ public function getLogger() { return $this->logger; } /** * Set a logging method * * @param callable $logger */ public function setLogger(callable $logger) { $this->logger = $logger; } /** * Get subaccount id * * @return int */ public function getSubaccount() { return $this->subaccount; } /** * Set subaccount id * * @param int $subaccount */ public function setSubaccount($subaccount) { $this->subaccount = $subaccount; } /** * Helper that handles dot notation * * @param array $arr * @param string $path * @param string $val * @return mixed */ protected function setMappedValue(array &$arr, $path, $val) { $loc = &$arr; foreach (explode('.', $path) as $step) { $loc = &$loc[$step]; } return $loc = $val; } /** * Map data using a given mapping array * * @param array $data * @param array $map * @return array */ protected function mapData($data, $map) { $mappedData = []; foreach ($data as $k => $v) { $key = $k; if (isset($map[$k])) { $key = $map[$k]; } $this->setMappedValue($mappedData, $key, $v); } return $mappedData; } /** * Create a transmission * * 'campaign' * 'metadata' * 'substitutionData' * 'description' * 'returnPath' * 'replyTo' * 'subject' * 'from' * 'html' * 'text' * 'attachments' * 'rfc822' * 'customHeaders' * 'recipients' * 'recipientList' * 'template' * 'trackOpens' * 'trackClicks' * 'startTime' * 'transactional' * 'sandbox' * 'useDraftTemplate' * 'inlineCss' * * @link https://developers.sparkpost.com/api/transmissions.html * @param array $data * @return array An array containing 3 keys: total_rejected_recipients, total_accepted_recipients, id */ public function createTransmission($data) { // Use the same mapping as official sdk $mapping = [ 'campaign' => 'campaign_id', 'metadata' => 'metadata', 'substitutionData' => 'substitution_data', 'description' => 'description', 'returnPath' => 'return_path', 'replyTo' => 'content.reply_to', 'subject' => 'content.subject', 'from' => 'content.from', 'html' => 'content.html', 'text' => 'content.text', 'attachments' => 'content.attachments', 'rfc822' => 'content.email_rfc822', 'customHeaders' => 'content.headers', 'recipients' => 'recipients', 'recipientList' => 'recipients.list_id', 'template' => 'content.template_id', 'trackOpens' => 'options.open_tracking', 'trackClicks' => 'options.click_tracking', 'startTime' => 'options.start_time', 'transactional' => 'options.transactional', 'sandbox' => 'options.sandbox', 'useDraftTemplate' => 'use_draft_template', 'inlineCss' => 'options.inline_css', ]; $data = $this->mapData($data, $mapping); return $this->makeRequest('transmissions', self::METHOD_POST, $data); } /** * Get the detail of a transmission * * @param string $id * @return array */ public function getTransmission($id) { return $this->makeRequest('transmissions/' . $id); } /** * Delete a transmission * * @param string $id * @return array */ public function deleteTransmission($id) { return $this->makeRequest('transmissions/' . $id, self::METHOD_DELETE); } /** * List tranmssions * * @param string $campaignId * @param string $templateId * @return array */ public function listTransmissions($campaignId = null, $templateId = null) { $params = []; if ($campaignId !== null) { $params['campaign_id'] = $campaignId; } if ($templateId !== null) { $params['template_id'] = $templateId; } return $this->makeRequest('transmissions', self::METHOD_GET, $params); } /** * Search message events * * Use the following parameters (default is current timezone, 100 messages for the last 7 days) * * 'bounce_classes' : delimited list of bounce classification codes to search. * 'campaign_ids' : delimited list of campaign ID's to search (i.e. campaign_id used during creation of a transmission). * 'delimiter' : Specifies the delimiter for query parameter lists * 'events' : delimited list of event types to search. Example: delivery, injection, bounce, delay, policy_rejection, out_of_band, open, click, ... * 'friendly_froms' : delimited list of friendly_froms to search. * 'from' : Datetime in format of YYYY-MM-DDTHH:MM. * 'message_ids' : delimited list of message ID's to search. * 'page' : The results page number to return. Used with per_page for paging through results * 'per_page' : Number of results to return per page. Must be between 1 and 10,000 (inclusive). * 'reason' : Bounce/failure/rejection reason that will be matched using a wildcard (e.g., %reason%) * 'recipients' : delimited list of recipients to search. * 'subaccounts' : delimited list of subaccount ID’s to search.. * 'template_ids' : delimited list of template ID's to search. * 'timezone' : Standard timezone identification string * 'to' : Datetime in format of YYYY-MM-DDTHH:MM * 'transmission_ids' : delimited list of transmission ID's to search (i.e. id generated during creation of a transmission). * * Result is an array that looks like this * * [customer_id] => 0000 * [delv_method] => esmtp * [event_id] => 99997643157770993 * [friendly_from] => some@email.ext * [ip_address] => 12.34.56.78 * [message_id] => abcd2fd71057477a0fa5 * [msg_from] => msprvs1=000000Q7Zx0yG=bounces-12345-1234@sparkpostmail1.com * [msg_size] => 1234 * [num_retries] => 0 * [queue_time] => 1234 * [raw_rcpt_to] => some@email.ext * [rcpt_meta] => Array * [rcpt_tags] => Array * [rcpt_to] => some@email.ext * [routing_domain] => email.ext * [subaccount_id] => 0000 * [subject] => my test subject * [tdate] => 2050-01-01T11:57:36.000Z * [template_id] => template_123456789 * [template_version] => 0 * [transactional] => 1 * [transmission_id] => 12234554568854 * [type] => delivery * [timestamp] => 2050-01-01T11:57:36.000Z * * @deprecated * @param array $params * @return array */ public function searchMessageEvents($params = []) { $defaultParams = [ 'timezone' => date_default_timezone_get(), 'per_page' => 100, 'from' => $this->createValidDatetime('-7 days'), ]; $params = array_merge($defaultParams, $params); return $this->makeRequest('message-events', self::METHOD_GET, $params); } /** * Search for Message Events * * Parameters * - from string, default is 24 hours ago * - per_page number, default is 1000 * - event_ids string * - events string, default is all event types * - recipients string * - recipient_domains string * - from_addresses string * - sending_domains string * - subjects string * - bounce_classes number * - reasons string * - campaigns string * - templates string * - sending_ips string * - ip_pools string * - subaccounts string * - messages string * - transmissions string * - mailbox_providers string * - mailbox_provider_regions string * - ab_tests string * - ab_test_versions number * * Result is an array of objects that looks like this * * "mailbox_provider" => "SomeProvider" * "template_version" => "0" * "friendly_from" => "noreply@testing.example.com" * "subject" => "My email" * "ip_pool" => "default" * "sending_domain" => "testing.example.com" * "rcpt_tags" => [] * "type" => "initial_open" * "mailbox_provider_region" => "Global" * "raw_rcpt_to" => "recipient@dest.com" * "msg_from" => "msprvs1=46848646040zazea=bounces-99999-1@bounce.example.com" * "geo_ip" => array:8 [▶] * "rcpt_to" => "recipient@dest.com" * "subaccount_id" => 1 * "transmission_id" => "1230984717797820762" * "user_agent" => "Mozilla/5.0 (Windows NT 5.1; rv:11.0) Gecko Firefox/11.0 (via ggpht.com GoogleImageProxy)" * "timestamp" => "2055-02-19T14:44:29.000Z" * "click_tracking" => true * "rcpt_meta" => [] * "message_id" => "122da1ce2f606b5c07dc" * "ip_address" => "12.123.11.11" * "initial_pixel" => true * "recipient_domain" => "dest.com" * "event_id" => "5454130313444582480096" * "routing_domain" => "dest.com" * "sending_ip" => "99.99.99.99" * "template_id" => "template_693098471779782565" * "delv_method" => "esmtp" * "customer_id" => 99999 * "open_tracking" => true * "injection_time" => "2055-02-19T14:43:45.000Z" * "transactional" => "1" * "msg_size" => "48613" * * @param array $params * @return array */ public function searchEvents($params = []) { return $this->makeRequest('events/message', self::METHOD_GET, $params); } /** * Create a webhook by providing a webhooks object as the POST request body. * On creation, events will begin to be pushed to the target URL specified in the POST request body. * * { * "name": "Example webhook", * "target": "http://client.example.com/example-webhook", * "auth_type": "oauth2", * "auth_request_details": { * "url": "http://client.example.com/tokens", * "body": { * "client_id": "CLIENT123", * "client_secret": "9sdfj791d2bsbf", * "grant_type": "client_credentials" * } * }, * "auth_token": "", * "events": [ * "delivery", * "injection", * "open", * "click" * ] * } * * @param string $params * @return array */ public function createWebhook($params = []) { return $this->makeRequest('webhooks', self::METHOD_POST, $params); } /** * A simpler call to the api * * @param string $name * @param string $target * @param array $events * @param bool $auth Should we use basic auth ? * @param array $credentials An array containing "username" and "password" * @return type */ public function createSimpleWebhook($name, $target, array $events = null, $auth = false, $credentials = null) { if ($events === null) { // Default to the most used events $events = [ 'delivery', 'injection', 'open', 'click', 'bounce', 'spam_complaint', 'list_unsubscribe', 'link_unsubscribe' ]; } $params = [ 'name' => $name, 'target' => $target, 'events' => $events, ]; if ($auth) { if ($credentials === null) { $credentials = ['username' => "sparkpost", "password" => "sparkpost"]; } $params['auth_type'] = 'basic'; $params['auth_credentials'] = $credentials; } return $this->createWebhook($params); } /** * List all webhooks * * @param string $timezone * @return array */ public function listAllWebhooks($timezone = null) { $params = []; if ($timezone) { $params['timezone'] = $timezone; } return $this->makeRequest('webhooks', self::METHOD_GET, $params); } /** * Get a webhook * * @param string $id * @return array */ public function getWebhook($id) { return $this->makeRequest('webhooks/' . $id, self::METHOD_GET); } /** * Update a webhook * * @param string $id * @param array $params * @return array */ public function updateWebhook($id, $params = []) { return $this->makeRequest('webhooks/' . $id, self::METHOD_PUT, $params); } /** * Delete a webhook * * @param string $id * @return array */ public function deleteWebhook($id) { return $this->makeRequest('webhooks/' . $id, self::METHOD_DELETE); } /** * Validate a webhook * * @param string $id * @return array */ public function validateWebhook($id) { return $this->makeRequest('webhooks/' . $id . '/validate', self::METHOD_POST, '{"msys": {}}'); } /** * Retrieve status information regarding batches that have been generated * for the given webhook by specifying its id in the URI path. Status * information includes the successes of batches that previously failed to * reach the webhook's target URL and batches that are currently in a failed state. * * @param string $id * @param int $limit * @return array */ public function webhookBatchStatus($id, $limit = 1000) { return $this->makeRequest('webhooks/' . $id . '/batch-status', self::METHOD_GET, ['limit' => 1000]); } /** * List an example of the event data that will be posted by a Webhook for the specified events. * * @param string $events bounce, delivery... * @return array */ public function getSampleEvents($events = null) { $params = []; if ($events) { $params['events'] = $events; } return $this->makeRequest('webhooks/events/samples/', self::METHOD_GET, $params); } /** * Create a sending domain * * @param string $params * @return array */ public function createSendingDomain($params = []) { return $this->makeRequest('sending-domains', self::METHOD_POST, $params); } /** * A simpler call to the api * * @param string $name * @return array */ public function createSimpleSendingDomain($name) { $params = [ 'domain' => $name, ]; return $this->createSendingDomain($params); } /** * List all sending domains * * @return array */ public function listAllSendingDomains() { return $this->makeRequest('sending-domains', self::METHOD_GET); } /** * Get a sending domain * * @param string $id * @return array */ public function getSendingDomain($id) { return $this->makeRequest('sending-domains/' . $id, self::METHOD_GET); } /** * Verify a sending domain - This will ask SparkPost to check if SPF and DKIM are valid * * @param string $id * @return array */ public function verifySendingDomain($id) { return $this->makeRequest('sending-domains/' . $id . '/verify', self::METHOD_POST, [ 'dkim_verify' => true, 'spf_verify' => true ]); } /** * Update a sending domain * * @param string $id * @param array $params * @return array */ public function updateSendingDomain($id, $params = []) { return $this->makeRequest('sending-domains/' . $id, self::METHOD_PUT, $params); } /** * Delete a sending domain * * @param string $id * @return array */ public function deleteSendingDomain($id) { return $this->makeRequest('sending-domains/' . $id, self::METHOD_DELETE); } /** * Create an inbound domain * * @param string $domain * @return array */ public function createInboundDomain($domain) { return $this->makeRequest('inbound-domains', self::METHOD_POST, ['domain' => $domain]); } /** * List all inbound domains * * @return array */ public function listInboundDomains() { return $this->makeRequest('inbound-domains', self::METHOD_GET); } /** * Get details of an inbound domain * * @param string $domain * @return array */ public function getInboundDomain($domain) { return $this->makeRequest('inbound-domains/' . $domain, self::METHOD_GET); } /** * Delete an inbound domain * * @param string $domain * @return array */ public function deleteInboundDomain($domain) { return $this->makeRequest('inbound-domains/' . $domain, self::METHOD_DELETE); } /** * Create a relay webhook * * "name": "Replies Webhook", * "target": "https://webhooks.customer.example/replies", * "auth_token": "5ebe2294ecd0e0f08eab7690d2a6ee69", * "match": { * "protocol": "SMTP", * "domain": "email.example.com" * } * * @param array|string $params * @return array */ public function createRelayWebhook($params) { return $this->makeRequest('relay-webhooks', self::METHOD_POST, $params); } /** * List all relay webhooks * * @return array */ public function listRelayWebhooks() { return $this->makeRequest('relay-webhooks', self::METHOD_GET); } /** * Get the details of a relay webhook * * @param int $id * @return array */ public function getRelayWebhook($id) { return $this->makeRequest('relay-webhooks/' . $id, self::METHOD_GET); } /** * Update a relay webhook * * @param int $id * @param array $params * @return array */ public function updateRelayWebhook($id, $params) { return $this->makeRequest('relay-webhooks/' . $id, self::METHOD_PUT, $params); } /** * Delete a relay webhook * * @param int $id * @return array */ public function deleteRelayWebhook($id) { return $this->makeRequest('relay-webhooks/' . $id, self::METHOD_DELETE); } /** * Create a valid date for the API * * @param string $time * @param string $format * @return string Datetime in format of YYYY-MM-DDTHH:MM */ public function createValidDatetime($time, $format = null) { if (!is_int($time)) { $time = strtotime($time); } if (!$format) { $dt = new DateTime('@' . $time); } else { $dt = DateTime::createFromFormat($format, $time); } return $dt->format(self::DATETIME_FORMAT); } /** * Build an address object * * @param string $email * @param string $name * @param string $header_to * @return array */ public function buildAddress($email, $name = null, $header_to = null) { $address = [ 'email' => $email ]; if ($name) { $address['name'] = $name; } if ($header_to) { $address['header_to'] = $header_to; } return $address; } /** * Build an address object from a RFC 822 email string * * @param string $string * @param string $header_to * @return array */ public function buildAddressFromString($string, $header_to = null) { $email = EmailUtils::get_email_from_rfc_email($string); $name = EmailUtils::get_displayname_from_rfc_email($string); return $this->buildAddress($email, $name, $header_to); } /** * Build a recipient * * @param string|array $address * @param array $tags * @param array $metadata * @param array $substitution_data * @return array * @throws Exception */ public function buildRecipient($address, array $tags = null, array $metadata = null, array $substitution_data = null) { if (is_array($address)) { if (empty($address['email'])) { throw new Exception('Address must contain an email'); } } $recipient = [ 'address' => $address ]; if (!empty($tags)) { $recipient['tags'] = $tags; } if (!empty($metadata)) { $recipient['metadata'] = $metadata; } if (!empty($tags)) { $recipient['substitution_data'] = $substitution_data; } return $recipient; } /** * Make a request to the api using curl * * @param string $endpoint * @param string $action * @param array $data * @return array * @throws Exception */ protected function makeRequest($endpoint, $action = null, $data = null) { if (!$this->key) { throw new Exception('You must set an API key before making requests'); } $ch = curl_init(); if ($action === null) { $action = self::METHOD_GET; } else { $action = strtoupper($action); } if ($action === self::METHOD_GET && !empty($data)) { $endpoint .= '?' . http_build_query($data); } if ($action === self::METHOD_POST && is_array($data)) { $data = json_encode($data); } $header = []; $header[] = 'Content-Type: application/json'; $header[] = 'Authorization: ' . $this->key; if ($this->subaccount) { $header[] = 'X-MSYS-SUBACCOUNT: ' . $this->subaccount; } curl_setopt($ch, CURLOPT_HTTPHEADER, $header); curl_setopt($ch, CURLOPT_USERAGENT, 'SparkPostApiClient v' . self::CLIENT_VERSION); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); if ($this->euEndpoint) { curl_setopt($ch, CURLOPT_URL, self::API_ENDPOINT_EU . '/' . $endpoint); } else { curl_setopt($ch, CURLOPT_URL, self::API_ENDPOINT . '/' . $endpoint); } curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, (int)$this->getCurlOption('connect_timeout')); curl_setopt($ch, CURLOPT_TIMEOUT, (int)$this->getCurlOption('timeout')); // Collect verbose data in a stream if ($this->getCurlOption('verbose')) { curl_setopt($ch, CURLOPT_VERBOSE, true); $verbose = fopen('php://temp', 'w+'); curl_setopt($ch, CURLOPT_STDERR, $verbose); } // This fixes ca cert issues if server is not configured properly if (strlen(ini_get('curl.cainfo')) === 0) { curl_setopt($ch, CURLOPT_CAINFO, \Composer\CaBundle\CaBundle::getBundledCaBundlePath()); } switch ($action) { case self::METHOD_POST: curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, $data); break; case self::METHOD_DELETE: curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "DELETE"); break; } $result = curl_exec($ch); if (!$result) { throw new Exception('Error: "' . curl_error($ch) . '" - Code: ' . curl_errno($ch)); } if ($this->getCurlOption('verbose')) { rewind($verbose); $this->verboseLog .= stream_get_contents($verbose); } curl_close($ch); // In some cases, SparkPost api returns this strange empty result if ($result == '{ }') { $decodedResult = ['results' => null]; } else { $decodedResult = json_decode($result, true); if (!$decodedResult) { throw new Exception("Failed to decode $result : " . json_last_error_msg()); } } $this->results[] = $decodedResult; if (isset($decodedResult['errors'])) { $errors = array_map(function ($item) use ($data) { $message = $item['message']; // Prepend code to message if (isset($item['code'])) { $message = $item['code'] . ' - ' . $message; // For invalid domains, append domain name to make error more useful if ($item['code'] == 7001) { $from = ''; if (!is_array($data)) { $data = json_decode($data, JSON_OBJECT_AS_ARRAY); } if (isset($data['content']['from'])) { $from = $data['content']['from']; } if ($from && is_string($from)) { $domain = substr(strrchr($from, "@"), 1); $message .= ' (' . $domain . ')'; } } // For invalid recipients, append recipients if ($item['code'] == 5002) { if (isset($data['recipients'])) { if (empty($data['recipients'])) { $message .= ' (empty recipients list)'; } else { if (is_array($data['recipients'])) { $addresses = []; foreach ($data['recipients'] as $recipient) { $addresses[] = json_encode($recipient['address']); } } $message .= ' (' . implode(',', $addresses) . ')'; } } else { $message .= ' (no recipients defined)'; } } } if (isset($item['description'])) { $message .= ': ' . $item['description']; } return $message; }, $decodedResult['errors']); throw new Exception("The API returned the following error(s) : " . implode("; ", $errors)); } return $decodedResult['results']; } /** * Get all results from the api * * @return array */ public function getResults() { return $this->results; } /** * Get last result * * @return array */ public function getLastResult() { return end($this->results); } } |