Sftp.php 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  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. /**
  8. * Sftp client interface
  9. *
  10. * @link http://www.php.net/manual/en/function.ssh2-connect.php
  11. */
  12. class Sftp extends AbstractIo
  13. {
  14. const REMOTE_TIMEOUT = 10;
  15. const SSH2_PORT = 22;
  16. /**
  17. * @var \phpseclib\Net\SFTP
  18. */
  19. protected $_connection = null;
  20. /**
  21. * Open a SFTP connection to a remote site.
  22. *
  23. * @param array $args Connection arguments
  24. * string $args[host] Remote hostname
  25. * string $args[username] Remote username
  26. * string $args[password] Connection password
  27. * int $args[timeout] Connection timeout [=10]
  28. * @return void
  29. * @throws \Exception
  30. */
  31. public function open(array $args = [])
  32. {
  33. if (!isset($args['timeout'])) {
  34. $args['timeout'] = self::REMOTE_TIMEOUT;
  35. }
  36. if (strpos($args['host'], ':') !== false) {
  37. list($host, $port) = explode(':', $args['host'], 2);
  38. } else {
  39. $host = $args['host'];
  40. $port = self::SSH2_PORT;
  41. }
  42. $this->_connection = new \phpseclib\Net\SFTP($host, $port, $args['timeout']);
  43. if (!$this->_connection->login($args['username'], $args['password'])) {
  44. throw new \Exception(
  45. sprintf("Unable to open SFTP connection as %s@%s", $args['username'], $args['host'])
  46. );
  47. }
  48. }
  49. /**
  50. * Close a connection
  51. *
  52. * @return void
  53. */
  54. public function close()
  55. {
  56. $this->_connection->disconnect();
  57. }
  58. /**
  59. * Create a directory
  60. *
  61. * @param string $dir
  62. * @param int $mode ignored here; uses logged-in user's umask
  63. * @param bool $recursive analogous to mkdir -p
  64. *
  65. * Note: if $recursive is true and an error occurs mid-execution,
  66. * false is returned and some part of the hierarchy might be created.
  67. * No rollback is performed.
  68. *
  69. * @return bool
  70. * @SuppressWarnings(PHPMD.UnusedFormalParameter)
  71. */
  72. public function mkdir($dir, $mode = 0777, $recursive = true)
  73. {
  74. if ($recursive) {
  75. $no_errors = true;
  76. $dirList = explode('/', $dir);
  77. reset($dirList);
  78. $currentWorkingDir = $this->_connection->pwd();
  79. while ($no_errors && ($dir_item = next($dirList))) {
  80. $no_errors = $this->_connection->mkdir($dir_item) && $this->_connection->chdir($dir_item);
  81. }
  82. $this->_connection->chdir($currentWorkingDir);
  83. return $no_errors;
  84. } else {
  85. return $this->_connection->mkdir($dir);
  86. }
  87. }
  88. /**
  89. * Delete a directory
  90. *
  91. * @param string $dir
  92. * @param bool $recursive
  93. * @return bool
  94. * @throws \Exception
  95. * @SuppressWarnings(PHPMD.CyclomaticComplexity)
  96. */
  97. public function rmdir($dir, $recursive = false)
  98. {
  99. if ($recursive) {
  100. $no_errors = true;
  101. $currentWorkingDir = $this->pwd();
  102. if (!$this->_connection->chdir($dir)) {
  103. throw new \Exception("chdir(): {$dir}: Not a directory");
  104. }
  105. $list = $this->_connection->nlist();
  106. if (!count($list)) {
  107. // Go back
  108. $this->_connection->chdir($currentWorkingDir);
  109. return $this->rmdir($dir, false);
  110. } else {
  111. foreach ($list as $filename) {
  112. if ($this->_connection->chdir($filename)) {
  113. // This is a directory
  114. $this->_connection->chdir('..');
  115. $no_errors = $no_errors && $this->rmdir($filename, $recursive);
  116. } else {
  117. $no_errors = $no_errors && $this->rm($filename);
  118. }
  119. }
  120. }
  121. $no_errors = $no_errors && ($this->_connection->chdir(
  122. $currentWorkingDir
  123. ) && $this->_connection->rmdir(
  124. $dir
  125. ));
  126. return $no_errors;
  127. } else {
  128. return $this->_connection->rmdir($dir);
  129. }
  130. }
  131. /**
  132. * Get current working directory
  133. *
  134. * @return mixed
  135. */
  136. public function pwd()
  137. {
  138. return $this->_connection->pwd();
  139. }
  140. /**
  141. * Change current working directory
  142. *
  143. * @param string $dir
  144. * @return bool
  145. * @SuppressWarnings(PHPMD.ShortMethodName)
  146. */
  147. public function cd($dir)
  148. {
  149. return $this->_connection->chdir($dir);
  150. }
  151. /**
  152. * Read a file
  153. *
  154. * @param string $filename remote file name
  155. * @param string|null $destination local file name (optional)
  156. * @return mixed
  157. */
  158. public function read($filename, $destination = null)
  159. {
  160. if ($destination === null) {
  161. $destination = false;
  162. }
  163. return $this->_connection->get($filename, $destination);
  164. }
  165. /**
  166. * Write a file
  167. *
  168. * @param string $filename
  169. * @param string $source string data or local file name
  170. * @param int $mode ignored parameter
  171. * @return bool
  172. */
  173. public function write($filename, $source, $mode = null)
  174. {
  175. $mode = is_readable($source) ? \phpseclib\Net\SFTP::SOURCE_LOCAL_FILE : \phpseclib\Net\SFTP::SOURCE_STRING;
  176. return $this->_connection->put($filename, $source, $mode);
  177. }
  178. /**
  179. * Delete a file
  180. *
  181. * @param string $filename
  182. * @return bool
  183. * @SuppressWarnings(PHPMD.ShortMethodName)
  184. */
  185. public function rm($filename)
  186. {
  187. return $this->_connection->delete($filename);
  188. }
  189. /**
  190. * Rename or move a directory or a file
  191. *
  192. * @param string $source
  193. * @param string $destination
  194. * @return bool
  195. * @SuppressWarnings(PHPMD.ShortMethodName)
  196. */
  197. public function mv($source, $destination)
  198. {
  199. return $this->_connection->rename($source, $destination);
  200. }
  201. /**
  202. * Change mode of a directory or a file
  203. *
  204. * @param string $filename
  205. * @param int $mode
  206. * @return mixed
  207. */
  208. public function chmod($filename, $mode)
  209. {
  210. return $this->_connection->chmod($mode, $filename);
  211. }
  212. /**
  213. * Get list of cwd subdirectories and files
  214. *
  215. * @param null $grep ignored parameter
  216. * @return array
  217. * @SuppressWarnings(PHPMD.ShortMethodName)
  218. * @SuppressWarnings(PHPMD.UnusedFormalParameter)
  219. */
  220. public function ls($grep = null)
  221. {
  222. $list = $this->_connection->nlist();
  223. $currentWorkingDir = $this->pwd();
  224. $result = [];
  225. foreach ($list as $name) {
  226. $result[] = ['text' => $name, 'id' => "{$currentWorkingDir}/{$name}"];
  227. }
  228. return $result;
  229. }
  230. /**
  231. * Returns a list of files in the current directory
  232. *
  233. * @return mixed
  234. */
  235. public function rawls()
  236. {
  237. $list = $this->_connection->rawlist();
  238. return $list;
  239. }
  240. }