CUrl.php 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423
  1. <?php
  2. namespace components\components;
  3. use Yii;
  4. use yii\base\Component;
  5. use yii\helpers\Json;
  6. use yii\web\HttpException;
  7. /**
  8. * CUrl Model
  9. * cURL 模型
  10. * ----------------
  11. * @version 1.0.0
  12. * @author Verdient。
  13. */
  14. class CUrl extends Component
  15. {
  16. /**
  17. * const CURLOPT_QUERY
  18. * 查询参数
  19. * -------------------
  20. * @author Verdient。
  21. */
  22. const CURLOPT_QUERY = 'query';
  23. /**
  24. * @var public $onlyContent
  25. * 只返回消息体
  26. * ------------------------
  27. * @author Verdient。
  28. */
  29. public $onlyContent = true;
  30. /**
  31. * @var private $_response
  32. * 响应内容
  33. * -----------------------
  34. * @author Verident。
  35. */
  36. private $_response = null;
  37. /**
  38. * @var private $_responseCode
  39. * 状态码
  40. * ---------------------------
  41. * @author Verdient。
  42. */
  43. private $_responseCode = null;
  44. /**
  45. * @var private $_options
  46. * 参数
  47. * ----------------------
  48. * @author Verdient。
  49. */
  50. private $_options = [];
  51. /**
  52. * @var private $_curl
  53. * cUrl实例
  54. * -------------------
  55. * @author Verdient。
  56. */
  57. private $_curl = null;
  58. /**
  59. * @var private $_defaultOptions
  60. * 默认参数
  61. * -----------------------------
  62. * @author Verdient。
  63. */
  64. private $_defaultOptions = [
  65. CURLOPT_USERAGENT => 'Yii2-CUrl-Agent',
  66. CURLOPT_TIMEOUT => 30,
  67. CURLOPT_CONNECTTIMEOUT => 30,
  68. CURLOPT_RETURNTRANSFER => true,
  69. CURLOPT_HEADER => false,
  70. CURLOPT_SSL_VERIFYPEER => false,
  71. CURLOPT_SSL_VERIFYHOST => false,
  72. CURLOPT_HTTPHEADER => [],
  73. self::CURLOPT_QUERY => [],
  74. ];
  75. /**
  76. * get(String $url, String $dataType)
  77. * 通过get方式获取数据
  78. * ------------------------------
  79. * @param String $url url地址
  80. * @param String $dataType 返回数据格式
  81. * ------------------------------------
  82. * @return Mixed
  83. * @author Verdient。
  84. */
  85. public function get($url, $dataType = null){
  86. return $this->_httpRequest('GET', $url, $dataType);
  87. }
  88. /**
  89. * head(String $url)
  90. * 通过head方式获取数据
  91. * --------------------
  92. * @param String $url url地址
  93. * --------------------------
  94. * @return Mixed
  95. * @author Verdient。
  96. */
  97. public function head($url){
  98. return $this->_httpRequest('HEAD', $url);
  99. }
  100. /**
  101. * post(String $url, String $dataType)
  102. * 通过post方式获取数据
  103. * -----------------------------------
  104. * @param String $url url地址
  105. * @param String $dataType 返回数据格式
  106. * ------------------------------------
  107. * @return Mixed
  108. * @author Verdient。
  109. */
  110. public function post($url, $dataType = null){
  111. return $this->_httpRequest('POST', $url, $dataType);
  112. }
  113. /**
  114. * put(String $url, String $dataType)
  115. * 通过put方式获取数据
  116. * -------------------
  117. * @param String $url url地址
  118. * @param String $dataType 返回数据格式
  119. * ------------------------------------
  120. * @return Mixed
  121. * @author Verdient。
  122. */
  123. public function put($url, $dataType = null){
  124. return $this->_httpRequest('PUT', $url, $dataType);
  125. }
  126. /**
  127. * delete(String $url, String $dataType)
  128. * 通过delete方式获取数据
  129. * -------------------------------------
  130. * @param String $url url地址
  131. * @param String $dataType 返回数据格式
  132. * ------------------------------------
  133. * @return Mixed
  134. * @author Verdient。
  135. */
  136. public function delete($url, $dataType = null){
  137. return $this->_httpRequest('DELETE', $url, $dataType);
  138. }
  139. /**
  140. * setHeader(Array $headers)
  141. * 设置发送的头部信息
  142. * --------------------------
  143. * @param Array $headers 头部信息
  144. * ------------------------------
  145. * @return Mixed
  146. * @author Verdient。
  147. */
  148. public function setHeader(Array $headers){
  149. $header = [];
  150. foreach($headers as $key => $value){
  151. $header[] = $key . ':' . $value;
  152. }
  153. $this->setOption(CURLOPT_HTTPHEADER, $header);
  154. }
  155. /**
  156. * setData(Array $data, Callable / String $callback = null)
  157. * 设置发送的数据
  158. * --------------------------------------------------------
  159. * @param Array $data 发送的数据
  160. * @param Callable / String $callback 回调函数
  161. * -------------------------------------------
  162. * @return Object
  163. * @author Verdient。
  164. */
  165. public function setData(Array $data, $callback = null){
  166. if(is_string($callback)){
  167. if(strtoupper($callback) == 'JSON'){
  168. $data = Json::encode($data);
  169. $this->setHeader(['Content-Type' => 'application/json', 'Content-Length' => strlen($data)]);
  170. return $this->setOption(CURLOPT_POSTFIELDS, $data);
  171. }
  172. }
  173. if(is_callable($callback)){
  174. $data = call_user_func($callback, $data);
  175. }
  176. return $this->setOption(CURLOPT_POSTFIELDS, $callback == null ? http_build_query($data) : $data);
  177. }
  178. /**
  179. * setQuery(Array $query)
  180. * 设置查询信息
  181. * ----------------------
  182. * @param Array $query 查询信息
  183. * ---------------------------
  184. * @return Mixed
  185. * @author Verdient。
  186. */
  187. public function setQuery(Array $query){
  188. $this->setOption(self::CURLOPT_QUERY, $query);
  189. }
  190. /**
  191. * setOption(String $key, Mixed $value)
  192. * 设置选项
  193. * ------------------------------------
  194. * @param String $key 选项名称
  195. * @param Mixed $value 选项内容
  196. * ----------------------------
  197. * @return Object
  198. * @author Verdient。
  199. */
  200. public function setOption($key, $value){
  201. if(isset($this->_options[$key]) && is_array($this->_options[$key])){
  202. $this->_options[$key] = array_merge($this->_options[$key], $value);
  203. }else{
  204. $this->_options[$key] = $value;
  205. }
  206. return $this;
  207. }
  208. /**
  209. * setOptions(Array $options)
  210. * 批量设置选项
  211. * --------------------------
  212. * @param String $options 选项集合
  213. * -------------------------------
  214. * @return Object
  215. * @author Verdient。
  216. */
  217. public function setOptions($options){
  218. foreach($options as $key => $value){
  219. $this->setOption($key, $value);
  220. }
  221. return $this;
  222. }
  223. /**
  224. * unsetOption(String $key)
  225. * 删除选项
  226. * ------------------------
  227. * @param String $key 选项名称
  228. * --------------------------
  229. * @return Object
  230. * @author Verdient。
  231. */
  232. public function unsetOption($key){
  233. if(isset($this->_options[$key])){
  234. unset($this->_options[$key]);
  235. }
  236. return $this;
  237. }
  238. /**
  239. * resetOptions()
  240. * 重置选项
  241. * --------------
  242. * @return Object
  243. * @author Verdient。
  244. */
  245. public function resetOptions(){
  246. if (isset($this->_options)) {
  247. $this->_options = [];
  248. }
  249. return $this;
  250. }
  251. /**
  252. * reset()
  253. * 重置
  254. * -------
  255. * @return Object
  256. * @author Verdient。
  257. */
  258. public function reset(){
  259. if($this->_curl !== null){
  260. @curl_close($this->_curl);
  261. }
  262. $this->_curl = null;
  263. $this->_options = [];
  264. $this->_response = null;
  265. $this->_responseCode = null;
  266. return $this;
  267. }
  268. /**
  269. * getOption(String $key)
  270. * 获取选项内容
  271. * ----------------------
  272. * @param String $key 选项名称
  273. * ---------------------------
  274. * @return Object
  275. * @author Verdient。
  276. */
  277. public function getOption($key){
  278. $mergesOptions = $this->getOptions();
  279. return isset($mergesOptions[$key]) ? $mergesOptions[$key] : false;
  280. }
  281. /**
  282. * getOptions()
  283. * 获取所有的选项内容
  284. * ------------------
  285. * @return Object
  286. * @author Verdient。
  287. */
  288. public function getOptions(){
  289. return $this->_options + $this->_defaultOptions;
  290. }
  291. /**
  292. * getInfo(String $opt)
  293. * 获取连接资源句柄的信息
  294. * ----------------------
  295. * @param String $opt 选项名称
  296. * ---------------------------
  297. * @return Object
  298. * @author Verdient。
  299. */
  300. public function getInfo($opt = null){
  301. if($this->_curl !== null && $opt === null){
  302. return curl_getinfo($this->_curl);
  303. }else if($this->_curl !== null && $opt !== null){
  304. return curl_getinfo($this->_curl, $opt);
  305. }else{
  306. return [];
  307. }
  308. }
  309. /**
  310. * getResponse()
  311. * 获取响应内容
  312. * -------------
  313. * @return Mixed
  314. * @author Verdient。
  315. */
  316. public function getResponse(){
  317. return $this->_response;
  318. }
  319. /**
  320. * getResponseCode()
  321. * 获取状态码
  322. * -----------------
  323. * @return Integer
  324. * @author Verdient。
  325. */
  326. public function getResponseCode(){
  327. return $this->_responseCode;
  328. }
  329. /**
  330. * _httpRequest(String $method, String $url, String $dataType)
  331. * http请求
  332. * -----------------------------------------------------------
  333. * @param String $method 请求方式
  334. * @param String $url 请求地址
  335. * @param String $dataType 返回数据格式
  336. * ------------------------------------
  337. * @return Object
  338. * @author Verdient。
  339. */
  340. private function _httpRequest($method, $url, $dataType = null){
  341. $this->setOption(CURLOPT_CUSTOMREQUEST, strtoupper($method));
  342. if($method === 'HEAD'){
  343. $this->setOption(CURLOPT_NOBODY, true);
  344. $this->unsetOption(CURLOPT_WRITEFUNCTION);
  345. }
  346. $query = $this->getOption(self::CURLOPT_QUERY);
  347. if(!empty($query)){
  348. $url = $url . '?' . http_build_query($query);
  349. }
  350. $this->_curl = curl_init($url);
  351. $options = $this->getOptions();
  352. $curlOptions = [];
  353. foreach($options as $key => $value){
  354. if(is_numeric($key)){
  355. $curlOptions[$key] = $value;
  356. }
  357. }
  358. curl_setopt_array($this->_curl, $curlOptions);
  359. $body = curl_exec($this->_curl);
  360. if($body === false){
  361. $errorCode = curl_errno($this->_curl);
  362. Yii::error(['code' => $errorCode, 'type' => curl_strerror($errorCode), 'message' => curl_error($this->_curl), 'info' => curl_getinfo($this->_curl), 'version' => curl_version()], __METHOD__);
  363. switch($errorCode){
  364. case 7:
  365. throw new HttpException(504, 'CUrl requset timeout');
  366. break;
  367. default:
  368. throw new HttpException(502, 'CUrl requset error(' . $errorCode . ')');
  369. break;
  370. }
  371. }
  372. $this->_responseCode = curl_getinfo($this->_curl, CURLINFO_HTTP_CODE);
  373. $this->_response = $body;
  374. if($this->getOption(CURLOPT_CUSTOMREQUEST) === 'HEAD'){
  375. $this->reset();
  376. return true;
  377. }
  378. Yii::trace(['method' => $method, 'url' => $url, 'options' => $options, 'code' => $this->_responseCode, 'response' => $this->_response], __METHOD__);
  379. switch(strtoupper($dataType)){
  380. case 'JSON':
  381. try{
  382. $this->_response = Json::decode($this->_response);
  383. }catch(\Exception $e){
  384. Yii::warning(['method' => $method, 'url' => $url, 'options' => $options, 'responseCode' => $this->_responseCode, 'response' => $this->_response], __METHOD__);
  385. }
  386. break;
  387. }
  388. if($this->onlyContent === true){
  389. $response = $this->_response;
  390. }else{
  391. $response = ['code' => $this->_responseCode, 'content' => $this->_response];
  392. }
  393. $this->reset();
  394. return $response;
  395. }
  396. }