17
17
18
18
namespace MongoDB ;
19
19
20
- use MongoDB \BSON \Serializable ;
21
- use MongoDB \Driver \Cursor ;
20
+ use MongoDB \Driver \CursorId ;
22
21
use MongoDB \Driver \Exception \ConnectionException ;
23
22
use MongoDB \Driver \Exception \RuntimeException ;
24
23
use MongoDB \Driver \Exception \ServerException ;
25
- use MongoDB \Exception \InvalidArgumentException ;
26
24
use MongoDB \Exception \ResumeTokenException ;
27
- use MongoDB \Model \TailableCursorIterator ;
25
+ use MongoDB \Model \ChangeStreamIterator ;
28
26
use Iterator ;
29
27
30
28
/**
@@ -42,13 +40,14 @@ class ChangeStream implements Iterator
42
40
*/
43
41
const CURSOR_NOT_FOUND = 43 ;
44
42
45
- private static $ errorCodeCappedPositionLost = 136 ;
46
- private static $ errorCodeInterrupted = 11601 ;
47
- private static $ errorCodeCursorKilled = 237 ;
43
+ private static $ nonResumableErrorCodes = [
44
+ 136 , // CappedPositionLost
45
+ 237 , // CursorKilled
46
+ 11601 , // Interrupted
47
+ ];
48
48
49
- private $ resumeToken ;
50
49
private $ resumeCallable ;
51
- private $ csIt ;
50
+ private $ iterator ;
52
51
private $ key = 0 ;
53
52
54
53
/**
@@ -61,14 +60,13 @@ class ChangeStream implements Iterator
61
60
* Constructor.
62
61
*
63
62
* @internal
64
- * @param Cursor $cursor
65
- * @param callable $resumeCallable
66
- * @param boolean $isFirstBatchEmpty
63
+ * @param ChangeStreamIterator $iterator
64
+ * @param callable $resumeCallable
67
65
*/
68
- public function __construct (Cursor $ cursor , callable $ resumeCallable, $ isFirstBatchEmpty )
66
+ public function __construct (ChangeStreamIterator $ iterator , callable $ resumeCallable )
69
67
{
68
+ $ this ->iterator = $ iterator ;
70
69
$ this ->resumeCallable = $ resumeCallable ;
71
- $ this ->csIt = new TailableCursorIterator ($ cursor , $ isFirstBatchEmpty );
72
70
}
73
71
74
72
/**
@@ -77,15 +75,29 @@ public function __construct(Cursor $cursor, callable $resumeCallable, $isFirstBa
77
75
*/
78
76
public function current ()
79
77
{
80
- return $ this ->csIt ->current ();
78
+ return $ this ->iterator ->current ();
81
79
}
82
80
83
81
/**
84
- * @return \MongoDB\Driver\ CursorId
82
+ * @return CursorId
85
83
*/
86
84
public function getCursorId ()
87
85
{
88
- return $ this ->csIt ->getInnerIterator ()->getId ();
86
+ return $ this ->iterator ->getInnerIterator ()->getId ();
87
+ }
88
+
89
+ /**
90
+ * Returns the resume token for the iterator's current position.
91
+ *
92
+ * Null may be returned if no change documents have been iterated and the
93
+ * server did not include a postBatchResumeToken in its aggregate or getMore
94
+ * command response.
95
+ *
96
+ * @return array|object|null
97
+ */
98
+ public function getResumeToken ()
99
+ {
100
+ return $ this ->iterator ->getResumeToken ();
89
101
}
90
102
91
103
/**
@@ -108,7 +120,7 @@ public function key()
108
120
public function next ()
109
121
{
110
122
try {
111
- $ this ->csIt ->next ();
123
+ $ this ->iterator ->next ();
112
124
$ this ->onIteration ($ this ->hasAdvanced );
113
125
} catch (RuntimeException $ e ) {
114
126
$ this ->resumeOrThrow ($ e );
@@ -123,7 +135,7 @@ public function next()
123
135
public function rewind ()
124
136
{
125
137
try {
126
- $ this ->csIt ->rewind ();
138
+ $ this ->iterator ->rewind ();
127
139
/* Unlike next() and resume(), the decision to increment the key
128
140
* does not depend on whether the change stream has advanced. This
129
141
* ensures that multiple calls to rewind() do not alter state. */
@@ -139,40 +151,7 @@ public function rewind()
139
151
*/
140
152
public function valid ()
141
153
{
142
- return $ this ->csIt ->valid ();
143
- }
144
-
145
- /**
146
- * Extracts the resume token (i.e. "_id" field) from the change document.
147
- *
148
- * @param array|object $document Change document
149
- * @return mixed
150
- * @throws InvalidArgumentException
151
- * @throws ResumeTokenException if the resume token is not found or invalid
152
- */
153
- private function extractResumeToken ($ document )
154
- {
155
- if ( ! is_array ($ document ) && ! is_object ($ document )) {
156
- throw InvalidArgumentException::invalidType ('$document ' , $ document , 'array or object ' );
157
- }
158
-
159
- if ($ document instanceof Serializable) {
160
- return $ this ->extractResumeToken ($ document ->bsonSerialize ());
161
- }
162
-
163
- $ resumeToken = is_array ($ document )
164
- ? (isset ($ document ['_id ' ]) ? $ document ['_id ' ] : null )
165
- : (isset ($ document ->_id ) ? $ document ->_id : null );
166
-
167
- if ( ! isset ($ resumeToken )) {
168
- throw ResumeTokenException::notFound ();
169
- }
170
-
171
- if ( ! is_array ($ resumeToken ) && ! is_object ($ resumeToken )) {
172
- throw ResumeTokenException::invalidType ($ resumeToken );
173
- }
174
-
175
- return $ resumeToken ;
154
+ return $ this ->iterator ->valid ();
176
155
}
177
156
178
157
/**
@@ -196,7 +175,7 @@ private function isResumableError(RuntimeException $exception)
196
175
return false ;
197
176
}
198
177
199
- if (in_array ($ exception ->getCode (), [ self ::$ errorCodeCappedPositionLost , self :: $ errorCodeCursorKilled , self :: $ errorCodeInterrupted ] )) {
178
+ if (in_array ($ exception ->getCode (), self ::$ nonResumableErrorCodes )) {
200
179
return false ;
201
180
}
202
181
@@ -222,13 +201,11 @@ private function onIteration($incrementKey)
222
201
}
223
202
224
203
/* Return early if there is not a current result. Avoid any attempt to
225
- * increment the iterator's key or extract a resume token */
204
+ * increment the iterator's key. */
226
205
if (!$ this ->valid ()) {
227
206
return ;
228
207
}
229
208
230
- $ this ->resumeToken = $ this ->extractResumeToken ($ this ->csIt ->current ());
231
-
232
209
if ($ incrementKey ) {
233
210
$ this ->key ++;
234
211
}
@@ -237,16 +214,14 @@ private function onIteration($incrementKey)
237
214
}
238
215
239
216
/**
240
- * Creates a new changeStream after a resumable server error.
217
+ * Recreates the ChangeStreamIterator after a resumable server error.
241
218
*
242
219
* @return void
243
220
*/
244
221
private function resume ()
245
222
{
246
- list ($ cursor , $ isFirstBatchEmpty ) = call_user_func ($ this ->resumeCallable , $ this ->resumeToken );
247
-
248
- $ this ->csIt = new TailableCursorIterator ($ cursor , $ isFirstBatchEmpty );
249
- $ this ->csIt ->rewind ();
223
+ $ this ->iterator = call_user_func ($ this ->resumeCallable , $ this ->getResumeToken ());
224
+ $ this ->iterator ->rewind ();
250
225
251
226
$ this ->onIteration ($ this ->hasAdvanced );
252
227
}
0 commit comments