18 | */
19 | abstract class AbstractProtocol
20 | {
21 | /**
22 | * @var string
23 | */
24 | protected $executable = "";
25 |
26 | /**
27 | * Shortcut to set options from array config
28 | *
29 | * @param array $options
30 | * @param $name
31 | * @param $method
32 | */
33 | protected function setOption(Array $options, $name, $method)
34 | {
35 | if (isset($options[$name])) {
36 | $this->$method($options[$name]);
37 | }
38 | }
39 |
40 | /**
41 | * Sets rsync executable location, i.e.: /usr/bin/rsync
42 | *
43 | * @param $rsyncLocation
44 | *
45 | * @throws \InvalidArgumentException If the rsync location is not executable
46 | */
47 | public function setExecutable($rsyncLocation)
48 | {
49 | if (!is_executable($rsyncLocation)) {
50 | throw new \InvalidArgumentException("Rsync location '".$rsyncLocation."' is invalid");
51 | }
52 |
53 | $this->executable = $rsyncLocation;
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/AFM/Rsync/Command.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read
9 | * the LICENSE file that was distributed with this source code.
10 | */
11 |
12 | namespace AFM\Rsync;
13 |
14 | /**
15 | * Command abstraction class, construct commands
16 | * using arguments options and parameters
17 | *
18 | * Command format:
19 | *
20 | * [executable] [-abLs](options) [-a value](argument) [--test value](argument) [parameter1] ... [parameterN]
21 | *
22 | *
23 | * @author Alberto
24 | */
25 | class Command
26 | {
27 | /**
28 | * @var string
29 | */
30 | private $executable;
31 |
32 | /**
33 | * @var array
34 | */
35 | private $options = array();
36 |
37 | /**
38 | * @var array
39 | */
40 | private $arguments = array();
41 |
42 | /**
43 | * @var string
44 | */
45 | private $command;
46 |
47 | /**
48 | * @var array
49 | */
50 | private $parameters = array();
51 |
52 | /**
53 | * Every command must have an executable
54 | *
55 | * @param $executable
56 | */
57 | public function __construct($executable)
58 | {
59 | $this->executable = $executable;
60 | }
61 |
62 | /**
63 | * Adds a parameter to the command, will be appended
64 | * in the same order as insertion at the end
65 | *
66 | * @param $parameter
67 | */
68 | public function addParameter($parameter)
69 | {
70 | $this->parameters[] = $parameter;
71 | }
72 |
73 | /**
74 | * Adds an option to the command, will be
75 | * appended to the command in the next format:
76 | *
77 | *
78 | * -aBLs
79 | *
80 | *
81 | * @param $option
82 | */
83 | public function addOption($option)
84 | {
85 | $this->options[] = $option;
86 | }
87 |
88 | /**
89 | * Adds an argument to the command. If the argument
90 | * is more than one letter, "-- " will be appended before
91 | * if not, it will act as an option with a value:
92 | *
93 | *
94 | * --argument [value]
95 | * -p [value]
96 | *
97 | *
98 | * @param $name
99 | * @param bool|mixed $value
100 | */
101 | public function addArgument($name, $value = true)
102 | {
103 | $this->arguments[$name][] = $value;
104 | }
105 |
106 | /**
107 | * @param $executable
108 | */
109 | public function setExecutable($executable)
110 | {
111 | $this->executable = $executable;
112 | }
113 |
114 | /**
115 | * @return string
116 | */
117 | public function getExecutable()
118 | {
119 | return $this->executable;
120 | }
121 |
122 | /**
123 | * Constructs the command appendind options,
124 | * arguments, executable and parameters
125 | *
126 | * @return string
127 | */
128 | protected function constructCommand()
129 | {
130 | $command = array();
131 | $command[] = $this->executable;
132 |
133 | if (!empty($this->options)) {
134 | $command[] = "-".implode($this->options);
135 | }
136 |
137 | foreach ($this->arguments as $argument => $values) {
138 | foreach ($values as $value) {
139 | if (strlen($argument) == 1) {
140 | $command[] = "-".$argument." '".$value."'";
141 | } else {
142 | $command[] = "--".(is_string($value) || is_int($value) ? $argument." '".$value."'" : $argument);
143 | }
144 | }
145 | }
146 |
147 | if (!empty($this->parameters)) {
148 | $command[] = implode(" ", $this->parameters);
149 | }
150 |
151 | $stringCommand = implode(" ", $command);
152 |
153 | return $stringCommand;
154 | }
155 |
156 | /**
157 | * Gets the command string
158 | *
159 | * @return mixed
160 | */
161 | public function getCommand()
162 | {
163 | if (is_null($this->command)) {
164 | $this->command = $this->constructCommand();
165 | }
166 |
167 | return $this->command;
168 | }
169 |
170 | /**
171 | * @see getCommand
172 | * @return mixed
173 | */
174 | public function __toString()
175 | {
176 | return $this->getCommand();
177 | }
178 |
179 | /**
180 | * Execute command, with optional output printer
181 | *
182 | * @param bool $showOutput
183 | */
184 | public function execute($showOutput = false)
185 | {
186 | $this->getCommand();
187 |
188 | if ($showOutput) {
189 | $this->executeWithOutput();
190 | } else {
191 | shell_exec($this->command);
192 | }
193 | }
194 |
195 | /**
196 | * Execute and buffers command result to print it
197 | *
198 | * @throws \InvalidArgumentException When the command couldn't be executed
199 | */
200 | private function executeWithOutput()
201 | {
202 | if (($fp = popen($this->command, "r"))) {
203 | while (!feof($fp)) {
204 | echo fread($fp, 1024);
205 | flush();
206 | }
207 |
208 | fclose($fp);
209 | } else {
210 | throw new \InvalidArgumentException("Cannot execute command: '".$this->command."'");
211 | }
212 | }
213 | }
214 |
--------------------------------------------------------------------------------
/src/AFM/Rsync/Rsync.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read
9 | * the LICENSE file that was distributed with this source code.
10 | */
11 |
12 | namespace AFM\Rsync;
13 |
14 | /**
15 | * Rsync wrapper. Many options are not implemented,
16 | * but you can use setOptionalParameters
17 | *
18 | * @author Alberto Fernández
19 | */
20 | class Rsync extends AbstractProtocol
21 | {
22 | /**
23 | * @var string
24 | */
25 | protected $executable = "/usr/bin/rsync";
26 |
27 | /**
28 | * @var bool
29 | */
30 | protected $archive = true;
31 |
32 | /**
33 | * @var bool
34 | */
35 | protected $skipNewerFiles = false;
36 |
37 | /**
38 | * @var bool
39 | */
40 | protected $followSymLinks = true;
41 |
42 | /**
43 | * @var bool
44 | */
45 | protected $dryRun = false;
46 |
47 | /**
48 | * @var array
49 | */
50 | protected $optionalParameters = array();
51 |
52 | /**
53 | * @var bool
54 | */
55 | protected $verbose = false;
56 |
57 | /**
58 | * @var bool
59 | */
60 | protected $deleteFromTarget = false;
61 |
62 | /**
63 | * @var bool
64 | */
65 | protected $deleteExcluded = false;
66 |
67 | /**
68 | * @var array
69 | */
70 | protected $exclude = array();
71 |
72 | /**
73 | * @var string
74 | */
75 | protected $excludeFrom = null;
76 |
77 | /**
78 | * @var bool
79 | */
80 | protected $recursive = true;
81 |
82 | /**
83 | * @var bool
84 | */
85 | protected $times = false;
86 |
87 | /**
88 | * @var bool
89 | */
90 | protected $showOutput = true;
91 |
92 | /**
93 | * @var bool
94 | */
95 | protected $compression = false;
96 |
97 | /**
98 | * @var bool
99 | */
100 | protected $remoteOrigin = false;
101 |
102 | /**
103 | * @var bool
104 | */
105 | protected $removeSource = false;
106 |
107 | /**
108 | * @var bool
109 | */
110 | protected $info = false;
111 |
112 | /**
113 | * @var bool
114 | */
115 | protected $compareDest = false;
116 |
117 | /**
118 | * @var bool
119 | */
120 | protected $pruneEmptyDirs = false;
121 |
122 | /**
123 | * @var SSH
124 | */
125 | protected $ssh;
126 |
127 | /**
128 | * Injects and validates config
129 | *
130 | * @param array $options
131 | */
132 | public function __construct(Array $options = array())
133 | {
134 | $this->setOption($options, 'executable', 'setExecutable');
135 | $this->setOption($options, 'archive', 'setArchive');
136 | $this->setOption($options, 'update', 'setSkipNewerFiles');
137 | $this->setOption($options, 'follow_symlinks', 'setFollowSymLinks');
138 | $this->setOption($options, 'dry_run', 'setDryRun');
139 | $this->setOption($options, 'option_parameters', 'setOptionalParameters');
140 | $this->setOption($options, 'verbose', 'setVerbose');
141 | $this->setOption($options, 'delete_from_target', 'setDeleteFromTarget');
142 | $this->setOption($options, 'delete_excluded', 'setDeleteExcluded');
143 | $this->setOption($options, 'exclude', 'setExclude');
144 | $this->setOption($options, 'excludeFrom', 'setExcludeFrom');
145 | $this->setOption($options, 'recursive', 'setRecursive');
146 | $this->setOption($options, 'times', 'setTimes');
147 | $this->setOption($options, 'show_output', 'setShowOutput');
148 | $this->setOption($options, 'ssh', 'setSshOptions');
149 | $this->setOption($options, 'compression', 'setCompression');
150 | $this->setOption($options, 'remote_origin', 'setRemoteOrigin');
151 | $this->setOption($options, 'remove_source', 'setRemoveSource');
152 | $this->setOption($options, 'info', 'setInfo');
153 | $this->setOption($options, 'compare_dest', 'setCompareDest');
154 | $this->setOption($options, 'prune_empty_dirs', 'setPruneEmptyDirs');
155 | }
156 |
157 | /**
158 | * @param $options
159 | */
160 | public function setSshOptions($options)
161 | {
162 | $this->ssh = new SSH($options);
163 | }
164 |
165 | /**
166 | * Sync $origin directory with $target one.
167 | * If SSH was configured, you must use absolute path
168 | * in the target directory
169 | *
170 | * @param $origin
171 | * @param $target
172 | *
173 | * @throws \InvalidArgumentException If the command failed
174 | */
175 | public function sync($origin, $target)
176 | {
177 | $command = $this->getCommand($origin, $target);
178 |
179 | $command->execute($this->showOutput);
180 | }
181 |
182 | /**
183 | * @return string
184 | */
185 | public function getExecutable()
186 | {
187 | return $this->executable;
188 | }
189 |
190 | /**
191 | * @return bool
192 | */
193 | public function getArchive()
194 | {
195 | return $this->archive;
196 | }
197 |
198 | /**
199 | * @param $archive
200 | */
201 | public function setArchive($archive)
202 | {
203 | $this->archive = $archive;
204 | }
205 |
206 | /**
207 | * @return bool
208 | */
209 | public function getPruneEmptyDirs()
210 | {
211 | return $this->pruneEmptyDirs;
212 | }
213 |
214 | /**
215 | * @param $pruneEmptyDirs
216 | */
217 | public function setPruneEmptyDirs($pruneEmptyDirs)
218 | {
219 | $this->pruneEmptyDirs = $pruneEmptyDirs;
220 | }
221 |
222 |
223 | /**
224 | * @param $skipNewerFiles
225 | */
226 | public function setSkipNewerFiles($skipNewerFiles)
227 | {
228 | $this->skipNewerFiles = $skipNewerFiles;
229 | }
230 |
231 | /**
232 | * @return bool
233 | */
234 | public function getSkipNewerFiles()
235 | {
236 | return $this->skipNewerFiles;
237 | }
238 |
239 | /**
240 | * @param $followSymLinks
241 | */
242 | public function setFollowSymLinks($followSymLinks)
243 | {
244 | $this->followSymLinks = $followSymLinks;
245 | }
246 |
247 | /**
248 | * @return bool
249 | */
250 | public function getFollowSymLinks()
251 | {
252 | return $this->followSymLinks;
253 | }
254 |
255 | /**
256 | * @param $dryRun
257 | */
258 | public function setDryRun($dryRun)
259 | {
260 | $this->dryRun = $dryRun;
261 | }
262 |
263 | /**
264 | * @return bool
265 | */
266 | public function getDryRun()
267 | {
268 | return $this->dryRun;
269 | }
270 |
271 | /**
272 | * @param $optionalParameters
273 | */
274 | public function setOptionalParameters($optionalParameters)
275 | {
276 | $this->optionalParameters = $optionalParameters;
277 | }
278 |
279 | /**
280 | * @return array
281 | */
282 | public function getOptionalParameters()
283 | {
284 | return $this->optionalParameters;
285 | }
286 |
287 | /**
288 | * @param $verbose
289 | */
290 | public function setVerbose($verbose)
291 | {
292 | $this->verbose = $verbose;
293 | }
294 |
295 | /**
296 | * @return bool
297 | */
298 | public function getVerbose()
299 | {
300 | return $this->verbose;
301 | }
302 |
303 | /**
304 | * @param $deleteExcluded
305 | */
306 | public function setDeleteExcluded($deleteExcluded)
307 | {
308 | $this->deleteExcluded = $deleteExcluded;
309 | }
310 |
311 | /**
312 | * @return bool
313 | */
314 | public function getDeleteExcluded()
315 | {
316 | return $this->deleteExcluded;
317 | }
318 |
319 | /**
320 | * @param $deleteFromTarget
321 | */
322 | public function setDeleteFromTarget($deleteFromTarget)
323 | {
324 | $this->deleteFromTarget = $deleteFromTarget;
325 | }
326 |
327 | /**
328 | * @return bool
329 | */
330 | public function getDeleteFromTarget()
331 | {
332 | return $this->deleteFromTarget;
333 | }
334 |
335 | /**
336 | * @param $exclude
337 | */
338 | public function setExclude($exclude)
339 | {
340 | $this->exclude = $exclude;
341 | }
342 |
343 | /**
344 | * @return array
345 | */
346 | public function getExclude()
347 | {
348 | return $this->exclude;
349 | }
350 |
351 | /**
352 | * @param $exclude
353 | */
354 | public function setExcludeFrom($excludeFrom)
355 | {
356 | $this->excludeFrom = $excludeFrom;
357 | }
358 |
359 | /**
360 | * @return string
361 | */
362 | public function getExcludeFrom()
363 | {
364 | return $this->excludeFrom;
365 | }
366 |
367 | /**
368 | * @param $recursive
369 | */
370 | public function setRecursive($recursive)
371 | {
372 | $this->recursive = $recursive;
373 | }
374 |
375 | /**
376 | * @return bool
377 | */
378 | public function getRecursive()
379 | {
380 | return $this->recursive;
381 | }
382 |
383 | /**
384 | * @param bool $times
385 | */
386 | public function setTimes($times)
387 | {
388 | $this->times = $times;
389 | }
390 |
391 | /**
392 | * @return bool
393 | */
394 | public function getTimes()
395 | {
396 | return $this->times;
397 | }
398 |
399 | /**
400 | * @param $showOutput
401 | */
402 | public function setShowOutput($showOutput)
403 | {
404 | $this->showOutput = $showOutput;
405 | }
406 |
407 | /**
408 | * @return bool
409 | */
410 | public function getShowOutput()
411 | {
412 | return $this->showOutput;
413 | }
414 |
415 | /**
416 | * @param $compression
417 | */
418 | public function setCompression($compression)
419 | {
420 | $this->compression = $compression;
421 | }
422 |
423 | /**
424 | * @return bool
425 | */
426 | public function getCompression()
427 | {
428 | return $this->compression;
429 | }
430 |
431 | /**
432 | * @param $remoteOrigin
433 | */
434 | public function setRemoteOrigin($remoteOrigin)
435 | {
436 | $this->remoteOrigin = (bool)$remoteOrigin;
437 | }
438 |
439 | /**
440 | * @return bool
441 | */
442 | public function getRemoteOrigin()
443 | {
444 | return $this->remoteOrigin;
445 | }
446 |
447 | /**
448 | * @param $removeSource
449 | */
450 | public function setRemoveSource($removeSource)
451 | {
452 | $this->removeSource = (bool)$removeSource;
453 | }
454 |
455 | /**
456 | * @return bool
457 | */
458 | public function getRemoveSource()
459 | {
460 | return $this->removeSource;
461 | }
462 |
463 | /**
464 | * @param $info
465 | */
466 | public function setInfo($info)
467 | {
468 | $this->info = $info;
469 | }
470 |
471 | /**
472 | * @return bool
473 | */
474 | public function getInfo()
475 | {
476 | return $this->info;
477 | }
478 |
479 | /**
480 | * @param $dest
481 | */
482 | public function setCompareDest($dest)
483 | {
484 | $this->compareDest = $dest;
485 | }
486 |
487 | /**
488 | * @return string
489 | */
490 | public function getCompareDest()
491 | {
492 | return $this->compareDest;
493 | }
494 |
495 | /**
496 | * Gets command generated for this current
497 | * rsync configuration. You can use it to test
498 | * or execute it later without using the sync method
499 | *
500 | * @param $origin
501 | * @param $target
502 | *
503 | * @return Command
504 | */
505 | public function getCommand($origin, $target)
506 | {
507 | $command = new Command($this->executable);
508 |
509 | if ($this->skipNewerFiles) {
510 | $command->addOption("u");
511 | }
512 |
513 | if ($this->followSymLinks) {
514 | $command->addOption("L");
515 | }
516 |
517 | if ($this->dryRun) {
518 | $command->addOption("n");
519 | }
520 |
521 | if ($this->verbose) {
522 | $command->addOption("v");
523 | }
524 |
525 | if ($this->compression) {
526 | $command->addOption("z");
527 | }
528 |
529 | // add any optional options we've specified
530 | $extra_options = $this->getOptionalParameters();
531 | if (!empty($extra_options)) {
532 | // if the extra options were given as a flat string, then convert it to an array
533 | if (is_string($extra_options)) {
534 | $extra_options = str_split($extra_options);
535 | }
536 |
537 | // add each extra option we've defined.
538 | if (is_array($extra_options)) {
539 | foreach ($extra_options as $option) {
540 | $command->addOption($option);
541 | }
542 | }
543 | }
544 |
545 | if ($this->times) {
546 | $command->addArgument('times');
547 | }
548 |
549 | if ($this->deleteFromTarget) {
550 | $command->addArgument('delete');
551 | }
552 |
553 | if ($this->removeSource) {
554 | $command->addArgument('remove-source-files');
555 | }
556 |
557 | if ($this->deleteExcluded) {
558 | $command->addArgument('delete-excluded');
559 | }
560 |
561 | if ($this->info) {
562 | $command->addArgument('info', $this->info);
563 | }
564 |
565 | if ($this->compareDest) {
566 | $command->addArgument('compare-dest', $this->compareDest);
567 | }
568 |
569 | if (!empty($this->exclude)) {
570 | foreach ($this->exclude as $excluded) {
571 | $command->addArgument('exclude', $excluded);
572 | }
573 | }
574 |
575 | if (!empty($this->excludeFrom)) {
576 | $command->addArgument('exclude-from', $this->excludeFrom);
577 | }
578 |
579 | if ($this->archive) {
580 | $command->addOption("a");
581 | }
582 |
583 | if (!$this->archive && $this->recursive) {
584 | $command->addOption("r");
585 | }
586 |
587 | if ($this->pruneEmptyDirs) {
588 | $command->addArgument('prune-empty-dirs');
589 | }
590 |
591 | if (!is_null($this->ssh)) {
592 | $ssh = $this->ssh->getConnectionOptions();
593 | $command->addArgument("rsh", $ssh);
594 | }
595 |
596 | if (is_null($this->ssh)) {
597 | $command->addParameter($origin);
598 | $command->addParameter($target);
599 | } elseif ($this->remoteOrigin) {
600 | $command->addParameter($this->ssh->getHostConnection().":".$origin);
601 | $command->addParameter($target);
602 | } else {
603 | $command->addParameter($origin);
604 | $command->addParameter($this->ssh->getHostConnection().":".$target);
605 | }
606 |
607 | return $command;
608 | }
609 | }
610 |
--------------------------------------------------------------------------------
/src/AFM/Rsync/SSH.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read
9 | * the LICENSE file that was distributed with this source code.
10 | */
11 |
12 | namespace AFM\Rsync;
13 |
14 | /**
15 | * Abstract SSH connection command. Note that if you
16 | * don't specify a public key, you will be prompted for
17 | * the remote server password
18 | *
19 | * @author Alberto
20 | */
21 | class SSH extends AbstractProtocol
22 | {
23 | /**
24 | * @var string
25 | */
26 | protected $executable = "ssh";
27 |
28 | /**
29 | * @var string
30 | */
31 | protected $host;
32 |
33 | /**
34 | * @var int
35 | */
36 | protected $port = 22;
37 |
38 | /**
39 | * @var string
40 | */
41 | protected $username;
42 |
43 | /**
44 | * @var null
45 | */
46 | protected $privateKey = null;
47 |
48 | /**
49 | * Injects and validates config
50 | *
51 | * @param array $options
52 | */
53 | public function __construct(Array $options = array())
54 | {
55 | $this->setOption($options, 'executable', 'setExecutable');
56 | $this->setOption($options, 'host', 'setHost');
57 | $this->setOption($options, 'port', 'setPort');
58 | $this->setOption($options, 'username', 'setUsername');
59 | $this->setOption($options, 'private_key', 'setPrivateKey');
60 | $this->setOption($options, 'public_key', 'setPrivateKey');
61 | }
62 |
63 | /**
64 | * @param $host
65 | */
66 | public function setHost($host)
67 | {
68 | $this->host = $host;
69 | }
70 |
71 | /**
72 | * @return mixed
73 | */
74 | public function getHost()
75 | {
76 | return $this->host;
77 | }
78 |
79 | /**
80 | * @param $port
81 | *
82 | * @throws \InvalidArgumentException If the port is not numeric
83 | */
84 | public function setPort($port)
85 | {
86 | if (!is_int($port)) {
87 | throw new \InvalidArgumentException("SSH port must be an integer");
88 | }
89 |
90 | $this->port = $port;
91 | }
92 |
93 | /**
94 | * @return int
95 | */
96 | public function getPort()
97 | {
98 | return $this->port;
99 | }
100 |
101 | /**
102 | * @param $privateKey
103 | * @throws \InvalidArgumentException
104 | */
105 | public function setPrivateKey($privateKey)
106 | {
107 | if (!is_readable($privateKey)) {
108 | throw new \InvalidArgumentException("SSH private key '".$privateKey."' is not readable");
109 | }
110 |
111 | $this->privateKey = $privateKey;
112 | }
113 |
114 | /**
115 | * @return string
116 | */
117 | public function getPrivateKey()
118 | {
119 | return $this->privateKey;
120 | }
121 |
122 | /**
123 | * @param $username
124 | */
125 | public function setUsername($username)
126 | {
127 | $this->username = $username;
128 | }
129 |
130 | /**
131 | * @return string
132 | */
133 | public function getUsername()
134 | {
135 | return $this->username;
136 | }
137 |
138 | /**
139 | * Gets commands for this SSH connection
140 | *
141 | * @param bool $hostConnection
142 | *
143 | * @return string
144 | *
145 | * @throws \InvalidArgumentException If you don't specify a SSH username or host
146 | */
147 | public function getCommand($hostConnection = true)
148 | {
149 | if (is_null($this->username)) {
150 | throw new \InvalidArgumentException("You must specify a SSH username");
151 | }
152 |
153 | if (is_null($this->host)) {
154 | throw new \InvalidArgumentException("You must specify a SSH host to connect");
155 | }
156 |
157 | $command = new Command($this->executable);
158 |
159 | if ($this->port != 22) {
160 | $command->addArgument("p", $this->port);
161 | }
162 |
163 | if (!is_null($this->privateKey)) {
164 | $command->addArgument("i", $this->privateKey);
165 | }
166 |
167 | if ($hostConnection) {
168 | $command->addParameter($this->getHostConnection());
169 | }
170 |
171 | return $command;
172 | }
173 |
174 | /**
175 | * Gets only connection options, without user@host string
176 | *
177 | * @return string
178 | */
179 | public function getConnectionOptions()
180 | {
181 | return (string)$this->getCommand(false);
182 | }
183 |
184 | /**
185 | * Gets only host connection, without the rest
186 | * of options
187 | *
188 | * @return string
189 | */
190 | public function getHostConnection()
191 | {
192 | return $this->username."@".$this->host;
193 | }
194 |
195 | /**
196 | * @param $executable
197 | */
198 | public function setExecutable($executable)
199 | {
200 | $this->executable = $executable;
201 | }
202 |
203 | /**
204 | * @return string
205 | */
206 | public function getExecutable()
207 | {
208 | return $this->executable;
209 | }
210 |
211 | /**
212 | * @deprecated
213 | */
214 | public function setPublicKey($privateKey)
215 | {
216 | $this->setPrivateKey($privateKey);
217 | }
218 |
219 | /**
220 | * @deprecated
221 | */
222 | public function getPublicKey()
223 | {
224 | return $this->getPrivateKey();
225 | }
226 | }
227 |
--------------------------------------------------------------------------------
/tests/AFM/Rsync/Tests/CommandTest.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read
9 | * the LICENSE file that was distributed with this source code.
10 | */
11 |
12 | namespace AFM\Rsync\Tests;
13 |
14 | use AFM\Rsync\Command;
15 |
16 | class CommandTest extends \PHPUnit_Framework_TestCase
17 | {
18 | public function testCommandWithOnlyOptions()
19 | {
20 | $command = new Command("test");
21 |
22 | $command->addOption("a");
23 |
24 | $actual = $command->getCommand();
25 | $expected = "test -a";
26 |
27 | $this->assertEquals($expected, $actual);
28 | }
29 |
30 | public function testCommandWithMultipleOptions()
31 | {
32 | $command = new Command("test");
33 |
34 | $command->addOption("a");
35 | $command->addOption("b");
36 | $command->addOption("z");
37 |
38 | $actual = $command->getCommand();
39 | $expected = "test -abz";
40 |
41 | $this->assertEquals($expected, $actual);
42 | }
43 |
44 | public function testCommandWithOnlyOneParameter()
45 | {
46 | $command = new Command("test");
47 |
48 | $command->addParameter("test");
49 |
50 | $actual = $command->getCommand();
51 | $expected = "test test";
52 |
53 | $this->assertEquals($expected, $actual);
54 | }
55 |
56 | public function testCommandWithOneOptionAndOneParameter()
57 | {
58 | $command = new Command("test");
59 |
60 | $command->addOption("a");
61 | $command->addParameter("test");
62 |
63 | $actual = $command->getCommand();
64 | $expected = "test -a test";
65 |
66 | $this->assertEquals($expected, $actual);
67 | }
68 |
69 | public function testCommandWithOneOptionAndMultipleParameters()
70 | {
71 | $command = new Command("test");
72 |
73 | $command->addOption("a");
74 | $command->addParameter("test");
75 | $command->addParameter("test2");
76 |
77 | $actual = $command->getCommand();
78 | $expected = "test -a test test2";
79 |
80 | $this->assertEquals($expected, $actual);
81 | }
82 |
83 | public function testCommandWithOnlyOneSimpleArgument()
84 | {
85 | $command = new Command("test");
86 |
87 | $command->addArgument("test");
88 |
89 | $actual = $command->getCommand();
90 | $expected = "test --test";
91 |
92 | $this->assertEquals($expected, $actual);
93 | }
94 |
95 | public function testCommandWithOnlyOneArgument()
96 | {
97 | $command = new Command("test");
98 |
99 | $command->addArgument("test", "value");
100 |
101 | $actual = $command->getCommand();
102 | $expected = "test --test 'value'";
103 |
104 | $this->assertEquals($expected, $actual);
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/tests/AFM/Rsync/Tests/RsyncTest.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read
9 | * the LICENSE file that was distributed with this source code.
10 | */
11 |
12 | namespace AFM\Rsync\Tests;
13 |
14 | use AFM\Rsync\Rsync;
15 |
16 | class RsyncTest extends \PHPUnit_Framework_TestCase
17 | {
18 | private static $targetDir;
19 |
20 | private static $sourceDir;
21 |
22 | public function setUp()
23 | {
24 | @rrmdir(self::$targetDir);
25 | }
26 |
27 | public static function setUpBeforeClass()
28 | {
29 | self::$sourceDir = __DIR__.'/dir1';
30 | self::$targetDir = __DIR__.'/dir2';
31 |
32 | @mkdir(self::$targetDir);
33 | }
34 |
35 | public static function tearDownAfterClass()
36 | {
37 | @rrmdir(self::$targetDir);
38 | }
39 |
40 | public function testValidExecutableLocation()
41 | {
42 | $rsync = new Rsync;
43 | $rsync->setExecutable("/usr/bin/rsync");
44 |
45 | $this->assertTrue(true);
46 | }
47 |
48 | /**
49 | * @expectedException \InvalidArgumentException
50 | */
51 | public function testInvalidExecutableLocation()
52 | {
53 | $rsync = new Rsync;
54 | $rsync->setExecutable("/usr/not/exists/rsync!!");
55 | }
56 |
57 | public function testFollowSymlinkOptions()
58 | {
59 | $rsync = new Rsync(array('follow_symlinks' => true));
60 |
61 | $this->assertTrue($rsync->getFollowSymLinks());
62 | }
63 |
64 | public function testBasicSync()
65 | {
66 | $rsync = new Rsync;
67 |
68 | $rsync->sync($this->getSourceDir()."/*", $this->getTargetDir());
69 |
70 | $this->assertTrue(compare_directories($this->getSourceDir(), $this->getTargetDir()));
71 | }
72 |
73 | public function testRsyncWithSSHConnection()
74 | {
75 | $user = getenv('USER') ?: 'test_no_ssh_server';
76 | $targetBaseDir = getenv('HOME') ?: '/home';
77 |
78 | $config = array(
79 | 'ssh' => array(
80 | 'username' => $user,
81 | 'host' => 'localhost',
82 | 'port' => 2222,
83 | ),
84 | );
85 |
86 | $rsync = new Rsync($config);
87 |
88 | $command = $rsync->getCommand(".", $targetBaseDir."/test/");
89 |
90 | $actual = $command->getCommand();
91 | $expected = "/usr/bin/rsync -La --rsh 'ssh -p '2222'' . ".$user."@localhost:".$targetBaseDir."/test/";
92 |
93 | $this->assertEquals($expected, $actual);
94 | }
95 |
96 | public function testRsyncWithRealSSHConnection()
97 | {
98 | $user = getenv('USER');
99 | $targetBaseDir = getenv('HOME');
100 | $sshKey = $targetBaseDir.'/.ssh/id_rsa_rsync_test';
101 |
102 | if (!file_exists($sshKey)) {
103 | $this->markTestIncomplete('Cannot perform real SSH rsync due to missing SSH configuration');
104 | }
105 |
106 | $config = array(
107 | 'ssh' => array(
108 | 'username' => $user,
109 | 'host' => 'localhost',
110 | 'port' => 2222,
111 | 'private_key' => $sshKey,
112 | ),
113 | );
114 |
115 | $rsync = new Rsync($config);
116 | $rsync->sync($this->getSourceDir()."/*", $this->getTargetDir());
117 |
118 | $this->assertTrue(compare_directories($this->getSourceDir(), $this->getTargetDir()));
119 | }
120 |
121 | public
122 | function testRsyncWithSingleExclude()
123 | {
124 | $rsync = new Rsync();
125 | $rsync->setExclude(array('exclude1'));
126 |
127 | $expected = "/usr/bin/rsync -La --exclude 'exclude1' /origin /target";
128 | $actual = $rsync->getCommand('/origin', '/target')->getCommand();
129 |
130 | $this->assertEquals($expected, $actual);
131 | }
132 |
133 | public
134 | function testRsyncWithMultipleExcludes()
135 | {
136 | $rsync = new Rsync();
137 | $rsync->setExclude(array('exclude1', 'exclude2', 'exclude3'));
138 |
139 | $expected = "/usr/bin/rsync -La --exclude 'exclude1' --exclude 'exclude2' --exclude 'exclude3' /origin /target";
140 | $actual = $rsync->getCommand('/origin', '/target')->getCommand();
141 |
142 | $this->assertEquals($expected, $actual);
143 | }
144 |
145 | public
146 | function testRsyncWithExcludeFrom()
147 | {
148 | $rsync = new Rsync();
149 | $rsync->setExcludeFrom('rsync_exclude.txt');
150 |
151 | $expected = "/usr/bin/rsync -La --exclude-from 'rsync_exclude.txt' /origin /target";
152 | $actual = $rsync->getCommand('/origin', '/target')->getCommand();
153 |
154 | $this->assertEquals($expected, $actual);
155 | }
156 |
157 | public
158 | function testRsyncWithTimes()
159 | {
160 | $rsync = new Rsync();
161 | $rsync->setTimes(true);
162 |
163 | $expected = "/usr/bin/rsync -La --times /origin /target";
164 | $actual = $rsync->getCommand('/origin', '/target')->getCommand();
165 |
166 | $this->assertEquals($expected, $actual);
167 | }
168 |
169 | public
170 | function testRsyncWithCompression()
171 | {
172 | $rsync = new Rsync();
173 | $rsync->setCompression(true);
174 |
175 | $expected = "/usr/bin/rsync -Lza /origin /target";
176 | $actual = $rsync->getCommand('/origin', '/target')->getCommand();
177 |
178 | $this->assertEquals($expected, $actual);
179 | }
180 |
181 | public
182 | function testRsyncWithOptionalParametersArray()
183 | {
184 | $rsync = new Rsync();
185 | $rsync->setOptionalParameters(array('z', 'p'));
186 |
187 | $expected = "/usr/bin/rsync -Lzpa /origin /target";
188 | $actual = $rsync->getCommand('/origin', '/target')->getCommand();
189 |
190 | $this->assertEquals($expected, $actual);
191 | }
192 |
193 | public
194 | function testRsyncWithOptionalParametersString()
195 | {
196 | $rsync = new Rsync();
197 | $rsync->setOptionalParameters('zp');
198 |
199 | $expected = "/usr/bin/rsync -Lzpa /origin /target";
200 | $actual = $rsync->getCommand('/origin', '/target')->getCommand();
201 |
202 | $this->assertEquals($expected, $actual);
203 | }
204 |
205 | public
206 | function testRsyncWithInfo()
207 | {
208 | $rsync = new Rsync();
209 | $rsync->setInfo('all0');
210 |
211 | $expected = "/usr/bin/rsync -La --info 'all0' /origin /target";
212 | $actual = $rsync->getCommand('/origin', '/target')->getCommand();
213 |
214 | $this->assertEquals($expected, $actual);
215 | }
216 |
217 | public
218 | function testRsyncWithCompareDest()
219 | {
220 | $rsync = new Rsync();
221 | $rsync->setCompareDest('/Path/To/File');
222 |
223 | $expected = "/usr/bin/rsync -La --compare-dest '/Path/To/File' /origin /target";
224 | $actual = $rsync->getCommand('/origin', '/target')->getCommand();
225 |
226 | $this->assertEquals($expected, $actual);
227 | }
228 |
229 | public
230 | function testRsyncWithRemoveSourceFile()
231 | {
232 | $rsync = new Rsync();
233 | $rsync->setRemoveSource(true);
234 |
235 | $expected = "/usr/bin/rsync -La --remove-source-files /origin /target";
236 | $actual = $rsync->getCommand('/origin', '/target')->getCommand();
237 |
238 | $this->assertEquals($expected, $actual);
239 | }
240 |
241 | public
242 | function testRsyncWithPruneEmptyDIrs()
243 | {
244 | $rsync = new Rsync();
245 | $rsync->setPruneEmptyDirs(true);
246 |
247 | $expected = "/usr/bin/rsync -La --prune-empty-dirs /origin /target";
248 | $actual = $rsync->getCommand('/origin', '/target')->getCommand();
249 |
250 | $this->assertEquals($expected, $actual);
251 | }
252 |
253 | public
254 | function getTargetDir()
255 | {
256 | return self::$targetDir;
257 | }
258 |
259 | public
260 | function getSourceDir()
261 | {
262 | return self::$sourceDir;
263 | }
264 | }
265 |
--------------------------------------------------------------------------------
/tests/AFM/Rsync/Tests/SSHTest.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read
9 | * the LICENSE file that was distributed with this source code.
10 | */
11 |
12 | namespace AFM\Rsync\Tests;
13 |
14 | use AFM\Rsync\SSH;
15 |
16 | class SSHTest extends \PHPUnit_Framework_TestCase
17 | {
18 | public function testValidConfiguration()
19 | {
20 | $fakePrivateKey = __DIR__.'/fake_key';
21 |
22 | touch($fakePrivateKey);
23 |
24 | new SSH(array('port' => 1443, 'private_key' => $fakePrivateKey));
25 |
26 | $this->assertTrue(true);
27 |
28 | unlink($fakePrivateKey);
29 | }
30 |
31 | /**
32 | * @expectedException \InvalidArgumentException
33 | */
34 | public function testInvalidPrivateKey()
35 | {
36 | new SSH(array('private_key' => '/cant/read!'));
37 | }
38 |
39 | /**
40 | * @expectedException \InvalidArgumentException
41 | */
42 | public function testInvalidPortNumber()
43 | {
44 | new SSH(array('port' => 'not_a_number'));
45 | }
46 |
47 | public function testGetConnectionString()
48 | {
49 | $ssh = new SSH(array('username' => 'test', 'host' => 'test.com'));
50 |
51 | $actual = $ssh->getCommand();
52 | $expected = "ssh test@test.com";
53 |
54 | $this->assertEquals($expected, $actual);
55 | }
56 |
57 | public function testGetConnectionNonStandardPort()
58 | {
59 | $ssh = new SSH(array('username' => 'test', 'host' => 'test.com', 'port' => 231));
60 |
61 | $actual = $ssh->getCommand();
62 | $expected = "ssh -p '231' test@test.com";
63 |
64 | $this->assertEquals($expected, $actual);
65 | }
66 |
67 | public function testGetConnectionWithPrivateKey()
68 | {
69 | $privateKey = "./key";
70 | $privateKeyWithSpaces = "./key key";
71 |
72 | touch($privateKey);
73 | touch($privateKeyWithSpaces);
74 |
75 | $ssh = new SSH(array('username' => 'test', 'host' => 'test.com', 'private_key' => $privateKey));
76 |
77 | $actual = $ssh->getCommand();
78 | $expected = "ssh -i '".$privateKey."' test@test.com";
79 |
80 | $this->assertEquals($expected, $actual);
81 |
82 | $ssh->setPrivateKey($privateKeyWithSpaces);
83 |
84 | $actual = $ssh->getCommand();
85 | $expected = "ssh -i '".$privateKeyWithSpaces."' test@test.com";
86 |
87 | $this->assertEquals($expected, $actual);
88 |
89 | unlink($privateKey);
90 | unlink($privateKeyWithSpaces);
91 | }
92 |
93 | public function testGetHostConnection()
94 | {
95 | $ssh = new SSH(array('username' => 'test', 'host' => 'test.com'));
96 |
97 | $actual = $ssh->getHostConnection();
98 | $expected = "test@test.com";
99 |
100 | $this->assertEquals($expected, $actual);
101 | }
102 |
103 | public function testGetConnectionOptions()
104 | {
105 | $ssh = new SSH(array('username' => 'test', 'host' => 'test.com', 'port' => 231, 'private_key' => '/dev/null'));
106 |
107 | $actual = $ssh->getConnectionOptions();
108 | $expected = "ssh -p '231' -i '/dev/null'";
109 |
110 | $this->assertEquals($expected, $actual);
111 | }
112 |
113 | /**
114 | * @expectedException \InvalidArgumentException
115 | */
116 | public function testGetConnectionNoUsername()
117 | {
118 | $ssh = new SSH;
119 |
120 | $ssh->getCommand();
121 | }
122 |
123 | /**
124 | * @expectedException \InvalidArgumentException
125 | */
126 | public function testGetConnectionNoHost()
127 | {
128 | $ssh = new SSH(array('username' => 'test'));
129 |
130 | $ssh->getCommand();
131 | }
132 |
133 | public function testSetExecutable()
134 | {
135 | $ssh = new SSH(
136 | array('username' => 'test', 'host' => 'test.com', 'port' => 231, 'executable' => 'c:/cygwin/bin/ssh.exe')
137 | );
138 |
139 | $actual = $ssh->getConnectionOptions();
140 | $expected = "c:/cygwin/bin/ssh.exe -p '231'";
141 |
142 | $this->assertEquals($expected, $actual);
143 | }
144 | }
145 |
146 |
--------------------------------------------------------------------------------
/tests/AFM/Rsync/Tests/dir1/file1:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/albertofem/rsync-lib/a4a1f1f26cb7309ac87a299a62aac04b77af0429/tests/AFM/Rsync/Tests/dir1/file1
--------------------------------------------------------------------------------
/tests/AFM/Rsync/Tests/dir1/file2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/albertofem/rsync-lib/a4a1f1f26cb7309ac87a299a62aac04b77af0429/tests/AFM/Rsync/Tests/dir1/file2
--------------------------------------------------------------------------------
/tests/bootstrap.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read
9 | * the LICENSE file that was distributed with this source code.
10 | */
11 |
12 | $loader = require __DIR__.'/../vendor/autoload.php';
13 | $loader->add('AFM\Rsync\Tests', __DIR__);
14 |
15 | /**
16 | * http://www.php.net/manual/en/function.rmdir.php#108113
17 | */
18 | function rrmdir($dir)
19 | {
20 | foreach (glob($dir.'/*') as $file) {
21 | if (is_dir($file)) {
22 | rrmdir($file);
23 | } else {
24 | unlink($file);
25 | }
26 | }
27 |
28 | rmdir($dir);
29 | }
30 |
31 | function compare_directories($dir1, $dir2)
32 | {
33 | $output = shell_exec("diff --brief ".$dir1." ".$dir2." 2>&1");
34 |
35 | if (strlen($output) > 0) {
36 | return false;
37 | }
38 |
39 | return true;
40 | }
--------------------------------------------------------------------------------