Passed
Push — master ( 368aec...e09a05 )
by Sebastian
01:50
created

Client::isDir()   A

Complexity

Conditions 2
Paths 3

Size

Total Lines 11
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 7
nc 3
nop 1
dl 0
loc 11
rs 9.4285
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 Ftp
18
 * @author  Sebastian Feldmann <[email protected]>
19
 * @link    https://github.com/sebastianfeldmann/ftp
20
 * @since   Class available since Release 1.0.0
21
 */
22
class Client
23
{
24
    /**
25
     * PHP FTP connection resource.
26
     *
27
     * @var resource
28
     */
29
    private $connection;
30
31
    /**
32
     * Host to connect to.
33
     *
34
     * @var string
35
     */
36
    private $host;
37
38
    /**
39
     * Port to connect to.
40
     *
41
     * @var int
42
     */
43
    private $port;
44
45
    /**
46
     * User to login.
47
     *
48
     * @var string
49
     */
50
    private $user;
51
52
    /**
53
     * Password to login.
54
     *
55
     * @var string
56
     */
57
    private $password;
58
59
    /**
60
     * Use passive ftp mode
61
     *
62
     * @var bool
63
     */
64
    private $passive;
65
66
    /**
67
     * Client constructor.
68
     *
69
     * @param string $url
70
     * @param bool   $passive
71
     */
72
    public function __construct(string $url, bool $passive = false)
73
    {
74
        if (!extension_loaded('ftp')) {
75
            throw new RuntimeException('FTP extension is not loaded.');
76
        }
77
        $this->passive = $passive;
78
        $this->setup($url);
79
        $this->login();
80
    }
81
82
    /**
83
     * Determine if file is a directory.
84
     *
85
     * @param  string $name
86
     * @return bool
87
     */
88
    public function isDir(string $name)
89
    {
90
        $current = $this->pwd();
0 ignored issues
show
Bug introduced by
The method pwd() does not exist on SebastianFeldmann\Ftp\Client. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

90
        /** @scrutinizer ignore-call */ 
91
        $current = $this->pwd();
Loading history...
91
        try {
92
            $this->chDir($name);
0 ignored issues
show
Bug introduced by
The method chDir() does not exist on SebastianFeldmann\Ftp\Client. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

92
            $this->/** @scrutinizer ignore-call */ 
93
                   chDir($name);
Loading history...
93
            $this->chDir($current);
94
            return true;
95
        } catch (\Exception $e) {
96
            // do nothing
97
        }
98
        return false;
99
    }
100
101
    /**
102
     * Returns to the home directory.
103
     *
104
     * @return void
105
     */
106
    public function chHome()
107
    {
108
        $this->chDir('');
109
    }
110
111
    /**
112
     * Return list of all files in directory.
113
     *
114
     * @param  string $path
115
     * @return \SebastianFeldmann\Ftp\File[]
116
     * @throws \Exception
117
     */
118
    public function ls(string $path = '') : array
119
    {
120
        $list = [];
121
        foreach ($this->nlist($path) as $name) {
0 ignored issues
show
Bug introduced by
The method nlist() does not exist on SebastianFeldmann\Ftp\Client. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

121
        foreach ($this->/** @scrutinizer ignore-call */ nlist($path) as $name) {
Loading history...
122
            $type   = $this->isDir($name);
123
            $mtime  = $this->mdtm($name);
0 ignored issues
show
Bug introduced by
The method mdtm() does not exist on SebastianFeldmann\Ftp\Client. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

123
            /** @scrutinizer ignore-call */ 
124
            $mtime  = $this->mdtm($name);
Loading history...
124
            $size   = $this->size($name);
0 ignored issues
show
Bug introduced by
The method size() does not exist on SebastianFeldmann\Ftp\Client. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

124
            /** @scrutinizer ignore-call */ 
125
            $size   = $this->size($name);
Loading history...
125
            $list[] = new File(['name' => $name, 'modify' => $mtime, 'type' => $type, 'size' => $size]);
126
        }
127
        return $list;
128
    }
129
130
    /**
131
     * Return list of directories in given path.
132
     *
133
     * @param  string $path
134
     * @return array
135
     * @throws \Exception
136
     */
137
    public function lsDirs(string $path = '') : array
138
    {
139
        return array_filter(
140
            $this->ls($path),
141
            function(File $file) {
142
                return $file->isDir();
143
            }
144
        );
145
    }
146
147
    /**
148
     * Return list of files in given path.
149
     *
150
     * @param  string $path
151
     * @return array
152
     * @throws \Exception
153
     */
154
    public function lsFiles(string $path = '') : array
155
    {
156
        return array_filter(
157
            $this->ls($path),
158
            function(File $file) {
159
                return $file->isFile();
160
            }
161
        );
162
    }
163
164
    /**
165
     * Upload local file to ftp server.
166
     *
167
     * @param  string $file Path to local file that should be uploaded.
168
     * @param  string $path Path to store the file under.
169
     * @param  string $name Filename on the ftp server.
170
     * @return void
171
     */
172
    public function uploadFile(string $file, string $path, string $name)
173
    {
174
        // to store the file we have to make sure the directory exists
175
        foreach ($this->extractDirectories($path) as $dir) {
176
            // if change to directory fails
177
            // create the dir and change into it afterwards
178
            if (!$this->chDir($dir)) {
179
                $this->mkDir($dir);
0 ignored issues
show
Bug introduced by
The method mkDir() does not exist on SebastianFeldmann\Ftp\Client. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

179
                $this->/** @scrutinizer ignore-call */ 
180
                       mkDir($dir);
Loading history...
180
                $this->chDir($dir);
181
            }
182
        }
183
        if (!$this->put($name, $file, FTP_BINARY)) {
0 ignored issues
show
Bug introduced by
The method put() does not exist on SebastianFeldmann\Ftp\Client. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

183
        if (!$this->/** @scrutinizer ignore-call */ put($name, $file, FTP_BINARY)) {
Loading history...
184
            $error   = error_get_last();
185
            $message = $error['message'];
186
            throw new RuntimeException(sprintf('error uploading file: %s - %s', $file, $message));
187
        }
188
    }
189
190
    /**
191
     * Setup local member variables by parsing the ftp url.
192
     *
193
     * @param string $url
194
     */
195
    private function setup(string $url)
196
    {
197
        $parts          = \parse_url($url);
198
        $this->host     = $parts['host'] ?? '';
199
        $this->port     = $parts['port'] ?? 21;
200
        $this->user     = $parts['user'] ?? '';
201
        $this->password = $parts['pass'] ?? '';
202
    }
203
204
    /**
205
     * Setup ftp connection
206
     *
207
     * @throws \RuntimeException
208
     */
209
    private function login()
210
    {
211
        if (empty($this->host)) {
212
            throw new RuntimeException('no host to connect to');
213
        }
214
215
        $old = error_reporting(0);
216
        if (!$this->connection = \ftp_connect($this->host, $this->port)) {
217
            error_reporting($old);
218
            throw new RuntimeException(sprintf('unable to connect to ftp server %s', $this->host));
219
        }
220
221
        if (!\ftp_login($this->connection, $this->user, $this->password)) {
222
            error_reporting($old);
223
            throw new RuntimeException(
224
                sprintf('authentication failed for %s@%s', $this->user, $this->host)
225
            );
226
        }
227
        // set passive mode if needed
228
        $this->pasv($this->passive);
0 ignored issues
show
Bug introduced by
The method pasv() does not exist on SebastianFeldmann\Ftp\Client. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

228
        $this->/** @scrutinizer ignore-call */ 
229
               pasv($this->passive);
Loading history...
229
        error_reporting($old);
230
    }
231
232
    /**
233
     * Return list of remote directories to travers.
234
     *
235
     * @param  string $path
236
     * @return array
237
     */
238
    private function extractDirectories(string $path) : array
239
    {
240
        $remoteDirs = [];
241
        if (!empty($path)) {
242
            $remoteDirs = explode('/', $path);
243
            // fix empty first array element for absolute path
244
            if (substr($path, 0, 1) === '/') {
245
                $remoteDirs[0] = '/';
246
            }
247
            $remoteDirs = array_filter($remoteDirs);
248
        }
249
        return $remoteDirs;
250
    }
251
252
    /**
253
     * Handle all ftp_* functions.
254
     *
255
     * @param  string $name
256
     * @param  array  $args
257
     * @return mixed
258
     */
259
    public function __call($name, $args)
260
    {
261
        $function = 'ftp_' . strtolower($name);
262
        if (!function_exists($function)) {
263
            throw new RuntimeException(sprintf('invalid method call: %s', $function));
264
        }
265
        $old = error_reporting(0);
266
        array_unshift($args, $this->connection);
267
        if (!$result = call_user_func_array($function, $args)) {
268
            $error = error_get_last();
269
            error_reporting($old);
270
            throw new RuntimeException($error['message']);
271
        }
272
        error_reporting($old);
273
        return $result;
274
    }
275
}
276