Skip to content

Commit 45984fe

Browse files
committed
Add http client apis.
1 parent 0fc17e0 commit 45984fe

File tree

12 files changed

+137
-23
lines changed

12 files changed

+137
-23
lines changed

examples/hello/tests/php/test.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,6 @@
2626

2727
function assert_eq($left, $right) {
2828
if ($left !== $right) {
29-
throw new Exception(sprintf("left != right,\n left: %s,\n right: %s", var_export($left, true), var_export($right, true)));
29+
throw new AssertionError(sprintf("left != right,\n left: %s,\n right: %s", var_export($left, true), var_export($right, true)));
3030
}
3131
}

examples/http-client/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ crate-type = ["cdylib"]
1313

1414
[dependencies]
1515
anyhow = "1.0.40"
16+
bytes = "1.0.1"
1617
phper = { version = "0.2.0-alpha.2", path = "../../phper" }
1718
reqwest = { version = "0.11.3", features = ["blocking"] }
1819
thiserror = "1.0.24"

examples/http-client/src/client.rs

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,48 @@
1-
use crate::errors::HttpClientError;
1+
use crate::{
2+
errors::HttpClientError,
3+
response::{ReadiedResponse, RESPONSE_CLASS_NAME},
4+
};
25
use anyhow::Context;
3-
use phper::{classes::DynamicClass, functions::Argument};
4-
use reqwest::blocking::{Client, ClientBuilder};
5-
use std::time::Duration;
6+
use phper::{classes::DynamicClass, functions::Argument, objects::Object};
7+
use reqwest::{
8+
blocking::{Client, ClientBuilder},
9+
Response,
10+
};
11+
use std::{mem::MaybeUninit, time::Duration};
612

713
const HTTP_CLIENT_CLASS_NAME: &'static str = "HttpClient\\HttpClient";
814

