Completed
Pull Request — master (#140)
by Vitaly
04:25
created

AmazonS3v3::sync()   B

Complexity

Conditions 3
Paths 4

Size

Total Lines 25
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 1
Bugs 0 Features 0
Metric Value
dl 0
loc 25
ccs 0
cts 15
cp 0
rs 8.8571
c 1
b 0
f 0
cc 3
eloc 16
nc 4
nop 2
crap 12
1
<?php
2
namespace phpbu\App\Backup\Sync;
3
4
use Aws\S3\S3Client;
5
use Aws\S3\MultipartUploader;
6
use phpbu\App\Backup\Collector;
7
use phpbu\App\Result;
8
use phpbu\App\Backup\Target;
9
10
/**
11
 * Amazon S3 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 3.0.0
20
 */
21
class AmazonS3v3 extends AmazonS3
22
{
23
    use Clearable;
24
25
    /**
26
     * Amazon S3 client.
27
     *
28
     * @var S3Client;
29
     */
30
    protected $client;
31
32
    /**
33
     * Configure the sync.
34
     *
35
     * @see    \phpbu\App\Backup\Sync::setup()
36
     * @param  array $config
37
     * @throws \phpbu\App\Backup\Sync\Exception
38
     * @throws \phpbu\App\Exception
39
     */
40 9
    public function setup(array $config)
41
    {
42 9
        parent::setup($config);
43
44 4
        $this->setUpClearable($config);
45 4
    }
46
47
    /**
48
     * Execute the sync.
49
     *
50
     * @see    \phpbu\App\Backup\Sync::sync()
51
     * @param  \phpbu\App\Backup\Target $target
52
     * @param  \phpbu\App\Result        $result
53
     * @throws \phpbu\App\Backup\Sync\Exception
54
     */
55
    public function sync(Target $target, Result $result)
56
    {
57
        $this->client = new S3Client([
58
            'region'  => $this->region,
59
            'version' => '2006-03-01',
60
            'credentials' => [
61
                'key'    => $this->key,
62
                'secret' => $this->secret,
63
            ]
64
        ]);
65
66
        if (!$this->client->doesBucketExist($this->bucket)) {
67
            $result->debug('create s3 bucket');
68
            $this->createBucket($this->client);
69
        }
70
71
        try {
72
            $this->upload($target, $this->client);
73
        } catch (\Exception $e) {
74
            throw new Exception($e->getMessage(), null, $e);
75
        }
76
        // run remote cleanup
77
        $this->cleanup($target, $result);
78
        $result->debug('upload: done');
79
    }
80
81
    /**
82
     * Creates collector for Amazon S3
83
     *
84
     * @param \phpbu\App\Backup\Target $target
85
     * @return \phpbu\App\Backup\Collector
86
     */
87
    protected function createCollector(Target $target): Collector
88
    {
89
        return new \phpbu\App\Backup\Collector\AmazonS3v3($target, $this->client, $this->bucket, $this->path);
90
    }
91
92
    /**
93
     * Simulate the sync execution.
94
     *
95
     * @param \phpbu\App\Backup\Target $target
96
     * @param \phpbu\App\Result        $result
97
     */
98 1
    public function simulate(Target $target, Result $result)
99
    {
100 1
        parent::simulate($target, $result);
101
102 1
        $this->simulateRemoteCleanup($target, $result);
103 1
    }
104
105
    /**
106
     * Create a s3 bucket.
107
     *
108
     * @param \Aws\S3\S3Client $s3
109
     */
110
    private function createBucket(S3Client $s3)
111
    {
112
        $s3->createBucket([
113
            'ACL'                       => $this->acl,
114
            'Bucket'                    => $this->bucket,
115
            'CreateBucketConfiguration' => [
116
                'LocationConstraint' => $this->region,
117
            ]
118
        ]);
119
    }
120
121
    /**
122
     * Upload backup to Amazon S3 bucket.
123
     *
124
     * @param \phpbu\App\Backup\Target $target
125
     * @param \Aws\S3\S3Client         $s3
126
     */
127
    private function upload(Target $target, S3Client $s3)
128
    {
129
        if ($this->useMultiPartUpload($target)) {
130
            $this->uploadMultiPart($target, $s3);
131
        } else {
132
            $this->uploadStream($target, $s3);
133
        }
134
    }
135
136
    /**
137
     * Upload via stream wrapper.
138
     *
139
     * @param  \phpbu\App\Backup\Target $target
140
     * @param  \Aws\S3\S3Client         $s3
141
     * @throws \phpbu\App\Backup\Sync\Exception
142
     */
143
    private function uploadStream(Target $target, S3Client $s3)
144
    {
145
        $s3->registerStreamWrapper();
146
        $source = $this->getFileHandle($target->getPathname(), 'r');
147
        $stream = $this->getFileHandle('s3://' . $this->bucket . '/' . $this->getUploadPath($target), 'w');
148
        while(!feof($source)) {
149
            fwrite($stream, fread($source, 4096));
150
        }
151
        fclose($stream);
152
    }
153
154
    /**
155
     * Upload via multi part.
156
     *
157
     * @param \phpbu\App\Backup\Target $target
158
     * @param \Aws\S3\S3Client         $s3
159
     * @param \Aws\Exception\MultipartUploadException
160
     */
161
    private function uploadMultiPart(Target $target, S3Client $s3)
162
    {
163
        $uploader = new MultipartUploader($s3, $target->getPathname(), [
164
            'bucket' => $this->bucket,
165
            'key'    => $this->getUploadPath($target),
166
        ]);
167
        $uploader->upload();
168
    }
169
170
    /**
171
     * Open stream and validate it.
172
     *
173
     * @param  string $path
174
     * @param  string $mode
175
     * @return resource
176
     * @throws \phpbu\App\Backup\Sync\Exception
177
     */
178
    private function getFileHandle($path, $mode)
179
    {
180
        $handle = fopen($path, $mode);
181
        if (!is_resource($handle)) {
182
            throw new Exception('fopen failed: could not open stream ' . $path);
183
        }
184
        return $handle;
185
    }
186
187
    /**
188
     * Get the s3 upload path
189
     *
190
     * @param \phpbu\App\Backup\Target $target
191
     * @return string
192
     */
193 2
    public function getUploadPath(Target $target)
194
    {
195
        // remove leading slash
196 2
        return (substr($this->path, 0, 1) == '/' ? substr($this->path, 1) : $this->path)
197 2
               . (substr($this->path, -1, 1) == '/' ? '' : '/')
198 2
               . $target->getFilename();
199
    }
200
}
201