Skip to content

Commit 4592d6b

Browse files
committed
Add generic type for ClassEntry, and add type id checks.
1 parent 45984fe commit 4592d6b

File tree

9 files changed

+129
-56
lines changed

9 files changed

+129
-56
lines changed

examples/http-client/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ crate-type = ["cdylib"]
1414
[dependencies]
1515
anyhow = "1.0.40"
1616
bytes = "1.0.1"
17+
indexmap = "1.6.2"
1718
phper = { version = "0.2.0-alpha.2", path = "../../phper" }
1819
reqwest = { version = "0.11.3", features = ["blocking"] }
1920
thiserror = "1.0.24"

examples/http-client/src/client.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,7 @@ pub fn make_client_class() -> DynamicClass<Client> {
3535
};
3636

3737
let mut response_object =
38-
Object::<Option<ReadiedResponse>>::new_by_class_name(RESPONSE_CLASS_NAME)
39-
.map_err(phper::Error::ClassNotFound)?;
38+
Object::<Option<ReadiedResponse>>::new_by_class_name(RESPONSE_CLASS_NAME)?;
4039
*response_object.as_mut_state() = Some(readied_response);
4140

4241
Ok::<_, HttpClientError>(response_object)

examples/http-client/src/errors.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use phper::{
2-
classes::{ClassEntry, DynamicClass},
2+
classes::{ClassEntry, DynamicClass, StatelessClassEntry},
33
errors::{Error::ClassNotFound, Throwable},
44
};
55

@@ -15,7 +15,7 @@ pub enum HttpClientError {
1515
}
1616

