Skip to content

Commit 0fc17e0

Browse files
committed
Add state for Class and Object.
1 parent 47e3f61 commit 0fc17e0

File tree

10 files changed

+113
-49
lines changed

10 files changed

+113
-49
lines changed

examples/http-client/src/client.rs

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
1+
use crate::errors::HttpClientError;
2+
use anyhow::Context;
3+
use phper::{classes::DynamicClass, functions::Argument};
14
use reqwest::blocking::{Client, ClientBuilder};
25
use std::time::Duration;
3-
use phper::classes::DynamicClass;
4-
use anyhow::Context;
5-
use crate::errors::HttpClientError;
6-
use phper::functions::Argument;
76

87
const HTTP_CLIENT_CLASS_NAME: &'static str = "HttpClient\\HttpClient";
98

@@ -15,8 +14,17 @@ pub fn make_client_class() -> DynamicClass<Client> {
1514
Ok::<_, HttpClientError>(client)
1615
});
1716

18-
http_client_class.add_method("get", |this, arguments| {
19-
}, vec![Argument::by_val("url")]);
17+
http_client_class.add_method(
18+
"get",
19+
|this, arguments| {
20+
let url = arguments[0].as_string()?;
21+
let client = this.as_state();
22+
let response = client.get(url).send().unwrap();
23+
let body = response.text().unwrap();
24+
Ok::<_, phper::Error>(body)
25+
},
26+
vec![Argument::by_val("url")],
27+
);
2028

2129
http_client_class
2230
}

examples/http-client/src/errors.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
use phper::errors::Throwable;
2-
use phper::classes::{ClassEntry, DynamicClass};
3-
use phper::errors::Error::ClassNotFound;
1+
use phper::{
2+
classes::{ClassEntry, DynamicClass},
3+
errors::{Error::ClassNotFound, Throwable},
4+
};
45

56
const EXCEPTION_CLASS_NAME: &'static str = "HttpClient\\HttpClientException";
67

@@ -19,4 +20,4 @@ impl Throwable for HttpClientError {
1920
pub fn make_exception_class() -> DynamicClass<()> {
2021
let exception_class = DynamicClass::new(EXCEPTION_CLASS_NAME);
2122
exception_class
22-
}
23+
}

examples/http-client/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1+
use crate::client::make_client_class;
12
use anyhow::Context;
23
use phper::{classes::DynamicClass, modules::Module, php_get_module};
3-
use crate::client::make_client_class;
44

55
pub mod client;
66
pub mod errors;

examples/http-client/src/request.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+

examples/http-client/src/response.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+

examples/http-client/tests/php/test.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@
77
error_reporting(E_ALL);
88

99
$client = new HttpClient();
10-
var_dump($client);
10+
$ip = $client->get("http://httpbin.org/ip");
11+
var_dump($ip);

phper/src/classes.rs

Lines changed: 24 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
//! Apis relate to [crate::sys::zend_class_entry].
22
33
use std::{
4+
any::Any,
5+
collections::HashMap,
46
convert::Infallible,
57
marker::PhantomData,
6-
mem::zeroed,
8+
mem::{size_of, zeroed, ManuallyDrop},
79
os::raw::c_int,
810
ptr::null_mut,
9-
sync::atomic::{AtomicPtr, Ordering},
11+
sync::{
12+
atomic::{AtomicPtr, Ordering},
13+
Arc,
14+
},
1015
};
11-
use std::any::Any;
12-
use std::collections::HashMap;
13-
use std::mem::{ManuallyDrop, size_of};
14-
use std::sync::Arc;
1516

1617
use dashmap::DashMap;
1718
use once_cell::sync::OnceCell;
@@ -21,12 +22,12 @@ use phper_alloc::EBox;
2122
use crate::{
2223
errors::{ClassNotFoundError, Throwable},
2324
functions::{Argument, FunctionEntity, FunctionEntry, Method},
24-
objects::Object,
25+
objects::{ExtendObject, Object},
2526
sys::*,
2627
utils::ensure_end_with_zero,
2728
values::{SetVal, Val},
2829
};
29-
use crate::objects::ExtendObject;
30+
use std::rc::Rc;
3031

