Skip to content

Commit 478aff4

Browse files
committed
wip
1 parent ed4cfa1 commit 478aff4

File tree

4 files changed

+398
-1
lines changed

4 files changed

+398
-1
lines changed

composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
],
1212
"require": {
1313
"php": ">=5.4",
14-
"ext-mongodb": "^1.0.0"
14+
"ext-mongodb": "^1.0.0",
15+
"ext-hash": "*"
1516
},
1617
"autoload": {
1718
"psr-4": { "MongoDB\\": "src/" },

src/GridFS/Bucket.php

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
<?php
2+
3+
namespace MongoDB\GridFS;
4+
5+
use MongoDB\Collection;
6+
use MongoDB\Database;
7+
use MongoDB\BSON\ObjectId;
8+
use MongoDB\Driver\ReadPreference;
9+
use MongoDB\Driver\WriteConcern;
10+
use MongoDB\Exception\InvalidArgumentException;
11+
use MongoDB\Exception\InvalidArgumentTypeException;
12+
use MongoDB\Exception\RuntimeException;
13+
use MongoDB\Exception\UnexpectedValueException;
14+
15+
/**
16+
* Bucket abstracts the GridFS files and chunks collections.
17+
*
18+
* @api
19+
*/
20+
class Bucket
21+
{
22+
private $manager;
23+
private $databaseName;
24+
private $options;
25+
26+
private $filesCollection;
27+
private $chunksCollection;
28+
29+
private $ensuredIndexes = false;
30+
31+
/**
32+
* Constructs a GridFS bucket.
33+
*
34+
* Supported options:
35+
*
36+
* * bucketName (string): The bucket name, which will be used as a prefix
37+
* for the files and chunks collections. Defaults to "fs".
38+
*
39+
* * chunkSizeBytes (integer): The chunk size in bytes. Defaults to
40+
* 261120 (i.e. 255 KiB).
41+
*
42+
* * readPreference (MongoDB\Driver\ReadPreference): Read preference.
43+
*
44+
* * writeConcern (MongoDB\Driver\WriteConcern): Write concern.
45+
*
46+
* @param Manager $manager Manager instance from the driver
47+
* @param string $databaseName Database name
48+
* @param array $options Bucket options
49+
* @throws InvalidArgumentException
50+
*/
51+
public function __construct(Manager $manager, $databaseName, array $options = [])
52+
{
53+
$options += [
54+
'bucketName' => 'fs',
55+
'chunkSizeBytes' => 261120,
56+
];
57+
58+
if (isset($options['bucketName']) && ! is_string($options['bucketName'])) {
59+
throw new InvalidArgumentTypeException('"bucketName" option', $options['bucketName'], 'string');
60+
}
61+
62+
if (isset($options['chunkSizeBytes']) && ! is_integer($options['chunkSizeBytes'])) {
63+
throw new InvalidArgumentTypeException('"chunkSizeBytes" option', $options['chunkSizeBytes'], 'integer');
64+
}
65+
66+
if (isset($options['readPreference']) && ! $options['readPreference'] instanceof ReadPreference) {
67+
throw new InvalidArgumentTypeException('"readPreference" option', $options['readPreference'], 'MongoDB\Driver\ReadPreference');
68+
}
69+
70+
if (isset($options['writeConcern']) && ! $options['writeConcern'] instanceof WriteConcern) {
71+
throw new InvalidArgumentTypeException('"writeConcern" option', $options['writeConcern'], 'MongoDB\Driver\WriteConcern');
72+
}
73+
74+
$this->manager = $manager;
75+
$this->databaseName = (string) $databaseName;
76+
$this->options = $options;
77+
78+
$this->filesCollection = new Collection(
79+
$manager,
80+
sprintf('%s.%s.files', $this->databaseName, $options['bucketName']),
81+
isset($options['writeConcern']) ? $options['writeConcern'] : null,
82+
isset($options['readPreference']) ? $options['readPreference'] : null,
83+
);
84+
85+
$this->chunksCollection = new Collection(
86+
$manager,
87+
sprintf('%s.%s.chunks', $this->databaseName, $options['bucketName']),
88+
isset($options['writeConcern']) ? $options['writeConcern'] : null,
89+
isset($options['readPreference']) ? $options['readPreference'] : null,
90+
);
91+
}
92+
93+
/**
94+
* Opens a Stream for reading the contents of a file specified by ID.
95+
*
96+
* @param ObjectId $id
97+
* @return Stream
98+
*/
99+
public function openDownloadStream(ObjectId $id)
100+
{
101+
102+
}
103+
104+
/**
105+
* Return the chunkSizeBytes option for this Bucket.
106+
*
107+
* @return integer
108+
*/
109+
public function getChunkSizeBytes()
110+
{
111+
return $this->options['chunkSizeBytes'];
112+
}
113+
114+
/**
115+
* Opens a Stream for writing the contents of a file.
116+
*
117+
* @param ObjectId $id
118+
* @return Stream
119+
*/
120+
public function openUploadStream($filename, array $options = [])
121+
{
122+
123+
}
124+
125+
private function ensureIndexes()
126+
{
127+
// Indexes should only be ensured once before the first write operation
128+
if ($this->ensuredIndexes) {
129+
return;
130+
}
131+
132+
if ( ! $this->isFilesCollectionEmpty()) {
133+
return;
134+
}
135+
136+
$this->ensureFilesIndex();
137+
$this->ensureChunksIndex();
138+
139+
$this->ensuredIndexes = true;
140+
}
141+
142+
private function ensureChunksIndex()
143+
{
144+
foreach ($this->chunksCollection->listIndexes() as $index) {
145+
if ($index->isUnique() && $index->getKey() === ['files_id' => 1, 'n' => 1]) {
146+
return;
147+
}
148+
}
149+
150+
$this->chunksCollection->createIndex(['files_id' => 1, 'n' => 1], ['unique' => true]);
151+
}
152+
153+
private function ensureFilesIndex()
154+
{
155+
foreach ($this->filesCollection->listIndexes() as $index) {
156+
if ($index->getKey() === ['filename' => 1, 'uploadDate' => 1]) {
157+
return;
158+
}
159+
}
160+
161+
$this->filesCollection->createIndex(['filename' => 1, 'uploadDate' => 1]);
162+
}
163+
164+
private function isFilesCollectionEmpty()
165+
{
166+
return null === $this->filesCollection->findOne([], [
167+
'readPreference' => new ReadPreference(ReadPreference::RP_PRIMARY),
168+
'projection' => ['_id' => 1],
169+
]);
170+
}
171+
}

src/GridFS/StreamWrapper.php

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
<?php
2+
3+
namespace MongoDB\GridFS;
4+
5+
use MongoDB\Driver\Server;
6+
use MongoDB\Exception\InvalidArgumentException;
7+
use MongoDB\Exception\InvalidArgumentTypeException;
8+
use MongoDB\Exception\RuntimeException;
9+
use MongoDB\Exception\UnexpectedValueException;
10+
11+
/**
12+
* Stream wrapper for reading and writing a GridFS file.
13+
*
14+
* @internal
15+
* @see MongoDB\GridFS\Bucket::openUploadStream()
16+
*/
17+
class StreamWrapper
18+
{
19+
private $bucket;
20+
private $filename;
21+
private $options;
22+
23+
private $at;
24+
private $hashContext;
25+
26+
/**
27+
* Register the GridFS stream wrapper.
28+
*
29+
* @param Manager $manager Manager instance from the driver
30+
* @param string $protocol Protocol to register
31+
*/
32+
public static function register(Manager $manager, $protocol = 'gridfs')
33+
{
34+
if (in_array($protocol, stream_get_wrappers())) {
35+
stream_wrapper_unregister($protocol);
36+
}
37+
38+
// Set the client passed in as the default stream context client
39+
stream_wrapper_register($protocol, get_called_class(), STREAM_IS_URL);
40+
$default = stream_context_get_options(stream_context_get_default());
41+
$default[$protocol]['manager'] = $manager;
42+
stream_context_set_default($default);
43+
}
44+
45+
public function stream_open($path, $mode, $options, &$openedPath)
46+
47+
/**
48+
* Constructs a writable upload stream.
49+
*
50+
* Supported options:
51+
*
52+
* * chunkSizeBytes (integer): The number of bytes per chunk of this file.
53+
* Defaults to the chunkSizeBytes of the Bucket.
54+
*
55+
* * metadata (document): User data for the "metadata" field of the files
56+
* collection document.
57+
*
58+
* The following options are deprecated:
59+
*
60+
* * aliases (string[]): An array of aliases (i.e. filenames). Applications
61+
* wishing to store aliases should add an aliases field to the metadata
62+
* document instead.
63+
*
64+
* * contentType (string): A valid MIME type. Applications wishing to store
65+
* a contentType should add a contentType field to the metadata document
66+
* instead.
67+
*
68+
* @param Bucket $bucket Database name
69+
* @param string $filename Filename
70+
* @param array $options Upload options
71+
* @throws InvalidArgumentException
72+
*/
73+
public function __construct(Bucket $bucket, $filename, array $options = [])
74+
{
75+
$options += [
76+
'chunkSizeBytes' => $bucket->getChunkSizeBytes(),
77+
];
78+
79+
if (isset($options['chunkSizeBytes']) && ! is_integer($options['chunkSizeBytes'])) {
80+
throw new InvalidArgumentTypeException('"chunkSizeBytes" option', $options['chunkSizeBytes'], 'integer');
81+
}
82+
83+
if (isset($options['metadata']) && ! is_array($options['metadata']) && ! is_object($options['metadata'])) {
84+
throw new InvalidArgumentTypeException('"metadata" option', $options['metadata'], 'array or object');
85+
}
86+
87+
if (isset($options['aliases'])) {
88+
if ( ! is_array($options['aliases'])) {
89+
throw new InvalidArgumentTypeException('"aliases" option', $options['aliases'], 'array or object');
90+
}
91+
92+
$expectedIndex = 0;
93+
94+
foreach ($options['aliases'] as $i => $alias) {
95+
if ($i !== $expectedIndex) {
96+
throw new InvalidArgumentException(sprintf('"aliases" option is not a list (unexpected index: "%s")', $i));
97+
}
98+
99+
if ( ! is_string($alias)) {
100+
throw new InvalidArgumentTypeException(sprintf('$options["aliases"][%d]', $i), $alias, 'string');
101+
}
102+
103+
$expectedIndex += 1;
104+
}
105+
}
106+
107+
if (isset($options['contentType']) && ! is_string($options['contentType'])) {
108+
throw new InvalidArgumentTypeException('"contentType" option', $options['contentType'], 'string');
109+
}
110+
111+
$this->bucket = $bucket;
112+
$this->filename = (string) $filename;
113+
$this->options = $options;
114+
$this->hashContext = hash_init('md5');
115+
}
116+
117+
function stream_write($data)
118+
{
119+
hash_update($this->hashContext, $data);
120+
121+
fopen('php://memory', )
122+
}
123+
}

0 commit comments

Comments
 (0)