11use crate :: convert:: { FromZval , FromZvalMut } ;
22use crate :: ffi:: { zend_object_iterator, ZEND_RESULT_CODE_SUCCESS } ;
33use crate :: flags:: DataType ;
4- use crate :: prelude:: PhpResult ;
54use crate :: types:: Zval ;
65use crate :: zend:: ExecutorGlobals ;
76use std:: fmt:: { Debug , Display , Formatter } ;
@@ -18,55 +17,70 @@ impl ZendIterator {
1817 /// # Returns
1918 ///
2019 /// Returns a iterator over the zend_object_iterator.
21- pub fn iter ( & mut self ) -> PhpResult < Iter > {
20+ pub fn iter ( & mut self ) -> Option < Iter > {
2221 self . index = 0 ;
23- self . rewind ( ) ?;
2422
25- Ok ( Iter { zi : self } )
23+ if self . rewind ( ) {
24+ return Some ( Iter { zi : self } ) ;
25+ }
26+
27+ None
2628 }
2729
2830 /// Check if the current position of the iterator is valid.
2931 ///
3032 /// As an example this will call the user defined valid method of the ['\Iterator'] interface.
3133 /// 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 {
3335 if let Some ( valid) = unsafe { ( * self . funcs ) . valid } {
3436 let valid = unsafe { valid ( & mut * self ) == ZEND_RESULT_CODE_SUCCESS } ;
3537
36- ExecutorGlobals :: throw_if_exception ( ) ?;
38+ if ExecutorGlobals :: has_exception ( ) {
39+ return false ;
40+ }
3741
38- Ok ( valid)
42+ valid
3943 } else {
40- Ok ( true )
44+ true
4145 }
4246 }
4347
4448 /// Rewind the iterator to the first element.
4549 ///
4650 /// As an example this will call the user defined rewind method of the ['\Iterator'] interface.
4751 /// 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 {
4958 if let Some ( rewind) = unsafe { ( * self . funcs ) . rewind } {
5059 unsafe {
5160 rewind ( & mut * self ) ;
5261 }
5362 }
5463
55- ExecutorGlobals :: throw_if_exception ( )
64+ ! ExecutorGlobals :: has_exception ( )
5665 }
5766
5867 /// Move the iterator forward to the next element.
5968 ///
6069 /// As an example this will call the user defined next method of the ['\Iterator'] interface.
6170 /// 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 {
6377 if let Some ( move_forward) = unsafe { ( * self . funcs ) . move_forward } {
6478 unsafe {
6579 move_forward ( & mut * self ) ;
6680 }
6781 }
6882
69- ExecutorGlobals :: throw_if_exception ( )
83+ ! ExecutorGlobals :: has_exception ( )
7084 }
7185
7286 /// Get the current data of the iterator.
@@ -75,16 +89,15 @@ impl ZendIterator {
7589 ///
7690 /// Returns a reference to the current data of the iterator if available
7791 /// , ['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 } ?;
8394 let value = unsafe { & * get_current_data ( & mut * self ) } ;
8495
85- ExecutorGlobals :: throw_if_exception ( ) ?;
96+ if ExecutorGlobals :: has_exception ( ) {
97+ return None ;
98+ }
8699
87- Ok ( Some ( value) )
100+ Some ( value)
88101 }
89102
90103 /// Get the current key of the iterator.
@@ -93,21 +106,19 @@ impl ZendIterator {
93106 ///
94107 /// Returns a new ['Zval'] containing the current key of the iterator if available
95108 /// , ['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 ? } ;
102111 let mut key = Zval :: new ( ) ;
103112
104113 unsafe {
105114 get_current_key ( & mut * self , & mut key) ;
106115 }
107116
108- ExecutorGlobals :: throw_if_exception ( ) ?;
117+ if ExecutorGlobals :: has_exception ( ) {
118+ return None ;
119+ }
109120
110- Ok ( Some ( key) )
121+ Some ( key)
111122 }
112123}
113124
@@ -164,40 +175,31 @@ pub struct Iter<'a> {
164175}
165176
166177impl < ' a > Iterator for Iter < ' a > {
167- type Item = PhpResult < ( IterKey , & ' a Zval ) > ;
178+ type Item = ( IterKey , & ' a Zval ) ;
168179
169180 fn next ( & mut self ) -> Option < Self :: Item > {
170181 // 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 ;
175184 }
176185
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 ;
181188 }
182189
183190 self . zi . index += 1 ;
184191
185192 let real_index = self . zi . index - 1 ;
186193
187194 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) {
191197 Some ( key) => key,
192198 None => IterKey :: Long ( real_index) ,
193199 } ,
194200 } ;
195201
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) )
201203 }
202204}
203205
@@ -208,3 +210,133 @@ impl<'a> FromZvalMut<'a> for &'a mut ZendIterator {
208210 zval. object ( ) ?. get_class_entry ( ) . get_iterator ( zval, false )
209211 }
210212}
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