3132
pub trait Classifiable {
3233
fn state_constructor(&self) -> Box<StateConstructor<Box<dyn Any>>>;
@@ -36,15 +37,11 @@ pub trait Classifiable {
3637
fn parent(&self) -> Option<&str>;
3738
}
3839

39-
// TODO Let pointer address as a field of Object isn't safe, will choose another plan later.
40-
pub(crate) const DATA_CONSTRUCTOR_PROPERTY_NAME: &'static str = "__very_important_do_not_modify_data_constructor__";
41-
pub(crate) const DATA_PROPERTY_NAME: &'static str = "__very_important_do_not_modify_data__";
42-
4340
pub type StateConstructor<T> = dyn Fn() -> Result<T, Box<dyn Throwable>> + Send + Sync;
4441

4542
pub struct DynamicClass<T: Send + Sync + 'static> {
4643
class_name: String,
47-
// data_constructor: Box<DataConstructor<T>>,
44+
state_constructor: Arc<StateConstructor<T>>,
4845
pub(crate) method_entities: Vec<FunctionEntity>,
4946
pub(crate) property_entities: Vec<PropertyEntity>,
5047
pub(crate) parent: Option<String>,
@@ -64,14 +61,14 @@ impl<T: Default + Send + Sync + 'static> DynamicClass<T> {
6461
}
6562

