1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Freyo\Flysystem\QcloudCOSv3; |
4
|
|
|
|
5
|
|
|
use Freyo\Flysystem\QcloudCOSv3\Client\Conf; |
6
|
|
|
use Freyo\Flysystem\QcloudCOSv3\Client\Cosapi; |
7
|
|
|
use Freyo\Flysystem\QcloudCOSv3\Exceptions\RuntimeException; |
8
|
|
|
use League\Flysystem\Adapter\AbstractAdapter; |
9
|
|
|
use League\Flysystem\AdapterInterface; |
10
|
|
|
use League\Flysystem\Config; |
11
|
|
|
use League\Flysystem\Util; |
12
|
|
|
|
13
|
|
|
/** |
14
|
|
|
* Class Adapter. |
15
|
|
|
*/ |
16
|
|
|
class Adapter extends AbstractAdapter |
17
|
|
|
{ |
18
|
|
|
/** |
19
|
|
|
* @var |
20
|
|
|
*/ |
21
|
|
|
protected $bucket; |
22
|
|
|
|
23
|
|
|
/** |
24
|
|
|
* Adapter constructor. |
25
|
|
|
* |
26
|
|
|
* @param $config |
27
|
|
|
*/ |
28
|
|
|
public function __construct($config) |
29
|
|
|
{ |
30
|
|
|
Conf::setAppId($config['app_id']); |
31
|
|
|
Conf::setSecretId($config['secret_id']); |
32
|
|
|
Conf::setSecretKey($config['secret_key']); |
33
|
|
|
|
34
|
|
|
$this->bucket = $config['bucket']; |
35
|
|
|
|
36
|
|
|
$this->setPathPrefix($config['protocol'].'://'.$config['domain'].'/'); |
37
|
|
|
|
38
|
|
|
Cosapi::setTimeout($config['timeout']); |
39
|
|
|
} |
40
|
|
|
|
41
|
|
|
/** |
42
|
|
|
* @return mixed |
43
|
|
|
*/ |
44
|
|
|
public function getBucket() |
45
|
|
|
{ |
46
|
|
|
return $this->bucket; |
47
|
|
|
} |
48
|
|
|
|
49
|
|
|
/** |
50
|
|
|
* @param $path |
51
|
|
|
* |
52
|
|
|
* @return string |
53
|
|
|
*/ |
54
|
|
|
public function getUrl($path) |
55
|
|
|
{ |
56
|
|
|
return $this->applyPathPrefix($path); |
57
|
|
|
} |
58
|
|
|
|
59
|
|
|
/** |
60
|
|
|
* @param string $path |
61
|
|
|
* @param string $contents |
62
|
|
|
* @param Config $config |
63
|
|
|
* |
64
|
|
|
* @throws RuntimeException |
65
|
|
|
* |
66
|
|
|
* @return bool |
67
|
|
|
*/ |
68
|
|
View Code Duplication |
public function write($path, $contents, Config $config) |
|
|
|
|
69
|
|
|
{ |
70
|
|
|
$tmpfname = $this->writeTempFile($contents); |
71
|
|
|
|
72
|
|
|
try { |
73
|
|
|
$response = $this->normalizeResponse( |
74
|
|
|
Cosapi::upload($this->getBucket(), $tmpfname, $path) |
|
|
|
|
75
|
|
|
); |
76
|
|
|
|
77
|
|
|
$this->deleteTempFile($tmpfname); |
78
|
|
|
|
79
|
|
|
$this->setContentType($path, $contents); |
80
|
|
|
} catch (RuntimeException $exception) { |
81
|
|
|
$this->deleteTempFile($tmpfname); |
82
|
|
|
|
83
|
|
|
if ($exception->getCode() == -4018) { |
84
|
|
|
return $this->getMetadata($path); |
|
|
|
|
85
|
|
|
} |
86
|
|
|
|
87
|
|
|
throw $exception; |
88
|
|
|
} |
89
|
|
|
|
90
|
|
|
return $response; |
91
|
|
|
} |
92
|
|
|
|
93
|
|
|
/** |
94
|
|
|
* @param string $path |
95
|
|
|
* @param resource $resource |
96
|
|
|
* @param Config $config |
97
|
|
|
* |
98
|
|
|
* @return bool |
99
|
|
|
*/ |
100
|
|
|
public function writeStream($path, $resource, Config $config) |
101
|
|
|
{ |
102
|
|
|
$uri = stream_get_meta_data($resource)['uri']; |
103
|
|
|
|
104
|
|
|
try { |
105
|
|
|
$response = $this->normalizeResponse( |
106
|
|
|
Cosapi::upload($this->getBucket(), $uri, $path) |
107
|
|
|
); |
108
|
|
|
|
109
|
|
|
$this->setContentType($path, stream_get_contents($resource)); |
110
|
|
|
} catch (RuntimeException $exception) { |
111
|
|
|
if ($exception->getCode() == -4018) { |
112
|
|
|
return $this->getMetadata($path); |
|
|
|
|
113
|
|
|
} |
114
|
|
|
|
115
|
|
|
throw $exception; |
116
|
|
|
} |
117
|
|
|
|
118
|
|
|
return $response; |
119
|
|
|
} |
120
|
|
|
|
121
|
|
|
/** |
122
|
|
|
* @param string $path |
123
|
|
|
* @param string $contents |
124
|
|
|
* @param Config $config |
125
|
|
|
* |
126
|
|
|
* @return bool |
127
|
|
|
*/ |
128
|
|
View Code Duplication |
public function update($path, $contents, Config $config) |
|
|
|
|
129
|
|
|
{ |
130
|
|
|
$tmpfname = $this->writeTempFile($contents); |
131
|
|
|
|
132
|
|
|
try { |
133
|
|
|
$response = $this->normalizeResponse( |
134
|
|
|
Cosapi::upload($this->getBucket(), $tmpfname, $path, null, null, 0) |
|
|
|
|
135
|
|
|
); |
136
|
|
|
|
137
|
|
|
$this->deleteTempFile($tmpfname); |
138
|
|
|
|
139
|
|
|
$this->setContentType($path, $contents); |
140
|
|
|
} catch (RuntimeException $exception) { |
141
|
|
|
$this->deleteTempFile($tmpfname); |
142
|
|
|
|
143
|
|
|
if ($exception->getCode() == -4018) { |
144
|
|
|
return $this->getMetadata($path); |
|
|
|
|
145
|
|
|
} |
146
|
|
|
|
147
|
|
|
throw $exception; |
148
|
|
|
} |
149
|
|
|
|
150
|
|
|
return $response; |
151
|
|
|
} |
152
|
|
|
|
153
|
|
|
/** |
154
|
|
|
* @param string $path |
155
|
|
|
* @param resource $resource |
156
|
|
|
* @param Config $config |
157
|
|
|
* |
158
|
|
|
* @return bool |
159
|
|
|
*/ |
160
|
|
|
public function updateStream($path, $resource, Config $config) |
161
|
|
|
{ |
162
|
|
|
$uri = stream_get_meta_data($resource)['uri']; |
163
|
|
|
|
164
|
|
|
try { |
165
|
|
|
$response = $this->normalizeResponse( |
166
|
|
|
Cosapi::upload($this->getBucket(), $uri, $path, $path, null, null, 0) |
|
|
|
|
167
|
|
|
); |
168
|
|
|
|
169
|
|
|
$this->setContentType($path, stream_get_contents($resource)); |
170
|
|
|
} catch (RuntimeException $exception) { |
171
|
|
|
if ($exception->getCode() == -4018) { |
172
|
|
|
return $this->getMetadata($path); |
|
|
|
|
173
|
|
|
} |
174
|
|
|
|
175
|
|
|
throw $exception; |
176
|
|
|
} |
177
|
|
|
|
178
|
|
|
return $response; |
179
|
|
|
} |
180
|
|
|
|
181
|
|
|
/** |
182
|
|
|
* @param string $path |
183
|
|
|
* @param string $newpath |
184
|
|
|
* |
185
|
|
|
* @return bool |
186
|
|
|
*/ |
187
|
|
|
public function rename($path, $newpath) |
188
|
|
|
{ |
189
|
|
|
return $this->normalizeResponse( |
190
|
|
|
Cosapi::move($this->getBucket(), $path, $newpath, 1) |
191
|
|
|
); |
192
|
|
|
} |
193
|
|
|
|
194
|
|
|
/** |
195
|
|
|
* @param string $path |
196
|
|
|
* @param string $newpath |
197
|
|
|
* |
198
|
|
|
* @return bool |
199
|
|
|
*/ |
200
|
|
|
public function copy($path, $newpath) |
201
|
|
|
{ |
202
|
|
|
$resource = $this->read($path); |
203
|
|
|
|
204
|
|
|
return $this->update($newpath, $resource['contents'], new Config()); |
205
|
|
|
} |
206
|
|
|
|
207
|
|
|
/** |
208
|
|
|
* @param string $path |
209
|
|
|
* |
210
|
|
|
* @return bool |
211
|
|
|
*/ |
212
|
|
|
public function delete($path) |
213
|
|
|
{ |
214
|
|
|
return $this->normalizeResponse( |
215
|
|
|
Cosapi::delFile($this->getBucket(), $path) |
216
|
|
|
); |
217
|
|
|
} |
218
|
|
|
|
219
|
|
|
/** |
220
|
|
|
* @param string $dirname |
221
|
|
|
* |
222
|
|
|
* @return bool |
223
|
|
|
*/ |
224
|
|
|
public function deleteDir($dirname) |
225
|
|
|
{ |
226
|
|
|
return $this->normalizeResponse( |
227
|
|
|
Cosapi::delFolder($this->getBucket(), $dirname) |
228
|
|
|
); |
229
|
|
|
} |
230
|
|
|
|
231
|
|
|
/** |
232
|
|
|
* @param string $dirname |
233
|
|
|
* @param Config $config |
234
|
|
|
* |
235
|
|
|
* @return bool |
236
|
|
|
*/ |
237
|
|
|
public function createDir($dirname, Config $config) |
238
|
|
|
{ |
239
|
|
|
return $this->normalizeResponse( |
240
|
|
|
Cosapi::createFolder($this->getBucket(), $dirname) |
241
|
|
|
); |
242
|
|
|
} |
243
|
|
|
|
244
|
|
|
/** |
245
|
|
|
* @param string $path |
246
|
|
|
* @param string $visibility |
247
|
|
|
* |
248
|
|
|
* @return mixed |
249
|
|
|
*/ |
250
|
|
|
public function setVisibility($path, $visibility) |
251
|
|
|
{ |
252
|
|
|
$visibility = $visibility === AdapterInterface::VISIBILITY_PUBLIC ? 'eWPrivateRPublic' : 'eWRPrivate'; |
253
|
|
|
|
254
|
|
|
return $this->normalizeResponse( |
255
|
|
|
Cosapi::update($this->getBucket(), $path, null, $visibility) |
256
|
|
|
); |
257
|
|
|
} |
258
|
|
|
|
259
|
|
|
/** |
260
|
|
|
* @param string $path |
261
|
|
|
* |
262
|
|
|
* @return bool |
263
|
|
|
*/ |
264
|
|
|
public function has($path) |
265
|
|
|
{ |
266
|
|
|
try { |
267
|
|
|
$this->getMetadata($path); |
268
|
|
|
} catch (RuntimeException $exception) { |
269
|
|
|
return false; |
270
|
|
|
} |
271
|
|
|
|
272
|
|
|
return true; |
273
|
|
|
} |
274
|
|
|
|
275
|
|
|
/** |
276
|
|
|
* @param string $path |
277
|
|
|
* |
278
|
|
|
* @return array |
279
|
|
|
*/ |
280
|
|
|
public function read($path) |
281
|
|
|
{ |
282
|
|
|
return ['contents' => file_get_contents($this->getUrl($path))]; |
283
|
|
|
} |
284
|
|
|
|
285
|
|
|
/** |
286
|
|
|
* @param string $path |
287
|
|
|
* |
288
|
|
|
* @return array |
289
|
|
|
*/ |
290
|
|
|
public function readStream($path) |
291
|
|
|
{ |
292
|
|
|
return ['stream' => fopen($this->getUrl($path), 'r')]; |
293
|
|
|
} |
294
|
|
|
|
295
|
|
|
/** |
296
|
|
|
* @param string $directory |
297
|
|
|
* @param bool $recursive |
298
|
|
|
* |
299
|
|
|
* @return bool |
300
|
|
|
*/ |
301
|
|
|
public function listContents($directory = '', $recursive = false) |
302
|
|
|
{ |
303
|
|
|
return $this->normalizeResponse( |
304
|
|
|
Cosapi::listFolder($this->getBucket(), $directory) |
305
|
|
|
); |
306
|
|
|
} |
307
|
|
|
|
308
|
|
|
/** |
309
|
|
|
* @param string $path |
310
|
|
|
* |
311
|
|
|
* @return bool |
312
|
|
|
*/ |
313
|
|
|
public function getMetadata($path) |
314
|
|
|
{ |
315
|
|
|
return $this->normalizeResponse( |
316
|
|
|
Cosapi::stat($this->getBucket(), $path) |
317
|
|
|
); |
318
|
|
|
} |
319
|
|
|
|
320
|
|
|
/** |
321
|
|
|
* @param string $path |
322
|
|
|
* |
323
|
|
|
* @return array |
324
|
|
|
*/ |
325
|
|
|
public function getSize($path) |
326
|
|
|
{ |
327
|
|
|
$stat = $this->getMetadata($path); |
328
|
|
|
|
329
|
|
|
if ($stat) { |
330
|
|
|
return ['size' => $stat['filesize']]; |
331
|
|
|
} |
332
|
|
|
|
333
|
|
|
return ['size' => 0]; |
334
|
|
|
} |
335
|
|
|
|
336
|
|
|
/** |
337
|
|
|
* @param string $path |
338
|
|
|
* |
339
|
|
|
* @return array |
340
|
|
|
*/ |
341
|
|
|
public function getMimetype($path) |
342
|
|
|
{ |
343
|
|
|
$stat = $this->getMetadata($path); |
344
|
|
|
|
345
|
|
|
if ($stat && !empty($stat['custom_headers']) && !empty($stat['custom_headers']['Content-Type'])) { |
346
|
|
|
return ['mimetype' => $stat['custom_headers']['Content-Type']]; |
347
|
|
|
} |
348
|
|
|
|
349
|
|
|
return ['mimetype' => '']; |
350
|
|
|
} |
351
|
|
|
|
352
|
|
|
/** |
353
|
|
|
* @param string $path |
354
|
|
|
* |
355
|
|
|
* @return array |
356
|
|
|
*/ |
357
|
|
|
public function getTimestamp($path) |
358
|
|
|
{ |
359
|
|
|
$stat = $this->getMetadata($path); |
360
|
|
|
|
361
|
|
|
if ($stat) { |
362
|
|
|
return ['timestamp' => $stat['ctime']]; |
363
|
|
|
} |
364
|
|
|
|
365
|
|
|
return ['timestamp' => 0]; |
366
|
|
|
} |
367
|
|
|
|
368
|
|
|
/** |
369
|
|
|
* @param string $path |
370
|
|
|
* |
371
|
|
|
* @return array |
372
|
|
|
*/ |
373
|
|
|
public function getVisibility($path) |
374
|
|
|
{ |
375
|
|
|
$stat = $this->getMetadata($path); |
376
|
|
|
|
377
|
|
|
$visibility = AdapterInterface::VISIBILITY_PRIVATE; |
378
|
|
|
|
379
|
|
|
if ($stat && $stat['authority'] === 'eWPrivateRPublic') { |
380
|
|
|
$visibility = AdapterInterface::VISIBILITY_PUBLIC; |
381
|
|
|
} |
382
|
|
|
|
383
|
|
|
return ['visibility' => $visibility]; |
384
|
|
|
} |
385
|
|
|
|
386
|
|
|
/** |
387
|
|
|
* @param $content |
388
|
|
|
* |
389
|
|
|
* @return bool|string |
390
|
|
|
*/ |
391
|
|
|
private function writeTempFile($content) |
392
|
|
|
{ |
393
|
|
|
$tmpfname = tempnam('/tmp', 'dir'); |
394
|
|
|
|
395
|
|
|
chmod($tmpfname, 0777); |
396
|
|
|
|
397
|
|
|
file_put_contents($tmpfname, $content); |
398
|
|
|
|
399
|
|
|
return $tmpfname; |
400
|
|
|
} |
401
|
|
|
|
402
|
|
|
/** |
403
|
|
|
* @param $tmpfname |
404
|
|
|
*/ |
405
|
|
|
private function deleteTempFile($tmpfname) |
406
|
|
|
{ |
407
|
|
|
return unlink($tmpfname); |
408
|
|
|
} |
409
|
|
|
|
410
|
|
|
/** |
411
|
|
|
* @param $path |
412
|
|
|
* @param $content |
413
|
|
|
* |
414
|
|
|
* @return bool |
415
|
|
|
*/ |
416
|
|
|
protected function setContentType($path, $content) |
417
|
|
|
{ |
418
|
|
|
$custom_headers = [ |
419
|
|
|
'Content-Type' => Util::guessMimeType($path, $content), |
420
|
|
|
]; |
421
|
|
|
|
422
|
|
|
return $this->normalizeResponse( |
423
|
|
|
Cosapi::update($this->getBucket(), $path, null, null, $custom_headers) |
424
|
|
|
); |
425
|
|
|
} |
426
|
|
|
|
427
|
|
|
/** |
428
|
|
|
* @param $response |
429
|
|
|
* |
430
|
|
|
* @throws RuntimeException |
431
|
|
|
* |
432
|
|
|
* @return mixed |
433
|
|
|
*/ |
434
|
|
|
protected function normalizeResponse($response) |
435
|
|
|
{ |
436
|
|
|
$response = is_array($response) ? $response : json_decode($response, true); |
437
|
|
|
|
438
|
|
|
if ($response['code'] == 0) { |
439
|
|
|
return isset($response['data']) ? $response['data'] : true; |
440
|
|
|
} |
441
|
|
|
|
442
|
|
|
throw new RuntimeException($response['message'], $response['code']); |
443
|
|
|
} |
444
|
|
|
} |
445
|
|
|
|
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.