Completed
Push — master ( 5b1091...4d0c85 )
by Drew
01:43
created

MailChimp::getApiEndpoint()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
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 <[email protected]>
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
     * @throws \Exception
33
     */
34
    public function __construct($api_key)
35
    {
36
        $this->api_key = $api_key;
37
38
        if (strpos($this->api_key, '-') === false) {
39
            throw new \Exception("Invalid MailChimp API key `{$api_key}` supplied.");
40
        }
41
42
        list(, $data_center) = explode('-', $this->api_key);
43
        $this->api_endpoint  = str_replace('<dc>', $data_center, $this->api_endpoint);
44
45
        $this->last_response = array('headers' => null, 'body' => null);
46
    }
47
48
    /**
49
     * Create a new instance of a Batch request. Optionally with the ID of an existing batch.
50
     * @param string $batch_id Optional ID of an existing batch, if you need to check its status for example.
51
     * @return Batch            New Batch object.
52
     */
53
    public function new_batch($batch_id = null)
54
    {
55
        return new Batch($this, $batch_id);
56
    }
57
58
    /**
59
     * Convert an email address into a 'subscriber hash' for identifying the subscriber in a method URL
60
     * @param   string $email The subscriber's email address
61
     * @return  string          Hashed version of the input
62
     */
63
    public function subscriberHash($email)
64
    {
65
        return md5(strtolower($email));
66
    }
67
68
    /**
69
     * Was the last request successful?
70
     * @return bool  True for success, false for failure
71
     */
72
    public function success()
73
    {
74
        return $this->request_successful;
75
    }
76
77
    /**
78
     * Get the last error returned by either the network transport, or by the API.
79
     * If something didn't work, this should contain the string describing the problem.
80
     * @return  array|false  describing the error
81
     */
82
    public function getLastError()
83
    {
84
        return $this->last_error ?: false;
85
    }
86
87
    /**
88
     * Get an array containing the HTTP headers and the body of the API response.
89
     * @return array  Assoc array with keys 'headers' and 'body'
90
     */
91
    public function getLastResponse()
92
    {
93
        return $this->last_response;
94
    }
95
96
    /**
97
     * Get an array containing the HTTP headers and the body of the API request.
98
     * @return array  Assoc array
99
     */
100
    public function getLastRequest()
101
    {
102
        return $this->last_request;
103
    }
104
105
    /**
106
     * Make an HTTP DELETE request - for deleting data
107
     * @param   string $method URL of the API request method
108
     * @param   array $args Assoc array of arguments (if any)
109
     * @param   int $timeout Timeout limit for request in seconds
110
     * @return  array|false   Assoc array of API response, decoded from JSON
111
     */
112
    public function delete($method, $args = array(), $timeout = 10)
113
    {
114
        return $this->makeRequest('delete', $method, $args, $timeout);
115
    }
116
117
    /**
118
     * Make an HTTP GET request - for retrieving data
119
     * @param   string $method URL of the API request method
120
     * @param   array $args Assoc array of arguments (usually your data)
121
     * @param   int $timeout Timeout limit for request in seconds
122
     * @return  array|false   Assoc array of API response, decoded from JSON
123
     */
124
    public function get($method, $args = array(), $timeout = 10)
125
    {
126
        return $this->makeRequest('get', $method, $args, $timeout);
127
    }
128
129
    /**
130
     * Make an HTTP PATCH request - for performing partial updates
131
     * @param   string $method URL of the API request method
132
     * @param   array $args Assoc array of arguments (usually your data)
133
     * @param   int $timeout Timeout limit for request in seconds
134
     * @return  array|false   Assoc array of API response, decoded from JSON
135
     */
136
    public function patch($method, $args = array(), $timeout = 10)
137
    {
138
        return $this->makeRequest('patch', $method, $args, $timeout);
139
    }
140
141
    /**
142
     * Make an HTTP POST request - for creating and updating items
143
     * @param   string $method URL of the API request method
144
     * @param   array $args Assoc array of arguments (usually your data)
145
     * @param   int $timeout Timeout limit for request in seconds
146
     * @return  array|false   Assoc array of API response, decoded from JSON
147
     */
148
    public function post($method, $args = array(), $timeout = 10)
149
    {
150
        return $this->makeRequest('post', $method, $args, $timeout);
151
    }
152
153
    /**
154
     * Make an HTTP PUT request - for creating new items
155
     * @param   string $method URL of the API request method
156
     * @param   array $args Assoc array of arguments (usually your data)
157
     * @param   int $timeout Timeout limit for request in seconds
158
     * @return  array|false   Assoc array of API response, decoded from JSON
159
     */
160
    public function put($method, $args = array(), $timeout = 10)
161
    {
162
        return $this->makeRequest('put', $method, $args, $timeout);
163
    }
164
165
    /**
166
     * Performs the underlying HTTP request. Not very exciting.
167
     * @param  string $http_verb The HTTP verb to use: get, post, put, patch, delete
168
     * @param  string $method The API method to be called
169
     * @param  array $args Assoc array of parameters to be passed
170
     * @param int $timeout
171
     * @return array|false Assoc array of decoded result
172
     * @throws \Exception
173
     */
174
    private function makeRequest($http_verb, $method, $args = array(), $timeout = 10)
