Sftp.php (8164B)
1 <?php 2 3 namespace Ssh; 4 5 use RuntimeException; 6 7 /** 8 * Secure File Transfer Protocol 9 * 10 * @author Antoine Hérault <antoine.herault@gmail.com> 11 */ 12 class Sftp extends Subsystem 13 { 14 /** 15 * Stats a symbolic link 16 * 17 * @param string $path The path of the symbolic link 18 * 19 * @return array 20 */ 21 public function lstat($path) 22 { 23 return ssh2_sftp_lstat($this->getResource(), $path); 24 } 25 26 /** 27 * Creates a directory 28 * 29 * @param string $dirname The name of the new directory 30 * @param int $mod The permissions on the new directory 31 * @param Boolean $recursive Whether to automatically create any required 32 * parent directory 33 * 34 * @return Boolean 35 */ 36 public function mkdir($dirname, $mod = 0777, $recursive = false) 37 { 38 return ssh2_sftp_mkdir($this->getResource(), $dirname, $mod, $recursive); 39 } 40 41 /** 42 * Returns the target of a symbolic link 43 * 44 * @param string $link The path of the symbolic link 45 * 46 * @return string The target of the symbolic link 47 */ 48 public function readlink($link) 49 { 50 return ssh2_sftp_readlink($this->getResource(), $link); 51 } 52 53 /** 54 * Resolves the realpath of a provided path string 55 * 56 * @param string $filename The filename to resolve 57 * 58 * @return string The real path of the file 59 */ 60 public function realpath($filename) 61 { 62 // This function creates a not documented warning on failure. 63 return @ssh2_sftp_realpath($this->getResource(), $filename); 64 } 65 66 /** 67 * Renames a remote file 68 * 69 * @param string $from The current file that is being renamed 70 * @param string $to The new file name that replaces from 71 * 72 * @return Boolean TRUE on success, or FALSE on failure 73 */ 74 public function rename($from, $to) 75 { 76 return ssh2_sftp_rename($this->getResource(), $from, $to); 77 } 78 79 /** 80 * Removes a directory 81 * 82 * @param string $dirname The directory that is being removed 83 * 84 * @return Boolean TRUE on success, or FALSE on failure 85 */ 86 public function rmdir($dirname) 87 { 88 return ssh2_sftp_rmdir($this->getResource(), $dirname); 89 } 90 91 /** 92 * Stats a file on the remote filesystem 93 * 94 * @param string $path The path of the file 95 * 96 * @return array 97 */ 98 public function stat($path) 99 { 100 // This function creates a undocumented warning on missing files. 101 return @ssh2_sftp_stat($this->getResource(), $path); 102 } 103 104 /** 105 * Creates a symlink 106 * 107 * @param string $target The target of the symlink 108 * @param string $link The path of the link 109 * 110 * @return Boolean TRUE on success, or FALSE on failure 111 */ 112 public function symlink($target, $link) 113 { 114 return ssh2_sftp_symlink($this->getResource(), $target, $link); 115 } 116 117 /** 118 * Deletes a file 119 * 120 * @param string $filename The name of the file that is being deleted 121 * 122 * @return Boolean TRUE on success, or FALSE on failure 123 */ 124 public function unlink($filename) 125 { 126 return ssh2_sftp_unlink($this->getResource(), $filename); 127 } 128 129 /** 130 * Indicates whether the specified distant file exists 131 * 132 * @param string $filename The distant filename 133 * 134 * @return boolean 135 */ 136 public function exists($filename) 137 { 138 return file_exists($this->getUrl($filename)); 139 } 140 141 /** 142 * Reads the content of the specified remote file. 143 * Will return false if file does not exist. 144 * 145 * @param string $filename The remote filename 146 * 147 * @return string|false 148 */ 149 public function read($filename) 150 { 151 // Suppress a warning, when file does not exist. 152 return @file_get_contents($this->getUrl($filename)); 153 } 154 155 /** 156 * Writes the given content to the specified remote file 157 * 158 * @param string $filename The remote filename 159 * 160 * @return integer The number of bytes that were written into the file, or 161 * FALSE on failure 162 */ 163 public function write($filename, $content) 164 { 165 return file_put_contents($this->getUrl($filename), $content); 166 } 167 168 /** 169 * Receive the specified distant file as the specified local file 170 * 171 * @param string $distant The distant filename 172 * @param string $local The local filename 173 * 174 * @return boolean TRUE on success, or FALSE on failure 175 */ 176 public function receive($distant, $local) 177 { 178 return file_put_contents($local, $this->read($distant)); 179 } 180 181 /** 182 * Sends the specified local file as the specified remote file 183 * 184 * @param string $local The local filename 185 * @param string $distant The distant filename 186 * 187 * @return integer|false The number of bytes that were sent, 188 * or FALSE on failure 189 */ 190 public function send($local, $distant) 191 { 192 return $this->write($distant, file_get_contents($local)); 193 } 194 195 /** 196 * Returns the URL of the specified file with the ssh2.sftp protocol. The 197 * result URL is suitable for stream resource creation (e.g using fopen) 198 * 199 * @param string $filename The distant filename 200 * 201 * @return string 202 */ 203 public function getUrl($filename) 204 { 205 return sprintf('ssh2.sftp://%s/%s', $this->getResource(), $filename); 206 } 207 208 /** 209 * Lists files and directories of the specified directory 210 * 211 * The returned array is of the form: 212 * 213 * array( 214 * 'files' => array(...), 215 * 'directories' => array(...) 216 * ) 217 * 218 * @param string $directory 219 * @param Boolean $recursive 220 * 221 * @return array 222 */ 223 public function listDirectory($directory, $recursive = false) 224 { 225 $results = $this->scanDirectory($directory, $recursive); 226 227 if (false === $results) { 228 throw new \RuntimeException(sprintf( 229 'Unable to list directory "%s", maybe it is not a directory '. 230 'or it does not exist.', 231 $directory 232 )); 233 } 234 235 return array( 236 'files' => $results[0], 237 'directories' => $results[1] 238 ); 239 } 240 241 /** 242 * {@inheritDoc} 243 */ 244 protected function createResource() 245 { 246 $resource = ssh2_sftp($this->getSessionResource()); 247 248 if (!is_resource($resource)) { 249 throw new RuntimeException('The initialization of the SFTP subsystem failed.'); 250 } 251 252 $this->resource = $resource; 253 } 254 255 /** 256 * Scans a directory 257 * 258 * Unfortunately, using a (recursive) directory iterator is not possible 259 * over SFTP: see https://bugs.php.net/bug.php?id=57378. Also, is_dir() is 260 * unreliable and often returns false for valid directories. Therefore, I 261 * use @scandir() instead. 262 * 263 * @param string $directory 264 * @param Boolean $recursive 265 * 266 * @return array 267 */ 268 private function scanDirectory($directory, $recursive) 269 { 270 if (!$results = @scandir($this->getUrl($directory))) { 271 return false; 272 } 273 274 $files = array(); 275 $directories = array(); 276 277 foreach ($results as $result) { 278 if (in_array($result, array('.', '..'))) { 279 continue; 280 } 281 282 $filename = sprintf('%s/%s', $directory, $result); 283 284 if (false === @scandir($this->getUrl($filename))) { 285 $files[] = $filename; 286 } else { 287 $directories[] = $filename; 288 289 if ($recursive) { 290 $children = $this->scanDirectory($filename, $recursive); 291 if (is_array($children)) { 292 $files = array_merge($files, $children[0]); 293 $directories = array_merge($directories, $children[1]); 294 } 295 } 296 } 297 } 298 299 return array($files, $directories); 300 } 301 }