Test Failed
Branch master (3a0aa4)
by Domenico
16:55
created

GetItemsInABucket::returnItemsFromCache()   A

Complexity

Conditions 6
Paths 7

Size

Total Lines 38
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 42

Importance

Changes 0
Metric Value
eloc 19
dl 0
loc 38
ccs 0
cts 19
cp 0
rs 9.0111
c 0
b 0
f 0
cc 6
nc 7
nop 3
crap 42
1
<?php
2
/**
3
 *  This file is part of the Simple S3 package.
4
 *
5
 * (c) Mauro Cassani<https://github.com/mauretto78>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 *
10
 */
11
12
namespace Matecat\SimpleS3\Commands\Handlers;
13
14
use Aws\S3\Exception\S3Exception;
15
use Exception;
16
use Matecat\SimpleS3\Commands\CommandHandler;
17
use Matecat\SimpleS3\Helpers\File;
18
19
class GetItemsInABucket extends CommandHandler
20
{
21
    /**
22
     * Get the list of keys in a bucket.
23
     * If 'hydrate' parameter is set to true, an array of hydrated Aws\Result is returned instead.
24
     *
25
     * @param array $params
26
     *
27
     * @return array
28
     * @throws Exception
29
     */
30
    public function handle(array $params = []): array
31
    {
32
        $bucketName = $params[ 'bucket' ];
33
34
        try {
35
            $config = [
36
                    'Bucket' => $bucketName,
37
            ];
38
39
            if (isset($params[ 'prefix' ])) {
40
                // add a final slash to prefix
41
                if (false === File::endsWith($params[ 'prefix' ], $this->client->getPrefixSeparator())) {
42
                    $params[ 'prefix' ] .= $this->client->getPrefixSeparator();
43
                }
44
45
                $config[ 'Delimiter' ] = (isset($params[ 'delimiter' ])) ? $params[ 'delimiter' ] : $this->client->getPrefixSeparator();
46
                $config[ 'Prefix' ]    = $params[ 'prefix' ];
47
            }
48
49
            // 1. If 'exclude-cache' is set, return records always from S3
50
            if (isset($params[ 'exclude-cache' ]) and true === $params[ 'exclude-cache' ]) {
51
                return $this->returnItemsFromS3($bucketName, $config, (isset($params[ 'hydrate' ])) ? $params[ 'hydrate' ] : null);
52
            }
53
54
            // 2. If the cache is set and there is a prefix, return records from cache
55
            if ($this->client->hasCache() and isset($config[ 'Prefix' ])) {
56
                return $this->returnItemsFromCache($bucketName, $config, (isset($params[ 'hydrate' ])) ? $params[ 'hydrate' ] : null);
57
            }
58
59
            // 3. Otherwise, return records from S3
60
            return $this->returnItemsFromS3($bucketName, $config, (isset($params[ 'hydrate' ])) ? $params[ 'hydrate' ] : null);
61
        } catch (S3Exception $e) {
62
            $this->commandHandlerLogger?->logExceptionAndReturnFalse($e);
63
64
            throw $e;
65
        }
66
    }
67
68
    /**
69
     * @param array $params
70
     *
71
     * @return bool
72
     */
73
    public function validateParams(array $params = []): bool
74
    {
75
        return (isset($params[ 'bucket' ]));
76
    }
77
78
    /**
79
     * @param string    $bucketName
80
     * @param array     $config
81
     * @param bool|null $hydrate
82
     *
83
     * @return array
84
     */
85
    protected function returnItemsFromCache(string $bucketName, array $config, ?bool $hydrate = null): array
86
    {
87
        $itemsFromCache = $this->client->getCache()->search($bucketName, $config[ 'Prefix' ]);
88
89
        // no data was found, try to retrieve data from S3
90
        if (count($itemsFromCache) == 0) {
91
            return $this->returnItemsFromS3($bucketName, $config, $hydrate);
92
        }
93
94
        // no hydrate, simply return the array of keys stored in redis
95
        if (null == $hydrate) {
96
            $this->commandHandlerLogger?->log($this, sprintf('Files of \'%s\' bucket were successfully obtained from CACHE', $bucketName));
97
98
            return $itemsFromCache;
99
        }
100
101
        // hydrate the key with the entire AWS\Result Object
102
        $items = [];
103
        foreach ($itemsFromCache as $key) {
104
            $version     = null;
105
            $originalKey = $key;
106
107
            if (str_contains($key, '<VERSION_ID:')) {
108
                $v       = explode('<VERSION_ID:', $key);
109
                $version = str_replace('>', '', $v[ 1 ]);
110
                $key     = $v[ 0 ];
111
            }
112
113
            if ($this->client->hasEncoder()) {
114
                $key = $this->client->getEncoder()->decode($key);
115
            }
116
117
            $items[ $originalKey ] = $this->client->getItem(['bucket' => $bucketName, 'key' => $key, 'version' => $version]);
118
        }
119
120
        $this->commandHandlerLogger?->log($this, sprintf('Files of \'%s\' bucket were successfully obtained from CACHE', $bucketName));
121
122
        return $items;
123
    }
124
125
    /**
126
     * @param string    $bucketName
127
     * @param array     $config
128
     * @param bool|null $hydrate
129
     *
130
     * @return array
131
     */
132
    protected function returnItemsFromS3(string $bucketName, array $config, ?bool $hydrate = null): array
133
    {
134
        if ($this->client->isBucketVersioned(['bucket' => $bucketName])) {
135
            return $this->returnVersionedItemsFromS3($bucketName, $config, $hydrate);
136
        }
137
138
        $resultPaginator = $this->client->getConn()->getPaginator('ListObjects', $config);
139
        $items           = [];
140
141
        foreach ($resultPaginator as $result) {
142
            if (is_array($contents = $result->get('Contents'))) {
143
                for ($i = 0; $i < count($contents); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
144
                    $key = $contents[ $i ][ 'Key' ];
145
146
                    if (false === File::endsWith($key, $this->client->getPrefixSeparator())) {
147
                        if ($this->client->hasEncoder()) {
148
                            $key = $this->client->getEncoder()->decode($key);
149
                        }
150
151
                        if (null != $hydrate and true === $hydrate) {
152
                            $items[ $key ] = $this->client->getItem(['bucket' => $bucketName, 'key' => $key]);
153
                        } else {
154
                            $items[] = $key;
155
                        }
156
157
                        // send to cache, just to be sure that S3 is syncronized with cache
158
                        if ($this->client->hasCache()) {
159
                            $this->client->getCache()->set($bucketName, $contents[ $i ][ 'Key' ], $this->client->getItem(['bucket' => $bucketName, 'key' => $key]));
160
                        }
161
                    }
162
                }
163
            }
164
        }
165
166
        $this->commandHandlerLogger?->log($this, sprintf('Files were successfully obtained from \'%s\' bucket', $bucketName));
167
168
        return $items;
169
    }
170
171
    /**
172
     * @param string    $bucketName
173
     * @param array     $config
174
     * @param bool|null $hydrate
175
     *
176
     * @return array
177
     */
178
    protected function returnVersionedItemsFromS3(string $bucketName, array $config, ?bool $hydrate = null): array
179
    {
180
        $results = $this->client->getConn()->listObjectVersions($config);
181
        $items   = [];
182
183
        if (false === isset($results[ 'Versions' ])) {
184
            return $items;
185
        }
186
187
        foreach ($results[ 'Versions' ] as $result) {
188
            $key     = $result[ 'Key' ];
189
            $version = $result[ 'VersionId' ];
190
191
            if (false === File::endsWith($key, $this->client->getPrefixSeparator())) {
192
                if ($this->client->hasEncoder()) {
193
                    $key = $this->client->getEncoder()->decode($key);
194
                }
195
196
                $index = $key . '<VERSION_ID:' . $version . '>';
197
198
                if (null != $hydrate and true === $hydrate) {
199
                    $items[ $index ] = $this->client->getItem(['bucket' => $bucketName, 'key' => $key, 'version' => $version]);
200
                } else {
201
                    $items[] = $index;
202
                }
203
204
                // send to cache, just to be sure that S3 is syncronized with cache
205
                if ($this->client->hasCache()) {
206
                    $this->client->getCache()->set($bucketName, $result[ 'Key' ], $this->client->getItem(['bucket' => $bucketName, 'key' => $key, 'version' => $version]), $version);
207
                }
208
            }
209
        }
210
211
        $this->commandHandlerLogger?->log($this, sprintf('Files (versioned) were successfully obtained from \'%s\' bucket', $bucketName));
212
213
        return $items;
214
    }
215
}
216