transport.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | Fanwe 方维直播系统
  4. // +----------------------------------------------------------------------
  5. // | Copyright (c) 2011 http://www.fanwe.com All rights reserved.
  6. // +----------------------------------------------------------------------
  7. // | Author: 云淡风轻(1956838968@qq.com)
  8. // +----------------------------------------------------------------------
  9. class transport
  10. {
  11. /**
  12. * 脚本执行时间。-1表示采用PHP的默认值。
  13. *
  14. * @access private
  15. * @var integer $time_limit
  16. */
  17. var $time_limit = -1;
  18. /**
  19. * 在多少秒之内,如果连接不可用,脚本就停止连接。-1表示采用PHP的默认值。
  20. *
  21. * @access private
  22. * @var integer $connect_timeout
  23. */
  24. var $connect_timeout = -1;
  25. /**
  26. * 连接后,限定多少秒超时。-1表示采用PHP的默认值。此项仅当采用CURL库时启用。
  27. *
  28. * @access private
  29. * @var integer $stream_timeout
  30. */
  31. var $stream_timeout = -1;
  32. /**
  33. * 是否使用CURL库来连接。false表示采用fsockopen进行连接。
  34. *
  35. * @access private
  36. * @var boolean $use_curl
  37. */
  38. var $use_curl = false;
  39. /**
  40. * 构造函数
  41. *
  42. * @access public
  43. * @param integer $time_limit
  44. * @param integer $connect_timeout
  45. * @param integer $stream_timeout
  46. * @param boolean $use_curl
  47. * @return void
  48. */
  49. function __construct($time_limit = -1, $connect_timeout = -1, $stream_timeout = -1, $use_curl = false)
  50. {
  51. //$this->transport($time_limit, $connect_timeout, $stream_timeout, $use_curl);
  52. $this->time_limit = $time_limit;
  53. $this->connect_timeout = $connect_timeout;
  54. $this->stream_timeout = $stream_timeout;
  55. $this->use_curl = $use_curl;
  56. }
  57. /**
  58. * 请求远程服务器
  59. *
  60. * @access public
  61. * @param string $url 远程服务器的URL
  62. * @param mix $params 查询参数,形如bar=foo&foo=bar;或者是一维关联数组,形如array('a'=>'aa',...)
  63. * @param string $method 请求方式,是POST还是GET
  64. * @param array $my_header 用户要发送的头部信息,为一维关联数组,形如array('a'=>'aa',...)
  65. * @return array 成功返回一维关联数组,形如array('header'=>'bar', 'body'=>'foo'),
  66. * 重大错误程序直接停止运行,否则返回false。
  67. */
  68. function request($url, $params = '', $method = 'POST', $my_header = '')
  69. {
  70. $fsock_exists = function_exists('fsockopen');
  71. $curl_exists = function_exists('curl_init');
  72. if (!$fsock_exists && !$curl_exists)
  73. {
  74. die('No method available!');
  75. }
  76. if (!$url)
  77. {
  78. die('Invalid url!');
  79. }
  80. if ($this->time_limit > -1)//如果为0,不限制执行时间
  81. {
  82. set_time_limit($this->time_limit);
  83. }
  84. $method = $method === 'GET' ? $method : 'POST';
  85. $response = '';
  86. $temp_str = '';
  87. /* 格式化将要发要送的参数 */
  88. if ($params && is_array($params))
  89. {
  90. foreach ($params AS $key => $value)
  91. {
  92. $temp_str .= '&' . $key . '=' . $value;
  93. }
  94. $params = preg_replace('/^&/', '', $temp_str);
  95. }
  96. /* 如果fsockopen存在,且用户不指定使用curl,则调用use_socket函数 */
  97. if ($fsock_exists && !$this->use_curl)
  98. {
  99. $response = $this->use_socket($url, $params, $method, $my_header);
  100. }
  101. /* 只要上述条件中的任一个不成立,流程就转向这里,这时如果curl模块可用,就调用use_curl函数 */
  102. elseif ($curl_exists)
  103. {
  104. $response = $this->use_curl($url, $params, $method, $my_header);
  105. }
  106. /* 空响应或者传输过程中发生错误,程序将返回false */
  107. if (!$response)
  108. {
  109. return false;
  110. }
  111. return $response;
  112. }
  113. /**
  114. * 使用fsockopen进行连接
  115. *
  116. * @access private
  117. * @param string $url 远程服务器的URL
  118. * @param string $params 查询参数,形如bar=foo&foo=bar
  119. * @param string $method 请求方式,是POST还是GET
  120. * @param array $my_header 用户要发送的头部信息,为一维关联数组,形如array('a'=>'aa',...)
  121. * @return array 成功返回一维关联数组,形如array('header'=>'bar', 'body'=>'foo'),
  122. * 否则返回false。
  123. */
  124. function use_socket($url, $params, $method, $my_header)
  125. {
  126. $query = '';
  127. $auth = '';
  128. $content_type = '';
  129. $content_length = '';
  130. $request_body = '';
  131. $request = '';
  132. $http_response = '';
  133. $temp_str = '';
  134. $error = '';
  135. $errstr = '';
  136. $crlf = $this->generate_crlf();
  137. if ($method === 'GET')
  138. {
  139. $query = $params ? "?$params" : '';
  140. }
  141. else
  142. {
  143. $request_body = $params;
  144. $content_type = 'Content-Type: application/x-www-form-urlencoded' . $crlf;
  145. $content_length = 'Content-Length: ' . strlen($request_body) . $crlf . $crlf;
  146. }
  147. $url_parts = $this->parse_raw_url($url);
  148. $path = $url_parts['path'] . $query;
  149. if (!empty($url_parts['user']))
  150. {
  151. $auth = 'Authorization: Basic '
  152. . base64_encode($url_parts['user'] . ':' . $url_parts['pass']) . $crlf;
  153. }
  154. /* 格式化自定义头部信息 */
  155. if ($my_header && is_array($my_header))
  156. {
  157. foreach ($my_header AS $key => $value)
  158. {
  159. $temp_str .= $key . ': ' . $value . $crlf;
  160. }
  161. $my_header = $temp_str;
  162. }
  163. /* 构造HTTP请求头部 */
  164. $request = "$method $path HTTP/1.0$crlf"
  165. . 'Host: ' . $url_parts['host'] . $crlf
  166. . $auth
  167. . $my_header
  168. . $content_type
  169. . $content_length
  170. . $request_body;
  171. if ($this->connect_timeout > -1)
  172. {
  173. $fp = @fsockopen($url_parts['host'], $url_parts['port'], $error, $errstr, $connect_timeout);
  174. }
  175. else
  176. {
  177. $fp = @fsockopen($url_parts['host'], $url_parts['port'], $error, $errstr);
  178. }
  179. if (!$fp)
  180. {
  181. return false;//打开失败
  182. }
  183. if (!@fwrite($fp, $request))
  184. {
  185. return false;//写入失败
  186. }
  187. while (!feof($fp))
  188. {
  189. $http_response .= fgets($fp);
  190. }
  191. if (!$http_response)
  192. {
  193. return false;//空响应
  194. }
  195. $separator = '/\r\n\r\n|\n\n|\r\r/';
  196. list($http_header, $http_body) = preg_split($separator, $http_response, 2);
  197. $http_response = array('header' => $http_header,//header肯定有值
  198. 'body' => $http_body);//body可能为空
  199. @fclose($fp);
  200. return $http_response;
  201. }
  202. /**
  203. * 使用curl进行连接
  204. *
  205. * @access private
  206. * @param string $url 远程服务器的URL
  207. * @param string $params 查询参数,形如bar=foo&foo=bar
  208. * @param string $method 请求方式,是POST还是GET
  209. * @param array $my_header 用户要发送的头部信息,为一维关联数组,形如array('a'=>'aa',...)
  210. * @return array 成功返回一维关联数组,形如array('header'=>'bar', 'body'=>'foo'),
  211. * 失败返回false。
  212. */
  213. function use_curl($url, $params, $method, $my_header)
  214. {
  215. /* 开始一个新会话 */
  216. $curl_session = curl_init();
  217. /* 基本设置 */
  218. curl_setopt($curl_session, CURLOPT_FORBID_REUSE, true); // 处理完后,关闭连接,释放资源
  219. curl_setopt($curl_session, CURLOPT_HEADER, true);//结果中包含头部信息
  220. curl_setopt($curl_session, CURLOPT_RETURNTRANSFER, true);//把结果返回,而非直接输出
  221. curl_setopt($curl_session, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);//采用1.0版的HTTP协议
  222. curl_setopt($curl_session, CURLOPT_SSL_VERIFYPEER, false);
  223. curl_setopt($curl_session, CURLOPT_SSL_VERIFYHOST, false);
  224. $url_parts = $this->parse_raw_url($url);
  225. /* 设置验证策略 */
  226. if (!empty($url_parts['user']))
  227. {
  228. $auth = $url_parts['user'] . ':' . $url_parts['pass'];
  229. curl_setopt($curl_session, CURLOPT_USERPWD, $auth);
  230. curl_setopt($curl_session, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
  231. }
  232. $header = array();
  233. /* 设置主机 */
  234. $header[] = 'Host: ' . $url_parts['host'];
  235. /* 格式化自定义头部信息 */
  236. if ($my_header && is_array($my_header))
  237. {
  238. foreach ($my_header AS $key => $value)
  239. {
  240. $header[] = $key . ': ' . $value;
  241. }
  242. }
  243. if ($method === 'GET')
  244. {
  245. curl_setopt($curl_session, CURLOPT_HTTPGET, true);
  246. $url .= $params ? '?' . $params : '';
  247. }
  248. else
  249. {
  250. curl_setopt($curl_session, CURLOPT_POST, true);
  251. $header[] = 'Content-Type: application/x-www-form-urlencoded';
  252. $header[] = 'Content-Length: ' . strlen($params);
  253. curl_setopt($curl_session, CURLOPT_POSTFIELDS, $params);
  254. }
  255. /* 设置请求地址 */
  256. curl_setopt($curl_session, CURLOPT_URL, $url);
  257. /* 设置头部信息 */
  258. curl_setopt($curl_session, CURLOPT_HTTPHEADER, $header);
  259. if ($this->connect_timeout > -1)
  260. {
  261. curl_setopt($curl_session, CURLOPT_CONNECTTIMEOUT, $this->connect_timeout);
  262. }
  263. if ($this->stream_timeout > -1)
  264. {
  265. curl_setopt($curl_session, CURLOPT_TIMEOUT, $this->stream_timeout);
  266. }
  267. /* 发送请求 */
  268. $http_response = curl_exec($curl_session);
  269. if (curl_errno($curl_session) != 0)
  270. {
  271. return false;
  272. }
  273. $separator = '/\r\n\r\n|\n\n|\r\r/';
  274. list($http_header, $http_body) = preg_split($separator, $http_response, 2);
  275. $http_response = array('header' => $http_header,//肯定有值
  276. 'body' => $http_body); //可能为空
  277. curl_close($curl_session);
  278. return $http_response;
  279. }
  280. /**
  281. * Similar to PHP's builtin parse_url() function, but makes sure what the schema,
  282. * path and port keys are set to http, /, 80 respectively if they're missing
  283. *
  284. * @access private
  285. * @param string $raw_url Raw URL to be split into an array
  286. * @author http://www.cpaint.net/
  287. * @return array
  288. */
  289. function parse_raw_url($raw_url)
  290. {
  291. $retval = array();
  292. $raw_url = (string) $raw_url;
  293. // make sure parse_url() recognizes the URL correctly.
  294. if (strpos($raw_url, '://') === false)
  295. {
  296. $raw_url = 'http://' . $raw_url;
  297. }
  298. // split request into array
  299. $retval = parse_url($raw_url);
  300. // make sure a path key exists
  301. if (!isset($retval['path']))
  302. {
  303. $retval['path'] = '/';
  304. }
  305. // set port to 80 if none exists
  306. if (!isset($retval['port']))
  307. {
  308. $retval['port'] = '80';
  309. }
  310. return $retval;
  311. }
  312. /**
  313. * 产生一个换行符,不同的操作系统会有不同的换行符
  314. *
  315. * @access private
  316. * @return string 用双引号引用的换行符
  317. */
  318. function generate_crlf()
  319. {
  320. $crlf = '';
  321. if (strtoupper(substr(PHP_OS, 0, 3) === 'WIN'))
  322. {
  323. $crlf = "\r\n";
  324. }
  325. elseif (strtoupper(substr(PHP_OS, 0, 3) === 'MAC'))
  326. {
  327. $crlf = "\r";
  328. }
  329. else
  330. {
  331. $crlf = "\n";
  332. }
  333. return $crlf;
  334. }
  335. }
  336. ?>