diff --git a/examples/http-client/src/client.rs b/examples/http-client/src/client.rs index 8e8c4374..1ff45428 100644 --- a/examples/http-client/src/client.rs +++ b/examples/http-client/src/client.rs @@ -8,22 +8,24 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use crate::{errors::HttpClientError, request::REQUEST_BUILDER_CLASS}; +use crate::{errors::HttpClientError, request::RequestBuilderClass}; use phper::{ alloc::ToRefOwned, - classes::{ClassEntity, StaticStateClass, Visibility}, + classes::{ClassEntity, StateClass, Visibility}, functions::Argument, }; use reqwest::blocking::{Client, ClientBuilder}; use std::{convert::Infallible, mem::take, time::Duration}; +pub type ClientBuilderClass = StateClass; + +pub type ClientClass = StateClass>; + const HTTP_CLIENT_BUILDER_CLASS_NAME: &str = "HttpClient\\HttpClientBuilder"; const HTTP_CLIENT_CLASS_NAME: &str = "HttpClient\\HttpClient"; -static HTTP_CLIENT_CLASS: StaticStateClass> = StaticStateClass::null(); - -pub fn make_client_builder_class() -> ClassEntity { +pub fn make_client_builder_class(client_class: ClientClass) -> ClassEntity { // `new_with_default_state_constructor` means initialize the state of // `ClientBuilder` as `Default::default`. let mut class = ClassEntity::new_with_default_state_constructor(HTTP_CLIENT_BUILDER_CLASS_NAME); @@ -52,10 +54,10 @@ pub fn make_client_builder_class() -> ClassEntity { // Inner call the `ClientBuilder::build`, and wrap the result `Client` in // Object. - class.add_method("build", Visibility::Public, |this, _arguments| { + class.add_method("build", Visibility::Public, move |this, _arguments| { let state = take(this.as_mut_state()); let client = ClientBuilder::build(state).map_err(HttpClientError::Reqwest)?; - let mut object = HTTP_CLIENT_CLASS.init_object()?; + let mut object = client_class.init_object()?; *object.as_mut_state() = Some(client); Ok::<_, phper::Error>(object) }); @@ -63,33 +65,34 @@ pub fn make_client_builder_class() -> ClassEntity { class } -pub fn make_client_class() -> ClassEntity> { +pub fn make_client_class( + request_builder_class: RequestBuilderClass, +) -> ClassEntity> { let mut class = ClassEntity::>::new_with_default_state_constructor(HTTP_CLIENT_CLASS_NAME); - class.bind(&HTTP_CLIENT_CLASS); - class.add_method("__construct", Visibility::Private, |_, _| { Ok::<_, Infallible>(()) }); + let request_build_class_ = request_builder_class.clone(); class - .add_method("get", Visibility::Public, |this, arguments| { + .add_method("get", Visibility::Public, move |this, arguments| { let url = arguments[0].expect_z_str()?.to_str().unwrap(); let client = this.as_state().as_ref().unwrap(); let request_builder = client.get(url); - let mut object = REQUEST_BUILDER_CLASS.init_object()?; + let mut object = request_build_class_.init_object()?; *object.as_mut_state() = Some(request_builder); Ok::<_, phper::Error>(object) }) .argument(Argument::by_val("url")); class - .add_method("post", Visibility::Public, |this, arguments| { + .add_method("post", Visibility::Public, move |this, arguments| { let url = arguments[0].expect_z_str()?.to_str().unwrap(); let client = this.as_state().as_ref().unwrap(); let request_builder = client.post(url); - let mut object = REQUEST_BUILDER_CLASS.init_object()?; + let mut object = request_builder_class.init_object()?; *object.as_mut_state() = Some(request_builder); Ok::<_, phper::Error>(object) }) diff --git a/examples/http-client/src/lib.rs b/examples/http-client/src/lib.rs index db34a621..11cfa8d8 100644 --- a/examples/http-client/src/lib.rs +++ b/examples/http-client/src/lib.rs @@ -30,10 +30,10 @@ pub fn get_module() -> Module { ); module.add_class(make_exception_class()); - module.add_class(make_client_class()); - module.add_class(make_client_builder_class()); - module.add_class(make_request_builder_class()); - module.add_class(make_response_class()); + let response_class = module.add_class(make_response_class()); + let request_builder_class = module.add_class(make_request_builder_class(response_class)); + let client_class = module.add_class(make_client_class(request_builder_class)); + module.add_class(make_client_builder_class(client_class)); module } diff --git a/examples/http-client/src/request.rs b/examples/http-client/src/request.rs index d5b4a239..6c389099 100644 --- a/examples/http-client/src/request.rs +++ b/examples/http-client/src/request.rs @@ -8,31 +8,30 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use crate::{errors::HttpClientError, response::RESPONSE_CLASS}; -use phper::classes::{ClassEntity, StaticStateClass, Visibility}; +use crate::{errors::HttpClientError, response::ResponseClass}; +use phper::classes::{ClassEntity, StateClass, Visibility}; use reqwest::blocking::RequestBuilder; use std::{convert::Infallible, mem::take}; -pub const REQUEST_BUILDER_CLASS_NAME: &str = "HttpClient\\RequestBuilder"; +pub type RequestBuilderClass = StateClass>; -pub static REQUEST_BUILDER_CLASS: StaticStateClass> = - StaticStateClass::null(); +pub const REQUEST_BUILDER_CLASS_NAME: &str = "HttpClient\\RequestBuilder"; -pub fn make_request_builder_class() -> ClassEntity> { +pub fn make_request_builder_class( + response_class: ResponseClass, +) -> ClassEntity> { let mut class = ClassEntity::>::new_with_default_state_constructor( REQUEST_BUILDER_CLASS_NAME, ); - class.bind(&REQUEST_BUILDER_CLASS); - class.add_method("__construct", Visibility::Private, |_, _| { Ok::<_, Infallible>(()) }); - class.add_method("send", Visibility::Public, |this, _arguments| { + class.add_method("send", Visibility::Public, move |this, _arguments| { let state = take(this.as_mut_state()); let response = state.unwrap().send().map_err(HttpClientError::Reqwest)?; - let mut object = RESPONSE_CLASS.new_object([])?; + let mut object = response_class.new_object([])?; *object.as_mut_state() = Some(response); Ok::<_, phper::Error>(object) }); diff --git a/examples/http-client/src/response.rs b/examples/http-client/src/response.rs index e10b1b69..ff4378c2 100644 --- a/examples/http-client/src/response.rs +++ b/examples/http-client/src/response.rs @@ -11,22 +11,20 @@ use crate::errors::HttpClientError; use phper::{ arrays::{InsertKey, ZArray}, - classes::{ClassEntity, StaticStateClass, Visibility}, + classes::{ClassEntity, StateClass, Visibility}, values::ZVal, }; use reqwest::blocking::Response; use std::mem::take; -pub const RESPONSE_CLASS_NAME: &str = "HttpClient\\Response"; +pub type ResponseClass = StateClass>; -pub static RESPONSE_CLASS: StaticStateClass> = StaticStateClass::null(); +pub const RESPONSE_CLASS_NAME: &str = "HttpClient\\Response"; pub fn make_response_class() -> ClassEntity> { let mut class = ClassEntity::>::new_with_default_state_constructor(RESPONSE_CLASS_NAME); - class.bind(&RESPONSE_CLASS); - class.add_method("body", Visibility::Public, |this, _arguments| { let response = take(this.as_mut_state()); let response = response.ok_or(HttpClientError::ResponseHadRead)?; diff --git a/examples/http-server/src/lib.rs b/examples/http-server/src/lib.rs index a30c9f16..5ec83b03 100644 --- a/examples/http-server/src/lib.rs +++ b/examples/http-server/src/lib.rs @@ -30,9 +30,9 @@ pub fn get_module() -> Module { // Register classes. module.add_class(make_exception_class()); - module.add_class(make_server_class()); - module.add_class(make_request_class()); - module.add_class(make_response_class()); + let request_class = module.add_class(make_request_class()); + let response_class = module.add_class(make_response_class()); + module.add_class(make_server_class(request_class, response_class)); module } diff --git a/examples/http-server/src/request.rs b/examples/http-server/src/request.rs index d9d88161..b0b74b0a 100644 --- a/examples/http-server/src/request.rs +++ b/examples/http-server/src/request.rs @@ -10,22 +10,18 @@ use phper::{ arrays::ZArray, - classes::{ClassEntity, StaticStateClass, Visibility}, - objects::StateObject, + classes::{ClassEntity, StateClass, Visibility}, }; use std::convert::Infallible; -pub const HTTP_REQUEST_CLASS_NAME: &str = "HttpServer\\HttpRequest"; +pub type RequestClass = StateClass<()>; -pub static HTTP_REQUEST_CLASS: StaticStateClass<()> = StaticStateClass::null(); +pub const HTTP_REQUEST_CLASS_NAME: &str = "HttpServer\\HttpRequest"; /// Register the class `HttpServer\HttpRequest` by `ClassEntity`. pub fn make_request_class() -> ClassEntity<()> { let mut class = ClassEntity::new(HTTP_REQUEST_CLASS_NAME); - // The state class will be initialized after class registered. - class.bind(&HTTP_REQUEST_CLASS); - // Register the http headers field with public visibility. class.add_property("headers", Visibility::Public, ()); @@ -41,8 +37,3 @@ pub fn make_request_class() -> ClassEntity<()> { class } - -/// Instantiate the object with class `HttpServer\HttpRequest`. -pub fn new_request_object() -> phper::Result> { - HTTP_REQUEST_CLASS.new_object([]) -} diff --git a/examples/http-server/src/response.rs b/examples/http-server/src/response.rs index b5eabfe3..e8c0b0ec 100644 --- a/examples/http-server/src/response.rs +++ b/examples/http-server/src/response.rs @@ -14,23 +14,19 @@ use axum::{ http::{HeaderName, HeaderValue, Response}, }; use phper::{ - classes::{ClassEntity, StaticStateClass, Visibility}, + classes::{ClassEntity, StateClass, Visibility}, functions::Argument, - objects::StateObject, }; -pub const HTTP_RESPONSE_CLASS_NAME: &str = "HttpServer\\HttpResponse"; +pub type ResponseClass = StateClass>; -pub static HTTP_RESPONSE_CLASS: StaticStateClass> = StaticStateClass::null(); +pub const HTTP_RESPONSE_CLASS_NAME: &str = "HttpServer\\HttpResponse"; /// Register the class `HttpServer\HttpResponse` by `ClassEntity`, with the /// inner state `Response`. pub fn make_response_class() -> ClassEntity> { let mut class = ClassEntity::new_with_default_state_constructor(HTTP_RESPONSE_CLASS_NAME); - // The state class will be initialized after class registered. - class.bind(&HTTP_RESPONSE_CLASS); - // Register the header method with public visibility, accept `name` and `value` // parameters. class @@ -62,8 +58,3 @@ pub fn make_response_class() -> ClassEntity> { class } - -/// Instantiate the object with class `HttpServer\HttpResponse`. -pub fn new_response_object() -> phper::Result>> { - HTTP_RESPONSE_CLASS.new_object([]) -} diff --git a/examples/http-server/src/server.rs b/examples/http-server/src/server.rs index b9765403..d79038f7 100644 --- a/examples/http-server/src/server.rs +++ b/examples/http-server/src/server.rs @@ -8,7 +8,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use crate::{errors::HttpServerError, request::new_request_object, response::new_response_object}; +use crate::{errors::HttpServerError, request::RequestClass, response::ResponseClass}; use axum::{ body::{self, Body}, http::{Request, Response, StatusCode}, @@ -31,10 +31,14 @@ const HTTP_SERVER_CLASS_NAME: &str = "HttpServer\\HttpServer"; thread_local! { // The map store the on request handlers, indexed by `HttpServer\HttpServer` object id. static ON_REQUEST_HANDLERS: RefCell> = Default::default(); + + static CLASSES_MAP: RefCell> = Default::default(); } /// Register the class `HttpServer\HttpServer` by `ClassEntity`. -pub fn make_server_class() -> ClassEntity<()> { +pub fn make_server_class( + request_class: RequestClass, response_class: ResponseClass, +) -> ClassEntity<()> { let mut class = ClassEntity::new(HTTP_SERVER_CLASS_NAME); // Register the server host field with public visibility. @@ -46,13 +50,21 @@ pub fn make_server_class() -> ClassEntity<()> { // Register the constructor method with public visibility, accept host and port // arguments, initialize the host and port member properties. class - .add_method("__construct", Visibility::Public, |this, arguments| { + .add_method("__construct", Visibility::Public, move |this, arguments| { let host = arguments[0].expect_z_str()?; let port = arguments[1].expect_long()?; this.set_property("host", host.to_owned()); this.set_property("port", port); + let (request_class, response_class) = (request_class.clone(), response_class.clone()); + + CLASSES_MAP.with(move |classes_map| { + classes_map + .borrow_mut() + .insert(this.handle(), (request_class, response_class)); + }); + Ok::<_, phper::Error>(()) }) .arguments([Argument::by_val("host"), Argument::by_val("port")]); @@ -98,40 +110,44 @@ pub fn make_server_class() -> ClassEntity<()> { .await .map_err(HttpServerError::new)?; - // Create PHP `HttpServer\HttpRequest` object. - let mut request = new_request_object()?; + CLASSES_MAP.with(|classes_map| { + let (request_class, response_class) = &classes_map.borrow()[&handle]; + + // Create PHP `HttpServer\HttpRequest` object. + let mut request = request_class.new_object([])?; - // Inject headers from Rust request object to PHP request object. - let request_headers = - request.get_mut_property("headers").expect_mut_z_arr()?; - for (key, value) in parts.headers { - if let Some(key) = key { - request_headers.insert(key.as_str(), value.as_bytes()); + // Inject headers from Rust request object to PHP request object. + let request_headers = + request.get_mut_property("headers").expect_mut_z_arr()?; + for (key, value) in parts.headers { + if let Some(key) = key { + request_headers.insert(key.as_str(), value.as_bytes()); + } } - } - // Inject body content from Rust request object to PHP request object. - request.set_property("data", &*body); + // Inject body content from Rust request object to PHP request object. + request.set_property("data", &*body); - let request_val = ZVal::from(request); + let request_val = ZVal::from(request); - // Create PHP `HttpServer\HttpResponse` object. - let mut response = new_response_object()?; + // Create PHP `HttpServer\HttpResponse` object. + let mut response = response_class.new_object([])?; - let response_val = ZVal::from(response.to_ref_owned()); + let response_val = ZVal::from(response.to_ref_owned()); - ON_REQUEST_HANDLERS.with(|handlers| { - // Get the on request handlers by object id. - let mut handlers = handlers.borrow_mut(); - let handler = handlers.get_mut(&handle).unwrap(); + ON_REQUEST_HANDLERS.with(|handlers| { + // Get the on request handlers by object id. + let mut handlers = handlers.borrow_mut(); + let handler = handlers.get_mut(&handle).unwrap(); - // Call the PHP on request handler. - handler.call([request_val, response_val])?; + // Call the PHP on request handler. + handler.call([request_val, response_val])?; - // Get the inner state. - let response = response.into_state().unwrap(); + // Get the inner state. + let response = response.into_state().unwrap(); - Ok::, phper::Error>(response) + Ok::, phper::Error>(response) + }) }) }; match fut.await { diff --git a/phper-doc/doc/_02_quick_start/_02_write_a_simple_http_client/index.md b/phper-doc/doc/_02_quick_start/_02_write_a_simple_http_client/index.md index 0e903a6e..c688ee51 100644 --- a/phper-doc/doc/_02_quick_start/_02_write_a_simple_http_client/index.md +++ b/phper-doc/doc/_02_quick_start/_02_write_a_simple_http_client/index.md @@ -185,7 +185,7 @@ Now let's begin to finish the logic. # use phper::{ alloc::ToRefOwned, - classes::{StaticStateClass, Visibility}, + classes::{StateClass, Visibility}, functions::Argument, }; use reqwest::blocking::{Client, ClientBuilder}; @@ -195,11 +195,9 @@ Now let's begin to finish the logic. const HTTP_CLIENT_CLASS_NAME: &str = "HttpClient\\HttpClient"; - // The static StaticStateClass is bind to ClassEntity of HttpClient, When the class registered, - // the StaticStateClass will be initialized, so you can use it to new stateful object, etc. - static HTTP_CLIENT_CLASS:StaticStateClass> =StaticStateClass::null(); + pub type ClientClass = StateClass>; - pub fn make_client_builder_class() -> ClassEntity { + pub fn make_client_builder_class(client_class: ClientClass) -> ClassEntity { // `new_with_default_state_constructor` means initialize the state of `ClientBuilder` as // `Default::default`. let mut class = ClassEntity::new_with_default_state_constructor(HTTP_CLIENT_BUILDER_CLASS_NAME); @@ -228,10 +226,10 @@ Now let's begin to finish the logic. // Inner call the `ClientBuilder::build`, and wrap the result `Client` in // Object. - class.add_method("build", Visibility::Public, |this, _arguments| { + class.add_method("build", Visibility::Public, move |this, _arguments| { let state = take(this.as_mut_state()); let client = ClientBuilder::build(state).map_err(HttpClientError::Reqwest)?; - let mut object = HTTP_CLIENT_CLASS.init_object()?; + let mut object = client_class.init_object()?; *object.as_mut_state() = Some(client); Ok::<_, phper::Error>(object) }); diff --git a/phper/src/classes.rs b/phper/src/classes.rs index 852031de..84ba558b 100644 --- a/phper/src/classes.rs +++ b/phper/src/classes.rs @@ -24,6 +24,7 @@ use crate::{ }; use std::{ any::Any, + cell::RefCell, ffi::{c_char, c_void, CString}, fmt::Debug, marker::PhantomData, @@ -32,7 +33,6 @@ use std::{ ptr::null_mut, rc::Rc, slice, - sync::atomic::{AtomicPtr, Ordering}, }; /// Predefined interface `Iterator`. @@ -235,65 +235,72 @@ fn find_global_class_entry_ptr(name: impl AsRef) -> *mut zend_class_entry { } } -/// The [StaticStateClass] holds -/// [zend_class_entry] and inner state, always as -/// the static variable, and then be bind to [ClassEntity]. +/// The [StateClass] holds [zend_class_entry] and inner state, created by +/// [Module::add_class](crate::modules::Module::add_class). /// -/// When the class registered (module initialized), the [StaticStateClass] will -/// be initialized, so you can use the [StaticStateClass] to new stateful +/// When the class registered (module initialized), the [StateClass] will +/// be initialized, so you can use the [StateClass] to new stateful /// object, etc. /// -/// So, You shouldn't use [StaticStateClass] in `module_init` stage, because it +/// So, You shouldn't use [StateClass] in `module_init` stage, because it /// hasn't initialized. /// /// # Examples /// /// ```rust -/// use phper::classes::{ClassEntity, StaticStateClass}; -/// -/// pub static FOO_CLASS: StaticStateClass = StaticStateClass::null(); +/// use phper::{ +/// classes::{ClassEntity, StateClass}, +/// modules::Module, +/// php_get_module, +/// }; /// /// #[derive(Default)] /// pub struct FooState; /// /// fn make_foo_class() -> ClassEntity { -/// let mut class = ClassEntity::new_with_default_state_constructor("Foo"); -/// class.bind(&FOO_CLASS); -/// class +/// ClassEntity::new_with_default_state_constructor("Foo") +/// } +/// +/// #[php_get_module] +/// pub fn get_module() -> Module { +/// let mut module = Module::new( +/// env!("CARGO_CRATE_NAME"), +/// env!("CARGO_PKG_VERSION"), +/// env!("CARGO_PKG_AUTHORS"), +/// ); +/// +/// let _foo_class: StateClass = module.add_class(make_foo_class()); +/// +/// module /// } /// ``` -#[repr(transparent)] -pub struct StaticStateClass { - inner: AtomicPtr, +pub struct StateClass { + inner: Rc>, _p: PhantomData, } -impl StaticStateClass { - /// Create empty [StaticStateClass], with null - /// [zend_class_entry]. - pub const fn null() -> Self { +impl StateClass { + fn null() -> Self { Self { - inner: AtomicPtr::new(null_mut()), + inner: Rc::new(RefCell::new(null_mut())), _p: PhantomData, } } - fn bind(&'static self, ptr: *mut zend_class_entry) { - self.inner.store(ptr, Ordering::Relaxed); + fn bind(&self, ptr: *mut zend_class_entry) { + *self.inner.borrow_mut() = ptr; } /// Converts to class entry. - pub fn as_class_entry(&'static self) -> &'static ClassEntry { - unsafe { ClassEntry::from_mut_ptr(self.inner.load(Ordering::Relaxed)) } + pub fn as_class_entry(&self) -> &ClassEntry { + unsafe { ClassEntry::from_mut_ptr(*self.inner.borrow()) } } /// Create the object from class and call `__construct` with arguments. /// /// If the `__construct` is private, or protected and the called scope isn't /// parent class, it will throw PHP Error. - pub fn new_object( - &'static self, arguments: impl AsMut<[ZVal]>, - ) -> crate::Result> { + pub fn new_object(&self, arguments: impl AsMut<[ZVal]>) -> crate::Result> { self.as_class_entry() .new_object(arguments) .map(ZObject::into_raw) @@ -303,7 +310,7 @@ impl StaticStateClass { /// Create the object from class, without calling `__construct`. /// /// **Be careful when `__construct` is necessary.** - pub fn init_object(&'static self) -> crate::Result> { + pub fn init_object(&self) -> crate::Result> { self.as_class_entry() .init_object() .map(ZObject::into_raw) @@ -311,52 +318,69 @@ impl StaticStateClass { } } -unsafe impl Sync for StaticStateClass {} +impl Clone for StateClass { + fn clone(&self) -> Self { + Self { + inner: self.inner.clone(), + _p: self._p, + } + } +} -/// The [StaticInterface] holds -/// [zend_class_entry], always as the static -/// variable, and then be bind to [InterfaceEntity]. +/// The [Interface] holds [zend_class_entry], created by +/// [Module::add_interface](crate::modules::Module::add_interface). /// -/// When the interface registered (module initialized), the [StaticInterface] +/// When the interface registered (module initialized), the [Interface] /// will be initialized. /// -/// So, You shouldn't use [StaticInterface] in `module_init` stage, because it +/// So, You shouldn't use [Interface] in `module_init` stage, because it /// hasn't initialized. /// /// # Examples /// /// ```rust -/// use phper::classes::{InterfaceEntity, StaticInterface}; -/// -/// pub static FOO_INTERFACE: StaticInterface = StaticInterface::null(); +/// use phper::{ +/// classes::{Interface, InterfaceEntity}, +/// modules::Module, +/// php_get_module, +/// }; /// /// fn make_foo_interface() -> InterfaceEntity { -/// let mut interface = InterfaceEntity::new("Foo"); -/// interface.bind(&FOO_INTERFACE); -/// interface +/// InterfaceEntity::new("Foo") +/// } +/// +/// #[php_get_module] +/// pub fn get_module() -> Module { +/// let mut module = Module::new( +/// env!("CARGO_CRATE_NAME"), +/// env!("CARGO_PKG_VERSION"), +/// env!("CARGO_PKG_AUTHORS"), +/// ); +/// +/// let _foo_interface: Interface = module.add_interface(make_foo_interface()); +/// +/// module /// } /// ``` -#[repr(transparent)] -pub struct StaticInterface { - inner: AtomicPtr, +#[derive(Clone)] +pub struct Interface { + inner: Rc>, } -impl StaticInterface { - /// Create empty [StaticInterface], with null - /// [zend_class_entry]. - pub const fn null() -> Self { +impl Interface { + fn null() -> Self { Self { - inner: AtomicPtr::new(null_mut()), + inner: Rc::new(RefCell::new(null_mut())), } } - fn bind(&'static self, ptr: *mut zend_class_entry) { - self.inner.store(ptr, Ordering::Relaxed); + fn bind(&self, ptr: *mut zend_class_entry) { + *self.inner.borrow_mut() = ptr; } /// Converts to class entry. - pub fn as_class_entry(&'static self) -> &'static ClassEntry { - unsafe { ClassEntry::from_mut_ptr(self.inner.load(Ordering::Relaxed)) } + pub fn as_class_entry(&self) -> &ClassEntry { + unsafe { ClassEntry::from_mut_ptr(*self.inner.borrow()) } } } @@ -378,7 +402,7 @@ pub struct ClassEntity { parent: Option &'static ClassEntry>>, interfaces: Vec &'static ClassEntry>>, constants: Vec, - bind_class: Option<&'static StaticStateClass>, + bind_class: StateClass, state_cloner: Option>, _p: PhantomData<(*mut (), T)>, } @@ -416,7 +440,7 @@ impl ClassEntity { parent: None, interfaces: Vec::new(), constants: Vec::new(), - bind_class: None, + bind_class: StateClass::null(), state_cloner: None, _p: PhantomData, } @@ -536,14 +560,6 @@ impl ClassEntity { self.interfaces.push(Box::new(interface)); } - /// Bind to static [StaticStateClass]. - /// - /// When the class registered, the [StaticStateClass] will be initialized, - /// so you can use the [StaticStateClass] to new stateful object, etc. - pub fn bind(&mut self, cls: &'static StaticStateClass) { - self.bind_class = Some(cls); - } - /// Add the state clone function, called when cloning PHP object. /// /// By default, the object registered by `phper` is uncloneable, if you @@ -602,9 +618,7 @@ impl ClassEntity { parent.cast(), ); - if let Some(bind_class) = self.bind_class { - bind_class.bind(class_ce); - } + self.bind_class.bind(class_ce); for interface in &self.interfaces { let interface_ce = interface().as_ptr(); @@ -675,6 +689,11 @@ impl ClassEntity { }) .collect() } + + #[inline] + pub(crate) fn bind_class(&self) -> StateClass { + self.bind_class.clone() + } } unsafe extern "C" fn class_init_handler( @@ -694,7 +713,7 @@ pub struct InterfaceEntity { method_entities: Vec, constants: Vec, extends: Vec &'static ClassEntry>>, - bind_interface: Option<&'static StaticInterface>, + bind_interface: Interface, } impl InterfaceEntity { @@ -705,7 +724,7 @@ impl InterfaceEntity { method_entities: Vec::new(), constants: Vec::new(), extends: Vec::new(), - bind_interface: None, + bind_interface: Interface::null(), } } @@ -743,15 +762,6 @@ impl InterfaceEntity { self.extends.push(Box::new(interface)); } - /// Bind to static [StaticInterface]. - /// - /// When the interface registered, the [StaticInterface] will be - /// initialized, so you can use the [StaticInterface] to be implemented - /// by other class. - pub fn bind(&mut self, i: &'static StaticInterface) { - self.bind_interface = Some(i); - } - #[allow(clippy::useless_conversion)] pub(crate) unsafe fn init(&self) -> *mut zend_class_entry { let class_ce = phper_init_class_entry_ex( @@ -762,9 +772,7 @@ impl InterfaceEntity { null_mut(), ); - if let Some(bind_interface) = self.bind_interface { - bind_interface.bind(class_ce); - } + self.bind_interface.bind(class_ce); for interface in &self.extends { let interface_ce = interface().as_ptr(); @@ -789,6 +797,11 @@ impl InterfaceEntity { Box::into_raw(methods.into_boxed_slice()).cast() } + + #[inline] + pub(crate) fn bind_interface(&self) -> Interface { + self.bind_interface.clone() + } } unsafe extern "C" fn interface_init_handler( diff --git a/phper/src/modules.rs b/phper/src/modules.rs index 8d408fd9..2a084e8b 100644 --- a/phper/src/modules.rs +++ b/phper/src/modules.rs @@ -11,7 +11,7 @@ //! Apis relate to [zend_module_entry]. use crate::{ - classes::{ClassEntity, InterfaceEntity}, + classes::{ClassEntity, Interface, InterfaceEntity, StateClass}, constants::Constant, errors::Throwable, functions::{Function, FunctionEntity, FunctionEntry, HandlerMap}, @@ -195,14 +195,18 @@ impl Module { } /// Register class to module. - pub fn add_class(&mut self, class: ClassEntity) { + pub fn add_class(&mut self, class: ClassEntity) -> StateClass { + let bind_class = class.bind_class(); self.class_entities .push(unsafe { transmute::, ClassEntity<()>>(class) }); + bind_class } /// Register interface to module. - pub fn add_interface(&mut self, interface: InterfaceEntity) { + pub fn add_interface(&mut self, interface: InterfaceEntity) -> Interface { + let bind_interface = interface.bind_interface(); self.interface_entities.push(interface); + bind_interface } /// Register constant to module. diff --git a/phper/src/objects.rs b/phper/src/objects.rs index 5f242447..15de8fd2 100644 --- a/phper/src/objects.rs +++ b/phper/src/objects.rs @@ -467,7 +467,7 @@ impl Debug for StateObj { } /// The object owned state, usually crated by -/// [StaticStateClass](crate::classes::StaticStateClass). +/// [StateClass](crate::classes::StateClass). pub struct StateObject { inner: *mut StateObj, } diff --git a/tests/integration/src/classes.rs b/tests/integration/src/classes.rs index d7c5a5d1..fd99680b 100644 --- a/tests/integration/src/classes.rs +++ b/tests/integration/src/classes.rs @@ -11,8 +11,7 @@ use phper::{ alloc::RefClone, classes::{ - array_access_class, iterator_class, ClassEntity, ClassEntry, InterfaceEntity, - StaticInterface, StaticStateClass, Visibility, + array_access_class, iterator_class, ClassEntity, ClassEntry, InterfaceEntity, Visibility, }, functions::Argument, modules::Module, @@ -66,8 +65,6 @@ fn integrate_a(module: &mut Module) { module.add_class(class); } -static FOO_CLASS: StaticStateClass = StaticStateClass::null(); - struct Foo { position: usize, array: HashMap, @@ -79,8 +76,6 @@ fn integrate_foo(module: &mut Module) { array: Default::default(), }); - class.bind(&FOO_CLASS); - class.implements(iterator_class); class.implements(array_access_class); @@ -148,13 +143,9 @@ fn integrate_foo(module: &mut Module) { module.add_class(class); } -static I_BAR_INTERFACE: StaticInterface = StaticInterface::null(); - fn integrate_i_bar(module: &mut Module) { let mut interface = InterfaceEntity::new(r"IntegrationTest\IBar"); - interface.bind(&I_BAR_INTERFACE); - interface.extends(|| array_access_class()); interface.extends(|| iterator_class());