175
    {
176
        if (!function_exists('curl_init') || !function_exists('curl_setopt')) {
177
            throw new \Exception("cURL support is required, but can't be found.");
178
        }
179
180
        $url = $this->api_endpoint . '/' . $method;
181
182
        $this->last_error         = '';
183
        $this->request_successful = false;
184
        $response                 = array('headers' => null, 'body' => null);
185
        $this->last_response      = $response;
186
187
        $this->last_request = array(
188
            'method'  => $http_verb,
189
            'path'    => $method,
190
            'url'     => $url,
191
            'body'    => '',
192
            'timeout' => $timeout,
193
        );
194
195
        $ch = curl_init();
196
        curl_setopt($ch, CURLOPT_URL, $url);
197
        curl_setopt($ch, CURLOPT_HTTPHEADER, array(
198
            'Accept: application/vnd.api+json',
199
            'Content-Type: application/vnd.api+json',
200
            'Authorization: apikey ' . $this->api_key
201
        ));
202
        curl_setopt($ch, CURLOPT_USERAGENT, 'DrewM/MailChimp-API/3.0 (github.com/drewm/mailchimp-api)');
203
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
204
        curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
205
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, $this->verify_ssl);
206
        curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
207
        curl_setopt($ch, CURLOPT_ENCODING, '');
208
        curl_setopt($ch, CURLINFO_HEADER_OUT, true);
209
210
        switch ($http_verb) {
211
            case 'post':
212
                curl_setopt($ch, CURLOPT_POST, true);
213
                $this->attachRequestPayload($ch, $args);
214
                break;
215
216
            case 'get':
217
                $query = http_build_query($args, '', '&');
218
                curl_setopt($ch, CURLOPT_URL, $url . '?' . $query);
219
                break;
220
221
            case 'delete':
222
                curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
223
                break;
224
225
            case 'patch':
226
                curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PATCH');
227
                $this->attachRequestPayload($ch, $args);
228
                break;
229
230
            case 'put':
231
                curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
232
                $this->attachRequestPayload($ch, $args);
233
                break;
234
        }
235
236
        $response['body']    = curl_exec($ch);
237
        $response['headers'] = curl_getinfo($ch);
238
239
        if (isset($response['headers']['request_header'])) {
240
            $this->last_request['headers'] = $response['headers']['request_header'];
241
        }
242
243
        if ($response['body'] === false) {
244
            $this->last_error = curl_error($ch);
245
        }
246
247
        curl_close($ch);
248
249
        $formattedResponse = $this->formatResponse($response);
250
251
        $this->determineSuccess($response, $formattedResponse);
252
253
        return $formattedResponse;
254
    }
255
    
256
    /**
257
     * @return string The url to the API endpoint
258
     */
259
    public function getApiEndpoint()
260
    {
261
        return $this->api_endpoint;
262
    }
263
264
    /**
265
     * Encode the data and attach it to the request
266
     * @param   resource $ch cURL session handle, used by reference
267
     * @param   array $data Assoc array of data to attach
268
     */
269
    private function attachRequestPayload(&$ch, $data)
270
    {
271
        $encoded = json_encode($data);
272
        $this->last_request['body'] = $encoded;
273
        curl_setopt($ch, CURLOPT_POSTFIELDS, $encoded);
274
    }
275
276
    /**
277
     * Decode the response and format any error messages for debugging
278
     * @param array $response The response from the curl request
279
     * @return array|false    The JSON decoded into an array
280
     */
281
    private function formatResponse($response)
282
    {
283
        $this->last_response = $response;
284
285
        if (!empty($response['body'])) {
286
            return json_decode($response['body'], true);
287
        }
288
289
        return false;
290
    }
291
292
    /**
293
     * Check if the response was successful or a failure. If it failed, store the error.
294
     * @param array $response The response from the curl request
295
     * @param array|false $formattedResponse The response body payload from the curl request
296
     * @return bool     If the request was successful
297
     */
298
    private function determineSuccess($response, $formattedResponse)
299
    {
300
        $status = $this->findHTTPStatus($response, $formattedResponse);
301
302
        if ($status >= 200 && $status <= 299) {
303
            $this->request_successful = true;
304
            return true;
305
        }
306
307
        if (isset($formattedResponse['detail'])) {
308
            $this->last_error = sprintf('%d: %s', $formattedResponse['status'], $formattedResponse['detail']);
309
            return false;
310
        }
311
312
        $this->last_error = 'Unknown error, call getLastResponse() to find out what happened.';
313
        return false;
314
    }
315
316
    /**
317
     * Find the HTTP status code from the headers or API response body
318
     * @param array $response The response from the curl request
319
     * @param array|false $formattedResponse The response body payload from the curl request
320
     * @return int  HTTP status code
321
     */
322
    private function findHTTPStatus($response, $formattedResponse)
323
    {
324
        if (!empty($response['headers']) && isset($response['headers']['http_code'])) {
325
            return (int) $response['headers']['http_code'];
326
        }
327
328
        if (!empty($response['body']) && isset($formattedResponse['status'])) {
329
            return (int) $formattedResponse['status'];
330
        }
331
332
        return 418;
333
    }
334
}
335