Twilio.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. <?php
  2. /*
  3. * Author: Neuman Vong neuman@twilio.com
  4. * License: http://creativecommons.org/licenses/MIT/ MIT
  5. * Link: https://twilio-php.readthedocs.org/en/latest/
  6. */
  7. function Services_Twilio_autoload($className) {
  8. if (substr($className, 0, 15) != 'Services_Twilio') {
  9. return false;
  10. }
  11. $file = str_replace('_', '/', $className);
  12. $file = str_replace('Services/', '', $file);
  13. return include dirname(__FILE__) . "/$file.php";
  14. }
  15. spl_autoload_register('Services_Twilio_autoload');
  16. /**
  17. * Create a client to talk to the Twilio API.
  18. *
  19. *
  20. * :param string $sid: Your Account SID
  21. * :param string $token: Your Auth Token from `your dashboard
  22. * <https://www.twilio.com/user/account>`_
  23. * :param string $version: API version to use
  24. * :param $_http: A HTTP client for making requests.
  25. * :type $_http: :php:class:`Services_Twilio_TinyHttp`
  26. * :param int $retryAttempts:
  27. * Number of times to retry failed requests. Currently only idempotent
  28. * requests (GET's and DELETE's) are retried.
  29. *
  30. * Here's an example:
  31. *
  32. * .. code-block:: php
  33. *
  34. * require('Services/Twilio.php');
  35. * $client = new Services_Twilio('AC123', '456bef', null, null, 3);
  36. * // Take some action with the client, etc.
  37. */
  38. class Services_Twilio extends Services_Twilio_Resource
  39. {
  40. const USER_AGENT = 'twilio-php/3.12.8';
  41. protected $http;
  42. protected $retryAttempts;
  43. protected $last_response;
  44. protected $version;
  45. protected $versions = array('2008-08-01', '2010-04-01');
  46. public function __construct(
  47. $sid,
  48. $token,
  49. $version = null,
  50. Services_Twilio_TinyHttp $_http = null,
  51. $retryAttempts = 1
  52. ) {
  53. $this->version = in_array($version, $this->versions) ?
  54. $version : end($this->versions);
  55. if (null === $_http) {
  56. if (!in_array('openssl', get_loaded_extensions())) {
  57. throw new Services_Twilio_HttpException("The OpenSSL extension is required but not currently enabled. For more information, see http://php.net/manual/en/book.openssl.php");
  58. }
  59. if (in_array('curl', get_loaded_extensions())) {
  60. $_http = new Services_Twilio_TinyHttp(
  61. "https://api.twilio.com",
  62. array(
  63. "curlopts" => array(
  64. CURLOPT_USERAGENT => self::qualifiedUserAgent(phpversion()),
  65. CURLOPT_HTTPHEADER => array('Accept-Charset: utf-8'),
  66. ),
  67. )
  68. );
  69. } else {
  70. $_http = new Services_Twilio_HttpStream(
  71. "https://api.twilio.com",
  72. array(
  73. "http_options" => array(
  74. "http" => array(
  75. "user_agent" => self::qualifiedUserAgent(phpversion()),
  76. "header" => "Accept-Charset: utf-8\r\n",
  77. ),
  78. "ssl" => array(
  79. 'verify_peer' => true,
  80. 'verify_depth' => 5,
  81. ),
  82. ),
  83. )
  84. );
  85. }
  86. }
  87. $_http->authenticate($sid, $token);
  88. $this->http = $_http;
  89. $this->accounts = new Services_Twilio_Rest_Accounts($this, "/{$this->version}/Accounts");
  90. $this->account = $this->accounts->get($sid);
  91. $this->retryAttempts = $retryAttempts;
  92. }
  93. /**
  94. * Fully qualified user agent with the current PHP Version.
  95. *
  96. * :return: the user agent
  97. * :rtype: string
  98. */
  99. public static function qualifiedUserAgent($php_version) {
  100. return self::USER_AGENT . " (php $php_version)";
  101. }
  102. /**
  103. * Get the api version used by the rest client
  104. *
  105. * :return: the API version in use
  106. * :returntype: string
  107. */
  108. public function getVersion() {
  109. return $this->version;
  110. }
  111. /**
  112. * Get the retry attempt limit used by the rest client
  113. *
  114. * :return: the number of retry attempts
  115. * :rtype: int
  116. */
  117. public function getRetryAttempts() {
  118. return $this->retryAttempts;
  119. }
  120. /**
  121. * Construct a URI based on initial path, query params, and paging
  122. * information
  123. *
  124. * We want to use the query params, unless we have a next_page_uri from the
  125. * API.
  126. *
  127. * :param string $path: The request path (may contain query params if it's
  128. * a next_page_uri)
  129. * :param array $params: Query parameters to use with the request
  130. * :param boolean $full_uri: Whether the $path contains the full uri
  131. *
  132. * :return: the URI that should be requested by the library
  133. * :returntype: string
  134. */
  135. public static function getRequestUri($path, $params, $full_uri = false) {
  136. $json_path = $full_uri ? $path : "$path.json";
  137. if (!$full_uri && !empty($params)) {
  138. $query_path = $json_path . '?' . http_build_query($params, '', '&');
  139. } else {
  140. $query_path = $json_path;
  141. }
  142. return $query_path;
  143. }
  144. /**
  145. * Helper method for implementing request retry logic
  146. *
  147. * :param array $callable: The function that makes an HTTP request
  148. * :param string $uri: The URI to request
  149. * :param int $retriesLeft: Number of times to retry
  150. *
  151. * :return: The object representation of the resource
  152. * :rtype: object
  153. */
  154. protected function _makeIdempotentRequest($callable, $uri, $retriesLeft) {
  155. $response = call_user_func_array($callable, array($uri));
  156. list($status, $headers, $body) = $response;
  157. if ($status >= 500 && $retriesLeft > 0) {
  158. return $this->_makeIdempotentRequest($callable, $uri, $retriesLeft - 1);
  159. } else {
  160. return $this->_processResponse($response);
  161. }
  162. }
  163. /**
  164. * GET the resource at the specified path.
  165. *
  166. * :param string $path: Path to the resource
  167. * :param array $params: Query string parameters
  168. * :param boolean $full_uri: Whether the full URI has been passed as an
  169. * argument
  170. *
  171. * :return: The object representation of the resource
  172. * :rtype: object
  173. */
  174. public function retrieveData($path, $params = array(),
  175. $full_uri = false
  176. ) {
  177. $uri = self::getRequestUri($path, $params, $full_uri);
  178. return $this->_makeIdempotentRequest(array($this->http, 'get'),
  179. $uri, $this->retryAttempts);
  180. }
  181. /**
  182. * DELETE the resource at the specified path.
  183. *
  184. * :param string $path: Path to the resource
  185. * :param array $params: Query string parameters
  186. *
  187. * :return: The object representation of the resource
  188. * :rtype: object
  189. */
  190. public function deleteData($path, $params = array())
  191. {
  192. $uri = self::getRequestUri($path, $params);
  193. return $this->_makeIdempotentRequest(array($this->http, 'delete'),
  194. $uri, $this->retryAttempts);
  195. }
  196. /**
  197. * POST to the resource at the specified path.
  198. *
  199. * :param string $path: Path to the resource
  200. * :param array $params: Query string parameters
  201. *
  202. * :return: The object representation of the resource
  203. * :rtype: object
  204. */
  205. public function createData($path, $params = array())
  206. {
  207. $path = "$path.json";
  208. $headers = array('Content-Type' => 'application/x-www-form-urlencoded');
  209. $response = $this->http->post(
  210. $path, $headers, self::buildQuery($params, '')
  211. );
  212. return $this->_processResponse($response);
  213. }
  214. /**
  215. * Build a query string from query data
  216. *
  217. * :param array $queryData: An associative array of keys and values. The
  218. * values can be a simple type or a list, in which case the list is
  219. * converted to multiple query parameters with the same key.
  220. * :param string $numericPrefix:
  221. * :param string $queryStringStyle: Determine how to build the url
  222. * - strict: Build a standards compliant query string without braces (can be hacked by using braces in key)
  223. * - php: Build a PHP compatible query string with nested array syntax
  224. * :return: The encoded query string
  225. * :rtype: string
  226. */
  227. public static function buildQuery($queryData, $numericPrefix = '') {
  228. $query = '';
  229. // Loop through all of the $query_data
  230. foreach ($queryData as $key => $value) {
  231. // If the key is an int, add the numeric_prefix to the beginning
  232. if (is_int($key)) {
  233. $key = $numericPrefix . $key;
  234. }
  235. // If the value is an array, we will end up recursing
  236. if (is_array($value)) {
  237. // Loop through the values
  238. foreach ($value as $value2) {
  239. // Add an arg_separator if needed
  240. if ($query !== '') {
  241. $query .= '&';
  242. }
  243. // Recurse
  244. $query .= self::buildQuery(array($key => $value2), $numericPrefix);
  245. }
  246. } else {
  247. // Add an arg_separator if needed
  248. if ($query !== '') {
  249. $query .= '&';
  250. }
  251. // Add the key and the urlencoded value (as a string)
  252. $query .= $key . '=' . urlencode((string)$value);
  253. }
  254. }
  255. return $query;
  256. }
  257. /**
  258. * Convert the JSON encoded resource into a PHP object.
  259. *
  260. * :param array $response: 3-tuple containing status, headers, and body
  261. *
  262. * :return: PHP object decoded from JSON
  263. * :rtype: object
  264. * :throws: A :php:class:`Services_Twilio_RestException` if the Response is
  265. * in the 300-500 range of status codes.
  266. */
  267. private function _processResponse($response)
  268. {
  269. list($status, $headers, $body) = $response;
  270. if ($status === 204) {
  271. return true;
  272. }
  273. $decoded = json_decode($body);
  274. if ($decoded === null) {
  275. throw new Services_Twilio_RestException(
  276. $status,
  277. 'Could not decode response body as JSON. ' .
  278. 'This likely indicates a 500 server error'
  279. );
  280. }
  281. if (200 <= $status && $status < 300) {
  282. $this->last_response = $decoded;
  283. return $decoded;
  284. }
  285. throw new Services_Twilio_RestException(
  286. $status,
  287. isset($decoded->message) ? $decoded->message : '',
  288. isset($decoded->code) ? $decoded->code : null,
  289. isset($decoded->more_info) ? $decoded->more_info : null
  290. );
  291. }
  292. }