Completed
Pull Request — master (#10)
by Andreas
02:32
created

AbstractCursor::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 11
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 11
ccs 8
cts 8
cp 1
rs 9.4286
nc 1
cc 1
eloc 7
nop 2
crap 1
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
namespace Alcaeus\MongoDbAdapter;
17
use MongoDB\Collection;
18
use MongoDB\Driver\Cursor;
19
use MongoDB\Driver\ReadPreference;
20
21
/**
22
 * @internal
23
 */
24
abstract class AbstractCursor
25
{
26
    /**
27
     * @var int
28
     */
29
    protected $batchSize;
30
31
    /**
32
     * @var Collection
33
     */
34
    protected $collection;
35
36
    /**
37
     * @var \MongoClient
38
     */
39
    protected $connection;
40
41
    /**
42
     * @var Cursor
43
     */
44
    protected $cursor;
45
46
    /**
47
     * @var \MongoDB\Database
48
     */
49
    protected $db;
50
51
    /**
52
     * @var \IteratorIterator
53
     */
54
    protected $iterator;
55
56
    /**
57
     * @var string
58
     */
59
    protected $ns;
60
61
    /**
62
     * @var array
63
     */
64
    protected $optionNames = [
65
        'batchSize',
66
        'readPreference',
67
    ];
68
69
    /**
70
     * @var array
71
     */
72
    protected $readPreference = [];
73
74
    /**
75
     * @return Cursor
76
     */
77
    abstract protected function ensureCursor();
78
79
    /**
80
     * @return array
81
     */
82
    abstract protected function getCursorInfo();
83
84
    /**
85
     * Create a new cursor
86
     * @link http://www.php.net/manual/en/mongocursor.construct.php
87
     * @param \MongoClient $connection Database connection.
88
     * @param string $ns Full name of database and collection.
89
     */
90 19
    public function __construct(\MongoClient $connection, $ns)
91
    {
92 19
        $this->connection = $connection;
93 19
        $this->ns = $ns;
94
95 19
        $nsParts = explode('.', $ns);
96 19
        $db = array_shift($nsParts);
97
98 19
        $this->db = $connection->selectDB($db)->getDb();
99 19
        $this->collection = $connection->selectCollection($db, implode('.', $nsParts))->getCollection();
100 19
    }
101
102
    /**
103
     * Returns the current element
104
     * @link http://www.php.net/manual/en/mongocursor.current.php
105
     * @return array
106
     */
107 1
    public function current()
108
    {
109 1
        $document = $this->ensureIterator()->current();
110 1
        if ($document !== null) {
111 1
            $document = TypeConverter::convertObjectToLegacyArray($document);
112 1
        }
113
114 1
        return $document;
115
    }
116
117
    /**
118
     * Returns the current result's _id
119
     * @link http://www.php.net/manual/en/mongocursor.key.php
120
     * @return string The current result's _id as a string.
121
     */
122 1
    public function key()
123
    {
124 1
        return $this->ensureIterator()->key();
125
    }
126
127
    /**
128
     * Advances the cursor to the next result
129
     * @link http://www.php.net/manual/en/mongocursor.next.php
130
     * @throws \MongoConnectionException
131
     * @throws \MongoCursorTimeoutException
132
     * @return void
133
     */
134 1
    public function next()
135
    {
136 1
        $this->ensureIterator()->next();
137 1
    }
138
139
    /**
140
     * Returns the cursor to the beginning of the result set
141
     * @throws \MongoConnectionException
142
     * @throws \MongoCursorTimeoutException
143
     * @return void
144
     */
145 17
    public function rewind()
146
    {
147
        // We can recreate the cursor to allow it to be rewound
148 17
        $this->reset();
149 17
        $this->ensureIterator()->rewind();
150 17
    }
151
152
    /**
153
     * Checks if the cursor is reading a valid result.
154
     * @link http://www.php.net/manual/en/mongocursor.valid.php
155
     * @return boolean If the current result is not null.
156
     */
157 17
    public function valid()
158
    {
159 17
        return $this->ensureIterator()->valid();
160
    }
161
162
    /**
163
     * Limits the number of elements returned in one batch.
164
     *
165
     * @link http://docs.php.net/manual/en/mongocursor.batchsize.php
166
     * @param int $batchSize The number of results to return per batch
167
     * @return $this Returns this cursor.
168
     */
169 1
    public function batchSize($batchSize)
170
    {
171 1
        $this->batchSize = $batchSize;
172
173 1
        return $this;
174
    }
175
176
    /**
177
     * Checks if there are documents that have not been sent yet from the database for this cursor
178
     * @link http://www.php.net/manual/en/mongocursor.dead.php
179
     * @return boolean Returns if there are more results that have not been sent to the client, yet.
180
     */
181
    public function dead()
182
    {
183
        return $this->ensureCursor()->isDead();
184
    }
185
186
    /**
187
     * Get the read preference for this query
188
     * @link http://www.php.net/manual/en/mongocursor.getreadpreference.php
189
     * @return array
190
     */
191
    public function getReadPreference()
192
    {
193
        return $this->readPreference;
194
    }
195
196
    /**
197
     * @return array
198
     */
199
    public function info()
200
    {
201
        return $this->getCursorInfo() + $this->getIterationInfo();
202
    }
203
204
    /**
205
     * @link http://www.php.net/manual/en/mongocursor.setreadpreference.php
206
     * @param string $readPreference
207
     * @param array $tags
208
     * @return $this Returns this cursor.
209
     */
210 2
    public function setReadPreference($readPreference, array $tags = [])
211
    {
212
        $availableReadPreferences = [
213 2
            \MongoClient::RP_PRIMARY,
214 2
            \MongoClient::RP_PRIMARY_PREFERRED,
215 2
            \MongoClient::RP_SECONDARY,
216 2
            \MongoClient::RP_SECONDARY_PREFERRED,
217
            \MongoClient::RP_NEAREST
218 2
        ];
219 2
        if (! in_array($readPreference, $availableReadPreferences)) {
220
            trigger_error("The value '$readPreference' is not valid as read preference type", E_WARNING);
221
            return $this;
222
        }
223
224 2
        if ($readPreference == \MongoClient::RP_PRIMARY && count($tags)) {
225
            trigger_error("You can't use read preference tags with a read preference of PRIMARY", E_WARNING);
226
            return $this;
227
        }
228
229 2
        $this->readPreference = [
230 2
            'type' => $readPreference,
231
            'tagsets' => $tags
232 2
        ];
233
234 2
        return $this;
235
    }
236
237
    /**
238
     * Sets a client-side timeout for this query
239
     * @link http://www.php.net/manual/en/mongocursor.timeout.php
240
     * @param int $ms The number of milliseconds for the cursor to wait for a response. By default, the cursor will wait forever.
241
     * @return $this Returns this cursor
242
     */
243
    public function timeout($ms)
0 ignored issues
show
Unused Code introduced by
The parameter $ms 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...
244
    {
245
        $this->notImplemented();
246
    }
247
248
    /**
249
     * Applies all options set on the cursor, overwriting any options that have already been set
250
     *
251
     * @param array $optionNames Array of option names to be applied (will be read from properties)
252
     * @return array
253
     */
254 18
    protected function getOptions($optionNames = null)
255
    {
256 18
        $options = [];
257
258 18
        if ($optionNames === null) {
259 17
            $optionNames = $this->optionNames;
260 17
        }
261
262 18
        foreach ($optionNames as $option) {
263 18
            $converter = 'convert' . ucfirst($option);
264 18
            $value = method_exists($this, $converter) ? $this->$converter() : $this->$option;
265
266 18
            if ($value === null) {
267 18
                continue;
268
            }
269
270 18
            $options[$option] = $value;
271 18
        }
272
273 18
        return $options;
274
    }
275
276
    /**
277
     * @return ReadPreference|null
278
     */
279 17
    protected function convertReadPreference()
280
    {
281 17
        $type = array_key_exists('type', $this->readPreference) ? $this->readPreference['type'] : null;
282 17
        if ($type === null) {
283 15
            return null;
284
        }
285
286
        switch ($type) {
287 2
            case \MongoClient::RP_PRIMARY_PREFERRED:
288
                $mode = ReadPreference::RP_PRIMARY_PREFERRED;
289
                break;
290 2
            case \MongoClient::RP_SECONDARY:
291 1
                $mode = ReadPreference::RP_SECONDARY;
292 1
                break;
293 1
            case \MongoClient::RP_SECONDARY_PREFERRED:
294
                $mode = ReadPreference::RP_SECONDARY_PREFERRED;
295
                break;
296 1
            case \MongoClient::RP_NEAREST:
297
                $mode = ReadPreference::RP_NEAREST;
298
                break;
299 1
            default:
300 1
                $mode = ReadPreference::RP_PRIMARY;
301 1
        }
302
303 2
        $tagSets = array_key_exists('tagsets', $this->readPreference) ? $this->readPreference['tagsets'] : [];
304
305 2
        return new ReadPreference($mode, $tagSets);
306
    }
307
308
    /**
309
     * @return \IteratorIterator
310
     */
311 17
    protected function ensureIterator()
312
    {
313 17
        if ($this->iterator === null) {
314 17
            $this->iterator = new \IteratorIterator($this->ensureCursor());
315 17
        }
316
317 17
        return $this->iterator;
318
    }
319
320
    /**
321
     * @throws \MongoCursorException
322
     */
323 13
    protected function errorIfOpened()
324
    {
325 13
        if ($this->cursor === null) {
326 13
            return;
327
        }
328
329
        throw new \MongoCursorException('cannot modify cursor after beginning iteration.');
330
    }
331
332
    /**
333
     * @return array
334
     */
335
    protected function getIterationInfo()
336
    {
337
        $iterationInfo = [
338
            'started_iterating' => $this->cursor !== null,
339
        ];
340
341
        if ($this->cursor !== null) {
342
            switch ($this->cursor->getServer()->getType()) {
343
                case \MongoDB\Driver\Server::TYPE_ARBITER:
344
                    $typeString = 'ARBITER';
345
                    break;
346
                case \MongoDB\Driver\Server::TYPE_MONGOS:
347
                    $typeString = 'MONGOS';
348
                    break;
349
                case \MongoDB\Driver\Server::TYPE_PRIMARY:
350
                    $typeString = 'PRIMARY';
351
                    break;
352
                case \MongoDB\Driver\Server::TYPE_SECONDARY:
353
                    $typeString = 'SECONDARY';
354
                    break;
355
                default:
356
                    $typeString = 'STANDALONE';
357
            }
358
359
            $iterationInfo += [
360
                'id' => (string)$this->cursor->getId(),
361
                'at' => null, // @todo Complete info for cursor that is iterating
362
                'numReturned' => null, // @todo Complete info for cursor that is iterating
363
                'server' => null, // @todo Complete info for cursor that is iterating
364
                'host' => $this->cursor->getServer()->getHost(),
365
                'port' => $this->cursor->getServer()->getPort(),
366
                'connection_type_desc' => $typeString,
367
            ];
368
        }
369
370
        return $iterationInfo;
371
    }
372
373
    /**
374
     * @throws \Exception
375
     */
376
    protected function notImplemented()
377
    {
378
        throw new \Exception('Not implemented');
379
    }
380
381
    /**
382
     * Clears the cursor
383
     *
384
     * This is generic but implemented as protected since it's only exposed in MongoCursor
385
     */
386 17
    protected function reset()
387
    {
388 17
        $this->cursor = null;
389 17
        $this->iterator = null;
390 17
    }
391
}
392