PluginProbe
Getwid – Gutenberg Blocks / 2.1.0
Getwid – Gutenberg Blocks v2.1.0
3.0.2 3.0.1 3.0.0 2.1.0 2.1.1 2.1.2 2.1.3 2.2.0 trunk 1.8.7 1.9.1 2.0.10 2.0.11 2.0.12 2.0.13 2.0.14 2.0.5 2.0.6 2.0.7 2.0.8 2.0.9
getwid / includes / libraries / mailchimp-api / src / MailChimp.php

MailChimp.php in Getwid – Gutenberg Blocks 2.1.0, at includes/libraries/mailchimp-api/src/MailChimp.php

493 lines 15.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace DrewM\MailChimp;
4
5 /**
6 * Super-simple, minimum abstraction MailChimp API v3 wrapper
7 * MailChimp API v3: http://developer.mailchimp.com
8 * This wrapper: https://github.com/drewm/mailchimp-api
9 *
10 * @author Drew McLellan <drew.mclellan@gmail.com>
11 * @version 2.5
12 */
13 class MailChimp
14 {
15 private $api_key;
16 private $api_endpoint = 'https://<dc>.api.mailchimp.com/3.0';
17
18 const TIMEOUT = 10;
19
20 /* SSL Verification
21 Read before disabling:
22 http://snippets.webaware.com.au/howto/stop-turning-off-curlopt_ssl_verifypeer-and-fix-your-php-config/
23 */
24 public $verify_ssl = true;
25
26 private $request_successful = false;
27 private $last_error = '';
28 private $last_response = array();
29 private $last_request = array();
30
31 /**
32 * Create a new instance
33 *
34 * @param string $api_key Your MailChimp API key
35 * @param string $api_endpoint Optional custom API endpoint
36 *
37 * @throws \Exception
38 */
39 public function __construct($api_key, $api_endpoint = null)
40 {
41 if (!function_exists('curl_init') || !function_exists('curl_setopt')) {
42 throw new \Exception("cURL support is required, but can't be found.");
43 }
44
45 $this->api_key = $api_key;
46
47 if ($api_endpoint === null) {
48 if (strpos($this->api_key, '-') === false) {
49 throw new \Exception("Invalid MailChimp API key supplied.");
50 }
51 list(, $data_center) = explode('-', $this->api_key);
52 $this->api_endpoint = str_replace('<dc>', $data_center, $this->api_endpoint);
53 } else {
54 $this->api_endpoint = $api_endpoint;
55 }
56
57 $this->last_response = array('headers' => null, 'body' => null);
58 }
59
60 /**
61 * Create a new instance of a Batch request. Optionally with the ID of an existing batch.
62 *
63 * @param string $batch_id Optional ID of an existing batch, if you need to check its status for example.
64 *
65 * @return Batch New Batch object.
66 */
67 public function new_batch($batch_id = null)
68 {
69 return new Batch($this, $batch_id);
70 }
71
72 /**
73 * @return string The url to the API endpoint
74 */
75 public function getApiEndpoint()
76 {
77 return $this->api_endpoint;
78 }
79
80
81 /**
82 * Convert an email address into a 'subscriber hash' for identifying the subscriber in a method URL
83 *
84 * @param string $email The subscriber's email address
85 *
86 * @return string Hashed version of the input
87 */
88 public static function subscriberHash($email)
89 {
90 return md5(strtolower($email));
91 }
92
93 /**
94 * Was the last request successful?
95 *
96 * @return bool True for success, false for failure
97 */
98 public function success()
99 {
100 return $this->request_successful;
101 }
102
103 /**
104 * Get the last error returned by either the network transport, or by the API.
105 * If something didn't work, this should contain the string describing the problem.
106 *
107 * @return string|false describing the error
108 */
109 public function getLastError()
110 {
111 return $this->last_error ?: false;
112 }
113
114 /**
115 * Get an array containing the HTTP headers and the body of the API response.
116 *
117 * @return array Assoc array with keys 'headers' and 'body'
118 */
119 public function getLastResponse()
120 {
121 return $this->last_response;
122 }
123
124 /**
125 * Get an array containing the HTTP headers and the body of the API request.
126 *
127 * @return array Assoc array
128 */
129 public function getLastRequest()
130 {
131 return $this->last_request;
132 }
133
134 /**
135 * Make an HTTP DELETE request - for deleting data
136 *
137 * @param string $method URL of the API request method
138 * @param array $args Assoc array of arguments (if any)
139 * @param int $timeout Timeout limit for request in seconds
140 *
141 * @return array|false Assoc array of API response, decoded from JSON
142 */
143 public function delete($method, $args = array(), $timeout = self::TIMEOUT)
144 {
145 return $this->makeRequest('delete', $method, $args, $timeout);
146 }
147
148 /**
149 * Make an HTTP GET request - for retrieving data
150 *
151 * @param string $method URL of the API request method
152 * @param array $args Assoc array of arguments (usually your data)
153 * @param int $timeout Timeout limit for request in seconds
154 *
155 * @return array|false Assoc array of API response, decoded from JSON
156 */
157 public function get($method, $args = array(), $timeout = self::TIMEOUT)
158 {
159 return $this->makeRequest('get', $method, $args, $timeout);
160 }
161
162 /**
163 * Make an HTTP PATCH request - for performing partial updates
164 *
165 * @param string $method URL of the API request method
166 * @param array $args Assoc array of arguments (usually your data)
167 * @param int $timeout Timeout limit for request in seconds
168 *
169 * @return array|false Assoc array of API response, decoded from JSON
170 */
171 public function patch($method, $args = array(), $timeout = self::TIMEOUT)
172 {
173 return $this->makeRequest('patch', $method, $args, $timeout);
174 }
175
176 /**
177 * Make an HTTP POST request - for creating and updating items
178 *
179 * @param string $method URL of the API request method
180 * @param array $args Assoc array of arguments (usually your data)
181 * @param int $timeout Timeout limit for request in seconds
182 *
183 * @return array|false Assoc array of API response, decoded from JSON
184 */
185 public function post($method, $args = array(), $timeout = self::TIMEOUT)
186 {
187 return $this->makeRequest('post', $method, $args, $timeout);
188 }
189
190 /**
191 * Make an HTTP PUT request - for creating new items
192 *
193 * @param string $method URL of the API request method
194 * @param array $args Assoc array of arguments (usually your data)
195 * @param int $timeout Timeout limit for request in seconds
196 *
197 * @return array|false Assoc array of API response, decoded from JSON
198 */
199 public function put($method, $args = array(), $timeout = self::TIMEOUT)
200 {
201 return $this->makeRequest('put', $method, $args, $timeout);
202 }
203
204 /**
205 * Performs the underlying HTTP request. Not very exciting.
206 *
207 * @param string $http_verb The HTTP verb to use: get, post, put, patch, delete
208 * @param string $method The API method to be called
209 * @param array $args Assoc array of parameters to be passed
210 * @param int $timeout
211 *
212 * @return array|false Assoc array of decoded result
213 */
214 private function makeRequest($http_verb, $method, $args = array(), $timeout = self::TIMEOUT)
215 {
216 $url = $this->api_endpoint . '/' . $method;
217
218 $response = $this->prepareStateForRequest($http_verb, $method, $url, $timeout);
219
220 $httpHeader = array(
221 'Accept: application/vnd.api+json',
222 'Content-Type: application/vnd.api+json',
223 'Authorization: apikey ' . $this->api_key
224 );
225
226 if (isset($args["language"])) {
227 $httpHeader[] = "Accept-Language: " . $args["language"];
228 }
229
230 if ($http_verb === 'put') {
231 $httpHeader[] = 'Allow: PUT, PATCH, POST';
232 }
233
234 $ch = curl_init();
235 curl_setopt($ch, CURLOPT_URL, $url);
236 curl_setopt($ch, CURLOPT_HTTPHEADER, $httpHeader);
237 curl_setopt($ch, CURLOPT_USERAGENT, 'DrewM/MailChimp-API/3.0 (github.com/drewm/mailchimp-api)');
238 curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
239 curl_setopt($ch, CURLOPT_VERBOSE, true);
240 curl_setopt($ch, CURLOPT_HEADER, true);
241 curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
242 curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, $this->verify_ssl);
243 curl_setopt($ch, CURLOPT_ENCODING, '');
244 curl_setopt($ch, CURLINFO_HEADER_OUT, true);
245
246 switch ($http_verb) {
247 case 'post':
248 curl_setopt($ch, CURLOPT_POST, true);
249 $this->attachRequestPayload($ch, $args);
250 break;
251
252 case 'get':
253 $query = http_build_query($args, '', '&');
254 curl_setopt($ch, CURLOPT_URL, $url . '?' . $query);
255 break;
256
257 case 'delete':
258 curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
259 break;
260
261 case 'patch':
262 curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PATCH');
263 $this->attachRequestPayload($ch, $args);
264 break;
265
266 case 'put':
267 curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
268 $this->attachRequestPayload($ch, $args);
269 break;
270 }
271
272 $responseContent = curl_exec($ch);
273 $response['headers'] = curl_getinfo($ch);
274 $response = $this->setResponseState($response, $responseContent, $ch);
275 $formattedResponse = $this->formatResponse($response);
276
277 curl_close($ch);
278
279 $isSuccess = $this->determineSuccess($response, $formattedResponse, $timeout);
280
281 return is_array($formattedResponse) ? $formattedResponse : $isSuccess;
282 }
283
284 /**
285 * @param string $http_verb
286 * @param string $method
287 * @param string $url
288 * @param integer $timeout
289 *
290 * @return array
291 */
292 private function prepareStateForRequest($http_verb, $method, $url, $timeout)
293 {
294 $this->last_error = '';
295
296 $this->request_successful = false;
297
298 $this->last_response = array(
299 'headers' => null, // array of details from curl_getinfo()
300 'httpHeaders' => null, // array of HTTP headers
301 'body' => null // content of the response
302 );
303
304 $this->last_request = array(
305 'method' => $http_verb,
306 'path' => $method,
307 'url' => $url,
308 'body' => '',
309 'timeout' => $timeout,
310 );
311
312 return $this->last_response;
313 }
314
315 /**
316 * Get the HTTP headers as an array of header-name => header-value pairs.
317 *
318 * The "Link" header is parsed into an associative array based on the
319 * rel names it contains. The original value is available under
320 * the "_raw" key.
321 *
322 * @param string $headersAsString
323 *
324 * @return array
325 */
326 private function getHeadersAsArray($headersAsString)
327 {
328 $headers = array();
329
330 foreach (explode("\r\n", $headersAsString) as $i => $line) {
331 if (preg_match('/HTTP\/[1-2]/', substr($line, 0, 7)) === 1) { // http code
332 continue;
333 }
334
335 $line = trim($line);
336 if (empty($line)) {
337 continue;
338 }
339
340 list($key, $value) = explode(': ', $line);
341
342 if ($key == 'Link') {
343 $value = array_merge(
344 array('_raw' => $value),
345 $this->getLinkHeaderAsArray($value)
346 );
347 }
348
349 $headers[$key] = $value;
350 }
351
352 return $headers;
353 }
354
355 /**
356 * Extract all rel => URL pairs from the provided Link header value
357 *
358 * Mailchimp only implements the URI reference and relation type from
359 * RFC 5988, so the value of the header is something like this:
360 *
361 * 'https://us13.api.mailchimp.com/schema/3.0/Lists/Instance.json; rel="describedBy",
362 * <https://us13.admin.mailchimp.com/lists/members/?id=XXXX>; rel="dashboard"'
363 *
364 * @param string $linkHeaderAsString
365 *
366 * @return array
367 */
368 private function getLinkHeaderAsArray($linkHeaderAsString)
369 {
370 $urls = array();
371
372 if (preg_match_all('/<(.*?)>\s*;\s*rel="(.*?)"\s*/', $linkHeaderAsString, $matches)) {
373 foreach ($matches[2] as $i => $relName) {
374 $urls[$relName] = $matches[1][$i];
375 }
376 }
377
378 return $urls;
379 }
380
381 /**
382 * Encode the data and attach it to the request
383 *
384 * @param resource $ch cURL session handle, used by reference
385 * @param array $data Assoc array of data to attach
386 */
387 private function attachRequestPayload(&$ch, $data)
388 {
389 $encoded = json_encode($data);
390 $this->last_request['body'] = $encoded;
391 curl_setopt($ch, CURLOPT_POSTFIELDS, $encoded);
392 }
393
394 /**
395 * Decode the response and format any error messages for debugging
396 *
397 * @param array $response The response from the curl request
398 *
399 * @return array|false The JSON decoded into an array
400 */
401 private function formatResponse($response)
402 {
403 $this->last_response = $response;
404
405 if (!empty($response['body'])) {
406 return json_decode($response['body'], true);
407 }
408
409 return false;
410 }
411
412 /**
413 * Do post-request formatting and setting state from the response
414 *
415 * @param array $response The response from the curl request
416 * @param string $responseContent The body of the response from the curl request
417 * @param resource $ch The curl resource
418 *
419 * @return array The modified response
420 */
421 private function setResponseState($response, $responseContent, $ch)
422 {
423 if ($responseContent === false) {
424 $this->last_error = curl_error($ch);
425 } else {
426
427 $headerSize = $response['headers']['header_size'];
428
429 $response['httpHeaders'] = $this->getHeadersAsArray(substr($responseContent, 0, $headerSize));
430 $response['body'] = substr($responseContent, $headerSize);
431
432 if (isset($response['headers']['request_header'])) {
433 $this->last_request['headers'] = $response['headers']['request_header'];
434 }
435 }
436
437 return $response;
438 }
439
440 /**
441 * Check if the response was successful or a failure. If it failed, store the error.
442 *
443 * @param array $response The response from the curl request
444 * @param array|false $formattedResponse The response body payload from the curl request
445 * @param int $timeout The timeout supplied to the curl request.
446 *
447 * @return bool If the request was successful
448 */
449 private function determineSuccess($response, $formattedResponse, $timeout)
450 {
451 $status = $this->findHTTPStatus($response, $formattedResponse);
452
453 if ($status >= 200 && $status <= 299) {
454 $this->request_successful = true;
455 return true;
456 }
457
458 if (isset($formattedResponse['detail'])) {
459 $this->last_error = sprintf('%d: %s', $formattedResponse['status'], $formattedResponse['detail']);
460 return false;
461 }
462
463 if ($timeout > 0 && $response['headers'] && $response['headers']['total_time'] >= $timeout) {
464 $this->last_error = sprintf('Request timed out after %f seconds.', $response['headers']['total_time']);
465 return false;
466 }
467
468 $this->last_error = 'Unknown error, call getLastResponse() to find out what happened.';
469 return false;
470 }
471
472 /**
473 * Find the HTTP status code from the headers or API response body
474 *
475 * @param array $response The response from the curl request
476 * @param array|false $formattedResponse The response body payload from the curl request
477 *
478 * @return int HTTP status code
479 */
480 private function findHTTPStatus($response, $formattedResponse)
481 {
482 if (!empty($response['headers']) && isset($response['headers']['http_code'])) {
483 return (int)$response['headers']['http_code'];
484 }
485
486 if (!empty($response['body']) && isset($formattedResponse['status'])) {
487 return (int)$formattedResponse['status'];
488 }
489
490 return 418;
491 }
492 }
493