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 6ebcc42

Browse files
committed
Use bucket alias for stream wrapper
1 parent 082080f commit 6ebcc42

File tree

5 files changed

+46
-54
lines changed

5 files changed

+46
-54
lines changed

‎src/GridFS/Bucket.php‎

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
use function array_intersect_key;
4040
use function array_key_exists;
4141
use function assert;
42+
use function explode;
4243
use function fopen;
4344
use function get_resource_type;
4445
use function in_array;
@@ -55,14 +56,10 @@
5556
use function property_exists;
5657
use function sprintf;
5758
use function str_contains;
58-
use function str_starts_with;
5959
use function stream_context_create;
6060
use function stream_copy_to_stream;
6161
use function stream_get_meta_data;
6262
use function stream_get_wrappers;
63-
use function strlen;
64-
use function substr;
65-
use function urldecode;
6663
use function urlencode;
6764

6865
/**
@@ -596,12 +593,18 @@ public function openUploadStream(string $filename, array $options = [])
596593
* For legacy applications that needs to open stream from a string, the bucket can be registered in the stream
597594
* wrapper to be used by default when no GridFS context is provided.
598595
* Supported file name must follow this pattern:
599-
* gridfs://<database_name>/<bucket_name>.fs/<file_name>
596+
* gridfs://<bucket-alias>/<filename>
597+
*
598+
* @param non-empty-string string $alias The alias to use for the bucket
600599
*/
601-
public function registerAsDefaultStreamWrapper(): void
600+
public function registerGlobalAliasForResolutionInStandaloneStreamOpen(string$alias): void
602601
{
602+
if ($alias === '' || str_contains($alias, '/')) {
603+
throw new InvalidArgumentException(sprintf('The bucket alias must be a non-empty string without any slash, "%s" given', $alias));
604+
}
605+
603606
// Use a closure to expose the private method into another class
604-
StreamWrapper::setDefaultContextResolver(fn (string $path, string $mode) => $this->resolveStreamContext($path, $mode));
607+
StreamWrapper::setContextResolver($alias, fn (string $path, string $mode) => $this->resolveStreamContext($path, $mode));
605608
}
606609

