Completed
Pull Request — master (#8)
by
unknown
12:52
created

MongoCollection::ensureIndex()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 5
rs 9.4286
cc 1
eloc 3
nc 1
nop 2
1
<?php
2
/*
3
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
 */
15
16
use Alcaeus\MongoDbAdapter\TypeConverter;
17
use MongoDB\Driver\ReadPreference;
18
use MongoDB\Driver\WriteConcern;
19
20
/**
21
 * Represents a database collection.
22
 * @link http://www.php.net/manual/en/class.mongocollection.php
23
 */
24
class MongoCollection
1 ignored issue
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
25
{
26
    const ASCENDING = 1;
27
    const DESCENDING = -1;
28
29
    /**
30
     * @var MongoDB
31
     */
32
    public $db = NULL;
33
34
    /**
35
     * @var string
36
     */
37
    protected $name;
38
39
    /**
40
     * @var \MongoDB\Collection
41
     */
42
    protected $collection;
43
44
    /**
45
     * @var \MongoDB\Driver\ReadPreference
46
     */
47
    protected $readPreference;
48
49
    /**
50
     * @var \MongoDB\Driver\WriteConcern
51
     */
52
    protected $writeConcern;
53
54
    /**
55
     * @var int<p>
56
     */
57
    protected $w = 1;
58
59
    /**
60
     * @var int <p>
61
     */
62
    protected $wtimeout;
63
64
    /**
65
     * Creates a new collection
66
     * @link http://www.php.net/manual/en/mongocollection.construct.php
67
     * @param MongoDB $db Parent database.
68
     * @param string $name Name for this collection.
69
     * @throws Exception
70
     * @return MongoCollection
0 ignored issues
show
Comprehensibility Best Practice introduced by
Adding a @return annotation to constructors is generally not recommended as a constructor does not have a meaningful return value.

Adding a @return annotation to a constructor is not recommended, since a constructor does not have a meaningful return value.

Please refer to the PHP core documentation on constructors.

Loading history...
71
     */
72
    public function __construct(MongoDB $db, $name)
73
    {
74
        $this->db = $db;
75
        $this->name = $name;
76
        $this->readPreference = new ReadPreference(ReadPreference::RP_PRIMARY);
77
        $this->writeConcern = new WriteConcern($this->w, $this->wtimeout);
78
        $this->createCollection();
79
    }
80
81
    /**
82
     * Gets the underlying collection for this object
83
     *
84
     * @internal This part is not of the ext-mongo API and should not be used
85
     * @return \MongoDB\Collection
86
     */
87
    public function getCollection()
88
    {
89
        return $this->collection;
90
    }
91
92
    /**
93
     * String representation of this collection
94
     * @link http://www.php.net/manual/en/mongocollection.--tostring.php
95
     * @return string Returns the full name of this collection.
96
     */
97
    public function __toString()
98
    {
99
        return (string) $this->db . '.' . $this->name;
100
    }
101
102
    /**
103
     * Gets a collection
104
     * @link http://www.php.net/manual/en/mongocollection.get.php
105
     * @param string $name The next string in the collection name.
106
     * @return MongoCollection
107
     */
108
    public function __get($name)
109
    {
110
        return $this->db->selectCollection($this->name . '.' . $name);
111
    }
112
113
    public function __set($name, $value)
114
    {
115
        if ($name === 'w') {
116
            $this->w = $value;
117
            $this->setWriteConcern($this->w, $this->wtimeout);
118
        } elseif ($name === 'wtimeout') {
119
            $this->wtimeout = $value;
120
            $this->setWriteConcern($this->w, $this->wtimeout);
121
        } else {
122
            trigger_error('trying to set invalid property', E_ERROR);
123
        }
124
    }
125
126
    /**
127
     * @link http://www.php.net/manual/en/mongocollection.aggregate.php
128
     * @param array $pipeline
129
     * @param array $op
130
     * @return array
131
     */
132
    public function aggregate(array $pipeline, array $op = [])
133
    {
134
        if (! TypeConverter::isNumericArray($pipeline)) {
135
            $pipeline = [];
136
            $options = [];
137
138
            $i = 0;
139
            foreach (func_get_args() as $operator) {
140
                $i++;
141
                if (! is_array($operator)) {
142
                    trigger_error("Argument $i is not an array", E_WARNING);
143
                    return;
144
                }
145
146
                $pipeline[] = $operator;
147
            }
148
        } else {
149
            $options = $op;
150
        }
151
152
        $command = [
153
            'aggregate' => $this->name,
154
            'pipeline' => $pipeline
155
        ];
156
157
        $command += $options;
158
159
        return $this->db->command($command, [], $hash);
160
    }
161
162
    /**
163
     * @link http://php.net/manual/en/mongocollection.aggregatecursor.php
164
     * @param array $pipeline
165
     * @param array $options
166
     * @return MongoCommandCursor
167
     */
168
    public function aggregateCursor(array $pipeline, array $options = [])
169
    {
170
        // Build command manually, can't use mongo-php-library here
171
        $command = [
172
            'aggregate' => $this->name,
173
            'pipeline' => $pipeline
174
        ];
175
176
        // Convert cursor option
177
        if (! isset($options['cursor']) || $options['cursor'] === true || $options['cursor'] === []) {
178
            // Cursor option needs to be an object convert bools and empty arrays since those won't be handled by TypeConverter
179
            $options['cursor'] = new \stdClass;
180
        }
181
182
        $command += $options;
183
184
        return new MongoCommandCursor($this->db->getConnection(), (string) $this, $command);
185
    }
186
187
    /**
188
     * Returns this collection's name
189
     * @link http://www.php.net/manual/en/mongocollection.getname.php
190
     * @return string
191
     */
192
    public function getName()
193
    {
194
        return $this->name;
195
    }
196
197
    /**
198
     * @link http://www.php.net/manual/en/mongocollection.getslaveokay.php
199
     * @return bool
200
     */
201
    public function getSlaveOkay()
202
    {
203
        return $this->readPreference->getMode() != ReadPreference::RP_PRIMARY;
204
    }
205
206
    /**
207
     * @link http://www.php.net/manual/en/mongocollection.setslaveokay.php
208
     * @param bool $ok
209
     * @return bool
210
     */
211
    public function setSlaveOkay($ok = true)
212
    {
213
        $result = $this->getSlaveOkay();
214
        $this->readPreference = new ReadPreference(
215
            $ok ? ReadPreference::RP_SECONDARY_PREFERRED : ReadPreference::RP_PRIMARY,
216
            $this->readPreference->getTagSets()
217
        );
218
        $this->createCollection();
219
        return $result;
220
    }
221
222
    /**
223
     * @link http://www.php.net/manual/en/mongocollection.getreadpreference.php
224
     * @return array
225
     */
226
    public function getReadPreference()
227
    {
228
        $mode = $this->readPreference->getMode();
229
        switch ($mode) {
230
            case ReadPreference::RP_PRIMARY:
231
                $type = 'primary';
232
                break;
233
            case ReadPreference::RP_PRIMARY_PREFERRED:
234
                $type = 'primaryPreferred';
235
                break;
236
            case ReadPreference::RP_SECONDARY:
237
                $type = 'secondary';
238
                break;
239
            case ReadPreference::RP_SECONDARY_PREFERRED:
240
                $type = 'secondayPreferred';
241
                break;
242
            case ReadPreference::RP_NEAREST:
243
                $type = 'nearest';
244
                break;
245
            default:
246
                throw new \LogicException();
247
        }
248
        return [
249
            'type'    => $type,
250
            'tagsets' => $this->readPreference->getTagSets(),
251
        ];
252
    }
253
254
    /**
255
     * @param string $read_preference
256
     * @param array $tags
257
     * @return bool
258
     */
259
    public function setReadPreference($read_preference, array $tags)
260
    {
261
        switch ($read_preference) {
262
            case MongoClient::RP_PRIMARY:
263
                $mode = ReadPreference::RP_PRIMARY;
264
                break;
265
            case MongoClient::RP_PRIMARY_PREFERRED:
266
                $mode = ReadPreference::RP_PRIMARY_PREFERRED;
267
                break;
268
            case MongoClient::RP_SECONDARY:
269
                $mode = ReadPreference::RP_SECONDARY;
270
                break;
271
            case MongoClient::RP_SECONDARY_PREFERRED:
272
                $mode = ReadPreference::RP_SECONDARY_PREFERRED;
273
                break;
274
            case MongoClient::RP_NEAREST:
275
                $mode = ReadPreference::RP_NEAREST;
276
                break;
277
            default:
278
                throw new \InvalidArgumentException('read preference invalid');
279
        }
280
        $this->readPreference = new ReadPreference($mode, $tags);
281
        $this->createCollection();
282
        return true;
283
    }
284
285
    /**
286
     * Drops this collection
287
     * @link http://www.php.net/manual/en/mongocollection.drop.php
288
     * @return array Returns the database response.
289
     */
290
    public function drop()
291
    {
292
        return $this->collection->drop();
293
    }
294
295
    /**
296
     * Validates this collection
297
     * @link http://www.php.net/manual/en/mongocollection.validate.php
298
     * @param bool $scan_data Only validate indices, not the base collection.
299
     * @return array Returns the database's evaluation of this object.
300
     */
301
    public function validate($scan_data = FALSE)
302
    {
303
        $command = [
304
            'validate' => $this->name,
305
            'full'     => $scan_data,
306
        ];
307
        $result = $this->db->command($command, [], $hash);
308
        if ($result) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $result of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
309
            return $result;
310
        }
311
312
        return false;
313
    }
314
315
    /**
316
     * Inserts an array into the collection
317
     * @link http://www.php.net/manual/en/mongocollection.insert.php
318
     * @param array|object $a
319
     * @param array $options
320
     * @throws MongoException if the inserted document is empty or if it contains zero-length keys. Attempting to insert an object with protected and private properties will cause a zero-length key error.
321
     * @throws MongoCursorException if the "w" option is set and the write fails.
322
     * @throws MongoCursorTimeoutException if the "w" option is set to a value greater than one and the operation takes longer than MongoCursor::$timeout milliseconds to complete. This does not kill the operation on the server, it is a client-side timeout. The operation in MongoCollection::$wtimeout is milliseconds.
323
     * @return bool|array Returns an array containing the status of the insertion if the "w" option is set.
324
     */
325
    public function insert($a, array $options = array())
326
    {
327
        return $this->collection->insertOne(TypeConverter::convertLegacyArrayToObject($a), $options);
328
    }
329
330
    /**
331
     * Inserts multiple documents into this collection
332
     * @link http://www.php.net/manual/en/mongocollection.batchinsert.php
333
     * @param array $a An array of arrays.
334
     * @param array $options Options for the inserts.
335
     * @throws MongoCursorException
336
     * @return mixed f "safe" is set, returns an associative array with the status of the inserts ("ok") and any error that may have occured ("err"). Otherwise, returns TRUE if the batch insert was successfully sent, FALSE otherwise.
337
     */
338
    public function batchInsert(array $a, array $options = array())
339
    {
340
        return $this->collection->insertMany($a, $options);
341
    }
342
343
    /**
344
     * Update records based on a given criteria
345
     * @link http://www.php.net/manual/en/mongocollection.update.php
346
     * @param array $criteria Description of the objects to update.
347
     * @param array $newobj The object with which to update the matching records.
348
     * @param array $options This parameter is an associative array of the form
349
     *        array("optionname" => boolean, ...).
350
     *
351
     *        Currently supported options are:
352
     *          "upsert": If no document matches $$criteria, a new document will be created from $$criteria and $$new_object (see upsert example).
353
     *
354
     *          "multiple": All documents matching $criteria will be updated. MongoCollection::update has exactly the opposite behavior of MongoCollection::remove- it updates one document by
355
     *          default, not all matching documents. It is recommended that you always specify whether you want to update multiple documents or a single document, as the
356
     *          database may change its default behavior at some point in the future.
357
     *
358
     *          "safe" Can be a boolean or integer, defaults to false. If false, the program continues executing without waiting for a database response. If true, the program will wait for
359
     *          the database response and throw a MongoCursorException if the update did not succeed. If you are using replication and the master has changed, using "safe" will make the driver
360
     *          disconnect from the master, throw and exception, and attempt to find a new master on the next operation (your application must decide whether or not to retry the operation on the new master).
361
     *          If you do not use "safe" with a replica set and the master changes, there will be no way for the driver to know about the change so it will continuously and silently fail to write.
362
     *          If safe is an integer, will replicate the update to that many machines before returning success (or throw an exception if the replication times out, see wtimeout).
363
     *          This overrides the w variable set on the collection.
364
     *
365
     *         "fsync": Boolean, defaults to false. Forces the update to be synced to disk before returning success. If true, a safe update is implied and will override setting safe to false.
366
     *
367
     *         "timeout" Integer, defaults to MongoCursor::$timeout. If "safe" is set, this sets how long (in milliseconds) for the client to wait for a database response. If the database does
368
     *         not respond within the timeout period, a MongoCursorTimeoutException will be thrown
369
     * @throws MongoCursorException
370
     * @return boolean
371
     */
372
    public function update(array $criteria , array $newobj, array $options = array())
373
    {
374
        $multiple = ($options['multiple']) ? $options['multiple'] : false;
375
//        $multiple = $options['multiple'] ?? false;
0 ignored issues
show
Unused Code Comprehensibility introduced by
60% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
376
        $method = $multiple ? 'updateMany' : 'updateOne';
377
378
        return $this->collection->$method($criteria, $newobj, $options);
379
    }
380
381
    /**
382
     * (PECL mongo &gt;= 0.9.0)<br/>
383
     * Remove records from this collection
384
     * @link http://www.php.net/manual/en/mongocollection.remove.php
385
     * @param array $criteria [optional] <p>Query criteria for the documents to delete.</p>
386
     * @param array $options [optional] <p>An array of options for the remove operation. Currently available options
387
     * include:
388
     * </p><ul>
389
     * <li><p><em>"w"</em></p><p>See {@link http://www.php.net/manual/en/mongo.writeconcerns.php Write Concerns}. The default value for <b>MongoClient</b> is <em>1</em>.</p></li>
390
     * <li>
391
     * <p>
392
     * <em>"justOne"</em>
393
     * </p>
394
     * <p>
395
     * Specify <strong><code>TRUE</code></strong> to limit deletion to just one document. If <strong><code>FALSE</code></strong> or
396
     * omitted, all documents matching the criteria will be deleted.
397
     * </p>
398
     * </li>
399
     * <li><p><em>"fsync"</em></p><p>Boolean, defaults to <b>FALSE</b>. If journaling is enabled, it works exactly like <em>"j"</em>. If journaling is not enabled, the write operation blocks until it is synced to database files on disk. If <strong><code>TRUE</code></strong>, an acknowledged insert is implied and this option will override setting <em>"w"</em> to <em>0</em>.</p><blockquote class="note"><p><strong class="note">Note</strong>: <span class="simpara">If journaling is enabled, users are strongly encouraged to use the <em>"j"</em> option instead of <em>"fsync"</em>. Do not use <em>"fsync"</em> and <em>"j"</em> simultaneously, as that will result in an error.</p></blockquote></li>
400
     * <li><p><em>"j"</em></p><p>Boolean, defaults to <b>FALSE</b>. Forces the write operation to block until it is synced to the journal on disk. If <strong><code>TRUE</code></strong>, an acknowledged write is implied and this option will override setting <em>"w"</em> to <em>0</em>.</p><blockquote class="note"><p><strong class="note">Note</strong>: <span class="simpara">If this option is used and journaling is disabled, MongoDB 2.6+ will raise an error and the write will fail; older server versions will simply ignore the option.</p></blockquote></li>
401
     * <li><p><em>"socketTimeoutMS"</em></p><p>This option specifies the time limit, in milliseconds, for socket communication. If the server does not respond within the timeout period, a <b>MongoCursorTimeoutException</b> will be thrown and there will be no way to determine if the server actually handled the write or not. A value of <em>-1</em> may be specified to block indefinitely. The default value for <b>MongoClient</b> is <em>30000</em> (30 seconds).</p></li>
402
     * <li><p><em>"w"</em></p><p>See {@link http://www.php.net/manual/en/mongo.writeconcerns.php Write Concerns }. The default value for <b>MongoClient</b> is <em>1</em>.</p></li>
403
     * <li><p><em>"wTimeoutMS"</em></p><p>This option specifies the time limit, in milliseconds, for {@link http://www.php.net/manual/en/mongo.writeconcerns.php write concern} acknowledgement. It is only applicable when <em>"w"</em> is greater than <em>1</em>, as the timeout pertains to replication. If the write concern is not satisfied within the time limit, a <a href="class.mongocursorexception.php" class="classname">MongoCursorException</a> will be thrown. A value of <em>0</em> may be specified to block indefinitely. The default value for {@link http://www.php.net/manual/en/class.mongoclient.php MongoClient} is <em>10000</em> (ten seconds).</p></li>
404
     * </ul>
405
     *
406
     * <p>
407
     * The following options are deprecated and should no longer be used:
408
     * </p><ul>
409
     * <li><p><em>"safe"</em></p><p>Deprecated. Please use the {@link http://www.php.net/manual/en/mongo.writeconcerns.php write concern} <em>"w"</em> option.</p></li>
410
     * <li><p><em>"timeout"</em></p><p>Deprecated alias for <em>"socketTimeoutMS"</em>.</p></li>
411
     * <li><p><b>"wtimeout"</b></p><p>Deprecated alias for <em>"wTimeoutMS"</em>.</p></p>
412
     * @throws MongoCursorException
413
     * @throws MongoCursorTimeoutException
414
     * @return bool|array <p>Returns an array containing the status of the removal if the
415
     * <em>"w"</em> option is set. Otherwise, returns <b>TRUE</b>.
416
     * </p>
417
     * <p>
418
     * Fields in the status array are described in the documentation for
419
     * <b>MongoCollection::insert()</b>.
420
     * </p>
421
     */
422
    public function remove(array $criteria = array(), array $options = array())
423
    {
424
        $multiple = isset($options['justOne']) ? !$options['justOne'] : false;
425
//        $multiple = !$options['justOne'] ?? false;
0 ignored issues
show
Unused Code Comprehensibility introduced by
63% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
426
        $method = $multiple ? 'deleteMany' : 'deleteOne';
427
428
        return $this->collection->$method($criteria, $options);
429
    }
430
431
    /**
432
     * Querys this collection
433
     * @link http://www.php.net/manual/en/mongocollection.find.php
434
     * @param array $query The fields for which to search.
435
     * @param array $fields Fields of the results to return.
436
     * @return MongoCursor
437
     */
438
    public function find(array $query = array(), array $fields = array())
439
    {
440
        return new MongoCursor($this->db->getConnection(), (string) $this, $query, $fields);
441
    }
442
443
    /**
444
     * Retrieve a list of distinct values for the given key across a collection
445
     * @link http://www.php.net/manual/ru/mongocollection.distinct.php
446
     * @param string $key The key to use.
447
     * @param array $query An optional query parameters
448
     * @return array|bool Returns an array of distinct values, or <b>FALSE</b> on failure
449
     */
450
    public function distinct($key, array $query = [])
451
    {
452
        return array_map([TypeConverter::class, 'convertToLegacyType'], $this->collection->distinct($key, $query));
453
    }
454
455
    /**
456
     * Update a document and return it
457
     * @link http://www.php.net/manual/ru/mongocollection.findandmodify.php
458
     * @param array $query The query criteria to search for.
459
     * @param array $update The update criteria.
460
     * @param array $fields Optionally only return these fields.
461
     * @param array $options An array of options to apply, such as remove the match document from the DB and return it.
462
     * @return array Returns the original document, or the modified document when new is set.
463
     */
464
    public function findAndModify(array $query, array $update = NULL, array $fields = NULL, array $options = [])
465
    {
466
        $query = TypeConverter::convertLegacyArrayToObject($query);
467
        if (isset($options['remove'])) {
468
            unset($options['remove']);
469
            $document = $this->collection->findOneAndDelete($query, $options);
470
        } else {
471
            if (is_array($update)) {
472
                $update = TypeConverter::convertLegacyArrayToObject($update);
473
            }
474
            if (is_array($fields)) {
475
                $fields = TypeConverter::convertLegacyArrayToObject($fields);
476
            }
477
            if (isset($options['new'])) {
478
                $options['returnDocument'] = \MongoDB\Operation\FindOneAndUpdate::RETURN_DOCUMENT_AFTER;
479
                unset($options['new']);
480
            }
481
            if ($fields) {
482
                $options['projection'] = $fields;
483
            }
484
            $document = $this->collection->findOneAndUpdate($query, $update, $options);
0 ignored issues
show
Bug introduced by
It seems like $update defined by parameter $update on line 464 can also be of type null; however, MongoDB\Collection::findOneAndUpdate() does only seem to accept array|object, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
485
        }
486
        if ($document) {
487
            $document = TypeConverter::convertObjectToLegacyArray($document);
488
        }
489
        return $document;
490
    }
491
492
    /**
493
     * Querys this collection, returning a single element
494
     * @link http://www.php.net/manual/en/mongocollection.findone.php
495
     * @param array $query The fields for which to search.
496
     * @param array $fields Fields of the results to return.
497
     * @return array|null
498
     */
499
    public function findOne(array $query = array(), array $fields = array())
500
    {
501
        $document = $this->collection->findOne(TypeConverter::convertLegacyArrayToObject($query), ['projection' => $fields]);
502
        if ($document !== null) {
503
            $document = TypeConverter::convertObjectToLegacyArray($document);
504
        }
505
506
        return $document;
507
    }
508
509
    /**
510
     * Creates an index on the given field(s), or does nothing if the index already exists
511
     * @link http://www.php.net/manual/en/mongocollection.createindex.php
512
     * @param array $keys Field or fields to use as index.
513
     * @param array $options [optional] This parameter is an associative array of the form array("optionname" => <boolean>, ...).
514
     * @return array Returns the database response.
515
     */
516
    public function createIndex(array $keys, array $options = array())
517
    {
518
        return $this->collection->createIndex($keys, $options);
519
    }
520
521
    /**
522
     * @deprecated Use MongoCollection::createIndex() instead.
523
     * Creates an index on the given field(s), or does nothing if the index already exists
524
     * @link http://www.php.net/manual/en/mongocollection.ensureindex.php
525
     * @param array $keys Field or fields to use as index.
526
     * @param array $options [optional] This parameter is an associative array of the form array("optionname" => <boolean>, ...).
527
     * @return boolean always true
528
     */
529
    public function ensureIndex(array $keys, array $options = array())
530
    {
531
        $this->createIndex($keys, $options);
532
        return true;
533
    }
534
535
    /**
536
     * Deletes an index from this collection
537
     * @link http://www.php.net/manual/en/mongocollection.deleteindex.php
538
     * @param string|array $keys Field or fields from which to delete the index.
539
     * @return array Returns the database response.
540
     */
541
    public function deleteIndex($keys)
542
    {
543
        if (is_string($keys)) {
544
            $indexName = $keys;
545
        } elseif (is_array($keys)) {
546
            $indexName = self::toIndexString($keys);
547
        } else {
548
            throw new \InvalidArgumentException();
549
        }
550
        return $this->collection->dropIndex($indexName);
551
    }
552
553
    /**
554
     * Delete all indexes for this collection
555
     * @link http://www.php.net/manual/en/mongocollection.deleteindexes.php
556
     * @return array Returns the database response.
557
     */
558
    public function deleteIndexes()
559
    {
560
        return $this->collection->dropIndexes();
561
    }
562
563
    /**
564
     * Returns an array of index names for this collection
565
     * @link http://www.php.net/manual/en/mongocollection.getindexinfo.php
566
     * @return array Returns a list of index names.
567
     */
568
    public function getIndexInfo()
569
    {
570
        $convertIndex = function($indexInfo) {
571
            return $indexInfo->__debugInfo();
572
        };
573
        return array_map($convertIndex, iterator_to_array($this->collection->listIndexes()));
574
    }
575
576
    /**
577
     * Counts the number of documents in this collection
578
     * @link http://www.php.net/manual/en/mongocollection.count.php
579
     * @param array|stdClass $query
580
     * @return int Returns the number of documents matching the query.
581
     */
582
    public function count($query = array())
583
    {
584
        return $this->collection->count($query);
585
    }
586
587
    /**
588
     * Saves an object to this collection
589
     * @link http://www.php.net/manual/en/mongocollection.save.php
590
     * @param array|object $a Array to save. If an object is used, it may not have protected or private properties.
591
     * Note: If the parameter does not have an _id key or property, a new MongoId instance will be created and assigned to it.
592
     * See MongoCollection::insert() for additional information on this behavior.
593
     * @param array $options Options for the save.
594
     * <dl>
595
     * <dt>"w"
596
     * <dd>See WriteConcerns. The default value for MongoClient is 1.
597
     * <dt>"fsync"
598
     * <dd>Boolean, defaults to FALSE. Forces the insert to be synced to disk before returning success. If TRUE, an acknowledged insert is implied and will override setting w to 0.
599
     * <dt>"timeout"
600
     * <dd>Integer, defaults to MongoCursor::$timeout. If "safe" is set, this sets how long (in milliseconds) for the client to wait for a database response. If the database does not respond within the timeout period, a MongoCursorTimeoutException will be thrown.
601
     * <dt>"safe"
602
     * <dd>Deprecated. Please use the WriteConcern w option.
603
     * </dl>
604
     * @throws MongoException if the inserted document is empty or if it contains zero-length keys. Attempting to insert an object with protected and private properties will cause a zero-length key error.
605
     * @throws MongoCursorException if the "w" option is set and the write fails.
606
     * @throws MongoCursorTimeoutException if the "w" option is set to a value greater than one and the operation takes longer than MongoCursor::$timeout milliseconds to complete. This does not kill the operation on the server, it is a client-side timeout. The operation in MongoCollection::$wtimeout is milliseconds.
607
     * @return array|boolean If w was set, returns an array containing the status of the save.
608
     * Otherwise, returns a boolean representing if the array was not empty (an empty array will not be inserted).
609
     */
610
    public function save($a, array $options = array())
0 ignored issues
show
Unused Code introduced by
The parameter $options is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
611
    {
612
        if (is_object($a)) {
613
            $a = (array)$a;
614
        }
615
        if ( ! array_key_exists('_id', $a)) {
616
            $id = new \MongoId();
617
        } else {
618
            $id = $a['_id'];
619
            unset($a['_id']);
620
        }
621
        $filter = ['_id' => $id];
622
        $filter = TypeConverter::convertLegacyArrayToObject($filter);
623
        $a = TypeConverter::convertLegacyArrayToObject($a);
624
        return $this->collection->updateOne($filter, ['$set' => $a], ['upsert' => true]);
625
    }
626
627
    /**
628
     * Creates a database reference
629
     * @link http://www.php.net/manual/en/mongocollection.createdbref.php
630
     * @param array $a Object to which to create a reference.
631
     * @return array Returns a database reference array.
632
     */
633
    public function createDBRef(array $a)
634
    {
635
        return \MongoDBRef::create($this->name, $a['_id']);
636
    }
637
638
    /**
639
     * Fetches the document pointed to by a database reference
640
     * @link http://www.php.net/manual/en/mongocollection.getdbref.php
641
     * @param array $ref A database reference.
642
     * @return array Returns the database document pointed to by the reference.
643
     */
644
    public function getDBRef(array $ref)
645
    {
646
        return \MongoDBRef::get($this->db, $ref);
647
    }
648
649
    /**
650
     * @param  mixed $keys
651
     * @static
652
     * @return string
653
     */
654
    protected static function toIndexString($keys)
655
    {
656
        $result = '';
657
        foreach ($keys as $name => $direction) {
658
            $result .= sprintf('%s_%d', $name, $direction);
659
        }
660
        return $result;
661
    }
662
663
    /**
664
     * Performs an operation similar to SQL's GROUP BY command
665
     * @link http://www.php.net/manual/en/mongocollection.group.php
666
     * @param mixed $keys Fields to group by. If an array or non-code object is passed, it will be the key used to group results.
667
     * @param array $initial Initial value of the aggregation counter object.
668
     * @param MongoCode $reduce A function that aggregates (reduces) the objects iterated.
669
     * @param array $condition An condition that must be true for a row to be considered.
670
     * @return array
671
     */
672
    public function group($keys, array $initial, $reduce, array $condition = [])
673
    {
674
        if (is_string($reduce)) {
675
            $reduce = new MongoCode($reduce);
676
        }
677
        if ( ! $reduce instanceof MongoCode) {
678
            throw new \InvalidArgumentExcption('reduce parameter should be a string or MongoCode instance.');
679
        }
680
        $command = [
681
            'group' => [
682
                'ns'      => $this->name,
683
                '$reduce' => (string)$reduce,
684
                'initial' => $initial,
685
                'cond'    => $condition,
686
            ],
687
        ];
688
689
        if ($keys instanceof MongoCode) {
690
            $command['group']['$keyf'] = (string)$keys;
691
        } else {
692
            $command['group']['key'] = $keys;
693
        }
694
        if (array_key_exists('condition', $condition)) {
695
            $command['group']['cond'] = $condition['condition'];
696
        }
697
        if (array_key_exists('finalize', $condition)) {
698
            if ($condition['finalize'] instanceof MongoCode) {
699
                $condition['finalize'] = (string)$condition['finalize'];
700
            }
701
            $command['group']['finalize'] = $condition['finalize'];
702
        }
703
704
        $result = $this->db->command($command, [], $hash);
705
        unset($result['waitedMS']);
706
        $result['ok'] = (int)$result['ok'];
707
        if (count($result['retval'])) {
708
            $result['retval'] = current($result['retval']);
709
        }
710
        return $result;
711
    }
712
713
    /**
714
     * Get the write concern for this connection
715
     * @return array <p>This function returns an array describing the write concern.
716
     * The array contains the values w for an integer acknowledgement level or string mode,
717
     * and wtimeout denoting the maximum number of milliseconds to wait for the server to satisfy the write concern.</p>
718
     */
719
    public function getWriteConcern()
720
    {
721
        return [
722
           'w'        => $this->writeConcern->getW(),
723
           'wtimeout' => $this->writeConcern->getWtimeout(),
724
        ];
725
    }
726
727
    /**
728
     * @link http://php.net/manual/en/mongodb.setwriteconcern.php
729
     * Set the write concern for this database
730
     * @param mixed $w <p>The write concern. This may be an integer denoting the number of servers required to acknowledge the write, or a string mode (e.g. "majority").</p>
731
     * @param int $wtimeout[optional] <p>The maximum number of milliseconds to wait for the server to satisfy the write concern.</p>
0 ignored issues
show
Documentation introduced by
There is no parameter named $wtimeout[optional]. Did you maybe mean $wtimeout?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
732
     * @return boolean Returns <b>TRUE</b> on success, or <b>FALSE</b> otherwise.
733
     */
734
    public function setWriteConcern($w, $wtimeout = null)
735
    {
736
        $this->writeConcern = new WriteConcern($w, $wtimeout);
737
        $this->createCollection();
738
        return true;
739
    }
740
741
    // public function parallelCollectionScan(int $num_cursors)
0 ignored issues
show
Unused Code Comprehensibility introduced by
46% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
742
    // {
743
    //     $command = [
744
    //         'parallelCollectionScan' => $this->name,
0 ignored issues
show
Unused Code Comprehensibility introduced by
56% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
745
    //         'numCursors'             => $num_cursors,
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
746
    //     ];
747
    //     $result = $this->db->command($command, [], $hash);
0 ignored issues
show
Unused Code Comprehensibility introduced by
62% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
748
    //     if ($result) {
0 ignored issues
show
Unused Code Comprehensibility introduced by
63% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
749
    //         return $result;
750
    //     }
751
    //     return false;
752
    // }
753
754
    protected function notImplemented()
755
    {
756
        throw new \Exception('Not implemented');
757
    }
758
759
    private function createCollection()
760
    {
761
        $this->collection = $this->db->getDb()->selectCollection(
762
            $this->name,
763
            [
764
                'writeConcern'   => $this->writeConcern,
765
                'readPreference' => $this->readPreference,
766
            ]
767
        );
768
    }
769
}
770
771