1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/* |
4
|
|
|
* This file is part of the Conveyor package. |
5
|
|
|
* |
6
|
|
|
* (c) Jeroen Fiege <[email protected]> |
7
|
|
|
* |
8
|
|
|
* For the full copyright and license information, please view the LICENSE |
9
|
|
|
* file that was distributed with this source code. |
10
|
|
|
*/ |
11
|
|
|
|
12
|
|
|
namespace Webcreate\Conveyor\Transporter; |
13
|
|
|
|
14
|
|
|
use Symfony\Component\EventDispatcher\EventDispatcherInterface; |
15
|
|
|
use Webcreate\Conveyor\Event\TransporterEvent; |
16
|
|
|
use Webcreate\Conveyor\Event\TransporterEvents; |
17
|
|
|
use Webcreate\Util\Cli; |
18
|
|
|
|
19
|
|
|
class RsyncTransporter extends AbstractTransporter implements SshCapableTransporterInterface |
20
|
|
|
{ |
21
|
|
|
protected $cli; |
22
|
|
|
protected $_exists = array(); |
23
|
|
|
|
24
|
11 |
|
public function __construct(EventDispatcherInterface $dispatcher, Cli $cli) |
25
|
|
|
{ |
26
|
11 |
|
parent::__construct($dispatcher); |
27
|
|
|
|
28
|
11 |
|
$this->cli = $cli; |
29
|
11 |
|
} |
30
|
|
|
|
31
|
|
|
/** |
32
|
|
|
* Checks if a file or directory exists on the remote server |
33
|
|
|
* |
34
|
|
|
* @param string $path remote source path |
35
|
|
|
* @return bool true when the resource exists, false otherwise |
36
|
|
|
*/ |
37
|
1 |
View Code Duplication |
public function exists($path) |
|
|
|
|
38
|
|
|
{ |
39
|
1 |
|
if (false === isset($this->_exists[$path])) { |
40
|
1 |
|
$remoteCommand = sprintf("test -e %s", $path); |
41
|
1 |
|
$commandline = sprintf("ssh %s@%s \"%s\"", $this->getUser(), $this->getHost(), $remoteCommand); |
42
|
|
|
|
43
|
1 |
|
$exitCode = $this->cli->execute($commandline); |
44
|
|
|
|
45
|
1 |
|
$this->_exists[$path] = (0 === $exitCode); |
46
|
|
|
} |
47
|
|
|
|
48
|
1 |
|
return $this->_exists[$path]; |
49
|
|
|
} |
50
|
|
|
|
51
|
|
|
/** |
52
|
|
|
* Create a directory on the remote server |
53
|
|
|
* |
54
|
|
|
* @param string $dest remote path |
55
|
|
|
* @param bool $recursive |
56
|
|
|
* @throws \RuntimeException |
57
|
|
|
*/ |
58
|
2 |
|
public function mkdir($dest, $recursive = true) |
59
|
|
|
{ |
60
|
2 |
|
$pwd = dirname($dest); |
61
|
2 |
|
if (true === $recursive && false === $this->exists($pwd)) { |
62
|
|
|
$this->mkdir($pwd); |
63
|
|
|
} |
64
|
|
|
|
65
|
2 |
|
$remoteCommand = sprintf("mkdir '%s'", $dest); |
66
|
2 |
|
$commandline = sprintf("ssh %s@%s \"%s\"", $this->getUser(), $this->getHost(), $remoteCommand); |
67
|
|
|
|
68
|
2 |
|
$this->_exists[$dest] = true; |
69
|
|
|
|
70
|
2 |
|
$this->dispatcher->dispatch(TransporterEvents::TRANSPORTER_MKDIR, new TransporterEvent($this, $dest)); |
71
|
|
|
|
72
|
2 |
|
if ($this->cli->execute($commandline)) { |
|
|
|
|
73
|
|
|
throw new \RuntimeException($this->cli->getErrorOutput()); |
74
|
|
|
} |
75
|
2 |
|
} |
76
|
|
|
|
77
|
|
|
/** |
78
|
|
|
* Retrieve file or directory from remote server |
79
|
|
|
* |
80
|
|
|
* @param string $src remote source path |
81
|
|
|
* @param string $dest (optional) local destination path |
82
|
|
|
* @throws \RuntimeException |
83
|
|
|
* @return string |
84
|
|
|
*/ |
85
|
2 |
View Code Duplication |
public function get($src, $dest = null) |
|
|
|
|
86
|
|
|
{ |
87
|
2 |
|
$realDest = $dest; |
88
|
|
|
|
89
|
2 |
|
if (null == $dest) { |
|
|
|
|
90
|
|
|
$realDest = tempnam(sys_get_temp_dir(), basename($src)); |
91
|
|
|
} |
92
|
|
|
|
93
|
2 |
|
$commandline = sprintf("rsync %s@%s:%s \"%s\"", $this->getUser(), $this->getHost(), $src, $realDest); |
94
|
|
|
|
95
|
2 |
|
$this->dispatcher->dispatch(TransporterEvents::TRANSPORTER_GET, new TransporterEvent($this, array('src' => $src, 'dest' => $dest))); |
96
|
|
|
|
97
|
2 |
|
if ($this->cli->execute($commandline)) { |
|
|
|
|
98
|
|
|
throw new \RuntimeException($this->cli->getErrorOutput()); |
99
|
|
|
} |
100
|
|
|
|
101
|
2 |
|
if (null == $dest) { |
|
|
|
|
102
|
|
|
$content = file_get_contents($realDest); |
103
|
|
|
unlink($realDest); |
104
|
|
|
|
105
|
|
|
return $content; |
106
|
|
|
} |
107
|
2 |
|
} |
108
|
|
|
|
109
|
|
|
/** |
110
|
|
|
* Upload a file or directory to remote server |
111
|
|
|
* |
112
|
|
|
* @param string $src local source path |
113
|
|
|
* @param string $dest remote destination path |
114
|
|
|
* @throws \RuntimeException |
115
|
|
|
* @throws \InvalidArgumentException |
116
|
|
|
*/ |
117
|
2 |
View Code Duplication |
public function put($src, $dest) |
|
|
|
|
118
|
|
|
{ |
119
|
2 |
|
if (false === file_exists($src)) { |
120
|
1 |
|
throw new \InvalidArgumentException(sprintf('Resource \'%s\' does not exist', $src)); |
121
|
|
|
} |
122
|
|
|
|
123
|
1 |
|
$pwd = dirname($dest); |
124
|
1 |
|
if (false === $this->exists($pwd)) { |
125
|
|
|
$this->mkdir($pwd); |
126
|
|
|
} |
127
|
|
|
|
128
|
1 |
|
$commandline = sprintf("rsync -az \"%s\" '%s@%s:%s'", $src, $this->getUser(), $this->getHost(), $dest); |
129
|
|
|
|
130
|
1 |
|
$this->_exists[$dest] = true; |
131
|
|
|
|
132
|
1 |
|
$this->dispatcher->dispatch(TransporterEvents::TRANSPORTER_PUT, new TransporterEvent($this, array('dest' => $dest, 'src' => $src))); |
133
|
|
|
|
134
|
1 |
|
if ($this->cli->execute($commandline)) { |
|
|
|
|
135
|
|
|
throw new \RuntimeException($this->cli->getErrorOutput()); |
136
|
|
|
} |
137
|
1 |
|
} |
138
|
|
|
|
139
|
|
|
/** |
140
|
|
|
* Upload a string to remote server |
141
|
|
|
* |
142
|
|
|
* @param string $content content |
143
|
|
|
* @param string $dest remote destination path |
144
|
|
|
* @throws \RuntimeException |
145
|
|
|
*/ |
146
|
1 |
View Code Duplication |
public function putContent($content, $dest) |
|
|
|
|
147
|
|
|
{ |
148
|
1 |
|
$temp_file = tempnam(sys_get_temp_dir(), 'rsync' . time()); |
149
|
|
|
|
150
|
1 |
|
file_put_contents($temp_file, $content); |
151
|
|
|
|
152
|
1 |
|
$commandline = sprintf("rsync -a \"%s\" '%s@%s:%s'", $temp_file, $this->getUser(), $this->getHost(), $dest); |
153
|
|
|
|
154
|
1 |
|
$this->_exists[$dest] = true; |
155
|
|
|
|
156
|
1 |
|
$this->dispatcher->dispatch(TransporterEvents::TRANSPORTER_PUT_CONTENT, new TransporterEvent($this, array('dest' => $dest, 'src' => $temp_file, 'content' => $content))); |
157
|
|
|
|
158
|
1 |
|
if ($this->cli->execute($commandline)) { |
|
|
|
|
159
|
|
|
@unlink($temp_file); |
|
|
|
|
160
|
|
|
|
161
|
|
|
throw new \RuntimeException($this->cli->getErrorOutput()); |
162
|
|
|
} |
163
|
|
|
|
164
|
1 |
|
@unlink($temp_file); |
|
|
|
|
165
|
1 |
|
} |
166
|
|
|
|
167
|
|
|
/** |
168
|
|
|
* Creates a symlink on the remote server |
169
|
|
|
* |
170
|
|
|
* @param $src |
171
|
|
|
* @param $dest |
172
|
|
|
* @throws \RuntimeException |
173
|
|
|
* @return mixed |
174
|
|
|
*/ |
175
|
1 |
|
public function symlink($src, $dest) |
176
|
|
|
{ |
177
|
1 |
|
$this->dispatcher->dispatch(TransporterEvents::TRANSPORTER_SYMLINK, new TransporterEvent($this, array('dest' => $dest, 'src' => $src))); |
178
|
|
|
|
179
|
|
|
// make src an absolute path |
180
|
1 |
|
if (0 !== strpos($src, '/')) { |
181
|
1 |
|
$remoteCommand = 'pwd'; |
182
|
1 |
|
$commandline = sprintf("ssh %s@%s \"%s\"", $this->getUser(), $this->getHost(), $remoteCommand); |
183
|
|
|
|
184
|
1 |
|
if ($this->cli->execute($commandline)) { |
|
|
|
|
185
|
|
|
throw new \RuntimeException($this->cli->getErrorOutput()); |
186
|
|
|
} |
187
|
|
|
|
188
|
1 |
|
$pwd = trim($this->cli->getOutput()); |
189
|
|
|
|
190
|
1 |
|
$src = $pwd . '/' . $src; |
|
|
|
|
191
|
|
|
} |
192
|
|
|
|
193
|
1 |
|
$remoteCommand = sprintf("ln -s -f %s %s", $src, $dest); |
194
|
1 |
|
$commandline = sprintf("ssh %s@%s \"%s\"", $this->getUser(), $this->getHost(), $remoteCommand); |
195
|
|
|
|
196
|
1 |
|
if ($this->cli->execute($commandline)) { |
|
|
|
|
197
|
|
|
throw new \RuntimeException($this->cli->getErrorOutput()); |
198
|
|
|
} |
199
|
1 |
|
} |
200
|
|
|
|
201
|
|
|
/** |
202
|
|
|
* Checks for symlink on the remote server |
203
|
|
|
* |
204
|
|
|
* @param $dest |
205
|
|
|
* @return bool |
206
|
|
|
*/ |
207
|
|
View Code Duplication |
public function isSymlink($dest) |
|
|
|
|
208
|
|
|
{ |
209
|
|
|
$remoteCommand = sprintf("readlink %s", $dest); |
210
|
|
|
$commandline = sprintf("ssh %s@%s \"%s\"", $this->getUser(), $this->getHost(), $remoteCommand); |
211
|
|
|
|
212
|
|
|
if ($this->cli->execute($commandline)) { |
|
|
|
|
213
|
|
|
return false; |
214
|
|
|
} |
215
|
|
|
|
216
|
|
|
return ('' !== trim($this->cli->getOutput())); |
217
|
|
|
} |
218
|
|
|
|
219
|
|
|
/** |
220
|
|
|
* Copies a file/directory on the remote host |
221
|
|
|
* |
222
|
|
|
* @param string $src |
223
|
|
|
* @param string $dest |
224
|
|
|
* @param bool $recursive |
225
|
|
|
* @throws \RuntimeException |
226
|
|
|
* @return mixed |
227
|
|
|
*/ |
228
|
1 |
|
public function copy($src, $dest, $recursive = true) |
229
|
|
|
{ |
230
|
1 |
|
$recursiveFlag = ($recursive ? 'r' : ''); |
231
|
|
|
|
232
|
1 |
|
$remoteCommand = sprintf("cp -{$recursiveFlag}f %s %s", $src, $dest); |
233
|
1 |
|
$commandline = sprintf("ssh %s@%s \"%s\"", $this->getUser(), $this->getHost(), $remoteCommand); |
234
|
|
|
|
235
|
1 |
|
$this->dispatcher->dispatch(TransporterEvents::TRANSPORTER_COPY, new TransporterEvent($this, array('dest' => $dest, 'src' => $src))); |
236
|
|
|
|
237
|
1 |
|
if ($this->cli->execute($commandline)) { |
|
|
|
|
238
|
|
|
throw new \RuntimeException($this->cli->getErrorOutput()); |
239
|
|
|
} |
240
|
1 |
|
} |
241
|
|
|
|
242
|
|
|
/** |
243
|
|
|
* Removes a file/directory on the remote host |
244
|
|
|
* |
245
|
|
|
* @param string $path |
246
|
|
|
* @param bool $recursive |
247
|
|
|
* @throws \RuntimeException |
248
|
|
|
* @return mixed |
249
|
|
|
*/ |
250
|
1 |
View Code Duplication |
public function remove($path, $recursive = true) |
|
|
|
|
251
|
|
|
{ |
252
|
1 |
|
$recursiveFlag = ($recursive ? 'r' : ''); |
253
|
|
|
|
254
|
1 |
|
$remoteCommand = sprintf("rm -{$recursiveFlag}f %s", $path); |
255
|
1 |
|
$commandline = sprintf("ssh %s@%s \"%s\"", $this->getUser(), $this->getHost(), $remoteCommand); |
256
|
|
|
|
257
|
1 |
|
$this->dispatcher->dispatch(TransporterEvents::TRANSPORTER_REMOVE, new TransporterEvent($this, array('path' => $path))); |
258
|
|
|
|
259
|
1 |
|
if ($this->cli->execute($commandline)) { |
|
|
|
|
260
|
|
|
throw new \RuntimeException($this->cli->getErrorOutput()); |
261
|
|
|
} |
262
|
1 |
|
} |
263
|
|
|
|
264
|
|
|
/** |
265
|
|
|
* @todo this is just a simple implementation which should be improved |
266
|
|
|
* |
267
|
|
|
* @param $command |
268
|
|
|
* @param null $callback |
269
|
|
|
* @throws \RuntimeException |
270
|
|
|
*/ |
271
|
|
View Code Duplication |
public function exec($command, $callback = null) |
|
|
|
|
272
|
|
|
{ |
273
|
|
|
$commandline = sprintf("ssh %s@%s \"%s\"", $this->getUser(), $this->getHost(), $command); |
274
|
|
|
|
275
|
|
|
if ($this->cli->execute($commandline, $callback)) { |
|
|
|
|
276
|
|
|
throw new \RuntimeException($this->cli->getErrorOutput()); |
277
|
|
|
} |
278
|
|
|
} |
279
|
|
|
|
280
|
|
|
/** |
281
|
|
|
* Lists files and directories |
282
|
|
|
* |
283
|
|
|
* returns an array with the following format: |
284
|
|
|
* |
285
|
|
|
* array( |
286
|
|
|
* 'filename' => array( |
287
|
|
|
* 'type' => 'directory', // or 'file' |
288
|
|
|
* 'mtime' => new \DateTime(), |
289
|
|
|
* ), |
290
|
|
|
* ); |
291
|
|
|
* |
292
|
|
|
* @param string $path |
293
|
|
|
* @throws \RuntimeException |
294
|
|
|
* @return array |
295
|
|
|
*/ |
296
|
|
|
public function ls($path) |
|
|
|
|
297
|
|
|
{ |
298
|
|
|
throw new \RuntimeException('Listing of files and directories is not supported (yet)'); |
299
|
|
|
} |
300
|
|
|
} |
301
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.