Skip to content

Commit a8f15d8

Browse files
committed
feat(iterator): handle exception
1 parent 57577b6 commit a8f15d8

File tree

3 files changed

+87
-33
lines changed

3 files changed

+87
-33
lines changed

src/types/iterable.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use super::array::Iter as ZendHashTableIter;
22
use super::iterator::Iter as ZendIteratorIter;
33
use crate::convert::FromZval;
4+
use crate::exception::PhpResult;
45
use crate::flags::DataType;
56
use crate::types::iterator::IterKey;
67
use crate::types::{ZendHashTable, ZendIterator, Zval};
@@ -15,10 +16,10 @@ pub enum Iterable<'a> {
1516

1617
impl<'a> Iterable<'a> {
1718
/// Creates a new rust iterator from a PHP iterable.
18-
pub fn iter(&mut self) -> Iter {
19+
pub fn iter(&mut self) -> PhpResult<Iter> {
1920
match self {
20-
Iterable::Array(array) => Iter::Array(array.iter()),
21-
Iterable::Traversable(traversable) => Iter::Traversable(traversable.iter()),
21+
Iterable::Array(array) => Ok(Iter::Array(array.iter())),
22+
Iterable::Traversable(traversable) => Ok(Iter::Traversable(traversable.iter()?)),
2223
}
2324
}
2425
}
@@ -46,11 +47,11 @@ pub enum Iter<'a> {
4647
}
4748

