See Release Notes
Long Term Support Release
Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 and 401] [Versions 400 and 401]
1 <?php 2 /* 3 * Copyright 2016-present MongoDB, Inc. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * https://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 namespace MongoDB\GridFS; 19 20 use MongoDB\BSON\UTCDateTime; 21 22 use function assert; 23 use function explode; 24 use function in_array; 25 use function is_integer; 26 use function is_resource; 27 use function stream_context_get_options; 28 use function stream_get_wrappers; 29 use function stream_wrapper_register; 30 use function stream_wrapper_unregister; 31 32 use const SEEK_CUR; 33 use const SEEK_END; 34 use const SEEK_SET; 35 use const STREAM_IS_URL; 36 37 /** 38 * Stream wrapper for reading and writing a GridFS file. 39 * 40 * @internal 41 * @see Bucket::openUploadStream() 42 * @see Bucket::openDownloadStream() 43 */ 44 class StreamWrapper 45 { 46 /** @var resource|null Stream context (set by PHP) */ 47 public $context; 48 49 /** @var string|null */ 50 private $mode; 51 52 /** @var string|null */ 53 private $protocol; 54 55 /** @var ReadableStream|WritableStream|null */ 56 private $stream; 57 58 public function __destruct() 59 { 60 /* This destructor is a workaround for PHP trying to use the stream well 61 * after all objects have been destructed. This can cause autoloading 62 * issues and possibly segmentation faults during PHP shutdown. */ 63 $this->stream = null; 64 } 65 66 /** 67 * Return the stream's file document. 68 */ 69 public function getFile(): object 70 { 71 assert($this->stream !== null); 72 73 return $this->stream->getFile(); 74 } 75 76 /** 77 * Register the GridFS stream wrapper. 78 * 79 * @param string $protocol Protocol to use for stream_wrapper_register() 80 */ 81 public static function register(string $protocol = 'gridfs'): void 82 { 83 if (in_array($protocol, stream_get_wrappers())) { 84 stream_wrapper_unregister($protocol); 85 } 86 87 stream_wrapper_register($protocol, static::class, STREAM_IS_URL); 88 } 89 90 /** 91 * Closes the stream. 92 * 93 * @see https://php.net/manual/en/streamwrapper.stream-close.php 94 */ 95 public function stream_close(): void 96 { 97 if (! $this->stream) { 98 return; 99 } 100 101 $this->stream->close(); 102 } 103 104 /** 105 * Returns whether the file pointer is at the end of the stream. 106 * 107 * @see https://php.net/manual/en/streamwrapper.stream-eof.php 108 */ 109 public function stream_eof(): bool 110 { 111 if (! $this->stream instanceof ReadableStream) { 112 return false; 113 } 114 115 return $this->stream->isEOF(); 116 } 117 118 /** 119 * Opens the stream. 120 * 121 * @see https://php.net/manual/en/streamwrapper.stream-open.php 122 * @param string $path Path to the file resource 123 * @param string $mode Mode used to open the file (only "r" and "w" are supported) 124 * @param integer $options Additional flags set by the streams API 125 * @param string|null $openedPath Not used 126 */ 127 public function stream_open(string $path, string $mode, int $options, ?string &$openedPath): bool 128 { 129 $this->initProtocol($path); 130 $this->mode = $mode; 131 132 if ($mode === 'r') { 133 return $this->initReadableStream(); 134 } 135 136 if ($mode === 'w') { 137 return $this->initWritableStream(); 138 } 139 140 return false; 141 } 142 143 /** 144 * Read bytes from the stream. 145 * 146 * Note: this method may return a string smaller than the requested length 147 * if data is not available to be read. 148 * 149 * @see https://php.net/manual/en/streamwrapper.stream-read.php 150 * @param integer $length Number of bytes to read 151 */ 152 public function stream_read(int $length): string 153 { 154 if (! $this->stream instanceof ReadableStream) { 155 return ''; 156 } 157 158 return $this->stream->readBytes($length); 159 } 160 161 /** 162 * Return the current position of the stream. 163 * 164 * @see https://php.net/manual/en/streamwrapper.stream-seek.php 165 * @param integer $offset Stream offset to seek to 166 * @param integer $whence One of SEEK_SET, SEEK_CUR, or SEEK_END 167 * @return boolean True if the position was updated and false otherwise 168 */ 169 public function stream_seek(int $offset, int $whence = SEEK_SET): bool 170 { 171 assert($this->stream !== null); 172 173 $size = $this->stream->getSize(); 174 175 if ($whence === SEEK_CUR) { 176 $offset += $this->stream->tell(); 177 } 178 179 if ($whence === SEEK_END) { 180 $offset += $size; 181 } 182 183 // WritableStreams are always positioned at the end of the stream 184 if ($this->stream instanceof WritableStream) { 185 return $offset === $size; 186 } 187 188 if ($offset < 0 || $offset > $size) { 189 return false; 190 } 191 192 $this->stream->seek($offset); 193 194 return true; 195 } 196 197 /** 198 * Return information about the stream. 199 * 200 * @see https://php.net/manual/en/streamwrapper.stream-stat.php 201 */ 202 public function stream_stat(): array 203 { 204 assert($this->stream !== null); 205 206 $stat = $this->getStatTemplate(); 207 208 $stat[2] = $stat['mode'] = $this->stream instanceof ReadableStream 209 ? 0100444 // S_IFREG & S_IRUSR & S_IRGRP & S_IROTH 210 : 0100222; // S_IFREG & S_IWUSR & S_IWGRP & S_IWOTH 211 $stat[7] = $stat['size'] = $this->stream->getSize(); 212 213 $file = $this->stream->getFile(); 214 215 if (isset($file->uploadDate) && $file->uploadDate instanceof UTCDateTime) { 216 $timestamp = $file->uploadDate->toDateTime()->getTimestamp(); 217 $stat[9] = $stat['mtime'] = $timestamp; 218 $stat[10] = $stat['ctime'] = $timestamp; 219 } 220 221 if (isset($file->chunkSize) && is_integer($file->chunkSize)) { 222 $stat[11] = $stat['blksize'] = $file->chunkSize; 223 } 224 225 return $stat; 226 } 227 228 /** 229 * Return the current position of the stream. 230 * 231 * @see https://php.net/manual/en/streamwrapper.stream-tell.php 232 * @return integer The current position of the stream 233 */ 234 public function stream_tell(): int 235 { 236 assert($this->stream !== null); 237 238 return $this->stream->tell(); 239 } 240 241 /** 242 * Write bytes to the stream. 243 * 244 * @see https://php.net/manual/en/streamwrapper.stream-write.php 245 * @param string $data Data to write 246 * @return integer The number of bytes written 247 */ 248 public function stream_write(string $data): int 249 { 250 if (! $this->stream instanceof WritableStream) { 251 return 0; 252 } 253 254 return $this->stream->writeBytes($data); 255 } 256 257 /** 258 * Returns a stat template with default values. 259 */ 260 private function getStatTemplate(): array 261 { 262 return [ 263 // phpcs:disable Squiz.Arrays.ArrayDeclaration.IndexNoNewline 264 0 => 0, 'dev' => 0, 265 1 => 0, 'ino' => 0, 266 2 => 0, 'mode' => 0, 267 3 => 0, 'nlink' => 0, 268 4 => 0, 'uid' => 0, 269 5 => 0, 'gid' => 0, 270 6 => -1, 'rdev' => -1, 271 7 => 0, 'size' => 0, 272 8 => 0, 'atime' => 0, 273 9 => 0, 'mtime' => 0, 274 10 => 0, 'ctime' => 0, 275 11 => -1, 'blksize' => -1, 276 12 => -1, 'blocks' => -1, 277 // phpcs:enable 278 ]; 279 } 280 281 /** 282 * Initialize the protocol from the given path. 283 * 284 * @see StreamWrapper::stream_open() 285 */ 286 private function initProtocol(string $path): void 287 { 288 $parts = explode('://', $path, 2); 289 $this->protocol = $parts[0] ?: 'gridfs'; 290 } 291 292 /** 293 * Initialize the internal stream for reading. 294 * 295 * @see StreamWrapper::stream_open() 296 */ 297 private function initReadableStream(): bool 298 { 299 assert(is_resource($this->context)); 300 $context = stream_context_get_options($this->context); 301 302 assert($this->protocol !== null); 303 $this->stream = new ReadableStream( 304 $context[$this->protocol]['collectionWrapper'], 305 $context[$this->protocol]['file'] 306 ); 307 308 return true; 309 } 310 311 /** 312 * Initialize the internal stream for writing. 313 * 314 * @see StreamWrapper::stream_open() 315 */ 316 private function initWritableStream(): bool 317 { 318 assert(is_resource($this->context)); 319 $context = stream_context_get_options($this->context); 320 321 assert($this->protocol !== null); 322 $this->stream = new WritableStream( 323 $context[$this->protocol]['collectionWrapper'], 324 $context[$this->protocol]['filename'], 325 $context[$this->protocol]['options'] 326 ); 327 328 return true; 329 } 330 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body