Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
81.20% |
216 / 266 |
|
60.61% |
20 / 33 |
CRAP | |
0.00% |
0 / 1 |
Azure | |
81.20% |
216 / 266 |
|
60.61% |
20 / 33 |
226.12 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 | |||
create | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
initClient | |
100.00% |
17 / 17 |
|
100.00% |
1 / 1 |
3 | |||
setClient | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
getClient | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
hasClient | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setAuth | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
getAuth | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
hasAuth | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
mkdir | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
rmdir | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
listDirs | |
83.33% |
20 / 24 |
|
0.00% |
0 / 1 |
17.19 | |||
listFiles | |
83.33% |
20 / 24 |
|
0.00% |
0 / 1 |
17.19 | |||
putFile | |
76.47% |
13 / 17 |
|
0.00% |
0 / 1 |
4.21 | |||
putFileContents | |
73.33% |
11 / 15 |
|
0.00% |
0 / 1 |
3.17 | |||
uploadFile | |
78.95% |
15 / 19 |
|
0.00% |
0 / 1 |
6.34 | |||
copyFile | |
72.22% |
13 / 18 |
|
0.00% |
0 / 1 |
10.74 | |||
copyFileToExternal | |
75.00% |
12 / 16 |
|
0.00% |
0 / 1 |
9.00 | |||
copyFileFromExternal | |
77.78% |
14 / 18 |
|
0.00% |
0 / 1 |
6.40 | |||
moveFileToExternal | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
moveFileFromExternal | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
3 | |||
renameFile | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
replaceFileContents | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
deleteFile | |
69.23% |
9 / 13 |
|
0.00% |
0 / 1 |
5.73 | |||
fetchFile | |
61.54% |
8 / 13 |
|
0.00% |
0 / 1 |
8.05 | |||
fetchFileInfo | |
75.00% |
12 / 16 |
|
0.00% |
0 / 1 |
4.25 | |||
fileExists | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
2 | |||
isDir | |
71.43% |
5 / 7 |
|
0.00% |
0 / 1 |
5.58 | |||
isFile | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
3 | |||
getFileSize | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
getFileType | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
3 | |||
getFileMTime | |
66.67% |
4 / 6 |
|
0.00% |
0 / 1 |
5.93 | |||
md5File | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
3 |
1 | <?php |
2 | /** |
3 | * Pop PHP Framework (https://www.popphp.org/) |
4 | * |
5 | * @link https://github.com/popphp/popphp-framework |
6 | * @author Nick Sagona, III <dev@noladev.com> |
7 | * @copyright Copyright (c) 2009-2025 NOLA Interactive, LLC. |
8 | * @license https://www.popphp.org/license New BSD License |
9 | */ |
10 | |
11 | /** |
12 | * @namespace |
13 | */ |
14 | namespace Pop\Storage\Adapter; |
15 | |
16 | use Pop\Storage\Adapter\Azure\Auth; |
17 | use Pop\Http\Client; |
18 | use Pop\Http\Client\Request; |
19 | use Pop\Utils\File; |
20 | |
21 | /** |
22 | * Storage adapter Azure class |
23 | * |
24 | * @category Pop |
25 | * @package Pop\Storage |
26 | * @author Nick Sagona, III <dev@noladev.com> |
27 | * @copyright Copyright (c) 2009-2025 NOLA Interactive, LLC. |
28 | * @license https://www.popphp.org/license New BSD License |
29 | * @version 2.1.0 |
30 | */ |
31 | class Azure extends AbstractAdapter |
32 | { |
33 | |
34 | /** |
35 | * HTTP client |
36 | * @var ?Client |
37 | */ |
38 | protected ?Client $client = null; |
39 | |
40 | /** |
41 | * Azure auth object |
42 | * @var ?Auth |
43 | */ |
44 | protected ?Auth $auth = null; |
45 | |
46 | /** |
47 | * Constructor |
48 | * |
49 | * @param string $location |
50 | * @param Auth $auth |
51 | */ |
52 | public function __construct(string $location, Auth $auth) |
53 | { |
54 | parent::__construct($location); |
55 | $this->setAuth($auth); |
56 | $this->initClient(); |
57 | } |
58 | |
59 | /** |
60 | * Create Azure client |
61 | * |
62 | * @param string $accountName |
63 | * @param string $accountKey |
64 | * @return Azure |
65 | */ |
66 | public static function create(string $accountName, string $accountKey): Azure |
67 | { |
68 | return new self($accountName, new Azure\Auth($accountName, $accountKey)); |
69 | } |
70 | |
71 | /** |
72 | * Initialize client |
73 | * |
74 | * @param string $method |
75 | * @param array $headers |
76 | * @param bool $auto |
77 | * @return Azure |
78 | */ |
79 | public function initClient(string $method = 'GET', array $headers = [], bool $auto = true): Azure |
80 | { |
81 | $request = new Request('/', $method); |
82 | $request->addHeader('Date', gmdate('D, d M Y H:i:s T')) |
83 | ->addHeader('Host', $this->auth->getAccountName() . '.blob.core.windows.net') |
84 | ->addHeader('Content-Type', Client\Request::URLENCODED) |
85 | ->addHeader('User-Agent', 'pop-storage/2.1.0 (PHP ' . PHP_VERSION . ')/' . PHP_OS) |
86 | ->addHeader('x-ms-client-request-id', uniqid()) |
87 | ->addHeader('x-ms-version', '2025-01-05'); |
88 | |
89 | if (!empty($headers)) { |
90 | foreach ($headers as $header => $value) { |
91 | $request->addHeader($header, $value); |
92 | } |
93 | } |
94 | |
95 | $this->setClient(new Client( |
96 | $request, [ |
97 | 'base_uri' => $this->auth->getBaseUri(), |
98 | 'auto' => $auto |
99 | ] |
100 | )); |
101 | |
102 | return $this; |
103 | } |
104 | |
105 | /** |
106 | * Set client |
107 | * |
108 | * @param Client $client |
109 | * @return Azure |
110 | */ |
111 | public function setClient(Client $client): Azure |
112 | { |
113 | $this->client = $client; |
114 | return $this; |
115 | } |
116 | |
117 | /** |
118 | * Get client |
119 | * |
120 | * @return ?Client |
121 | */ |
122 | public function getClient(): ?Client |
123 | { |
124 | return $this->client; |
125 | } |
126 | |
127 | /** |
128 | * Has client |
129 | * |
130 | * @return bool |
131 | */ |
132 | public function hasClient(): bool |
133 | { |
134 | return ($this->client !== null); |
135 | } |
136 | |
137 | /** |
138 | * Set auth |
139 | * |
140 | * @param Auth $auth |
141 | * @return Azure |
142 | */ |
143 | public function setAuth(Auth $auth): Azure |
144 | { |
145 | $this->auth = $auth; |
146 | return $this; |
147 | } |
148 | |
149 | /** |
150 | * Get auth |
151 | * |
152 | * @return ?Auth |
153 | */ |
154 | public function getAuth(): ?Auth |
155 | { |
156 | return $this->auth; |
157 | } |
158 | |
159 | /** |
160 | * Has auth |
161 | * |
162 | * @return bool |
163 | */ |
164 | public function hasAuth(): bool |
165 | { |
166 | return ($this->auth !== null); |
167 | } |
168 | |
169 | /** |
170 | * Make directory |
171 | * |
172 | * @param string $directory |
173 | * @return void |
174 | */ |
175 | public function mkdir(string $directory): void |
176 | { |
177 | /** |
178 | * Azure storage doesn't allow the creation of empty "directories" (prefixes.) |
179 | * A new "directory" (prefix) is automatically created with an uploaded file that utilizes a prefix |
180 | */ |
181 | } |
182 | |
183 | /** |
184 | * Remove a directory |
185 | * |
186 | * @param string $directory |
187 | * @return void |
188 | */ |
189 | public function rmdir(string $directory): void |
190 | { |
191 | /** |
192 | * Azure storage doesn't allow the direct removal of "directories" (prefixes.) |
193 | * A "directory" (prefix) is automatically removed when the last file that utilizes the prefix is deleted. |
194 | */ |
195 | } |
196 | |
197 | /** |
198 | * List directories |
199 | * |
200 | * @param ?string $search |
201 | * @return array |
202 | */ |
203 | public function listDirs(?string $search = null): array |
204 | { |
205 | $dirs = []; |
206 | |
207 | $uri = '/' . $this->baseDirectory; |
208 | |
209 | $params = ['restype' => 'container', 'comp' => 'list']; |
210 | |
211 | if ($this->baseDirectory !== $this->directory) { |
212 | $directory = str_replace($this->baseDirectory, '', $this->directory); |
213 | if (str_ends_with($directory, '/')) { |
214 | $directory = substr($directory, 0, -1); |
215 | } |
216 | $params['prefix'] = $directory; |
217 | } |
218 | |
219 | $this->initClient(); |
220 | $this->client->getRequest()->setQuery($params); |
221 | $this->client->getRequest()->setUri($uri); |
222 | $this->auth->signRequest($this->client->getRequest()); |
223 | |
224 | $response = $this->client->send(); |
225 | |
226 | if (is_array($response) && !empty($response['Blobs']) && !empty($response['Blobs']['Blob'])) { |
227 | $blobs = (!isset($response['Blobs']['Blob'][0])) ? [$response['Blobs']['Blob']] : $response['Blobs']['Blob']; |
228 | foreach ($blobs as $blob) { |
229 | if (isset($blob['Properties']) && isset($blob['Properties']['ResourceType']) && |
230 | ($blob['Properties']['ResourceType'] == 'directory')) { |
231 | if ((!isset($params['prefix']) && !str_contains($blob['Name'], '/')) || |
232 | (isset($params['prefix']) && str_contains($blob['Name'], '/'))) { |
233 | $dirs[] = $blob['Name']; |
234 | } |
235 | } |
236 | } |
237 | } |
238 | |
239 | if ($search !== null) { |
240 | $dirs = $this->searchFilter($dirs, $search); |
241 | } |
242 | |
243 | return $dirs; |
244 | } |
245 | |
246 | /** |
247 | * List files |
248 | * |
249 | * @param ?string $search |
250 | * @return array |
251 | */ |
252 | public function listFiles(?string $search = null): array |
253 | { |
254 | $files = []; |
255 | |
256 | $uri = '/' . $this->baseDirectory; |
257 | |
258 | $params = ['restype' => 'container', 'comp' => 'list']; |
259 | |
260 | if ($this->baseDirectory !== $this->directory) { |
261 | $directory = str_replace($this->baseDirectory, '', $this->directory); |
262 | if (str_ends_with($directory, '/')) { |
263 | $directory = substr($directory, 0, -1); |
264 | } |
265 | $params['prefix'] = $directory; |
266 | } |
267 | |
268 | $this->initClient(); |
269 | $this->client->getRequest()->setQuery($params); |
270 | $this->client->getRequest()->setUri($uri); |
271 | $this->auth->signRequest($this->client->getRequest()); |
272 | |
273 | $response = $this->client->send(); |
274 | |
275 | if (is_array($response) && !empty($response['Blobs']) && !empty($response['Blobs']['Blob'])) { |
276 | $blobs = (!isset($response['Blobs']['Blob'][0])) ? [$response['Blobs']['Blob']] : $response['Blobs']['Blob']; |
277 | foreach ($blobs as $blob) { |
278 | if (isset($blob['Properties']) && isset($blob['Properties']['ResourceType']) && |
279 | ($blob['Properties']['ResourceType'] == 'file')) { |
280 | if ((!isset($params['prefix']) && !str_contains($blob['Name'], '/')) || |
281 | (isset($params['prefix']) && str_contains($blob['Name'], '/'))) { |
282 | $files[] = $blob['Name']; |
283 | } |
284 | } |
285 | } |
286 | } |
287 | |
288 | if ($search !== null) { |
289 | $files = $this->searchFilter($files, $search); |
290 | } |
291 | |
292 | return $files; |
293 | } |
294 | |
295 | /** |
296 | * Put file |
297 | * |
298 | * @param string $fileFrom |
299 | * @param bool $copy |
300 | * @throws Exception|Client\Handler\Exception|\Pop\Http\Exception|\Pop\Utils\Exception |
301 | * @return void |
302 | */ |
303 | public function putFile(string $fileFrom, bool $copy = true): void |
304 | { |
305 | if (file_exists($fileFrom)) { |
306 | $uri = '/' . $this->baseDirectory . '/' . basename($fileFrom); |
307 | if ($this->baseDirectory !== $this->directory) { |
308 | $directory = str_replace($this->baseDirectory, '', $this->directory); |
309 | if (str_ends_with($directory, '/')) { |
310 | $directory = substr($directory, 0, -1); |
311 | } |
312 | $uri = $directory . $uri; |
313 | } |
314 | |
315 | $fileContents = file_get_contents($fileFrom); |
316 | |
317 | $this->initClient('PUT', [ |
318 | 'content-length' => strlen($fileContents), |
319 | 'x-ms-blob-type' => 'BlockBlob', |
320 | 'x-ms-blob-content-type' => File::getFileMimeType($fileFrom) |
321 | ]); |
322 | $this->client->getRequest()->setUri($uri); |
323 | $this->client->getRequest()->setBody($fileContents); |
324 | $this->auth->signRequest($this->client->getRequest()); |
325 | $this->client->send(); |
326 | } |
327 | } |
328 | |
329 | /** |
330 | * Put file contents |
331 | * |
332 | * @param string $filename |
333 | * @param string $fileContents |
334 | * @return void |
335 | */ |
336 | public function putFileContents(string $filename, string $fileContents): void |
337 | { |
338 | $uri = '/' . $this->baseDirectory . '/' . $filename; |
339 | if ($this->baseDirectory !== $this->directory) { |
340 | $directory = str_replace($this->baseDirectory, '', $this->directory); |
341 | if (str_ends_with($directory, '/')) { |
342 | $directory = substr($directory, 0, -1); |
343 | } |
344 | $uri = $directory . $uri; |
345 | } |
346 | |
347 | $this->initClient('PUT', [ |
348 | 'content-length' => strlen($fileContents), |
349 | 'x-ms-blob-type' => 'BlockBlob', |
350 | 'x-ms-blob-content-type' => File::getFileMimeType($filename) |
351 | ]); |
352 | $this->client->getRequest()->setUri($uri); |
353 | $this->client->getRequest()->setBody($fileContents); |
354 | $this->auth->signRequest($this->client->getRequest()); |
355 | $this->client->send(); |
356 | } |
357 | |
358 | /** |
359 | * Upload file from server request $_FILES['file'] |
360 | * |
361 | * @param array $file |
362 | * @throws Exception |
363 | * @return void |
364 | */ |
365 | public function uploadFile(array $file): void |
366 | { |
367 | if (!isset($file['tmp_name']) || !isset($file['name'])) { |
368 | throw new Exception('Error: The uploaded file array was not valid'); |
369 | } |
370 | if (file_exists($file['tmp_name'])) { |
371 | $uri = '/' . $this->baseDirectory . '/' . $file['name']; |
372 | if ($this->baseDirectory !== $this->directory) { |
373 | $directory = str_replace($this->baseDirectory, '', $this->directory); |
374 | if (str_ends_with($directory, '/')) { |
375 | $directory = substr($directory, 0, -1); |
376 | } |
377 | $uri = $directory . $uri; |
378 | } |
379 | |
380 | $fileContents = file_get_contents($file['tmp_name']); |
381 | |
382 | $this->initClient('PUT', [ |
383 | 'content-length' => strlen($fileContents), |
384 | 'x-ms-blob-type' => 'BlockBlob', |
385 | 'x-ms-blob-content-type' => File::getFileMimeType($file['name']) |
386 | ]); |
387 | $this->client->getRequest()->setUri($uri); |
388 | $this->client->getRequest()->setBody($fileContents); |
389 | $this->auth->signRequest($this->client->getRequest()); |
390 | $this->client->send(); |
391 | } |
392 | } |
393 | |
394 | /** |
395 | * Copy file |
396 | * |
397 | * @param string $sourceFile |
398 | * @param string $destFile |
399 | * @return void |
400 | */ |
401 | public function copyFile(string $sourceFile, string $destFile): void |
402 | { |
403 | $sourceFileInfo = $this->fetchFileInfo($sourceFile); |
404 | |
405 | if (is_array($sourceFileInfo) && isset($sourceFileInfo['headers']) && |
406 | isset($sourceFileInfo['headers']['Content-Type']) && (!$sourceFileInfo['isError'])) { |
407 | $sourceUri = (!str_starts_with($sourceFile, '/')) ? '/' . $this->baseDirectory . '/' . $sourceFile : $sourceFile; |
408 | $destUri = (!str_starts_with($destFile, '/')) ? '/' . $this->baseDirectory . '/' . $destFile : $destFile; |
409 | |
410 | if ($this->baseDirectory !== $this->directory) { |
411 | $directory = str_replace($this->baseDirectory, '', $this->directory); |
412 | if (str_ends_with($directory, '/')) { |
413 | $directory = substr($directory, 0, -1); |
414 | } |
415 | $sourceUri = $directory . $sourceUri; |
416 | $destUri = $directory . $destUri; |
417 | } |
418 | |
419 | $this->initClient('PUT', [ |
420 | 'content-length' => $sourceFileInfo['headers']['Content-Length'], |
421 | 'x-ms-copy-source' => $this->auth->getBaseUri() . $sourceUri, |
422 | ]); |
423 | $this->client->getRequest()->setUri($destUri); |
424 | $this->auth->signRequest($this->client->getRequest()); |
425 | $this->client->send(); |
426 | } |
427 | } |
428 | |
429 | /** |
430 | * Copy file to a location external to the current location |
431 | * |
432 | * @param string $sourceFile |
433 | * @param string $externalFile |
434 | * @return void |
435 | */ |
436 | public function copyFileToExternal(string $sourceFile, string $externalFile): void |
437 | { |
438 | $sourceFileInfo = $this->fetchFileInfo($sourceFile); |
439 | |
440 | if (is_array($sourceFileInfo) && isset($sourceFileInfo['headers']) && |
441 | isset($sourceFileInfo['headers']['Content-Type']) && (!$sourceFileInfo['isError'])) { |
442 | $sourceUri = (!str_starts_with($sourceFile, '/')) ? '/' . $this->baseDirectory . '/' . $sourceFile : $sourceFile; |
443 | |
444 | if ($this->baseDirectory !== $this->directory) { |
445 | $directory = str_replace($this->baseDirectory, '', $this->directory); |
446 | if (str_ends_with($directory, '/')) { |
447 | $directory = substr($directory, 0, -1); |
448 | } |
449 | $sourceUri = $directory . $sourceUri; |
450 | } |
451 | |
452 | $this->initClient('PUT', [ |
453 | 'content-length' => $sourceFileInfo['headers']['Content-Length'], |
454 | 'x-ms-copy-source' => $this->auth->getBaseUri() . $sourceUri, |
455 | ]); |
456 | $this->client->getRequest()->setUri($externalFile); |
457 | $this->auth->signRequest($this->client->getRequest()); |
458 | $this->client->send(); |
459 | } |
460 | } |
461 | |
462 | /** |
463 | * Copy file from a location external to the current location |
464 | * |
465 | * @param string $externalFile |
466 | * @param string $destFile |
467 | * @return void |
468 | */ |
469 | public function copyFileFromExternal(string $externalFile, string $destFile): void |
470 | { |
471 | $this->initClient('HEAD', [], false); |
472 | $this->client->getRequest()->setUri($externalFile); |
473 | $this->auth->signRequest($this->client->getRequest()); |
474 | $response = $this->client->send(); |
475 | |
476 | if (($response->isSuccess()) && ($response->hasHeader('Content-Length'))) { |
477 | $destUri = (!str_starts_with($destFile, '/')) ? '/' . $this->baseDirectory . '/' . $destFile : $destFile; |
478 | |
479 | if ($this->baseDirectory !== $this->directory) { |
480 | $directory = str_replace($this->baseDirectory, '', $this->directory); |
481 | if (str_ends_with($directory, '/')) { |
482 | $directory = substr($directory, 0, -1); |
483 | } |
484 | $destUri = $directory . $destUri; |
485 | } |
486 | |
487 | $this->initClient('PUT', [ |
488 | 'content-length' => $response->getHeader('Content-Length')->getValueAsString(), |
489 | 'x-ms-copy-source' => $this->auth->getBaseUri() . $externalFile, |
490 | ]); |
491 | $this->client->getRequest()->setUri($destUri); |
492 | $this->auth->signRequest($this->client->getRequest()); |
493 | $this->client->send(); |
494 | } |
495 | } |
496 | |
497 | /** |
498 | * Move file to a location external to the current location |
499 | * |
500 | * @param string $sourceFile |
501 | * @param string $externalFile |
502 | * @return void |
503 | */ |
504 | public function moveFileToExternal(string $sourceFile, string $externalFile): void |
505 | { |
506 | $this->copyFileToExternal($sourceFile, $externalFile); |
507 | $this->deleteFile($sourceFile); |
508 | } |
509 | |
510 | /** |
511 | * Move file from a location external to the current location |
512 | * |
513 | * @param string $externalFile |
514 | * @param string $destFile |
515 | * @param ?string $snapshots ['include', 'only', null] |
516 | * @return void |
517 | */ |
518 | public function moveFileFromExternal(string $externalFile, string $destFile, ?string $snapshots = 'include'): void |
519 | { |
520 | $this->copyFileFromExternal($externalFile, $destFile); |
521 | |
522 | $headers = []; |
523 | if ($snapshots !== null) { |
524 | $headers['x-ms-delete-snapshots'] = ($snapshots == 'only') ? 'only' : 'include'; |
525 | } |
526 | |
527 | $this->initClient('DELETE', $headers); |
528 | $this->client->getRequest()->setUri($externalFile); |
529 | $this->auth->signRequest($this->client->getRequest()); |
530 | $this->client->send(); |
531 | } |
532 | |
533 | /** |
534 | * Rename file |
535 | * |
536 | * @param string $oldFile |
537 | * @param string $newFile |
538 | * @return void |
539 | */ |
540 | public function renameFile(string $oldFile, string $newFile): void |
541 | { |
542 | $this->copyFile($oldFile, $newFile); |
543 | $this->deleteFile($oldFile); |
544 | } |
545 | |
546 | /** |
547 | * Replace file |
548 | * |
549 | * @param string $filename |
550 | * @param string $fileContents |
551 | * @return void |
552 | */ |
553 | public function replaceFileContents(string $filename, string $fileContents): void |
554 | { |
555 | $this->putFileContents($filename, $fileContents); |
556 | } |
557 | |
558 | /** |
559 | * Delete file |
560 | * |
561 | * @param string $filename |
562 | * @param ?string $snapshots ['include', 'only', null] |
563 | * @return void |
564 | */ |
565 | public function deleteFile(string $filename, ?string $snapshots = 'include'): void |
566 | { |
567 | $uri = '/' . $this->baseDirectory . '/' . $filename; |
568 | if ($this->baseDirectory !== $this->directory) { |
569 | $directory = str_replace($this->baseDirectory, '', $this->directory); |
570 | if (str_ends_with($directory, '/')) { |
571 | $directory = substr($directory, 0, -1); |
572 | } |
573 | $uri = $directory . $uri; |
574 | } |
575 | |
576 | $headers = []; |
577 | if ($snapshots !== null) { |
578 | $headers['x-ms-delete-snapshots'] = ($snapshots == 'only') ? 'only' : 'include'; |
579 | } |
580 | |
581 | $this->initClient('DELETE', $headers); |
582 | $this->client->getRequest()->setUri($uri); |
583 | $this->auth->signRequest($this->client->getRequest()); |
584 | $this->client->send(); |
585 | } |
586 | |
587 | /** |
588 | * Fetch file |
589 | * |
590 | * @param string $filename |
591 | * @param bool $raw |
592 | * @return mixed |
593 | */ |
594 | public function fetchFile(string $filename, bool $raw = true): mixed |
595 | { |
596 | $filename = (!str_starts_with($filename, '/')) ? '/' . $this->baseDirectory . '/' . $filename : $filename; |
597 | |
598 | if ($this->baseDirectory !== $this->directory) { |
599 | $directory = str_replace($this->baseDirectory, '', $this->directory); |
600 | if (str_ends_with($directory, '/')) { |
601 | $directory = substr($directory, 0, -1); |
602 | } |
603 | $filename = $directory . $filename; |
604 | } |
605 | |
606 | $this->initClient('GET', [], false); |
607 | $this->client->getRequest()->setUri($filename); |
608 | $this->auth->signRequest($this->client->getRequest()); |
609 | $response = $this->client->send(); |
610 | |
611 | if ($response->isSuccess()) { |
612 | return ($raw) ? $response->getBody()->getContent(): $response; |
613 | } else { |
614 | return null; |
615 | } |
616 | } |
617 | |
618 | /** |
619 | * Fetch file info |
620 | * |
621 | * @param string $filename |
622 | * @return array |
623 | */ |
624 | public function fetchFileInfo(string $filename): array |
625 | { |
626 | $filename = (!str_starts_with($filename, '/')) ? '/' . $this->baseDirectory . '/' . $filename : $filename; |
627 | |
628 | if ($this->baseDirectory !== $this->directory) { |
629 | $directory = str_replace($this->baseDirectory, '', $this->directory); |
630 | if (str_ends_with($directory, '/')) { |
631 | $directory = substr($directory, 0, -1); |
632 | } |
633 | $filename = $directory . $filename; |
634 | } |
635 | |
636 | $this->initClient('HEAD', [], false); |
637 | $this->client->getRequest()->setUri($filename); |
638 | $this->auth->signRequest($this->client->getRequest()); |
639 | $response = $this->client->send(); |
640 | |
641 | return [ |
642 | 'code' => $response->getCode(), |
643 | 'message' => $response->getMessage(), |
644 | 'headers' => $response->getHeadersAsArray(), |
645 | 'isError' => $response->isError() |
646 | ]; |
647 | } |
648 | |
649 | /** |
650 | * File exists |
651 | * |
652 | * @param string $filename |
653 | * @return bool |
654 | */ |
655 | public function fileExists(string $filename): bool |
656 | { |
657 | $info = $this->fetchFileInfo($filename); |
658 | return (isset($info['code']) && ((int)$info['code'] == 200)); |
659 | } |
660 | |
661 | /** |
662 | * Check if is a dir |
663 | * |
664 | * @param string $directory |
665 | * @return bool |
666 | */ |
667 | public function isDir(string $directory): bool |
668 | { |
669 | if (str_starts_with($directory, '/')) { |
670 | $directory = substr($directory, 1); |
671 | } |
672 | if (str_ends_with($directory, '/')) { |
673 | $directory = substr($directory, 0, -1); |
674 | } |
675 | $info = $this->fetchFileInfo($directory); |
676 | return (isset($info['headers']) && isset($info['headers']['x-ms-resource-type']) && |
677 | $info['headers']['x-ms-resource-type'] == 'directory'); |
678 | } |
679 | |
680 | /** |
681 | * Check if is a file |
682 | * |
683 | * @param string $filename |
684 | * @return bool |
685 | */ |
686 | public function isFile(string $filename): bool |
687 | { |
688 | $info = $this->fetchFileInfo($filename); |
689 | return (isset($info['headers']) && isset($info['headers']['x-ms-resource-type']) && |
690 | $info['headers']['x-ms-resource-type'] == 'file'); |
691 | } |
692 | |
693 | /** |
694 | * Get file size |
695 | * |
696 | * @param string $filename |
697 | * @return int|bool |
698 | */ |
699 | public function getFileSize(string $filename): int|bool |
700 | { |
701 | $info = $this->fetchFileInfo($filename); |
702 | return $info['headers']['Content-Length'] ?? false; |
703 | } |
704 | |
705 | /** |
706 | * Get file type |
707 | * |
708 | * @param string $filename |
709 | * @return string|bool |
710 | */ |
711 | public function getFileType(string $filename): string|bool |
712 | { |
713 | if ($this->isFile($filename)) { |
714 | return 'file'; |
715 | } else if ($this->isDir($filename)) { |
716 | return 'dir'; |
717 | } else { |
718 | return false; |
719 | } |
720 | } |
721 | |
722 | /** |
723 | * Get file modified time |
724 | * |
725 | * @param string $filename |
726 | * @return int|string|bool |
727 | */ |
728 | public function getFileMTime(string $filename): int|string|bool |
729 | { |
730 | $info = $this->fetchFileInfo($filename); |
731 | if (isset($info['headers']) && !empty($info['headers']['Last-Modified'])) { |
732 | return $info['headers']['Last-Modified']; |
733 | } else if (isset($info['headers']) && !empty($info['headers']['x-ms-creation-time'])) { |
734 | return $info['headers']['x-ms-creation-time']; |
735 | } else { |
736 | return false; |
737 | } |
738 | } |
739 | |
740 | /** |
741 | * Create MD5 checksum of the file |
742 | * |
743 | * @param string $filename |
744 | * @return string|bool |
745 | */ |
746 | public function md5File(string $filename): string|bool |
747 | { |
748 | $info = $this->fetchFileInfo($filename); |
749 | if (isset($info['headers']) && !empty($info['headers']['Content-MD5'])) { |
750 | return $info['headers']['Content-MD5']; |
751 | } else { |
752 | return false; |
753 | } |
754 | } |
755 | |
756 | } |