├── .gitignore ├── examples ├── _autoload.php └── user.php ├── composer.json ├── sailthru ├── Sailthru_Client_Exception.php ├── Sailthru_Util.php └── Sailthru_Client.php ├── tests ├── Sailthru_Client_ExceptionTest.php ├── Sailthru_UtilTest.php └── Sailthru_ClientTest.php ├── .travis.yml ├── phpunit.xml.dist ├── MIT-LICENSE ├── CHANGELOG.md └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | composer.lock 3 | phpunit.xml 4 | vendor/ 5 | 6 | -------------------------------------------------------------------------------- /examples/_autoload.php: -------------------------------------------------------------------------------- 1 | getEmail("praj@sailthru.com"); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | dist: trusty 2 | language: php 3 | php: 4 | - 5.4 5 | - 5.5 6 | - 5.6 7 | - 7.0 8 | - 7.1 9 | - hhvm 10 | 11 | env: COVERAGE=0 12 | 13 | matrix: 14 | include: 15 | - php: 5.6 16 | env: COVERAGE=1 17 | 18 | before_script: 19 | - if [[ "$COVERAGE" -eq 0 ]]; then composer install; else composer require satooshi/php-coveralls --dev; fi 20 | 21 | script: 22 | - if [[ "$COVERAGE" -eq 0 ]]; then vendor/bin/phpunit; else mkdir -p build/logs; vendor/bin/phpunit --coverage-clover=build/logs/clover.xml; fi 23 | 24 | after_success: 25 | - if [[ "$COVERAGE" -gt 0 ]]; then vendor/bin/coveralls; fi 26 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 14 | 15 | 16 | ./tests 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /MIT-LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 Sailthru, Inc., http://www.sailthru.com/ 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /tests/Sailthru_UtilTest.php: -------------------------------------------------------------------------------- 1 | params = array( 7 | 'item1' => 'value1', 8 | 'item2' => 'value2', 9 | 'item3' => array('value3', 'value4'), 10 | 'item4' => false, 11 | 'item5' => true 12 | ); 13 | } 14 | 15 | public function testExtractParamValues() { 16 | $expected = array('value1', 'value2', 'value3', 'value4', 0, 1); 17 | $actual = array(); 18 | Sailthru_Util::extractParamValues($this->params, $actual); 19 | $this->assertEquals(array_values($expected), $actual); 20 | } 21 | 22 | public function testGetSignatureString() { 23 | $expected_arr = array('value1', 'value2', 'value3', 'value4', 0, 1); 24 | $secret = "ABCXYZ"; 25 | $expected_str = $secret; 26 | sort($expected_arr, SORT_STRING); 27 | foreach ($expected_arr as $val) { 28 | $expected_str .= $val; 29 | } 30 | $actual_signature_str = Sailthru_Util::getSignatureString($this->params, $secret); 31 | $this->assertEquals($expected_str, $actual_signature_str); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 1.2.1 (May 11th, 2016) 2 | - Fix typo, make tests pass 3 | 4 | ## 1.2.0 (May 4th, 2016) 5 | - Added getLastRateLimitInfo() call 6 | 7 | ## 1.1.0 (July 16, 2014) 8 | - Bump Version number for packagist to pick up 9 | - Removed contact import API call 10 | - Added stats_send call 11 | - Updated cancelSend() 12 | - Added Preview and Trigger API calls 13 | - Removed createNewUser() call 14 | - Removed setHorizon() call 15 | - Added Event call 16 | - Added options to pushContent() 17 | ## 1.09 (October 31, 2011) 18 | - Purchase API call 19 | 20 | ## 1.08 (September 07, 2011) 21 | - Added getBlasts() for querying blasts meta-data 22 | - Update job methods: processUpdateJob(), processUpdateJobFromUrl(), processUpdateJobFromFile() and processUpdateJobFromEmails() 23 | - Fixed bug for purchase API request 24 | 25 | ## 1.07 (June 02, 2011) 26 | - Preserve data type when sending to the server, All data are sent in json encoded format 27 | - Job API Call with file uploading support 28 | - Template Revisions 29 | 30 | ## 1.06 (May 14, 2011) 31 | - Added Hard bounce postback call 32 | - Added delete template API call 33 | - Added getLastResponseInfo() 34 | - Bug Fix: When boolean False is used for any parameter values, generated signature hash gets invalidated -------------------------------------------------------------------------------- /examples/user.php: -------------------------------------------------------------------------------- 1 | saveUser("4e2879472d7acd6d97144f9e", array( 11 | 'keys' => array( 12 | 'email' => 'praj@sailthru.com', 13 | 'twitter' => 'infynyxx', 14 | 'fb' => 726310296 15 | ), 16 | 'lists' => array( 17 | 'list-1' => 1, 18 | 'list-2' => 1, 19 | 'list-3' => 0 20 | ) 21 | )); 22 | 23 | //update existing user by email or create new user using email address 24 | $response = $client->saveUser('praj@sailthru.com', array( 25 | 'key' => 'email', 26 | 'lists' => array( 27 | 'list-1' => 0 // optout from list-1 28 | ) 29 | )); 30 | 31 | // get user by Sailthru ID 32 | $fields = array( 33 | 'keys' => 1, 34 | 'vars' => 1, 35 | 'activity' => 1 36 | ); 37 | $response = $client->getUseBySid("4e2879472d7acd6d97144f9e", $fields); 38 | 39 | // get user by Custom key 40 | $response = $client->getUserByKey("praj@sailthru.com", 'email', $fields); 41 | 42 | // get last rate limit info 43 | $rate_limit_info = $client->getLastRateLimitInfo("user", "GET"); 44 | 45 | } catch (Sail_Client_Exception $e) { 46 | // deal with exception 47 | } 48 | -------------------------------------------------------------------------------- /sailthru/Sailthru_Util.php: -------------------------------------------------------------------------------- 1 | $v) { 44 | if (is_array($v) || is_object($v)) { 45 | self::extractParamValues($v, $values); 46 | } else { 47 | if (is_bool($v)) { 48 | //if a value is set as false, invalid hash will generate 49 | //https://github.com/sailthru/sailthru-php5-client/issues/4 50 | $v = intval($v); 51 | } 52 | $values[] = $v; 53 | } 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | sailthru-php5-client 2 | ==================== 3 | 4 | [![Build Status](https://travis-ci.org/sailthru/sailthru-php5-client.svg?branch=master)](https://travis-ci.org/sailthru/sailthru-php5-client) 5 | [![Coverage Status](https://coveralls.io/repos/github/sailthru/sailthru-php5-client/badge.svg?branch=master)](https://coveralls.io/github/sailthru/sailthru-php5-client?branch=master) 6 | 7 | For installation instructions, documentation, and examples please visit: 8 | [http://getstarted.sailthru.com/new-for-developers-overview/api-client-library/php5](http://getstarted.sailthru.com/new-for-developers-overview/api-client-library/php5) 9 | 10 | A simple client library to remotely access the `Sailthru REST API` as per [http://getstarted.sailthru.com/developers/api](http://getstarted.sailthru.com/developers/api) 11 | 12 | By default, it will make request in `JSON` format. 13 | 14 | ## Optional parameters for connection/read timeout settings 15 | 16 | Increase timeout from 10 (default) to 30 seconds. 17 | 18 | $client = new Sailthru_Client($this->api_key, $this->secret, $this->api_url, array('timeout' => 30000, 'connect_timeout' => 30000)); 19 | 20 | ## API Rate Limiting 21 | 22 | Here is an example how to check rate limiting and throttle API calls based on that. For more information about Rate Limiting, see [Sailthru Documentation](https://getstarted.sailthru.com/new-for-developers-overview/api/api-technical-details/#Rate_Limiting) 23 | 24 | 25 | ```php 26 | // get last rate limit info 27 | $rate_limit_info = $sailthru_client->getLastRateLimitInfo("user", "POST"); 28 | 29 | // getRateLimitInfo returns null if given endpoint/method wasn't triggered previously 30 | if ($rate_limit_info) { 31 | $limit = $rate_limit_info['limit']; 32 | $remaining = $rate_limit_info['remaining']; 33 | $reset_timestamp = $rate_limit_info['reset']; 34 | 35 | // throttle api calls based on last rate limit info 36 | if ($remaining <= 0) { 37 | $seconds_till_reset = $reset_timestamp - time(); 38 | 39 | // sleep or perform other business logic before next user api call 40 | sleep($seconds_till_reset); 41 | } 42 | } 43 | ``` 44 | 45 | ## Tests 46 | 47 | You can run the tests locally with: 48 | 49 | ```shell 50 | vendor/bin/phpunit 51 | ``` 52 | 53 | 54 | -------------------------------------------------------------------------------- /tests/Sailthru_ClientTest.php: -------------------------------------------------------------------------------- 1 | api_key, $this->api_secret, $this->api_url); 13 | $this->assertEquals(10000, $sailthru_client->getTimeout()); 14 | $this->assertEquals(10000, $sailthru_client->getConnectTimeout()); 15 | } 16 | 17 | public function testCustomTimeoutParameter() { 18 | $sailthru_client = new Sailthru_Client($this->api_key, $this->api_secret, $this->api_url, 19 | array('timeout' => 1, 'connect_timeout' => 2)); 20 | $this->assertEquals(1, $sailthru_client->getTimeout()); 21 | $this->assertEquals(2, $sailthru_client->getConnectTimeout()); 22 | } 23 | 24 | public function testSendWhenTemplateNameIsInvalid() { 25 | $template_name = 'invalid_template'; 26 | $email = 'praj@sailthru.com'; 27 | $json_response = json_encode(array('error' => 14, 'errormsg' => 'Unknown template: ' . $template_name)); 28 | $mock = $this->getMock('Sailthru_Client', array('send'), array($this->api_key, $this->api_secret, $this->api_url)); 29 | $mock->expects($this->once()) 30 | ->method('send') 31 | ->will($this->returnValue($json_response)); 32 | $this->assertEquals($json_response, $mock->send($template_name, $email)); 33 | } 34 | 35 | public function testSendWhenTemplateIsValid() { 36 | $template_name = 'my_template'; 37 | $email = 'praj@sailthru.com'; 38 | $json_response = json_encode(array('email' => $email, 'send_id' => 'some_unique_id', 'template' => $template_name, 'status' => 'unknown')); 39 | $mock = $this->getMock('Sailthru_Client', array('send'), array($this->api_key, $this->api_secret, $this->api_url)); 40 | $mock->expects($this->once()) 41 | ->method('send') 42 | ->will($this->returnvalue($json_response)); 43 | $this->assertEquals($json_response, $mock->send($template_name, $email)); 44 | } 45 | 46 | public function testApiPostWithValidJsonResponse() { 47 | $mock = $this->getMock('Sailthru_Client', array('apiPost'), array($this->api_key, $this->api_secret, $this->api_url)); 48 | $json_response = array( 49 | 'email' => 'praj@infynyxx.com', 50 | 'profile_id' => '4f284c28a3a627b6389bfb4c', 51 | 'verified' => 0, 52 | 'vars' => array( 53 | 'name' => 'Prajwal Tuladhar' 54 | ) 55 | ); 56 | $mock->expects($this->once()) 57 | ->method('apiPost') 58 | ->will($this->returnValue($json_response)); 59 | $this->assertTrue(is_array($mock->apiPost('email', $json_response))); 60 | } 61 | 62 | 63 | /** 64 | * @expectedException Sailthru_Client_Exception 65 | */ 66 | public function testApiPostWithInvalidJsonResponse() { 67 | $mock = $this->getMock('Sailthru_Client', array('apiPost'), array($this->api_key, $this->api_secret, $this->api_url)); 68 | $mock->expects($this->once()) 69 | ->method('apiPost') 70 | ->will($this->throwException(new Sailthru_Client_Exception())); 71 | $response = $mock->apiPost('email', array('email' => 'praj@infynyxx.com')); 72 | $this->assertTrue(is_array($response)); // this will never be called 73 | } 74 | 75 | public function testPrepareJsonPayload() { 76 | $this->sailthru_client = new Sailthru_Client($this->api_key, $this->api_secret, $this->api_url); 77 | $method = new ReflectionMethod('Sailthru_Client', 'prepareJsonPayload'); 78 | $method->setAccessible(true); 79 | $json_payload_without_binary_data = array( 80 | 'email' => 'praj@infynyxx.com', 81 | 'vars' => array( 82 | 'name' => 'Prajwal Tuladhar' 83 | ), 84 | 'action' => 'user' 85 | ); 86 | $invoked = $method->invoke($this->sailthru_client, $json_payload_without_binary_data); 87 | $this->assertEquals($invoked['api_key'], $this->api_key); 88 | $this->assertTrue(isset($invoked['sig'])); 89 | } 90 | 91 | public function testPrepareJsonPayloadWithBinaryData() { 92 | $this->sailthru_client = new Sailthru_Client($this->api_key, $this->api_secret, $this->api_url); 93 | $method = new ReflectionMethod('Sailthru_Client', 'prepareJsonPayload'); 94 | $method->setAccessible(true); 95 | $json_payload = array( 96 | 'email' => 'praj@infynyxx.com', 97 | 'vars' => array( 98 | 'name' => 'Prajwal Tuladhar' 99 | ), 100 | 'action' => 'user' 101 | ); 102 | $binary_data_param = array('file' => '/tmp/file.txt'); 103 | $invoked = $method->invoke($this->sailthru_client, $json_payload, $binary_data_param); 104 | $this->assertEquals($invoked['api_key'], $this->api_key); 105 | $this->assertEquals($invoked['file'], $binary_data_param['file']); 106 | } 107 | 108 | public function testParseRateLimitHeaders() { 109 | $sailthru_client = new Sailthru_Client($this->api_key, $this->api_secret, $this->api_url); 110 | $method = new ReflectionMethod("Sailthru_Client", "parseRateLimitHeaders"); 111 | $method->setAccessible(true); 112 | $headers = <<invoke($sailthru_client, $headers); 124 | $expected = ['limit' => 40, 'remaining' => 3, "reset" => 1459190520]; 125 | $this->assertEquals($expected, $parsed_rate_limit); 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /sailthru/Sailthru_Client.php: -------------------------------------------------------------------------------- 1 | Sailthru_Client::DEFAULT_READ_TIMEOUT, 'connect_timeout' => Sailthru_Client::DEFAULT_CONNECT_TIMEOUT ]; 69 | 70 | /** 71 | * Instantiate a new client; constructor optionally takes overrides for api_uri and whether 72 | * to share the version of PHP that is being used. 73 | * 74 | * @param string $api_key 75 | * @param string $secret 76 | * @param string $api_uri 77 | * @param array $options - optional parameters for connect/read timeout 78 | */ 79 | public function __construct($api_key, $secret, $api_uri = false, $options = null) { 80 | $this->api_key = $api_key; 81 | $this->secret = $secret; 82 | if ($api_uri !== false) { 83 | $this->api_uri = $api_uri; 84 | } 85 | 86 | $this->http_request_type = function_exists('curl_init') ? 'httpRequestCurl' : 'httpRequestWithoutCurl'; 87 | 88 | if (isset($options)) { 89 | $this->options['timeout'] = isset($options['timeout']) ? (int) $options['timeout'] : Sailthru_Client::DEFAULT_READ_TIMEOUT; 90 | $this->options['connect_timeout'] = 91 | isset($options['connect_timeout']) ? (int) $options['connect_timeout'] : Sailthru_Client::DEFAULT_CONNECT_TIMEOUT; 92 | } 93 | } 94 | 95 | public function getConnectTimeout() { 96 | return $this->options['connect_timeout']; 97 | } 98 | 99 | public function getTimeout() { 100 | return $this->options['timeout']; 101 | } 102 | 103 | public function setHttpHeaders(array $headers) { 104 | $this->httpHeaders = array_merge($this->httpHeaders, $headers); 105 | return true; 106 | } 107 | 108 | /** 109 | * Remotely send an email template to a single email address. 110 | * 111 | * If you pass the $schedule_time parameter, the send will be scheduled for a future time. 112 | * 113 | * Options: 114 | * replyto: override Reply-To header 115 | * test: send as test email (subject line will be marked, will not count towards stats) 116 | * 117 | * @param string $template 118 | * @param string $email 119 | * @param array $vars 120 | * @param array $options 121 | * @param string $schedule_time 122 | * @link http://docs.sailthru.com/api/send 123 | * @return array API result 124 | */ 125 | public function send($template, $email, $vars = [ ], $options = [ ], $schedule_time = null) { 126 | $post = [ ]; 127 | $post['template'] = $template; 128 | $post['email'] = $email; 129 | $post['vars'] = $vars; 130 | $post['options'] = $options; 131 | if ($schedule_time) { 132 | $post['schedule_time'] = $schedule_time; 133 | } 134 | $result = $this->apiPost('send', $post); 135 | return $result; 136 | } 137 | 138 | /** 139 | * Remotely send an email template to multiple email addresses. 140 | * 141 | * Use the evars parameter to set replacement vars for a particular email address. 142 | * 143 | * @param string $template_name 144 | * @param array $emails 145 | * @param array $vars 146 | * @param array $evars 147 | * @param array $options 148 | * @link http://docs.sailthru.com/api/send 149 | * @return array API result 150 | */ 151 | public function multisend($template_name, $emails, $vars = [ ], $evars = [ ], $options = [ ]) { 152 | $post['template'] = $template_name; 153 | $post['email'] = is_array($emails) ? implode(',', $emails) : $emails; 154 | $post['vars'] = $vars; 155 | $post['evars'] = $evars; 156 | $post['options'] = $options; 157 | $result = $this->apiPost('send', $post); 158 | return $result; 159 | } 160 | 161 | /** 162 | * Get the status of a send. 163 | * 164 | * @param string $send_id 165 | * @link http://docs.sailthru.com/api/send 166 | * @return array API result 167 | */ 168 | public function getSend($send_id) { 169 | return $this->apiGet('send', [ 'send_id' => $send_id ]); 170 | } 171 | 172 | /** 173 | * Cancel a send that was scheduled for a future time. 174 | * 175 | * @param string $send_id 176 | * @link http://docs.sailthru.com/api/send 177 | * @return array API result 178 | */ 179 | public function cancelSend($send_id) { 180 | return $this->apiDelete('send', [ 'send_id' => $send_id ]); 181 | } 182 | 183 | /** 184 | * Return information about an email address, including replacement vars and lists. 185 | * 186 | * @param string $email 187 | * @param array $options 188 | * @link http://docs.sailthru.com/api/email 189 | * @return array API result 190 | */ 191 | public function getEmail($email, array $options = [ ]) { 192 | return $this->apiGet('email', array_merge([ 'email' => $email ], $options)); 193 | } 194 | 195 | /** 196 | * Set replacement vars and/or list subscriptions for an email address. 197 | * 198 | * $lists should be an assoc array mapping list name => 1 for subscribed, 0 for unsubscribed 199 | * 200 | * @param string $email 201 | * @param array $vars 202 | * @param array $lists 203 | * @param array $templates 204 | * @param integer $verified 1 or 0 205 | * @param string $optout 206 | * @param string $send 207 | * @param array $send_vars 208 | * @link http://docs.sailthru.com/api/email 209 | * @return array API result 210 | */ 211 | public function setEmail($email, $vars = [ ], $lists = [ ], $templates = [ ], $verified = 0, $optout = null, $send = null, $send_vars = [ ]) { 212 | $data = [ 'email' => $email ]; 213 | if ($vars) { 214 | $data['vars'] = $vars; 215 | } 216 | if ($lists) { 217 | $data['lists'] = $lists; 218 | } 219 | if ($templates) { 220 | $data['templates'] = $templates; 221 | } 222 | $data['verified'] = (int) $verified; 223 | if ($optout !== null) { 224 | $data['optout'] = $optout; 225 | } 226 | if ($send !== null) { 227 | $data['send'] = $send; 228 | } 229 | if (!empty($send_vars)) { 230 | $data['send_vars'] = $send_vars; 231 | } 232 | 233 | return $this->apiPost('email', $data); 234 | } 235 | 236 | /** 237 | * Update / add email address 238 | * 239 | * @link http://docs.sailthru.com/api/email 240 | * @return array API result 241 | */ 242 | public function setEmail2($email, array $options = [ ]) { 243 | $options['email'] = $email; 244 | return $this->apiPost('email', $options); 245 | } 246 | 247 | /** 248 | * Schedule a mass mail blast 249 | * 250 | * @param string $name the name to give to this new blast 251 | * @param string $list the mailing list name to send to 252 | * @param string $schedule_time when the blast should send. Dates in the past will be scheduled for immediate delivery. Any English textual datetime format known to PHP's strtotime function is acceptable, such as 2009-03-18 23:57:22 UTC, now (immediate delivery), +3 hours (3 hours from now), or February 14, 9:30 EST. Be sure to specify a timezone if you use an exact time. 253 | * @param string $from_name the name appearing in the "From" of the email 254 | * @param string $from_email The email address to use as the "from" – choose from any of your verified emails 255 | * @param string $subject the subject line of the email 256 | * @param string $content_html the HTML-format version of the email 257 | * @param string $content_text the text-format version of the email 258 | * @param array $options associative array 259 | * blast_id 260 | * copy_blast 261 | * copy_template 262 | * replyto 263 | * report_email 264 | * is_link_tracking 265 | * is_google_analytics 266 | * is_public 267 | * suppress_list 268 | * test_vars 269 | * email_hour_range 270 | * abtest 271 | * test_percent 272 | * data_feed_url 273 | * @link http://docs.sailthru.com/api/blast 274 | * @return array API result 275 | */ 276 | public function scheduleBlast($name, $list, $schedule_time, $from_name, 277 | $from_email, $subject, $content_html, $content_text, $options = [ ] 278 | ) { 279 | $data = $options; 280 | $data['name'] = $name; 281 | $data['list'] = $list; 282 | $data['schedule_time'] = $schedule_time; 283 | $data['from_name'] = $from_name; 284 | $data['from_email'] = $from_email; 285 | $data['subject'] = $subject; 286 | $data['content_html'] = $content_html; 287 | $data['content_text'] = $content_text; 288 | 289 | return $this->apiPost('blast', $data); 290 | } 291 | 292 | /** 293 | * Schedule a mass mail from a template 294 | * 295 | * @param String $template 296 | * @param String $list 297 | * @param String $schedule_time 298 | * @param array $options 299 | * @link http://docs.sailthru.com/api/blast 300 | * @return array API result 301 | **/ 302 | public function scheduleBlastFromTemplate($template, $list, $schedule_time, $options = [ ]) { 303 | $data = $options; 304 | $data['copy_template'] = $template; 305 | $data['list'] = $list; 306 | $data['schedule_time'] = $schedule_time; 307 | return $this->apiPost('blast', $data); 308 | } 309 | 310 | /** 311 | * Schedule a mass mail blast from previous blast 312 | * 313 | * @param String|Integer $blast_id 314 | * @param String $schedule_time 315 | * @param array $options 316 | * @link http://docs.sailthru.com/api/blast 317 | * @return array API result 318 | **/ 319 | public function scheduleBlastFromBlast($blast_id, $schedule_time, $options = [ ]) { 320 | $data = $options; 321 | $data['copy_blast'] = $blast_id; 322 | $data['schedule_time'] = $schedule_time; 323 | return $this->apiPost('blast', $data); 324 | } 325 | 326 | /** 327 | * updates existing blast 328 | * 329 | * @param string /integer $blast_id 330 | * @param string $name 331 | * @param string $list 332 | * @param string $schedule_time 333 | * @param string $from_name 334 | * @param string $from_email 335 | * @param string $subject 336 | * @param string $content_html 337 | * @param string $content_text 338 | * @param array $options associative array 339 | * blast_id 340 | * copy_blast 341 | * copy_template 342 | * replyto 343 | * report_email 344 | * is_link_tracking 345 | * is_google_analytics 346 | * is_public 347 | * suppress_list 348 | * test_vars 349 | * email_hour_range 350 | * abtest 351 | * test_percent 352 | * data_feed_url 353 | * @link http://docs.sailthru.com/api/blast 354 | * @return array API result 355 | */ 356 | public function updateBlast($blast_id, $name = null, $list = null, 357 | $schedule_time = null, $from_name = null, $from_email = null, 358 | $subject = null, $content_html = null, $content_text = null, 359 | $options = [ ] 360 | ) { 361 | $data = $options; 362 | $data['blast_id'] = $blast_id; 363 | if (!is_null($name)) { 364 | $data['name'] = $name; 365 | } 366 | if (!is_null($list)) { 367 | $data['list'] = $list; 368 | } 369 | if (!is_null($schedule_time)) { 370 | $data['schedule_time'] = $schedule_time; 371 | } 372 | if (!is_null($from_name)) { 373 | $data['from_name'] = $from_name; 374 | } 375 | if (!is_null($from_email)) { 376 | $data['from_email'] = $from_email; 377 | } 378 | if (!is_null($subject)) { 379 | $data['subject'] = $subject; 380 | } 381 | if (!is_null($content_html)) { 382 | $data['content_html'] = $content_html; 383 | } 384 | if (!is_null($content_text)) { 385 | $data['content_text'] = $content_text; 386 | } 387 | 388 | return $this->apiPost('blast', $data); 389 | } 390 | 391 | /** 392 | * Get Blast information 393 | * @param string /integer $blast_id 394 | * @link http://docs.sailthru.com/api/blast 395 | * @return array API result 396 | */ 397 | public function getBlast($blast_id) { 398 | return $this->apiGet('blast', [ 'blast_id' => $blast_id ]); 399 | } 400 | 401 | /** 402 | * Get info on multiple blasts 403 | * @param array $options associative array 404 | * start_date (required) 405 | * end-date (required) 406 | * status 407 | * @link http://docs.sailthru.com/api/blast 408 | * @return array API result 409 | */ 410 | public function getBlasts($options) { 411 | return $this->apiGet('blast', $options); 412 | } 413 | 414 | /** 415 | * Delete Blast 416 | * @param integer /string $blast_id 417 | * @link http://docs.sailthru.com/api/blast 418 | * @return array API result 419 | */ 420 | public function deleteBlast($blast_id) { 421 | return $this->apiDelete('blast', [ 'blast_id' => $blast_id ]); 422 | } 423 | 424 | /** 425 | * Cancel a scheduled Blast 426 | * @param integer /string $blast_id 427 | * @link http://docs.sailthru.com/api/blast 428 | * @return array API result 429 | */ 430 | public function cancelBlast($blast_id) { 431 | $data = [ 432 | 'blast_id' => $blast_id, 433 | 'schedule_time' => '' 434 | ]; 435 | return $this->apiPost('blast', $data); 436 | } 437 | 438 | /** 439 | * Fetch information about a template 440 | * 441 | * @param string $template_name 442 | * @param array $options 443 | * @return array API result 444 | * @link http://docs.sailthru.com/api/template 445 | */ 446 | public function getTemplate($template_name, array $options = [ ]) { 447 | $options['template'] = $template_name; 448 | return $this->apiGet('template', $options); 449 | } 450 | 451 | /** 452 | * Fetch name of all existing templates 453 | * @link http://docs.sailthru.com/api/template 454 | * @return array API result 455 | */ 456 | public function getTemplates() { 457 | return $this->apiGet('template'); 458 | } 459 | 460 | public function getTemplateFromRevision($revision_id) { 461 | return $this->apiGet('template', [ 'revision' => (int) $revision_id ]); 462 | } 463 | 464 | /** 465 | * Save a template. 466 | * 467 | * @param string $template_name 468 | * @param array $template_fields 469 | * @link http://docs.sailthru.com/api/template 470 | * @return array API result 471 | */ 472 | public function saveTemplate($template_name, array $template_fields = [ ]) { 473 | $data = $template_fields; 474 | $data['template'] = $template_name; 475 | return $this->apiPost('template', $data); 476 | } 477 | 478 | /** 479 | * Save a template from revision 480 | * 481 | * @param string $template_name 482 | * @param $revision_id 483 | * @return array API result 484 | * @link http://docs.sailthru.com/api/template 485 | */ 486 | public function saveTemplateFromRevision($template_name, $revision_id) { 487 | $revision_id = (int) $revision_id; 488 | return $this->saveTemplate($template_name, [ 'revision' => $revision_id ]); 489 | } 490 | 491 | /** 492 | * Delete a template. 493 | * 494 | * @param string $template_name 495 | * @return array API result 496 | * @link http://docs.sailthru.com/api/template 497 | */ 498 | public function deleteTemplate($template_name) { 499 | return $this->apiDelete('template', [ 'template' => $template_name ]); 500 | } 501 | 502 | /** 503 | * Fetch information about an include 504 | * 505 | * @param string $include_name 506 | * @return array API result 507 | */ 508 | public function getInclude($include_name, array $options = [ ]) { 509 | $options['include'] = $include_name; 510 | return $this->apiGet('include', $options); 511 | } 512 | 513 | /** 514 | * Fetch name of all existing includes 515 | * @return array API result 516 | */ 517 | public function getIncludes() { 518 | return $this->apiGet('include'); 519 | } 520 | 521 | /** 522 | * Save an include 523 | * 524 | * @param string $include_name 525 | * @param array $include_fields 526 | * @return array API result 527 | */ 528 | public function saveInclude($include_name, array $include_fields = [ ]) { 529 | $data = $include_fields; 530 | $data['include'] = $include_name; 531 | return $this->apiPost('include', $data); 532 | } 533 | 534 | /** 535 | * Get information about a list. 536 | * 537 | * @param string $list 538 | * @return array 539 | * @link http://docs.sailthru.com/api/list 540 | */ 541 | public function getList($list) { 542 | return $this->apiGet('list', [ 'list' => $list ]); 543 | } 544 | 545 | /** 546 | * Get information about all lists 547 | * @return array 548 | * @link http://docs.sailthru.com/api/list 549 | */ 550 | public function getLists() { 551 | return $this->apiGet('list', [ ]); 552 | } 553 | 554 | /** 555 | * Create a list, or update a list. 556 | * 557 | * @param string $list 558 | * @param string $list 559 | * @param string $type 560 | * @param bool $primary 561 | * @param array $query 562 | * @return array 563 | * @link http://docs.sailthru.com/api/list 564 | * @link http://docs.sailthru.com/api/query 565 | */ 566 | public function saveList($list, $type = null, $primary = null, $query = [ ], $vars = []) { 567 | $data = [ 568 | 'list' => $list, 569 | 'type' => $type, 570 | 'primary' => $primary ? 1 : 0, 571 | 'query' => $query, 572 | 'vars' => $vars, 573 | ]; 574 | return $this->apiPost('list', $data); 575 | } 576 | 577 | /** 578 | * Deletes a list. 579 | * 580 | * @param string $list 581 | * @return array 582 | * @link http://docs.sailthru.com/api/list 583 | */ 584 | public function deleteList($list) { 585 | return $this->apiDelete('list', [ 'list' => $list ]); 586 | } 587 | 588 | /** 589 | * 590 | * Push a new piece of content to Sailthru, triggering any applicable alerts. 591 | * 592 | * @param String $title 593 | * @param String $url 594 | * @param String $date 595 | * @param Mixed $tags Null for empty values, or String or arrays 596 | * @link http://docs.sailthru.com/api/content 597 | * @return array API result 598 | */ 599 | public function pushContent($title, $url, $date = null, $tags = null, $vars = [ ], $options = [ ]) { 600 | $data = $options; 601 | $data['title'] = $title; 602 | $data['url'] = $url; 603 | if (!is_null($tags)) { 604 | $data['tags'] = is_array($tags) ? implode(",", $tags) : $tags; 605 | } 606 | if (!is_null($date)) { 607 | $data['date'] = $date; 608 | } 609 | if (!empty($vars)) { 610 | $data['vars'] = $vars; 611 | } 612 | return $this->apiPost('content', $data); 613 | } 614 | 615 | /** 616 | * 617 | * Retrieve a user's alert settings. 618 | * 619 | * @link http://docs.sailthru.com/api/alert 620 | * @param String $email 621 | * @return array API result 622 | */ 623 | public function getAlert($email) { 624 | $data = [ 625 | 'email' => $email 626 | ]; 627 | return $this->apiGet('alert', $data); 628 | } 629 | 630 | /** 631 | * 632 | * Add a new alert to a user. You can add either a realtime or a summary alert (daily/weekly). 633 | * $when is only required when alert type is weekly or daily 634 | * 635 | * 636 | * array( 639 | * 'type' => array('shoes', 'shirts'), 640 | * 'min' => array('price' => 3000), 641 | * 'tags' => array('blue', 'red'), 642 | * ) 643 | * ); 644 | * $response = $sailthruClient->saveAlert("praj@sailthru.com", 'realtime', 'default', null, $options); 645 | * ?> 646 | * 647 | * 648 | * @link http://docs.sailthru.com/api/alert 649 | * @param String $email 650 | * @param String $type 651 | * @param String $template 652 | * @param String $when 653 | * @param array $options Associative array of additive nature 654 | * match Exact-match a custom variable match[type]=shoes 655 | * min Minimum-value variables min[price]=30000 656 | * max Maximum-value match max[price]=50000 657 | * tags Tag-match tags[]=blue 658 | * @return array API result 659 | */ 660 | public function saveAlert($email, $type, $template, $when = null, $options = [ ]) { 661 | $data = $options; 662 | $data['email'] = $email; 663 | $data['type'] = $type; 664 | $data['template'] = $template; 665 | if ($type == 'weekly' || $type == 'daily') { 666 | $data['when'] = $when; 667 | } 668 | return $this->apiPost('alert', $data); 669 | } 670 | 671 | /** 672 | * Remove an alert from a user's settings. 673 | * @link http://docs.sailthru.com/api/alert 674 | * @param $email 675 | * @param $alert_id 676 | * @return array API result 677 | */ 678 | public function deleteAlert($email, $alert_id) { 679 | $data = [ 680 | 'email' => $email, 681 | 'alert_id' => $alert_id 682 | ]; 683 | return $this->apiDelete('alert', $data); 684 | } 685 | 686 | /** 687 | * Record that a user has made a purchase, or has added items to their purchase total. 688 | * @link http://docs.sailthru.com/api/purchase 689 | * @return array API result 690 | */ 691 | public function purchase($email, array $items, $incomplete = null, $message_id = null, array $options = [ ]) { 692 | $data = $options; 693 | $data['email'] = $email; 694 | $data['items'] = $items; 695 | if (!is_null($incomplete)) { 696 | $data['incomplete'] = (int) $incomplete; 697 | } 698 | if (!is_null($message_id)) { 699 | $data['message_id'] = $message_id; 700 | } 701 | return $this->apiPost('purchase', $data); 702 | } 703 | 704 | /** 705 | * Make a purchase API call with incomplete flag 706 | * @link http://docs.sailthru.com/api/purchase 707 | * @return array API result 708 | */ 709 | public function purchaseIncomplete($email, array $items, $message_id, array $options = [ ]) { 710 | return $this->purchase($email, $items, 1, $message_id, $options); 711 | } 712 | 713 | /** 714 | * Retrieve information about your subscriber counts on a particular list, on a particular day. 715 | * @link http://docs.sailthru.com/api/stats 716 | * @param String $list 717 | * @param String $date 718 | * @return array API result 719 | */ 720 | public function stats_list($list = null, $date = null) { 721 | $data = [ ]; 722 | if (!is_null($list)) { 723 | $data['list'] = $list; 724 | } 725 | 726 | if (!is_null($date)) { 727 | $data['date'] = $date; 728 | } 729 | $data['stat'] = 'list'; 730 | return $this->stats($data); 731 | } 732 | 733 | /** 734 | * Retrieve information about a particular blast or aggregated information from all of blasts over a specified date range. 735 | * @param array $data 736 | * @return array API result 737 | */ 738 | public function stats_blast($blast_id = null, $start_date = null, $end_date = null, array $data = [ ]) { 739 | $data['stat'] = 'blast'; 740 | if (!is_null($blast_id)) { 741 | $data['blast_id'] = $blast_id; 742 | } 743 | if (!is_null($start_date)) { 744 | $data['start_date'] = $start_date; 745 | } 746 | if (!is_null($end_date)) { 747 | $data['end_date'] = $end_date; 748 | } 749 | return $this->stats($data); 750 | } 751 | 752 | /** 753 | * Retrieve information about a particular send or aggregated information from all of templates over a specified date range. 754 | * @param array $data 755 | * @return array API result 756 | */ 757 | public function stats_send($template = null, $start_date = null, $end_date = null, array $data = [ ]) { 758 | $data['stat'] = 'send'; 759 | 760 | if (!is_null($template)) { 761 | $data['template'] = $template; 762 | } 763 | if (!is_null($start_date)) { 764 | $data['start_date'] = $start_date; 765 | } 766 | if (!is_null($end_date)) { 767 | $data['end_date'] = $end_date; 768 | } 769 | 770 | return $this->stats($data); 771 | } 772 | 773 | /** 774 | * Make Stats API Request 775 | * @param array $data 776 | * @return array API result 777 | */ 778 | public function stats(array $data) { 779 | return $this->apiGet('stats', $data); 780 | } 781 | 782 | /** 783 | * 784 | * Returns true if the incoming request is an authenticated verify post. 785 | * @link http://docs.sailthru.com/api/postbacks 786 | * @return boolean 787 | */ 788 | public function receiveVerifyPost() { 789 | $params = $_POST; 790 | foreach ([ 'action', 'email', 'send_id', 'sig' ] as $k) { 791 | if (!isset($params[$k])) { 792 | return false; 793 | } 794 | } 795 | 796 | if ($params['action'] != 'verify') { 797 | return false; 798 | } 799 | $sig = $params['sig']; 800 | unset($params['sig']); 801 | if ($sig != Sailthru_Util::getSignatureHash($params, $this->secret)) { 802 | return false; 803 | } 804 | $send = $this->getSend($params['send_id']); 805 | if (!isset($send['email'])) { 806 | return false; 807 | } 808 | if ($send['email'] != $params['email']) { 809 | return false; 810 | } 811 | return true; 812 | } 813 | 814 | /** 815 | * 816 | * Optout postbacks 817 | * @return boolean 818 | * @link http://docs.sailthru.com/api/postbacks 819 | */ 820 | public function receiveOptoutPost() { 821 | $params = $_POST; 822 | foreach ([ 'action', 'email', 'sig' ] as $k) { 823 | if (!isset($params[$k])) { 824 | return false; 825 | } 826 | } 827 | 828 | if ($params['action'] != 'optout') { 829 | return false; 830 | } 831 | $sig = $params['sig']; 832 | unset($params['sig']); 833 | if ($sig != Sailthru_Util::getSignatureHash($params, $this->secret)) { 834 | return false; 835 | } 836 | return true; 837 | } 838 | 839 | /** 840 | * 841 | * Update postbacks 842 | * @return boolean 843 | * @link http://docs.sailthru.com/api/postbacks 844 | */ 845 | public function receiveUpdatePost() { 846 | $params = $_POST; 847 | foreach ([ 'action', 'sid', 'sig' ] as $k) { 848 | if (!isset($params[$k])) { 849 | return false; 850 | } 851 | } 852 | 853 | if ($params['action'] != 'update') { 854 | return false; 855 | } 856 | $sig = $params['sig']; 857 | unset($params['sig']); 858 | if ($sig != Sailthru_Util::getSignatureHash($params, $this->secret)) { 859 | return false; 860 | } 861 | return true; 862 | } 863 | 864 | /** 865 | * 866 | * Hard bounce postbacks 867 | * @return boolean 868 | * @link http://docs.sailthru.com/api/postbacks 869 | */ 870 | public function receiveHardBouncePost() { 871 | $params = $_POST; 872 | foreach ([ 'action', 'email', 'sig' ] as $k) { 873 | if (!isset($params[$k])) { 874 | return false; 875 | } 876 | } 877 | if ($params['action'] != 'hardbounce') { 878 | return false; 879 | } 880 | $sig = $params['sig']; 881 | unset($params['sig']); 882 | if ($sig != Sailthru_Util::getSignatureHash($params, $this->secret)) { 883 | return false; 884 | } 885 | if (isset($params['send_id'])) { 886 | $send_id = $params['send_id']; 887 | $send = $this->getSend($send_id); 888 | if (!isset($send['email'])) { 889 | return false; 890 | } 891 | } else if (isset($params['blast_id'])) { 892 | $blast_id = $params['blast_id']; 893 | $blast = $this->getBlast($blast_id); 894 | if (isset($blast['error'])) { 895 | return false; 896 | } 897 | } 898 | return true; 899 | } 900 | 901 | /** 902 | * Get status of a job 903 | * @param String $job_id 904 | * @return array 905 | */ 906 | public function getJobStatus($job_id) { 907 | return $this->apiGet('job', [ 'job_id' => $job_id ]); 908 | } 909 | 910 | /** 911 | * process job api call 912 | * @param String $job 913 | * @param array $data 914 | * @param bool|String $report_email 915 | * @param bool|String $postback_url 916 | * @param array $binary_data_param 917 | * @param array $options 918 | * @return array 919 | */ 920 | protected function processJob($job, array $data = [ ], $report_email = false, $postback_url = false, array $binary_data_param = [ ], 921 | array $options = [ ]) { 922 | $data['job'] = $job; 923 | if ($report_email) { 924 | $data['report_email'] = $report_email; 925 | } 926 | if ($postback_url) { 927 | $data['postback_url'] = $postback_url; 928 | } 929 | return $this->apiPost('job', $data, $binary_data_param, $options); 930 | } 931 | 932 | /** 933 | * Process import job from given email string CSV 934 | * @param String $list 935 | * @param String $emails 936 | * @param bool|String $report_email 937 | * @param bool|String $postback_url 938 | * @return array 939 | */ 940 | public function processImportJob($list, $emails, $report_email = false, $postback_url = false, array $options = [ ]) { 941 | $data = [ 942 | 'emails' => $emails, 943 | 'list' => $list 944 | ]; 945 | return $this->processJob('import', $data, $report_email, $postback_url, [ ], $options); 946 | } 947 | 948 | /** 949 | * Process import job from given file path of a CSV or email per line file 950 | * 951 | * @param String $list 952 | * @param $file_path 953 | * @param bool|String $report_email 954 | * @param bool|String $postback_url 955 | * @param array $options 956 | * @return array 957 | */ 958 | public function processImportJobFromFile($list, $file_path, $report_email = false, $postback_url = false, array $options = [ ]) { 959 | $data = [ 960 | 'file' => $file_path, 961 | 'list' => $list 962 | ]; 963 | return $this->processJob('import', $data, $report_email, $postback_url, [ 'file' ], $options); 964 | } 965 | 966 | /** 967 | * Process purchase import job from given file path of an email per line JSON file 968 | * 969 | * @param String $file_path 970 | * @param bool|String $report_email 971 | * @param bool|String $postback_url 972 | * @param array $options 973 | * @return array 974 | */ 975 | public function processPurchaseImportJobFromFile($file_path, $report_email = false, $postback_url = false, array $options = [ ]) { 976 | $data = [ 977 | 'file' => $file_path 978 | ]; 979 | return $this->processJob('purchase_import', $data, $report_email, $postback_url, [ 'file' ], $options); 980 | } 981 | 982 | /** 983 | * Process a snapshot job 984 | * 985 | * @param array $query 986 | * @param bool|String $report_email 987 | * @param bool|String $postback_url 988 | * @return array 989 | */ 990 | public function processSnapshotJob(array $query, $report_email = false, $postback_url = false, array $options = [ ]) { 991 | $data = [ 'query' => $query ]; 992 | return $this->processJob('snaphot', $data, $report_email, $postback_url, [ ], $options); 993 | } 994 | 995 | /** 996 | * Process a export list job 997 | * @param String $list 998 | * @param bool|String $report_email 999 | * @param bool|String $postback_url 1000 | * @param array $options 1001 | * @return array 1002 | */ 1003 | public function processExportListJob($list, $report_email = false, $postback_url = false, array $options = [ ]) { 1004 | $data = [ 'list' => $list ]; 1005 | return $this->processJob('export_list_data', $data, $report_email, $postback_url, [ ], $options); 1006 | } 1007 | 1008 | /** 1009 | * Export blast data in CSV format 1010 | * @param integer $blast_id 1011 | * @param bool|String $report_email 1012 | * @param bool|String $postback_url 1013 | * @param array $options 1014 | * @return array 1015 | */ 1016 | public function processBlastQueryJob($blast_id, $report_email = false, $postback_url = false, array $options = [ ]) { 1017 | return $this->processJob('blast_query', [ 'blast_id' => $blast_id ], $report_email, $postback_url, [ ], $options); 1018 | } 1019 | 1020 | /** 1021 | * Perform a bulk update of any number of user profiles from given context: String CSV, file, URL or query 1022 | * @param String $context 1023 | * @param $value 1024 | * @param array $update 1025 | * @param bool|String $report_email 1026 | * @param bool|String $postback_url 1027 | * @param array $file_params 1028 | * @return array 1029 | */ 1030 | public function processUpdateJob($context, $value, array $update = [ ], $report_email = false, $postback_url = false, array $file_params = [ ], 1031 | array $options = [ ]) { 1032 | $data = [ 1033 | $context => $value 1034 | ]; 1035 | if (count($update) > 0) { 1036 | $data['update'] = $update; 1037 | } 1038 | return $this->processJob('update', $data, $report_email, $postback_url, $file_params, $options); 1039 | } 1040 | 1041 | /** 1042 | * Perform a bulk update of any number of user profiles from given URL 1043 | * @param String $url 1044 | * @param array $update 1045 | * @param bool|String $report_email 1046 | * @param bool|String $postback_url 1047 | * @param array $options 1048 | * @return array 1049 | */ 1050 | public function processUpdateJobFromUrl($url, array $update = [ ], $report_email = false, $postback_url = false, array $options = [ ]) { 1051 | return $this->processUpdateJob('url', $url, $update, $report_email, $postback_url, [ ], $options); 1052 | } 1053 | 1054 | /** 1055 | * Perform a bulk update of any number of user profiles from given file 1056 | * @param $file 1057 | * @param Array $update 1058 | * @param bool|String $report_email 1059 | * @param bool|String $postback_url 1060 | * @param array $options 1061 | * @return array 1062 | * @internal param String $url 1063 | */ 1064 | public function processUpdateJobFromFile($file, array $update = [ ], $report_email = false, $postback_url = false, array $options = [ ]) { 1065 | return $this->processUpdateJob('file', $file, $update, $report_email, $postback_url, [ 'file' ], $options); 1066 | } 1067 | 1068 | /** 1069 | * Perform a bulk update of any number of user profiles from a query 1070 | * @param Array $query 1071 | * @param Array $update 1072 | * @param String $report_email 1073 | * @param String $postback_url 1074 | */ 1075 | public function processUpdateJobFromQuery($query, array $update = [ ], $report_email = false, $postback_url = false, array $options = [ ]) { 1076 | return $this->processUpdateJob('query', $query, $update, $report_email, $postback_url, [ ], $options); 1077 | } 1078 | 1079 | /** 1080 | * Perform a bulk update of any number of user profiles from emails CSV 1081 | * @param String $emails 1082 | * @param Array $update 1083 | * @param bool|String $report_email 1084 | * @param bool|String $postback_url 1085 | * @return array 1086 | */ 1087 | public function processUpdateJobFromEmails($emails, array $update = [ ], $report_email = false, $postback_url = false, array $options = [ ]) { 1088 | return $this->processUpdateJob('emails', $emails, $update, $report_email, $postback_url, [ ], $options); 1089 | } 1090 | 1091 | /** 1092 | * Save existing user 1093 | * @param String $id 1094 | * @param array $options 1095 | * @return array 1096 | */ 1097 | public function saveUser($id, array $options = [ ]) { 1098 | $data = $options; 1099 | $data['id'] = $id; 1100 | return $this->apiPost('user', $data); 1101 | } 1102 | 1103 | /** 1104 | * Get user by Sailthru ID 1105 | * @param String $id 1106 | * @return array 1107 | */ 1108 | public function getUserBySid($id) { 1109 | return $this->apiGet('user', [ 'id' => $id ]); 1110 | } 1111 | 1112 | /** 1113 | * Get user by specified key 1114 | * @param String $id 1115 | * @param String $key 1116 | * @param array $fields 1117 | * @return array 1118 | */ 1119 | public function getUserByKey($id, $key, array $fields = [ ]) { 1120 | $data = [ 1121 | 'id' => $id, 1122 | 'key' => $key, 1123 | 'fields' => $fields 1124 | ]; 1125 | return $this->apiGet('user', $data); 1126 | } 1127 | 1128 | /** 1129 | * 1130 | * Set Horizon cookie 1131 | * 1132 | * @param string $email horizon user email 1133 | * @param string $domain 1134 | * @param integer $duration 1135 | * @param boolean $secure 1136 | * @return boolean 1137 | */ 1138 | public function setHorizonCookie($email, $domain = null, $duration = null, $secure = false) { 1139 | $data = $this->getUserByKey($email, 'email', [ 'keys' => 1 ]); 1140 | if (!isset($data['keys']['cookie'])) { 1141 | return false; 1142 | } 1143 | if (!$domain) { 1144 | $domain_parts = explode('.', $_SERVER['HTTP_HOST']); 1145 | $domain = $domain_parts[sizeof($domain_parts) - 2] . '.' . $domain_parts[sizeof($domain_parts) - 1]; 1146 | } 1147 | if ($duration === null) { 1148 | $expire = time() + 31556926; 1149 | } else if ($duration) { 1150 | $expire = time() + $duration; 1151 | } else { 1152 | $expire = 0; 1153 | } 1154 | return setcookie('sailthru_hid', $data['keys']['cookie'], $expire, '/', $domain, $secure); 1155 | } 1156 | 1157 | /** 1158 | * Get an HTML preview of a template. 1159 | * @param $template 1160 | * @param $email 1161 | * @return array 1162 | * @link http://docs.sailthru.com/api/preview 1163 | */ 1164 | public function previewTemplateWithHTML($template, $email) { 1165 | $data = [ ]; 1166 | $data['template'] = $template; 1167 | $data['email'] = $email; 1168 | 1169 | $result = $this->apiPost('preview', $data); 1170 | return $result; 1171 | } 1172 | 1173 | /** 1174 | * Get an HTML preview of a blast. 1175 | * @param $blast_id 1176 | * @param $email 1177 | * @return array 1178 | * @link http://docs.sailthru.com/api/preview 1179 | */ 1180 | public function previewBlastWithHTML($blast_id, $email) { 1181 | $data = [ ]; 1182 | $data['blast_id'] = $blast_id; 1183 | $data['email'] = $email; 1184 | 1185 | $result = $this->apiPost('preview', $data); 1186 | return $result; 1187 | } 1188 | 1189 | /** 1190 | * Get an HTML preview of a recurring blast. 1191 | * @param $blast_repeat_id 1192 | * @param $email 1193 | * @return array 1194 | * @link http://docs.sailthru.com/api/preview 1195 | */ 1196 | public function previewRecurringBlastWithHTML($blast_repeat_id, $email) { 1197 | $data = [ ]; 1198 | $data['blast_repeat_id'] = $blast_repeat_id; 1199 | $data['email'] = $email; 1200 | 1201 | $result = $this->apiPost('preview', $data); 1202 | } 1203 | 1204 | /** 1205 | * Get an HTML preview of content_html. 1206 | * @param $content_html 1207 | * @param $email 1208 | * @return array 1209 | * @link http://docs.sailthru.com/api/preview 1210 | */ 1211 | public function previewContentWithHTML($content_html, $email) { 1212 | $data = [ ]; 1213 | $data['content_html'] = $content_html; 1214 | $data['email'] = $email; 1215 | 1216 | $result = $this->apiPost('preview', $data); 1217 | return $result; 1218 | } 1219 | 1220 | /** 1221 | * Get an email preview of a template. 1222 | * @param $template 1223 | * @param $send_email 1224 | * @return array 1225 | * @link http://docs.sailthru.com/api/preview 1226 | */ 1227 | public function previewTemplateWithEmail($template, $send_email) { 1228 | $data = [ ]; 1229 | $data['template'] = $template; 1230 | $data['send_email'] = $send_email; 1231 | 1232 | $result = $this->apiPost('preview', $data); 1233 | return $result; 1234 | } 1235 | 1236 | /** 1237 | * Get an email preview of a blast. 1238 | * @param $blast_id 1239 | * @param $send_email 1240 | * @return array 1241 | * @link http://docs.sailthru.com/api/preview 1242 | */ 1243 | public function previewBlastWithEmail($blast_id, $send_email) { 1244 | $data = [ ]; 1245 | $data['blast_id'] = $blast_id; 1246 | $data['send_email'] = $send_email; 1247 | 1248 | $result = $this->apiPost('preview', $data); 1249 | return $result; 1250 | } 1251 | 1252 | /** 1253 | * Get an email preview of a recurring blast. 1254 | * @param $blast_repeat_id 1255 | * @param $send_email 1256 | * @return array 1257 | * @link http://docs.sailthru.com/api/preview 1258 | */ 1259 | public function previewRecurringBlastWithEmail($blast_repeat_id, $send_email) { 1260 | $data = [ ]; 1261 | $data['blast_repeat_id'] = $blast_repeat_id; 1262 | $data['send_email'] = $send_email; 1263 | 1264 | $result = $this->apiPost('preview', $data); 1265 | return $result; 1266 | } 1267 | 1268 | /** 1269 | * Get an email preview of content_html. 1270 | * @param $content_html 1271 | * @param $send_email 1272 | * @return array 1273 | * @link http://docs.sailthru.com/api/preview 1274 | */ 1275 | public function previewContentWithEmail($content_html, $send_email) { 1276 | $data = [ ]; 1277 | $data['content_html'] = $content_html; 1278 | $data['send_email'] = $send_email; 1279 | 1280 | $result = $this->apiPost('preview', $data); 1281 | return $result; 1282 | } 1283 | 1284 | /** 1285 | * Get Triggers 1286 | * @return array 1287 | * @link http://docs.sailthru.com/api/trigger 1288 | */ 1289 | public function getTriggers() { 1290 | $result = $this->apiGet('trigger'); 1291 | return $result; 1292 | } 1293 | 1294 | /** 1295 | * Get information on a trigger 1296 | * @param string $template 1297 | * @param string $trigger_id 1298 | * @return array 1299 | * @link http://docs.sailthru.com/api/trigger 1300 | */ 1301 | public function getTriggerByTemplate($template, $trigger_id = null) { 1302 | $data = [ ]; 1303 | $data['template'] = $template; 1304 | if (!is_null($trigger_id)) { 1305 | $data['trigger_id'] = $trigger_id; 1306 | } 1307 | 1308 | $result = $this->apiGet('trigger', $data); 1309 | return $result; 1310 | } 1311 | 1312 | /** 1313 | * Get information on a trigger 1314 | * @param string $event 1315 | * @return array 1316 | * @link http://docs.sailthru.com/api/trigger 1317 | */ 1318 | public function getTriggerByEvent($event) { 1319 | $data = [ ]; 1320 | $data['event'] = $event; 1321 | 1322 | $result = $this->apiGet('trigger', $data); 1323 | return $result; 1324 | } 1325 | 1326 | /** 1327 | * Get information on a trigger 1328 | * @param string $trigger_id 1329 | * @return array 1330 | * @link http://docs.sailthru.com/api/trigger 1331 | */ 1332 | public function getTriggerById($trigger_id) { 1333 | $data = [ ]; 1334 | $data['trigger_id'] = $trigger_id; 1335 | 1336 | $result = $this->apiGet('trigger', $data); 1337 | return $result; 1338 | } 1339 | 1340 | /** 1341 | * Create a trigger for templates 1342 | * @param string $template 1343 | * @param integer $time 1344 | * @param string $time_unit 1345 | * @param string $event 1346 | * @param string $zephyr 1347 | * @return array 1348 | * @link http://docs.sailthru.com/api/trigger 1349 | */ 1350 | public function postTrigger($template, $time, $time_unit, $event, $zephyr) { 1351 | $data = [ ]; 1352 | $data['template'] = $template; 1353 | $data['time'] = $time; 1354 | $data['time_unit'] = $time_unit; 1355 | $data['event'] = $event; 1356 | $data['zephyr'] = $zephyr; 1357 | 1358 | $result = $this->apiPost('trigger', $data); 1359 | return $result; 1360 | } 1361 | 1362 | /** 1363 | * Create a trigger for events 1364 | * @param integer $time 1365 | * @param string $time_unit 1366 | * @param string $event 1367 | * @param string $zephyr 1368 | * @return array 1369 | * @link http://docs.sailthru.com/api/trigger 1370 | */ 1371 | public function postEventTrigger($event, $time, $time_unit, $zephyr) { 1372 | $data = [ ]; 1373 | $data['time'] = $time; 1374 | $data['time_unit'] = $time_unit; 1375 | $data['event'] = $event; 1376 | $data['zephyr'] = $zephyr; 1377 | 1378 | $result = $this->apiPost('trigger', $data); 1379 | return $result; 1380 | } 1381 | 1382 | /** 1383 | * Notify Sailthru of an event 1384 | * @param string $id 1385 | * @param string $event 1386 | * @param array $options 1387 | * @return array 1388 | * @link http://docs.sailthru.com/api/event 1389 | */ 1390 | public function postEvent($id, $event, $options = [ ]) { 1391 | $data = $options; 1392 | $data['id'] = $id; 1393 | $data['event'] = $event; 1394 | 1395 | $result = $this->apiPost('event', $data); 1396 | return $result; 1397 | } 1398 | 1399 | /** 1400 | * Perform an HTTP request using the curl extension 1401 | * 1402 | * @param string $action 1403 | * @param array $data 1404 | * @param string $method 1405 | * @param array $options 1406 | * @return string 1407 | * @throws Sailthru_Client_Exception 1408 | */ 1409 | protected function httpRequestCurl($action, array $data, $method = 'POST', $options = [ ]) { 1410 | $url = $this->api_uri . "/" . $action; 1411 | $ch = curl_init(); 1412 | $options = array_merge($this->options, $options); 1413 | if ($method == 'POST') { 1414 | curl_setopt($ch, CURLOPT_POST, true); 1415 | if ($this->fileUpload === true) { 1416 | curl_setopt($ch, CURLOPT_POSTFIELDS, $data); 1417 | $this->fileUpload = false; 1418 | } else { 1419 | curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data, '', '&')); 1420 | } 1421 | } else { 1422 | $url .= '?' . http_build_query($data, '', '&'); 1423 | if ($method != 'GET') { 1424 | curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method); 1425 | } 1426 | } 1427 | curl_setopt($ch, CURLOPT_URL, $url); 1428 | curl_setopt($ch, CURLOPT_HEADER, true); 1429 | curl_setopt($ch, CURLOPT_ENCODING, 'gzip'); 1430 | curl_setopt($ch, CURLOPT_TIMEOUT_MS, $options['timeout']); 1431 | curl_setopt($ch, CURLOPT_CONNECTTIMEOUT_MS, $options['connect_timeout']); 1432 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 1433 | 1434 | curl_setopt($ch, CURLOPT_HTTPHEADER, $this->httpHeaders); 1435 | $response = curl_exec($ch); 1436 | $this->lastResponseInfo = curl_getinfo($ch); 1437 | curl_close($ch); 1438 | 1439 | if (!$response) { 1440 | throw new Sailthru_Client_Exception( 1441 | "Bad response received from $url", 1442 | Sailthru_Client_Exception::CODE_RESPONSE_EMPTY 1443 | ); 1444 | } 1445 | 1446 | // parse headers and body 1447 | $parts = explode("\r\n\r\nHTTP/", $response); 1448 | $parts = (count($parts) > 1 ? 'HTTP/' : '') . array_pop($parts); // deal with HTTP/1.1 100 Continue before other headers 1449 | list($headers, $body) = explode("\r\n\r\n", $parts, 2); 1450 | $this->lastRateLimitInfo[$action][$method] = self::parseRateLimitHeaders($headers); 1451 | 1452 | return $body; 1453 | } 1454 | 1455 | /** 1456 | * Adapted from: http://netevil.org/blog/2006/nov/http-post-from-php-without-curl 1457 | * 1458 | * @param string $action 1459 | * @param array $data 1460 | * @param string $method 1461 | * @param array $options 1462 | * @return string 1463 | * @throws Sailthru_Client_Exception 1464 | */ 1465 | protected function httpRequestWithoutCurl($action, $data, $method = 'POST', $options = [ ]) { 1466 | if ($this->fileUpload === true) { 1467 | $this->fileUpload = false; 1468 | throw new Sailthru_Client_Exception( 1469 | 'cURL extension is required for the request with file upload', 1470 | Sailthru_Client_Exception::CODE_GENERAL 1471 | ); 1472 | } 1473 | 1474 | $url = $this->api_uri . "/" . $action; 1475 | $params = [ 'http' => [ 'method' => $method, 'ignore_errors' => true ] ]; 1476 | if ($method == 'POST') { 1477 | $params['http']['content'] = is_array($data) ? http_build_query($data, '', '&') : $data; 1478 | } else { 1479 | $url .= '?' . http_build_query($data, '', '&'); 1480 | } 1481 | $params['http']['header'] = "User-Agent: {$this->user_agent_string}\nContent-Type: application/x-www-form-urlencoded"; 1482 | $ctx = stream_context_create($params); 1483 | $fp = @fopen($url, 'rb', false, $ctx); 1484 | if (!$fp) { 1485 | throw new Sailthru_Client_Exception( 1486 | "Unable to open stream: $url", 1487 | Sailthru_Client_Exception::CODE_GENERAL 1488 | ); 1489 | } 1490 | $response = @stream_get_contents($fp); 1491 | if ($response === false) { 1492 | throw new Sailthru_Client_Exception( 1493 | "No response received from stream: $url", 1494 | Sailthru_Client_Exception::CODE_RESPONSE_EMPTY 1495 | ); 1496 | } 1497 | return $response; 1498 | } 1499 | 1500 | /** 1501 | * Perform an HTTP request, checking for curl extension support 1502 | * 1503 | * @param $action 1504 | * @param array $data 1505 | * @param string $method 1506 | * @param array $options 1507 | * @return string 1508 | * @throws Sailthru_Client_Exception 1509 | */ 1510 | protected function httpRequest($action, $data, $method = 'POST', $options = [ ]) { 1511 | $response = $this->{$this->http_request_type}($action, $data, $method, $options); 1512 | $json = json_decode($response, true); 1513 | if ($json === NULL) { 1514 | throw new Sailthru_Client_Exception( 1515 | "Response: {$response} is not a valid JSON", 1516 | Sailthru_Client_Exception::CODE_RESPONSE_INVALID 1517 | ); 1518 | } 1519 | if (!empty($json['error'])) { 1520 | throw new Sailthru_Client_Exception($json['errormsg'], $json['error']); 1521 | } 1522 | 1523 | return $json; 1524 | } 1525 | 1526 | /** 1527 | * Perform an API POST (or other) request, using the shared-secret auth hash. 1528 | * if binary_data_param is set, its appends '@' so that cURL can make binary POST request 1529 | * 1530 | * @param string $action 1531 | * @param array $data 1532 | * @param array $binary_data_param 1533 | * @param array $options 1534 | * @return array 1535 | */ 1536 | public function apiPost($action, $data, array $binary_data_param = [ ], $options = [ ]) { 1537 | $binary_data = [ ]; 1538 | if (!empty ($binary_data_param)) { 1539 | foreach ($binary_data_param as $param) { 1540 | if (isset($data[$param]) && file_exists($data[$param])) { 1541 | $binary_data[$param] = version_compare(PHP_VERSION, '5.5.0') >= 0 && class_exists('CURLFile') 1542 | ? new CURLFile($data[$param]) 1543 | : "@{$data[$param]}"; 1544 | unset($data[$param]); 1545 | $this->fileUpload = true; 1546 | } 1547 | } 1548 | } 1549 | $payload = $this->prepareJsonPayload($data, $binary_data); 1550 | return $this->httpRequest($action, $payload, 'POST', $options); 1551 | } 1552 | 1553 | /** 1554 | * Perform an API GET request, using the shared-secret auth hash. 1555 | * 1556 | * @param string $action 1557 | * @param array $data 1558 | * @return array 1559 | */ 1560 | public function apiGet($action, $data = [ ], $method = 'GET', $options = [ ]) { 1561 | return $this->httpRequest($action, $this->prepareJsonPayload($data), $method, $options); 1562 | } 1563 | 1564 | /** 1565 | * Perform an API DELETE request, using the shared-secret auth hash. 1566 | * 1567 | * @param string $action 1568 | * @param array $data 1569 | * @return array 1570 | */ 1571 | public function apiDelete($action, $data, $options = [ ]) { 1572 | return $this->apiGet($action, $data, 'DELETE', $options); 1573 | } 1574 | 1575 | /** 1576 | * get information from last server response when used with cURL 1577 | * returns associative array as per http://us.php.net/curl_getinfo 1578 | * @return array or null 1579 | */ 1580 | public function getLastResponseInfo() { 1581 | return $this->lastResponseInfo; 1582 | } 1583 | 1584 | /** 1585 | * Prepare JSON payload 1586 | */ 1587 | protected function prepareJsonPayload(array $data, array $binary_data = [ ]) { 1588 | $payload = [ 1589 | 'api_key' => $this->api_key, 1590 | 'format' => 'json', 1591 | 'json' => json_encode($data) 1592 | ]; 1593 | $payload['sig'] = Sailthru_Util::getSignatureHash($payload, $this->secret); 1594 | if (!empty($binary_data)) { 1595 | $payload = array_merge($payload, $binary_data); 1596 | } 1597 | return $payload; 1598 | } 1599 | 1600 | /** 1601 | * get the rate limit information for the very last call with given action and method 1602 | * @param string $action 1603 | * @param string $method GET, POST or DELETE 1604 | * @return array or null 1605 | */ 1606 | public function getLastRateLimitInfo($action, $method) { 1607 | $rate_limit_info = $this->lastRateLimitInfo; 1608 | $method = strtoupper($method); 1609 | return (isset($rate_limit_info[$action]) && isset($rate_limit_info[$action][$method])) ? 1610 | $rate_limit_info[$action][$method] : null; 1611 | } 1612 | 1613 | /** 1614 | * parse rate limit headers from http response 1615 | * @param string $headers 1616 | * @return array|null 1617 | */ 1618 | private function parseRateLimitHeaders($headers) { 1619 | if ($headers === null) { 1620 | return null; 1621 | } 1622 | 1623 | $header_lines = explode("\n", $headers); 1624 | $rate_limit_headers = [ ]; 1625 | foreach ($header_lines as $hl) { 1626 | if (strpos($hl, "X-Rate-Limit-Limit") !== FALSE && !isset($rate_limit_headers['limit'])) { 1627 | list($header_name, $header_value) = explode(":", $hl, 2); 1628 | $rate_limit_headers['limit'] = intval($header_value); 1629 | } else if (strpos($hl, "X-Rate-Limit-Remaining") !== FALSE && !isset($rate_limit_headers['remaining'])) { 1630 | list($header_name, $header_value) = explode(":", $hl, 2); 1631 | $rate_limit_headers['remaining'] = intval($header_value); 1632 | } else if (strpos($hl, "X-Rate-Limit-Reset") !== FALSE && !isset($rate_limit_headers['reset'])) { 1633 | list($header_name, $header_value) = explode(":", $hl, 2); 1634 | $rate_limit_headers['reset'] = intval($header_value); 1635 | } 1636 | 1637 | if (count($rate_limit_headers) === 3) { 1638 | return $rate_limit_headers; 1639 | } 1640 | } 1641 | 1642 | return null; 1643 | } 1644 | } 1645 | --------------------------------------------------------------------------------