915
pub fn make_client_class() -> DynamicClass<Client> {
10-
let mut http_client_class = DynamicClass::new_with_constructor(HTTP_CLIENT_CLASS_NAME, || {
16+
let mut class = DynamicClass::new_with_constructor(HTTP_CLIENT_CLASS_NAME, || {
1117
let client = ClientBuilder::new()
1218
.timeout(Duration::from_secs(15))
1319
.build()?;
1420
Ok::<_, HttpClientError>(client)
1521
});
1622

17-
http_client_class.add_method(
23+
class.add_method(
1824
"get",
1925
|this, arguments| {
2026
let url = arguments[0].as_string()?;
2127
let client = this.as_state();
22-
let response = client.get(url).send().unwrap();
23-
let body = response.text().unwrap();
24-
Ok::<_, phper::Error>(body)
28+
let response = client.get(url).send()?;
29+
30+
let readied_response = ReadiedResponse {
31+
status: response.status(),
32+
remote_addr: response.remote_addr(),
33+
headers: response.headers().clone(),
34+
body: response.bytes()?,
35+
};
36+
37+
let mut response_object =
38+
Object::<Option<ReadiedResponse>>::new_by_class_name(RESPONSE_CLASS_NAME)
39+
.map_err(phper::Error::ClassNotFound)?;
40+
*response_object.as_mut_state() = Some(readied_response);
41+
42+
Ok::<_, HttpClientError>(response_object)
2543
},
2644
vec![Argument::by_val("url")],
2745
);
2846

29-
http_client_class
47+
class
3048
}

examples/http-client/src/errors.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ const EXCEPTION_CLASS_NAME: &'static str = "HttpClient\\HttpClientException";
77

88
#[derive(thiserror::Error, Debug)]
99
pub enum HttpClientError {
10+
#[error(transparent)]
11+
Phper(#[from] phper::Error),
12+
1013
#[error(transparent)]
1114
Reqwest(#[from] reqwest::Error),
1215
}
@@ -18,6 +21,7 @@ impl Throwable for HttpClientError {
1821
}
1922

2023
pub fn make_exception_class() -> DynamicClass<()> {
21-
let exception_class = DynamicClass::new(EXCEPTION_CLASS_NAME);
24+
let mut exception_class = DynamicClass::new(EXCEPTION_CLASS_NAME);
25+
exception_class.extends("Exception");
2226
exception_class
2327
}

examples/http-client/src/lib.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
use crate::client::make_client_class;
1+
use crate::{
2+
client::make_client_class, errors::make_exception_class, response::make_response_class,
3+
};
24
use anyhow::Context;
35
use phper::{classes::DynamicClass, modules::Module, php_get_module};
46

57
pub mod client;
68
pub mod errors;
7-
pub mod request;
89
pub mod response;
910

1011
#[php_get_module]
@@ -16,6 +17,8 @@ pub fn get_module() -> Module {
1617
);
1718

1819
module.add_class(make_client_class());
20+
module.add_class(make_exception_class());
21+
module.add_class(make_response_class());
1922

2023
module
2124
}

examples/http-client/src/request.rs

Lines changed: 0 additions & 1 deletion
This file was deleted.

examples/http-client/src/response.rs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,44 @@
1+
use bytes::Bytes;
2+
use phper::classes::DynamicClass;
3+
use reqwest::{blocking::Response, header::HeaderMap, StatusCode};
4+
use std::{
5+
convert::Infallible,
6+
mem::{zeroed, MaybeUninit},
7+
net::SocketAddr,
8+
};
19

10+
pub const RESPONSE_CLASS_NAME: &'static str = "HttpClient\\Response";
11+
12+
pub struct ReadiedResponse {
13+
pub status: StatusCode,
14+
pub remote_addr: Option<SocketAddr>,
15+
pub headers: HeaderMap,
16+
pub body: Bytes,
17+
}
18+
19+
pub fn make_response_class() -> DynamicClass<Option<ReadiedResponse>> {
20+
let mut class = DynamicClass::new_with_constructor(RESPONSE_CLASS_NAME, || unsafe {
21+
Ok::<Option<ReadiedResponse>, Infallible>(None)
22+
});
23+
24+
class.add_method(
25+
"body",
26+
|this, arguments| {
27+
let readied_response = this.as_state().as_ref().unwrap();
28+
let body: &[u8] = readied_response.body.as_ref();
29+
body.to_vec()
30+
},
31+
vec![],
32+
);
33+
34+
class.add_method(
35+
"status",
36+
|this, arguments| {
37+
let readied_response = this.as_state().as_ref().unwrap();
38+
readied_response.status.as_u16() as i64
39+
},
40+
vec![],
41+
);
42+
43+
class
44+
}
Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,22 @@
11
<?php
22

33
use HttpClient\HttpClient;
4+
use HttpClient\HttpClientException;
45

56
ini_set("display_errors", "On");
67
ini_set("display_startup_errors", "On");
78
error_reporting(E_ALL);
89

910
$client = new HttpClient();
10-
$ip = $client->get("http://httpbin.org/ip");
11-
var_dump($ip);
11+
12+
$resp = $client->get("https://httpbin.org/ip");
13+
var_dump([
14+
"status" => $resp->status(),
15+
"body" => $resp->body(),
16+
]);
17+
18+
try {
19+
$client->get("file:///");
20+
throw new AssertionError("no throw exception");
21+
} catch (HttpClientException $e) {
22+
}

phper-test/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ pub fn test_php_scripts_with_condition(
8989
}
9090

9191
println!(
92-
"command: {} {}\nstdout: {}\nstderr: {}",
92+
"===== command =====\n{} {}\n\n===== stdout =====\n{}\n\n===== stderr =====\n{}\n",
9393
&context.php_bin,
9494
args.join(" "),
9595
stdout,

phper/src/classes.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,12 @@ impl<T: Default + Send + Sync + 'static> DynamicClass<T> {
6060
}
6161
}
6262

63+
impl<T: Send + Sync + 'static> DynamicClass<Option<T>> {
64+
pub fn new_with_none(class_name: impl ToString) -> Self {
65+
Self::new_with_constructor(class_name, || Ok::<_, Infallible>(None))
66+
}
67+
}
68+
6369
impl<T: Send + Sync + 'static> DynamicClass<T> {
6470
pub fn new_with_constructor<F, E>(class_name: impl ToString, state_constructor: F) -> Self
6571
where
@@ -127,12 +133,16 @@ impl<T: Send + Sync> Classifiable for DynamicClass<T> {
127133
}
128134
}
129135

136+
/// Wrapper of [crate::sys::zend_class_entry].
137+
///
138+
/// TODO Add generic type.
130139
#[repr(transparent)]
131140
pub struct ClassEntry {
132141
inner: zend_class_entry,
133142
}
134143

135144
impl ClassEntry {
145+
// TODO After added generic type, Check generic type is relate to class_name, otherwise return error.
136146
pub fn from_globals<'a>(class_name: impl AsRef<str>) -> Result<&'a Self, ClassNotFoundError> {
137147
let name = class_name.as_ref();
138148
let ptr: *mut Self = find_global_class_entry_ptr(name).cast();
@@ -153,6 +163,14 @@ impl ClassEntry {
153163
pub fn as_mut_ptr(&mut self) -> *mut zend_class_entry {
154164
&mut self.inner
155165
}
166+
167+
pub(crate) fn create_object<T>(&self) -> EBox<Object<T>> {
168+
unsafe {
169+
let f = self.inner.__bindgen_anon_2.create_object.unwrap();
170+
let object = f(self.as_ptr() as *mut _);
171+
EBox::from_raw(object.cast())
172+
}
173+
}
156174
}
157175

158176
fn find_global_class_entry_ptr(name: impl AsRef<str>) -> *mut zend_class_entry {

0 commit comments

Comments
 (0)