Completed
Pull Request — master (#145)
by Vitaly
05:41
created

Sftp::createCollector()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 0
cts 0
cp 0
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
crap 2
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\Result;
7
use phpbu\App\Util;
8
use phpseclib;
9
10
/**
11
 * Sftp sync
12
 *
13
 * @package    phpbu
14
 * @subpackage Backup
15
 * @author     Sebastian Feldmann <[email protected]>
16
 * @copyright  Sebastian Feldmann <[email protected]>
17
 * @license    https://opensource.org/licenses/MIT The MIT License (MIT)
18
 * @link       http://phpbu.de/
19
 * @since      Class available since Release 1.0.0
20
 */
21
class Sftp extends Xtp
22
{
23
    /**
24
     * @var phpseclib\Net\SFTP
25
     */
26
    protected $sftp;
27
28
    /**
29
     * @var string
30
     */
31
    protected $privateKey;
32
33
    /**
34
     * @var string
35
     */
36 7
    protected $privateKeyPassword;
37
38 7
    /**
39
     * (non-PHPDoc)
40 3
     *
41 3
     * @see    \phpbu\App\Backup\Sync::setup()
42
     * @param  array $config
43
     * @throws \phpbu\App\Backup\Sync\Exception
44
     * @throws \phpbu\App\Exception
45
     */
46
    public function setup(array $config)
47
    {
48 7
        if (!Util\Arr::isSetAndNotEmptyString($config, 'password') && !Util\Arr::isSetAndNotEmptyString($config, 'private_key')) {
49
            throw new Exception('\'password\' or \'private_key\' must be presented');
50 7
        }
51
        parent::setup($config);
52
53 7
        $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...
54
        $privateKey = Util\Arr::getValue($config, 'private_key', '');
55
        if (!empty($privateKey)) {
56
            // get absolute private key path
57
            $privateKey = realpath(Util\Path::toAbsolutePath($privateKey, Configuration::getWorkingDirectory()));
58
            if ($privateKey === false) {
59
                throw new \phpbu\App\Exception("Private key not found at specified path");
60 1
            }
61
        }
62 1
        $this->privateKey         = $privateKey;
63
        $this->privateKeyPassword = Util\Arr::getValue($config, 'private_key_password', '');
64
65
        $this->setUpClearable($config);
66
    }
67
68
    /**
69
     * Check for required loaded libraries or extensions.
70
     *
71
     * @throws \phpbu\App\Backup\Sync\Exception
72
     */
73 1
    protected function checkRequirements()
74
    {
75 1
        if (!class_exists('\\phpseclib\\Net\\SFTP')) {
76 1
            throw new Exception('phpseclib not installed - use composer to install "phpseclib/phpseclib" version 2.x');
77 1
        }
78
    }
79 1
80 1
    /**
81 1
     * Return implemented (*)TP protocol name.
82 1
     *
83
     * @return string
84 1
     */
85 1
    protected function getProtocolName()
86
    {
87
        return 'SFTP';
88 1
    }
89 1
90
    /**
91 1
     * (non-PHPDoc)
92
     *
93
     * @see    \phpbu\App\Backup\Sync::sync()
94
     * @param  \phpbu\App\Backup\Target $target
95
     * @param  \phpbu\App\Result        $result
96 1
     * @throws \phpbu\App\Backup\Sync\Exception
97 1
     */
98
    public function sync(Target $target, Result $result)
99
    {
100
        $this->sftp     = $this->login();
101
        $remoteFilename = $target->getFilename();
102
        $localFile      = $target->getPathname();
103
104
        foreach ($this->getRemoteDirectoryList() as $dir) {
105
            if (!$this->sftp->is_dir($dir)) {
106
                $result->debug(sprintf('creating remote dir \'%s\'', $dir));
107
                $this->sftp->mkdir($dir);
108
            }
109
            $result->debug(sprintf('change to remote dir \'%s\'', $dir));
110
            $this->sftp->chdir($dir);
111
        }
112
113
        $result->debug(sprintf('store file \'%s\' as \'%s\'', $localFile, $remoteFilename));
114
        $result->debug(sprintf('last error \'%s\'', $this->sftp->getLastSFTPError()));
115
116
        if (!$this->sftp->put($remoteFilename, $localFile, phpseclib\Net\SFTP::SOURCE_LOCAL_FILE)) {
117
            throw new Exception(sprintf('error uploading file: %s - %s', $localFile, $this->sftp->getLastSFTPError()));
118
        }
119
120
        // run remote cleanup
121
        $this->cleanup($target, $result);
122
    }
123
124
    /**
125
     * Create a sftp handle.
126
     *
127
     * @return \phpseclib\Net\SFTP
128
     * @throws \phpbu\App\Backup\Sync\Exception
129
     */
130
    protected function login() : phpseclib\Net\SFTP
131
    {
132 1
        // silence phpseclib
133
        $old  = error_reporting(0);
134 1
        $sftp = new phpseclib\Net\SFTP($this->host);
135 1
        if ($this->privateKey) {
136 1
            $auth = new phpseclib\Crypt\RSA();
137
            $auth->loadKey(file_get_contents($this->privateKey));
138 1
            if ($this->privateKeyPassword) {
139 1
                $auth->setPassword($this->privateKeyPassword);
140
            }
141 1
        } else {
142
            $auth = $this->password;
143 1
        }
144
        if (!$sftp->login($this->user, $auth)) {
145
            error_reporting($old);
146
            throw new Exception(
147
                sprintf(
148
                    'authentication failed for %s@%s%s',
149
                    $this->user,
150
                    $this->host,
151
                    empty($this->password) ? '' : ' with password ****'
152
                )
153
            );
154
        }
155
        // restore old error reporting
156
        error_reporting($old);
157
158
        return $sftp;
159
    }
160
161
    /**
162
     * Return list of remote directories to travers.
163
     *
164
     * @return array
165
     */
166
    private function getRemoteDirectoryList() : array
167
    {
168
        return Util\Path::getDirectoryListFromAbsolutePath($this->remotePath);
169
    }
170
171
    /**
172
     * Creates collector for SFTP
173
     *
174
     * @param \phpbu\App\Backup\Target $target
175
     * @return \phpbu\App\Backup\Collector
176
     */
177
    protected function createCollector(Target $target): Collector
178
    {
179
        return new Collector\Sftp($target, $this->sftp, $this->remotePath);
180
    }
181
}
182