Passed
Push — master ( ccc9fa...bee35f )
by Gabor
03:14
created

ServiceAdapter::getOctalChmod()   B

Complexity

Conditions 2
Paths 2

Size

Total Lines 37
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 37
ccs 0
cts 32
cp 0
rs 8.8571
c 0
b 0
f 0
cc 2
eloc 25
nc 2
nop 1
crap 6

1 Method

Rating   Name   Duplication   Size   Complexity  
B ServiceAdapter::upload() 0 25 3
1
<?php
2
/**
3
 * WebHemi.
4
 *
5
 * PHP version 7.1
6
 *
7
 * @copyright 2012 - 2017 Gixx-web (http://www.gixx-web.com)
8
 * @license   https://opensource.org/licenses/MIT The MIT License (MIT)
9
 *
10
 * @link      http://www.gixx-web.com
11
 */
12
declare(strict_types = 1);
13
14
namespace WebHemi\Ftp\ServiceAdapter\Base;
15
16
use RuntimeException;
17
use WebHemi\Ftp\ServiceInterface;
18
use WebHemi\Ftp\ServiceAdapter\AbstractServiceAdapter;
19
20
/**
21
 * Class ServiceAdapter.
22
 *
23
 * @codeCoverageIgnore - don't test third party library.
24
 */
