PluginProbe
Enter Addons – Ultimate Template Builder for Elementor / 2.2.8
Enter Addons – Ultimate Template Builder for Elementor v2.2.8
trunk 2.2.10 2.2.2 2.2.3 2.2.4 2.2.5 2.2.6 2.2.7 2.2.8 2.2.9 2.3.0 2.3.1 2.3.2 2.3.3 2.3.4
enteraddons / classes / Mail_Chimp.php

Mail_Chimp.php in Enter Addons – Ultimate Template Builder for Elementor 2.2.8, at classes/Mail_Chimp.php

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