1
1
use crate :: convert:: { FromZval , FromZvalMut } ;
2
2
use crate :: ffi:: { zend_object_iterator, ZEND_RESULT_CODE_SUCCESS } ;
3
3
use crate :: flags:: DataType ;
4
- use crate :: prelude:: PhpResult ;
5
4
use crate :: types:: Zval ;
6
5
use crate :: zend:: ExecutorGlobals ;
7
6
use std:: fmt:: { Debug , Display , Formatter } ;
@@ -18,55 +17,70 @@ impl ZendIterator {
18
17
/// # Returns
19
18
///
20
19
/// Returns a iterator over the zend_object_iterator.
21
- pub fn iter ( & mut self ) -> PhpResult < Iter > {
20
+ pub fn iter ( & mut self ) -> Option < Iter > {
22
21
self . index = 0 ;
23
- self . rewind ( ) ?;
24
22
25
- Ok ( Iter { zi : self } )
23
+ if self . rewind ( ) {
24
+ return Some ( Iter { zi : self } ) ;
25
+ }
26
+
27
+ None
26
28
}
27
29
28
30
/// Check if the current position of the iterator is valid.
29
31
///
30
32
/// As an example this will call the user defined valid method of the ['\Iterator'] interface.
31
33
/// see <https://www.php.net/manual/en/iterator.valid.php>
32
- pub fn valid ( & mut self ) -> PhpResult < bool > {
34
+ pub fn valid ( & mut self ) -> bool {
33
35
if let Some ( valid) = unsafe { ( * self . funcs ) . valid } {
34
36
let valid = unsafe { valid ( & mut * self ) == ZEND_RESULT_CODE_SUCCESS } ;
35
37
36
- ExecutorGlobals :: throw_if_exception ( ) ?;
38
+ if ExecutorGlobals :: has_exception ( ) {
39
+ return false ;
40
+ }
37
41
38
- Ok ( valid)
42
+ valid
39
43
} else {
40
- Ok ( true )
44
+ true
41
45
}
42
46
}
43
47
44
48
/// Rewind the iterator to the first element.
45
49
///
46
50
/// As an example this will call the user defined rewind method of the ['\Iterator'] interface.
47
51
/// see <https://www.php.net/manual/en/iterator.rewind.php>
48
- pub fn rewind ( & mut self ) -> PhpResult < ( ) > {
52
+ ///
53
+ /// # Returns
54
+ ///
55
+ /// Returns true if the iterator was successfully rewind, false otherwise. (when there is
56
+ /// an exception during rewind)
57
+ pub fn rewind ( & mut self ) -> bool {
49
58
if let Some ( rewind) = unsafe { ( * self . funcs ) . rewind } {
50
59
unsafe {
51
60
rewind ( & mut * self ) ;
52
61
}
53
62
}
54
63
55
- ExecutorGlobals :: throw_if_exception ( )
64
+ ! ExecutorGlobals :: has_exception ( )
56
65
}
57
66
58
67
/// Move the iterator forward to the next element.
59
68
///
60
69
/// As an example this will call the user defined next method of the ['\Iterator'] interface.
61
70
/// see <https://www.php.net/manual/en/iterator.next.php>
62
- pub fn move_forward ( & mut self ) -> PhpResult < ( ) > {
71
+ ///
72
+ /// # Returns
73
+ ///
74
+ /// Returns true if the iterator was successfully move, false otherwise. (when there is
75
+ /// an exception during next)
76
+ pub fn move_forward ( & mut self ) -> bool {
63
77
if let Some ( move_forward) = unsafe { ( * self . funcs ) . move_forward } {
64
78
unsafe {
65
79
move_forward ( & mut * self ) ;
66
80
}
67
81
}
68
82
69
- ExecutorGlobals :: throw_if_exception ( )
83
+ ! ExecutorGlobals :: has_exception ( )
70
84
}
71
85
72
86
/// Get the current data of the iterator.
@@ -75,16 +89,15 @@ impl ZendIterator {
75
89
///
76
90
/// Returns a reference to the current data of the iterator if available
77
91
/// , ['None'] otherwise.
78
- pub fn get_current_data < ' a > ( & mut self ) -> PhpResult < Option < & ' a Zval > > {
79
- let get_current_data = match unsafe { ( * self . funcs ) . get_current_data } {
80
- Some ( get_current_data) => get_current_data,
81
- None => return Ok ( None ) ,
82
- } ;
92
+ pub fn get_current_data < ' a > ( & mut self ) -> Option < & ' a Zval > {
93
+ let get_current_data = unsafe { ( * self . funcs ) . get_current_data } ?;
83
94
let value = unsafe { & * get_current_data ( & mut * self ) } ;
84
95
85
- ExecutorGlobals :: throw_if_exception ( ) ?;
96
+ if ExecutorGlobals :: has_exception ( ) {
97
+ return None ;
98
+ }
86
99
87
- Ok ( Some ( value) )
100
+ Some ( value)
88
101
}
89
102
90
103
/// Get the current key of the iterator.
@@ -93,21 +106,19 @@ impl ZendIterator {
93
106
///
94
107
/// Returns a new ['Zval'] containing the current key of the iterator if available
95
108
/// , ['None'] otherwise.
96
- pub fn get_current_key ( & mut self ) -> PhpResult < Option < Zval > > {
97
- let get_current_key = match unsafe { ( * self . funcs ) . get_current_key } {
98
- Some ( get_current_key) => get_current_key,
99
- None => return Ok ( None ) ,
100
- } ;
101
-
109
+ pub fn get_current_key ( & mut self ) -> Option < Zval > {
110
+ let get_current_key = unsafe { ( * self . funcs ) . get_current_key ? } ;
102
111
let mut key = Zval :: new ( ) ;
103
112
104
113
unsafe {
105
114
get_current_key ( & mut * self , & mut key) ;
106
115
}
107
116
108
- ExecutorGlobals :: throw_if_exception ( ) ?;
117
+ if ExecutorGlobals :: has_exception ( ) {
118
+ return None ;
119
+ }
109
120
110
- Ok ( Some ( key) )
121
+ Some ( key)
111
122
}
112
123
}
113
124
@@ -164,40 +175,31 @@ pub struct Iter<'a> {
164
175
}
165
176
166
177
impl < ' a > Iterator for Iter < ' a > {
167
- type Item = PhpResult < ( IterKey , & ' a Zval ) > ;
178
+ type Item = ( IterKey , & ' a Zval ) ;
168
179
169
180
fn next ( & mut self ) -> Option < Self :: Item > {
170
181
// Call next when index > 0, so next is really called at the start of each iteration, which allow to work better with generator iterator
171
- if self . zi . index > 0 {
172
- if let Err ( err) = self . zi . move_forward ( ) {
173
- return Some ( Err ( err) ) ;
174
- }
182
+ if self . zi . index > 0 && !self . zi . move_forward ( ) {
183
+ return None ;
175
184
}
176
185
177
- match self . zi . valid ( ) {
178
- Err ( err) => return Some ( Err ( err) ) ,
179
- Ok ( false ) => return None ,
180
- Ok ( true ) => ( ) ,
186
+ if !self . zi . valid ( ) {
187
+ return None ;
181
188
}
182
189
183
190
self . zi . index += 1 ;
184
191
185
192
let real_index = self . zi . index - 1 ;
186
193
187
194
let key = match self . zi . get_current_key ( ) {
188
- Err ( err) => return Some ( Err ( err) ) ,
189
- Ok ( None ) => IterKey :: Long ( real_index) ,
190
- Ok ( Some ( key) ) => match IterKey :: from_zval ( & key) {
195
+ None => IterKey :: Long ( real_index) ,
196
+ Some ( key) => match IterKey :: from_zval ( & key) {
191
197
Some ( key) => key,
192
198
None => IterKey :: Long ( real_index) ,
193
199
} ,
194
200
} ;
195
201
196
- match self . zi . get_current_data ( ) {
197
- Err ( err) => Some ( Err ( err) ) ,
198
- Ok ( None ) => None ,
199
- Ok ( Some ( value) ) => Some ( Ok ( ( key, value) ) ) ,
200
- }
202
+ self . zi . get_current_data ( ) . map ( |value| ( key, value) )
201
203
}
202
204
}
203
205
@@ -208,3 +210,133 @@ impl<'a> FromZvalMut<'a> for &'a mut ZendIterator {
208
210
zval. object ( ) ?. get_class_entry ( ) . get_iterator ( zval, false )
209
211
}
210
212
}
213
+
214
+ #[ cfg( test) ]
215
+ #[ cfg( feature = "embed" ) ]
216
+ mod tests {
217
+ use crate :: embed:: Embed ;
218
+ use crate :: types:: iterator:: IterKey ;
219
+
220
+ #[ test]
221
+ fn test_generator ( ) {
222
+ Embed :: run ( || {
223
+ let result = Embed :: run_script ( "src/types/iterator.test.php" ) ;
224
+
225
+ assert ! ( result. is_ok( ) ) ;
226
+
227
+ let generator = Embed :: eval ( "$generator;" ) ;
228
+
229
+ assert ! ( generator. is_ok( ) ) ;
230
+
231
+ let zval = generator. unwrap ( ) ;
232
+
233
+ assert ! ( zval. is_traversable( ) ) ;
234
+
235
+ let iterator = zval. traversable ( ) . unwrap ( ) ;
236
+
237
+ assert ! ( iterator. valid( ) ) ;
238
+
239
+ {
240
+ let mut iter = iterator. iter ( ) . unwrap ( ) ;
241
+
242
+ let ( key, value) = iter. next ( ) . unwrap ( ) ;
243
+
244
+ assert_eq ! ( key, IterKey :: Long ( 0 ) ) ;
245
+ assert ! ( value. is_long( ) ) ;
246
+ assert_eq ! ( value. long( ) . unwrap( ) , 1 ) ;
247
+
248
+ let ( key, value) = iter. next ( ) . unwrap ( ) ;
249
+
250
+ assert_eq ! ( key, IterKey :: Long ( 1 ) ) ;
251
+ assert ! ( value. is_long( ) ) ;
252
+ assert_eq ! ( value. long( ) . unwrap( ) , 2 ) ;
253
+
254
+ let ( key, value) = iter. next ( ) . unwrap ( ) ;
255
+
256
+ assert_eq ! ( key, IterKey :: Long ( 2 ) ) ;
257
+ assert ! ( value. is_long( ) ) ;
258
+ assert_eq ! ( value. long( ) . unwrap( ) , 3 ) ;
259
+
260
+ let next = iter. next ( ) ;
261
+
262
+ assert ! ( next. is_none( ) ) ;
263
+ }
264
+ } ) ;
265
+ }
266
+
267
+ #[ test]
268
+ fn test_iterator ( ) {
269
+ Embed :: run ( || {
270
+ let result = Embed :: run_script ( "src/types/iterator.test.php" ) ;
271
+
272
+ assert ! ( result. is_ok( ) ) ;
273
+
274
+ let generator = Embed :: eval ( "$iterator;" ) ;
275
+
276
+ assert ! ( generator. is_ok( ) ) ;
277
+
278
+ let zval = generator. unwrap ( ) ;
279
+
280
+ assert ! ( zval. is_traversable( ) ) ;
281
+
282
+ let iterator = zval. traversable ( ) . unwrap ( ) ;
283
+
284
+ assert ! ( iterator. valid( ) ) ;
285
+
286
+ {
287
+ let mut iter = iterator. iter ( ) . unwrap ( ) ;
288
+
289
+ let ( key, value) = iter. next ( ) . unwrap ( ) ;
290
+
291
+ assert ! ( !key. is_numerical( ) ) ;
292
+ assert_eq ! ( key, IterKey :: String ( "key" . to_string( ) ) ) ;
293
+ assert ! ( value. is_string( ) ) ;
294
+ assert_eq ! ( value. string( ) . unwrap( ) , "foo" ) ;
295
+
296
+ let ( key, value) = iter. next ( ) . unwrap ( ) ;
297
+
298
+ assert ! ( key. is_numerical( ) ) ;
299
+ assert_eq ! ( key, IterKey :: Long ( 10 ) ) ;
300
+ assert ! ( value. is_string( ) ) ;
301
+ assert_eq ! ( value. string( ) . unwrap( ) , "bar" ) ;
302
+
303
+ let ( key, value) = iter. next ( ) . unwrap ( ) ;
304
+
305
+ assert_eq ! ( key, IterKey :: Long ( 2 ) ) ;
306
+ assert ! ( value. is_string( ) ) ;
307
+ assert_eq ! ( value. string( ) . unwrap( ) , "baz" ) ;
308
+
309
+ let next = iter. next ( ) ;
310
+
311
+ assert ! ( next. is_none( ) ) ;
312
+ }
313
+
314
+ // Test rewind
315
+ {
316
+ let mut iter = iterator. iter ( ) . unwrap ( ) ;
317
+
318
+ let ( key, value) = iter. next ( ) . unwrap ( ) ;
319
+
320
+ assert_eq ! ( key, IterKey :: String ( "key" . to_string( ) ) ) ;
321
+ assert ! ( value. is_string( ) ) ;
322
+ assert_eq ! ( value. string( ) . unwrap( ) , "foo" ) ;
323
+
324
+ let ( key, value) = iter. next ( ) . unwrap ( ) ;
325
+
326
+ assert_eq ! ( key, IterKey :: Long ( 10 ) ) ;
327
+ assert ! ( value. is_string( ) ) ;
328
+ assert_eq ! ( value. string( ) . unwrap( ) , "bar" ) ;
329
+
330
+ let ( key, value) = iter. next ( ) . unwrap ( ) ;
331
+
332
+ assert_eq ! ( key, IterKey :: Long ( 2 ) ) ;
333
+ assert ! ( value. is_string( ) ) ;
334
+ assert_eq ! ( value. string( ) . unwrap( ) , "baz" ) ;
335
+
336
+ let next = iter. next ( ) ;
337
+
338
+ assert ! ( next. is_none( ) ) ;
339
+ }
340
+ } ) ;
341
+ }
342
+ }
0 commit comments