├── .gitignore ├── LICENSE ├── README.md ├── composer.json ├── src └── FtpClient │ ├── FtpClient.php │ ├── FtpException.php │ └── FtpWrapper.php └── tests ├── .atoum.php ├── bootstrap.php └── units └── FtpClient ├── FtpClient.php ├── FtpException.php └── FtpWrapper.php /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor 2 | composer.phar 3 | composer.lock 4 | .DS_Store 5 | Thumbs.db 6 | /.Trash-1000 7 | .idea/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Nicolas Tallefourtane dev@nicolab.net 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nicolab/php-ftp-client 2 | 3 | A flexible FTP and SSL-FTP client for PHP. 4 | This lib provides helpers easy to use to manage the remote files. 5 | 6 | > This package is aimed to remain simple and light. It's only a wrapper of the FTP native API of PHP, with some useful helpers. If you want to customize some methods, you can do this by inheriting one of the [3 classes of the package](src/FtpClient). 7 | 8 | 9 | ## Install 10 | 11 | * Install package with composer 12 | ``` 13 | composer require nicolab/php-ftp-client 14 | ``` 15 | 16 | * Or use GIT clone command: 17 | ``` 18 | git clone git@github.com:Nicolab/php-ftp-client.git 19 | ``` 20 | 21 | * Or [download](https://github.com/Nicolab/php-ftp-client/archive/refs/heads/master.zip) the library, configure your autoloader or include the 3 files of `php-ftp-client/src/FtpClient` directory. 22 | 23 | 24 | ## Getting Started 25 | 26 | Connect to a server FTP : 27 | 28 | ```php 29 | $ftp = new \FtpClient\FtpClient(); 30 | $ftp->connect($host); 31 | $ftp->login($login, $password); 32 | ``` 33 | 34 | OR 35 | 36 | Connect to a server FTP via SSL (on port 990 or another port) : 37 | 38 | ```php 39 | $ftp = new \FtpClient\FtpClient(); 40 | $ftp->connect($host, true, 990); 41 | $ftp->login($login, $password); 42 | ``` 43 | 44 | Note: The connection is implicitly closed at the end of script execution (when the object is destroyed). Therefore it is unnecessary to call `$ftp->close()`, except for an explicit re-connection. 45 | 46 | 47 | ### Usage 48 | 49 | Upload all files and all directories is easy : 50 | 51 | ```php 52 | // upload with the BINARY mode 53 | $ftp->putAll($source_directory, $target_directory); 54 | 55 | // Is equal to 56 | $ftp->putAll($source_directory, $target_directory, FTP_BINARY); 57 | 58 | // or upload with the ASCII mode 59 | $ftp->putAll($source_directory, $target_directory, FTP_ASCII); 60 | ``` 61 | 62 | *Note : FTP_ASCII and FTP_BINARY are predefined PHP internal constants.* 63 | 64 | Get a directory size : 65 | 66 | ```php 67 | // size of the current directory 68 | $size = $ftp->dirSize(); 69 | 70 | // size of a given directory 71 | $size = $ftp->dirSize('/path/of/directory'); 72 | ``` 73 | 74 | Count the items in a directory : 75 | 76 | ```php 77 | // count in the current directory 78 | $total = $ftp->countItems(); 79 | // or alias 80 | $total = $ftp->count(); 81 | 82 | // count in a given directory 83 | $total = $ftp->countItems('/path/of/directory'); 84 | 85 | // count only the "files" in the current directory 86 | $total_file = $ftp->countItems('.', 'file'); 87 | 88 | // count only the "files" in a given directory 89 | $total_file = $ftp->countItems('/path/of/directory', 'file'); 90 | 91 | // count only the "directories" in a given directory 92 | $total_dir = $ftp->countItems('/path/of/directory', 'directory'); 93 | 94 | // count only the "symbolic links" in a given directory 95 | $total_link = $ftp->countItems('/path/of/directory', 'link'); 96 | ``` 97 | 98 | Detailed list of all files and directories : 99 | 100 | ```php 101 | // scan the current directory and returns the details of each item 102 | $items = $ftp->scanDir(); 103 | 104 | // scan the current directory (recursive) and returns the details of each item 105 | var_dump($ftp->scanDir('.', true)); 106 | ``` 107 | 108 | Result: 109 | 110 | 'directory#www' => 111 | array (size=10) 112 | 'permissions' => string 'drwx---r-x' (length=10) 113 | 'number' => string '3' (length=1) 114 | 'owner' => string '32385' (length=5) 115 | 'group' => string 'users' (length=5) 116 | 'size' => string '5' (length=1) 117 | 'month' => string 'Nov' (length=3) 118 | 'day' => string '24' (length=2) 119 | 'time' => string '17:25' (length=5) 120 | 'name' => string 'www' (length=3) 121 | 'type' => string 'directory' (length=9) 122 | 123 | 'link#www/index.html' => 124 | array (size=11) 125 | 'permissions' => string 'lrwxrwxrwx' (length=10) 126 | 'number' => string '1' (length=1) 127 | 'owner' => string '0' (length=1) 128 | 'group' => string 'users' (length=5) 129 | 'size' => string '38' (length=2) 130 | 'month' => string 'Nov' (length=3) 131 | 'day' => string '16' (length=2) 132 | 'time' => string '14:57' (length=5) 133 | 'name' => string 'index.html' (length=10) 134 | 'type' => string 'link' (length=4) 135 | 'target' => string '/var/www/shared/index.html' (length=26) 136 | 137 | 'file#www/README' => 138 | array (size=10) 139 | 'permissions' => string '-rw----r--' (length=10) 140 | 'number' => string '1' (length=1) 141 | 'owner' => string '32385' (length=5) 142 | 'group' => string 'users' (length=5) 143 | 'size' => string '0' (length=1) 144 | 'month' => string 'Nov' (length=3) 145 | 'day' => string '24' (length=2) 146 | 'time' => string '17:25' (length=5) 147 | 'name' => string 'README' (length=6) 148 | 'type' => string 'file' (length=4) 149 | 150 | 151 | All FTP PHP functions are supported and some improved : 152 | 153 | ```php 154 | // Requests execution of a command on the FTP server 155 | $ftp->exec($command); 156 | 157 | // Turns passive mode on or off 158 | $ftp->pasv(true); 159 | 160 | // Set permissions on a file via FTP 161 | $ftp->chmod(0777, 'file.php'); 162 | 163 | // Removes a directory 164 | $ftp->rmdir('path/of/directory/to/remove'); 165 | 166 | // Removes a directory (recursive) 167 | $ftp->rmdir('path/of/directory/to/remove', true); 168 | 169 | // Creates a directory 170 | $ftp->mkdir('path/of/directory/to/create'); 171 | 172 | // Creates a directory (recursive), 173 | // creates automaticaly the sub directory if not exist 174 | $ftp->mkdir('path/of/directory/to/create', true); 175 | 176 | // and more ... 177 | ``` 178 | 179 | Get the help information of remote FTP server : 180 | 181 | ```php 182 | var_dump($ftp->help()); 183 | ``` 184 | 185 | Result : 186 | 187 | array (size=6) 188 | 0 => string '214-The following SITE commands are recognized' (length=46) 189 | 1 => string ' ALIAS' (length=6) 190 | 2 => string ' CHMOD' (length=6) 191 | 3 => string ' IDLE' (length=5) 192 | 4 => string ' UTIME' (length=6) 193 | 5 => string '214 Pure-FTPd - http://pureftpd.org/' (length=36) 194 | 195 | 196 | _Note : The result depend of FTP server._ 197 | 198 | 199 | ### Extend 200 | 201 | Create your custom `FtpClient`. 202 | 203 | ```php 204 | // MyFtpClient.php 205 | 206 | /** 207 | * My custom FTP Client 208 | * @inheritDoc 209 | */ 210 | class MyFtpClient extends \FtpClient\FtpClient { 211 | 212 | public function removeByTime($path, $timestamp) { 213 | // your code here 214 | } 215 | 216 | public function search($regex) { 217 | // your code here 218 | } 219 | } 220 | ``` 221 | 222 | ```php 223 | // example.php 224 | $ftp = new MyFtpClient(); 225 | $ftp->connect($host); 226 | $ftp->login($login, $password); 227 | 228 | // remove the old files 229 | $ftp->removeByTime('/www/mysite.com/demo', time() - 86400); 230 | 231 | // search PNG files 232 | $ftp->search('/(.*)\.png$/i'); 233 | ``` 234 | 235 | 236 | ## API doc 237 | 238 | See the [source code](https://github.com/Nicolab/php-ftp-client/tree/master/src/FtpClient) for more details. 239 | It is fully documented :blue_book: 240 | 241 | 242 | ## Testing 243 | 244 | Tested with "atoum" unit testing framework. 245 | 246 | 247 | ## License 248 | 249 | [MIT](https://github.com/Nicolab/php-ftp-client/blob/master/LICENSE) c) 2014, Nicolas Tallefourtane. 250 | 251 | 252 | ## Author 253 | 254 | | [![Nicolas Tallefourtane - Nicolab.net](http://www.gravatar.com/avatar/d7dd0f4769f3aa48a3ecb308f0b457fc?s=64)](http://nicolab.net) | 255 | |---| 256 | | [Nicolas Talle](http://nicolab.net) | 257 | | [![Make a donation via Paypal](https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=PGRH4ZXP36GUC) | 258 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nicolab/php-ftp-client", 3 | "type": "library", 4 | "description": "A flexible FTP and SSL-FTP client for PHP. This lib provides helpers easy to use to manage the remote files.", 5 | "license": "MIT", 6 | "keywords": ["ftp", "sftp", "ssl-ftp", "ssl", "file", "server", "lib", "helper"], 7 | "homepage": "https://github.com/Nicolab/php-ftp-client", 8 | 9 | "authors" : [ 10 | { 11 | "name" : "Nicolas Tallefourtane", 12 | "email" : "dev@nicolab.net", 13 | "homepage" : "http://nicolab.net" 14 | } 15 | ], 16 | "require": { 17 | "php": ">=5.4", 18 | "ext-ftp": "*" 19 | }, 20 | "autoload": { 21 | "psr-0": {"FtpClient": "src/"} 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/FtpClient/FtpClient.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | * 10 | * @copyright Nicolas Tallefourtane http://nicolab.net 11 | */ 12 | namespace FtpClient; 13 | 14 | use \Countable; 15 | 16 | /** 17 | * The FTP and SSL-FTP client for PHP. 18 | * 19 | * @method bool alloc(int $filesize, string &$result = null) Allocates space for a file to be uploaded 20 | * @method bool append(string $remote_file, string $local_file, int $mode = FTP_BINARY) Append the contents of a file to another file on the FTP server 21 | * @method bool cdup() Changes to the parent directory 22 | * @method bool chdir(string $directory) Changes the current directory on a FTP server 23 | * @method int chmod(int $mode, string $filename) Set permissions on a file via FTP 24 | * @method bool delete(string $path) Deletes a file on the FTP server 25 | * @method bool exec(string $command) Requests execution of a command on the FTP server 26 | * @method bool fget(resource $handle, string $remote_file, int $mode, int $resumepos = 0) Downloads a file from the FTP server and saves to an open file 27 | * @method bool fput(string $remote_file, resource $handle, int $mode, int $startpos = 0) Uploads from an open file to the FTP server 28 | * @method mixed get_option(int $option) Retrieves various runtime behaviours of the current FTP stream 29 | * @method bool get(string $local_file, string $remote_file, int $mode, int $resumepos = 0) Downloads a file from the FTP server 30 | * @method int mdtm(string $remote_file) Returns the last modified time of the given file 31 | * @method array mlsd(string $remote_dir) Returns a list of files in the given directory 32 | * @method int nb_continue() Continues retrieving/sending a file (non-blocking) 33 | * @method int nb_fget(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) 34 | * @method int nb_fput(string $remote_file, resource $handle, int $mode, int $startpos = 0) Stores a file from an open file to the FTP server (non-blocking) 35 | * @method int nb_get(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) 36 | * @method int nb_put(string $remote_file, string $local_file, int $mode, int $startpos = 0) Stores a file on the FTP server (non-blocking) 37 | * @method bool pasv(bool $pasv) Turns passive mode on or off 38 | * @method bool put(string $remote_file, string $local_file, int $mode, int $startpos = 0) Uploads a file to the FTP server 39 | * @method string pwd() Returns the current directory name 40 | * @method bool quit() Closes an FTP connection 41 | * @method array raw(string $command) Sends an arbitrary command to an FTP server 42 | * @method bool rename(string $oldname, string $newname) Renames a file or a directory on the FTP server 43 | * @method bool set_option(int $option, mixed $value) Set miscellaneous runtime FTP options 44 | * @method bool site(string $command) Sends a SITE command to the server 45 | * @method int size(string $remote_file) Returns the size of the given file 46 | * @method string systype() Returns the system type identifier of the remote FTP server 47 | * 48 | * @author Nicolas Tallefourtane 49 | */ 50 | class FtpClient implements Countable 51 | { 52 | /** 53 | * The connection with the server. 54 | * 55 | * @var resource 56 | */ 57 | protected $conn; 58 | 59 | /** 60 | * PHP FTP functions wrapper. 61 | * 62 | * @var FtpWrapper 63 | */ 64 | private $ftp; 65 | 66 | /** 67 | * Constructor. 68 | * 69 | * @param resource|null $connection 70 | * @throws FtpException If FTP extension is not loaded. 71 | */ 72 | public function __construct($connection = null) 73 | { 74 | if (!extension_loaded('ftp')) { 75 | throw new FtpException('FTP extension is not loaded!'); 76 | } 77 | 78 | if ($connection) { 79 | $this->conn = $connection; 80 | } 81 | 82 | $this->setWrapper(new FtpWrapper($this->conn)); 83 | } 84 | 85 | /** 86 | * Close the connection when the object is destroyed. 87 | */ 88 | public function __destruct() 89 | { 90 | if ($this->conn) { 91 | $this->ftp->close(); 92 | } 93 | } 94 | 95 | /** 96 | * Call an internal method or a FTP method handled by the wrapper. 97 | * 98 | * Wrap the FTP PHP functions to call as method of FtpClient object. 99 | * The connection is automaticaly passed to the FTP PHP functions. 100 | * 101 | * @param string $method 102 | * @param array $arguments 103 | * @return mixed 104 | * @throws FtpException When the function is not valid 105 | */ 106 | public function __call($method, array $arguments) 107 | { 108 | return $this->ftp->__call($method, $arguments); 109 | } 110 | 111 | /** 112 | * Overwrites the PHP limit 113 | * 114 | * @param string|null $memory The memory limit, if null is not modified 115 | * @param int $time_limit The max execution time, unlimited by default 116 | * @param bool $ignore_user_abort Ignore user abort, true by default 117 | * @return FtpClient 118 | */ 119 | public function setPhpLimit($memory = null, $time_limit = 0, $ignore_user_abort = true) 120 | { 121 | if (null !== $memory) { 122 | ini_set('memory_limit', $memory); 123 | } 124 | 125 | ignore_user_abort($ignore_user_abort); 126 | set_time_limit($time_limit); 127 | 128 | return $this; 129 | } 130 | 131 | /** 132 | * Get the help information of the remote FTP server. 133 | * 134 | * @return array 135 | */ 136 | public function help() 137 | { 138 | return $this->ftp->raw('help'); 139 | } 140 | 141 | /** 142 | * Open a FTP connection. 143 | * 144 | * @param string $host 145 | * @param bool $ssl 146 | * @param int $port 147 | * @param int $timeout 148 | * 149 | * @return FtpClient 150 | * @throws FtpException If unable to connect 151 | */ 152 | public function connect($host, $ssl = false, $port = 21, $timeout = 90) 153 | { 154 | if ($ssl) { 155 | $this->conn = $this->ftp->ssl_connect($host, $port, $timeout); 156 | } else { 157 | $this->conn = $this->ftp->connect($host, $port, $timeout); 158 | } 159 | 160 | if (!$this->conn) { 161 | throw new FtpException('Unable to connect'); 162 | } 163 | 164 | return $this; 165 | } 166 | 167 | /** 168 | * Closes the current FTP connection. 169 | * 170 | * @return bool 171 | */ 172 | public function close() 173 | { 174 | if ($this->conn) { 175 | $this->ftp->close(); 176 | $this->conn = null; 177 | } 178 | } 179 | 180 | /** 181 | * Get the connection with the server. 182 | * 183 | * @return resource 184 | */ 185 | public function getConnection() 186 | { 187 | return $this->conn; 188 | } 189 | 190 | /** 191 | * Get the wrapper. 192 | * 193 | * @return FtpWrapper 194 | */ 195 | public function getWrapper() 196 | { 197 | return $this->ftp; 198 | } 199 | 200 | /** 201 | * Logs in to an FTP connection. 202 | * 203 | * @param string $username 204 | * @param string $password 205 | * 206 | * @return FtpClient 207 | * @throws FtpException If the login is incorrect 208 | */ 209 | public function login($username = 'anonymous', $password = '') 210 | { 211 | $result = $this->ftp->login($username, $password); 212 | 213 | if ($result === false) { 214 | throw new FtpException('Login incorrect'); 215 | } 216 | 217 | return $this; 218 | } 219 | 220 | /** 221 | * Returns the last modified time of the given file. 222 | * Return -1 on error 223 | * 224 | * @param string $remoteFile 225 | * @param string|null $format 226 | * 227 | * @return int 228 | */ 229 | public function modifiedTime($remoteFile, $format = null) 230 | { 231 | $time = $this->ftp->mdtm($remoteFile); 232 | 233 | if ($time !== -1 && $format !== null) { 234 | return date($format, $time); 235 | } 236 | 237 | return $time; 238 | } 239 | 240 | /** 241 | * Changes to the parent directory. 242 | * 243 | * @throws FtpException 244 | * @return FtpClient 245 | */ 246 | public function up() 247 | { 248 | $result = $this->ftp->cdup(); 249 | 250 | if ($result === false) { 251 | throw new FtpException('Unable to get parent folder'); 252 | } 253 | 254 | return $this; 255 | } 256 | 257 | /** 258 | * Returns a list of files in the given directory. 259 | * 260 | * @param string $directory The directory, by default is "." the current directory 261 | * @param bool $recursive 262 | * @param callable $filter A callable to filter the result, by default is asort() PHP function. 263 | * The result is passed in array argument, 264 | * must take the argument by reference ! 265 | * The callable should proceed with the reference array 266 | * because is the behavior of several PHP sorting 267 | * functions (by reference ensure directly the compatibility 268 | * with all PHP sorting functions). 269 | * 270 | * @return array 271 | * @throws FtpException If unable to list the directory 272 | */ 273 | public function nlist($directory = '.', $recursive = false, $filter = 'sort') 274 | { 275 | if (!$this->isDir($directory)) { 276 | throw new FtpException('"'.$directory.'" is not a directory'); 277 | } 278 | 279 | $files = $this->ftp->nlist($directory); 280 | 281 | if ($files === false) { 282 | throw new FtpException('Unable to list directory'); 283 | } 284 | 285 | $result = array(); 286 | $dir_len = strlen($directory); 287 | 288 | // if it's the current 289 | if (false !== ($kdot = array_search('.', $files))) { 290 | unset($files[$kdot]); 291 | } 292 | 293 | // if it's the parent 294 | if(false !== ($kdot = array_search('..', $files))) { 295 | unset($files[$kdot]); 296 | } 297 | 298 | if (!$recursive) { 299 | $result = $files; 300 | 301 | // working with the reference (behavior of several PHP sorting functions) 302 | $filter($result); 303 | 304 | return $result; 305 | } 306 | 307 | // utils for recursion 308 | $flatten = function (array $arr) use (&$flatten) { 309 | $flat = []; 310 | 311 | foreach ($arr as $k => $v) { 312 | if (is_array($v)) { 313 | $flat = array_merge($flat, $flatten($v)); 314 | } else { 315 | $flat[] = $v; 316 | } 317 | } 318 | 319 | return $flat; 320 | }; 321 | 322 | foreach ($files as $file) { 323 | $file = $directory.'/'.$file; 324 | 325 | // if contains the root path (behavior of the recursivity) 326 | if (0 === strpos($file, $directory, $dir_len)) { 327 | $file = substr($file, $dir_len); 328 | } 329 | 330 | if ($this->isDir($file)) { 331 | $result[] = $file; 332 | $items = $flatten($this->nlist($file, true, $filter)); 333 | 334 | foreach ($items as $item) { 335 | $result[] = $item; 336 | } 337 | 338 | } else { 339 | $result[] = $file; 340 | } 341 | } 342 | 343 | $result = array_unique($result); 344 | $filter($result); 345 | 346 | return $result; 347 | } 348 | 349 | /** 350 | * Creates a directory. 351 | * 352 | * @see FtpClient::rmdir() 353 | * @see FtpClient::remove() 354 | * @see FtpClient::put() 355 | * @see FtpClient::putAll() 356 | * 357 | * @param string $directory The directory 358 | * @param bool $recursive 359 | * @return bool 360 | */ 361 | public function mkdir($directory, $recursive = false) 362 | { 363 | if (!$recursive or $this->isDir($directory)) { 364 | return $this->ftp->mkdir($directory); 365 | } 366 | 367 | $result = false; 368 | $pwd = $this->ftp->pwd(); 369 | $parts = explode('/', $directory); 370 | 371 | foreach ($parts as $part) { 372 | if ($part == '') { 373 | continue; 374 | } 375 | 376 | if (!@$this->ftp->chdir($part)) { 377 | $result = $this->ftp->mkdir($part); 378 | $this->ftp->chdir($part); 379 | } 380 | } 381 | 382 | $this->ftp->chdir($pwd); 383 | 384 | return $result; 385 | } 386 | 387 | /** 388 | * Remove a directory. 389 | * 390 | * @see FtpClient::mkdir() 391 | * @see FtpClient::cleanDir() 392 | * @see FtpClient::remove() 393 | * @see FtpClient::delete() 394 | * @param string $directory 395 | * @param bool $recursive Forces deletion if the directory is not empty 396 | * @return bool 397 | * @throws FtpException If unable to list the directory to remove 398 | */ 399 | public function rmdir($directory, $recursive = true) 400 | { 401 | if ($recursive) { 402 | $files = $this->nlist($directory, false, 'rsort'); 403 | 404 | // remove children 405 | foreach ($files as $file) { 406 | $this->remove($file, true); 407 | } 408 | } 409 | 410 | // remove the directory 411 | return $this->ftp->rmdir($directory); 412 | } 413 | 414 | /** 415 | * Empty directory. 416 | * 417 | * @see FtpClient::remove() 418 | * @see FtpClient::delete() 419 | * @see FtpClient::rmdir() 420 | * 421 | * @param string $directory 422 | * @return bool 423 | */ 424 | public function cleanDir($directory) 425 | { 426 | if (!$files = $this->nlist($directory)) { 427 | return $this->isEmpty($directory); 428 | } 429 | 430 | // remove children 431 | foreach ($files as $file) { 432 | $this->remove($file, true); 433 | } 434 | 435 | return $this->isEmpty($directory); 436 | } 437 | 438 | /** 439 | * Remove a file or a directory. 440 | * 441 | * @see FtpClient::rmdir() 442 | * @see FtpClient::cleanDir() 443 | * @see FtpClient::delete() 444 | * @param string $path The path of the file or directory to remove 445 | * @param bool $recursive Is effective only if $path is a directory, {@see FtpClient::rmdir()} 446 | * @return bool 447 | */ 448 | public function remove($path, $recursive = false) 449 | { 450 | if ($path == '.' || $path == '..') { 451 | return false; 452 | } 453 | 454 | try { 455 | if (@$this->ftp->delete($path) 456 | or ($this->isDir($path) 457 | and $this->rmdir($path, $recursive))) { 458 | return true; 459 | } else { 460 | // in special cases the delete can fail (for example, at Symfony's "r+e.gex[c]a(r)s" directory) 461 | $newPath = preg_replace('/[^A-Za-z0-9\/]/', '', $path); 462 | if ($this->rename($path, $newPath)) { 463 | if (@$this->ftp->delete($newPath) 464 | or ($this->isDir($newPath) 465 | and $this->rmdir($newPath, $recursive))) { 466 | return true; 467 | } 468 | } 469 | } 470 | 471 | return false; 472 | } catch (\Exception $e) { 473 | return false; 474 | } 475 | } 476 | 477 | /** 478 | * Check if a directory exist. 479 | * 480 | * @param string $directory 481 | * @return bool 482 | * @throws FtpException 483 | */ 484 | public function isDir($directory) 485 | { 486 | $pwd = $this->ftp->pwd(); 487 | 488 | if ($pwd === false) { 489 | throw new FtpException('Unable to resolve the current directory'); 490 | } 491 | 492 | if (@$this->ftp->chdir($directory)) { 493 | $this->ftp->chdir($pwd); 494 | return true; 495 | } 496 | 497 | $this->ftp->chdir($pwd); 498 | 499 | return false; 500 | } 501 | 502 | /** 503 | * Check if a directory is empty. 504 | * 505 | * @param string $directory 506 | * @return bool 507 | */ 508 | public function isEmpty($directory) 509 | { 510 | return $this->countItems($directory, null, false) === 0 ? true : false; 511 | } 512 | 513 | /** 514 | * Scan a directory and returns the details of each item. 515 | * 516 | * @see FtpClient::nlist() 517 | * @see FtpClient::rawlist() 518 | * @see FtpClient::parseRawList() 519 | * @see FtpClient::dirSize() 520 | * @param string $directory 521 | * @param bool $recursive 522 | * @return array 523 | */ 524 | public function scanDir($directory = '.', $recursive = false) 525 | { 526 | return $this->parseRawList($this->rawlist($directory, $recursive)); 527 | } 528 | 529 | /** 530 | * Returns the total size of the given directory in bytes. 531 | * 532 | * @param string $directory The directory, by default is the current directory. 533 | * @param bool $recursive true by default 534 | * @return int The size in bytes. 535 | */ 536 | public function dirSize($directory = '.', $recursive = true) 537 | { 538 | $items = $this->scanDir($directory, $recursive); 539 | $size = 0; 540 | 541 | foreach ($items as $item) { 542 | $size += (int) $item['size']; 543 | } 544 | 545 | return $size; 546 | } 547 | 548 | /** 549 | * Count the items (file, directory, link, unknown). 550 | * 551 | * @param string $directory The directory, by default is the current directory. 552 | * @param string|null $type The type of item to count (file, directory, link, unknown) 553 | * @param bool $recursive true by default 554 | * @return int 555 | */ 556 | public function countItems($directory = '.', $type = null, $recursive = true) 557 | { 558 | $items = (null === $type ? $this->nlist($directory, $recursive) 559 | : $this->scanDir($directory, $recursive)); 560 | 561 | $count = 0; 562 | foreach ($items as $item) { 563 | if (null === $type or $item['type'] == $type) { 564 | $count++; 565 | } 566 | } 567 | 568 | return $count; 569 | } 570 | 571 | /** 572 | * Count the items (file, directory, link, unknown). 573 | * This method call `countItems()` with the default arguments. 574 | * 575 | * @see countItems 576 | * @return int 577 | */ 578 | public function count() 579 | { 580 | return $this->countItems(); 581 | } 582 | 583 | /** 584 | * Downloads a file from the FTP server into a string 585 | * 586 | * @param string $remote_file 587 | * @param int $mode 588 | * @param int $resumepos 589 | * @return string|null 590 | */ 591 | public function getContent($remote_file, $mode = FTP_BINARY, $resumepos = 0) 592 | { 593 | $handle = fopen('php://temp', 'r+'); 594 | 595 | if ($this->ftp->fget($handle, $remote_file, $mode, $resumepos)) { 596 | rewind($handle); 597 | return stream_get_contents($handle); 598 | } 599 | 600 | return null; 601 | } 602 | 603 | /** 604 | * Uploads a file to the server from a string. 605 | * 606 | * @param string $remote_file 607 | * @param string $content 608 | * @return FtpClient 609 | * @throws FtpException When the transfer fails 610 | */ 611 | public function putFromString($remote_file, $content) 612 | { 613 | $handle = fopen('php://temp', 'w'); 614 | 615 | fwrite($handle, $content); 616 | rewind($handle); 617 | 618 | if ($this->ftp->fput($remote_file, $handle, FTP_BINARY)) { 619 | return $this; 620 | } 621 | 622 | throw new FtpException('Unable to put the file "'.$remote_file.'"'); 623 | } 624 | 625 | /** 626 | * Uploads a file to the server. 627 | * 628 | * @param string $local_file 629 | * @return FtpClient 630 | * @throws FtpException When the transfer fails 631 | */ 632 | public function putFromPath($local_file) 633 | { 634 | $remote_file = basename($local_file); 635 | $handle = fopen($local_file, 'r'); 636 | 637 | if ($this->ftp->fput($remote_file, $handle, FTP_BINARY)) { 638 | rewind($handle); 639 | return $this; 640 | } 641 | 642 | throw new FtpException( 643 | 'Unable to put the remote file from the local file "'.$local_file.'"' 644 | ); 645 | } 646 | 647 | /** 648 | * Upload files. 649 | * 650 | * @param string $source_directory 651 | * @param string $target_directory 652 | * @param int $mode 653 | * @return FtpClient 654 | */ 655 | public function putAll($source_directory, $target_directory, $mode = FTP_BINARY) 656 | { 657 | $d = dir($source_directory); 658 | 659 | // do this for each file in the directory 660 | while ($file = $d->read()) { 661 | 662 | // to prevent an infinite loop 663 | if ($file != "." && $file != "..") { 664 | 665 | // do the following if it is a directory 666 | if (is_dir($source_directory.'/'.$file)) { 667 | if (!$this->isDir($target_directory.'/'.$file)) { 668 | 669 | // create directories that do not yet exist 670 | $this->ftp->mkdir($target_directory.'/'.$file); 671 | } 672 | 673 | // recursive part 674 | $this->putAll( 675 | $source_directory.'/'.$file, $target_directory.'/'.$file, 676 | $mode 677 | ); 678 | } else { 679 | 680 | // put the files 681 | $this->ftp->put( 682 | $target_directory.'/'.$file, $source_directory.'/'.$file, 683 | $mode 684 | ); 685 | } 686 | } 687 | } 688 | 689 | $d->close(); 690 | 691 | return $this; 692 | } 693 | 694 | /** 695 | * Downloads all files from remote FTP directory 696 | * 697 | * @param string $source_directory The remote directory 698 | * @param string $target_directory The local directory 699 | * @param int $mode 700 | * @return FtpClient 701 | */ 702 | public function getAll($source_directory, $target_directory, $mode = FTP_BINARY) 703 | { 704 | if ($source_directory != ".") { 705 | if ($this->ftp->chdir($source_directory) == false) { 706 | throw new FtpException("Unable to change directory: ".$source_directory); 707 | } 708 | 709 | if (!(is_dir($target_directory))) { 710 | mkdir($target_directory); 711 | } 712 | 713 | chdir($target_directory); 714 | } 715 | 716 | $contents = $this->ftp->nlist("."); 717 | 718 | foreach ($contents as $file) { 719 | if ($file == '.' || $file == '..') { 720 | continue; 721 | } 722 | 723 | $this->ftp->get($target_directory."/".$file, $file, $mode); 724 | } 725 | 726 | $this->ftp->chdir(".."); 727 | chdir(".."); 728 | 729 | return $this; 730 | } 731 | 732 | /** 733 | * Returns a detailed list of files in the given directory. 734 | * 735 | * @see FtpClient::nlist() 736 | * @see FtpClient::scanDir() 737 | * @see FtpClient::dirSize() 738 | * @param string $directory The directory, by default is the current directory 739 | * @param bool $recursive 740 | * @return array 741 | * @throws FtpException 742 | */ 743 | public function rawlist($directory = '.', $recursive = false) 744 | { 745 | if (!$this->isDir($directory)) { 746 | throw new FtpException('"'.$directory.'" is not a directory.'); 747 | } 748 | 749 | if (strpos($directory, " ") > 0) { 750 | $ftproot = $this->ftp->pwd(); 751 | $this->ftp->chdir($directory); 752 | $list = $this->ftp->rawlist(""); 753 | $this->ftp->chdir($ftproot); 754 | } else { 755 | $list = $this->ftp->rawlist($directory); 756 | } 757 | 758 | $items = array(); 759 | 760 | if (!$list) { 761 | return $items; 762 | } 763 | 764 | if (false == $recursive) { 765 | foreach ($list as $path => $item) { 766 | $chunks = preg_split("/\s+/", $item, 9); 767 | 768 | // if not "name" 769 | if (!isset($chunks[8]) || strlen($chunks[8]) === 0 || $chunks[8] == '.' || $chunks[8] == '..') { 770 | continue; 771 | } 772 | 773 | $path = $directory.'/'.$chunks[8]; 774 | 775 | if (isset($chunks[9])) { 776 | $nbChunks = count($chunks); 777 | 778 | for ($i = 9; $i < $nbChunks; $i++) { 779 | $path .= ' '.$chunks[$i]; 780 | } 781 | } 782 | 783 | 784 | if (substr($path, 0, 2) == './') { 785 | $path = substr($path, 2); 786 | } 787 | 788 | $items[ $this->rawToType($item).'#'.$path ] = $item; 789 | } 790 | 791 | return $items; 792 | } 793 | 794 | $path = ''; 795 | 796 | foreach ($list as $item) { 797 | $len = strlen($item); 798 | 799 | if (!$len 800 | 801 | // "." 802 | || ($item[$len-1] == '.' && $item[$len-2] == ' ' 803 | 804 | // ".." 805 | or $item[$len-1] == '.' && $item[$len-2] == '.' && $item[$len-3] == ' ') 806 | ) { 807 | 808 | continue; 809 | } 810 | 811 | $chunks = preg_split("/\s+/", $item); 812 | 813 | // if not "name" 814 | if (!isset($chunks[8]) || strlen($chunks[8]) === 0 || $chunks[8] == '.' || $chunks[8] == '..') { 815 | continue; 816 | } 817 | 818 | $path = $directory.'/'.$chunks[8]; 819 | 820 | if (isset($chunks[9])) { 821 | $nbChunks = count($chunks); 822 | 823 | for ($i = 9; $i < $nbChunks; $i++) { 824 | $path .= ' '.$chunks[$i]; 825 | } 826 | } 827 | 828 | if (substr($path, 0, 2) == './') { 829 | $path = substr($path, 2); 830 | } 831 | 832 | $items[$this->rawToType($item).'#'.$path] = $item; 833 | 834 | if ($item[0] == 'd') { 835 | $sublist = $this->rawlist($path, true); 836 | 837 | foreach ($sublist as $subpath => $subitem) { 838 | $items[$subpath] = $subitem; 839 | } 840 | } 841 | } 842 | 843 | return $items; 844 | } 845 | 846 | /** 847 | * Parse raw list. 848 | * 849 | * @see FtpClient::rawlist() 850 | * @see FtpClient::scanDir() 851 | * @see FtpClient::dirSize() 852 | * @param array $rawlist 853 | * @return array 854 | */ 855 | public function parseRawList(array $rawlist) 856 | { 857 | $items = array(); 858 | $path = ''; 859 | 860 | foreach ($rawlist as $key => $child) { 861 | $chunks = preg_split("/\s+/", $child, 9); 862 | 863 | if (isset($chunks[8]) && ($chunks[8] == '.' or $chunks[8] == '..')) { 864 | continue; 865 | } 866 | 867 | if (count($chunks) === 1) { 868 | $len = strlen($chunks[0]); 869 | 870 | if ($len && $chunks[0][$len-1] == ':') { 871 | $path = substr($chunks[0], 0, -1); 872 | } 873 | 874 | continue; 875 | } 876 | 877 | // Prepare for filename that has space 878 | $nameSlices = array_slice($chunks, 8, true); 879 | 880 | $item = [ 881 | 'permissions' => $chunks[0], 882 | 'number' => $chunks[1], 883 | 'owner' => $chunks[2], 884 | 'group' => $chunks[3], 885 | 'size' => $chunks[4], 886 | 'month' => $chunks[5], 887 | 'day' => $chunks[6], 888 | 'time' => $chunks[7], 889 | 'name' => implode(' ', $nameSlices), 890 | 'type' => $this->rawToType($chunks[0]), 891 | ]; 892 | 893 | if ($item['type'] == 'link' && isset($chunks[10])) { 894 | $item['target'] = $chunks[10]; // 9 is "->" 895 | } 896 | 897 | // if the key is not the path, behavior of ftp_rawlist() PHP function 898 | if (is_int($key) || false === strpos($key, $item['name'])) { 899 | array_splice($chunks, 0, 8); 900 | 901 | $key = $item['type'].'#' 902 | .($path ? $path.'/' : '') 903 | .implode(' ', $chunks); 904 | 905 | if ($item['type'] == 'link') { 906 | // get the first part of 'link#the-link.ext -> /path/of/the/source.ext' 907 | $exp = explode(' ->', $key); 908 | $key = rtrim($exp[0]); 909 | } 910 | 911 | $items[$key] = $item; 912 | } else { 913 | // the key is the path, behavior of FtpClient::rawlist() method() 914 | $items[$key] = $item; 915 | } 916 | } 917 | 918 | return $items; 919 | } 920 | 921 | /** 922 | * Convert raw info (drwx---r-x ...) to type (file, directory, link, unknown). 923 | * Only the first char is used for resolving. 924 | * 925 | * @param string $permission Example : drwx---r-x 926 | * 927 | * @return string The file type (file, directory, link, unknown) 928 | * @throws FtpException 929 | */ 930 | public function rawToType($permission) 931 | { 932 | if (!is_string($permission)) { 933 | throw new FtpException('The "$permission" argument must be a string, "' 934 | .gettype($permission).'" given.'); 935 | } 936 | 937 | if (empty($permission[0])) { 938 | return 'unknown'; 939 | } 940 | 941 | switch ($permission[0]) { 942 | case '-': 943 | return 'file'; 944 | 945 | case 'd': 946 | return 'directory'; 947 | 948 | case 'l': 949 | return 'link'; 950 | 951 | default: 952 | return 'unknown'; 953 | } 954 | } 955 | 956 | /** 957 | * Set the wrapper which forward the PHP FTP functions to use in FtpClient instance. 958 | * 959 | * @param FtpWrapper $wrapper 960 | * @return FtpClient 961 | */ 962 | protected function setWrapper(FtpWrapper $wrapper) 963 | { 964 | $this->ftp = $wrapper; 965 | 966 | return $this; 967 | } 968 | } 969 | -------------------------------------------------------------------------------- /src/FtpClient/FtpException.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | * 10 | * @copyright Nicolas Tallefourtane http://nicolab.net 11 | */ 12 | namespace FtpClient; 13 | 14 | /** 15 | * The FtpException class. 16 | * Exception thrown if an error on runtime of the FTP client occurs. 17 | * @inheritDoc 18 | * @author Nicolas Tallefourtane 19 | */ 20 | class FtpException extends \Exception {} 21 | -------------------------------------------------------------------------------- /src/FtpClient/FtpWrapper.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | * 10 | * @copyright Nicolas Tallefourtane http://nicolab.net 11 | */ 12 | namespace FtpClient; 13 | 14 | /** 15 | * Wrap the PHP FTP functions 16 | * 17 | * @method bool alloc(int $filesize, string &$result = null) Allocates space for a file to be uploaded 18 | * @method bool cdup() Changes to the parent directory 19 | * @method bool chdir(string $directory) Changes the current directory on a FTP server 20 | * @method int chmod(int $mode, string $filename) Set permissions on a file via FTP 21 | * @method bool close() Closes an FTP connection 22 | * @method bool delete(string $path) Deletes a file on the FTP server 23 | * @method bool exec(string $command) Requests execution of a command on the FTP server 24 | * @method bool fget(resource $handle, string $remote_file, int $mode, int $resumepos = 0) Downloads a file from the FTP server and saves to an open file 25 | * @method bool fput(string $remote_file, resource $handle, int $mode, int $startpos = 0) Uploads from an open file to the FTP server 26 | * @method mixed get_option(int $option) Retrieves various runtime behaviours of the current FTP stream 27 | * @method bool get(string $local_file, string $remote_file, int $mode, int $resumepos = 0) Downloads a file from the FTP server 28 | * @method bool login(string $username, string $password) Logs in to an FTP connection 29 | * @method int mdtm(string $remote_file) Returns the last modified time of the given file 30 | * @method bool mkdir(string $directory) Creates a directory 31 | * @method array mlsd(string $remote_dir) Returns a list of files in the given directory 32 | * @method int nb_continue() Continues retrieving/sending a file (non-blocking) 33 | * @method int nb_fget(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) 34 | * @method int nb_fput(string $remote_file, resource $handle, int $mode, int $startpos = 0) Stores a file from an open file to the FTP server (non-blocking) 35 | * @method int nb_get(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) 36 | * @method int nb_put(string $remote_file, string $local_file, int $mode, int $startpos = 0) Stores a file on the FTP server (non-blocking) 37 | * @method array nlist(string $directory) Returns a list of file names in the given directory; remote_dir parameter may also include arguments 38 | * @method bool pasv(bool $pasv) Turns passive mode on or off 39 | * @method bool put(string $remote_file, string $local_file, int $mode, int $startpos = 0) Uploads a file to the FTP server 40 | * @method string pwd() Returns the current directory name 41 | * @method bool quit() Closes an FTP connection 42 | * @method array raw(string $command) Sends an arbitrary command to an FTP server 43 | * @method array rawlist(string $directory, bool $recursive = false) Returns a detailed list of files in the given directory 44 | * @method bool rename(string $oldname, string $newname) Renames a file or a directory on the FTP server 45 | * @method bool rmdir(string $directory) Removes a directory 46 | * @method bool set_option(int $option, mixed $value) Set miscellaneous runtime FTP options 47 | * @method bool site(string $command) Sends a SITE command to the server 48 | * @method int size(string $remote_file) Returns the size of the given file 49 | * @method string systype() Returns the system type identifier of the remote FTP server 50 | * 51 | * @author Nicolas Tallefourtane 52 | */ 53 | class FtpWrapper 54 | { 55 | /** 56 | * The connection with the server 57 | * 58 | * @var resource 59 | */ 60 | protected $conn; 61 | 62 | /** 63 | * Constructor. 64 | * 65 | * @param resource &$connection The FTP (or SSL-FTP) connection (takes by reference). 66 | */ 67 | public function __construct(&$connection) 68 | { 69 | $this->conn = &$connection; 70 | } 71 | 72 | /** 73 | * Forward the method call to FTP functions 74 | * 75 | * @param string $function 76 | * @param array $arguments 77 | * @return mixed 78 | * @throws FtpException When the function is not valid 79 | */ 80 | public function __call($function, array $arguments) 81 | { 82 | $function = 'ftp_' . $function; 83 | 84 | if (function_exists($function)) { 85 | array_unshift($arguments, $this->conn); 86 | return @call_user_func_array($function, $arguments); 87 | } 88 | 89 | throw new FtpException("{$function} is not a valid FTP function"); 90 | } 91 | 92 | /** 93 | * Opens a FTP connection 94 | * 95 | * @param string $host 96 | * @param int $port 97 | * @param int $timeout 98 | * @return resource 99 | */ 100 | public function connect($host, $port = 21, $timeout = 90) 101 | { 102 | return @ftp_connect($host, $port, $timeout); 103 | } 104 | 105 | /** 106 | * Opens a Secure SSL-FTP connection 107 | * @param string $host 108 | * @param int $port 109 | * @param int $timeout 110 | * @return resource 111 | */ 112 | public function ssl_connect($host, $port = 21, $timeout = 90) 113 | { 114 | return @ftp_ssl_connect($host, $port, $timeout); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /tests/.atoum.php: -------------------------------------------------------------------------------- 1 | addDefaultReport(); 5 | 6 | // This will add a green or red logo after each run depending on its status. 7 | $report->addField(new atoum\report\fields\runner\result\logo()); 8 | 9 | $script->bootstrapFile(__DIR__. '/bootstrap.php'); 10 | $runner->addTestsFromDirectory(__DIR__. '/units'); 11 | -------------------------------------------------------------------------------- /tests/bootstrap.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | * 10 | * @copyright Nicolas Tallefourtane http://nicolab.net 11 | */ 12 | namespace tests\units\FtpClient; 13 | 14 | use 15 | mageekguy\atoum, 16 | FtpClient\FtpClient as TestedClass 17 | ; 18 | 19 | /** 20 | * Tests the FtpClient\FtpClient class. 21 | * @author Nicolas Tallefourtane 22 | */ 23 | class FtpClient extends atoum\test 24 | { 25 | 26 | public function test__construct() 27 | { 28 | $this 29 | ->given($ftp = new TestedClass()) 30 | ->object($ftp) 31 | ->isInstanceOf('\FtpClient\FtpClient') 32 | ; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /tests/units/FtpClient/FtpException.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | * 10 | * @copyright Nicolas Tallefourtane http://nicolab.net 11 | */ 12 | namespace tests\units\FtpClient; 13 | 14 | use 15 | mageekguy\atoum, 16 | FtpClient\FtpException as TestedClass 17 | ; 18 | 19 | /** 20 | * Tests the FtpClient\FtpException class. 21 | * @author Nicolas Tallefourtane 22 | */ 23 | class FtpException extends atoum\test 24 | { 25 | 26 | public function test__instance() 27 | { 28 | $ftp = new \FtpClient\FtpClient(); 29 | 30 | $this 31 | ->given($e = new TestedClass()) 32 | ->object($e) 33 | ->isInstanceOf('\FtpClient\FtpException') 34 | ->isInstanceOf('\Exception') 35 | 36 | ->exception(function () use ($ftp) { 37 | $ftp->doNotExist(); 38 | }) 39 | ->isInstanceOf('\FtpClient\FtpException') 40 | ->isInstanceOf('\Exception') 41 | ; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /tests/units/FtpClient/FtpWrapper.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | * 10 | * @copyright Nicolas Tallefourtane http://nicolab.net 11 | */ 12 | namespace tests\units\FtpClient; 13 | 14 | use 15 | mageekguy\atoum, 16 | FtpClient\FtpWrapper as TestedClass 17 | ; 18 | 19 | /** 20 | * Tests the FtpClient\FtpWrapper class. 21 | * @author Nicolas Tallefourtane 22 | */ 23 | class FtpWrapper extends atoum\test 24 | { 25 | 26 | public function test__construct() 27 | { 28 | $conn = null; 29 | 30 | $this 31 | ->given($wrapper = new TestedClass($conn)) 32 | ->object($wrapper) 33 | ->isInstanceOf('\FtpClient\FtpWrapper') 34 | ; 35 | } 36 | 37 | public function test__call() 38 | { 39 | $conn = null; 40 | 41 | $this 42 | ->given($wrapper = new TestedClass($conn)) 43 | ->exception(function () use ($wrapper) { 44 | $wrapper->doNotExist(); 45 | }) 46 | ->isInstanceOf('\FtpClient\FtpException') 47 | ->isInstanceOf('\Exception') 48 | 49 | ->variable(array($wrapper, 'alloc')) 50 | ->isCallable() 51 | ; 52 | } 53 | } 54 | --------------------------------------------------------------------------------