Skip to content

Commit de67662

Browse files
authored
Add classes implements method and errors tests. (#90)
1 parent c7a8e40 commit de67662

File tree

6 files changed

+153
-7
lines changed

6 files changed

+153
-7
lines changed

phper/src/classes.rs

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,18 @@ use std::{
3636
rc::Rc,
3737
};
3838

39+
/// Predefined interface `Iterator`.
40+
#[inline]
41+
pub fn iterator_class<'a>() -> &'a ClassEntry {
42+
unsafe { ClassEntry::from_ptr(zend_ce_iterator) }
43+
}
44+
45+
/// Predefined interface `ArrayAccess`.
46+
#[inline]
47+
pub fn array_access_class<'a>() -> &'a ClassEntry {
48+
unsafe { ClassEntry::from_ptr(zend_ce_arrayaccess) }
49+
}
50+
3951
/// Wrapper of [crate::sys::zend_class_entry].
4052
#[repr(transparent)]
4153
pub struct ClassEntry {
@@ -128,9 +140,8 @@ impl ClassEntry {
128140
}
129141
}
130142

131-
#[allow(clippy::useless_conversion)]
132-
pub fn instance_of(&self, parent: &ClassEntry) -> bool {
133-
unsafe { phper_instanceof_function(self.as_ptr(), parent.as_ptr()) != false.into() }
143+
pub fn is_instance_of(&self, parent: &ClassEntry) -> bool {
144+
unsafe { phper_instanceof_function(self.as_ptr(), parent.as_ptr()) }
134145
}
135146
}
136147

@@ -164,6 +175,7 @@ pub struct ClassEntity<T> {
164175
method_entities: Vec<MethodEntity>,
165176
property_entities: Vec<PropertyEntity>,
166177
parent: Option<Box<dyn Fn() -> &'static ClassEntry>>,
178+
interfaces: Vec<Box<dyn Fn() -> &'static ClassEntry>>,
167179
_p: PhantomData<(*mut (), T)>,
168180
}
169181

@@ -189,6 +201,7 @@ impl<T: 'static> ClassEntity<T> {
189201
method_entities: Vec::new(),
190202
property_entities: Vec::new(),
191203
parent: None,
204+
interfaces: Vec::new(),
192205
_p: PhantomData,
193206
}
194207
}
@@ -234,6 +247,10 @@ impl<T: 'static> ClassEntity<T> {
234247
self.parent = Some(Box::new(parent));
235248
}
236249

250+
pub fn implements(&mut self, interface: impl Fn() -> &'static ClassEntry + 'static) {
251+
self.interfaces.push(Box::new(interface));
252+
}
253+
237254
#[allow(clippy::useless_conversion)]
238255
pub(crate) unsafe fn init(&mut self) -> *mut zend_class_entry {
239256
let parent: *mut zend_class_entry = self
@@ -251,6 +268,11 @@ impl<T: 'static> ClassEntity<T> {
251268
parent.cast(),
252269
);
253270

271+
for interface in &self.interfaces {
272+
let interface_ce = interface().as_ptr();
273+
zend_class_implements(class_ce, 1, interface_ce);
274+
}
275+
254276
*phper_get_create_object(class_ce) = Some(create_object);
255277

256278
class_ce

phper/src/errors.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -184,8 +184,8 @@ pub enum Error {
184184
}
185185

