Test Failed
Push — main ( 84e709...fb0dfd )
by Gabor
10:16
created

Adapter::getObjectListByPrefix()   C

Complexity

Conditions 13
Paths 48

Size

Total Lines 54
Code Lines 31

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 13
eloc 31
c 1
b 0
f 0
nc 48
nop 3
dl 0
loc 54
rs 6.6166

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * Worst Practice Aws S3 Adapter
5
 *
6
 * PHP version 8.0
7
 *
8
 * @copyright 2021 Worst Practice
9
 * @license   https://opensource.org/licenses/MIT The MIT License (MIT)
10
 *
11
 * @link http://www.worstpractice.dev
12
 */
13
14
declare(strict_types=1);
15
16
namespace WorstPractice\Component\Aws\S3;
17
18
use Aws\S3\S3Client;
19
20
/**
21
 * AWS S3 adapter.
22
 */
23
class Adapter implements AdapterInterface
24
{
25
    /** @var string */
26
    private string $bucket;
27
    /** @var string[] */
28
    private array $validSortByKeys = [
29
        self::OBJECT_SORT_BY_NAME,
30
        self::OBJECT_SORT_BY_NAME_DESC,
31
        self::OBJECT_SORT_BY_DATE,
32
        self::OBJECT_SORT_BY_DATE_DESC,
33
    ];
34
35
    /**
36
     * Adapter constructor.
37
     *
38
     * @param S3Client $s3Client  The AWS SDK S3 Client instance
39
     *
40
     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
41
     */
42
    public function __construct(private S3Client $s3Client)
43
    {
44
    }
45
46
    /**
47
     * Set active bucket.
48
     *
49
     * @param string $bucket
50
     */
51
    public function setBucket(string $bucket): void
52
    {
53
        $this->bucket = $bucket;
54
    }
55
56
    /**
57
     * Gets the last uploaded object's key in the given S3 bucket by key prefix.
58
     *
59
     * @param string $keyPrefix
60
     * @return string|null
61
     */
62
    public function getLastUploadedKeyByPrefix(string $keyPrefix): ?string
63
    {
64
        $object = $this->getObjectListByPrefix($keyPrefix, self::OBJECT_SORT_BY_DATE_DESC, 1);
65
66
        return $object[0]['Key'] ?? null;
67
    }
68
69
    /**
70
     * Gets the object list for the given S3 bucket by key prefix.
71
     *
72
     * @param string $keyPrefix The path on the S3 bucket. Mandatory.
73
     * @param string|null $sortBy Sort the results by the given key.
74
     *                            Use the constants because this parameter requires special values.
75
     * @param int $limit Limit the results. 0 means return all.
76
     * @return array
77
     */
78
    public function getObjectListByPrefix(string $keyPrefix, string $sortBy = null, int $limit = 0): array
79
    {
80
        $results = [];
81
        $continuationToken = '';
82
        $options = [
83
            'Bucket' => $this->bucket,
84
            'EncodingType' => 'url',
85
            'Prefix' => $keyPrefix,
86
            'RequestPayer' => 'requester'
87
        ];
88
89
        // We can add a query limit here only when we don't want any special sorting.
90
        // The default chunk limit is 1000. Probably the guys at the AWS know why not recommended to go over this limit
91
        // so I won't do either.
92
        if (empty($sortBy) && $limit > 0 && $limit < self::AWS_DEFAULT_LIST_LIMIT) {
93
            $options['MaxKeys'] = $limit;
94
            // Set the parameter to 0 to avoid the unnecessary array_chunk later.
95
            $limit = 0;
96
        }
97
98
        do {
99
            if (!empty($continuationToken)) {
100
                $options['ContinuationToken'] = $continuationToken;
101
            }
102
103
            $response = $this->s3Client->listObjectsV2($options);
104
105
            if (empty($response['Contents'])) {
106
                break;
107
            }
108
109
            $results[] = $response['Contents'];
110
            $continuationToken = $response['NextContinuationToken'];
111
            $isTruncated = $response['IsTruncated'];
112
            usleep(50000); // 50 ms pause to avoid CPU spikes
113
        } while ($isTruncated);
114
115
        $results = array_merge([], ...$results);
116
117
        if (!empty($sortBy) && in_array($sortBy, $this->validSortByKeys, true)) {
118
            $direction = $sortBy[0] === '^' ? 'asc' : 'desc';
119
            $sortByKey = substr($sortBy, 1);
120
121
            usort($results, static function ($a, $b) use ($direction, $sortByKey) {
122
                $cmp = strcmp($a[$sortByKey], $b[$sortByKey]);
123
                return $direction === 'asc' ? $cmp : -$cmp;
124
            });
125
        }
126
127
        if (!empty($results) && $limit > 0) {
128
            $results = array_chunk($results, $limit)[0];
129
        }
130
131
        return $results;
132
    }
133
}
134