See Release Notes
Long Term Support Release
Differences Between: [Versions 39 and 311] [Versions 39 and 400] [Versions 39 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\Operation; 19 20 use MongoDB\Driver\Command; 21 use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException; 22 use MongoDB\Driver\Server; 23 use MongoDB\Driver\Session; 24 use MongoDB\Driver\WriteConcern; 25 use MongoDB\Exception\InvalidArgumentException; 26 use MongoDB\Exception\UnexpectedValueException; 27 use MongoDB\Exception\UnsupportedException; 28 use function current; 29 use function is_array; 30 use function is_bool; 31 use function is_integer; 32 use function is_object; 33 use function MongoDB\create_field_path_type_map; 34 use function MongoDB\is_pipeline; 35 use function MongoDB\server_supports_feature; 36 37 /** 38 * Operation for the findAndModify command. 39 * 40 * This class is used internally by the FindOneAndDelete, FindOneAndReplace, and 41 * FindOneAndUpdate operation classes. 42 * 43 * @internal 44 * @see http://docs.mongodb.org/manual/reference/command/findAndModify/ 45 */ 46 class FindAndModify implements Executable, Explainable 47 { 48 /** @var integer */ 49 private static $wireVersionForArrayFilters = 6; 50 51 /** @var integer */ 52 private static $wireVersionForCollation = 5; 53 54 /** @var integer */ 55 private static $wireVersionForDocumentLevelValidation = 4; 56 57 /** @var integer */ 58 private static $wireVersionForWriteConcern = 4; 59 60 /** @var string */ 61 private $databaseName; 62 63 /** @var string */ 64 private $collectionName; 65 66 /** @var array */ 67 private $options; 68 69 /** 70 * Constructs a findAndModify command. 71 * 72 * Supported options: 73 * 74 * * arrayFilters (document array): A set of filters specifying to which 75 * array elements an update should apply. 76 * 77 * This is not supported for server versions < 3.6 and will result in an 78 * exception at execution time if used. 79 * 80 * * collation (document): Collation specification. 81 * 82 * This is not supported for server versions < 3.4 and will result in an 83 * exception at execution time if used. 84 * 85 * * bypassDocumentValidation (boolean): If true, allows the write to 86 * circumvent document level validation. 87 * 88 * For servers < 3.2, this option is ignored as document level validation 89 * is not available. 90 * 91 * * fields (document): Limits the fields to return for the matching 92 * document. 93 * 94 * * maxTimeMS (integer): The maximum amount of time to allow the query to 95 * run. 96 * 97 * * new (boolean): When true, returns the modified document rather than 98 * the original. This option is ignored for remove operations. The 99 * The default is false. 100 * 101 * * query (document): Query by which to filter documents. 102 * 103 * * remove (boolean): When true, removes the matched document. This option 104 * cannot be true if the update option is set. The default is false. 105 * 106 * * session (MongoDB\Driver\Session): Client session. 107 * 108 * Sessions are not supported for server versions < 3.6. 109 * 110 * * sort (document): Determines which document the operation modifies if 111 * the query selects multiple documents. 112 * 113 * * typeMap (array): Type map for BSON deserialization. 114 * 115 * * update (document): Update or replacement to apply to the matched 116 * document. This option cannot be set if the remove option is true. 117 * 118 * * upsert (boolean): When true, a new document is created if no document 119 * matches the query. This option is ignored for remove operations. The 120 * default is false. 121 * 122 * * writeConcern (MongoDB\Driver\WriteConcern): Write concern. 123 * 124 * This is not supported for server versions < 3.2 and will result in an 125 * exception at execution time if used. 126 * 127 * @param string $databaseName Database name 128 * @param string $collectionName Collection name 129 * @param array $options Command options 130 * @throws InvalidArgumentException for parameter/option parsing errors 131 */ 132 public function __construct($databaseName, $collectionName, array $options) 133 { 134 $options += [ 135 'new' => false, 136 'remove' => false, 137 'upsert' => false, 138 ]; 139 140 if (isset($options['arrayFilters']) && ! is_array($options['arrayFilters'])) { 141 throw InvalidArgumentException::invalidType('"arrayFilters" option', $options['arrayFilters'], 'array'); 142 } 143 144 if (isset($options['bypassDocumentValidation']) && ! is_bool($options['bypassDocumentValidation'])) { 145 throw InvalidArgumentException::invalidType('"bypassDocumentValidation" option', $options['bypassDocumentValidation'], 'boolean'); 146 } 147 148 if (isset($options['collation']) && ! is_array($options['collation']) && ! is_object($options['collation'])) { 149 throw InvalidArgumentException::invalidType('"collation" option', $options['collation'], 'array or object'); 150 } 151 152 if (isset($options['fields']) && ! is_array($options['fields']) && ! is_object($options['fields'])) { 153 throw InvalidArgumentException::invalidType('"fields" option', $options['fields'], 'array or object'); 154 } 155 156 if (isset($options['maxTimeMS']) && ! is_integer($options['maxTimeMS'])) { 157 throw InvalidArgumentException::invalidType('"maxTimeMS" option', $options['maxTimeMS'], 'integer'); 158 } 159 160 if (! is_bool($options['new'])) { 161 throw InvalidArgumentException::invalidType('"new" option', $options['new'], 'boolean'); 162 } 163 164 if (isset($options['query']) && ! is_array($options['query']) && ! is_object($options['query'])) { 165 throw InvalidArgumentException::invalidType('"query" option', $options['query'], 'array or object'); 166 } 167 168 if (! is_bool($options['remove'])) { 169 throw InvalidArgumentException::invalidType('"remove" option', $options['remove'], 'boolean'); 170 } 171 172 if (isset($options['session']) && ! $options['session'] instanceof Session) { 173 throw InvalidArgumentException::invalidType('"session" option', $options['session'], Session::class); 174 } 175 176 if (isset($options['sort']) && ! is_array($options['sort']) && ! is_object($options['sort'])) { 177 throw InvalidArgumentException::invalidType('"sort" option', $options['sort'], 'array or object'); 178 } 179 180 if (isset($options['typeMap']) && ! is_array($options['typeMap'])) { 181 throw InvalidArgumentException::invalidType('"typeMap" option', $options['typeMap'], 'array'); 182 } 183 184 if (isset($options['update']) && ! is_array($options['update']) && ! is_object($options['update'])) { 185 throw InvalidArgumentException::invalidType('"update" option', $options['update'], 'array or object'); 186 } 187 188 if (isset($options['writeConcern']) && ! $options['writeConcern'] instanceof WriteConcern) { 189 throw InvalidArgumentException::invalidType('"writeConcern" option', $options['writeConcern'], WriteConcern::class); 190 } 191 192 if (! is_bool($options['upsert'])) { 193 throw InvalidArgumentException::invalidType('"upsert" option', $options['upsert'], 'boolean'); 194 } 195 196 if (! (isset($options['update']) xor $options['remove'])) { 197 throw new InvalidArgumentException('The "remove" option must be true or an "update" document must be specified, but not both'); 198 } 199 200 if (isset($options['writeConcern']) && $options['writeConcern']->isDefault()) { 201 unset($options['writeConcern']); 202 } 203 204 $this->databaseName = (string) $databaseName; 205 $this->collectionName = (string) $collectionName; 206 $this->options = $options; 207 } 208 209 /** 210 * Execute the operation. 211 * 212 * @see Executable::execute() 213 * @param Server $server 214 * @return array|object|null 215 * @throws UnexpectedValueException if the command response was malformed 216 * @throws UnsupportedException if array filters, collation, or write concern is used and unsupported 217 * @throws DriverRuntimeException for other driver errors (e.g. connection errors) 218 */ 219 public function execute(Server $server) 220 { 221 if (isset($this->options['arrayFilters']) && ! server_supports_feature($server, self::$wireVersionForArrayFilters)) { 222 throw UnsupportedException::arrayFiltersNotSupported(); 223 } 224 225 if (isset($this->options['collation']) && ! server_supports_feature($server, self::$wireVersionForCollation)) { 226 throw UnsupportedException::collationNotSupported(); 227 } 228 229 if (isset($this->options['writeConcern']) && ! server_supports_feature($server, self::$wireVersionForWriteConcern)) { 230 throw UnsupportedException::writeConcernNotSupported(); 231 } 232 233 $inTransaction = isset($this->options['session']) && $this->options['session']->isInTransaction(); 234 if ($inTransaction && isset($this->options['writeConcern'])) { 235 throw UnsupportedException::writeConcernNotSupportedInTransaction(); 236 } 237 238 $cursor = $server->executeWriteCommand($this->databaseName, new Command($this->createCommandDocument($server)), $this->createOptions()); 239 240 if (isset($this->options['typeMap'])) { 241 $cursor->setTypeMap(create_field_path_type_map($this->options['typeMap'], 'value')); 242 } 243 244 $result = current($cursor->toArray()); 245 246 return isset($result->value) ? $result->value : null; 247 } 248 249 public function getCommandDocument(Server $server) 250 { 251 return $this->createCommandDocument($server); 252 } 253 254 /** 255 * Create the findAndModify command document. 256 * 257 * @param Server $server 258 * @return array 259 */ 260 private function createCommandDocument(Server $server) 261 { 262 $cmd = ['findAndModify' => $this->collectionName]; 263 264 if ($this->options['remove']) { 265 $cmd['remove'] = true; 266 } else { 267 $cmd['new'] = $this->options['new']; 268 $cmd['upsert'] = $this->options['upsert']; 269 } 270 271 foreach (['collation', 'fields', 'query', 'sort'] as $option) { 272 if (isset($this->options[$option])) { 273 $cmd[$option] = (object) $this->options[$option]; 274 } 275 } 276 277 if (isset($this->options['update'])) { 278 $cmd['update'] = is_pipeline($this->options['update']) 279 ? $this->options['update'] 280 : (object) $this->options['update']; 281 } 282 283 if (isset($this->options['arrayFilters'])) { 284 $cmd['arrayFilters'] = $this->options['arrayFilters']; 285 } 286 287 if (isset($this->options['maxTimeMS'])) { 288 $cmd['maxTimeMS'] = $this->options['maxTimeMS']; 289 } 290 291 if (! empty($this->options['bypassDocumentValidation']) && 292 server_supports_feature($server, self::$wireVersionForDocumentLevelValidation) 293 ) { 294 $cmd['bypassDocumentValidation'] = $this->options['bypassDocumentValidation']; 295 } 296 297 return $cmd; 298 } 299 300 /** 301 * Create options for executing the command. 302 * 303 * @see http://php.net/manual/en/mongodb-driver-server.executewritecommand.php 304 * @return array 305 */ 306 private function createOptions() 307 { 308 $options = []; 309 310 if (isset($this->options['session'])) { 311 $options['session'] = $this->options['session']; 312 } 313 314 if (isset($this->options['writeConcern'])) { 315 $options['writeConcern'] = $this->options['writeConcern']; 316 } 317 318 return $options; 319 } 320 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body