Client::isDir()   A
last analyzed

Complexity

Conditions 2
Paths 3

Size

Total Lines 11
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 7
nc 3
nop 1
dl 0
loc 11
ccs 7
cts 7
cp 1
crap 2
rs 10
c 0
b 0
f 0
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   cdUp()                                     - Changes to the parent directory
23
 * @method string chDir(string $directory)                   - Changes the current directory on a FTP server
24
 * @method string mdtm(string $file)                         - Returns last modification time from given file
25
 * @method bool   mkDir(string $path)                        - Create a directory on the FTP server
26
 * @method array  mlsd(string $path)                         - Return list of file info arrays (>= php 7.2.0)
27
 * @method array  nlist(string $path)                        - Returns list of files in given dir
28
 * @method bool   pasv(bool $passive)                        - Sets the ftp passive mode on or off
29
 * @method bool   put(string $name, string $file, int $mode) - Uploads a file to the FTP server
30
 * @method string pwd()                                      - Returns current working directory
31
 * @method int    size(string $file)                         - Returns given files sizes in bytes
32
 */
33
class Client
34
{
35
    /**
36
     * PHP FTP connection resource.
37
     *
38
     * @var resource
39
     */
40
    private $connection;
41
42
    /**
43
     * Host to connect to.
44
     *
45
     * @var string
46
     */
47
    private $host;
48
49
    /**
50
     * Port to connect to.
51
     *
52
     * @var int
53
     */
54
    private $port;
55
56
    /**
57
     * User to login.
58
     *
59
     * @var string
60
     */
61
    private $user;
62
63
    /**
64
     * Password to login.
65
     *
66
     * @var string
67
     */
68
    private $password;
69
70
    /**
71
     * Use passive ftp mode
72
     *
73
     * @var bool
74
     */
75
    private $passive;
76
77
    /**
78
     * Use ftps connection
79
     *
80
     * @var bool
81
     */
82
    private $isSecure;
83
84
    /**
85
     * Client constructor.
86
     *
87
     * @param string $url
88
     * @param bool   $passive
89
     * @param bool   $isSecure
90
     */
91 1
    public function __construct(string $url, bool $passive = false, bool $isSecure = false)
92
    {
93 1
        if (!extension_loaded('ftp')) {
94
            throw new RuntimeException('FTP extension is not loaded.');
95
        }
96 1
        $this->passive  = $passive;
97 1
        $this->isSecure = $isSecure;
98 1
        $this->setup($url);
99 1
        $this->login();
100 1
    }
101
102
    /**
103
     * Determine if file is a directory.
104
     *
105
     * @param  string $name
106
     * @return bool
107
     */
108 1
    public function isDir(string $name)
109
    {
110 1
        $current = $this->pwd();
111
        try {
112 1
            $this->chDir($name);
113 1
            $this->chDir($current);
114 1
            return true;
115 1
        } catch (\Exception $e) {
116
            // do nothing
117
        }
118 1
        return false;
119
    }
120
121
    /**
122
     * Returns to the home directory.
123
     *
124
     * @return void
125
     */
126
    public function chHome()
127
    {
128
        $this->chDir('');
129
    }
130
131
    /**
132
     * Return list of all files in directory.
133
     *
134
     * @param  string $path
135
     * @return \SebastianFeldmann\Ftp\File[]
136
     * @throws \Exception
137
     */
138 1
    public function ls(string $path = '') : array
139
    {
140 1
        return version_compare(PHP_VERSION, '7.2.0', '>=')
141
            ? $this->ls72($path)
142 1
            : $this->lsLegacy($path);
143
144
    }
145
146
    /**
147
     * Return list of all files in directory for php 7.2.0 and higher.
148
     *
149
     * @param  string $path
150
     * @return \SebastianFeldmann\Ftp\File[]
151
     * @throws \Exception
152
     */
153
    private function ls72(string $path) : array
154
    {
155
        $list = [];
156
        foreach ($this->mlsd($path) as $fileInfo) {
157
            $list[] = new File($fileInfo);
158
        }
159
        return $list;
160
    }
161
162
    /**
163
     * Return list of all files in directory for php version below 7.2.0.
164
     *
165
     * @param  string $path
166
     * @return \SebastianFeldmann\Ftp\File[]
167
     * @throws \Exception
168
     */
169 1
    private function lsLegacy(string $path) : array
170
    {
171 1
        $list = [];
172 1
        foreach ($this->nlist($path) as $name) {
173 1
            $type   = $this->isDir($name) ? 'dir' : 'file';
174 1
            $size   = $this->size($name);
175 1
            $mtime  = $this->mdtm($name);
176
177 1
            if ($mtime == -1) {
178
                throw new RuntimeException('FTP server doesnt support \'ftp_mdtm\'');
179
            }
180
181 1
            $list[] = new File(['name' => $name, 'modify' => $mtime, 'type' => $type, 'size' => $size]);
182
        }
183 1
        return $list;
184
    }
185
186
    /**
187
     * Return list of directories in given path.
188
     *
189
     * @param  string $path
190
     * @return array
191
     * @throws \Exception
192
     */
193
    public function lsDirs(string $path = '') : array
194
    {
195
        return array_filter(
196
            $this->ls($path),
197
            function(File $file) {
198
                return $file->isDir();
199
            }
200
        );
201
    }
202
203
    /**
204
     * Return list of files in given path.
205
     *
206
     * @param  string $path
207
     * @return array
208
     * @throws \Exception
209
     */
210
    public function lsFiles(string $path = '') : array
211
    {
212
        return array_filter(
213
            $this->ls($path),
214
            function(File $file) {
215
                return $file->isFile();
216
            }
217
        );
218
    }
219
220
    /**
221
     * Upload local file to ftp server.
222
     *
223
     * @param  string $file Path to local file that should be uploaded.
224
     * @param  string $path Path to store the file under.
225
     * @param  string $name Filename on the ftp server.
226
     * @return void
227
     */
228
    public function uploadFile(string $file, string $path, string $name)
229
    {
230
        // to store the file we have to make sure the directory exists
231
        foreach ($this->extractDirectories($path) as $dir) {
232
            // if change to directory fails
233
            // create the dir and change into it afterwards
234
            try {
235
                $this->chDir($dir);
236
            } catch (\Exception $e) {
237
                $this->mkDir($dir);
238
                $this->chDir($dir);
239
            }
240
        }
241
        if (!$this->put($name, $file, FTP_BINARY)) {
242
            $error   = error_get_last();
243
            $message = $error['message'];
244
            throw new RuntimeException(sprintf('error uploading file: %s - %s', $file, $message));
245
        }
246
    }
247
248
    /**
249
     * Setup local member variables by parsing the ftp url.
250
     *
251
     * @param string $url
252
     */
253 1
    private function setup(string $url)
254
    {
255 1
        $parts          = \parse_url($url);
256 1
        $this->host     = $parts['host'] ?? '';
257 1
        $this->port     = $parts['port'] ?? 21;
258 1
        $this->user     = $parts['user'] ?? '';
259 1
        $this->password = $parts['pass'] ?? '';
260 1
    }
261
262
    /**
263
     * Setup ftp connection
264
     *
265
     * @throws \RuntimeException
266
     */
267 1
    private function login()
268
    {
269 1
        if (empty($this->host)) {
270
            throw new RuntimeException('no host to connect to');
271
        }
272
273 1
        $old  = error_reporting(0);
274 1
        $link = $this->isSecure
275
              ? ftp_ssl_connect($this->host, $this->port)
276 1
              : ftp_connect($this->host, $this->port);
277
278 1
        if (!$link) {
279
            error_reporting($old);
280
            throw new RuntimeException(sprintf('unable to connect to ftp server %s', $this->host));
281
        }
282
283 1
        $this->connection = $link;
284 1
        if (!ftp_login($this->connection, $this->user, $this->password)) {
285
            error_reporting($old);
286
            throw new RuntimeException(
287
                sprintf('authentication failed for %s@%s', $this->user, $this->host)
288
            );
289
        }
290
        // set passive mode if needed
291 1
        $this->pasv($this->passive);
292 1
        error_reporting($old);
293 1
    }
294
295
    /**
296
     * Return list of remote directories to travers.
297
     *
298
     * @param  string $path
299
     * @return array
300
     */
301
    private function extractDirectories(string $path) : array
302
    {
303
        $remoteDirs = [];
304
        if (!empty($path)) {
305
            $remoteDirs = explode('/', $path);
306
            // fix empty first array element for absolute path
307
            if (substr($path, 0, 1) === '/') {
308
                $remoteDirs[0] = '/';
309
            }
310
            $remoteDirs = array_filter($remoteDirs);
311
        }
312
        return $remoteDirs;
313
    }
314
315
    /**
316
     * Handle all ftp_* functions.
317
     *
318
     * @param  string $name
319
     * @param  array  $args
320
     * @return mixed
321
     */
322 1
    public function __call($name, $args)
323
    {
324 1
        $function = 'ftp_' . strtolower($name);
325 1
        if (!function_exists($function)) {
326
            throw new RuntimeException(sprintf('invalid method call: %s', $function));
327
        }
328 1
        $old = error_reporting(0);
329 1
        array_unshift($args, $this->connection);
330 1
        if (!$result = call_user_func_array($function, $args)) {
331 1
            $error = error_get_last();
332 1
            error_reporting($old);
333 1
            throw new RuntimeException($error['message']);
334
        }
335 1
        error_reporting($old);
336 1
        return $result;
337
    }
338
}
339