PageRenderTime 41ms CodeModel.GetById 14ms RepoModel.GetById 1ms app.codeStats 0ms

/fuel/core/classes/ftp.php

https://bitbucket.org/sriedel/iccrm-wip
PHP | 657 lines | 330 code | 95 blank | 232 comment | 67 complexity | c83daea8ae03fe5ada09024fe2bdf0e7 MD5 | raw file
Possible License(s): MIT
  1. <?php
  2. /**
  3. * Part of the Fuel framework.
  4. *
  5. * @package Fuel
  6. * @version 1.0
  7. * @author Fuel Development Team
  8. * @license MIT License
  9. * @copyright 2010 - 2012 Fuel Development Team
  10. * @link http://fuelphp.com
  11. */
  12. namespace Fuel\Core;
  13. class FtpConnectionException extends \FuelException {}
  14. class FtpFileAccessException extends \FuelException {}
  15. /**
  16. * FTP Class
  17. *
  18. * @package Fuel
  19. * @category Core
  20. * @author Phil Sturgeon
  21. * @link http://docs.fuelphp.com/classes/ftp.html
  22. */
  23. class Ftp
  24. {
  25. public static $initialized = false;
  26. protected $_hostname = 'localhost';
  27. protected $_username = '';
  28. protected $_password = '';
  29. protected $_port = 21;
  30. protected $_timeout = 90;
  31. protected $_passive = true;
  32. protected $_debug = false;
  33. protected $_conn_id = false;
  34. /**
  35. * Returns a new Ftp object. If you do not define the "file" parameter,
  36. *
  37. * $ftp = static::forge('group');
  38. *
  39. * @param string|array The name of the config group to use, or a configuration array.
  40. * @param bool Automatically connect to this server.
  41. * @return Ftp
  42. */
  43. public static function forge($config = 'default', $connect = true)
  44. {
  45. $ftp = new static($config);
  46. // Unless told not to, connect automatically
  47. $connect === true and $ftp->connect();
  48. return $ftp;
  49. }
  50. /**
  51. * Sets the initial Ftp filename and local data.
  52. *
  53. * @param string|array The name of the config group to use, or a configuration array.
  54. * @param bool Automatically connect to this server.
  55. * @return void
  56. */
  57. public function __construct($config = 'default')
  58. {
  59. \Config::load('ftp', true);
  60. // If it is a string we're looking at a predefined config group
  61. if (is_string($config))
  62. {
  63. $config_arr = \Config::get('ftp.'.$config);
  64. // Check that it exists
  65. if ( ! is_array($config_arr) or $config_arr === array())
  66. {
  67. throw new \UnexpectedValueException('You have specified an invalid ftp connection group: '.$config);
  68. }
  69. $config = $config_arr;
  70. }
  71. // Prep the hostname
  72. $this->_hostname = preg_replace('|.+?://|', '', $config['hostname']);
  73. $this->_username = $config['username'];
  74. $this->_password = $config['password'];
  75. $this->_timeout = ! empty($config['timeout']) ? (int) $config['timeout'] : 90;
  76. $this->_port = ! empty($config['port']) ? (int) $config['port'] : 21;
  77. $this->_passive = (bool) $config['passive'];
  78. $this->_ssl_mode = (bool) $config['ssl_mode'];
  79. $this->_debug = (bool) $config['debug'];
  80. static::$initialized = true;
  81. }
  82. // --------------------------------------------------------------------
  83. /**
  84. * FTP Connect
  85. *
  86. * @access public
  87. * @param array the connection values
  88. * @return bool
  89. */
  90. public function connect()
  91. {
  92. if($this->_ssl_mode === true)
  93. {
  94. if( ! function_exists('ftp_ssl_connect'))
  95. {
  96. throw new \RuntimeException('ftp_ssl_connect() function is missing.');
  97. }
  98. $this->_conn_id = @ftp_ssl_connect($this->_hostname, $this->_port, $this->_timeout);
  99. }
  100. else
  101. {
  102. $this->_conn_id = @ftp_connect($this->_hostname, $this->_port, $this->_timeout);
  103. }
  104. if ($this->_conn_id === false)
  105. {
  106. if ($this->_debug == true)
  107. {
  108. throw new \FtpConnectionException('Unable to establish a connection');
  109. }
  110. return false;
  111. }
  112. if ( ! $this->_login())
  113. {
  114. if ($this->_debug == true)
  115. {
  116. throw new \FtpConnectionException('Unable to login');
  117. }
  118. }
  119. // Set passive mode if needed
  120. if ($this->_passive == true)
  121. {
  122. ftp_pasv($this->_conn_id, true);
  123. }
  124. return $this;
  125. }
  126. // --------------------------------------------------------------------
  127. /**
  128. * FTP Login
  129. *
  130. * @return bool
  131. */
  132. protected function _login()
  133. {
  134. return @ftp_login($this->_conn_id, $this->_username, $this->_password);
  135. }
  136. // --------------------------------------------------------------------
  137. /**
  138. * Validates the connection ID
  139. *
  140. * @return bool
  141. */
  142. protected function _is_conn()
  143. {
  144. if ( ! is_resource($this->_conn_id))
  145. {
  146. if ($this->_debug == true)
  147. {
  148. throw new \InvalidArgumentException('Invalid connection');
  149. }
  150. return false;
  151. }
  152. return true;
  153. }
  154. // --------------------------------------------------------------------
  155. /**
  156. * Change directory
  157. *
  158. * The second parameter lets us momentarily turn off debugging so that
  159. * this function can be used to test for the existence of a folder
  160. * without throwing an error. There's no FTP equivalent to is_dir()
  161. * so we do it by trying to change to a particular directory.
  162. * Internally, this parameter is only used by the "mirror" function below.
  163. *
  164. * @access public
  165. * @param string
  166. * @param bool
  167. * @return bool
  168. */
  169. public function change_dir($path = '')
  170. {
  171. if ($path == '' or ! $this->_is_conn())
  172. {
  173. return false;
  174. }
  175. $result = @ftp_chdir($this->_conn_id, $path);
  176. if ($result === false)
  177. {
  178. if ($this->_debug == true)
  179. {
  180. throw new \FtpFileAccessException('Unable to change the directory');
  181. }
  182. return false;
  183. }
  184. return true;
  185. }
  186. // --------------------------------------------------------------------
  187. /**
  188. * Create a directory
  189. *
  190. * @access public
  191. * @param string
  192. * @return bool
  193. */
  194. public function mkdir($path, $permissions = null)
  195. {
  196. if ( ! $this->_is_conn())
  197. {
  198. return false;
  199. }
  200. $result = ftp_mkdir($this->_conn_id, $path);
  201. if ($result === false)
  202. {
  203. if ($this->_debug == true)
  204. {
  205. throw new \FtpFileAccessException('Unable to create directory');
  206. }
  207. return false;
  208. }
  209. // Set file permissions if needed
  210. if ($permissions !== null)
  211. {
  212. $this->chmod($path, (int) $permissions);
  213. }
  214. return true;
  215. }
  216. // --------------------------------------------------------------------
  217. /**
  218. * Upload a file to the server
  219. *
  220. * @access public
  221. * @param string
  222. * @param string
  223. * @param string
  224. * @return bool
  225. */
  226. public function upload($local_path, $remote_path, $mode = 'auto', $permissions = null)
  227. {
  228. if ( ! $this->_is_conn())
  229. {
  230. return false;
  231. }
  232. if ( ! file_exists($local_path))
  233. {
  234. throw new \FtpFileAccessException('No source file');
  235. return false;
  236. }
  237. // Set the mode if not specified
  238. if ($mode == 'auto')
  239. {
  240. // Get the file extension so we can set the upload type
  241. $ext = pathinfo($local_path, PATHINFO_EXTENSION);
  242. $mode = $this->_settype($ext);
  243. }
  244. $mode = ($mode == 'ascii') ? FTP_ASCII : FTP_BINARY;
  245. $result = @ftp_put($this->_conn_id, $remote_path, $local_path, $mode);
  246. if ($result === false)
  247. {
  248. if ($this->_debug == true)
  249. {
  250. throw new \FtpFileAccessException('Unable to upload');
  251. }
  252. return false;
  253. }
  254. // Set file permissions if needed
  255. if ($permissions !== null)
  256. {
  257. $this->chmod($remote_path, (int) $permissions);
  258. }
  259. return true;
  260. }
  261. // --------------------------------------------------------------------
  262. /**
  263. * Download a file from a remote server to the local server
  264. *
  265. * @access public
  266. * @param string
  267. * @param string
  268. * @param string
  269. * @return bool
  270. */
  271. public function download($remote_path, $local_path, $mode = 'auto')
  272. {
  273. if ( ! $this->_is_conn())
  274. {
  275. return false;
  276. }
  277. // Set the mode if not specified
  278. if ($mode == 'auto')
  279. {
  280. // Get the file extension so we can set the upload type
  281. $ext = pathinfo($remote_path, PATHINFO_BASENAME);
  282. $mode = $this->_settype($ext);
  283. }
  284. $mode = ($mode == 'ascii') ? FTP_ASCII : FTP_BINARY;
  285. $result = @ftp_get($this->_conn_id, $local_path, $remote_path, $mode);
  286. if ($result === false)
  287. {
  288. if ($this->_debug === true)
  289. {
  290. throw new \FtpFileAccessException('Unable to download');
  291. }
  292. return false;
  293. }
  294. return true;
  295. }
  296. // --------------------------------------------------------------------
  297. /**
  298. * Rename (or move) a file
  299. *
  300. * @access public
  301. * @param string
  302. * @param string
  303. * @param bool
  304. * @return bool
  305. */
  306. public function rename($old_file, $new_file, $move = false)
  307. {
  308. if ( ! $this->_is_conn())
  309. {
  310. return false;
  311. }
  312. $result = @ftp_rename($this->_conn_id, $old_file, $new_file);
  313. if ($result === false)
  314. {
  315. if ($this->_debug == true)
  316. {
  317. $msg = ($move == false) ? 'Unable to rename' : 'Unable to move';
  318. throw new \FtpFileAccessException($msg);
  319. }
  320. return false;
  321. }
  322. return true;
  323. }
  324. // --------------------------------------------------------------------
  325. /**
  326. * Move a file
  327. *
  328. * @access public
  329. * @param string
  330. * @param string
  331. * @return bool
  332. */
  333. public function move($old_file, $new_file)
  334. {
  335. return $this->rename($old_file, $new_file, true);
  336. }
  337. // --------------------------------------------------------------------
  338. /**
  339. * Rename (or move) a file
  340. *
  341. * @access public
  342. * @param string
  343. * @return bool
  344. */
  345. function delete_file($filepath)
  346. {
  347. if ( ! $this->_is_conn())
  348. {
  349. return false;
  350. }
  351. $result = @ftp_delete($this->_conn_id, $filepath);
  352. if ($result === false)
  353. {
  354. if ($this->_debug == true)
  355. {
  356. throw new \FtpFileAccessException('Unable to delete');
  357. }
  358. return false;
  359. }
  360. return true;
  361. }
  362. // --------------------------------------------------------------------
  363. /**
  364. * Delete a folder and recursively delete everything (including sub-folders)
  365. * containted within it.
  366. *
  367. * @access public
  368. * @param string
  369. * @return bool
  370. */
  371. function delete_dir($filepath)
  372. {
  373. if ( ! $this->_is_conn())
  374. {
  375. return false;
  376. }
  377. // Add a trailing slash to the file path if needed
  378. $filepath = preg_replace("/(.+?)\/*$/", "\\1/", $filepath);
  379. $list = $this->list_files($filepath);
  380. if ($list !== false and count($list) > 0)
  381. {
  382. foreach ($list as $item)
  383. {
  384. // If we can't delete the item it's probaly a folder so
  385. // we'll recursively call delete_dir()
  386. if ( ! @ftp_delete($this->_conn_id, $item))
  387. {
  388. $this->delete_dir($item);
  389. }
  390. }
  391. }
  392. $result = @ftp_rmdir($this->_conn_id, $filepath);
  393. if ($result === false)
  394. {
  395. if ($this->_debug == true)
  396. {
  397. throw new \FtpFileAccessException('Unable to delete');
  398. }
  399. return false;
  400. }
  401. return true;
  402. }
  403. // --------------------------------------------------------------------
  404. /**
  405. * Set file permissions
  406. *
  407. * @access public
  408. * @param string the file path
  409. * @param string the permissions
  410. * @return bool
  411. */
  412. public function chmod($path, $perm)
  413. {
  414. if ( ! $this->_is_conn())
  415. {
  416. return false;
  417. }
  418. // Permissions can only be set when running PHP 5
  419. if ( ! function_exists('ftp_chmod'))
  420. {
  421. if ($this->_debug == true)
  422. {
  423. throw new \FtpFileAccessException('CHMOD function does not exist');
  424. }
  425. return false;
  426. }
  427. $result = @ftp_chmod($this->_conn_id, $perm, $path);
  428. if ($result === false)
  429. {
  430. if ($this->_debug == true)
  431. {
  432. throw new \FtpFileAccessException('Unable to CHMOD');
  433. }
  434. return false;
  435. }
  436. return true;
  437. }
  438. // --------------------------------------------------------------------
  439. /**
  440. * FTP List files in the specified directory
  441. *
  442. * @access public
  443. * @return array
  444. */
  445. public function list_files($path = '.')
  446. {
  447. if ( ! $this->_is_conn())
  448. {
  449. return false;
  450. }
  451. return ftp_nlist($this->_conn_id, $path);
  452. }
  453. // ------------------------------------------------------------------------
  454. /**
  455. * Read a directory and recreate it remotely
  456. *
  457. * This function recursively reads a folder and everything it contains (including
  458. * sub-folders) and creates a mirror via FTP based on it. Whatever the directory structure
  459. * of the original file path will be recreated on the server.
  460. *
  461. * @access public
  462. * @param string path to source with trailing slash
  463. * @param string path to destination - include the base folder with trailing slash
  464. * @return bool
  465. */
  466. public function mirror($local_path, $remote_path)
  467. {
  468. if ( ! $this->_is_conn())
  469. {
  470. return false;
  471. }
  472. // Open the local file path
  473. if ($fp = @opendir($local_path))
  474. {
  475. // Attempt to open the remote file path.
  476. if ( ! $this->change_dir($remote_path, true))
  477. {
  478. // If it doesn't exist we'll attempt to create the direcotory
  479. if ( ! $this->mkdir($remote_path) or ! $this->change_dir($remote_path))
  480. {
  481. return false;
  482. }
  483. }
  484. // Recursively read the local directory
  485. while (false !== ($file = readdir($fp)))
  486. {
  487. if (@is_dir($local_path.$file) and substr($file, 0, 1) != '.')
  488. {
  489. $this->mirror($local_path.$file."/", $remote_path.$file."/");
  490. }
  491. elseif (substr($file, 0, 1) != ".")
  492. {
  493. // Get the file extension so we can se the upload type
  494. $ext = pathinfo($file, PATHINFO_EXTENSION);
  495. $mode = $this->_settype($ext);
  496. $this->upload($local_path.$file, $remote_path.$file, $mode);
  497. }
  498. }
  499. return true;
  500. }
  501. return false;
  502. }
  503. // --------------------------------------------------------------------
  504. /**
  505. * Set the upload type
  506. *
  507. * @param string
  508. * @return string
  509. */
  510. protected function _settype($ext)
  511. {
  512. $text_types = array(
  513. 'txt',
  514. 'text',
  515. 'php',
  516. 'phps',
  517. 'php4',
  518. 'js',
  519. 'css',
  520. 'htm',
  521. 'html',
  522. 'phtml',
  523. 'shtml',
  524. 'log',
  525. 'xml'
  526. );
  527. return in_array($ext, $text_types) ? 'ascii' : 'binary';
  528. }
  529. // ------------------------------------------------------------------------
  530. /**
  531. * Close the connection
  532. *
  533. * @access public
  534. * @return void
  535. */
  536. public function close()
  537. {
  538. if ( ! $this->_is_conn())
  539. {
  540. return false;
  541. }
  542. @ftp_close($this->_conn_id);
  543. }
  544. // ------------------------------------------------------------------------
  545. /**
  546. * Close the connection when the class is unset
  547. *
  548. * @access public
  549. * @return void
  550. */
  551. public function __destruct()
  552. {
  553. $this->close();
  554. }
  555. }