GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

Ftp::download()   A
last analyzed

Complexity

Conditions 6
Paths 13

Size

Total Lines 28
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 13
nc 13
nop 3
dl 0
loc 28
rs 9.2222
c 0
b 0
f 0
1
<?php
2
/**
3
 * This file is part of the O2System Framework package.
4
 *
5
 * For the full copyright and license information, please view the LICENSE
6
 * file that was distributed with this source code.
7
 *
8
 * @author         Steeve Andrian Salim
9
 * @copyright      Copyright (c) Steeve Andrian Salim
10
 */
11
12
// ------------------------------------------------------------------------
13
14
namespace O2System\Filesystem\Handlers;
15
16
// ------------------------------------------------------------------------
17
18
use O2System\Spl\Exceptions\RuntimeException;
19
use O2System\Spl\Traits\Collectors\ConfigCollectorTrait;
20
use O2System\Spl\Traits\Collectors\ErrorCollectorTrait;
21
22
/**
23
 * Class Ftp
24
 *
25
 * @package O2System\Filesystem\Handlers
26
 */
27
class Ftp
28
{
29
    use ConfigCollectorTrait;
30
    use ErrorCollectorTrait;
31
32
    /**
33
     * Passive mode flag
34
     *
35
     * @var    bool
36
     */
37
    public $passiveMode = true;
38
39
    /**
40
     * Debug flag
41
     *
42
     * Specifies whether to display error messages.
43
     *
44
     * @var    bool
45
     */
46
    public $debugMode = false;
47
48
    /**
49
     * Ftp::$config
50
     *
51
     * Ftp configuration.
52
     *
53
     * @var array
54
     */
55
    protected $config;
56
57
    // --------------------------------------------------------------------
58
    /**
59
     * Connection ID
60
     *
61
     * @var    resource
62
     */
63
    protected $handle;
64
65
    // --------------------------------------------------------------------
66
67
    /**
68
     * Ftp::__construct
69
     */
70
    public function __construct(array $config = [])
71
    {
72
        $this->setConfig($config);
73
74
        // Prep the port
75
        $this->config[ 'port' ] = empty($this->config[ 'port' ]) ? 21 : (int)$this->config[ 'port' ];
76
77
        // Prep the hostname
78
        $this->config[ 'hostname' ] = preg_replace('|.+?://|', '', $this->config[ 'hostname' ]);
79
80
        language()
81
            ->addFilePath(str_replace('Handlers', '', __DIR__) . DIRECTORY_SEPARATOR)
82
            ->loadFile('ftp');
83
    }
84
85
    // --------------------------------------------------------------------
86
87
    /**
88
     * Ftp::connect
89
     *
90
     * Connect to FTP server.
91
     *
92
     * @return bool Returns TRUE on success or FALSE on failure.
93
     * @throws RuntimeException
94
     */
95
    public function connect()
96
    {
97
        if (false === ($this->handle = @ftp_connect($this->config[ 'hostname' ], $this->config[ 'port' ]))) {
98
            if ($this->debugMode === true) {
99
                throw new RuntimeException('FTP_E_UNABLE_TO_CONNECT');
100
            }
101
102
            $this->addError(1, 'FTP_E_UNABLE_TO_CONNECT');
103
104
            return false;
105
        }
106
107
        if (false !== (@ftp_login($this->handle, $this->config[ 'username' ], $this->config[ 'password' ]))) {
108
            if ($this->debugMode === true) {
109
                throw new RuntimeException('FTP_E_UNABLE_TO_LOGIN');
110
            }
111
112
            $this->addError(2, 'FTP_E_UNABLE_TO_LOGIN');
113
114
            return false;
115
        }
116
117
        // Set passive mode if needed
118
        if ($this->passiveMode === true) {
119
            ftp_pasv($this->handle, true);
120
        }
121
122
        return true;
123
    }
124
125
    // --------------------------------------------------------------------
126
127
    /**
128
     * Ftp::download
129
     *
130
     * Download a file from a remote server to the local server
131
     *
132
     * @param   string $remoteFilePath Remote file path.
133
     * @param   string $localFilePath  Local destination file path.
134
     * @param   string $mode           File transfer mode.
135
     *
136
     * @return  bool Returns TRUE on success or FALSE on failure.
137
     * @throws  RuntimeException
138
     */
139
    public function download($remoteFilePath, $localFilePath, $mode = 'auto')
140
    {
141
        if ( ! $this->isConnected()) {
142
            return false;
143
        }
144
145
        // Set the mode if not specified
146
        if ($mode === 'auto') {
147
            // Get the file extension so we can set the upload type
148
            $ext = $this->getExtension($remoteFilePath);
149
            $mode = $this->getTransferMode($ext);
150
        }
151
152
        $mode = ($mode === 'ascii') ? FTP_ASCII : FTP_BINARY;
153
154
        $result = @ftp_get($this->handle, $localFilePath, $remoteFilePath, $mode);
155
156
        if ($result === false) {
157
            if ($this->debugMode === true) {
158
                throw new RuntimeException('FTP_E_UNABLE_TO_DOWNLOAD');
159
            }
160
161
            $this->addError(3, 'FTP_E_UNABLE_TO_DOWNLOAD');
162
163
            return false;
164
        }
165
166
        return true;
167
    }
168
169
    // --------------------------------------------------------------------
170
171
    /**
172
     * Ftp::isConnected
173
     *
174
     * Validates the connection ID
175
     *
176
     * @return bool Returns TRUE on success or FALSE on failure.
177
     * @throws RuntimeException
178
     */
179
    protected function isConnected()
180
    {
181
        if ( ! is_resource($this->handle)) {
182
            if ($this->debugMode === true) {
183
                throw new RuntimeException('FTP_E_NO_CONNECTION');
184
            }
185
186
            $this->addError(4, 'FTP_E_NO_CONNECTION');
187
188
            return false;
189
        }
190
191
        return true;
192
    }
193
194
    // --------------------------------------------------------------------
195
196
    /**
197
     * Ftp::getExtension
198
     *
199
     * Extract the file extension.
200
     *
201
     * @param   string $filename String of filename to be extracted.
202
     *
203
     * @return  string By default it's set into txt file extension.
204
     */
205
    protected function getExtension($filename)
206
    {
207
        return (($dot = strrpos($filename, '.')) === false)
208
            ? 'txt'
209
            : substr($filename, $dot + 1);
210
    }
211
212
    // --------------------------------------------------------------------
213
214
    /**
215
     * Ftp::getTransferMode
216
     *
217
     * Gets upload transfer mode.
218
     *
219
     * @param   string $ext Filename extension.
220
     *
221
     * @return  string By default it's set into ascii mode.
222
     */
223
    protected function getTransferMode($ext)
224
    {
225
        return in_array(
226
            $ext,
227
            ['txt', 'text', 'php', 'phps', 'php4', 'js', 'css', 'htm', 'html', 'phtml', 'shtml', 'log', 'xml'],
228
            true
229
        )
230
            ? 'ascii'
231
            : 'binary';
232
    }
233
234
    // --------------------------------------------------------------------
235
236
    /**
237
     * Ftp::rename
238
     *
239
     * Rename a file on ftp server.
240
     *
241
     * @param   string $oldFilename Old filename.
242
     * @param   string $newFilename New filename.
243
     *
244
     * @return  bool Returns TRUE on success or FALSE on failure.
245
     * @throws  RuntimeException
246
     */
247
    public function rename($oldFilename, $newFilename)
248
    {
249
        if ( ! $this->isConnected()) {
250
            return false;
251
        }
252
253
        $result = @ftp_rename($this->handle, $oldFilename, $newFilename);
254
255
        if ($result === false) {
256
            if ($this->debugMode === true) {
257
                throw new RuntimeException('FTP_UNABLE_TO_RENAME');
258
            }
259
260
            $this->addError(5, 'FTP_UNABLE_TO_RENAME');
261
262
            return false;
263
        }
264
265
        return true;
266
    }
267
268
    // --------------------------------------------------------------------
269
270
    /**
271
     * Ftp::moveFile
272
     *
273
     * Moves a file on the FTP server.
274
     *
275
     * @param    string $oldRemoteFilePath Old file path on the FTP server.
276
     * @param    string $newRemoteFilePath New file path on the FTP server.
277
     *
278
     * @return  bool Returns TRUE on success or FALSE on failure.
279
     * @throws  RuntimeException
280
     */
281
    public function move($oldRemoteFilePath, $newRemoteFilePath)
282
    {
283
        if ( ! $this->isConnected()) {
284
            return false;
285
        }
286
287
        $result = @ftp_rename($this->handle, $oldRemoteFilePath, $newRemoteFilePath);
288
289
        if ($result === false) {
290
            if ($this->debugMode === true) {
291
                throw new RuntimeException('FTP_UNABLE_TO_MOVE');
292
            }
293
294
            $this->addError(6, 'FTP_UNABLE_TO_MOVE');
295
296
            return false;
297
        }
298
299
        return true;
300
    }
301
302
    // --------------------------------------------------------------------
303
304
    /**
305
     * Ftp::deleteFile
306
     *
307
     * Deletes a file on the FTP server
308
     *
309
     * @param   string $filePath Path to the file to be deleted.
310
     *
311
     * @return  bool Returns TRUE on success or FALSE on failure.
312
     * @throws  RuntimeException
313
     */
314
    public function deleteFile($filePath)
315
    {
316
        if ( ! $this->isConnected()) {
317
            return false;
318
        }
319
320
        $result = @ftp_delete($this->handle, $filePath);
321
322
        if ($result === false) {
323
            if ($this->debugMode === true) {
324
                throw new RuntimeException('FTP_E_UNABLE_TO_DELETE');
325
            }
326
327
            $this->addError(7, 'FTP_E_UNABLE_TO_DELETE');
328
329
            return false;
330
        }
331
332
        return true;
333
    }
334
335
    // --------------------------------------------------------------------
336
337
    /**
338
     * Ftp::deleteDir
339
     *
340
     * Delete a folder and recursively delete everything (including sub-folders)
341
     * contained within it on the FTP server.
342
     *
343
     * @param   string $remotePath Path to the directory to be deleted on the FTP server.
344
     *
345
     * @return  bool Returns TRUE on success or FALSE on failure.
346
     * @throws  RuntimeException
347
     */
348
    public function deleteDir($remotePath)
349
    {
350
        if ( ! $this->isConnected()) {
351
            return false;
352
        }
353
354
        // Add a trailing slash to the file path if needed
355
        $remotePath = preg_replace('/(.+?)\/*$/', '\\1/', $remotePath);
356
357
        $list = $this->getFiles($remotePath);
358
        if ( ! empty($list)) {
359
            for ($i = 0, $c = count($list); $i < $c; $i++) {
360
                // If we can't delete the item it's probaly a directory,
361
                // so we'll recursively call delete_dir()
362
                if ( ! preg_match('#/\.\.?$#', $list[ $i ]) && ! @ftp_delete($this->handle, $list[ $i ])) {
363
                    $this->deleteDir($list[ $i ]);
364
                }
365
            }
366
        }
367
368
        if (@ftp_rmdir($this->handle, $remotePath) === false) {
369
            if ($this->debugMode === true) {
370
                throw new RuntimeException('FTP_E_UNABLE_TO_DELETE_DIRECTORY');
371
            }
372
373
            $this->addError(8, 'FTP_E_UNABLE_TO_DELETE_DIRECTORY');
374
375
            return false;
376
        }
377
378
        return true;
379
    }
380
381
    // --------------------------------------------------------------------
382
383
    /**
384
     * Ftp::getFiles
385
     *
386
     * FTP List files in the specified directory.
387
     *
388
     * @param    string $remotePath Path to the remote directory.
389
     *
390
     * @return  array|bool Returns array of files list or FALSE on failure.
391
     * @throws  RuntimeException
392
     */
393
    public function getFiles($remotePath = '.')
394
    {
395
        return $this->isConnected()
396
            ? ftp_nlist($this->handle, $remotePath)
397
            : false;
398
    }
399
400
    // --------------------------------------------------------------------
401
402
    /**
403
     * Ftp::mirror
404
     *
405
     * Read a directory and recreate it remotely.
406
     *
407
     * This function recursively reads a folder and everything it contains
408
     * (including sub-folders) and creates a mirror via FTP based on it.
409
     * Whatever the directory structure of the original file path will be
410
     * recreated on the server.
411
     *
412
     * @param    string $localPath  Path to source with trailing slash
413
     * @param    string $remotePath Path to destination - include the base folder with trailing slash
414
     *
415
     * @return  bool Returns TRUE on success or FALSE on failure.
416
     * @throws  RuntimeException
417
     */
418
    public function mirror($localPath, $remotePath)
419
    {
420
        if ( ! $this->isConnected()) {
421
            return false;
422
        }
423
424
        // Open the local file path
425
        if ($fp = @opendir($localPath)) {
426
            // Attempt to open the remote file path and try to create it, if it doesn't exist
427
            if ( ! $this->changeDir($remotePath, true) && ( ! $this->makeDir($remotePath) OR ! $this->changeDir(
428
                        $remotePath
429
                    ))
430
            ) {
431
                return false;
432
            }
433
434
            // Recursively read the local directory
435
            while (false !== ($file = readdir($fp))) {
436
                if (is_dir($localPath . $file) && $file[ 0 ] !== '.') {
437
                    $this->mirror($localPath . $file . '/', $remotePath . $file . '/');
438
                } elseif ($file[ 0 ] !== '.') {
439
                    // Get the file extension so we can se the upload type
440
                    $ext = $this->getExtension($file);
441
                    $mode = $this->getTransferMode($ext);
442
443
                    $this->upload($localPath . $file, $remotePath . $file, $mode);
444
                }
445
            }
446
447
            return true;
448
        }
449
450
        return false;
451
    }
452
453
    // --------------------------------------------------------------------
454
455
    /**
456
     * Ftp::changeDir
457
     *
458
     * The second parameter lets us momentarily turn off debugging so that
459
     * this function can be used to test for the existence of a folder
460
     * without throwing an error. There's no FTP equivalent to is_dir()
461
     * so we do it by trying to change to a particular directory.
462
     * Internally, this parameter is only used by the "mirror" function below.
463
     *
464
     * @param   string $remotePath    The remote directory path.
465
     * @param   bool   $suppressDebug Suppress debug mode.
466
     *
467
     * @return  bool  Returns TRUE on success or FALSE on failure.
468
     * @throws  RuntimeException
469
     */
470
    public function changeDir($remotePath, $suppressDebug = false)
471
    {
472
        if ( ! $this->isConnected()) {
473
            return false;
474
        }
475
476
        $result = @ftp_chdir($this->handle, $remotePath);
477
478
        if ($result === false) {
479
            if ($this->debugMode === true AND $suppressDebug === false) {
480
                throw new RuntimeException('FTP_E_UNABLE_TO_CHANGE_DIRECTORY');
481
            }
482
483
            $this->addError(9, 'FTP_E_UNABLE_TO_CHANGE_DIRECTORY');
484
485
            return false;
486
        }
487
488
        return true;
489
    }
490
491
    // ------------------------------------------------------------------------
492
493
    /**
494
     * Ftp::makeDir
495
     *
496
     * Create a remote directory on the ftp server.
497
     *
498
     * @param   string $remotePath  The remote directory that will be created on ftp server.
499
     * @param   int    $permissions The remote directory permissions.
500
     *
501
     * @return  bool Returns TRUE on success or FALSE on failure.
502
     * @throws  RuntimeException
503
     */
504
    public function makeDir($remotePath, $permissions = null)
505
    {
506
        if ($remotePath === '' OR ! $this->isConnected()) {
507
            return false;
508
        }
509
510
        $result = @ftp_mkdir($this->handle, $remotePath);
511
512
        if ($result === false) {
513
            if ($this->debugMode === true) {
514
                throw new RuntimeException('FTP_E_UNABLE_TO_MAKE_DIRECTORY');
515
            }
516
517
            $this->addError(10, 'FTP_E_UNABLE_TO_MAKE_DIRECTORY');
518
519
            return false;
520
        }
521
522
        // Set file permissions if needed
523
        if ($permissions !== null) {
524
            $this->setChmod($remotePath, (int)$permissions);
525
        }
526
527
        return true;
528
    }
529
530
    // --------------------------------------------------------------------
531
532
    /**
533
     * Ftp::setChmod
534
     *
535
     * Set remote file permissions.
536
     *
537
     * @param   string $remotePath Path to the remote directory or file to be changed.
538
     * @param   int    $mode       Remote directory permissions mode.
539
     *
540
     * @return  bool Returns TRUE on success or FALSE on failure.
541
     * @throws  RuntimeException
542
     */
543
    public function setChmod($remotePath, $mode)
544
    {
545
        if ( ! $this->isConnected()) {
546
            return false;
547
        }
548
549
        if (@ftp_chmod($this->handle, $mode, $remotePath) === false) {
550
            if ($this->debugMode === true) {
551
                throw new RuntimeException('FTP_E_UNABLE_TO_CHMOD');
552
            }
553
554
            $this->addError(11, 'FTP_E_UNABLE_TO_CHMOD');
555
556
            return false;
557
        }
558
559
        return true;
560
    }
561
562
    // --------------------------------------------------------------------
563
564
    /**
565
     * Ftp::upload
566
     *
567
     * Uploader a file to the ftp server.
568
     *
569
     * @param    string $localFilePath  Local source file path.
570
     * @param    string $remoteFilePath Remote destination file path.
571
     * @param    string $mode           File transfer mode.
572
     * @param    int    $permissions    Remote file permissions.
573
     *
574
     * @return  bool Returns TRUE on success or FALSE on failure.
575
     * @throws  RuntimeException
576
     */
577
    public function upload($localFilePath, $remoteFilePath, $mode = 'auto', $permissions = null)
578
    {
579
        if ( ! $this->isConnected()) {
580
            return false;
581
        }
582
583
        if (is_file($localFilePath)) {
584
            // Set the mode if not specified
585
            if ($mode === 'auto') {
586
                // Get the file extension so we can set the upload type
587
                $ext = $this->getExtension($localFilePath);
588
                $mode = $this->getTransferMode($ext);
589
            }
590
591
            $mode = ($mode === 'ascii') ? FTP_ASCII : FTP_BINARY;
592
593
            $result = @ftp_put($this->handle, $remoteFilePath, $localFilePath, $mode);
594
595
            if ($result === false) {
596
                if ($this->debugMode === true) {
597
                    throw new RuntimeException('FTP_E_UNABLE_TO_UPLOAD');
598
                }
599
600
                $this->addError(12, 'FTP_E_UNABLE_TO_UPLOAD');
601
602
                return false;
603
            }
604
605
            // Set file permissions if needed
606
            if ($permissions !== null) {
607
                $this->setChmod($remoteFilePath, (int)$permissions);
608
            }
609
610
            return true;
611
        }
612
613
        return false;
614
    }
615
616
    // ------------------------------------------------------------------------
617
618
    /**
619
     * Ftp::close
620
     *
621
     * Close the current ftp connection.
622
     *
623
     * @return  bool    Returns TRUE on success or FALSE on failure.
624
     * @throws  RuntimeException
625
     */
626
    public function close()
627
    {
628
        return $this->isConnected()
629
            ? @ftp_close($this->handle)
630
            : false;
631
    }
632
}