国内流行的内容管理系统(CMS)多端全媒体解决方案 https://www.dedebiz.com
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

631 строка
19KB

  1. <?php
  2. if (!defined('DEDEINC')) exit ('dedebiz');
  3. /**
  4. * HTTP下载类
  5. *
  6. * @version $id:dedehttpdown.class.php 11:42 2010年7月6日 tianya $
  7. * @package DedeBIZ.Libraries
  8. * @copyright Copyright (c) 2022 DedeBIZ.COM
  9. * @license GNU GPL v2 (https://www.dedebiz.com/license)
  10. * @link https://www.dedebiz.com
  11. */
  12. @set_time_limit(0);
  13. class DedeHttpDown
  14. {
  15. var $m_ch = null;
  16. var $m_url = '';
  17. var $m_urlpath = '';
  18. var $m_scheme = 'http';
  19. var $m_host = '';
  20. var $m_port = '80';
  21. var $m_user = '';
  22. var $m_pass = '';
  23. var $m_path = '/';
  24. var $m_query = '';
  25. var $m_fp = '';
  26. var $m_error = '';
  27. var $m_httphead = array();
  28. var $m_html = '';
  29. var $m_puthead = array();
  30. var $m_cookies = '';
  31. var $BaseUrlPath = '';
  32. var $HomeUrl = '';
  33. var $reTry = 0;
  34. var $JumpCount = 0;
  35. /**
  36. * 初始化系统
  37. *
  38. * @access public
  39. * @param string $url 需要下载的地址
  40. * @return string
  41. */
  42. function PrivateInit($url)
  43. {
  44. if ($url == '') {
  45. return;
  46. }
  47. $urls = '';
  48. $urls = @parse_url($url);
  49. $this->m_url = $url;
  50. if (is_array($urls)) {
  51. $this->m_host = $urls["host"];
  52. if (!empty($urls["scheme"])) {
  53. $this->m_scheme = $urls["scheme"];
  54. }
  55. if (!empty($urls["user"])) {
  56. $this->m_user = $urls["user"];
  57. }
  58. if (!empty($urls["pass"])) {
  59. $this->m_pass = $urls["pass"];
  60. }
  61. if (!empty($urls["port"])) {
  62. $this->m_port = $urls["port"];
  63. }
  64. if (!empty($urls["path"])) {
  65. $this->m_path = $urls["path"];
  66. }
  67. $this->m_urlpath = $this->m_path;
  68. if (!empty($urls["query"])) {
  69. $this->m_query = $urls["query"];
  70. $this->m_urlpath .= "?".$this->m_query;
  71. }
  72. $this->HomeUrl = $urls["host"];
  73. $this->BaseUrlPath = $this->HomeUrl.$urls["path"];
  74. $this->BaseUrlPath = preg_replace("/\/([^\/]*)\.(.*)$/", "/", $this->BaseUrlPath);
  75. $this->BaseUrlPath = preg_replace("/\/$/", "", $this->BaseUrlPath);
  76. }
  77. }
  78. /**
  79. * 重设各参数
  80. *
  81. * @access public
  82. * @return void
  83. */
  84. function ResetAny()
  85. {
  86. $this->m_ch = '';
  87. $this->m_url = '';
  88. $this->m_urlpath = '';
  89. $this->m_scheme = "http";
  90. $this->m_host = '';
  91. $this->m_port = "80";
  92. $this->m_user = '';
  93. $this->m_pass = '';
  94. $this->m_path = "/";
  95. $this->m_query = '';
  96. $this->m_cookies = '';
  97. $this->m_error = '';
  98. }
  99. /**
  100. * 打开指定网址
  101. *
  102. * @access public
  103. * @param string $url 地址
  104. * @param string $requestType 请求类型
  105. * @return string
  106. */
  107. function OpenUrl($url, $requestType = "GET")
  108. {
  109. $this->ResetAny();
  110. $this->JumpCount = 0;
  111. $this->m_httphead = array();
  112. $this->m_html = '';
  113. $this->reTry = 0;
  114. $this->Close();
  115. //初始化系统
  116. $this->PrivateInit($url);
  117. $this->PrivateStartSession($requestType);
  118. }
  119. /**
  120. * 跳转303重定向网址
  121. *
  122. * @access public
  123. * @param string $url 地址
  124. * @return string
  125. */
  126. function JumpOpenUrl($url)
  127. {
  128. $this->ResetAny();
  129. $this->JumpCount++;
  130. $this->m_httphead = array();
  131. $this->m_html = '';
  132. $this->Close();
  133. //初始化系统
  134. $this->PrivateInit($url);
  135. $this->PrivateStartSession('GET');
  136. }
  137. /**
  138. * 获得某操作错误的原因
  139. *
  140. * @access public
  141. * @return void
  142. */
  143. function printError()
  144. {
  145. echo "错误信息:".$this->m_error;
  146. echo "<br>具体返回头:<br>";
  147. foreach ($this->m_httphead as $k => $v) {
  148. echo "$k => $v <br>\r\n";
  149. }
  150. }
  151. /**
  152. * 判别用Get方法发送的头的应答结果是否正确
  153. *
  154. * @access public
  155. * @return bool
  156. */
  157. function IsGetOK()
  158. {
  159. if (preg_match("/^2/", $this->GetHead("http-state"))) {
  160. return TRUE;
  161. } else {
  162. $this->m_error .= $this->GetHead("http-state")." - ".$this->GetHead("http-describe")."<br>";
  163. return FALSE;
  164. }
  165. }
  166. /**
  167. * 看看返回的网页是否是text类型
  168. *
  169. * @access public
  170. * @return bool
  171. */
  172. function IsText()
  173. {
  174. if (preg_match("/^2/", $this->GetHead("http-state")) && preg_match("/text|xml/i", $this->GetHead("content-type"))) {
  175. return TRUE;
  176. } else {
  177. $this->m_error .= "文档为非文本类型或网址重定向<br>";
  178. return FALSE;
  179. }
  180. }
  181. /**
  182. * 判断返回的网页是否是特定的类型
  183. *
  184. * @access public
  185. * @param string $ctype 文档类型
  186. * @return string
  187. */
  188. function IsContentType($ctype)
  189. {
  190. if (
  191. preg_match("/^2/", $this->GetHead("http-state"))
  192. && $this->GetHead("content-type") == strtolower($ctype)
  193. ) {
  194. return TRUE;
  195. } else {
  196. $this->m_error .= "类型不对 ".$this->GetHead("content-type")."<br>";
  197. return FALSE;
  198. }
  199. }
  200. /**
  201. * 用Http协议下载文件
  202. *
  203. * @access public
  204. * @param string $savefilename 保存文件名称
  205. * @return string
  206. */
  207. function SaveToBin($savefilename)
  208. {
  209. if (!$this->IsGetOK()) {
  210. return FALSE;
  211. }
  212. if (function_exists('curl_init') && function_exists('curl_exec')) {
  213. file_put_contents($savefilename, $this->m_html);
  214. return TRUE;
  215. }
  216. if (@feof($this->m_fp)) {
  217. $this->m_error = "连接已经关闭";
  218. return FALSE;
  219. }
  220. $fp = fopen($savefilename, "w");
  221. while (!feof($this->m_fp)) {
  222. fwrite($fp, fread($this->m_fp, 1024));
  223. }
  224. fclose($this->m_fp);
  225. fclose($fp);
  226. return TRUE;
  227. }
  228. /**
  229. * 保存网页文档为Text文件
  230. *
  231. * @access public
  232. * @param string $savefilename 保存文件名称
  233. * @return string
  234. */
  235. function SaveToText($savefilename)
  236. {
  237. if ($this->IsText()) {
  238. $this->SaveBinFile($savefilename);
  239. } else {
  240. return "";
  241. }
  242. }
  243. function SaveBinFile($filename)
  244. {
  245. return $this->SaveBinFile($filename);
  246. }
  247. /**
  248. * 用Http协议获得一个网页的文档
  249. *
  250. * @access public
  251. * @return string
  252. */
  253. function GetHtml()
  254. {
  255. if ($this->m_html != '') {
  256. return $this->m_html;
  257. }
  258. if (!$this->IsText()) {
  259. return '';
  260. }
  261. if (!$this->m_fp || @feof($this->m_fp)) {
  262. return '';
  263. }
  264. while (!feof($this->m_fp)) {
  265. $this->m_html .= fgets($this->m_fp, 256);
  266. }
  267. @fclose($this->m_fp);
  268. return $this->m_html;
  269. }
  270. /**
  271. * 获取请求解析后的JSON数据
  272. *
  273. * @access public
  274. * @return mixed
  275. */
  276. function GetJSON()
  277. {
  278. if ($this->m_html != '') {
  279. return json_decode($this->m_html);
  280. }
  281. if (!$this->IsText()) {
  282. return '';
  283. }
  284. if (!$this->m_fp || @feof($this->m_fp)) {
  285. return '';
  286. }
  287. while (!feof($this->m_fp)) {
  288. $this->m_html .= fgets($this->m_fp, 256);
  289. }
  290. @fclose($this->m_fp);
  291. return json_decode($this->m_html);
  292. }
  293. /**
  294. * 判断当前是否是https站点
  295. *
  296. * @access public
  297. * @return bool
  298. */
  299. function IsSSL()
  300. {
  301. if ($_SERVER['HTTPS'] && ('1' == $_SERVER['HTTPS'] || 'on' == strtolower($_SERVER['HTTPS']))) {
  302. return true;
  303. } elseif ('https' == $_SERVER['REQUEST_SCHEME']) {
  304. return true;
  305. } elseif ('443' == $_SERVER['SERVER_PORT']) {
  306. return true;
  307. } elseif ('https' == $_SERVER['HTTP_X_FORWARDED_PROTO']) {
  308. return true;
  309. }
  310. return false;
  311. }
  312. /**
  313. * 开始HTTP会话
  314. *
  315. * @access public
  316. * @param string $requestType 请求类型
  317. * @return string
  318. */
  319. function PrivateStartSession($requestType = "GET")
  320. {
  321. if ($this->m_scheme == "https") {
  322. $this->m_port = "443";
  323. }
  324. if (function_exists('curl_init') && function_exists('curl_exec')) {
  325. $this->m_ch = curl_init();
  326. curl_setopt($this->m_ch, CURLOPT_URL, $this->m_url);
  327. curl_setopt($this->m_ch, CURLOPT_RETURNTRANSFER, 1);
  328. curl_setopt($this->m_ch, CURLOPT_FOLLOWLOCATION, 1);
  329. if ($requestType == "POST") {
  330. curl_setopt($this->m_ch, CURLOPT_POST, 1);
  331. //$content = is_array($post) ? http_build_query($post) : $post;
  332. //curl_setopt($this->m_ch, CURLOPT_POSTFIELDS, urldecode($content));
  333. }
  334. if (!empty($this->m_cookies)) {
  335. curl_setopt($this->m_ch, CURLOPT_COOKIE, $this->m_cookies);
  336. }
  337. if ($this->m_scheme == "https") {
  338. curl_setopt($this->m_ch, CURLOPT_SSL_VERIFYPEER, false);
  339. curl_setopt($this->m_ch, CURLOPT_SSL_VERIFYHOST, false);
  340. }
  341. $this->m_puthead = array();
  342. $this->m_puthead["Host"] = $this->m_host;
  343. //发送会员自定义的请求头
  344. if (!isset($this->m_puthead["Accept"])) {
  345. $this->m_puthead["Accept"] = "*/*";
  346. }
  347. if (!isset($this->m_puthead["User-Agent"])) {
  348. $this->m_puthead["User-Agent"] = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2)";
  349. }
  350. if (!isset($this->m_puthead["Refer"])) {
  351. $this->m_puthead["Refer"] = "http://".$this->m_puthead["Host"];
  352. }
  353. $headers = array();
  354. foreach ($this->m_puthead as $k => $v) {
  355. $k = trim($k);
  356. $v = trim($v);
  357. if ($k != "" && $v != "") {
  358. $headers[] = "$k: $v";
  359. }
  360. }
  361. if (count($headers) > 0) {
  362. curl_setopt($this->m_ch, CURLOPT_HTTPHEADER, $headers);
  363. }
  364. curl_setopt($this->m_ch, CURLOPT_CONNECTTIMEOUT, 20);
  365. curl_setopt($this->m_ch, CURLOPT_TIMEOUT, 900);
  366. $this->m_html = curl_exec($this->m_ch);
  367. $status = curl_getinfo($this->m_ch);
  368. if (count($status) > 0) {
  369. foreach ($status as $key => $value) {
  370. $key = str_replace("_", "-", $key);
  371. if ($key == "http-code") {
  372. $this->m_httphead["http-state"] = $value;
  373. }
  374. $this->m_httphead[$key] = $value;
  375. }
  376. }
  377. $this->m_error = curl_errno($this->m_ch);
  378. return TRUE;
  379. }
  380. if (!$this->PrivateOpenHost()) {
  381. $this->m_error .= "打开远程主机出错!";
  382. return FALSE;
  383. }
  384. $this->reTry++;
  385. if ($this->GetHead("http-edition") == "HTTP/1.1") {
  386. $httpv = "HTTP/1.1";
  387. } else {
  388. $httpv = "HTTP/1.0";
  389. }
  390. $ps = explode('?', $this->m_urlpath);
  391. $headString = '';
  392. //发送固定的起始请求头GET、Host信息
  393. if ($requestType == "GET") {
  394. $headString .= "GET ".$this->m_urlpath." $httpv\r\n";
  395. } else {
  396. $headString .= "POST ".$ps[0]." $httpv\r\n";
  397. }
  398. $this->m_puthead["Host"] = $this->m_host;
  399. //发送会员自定义的请求头
  400. if (!isset($this->m_puthead["Accept"])) {
  401. $this->m_puthead["Accept"] = "*/*";
  402. }
  403. if (!isset($this->m_puthead["User-Agent"])) {
  404. $this->m_puthead["User-Agent"] = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2)";
  405. }
  406. if (!isset($this->m_puthead["Refer"])) {
  407. $this->m_puthead["Refer"] = "http://".$this->m_puthead["Host"];
  408. }
  409. foreach ($this->m_puthead as $k => $v) {
  410. $k = trim($k);
  411. $v = trim($v);
  412. if ($k != "" && $v != "") {
  413. $headString .= "$k: $v\r\n";
  414. }
  415. }
  416. fputs($this->m_fp, $headString);
  417. if ($requestType == "POST") {
  418. $postdata = '';
  419. if (count($ps) > 1) {
  420. for ($i = 1; $i < count($ps); $i++) {
  421. $postdata .= $ps[$i];
  422. }
  423. } else {
  424. $postdata = "OK";
  425. }
  426. $plen = strlen($postdata);
  427. fputs($this->m_fp, "Content-Type: application/x-www-form-urlencoded\r\n");
  428. fputs($this->m_fp, "Content-Length: $plen\r\n");
  429. }
  430. //发送固定的结束请求头HTTP1.1协议必须指定文档结束后关闭链接,否则读取文档时无法使用feof判断结束
  431. if ($httpv == "HTTP/1.1") {
  432. fputs($this->m_fp, "Connection: Close\r\n\r\n");
  433. } else {
  434. fputs($this->m_fp, "\r\n");
  435. }
  436. if ($requestType == "POST") {
  437. fputs($this->m_fp, $postdata);
  438. }
  439. //获取应答头状态信息
  440. $httpstas = explode(" ", fgets($this->m_fp, 256));
  441. $this->m_httphead["http-edition"] = trim($httpstas[0]);
  442. $this->m_httphead["http-state"] = trim($httpstas[1]);
  443. $this->m_httphead["http-describe"] = '';
  444. for ($i = 2; $i < count($httpstas); $i++) {
  445. $this->m_httphead["http-describe"] .= " ".trim($httpstas[$i]);
  446. }
  447. //获取详细应答头
  448. while (!feof($this->m_fp)) {
  449. $line = trim(fgets($this->m_fp, 256));
  450. if ($line == "") {
  451. break;
  452. }
  453. $hkey = '';
  454. $hvalue = '';
  455. $v = 0;
  456. for ($i = 0; $i < strlen($line); $i++) {
  457. if ($v == 1) {
  458. $hvalue .= $line[$i];
  459. }
  460. if ($line[$i] == ":") {
  461. $v = 1;
  462. }
  463. if ($v == 0) {
  464. $hkey .= $line[$i];
  465. }
  466. }
  467. $hkey = trim($hkey);
  468. if ($hkey != "") {
  469. $this->m_httphead[strtolower($hkey)] = trim($hvalue);
  470. }
  471. }
  472. //如果连接被不正常关闭,重试
  473. if (feof($this->m_fp)) {
  474. if ($this->reTry > 10) {
  475. return FALSE;
  476. }
  477. $this->PrivateStartSession($requestType);
  478. }
  479. //判断是否是3xx开头的应答
  480. if (preg_match("/^3/", $this->m_httphead["http-state"])) {
  481. if ($this->JumpCount > 3) {
  482. return;
  483. }
  484. if (isset($this->m_httphead["location"])) {
  485. $newurl = $this->m_httphead["location"];
  486. if (preg_match("/^http/i", $newurl)) {
  487. $this->JumpOpenUrl($newurl);
  488. } else {
  489. $newurl = $this->FillUrl($newurl);
  490. $this->JumpOpenUrl($newurl);
  491. }
  492. } else {
  493. $this->m_error = "无法识别的答复";
  494. }
  495. }
  496. }
  497. /**
  498. * 获得一个Http头的值
  499. *
  500. * @access public
  501. * @param string $headname 头文件名称
  502. * @return string
  503. */
  504. function GetHead($headname)
  505. {
  506. $headname = strtolower($headname);
  507. return isset($this->m_httphead[$headname]) ? $this->m_httphead[$headname] : '';
  508. }
  509. function SetCookie($cookie)
  510. {
  511. $this->m_cookies = $cookie;
  512. }
  513. /**
  514. * 设置Http头的值
  515. *
  516. * @access public
  517. * @param string $skey 键
  518. * @param string $svalue 值
  519. * @return string
  520. */
  521. function SetHead($skey, $svalue)
  522. {
  523. $this->m_puthead[$skey] = $svalue;
  524. }
  525. /**
  526. * 打开连接
  527. *
  528. * @access public
  529. * @return bool
  530. */
  531. function PrivateOpenHost()
  532. {
  533. if ($this->m_host == "") {
  534. return FALSE;
  535. }
  536. $errno = '';
  537. $errstr = '';
  538. $this->m_fp = @fsockopen($this->m_host, $this->m_port, $errno, $errstr, 10);
  539. if (!$this->m_fp) {
  540. $this->m_error = $errstr;
  541. return FALSE;
  542. } else {
  543. return TRUE;
  544. }
  545. }
  546. /**
  547. * 关闭连接
  548. *
  549. * @access public
  550. * @return void
  551. */
  552. function Close()
  553. {
  554. if (function_exists('curl_init') && function_exists('curl_exec') && $this->m_ch) {
  555. @curl_close($this->m_ch);
  556. }
  557. if ($this->m_fp) {
  558. @fclose($this->m_fp);
  559. }
  560. }
  561. /**
  562. * 补全相对网址
  563. *
  564. * @access public
  565. * @param string $surl 需要不全的地址
  566. * @return string
  567. */
  568. function FillUrl($surl)
  569. {
  570. $i = 0;
  571. $dstr = '';
  572. $pstr = '';
  573. $okurl = '';
  574. $pathStep = 0;
  575. $surl = trim($surl);
  576. if ($surl == "") {
  577. return "";
  578. }
  579. $pos = strpos($surl, "#");
  580. $proto = $this->IsSSL()? "https://" : "http://";
  581. if ($pos > 0) {
  582. $surl = substr($surl, 0, $pos);
  583. }
  584. if ($surl[0] == "/") {
  585. $okurl = $proto .$this->HomeUrl.$surl;
  586. } else if ($surl[0] == ".") {
  587. if (strlen($surl) <= 1) {
  588. return "";
  589. } else if ($surl[1] == "/") {
  590. $okurl = $proto.$this->BaseUrlPath."/".substr($surl, 2, strlen($surl) - 2);
  591. } else {
  592. $urls = explode("/", $surl);
  593. foreach ($urls as $u) {
  594. if ($u == "..") {
  595. $pathStep++;
  596. } else if ($i < count($urls) - 1) {
  597. $dstr .= $urls[$i]."/";
  598. } else {
  599. $dstr .= $urls[$i];
  600. }
  601. $i++;
  602. }
  603. $urls = explode("/", $this->BaseUrlPath);
  604. if (count($urls) <= $pathStep) {
  605. return "";
  606. } else {
  607. $pstr = $proto;
  608. for ($i = 0; $i < count($urls) - $pathStep; $i++) {
  609. $pstr .= $urls[$i]."/";
  610. }
  611. $okurl = $pstr.$dstr;
  612. }
  613. }
  614. } else {
  615. if (strlen($surl) < 7) {
  616. $okurl = $proto .$this->BaseUrlPath."/".$surl;
  617. } else if (strtolower(substr($surl, 0, 7)) == "http://") {
  618. $okurl = $surl;
  619. } else if (strtolower(substr($surl, 0, 8)) == "https://") {
  620. $okurl = $surl;
  621. } else {
  622. $okurl = $proto.$this->BaseUrlPath."/".$surl;
  623. }
  624. }
  625. $okurl = preg_replace("/^((http|https):\/\/)/i", "", $okurl);
  626. $okurl = preg_replace("/\/{1,}/", "/", $okurl);
  627. return $proto.$okurl;
  628. }
  629. }//End Class
  630. ?>