Skip to content

Commit 18087ec

Browse files
authored
Redesign the way to bind Class and Interface (#175)
1 parent b9358d7 commit 18087ec

File tree

13 files changed

+192
-188
lines changed

13 files changed

+192
-188
lines changed

examples/http-client/src/client.rs

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,22 +8,24 @@
88
// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
99
// See the Mulan PSL v2 for more details.
1010

11-
use crate::{errors::HttpClientError, request::REQUEST_BUILDER_CLASS};
11+
use crate::{errors::HttpClientError, request::RequestBuilderClass};
1212
use phper::{
1313
alloc::ToRefOwned,
14-
classes::{ClassEntity, StaticStateClass, Visibility},
14+
classes::{ClassEntity, StateClass, Visibility},
1515
functions::Argument,
1616
};
1717
use reqwest::blocking::{Client, ClientBuilder};
1818
use std::{convert::Infallible, mem::take, time::Duration};
1919

20+
pub type ClientBuilderClass = StateClass<ClientBuilder>;
21+
22+
pub type ClientClass = StateClass<Option<Client>>;
23+
2024
const HTTP_CLIENT_BUILDER_CLASS_NAME: &str = "HttpClient\\HttpClientBuilder";
2125

2226
const HTTP_CLIENT_CLASS_NAME: &str = "HttpClient\\HttpClient";
2327

24-
static HTTP_CLIENT_CLASS: StaticStateClass<Option<Client>> = StaticStateClass::null();
25-
26-
pub fn make_client_builder_class() -> ClassEntity<ClientBuilder> {
28+
pub fn make_client_builder_class(client_class: ClientClass) -> ClassEntity<ClientBuilder> {
2729
// `new_with_default_state_constructor` means initialize the state of
2830
// `ClientBuilder` as `Default::default`.
2931
let mut class = ClassEntity::new_with_default_state_constructor(HTTP_CLIENT_BUILDER_CLASS_NAME);
@@ -52,44 +54,45 @@ pub fn make_client_builder_class() -> ClassEntity<ClientBuilder> {
5254

5355
// Inner call the `ClientBuilder::build`, and wrap the result `Client` in
5456
// Object.
55-
class.add_method("build", Visibility::Public, |this, _arguments| {
57+
class.add_method("build", Visibility::Public, move |this, _arguments| {
5658
let state = take(this.as_mut_state());
5759
let client = ClientBuilder::build(state).map_err(HttpClientError::Reqwest)?;
58-
let mut object = HTTP_CLIENT_CLASS.init_object()?;
60+
let mut object = client_class.init_object()?;
5961
*object.as_mut_state() = Some(client);
6062
Ok::<_, phper::Error>(object)
6163
});
6264

6365
class
6466
}
6567

66-
pub fn make_client_class() -> ClassEntity<Option<Client>> {
68+
pub fn make_client_class(
69+
request_builder_class: RequestBuilderClass,
70+
) -> ClassEntity<Option<Client>> {
6771
let mut class =
6872
ClassEntity::<Option<Client>>::new_with_default_state_constructor(HTTP_CLIENT_CLASS_NAME);
6973

70-
class.bind(&HTTP_CLIENT_CLASS);
71-
7274
class.add_method("__construct", Visibility::Private, |_, _| {
7375
Ok::<_, Infallible>(())
7476
});
7577

78+
let request_build_class_ = request_builder_class.clone();
7679
class
77-
.add_method("get", Visibility::Public, |this, arguments| {
80+
.add_method("get", Visibility::Public, move |this, arguments| {
7881
let url = arguments[0].expect_z_str()?.to_str().unwrap();
7982
let client = this.as_state().as_ref().unwrap();
8083
let request_builder = client.get(url);
81-
let mut object = REQUEST_BUILDER_CLASS.init_object()?;
84+
let mut object = request_build_class_.init_object()?;
8285
*object.as_mut_state() = Some(request_builder);
8386
Ok::<_, phper::Error>(object)
8487
})
8588
.argument(Argument::by_val("url"));
8689

8790
class
88-
.add_method("post", Visibility::Public, |this, arguments| {
91+
.add_method("post", Visibility::Public, move |this, arguments| {
8992
let url = arguments[0].expect_z_str()?.to_str().unwrap();
9093
let client = this.as_state().as_ref().unwrap();
9194
let request_builder = client.post(url);
92-
let mut object = REQUEST_BUILDER_CLASS.init_object()?;
95+
let mut object = request_builder_class.init_object()?;
9396
*object.as_mut_state() = Some(request_builder);
9497
Ok::<_, phper::Error>(object)
9598
})

examples/http-client/src/lib.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,10 @@ pub fn get_module() -> Module {
3030
);
3131

3232
module.add_class(make_exception_class());
33-
module.add_class(make_client_class());
34-
module.add_class(make_client_builder_class());
35-
module.add_class(make_request_builder_class());
36-
module.add_class(make_response_class());
33+
let response_class = module.add_class(make_response_class());
34+
let request_builder_class = module.add_class(make_request_builder_class(response_class));
35+
let client_class = module.add_class(make_client_class(request_builder_class));
36+
module.add_class(make_client_builder_class(client_class));
3737

3838
module
3939
}

examples/http-client/src/request.rs

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,31 +8,30 @@
88
// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
99
// See the Mulan PSL v2 for more details.
1010

11-
use crate::{errors::HttpClientError, response::RESPONSE_CLASS};
12-
use phper::classes::{ClassEntity, StaticStateClass, Visibility};
11+
use crate::{errors::HttpClientError, response::ResponseClass};
12+
use phper::classes::{ClassEntity, StateClass, Visibility};
1313
use reqwest::blocking::RequestBuilder;
1414
use std::{convert::Infallible, mem::take};
1515

16-
pub const REQUEST_BUILDER_CLASS_NAME: &str = "HttpClient\\RequestBuilder";
16+
pub type RequestBuilderClass = StateClass<Option<RequestBuilder>>;
1717

18-
pub static REQUEST_BUILDER_CLASS: StaticStateClass<Option<RequestBuilder>> =
19-
StaticStateClass::null();
18+
pub const REQUEST_BUILDER_CLASS_NAME: &str = "HttpClient\\RequestBuilder";
2019

21-
pub fn make_request_builder_class() -> ClassEntity<Option<RequestBuilder>> {
20+
pub fn make_request_builder_class(
21+
response_class: ResponseClass,
22+
) -> ClassEntity<Option<RequestBuilder>> {
2223
let mut class = ClassEntity::<Option<RequestBuilder>>::new_with_default_state_constructor(
2324
REQUEST_BUILDER_CLASS_NAME,
2425
);
2526

26-
class.bind(&REQUEST_BUILDER_CLASS);
27-
2827
class.add_method("__construct", Visibility::Private, |_, _| {
2928
Ok::<_, Infallible>(())
3029
});
3130

32-
class.add_method("send", Visibility::Public, |this, _arguments| {
31+
class.add_method("send", Visibility::Public, move |this, _arguments| {
3332
let state = take(this.as_mut_state());
3433
let response = state.unwrap().send().map_err(HttpClientError::Reqwest)?;
35-
let mut object = RESPONSE_CLASS.new_object([])?;
34+
let mut object = response_class.new_object([])?;
3635
*object.as_mut_state() = Some(response);
3736
Ok::<_, phper::Error>(object)
3837
});

examples/http-client/src/response.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,22 +11,20 @@
1111
use crate::errors::HttpClientError;
1212
use phper::{
1313
arrays::{InsertKey, ZArray},
14-
classes::{ClassEntity, StaticStateClass, Visibility},
14+
classes::{ClassEntity, StateClass, Visibility},
1515
values::ZVal,
1616
};
1717
use reqwest::blocking::Response;
1818
use std::mem::take;
1919

20-
pub const RESPONSE_CLASS_NAME: &str = "HttpClient\\Response";
20+
pub type ResponseClass = StateClass<Option<Response>>;
2121

22-
pub static RESPONSE_CLASS: StaticStateClass<Option<Response>> = StaticStateClass::null();
22+
pub const RESPONSE_CLASS_NAME: &str = "HttpClient\\Response";
2323

2424
pub fn make_response_class() -> ClassEntity<Option<Response>> {
2525
let mut class =
2626
ClassEntity::<Option<Response>>::new_with_default_state_constructor(RESPONSE_CLASS_NAME);
2727

28-
class.bind(&RESPONSE_CLASS);
29-
3028
class.add_method("body", Visibility::Public, |this, _arguments| {
3129
let response = take(this.as_mut_state());
3230
let response = response.ok_or(HttpClientError::ResponseHadRead)?;

examples/http-server/src/lib.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,9 @@ pub fn get_module() -> Module {
3030

3131
// Register classes.
3232
module.add_class(make_exception_class());
33-
module.add_class(make_server_class());
34-
module.add_class(make_request_class());
35-
module.add_class(make_response_class());
33+
let request_class = module.add_class(make_request_class());
34+
let response_class = module.add_class(make_response_class());
35+
module.add_class(make_server_class(request_class, response_class));
3636

3737
module
3838
}

examples/http-server/src/request.rs

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,22 +10,18 @@
1010

1111
use phper::{
1212
arrays::ZArray,
13-
classes::{ClassEntity, StaticStateClass, Visibility},
14-
objects::StateObject,
13+
classes::{ClassEntity, StateClass, Visibility},
1514
};
1615
use std::convert::Infallible;
1716

18-
pub const HTTP_REQUEST_CLASS_NAME: &str = "HttpServer\\HttpRequest";
17+
pub type RequestClass = StateClass<()>;
1918

20-
pub static HTTP_REQUEST_CLASS: StaticStateClass<()> = StaticStateClass::null();
19+
pub const HTTP_REQUEST_CLASS_NAME: &str = "HttpServer\\HttpRequest";
2120

2221
/// Register the class `HttpServer\HttpRequest` by `ClassEntity`.
2322
pub fn make_request_class() -> ClassEntity<()> {
2423
let mut class = ClassEntity::new(HTTP_REQUEST_CLASS_NAME);
2524

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

@@ -41,8 +37,3 @@ pub fn make_request_class() -> ClassEntity<()> {
4137

4238
class
4339
}
44-
45-
/// Instantiate the object with class `HttpServer\HttpRequest`.
46-
pub fn new_request_object() -> phper::Result<StateObject<()>> {
47-
HTTP_REQUEST_CLASS.new_object([])
48-
}

examples/http-server/src/response.rs

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,23 +14,19 @@ use axum::{
1414
http::{HeaderName, HeaderValue, Response},
1515
};
1616
use phper::{
17-
classes::{ClassEntity, StaticStateClass, Visibility},
17+
classes::{ClassEntity, StateClass, Visibility},
1818
functions::Argument,
19-
objects::StateObject,
2019
};
2120

22-
pub const HTTP_RESPONSE_CLASS_NAME: &str = "HttpServer\\HttpResponse";
21+
pub type ResponseClass = StateClass<Response<Body>>;
2322

24-
pub static HTTP_RESPONSE_CLASS: StaticStateClass<Response<Body>> = StaticStateClass::null();
23+
pub const HTTP_RESPONSE_CLASS_NAME: &str = "HttpServer\\HttpResponse";
2524

2625
/// Register the class `HttpServer\HttpResponse` by `ClassEntity`, with the
2726
/// inner state `Response<Body>`.
2827
pub fn make_response_class() -> ClassEntity<Response<Body>> {
2928
let mut class = ClassEntity::new_with_default_state_constructor(HTTP_RESPONSE_CLASS_NAME);
3029

31-
// The state class will be initialized after class registered.
32-
class.bind(&HTTP_RESPONSE_CLASS);
33-
3430
// Register the header method with public visibility, accept `name` and `value`
3531
// parameters.
3632
class
@@ -62,8 +58,3 @@ pub fn make_response_class() -> ClassEntity<Response<Body>> {
6258

6359
class
6460
}
65-
66-
/// Instantiate the object with class `HttpServer\HttpResponse`.
67-
pub fn new_response_object() -> phper::Result<StateObject<Response<Body>>> {
68-
HTTP_RESPONSE_CLASS.new_object([])
69-
}

examples/http-server/src/server.rs

Lines changed: 43 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
99
// See the Mulan PSL v2 for more details.
1010

11-
use crate::{errors::HttpServerError, request::new_request_object, response::new_response_object};
11+
use crate::{errors::HttpServerError, request::RequestClass, response::ResponseClass};
1212
use axum::{
1313
body::{self, Body},
1414
http::{Request, Response, StatusCode},
@@ -31,10 +31,14 @@ const HTTP_SERVER_CLASS_NAME: &str = "HttpServer\\HttpServer";
3131
thread_local! {
3232
// The map store the on request handlers, indexed by `HttpServer\HttpServer` object id.
3333
static ON_REQUEST_HANDLERS: RefCell<HashMap<u32, ZVal>> = Default::default();
34+
35+
static CLASSES_MAP: RefCell<HashMap<u32, (RequestClass, ResponseClass)>> = Default::default();
3436
}
3537

3638
/// Register the class `HttpServer\HttpServer` by `ClassEntity`.
37-
pub fn make_server_class() -> ClassEntity<()> {
39+
pub fn make_server_class(
40+
request_class: RequestClass, response_class: ResponseClass,
41+
) -> ClassEntity<()> {
3842
let mut class = ClassEntity::new(HTTP_SERVER_CLASS_NAME);
3943

4044
// Register the server host field with public visibility.
@@ -46,13 +50,21 @@ pub fn make_server_class() -> ClassEntity<()> {
4650
// Register the constructor method with public visibility, accept host and port
4751
// arguments, initialize the host and port member properties.
4852
class
49-
.add_method("__construct", Visibility::Public, |this, arguments| {
53+
.add_method("__construct", Visibility::Public, move |this, arguments| {
5054
let host = arguments[0].expect_z_str()?;
5155
let port = arguments[1].expect_long()?;
5256

5357
this.set_property("host", host.to_owned());
5458
this.set_property("port", port);
5559

60+
let (request_class, response_class) = (request_class.clone(), response_class.clone());
61+
62+
CLASSES_MAP.with(move |classes_map| {
63+
classes_map
64+
.borrow_mut()
65+
.insert(this.handle(), (request_class, response_class));
66+
});
67+
5668
Ok::<_, phper::Error>(())
5769
})
5870
.arguments([Argument::by_val("host"), Argument::by_val("port")]);
@@ -98,40 +110,44 @@ pub fn make_server_class() -> ClassEntity<()> {
98110
.await
99111
.map_err(HttpServerError::new)?;
100112

101-
// Create PHP `HttpServer\HttpRequest` object.
102-
let mut request = new_request_object()?;
113+
CLASSES_MAP.with(|classes_map| {
114+
let (request_class, response_class) = &classes_map.borrow()[&handle];
115+
116+
// Create PHP `HttpServer\HttpRequest` object.
117+
let mut request = request_class.new_object([])?;
103118

104-
// Inject headers from Rust request object to PHP request object.
105-
let request_headers =
106-
request.get_mut_property("headers").expect_mut_z_arr()?;
107-
for (key, value) in parts.headers {
108-
if let Some(key) = key {
109-
request_headers.insert(key.as_str(), value.as_bytes());
119+
// Inject headers from Rust request object to PHP request object.
120+
let request_headers =
121+
request.get_mut_property("headers").expect_mut_z_arr()?;
122+
for (key, value) in parts.headers {
123+
if let Some(key) = key {
124+
request_headers.insert(key.as_str(), value.as_bytes());
125+
}
110126
}
111-
}
112127

113-
// Inject body content from Rust request object to PHP request object.
114-
request.set_property("data", &*body);
128+
// Inject body content from Rust request object to PHP request object.
129+
request.set_property("data", &*body);
115130

116-
let request_val = ZVal::from(request);
131+
let request_val = ZVal::from(request);
117132

118-
// Create PHP `HttpServer\HttpResponse` object.
119-
let mut response = new_response_object()?;
133+
// Create PHP `HttpServer\HttpResponse` object.
134+
let mut response = response_class.new_object([])?;
120135

121-
let response_val = ZVal::from(response.to_ref_owned());
136+
let response_val = ZVal::from(response.to_ref_owned());
122137

123-
ON_REQUEST_HANDLERS.with(|handlers| {
124-
// Get the on request handlers by object id.
125-
let mut handlers = handlers.borrow_mut();
126-
let handler = handlers.get_mut(&handle).unwrap();
138+
ON_REQUEST_HANDLERS.with(|handlers| {
139+
// Get the on request handlers by object id.
140+
let mut handlers = handlers.borrow_mut();
141+
let handler = handlers.get_mut(&handle).unwrap();
127142

128-
// Call the PHP on request handler.
129-
handler.call([request_val, response_val])?;
143+
// Call the PHP on request handler.
144+
handler.call([request_val, response_val])?;
130145

131-
// Get the inner state.
132-
let response = response.into_state().unwrap();
146+
// Get the inner state.
147+
let response = response.into_state().unwrap();
133148

134-
Ok::<Response<Body>, phper::Error>(response)
149+
Ok::<Response<Body>, phper::Error>(response)
150+
})
135151
})
136152
};
137153
match fut.await {

0 commit comments

Comments
 (0)