Skip to content

Commit 47e3f61

Browse files
committed
Refactoring Class and Object.
1 parent 9aacbc6 commit 47e3f61

File tree

9 files changed

+168
-71
lines changed

9 files changed

+168
-71
lines changed

examples/http-client/src/client.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use phper::functions::Argument;
88
const HTTP_CLIENT_CLASS_NAME: &'static str = "HttpClient\\HttpClient";
99

1010
pub fn make_client_class() -> DynamicClass<Client> {
11-
let mut http_client_class = DynamicClass::new_with_constructor(HTTP_CLIENT_CLASS_NAME, |_| {
11+
let mut http_client_class = DynamicClass::new_with_constructor(HTTP_CLIENT_CLASS_NAME, || {
1212
let client = ClientBuilder::new()
1313
.timeout(Duration::from_secs(15))
1414
.build()?;
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
use phper_test::test_php_scripts;
2+
use std::{env, path::Path};
3+
4+
#[test]
5+
fn test_php() {
6+
test_php_scripts(
7+
env!("CARGO_BIN_EXE_http-client"),
8+
&[&Path::new(env!("CARGO_MANIFEST_DIR"))
9+
.join("tests")
10+
.join("php")
11+
.join("test.php")],
12+
);
13+
}
Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
<?php
22

3+
use HttpClient\HttpClient;
4+
35
ini_set("display_errors", "On");
46
ini_set("display_startup_errors", "On");
57
error_reporting(E_ALL);
68

7-
phpinfo();
9+
$client = new HttpClient();
10+
var_dump($client);

phper-sys/php_wrapper.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,4 +132,8 @@ void phper_zval_ptr_dtor(zval *pDest) {
132132

133133
size_t phper_zend_object_properties_size(zend_class_entry *ce) {
134134
return zend_object_properties_size(ce);
135-
}
135+
}
136+
137+
void *phper_zend_object_alloc(size_t obj_size, zend_class_entry *ce) {
138+
return zend_object_alloc(obj_size, ce);
139+
}

phper-sys/php_wrapper.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,6 @@ void phper_zval_ptr_dtor(zval *pDest);
4646

4747
size_t phper_zend_object_properties_size(zend_class_entry *ce);
4848

49+
void *phper_zend_object_alloc(size_t obj_size, zend_class_entry *ce);
50+
4951
#endif //PHPER_PHP_WRAPPER_H

phper-test/src/lib.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -161,14 +161,16 @@ fn get_lib_path(exe_path: impl AsRef<Path>) -> PathBuf {
161161
let exe_path = exe_path.as_ref();
162162
let exe_stem = exe_path
163163
.file_stem()
164-
.expect("failed to get current exe stem");
164+
.expect("failed to get current exe stem")
165+
.to_str()
166+
.expect("failed to convert to utf-8 str");
165167
let target_dir = exe_path
166168
.parent()
167169
.expect("failed to get current exe directory");
168170

169171
let mut ext_name = OsString::new();
170172
ext_name.push("lib");
171-
ext_name.push(exe_stem);
173+
ext_name.push(exe_stem.replace('-', "_"));
172174
ext_name.push(".so");
173175

174176
target_dir.join(ext_name)

phper/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ keywords = ["php", "binding", "extension"]
1515
[dependencies]
1616
anyhow = "1.0.40"
1717
clap = "3.0.0-beta.2"
18+
dashmap = "4.0.2"
1819
indexmap = "1.6.2"
1920
num-derive = "0.3"
2021
num-traits = "0.2"

phper/src/classes.rs

Lines changed: 113 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,5 @@
11
//! Apis relate to [crate::sys::zend_class_entry].
22
3-
use crate::{
4-
errors::{ClassNotFoundError, Throwable},
5-
functions::{Argument, FunctionEntity, FunctionEntry, Method},
6-
objects::Object,
7-
sys::*,
8-
utils::ensure_end_with_zero,
9-
values::{SetVal, Val},
10-
};
11-
use once_cell::sync::OnceCell;
123
use std::{
134
convert::Infallible,
145
marker::PhantomData,
@@ -17,17 +8,43 @@ use std::{
178
ptr::null_mut,
189
sync::atomic::{AtomicPtr, Ordering},
1910
};
11+
use std::any::Any;
12+
use std::collections::HashMap;
13+
use std::mem::{ManuallyDrop, size_of};
14+
use std::sync::Arc;
15+
16+
use dashmap::DashMap;
17+
use once_cell::sync::OnceCell;
18+
19+
use phper_alloc::EBox;
20+
21+
use crate::{
22+
errors::{ClassNotFoundError, Throwable},
23+
functions::{Argument, FunctionEntity, FunctionEntry, Method},
24+
objects::Object,
25+
sys::*,
26+
utils::ensure_end_with_zero,
27+
values::{SetVal, Val},
28+
};
29+
use crate::objects::ExtendObject;
2030

2131
pub trait Classifiable {
32+
fn state_constructor(&self) -> Box<StateConstructor<Box<dyn Any>>>;
2233
fn class_name(&self) -> &str;
2334
fn methods(&mut self) -> &mut [FunctionEntity];
2435
fn properties(&mut self) -> &mut [PropertyEntity];
2536
fn parent(&self) -> Option<&str>;
2637
}
2738

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+
43+
pub type StateConstructor<T> = dyn Fn() -> Result<T, Box<dyn Throwable>> + Send + Sync;
44+
2845
pub struct DynamicClass<T: Send + Sync + 'static> {
2946
class_name: String,
30-
data_constructor: Box<dyn FnOnce(&mut Object<T>) -> Result<T, Box<dyn Throwable>>>,
47+
// data_constructor: Box<DataConstructor<T>>,
3148
pub(crate) method_entities: Vec<FunctionEntity>,
3249
pub(crate) property_entities: Vec<PropertyEntity>,
3350
pub(crate) parent: Option<String>,
@@ -36,30 +53,35 @@ pub struct DynamicClass<T: Send + Sync + 'static> {
3653

3754
impl DynamicClass<()> {
3855
pub fn new(class_name: impl ToString) -> Self {
39-
Self::new_with_constructor(class_name, |_| Ok::<_, Infallible>(()))
56+
Self::new_with_constructor(class_name, || Ok::<_, Infallible>(()))
4057
}
4158
}
4259

4360
impl<T: Default + Send + Sync + 'static> DynamicClass<T> {
4461
pub fn new_with_default(class_name: impl ToString) -> Self {
45-
Self::new_with_constructor(class_name, |_| Ok::<_, Infallible>(Default::default()))
62+
Self::new_with_constructor(class_name, || Ok::<_, Infallible>(Default::default()))
4663
}
4764
}
4865

4966
impl<T: Send + Sync + 'static> DynamicClass<T> {
5067
pub fn new_with_constructor<F, E>(class_name: impl ToString, data_constructor: F) -> Self
5168
where
52-
F: FnOnce(&mut Object<T>) -> Result<T, E> + 'static,
69+
F: Fn() -> Result<T, E> + Send + Sync + 'static,
5370
E: Throwable + 'static,
5471
{
55-
Self {
72+
let mut dyn_class = Self {
5673
class_name: class_name.to_string(),
57-
data_constructor: Box::new(|o| data_constructor(o).map_err(|e| Box::new(e) as _)),
74+
// data_constructor: Box::new(|| data_constructor().map_err(|e| Box::new(e) as _)),
5875
method_entities: Vec::new(),
5976
property_entities: Vec::new(),
6077
parent: None,
6178
_p: Default::default(),
62-
}
79+
};
80+
81+
// let ptr = &dyn_class.data_constructor as *const _ as usize;
82+
// dyn_class.add_property(DATA_CONSTRUCTOR_PROPERTY_NAME, ptr.to_string());
83+
84+
dyn_class
6385
}
6486

6587
pub fn add_method<F, R>(&mut self, name: impl ToString, handler: F, arguments: Vec<Argument>)
@@ -86,6 +108,10 @@ impl<T: Send + Sync + 'static> DynamicClass<T> {
86108
}
87109

88110
impl<T: Send + Sync> Classifiable for DynamicClass<T> {
111+
fn state_constructor(&self) -> Box<StateConstructor<Box<dyn Any>>> {
112+
Box::new(|| Ok(Box::new(())))
113+
}
114+
89115
fn class_name(&self) -> &str {
90116
&self.class_name
91117
}
@@ -147,16 +173,16 @@ fn find_global_class_entry_ptr(name: impl AsRef<str>) -> *mut zend_class_entry {
147173
pub struct ClassEntity {
148174
pub(crate) name: String,
149175
pub(crate) entry: AtomicPtr<ClassEntry>,
150-
pub(crate) class: Box<dyn Classifiable>,
176+
pub(crate) classifiable: Box<dyn Classifiable>,
151177
pub(crate) function_entries: OnceCell<AtomicPtr<FunctionEntry>>,
152178
}
153179

154180
impl ClassEntity {
155-
pub(crate) unsafe fn new(class: impl Classifiable + 'static) -> Self {
181+
pub(crate) unsafe fn new(classifiable: impl Classifiable + 'static) -> Self {
156182
Self {
157-
name: class.class_name().to_string(),
183+
name: classifiable.class_name().to_string(),
158184
entry: AtomicPtr::new(null_mut()),
159-
class: Box::new(class),
185+
classifiable: Box::new(classifiable),
160186
function_entries: Default::default(),
161187
}
162188
}
@@ -169,17 +195,22 @@ impl ClassEntity {
169195
);
170196

171197
let parent = self
172-
.class
198+
.classifiable
173199
.parent()
174200
.map(|s| ClassEntry::from_globals(s).unwrap());
175201

176-
let ptr = match parent {
202+
let class: *mut ClassEntry = match parent {
177203
Some(parent) => {
178204
zend_register_internal_class_ex(&mut class_ce, parent.as_ptr() as *mut _).cast()
179205
}
180206
None => zend_register_internal_class(&mut class_ce).cast(),
181207
};
182-
self.entry.store(ptr, Ordering::SeqCst);
208+
self.entry.store(class, Ordering::SeqCst);
209+
210+
(*class).inner.__bindgen_anon_2.create_object = Some(create_object);
211+
212+
// let classifiable = self.classifiable.clone();
213+
// get_class_constructor_map().insert(class as usize, Box::new(move || classifiable.state_constructor()));
183214

184215
// let methods = self.class.methods();
185216
// for method in methods {
@@ -193,7 +224,7 @@ impl ClassEntity {
193224
}
194225

195226
pub(crate) unsafe fn declare_properties(&mut self) {
196-
let properties = self.class.properties();
227+
let properties = self.classifiable.properties();
197228
for property in properties {
198229
let value = ensure_end_with_zero(property.value.clone());
199230
zend_declare_property_string(
@@ -207,18 +238,32 @@ impl ClassEntity {
207238
}
208239

209240
unsafe fn function_entries(&mut self) -> &AtomicPtr<FunctionEntry> {
210-
let methods = &*self.class.methods();
241+
let last_entry = self.take_classifiable_into_function_entry();
242+
let methods = &*self.classifiable.methods();
211243

212244
self.function_entries.get_or_init(|| {
213245
let mut methods = methods
214246
.iter()
215247
.map(|method| method.entry())
216248
.collect::<Vec<_>>();
249+
217250
methods.push(zeroed::<zend_function_entry>());
251+
252+
// Store the classifiable pointer to zend_class_entry
253+
methods.push(last_entry);
254+
218255
let entry = Box::into_raw(methods.into_boxed_slice()).cast();
219256
AtomicPtr::new(entry)
220257
})
221258
}
259+
260+
unsafe fn take_classifiable_into_function_entry(&self) -> zend_function_entry {
261+
let mut entry = zeroed::<zend_function_entry>();
262+
let ptr = &mut entry as *mut _ as *mut ManuallyDrop<Box<StateConstructor<Box<Any>>>>;
263+
let state_constructor = ManuallyDrop::new(self.classifiable.state_constructor());
264+
ptr.write(state_constructor);
265+
entry
266+
}
222267
}
223268

224269
pub struct PropertyEntity {
@@ -243,3 +288,46 @@ pub enum Visibility {
243288
Protected = ZEND_ACC_PROTECTED,
244289
Private = ZEND_ACC_PRIVATE,
245290
}
291+
292+
fn get_object_handlers() -> &'static zend_object_handlers {
293+
static OBJECT_HANDLERS: OnceCell<zend_object_handlers> = OnceCell::new();
294+
OBJECT_HANDLERS.get_or_init(|| unsafe {
295+
let mut handlers = std_object_handlers;
296+
handlers.offset = ExtendObject::offset() as c_int;
297+
handlers.free_obj = Some(destroy_object);
298+
handlers
299+
})
300+
}
301+
302+
unsafe extern "C" fn create_object(ce: *mut zend_class_entry) -> *mut zend_object {
303+
// Alloc more memory size to store state data.
304+
let extend_object: *mut ExtendObject = phper_zend_object_alloc(size_of::<ExtendObject>(), ce).cast();
305+
306+
// Common initialize process.
307+
let object = &mut (*extend_object).object;
308+
zend_object_std_init(object, ce);
309+
object_properties_init(object, ce);
310+
rebuild_object_properties(object);
311+
object.handlers = get_object_handlers();
312+
313+
// Get state constructor.
314+
let mut func_ptr = (*ce).info.internal.builtin_functions;
315+
while !(*func_ptr).fname.is_null() {
316+
func_ptr = func_ptr.offset(1);
317+
}
318+
func_ptr = func_ptr.offset(1);
319+
let state_constructor = func_ptr as *const ManuallyDrop<Box<StateConstructor<Box<Any>>>>;
320+
let state_constructor = state_constructor.read();
321+
322+
// Call the state constructor.
323+
// TODO Throw an exception rather than unwrap.
324+
let data: Box<dyn Any> = state_constructor().unwrap();
325+
(*extend_object).state = ManuallyDrop::new(data);
326+
327+
object
328+
}
329+
330+
unsafe extern "C" fn destroy_object(object: *mut zend_object) {
331+
// Original destroy call.
332+
zend_object_std_dtor(object);
333+
}

0 commit comments

Comments
 (0)