Completed
Branch dev (d5d70c)
by Raffael
11:00
created

Gridfs   A

Complexity

Total Complexity 21

Size/Duplication

Total Lines 226
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 4

Importance

Changes 0
Metric Value
wmc 21
lcom 1
dl 0
loc 226
rs 10
c 0
b 0
f 0
cbo 4

9 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 1
A hasFile() 0 4 1
C deleteFile() 0 48 7
A getFile() 0 8 2
A getFileMeta() 0 13 3
B storeFile() 0 31 3
A getFileById() 0 4 1
A getFileByHash() 0 4 1
B storeNew() 0 33 2
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * balloon
7
 *
8
 * @copyright   Copryright (c) 2012-2018 gyselroth GmbH (https://gyselroth.com)
9
 * @license     GPL-3.0 https://opensource.org/licenses/GPL-3.0
10
 */
11
12
namespace Balloon\Filesystem\Storage\Adapter;
13
14
use Balloon\Filesystem\Exception;
15
use Balloon\Filesystem\Node\File;
16
use MongoDB\BSON\ObjectId;
17
use MongoDB\Database;
18
use MongoDB\GridFS\Bucket;
19
use Psr\Log\LoggerInterface;
20
21
class Gridfs implements AdapterInterface
22
{
23
    /**
24
     * Database.
25
     *
26
     * @var Database
27
     */
28
    protected $db;
29
30
    /**
31
     * GridFS.
32
     *
33
     * @var Bucket
34
     */
35
    protected $gridfs;
36
37
    /**
38
     * Logger.
39
     *
40
     * @var LoggerInterface
41
     */
42
    protected $logger;
43
44
    /**
45
     * GridFS storage.
46
     *
47
     * @param Database
48
     * @param LoggerInterface $logger
49
     */
50
    public function __construct(Database $db, LoggerInterface $logger)
51
    {
52
        $this->db = $db;
53
        $this->gridfs = $db->selectGridFSBucket();
54
        $this->logger = $logger;
55
    }
56
57
    /**
58
     * {@inheritdoc}
59
     */
60
    public function hasFile(File $file, array $attributes): bool
61
    {
62
        return null !== $this->getFileById($attributes);
0 ignored issues
show
Documentation introduced by
$attributes is of type array, but the function expects a object<MongoDB\BSON\ObjectId>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
63
    }
64
65
    /**
66
     * {@inheritdoc}
67
     */
68
    public function deleteFile(File $file, array $attributes): bool
69
    {
70
        if (!isset($attributes['_id'])) {
71
            throw new Exception('attributes do not contain a gridfs id');
72
        }
73
74
        $exists = $this->getFileById($attributes['_id']);
75
76
        if (null === $exists) {
77
            $this->logger->debug('gridfs content node ['.$exists['_id'].'] was not found, file reference=['.$file->getId().']', [
78
                'category' => get_class($this),
79
            ]);
80
81
            return false;
82
        }
83
84
        if (!isset($exists['metadata'])) {
85
            $this->gridfs->delete($exists['_id']);
86
87
            return true;
88
        }
89
90
        $references = $exists['metadata']['ref'];
91
        $key = array_search($file->getId(), array_column($references, 'id'));
92
93
        if (!isset($references[$key]) && count($references) > 1) {
94
            return true;
95
        }
96
        if (count($references) > 1) {
97
            $this->logger->debug('gridfs content node ['.$exists['_id'].'] still has references left, just remove the reference ['.$file->getId().']', [
98
                'category' => get_class($this),
99
            ]);
100
101
            $this->db->{'fs.files'}->updateOne(['_id' => $exists['_id']], [
102
                '$pull' => [
103
                    'metadata.ref' => $references[$key],
104
                ],
105
            ]);
106
        } else {
107
            $this->logger->debug('gridfs content node ['.$exists['_id'].'] has no references left, delete node completely', [
108
                'category' => get_class($this),
109
            ]);
110
111
            $this->gridfs->delete($exists['_id']);
112
        }
113
114
        return true;
115
    }
116
117
    /**
118
     * {@inheritdoc}
119
     */
120
    public function getFile(File $file, array $attributes)
121
    {
122
        if (!isset($attributes['_id'])) {
123
            throw new Exception('attributes do not contain a gridfs id');
124
        }
125
126
        return $this->gridfs->openDownloadStream($attributes['_id']);
127
    }
128
129
    /**
130
     * {@inheritdoc}
131
     */
132
    public function getFileMeta(File $file, array $attributes): array
133
    {
134
        if (!isset($attributes['_id'])) {
135
            throw new Exception('attributes do not contain a gridfs id');
136
        }
137
138
        $file = $this->getFileById($attributes['_id']);
139
        if (null === $file) {
140
            throw new Exception('file was not found');
141
        }
142
143
        return $file;
144
    }
145
146
    /**
147
     * {@inheritdoc}
148
     */
149
    public function storeFile(File $file, $contents): array
150
    {
151
        $exists = $this->getFileByHash($file->getHash());
152
153
        if (null === $exists) {
154
            return $this->storeNew($file, $contents);
155
        }
156
157
        $this->logger->info('gridfs content node with hash ['.$file->getHash().'] does already exists,
158
          add new file reference for ['.$file->getId().']', [
159
            'category' => get_class($this),
160
        ]);
161
162
        $action['$addToSet'] = [
0 ignored issues
show
Coding Style Comprehensibility introduced by
$action was never initialized. Although not strictly required by PHP, it is generally a good practice to add $action = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
163
            'metadata.ref' => [
164
                'id' => $file->getId(),
165
                'owner' => $file->getOwner(),
166
            ],
167
        ];
168
169
        if ($file->isShareMember()) {
170
            $action['$addToSet']['metadata.share_ref'] = [
171
                'id' => $file->getId(),
172
                'share' => $file->getShareId(),
173
            ];
174
        }
175
176
        $this->db->{'fs.files'}->updateOne(['md5' => $file->getHash()], $action);
177
178
        return ['_id' => $exists['_id']];
179
    }
180
181
    /**
182
     * Get stored file.
183
     *
184
     * @param ObjectId $id
185
     *
186
     * @return array
187
     */
188
    protected function getFileById(ObjectId $id): ?array
189
    {
190
        return $this->gridfs->findOne(['_id' => $id]);
191
    }
192
193
    /**
194
     * Get stored file.
195
     *
196
     * @param string $hash
197
     *
198
     * @return array
199
     */
200
    protected function getFileByHash(string $hash): ?array
201
    {
202
        return $this->gridfs->findOne(['md5' => $hash]);
203
    }
204
205
    /**
206
     * Store new file.
207
     *
208
     * @param File     $file
209
     * @param resource $contents
210
     *
211
     * @return array
212
     */
213
    protected function storeNew(File $file, $contents): array
214
    {
215
        $meta = [
216
            'ref' => [[
217
                'id' => $file->getId(),
218
                'owner' => $file->getOwner(),
219
            ]],
220
        ];
221
222
        if ($file->isShareMember()) {
223
            $meta['share_ref'] = [[
224
                'id' => $file->getId(),
225
                'share' => $file->getShareId(),
226
            ]];
227
        } else {
228
            $meta['share_ref'] = [];
229
        }
230
231
        set_time_limit((int) ($file->getSize() / 15339168));
232
        $id = new ObjectId();
233
234
        //somehow mongo-connector does not catch metadata when set during uploadFromStream()
235
        $stream = $this->gridfs->uploadFromStream($id, $contents, ['_id' => $id/*, 'metadata' => $file*/]);
0 ignored issues
show
Unused Code introduced by
$stream is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
Unused Code Comprehensibility introduced by
58% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
236
        $this->db->{'fs.files'}->updateOne(['_id' => $id], [
237
            '$set' => ['metadata' => $meta],
238
        ]);
239
240
        $this->logger->info('added new gridfs content node ['.$id.'] for file ['.$file->getId().']', [
241
            'category' => get_class($this),
242
        ]);
243
244
        return ['_id' => $id];
245
    }
246
}
247