25
class ServiceAdapter extends AbstractServiceAdapter
26
{
27
    /** @var resource */
28
    private $connectionId = null;
29
30
    /**
31
     * Disconnected by garbage collection.
32
     */
33
    public function __destruct()
34
    {
35
        $this->disconnect();
36
    }
37
38
    /**
39
     * Connect and login to remote host.
40
     *
41
     * @return ServiceInterface
42
     */
43
    public function connect() : ServiceInterface
44
    {
45
        if ($this->getOption('secure', true)) {
46
            $this->connectionId = @ftp_ssl_connect($this->getOption('host'));
47
        } else {
48
            $this->connectionId = @ftp_connect($this->getOption('host'));
49
        }
50
51
        if (!$this->connectionId) {
52
            throw new RuntimeException(
53
                sprintf('Cannot establish connection to server: %s', $this->getOption('host')),
54
                1000
55
            );
56
        }
57
58
        $loginResult = @ftp_login($this->connectionId, $this->getOption('username'), $this->getOption('password'));
59
60
        if (!$loginResult) {
61
            throw new RuntimeException('Cannot connect to remote host: invalid credentials', 1001);
62
        }
63
64
        return $this;
65
    }
66
67
    /**
68
     * Disconnect from remote host.
69
     *
70
     * @return ServiceInterface
71
     */
72
    public function disconnect() : ServiceInterface
73
    {
74
        if (!empty($this->connectionId)) {
75
            ftp_close($this->connectionId);
76
            $this->connectionId = null;
77
        }
78
    }
79
80
    /**
81
     * Toggles connection security level.
82
     *
83
     * @param bool $state
84
     * @return ServiceInterface
85
     */
86
    public function setSecureConnection(bool $state) : ServiceInterface
87
    {
88
        $this->setOption('secure', (bool) $state);
89
90
        if (!empty($this->connectionId)) {
91
            ftp_close($this->connectionId);
92
            $this->connectionId = null;
93
            $this->connect();
94
        }
95
96
        return $this;
97
    }
98
99
    /**
100
     * Toggles connection passive mode.
101
     *
102
     * @param bool $state
103
     * @return ServiceInterface
104
     */
105
    public function setPassiveMode(bool $state) : ServiceInterface
106
    {
107
        ftp_pasv($this->connectionId, (bool) $state);
108
        return $this;
109
    }
110
111
    /**
112
     * Sets remote path.
113
     *
114
     * @param string $path
115
     * @return ServiceInterface
116
     */
117
    public function setRemotePath(string $path) : ServiceInterface
118
    {
119
        if (trim($this->getRemotePath(), '/') == trim($path, '/')) {
120
            return $this;
121
        }
122
123
        if (strpos($path, '/') !== 0) {
124
            $path = $this->getRemotePath().$path;
125
        }
126
127
        $chdirResult = @ftp_chdir($this->connectionId, $path);
128
129
        if (!$chdirResult) {
130
            throw new RuntimeException(sprintf('No such directory on remote host: %s', $path), 1002);
131
        }
132
133
        return $this;
134
    }
135
136
    /**
137
     * Gets remote path.
138
     *
139
     * @return string
140
     */
141
    public function getRemotePath() : string
142
    {
143
        return ftp_pwd($this->connectionId).'/';
144
    }
145
146
    /**
147
     * Lists remote path.
148
     *
149
     * @param null|string $path
150
     * @param bool|null $changeToDirectory
151
     * @return array
152
     */
153
    public function getRemoteFileList(? string $path, ? bool $changeToDirectory) : array
154
    {
155
        $fileList = [];
156
157
        if (!empty($path) && $changeToDirectory) {
158
            $this->setRemotePath($path);
159
            $path = '.';
160
        }
161
162
        $result = @ftp_rawlist($this->connectionId, $path);
163
164
        if (!is_array($result)) {
165
            throw new RuntimeException('Cannot retrieve file list', 1006);
166
        }
167
168
        foreach ($result as $fileRawData) {
169
            $fileData = [];
170
171
            preg_match(
172
                '/^(?P<rights>(?P<type>(-|d))[^\s]+)\s+(?P<symlinks>\d+)\s+(?P<user>[^\s]+)\s+(?P<group>[^\s]+)\s+'
173
                    .'(?P<size>\d+)\s+(?P<date>(?P<month>[^\s]+)\s+(?P<day>[^\s]+)\s+'
174
                    .'(?P<time>[^\s]+))\s+(?P<filename>.+)$/',
175
                $fileRawData,
176
                $fileData
177
            );
178
            $fileInfo = pathinfo($fileData['filename']);
179
180
            $fileList[] = [
181
                'type' => $this->getFileType($fileData['type']),
182
                'chmod' => $this->getOctalChmod($fileData['rights']),
183
                'symlinks' => $fileData['symlinks'],
184
                'user' => $fileData['user'],
185
                'group' => $fileData['group'],
186
                'size' => $fileData['size'],
187
                'date' => $this->getFileDate($fileData),
188
                'basename' => $fileInfo['basename'],
189
                'filename' => $fileInfo['filename'],
190
                'extension' => $fileInfo['extension'] ?? '',
191
            ];
192
        }
193
194
        return $fileList;
195
    }
196
197
    /**
198
     * @param string $fileData
199
     * @return string
200
     */
201
    private function getFileType(string $fileData) : string
202
    {
203
        switch ($fileData) {
204
            case 'd':
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
205
                $fileType = 'directory';
206
                break;
207
208
            case 'l':
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
209
                $fileType = 'symlink';
210
                break;
211
212
            default:
213
                $fileType = 'file';
214
        }
215
216
        return $fileType;
217
    }
218
219
    /**
220
     * @param array $fileData
221
     * @return string
222
     */
223
    private function getFileDate(array $fileData) : string
224
    {
225
        if (strpos($fileData['time'], ':') !== false) {
226
            $date = $fileData['month'].' '.$fileData['day'].' '.date('Y').' '.$fileData['time'];
227
        } else {
228
            $date = $fileData['date'].' 12:00:00';
229
        }
230
231
        $time = strtotime($date);
232
233
        return date('Y-m-d H:i:s', $time);
234
    }
235
236
    /**
237
     * Uploads file to remote host.
238
     *
239
     * @see self::setRemotePath
240
     * @see self::setLocalPath
241
     *
242
     * @param string $sourceFileName
243
     * @param string $destinationFileName
244
     * @param int $fileMode
245
     * @return mixed
246
     */
247
    public function upload(
248
        string $sourceFileName,
249
        string $destinationFileName,
250
        int $fileMode = self::FILE_MODE_BINARY
251
    ) : ServiceInterface {
252
        $this->checkLocalFile($sourceFileName);
253
        $this->checkRemoteFile($destinationFileName);
254
255
        if (!file_exists($this->localPath.'/'.$sourceFileName)) {
256
            throw new RuntimeException(sprintf('File not found: %s', $this->localPath.'/'.$sourceFileName), 1007);
257
        }
258
259
        $uploadResult = @ftp_put(
260
            $this->connectionId,
261
            $destinationFileName,
262
            $this->localPath.'/'.$sourceFileName,
263
            $fileMode
264
        );
265
266
        if (!$uploadResult) {
267
            throw new RuntimeException(sprintf('There was a problem while uploading file: %s', $sourceFileName), 1008);
268
        }
269
270
        return $this;
271
    }
272
273
    /**
274
     * Downloads file from remote host.
275
     *
276
     * @see self::setRemotePath
277
     * @see self::setLocalPath
278
     *
279
     * @param string $remoteFileName
280
     * @param string $localFileName
281
     * @param int $fileMode
282
     * @return mixed
283
     */
284
    public function download(
285
        string $remoteFileName,
286
        string&$localFileName,
287
        int $fileMode = self::FILE_MODE_BINARY
288
    ) : ServiceInterface {
289
        $this->checkRemoteFile($remoteFileName);
290
        $this->checkLocalFile($localFileName, true);
291
292
        $downloadResult = @ftp_get(
293
            $this->connectionId,
294
            $this->localPath.'/'.$localFileName,
295
            $remoteFileName,
296
            $fileMode
297
        );
298
299
        if (!$downloadResult) {
300
            throw new RuntimeException(
301
                sprintf('There was a problem while downloading file: %s', $remoteFileName),
302
                1010
303
            );
304
        }
305
306
        return $this;
307
    }
308
309
    /**
310
     * Check remote file name.
311
     *
312
     * @param string $remoteFileName
313
     */
314
    protected function checkRemoteFile(string&$remoteFileName) : void
315
    {
316
        $pathInfo = pathinfo($remoteFileName);
317
318
        if ($pathInfo['dirname'] != '.') {
319
            $this->setRemotePath($pathInfo['dirname']);
320
            $remoteFileName = $pathInfo['basename'];
321
        }
322
    }
323
324
    /**
325
     * Moves file on remote host.
326
     *
327
     * @param string $currentPath
328
     * @param string $newPath
329
     * @return ServiceInterface
330
     */
331
    public function moveRemoteFile(string $currentPath, string $newPath) : ServiceInterface
332
    {
333
        $result = @ftp_rename($this->connectionId, $currentPath, $newPath);
334
335
        if (!$result) {
336
            throw new RuntimeException(
337
                sprintf('Unable to move/rename file from %s to %s', $currentPath, $newPath),
338
                1011
339
            );
340
        }
341
342
        return $this;
343
    }
344
345
    /**
346
     * Deletes file on remote host.
347
     *
348
     * @param string $path
349
     * @return ServiceInterface
350
     */
351
    public function deleteRemoteFile(string $path) : ServiceInterface
352
    {
353
        $result = @ftp_delete($this->connectionId, $path);
354
355
        if (!$result) {
356
            throw new RuntimeException(sprintf('Unable to delete file on remote host: %s', $path), 1012);
357
        }
358
359
        return $this;
360
    }
361
}
362