Differences Between: [Versions 311 and 401]
1 <?php 2 /* 3 * Copyright 2015-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; 19 20 use Exception; 21 use MongoDB\BSON\Serializable; 22 use MongoDB\Driver\Manager; 23 use MongoDB\Driver\ReadPreference; 24 use MongoDB\Driver\Server; 25 use MongoDB\Driver\Session; 26 use MongoDB\Exception\InvalidArgumentException; 27 use MongoDB\Exception\RuntimeException; 28 use MongoDB\Operation\WithTransaction; 29 use ReflectionClass; 30 use ReflectionException; 31 use function end; 32 use function get_object_vars; 33 use function in_array; 34 use function is_array; 35 use function is_object; 36 use function is_string; 37 use function key; 38 use function MongoDB\BSON\fromPHP; 39 use function MongoDB\BSON\toPHP; 40 use function reset; 41 use function substr; 42 43 /** 44 * Applies a type map to a document. 45 * 46 * This function is used by operations where it is not possible to apply a type 47 * map to the cursor directly because the root document is a command response 48 * (e.g. findAndModify). 49 * 50 * @internal 51 * @param array|object $document Document to which the type map will be applied 52 * @param array $typeMap Type map for BSON deserialization. 53 * @return array|object 54 * @throws InvalidArgumentException 55 */ 56 function apply_type_map_to_document($document, array $typeMap) 57 { 58 if (! is_array($document) && ! is_object($document)) { 59 throw InvalidArgumentException::invalidType('$document', $document, 'array or object'); 60 } 61 62 return toPHP(fromPHP($document), $typeMap); 63 } 64 65 /** 66 * Generate an index name from a key specification. 67 * 68 * @internal 69 * @param array|object $document Document containing fields mapped to values, 70 * which denote order or an index type 71 * @return string 72 * @throws InvalidArgumentException 73 */ 74 function generate_index_name($document) 75 { 76 if ($document instanceof Serializable) { 77 $document = $document->bsonSerialize(); 78 } 79 80 if (is_object($document)) { 81 $document = get_object_vars($document); 82 } 83 84 if (! is_array($document)) { 85 throw InvalidArgumentException::invalidType('$document', $document, 'array or object'); 86 } 87 88 $name = ''; 89 90 foreach ($document as $field => $type) { 91 $name .= ($name != '' ? '_' : '') . $field . '_' . $type; 92 } 93 94 return $name; 95 } 96 97 /** 98 * Return whether the first key in the document starts with a "$" character. 99 * 100 * This is used for differentiating update and replacement documents. 101 * 102 * @internal 103 * @param array|object $document Update or replacement document 104 * @return boolean 105 * @throws InvalidArgumentException 106 */ 107 function is_first_key_operator($document) 108 { 109 if ($document instanceof Serializable) { 110 $document = $document->bsonSerialize(); 111 } 112 113 if (is_object($document)) { 114 $document = get_object_vars($document); 115 } 116 117 if (! is_array($document)) { 118 throw InvalidArgumentException::invalidType('$document', $document, 'array or object'); 119 } 120 121 reset($document); 122 $firstKey = (string) key($document); 123 124 return isset($firstKey[0]) && $firstKey[0] === '$'; 125 } 126 127 /** 128 * Returns whether an update specification is a valid aggregation pipeline. 129 * 130 * @internal 131 * @param mixed $pipeline 132 * @return boolean 133 */ 134 function is_pipeline($pipeline) 135 { 136 if (! is_array($pipeline)) { 137 return false; 138 } 139 140 if ($pipeline === []) { 141 return false; 142 } 143 144 $expectedKey = 0; 145 146 foreach ($pipeline as $key => $stage) { 147 if (! is_array($stage) && ! is_object($stage)) { 148 return false; 149 } 150 151 if ($expectedKey !== $key) { 152 return false; 153 } 154 155 $expectedKey++; 156 $stage = (array) $stage; 157 reset($stage); 158 $key = key($stage); 159 160 if (! isset($key[0]) || $key[0] !== '$') { 161 return false; 162 } 163 } 164 165 return true; 166 } 167 168 /** 169 * Returns whether we are currently in a transaction. 170 * 171 * @internal 172 * @param array $options Command options 173 * @return boolean 174 */ 175 function is_in_transaction(array $options) 176 { 177 if (isset($options['session']) && $options['session'] instanceof Session && $options['session']->isInTransaction()) { 178 return true; 179 } 180 181 return false; 182 } 183 184 /** 185 * Return whether the aggregation pipeline ends with an $out or $merge operator. 186 * 187 * This is used for determining whether the aggregation pipeline must be 188 * executed against a primary server. 189 * 190 * @internal 191 * @param array $pipeline List of pipeline operations 192 * @return boolean 193 */ 194 function is_last_pipeline_operator_write(array $pipeline) 195 { 196 $lastOp = end($pipeline); 197 198 if ($lastOp === false) { 199 return false; 200 } 201 202 $lastOp = (array) $lastOp; 203 204 return in_array(key($lastOp), ['$out', '$merge'], true); 205 } 206 207 /** 208 * Return whether the "out" option for a mapReduce operation is "inline". 209 * 210 * This is used to determine if a mapReduce command requires a primary. 211 * 212 * @internal 213 * @see https://docs.mongodb.com/manual/reference/command/mapReduce/#output-inline 214 * @param string|array|object $out Output specification 215 * @return boolean 216 * @throws InvalidArgumentException 217 */ 218 function is_mapreduce_output_inline($out) 219 { 220 if (! is_array($out) && ! is_object($out)) { 221 return false; 222 } 223 224 if ($out instanceof Serializable) { 225 $out = $out->bsonSerialize(); 226 } 227 228 if (is_object($out)) { 229 $out = get_object_vars($out); 230 } 231 232 if (! is_array($out)) { 233 throw InvalidArgumentException::invalidType('$out', $out, 'array or object'); 234 } 235 236 reset($out); 237 238 return key($out) === 'inline'; 239 } 240 241 /** 242 * Return whether the server supports a particular feature. 243 * 244 * @internal 245 * @param Server $server Server to check 246 * @param integer $feature Feature constant (i.e. wire protocol version) 247 * @return boolean 248 */ 249 function server_supports_feature(Server $server, $feature) 250 { 251 $info = $server->getInfo(); 252 $maxWireVersion = isset($info['maxWireVersion']) ? (integer) $info['maxWireVersion'] : 0; 253 $minWireVersion = isset($info['minWireVersion']) ? (integer) $info['minWireVersion'] : 0; 254 255 return $minWireVersion <= $feature && $maxWireVersion >= $feature; 256 } 257 258 function is_string_array($input) 259 { 260 if (! is_array($input)) { 261 return false; 262 } 263 foreach ($input as $item) { 264 if (! is_string($item)) { 265 return false; 266 } 267 } 268 269 return true; 270 } 271 272 /** 273 * Performs a deep copy of a value. 274 * 275 * This function will clone objects and recursively copy values within arrays. 276 * 277 * @internal 278 * @see https://bugs.php.net/bug.php?id=49664 279 * @param mixed $element Value to be copied 280 * @return mixed 281 * @throws ReflectionException 282 */ 283 function recursive_copy($element) 284 { 285 if (is_array($element)) { 286 foreach ($element as $key => $value) { 287 $element[$key] = recursive_copy($value); 288 } 289 290 return $element; 291 } 292 293 if (! is_object($element)) { 294 return $element; 295 } 296 297 if (! (new ReflectionClass($element))->isCloneable()) { 298 return $element; 299 } 300 301 return clone $element; 302 } 303 304 /** 305 * Creates a type map to apply to a field type 306 * 307 * This is used in the Aggregate, Distinct, and FindAndModify operations to 308 * apply the root-level type map to the document that will be returned. It also 309 * replaces the root type with object for consistency within these operations 310 * 311 * An existing type map for the given field path will not be overwritten 312 * 313 * @internal 314 * @param array $typeMap The existing typeMap 315 * @param string $fieldPath The field path to apply the root type to 316 * @return array 317 */ 318 function create_field_path_type_map(array $typeMap, $fieldPath) 319 { 320 // If some field paths already exist, we prefix them with the field path we are assuming as the new root 321 if (isset($typeMap['fieldPaths']) && is_array($typeMap['fieldPaths'])) { 322 $fieldPaths = $typeMap['fieldPaths']; 323 324 $typeMap['fieldPaths'] = []; 325 foreach ($fieldPaths as $existingFieldPath => $type) { 326 $typeMap['fieldPaths'][$fieldPath . '.' . $existingFieldPath] = $type; 327 } 328 } 329 330 // If a root typemap was set, apply this to the field object 331 if (isset($typeMap['root'])) { 332 $typeMap['fieldPaths'][$fieldPath] = $typeMap['root']; 333 } 334 335 /* Special case if we want to convert an array, in which case we need to 336 * ensure that the field containing the array is exposed as an array, 337 * instead of the type given in the type map's array key. */ 338 if (substr($fieldPath, -2, 2) === '.$') { 339 $typeMap['fieldPaths'][substr($fieldPath, 0, -2)] = 'array'; 340 } 341 342 $typeMap['root'] = 'object'; 343 344 return $typeMap; 345 } 346 347 /** 348 * Execute a callback within a transaction in the given session 349 * 350 * This helper takes care of retrying the commit operation or the entire 351 * transaction if an error occurs. 352 * 353 * If the commit fails because of an UnknownTransactionCommitResult error, the 354 * commit is retried without re-invoking the callback. 355 * If the commit fails because of a TransientTransactionError, the entire 356 * transaction will be retried. In this case, the callback will be invoked 357 * again. It is important that the logic inside the callback is idempotent. 358 * 359 * In case of failures, the commit or transaction are retried until 120 seconds 360 * from the initial call have elapsed. After that, no retries will happen and 361 * the helper will throw the last exception received from the driver. 362 * 363 * @see Client::startSession 364 * @see Session::startTransaction for supported transaction options 365 * 366 * @param Session $session A session object as retrieved by Client::startSession 367 * @param callable $callback A callback that will be invoked within the transaction 368 * @param array $transactionOptions Additional options that are passed to Session::startTransaction 369 * @return void 370 * @throws RuntimeException for driver errors while committing the transaction 371 * @throws Exception for any other errors, including those thrown in the callback 372 */ 373 function with_transaction(Session $session, callable $callback, array $transactionOptions = []) 374 { 375 $operation = new WithTransaction($callback, $transactionOptions); 376 $operation->execute($session); 377 } 378 379 /** 380 * Returns the session option if it is set and valid. 381 * 382 * @internal 383 * @param array $options 384 * @return Session|null 385 */ 386 function extract_session_from_options(array $options) 387 { 388 if (! isset($options['session']) || ! $options['session'] instanceof Session) { 389 return null; 390 } 391 392 return $options['session']; 393 } 394 395 /** 396 * Returns the readPreference option if it is set and valid. 397 * 398 * @internal 399 * @param array $options 400 * @return ReadPreference|null 401 */ 402 function extract_read_preference_from_options(array $options) 403 { 404 if (! isset($options['readPreference']) || ! $options['readPreference'] instanceof ReadPreference) { 405 return null; 406 } 407 408 return $options['readPreference']; 409 } 410 411 /** 412 * Performs server selection, respecting the readPreference and session options 413 * (if given) 414 * 415 * @internal 416 * @return Server 417 */ 418 function select_server(Manager $manager, array $options) 419 { 420 $session = extract_session_from_options($options); 421 if ($session instanceof Session && $session->getServer() !== null) { 422 return $session->getServer(); 423 } 424 425 $readPreference = extract_read_preference_from_options($options); 426 if (! $readPreference instanceof ReadPreference) { 427 // TODO: PHPLIB-476: Read transaction read preference once PHPC-1439 is implemented 428 $readPreference = new ReadPreference(ReadPreference::RP_PRIMARY); 429 } 430 431 return $manager->selectServer($readPreference); 432 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body