Completed
Pull Request — master (#36)
by Andreas
18:24
created

AbstractCursor::setReadPreference()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 2
Bugs 0 Features 1
Metric Value
c 2
b 0
f 1
dl 0
loc 6
ccs 3
cts 3
cp 1
rs 9.4285
cc 1
eloc 3
nc 1
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
18
use Alcaeus\MongoDbAdapter\Helper\ReadPreference;
19
use MongoDB\Collection;
20
use MongoDB\Driver\Cursor;
21
22
/**
23
 * @internal
24
 */
25
abstract class AbstractCursor
26
{
27
    use ReadPreference;
28
29
    /**
30
     * @var int
31
     */
32
    protected $batchSize;
33
34
    /**
35
     * @var Collection
36
     */
37
    protected $collection;
38
39
    /**
40
     * @var \MongoClient
41
     */
42
    protected $connection;
43
44
    /**
45
     * @var Cursor
46
     */
47
    protected $cursor;
48
49
    /**
50
     * @var \MongoDB\Database
51
     */
52
    protected $db;
53
54
    /**
55
     * @var \IteratorIterator
56
     */
57
    protected $iterator;
58
59
    /**
60
     * @var string
61
     */
62
    protected $ns;
63
64
    /**
65
     * @var array
66
     */
67
    protected $optionNames = [
68
        'batchSize',
69
        'readPreference',
70
    ];
71
72
    /**
73
     * @return Cursor
74
     */
75
    abstract protected function ensureCursor();
76
77
    /**
78
     * @return array
79
     */
80
    abstract protected function getCursorInfo();
81
82
    /**
83
     * Create a new cursor
84
     * @link http://www.php.net/manual/en/mongocursor.construct.php
85
     * @param \MongoClient $connection Database connection.
86
     * @param string $ns Full name of database and collection.
87
     */
88 41
    public function __construct(\MongoClient $connection, $ns)
89
    {
90 41
        $this->connection = $connection;
91 41
        $this->ns = $ns;
92
93 41
        $nsParts = explode('.', $ns);
94 41
        $dbName = array_shift($nsParts);
95 41
        $collectionName = implode('.', $nsParts);
96
97 41
        $this->db = $connection->selectDB($dbName)->getDb();
98
99 41
        if ($collectionName) {
100 33
            $this->collection = $connection->selectCollection($dbName, $collectionName)->getCollection();
101 33
        }
102 41
    }
103
104
    /**
105
     * Returns the current element
106
     * @link http://www.php.net/manual/en/mongocursor.current.php
107
     * @return array
108
     */
109 18
    public function current()
110
    {
111 18
        $document = $this->ensureIterator()->current();
112 18
        if ($document !== null) {
113 18
            $document = TypeConverter::toLegacy($document);
114 18
        }
115
116 18
        return $document;
117
    }
118
119
    /**
120
     * Returns the current result's _id
121
     * @link http://www.php.net/manual/en/mongocursor.key.php
122
     * @return string The current result's _id as a string.
123
     */
124 10
    public function key()
125
    {
126 10
        return $this->ensureIterator()->key();
127
    }
128
129
    /**
130
     * Advances the cursor to the next result
131
     * @link http://www.php.net/manual/en/mongocursor.next.php
132
     * @throws \MongoConnectionException
133
     * @throws \MongoCursorTimeoutException
134
     * @return void
135
     */
136 18
    public function next()
137
    {
138 18
        $this->ensureIterator()->next();
139 18
    }
140
141
    /**
142
     * Returns the cursor to the beginning of the result set
143
     * @throws \MongoConnectionException
144
     * @throws \MongoCursorTimeoutException
145
     * @return void
146
     */
147 36
    public function rewind()
148
    {
149
        // We can recreate the cursor to allow it to be rewound
150 36
        $this->reset();
151 36
        $this->ensureIterator()->rewind();
152 34
    }
153
154
    /**
155
     * Checks if the cursor is reading a valid result.
156
     * @link http://www.php.net/manual/en/mongocursor.valid.php
157
     * @return boolean If the current result is not null.
158
     */
159 34
    public function valid()
160
    {
161 34
        return $this->ensureIterator()->valid();
162
    }
163
164
    /**
165
     * Limits the number of elements returned in one batch.
166
     *
167
     * @link http://docs.php.net/manual/en/mongocursor.batchsize.php
168
     * @param int $batchSize The number of results to return per batch
169
     * @return $this Returns this cursor.
170
     */
171 1
    public function batchSize($batchSize)
172
    {
173 1
        $this->batchSize = $batchSize;
174
175 1
        return $this;
176
    }
177
178
    /**
179
     * Checks if there are documents that have not been sent yet from the database for this cursor
180
     * @link http://www.php.net/manual/en/mongocursor.dead.php
181
     * @return boolean Returns if there are more results that have not been sent to the client, yet.
182
     */
183
    public function dead()
184
    {
185
        return $this->ensureCursor()->isDead();
186
    }
187
188
    /**
189
     * @return array
190
     */
191 2
    public function info()
192
    {
193 2
        return $this->getCursorInfo() + $this->getIterationInfo();
194
    }
195
196
    /**
197
     * @link http://www.php.net/manual/en/mongocursor.setreadpreference.php
198
     * @param string $readPreference
199
     * @param array $tags
200
     * @return $this Returns this cursor.
201
     */
202 41
    public function setReadPreference($readPreference, $tags = null)
203
    {
204 41
        $this->setReadPreferenceFromParameters($readPreference, $tags);
205
206 41
        return $this;
207
    }
208
209
    /**
210
     * Sets a client-side timeout for this query
211
     * @link http://www.php.net/manual/en/mongocursor.timeout.php
212
     * @param int $ms The number of milliseconds for the cursor to wait for a response. By default, the cursor will wait forever.
213
     * @return $this Returns this cursor
214
     */
215
    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...
216
    {
217
        $this->notImplemented();
218
    }
219
220
    /**
221
     * Applies all options set on the cursor, overwriting any options that have already been set
222
     *
223
     * @param array $optionNames Array of option names to be applied (will be read from properties)
224
     * @return array
225
     */
226 38
    protected function getOptions($optionNames = null)
227
    {
228 38
        $options = [];
229
230 38
        if ($optionNames === null) {
231 36
            $optionNames = $this->optionNames;
232 36
        }
233
234 38
        foreach ($optionNames as $option) {
235 38
            $converter = 'convert' . ucfirst($option);
236 38
            $value = method_exists($this, $converter) ? $this->$converter() : $this->$option;
237
238 38
            if ($value === null) {
239 38
                continue;
240
            }
241
242 37
            $options[$option] = $value;
243 38
        }
244
245 38
        return $options;
246
    }
247
248
    /**
249
     * @return \Generator
250
     */
251 36
    protected function ensureIterator()
252
    {
253 36
        if ($this->iterator === null) {
254
            // MongoDB\Driver\Cursor needs to be wrapped into a \Generator so that a valid \Iterator with working implementations of
255
            // next, current, valid, key and rewind is returned. These methods don't work if we wrap the Cursor inside an \IteratorIterator
256 36
            $this->iterator = $this->wrapTraversable($this->ensureCursor());
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->wrapTraversable($this->ensureCursor()) of type object<Generator> is incompatible with the declared type object<IteratorIterator> of property $iterator.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
257 34
        }
258
259 34
        return $this->iterator;
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->iterator; of type Generator|IteratorIterator adds the type IteratorIterator to the return on line 259 which is incompatible with the return type documented by Alcaeus\MongoDbAdapter\A...tCursor::ensureIterator of type Generator.
Loading history...
260
    }
261
262
    /**
263
     * @param \Traversable $traversable
264
     * @return \Generator
265
     */
266 34
    private function wrapTraversable(\Traversable $traversable)
267
    {
268 34
        foreach ($traversable as $key => $value) {
269 18
            yield $key => $value;
270 34
        }
271 34
    }
272
273
    /**
274
     * @throws \MongoCursorException
275
     */
276 15
    protected function errorIfOpened()
277
    {
278 15
        if ($this->cursor === null) {
279 15
            return;
280
        }
281
282
        throw new \MongoCursorException('cannot modify cursor after beginning iteration.');
283
    }
284
285
    /**
286
     * @return array
287
     */
288 2
    protected function getIterationInfo()
289
    {
290
        $iterationInfo = [
291 2
            'started_iterating' => $this->cursor !== null,
292 2
        ];
293
294 2
        if ($this->cursor !== null) {
295 2
            switch ($this->cursor->getServer()->getType()) {
296 2
                case \MongoDB\Driver\Server::TYPE_RS_ARBITER:
297
                    $typeString = 'ARBITER';
298
                    break;
299 2
                case \MongoDB\Driver\Server::TYPE_MONGOS:
300
                    $typeString = 'MONGOS';
301
                    break;
302 2
                case \MongoDB\Driver\Server::TYPE_RS_PRIMARY:
303
                    $typeString = 'PRIMARY';
304
                    break;
305 2
                case \MongoDB\Driver\Server::TYPE_RS_SECONDARY:
306
                    $typeString = 'SECONDARY';
307
                    break;
308 2
                default:
309 2
                    $typeString = 'STANDALONE';
310 2
            }
311
312
            $iterationInfo += [
313 2
                'id' => (string) $this->cursor->getId(),
314 2
                'at' => null, // @todo Complete info for cursor that is iterating
315 2
                'numReturned' => null, // @todo Complete info for cursor that is iterating
316 2
                'server' => null, // @todo Complete info for cursor that is iterating
317 2
                'host' => $this->cursor->getServer()->getHost(),
318 2
                'port' => $this->cursor->getServer()->getPort(),
319 2
                'connection_type_desc' => $typeString,
320
            ];
321 2
        }
322
323 2
        return $iterationInfo;
324
    }
325
326
    /**
327
     * @throws \Exception
328
     */
329
    protected function notImplemented()
330
    {
331
        throw new \Exception('Not implemented');
332
    }
333
334
    /**
335
     * Clears the cursor
336
     *
337
     * This is generic but implemented as protected since it's only exposed in MongoCursor
338
     */
339 36
    protected function reset()
340
    {
341 36
        $this->cursor = null;
342 36
        $this->iterator = null;
343 36
    }
344
}
345