ftp.php 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503
  1. <?php
  2. /**
  3. * FTP基本操作:
  4. * 1) 登陆; connect
  5. * 2) 当前目录文件列表; filelist
  6. * 3) 目录改变; chgdir
  7. * 4) 重命名/移动; rename
  8. * 5) 创建文件夹; mkdir
  9. * 6) 删除; delete_dir/delete_file
  10. * 7) 上传; upload
  11. * 8) 下载 download
  12. *
  13. */
  14. class Ftp {
  15. private $hostname = '';
  16. private $username = '';
  17. private $password = '';
  18. private $port = 21;
  19. private $passive = 1;
  20. private $debug = TRUE;
  21. private $conn_id = FALSE;
  22. private $ssl = 0;
  23. private $timeout = 0;
  24. private $rootdir = '';
  25. /**
  26. * 构造函数
  27. *
  28. * @param array 配置数组
  29. */
  30. public function __construct($config = array()) {
  31. if(count($config) > 0) {
  32. $this->_init($config);
  33. }
  34. }
  35. /**
  36. * FTP连接
  37. *
  38. * @access public
  39. * @param array 配置数组
  40. * @return boolean
  41. */
  42. public function connect($config = array()) {
  43. if(count($config) > 0) {
  44. $this->_init($config);
  45. }
  46. $funcname = $this->ssl && function_exists('ftp_ssl_connect') ? 'ftp_ssl_connect' : 'ftp_connect';
  47. if(!function_exists($funcname)) {
  48. return FALSE;
  49. }
  50. if(FALSE === ($this->conn_id = @$funcname($this->hostname, $this->port))) {
  51. if($this->debug === TRUE) {
  52. $this->_error("ftp_unable_to_connect");
  53. }
  54. return FALSE;
  55. }
  56. if (!empty($this->timeout)) {
  57. $this->set_option(FTP_TIMEOUT_SEC, $this->timeout);
  58. }
  59. if( ! $this->_login()) {
  60. if($this->debug === TRUE) {
  61. $this->_error("ftp_unable_to_login");
  62. }
  63. return FALSE;
  64. }
  65. if(!empty($this->passive)) {
  66. ftp_pasv($this->conn_id, TRUE);
  67. }
  68. if (!empty($this->rootdir)) {
  69. $this->chgdir($this->rootdir);
  70. }
  71. return TRUE;
  72. }
  73. /**
  74. * 目录改变
  75. *
  76. * @access public
  77. * @param string 目录标识(ftp)
  78. * @param boolean
  79. * @return boolean
  80. */
  81. public function chgdir($path = '', $supress_debug = FALSE) {
  82. if($path == '' OR ! $this->_isconn()) {
  83. return FALSE;
  84. }
  85. $result = @ftp_chdir($this->conn_id, $path);
  86. if($result === FALSE) {
  87. if($this->debug === TRUE AND $supress_debug == FALSE) {
  88. $this->_error("ftp_unable_to_chgdir:dir[".$path."]");
  89. }
  90. return FALSE;
  91. }
  92. return TRUE;
  93. }
  94. /**
  95. * 目录生成
  96. *
  97. * @access public
  98. * @param string 目录标识(ftp)
  99. * @param int 文件权限列表
  100. * @return boolean
  101. */
  102. public function mkdir($path = '', $permissions = NULL) {
  103. if($path == '' OR ! $this->_isconn()) {
  104. return FALSE;
  105. }
  106. $result = @ftp_mkdir($this->conn_id, $path);
  107. if($result === FALSE) {
  108. if($this->debug === TRUE) {
  109. $this->_error("ftp_unable_to_mkdir:dir[".$path."]");
  110. }
  111. return FALSE;
  112. }
  113. if( ! is_null($permissions)) {
  114. $this->chmod($path,(int)$permissions);
  115. }
  116. return TRUE;
  117. }
  118. public function mkdirs($path = '', $permissions = NULL) {
  119. $targetpaths = explode('/', $path);
  120. $dir = ''; $comma = '';
  121. foreach($targetpaths as $pathitem) {
  122. $dir .= $comma.$pathitem;
  123. $comma = '/';
  124. $return = $this->mkdir($dir);
  125. }
  126. return $return;
  127. }
  128. /**
  129. * 上传
  130. *
  131. * @access public
  132. * @param string 本地目录标识
  133. * @param string 远程目录标识(ftp)
  134. * @param string 上传模式 auto || ascii
  135. * @param int 上传后的文件权限列表
  136. * @return boolean
  137. */
  138. public function upload($localpath, $remotepath, $mode = 'auto', $permissions = NULL) {
  139. if( ! $this->_isconn()) {
  140. return FALSE;
  141. }
  142. if( ! file_exists($localpath)) {
  143. if($this->debug === TRUE) {
  144. $this->_error("ftp_no_source_file:".$localpath);
  145. }
  146. return FALSE;
  147. }
  148. if($mode == 'auto') {
  149. $ext = $this->_getext($localpath);
  150. $mode = $this->_settype($ext);
  151. }
  152. $mode = ($mode == 'ascii') ? FTP_ASCII : FTP_BINARY;
  153. $old_dir = $this->ftp_pwd();
  154. $dirname = dirname($remotepath);
  155. $filename = basename($remotepath);
  156. if(!$this->chgdir($dirname)) {
  157. if($this->mkdirs($dirname)) {
  158. if(!$this->chgdir($dirname)) {
  159. $this->_error("ftp_error_change_dir");
  160. }
  161. } else {
  162. $this->_error("ftp_error_make_dir");
  163. $this->set_error(FTP_ERR_MKDIR);
  164. }
  165. }
  166. if($fp = @fopen($localpath, 'rb')) {
  167. $result = ftp_fput($this->conn_id, $filename, $fp, $mode);
  168. @fclose($fp);
  169. } else {
  170. $this->_error("ftp_error_read_file");
  171. }
  172. if($result === FALSE) {
  173. if($this->debug === TRUE) {
  174. $this->_error("ftp_unable_to_upload:localpath[".$localpath."]/remotepath[".$remotepath."]");
  175. }
  176. return FALSE;
  177. }
  178. if( ! is_null($permissions)) {
  179. $this->chmod($remotepath,(int)$permissions);
  180. }
  181. $this->chgdir($old_dir);
  182. return TRUE;
  183. }
  184. /**
  185. * 下载
  186. *
  187. * @access public
  188. * @param string 远程目录标识(ftp)
  189. * @param string 本地目录标识
  190. * @param string 下载模式 auto || ascii
  191. * @return boolean
  192. */
  193. public function download($remotepath, $localpath, $mode = 'auto') {
  194. if( ! $this->_isconn()) {
  195. return FALSE;
  196. }
  197. if($mode == 'auto') {
  198. $ext = $this->_getext($remotepath);
  199. $mode = $this->_settype($ext);
  200. }
  201. $mode = ($mode == 'ascii') ? FTP_ASCII : FTP_BINARY;
  202. $result = @ftp_get($this->conn_id, $localpath, $remotepath, $mode);
  203. if($result === FALSE) {
  204. if($this->debug === TRUE) {
  205. $this->_error("ftp_unable_to_download:localpath[".$localpath."]-remotepath[".$remotepath."]");
  206. }
  207. return FALSE;
  208. }
  209. return TRUE;
  210. }
  211. /**
  212. * 重命名/移动
  213. *
  214. * @access public
  215. * @param string 远程目录标识(ftp)
  216. * @param string 新目录标识
  217. * @param boolean 判断是重命名(FALSE)还是移动(TRUE)
  218. * @return boolean
  219. */
  220. public function rename($oldname, $newname, $move = FALSE) {
  221. if( ! $this->_isconn()) {
  222. return FALSE;
  223. }
  224. $result = @ftp_rename($this->conn_id, $oldname, $newname);
  225. if($result === FALSE) {
  226. if($this->debug === TRUE) {
  227. $msg = ($move == FALSE) ? "ftp_unable_to_rename" : "ftp_unable_to_move";
  228. $this->_error($msg);
  229. }
  230. return FALSE;
  231. }
  232. return TRUE;
  233. }
  234. /**
  235. * 删除文件
  236. *
  237. * @access public
  238. * @param string 文件标识(ftp)
  239. * @return boolean
  240. */
  241. public function delete_file($file) {
  242. if( ! $this->_isconn()) {
  243. return FALSE;
  244. }
  245. $result = @ftp_delete($this->conn_id, $file);
  246. if($result === FALSE) {
  247. if($this->debug === TRUE) {
  248. $this->_error("ftp_unable_to_delete_file:file[".$file."]");
  249. }
  250. return FALSE;
  251. }
  252. return TRUE;
  253. }
  254. /**
  255. * 删除文件夹
  256. *
  257. * @access public
  258. * @param string 目录标识(ftp)
  259. * @return boolean
  260. */
  261. public function delete_dir($path) {
  262. if( ! $this->_isconn()) {
  263. return FALSE;
  264. }
  265. //对目录宏的'/'字符添加反斜杠'\'
  266. $path = preg_replace("/(.+?)\/*$/", "\\1/", $path);
  267. //获取目录文件列表
  268. $filelist = $this->filelist($path);
  269. if($filelist !== FALSE AND count($filelist) > 0) {
  270. foreach($filelist as $item) {
  271. //如果我们无法删除,那么就可能是一个文件夹
  272. //所以我们递归调用delete_dir()
  273. if( ! @delete_file($item)) {
  274. $this->delete_dir($item);
  275. }
  276. }
  277. }
  278. //删除文件夹(空文件夹)
  279. $result = @ftp_rmdir($this->conn_id, $path);
  280. if($result === FALSE) {
  281. if($this->debug === TRUE) {
  282. $this->_error("ftp_unable_to_delete_dir:dir[".$path."]");
  283. }
  284. return FALSE;
  285. }
  286. return TRUE;
  287. }
  288. /**
  289. * 修改文件权限
  290. *
  291. * @access public
  292. * @param string 目录标识(ftp)
  293. * @return boolean
  294. */
  295. public function chmod($path, $perm) {
  296. if( ! $this->_isconn()) {
  297. return FALSE;
  298. }
  299. //只有在PHP5中才定义了修改权限的函数(ftp)
  300. if( ! function_exists('ftp_chmod')) {
  301. if($this->debug === TRUE) {
  302. $this->_error("ftp_unable_to_chmod(function)");
  303. }
  304. return FALSE;
  305. }
  306. $result = @ftp_chmod($this->conn_id, $perm, $path);
  307. if($result === FALSE) {
  308. if($this->debug === TRUE) {
  309. $this->_error("ftp_unable_to_chmod:path[".$path."]-chmod[".$perm."]");
  310. }
  311. return FALSE;
  312. }
  313. return TRUE;
  314. }
  315. /**
  316. * 获取目录文件列表
  317. *
  318. * @access public
  319. * @param string 目录标识(ftp)
  320. * @return array
  321. */
  322. public function filelist($path = '.') {
  323. if( ! $this->_isconn()) {
  324. return FALSE;
  325. }
  326. return ftp_nlist($this->conn_id, $path);
  327. }
  328. public function ftp_pwd() {
  329. if( ! $this->_isconn()) {
  330. return FALSE;
  331. }
  332. return @ftp_pwd($this->conn_id);
  333. }
  334. /**
  335. * 关闭FTP
  336. *
  337. * @access public
  338. * @return boolean
  339. */
  340. public function close() {
  341. if( ! $this->_isconn()) {
  342. return FALSE;
  343. }
  344. return @ftp_close($this->conn_id);
  345. }
  346. public function set_option($cmd, $value) {
  347. if(function_exists('ftp_set_option')) {
  348. return @ftp_set_option($this->conn_id, $cmd, $value);
  349. }
  350. }
  351. /**
  352. * FTP成员变量初始化
  353. *
  354. * @access private
  355. * @param array 配置数组
  356. * @return void
  357. */
  358. private function _init($config = array()) {
  359. foreach($config as $key => $val) {
  360. if(isset($this->$key)) {
  361. $this->$key = $val;
  362. }
  363. }
  364. //特殊字符过滤
  365. $this->hostname = preg_replace('|.+?://|','',$this->hostname);
  366. }
  367. /**
  368. * FTP登陆
  369. *
  370. * @access private
  371. * @return boolean
  372. */
  373. private function _login() {
  374. return @ftp_login($this->conn_id, $this->username, $this->password);
  375. }
  376. /**
  377. * 判断con_id
  378. *
  379. * @access private
  380. * @return boolean
  381. */
  382. private function _isconn() {
  383. if( ! is_resource($this->conn_id)) {
  384. if($this->debug === TRUE) {
  385. $this->_error("ftp_no_connection");
  386. }
  387. return FALSE;
  388. }
  389. return TRUE;
  390. }
  391. /**
  392. * 从文件名中获取后缀扩展
  393. *
  394. * @access private
  395. * @param string 目录标识
  396. * @return string
  397. */
  398. private function _getext($filename) {
  399. if(FALSE === strpos($filename, '.')) {
  400. return 'txt';
  401. }
  402. $extarr = explode('.', $filename);
  403. return end($extarr);
  404. }
  405. /**
  406. * 从后缀扩展定义FTP传输模式 ascii 或 binary
  407. *
  408. * @access private
  409. * @param string 后缀扩展
  410. * @return string
  411. */
  412. private function _settype($ext) {
  413. $text_type = array (
  414. 'txt',
  415. 'text',
  416. 'php',
  417. 'phps',
  418. 'php4',
  419. 'js',
  420. 'css',
  421. 'htm',
  422. 'html',
  423. 'phtml',
  424. 'shtml',
  425. 'log',
  426. 'xml'
  427. );
  428. return (in_array($ext, $text_type)) ? 'ascii' : 'binary';
  429. }
  430. /**
  431. * 错误日志记录
  432. *
  433. * @access prvate
  434. * @return boolean
  435. */
  436. private function _error($msg) {
  437. // return @file_put_contents('ftp_err.log', "date[".date("Y-m-d H:i:s")."]-hostname[".$this->hostname."]-username[".$this->username."]-password[".$this->password."]-msg[".$msg."]\n", FILE_APPEND);
  438. }
  439. }