From e1eb3fda0488b3279a86f9a4bad225f9cbaeae85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Tamarelle?= Date: Wed, 22 Nov 2023 17:27:44 +0100 Subject: [PATCH 1/2] PHPLIB-1313 Ensure the GridFS stream is saved when the script ends --- CHANGELOG.md | 2 +- src/GridFS/StreamWrapper.php | 9 +++++---- tests/GridFS/StreamWrapperFunctionalTest.php | 17 +++++++++++++++++ tests/GridFS/scripts/stream-autoclose.php | 13 +++++++++++++ 4 files changed, 36 insertions(+), 5 deletions(-) create mode 100644 tests/GridFS/scripts/stream-autoclose.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 5dc7290c5..9cdc381ea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,4 +4,4 @@ ## 1.18.0 (unreleased) * Add `addSubscriber` and `removeSubscriber` methods to the `Client` class to ease dependency injection configuration - + * Fix GridFS stream closing when the PHP script ends diff --git a/src/GridFS/StreamWrapper.php b/src/GridFS/StreamWrapper.php index 71a36d07b..c9758cf9a 100644 --- a/src/GridFS/StreamWrapper.php +++ b/src/GridFS/StreamWrapper.php @@ -53,10 +53,11 @@ class StreamWrapper public function __destruct() { - /* This destructor is a workaround for PHP trying to use the stream well - * after all objects have been destructed. This can cause autoloading - * issues and possibly segmentation faults during PHP shutdown. */ - $this->stream = null; + /* Ensure the stream is closed so the last chunk is written. This is + * necessary because PHP would close the stream after all objects have + * been destructed. This can cause autoloading issues and possibly + * segmentation faults during PHP shutdown. */ + $this->stream_close(); } /** diff --git a/tests/GridFS/StreamWrapperFunctionalTest.php b/tests/GridFS/StreamWrapperFunctionalTest.php index ebe6eb9ba..b513e5a8a 100644 --- a/tests/GridFS/StreamWrapperFunctionalTest.php +++ b/tests/GridFS/StreamWrapperFunctionalTest.php @@ -5,6 +5,8 @@ use MongoDB\BSON\Binary; use MongoDB\BSON\UTCDateTime; +use function escapeshellarg; +use function exec; use function fclose; use function feof; use function fread; @@ -12,6 +14,7 @@ use function fstat; use function fwrite; +use const PHP_BINARY; use const SEEK_CUR; use const SEEK_END; use const SEEK_SET; @@ -204,4 +207,18 @@ public function testWritableStreamWrite(): void $this->assertSame(6, fwrite($stream, 'foobar')); } + + public function testAutocloseOnScriptEnd(): void + { + $command = PHP_BINARY . ' ' . escapeshellarg(__DIR__ . '/scripts/stream-autoclose.php'); + exec($command, $output, $exitCode); + + $this->assertSame([], $output); + $this->assertSame(0, $exitCode); + + $fileDocument = $this->filesCollection->findOne(['filename' => 'hello.txt']); + + $this->assertNotNull($fileDocument); + $this->assertSame(14, $fileDocument->length); + } } diff --git a/tests/GridFS/scripts/stream-autoclose.php b/tests/GridFS/scripts/stream-autoclose.php new file mode 100644 index 000000000..53610d6de --- /dev/null +++ b/tests/GridFS/scripts/stream-autoclose.php @@ -0,0 +1,13 @@ +selectDatabase(getenv('MONGODB_DATABASE') ?: 'phplib_test'); +$gridfs = $database->selectGridFSBucket(); +$stream = $gridfs->openUploadStream('hello.txt'); +fwrite($stream, 'Hello MongoDB!'); + +// The WriteStream must be closed and the file inserted automatically From 169785230e16dac0b7a90d84fef66313e233c817 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Tamarelle?= Date: Thu, 23 Nov 2023 15:23:31 +0100 Subject: [PATCH 2/2] Update existing test instead of creating a new identical one --- tests/GridFS/BucketFunctionalTest.php | 29 ++++++++++++++------ tests/GridFS/StreamWrapperFunctionalTest.php | 17 ------------ tests/GridFS/scripts/stream-autoclose.php | 13 --------- 3 files changed, 21 insertions(+), 38 deletions(-) delete mode 100644 tests/GridFS/scripts/stream-autoclose.php diff --git a/tests/GridFS/BucketFunctionalTest.php b/tests/GridFS/BucketFunctionalTest.php index c87003e24..6cf38c729 100644 --- a/tests/GridFS/BucketFunctionalTest.php +++ b/tests/GridFS/BucketFunctionalTest.php @@ -23,6 +23,7 @@ use function array_merge; use function call_user_func; use function current; +use function escapeshellarg; use function exec; use function fclose; use function fopen; @@ -39,7 +40,7 @@ use function strncasecmp; use function substr; -use const PHP_EOL; +use const PHP_BINARY; use const PHP_OS; /** @@ -853,21 +854,33 @@ public function testDanglingOpenWritableStream(): void $this->markTestSkipped('Test does not apply to Windows'); } - $path = __DIR__ . '/../../vendor/autoload.php'; - $command = <<test->selectGridFSBucket()->openUploadStream('filename', ['disableMD5' => true]);" 2>&1 -CMD; + $code = <<<'PHP' + require '%s'; + $client = new \MongoDB\Client(getenv('MONGODB_URI') ?: 'mongodb://127.0.0.1:27017/?serverSelectionTimeoutMS=100'); + $database = $client->selectDatabase(getenv('MONGODB_DATABASE') ?: 'phplib_test'); + $gridfs = $database->selectGridFSBucket(); + $stream = $gridfs->openUploadStream('hello.txt', ['disableMD5' => true]); + fwrite($stream, 'Hello MongoDB!'); + PHP; @exec( - $command, + implode(' ', [ + PHP_BINARY, + '-r', + escapeshellarg(sprintf($code, __DIR__ . '/../../vendor/autoload.php')), + '2>&1', + ]), $output, $return, ); + $this->assertSame([], $output); $this->assertSame(0, $return); - $output = implode(PHP_EOL, $output); - $this->assertSame('', $output); + $fileDocument = $this->filesCollection->findOne(['filename' => 'hello.txt']); + + $this->assertNotNull($fileDocument); + $this->assertSame(14, $fileDocument->length); } /** diff --git a/tests/GridFS/StreamWrapperFunctionalTest.php b/tests/GridFS/StreamWrapperFunctionalTest.php index b513e5a8a..ebe6eb9ba 100644 --- a/tests/GridFS/StreamWrapperFunctionalTest.php +++ b/tests/GridFS/StreamWrapperFunctionalTest.php @@ -5,8 +5,6 @@ use MongoDB\BSON\Binary; use MongoDB\BSON\UTCDateTime; -use function escapeshellarg; -use function exec; use function fclose; use function feof; use function fread; @@ -14,7 +12,6 @@ use function fstat; use function fwrite; -use const PHP_BINARY; use const SEEK_CUR; use const SEEK_END; use const SEEK_SET; @@ -207,18 +204,4 @@ public function testWritableStreamWrite(): void $this->assertSame(6, fwrite($stream, 'foobar')); } - - public function testAutocloseOnScriptEnd(): void - { - $command = PHP_BINARY . ' ' . escapeshellarg(__DIR__ . '/scripts/stream-autoclose.php'); - exec($command, $output, $exitCode); - - $this->assertSame([], $output); - $this->assertSame(0, $exitCode); - - $fileDocument = $this->filesCollection->findOne(['filename' => 'hello.txt']); - - $this->assertNotNull($fileDocument); - $this->assertSame(14, $fileDocument->length); - } } diff --git a/tests/GridFS/scripts/stream-autoclose.php b/tests/GridFS/scripts/stream-autoclose.php deleted file mode 100644 index 53610d6de..000000000 --- a/tests/GridFS/scripts/stream-autoclose.php +++ /dev/null @@ -1,13 +0,0 @@ -selectDatabase(getenv('MONGODB_DATABASE') ?: 'phplib_test'); -$gridfs = $database->selectGridFSBucket(); -$stream = $gridfs->openUploadStream('hello.txt'); -fwrite($stream, 'Hello MongoDB!'); - -// The WriteStream must be closed and the file inserted automatically