186186
impl Error {
187-
pub fn boxed(e: impl error::Error + 'static) -> Self {
188-
Self::Boxed(Box::new(e))
187+
pub fn boxed(e: impl Into<Box<dyn error::Error>> + 'static) -> Self {
188+
Self::Boxed(e.into())
189189
}
190190

191191
pub fn throw(t: impl Throwable) -> Self {
@@ -283,7 +283,7 @@ pub struct ThrowObject(ZObject);
283283

284284
impl ThrowObject {
285285
pub fn new(obj: ZObject) -> result::Result<Self, NotImplementThrowableError> {
286-
if !obj.get_class().instance_of(throwable_class()) {
286+
if !obj.get_class().is_instance_of(throwable_class()) {
287287
return Err(NotImplementThrowableError);
288288
}
289289
Ok(Self(obj))

tests/integration/src/classes.rs

Lines changed: 80 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,17 @@
99
// See the Mulan PSL v2 for more details.
1010

1111
use phper::{
12-
classes::{ClassEntity, Visibility},
12+
alloc::RefClone,
13+
classes::{array_access_class, iterator_class, ClassEntity, Visibility},
1314
functions::Argument,
1415
modules::Module,
1516
values::ZVal,
1617
};
18+
use std::collections::HashMap;
1719

1820
pub fn integrate(module: &mut Module) {
1921
integrate_a(module);
22+
integrate_foo(module);
2023
}
2124

2225
fn integrate_a(module: &mut Module) {
@@ -48,3 +51,79 @@ fn integrate_a(module: &mut Module) {
4851

4952
module.add_class(class);
5053
}
54+
55+
struct Foo {
56+
position: usize,
57+
array: HashMap<i64, ZVal>,
58+
}
59+
60+
fn integrate_foo(module: &mut Module) {
61+
let mut class = ClassEntity::new_with_state_constructor("IntegrationTest\\Foo", || Foo {
62+
position: 0,
63+
array: Default::default(),
64+
});
65+
66+
class.implements(iterator_class);
67+
class.implements(array_access_class);
68+
69+
// Implement Iterator interface.
70+
class.add_method("current", Visibility::Public, |this, _arguments| {
71+
let state = this.as_state();
72+
Ok::<_, phper::Error>(format!("Current: {}", state.position))
73+
});
74+
class.add_method("key", Visibility::Public, |this, _arguments| {
75+
let state = this.as_state();
76+
Ok::<_, phper::Error>(state.position as i64)
77+
});
78+
class.add_method("next", Visibility::Public, |this, _arguments| {
79+
let state = this.as_mut_state();
80+
state.position += 1;
81+
});
82+
class.add_method("rewind", Visibility::Public, |this, _arguments| {
83+
let state = this.as_mut_state();
84+
state.position = 0;
85+
});
86+
class.add_method("valid", Visibility::Public, |this, _arguments| {
87+
let state = this.as_state();
88+
state.position < 3
89+
});
90+
91+
// Implement ArrayAccess interface.
92+
class
93+
.add_method("offsetExists", Visibility::Public, |this, arguments| {
94+
let offset = arguments[0].expect_long()?;
95+
let state = this.as_state();
96+
Ok::<_, phper::Error>(state.array.get(&offset).is_some())
97+
})
98+
.argument(Argument::by_val("offset"));
99+
100+
class
101+
.add_method("offsetGet", Visibility::Public, |this, arguments| {
102+
let offset = arguments[0].expect_long()?;
103+
let state = this.as_mut_state();
104+
let val = state.array.get_mut(&offset).map(|val| val.ref_clone());
105+
Ok::<_, phper::Error>(val)
106+
})
107+
.argument(Argument::by_val("offset"));
108+
109+
class
110+
.add_method("offsetSet", Visibility::Public, |this, arguments| {
111+
let offset = arguments[0].expect_long()?;
112+
let value = arguments[1].clone();
113+
let state = this.as_mut_state();
114+
state.array.insert(offset, value);
115+
Ok::<_, phper::Error>(())
116+
})
117+
.arguments([Argument::by_val("offset"), Argument::by_val("value")]);
118+
119+
class
120+
.add_method("offsetUnset", Visibility::Public, |this, arguments| {
121+
let offset = arguments[0].expect_long()?;
122+
let state = this.as_mut_state();
123+
state.array.remove(&offset);
124+
Ok::<_, phper::Error>(())
125+
})
126+
.argument(Argument::by_val("offset"));
127+
128+
module.add_class(class);
129+
}

tests/integration/src/errors.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Copyright (c) 2022 PHPER Framework Team
2+
// PHPER is licensed under Mulan PSL v2.
3+
// You can use this software according to the terms and conditions of the Mulan
4+
// PSL v2. You may obtain a copy of Mulan PSL v2 at:
5+
// http://license.coscl.org.cn/MulanPSL2
6+
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY
7+
// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
8+
// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
9+
// See the Mulan PSL v2 for more details.
10+
11+
use phper::modules::Module;
12+
use std::io;
13+
14+
pub fn integrate(_module: &mut Module) {
15+
{
16+
let e = phper::Error::boxed("something wrong");
17+
assert!(matches!(e, phper::Error::Boxed(..)));
18+
assert_eq!(e.to_string(), "something wrong");
19+
}
20+
21+
{
22+
let e = phper::Error::boxed(io::Error::new(io::ErrorKind::Other, "oh no!"));
23+
assert!(matches!(e, phper::Error::Boxed(..)));
24+
assert_eq!(e.to_string(), "oh no!");
25+
}
26+
}

tests/integration/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ mod arguments;
1414
mod arrays;
1515
mod classes;
1616
mod constants;
17+
mod errors;
1718
mod functions;
1819
mod ini;
1920
mod objects;
@@ -39,6 +40,7 @@ pub fn get_module() -> Module {
3940
values::integrate(&mut module);
4041
constants::integrate(&mut module);
4142
ini::integrate(&mut module);
43+
errors::integrate(&mut module);
4244

4345
module
4446
}

tests/integration/tests/php/classes.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,20 @@
2828

2929
$property_name = $reflection_class->getProperty("name");
3030
assert_true($property_name->isPrivate());
31+
32+
33+
$foo = new \IntegrationTest\Foo();
34+
35+
// Test implementation of Iterator interface.
36+
$tmp_arr = [];
37+
foreach ($foo as $key => $value) {
38+
$tmp_arr[] = [$key, $value];
39+
}
40+
assert_eq($tmp_arr, [[0, 'Current: 0'], [1, 'Current: 1'], [2, 'Current: 2']]);
41+
42+
// Test implementation of ArrayAccess interface.
43+
assert_eq($foo[10], null);
44+
$foo[10] = "10";
45+
assert_eq($foo[10], "10");
46+
unset($foo[10]);
47+
assert_eq($foo[10], null);

0 commit comments

Comments
 (0)