GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( 4f7f40...089298 )
by De
02:05
created

Cursor::__construct()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 13
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 13
c 0
b 0
f 0
rs 9.4285
cc 2
eloc 6
nc 2
nop 2
1
<?php
2
3
/**
4
 * This file is part of the PHPMongo package.
5
 *
6
 * (c) Dmytro Sokil <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Sokil\Mongo;
13
14
use Sokil\Mongo\Exception\CursorException;
15
use Sokil\Mongo\Exception\FeatureNotSupportedException;
16
use Sokil\Mongo\Exception\WriteException;
17
18
/**
19
 * @mixin Expression
20
 */
21
class Cursor implements
22
    \Iterator,
23
    \Countable
24
{
25
    /**
26
     *
27
     * @var \Sokil\Mongo\Client
28
     */
29
    private $client;
30
31
    /**
32
     *
33
     * @var \Sokil\Mongo\Collection
34
     */
35
    private $collection;
36
37
    /**
38
     *
39
     * @var array
40
     */
41
    private $fields = array();
42
43
    /**
44
     *
45
     * @var \MongoCursor
46
     */
47
    private $cursor;
48
    /**
49
     *
50
     * @var Expression
51
     */
52
    private $expression;
53
54
    /**
55
     * Offset
56
     * @var int
57
     */
58
    private $skip = 0;
59
60
    /**
61
     * Limit
62
     * @var int
63
     */
64
    private $limit = 0;
65
66
    /**
67
     * Definition of sort
68
     * @var array
69
     */
70
    private $sort = array();
71
72
    /**
73
     * Definition of read preference
74
     * @var array
75
     */
76
    private $readPreference = array();
77
78
    /**
79
     * Return result as array or as Document instance
80
     * @var boolean
81
     */
82
    private $isResultAsArray = false;
83
84
    /**
85
     * Cursor options
86
     * @var array
87
     */
88
    private $options = array(
89
        'expressionClass' => '\Sokil\Mongo\Expression',
90
        /**
91
         * @link http://docs.mongodb.org/manual/reference/method/cursor.batchSize/
92
         * @var int number of documents to return in each batch of the response from the MongoDB instance
93
         */
94
        'batchSize' => null,
95
        // client timeout
96
        'clientTimeout' => null,
97
        // Specifies a cumulative time limit in milliseconds to be allowed by the server for processing operations on the cursor.
98
        'serverTimeout' => null,
99
    );
100
101
    /**
102
     * Use document pool to create Document object from array
103
     * @var bool
104
     */
105
    private $isDocumentPoolUsed = true;
106
107
    /**
108
     * Index hinting
109
     * @param \Sokil\Mongo\Collection $collection
110
     * @param array $options
111
     */
112
    private $hint;
113
114
    public function __construct(Collection $collection, array $options = null)
115
    {
116
        $this->collection = $collection;
117
118
        $this->client = $this->collection->getDatabase()->getClient();
119
120
        if (!empty($options)) {
121
            $this->options = $options + $this->options;
122
        }
123
124
        // expression
125
        $this->expression = $this->expression();
126
    }
127
128
    public function __call($name, $arguments)
129
    {
130
        call_user_func_array(
131
            array($this->expression, $name),
132
            $arguments
133
        );
134
135
        return $this;
136
    }
137
138
    /**
139
     * Get option
140
     *
141
     * @param string|int $name
142
     * @param mixed $default
143
     *
144
     * @return mixed
145
     */
146
    public function getOption($name, $default = null)
147
    {
148
        return isset($this->options[$name]) ? $this->options[$name] : $default;
149
    }
150
151
    /**
152
     * Get result as array
153
     *
154
     * @return $this
155
     */
156
    public function asArray()
157
    {
158
        $this->isResultAsArray = true;
159
        return $this;
160
    }
161
162
    /**
163
     * Get result as object
164
     * @return $this
165
     */
166
    public function asObject()
167
    {
168
        $this->isResultAsArray = false;
169
        return $this;
170
    }
171
172
    /**
173
     * Check if result returned as array
174
     * @return bool
175
     */
176
    public function isResultAsArray()
177
    {
178
        return $this->isResultAsArray;
179
    }
180
181
    /**
182
     * Return only specified fields
183
     *
184
     * @param array $fields
185
     * @return Cursor
186
     */
187
    public function fields(array $fields)
188
    {
189
        $this->fields = array_fill_keys($fields, 1);
190
191
        $this->skipDocumentPool();
192
193
        return $this;
194
    }
195
196
    /**
197
     * Return all fields except specified
198
     *
199
     * @param array $fields
200
     * @return \Sokil\Mongo\Cursor
201
     */
202
    public function skipFields(array $fields)
203
    {
204
        $this->fields = array_fill_keys($fields, 0);
205
206
        $this->skipDocumentPool();
207
208
        return $this;
209
    }
210
211
    /**
212
     * Append field to accept list
213
     *
214
     * @param string $field field name
215
     *
216
     * @return Cursor
217
     */
218
    public function field($field)
219
    {
220
        $this->fields[$field] = 1;
221
222
        $this->skipDocumentPool();
223
224
        return $this;
225
    }
226
227
    /**
228
     * Append field to skip list
229
     *
230
     * @param string $field field name
231
     * @return Cursor
232
     */
233
    public function skipField($field)
234
    {
235
        $this->fields[$field] = 0;
236
237
        $this->skipDocumentPool();
238
239
        return $this;
240
    }
241
242
    /**
243
     * Paginate list of sub-documents
244
     *
245
     * @param string $field
246
     * @param integer $limit
247
     * @param integer $skip
248
     * @return \Sokil\Mongo\Cursor
249
     * @throws Exception
250
     */
251
    public function slice($field, $limit, $skip = null)
252
    {
253
        $limit  = (int) $limit;
254
        $skip   = (int) $skip;
255
256
        if ($skip) {
257
            $this->fields[$field] = array('$slice' => array($skip, $limit));
258
        } else {
259
            $this->fields[$field] = array('$slice' => $limit);
260
        }
261
262
        $this->skipDocumentPool();
263
264
        return $this;
265
    }
266
267
    /**
268
     * Merge expression
269
     * @param \Sokil\Mongo\Expression $expression
270
     * @return \Sokil\Mongo\Cursor
271
     */
272
    public function query(Expression $expression)
273
    {
274
        $this->expression->merge($expression);
275
        return $this;
276
    }
277
278
    /**
279
     * Helper to create new expression
280
     *
281
     * @return \Sokil\Mongo\Expression
282
     */
283
    public function expression()
284
    {
285
        return new $this->options['expressionClass'];
286
    }
287
288
    /**
289
     * Filter by list of \MongoId
290
     *
291
     * @param array $idList list of ids
292
     * @return \Sokil\Mongo\Cursor
293
     */
294
    public function byIdList(array $idList)
295
    {
296
        $this->expression->whereIn('_id', self::mixedToMongoIdList($idList));
297
        return $this;
298
    }
299
300
    /**
301
     * Filter by id
302
     *
303
     * @param string|\MongoId $id id of document
304
     * @return \Sokil\Mongo\Cursor
305
     */
306
    public function byId($id)
307
    {
308
        if ($id instanceof \MongoId) {
309
            $this->expression->where('_id', $id);
310
        } else {
311
            try {
312
                $this->expression->where('_id', new \MongoId($id));
313
            } catch (\MongoException $e) {
314
                $this->expression->where('_id', $id);
315
            }
316
        }
317
318
        return $this;
319
    }
320
321
    /**
322
     * Skip defined number of documents
323
     *
324
     * @param int $skip number of documents to skip
325
     * @return \Sokil\Mongo\Cursor
326
     */
327
    public function skip($skip)
328
    {
329
        $this->skip = (int) $skip;
330
331
        return $this;
332
    }
333
334
    /**
335
     * Limit result set to specified number of elements
336
     *
337
     * @param int $limit number of elements in result set
338
     * @param int|null $offset number of elements to skip
339
     * @return \Sokil\Mongo\Cursor
340
     */
341
    public function limit($limit, $offset = null)
342
    {
343
        $this->limit = (int) $limit;
344
345
        if (null !== $offset) {
346
            $this->skip($offset);
347
        }
348
349
        return $this;
350
    }
351
352
    /**
353
     * Specifies the number of documents to return in each batch of the response from the MongoDB instance.
354
     *
355
     * @param int $size number of documents
356
     * @link http://docs.mongodb.org/manual/reference/method/cursor.batchSize/
357
     * @return \Sokil\Mongo\Cursor
358
     */
359
    public function setBatchSize($size)
360
    {
361
        $this->options['batchSize'] = (int) $size;
362
363
        return $this;
364
    }
365
366
    /**
367
     * Instructs the driver to stop waiting for a response and throw a
368
     * MongoCursorTimeoutException after a set time.
369
     * A timeout can be set at any time and will affect subsequent queries on
370
     * the cursor, including fetching more results from the database.
371
     *
372
     * @param int $ms
373
     * @return Cursor
374
     */
375
    public function setClientTimeout($ms)
376
    {
377
        $this->options['clientTimeout'] = (int) $ms;
378
        return $this;
379
    }
380
381
    /**
382
     * Server-side timeout for a query,
383
     * Specifies a cumulative time limit in milliseconds to be allowed
384
     * by the server for processing operations on the cursor.
385
     *
386
     * @param int $ms
387
     * @return \Sokil\Mongo\Cursor
388
     */
389
    public function setServerTimeout($ms)
390
    {
391
        $this->options['serverTimeout'] = (int) $ms;
392
        return $this;
393
    }
394
395
    /**
396
     * Sort result by specified keys and directions
397
     *
398
     *  An array of fields by which to sort. Each element in the array has as key the field name, and as value either
399
     * 1 for ascending sort, or -1 for descending sort. Each result is first sorted on the first field in the array,
400
     * then (if it exists) on the second field in the array, etc. This means that the order of the fields in the
401
     * fields array is important. See also the examples section.
402
     *
403
     * @param array $sort
404
     * @return Cursor
405
     */
406
    public function sort(array $sort)
407
    {
408
        $this->sort = $sort;
409
        return $this;
410
    }
411
412
    /**
413
     * Create native driver's cursor
414
     *
415
     * @return \MongoCursor
416
     */
417
    private function getCursor()
418
    {
419
        if ($this->cursor) {
420
            return $this->cursor;
421
        }
422
423
        $this->cursor = $this->collection
424
            ->getMongoCollection()
425
            ->find(
426
                $this->expression->toArray(),
427
                $this->fields
428
            );
429
430
        if ($this->skip) {
431
            $this->cursor->skip($this->skip);
432
        }
433
434
        if ($this->limit) {
435
            $this->cursor->limit($this->limit);
436
        }
437
438
        if ($this->options['batchSize']) {
439
            $this->cursor->batchSize($this->options['batchSize']);
440
        }
441
442
        if ($this->options['clientTimeout']) {
443
            $this->cursor->timeout($this->options['clientTimeout']);
444
        }
445
446
        if ($this->options['serverTimeout']) {
447
            $this->cursor->maxTimeMS($this->options['clientTimeout']);
448
        }
449
450
        if (!empty($this->sort)) {
451
            $this->cursor->sort($this->sort);
452
        }
453
454
        if ($this->hint) {
455
            $this->cursor->hint($this->hint);
456
        }
457
458
        // log request
459
        if ($this->client->hasLogger()) {
460
            $this->client->getLogger()->debug(get_called_class() . ': ' . json_encode(array(
461
                'collection' => $this->collection->getName(),
462
                'query' => $this->expression->toArray(),
463
                'project' => $this->fields,
464
                'sort' => $this->sort,
465
            )));
466
        }
467
468
        $this->cursor->rewind();
469
470
        // define read preferences
471
        if (!empty($this->readPreference)) {
472
            $this->cursor->setReadPreference(
473
                $this->readPreference['type'],
474
                $this->readPreference['tagsets']
475
            );
476
        }
477
478
        return $this->cursor;
479
    }
480
481
    /**
482
     * Count documents in result without applying limit and offset
483
     * @return int count
484
     */
485
    public function count()
486
    {
487
        return (int) $this->collection
488
            ->getMongoCollection()
489
            ->count($this->expression->toArray());
490
    }
491
492
    /**
493
     * Explain expression
494
     *
495
     * @return array
496
     *
497
     * @throws FeatureNotSupportedException
498
     */
499
    public function explain()
500
    {
501
        if (Client::isEmulationMode()) {
502
            throw new FeatureNotSupportedException('Feature not implemented in emulation mode');
503
        }
504
505
        return $this->getCursor()->explain();
506
    }
507
508
    /**
509
     * Count documents in result with applying limit and offset
510
     *
511
     * ext-mongo:1.0.7  Added limit and skip as second and third parameters, respectively.
512
     * ext-mongo:1.6.0  The second parameter is now an options array. Passing limit and skip as the second and third
513
     *                  parameters, respectively, is deprecated.
514
     *
515
     * @return int
516
     *
517
     * @throws FeatureNotSupportedException
518
     */
519
    public function limitedCount()
520
    {
521
        if (version_compare(\MongoClient::VERSION, '1.0.7', '<')) {
522
            throw new FeatureNotSupportedException('Limit and skip not supported in ext-mongo versions prior to 1.0.7');
523
        }
524
525
        return (int) $this->collection
526
            ->getMongoCollection()
527
            ->count(
528
                $this->expression->toArray(),
529
                $this->limit,
530
                $this->skip
531
            );
532
    }
533
534
535
    /**
536
     * Gte list of \MongoId of current search query
537
     * @return array
538
     */
539
    public function getIdList()
540
    {
541
        return self::mixedToMongoIdList($this->findAll());
542
    }
543
544
    /**
545
     * Find one document which correspond to expression
546
     *
547
     * @return Document|array|null
548
     *
549
     * @throws CursorException
550
     */
551
    public function findOne()
552
    {
553
        try {
554
            $mongoDocument = $this->collection
555
                ->getMongoCollection()
556
                ->findOne(
557
                    $this->expression->toArray(),
558
                    $this->fields
559
                );
560
        } catch (\Exception $e) {
561
            throw new CursorException(
562
                $e->getMessage(),
563
                $e->getCode(),
564
                $e
565
            );
566
        }
567
568
        if (empty($mongoDocument)) {
569
            return null;
570
        }
571
572
        if (true === $this->isResultAsArray) {
573
            return $mongoDocument;
574
        }
575
576
        return $this->collection->hydrate(
577
            $mongoDocument,
578
            $this->isDocumentPoolUsed()
579
        );
580
    }
581
582
    /**
583
     * Get result of searching
584
     *
585
     * @return array
586
     */
587
    public function findAll()
588
    {
589
        return iterator_to_array($this);
590
    }
591
592
    /**
593
     * Get random document
594
     *
595
     * @return Document|null
596
     */
597
    public function findRandom()
598
    {
599
        $count = $this->count();
600
601
        if (0 === $count) {
602
            return null;
603
        }
604
605
        if (1 === $count) {
606
            return $this->findOne();
607
        }
608
609
        return $this
610
            ->skip(mt_rand(0, $count - 1))
611
            ->limit(1)
612
            ->current();
613
    }
614
615
    /**
616
     * Get query builder's expression
617
     *
618
     * @return Expression
619
     */
620
    public function getExpression()
621
    {
622
        return $this->expression;
623
    }
624
625
    /**
626
     * Get MongoDB query array
627
     *
628
     * @return array
629
     */
630
    public function getMongoQuery()
631
    {
632
        return $this->expression->toArray();
633
    }
634
    
635
    /**
636
     * Return the values from a single field in the result set of documents
637
     *
638
     * @param string $fieldName
639
     * @return array
640
     */
641
    public function pluck($fieldName)
642
    {
643
        $isEmbeddedDocumentField = false !== strpos($fieldName, '.');
644
645
        $valueList = array();
646
647
        if ($isEmbeddedDocumentField) {
648
            // get result
649
            if ($this->isResultAsArray) {
650
                $cursor = clone $this;
651
                $documentObjectList = $cursor->asObject()->findAll();
652
                unset($cursor);
653
            } else {
654
                $documentObjectList = $this->findAll();
655
            }
656
            // get value of field
657
            foreach ($documentObjectList as $key => $documentObject) {
658
                $valueList[$key] = $documentObject->get($fieldName);
659
            }
660
        } else {
661
            // get result
662
            if ($this->isResultAsArray) {
663
                $documentArrayList = $this->findAll();
664
            } else {
665
                $cursor = clone $this;
666
                $documentArrayList = $cursor->asArray()->findAll();
667
                unset($cursor);
668
            }
669
            // get values of field
670
            $valueList = array_column($documentArrayList, $fieldName, '_id');
671
        }
672
673
        return $valueList;
674
    }
675
676
    /**
677
     * Get document instance and remove it from collection
678
     *
679
     * @return \Sokil\Mongo\Document
680
     */
681
    public function findAndRemove()
682
    {
683
        $mongoDocument = $this->collection->getMongoCollection()->findAndModify(
684
            $this->expression->toArray(),
685
            null,
686
            $this->fields,
687
            array(
688
                'remove' => true,
689
                'sort' => $this->sort,
690
            )
691
        );
692
693
        if (empty($mongoDocument)) {
694
            return null;
695
        }
696
697
        return $this->collection->hydrate(
698
            $mongoDocument,
699
            $this->isDocumentPoolUsed()
700
        );
701
    }
702
703
    /**
704
     * Find first document and update it
705
     *
706
     * @param Operator $operator operations with document to update
707
     * @param bool $upsert if document not found - create
708
     * @param bool $returnUpdated if true - return updated document
709
     *
710
     * @return null|Document
711
     */
712
    public function findAndUpdate(Operator $operator, $upsert = false, $returnUpdated = true)
713
    {
714
        $mongoDocument = $this->collection
715
            ->getMongoCollection()
716
            ->findAndModify(
717
                $this->expression->toArray(),
718
                $operator ? $operator->toArray() : null,
719
                $this->fields,
720
                array(
721
                    'new' => $returnUpdated,
722
                    'sort' => $this->sort,
723
                    'upsert' => $upsert,
724
                )
725
            );
726
727
        if (empty($mongoDocument)) {
728
            return null;
729
        }
730
731
        return $this->collection->hydrate($mongoDocument, $this->isDocumentPoolUsed());
732
    }
733
734
    /**
735
     * Apply callable to all documents in cursor
736
     *
737
     * @param callable $handler
738
     * @return array
739
     */
740 View Code Duplication
    public function map($handler)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
741
    {
742
        $result = array();
743
744
        foreach ($this as $id => $document) {
745
            $result[$id] = $handler($document);
746
        }
747
748
        return $result;
749
    }
750
751
    /**
752
     * Filter documents in cursor by condition in callable
753
     *
754
     * @param callable $handler
755
     * @return array
756
     */
757 View Code Duplication
    public function filter($handler)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
758
    {
759
        $result = array();
760
761
        foreach ($this as $id => $document) {
762
            if (!$handler($document)) {
763
                continue;
764
            }
765
766
            $result[$id] = $document;
767
        }
768
769
        return $result;
770
    }
771
772
    /**
773
     * Get result set of documents.
774
     *
775
     * @return \Sokil\Mongo\ResultSet
776
     */
777
    public function getResultSet()
778
    {
779
        return new ResultSet($this->findAll());
780
    }
781
782
    /**
783
     * Get paginator
784
     *
785
     * @param int $page page number
786
     * @param int $itemsOnPage number of items on page
787
     * @return Paginator
788
     */
789
    public function paginate($page, $itemsOnPage = 30)
790
    {
791
        $paginator = new Paginator($this);
792
793
        return $paginator
794
            ->setCurrentPage($page)
795
            ->setItemsOnPage($itemsOnPage);
796
    }
797
798
    /**
799
     * Clears the cursor
800
     */
801
    public function reset()
802
    {
803
        if ($this->cursor) {
804
            $this->cursor->reset();
805
        }
806
807
        return $this;
808
    }
809
810
    /**
811
     * Returns the cursor to the beginning of the result set.
812
     * This is identical to call reset() && next().
813
     * @return $this
814
     */
815
    public function rewind()
816
    {
817
        $this->getCursor()->rewind();
818
        return $this;
819
    }
820
821
    public function valid()
822
    {
823
        return $this->getCursor()->valid();
824
    }
825
826
    public function current()
827
    {
828
        $mongoDocument = $this->getCursor()->current();
829
        if (empty($mongoDocument)) {
830
            return null;
831
        }
832
833
        if ($this->isResultAsArray) {
834
            return $mongoDocument;
835
        }
836
837
        return $this->collection->hydrate(
838
            $mongoDocument,
839
            $this->isDocumentPoolUsed
840
        );
841
    }
842
843
    public function key()
844
    {
845
        return $this->getCursor()->key();
846
    }
847
848
    public function next()
849
    {
850
        $this->getCursor()->next();
851
        return $this;
852
    }
853
854
    public function readPrimaryOnly()
855
    {
856
        $this->readPreference = array(
857
            'type'      => \MongoClient::RP_PRIMARY,
858
            'tagsets'   => array(),
859
        );
860
861
        return $this;
862
    }
863
864
    public function readPrimaryPreferred(array $tags = null)
865
    {
866
        $this->readPreference = array(
867
            'type'      => \MongoClient::RP_PRIMARY_PREFERRED,
868
            'tagsets'   => $tags,
869
        );
870
871
        return $this;
872
    }
873
874
    public function readSecondaryOnly(array $tags = null)
875
    {
876
        $this->readPreference = array(
877
            'type'      => \MongoClient::RP_SECONDARY,
878
            'tagsets'   => $tags,
879
        );
880
881
        return $this;
882
    }
883
884
    public function readSecondaryPreferred(array $tags = null)
885
    {
886
        $this->readPreference = array(
887
            'type'      => \MongoClient::RP_SECONDARY_PREFERRED,
888
            'tagsets'   => $tags,
889
        );
890
891
        return $this;
892
    }
893
894
    public function readNearest(array $tags = null)
895
    {
896
        $this->readPreference = array(
897
            'type'      => \MongoClient::RP_NEAREST,
898
            'tagsets'   => $tags,
899
        );
900
901
        return $this;
902
    }
903
904
    /**
905
     * @return array
906
     */
907
    public function getReadPreference()
908
    {
909
        if ($this->cursor) {
910
            return $this->cursor->getReadPreference();
911
        }
912
913
        return $this->readPreference;
914
    }
915
916
    public function isDocumentPoolUsed()
917
    {
918
        return $this->isDocumentPoolUsed;
919
    }
920
921
    public function useDocumentPool()
922
    {
923
        $this->isDocumentPoolUsed = true;
924
        return $this;
925
    }
926
927
    public function skipDocumentPool()
928
    {
929
        $this->isDocumentPoolUsed = false;
930
        return $this;
931
    }
932
933
    /**
934
     * Specify index to use
935
     *
936
     * @link http://docs.mongodb.org/manual/reference/operator/meta/hint/
937
     * @param array|string $specification Specify the index either by the index name or by document
938
     * @return \Sokil\Mongo\Cursor
939
     */
940
    public function hint($specification)
941
    {
942
        $this->hint = $specification;
943
        return $this;
944
    }
945
946
    /**
947
     * Copy selected documents to another collection
948
     *
949
     * @param string $targetCollectionName
950
     * @param string|null $targetDatabaseName Target database name. If not specified - use current
951
     * @param int $batchLimit count of documents to get from old and insert to new collection per time
952
     *
953
     * @return Cursor
954
     *
955
     * @throws WriteException
956
     */
957
    public function copyToCollection(
958
        $targetCollectionName,
959
        $targetDatabaseName = null,
960
        $batchLimit = 100
961
    ) {
962
        // target database
963
        if (empty($targetDatabaseName)) {
964
            $database = $this->collection->getDatabase();
965
        } else {
966
            $database = $this->client->getDatabase($targetDatabaseName);
967
        }
968
969
        // target collection
970
        $targetMongoCollection = $database
971
            ->getCollection($targetCollectionName)
972
            ->getMongoCollection();
973
974
        // cursor
975
        $cursor = $this->getCursor();
976
977
        // copy data
978
        $inProgress = true;
979
        while ($inProgress) {
980
            // get next pack of documents
981
            $documentList = array();
982
            for ($i = 0; $i < $batchLimit; $i++) {
983
                if (!$cursor->valid()) {
984
                    $inProgress = false;
985
986
                    if (!empty($documentList)) {
987
                        // still need batch insert
988
                        break;
989
                    } else {
990
                        // no documents to insert - just exit
991
                        break(2);
992
                    }
993
                }
994
995
                $documentList[] = $cursor->current();
996
                $cursor->next();
997
            }
998
999
            // insert
1000
            $result = $targetMongoCollection->batchInsert($documentList);
1001
1002
            // With passed write concern, returns an associative array with the status of the inserts ("ok")
1003
            // and any error that may have occurred ("err").
1004
            // Otherwise, returns TRUE if the batch insert was successfully sent, FALSE otherwise.
1005
            if (is_array($result)) {
1006
                if ($result['ok'] != 1) {
1007
                    throw new WriteException('Batch insert error: ' . $result['err']);
1008
                }
1009
            } elseif (false === $result) {
1010
                throw new WriteException('Batch insert error');
1011
            }
1012
        }
1013
1014
        return $this;
1015
    }
1016
1017
    /**
1018
     * Move selected documents to another collection.
1019
     * Documents will be removed from source collection only after
1020
     * copying them to target collection.
1021
     *
1022
     * @param string $targetCollectionName
1023
     * @param string|null $targetDatabaseName Target database name. If not specified - use current
1024
     * @param int $batchLimit count of documents to get from old and insert to new collection per time
1025
     */
1026
    public function moveToCollection(
1027
        $targetCollectionName,
1028
        $targetDatabaseName = null,
1029
        $batchLimit = 100
1030
    ) {
1031
        // copy to target
1032
        $this->copyToCollection($targetCollectionName, $targetDatabaseName, $batchLimit);
1033
1034
        // remove from source
1035
        $this->collection->batchDelete($this->expression);
0 ignored issues
show
Documentation introduced by
$this->expression is of type object<Sokil\Mongo\Expression>, but the function expects a callable.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1036
    }
1037
1038
    /**
1039
     * Used to get hash that uniquely identifies current query
1040
     *
1041
     * @return string
1042
     */
1043
    public function getHash()
1044
    {
1045
        $hash = array();
1046
1047
        // expression
1048
        $hash[] = json_encode($this->expression->toArray());
1049
1050
        // sorts
1051 View Code Duplication
        if (!empty($this->sort)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1052
            $sort = $this->sort;
1053
            ksort($sort);
1054
            $hash[] = implode('', array_merge(array_keys($sort), array_values($sort)));
1055
        }
1056
1057
        // fields
1058 View Code Duplication
        if (!empty($this->fields)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1059
            $fields = $this->fields;
1060
            ksort($fields);
1061
            $hash[] = implode('', array_merge(array_keys($fields), array_values($fields)));
1062
        }
1063
1064
        // skip and limit
1065
        $hash[] = $this->skip;
1066
        $hash[] = $this->limit;
1067
1068
        // get hash
1069
        return md5(implode(':', $hash));
1070
    }
1071
1072
    /**
1073
     * Get list of MongoId objects from array of strings, MongoId's and Document's
1074
     *
1075
     * @param array $list
1076
     * @return array list of \MongoId
1077
     */
1078
    public static function mixedToMongoIdList(array $list)
1079
    {
1080
        return array_map(function ($element) {
1081
            // MongoId
1082
            if ($element instanceof \MongoId) {
1083
                return $element;
1084
            }
1085
1086
            // \Sokil\Mongo\Document
1087
            if ($element instanceof Document) {
1088
                return $element->getId();
1089
            }
1090
1091
            // array with id key
1092
            if (is_array($element)) {
1093
                if (!isset($element['_id'])) {
1094
                    throw new \InvalidArgumentException('Array must have _id key');
1095
                }
1096
                return $element['_id'];
1097
            }
1098
1099
            // string
1100
            if (is_string($element)) {
1101
                try {
1102
                    return new \MongoId($element);
1103
                } catch (\MongoException $e) {
1104
                    return $element;
1105
                }
1106
            }
1107
1108
            // int
1109
            if (is_int($element)) {
1110
                return $element;
1111
            }
1112
1113
            throw new \InvalidArgumentException('Must be \MongoId, \Sokil\Mongo\Document, array with _id key, string or integer');
1114
        }, array_values($list));
1115
    }
1116
}
1117