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