Completed
Pull Request — master (#145)
by Vitaly
10:03
created

Sftp   A

Complexity

Total Complexity 19

Size/Duplication

Total Lines 161
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 9

Test Coverage

Coverage 65.31%

Importance

Changes 0
Metric Value
wmc 19
lcom 1
cbo 9
dl 0
loc 161
ccs 32
cts 49
cp 0.6531
rs 10
c 0
b 0
f 0

7 Methods

Rating   Name   Duplication   Size   Complexity  
B setup() 0 21 5
A checkRequirements() 0 6 2
A getProtocolName() 0 4 1
B sync() 0 25 4
B login() 0 30 5
A getRemoteDirectoryList() 0 4 1
A createCollector() 0 4 1
1
<?php
2
namespace phpbu\App\Backup\Sync;
3
4
use phpbu\App\Backup\Collector;
5
use phpbu\App\Backup\Target;
6
use phpbu\App\Configuration;
7
use phpbu\App\Result;
8
use phpbu\App\Util;
9
use phpseclib;
10
11
/**
12
 * Sftp sync
13
 *
14
 * @package    phpbu
15
 * @subpackage Backup
16
 * @author     Sebastian Feldmann <[email protected]>
17
 * @copyright  Sebastian Feldmann <[email protected]>
18
 * @license    https://opensource.org/licenses/MIT The MIT License (MIT)
19
 * @link       http://phpbu.de/
20
 * @since      Class available since Release 1.0.0
21
 */
22
class Sftp extends Xtp
23
{
24
    /**
25
     * @var phpseclib\Net\SFTP
26
     */
27
    protected $sftp;
28
29
    /**
30
     * @var string
31
     */
32
    protected $privateKey;
33
34
    /**
35
     * @var string
36 7
     */
37
    protected $privateKeyPassword;
38 7
39
    /**
40 3
     * (non-PHPDoc)
41 3
     *
42
     * @see    \phpbu\App\Backup\Sync::setup()
43
     * @param  array $config
44
     * @throws \phpbu\App\Backup\Sync\Exception
45
     * @throws \phpbu\App\Exception
46
     */
47
    public function setup(array $config)
48 7
    {
49
        if (!Util\Arr::isSetAndNotEmptyString($config, 'password') && !Util\Arr::isSetAndNotEmptyString($config, 'private_key')) {
50 7
            throw new Exception('\'password\' or \'private_key\' must be presented');
51
        }
52
        parent::setup($config);
53 7
54
        $this->time = time();
0 ignored issues
show
Bug introduced by
The property time does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
55
        $privateKey = Util\Arr::getValue($config, 'private_key', '');
56
        if (!empty($privateKey)) {
57
            // get absolute private key path
58
            $privateKey = realpath(Util\Path::toAbsolutePath($privateKey, Configuration::getWorkingDirectory()));
59
            if ($privateKey === false) {
60 1
                throw new \phpbu\App\Exception("Private key not found at specified path");
61
            }
62 1
        }
63
        $this->privateKey         = $privateKey;
64
        $this->privateKeyPassword = Util\Arr::getValue($config, 'private_key_password', '');
65
66
        $this->setUpClearable($config);
67
    }
68
69
    /**
70
     * Check for required loaded libraries or extensions.
71
     *
72
     * @throws \phpbu\App\Backup\Sync\Exception
73 1
     */
74
    protected function checkRequirements()
75 1
    {
76 1
        if (!class_exists('\\phpseclib\\Net\\SFTP')) {
77 1
            throw new Exception('phpseclib not installed - use composer to install "phpseclib/phpseclib" version 2.x');
78
        }
79 1
    }
80 1
81 1
    /**
82 1
     * Return implemented (*)TP protocol name.
83
     *
84 1
     * @return string
85 1
     */
86
    protected function getProtocolName()
87
    {
88 1
        return 'SFTP';
89 1
    }
90
91 1
    /**
92
     * (non-PHPDoc)
93
     *
94
     * @see    \phpbu\App\Backup\Sync::sync()
95
     * @param  \phpbu\App\Backup\Target $target
96 1
     * @param  \phpbu\App\Result        $result
97 1
     * @throws \phpbu\App\Backup\Sync\Exception
98
     */
99
    public function sync(Target $target, Result $result)
100
    {
101
        $this->sftp     = $this->login();
102
        $remoteFilename = $target->getFilename();
103
        $localFile      = $target->getPathname();
104
105
        foreach ($this->getRemoteDirectoryList() as $dir) {
106
            if (!$this->sftp->is_dir($dir)) {
107
                $result->debug(sprintf('creating remote dir \'%s\'', $dir));
108
                $this->sftp->mkdir($dir);
109
            }
110
            $result->debug(sprintf('change to remote dir \'%s\'', $dir));
111
            $this->sftp->chdir($dir);
112
        }
113
114
        $result->debug(sprintf('store file \'%s\' as \'%s\'', $localFile, $remoteFilename));
115
        $result->debug(sprintf('last error \'%s\'', $this->sftp->getLastSFTPError()));
116
117
        if (!$this->sftp->put($remoteFilename, $localFile, phpseclib\Net\SFTP::SOURCE_LOCAL_FILE)) {
118
            throw new Exception(sprintf('error uploading file: %s - %s', $localFile, $this->sftp->getLastSFTPError()));
119
        }
120
121
        // run remote cleanup
122
        $this->cleanup($target, $result);
123
    }
124
125
    /**
126
     * Create a sftp handle.
127
     *
128
     * @return \phpseclib\Net\SFTP
129
     * @throws \phpbu\App\Backup\Sync\Exception
130
     */
131
    protected function login() : phpseclib\Net\SFTP
132 1
    {
133
        // silence phpseclib
134 1
        $old  = error_reporting(0);
135 1
        $sftp = new phpseclib\Net\SFTP($this->host);
136 1
        if ($this->privateKey) {
137
            $auth = new phpseclib\Crypt\RSA();
138 1
            $auth->loadKey(file_get_contents($this->privateKey));
139 1
            if ($this->privateKeyPassword) {
140
                $auth->setPassword($this->privateKeyPassword);
141 1
            }
142
        } else {
143 1
            $auth = $this->password;
144
        }
145
        if (!$sftp->login($this->user, $auth)) {
146
            error_reporting($old);
147
            throw new Exception(
148
                sprintf(
149
                    'authentication failed for %s@%s%s',
150
                    $this->user,
151
                    $this->host,
152
                    empty($this->password) ? '' : ' with password ****'
153
                )
154
            );
155
        }
156
        // restore old error reporting
157
        error_reporting($old);
158
159
        return $sftp;
160
    }
161
162
    /**
163
     * Return list of remote directories to travers.
164
     *
165
     * @return array
166
     */
167
    private function getRemoteDirectoryList() : array
168
    {
169
        return Util\Path::getDirectoryListFromAbsolutePath($this->remotePath);
170
    }
171
172
    /**
173
     * Creates collector for SFTP
174
     *
175
     * @param \phpbu\App\Backup\Target $target
176
     * @return \phpbu\App\Backup\Collector
177
     */
178
    protected function createCollector(Target $target): Collector
179
    {
180
        return new Collector\Sftp($target, $this->sftp, $this->remotePath);
181
    }
182
}
183