Skip to content

Commit 0bf6424

Browse files
committed
feat(iterator): add helper for zend_object_iterator
1 parent 1a8211c commit 0bf6424

File tree

3 files changed

+118
-0
lines changed

3 files changed

+118
-0
lines changed

src/types/iterator.rs

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
use crate::convert::FromZvalMut;
2+
use crate::ffi::zend_object_iterator;
3+
use crate::flags::DataType;
4+
use crate::types::{ZendLong, Zval};
5+
use std::convert::TryInto;
6+
7+
pub type ZendIterator = zend_object_iterator;
8+
9+
pub struct Iter<'a> {
10+
zi: &'a mut ZendIterator,
11+
}
12+
13+
impl ZendIterator {
14+
pub fn iter(&mut self) -> Iter {
15+
self.index = 0;
16+
self.rewind();
17+
18+
Iter { zi: self }
19+
}
20+
21+
pub fn valid(&mut self) -> bool {
22+
if let Some(valid) = unsafe { (*self.funcs).valid } {
23+
unsafe { valid(&mut *self) != 0 }
24+
} else {
25+
true
26+
}
27+
}
28+
29+
pub fn rewind(&mut self) {
30+
if let Some(rewind) = unsafe { (*self.funcs).rewind } {
31+
unsafe {
32+
rewind(&mut *self);
33+
}
34+
}
35+
}
36+
37+
pub fn move_forward(&mut self) {
38+
if let Some(move_forward) = unsafe { (*self.funcs).move_forward } {
39+
unsafe {
40+
move_forward(&mut *self);
41+
}
42+
}
43+
}
44+
45+
pub fn get_current_data<'a>(&mut self) -> Option<&'a Zval> {
46+
let get_current_data = unsafe { (*self.funcs).get_current_data }?;
47+
let value = unsafe { &*get_current_data(&mut *self) };
48+
49+
Some(value)
50+
}
51+
52+
pub fn get_current_key(&mut self) -> Option<Zval> {
53+
let get_current_key = unsafe { (*self.funcs).get_current_key }?;
54+
let mut key = Zval::new();
55+
unsafe {
56+
get_current_key(&mut *self, &mut key);
57+
}
58+
59+
Some(key)
60+
}
61+
}
62+
63+
impl<'a> Iterator for Iter<'a> {
64+
type Item = (u64, Option<String>, &'a Zval);
65+
66+
fn next(&mut self) -> Option<Self::Item> {
67+
// Call next when index > 0, so next is really called at the start of each iteration, which allow to work better with generator iterator
68+
if self.zi.index > 0 {
69+
self.zi.move_forward();
70+
71+
if !self.zi.valid() {
72+
return None;
73+
}
74+
}
75+
76+
self.zi.index += 1;
77+
78+
let key = self.zi.get_current_key();
79+
let value = self.zi.get_current_data()?;
80+
let real_index = self.zi.index - 1;
81+
82+
Some(match key {
83+
Some(key) => match key.is_long() {
84+
false => (real_index, key.try_into().ok(), value),
85+
true => (
86+
key.long().unwrap_or(real_index as ZendLong) as u64,
87+
None,
88+
value,
89+
),
90+
},
91+
None => (real_index, None, value),
92+
})
93+
}
94+
}
95+
96+
impl<'a> FromZvalMut<'a> for &'a mut ZendIterator {
97+
const TYPE: DataType = DataType::Object(Some("Traversable"));
98+
99+
fn from_zval_mut(zval: &'a mut Zval) -> Option<Self> {
100+
let zend_object = zval.object()?;
101+
let ce = zend_object.get_class_entry_mut();
102+
let iterator = unsafe { ce.get_iterator?(&mut *ce, &mut *zval, 0) };
103+
104+
unsafe { iterator.as_mut() }
105+
}
106+
}

src/types/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
mod array;
77
mod callable;
88
mod class_object;
9+
mod iterator;
910
mod long;
1011
mod object;
1112
mod string;

src/types/object.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,17 @@ impl ZendObject {
103103
unsafe { self.ce.as_ref() }.expect("Could not retrieve class entry.")
104104
}
105105

106+
/// Returns the [`ClassEntry`] associated with this object.
107+
///
108+
/// # Panics
109+
///
110+
/// Panics if the class entry is invalid.
111+
pub fn get_class_entry_mut(&self) -> &'static mut ClassEntry {
112+
// SAFETY: it is OK to panic here since PHP would segfault anyway
113+
// when encountering an object with no class entry.
114+
unsafe { self.ce.as_mut() }.expect("Could not retrieve class entry.")
115+
}
116+
106117
/// Attempts to retrieve the class name of the object.
107118
pub fn get_class_name(&self) -> Result<String> {
108119
unsafe {

0 commit comments

Comments
 (0)