CurlClient.php 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. <?php
  2. namespace OntraportAPI;
  3. /**
  4. * Class CurlClient
  5. *
  6. * @author ONTRAPORT
  7. *
  8. * @package OntraportAPI
  9. */
  10. class CurlClient
  11. {
  12. const HTTP_OK = 200;
  13. const HTTP_BAD_REQUEST = 400;
  14. const HTTP_UNAUTHORIZED = 401;
  15. const HTTP_FORBIDDEN = 403;
  16. const HTTP_NOT_FOUND = 404;
  17. const HTTP_RATE_LIMIT = 429;
  18. const HTTP_ERROR = 500;
  19. const RATE_LIMIT_RESET = "x-rate-limit-reset";
  20. /**
  21. * @var array of headers sent with HTTP requests
  22. */
  23. private $_requestHeaders = array();
  24. /**
  25. * @var int the last HTTP status code received
  26. */
  27. private $_lastStatusCode = null;
  28. public function __construct($apiKey,$siteID)
  29. {
  30. $this->_setRequestHeader("Api-key", $apiKey);
  31. $this->_setRequestHeader("Api-Appid", $siteID);
  32. }
  33. /**
  34. * @brief sets an element in an array of headers to be sent with HTTP requests
  35. *
  36. * @param string $header
  37. * @param string $value
  38. */
  39. private function _setRequestHeader($header, $value)
  40. {
  41. $this->_requestHeaders[$header] = $header . ": " . $value;
  42. }
  43. /**
  44. * @brief retrieves current request headers
  45. *
  46. * @return array
  47. */
  48. public function getRequestHeaders()
  49. {
  50. return $this->_requestHeaders;
  51. }
  52. /**
  53. * @brief gets an array of supported cURL methods
  54. *
  55. * @return array
  56. */
  57. private function _getWhitelistedRequestTypes()
  58. {
  59. return array(
  60. "get",
  61. "post",
  62. "put",
  63. "delete"
  64. );
  65. }
  66. /**
  67. * @brief checks that HTTP request is formed properly before sending
  68. *
  69. * @param array $requestParams
  70. * @param array $requiredParams
  71. * @param int $method
  72. *
  73. * @return boolean
  74. *
  75. * @throws Exceptions\HttpMethodException
  76. * @throws Exceptions\RequiredParamsException
  77. * @throws Exceptions\TypeException
  78. */
  79. private function _validateRequest($requestParams, $requiredParams, $method)
  80. {
  81. $allowedMethods = $this->_getWhitelistedRequestTypes();
  82. if (!in_array($method, $allowedMethods))
  83. {
  84. throw new Exceptions\HttpMethodException($method);
  85. }
  86. if ($requestParams && (!is_array($requestParams)))
  87. {
  88. $dataType = gettype($requestParams);
  89. throw new Exceptions\TypeException($dataType);
  90. }
  91. if ($missingParams = $this->_checkRequiredParams($requestParams, $requiredParams))
  92. {
  93. $missingParams = implode(",",$missingParams);
  94. throw new Exceptions\RequiredParamsException($missingParams);
  95. }
  96. return true;
  97. }
  98. /**
  99. * @brief checks that all required parameters were included with HTTP request
  100. *
  101. * @param array $requestParams
  102. * @param array $requiredParams
  103. * @param string $url
  104. *
  105. * @return array $missingParams
  106. */
  107. private function _checkRequiredParams($requestParams, $requiredParams)
  108. {
  109. $missingParams = array();
  110. if ($requiredParams && is_array($requiredParams))
  111. {
  112. foreach ($requiredParams as $requiredParam)
  113. {
  114. if (!array_key_exists($requiredParam, $requestParams))
  115. {
  116. // Covers special case: when ids is required, group_ids can be substituted
  117. if ($requiredParam == "ids")
  118. {
  119. if (!array_key_exists("group_ids", $requestParams))
  120. {
  121. $missingParams[] = $requiredParam;
  122. }
  123. }
  124. else
  125. {
  126. $missingParams[] = $requiredParam;
  127. }
  128. }
  129. }
  130. }
  131. return $missingParams;
  132. }
  133. /**
  134. * @brief makes an HTTP request
  135. *
  136. * @param array $requestParams
  137. * @param string $url
  138. * @param int $method
  139. * @param array $requiredParams
  140. * @param array $options
  141. *
  142. * @return string|boolean
  143. */
  144. public function httpRequest($requestParams, $url, $method, $requiredParams, $options)
  145. {
  146. if (!$this->_validateRequest($requestParams, $requiredParams, $method))
  147. {
  148. return false;
  149. }
  150. if ($options && is_array($options))
  151. {
  152. if (array_key_exists("headers", $options))
  153. {
  154. foreach ($options["headers"] as $header => $value)
  155. {
  156. $this->_setRequestHeader($header, $value);
  157. }
  158. }
  159. }
  160. if (array_key_exists("Content-Type", $this->_requestHeaders) && $this->_requestHeaders["Content-Type"] == "Content-Type: application/json")
  161. {
  162. $requestParams = json_encode($requestParams);
  163. }
  164. if (is_array($requestParams))
  165. {
  166. $requestParams = http_build_query($requestParams);
  167. }
  168. $curlHandle = curl_init();
  169. $headers = array();
  170. switch(strtolower($method))
  171. {
  172. case "post":
  173. curl_setopt($curlHandle, CURLOPT_POST, 1);
  174. curl_setopt($curlHandle, CURLOPT_POSTFIELDS, $requestParams);
  175. break;
  176. case "get":
  177. curl_setopt($curlHandle, CURLOPT_HTTPGET, 1);
  178. $url = $url."?".$requestParams;
  179. break;
  180. case "put":
  181. curl_setopt($curlHandle, CURLOPT_CUSTOMREQUEST, "PUT");
  182. curl_setopt($curlHandle, CURLOPT_POSTFIELDS, $requestParams);
  183. break;
  184. case "delete":
  185. curl_setopt($curlHandle, CURLOPT_CUSTOMREQUEST, "DELETE");
  186. if (array_key_exists("Content-Type", $this->_requestHeaders) && $this->_requestHeaders["Content-Type"] == "Content-Type: application/json")
  187. {
  188. curl_setopt($curlHandle, CURLOPT_POSTFIELDS, $requestParams);
  189. }
  190. else
  191. {
  192. $url = $url."?".$requestParams;
  193. }
  194. break;
  195. }
  196. curl_setopt($curlHandle, CURLOPT_URL, $url);
  197. curl_setopt($curlHandle, CURLOPT_HTTPHEADER, $this->_requestHeaders);
  198. curl_setopt($curlHandle, CURLOPT_RETURNTRANSFER, true);
  199. curl_setopt($curlHandle, CURLOPT_HEADER, false);
  200. curl_setopt($curlHandle, CURLOPT_HEADERFUNCTION,
  201. function($curl, $header) use (&$headers)
  202. {
  203. $len = strlen($header);
  204. $header = explode(":", $header, 2);
  205. if (count($header) < 2)
  206. {
  207. return $len;
  208. }
  209. $name = strtolower(trim($header[0]));
  210. $headers[$name] = trim($header[1]);
  211. return $len;
  212. }
  213. );
  214. curl_setopt($curlHandle, CURLOPT_TIMEOUT, 60);
  215. $result = curl_exec($curlHandle);
  216. $this->_lastStatusCode = curl_getinfo($curlHandle, CURLINFO_HTTP_CODE);
  217. if ($this->_lastStatusCode == self::HTTP_RATE_LIMIT)
  218. {
  219. $result = $this->retry($headers[self::RATE_LIMIT_RESET], $curlHandle);
  220. }
  221. curl_close($curlHandle);
  222. unset($this->_requestHeaders["Content-Type"]);
  223. return $result;
  224. }
  225. /**
  226. * @brief get the last HTTP status code received
  227. *
  228. * @return int
  229. */
  230. public function getLastStatusCode()
  231. {
  232. return $this->_lastStatusCode;
  233. }
  234. /**
  235. * @brief set the last HTTP status code received
  236. *
  237. * @param int $statusCode The code that was received
  238. */
  239. public function setLastStatusCode($statusCode)
  240. {
  241. $this->_lastStatusCode = $statusCode;
  242. }
  243. /**
  244. * @brief Retry the request after waiting for the rate limit to roll over
  245. *
  246. * @param int $wait
  247. * @param $curlHandle
  248. *
  249. * @return mixed
  250. */
  251. public function retry($wait, $curlHandle)
  252. {
  253. sleep($wait);
  254. $result = curl_exec($curlHandle);
  255. $this->_lastStatusCode = curl_getinfo($curlHandle, CURLINFO_HTTP_CODE);
  256. return $result;
  257. }
  258. }