How do I use MongoDB\GridFS\StreamWrapper?

Included in the PHP 1.8 MongoDB driver library is a component called StreamWrapper. (in the MongoDB\GridFS namespace.) This component is compatible with the PHP stream_wrapper_register() function. This allows native PHP functions such as fopen() and fwrite() to access GridFS as if it were a file system.

I would like to use StreamWrapper but I cannot find any documentation on how to do so. I know you instantiate it using StreamWrapper::register() but that does not work alone. I have taken a look through the code and it seems I need to set a stream context option called ‘collectionWrapper’ but beyond that I am stumped. I have spent a long time looking for any relevant documentation too but have not been able to find any.

Do you know either where I can find documentation for the object or can you give me an example of how to use it?

I have taken some more time today to go through the code and experiment. I can tell that the Stream Wrapper is instantiated automatically when you create a new GridFS\Bucket so my code now initialises a bucket on start-up.

But. That does not solve the problem. I can use the openUploadStream() method of the Bucket object to get a file handle and can write to it, but I need to use native methods as I have third-party code that uses the built-in PHP file system functions. Help! :pleading_face:

The MongoDB\GridFS\StreamWrapper class is intentionally undocumented, as it’s an internal class used by the library’s own GridFS implementation. The same applies to the CollectionWrapper, ReadableStream, and WritableStream classes in the same namespace. Only MongoDB\GridFS\Bucket and related exception classes are intended to be public.

The GridFS tutorial in the library documentation should help you get started with the public API.

PHP doesn’t currently allow any way to define private functions and classes, but we’ve attempted to signal our intention by adding @internal to their doc blocks.

1 Like

What “native methods” are you referring to? It may be helpful to share some code and highlight exactly what is incompatible. Note that the Bucket API provides two ways to write files. One method gives you a stream resource, which you can use with PHP’s file IO functions, and the second method exhaustively reads a stream that you provide into GridFS (without needing to work with PHP’s functions).

@jmikola Thanks for the response. This is what I needed to know, especially with regard to why StreamWrapper is undocumented! (Saves me tearing my hair out trying to get it working.)

My issue is that I have a third-party library that caches to the file system. (A template library.) I can provide a path for the cache in any supported form but the library uses the built in PHP file handling functions, e.g. fopen(), fwrite(), file_put_contents(), etc. My architecture makes using local file systems on servers impractical. I had thought I could instantiate and use StreamWrapper to provide a file system-alike wrapper for the built-in functions. However, given that isn’t the case I suppose I will need to write my own custom PHP stream wrapper object if I want to use GridFS in this way.

Ah, the GridFS StreamWrapper definitely won’t work out of the box in this scenario. If you’ve looked into it, you might have noticed that the file/path string really isn’t used at all. Excluding the actual data, which will be written to the stream, we supply everything in the stream context (i.e. fourth parameter for fopen). This likely deviates from other stream wrappers you might have encountered, such as AWS SDK’s S3 adapter.

I agree that you’ll probably need to create your own stream wrapper that internally uses the library’s Bucket API. You may also see better performance by collecting data in memory (e.g. php://memory) and waiting until fclose to upload that stream to GridFS in one shot via uploadFromStream. Doing so should minimize the amount of interaction with the MongoDB driver.

That’s an excellent suggestion, thanks. I already batch all the database changes and push them at the end of script processing so this would be straightforward.

Thanks for your help on this. I am going to mark this as the solution!