Client.php 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. <?php
  2. namespace Gogs\Lib\Curl {
  3. /**
  4. * A trait used for every class referencing the api-url and token.
  5. *
  6. * @author Joachim M. Giaever (joachim[]giaever.org)
  7. * @package curl
  8. * @version 0.1.3
  9. */
  10. trait Client {
  11. private $_version = "0.1.3";
  12. private static $log = array();
  13. protected $url;
  14. protected $token;
  15. protected $basic = false;
  16. protected $user_agent = "Gogs PHP API Client/%s (%s) PHP/%s Client\\%s %s";
  17. protected $timeout = 30;
  18. protected $max_redirects = 4;
  19. /**
  20. * Basic sets the user for basic HTTP-authentication.
  21. *
  22. * @param string $user
  23. */
  24. public function basic(string $user) {
  25. $this->basic = $user;
  26. }
  27. /**
  28. * array_2_params takes an array and converts it into a
  29. * query string (e.g param=val&param2=val2).
  30. *
  31. * @param array $params parameters to pass
  32. * @return string
  33. */
  34. private function array_2_params(array $params) {
  35. return join("&", array_map(function($k, $v) {
  36. return sprintf("%s=%s", $k, rawurlencode(is_bool($v) ? ($v ? "true" : "false") : $v ));
  37. }, array_keys($params), $params));
  38. }
  39. /**
  40. * array_2_json takes an array and converts it into a
  41. * json-string (e.g {'name': 'This'}) which is typically
  42. * used in a request body.
  43. *
  44. * @param array $params paramters to pass
  45. * @return string
  46. */
  47. private function array_2_json(array $params) {
  48. return count($params) == 0 ? null : json_encode($params);
  49. }
  50. /**
  51. * Initializes a curl request of different kinds, depending
  52. * on the specified method. This can be
  53. *
  54. * DELETE, PATCH, POST or GET. An unidentified value will
  55. * become a GET-request.
  56. *
  57. * @param string $method either DELETE, PATCH, POST, GET
  58. * @param string &$req variable to store request body in
  59. * @param string $scope scope within the API (e.g /user/repos)
  60. * @param array $params parameters to pass
  61. * @param bool $ret return transfer
  62. * @return int the status code
  63. */
  64. protected function method(string $method, string &$req, string $scope, array $params, bool $ret) {
  65. $c = curl_init();
  66. if (!$c) {
  67. return false;
  68. }
  69. $headers = array();
  70. $url = sprintf("%s%s", $this->url, $scope);
  71. curl_setopt($c, CURLOPT_USERAGENT, $agent = sprintf($this->user_agent, $this->_version, PHP_OS, phpversion(), get_class($this), self::VERSION));
  72. self::$log[] = sprintf(
  73. "%s:[%s] %s, %s, %s",
  74. date("y-m-d H:i:s"),
  75. $method,
  76. $url,
  77. !empty($p = $this->array_2_json($params)) ? $p : "none",
  78. $agent
  79. );
  80. if (!$this->basic)
  81. $headers[] = sprintf("Authorization: token %s", $this->token);
  82. else
  83. curl_setopt($c, CURLOPT_USERPWD, sprintf("%s:%s", $this->basic, $this->token));
  84. if (in_array($method, array("DELETE", "PATCH", "POST"))) {
  85. $json = $this->array_2_json($params);
  86. curl_setopt($c, CURLOPT_CUSTOMREQUEST, $method);
  87. curl_setopt($c, CURLOPT_POSTFIELDS, $json);
  88. array_unshift($headers, "Content-Type: application/json");
  89. array_push($headers, "Content-Length: " . strlen($json));
  90. } else {
  91. $url .= !empty($params = $this->array_2_params($params)) ? "?" . $params : "";
  92. }
  93. curl_setopt($c, CURLOPT_URL, $url);
  94. curl_setopt($c, CURLOPT_HTTPHEADER, $headers);
  95. curl_setopt($c, CURLOPT_RETURNTRANSFER, $ret);
  96. curl_setopt($c, CURLOPT_TIMEOUT, $this->timeout);
  97. curl_setopt($c, CURLOPT_MAXREDIRS, $this->max_redirects);
  98. curl_setopt($c, CURLOPT_FOLLOWLOCATION, true);
  99. $req = curl_exec($c);
  100. $status_code = curl_getinfo($c, CURLINFO_HTTP_CODE);
  101. curl_close($c);
  102. array_push(
  103. self::$log,
  104. sprintf(
  105. "%s:[%s] %s, %d, %s",
  106. date("y-m-d H:i:s"),
  107. $method,
  108. $url,
  109. $status_code,
  110. substr($req, 0, 100) . (strlen($req) > 100 ? "..." : ".")
  111. )
  112. );
  113. return $status_code;
  114. }
  115. /**
  116. * Checks if the user is authorized for the scope. Shouldn't
  117. * be used frequently. One test for one scope should be enough,
  118. * but if you know for sure thats you're programming with the
  119. * use of an authorized user you should leave this and just
  120. * handle the NotAuthorizedExeption whenever thrown.
  121. *
  122. * @param $scope the scope, a relative uri.
  123. * @throws Not AuthorizedException if server responde with a 401
  124. * @return bool
  125. */
  126. protected function authorized(string $scope = "") {
  127. $ret = "";
  128. if (in_array(($code = $this->method("GET", $ret, $scope, array(), false)),
  129. array(400, 401, 402, 403)
  130. )) {
  131. throw new NotAuthorizedException("Not authorized", 401);
  132. }
  133. return true;
  134. }
  135. /**
  136. * Post method.
  137. *
  138. * @param string $scope the scope, a relative uri.
  139. * @param array $params the parameters to post.
  140. * @throws NotAuthorizedException on 401, 403
  141. * @throws HTTPUnexpectedResponse when not 200,201,401,403
  142. * @return string the request content.
  143. */
  144. private function post(string $scope = "", array $params = array()) {
  145. $req = "";
  146. $code = $this->method("POST", $req, $scope, $params, true);
  147. switch ($code) {
  148. case 200:
  149. case 201:
  150. return $req;
  151. case 400:
  152. case 401:
  153. case 403:
  154. throw new Exception\NotAuthorizedException($req, $code);
  155. default:
  156. throw new Exception\HTTPUnexpectedResponse($req, $code);
  157. }
  158. }
  159. /**
  160. * Delete method.
  161. *
  162. * @param string $scope the scope, a relative uri.
  163. * @throws NotAuthorizedException on 401, 403
  164. * @throws HTTPUnexpectedResponse when not 200,204,401,403
  165. * @return string the request content.
  166. */
  167. private function delete(string $scope = "") {
  168. $req = "";
  169. $code = $this->method("DELETE", $req, $scope, array(), true);
  170. switch ($code) {
  171. case 200:
  172. case 204:
  173. return true;
  174. case 401:
  175. case 403:
  176. throw new Exception\NotAuthorizedException($req, $code);
  177. default:
  178. throw new Exception\HTTPUnexpectedResponse($req, $code);
  179. }
  180. }
  181. /**
  182. * GET method.
  183. *
  184. * @param string $scope the scope, a relative uri.
  185. * @param array $params the parameters to post.
  186. * @throws NotAuthorizedException on 401, 403
  187. * @throws HTTPUnexpectedResponse when not 200,401,403
  188. * @return string the request content.
  189. */
  190. private function get($scope = "", $params = array()) {
  191. $req = "";
  192. $code = $this->method("GET", $req, $scope, $params, true);
  193. switch ($code) {
  194. case 200:
  195. return $req;
  196. case 401:
  197. case 403:
  198. throw new Exception\NotAuthorizedException($req, $code);
  199. default:
  200. throw new Exception\HTTPUnexpectedResponse($req, $code);
  201. }
  202. }
  203. /**
  204. * Returns log entries for the client.
  205. *
  206. * @return array
  207. */
  208. public static function get_log() {
  209. if (empty(self::$log))
  210. return self::$log;
  211. return array_merge(self::$log, array("\n"));
  212. }
  213. }
  214. }
  215. ?>