Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit c735a2a

Browse files
committed
wip
1 parent a3c0b97 commit c735a2a

File tree

4 files changed

+455
-1
lines changed

4 files changed

+455
-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: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
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+
public $context;
20+
21+
private $bucket;
22+
private $filename;
23+
private $options;
24+
25+
private $protocol = 'gridfs';
26+
27+
private $at;
28+
private $hashContext;
29+
30+
/**
31+
* Constructs a writable upload stream.
32+
*
33+
* Supported options:
34+
*
35+
* * chunkSizeBytes (integer): The number of bytes per chunk of this file.
36+
* Defaults to the chunkSizeBytes of the Bucket.
37+
*
38+
* * metadata (document): User data for the "metadata" field of the files
39+
* collection document.
40+
*
41+
* The following options are deprecated:
42+
*
43+
* * aliases (string[]): An array of aliases (i.e. filenames). Applications
44+
* wishing to store aliases should add an aliases field to the metadata
45+
* document instead.
46+
*
47+
* * contentType (string): A valid MIME type. Applications wishing to store
48+
* a contentType should add a contentType field to the metadata document
49+
* instead.
50+
*
51+
* @param Bucket $bucket Database name
52+
* @param string $filename Filename
53+
* @param array $options Upload options
54+
* @throws InvalidArgumentException
55+
*/
56+
public function __construct(Bucket $bucket, $filename, array $options = [])
57+
{
58+
$options += [
59+
'chunkSizeBytes' => $bucket->getChunkSizeBytes(),
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['metadata']) && ! is_array($options['metadata']) && ! is_object($options['metadata'])) {
67+
throw new InvalidArgumentTypeException('"metadata" option', $options['metadata'], 'array or object');
68+
}
69+
70+
if (isset($options['aliases'])) {
71+
if ( ! is_array($options['aliases'])) {
72+
throw new InvalidArgumentTypeException('"aliases" option', $options['aliases'], 'array or object');
73+
}
74+
75+
$expectedIndex = 0;
76+
77+
foreach ($options['aliases'] as $i => $alias) {
78+
if ($i !== $expectedIndex) {
79+
throw new InvalidArgumentException(sprintf('"aliases" option is not a list (unexpected index: "%s")', $i));
80+
}
81+
82+
if ( ! is_string($alias)) {
83+
throw new InvalidArgumentTypeException(sprintf('$options["aliases"][%d]', $i), $alias, 'string');
84+
}
85+
86+
$expectedIndex += 1;
87+
}
88+
}
89+
90+
if (isset($options['contentType']) && ! is_string($options['contentType'])) {
91+
throw new InvalidArgumentTypeException('"contentType" option', $options['contentType'], 'string');
92+
}
93+
94+
$this->bucket = $bucket;
95+
$this->filename = (string) $filename;
96+
$this->options = $options;
97+
$this->hashContext = hash_init('md5');
98+
}
99+
100+
public function stream_write($data)
101+
{
102+
hash_update($this->hashContext, $data);
103+
104+
//fopen('php://memory', )
105+
}
106+
107+
/**
108+
* Register the GridFS stream wrapper.
109+
*
110+
* @param Manager $manager Manager instance from the driver
111+
* @param string $protocol Protocol to register
112+
*/
113+
public static function register(Manager $manager, $protocol = 'gridfs')
114+
{
115+
if (in_array($protocol, stream_get_wrappers())) {
116+
stream_wrapper_unregister($protocol);
117+
}
118+
119+
// Set the client passed in as the default stream context client
120+
stream_wrapper_register($protocol, get_called_class(), STREAM_IS_URL);
121+
$default = stream_context_get_options(stream_context_get_default());
122+
$default[$protocol]['manager'] = $manager;
123+
stream_context_set_default($default);
124+
}
125+
126+
public function stream_open($path, $mode, $options, &$openedPath)
127+
{
128+
$this->initProtocol($path);
129+
$this->params = $this->getDatabase($path);
130+
$this->mode = rtrim($mode, 'bt');
131+
132+
if ($errors = $this->validate($path, $this->mode)) {
133+
return $this->triggerError($errors);
134+
}
135+
136+
return $this->boolCall(function() use ($path) {
137+
switch ($this->mode) {
138+
case 'r': return $this->openReadStream($path);
139+
case 'a': return $this->openAppendStream($path);
140+
default: return $this->openWriteStream($path);
141+
}
142+
});
143+
}
144+
145+
private function validate($path, $mode)
146+
{
147+
$errors = [];
148+
149+
if (!in_array($mode, ['r', 'w', 'a', 'x'])) {
150+
$errors[] = "Mode not supported: {$mode}. "
151+
. "Use one 'r', 'w', 'a', or 'x'.";
152+
}
153+
154+
return $errors;
155+
}
156+
157+
/**
158+
* Trigger one or more errors
159+
*
160+
* @param string|array $errors Errors to trigger
161+
* @param mixed $flags If set to STREAM_URL_STAT_QUIET, then no
162+
* error or exception occurs
163+
*
164+
* @return bool Returns false
165+
* @throws \RuntimeException if throw_errors is true
166+
*/
167+
private function triggerError($errors, $flags = null)
168+
{
169+
// This is triggered with things like file_exists()
170+
if ($flags & STREAM_URL_STAT_QUIET) {
171+
return $flags & STREAM_URL_STAT_LINK
172+
// This is triggered for things like is_link()
173+
? $this->formatUrlStat(false)
174+
: false;
175+
}
176+
// This is triggered when doing things like lstat() or stat()
177+
trigger_error(implode("\n", (array) $errors), E_USER_WARNING);
178+
return false;
179+
}
180+
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /