Ftp.php 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  1. <?php
  2. /**
  3. * Copyright © Magento, Inc. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. namespace Magento\Framework\Filesystem\Io;
  7. use Magento\Framework\Phrase;
  8. use Magento\Framework\Exception\LocalizedException;
  9. /**
  10. * FTP client
  11. */
  12. class Ftp extends AbstractIo
  13. {
  14. const ERROR_EMPTY_HOST = 1;
  15. const ERROR_INVALID_CONNECTION = 2;
  16. const ERROR_INVALID_LOGIN = 3;
  17. const ERROR_INVALID_PATH = 4;
  18. const ERROR_INVALID_MODE = 5;
  19. const ERROR_INVALID_DESTINATION = 6;
  20. const ERROR_INVALID_SOURCE = 7;
  21. /**
  22. * Connection config
  23. *
  24. * @var array
  25. */
  26. protected $_config;
  27. /**
  28. * An FTP connection
  29. *
  30. * @var resource
  31. */
  32. protected $_conn;
  33. /**
  34. * Error code
  35. *
  36. * @var int
  37. */
  38. protected $_error;
  39. /**
  40. * @var string
  41. */
  42. protected $_tmpFilename;
  43. /**
  44. * Open a connection
  45. *
  46. * Possible argument keys:
  47. * - host required
  48. * - port default 21
  49. * - timeout default 90
  50. * - user default anonymous
  51. * - password default empty
  52. * - ssl default false
  53. * - passive default false
  54. * - path default empty
  55. * - file_mode default FTP_BINARY
  56. *
  57. * @param array $args
  58. * @return true
  59. * @throws LocalizedException
  60. * @SuppressWarnings(PHPMD.CyclomaticComplexity)
  61. * @SuppressWarnings(PHPMD.NPathComplexity)
  62. */
  63. public function open(array $args = [])
  64. {
  65. if (empty($args['host'])) {
  66. $this->_error = self::ERROR_EMPTY_HOST;
  67. throw new LocalizedException(new Phrase('The specified host is empty. Set the host and try again.'));
  68. }
  69. if (empty($args['port'])) {
  70. $args['port'] = 21;
  71. }
  72. if (empty($args['user'])) {
  73. $args['user'] = 'anonymous';
  74. $args['password'] = 'anonymous@noserver.com';
  75. }
  76. if (empty($args['password'])) {
  77. $args['password'] = '';
  78. }
  79. if (empty($args['timeout'])) {
  80. $args['timeout'] = 90;
  81. }
  82. if (empty($args['file_mode'])) {
  83. $args['file_mode'] = FTP_BINARY;
  84. }
  85. $this->_config = $args;
  86. if (empty($this->_config['ssl'])) {
  87. $this->_conn = @ftp_connect($this->_config['host'], $this->_config['port'], $this->_config['timeout']);
  88. } else {
  89. $this->_conn = @ftp_ssl_connect($this->_config['host'], $this->_config['port'], $this->_config['timeout']);
  90. }
  91. if (!$this->_conn) {
  92. $this->_error = self::ERROR_INVALID_CONNECTION;
  93. throw new LocalizedException(
  94. new Phrase("The FTP connection couldn't be established because of an invalid host or port.")
  95. );
  96. }
  97. if (!@ftp_login($this->_conn, $this->_config['user'], $this->_config['password'])) {
  98. $this->_error = self::ERROR_INVALID_LOGIN;
  99. $this->close();
  100. throw new LocalizedException(new Phrase('The username or password is invalid. Verify both and try again.'));
  101. }
  102. if (!empty($this->_config['path'])) {
  103. if (!@ftp_chdir($this->_conn, $this->_config['path'])) {
  104. $this->_error = self::ERROR_INVALID_PATH;
  105. $this->close();
  106. throw new LocalizedException(new Phrase('The path is invalid. Verify and try again.'));
  107. }
  108. }
  109. if (!empty($this->_config['passive'])) {
  110. if (!@ftp_pasv($this->_conn, true)) {
  111. $this->_error = self::ERROR_INVALID_MODE;
  112. $this->close();
  113. throw new LocalizedException(new Phrase('The file transfer mode is invalid. Verify and try again.'));
  114. }
  115. }
  116. return true;
  117. }
  118. /**
  119. * Close a connection
  120. *
  121. * @return bool
  122. */
  123. public function close()
  124. {
  125. return @ftp_close($this->_conn);
  126. }
  127. /**
  128. * Create a directory
  129. *
  130. * @todo implement $mode and $recursive
  131. * @param string $dir
  132. * @param int $mode
  133. * @param bool $recursive
  134. * @return bool
  135. * @SuppressWarnings(PHPMD.UnusedFormalParameter)
  136. */
  137. public function mkdir($dir, $mode = 0777, $recursive = true)
  138. {
  139. return @ftp_mkdir($this->_conn, $dir);
  140. }
  141. /**
  142. * Delete a directory
  143. *
  144. * @param string $dir
  145. * @param bool $recursive
  146. * @return bool
  147. * @SuppressWarnings(PHPMD.UnusedFormalParameter)
  148. */
  149. public function rmdir($dir, $recursive = false)
  150. {
  151. return @ftp_rmdir($this->_conn, $dir);
  152. }
  153. /**
  154. * Get current working directory
  155. *
  156. * @return string
  157. */
  158. public function pwd()
  159. {
  160. return @ftp_pwd($this->_conn);
  161. }
  162. /**
  163. * Change current working directory
  164. *
  165. * @param string $dir
  166. * @return bool
  167. * @SuppressWarnings(PHPMD.ShortMethodName)
  168. */
  169. public function cd($dir)
  170. {
  171. return @ftp_chdir($this->_conn, $dir);
  172. }
  173. /**
  174. * Read a file to result, file or stream
  175. *
  176. * @param string $filename
  177. * @param string|resource|null $dest destination file name, stream, or if null will return file contents
  178. * @return false|string
  179. */
  180. public function read($filename, $dest = null)
  181. {
  182. if (is_string($dest)) {
  183. $result = ftp_get($this->_conn, $dest, $filename, $this->_config['file_mode']);
  184. } else {
  185. if (is_resource($dest)) {
  186. $stream = $dest;
  187. } elseif ($dest === null) {
  188. $stream = tmpfile();
  189. } else {
  190. $this->_error = self::ERROR_INVALID_DESTINATION;
  191. return false;
  192. }
  193. $result = ftp_fget($this->_conn, $stream, $filename, $this->_config['file_mode']);
  194. if ($dest === null) {
  195. fseek($stream, 0);
  196. $result = '';
  197. for ($result = ''; $s = fread($stream, 4096); $result .= $s) {
  198. }
  199. fclose($stream);
  200. }
  201. }
  202. return $result;
  203. }
  204. /**
  205. * Write a file from string, file or stream
  206. *
  207. * @param string $filename
  208. * @param string|resource $src filename, string data or source stream
  209. * @param null $mode
  210. * @return bool
  211. * @SuppressWarnings(PHPMD.UnusedFormalParameter)
  212. */
  213. public function write($filename, $src, $mode = null)
  214. {
  215. if (is_string($src) && is_readable($src)) {
  216. return @ftp_put($this->_conn, $filename, $src, $this->_config['file_mode']);
  217. } else {
  218. if (is_string($src)) {
  219. $stream = tmpfile();
  220. fputs($stream, $src);
  221. fseek($stream, 0);
  222. } elseif (is_resource($src)) {
  223. $stream = $src;
  224. } else {
  225. $this->_error = self::ERROR_INVALID_SOURCE;
  226. return false;
  227. }
  228. $result = ftp_fput($this->_conn, $filename, $stream, $this->_config['file_mode']);
  229. if (is_string($src)) {
  230. fclose($stream);
  231. }
  232. return $result;
  233. }
  234. }
  235. /**
  236. * Delete a file
  237. *
  238. * @param string $filename
  239. * @return bool
  240. * @SuppressWarnings(PHPMD.ShortMethodName)
  241. */
  242. public function rm($filename)
  243. {
  244. return @ftp_delete($this->_conn, $filename);
  245. }
  246. /**
  247. * Rename or move a directory or a file
  248. *
  249. * @param string $src
  250. * @param string $dest
  251. * @return bool
  252. * @SuppressWarnings(PHPMD.ShortMethodName)
  253. */
  254. public function mv($src, $dest)
  255. {
  256. return @ftp_rename($this->_conn, $src, $dest);
  257. }
  258. /**
  259. * Change mode of a directory or a file
  260. *
  261. * @param string $filename
  262. * @param int $mode
  263. * @return bool
  264. */
  265. public function chmod($filename, $mode)
  266. {
  267. return @ftp_chmod($this->_conn, $mode, $filename);
  268. }
  269. /**
  270. * @param null $grep ignored parameter
  271. * @return array
  272. * @SuppressWarnings(PHPMD.ShortMethodName)
  273. * @SuppressWarnings(PHPMD.UnusedFormalParameter)
  274. */
  275. public function ls($grep = null)
  276. {
  277. $ls = @ftp_nlist($this->_conn, '.');
  278. $list = [];
  279. foreach ($ls as $file) {
  280. $list[] = ['text' => $file, 'id' => $this->pwd() . '/' . $file];
  281. }
  282. return $list;
  283. }
  284. /**
  285. * @param bool $new
  286. * @return string
  287. */
  288. protected function _tmpFilename($new = false)
  289. {
  290. if ($new || !$this->_tmpFilename) {
  291. $this->_tmpFilename = tempnam(md5(uniqid(rand(), true)), '');
  292. }
  293. return $this->_tmpFilename;
  294. }
  295. }