6663
impl<T: Send + Sync + 'static> DynamicClass<T> {
67-
pub fn new_with_constructor<F, E>(class_name: impl ToString, data_constructor: F) -> Self
64+
pub fn new_with_constructor<F, E>(class_name: impl ToString, state_constructor: F) -> Self
6865
where
6966
F: Fn() -> Result<T, E> + Send + Sync + 'static,
7067
E: Throwable + 'static,
7168
{
7269
let mut dyn_class = Self {
7370
class_name: class_name.to_string(),
74-
// data_constructor: Box::new(|| data_constructor().map_err(|e| Box::new(e) as _)),
71+
state_constructor: Arc::new(move || state_constructor().map_err(|e| Box::new(e) as _)),
7572
method_entities: Vec::new(),
7673
property_entities: Vec::new(),
7774
parent: None,
@@ -109,7 +106,8 @@ impl<T: Send + Sync + 'static> DynamicClass<T> {
109106

110107
impl<T: Send + Sync> Classifiable for DynamicClass<T> {
111108
fn state_constructor(&self) -> Box<StateConstructor<Box<dyn Any>>> {
112-
Box::new(|| Ok(Box::new(())))
109+
let sc = self.state_constructor.clone();
110+
Box::new(move || sc().map(|x| Box::new(x) as _))
113111
}
114112

115113
fn class_name(&self) -> &str {
@@ -294,17 +292,18 @@ fn get_object_handlers() -> &'static zend_object_handlers {
294292
OBJECT_HANDLERS.get_or_init(|| unsafe {
295293
let mut handlers = std_object_handlers;
296294
handlers.offset = ExtendObject::offset() as c_int;
297-
handlers.free_obj = Some(destroy_object);
295+
handlers.free_obj = Some(free_object);
298296
handlers
299297
})
300298
}
301299

302300
unsafe extern "C" fn create_object(ce: *mut zend_class_entry) -> *mut zend_object {
303301
// Alloc more memory size to store state data.
304-
let extend_object: *mut ExtendObject = phper_zend_object_alloc(size_of::<ExtendObject>(), ce).cast();
302+
let extend_object: *mut ExtendObject =
303+
phper_zend_object_alloc(size_of::<ExtendObject>(), ce).cast();
305304

306305
// Common initialize process.
307-
let object = &mut (*extend_object).object;
306+
let object = ExtendObject::as_mut_object(extend_object);
308307
zend_object_std_init(object, ce);
309308
object_properties_init(object, ce);
310309
rebuild_object_properties(object);
@@ -322,12 +321,16 @@ unsafe extern "C" fn create_object(ce: *mut zend_class_entry) -> *mut zend_objec
322321
// Call the state constructor.
323322
// TODO Throw an exception rather than unwrap.
324323
let data: Box<dyn Any> = state_constructor().unwrap();
325-
(*extend_object).state = ManuallyDrop::new(data);
324+
*ExtendObject::as_mut_state(extend_object) = ManuallyDrop::new(data);
326325

327326
object
328327
}
329328

330-
unsafe extern "C" fn destroy_object(object: *mut zend_object) {
329+
unsafe extern "C" fn free_object(object: *mut zend_object) {
330+
// Drop the state.
331+
let extend_object = ExtendObject::fetch_ptr(object);
332+
ExtendObject::drop_state(extend_object);
333+
331334
// Original destroy call.
332335
zend_object_std_dtor(object);
333336
}

phper/src/functions.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ where
5656
}
5757
}
5858

59-
impl<F, R, T> Callable for Method<F, R, T>
59+
impl<F, R, T: 'static> Callable for Method<F, R, T>
6060
where
6161
F: Fn(&mut Object<T>, &mut [Val]) -> R + Send + Sync,
6262
R: SetVal,
@@ -229,6 +229,7 @@ unsafe extern "C" fn invoke(execute_data: *mut zend_execute_data, return_value:
229229
let handler = handler.as_ref().expect("handler is null");
230230

231231
// Check arguments count.
232+
// TODO Use `zend_argument_count_error` rather than just throw an exception.
232233
let num_args = execute_data.num_args() as usize;
233234
let required_num_args = execute_data.common_required_num_args() as usize;
234235
if num_args < required_num_args {

phper/src/objects.rs

Lines changed: 62 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
//! Apis relate to [crate::sys::zend_object].
22
3-
use std::{marker::PhantomData, ptr::null_mut};
4-
use std::any::Any;
5-
use std::ffi::c_void;
6-
use std::hash::Hasher;
7-
use std::io::Write;
8-
use std::mem::{size_of, ManuallyDrop};
9-
use std::slice::{from_raw_parts, from_raw_parts_mut};
3+
use std::{
4+
any::Any,
5+
ffi::c_void,
6+
hash::Hasher,
7+
io::Write,
8+
marker::PhantomData,
9+
mem::{size_of, ManuallyDrop},
10+
ptr::null_mut,
11+
slice::{from_raw_parts, from_raw_parts_mut},
12+
};
1013

1114
use crate::{
1215
alloc::{EAllocatable, EBox},
@@ -15,15 +18,16 @@ use crate::{
1518
sys::*,
1619
values::Val,
1720
};
21+
use std::ops::Deref;
1822

1923
/// Wrapper of [crate::sys::zend_object].
2024
#[repr(transparent)]
21-
pub struct Object<T> {
25+
pub struct Object<T: 'static> {
2226
inner: zend_object,
2327
_p: PhantomData<T>,
2428
}
2529

26-
impl<T> Object<T> {
30+
impl<T: 'static> Object<T> {
2731
pub fn new(class_entry: &ClassEntry) -> EBox<Self> {
2832
unsafe {
2933
let ptr = zend_objects_new(class_entry.as_ptr() as *mut _);
@@ -50,6 +54,16 @@ impl<T> Object<T> {
5054
&mut self.inner
5155
}
5256

57+
pub fn as_state(&self) -> &T {
58+
let eo = ExtendObject::fetch(&self.inner);
59+
eo.state.downcast_ref().unwrap()
60+
}
61+
62+
pub fn as_mut_state(&mut self) -> &mut T {
63+
let eo = ExtendObject::fetch_mut(&mut self.inner);
64+
eo.state.downcast_mut().unwrap()
65+
}
66+
5367
pub fn get_property(&self, name: impl AsRef<str>) -> &Val {
5468
let name = name.as_ref();
5569

@@ -152,16 +166,50 @@ impl<T> Drop for Object<T> {
152166
}
153167
}
154168

169+
pub(crate) type ManuallyDropState = ManuallyDrop<Box<dyn Any>>;
170+
155171
/// The Object contains `zend_object` and the user defined state data.
156172
#[repr(C)]
157-
pub struct ExtendObject {
158-
pub(crate) state: ManuallyDrop<Box<dyn Any>>,
159-
pub(crate) object: zend_object,
173+
pub(crate) struct ExtendObject {
174+
state: ManuallyDropState,
175+
object: zend_object,
160176
}
161177

162178
impl ExtendObject {
163179
pub(crate) const fn offset() -> usize {
164-
size_of::<ManuallyDrop<Box<dyn Any>>>()
180+
size_of::<ManuallyDropState>()
181+
}
182+
183+
pub(crate) fn fetch(object: &zend_object) -> &Self {
184+
unsafe {
185+
(((object as *const _ as usize) - ExtendObject::offset()) as *const Self)
186+
.as_ref()
187+
.unwrap()
188+
}
165189
}
166-
}
167190

191+
pub(crate) fn fetch_mut(object: &mut zend_object) -> &mut Self {
192+
unsafe {
193+
(((object as *mut _ as usize) - ExtendObject::offset()) as *mut Self)
194+
.as_mut()
195+
.unwrap()
196+
}
197+
}
198+
199+
pub(crate) fn fetch_ptr(object: *mut zend_object) -> *mut Self {
200+
(object as usize - ExtendObject::offset()) as *mut Self
201+
}
202+
203+
pub(crate) unsafe fn drop_state(this: *mut Self) {
204+
let state = &mut (*this).state;
205+
ManuallyDrop::drop(state);
206+
}
207+
208+
pub(crate) unsafe fn as_mut_state<'a>(this: *mut Self) -> &'a mut ManuallyDropState {
209+
&mut (*this).state
210+
}
211+
212+
pub(crate) unsafe fn as_mut_object<'a>(this: *mut Self) -> &'a mut zend_object {
213+
&mut (*this).object
214+
}
215+
}

phper/src/values.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ impl Val {
181181
}
182182
}
183183

184-
pub fn as_object<T>(&self) -> crate::Result<&Object<T>> {
184+
pub fn as_object(&self) -> crate::Result<&Object<()>> {
185185
if self.get_type().is_object() {
186186
unsafe {
187187
let ptr = self.inner.value.obj;

0 commit comments

Comments
 (0)