PageRenderTime 48ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/src/Ftp.php

http://github.com/dg/ftp-php
PHP | 265 lines | 141 code | 36 blank | 88 comment | 23 complexity | 42fb91518236fe402a9a55dc65476787 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. <?php
  2. /**
  3. * FTP - access to an FTP server.
  4. *
  5. * @author David Grudl
  6. * @copyright Copyright (c) 2008 David Grudl
  7. * @license New BSD License
  8. * @link http://phpfashion.com/
  9. * @version 1.2
  10. *
  11. * @method void alloc(int $filesize, string & $result) - Allocates space for a file to be uploaded
  12. * @method void cdUp() - Changes to the parent directory
  13. * @method void chDir(string $directory) - Changes the current directory on a FTP server
  14. * @method int chMod(int $mode, string $filename) - Set permissions on a file via FTP
  15. * @method void close() - Closes an FTP connection
  16. * @method void connect(string $host, int $port = 21, int $timeout = 90) - Opens an FTP connection
  17. * @method void delete(string $path) - Deletes a file on the FTP server
  18. * @method bool exec(string $command) - Requests execution of a command on the FTP server
  19. * @method void fGet(resource $handle, string $remote_file, int $mode, int $resumepos = 0) - Downloads a file from the FTP server and saves to an open file
  20. * @method void fPut(string $remote_file, resource $handle, int $mode, int $startpos = 0) - Uploads from an open file to the FTP server
  21. * @method mixed getOption(int $option) - Retrieves various runtime behaviours of the current FTP stream
  22. * @method void get(string $local_file, string $remote_file, int $mode, int $resumepos = 0) - Downloads a file from the FTP server
  23. * @method void login(string $username, string $password) - Logs in to an FTP connection
  24. * @method int mdTm(string $remote_file) - Returns the last modified time of the given file
  25. * @method string mkDir(string $directory) - Creates a directory
  26. * @method int nbContinue() - Continues retrieving/sending a file(non-blocking)
  27. * @method int nbFGet(resource $handle, string $remote_file, int $mode, int $resumepos = 0) - Retrieves a file from the FTP server and writes it to an open file(non-blocking)
  28. * @method int nbFPut(string $remote_file, resource $handle, int $mode, int $startpos = 0) - Stores a file from an open file to the FTP server(non-blocking)
  29. * @method int nbGet(string $local_file, string $remote_file, int $mode, int $resumepos = 0) - Retrieves a file from the FTP server and writes it to a local file(non-blocking)
  30. * @method int nbPut(string $remote_file, string $local_file, int $mode, int $startpos = 0) - Stores a file on the FTP server(non-blocking)
  31. * @method array nList(string $directory) - Returns a list of files in the given directory
  32. * @method void pasv(bool $pasv) - Turns passive mode on or off
  33. * @method void put(string $remote_file, string $local_file, int $mode, int $startpos = 0) - Uploads a file to the FTP server
  34. * @method string pwd() - Returns the current directory name
  35. * @method void quit() - Closes an FTP connection(alias of close)
  36. * @method array raw(string $command) - Sends an arbitrary command to an FTP server
  37. * @method mixed rawList(string $directory, bool $recursive = false) - Returns a detailed list of files in the given directory
  38. * @method void rename(string $oldname, string $newname) - Renames a file or a directory on the FTP server
  39. * @method void rmDir(string $directory) - Removes a directory
  40. * @method bool setOption(int $option, mixed $value) - Set miscellaneous runtime FTP options
  41. * @method void site(string $command) - Sends a SITE command to the server
  42. * @method int size(string $remote_file) - Returns the size of the given file
  43. * @method void sslConnect(string $host, int $port = 21, int $timeout = 90) - Opens an Secure SSL-FTP connection
  44. * @method string sysType() - Returns the system type identifier of the remote FTP server
  45. */
  46. class Ftp
  47. {
  48. /**#@+ FTP constant alias */
  49. const ASCII = FTP_ASCII;
  50. const TEXT = FTP_TEXT;
  51. const BINARY = FTP_BINARY;
  52. const IMAGE = FTP_IMAGE;
  53. const TIMEOUT_SEC = FTP_TIMEOUT_SEC;
  54. const AUTOSEEK = FTP_AUTOSEEK;
  55. const AUTORESUME = FTP_AUTORESUME;
  56. const FAILED = FTP_FAILED;
  57. const FINISHED = FTP_FINISHED;
  58. const MOREDATA = FTP_MOREDATA;
  59. /**#@-*/
  60. private static $aliases = array(
  61. 'sslconnect' => 'ssl_connect',
  62. 'getoption' => 'get_option',
  63. 'setoption' => 'set_option',
  64. 'nbcontinue' => 'nb_continue',
  65. 'nbfget' => 'nb_fget',
  66. 'nbfput' => 'nb_fput',
  67. 'nbget' => 'nb_get',
  68. 'nbput' => 'nb_put',
  69. );
  70. /** @var resource */
  71. private $resource;
  72. /** @var array */
  73. private $state;
  74. /** @var string */
  75. private $errorMsg;
  76. /**
  77. * @param string URL ftp://...
  78. * @param bool
  79. */
  80. public function __construct($url = NULL, $passiveMode = TRUE)
  81. {
  82. if (!extension_loaded('ftp')) {
  83. throw new Exception('PHP extension FTP is not loaded.');
  84. }
  85. if ($url) {
  86. $parts = parse_url($url);
  87. if (!isset($parts['scheme']) || !in_array($parts['scheme'], array('ftp', 'ftps', 'sftp'))) {
  88. throw new InvalidArgumentException('Invalid URL.');
  89. }
  90. $func = $parts['scheme'] === 'ftp' ? 'connect' : 'ssl_connect';
  91. $this->$func($parts['host'], empty($parts['port']) ? NULL : (int) $parts['port']);
  92. $this->login(urldecode($parts['user']), urldecode($parts['pass']));
  93. $this->pasv((bool) $passiveMode);
  94. if (isset($parts['path'])) {
  95. $this->chdir($parts['path']);
  96. }
  97. }
  98. }
  99. /**
  100. * Magic method (do not call directly).
  101. * @param string method name
  102. * @param array arguments
  103. * @return mixed
  104. * @throws Exception
  105. * @throws FtpException
  106. */
  107. public function __call($name, $args)
  108. {
  109. $name = strtolower($name);
  110. $silent = strncmp($name, 'try', 3) === 0;
  111. $func = $silent ? substr($name, 3) : $name;
  112. $func = 'ftp_' . (isset(self::$aliases[$func]) ? self::$aliases[$func] : $func);
  113. if (!function_exists($func)) {
  114. throw new Exception("Call to undefined method Ftp::$name().");
  115. }
  116. $this->errorMsg = NULL;
  117. set_error_handler(array($this, '_errorHandler'));
  118. if ($func === 'ftp_connect' || $func === 'ftp_ssl_connect') {
  119. $this->state = array($name => $args);
  120. $this->resource = call_user_func_array($func, $args);
  121. $res = NULL;
  122. } elseif (!is_resource($this->resource)) {
  123. restore_error_handler();
  124. throw new FtpException("Not connected to FTP server. Call connect() or ssl_connect() first.");
  125. } else {
  126. if ($func === 'ftp_login' || $func === 'ftp_pasv') {
  127. $this->state[$name] = $args;
  128. }
  129. array_unshift($args, $this->resource);
  130. $res = call_user_func_array($func, $args);
  131. if ($func === 'ftp_chdir' || $func === 'ftp_cdup') {
  132. $this->state['chdir'] = array(ftp_pwd($this->resource));
  133. }
  134. }
  135. restore_error_handler();
  136. if (!$silent && $this->errorMsg !== NULL) {
  137. if (ini_get('html_errors')) {
  138. $this->errorMsg = html_entity_decode(strip_tags($this->errorMsg));
  139. }
  140. if (($a = strpos($this->errorMsg, ': ')) !== FALSE) {
  141. $this->errorMsg = substr($this->errorMsg, $a + 2);
  142. }
  143. throw new FtpException($this->errorMsg);
  144. }
  145. return $res;
  146. }
  147. /**
  148. * Internal error handler. Do not call directly.
  149. */
  150. public function _errorHandler($code, $message)
  151. {
  152. $this->errorMsg = $message;
  153. }
  154. /**
  155. * Reconnects to FTP server.
  156. * @return void
  157. */
  158. public function reconnect()
  159. {
  160. @ftp_close($this->resource); // intentionally @
  161. foreach ($this->state as $name => $args) {
  162. call_user_func_array(array($this, $name), $args);
  163. }
  164. }
  165. /**
  166. * Checks if file or directory exists.
  167. * @param string
  168. * @return bool
  169. */
  170. public function fileExists($file)
  171. {
  172. return (bool) $this->nlist($file);
  173. }
  174. /**
  175. * Checks if directory exists.
  176. * @param string
  177. * @return bool
  178. */
  179. public function isDir($dir)
  180. {
  181. $current = $this->pwd();
  182. try {
  183. $this->chdir($dir);
  184. } catch (FtpException $e) {
  185. }
  186. $this->chdir($current);
  187. return empty($e);
  188. }
  189. /**
  190. * Recursive creates directories.
  191. * @param string
  192. * @return void
  193. */
  194. public function mkDirRecursive($dir)
  195. {
  196. $parts = explode('/', $dir);
  197. $path = '';
  198. while (!empty($parts)) {
  199. $path .= array_shift($parts);
  200. try {
  201. if ($path !== '') $this->mkdir($path);
  202. } catch (FtpException $e) {
  203. if (!$this->isDir($path)) {
  204. throw new FtpException("Cannot create directory '$path'.");
  205. }
  206. }
  207. $path .= '/';
  208. }
  209. }
  210. /**
  211. * Recursive deletes path.
  212. * @param string
  213. * @return void
  214. */
  215. public function deleteRecursive($path)
  216. {
  217. if (!$this->tryDelete($path)) {
  218. foreach ((array) $this->nlist($path) as $file) {
  219. if ($file !== '.' && $file !== '..') {
  220. $this->deleteRecursive(strpos($file, '/') === FALSE ? "$path/$file" : $file);
  221. }
  222. }
  223. $this->rmdir($path);
  224. }
  225. }
  226. }
  227. class FtpException extends Exception
  228. {
  229. }