Completed
Push — master ( 86094f...5707a0 )
by Sebastian
03:08
created

AmazonS3v3::sync()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 18
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 3

Importance

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