PluginProbe
FV Player 8 / trunk
FV Player 8 vtrunk
trunk 8.0.18 8.0.19 8.0.20 8.0.21 8.0.25 8.0.27 8.1 8.1.3
fv-player / includes / mailchimp-api / src / MailChimp.php

MailChimp.php in FV Player 8 trunk, at includes/mailchimp-api/src/MailChimp.php

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