Skip to content

Commit 9b1f164

Browse files
Will Banfieldjmikola
authored andcommitted
restructure some aspects to be more inline with SPEC and add additional tests
1 parent ba3efc0 commit 9b1f164

File tree

7 files changed

+341
-67
lines changed

7 files changed

+341
-67
lines changed

src/GridFS/Bucket.php

Lines changed: 34 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,9 @@ class Bucket
4343
*/
4444
public function __construct(Manager $manager, $databaseName, array $options = [])
4545
{
46-
$collectionOptions = [];
4746
$options += [
48-
'bucketName' => 'fs',
4947
'chunkSizeBytes' => 261120,
48+
'bucketName' => 'fs'
5049
];
5150
$this->databaseName = (string) $databaseName;
5251
$this->options = $options;
@@ -80,7 +79,7 @@ public function openUploadStream($filename, array $options = [])
8079
*/
8180
public function uploadFromStream($filename, $source, array $options = [])
8281
{
83-
$options['chunkSizeBytes'] = $this->options['chunkSizeBytes'];
82+
$options+= ['chunkSizeBytes' => $this->options['chunkSizeBytes']];
8483
$gridFsStream = new GridFsUpload($this->collectionsWrapper, $filename, $options);
8584
return $gridFsStream->uploadFromStream($source);
8685
}
@@ -92,11 +91,11 @@ public function uploadFromStream($filename, $source, array $options = [])
9291
*/
9392
public function openDownloadStream(\MongoDB\BSON\ObjectId $id)
9493
{
95-
$options = [
96-
'collectionsWrapper' => $this->collectionsWrapper
97-
];
98-
$context = stream_context_create(['gridfs' => $options]);
99-
return fopen(sprintf('gridfs://%s/%s', $this->databaseName, $id), 'r', false, $context);
94+
$file = $this->collectionsWrapper->getFilesCollection()->findOne(['_id' => $id]);
95+
if (is_null($file)) {
96+
throw new \MongoDB\Exception\GridFSFileNotFoundException($id, $this->collectionsWrapper->getFilesCollection()->getNameSpace());
97+
}
98+
return $this->openDownloadStreamByFile($file);
10099
}
101100
/**
102101
* Downloads the contents of the stored file specified by id and writes
@@ -106,7 +105,11 @@ public function openDownloadStream(\MongoDB\BSON\ObjectId $id)
106105
*/
107106
public function downloadToStream(\MongoDB\BSON\ObjectId $id, $destination)
108107
{
109-
$gridFsStream = new GridFsDownload($this->collectionsWrapper, $id);
108+
$file = $this->collectionsWrapper->getFilesCollection()->findOne(['_id' => $id]);
109+
if (is_null($file)) {
110+
throw new \MongoDB\Exception\GridFSFileNotFoundException($id, $this->collectionsWrapper->getFilesCollection()->getNameSpace());
111+
}
112+
$gridFsStream = new GridFsDownload($this->collectionsWrapper, $file);
110113
$gridFsStream->downloadToStream($destination);
111114
}
112115
/**
@@ -122,7 +125,6 @@ public function delete(\MongoDB\BSON\ObjectId $id)
122125
if (is_null($file)) {
123126
throw new \MongoDB\Exception\GridFSFileNotFoundException($id, $this->collectionsWrapper->getFilesCollection()->getNameSpace());
124127
}
125-
126128
$this->collectionsWrapper->getFilesCollection()->deleteOne(['_id' => $id]);
127129
}
128130
/**
@@ -133,12 +135,8 @@ public function delete(\MongoDB\BSON\ObjectId $id)
133135
*/
134136
public function openDownloadStreamByName($filename, $revision = -1)
135137
{
136-
$file = $this->bucket->findFileRevision($filename, $revision);
137-
$options = ['bucket' => $this->bucket,
138-
'file' => $file
139-
];
140-
$context = stream_context_create(['gridfs' => $options]);
141-
return fopen(sprintf('gridfs://%s/%s', $this->bucket->getDatabaseName(), $filename), 'r', false, $context);
138+
$file = $this->findFileRevision($filename, $revision);
139+
return $this->openDownloadStreamByFile($file);
142140
}
143141
/**
144142
* Download a file from the GridFS bucket by name. Searches for the file by the specified name then loads data into the stream
@@ -150,7 +148,7 @@ public function openDownloadStreamByName($filename, $revision = -1)
150148
public function downloadToStreamByName($filename, $destination, $revision=-1)
151149
{
152150
$file = $this->findFileRevision($filename, $revision);
153-
$gridFsStream = new GridFsDownload($this->collectionsWrapper, null, $file);
151+
$gridFsStream = new GridFsDownload($this->collectionsWrapper, $file);
154152
$gridFsStream->downloadToStream($destination);
155153
}
156154
/**
@@ -163,8 +161,25 @@ public function find($filter, array $options =[])
163161
{
164162
return $this->collectionsWrapper->getFilesCollection()->find($filter, $options);
165163
}
166-
167-
164+
public function getCollectionsWrapper()
165+
{
166+
return $this->collectionsWrapper;
167+
}
168+
public function getDatabaseName(){
169+
return $this->databaseName;
170+
}
171+
private function openDownloadStreamByFile($file)
172+
{
173+
$options = ['collectionsWrapper' => $this->collectionsWrapper,
174+
'file' => $file
175+
];
176+
$context = stream_context_create(['gridfs' => $options]);
177+
//db/prefix/(filter criteria as BSON}
178+
// find criteria being MongoDB\BSON\fromPHP(['_id' => $file['_id']])
179+
// stream wrapper can explode('/', 3), which returns array of db, prefix, and BSON blob
180+
// MongoDB\BSON\toPHP(bson blob) yields find() criteria
181+
return fopen(sprintf('gridfs://%s/%s', $this->databaseName, $file->filename), 'r', false, $context);
182+
}
168183
private function findFileRevision($filename, $revision)
169184
{
170185
if ($revision < 0) {
@@ -181,8 +196,4 @@ private function findFileRevision($filename, $revision)
181196
}
182197
return $file;
183198
}
184-
public function getCollectionsWrapper()
185-
{
186-
return $this->collectionsWrapper;
187-
}
188199
}

src/GridFS/GridFSCollectionsWrapper.php

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,9 @@
1414
/**
1515
* Bucket abstracts the GridFS files and chunks collections.
1616
*
17-
* @api
1817
*/
18+
19+
//rename to context options
1920
class GridFSCollectionsWrapper
2021
{
2122
private $filesCollection;
@@ -44,9 +45,13 @@ class GridFSCollectionsWrapper
4445
public function __construct(Manager $manager, $databaseName, $options)
4546
{
4647
$collectionOptions = [];
47-
$options += [
48-
'bucketName' => 'fs',
49-
];
48+
49+
if (isset($options['bucketName']) && ! is_string($options['bucketName'])) {
50+
throw new InvalidArgumentTypeException('"bucketName" option', $options['bucketName'], 'string');
51+
}
52+
if (isset($options['chunkSizeBytes']) && ! is_integer($options['chunkSizeBytes'])) {
53+
throw new InvalidArgumentTypeException('"chunkSizeBytes" option', $options['chunkSizeBytes'], 'integer');
54+
}
5055
if (isset($options['readPreference'])) {
5156
if (! $options['readPreference'] instanceof ReadPreference) {
5257
throw new InvalidArgumentTypeException('"readPreference" option', $options['readPreference'], 'MongoDB\Driver\ReadPreference');

src/GridFS/GridFsDownload.php

Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,8 @@
55
use MongoDB\Exception\RuntimeException;
66
use MongoDB\BSON\ObjectId;
77
/**
8-
* GridFsupload abstracts the processes of inserting into a GridFSBucket
8+
* GridFSDownload abstracts the processes of downloading from a GridFSBucket
99
*
10-
* @api
1110
*/
1211
class GridFsDownload
1312
{
@@ -33,24 +32,17 @@ class GridFsDownload
3332
*/
3433
public function __construct(
3534
GridFSCollectionsWrapper $collectionsWrapper,
36-
$objectId,
37-
$file = null
35+
$file
3836
)
3937
{
4038
$this->collectionsWrapper = $collectionsWrapper;
41-
42-
if(!is_null($file)) {
43-
$this->file = $file;
44-
} else {
45-
$this->file = $collectionsWrapper->getFilesCollection()->findOne(['_id' => $objectId]);
46-
if (is_null($this->file)) {
47-
throw new \MongoDB\Exception\GridFSFileNotFoundException($objectId, $this->collectionsWrapper->getFilesCollection()->getNameSpace());
48-
}
49-
}
39+
$this->file = $file;
40+
$cursor = $this->collectionsWrapper->getChunksCollection()->find(['files_id' => $this->file->_id], ['sort' => ['n' => 1]]);
41+
$this->chunksIterator = new \IteratorIterator($cursor);
5042
if ($this->file->length >= 0) {
51-
$cursor = $this->collectionsWrapper->getChunksCollection()->find(['files_id' => $this->file->_id], ['sort' => ['n' => 1]]);
52-
$this->chunksIterator = new \IteratorIterator($cursor);
5343
$this->numChunks = ceil($this->file->length / $this->file->chunkSize);
44+
} else {
45+
$this->numChunks = 0;
5446
}
5547
$this->buffer = fopen('php://temp', 'w+');
5648
}
@@ -88,15 +80,22 @@ public function downloadNumBytes($numToRead) {
8880

8981
while(strlen($output) < $numToRead && $this->advanceChunks()) {
9082
$bytesLeft = $numToRead - strlen($output);
91-
$output .= substr($this->chunksIterator->current()->data, 0, $bytesLeft);
83+
$output .= substr($this->chunksIterator->current()->data->getData(), 0, $bytesLeft);
9284
}
93-
if ($bytesLeft < strlen($this->chunksIterator->current()->data)) {
94-
fwrite($this->buffer, substr($this->chunksIterator->current()->data, $bytesLeft));
85+
if ($this->file->length > 0 && $bytesLeft < strlen($this->chunksIterator->current()->data->getData())) {
86+
fwrite($this->buffer, substr($this->chunksIterator->current()->data->getData(), $bytesLeft));
9587
$this->bufferEmpty=false;
9688
}
9789
return $output;
9890
}
99-
91+
public function getSize()
92+
{
93+
return $this->file->length;
94+
}
95+
public function getId()
96+
{
97+
return $this->file->_id;
98+
}
10099
private function advanceChunks()
101100
{
102101
if($this->chunkOffset >= $this->numChunks) {

src/GridFS/GridFsUpload.php

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
/**
1010
* GridFsupload abstracts the processes of inserting into a GridFSBucket
1111
*
12-
* @api
1312
*/
1413
class GridFsUpload
1514
{
@@ -56,8 +55,8 @@ public function __construct(
5655
$this->collectionsWrapper = $collectionsWrapper;
5756
$this->buffer = fopen('php://temp', 'w+');
5857
$this->chunkSize = $options['chunkSizeBytes'];
59-
60-
$uploadDate = time();
58+
$time = $this->millitime();
59+
$uploadDate = new \MongoDB\BSON\UTCDateTime($time);
6160
$objectId = new \MongoDB\BSON\ObjectId();
6261
$main_file = [
6362
"chunkSize" => $this->chunkSize,
@@ -151,6 +150,14 @@ public function close()
151150
fclose($this->buffer);
152151
$this->fileCollectionInsert();
153152
}
153+
public function getSize()
154+
{
155+
return $this->length;
156+
}
157+
public function getId()
158+
{
159+
return $this->file["_id"];
160+
}
154161
private function insertChunk($data)
155162
{
156163
$toUpload = ["files_id" => $this->file['_id'], "n" => $this->chunkOffset, "data" => new \MongoDB\BSON\Binary($data, \MongoDB\BSON\Binary::TYPE_GENERIC)];
@@ -167,4 +174,10 @@ private function fileCollectionInsert()
167174
$this->collectionsWrapper->fileInsert($this->file);
168175
return $this->file['_id'];
169176
}
177+
//from: http://stackoverflow.com/questions/3656713/how-to-get-current-time-in-milliseconds-in-php
178+
private function millitime() {
179+
$microtime = microtime();
180+
$comps = explode(' ', $microtime);
181+
return sprintf('%d%03d', $comps[1], $comps[0] * 1000);
182+
}
170183
}

src/GridFS/StreamWrapper.php

Lines changed: 44 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -33,26 +33,29 @@ public static function register()
3333
}
3434
stream_wrapper_register('gridfs', get_called_class(), STREAM_IS_URL);
3535
}
36-
private function initProtocol($path)
37-
{
38-
$parsed_path = parse_url($path);
39-
$this->databaseName = $parsed_path["host"];
40-
$this->identifier = substr($parsed_path["path"], 1);
41-
}
4236
public function stream_write($data)
4337
{
4438
$this->gridFsStream->insertChunks($data);
4539
return strlen($data);
4640
}
47-
public function stream_read($count) {
41+
public function stream_read($count)
42+
{
4843
return $this->gridFsStream->downloadNumBytes($count);
4944
}
50-
public function stream_eof() {
45+
public function stream_eof()
46+
{
5147
return $this->gridFsStream->isEOF();
5248
}
53-
public function stream_close() {
49+
public function stream_close()
50+
{
5451
$this->gridFsStream->close();
55-
52+
}
53+
public function stream_stat()
54+
{
55+
$stat = $this->getStatTemplate();
56+
$stat[7] = $stat['size'] = $this->gridFsStream->getSize();
57+
$stat[2] = $stat['mode'] = $this->mode;
58+
return $stat;
5659
}
5760
public function stream_open($path, $mode, $options, &$openedPath)
5861
{
@@ -75,12 +78,37 @@ public function openWriteStream() {
7578

7679
public function openReadStream() {
7780
$context = stream_context_get_options($this->context);
78-
if(isset($context['gridfs']['file'])){
79-
$this->gridFsStream = new GridFsDownload($this->collectionsWrapper, null, $context['gridfs']['file']);
80-
} else {
81-
$objectId = new \MongoDB\BSON\ObjectId($this->identifier);
82-
$this->gridFsStream = new GridFsDownload($this->collectionsWrapper, $objectId);
83-
}
81+
$this->gridFsStream = new GridFsDownload($this->collectionsWrapper, $context['gridfs']['file']);
8482
return true;
8583
}
84+
85+
/**
86+
* Gets a URL stat template with default values
87+
* from https://github.com/aws/aws-sdk-php/blob/master/src/S3/StreamWrapper.php
88+
* @return array
89+
*/
90+
private function getStatTemplate()
91+
{
92+
return [
93+
0 => 0, 'dev' => 0,
94+
1 => 0, 'ino' => 0,
95+
2 => 0, 'mode' => 0,
96+
3 => 0, 'nlink' => 0,
97+
4 => 0, 'uid' => 0,
98+
5 => 0, 'gid' => 0,
99+
6 => -1, 'rdev' => -1,
100+
7 => 0, 'size' => 0,
101+
8 => 0, 'atime' => 0,
102+
9 => 0, 'mtime' => 0,
103+
10 => 0, 'ctime' => 0,
104+
11 => -1, 'blksize' => -1,
105+
12 => -1, 'blocks' => -1,
106+
];
107+
}
108+
private function initProtocol($path)
109+
{
110+
$parsed_path = parse_url($path);
111+
$this->databaseName = $parsed_path["host"];
112+
$this->identifier = substr($parsed_path["path"], 1);
113+
}
86114
}

0 commit comments

Comments
 (0)