607610
/**
@@ -775,10 +778,9 @@ private function registerStreamWrapper(): void
775778
}
776779

777780
/**
778-
* Create a stream context from
781+
* Create a stream context from the path and mode provided to fopen().
779782
*
780-
* @see StreamWrapper::setDefaultContextResolver()
781-
* @see stream_context_create()
783+
* @see StreamWrapper::setContextResolver()
782784
*
783785
* @param string $path The full url provided to fopen(). It contains the filename.
784786
* gridfs://database_name/collection_name.files/file_name
@@ -787,13 +789,8 @@ private function registerStreamWrapper(): void
787789
*/
788790
private function resolveStreamContext(string $path, string $mode): ?array
789791
{
790-
// The file can be read only if it belongs to this bucket
791-
$basePath = $this->createPathForFile((object) ['_id' => '']);
792-
if (! str_starts_with($path, $basePath)) {
793-
return null;
794-
}
795-
796-
$filename = urldecode(substr($path, strlen($basePath)));
792+
$parts = explode('/', $path, 4);
793+
$filename = $parts[3] ?? '';
797794

798795
if (str_contains($mode, 'r')) {
799796
$file = $this->collectionWrapper->findFileByFilenameAndRevision($filename, -1);

‎src/GridFS/StreamWrapper.php‎

Lines changed: 20 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
use MongoDB\BSON\UTCDateTime;
2222

2323
use function assert;
24+
use function count;
2425
use function explode;
2526
use function in_array;
2627
use function is_array;
@@ -59,8 +60,8 @@ class StreamWrapper
5960
/** @var ReadableStream|WritableStream|null */
6061
private $stream;
6162

62-
/** @var Closure(string, string): ContextOptions|null */
63-
private static ?Closure$contextResolver = null;
63+
/** @var array<string, Closure(string, string): ContextOptions|null> */
64+
private static array$contextResolvers = [];
6465

6566
public function __destruct()
6667
{
@@ -100,9 +101,9 @@ public static function register(string $protocol = 'gridfs'): void
100101
*
101102
* @param Closure(string, string):ContextOptions|null $resolver
102103
*/
103-
public static function setDefaultContextResolver(?Closure $resolver): void
104+
public static function setContextResolver(string$name, ?Closure $resolver): void
104105
{
105-
self::$contextResolver = $resolver;
106+
self::$contextResolvers[$name] = $resolver;
106107
}
107108

108109
/**
@@ -144,24 +145,34 @@ public function stream_eof(): bool
144145
*/
145146
public function stream_open(string $path, string $mode, int $options, ?string &$openedPath): bool
146147
{
147-
$protocol = $this->initProtocol($path);
148+
[$protocol] = explode('://', $path, 2);
148149

149150
assert(is_resource($this->context));
150151
$contextOptions = stream_context_get_options($this->context)[$protocol] ?? null;
151152

152153
if ($contextOptions === null) {
153-
if (! isset(self::$contextResolver)) {
154+
$parts = explode('/', $path, 4);
155+
156+
if (count($parts) < 4) {
157+
if ($options & STREAM_REPORT_ERRORS) {
158+
trigger_error(sprintf('Invalid GridFS file name: "%s"', $path), E_USER_WARNING);
159+
}
160+
161+
return false;
162+
}
163+
164+
if (! isset(self::$contextResolvers[$parts[2]])) {
154165
if ($options & STREAM_REPORT_ERRORS) {
155-
trigger_error(sprintf('No stream context provided for "%s" protocol. Use "%s::setDefaultContextResolver() to provide a default context."', $protocol, self::class), E_USER_WARNING);
166+
trigger_error(sprintf('Unknown GridFS Bucket "%1$s". Call $bucket->asStreamWrap(\'%1$s\') on the Bucket you want to access.', $parts[2]), E_USER_WARNING);
156167
}
157168

158169
return false;
159170
}
160171

161-
$contextOptions = (self::$contextResolver)($path, $mode);
172+
$contextOptions = self::$contextResolvers[$parts[2]]($path, $mode);
162173
if ($contextOptions === null) {
163174
if ($options & STREAM_REPORT_ERRORS) {
164-
trigger_error(sprintf('File not found "%s" with the default GridFS resolver.', $path), E_USER_WARNING);
175+
trigger_error(sprintf('File not found "%s".', $path), E_USER_WARNING);
165176
}
166177

167178
return false;
@@ -325,18 +336,6 @@ private function getStatTemplate(): array
325336
];
326337
}
327338

328-
/**
329-
* Initialize the protocol from the given path.
330-
*
331-
* @see StreamWrapper::stream_open()
332-
*/
333-
private function initProtocol(string $path): string
334-
{
335-
$parts = explode('://', $path, 2);
336-
337-
return $parts[0] ?: 'gridfs';
338-
}
339-
340339
/**
341340
* Initialize the internal stream for reading.
342341
*

‎tests/GridFS/BucketFunctionalTest.php‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -904,7 +904,7 @@ public function testResolveStreamContextForRead(): void
904904
$method->setAccessible(true);
905905
$method->setAccessible(true);
906906

907-
$context = $method->invokeArgs($this->bucket, [$this->getFileUrl('filename'), 'rb']);
907+
$context = $method->invokeArgs($this->bucket, ['gridfs://bucket/filename', 'rb']);
908908

909909
$this->assertIsArray($context);
910910
$this->assertArrayHasKey('collectionWrapper', $context);
@@ -921,7 +921,7 @@ public function testResolveStreamContextForWrite(): void
921921
$method->setAccessible(true);
922922
$method->setAccessible(true);
923923

924-
$context = $method->invokeArgs($this->bucket, [$this->getFileUrl('filename'), 'wb']);
924+
$context = $method->invokeArgs($this->bucket, ['gridfs://bucket/filename', 'wb']);
925925

926926
$this->assertIsArray($context);
927927
$this->assertArrayHasKey('collectionWrapper', $context);

‎tests/GridFS/FunctionalTestCase.php‎

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ public function setUp(): void
3838

3939
public function tearDown(): void
4040
{
41-
StreamWrapper::setDefaultContextResolver(null);
41+
StreamWrapper::setContextResolver('default', null);
4242

4343
parent::tearDown();
4444
}
@@ -70,9 +70,4 @@ protected function createStream(string $data = '')
7070

7171
return $stream;
7272
}
73-
74-
protected function getFileUrl(string $filename): string
75-
{
76-
return sprintf('gridfs://%s/%s.files/%s', $this->bucket->getDatabaseName(), $this->bucket->getBucketName(), $filename);
77-
}
7873
}

‎tests/GridFS/StreamWrapperFunctionalTest.php‎

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -206,35 +206,36 @@ public function testWritableStreamWrite(): void
206206
$this->assertSame(6, fwrite($stream, 'foobar'));
207207
}
208208

209-
public function testStreamWithDefaultResolver(): void
209+
public function testStreamWithContextResolver(): void
210210
{
211-
$this->bucket->registerAsDefaultStreamWrapper();
211+
$filename = 'gridfs://bucket/path/to/filename';
212+
$this->bucket->registerGlobalAliasForResolutionInStandaloneStreamOpen('bucket');
212213

213-
$stream = fopen($this->getFileUrl('filename'), 'wb');
214+
$stream = fopen($filename, 'wb');
214215

215216
$this->assertSame(6, fwrite($stream, 'foobar'));
216217
$this->assertTrue(fclose($stream));
217218

218-
$stream = fopen($this->getFileUrl('filename'), 'rb');
219+
$stream = fopen($filename, 'rb');
219220

220221
$this->assertSame('foobar', fread($stream, 10));
221222
$this->assertTrue(fclose($stream));
222223
}
223224

224-
public function testFileNoFoundWithDefaultResolver(): void
225+
public function testFileNoFoundWithContextResolver(): void
225226
{
226-
$this->bucket->registerAsDefaultStreamWrapper();
227+
$this->bucket->registerGlobalAliasForResolutionInStandaloneStreamOpen('default');
227228

228229
$this->expectWarning();
229-
$stream = fopen($this->getFileUrl('filename'), 'r');
230+
$stream = fopen('gridfs://bucket/path/to/filename', 'r');
230231

231232
$this->assertFalse($stream);
232233
}
233234

234235
public function testFileNoFoundWithoutDefaultResolver(): void
235236
{
236237
$this->expectWarning();
237-
$stream = fopen($this->getFileUrl('filename'), 'r');
238+
$stream = fopen('gridfs://bucket/path/to/filename', 'r');
238239

239240
$this->assertFalse($stream);
240241
}

0 commit comments

Comments
(0)

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