1717
impl Throwable for HttpClientError {
18-
fn class_entry(&self) -> &ClassEntry {
18+
fn class_entry(&self) -> &StatelessClassEntry {
1919
ClassEntry::from_globals(EXCEPTION_CLASS_NAME).unwrap()
2020
}
2121
}

examples/http-client/src/response.rs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
use bytes::Bytes;
2-
use phper::classes::DynamicClass;
2+
use indexmap::map::IndexMap;
3+
use phper::{arrays::Array, classes::DynamicClass};
34
use reqwest::{blocking::Response, header::HeaderMap, StatusCode};
45
use std::{
6+
collections::BTreeMap,
57
convert::Infallible,
68
mem::{zeroed, MaybeUninit},
79
net::SocketAddr,
@@ -40,5 +42,24 @@ pub fn make_response_class() -> DynamicClass<Option<ReadiedResponse>> {
4042
vec![],
4143
);
4244

45+
class.add_method(
46+
"headers",
47+
|this, arguments| {
48+
let readied_response = this.as_state().as_ref().unwrap();
49+
let headers_map =
50+
readied_response
51+
.headers
52+
.iter()
53+
.fold(IndexMap::new(), |mut acc, (key, value)| {
54+
acc.entry(key.as_str().to_owned())
55+
.or_insert(vec![])
56+
.push(value.as_bytes().to_owned());
57+
acc
58+
});
59+
headers_map
60+
},
61+
vec![],
62+
);
63+
4364
class
4465
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
$resp = $client->get("https://httpbin.org/ip");
1313
var_dump([
1414
"status" => $resp->status(),
15+
"headers" => $resp->headers(),
1516
"body" => $resp->body(),
1617
]);
1718

phper/src/classes.rs

Lines changed: 54 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
//! Apis relate to [crate::sys::zend_class_entry].
22
3+
use once_cell::sync::OnceCell;
34
use std::{
45
any::Any,
5-
collections::HashMap,
66
convert::Infallible,
77
marker::PhantomData,
88
mem::{size_of, zeroed, ManuallyDrop},
@@ -14,23 +14,22 @@ use std::{
1414
},
1515
};
1616

17-
use dashmap::DashMap;
18-
use once_cell::sync::OnceCell;
19-
2017
use phper_alloc::EBox;
2118

2219
use crate::{
23-
errors::{ClassNotFoundError, Throwable},
20+
errors::{ClassNotFoundError, StateTypeError, Throwable},
2421
functions::{Argument, FunctionEntity, FunctionEntry, Method},
2522
objects::{ExtendObject, Object},
2623
sys::*,
2724
utils::ensure_end_with_zero,
2825
values::{SetVal, Val},
2926
};
30-
use std::rc::Rc;
27+
use dashmap::DashMap;
28+
use std::{any::TypeId, iter::Once};
3129

3230
pub trait Classifiable {
3331
fn state_constructor(&self) -> Box<StateConstructor<Box<dyn Any>>>;
32+
fn state_type_id(&self) -> TypeId;
3433
fn class_name(&self) -> &str;
3534
fn methods(&mut self) -> &mut [FunctionEntity];
3635
fn properties(&mut self) -> &mut [PropertyEntity];
@@ -116,6 +115,10 @@ impl<T: Send + Sync> Classifiable for DynamicClass<T> {
116115
Box::new(move || sc().map(|x| Box::new(x) as _))
117116
}
118117

118+
fn state_type_id(&self) -> TypeId {
119+
TypeId::of::<T>()
120+
}
121+
119122
fn class_name(&self) -> &str {
120123
&self.class_name
121124
}
@@ -133,22 +136,42 @@ impl<T: Send + Sync> Classifiable for DynamicClass<T> {
133136
}
134137
}
135138

139+
/// Used to represent classes not registered by this framework,
140+
/// or classes that do not have or do not want to process state.
141+
pub type StatelessClassEntry = ClassEntry<()>;
142+
136143
/// Wrapper of [crate::sys::zend_class_entry].
137-
///
138-
/// TODO Add generic type.
139144
#[repr(transparent)]
140-
pub struct ClassEntry {
145+
pub struct ClassEntry<T: 'static> {
141146
inner: zend_class_entry,
147+
_p: PhantomData<T>,
142148
}
143149

144-
impl ClassEntry {
145-
// TODO After added generic type, Check generic type is relate to class_name, otherwise return error.
146-
pub fn from_globals<'a>(class_name: impl AsRef<str>) -> Result<&'a Self, ClassNotFoundError> {
150+
impl<T: 'static> ClassEntry<T> {
151+
pub fn from_globals<'a>(class_name: impl AsRef<str>) -> crate::Result<&'a Self> {
147152
let name = class_name.as_ref();
148153
let ptr: *mut Self = find_global_class_entry_ptr(name).cast();
149-
unsafe {
150-
ptr.as_ref()
151-
.ok_or_else(|| ClassNotFoundError::new(name.to_string()))
154+
let r = unsafe {
155+
ptr.as_ref().ok_or_else(|| {
156+
crate::Error::ClassNotFound(ClassNotFoundError::new(name.to_string()))
157+
})
158+
};
159+
Self::check_type_id(ptr);
160+
r
161+
}
162+
163+
fn check_type_id(this: *mut Self) -> Result<(), StateTypeError> {
164+
if TypeId::of::<T>() == TypeId::of::<()>() {
165+
return Ok(());
166+
}
167+
let is = get_registered_class_type_map()
168+
.get(&(this as usize))
169+
.map(|t| *t == TypeId::of::<T>())
170+
.unwrap_or_default();
171+
if is {
172+
Ok(())
173+
} else {
174+
Err(StateTypeError)
152175
}
153176
}
154177

@@ -164,7 +187,7 @@ impl ClassEntry {
164187
&mut self.inner
165188
}
166189

167-
pub(crate) fn create_object<T>(&self) -> EBox<Object<T>> {
190+
pub fn create_object(&self) -> EBox<Object<T>> {
168191
unsafe {
169192
let f = self.inner.__bindgen_anon_2.create_object.unwrap();
170193
let object = f(self.as_ptr() as *mut _);
@@ -188,7 +211,7 @@ fn find_global_class_entry_ptr(name: impl AsRef<str>) -> *mut zend_class_entry {
188211

189212
pub struct ClassEntity {
190213
pub(crate) name: String,
191-
pub(crate) entry: AtomicPtr<ClassEntry>,
214+
pub(crate) entry: AtomicPtr<ClassEntry<Box<dyn Any>>>,
192215
pub(crate) classifiable: Box<dyn Classifiable>,
193216
pub(crate) function_entries: OnceCell<AtomicPtr<FunctionEntry>>,
194217
}
@@ -210,21 +233,23 @@ impl ClassEntity {
210233
self.function_entries().load(Ordering::SeqCst).cast(),
211234
);
212235

213-
let parent = self
236+
let parent: Option<&StatelessClassEntry> = self
214237
.classifiable
215238
.parent()
216239
.map(|s| ClassEntry::from_globals(s).unwrap());
217240

218-
let class: *mut ClassEntry = match parent {
241+
let class: *mut ClassEntry<()> = match parent {
219242
Some(parent) => {
220243
zend_register_internal_class_ex(&mut class_ce, parent.as_ptr() as *mut _).cast()
221244
}
222245
None => zend_register_internal_class(&mut class_ce).cast(),
223246
};
224-
self.entry.store(class, Ordering::SeqCst);
247+
// self.entry.store(class, Ordering::SeqCst);
225248

226249
(*class).inner.__bindgen_anon_2.create_object = Some(create_object);
227250

251+
get_registered_class_type_map().insert(class as usize, self.classifiable.state_type_id());
252+
228253
// let classifiable = self.classifiable.clone();
229254
// get_class_constructor_map().insert(class as usize, Box::new(move || classifiable.state_constructor()));
230255

@@ -275,7 +300,7 @@ impl ClassEntity {
275300

276301
unsafe fn take_classifiable_into_function_entry(&self) -> zend_function_entry {
277302
let mut entry = zeroed::<zend_function_entry>();
278-
let ptr = &mut entry as *mut _ as *mut ManuallyDrop<Box<StateConstructor<Box<Any>>>>;
303+
let ptr = &mut entry as *mut _ as *mut ManuallyDrop<Box<StateConstructor<Box<dyn Any>>>>;
279304
let state_constructor = ManuallyDrop::new(self.classifiable.state_constructor());
280305
ptr.write(state_constructor);
281306
entry
@@ -305,9 +330,14 @@ pub enum Visibility {
305330
Private = ZEND_ACC_PRIVATE,
306331
}
307332

333+
fn get_registered_class_type_map() -> &'static DashMap<usize, TypeId> {
334+
static MAP: OnceCell<DashMap<usize, TypeId>> = OnceCell::new();
335+
MAP.get_or_init(DashMap::new)
336+
}
337+
308338
fn get_object_handlers() -> &'static zend_object_handlers {
309-
static OBJECT_HANDLERS: OnceCell<zend_object_handlers> = OnceCell::new();
310-
OBJECT_HANDLERS.get_or_init(|| unsafe {
339+
static HANDLERS: OnceCell<zend_object_handlers> = OnceCell::new();
340+
HANDLERS.get_or_init(|| unsafe {
311341
let mut handlers = std_object_handlers;
312342
handlers.offset = ExtendObject::offset() as c_int;
313343
handlers.free_obj = Some(free_object);
@@ -333,7 +363,7 @@ unsafe extern "C" fn create_object(ce: *mut zend_class_entry) -> *mut zend_objec
333363
func_ptr = func_ptr.offset(1);
334364
}
335365
func_ptr = func_ptr.offset(1);
336-
let state_constructor = func_ptr as *const ManuallyDrop<Box<StateConstructor<Box<Any>>>>;
366+
let state_constructor = func_ptr as *const ManuallyDrop<Box<StateConstructor<Box<dyn Any>>>>;
337367
let state_constructor = state_constructor.read();
338368

339369
// Call the state constructor.

phper/src/errors.rs

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
11
//! The errors for crate and php.
22
3-
use crate::{classes::ClassEntry, sys::*, Error::Other};
3+
use crate::{
4+
classes::{ClassEntry, StatelessClassEntry},
5+
sys::*,
6+
Error::Other,
7+
};
48
use anyhow::anyhow;
5-
use std::{convert::Infallible, error, ffi::FromBytesWithNulError, io, str::Utf8Error};
9+
use std::{
10+
any::TypeId, convert::Infallible, error, ffi::FromBytesWithNulError, io, str::Utf8Error,
11+
};
612

713
/// PHP Throwable, can cause throwing an exception when setting to [crate::values::Val].
814
pub trait Throwable: error::Error {
9-
fn class_entry(&self) -> &ClassEntry;
15+
fn class_entry(&self) -> &StatelessClassEntry;
1016

1117
fn code(&self) -> u64 {
1218
0
@@ -18,7 +24,7 @@ pub trait Throwable: error::Error {
1824
}
1925

2026
impl Throwable for Infallible {
21-
fn class_entry(&self) -> &ClassEntry {
27+
fn class_entry(&self) -> &StatelessClassEntry {
2228
unreachable!()
2329
}
2430
}
@@ -63,7 +69,7 @@ impl Error {
6369

6470
// TODO Add message() implement.
6571
impl Throwable for Error {
66-
fn class_entry(&self) -> &ClassEntry {
72+
fn class_entry(&self) -> &StatelessClassEntry {
6773
match self {
6874
Self::Type(e) => e.class_entry(),
6975
Self::ClassNotFound(e) => e.class_entry(),
@@ -95,7 +101,7 @@ impl TypeError {
95101
}
96102

97103
impl Throwable for TypeError {
98-
fn class_entry(&self) -> &ClassEntry {
104+
fn class_entry(&self) -> &ClassEntry<()> {
99105
ClassEntry::from_globals("TypeError").unwrap()
100106
}
101107
}
@@ -113,7 +119,20 @@ impl ClassNotFoundError {
113119
}
114120

115121
impl Throwable for ClassNotFoundError {
116-
fn class_entry(&self) -> &ClassEntry {
122+
fn class_entry(&self) -> &StatelessClassEntry {
123+
ClassEntry::from_globals("Error").unwrap()
124+
}
125+
}
126+
127+
#[derive(thiserror::Error, Debug)]
128+
#[error(
129+
"Actual State type in generic type parameter isn't the state type registered in the class, \
130+
please confirm the real state type, or use StatelessClassEntry"
131+
)]
132+
pub struct StateTypeError;
133+
134+
impl Throwable for StateTypeError {
135+
fn class_entry(&self) -> &StatelessClassEntry {
117136
ClassEntry::from_globals("Error").unwrap()
118137
}
119138
}
@@ -137,7 +156,7 @@ impl ArgumentCountError {
137156
}
138157

139158
impl Throwable for ArgumentCountError {
140-
fn class_entry(&self) -> &ClassEntry {
159+
fn class_entry(&self) -> &StatelessClassEntry {
141160
let class_name = if PHP_VERSION_ID >= 70100 {
142161
"ArgumentCountError"
143162
} else {

0 commit comments

Comments
 (0)