1 | <?php |
||
2 | namespace Sreedev\Slack; |
||
3 | |||
4 | use Exception; |
||
5 | |||
6 | class Slack |
||
7 | { |
||
8 | private $api_endpoint = "https://slack.com/api"; |
||
9 | private $request_successful = false; |
||
10 | private $last_error = ''; |
||
11 | private $api_token = ''; |
||
12 | private $last_response = array(); |
||
13 | private $last_request = array(); |
||
14 | private $verify_ssl = false; |
||
15 | |||
16 | const TIMEOUT = 10; |
||
17 | |||
18 | /** |
||
19 | * __construct |
||
20 | * |
||
21 | * @param mixed $api_token |
||
22 | * @return void |
||
23 | */ |
||
24 | public function __construct($api_token) |
||
25 | { |
||
26 | $this->api_token = $api_token; |
||
27 | } |
||
28 | |||
29 | |||
30 | /** |
||
31 | * Chat |
||
32 | * |
||
33 | * @return Chat class instance |
||
34 | */ |
||
35 | public function Chat($method, $data) |
||
36 | { |
||
37 | return new \Sreedev\Slack\Api\Chat($this, $method, $data); |
||
0 ignored issues
–
show
Bug
Best Practice
introduced
by
![]() |
|||
38 | } |
||
39 | |||
40 | /** |
||
41 | * Calls |
||
42 | * |
||
43 | * @param mixed $method |
||
44 | * @param mixed $data |
||
45 | * @return void |
||
46 | */ |
||
47 | public function Calls($method, $data) |
||
48 | { |
||
49 | return new \Sreedev\Slack\Api\Calls($this, $method, $data); |
||
0 ignored issues
–
show
|
|||
50 | } |
||
51 | |||
52 | /** |
||
53 | * Conversations |
||
54 | * |
||
55 | * @param mixed $method |
||
56 | * @param mixed $data |
||
57 | * @return void |
||
58 | */ |
||
59 | public function Conversations($method, $data) |
||
60 | { |
||
61 | return new \Sreedev\Slack\Api\Conversations($this, $method, $data); |
||
0 ignored issues
–
show
|
|||
62 | } |
||
63 | |||
64 | |||
65 | /** |
||
66 | * Performs the underlying HTTP request. Not very exciting. |
||
67 | * |
||
68 | * @param string $http_verb The HTTP verb to use: get, post, put, patch, delete |
||
69 | * @param string $method The API method to be called |
||
70 | * @param array $args Assoc array of parameters to be passed |
||
71 | * @param int $timeout |
||
72 | * |
||
73 | * @return array|false Assoc array of decoded result |
||
74 | */ |
||
75 | public function makeRequest($http_verb, $method, $args = array(), $timeout = self::TIMEOUT) |
||
76 | { |
||
77 | if( $this->api_token == '') |
||
78 | { |
||
79 | throw new Exception('\Exception'); |
||
80 | } |
||
81 | $url = $this->api_endpoint . '/' . $method; |
||
82 | |||
83 | $response = $this->prepareStateForRequest($http_verb, $method, $url, $timeout); |
||
84 | |||
85 | $httpHeader = array( |
||
86 | 'Content-type: application/json', |
||
87 | 'Authorization: Bearer '. $this->api_token |
||
88 | ); |
||
89 | |||
90 | |||
91 | if (isset($args["language"])) { |
||
92 | $httpHeader[] = "Accept-Language: " . $args["language"]; |
||
93 | } |
||
94 | |||
95 | if ($http_verb === 'put') { |
||
96 | $httpHeader[] = 'Allow: PUT, PATCH, POST'; |
||
97 | } |
||
98 | |||
99 | $ch = curl_init(); |
||
100 | curl_setopt($ch, CURLOPT_URL, $url); |
||
101 | curl_setopt($ch, CURLOPT_HTTPHEADER, $httpHeader); |
||
102 | curl_setopt($ch, CURLOPT_USERAGENT, 'Sreedev/laravel-slack (github.com/rsreedevan/laravel-slack)'); |
||
103 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); |
||
104 | curl_setopt($ch, CURLOPT_VERBOSE, true); |
||
105 | curl_setopt($ch, CURLOPT_HEADER, true); |
||
106 | curl_setopt($ch, CURLOPT_TIMEOUT, $timeout); |
||
107 | //curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, $this->verify_ssl); |
||
108 | curl_setopt($ch, CURLOPT_ENCODING, ''); |
||
109 | curl_setopt($ch, CURLINFO_HEADER_OUT, true); |
||
110 | |||
111 | switch ($http_verb) { |
||
112 | case 'POST': |
||
113 | curl_setopt($ch, CURLOPT_POST, true); |
||
114 | $this->attachRequestPayload($ch, $args); |
||
115 | break; |
||
116 | |||
117 | case 'GET': |
||
118 | $query = http_build_query($args, '', '&'); |
||
119 | curl_setopt($ch, CURLOPT_URL, $url . '?' . $query); |
||
120 | break; |
||
121 | |||
122 | case 'DELETE': |
||
123 | curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE'); |
||
124 | break; |
||
125 | |||
126 | case 'PATCH': |
||
127 | curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PATCH'); |
||
128 | $this->attachRequestPayload($ch, $args); |
||
129 | break; |
||
130 | |||
131 | case 'PUT': |
||
132 | curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT'); |
||
133 | $this->attachRequestPayload($ch, $args); |
||
134 | break; |
||
135 | } |
||
136 | |||
137 | $responseContent = curl_exec($ch); |
||
138 | $response['headers'] = curl_getinfo($ch); |
||
139 | $response = $this->setResponseState($response, $responseContent, $ch); |
||
140 | $formattedResponse = $this->formatResponse($response); |
||
141 | |||
142 | curl_close($ch); |
||
143 | |||
144 | $isSuccess = $this->determineSuccess($response, $formattedResponse, $timeout); |
||
145 | |||
146 | return is_array($formattedResponse) ? $formattedResponse : $isSuccess; |
||
0 ignored issues
–
show
|
|||
147 | } |
||
148 | |||
149 | /** |
||
150 | * @param string $http_verb |
||
151 | * @param string $method |
||
152 | * @param string $url |
||
153 | * @param integer $timeout |
||
154 | * |
||
155 | * @return array |
||
156 | */ |
||
157 | private function prepareStateForRequest($http_verb, $method, $url, $timeout) |
||
158 | { |
||
159 | $this->last_error = ''; |
||
160 | |||
161 | $this->request_successful = false; |
||
162 | |||
163 | $this->last_response = array( |
||
164 | 'headers' => null, // array of details from curl_getinfo() |
||
165 | 'httpHeaders' => null, // array of HTTP headers |
||
166 | 'body' => null // content of the response |
||
167 | ); |
||
168 | |||
169 | $this->last_request = array( |
||
170 | 'method' => $http_verb, |
||
171 | 'path' => $method, |
||
172 | 'url' => $url, |
||
173 | 'body' => '', |
||
174 | 'timeout' => $timeout, |
||
175 | ); |
||
176 | |||
177 | return $this->last_response; |
||
178 | } |
||
179 | |||
180 | /** |
||
181 | * Get the HTTP headers as an array of header-name => header-value pairs. |
||
182 | * |
||
183 | * The "Link" header is parsed into an associative array based on the |
||
184 | * rel names it contains. The original value is available under |
||
185 | * the "_raw" key. |
||
186 | * |
||
187 | * @param string $headersAsString |
||
188 | * |
||
189 | * @return array |
||
190 | */ |
||
191 | private function getHeadersAsArray($headersAsString) |
||
192 | { |
||
193 | $headers = array(); |
||
194 | |||
195 | foreach (explode("\r\n", $headersAsString) as $i => $line) { |
||
196 | if (preg_match('/HTTP\/[1-2]/', substr($line, 0, 7)) === 1) { // http code |
||
197 | continue; |
||
198 | } |
||
199 | |||
200 | $line = trim($line); |
||
201 | if (empty($line)) { |
||
202 | continue; |
||
203 | } |
||
204 | |||
205 | list($key, $value) = explode(': ', $line); |
||
206 | |||
207 | if ($key == 'Link') { |
||
208 | $value = array_merge( |
||
209 | array('_raw' => $value), |
||
210 | $this->getLinkHeaderAsArray($value) |
||
211 | ); |
||
212 | } |
||
213 | |||
214 | $headers[$key] = $value; |
||
215 | } |
||
216 | |||
217 | return $headers; |
||
218 | } |
||
219 | |||
220 | /** |
||
221 | * Extract all rel => URL pairs from the provided Link header value |
||
222 | * |
||
223 | * Mailchimp only implements the URI reference and relation type from |
||
224 | * RFC 5988, so the value of the header is something like this: |
||
225 | * |
||
226 | * 'https://us13.api.mailchimp.com/schema/3.0/Lists/Instance.json; rel="describedBy", |
||
227 | * <https://us13.admin.mailchimp.com/lists/members/?id=XXXX>; rel="dashboard"' |
||
228 | * |
||
229 | * @param string $linkHeaderAsString |
||
230 | * |
||
231 | * @return array |
||
232 | */ |
||
233 | private function getLinkHeaderAsArray($linkHeaderAsString) |
||
234 | { |
||
235 | $urls = array(); |
||
236 | |||
237 | if (preg_match_all('/<(.*?)>\s*;\s*rel="(.*?)"\s*/', $linkHeaderAsString, $matches)) { |
||
238 | foreach ($matches[2] as $i => $relName) { |
||
239 | $urls[$relName] = $matches[1][$i]; |
||
240 | } |
||
241 | } |
||
242 | |||
243 | return $urls; |
||
244 | } |
||
245 | |||
246 | /** |
||
247 | * Encode the data and attach it to the request |
||
248 | * |
||
249 | * @param resource $ch cURL session handle, used by reference |
||
250 | * @param array $data Assoc array of data to attach |
||
251 | */ |
||
252 | private function attachRequestPayload(&$ch, $data) |
||
253 | { |
||
254 | $encoded = json_encode($data); |
||
255 | $this->last_request['body'] = $encoded; |
||
256 | curl_setopt($ch, CURLOPT_POSTFIELDS, $encoded); |
||
257 | } |
||
258 | |||
259 | /** |
||
260 | * Decode the response and format any error messages for debugging |
||
261 | * |
||
262 | * @param array $response The response from the curl request |
||
263 | * |
||
264 | * @return array|false The JSON decoded into an array |
||
265 | */ |
||
266 | private function formatResponse($response) |
||
267 | { |
||
268 | $this->last_response = $response; |
||
269 | |||
270 | if (!empty($response['body'])) { |
||
271 | return json_decode($response['body'], true); |
||
272 | } |
||
273 | |||
274 | return false; |
||
275 | } |
||
276 | |||
277 | /** |
||
278 | * Do post-request formatting and setting state from the response |
||
279 | * |
||
280 | * @param array $response The response from the curl request |
||
281 | * @param string $responseContent The body of the response from the curl request |
||
282 | * @param resource $ch The curl resource |
||
283 | * |
||
284 | * @return array The modified response |
||
285 | */ |
||
286 | private function setResponseState($response, $responseContent, $ch) |
||
287 | { |
||
288 | if ($responseContent === false) { |
||
289 | $this->last_error = curl_error($ch); |
||
290 | } else { |
||
291 | |||
292 | $headerSize = $response['headers']['header_size']; |
||
293 | |||
294 | $response['httpHeaders'] = $this->getHeadersAsArray(substr($responseContent, 0, $headerSize)); |
||
295 | $response['body'] = substr($responseContent, $headerSize); |
||
296 | |||
297 | if (isset($response['headers']['request_header'])) { |
||
298 | $this->last_request['headers'] = $response['headers']['request_header']; |
||
299 | } |
||
300 | } |
||
301 | |||
302 | return $response; |
||
303 | } |
||
304 | |||
305 | /** |
||
306 | * Check if the response was successful or a failure. If it failed, store the error. |
||
307 | * |
||
308 | * @param array $response The response from the curl request |
||
309 | * @param array|false $formattedResponse The response body payload from the curl request |
||
310 | * @param int $timeout The timeout supplied to the curl request. |
||
311 | * |
||
312 | * @return bool If the request was successful |
||
313 | */ |
||
314 | private function determineSuccess($response, $formattedResponse, $timeout) |
||
315 | { |
||
316 | $status = $this->findHTTPStatus($response, $formattedResponse); |
||
317 | |||
318 | if ($status >= 200 && $status <= 299) { |
||
319 | $this->request_successful = true; |
||
320 | return true; |
||
321 | } |
||
322 | |||
323 | if (isset($formattedResponse['detail'])) { |
||
324 | $this->last_error = sprintf('%d: %s', $formattedResponse['status'], $formattedResponse['detail']); |
||
325 | return false; |
||
326 | } |
||
327 | |||
328 | if ($timeout > 0 && $response['headers'] && $response['headers']['total_time'] >= $timeout) { |
||
329 | $this->last_error = sprintf('Request timed out after %f seconds.', $response['headers']['total_time']); |
||
330 | return false; |
||
331 | } |
||
332 | |||
333 | $this->last_error = 'Unknown error, call getLastResponse() to find out what happened.'; |
||
334 | return false; |
||
335 | } |
||
336 | |||
337 | /** |
||
338 | * Find the HTTP status code from the headers or API response body |
||
339 | * |
||
340 | * @param array $response The response from the curl request |
||
341 | * @param array|false $formattedResponse The response body payload from the curl request |
||
342 | * |
||
343 | * @return int HTTP status code |
||
344 | */ |
||
345 | private function findHTTPStatus($response, $formattedResponse) |
||
346 | { |
||
347 | if (!empty($response['headers']) && isset($response['headers']['http_code'])) { |
||
348 | return (int)$response['headers']['http_code']; |
||
349 | } |
||
350 | |||
351 | if (!empty($response['body']) && isset($formattedResponse['status'])) { |
||
352 | return (int)$formattedResponse['status']; |
||
353 | } |
||
354 | |||
355 | return 418; |
||
356 | } |
||
357 | } |