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