Skip to content

Commit 3405afd

Browse files
authored
Add StateClass and StateObject, fix construct. (#119)
1 parent d50bcc0 commit 3405afd

File tree

7 files changed

+207
-45
lines changed

7 files changed

+207
-45
lines changed

examples/http-server/src/request.rs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,22 @@
1010

1111
use phper::{
1212
arrays::ZArray,
13-
classes::{ClassEntity, ClassEntry, Visibility},
14-
objects::ZObject,
13+
classes::{ClassEntity, StateClass, Visibility},
14+
objects::StateObject,
1515
};
1616
use std::convert::Infallible;
1717

1818
pub const HTTP_REQUEST_CLASS_NAME: &str = "HttpServer\\HttpRequest";
1919

20+
pub static HTTP_REQUEST_CLASS: StateClass<()> = StateClass::new();
21+
2022
/// Register the class `HttpServer\HttpRequest` by `ClassEntity`.
2123
pub fn make_request_class() -> ClassEntity<()> {
2224
let mut class = ClassEntity::new(HTTP_REQUEST_CLASS_NAME);
2325

26+
// The state class will be initialized after class registered.
27+
class.bind(&HTTP_REQUEST_CLASS);
28+
2429
// Register the http headers field with public visibility.
2530
class.add_property("headers", Visibility::Public, ());
2631

@@ -38,6 +43,6 @@ pub fn make_request_class() -> ClassEntity<()> {
3843
}
3944

4045
/// New the object with class `HttpServer\HttpRequest`.
41-
pub fn new_request_object() -> phper::Result<ZObject> {
42-
ClassEntry::from_globals(HTTP_REQUEST_CLASS_NAME)?.new_object([])
46+
pub fn new_request_object() -> phper::Result<StateObject<()>> {
47+
HTTP_REQUEST_CLASS.new_object([])
4348
}

examples/http-server/src/response.rs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,23 @@ use axum::{
1414
http::{HeaderName, HeaderValue, Response},
1515
};
1616
use phper::{
17-
classes::{ClassEntity, ClassEntry, Visibility},
17+
classes::{ClassEntity, StateClass, Visibility},
1818
functions::Argument,
19-
objects::ZObject,
19+
objects::StateObject,
2020
};
2121

2222
pub const HTTP_RESPONSE_CLASS_NAME: &str = "HttpServer\\HttpResponse";
2323

24+
pub static HTTP_RESPONSE_CLASS: StateClass<Response<Body>> = StateClass::new();
25+
2426
/// Register the class `HttpServer\HttpResponse` by `ClassEntity`, with the
2527
/// inner state `Response<Body>`.
2628
pub fn make_response_class() -> ClassEntity<Response<Body>> {
2729
let mut class = ClassEntity::new_with_default_state_constructor(HTTP_RESPONSE_CLASS_NAME);
2830

31+
// The state class will be initialized after class registered.
32+
class.bind(&HTTP_RESPONSE_CLASS);
33+
2934
// Register the header method with public visibility, accept `name` and `value`
3035
// parameters.
3136
class
@@ -59,6 +64,6 @@ pub fn make_response_class() -> ClassEntity<Response<Body>> {
5964
}
6065

6166
/// New the object with class `HttpServer\HttpResponse`.
62-
pub fn new_response_object() -> phper::Result<ZObject> {
63-
ClassEntry::from_globals(HTTP_RESPONSE_CLASS_NAME)?.new_object([])
67+
pub fn new_response_object() -> phper::Result<StateObject<Response<Body>>> {
68+
HTTP_RESPONSE_CLASS.new_object([])
6469
}

phper-sys/php_wrapper.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,10 @@ uint32_t phper_z_addref_p(zval *zv) {
8383
return Z_ADDREF_P(zv);
8484
}
8585

86+
zend_function *phper_z_func_p(const zval *zv) {
87+
return Z_FUNC_P(zv);
88+
}
89+
8690
void *phper_z_ptr_p(const zval *zv) {
8791
return Z_PTR_P(zv);
8892
}

phper/src/classes.rs

Lines changed: 98 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use crate::{
1414
arrays::ZArr,
1515
errors::{ClassNotFoundError, InitializeObjectError, Throwable},
1616
functions::{Function, FunctionEntry, Method, MethodEntity},
17-
objects::{StateObj, StateObject, ZObj, ZObject},
17+
objects::{CStateObject, StateObj, StateObject, ZObj, ZObject},
1818
strings::ZStr,
1919
sys::*,
2020
types::Scalar,
@@ -34,6 +34,7 @@ use std::{
3434
os::raw::c_int,
3535
ptr::null_mut,
3636
rc::Rc,
37+
sync::atomic::{AtomicPtr, Ordering},
3738
};
3839

3940
/// Predefined interface `Iterator`.
@@ -136,6 +137,9 @@ impl ClassEntry {
136137
}
137138

138139
/// Create the object from class and call `__construct` with arguments.
140+
///
141+
/// If the `__construct` is private, or protected and the called scope isn't
142+
/// parent class, it will throw PHP Error.
139143
pub fn new_object(&self, arguments: impl AsMut<[ZVal]>) -> crate::Result<ZObject> {
140144
let mut object = self.init_object()?;
141145
object.call_construct(arguments)?;
@@ -199,6 +203,77 @@ fn find_global_class_entry_ptr(name: impl AsRef<str>) -> *mut zend_class_entry {
199203
}
200204
}
201205

206+
/// The [StateClass] holds [zend_class_entry](crate::sys::zend_class_entry) and
207+
/// inner state, always as the static variable, and then be bind to
208+
/// [ClassEntity].
209+
///
210+
/// When the class registered (module initialized), the [StateClass] will be
211+
/// initialized, so you can use the [StateClass] to new stateful object, etc.
212+
///
213+
/// So, You shouldn't use [StateClass] in `module_init` stage, because it hasn't
214+
/// initialized.
215+
///
216+
/// # Examples
217+
///
218+
/// ```rust
219+
/// use phper::classes::{ClassEntity, StateClass};
220+
///
221+
/// pub static FOO_CLASS: StateClass<FooState> = StateClass::new();
222+
///
223+
/// #[derive(Default)]
224+
/// pub struct FooState;
225+
///
226+
/// fn make_foo_class() -> ClassEntity<FooState> {
227+
/// let mut class = ClassEntity::new_with_default_state_constructor("Foo");
228+
/// class.bind(&FOO_CLASS);
229+
/// class
230+
/// }
231+
/// ```
232+
#[repr(transparent)]
233+
pub struct StateClass<T> {
234+
inner: AtomicPtr<zend_class_entry>,
235+
_p: PhantomData<T>,
236+
}
237+
238+
impl<T> StateClass<T> {
239+
/// Create empty [StateClass], with null
240+
/// [zend_class_entry](crate::sys::zend_class_entry).
241+
pub const fn new() -> Self {
242+
Self {
243+
inner: AtomicPtr::new(null_mut()),
244+
_p: PhantomData,
245+
}
246+
}
247+
248+
fn bind(&'static self, ptr: *mut zend_class_entry) {
249+
self.inner.store(ptr, Ordering::Relaxed);
250+
}
251+
252+
/// Converts to class entry.
253+
pub fn as_class_entry(&'static self) -> &'static ClassEntry {
254+
unsafe { ClassEntry::from_mut_ptr(self.inner.load(Ordering::Relaxed)) }
255+
}
256+
257+
/// Create the object from class and call `__construct` with arguments.
258+
///
259+
/// If the `__construct` is private, or protected and the called scope isn't
260+
/// parent class, it will throw PHP Error.
261+
pub fn new_object(
262+
&'static self, arguments: impl AsMut<[ZVal]>,
263+
) -> crate::Result<StateObject<T>> {
264+
self.as_class_entry()
265+
.new_object(arguments)
266+
.map(StateObject::new)
267+
}
268+
269+
/// Create the object from class, without calling `__construct`.
270+
///
271+
/// **Be careful when `__construct` is necessary.**
272+
pub fn init_object(&'static self) -> crate::Result<StateObject<T>> {
273+
self.as_class_entry().init_object().map(StateObject::new)
274+
}
275+
}
276+
202277
pub(crate) type StateConstructor = dyn Fn() -> Box<dyn Any>;
203278

204279
/// Builder for registering class.
@@ -207,13 +282,14 @@ pub(crate) type StateConstructor = dyn Fn() -> Box<dyn Any>;
207282
///
208283
/// *It is a common practice for PHP extensions to use PHP objects to package
209284
/// third-party resources.*
210-
pub struct ClassEntity<T> {
285+
pub struct ClassEntity<T: 'static> {
211286
class_name: CString,
212287
state_constructor: Rc<StateConstructor>,
213288
method_entities: Vec<MethodEntity>,
214289
property_entities: Vec<PropertyEntity>,
215290
parent: Option<Box<dyn Fn() -> &'static ClassEntry>>,
216291
interfaces: Vec<Box<dyn Fn() -> &'static ClassEntry>>,
292+
bind_class: Option<&'static StateClass<T>>,
217293
_p: PhantomData<(*mut (), T)>,
218294
}
219295

@@ -245,6 +321,7 @@ impl<T: 'static> ClassEntity<T> {
245321
property_entities: Vec::new(),
246322
parent: None,
247323
interfaces: Vec::new(),
324+
bind_class: None,
248325
_p: PhantomData,
249326
}
250327
}
@@ -331,6 +408,14 @@ impl<T: 'static> ClassEntity<T> {
331408
self.interfaces.push(Box::new(interface));
332409
}
333410

411+
/// Bind to static [StateClass].
412+
///
413+
/// When the class registered, the [StateClass] will be initialized, so you
414+
/// can use the [StateClass] to new stateful object, etc.
415+
pub fn bind(&mut self, cls: &'static StateClass<T>) {
416+
self.bind_class = Some(cls);
417+
}
418+
334419
#[allow(clippy::useless_conversion)]
335420
pub(crate) unsafe fn init(&self) -> *mut zend_class_entry {
336421
let parent: *mut zend_class_entry = self
@@ -348,6 +433,10 @@ impl<T: 'static> ClassEntity<T> {
348433
parent.cast(),
349434
);
350435

436+
if let Some(bind_class) = self.bind_class {
437+
bind_class.bind(class_ce);
438+
}
439+
351440
for interface in &self.interfaces {
352441
let interface_ce = interface().as_ptr();
353442
zend_class_implements(class_ce, 1, interface_ce);
@@ -482,7 +571,7 @@ fn get_object_handlers() -> &'static zend_object_handlers {
482571
static HANDLERS: OnceCell<zend_object_handlers> = OnceCell::new();
483572
HANDLERS.get_or_init(|| unsafe {
484573
let mut handlers = std_object_handlers;
485-
handlers.offset = StateObject::offset() as c_int;
574+
handlers.offset = CStateObject::offset() as c_int;
486575
handlers.free_obj = Some(free_object);
487576
handlers
488577
})
@@ -491,11 +580,11 @@ fn get_object_handlers() -> &'static zend_object_handlers {
491580
#[allow(clippy::useless_conversion)]
492581
unsafe extern "C" fn create_object(ce: *mut zend_class_entry) -> *mut zend_object {
493582
// Alloc more memory size to store state data.
494-
let state_object: *mut StateObject =
495-
phper_zend_object_alloc(size_of::<StateObject>().try_into().unwrap(), ce).cast();
583+
let state_object: *mut CStateObject =
584+
phper_zend_object_alloc(size_of::<CStateObject>().try_into().unwrap(), ce).cast();
496585

497586
// Common initialize process.
498-
let object = StateObject::as_mut_object(state_object);
587+
let object = CStateObject::as_mut_object(state_object);
499588
zend_object_std_init(object, ce);
500589
object_properties_init(object, ce);
501590
rebuild_object_properties(object);
@@ -512,15 +601,15 @@ unsafe extern "C" fn create_object(ce: *mut zend_class_entry) -> *mut zend_objec
512601

513602
// Call the state constructor.
514603
let data: Box<dyn Any> = (state_constructor.as_ref().unwrap())();
515-
*StateObject::as_mut_state(state_object) = Box::into_raw(data);
604+
*CStateObject::as_mut_state(state_object) = Box::into_raw(data);
516605

517606
object
518607
}
519608

520609
unsafe extern "C" fn free_object(object: *mut zend_object) {
521610
// Drop the state.
522-
let state_object = StateObject::fetch_ptr(object);
523-
StateObject::drop_state(state_object);
611+
let state_object = CStateObject::fetch_ptr(object);
612+
CStateObject::drop_state(state_object);
524613

525614
// Original destroy call.
526615
zend_object_std_dtor(object);

phper/src/functions.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -291,11 +291,11 @@ impl Argument {
291291

292292
/// Wrapper of [`zend_function`].
293293
#[repr(transparent)]
294-
pub struct ZendFunc {
294+
pub struct ZFunc {
295295
inner: zend_function,
296296
}
297297

298-
impl ZendFunc {
298+
impl ZFunc {
299299
/// Wraps a raw pointer.
300300
///
301301
/// # Safety
@@ -305,7 +305,7 @@ impl ZendFunc {
305305
/// # Panics
306306
///
307307
/// Panics if pointer is null.
308-
pub(crate) unsafe fn from_mut_ptr<'a>(ptr: *mut zend_function) -> &'a mut ZendFunc {
308+
pub(crate) unsafe fn from_mut_ptr<'a>(ptr: *mut zend_function) -> &'a mut ZFunc {
309309
let ptr = ptr as *mut Self;
310310
ptr.as_mut().expect("ptr shouldn't be null")
311311
}

0 commit comments

Comments
 (0)