4849
impl<'a> Iterator for Iter<'a> {
49-
type Item = (IterKey, &'a Zval);
50+
type Item = PhpResult<(IterKey, &'a Zval)>;
5051

5152
fn next(&mut self) -> Option<Self::Item> {
5253
match self {
53-
Iter::Array(array) => array.next(),
54+
Iter::Array(array) => array.next().map(Ok),
5455
Iter::Traversable(traversable) => traversable.next(),
5556
}
5657
}

src/types/iterator.rs

Lines changed: 59 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
use crate::convert::{FromZval, FromZvalMut};
2-
use crate::ffi::zend_object_iterator;
2+
use crate::ffi::{zend_object_iterator, ZEND_RESULT_CODE_SUCCESS};
33
use crate::flags::DataType;
4+
use crate::prelude::PhpResult;
45
use crate::types::Zval;
6+
use crate::zend::ExecutorGlobals;
57
use std::fmt::{Debug, Display, Formatter};
68

79
/// A PHP Iterator.
@@ -16,47 +18,55 @@ impl ZendIterator {
1618
/// # Returns
1719
///
1820
/// Returns a iterator over the zend_object_iterator.
19-
pub fn iter(&mut self) -> Iter {
21+
pub fn iter(&mut self) -> PhpResult<Iter> {
2022
self.index = 0;
21-
self.rewind();
23+
self.rewind()?;
2224

23-
Iter { zi: self }
25+
Ok(Iter { zi: self })
2426
}
2527

2628
/// Check if the current position of the iterator is valid.
2729
///
2830
/// As an example this will call the user defined valid method of the ['\Iterator'] interface.
2931
/// see <https://www.php.net/manual/en/iterator.valid.php>
30-
pub fn valid(&mut self) -> bool {
32+
pub fn valid(&mut self) -> PhpResult<bool> {
3133
if let Some(valid) = unsafe { (*self.funcs).valid } {
32-
unsafe { valid(&mut *self) != 0 }
34+
let valid = unsafe { valid(&mut *self) == ZEND_RESULT_CODE_SUCCESS };
35+
36+
ExecutorGlobals::throw_if_exception()?;
37+
38+
Ok(valid)
3339
} else {
34-
true
40+
Ok(true)
3541
}
3642
}
3743

3844
/// Rewind the iterator to the first element.
3945
///
4046
/// As an example this will call the user defined rewind method of the ['\Iterator'] interface.
4147
/// see <https://www.php.net/manual/en/iterator.rewind.php>
42-
pub fn rewind(&mut self) {
48+
pub fn rewind(&mut self) -> PhpResult<()> {
4349
if let Some(rewind) = unsafe { (*self.funcs).rewind } {
4450
unsafe {
4551
rewind(&mut *self);
4652
}
4753
}
54+
55+
ExecutorGlobals::throw_if_exception()
4856
}
4957

5058
/// Move the iterator forward to the next element.
5159
///
5260
/// As an example this will call the user defined next method of the ['\Iterator'] interface.
5361
/// see <https://www.php.net/manual/en/iterator.next.php>
54-
pub fn move_forward(&mut self) {
62+
pub fn move_forward(&mut self) -> PhpResult<()> {
5563
if let Some(move_forward) = unsafe { (*self.funcs).move_forward } {
5664
unsafe {
5765
move_forward(&mut *self);
5866
}
5967
}
68+
69+
ExecutorGlobals::throw_if_exception()
6070
}
6171

6272
/// Get the current data of the iterator.
@@ -65,11 +75,16 @@ impl ZendIterator {
6575
///
6676
/// Returns a reference to the current data of the iterator if available
6777
/// , ['None'] otherwise.
68-
pub fn get_current_data<'a>(&mut self) -> Option<&'a Zval> {
69-
let get_current_data = unsafe { (*self.funcs).get_current_data }?;
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+
};
7083
let value = unsafe { &*get_current_data(&mut *self) };
7184

72-
Some(value)
85+
ExecutorGlobals::throw_if_exception()?;
86+
87+
Ok(Some(value))
7388
}
7489

7590
/// Get the current key of the iterator.
@@ -78,14 +93,21 @@ impl ZendIterator {
7893
///
7994
/// Returns a new ['Zval'] containing the current key of the iterator if available
8095
/// , ['None'] otherwise.
81-
pub fn get_current_key(&mut self) -> Option<Zval> {
82-
let get_current_key = unsafe { (*self.funcs).get_current_key }?;
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+
83102
let mut key = Zval::new();
103+
84104
unsafe {
85105
get_current_key(&mut *self, &mut key);
86106
}
87107

88-
Some(key)
108+
ExecutorGlobals::throw_if_exception()?;
109+
110+
Ok(Some(key))
89111
}
90112
}
91113

@@ -142,31 +164,40 @@ pub struct Iter<'a> {
142164
}
143165

144166
impl<'a> Iterator for Iter<'a> {
145-
type Item = (IterKey, &'a Zval);
167+
type Item = PhpResult<(IterKey, &'a Zval)>;
146168

147169
fn next(&mut self) -> Option<Self::Item> {
148170
// Call next when index > 0, so next is really called at the start of each iteration, which allow to work better with generator iterator
149171
if self.zi.index > 0 {
150-
self.zi.move_forward();
151-
152-
if !self.zi.valid() {
153-
return None;
172+
if let Err(err) = self.zi.move_forward() {
173+
return Some(Err(err));
154174
}
155175
}
156176

177+
match self.zi.valid() {
178+
Err(err) => return Some(Err(err)),
179+
Ok(false) => return None,
180+
Ok(true) => (),
181+
}
182+
157183
self.zi.index += 1;
158184

159-
let key = self.zi.get_current_key();
160-
let value = self.zi.get_current_data()?;
161185
let real_index = self.zi.index - 1;
162186

163-
Some(match key {
164-
Some(key) => match IterKey::from_zval(&key) {
165-
Some(key) => (key, value),
166-
None => (IterKey::Long(real_index), value),
187+
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) {
191+
Some(key) => key,
192+
None => IterKey::Long(real_index),
167193
},
168-
None => (IterKey::Long(real_index), value),
169-
})
194+
};
195+
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+
}
170201
}
171202
}
172203

src/zend/globals.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use std::ops::{Deref, DerefMut};
66
use parking_lot::{const_rwlock, RwLock, RwLockReadGuard, RwLockWriteGuard};
77

88
use crate::boxed::ZBox;
9+
use crate::exception::PhpResult;
910
#[cfg(php82)]
1011
use crate::ffi::zend_atomic_bool_store;
1112
use crate::ffi::{_zend_executor_globals, ext_php_rs_executor_globals, zend_ini_entry};
@@ -87,6 +88,13 @@ impl ExecutorGlobals {
8788
/// could lead to a deadlock if the globals are already borrowed immutably
8889
/// or mutably.
8990
pub fn take_exception() -> Option<ZBox<ZendObject>> {
91+
{
92+
// This avoid a write lock if there is no exception.
93+
if Self::get().exception.is_null() {
94+
return None;
95+
}
96+
}
97+
9098
let mut globals = Self::get_mut();
9199

92100
let mut exception_ptr = std::ptr::null_mut();
@@ -96,6 +104,20 @@ impl ExecutorGlobals {
96104
Some(unsafe { ZBox::from_raw(exception_ptr.as_mut()?) })
97105
}
98106

107+
/// Attempts to extract the last PHP exception captured by the interpreter.
108+
/// Returned inside a [`PhpResult`].
109+
///
110+
/// This function requires the executor globals to be mutably held, which
111+
/// could lead to a deadlock if the globals are already borrowed immutably
112+
/// or mutably.
113+
pub fn throw_if_exception() -> PhpResult<()> {
114+
if let Some(e) = Self::take_exception() {
115+
Err(crate::error::Error::Exception(e).into())
116+
} else {
117+
Ok(())
118+
}
119+
}
120+
99121
/// Request an interrupt of the PHP VM. This will call the registered
100122
/// interrupt handler function.
101123
/// set with [`crate::ffi::zend_interrupt_function`].

0 commit comments

Comments
 (0)