Test Failed
Branch master (446184)
by Domenico
04:39
created

GetItemsInABucket   A

Complexity

Total Complexity 42

Size/Duplication

Total Lines 205
Duplicated Lines 0 %

Test Coverage

Coverage 91.36%

Importance

Changes 0
Metric Value
wmc 42
eloc 81
dl 0
loc 205
ccs 74
cts 81
cp 0.9136
rs 9.0399
c 0
b 0
f 0

5 Methods

Rating   Name   Duplication   Size   Complexity  
B returnItemsFromS3() 0 39 11
B returnItemsFromCache() 0 42 8
C handle() 0 38 13
B returnVersionedItemsFromS3() 0 38 9
A validateParams() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like GetItemsInABucket often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use GetItemsInABucket, and based on these observations, apply Extract Interface, too.

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 Matecat\SimpleS3\Commands\CommandHandler;
16
use Matecat\SimpleS3\Helpers\File;
17
18
class GetItemsInABucket extends CommandHandler
19
{
20
    /**
21
     * Get the list of keys in a bucket.
22
     * If 'hydrate' parameter is set to true, an array of hydrated Aws\Result is returned instead.
23
     *
24
     * @param array $params
25
     *
26
     * @return array|mixed
27
     * @throws \Exception
28
     */
29 18
    public function handle($params = [])
30
    {
31 18
        $bucketName = $params['bucket'];
32
33
        try {
34
            $config = [
35 18
                'Bucket' => $bucketName,
36
            ];
37
38 18
            if (isset($params['prefix'])) {
39
40
                // add a final slash to prefix
41 10
                if (false === File::endsWith($params['prefix'], $this->client->getPrefixSeparator())) {
42 8
                    $params['prefix'] .= $this->client->getPrefixSeparator();
43
                }
44
45 10
                $config['Delimiter'] = (isset($params['delimiter'])) ? $params['delimiter'] : $this->client->getPrefixSeparator();
46 10
                $config['Prefix'] = $params['prefix'];
47
            }
48
49
            // 1. If 'exclude-cache' is set, return records always from S3
50 18
            if (isset($params['exclude-cache']) and true === $params['exclude-cache']) {
51 2
                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 16
            if ($this->client->hasCache() and isset($config['Prefix'])) {
56 7
                return $this->returnItemsFromCache($bucketName, $config, (isset($params['hydrate'])) ? $params['hydrate'] : null);
57
            }
58
59
            // 3. Otherwise, return records from S3
60 9
            return $this->returnItemsFromS3($bucketName, $config, (isset($params['hydrate'])) ? $params['hydrate'] : null);
61
        } catch (S3Exception $e) {
62
            if (null !== $this->commandHandlerLogger) {
63
                $this->commandHandlerLogger->logExceptionAndReturnFalse($e);
64
            }
65
66
            throw $e;
67
        }
68
    }
69
70
    /**
71
     * @param array $params
72
     *
73
     * @return bool
74
     */
75 18
    public function validateParams($params = [])
76
    {
77 18
        return (isset($params['bucket']));
78
    }
79
80
    /**
81
     * @param string $bucketName
82
     * @param array $config
83
     * @param null $hydrate
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $hydrate is correct as it would always require null to be passed?
Loading history...
84
     *
85
     * @return array
86
     */
87 7
    protected function returnItemsFromCache($bucketName, $config, $hydrate = null)
88
    {
89 7
        $itemsFromCache = $this->client->getCache()->search($bucketName, $config['Prefix']);
90
91
        // no data was found, try to retrieve data from S3
92 7
        if (count($itemsFromCache) == 0) {
93
            return $this->returnItemsFromS3($bucketName, $config, $hydrate);
94
        }
95
96
        // no hydrate, simply return the array of keys stored in redis
97 7
        if (null == $hydrate) {
0 ignored issues
show
introduced by
The condition null == $hydrate is always true.
Loading history...
98 5
            if (null !== $this->commandHandlerLogger) {
99 5
                $this->commandHandlerLogger->log($this, sprintf('Files of \'%s\' bucket were successfully obtained from CACHE', $bucketName));
100
            }
101
102 5
            return $itemsFromCache;
103
        }
104
105
        // hydrate the key with the entire AWS\Result Object
106 2
        $items = [];
107 2
        foreach ($itemsFromCache as $key) {
108 2
            $version = null;
109 2
            $originalKey = $key;
110
111 2
            if (strpos($key, '<VERSION_ID:') !== false) {
112 1
                $v = explode('<VERSION_ID:', $key);
113 1
                $version = str_replace('>', '', $v[1]);
114 1
                $key = $v[0];
115
            }
116
117 2
            if ($this->client->hasEncoder()) {
118 2
                $key = $this->client->getEncoder()->decode($key);
119
            }
120
121 2
            $items[$originalKey] = $this->client->getItem(['bucket' => $bucketName, 'key' => $key, 'version' => $version]);
122
        }
123
124 2
        if (null !== $this->commandHandlerLogger) {
125 2
            $this->commandHandlerLogger->log($this, sprintf('Files of \'%s\' bucket were successfully obtained from CACHE', $bucketName));
126
        }
127
128 2
        return $items;
129
    }
130
131
    /**
132
     * @param string $bucketName
133
     * @param array $config
134
     *
135
     * @return array
136
     */
137 11
    protected function returnItemsFromS3($bucketName, $config, $hydrate = null)
138
    {
139 11
        if ($this->client->isBucketVersioned(['bucket' => $bucketName])) {
140 1
            return $this->returnVersionedItemsFromS3($bucketName, $config, $hydrate);
141
        }
142
143 11
        $resultPaginator = $this->client->getConn()->getPaginator('ListObjects', $config);
144 11
        $items = [];
145
146 11
        foreach ($resultPaginator as $result) {
147 11
            if (is_array($contents = $result->get('Contents'))) {
148 9
                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...
149 9
                    $key = $contents[$i]['Key'];
150
151 9
                    if (false === File::endsWith($key, $this->client->getPrefixSeparator())) {
152 9
                        if ($this->client->hasEncoder()) {
153 6
                            $key = $this->client->getEncoder()->decode($key);
154
                        }
155
156 9
                        if (null != $hydrate and true === $hydrate) {
157 2
                            $items[$key] = $this->client->getItem(['bucket' => $bucketName, 'key' => $key]);
158
                        } else {
159 7
                            $items[] = $key;
160
                        }
161
162
                        // send to cache, just to be sure that S3 is syncronized with cache
163 9
                        if ($this->client->hasCache()) {
164 6
                            $this->client->getCache()->set($bucketName, $contents[$i]['Key'], $this->client->getItem(['bucket' => $bucketName, 'key' => $key]));
165
                        }
166
                    }
167
                }
168
            }
169
        }
170
171 11
        if (null !== $this->commandHandlerLogger) {
172 10
            $this->commandHandlerLogger->log($this, sprintf('Files were successfully obtained from \'%s\' bucket', $bucketName));
173
        }
174
175 11
        return $items;
176
    }
177
178
    /**
179
     * @param string $bucketName
180
     * @param array $config
181
     * @param null $hydrate
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $hydrate is correct as it would always require null to be passed?
Loading history...
182
     *
183
     * @return array
184
     */
185 1
    protected function returnVersionedItemsFromS3($bucketName, $config, $hydrate = null)
186
    {
187 1
        $results = $this->client->getConn()->listObjectVersions($config);
188 1
        $items = [];
189
190 1
        if (false === isset($results['Versions'])) {
191
            return $items;
192
        }
193
194 1
        foreach ($results['Versions'] as $result) {
195 1
            $key = $result['Key'];
196 1
            $version = $result['VersionId'];
197
198 1
            if (false === File::endsWith($key, $this->client->getPrefixSeparator())) {
199 1
                if ($this->client->hasEncoder()) {
200 1
                    $key = $this->client->getEncoder()->decode($key);
201
                }
202
203 1
                $index = $key.'<VERSION_ID:'.$version.'>';
204
205 1
                if (null != $hydrate and true === $hydrate) {
206
                    $items[$index] = $this->client->getItem(['bucket' => $bucketName, 'key' => $key, 'version' => $version]);
207
                } else {
208 1
                    $items[] = $index;
209
                }
210
211
                // send to cache, just to be sure that S3 is syncronized with cache
212 1
                if ($this->client->hasCache()) {
213 1
                    $this->client->getCache()->set($bucketName, $result['Key'], $this->client->getItem(['bucket' => $bucketName, 'key' => $key, 'version' => $version]), $version);
214
                }
215
            }
216
        }
217
218 1
        if (null !== $this->commandHandlerLogger) {
219 1
            $this->commandHandlerLogger->log($this, sprintf('Files (versioned) were successfully obtained from \'%s\' bucket', $bucketName));
220
        }
221
222 1
        return $items;
223
    }
224
}
225