Completed
Push — master ( 33b021...8d8d40 )
by Nicolas
03:00 queued 45s
created

GridFS::size()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 12
rs 9.8666
c 0
b 0
f 0
cc 3
nc 3
nop 1
1
<?php
2
3
namespace Gaufrette\Adapter;
4
5
use Gaufrette\Adapter;
6
use MongoDB\BSON\Regex;
7
use MongoDB\GridFS\Bucket;
8
use MongoDB\GridFS\Exception\FileNotFoundException;
9
10
/**
11
 * Adapter for the GridFS filesystem on MongoDB database.
12
 *
13
 * @author Tomi Saarinen <[email protected]>
14
 * @author Antoine Hérault <[email protected]>
15
 * @author Leszek Prabucki <[email protected]>
16
 */
17
class GridFS implements Adapter,
18
                        ChecksumCalculator,
19
                        MetadataSupporter,
20
                        ListKeysAware,
21
                        SizeCalculator
22
{
23
    /** @var array */
24
    private $metadata = [];
25
26
    /** @var Bucket */
27
    private $bucket;
28
29
    /**
30
     * @param Bucket $bucket
31
     */
32
    public function __construct(Bucket $bucket)
33
    {
34
        $this->bucket = $bucket;
35
    }
36
37
    /**
38
     * {@inheritdoc}
39
     */
40
    public function read($key)
41
    {
42
        try {
43
            $stream = $this->bucket->openDownloadStreamByName($key);
44
        } catch (FileNotFoundException $e) {
45
            return false;
46
        }
47
48
        try {
49
            return stream_get_contents($stream);
50
        } finally {
51
            fclose($stream);
52
        }
53
    }
54
55
    /**
56
     * {@inheritdoc}
57
     */
58
    public function write($key, $content)
59
    {
60
        $stream = $this->bucket->openUploadStream($key, ['metadata' => $this->getMetadata($key)]);
61
62
        try {
63
            return fwrite($stream, $content);
64
        } finally {
65
            fclose($stream);
66
        }
67
68
        return false;
69
    }
70
71
    /**
72
     * {@inheritdoc}
73
     */
74
    public function isDirectory($key)
75
    {
76
        return false;
77
    }
78
79
    /**
80
     * {@inheritdoc}
81
     */
82
    public function rename($sourceKey, $targetKey)
83
    {
84
        $metadata = $this->getMetadata($sourceKey);
85
        $writable = $this->bucket->openUploadStream($targetKey, ['metadata' => $metadata]);
86
87
        try {
88
            $this->bucket->downloadToStreamByName($sourceKey, $writable);
89
            $this->setMetadata($targetKey, $metadata);
90
            $this->delete($sourceKey);
91
        } catch (FileNotFoundException $e) {
92
            return false;
93
        } finally {
94
            fclose($writable);
95
        }
96
97
        return true;
98
    }
99
100
    /**
101
     * {@inheritdoc}
102
     */
103
    public function exists($key)
104
    {
105
        return (boolean) $this->bucket->findOne(['filename' => $key]);
106
    }
107
108
    /**
109
     * {@inheritdoc}
110
     */
111
    public function keys()
112
    {
113
        $keys = [];
114
        $cursor = $this->bucket->find([], ['projection' => ['filename' => 1]]);
115
116
        foreach ($cursor as $file) {
117
            $keys[] = $file['filename'];
118
        }
119
120
        return $keys;
121
    }
122
123
    /**
124
     * {@inheritdoc}
125
     */
126
    public function mtime($key)
127
    {
128
        $file = $this->bucket->findOne(['filename' => $key], ['projection' => ['uploadDate' => 1]]);
129
130
        return $file ? (int) $file['uploadDate']->toDateTime()->format('U') : false;
131
    }
132
133
    /**
134
     * {@inheritdoc}
135
     */
136
    public function checksum($key)
137
    {
138
        $file = $this->bucket->findOne(['filename' => $key], ['projection' => ['md5' => 1]]);
139
140
        return $file ? $file['md5'] : false;
141
    }
142
143
    /**
144
     * {@inheritdoc}
145
     */
146
    public function delete($key)
147
    {
148
        if (null === $file = $this->bucket->findOne(['filename' => $key], ['projection' => ['_id' => 1]])) {
149
            return false;
150
        }
151
152
        $this->bucket->delete($file['_id']);
153
154
        return true;
155
    }
156
157
    /**
158
     * {@inheritdoc}
159
     */
160
    public function setMetadata($key, $metadata)
161
    {
162
        $this->metadata[$key] = $metadata;
163
    }
164
165
    /**
166
     * {@inheritdoc}
167
     */
168
    public function getMetadata($key)
169
    {
170
        if (isset($this->metadata[$key])) {
171
            return $this->metadata[$key];
172
        } else {
173
            $meta = $this->bucket->findOne(['filename' => $key], ['projection' => ['metadata' => 1,'_id' => 0]]);
174
            if ($meta === null) {
175
                return array();
176
            }
177
            $this->metadata[$key] = iterator_to_array($meta['metadata']);
178
            return $this->metadata[$key];
179
        }
180
    }
181
182
    /**
183
     * {@inheritdoc}
184
     */
185
    public function listKeys($prefix = '')
186
    {
187
        $prefix = trim($prefix);
188
189
        if ($prefix === '') {
190
            return [
191
                'dirs' => [],
192
                'keys' => $this->keys(),
193
            ];
194
        }
195
196
        $regex = new Regex(sprintf('^%s', $prefix), '');
197
        $files = $this->bucket->find(['filename' => $regex], ['projection' => ['filename' => 1]]);
198
        $result = [
199
            'dirs' => [],
200
            'keys' => [],
201
        ];
202
203
        foreach ($files as $file) {
204
            $result['keys'][] = $file['filename'];
205
        }
206
207
        return $result;
208
    }
209
    
210
    public function size($key)
211
    {
212
        if (!$this->exists($key)) {
213
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type declared by the interface Gaufrette\Adapter\SizeCalculator::size of type integer.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
214
        }
215
        $size = $this->bucket->findOne(['filename' => $key], ['projection' => ['length' => 1,'_id' => 0]]);
216
        if (!isset($size['length'])) {
217
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type declared by the interface Gaufrette\Adapter\SizeCalculator::size of type integer.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
218
        }
219
220
        return $size['length'];
221
    }
222
    
223
}
224