Completed
Pull Request — master (#5)
by
unknown
02:28
created

Client::isDir()   A

Complexity

Conditions 2
Paths 3

Size

Total Lines 11
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 7
c 1
b 0
f 0
nc 3
nop 1
dl 0
loc 11
ccs 0
cts 7
cp 0
crap 6
rs 10
1
<?php
2
/**
3
 * This file is part of SebastianFeldmann\Ftp.
4
 *
5
 * (c) Sebastian Feldmann <[email protected]>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
namespace SebastianFeldmann\Ftp;
11
12
use RuntimeException;
13
14
/**
15
 * Class Client
16
 *
17
 * @package SebastianFeldmann\Ftp
18
 * @author  Sebastian Feldmann <[email protected]>
19
 * @link    https://github.com/sebastianfeldmann/ftp
20
 * @since   Class available since Release 0.9.0
21
 *
22
 * @method bool     alloc(int $filesize, ?string &$result)                                          - Allocates space for a file to be uploaded
23
 * @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  (>= php 7.2.0)
24
 * @method bool     cdUp()                                                                          - Changes to the parent directory
25
 * @method string   chDir(string $directory)                                                        - Changes the current directory on a FTP server
26
 * @method int      chmod(int $mode, string $filename)                                              - Set permissions on a file via FTP
27
 * @method bool     close()                                                                         - Closes an FTP connection
28
 * @method bool     delete(string $path)                                                            - Deletes a file on the FTP server
29
 * @method bool     exec(string $command)                                                           - Requests execution of a command on the FTP server
30
 * @method bool     fget(resource $handle , string $remote_file, ?int $mode, ?int $resumepos)       - Downloads a file from the FTP server and saves to an open file
31
 * @method bool     fput(string $remote_file, resource $handle, ?int $mode, ?int $startpos)         - Uploads from an open file to the FTP server
32
 * @method mixed    get_option(int $option)                                                         - Retrieves various runtime behaviours of the current FTP stream
33
 * @method bool     get(string $local_file, string $remote_file, ?int $mode, ?int $resumepos)       - Downloads a file from the FTP server
34
 * @method string   mdtm(string $file)                                                              - Returns last modification time from given file
35
 * @method bool     mkDir(string $path)                                                             - Create a directory on the FTP server
36
 * @method array    mlsd(string $path)                                                              - Return list of file info arrays (>= php 7.2.0)
37
 * @method int      nb_continue()                                                                   - Continues retrieving/sending a file (non-blocking)
38
 * @method int      nb_fget(resource $handle, string $remote_file, ?int $mode, ?int $resumepos)     - Retrieves a file from the FTP server and writes it to an open file (non-blocking)
39
 * @method int      nb_fput(resource $handle, string $remote_file, ?int $mode, ?int $resumepos)     - Stores a file from an open file to the FTP server (non-blocking)
40
 * @method int      nb_get(string $local_file , string $remote_file, ?int $mode, ?int $resumepos)   - Retrieves a file from the FTP server and writes it to a local file (non-blocking)
41
 * @method int      nb_put(string $local_file , string $remote_file, ?int $mode, ?int $resumepos)   - Stores a file on the FTP server (non-blocking)
42
 * @method array    nlist(string $path)                                                             - Returns list of files in given dir
43
 * @method bool     pasv(bool $passive)                                                             - Sets the ftp passive mode on or off
44
 * @method bool     put(string $name, string $file, int $mode)                                      - Uploads a file to the FTP server
45
 * @method string   pwd()                                                                           - Returns current working directory
46
 * @method bool     quit()                                                                          - Alias of close()
47
 * @method array    raw(string $command)                                                            - Sends an arbitrary command to an FTP server
48
 * @method array    rawlist(string $directory, ?bool $recursive)                                    - Returns a detailed list of files in the given directory
49
 * @method bool     rename(string $oldname, string $newname)                                        - Renames a file or a directory on the FTP server
50
 * @method bool     rmdir(string $directory)                                                        - Removes a directory
51
 * @method bool     set_option(int $option, mixed $value)                                           - Set miscellaneous runtime FTP options
52
 * @method bool     site(string $command)                                                           - Sends a SITE command to the server
53
 * @method int      size(string $file)                                                              - Returns given files sizes in bytes
54
 * @method string   systype()                                                                       - Returns the system type identifier of the remote FTP server
55
 *
56
 *
57
 */
58
class Client
59
{
60
    /**
61
     * PHP FTP connection resource.
62
     *
63
     * @var resource
64
     */
65
    private $connection;
66
67
    /**
68
     * Host to connect to.
69
     *
70
     * @var string
71
     */
72
    private $host;
73
74
    /**
75
     * Port to connect to.
76
     *
77
     * @var int
78
     */
79
    private $port;
80
81
    /**
82
     * User to login.
83
     *
84
     * @var string
85
     */
86
    private $user;
87
88
    /**
89
     * Password to login.
90
     *
91
     * @var string
92
     */
93
    private $password;
94
95
    /**
96
     * Use passive ftp mode
97
     *
98
     * @var bool
99
     */
100
    private $passive;
101
102
    /**
103
     * Use ftps connection
104
     *
105
     * @var bool
106
     */
107
    private $isSecure;
108
109
    /**
110
     * Client constructor.
111
     *
112
     * @param string $url
113
     * @param bool   $passive
114
     * @param bool   $isSecure
115
     */
116 1
    public function __construct(string $url, bool $passive = false, bool $isSecure = false)
117
    {
118 1
        if (!extension_loaded('ftp')) {
119
            throw new RuntimeException('FTP extension is not loaded.');
120
        }
121 1
        $this->passive  = $passive;
122 1
        $this->isSecure = $isSecure;
123 1
        $this->setup($url);
124 1
        $this->login();
125 1
    }
126
127
    /**
128
     * Determine if file is a directory.
129
     *
130
     * @param  string $name
131
     * @return bool
132
     */
133
    public function isDir(string $name)
134
    {
135
        $current = $this->pwd();
136
        try {
137
            $this->chDir($name);
138
            $this->chDir($current);
139
            return true;
140
        } catch (\Exception $e) {
141
            // do nothing
142
        }
143
        return false;
144
    }
145
146
    /**
147
     * Returns to the home directory.
148
     *
149
     * @return void
150
     */
151
    public function chHome()
152
    {
153
        $this->chDir('');
154
    }
155
156
    /**
157
     * Return list of all files in directory.
158
     *
159
     * @param  string $path
160
     * @return \SebastianFeldmann\Ftp\File[]
161
     * @throws \Exception
162
     */
163 1
    public function ls(string $path = '') : array
164
    {
165 1
        return version_compare(PHP_VERSION, '7.2.0', '>=')
166 1
            ? $this->ls72($path)
167 1
            : $this->lsLegacy($path);
168
169
    }
170
171
    /**
172
     * Return list of all files in directory for php 7.2.0 and higher.
173
     *
174
     * @param  string $path
175
     * @return \SebastianFeldmann\Ftp\File[]
176
     * @throws \Exception
177
     */
178 1
    private function ls72(string $path) : array
179
    {
180 1
        $list = [];
181 1
        foreach ($this->mlsd($path) as $fileInfo) {
182 1
            $list[] = new File($fileInfo);
183
        }
184 1
        return $list;
185
    }
186
187
    /**
188
     * Return list of all files in directory for php version below 7.2.0.
189
     *
190
     * @param  string $path
191
     * @return \SebastianFeldmann\Ftp\File[]
192
     * @throws \Exception
193
     */
194
    private function lsLegacy(string $path) : array
195
    {
196
        $list = [];
197
        foreach ($this->nlist($path) as $name) {
198
            $type   = $this->isDir($name) ? 'dir' : 'file';
199
            $size   = $this->size($name);
200
            $mtime  = $this->mdtm($name);
201
202
            if ($mtime == -1) {
203
                throw new RuntimeException('FTP server doesnt support \'ftp_mdtm\'');
204
            }
205
206
            $list[] = new File(['name' => $name, 'modify' => $mtime, 'type' => $type, 'size' => $size]);
207
        }
208
        return $list;
209
    }
210
211
    /**
212
     * Return list of directories in given path.
213
     *
214
     * @param  string $path
215
     * @return array
216
     * @throws \Exception
217
     */
218
    public function lsDirs(string $path = '') : array
219
    {
220
        return array_filter(
221
            $this->ls($path),
222
            function(File $file) {
223
                return $file->isDir();
224
            }
225
        );
226
    }
227
228
    /**
229
     * Return list of files in given path.
230
     *
231
     * @param  string $path
232
     * @return array
233
     * @throws \Exception
234
     */
235
    public function lsFiles(string $path = '') : array
236
    {
237
        return array_filter(
238
            $this->ls($path),
239
            function(File $file) {
240
                return $file->isFile();
241
            }
242
        );
243
    }
244
245
    /**
246
     * Upload local file to ftp server.
247
     *
248
     * @param  string $file Path to local file that should be uploaded.
249
     * @param  string $path Path to store the file under.
250
     * @param  string $name Filename on the ftp server.
251
     * @return void
252
     */
253
    public function uploadFile(string $file, string $path, string $name)
254
    {
255
        // to store the file we have to make sure the directory exists
256
        foreach ($this->extractDirectories($path) as $dir) {
257
            // if change to directory fails
258
            // create the dir and change into it afterwards
259
            try {
260
                $this->chDir($dir);
261
            } catch (\Exception $e) {
262
                $this->mkDir($dir);
263
                $this->chDir($dir);
264
            }
265
        }
266
        if (!$this->put($name, $file, FTP_BINARY)) {
267
            $error   = error_get_last();
268
            $message = $error['message'];
269
            throw new RuntimeException(sprintf('error uploading file: %s - %s', $file, $message));
270
        }
271
    }
272
273
    /**
274
     * Setup local member variables by parsing the ftp url.
275
     *
276
     * @param string $url
277
     */
278 1
    private function setup(string $url)
279
    {
280 1
        $parts          = \parse_url($url);
281 1
        $this->host     = $parts['host'] ?? '';
282 1
        $this->port     = $parts['port'] ?? 21;
283 1
        $this->user     = $parts['user'] ?? '';
284 1
        $this->password = $parts['pass'] ?? '';
285 1
    }
286
287
    /**
288
     * Setup ftp connection
289
     *
290
     * @throws \RuntimeException
291
     */
292 1
    private function login()
293
    {
294 1
        if (empty($this->host)) {
295
            throw new RuntimeException('no host to connect to');
296
        }
297
298 1
        $old  = error_reporting(0);
299 1
        $link = $this->isSecure
300
              ? ftp_ssl_connect($this->host, $this->port)
301 1
              : ftp_connect($this->host, $this->port);
302
303 1
        if (!$link) {
304
            error_reporting($old);
305
            throw new RuntimeException(sprintf('unable to connect to ftp server %s', $this->host));
306
        }
307
308 1
        $this->connection = $link;
309 1
        if (!ftp_login($this->connection, $this->user, $this->password)) {
310
            error_reporting($old);
311
            throw new RuntimeException(
312
                sprintf('authentication failed for %s@%s', $this->user, $this->host)
313
            );
314
        }
315
        // set passive mode if needed
316 1
        $this->pasv($this->passive);
317 1
        error_reporting($old);
318 1
    }
319
320
    /**
321
     * Return list of remote directories to travers.
322
     *
323
     * @param  string $path
324
     * @return array
325
     */
326
    private function extractDirectories(string $path) : array
327
    {
328
        $remoteDirs = [];
329
        if (!empty($path)) {
330
            $remoteDirs = explode('/', $path);
331
            // fix empty first array element for absolute path
332
            if (substr($path, 0, 1) === '/') {
333
                $remoteDirs[0] = '/';
334
            }
335
            $remoteDirs = array_filter($remoteDirs);
336
        }
337
        return $remoteDirs;
338
    }
339
340
    /**
341
     * Handle all ftp_* functions.
342
     *
343
     * @param  string $name
344
     * @param  array  $args
345
     * @return mixed
346
     */
347 1
    public function __call($name, $args)
348
    {
349 1
        $function = 'ftp_' . strtolower($name);
350 1
        if (!function_exists($function)) {
351
            throw new RuntimeException(sprintf('invalid method call: %s', $function));
352
        }
353 1
        $old = error_reporting(0);
354 1
        array_unshift($args, $this->connection);
355 1
        if (!$result = call_user_func_array($function, $args)) {
356
            $error = error_get_last();
357
            error_reporting($old);
358
            throw new RuntimeException($error['message']);
359
        }
360 1
        error_reporting($old);
361 1
        return $result;
362
    }
363
}
364