Skip to content

Commit bcf4704

Browse files
authored
Merge pull request #14 from jmjoy/0.2.0-alpha.3-dev
2 parents 258ea35 + d56489f commit bcf4704

File tree

40 files changed

+755
-386
lines changed

40 files changed

+755
-386
lines changed

.github/workflows/ci.yml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,11 @@ on:
77
branches: [ "**" ]
88

99
env:
10-
# RUST_LOG: debug
10+
RUST_LOG: debug
1111
CARGO_TERM_COLOR: always
12-
RUST_BACKTRACE: 1
12+
RUST_BACKTRACE: "1"
1313
RUSTFLAGS: "-D warnings"
14+
LLVM_CONFIG_PATH: llvm-config-10
1415

1516
jobs:
1617
ci:
@@ -33,6 +34,9 @@ jobs:
3334
- name: Checkout
3435
uses: actions/checkout@v2
3536

37+
- name: Install libclang
38+
run: sudo apt-get install -y llvm-10-dev libclang-10-dev
39+
3640
- name: Setup PHP
3741
uses: shivammathur/setup-php@v2
3842
with:

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@
33
Cargo.lock
44
/.cargo
55
/vendor
6-
/core
6+
core

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ A library that allows us to write PHP extensions using pure Rust and using safe
5353

5454
```bash
5555
# If you are using debian like linux system:
56-
sudo apt install libclang-10-dev php-cli
56+
sudo apt install llvm-10-dev libclang-10-dev php-cli
5757
```
5858

5959
2. Create you cargo project, suppose your application is called myapp.

examples/hello/Cargo.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@ license = "Unlicense"
1212
crate-type = ["cdylib"]
1313

1414
[dependencies]
15-
phper = { version = "0.2.0-alpha.2", path = "../../phper" }
15+
phper = { version = "0.2.0-alpha.3", path = "../../phper" }
1616

1717
[dev-dependencies]
18-
phper-test = { version = "0.2.0-alpha.2", path = "../../phper-test" }
18+
phper-test = { version = "0.2.0-alpha.3", path = "../../phper-test" }
1919

2020
[build-dependencies]
21-
phper-build = { version = "0.2.0-alpha.2", path = "../../phper-build" }
21+
phper-build = { version = "0.2.0-alpha.3", path = "../../phper-build" }

examples/hello/src/lib.rs

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
use phper::{
22
arrays::Array,
3-
classes::DynamicClass,
3+
classes::{DynamicClass, Visibility},
44
functions::Argument,
5-
ini::Policy,
5+
ini::{Ini, Policy},
66
modules::{Module, ModuleArgs},
77
objects::Object,
88
php_get_module,
@@ -31,10 +31,10 @@ pub fn get_module() -> Module {
3131
);
3232

3333
// register module ini
34-
module.add_bool_ini("hello.enable", false, Policy::All);
35-
module.add_long_ini("hello.num", 100, Policy::All);
36-
module.add_real_ini("hello.ratio", 1.5, Policy::All);
37-
module.add_str_ini("hello.description", "hello world.", Policy::All);
34+
Ini::add("hello.enable", false, Policy::All);
35+
Ini::add("hello.num", 100, Policy::All);
36+
Ini::add("hello.ratio", 1.5, Policy::All);
37+
Ini::add("hello.description", "hello world.".to_owned(), Policy::All);
3838

3939
// register hook functions
4040
module.on_module_init(module_init);
@@ -50,10 +50,10 @@ pub fn get_module() -> Module {
5050
|_: &mut [Val]| {
5151
let mut arr = Array::new();
5252

53-
let hello_enable = Val::new(Module::get_bool_ini("hello.enable"));
53+
let hello_enable = Val::new(Ini::get::<bool>("hello.enable"));
5454
arr.insert("hello.enable", hello_enable);
5555

56-
let hello_description = Val::new(Module::get_str_ini("hello.description"));
56+
let hello_description = Val::new(Ini::get::<String>("hello.description"));
5757
arr.insert("hello.description", hello_description);
5858

5959
arr
@@ -63,17 +63,19 @@ pub fn get_module() -> Module {
6363

6464
// register classes
6565
let mut foo_class = DynamicClass::new("FooClass");
66-
foo_class.add_property("foo", "100".to_string());
66+
foo_class.add_property("foo", Visibility::Private, 100);
6767
foo_class.add_method(
6868
"getFoo",
69-
|this: &mut Object<()>, _: &mut [Val]| -> phper::Result<Val> {
69+
Visibility::Public,
70+
|this: &mut Object<()>, _: &mut [Val]| {
7071
let prop = this.get_property("foo");
71-
Ok(Val::new(prop.as_string_value()?))
72+
Ok::<_, phper::Error>(prop.as_string_value()?)
7273
},
7374
vec![],
7475
);
7576
foo_class.add_method(
7677
"setFoo",
78+
Visibility::Public,
7779
|this: &mut Object<()>, arguments: &mut [Val]| -> phper::Result<()> {
7880
this.set_property("foo", Val::new(arguments[0].as_string_value()?));
7981
Ok(())

examples/hello/tests/php/test.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,7 @@
1818
]);
1919

2020
$foo = new FooClass();
21-
// TODO change '100' to 100.
22-
assert_eq($foo->getFoo(), '100');
21+
assert_eq($foo->getFoo(), "100");
2322

2423
$foo->setFoo("Hello");
2524
assert_eq($foo->getFoo(), "Hello");

examples/http-client/Cargo.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ crate-type = ["cdylib"]
1515
anyhow = "1.0.40"
1616
bytes = "1.0.1"
1717
indexmap = "1.6.2"
18-
phper = { version = "0.2.0-alpha.2", path = "../../phper" }
19-
reqwest = { version = "0.11.3", features = ["blocking"] }
18+
phper = { version = "0.2.0-alpha.3", path = "../../phper" }
19+
reqwest = { version = "0.11.3", features = ["blocking", "cookies"] }
2020
thiserror = "1.0.24"
2121

2222
[dev-dependencies]
23-
phper-test = { version = "0.2.0-alpha.2", path = "../../phper-test" }
23+
phper-test = { version = "0.2.0-alpha.3", path = "../../phper-test" }

examples/http-client/src/client.rs

Lines changed: 88 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,103 @@
11
use crate::{
2-
errors::HttpClientError,
3-
response::{ReadiedResponse, RESPONSE_CLASS_NAME},
2+
errors::HttpClientError, replace_and_get, replace_and_set, request::REQUEST_BUILDER_CLASS_NAME,
43
};
5-
6-
use phper::{classes::DynamicClass, functions::Argument, objects::Object};
7-
use reqwest::blocking::{Client, ClientBuilder};
4+
use phper::{
5+
classes::{ClassEntry, DynamicClass, Visibility},
6+
functions::Argument,
7+
objects::Object,
8+
};
9+
use reqwest::blocking::{Client, ClientBuilder, RequestBuilder};
810
use std::time::Duration;
911

12+
const HTTP_CLIENT_BUILDER_CLASS_NAME: &'static str = "HttpClient\\HttpClientBuilder";
1013
const HTTP_CLIENT_CLASS_NAME: &'static str = "HttpClient\\HttpClient";
1114

12-
pub fn make_client_class() -> DynamicClass<Client> {
13-
let mut class = DynamicClass::new_with_constructor(HTTP_CLIENT_CLASS_NAME, || {
14-
let client = ClientBuilder::new()
15-
.timeout(Duration::from_secs(15))
16-
.build()?;
17-
Ok::<_, HttpClientError>(client)
18-
});
15+
pub fn make_client_builder_class() -> DynamicClass<ClientBuilder> {
16+
let mut class = DynamicClass::new_with_default(HTTP_CLIENT_BUILDER_CLASS_NAME);
1917

2018
class.add_method(
21-
"get",
19+
"timeout",
20+
Visibility::Public,
2221
|this, arguments| {
23-
let url = arguments[0].as_string()?;
24-
let client = this.as_state();
25-
let response = client.get(url).send()?;
22+
let ms = arguments[0].as_long()?;
23+
let state = this.as_mut_state();
24+
replace_and_set(state, ClientBuilder::new(), |builder| {
25+
builder.timeout(Duration::from_millis(ms as u64))
26+
});
27+
Ok::<_, HttpClientError>(())
28+
},
29+
vec![Argument::by_val("ms")],
30+
);
31+
32+
class.add_method(
33+
"cookie_store",
34+
Visibility::Public,
35+
|this, arguments| {
36+
let enable = arguments[0].as_bool()?;
37+
let state = this.as_mut_state();
38+
replace_and_set(state, ClientBuilder::new(), |builder| {
39+
builder.cookie_store(enable)
40+
});
41+
Ok::<_, HttpClientError>(())
42+
},
43+
vec![Argument::by_val("enable")],
44+
);
2645

27-
let readied_response = ReadiedResponse {
28-
status: response.status(),
29-
remote_addr: response.remote_addr(),
30-
headers: response.headers().clone(),
31-
body: response.bytes()?,
32-
};
46+
class.add_method(
47+
"build",
48+
Visibility::Public,
49+
|this, _arguments| {
50+
let state = this.as_mut_state();
51+
let client = replace_and_get(state, ClientBuilder::new(), ClientBuilder::build)?;
52+
let mut object =
53+
ClassEntry::<Option<Client>>::from_globals(HTTP_CLIENT_CLASS_NAME)?.new_object();
54+
*object.as_mut_state() = Some(client);
55+
Ok::<_, HttpClientError>(object)
56+
},
57+
vec![],
58+
);
3359

34-
let mut response_object =
35-
Object::<Option<ReadiedResponse>>::new_by_class_name(RESPONSE_CLASS_NAME)?;
36-
*response_object.as_mut_state() = Some(readied_response);
60+
class
61+
}
3762

38-
Ok::<_, HttpClientError>(response_object)
63+
pub fn make_client_class() -> DynamicClass<Option<Client>> {
64+
let mut class = DynamicClass::new_with_none(HTTP_CLIENT_CLASS_NAME);
65+
66+
class.add_method(
67+
"__construct",
68+
Visibility::Private,
69+
|_: &mut Object<Option<Client>>, _| {},
70+
vec![],
71+
);
72+
73+
class.add_method(
74+
"get",
75+
Visibility::Public,
76+
|this, arguments| {
77+
let url = arguments[0].as_string()?;
78+
let client = this.as_state().as_ref().unwrap();
79+
let request_builder = client.get(url);
80+
let mut object =
81+
ClassEntry::<Option<RequestBuilder>>::from_globals(REQUEST_BUILDER_CLASS_NAME)?
82+
.new_object();
83+
*object.as_mut_state() = Some(request_builder);
84+
Ok::<_, HttpClientError>(object)
85+
},
86+
vec![Argument::by_val("url")],
87+
);
88+
89+
class.add_method(
90+
"post",
91+
Visibility::Public,
92+
|this, arguments| {
93+
let url = arguments[0].as_string()?;
94+
let client = this.as_state().as_ref().unwrap();
95+
let request_builder = client.post(url);
96+
let mut object =
97+
ClassEntry::<Option<RequestBuilder>>::from_globals(REQUEST_BUILDER_CLASS_NAME)?
98+
.new_object();
99+
*object.as_mut_state() = Some(request_builder);
100+
Ok::<_, HttpClientError>(object)
39101
},
40102
vec![Argument::by_val("url")],
41103
);

examples/http-client/src/errors.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,20 @@ pub enum HttpClientError {
1212

1313
#[error(transparent)]
1414
Reqwest(#[from] reqwest::Error),
15+
16+
#[error("should call '{method_name}()' before call 'body()'")]
17+
ResponseAfterRead { method_name: String },
18+
19+
#[error("should not call 'body()' multi time")]
20+
ResponseHadRead,
1521
}
1622

1723
impl Throwable for HttpClientError {
1824
fn class_entry(&self) -> &StatelessClassEntry {
19-
ClassEntry::from_globals(EXCEPTION_CLASS_NAME).unwrap()
25+
match self {
26+
HttpClientError::Phper(e) => e.class_entry(),
27+
_ => ClassEntry::from_globals(EXCEPTION_CLASS_NAME).unwrap(),
28+
}
2029
}
2130
}
2231

examples/http-client/src/lib.rs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
11
use crate::{
2-
client::make_client_class, errors::make_exception_class, response::make_response_class,
2+
client::{make_client_builder_class, make_client_class},
3+
errors::make_exception_class,
4+
request::make_request_builder_class,
5+
response::make_response_class,
36
};
47
use phper::{modules::Module, php_get_module};
8+
use std::mem::replace;
59

610
pub mod client;
711
pub mod errors;
12+
pub mod request;
813
pub mod response;
914

1015
#[php_get_module]
@@ -15,9 +20,20 @@ pub fn get_module() -> Module {
1520
env!("CARGO_PKG_AUTHORS"),
1621
);
1722

18-
module.add_class(make_client_class());
1923
module.add_class(make_exception_class());
24+
module.add_class(make_client_class());
25+
module.add_class(make_client_builder_class());
26+
module.add_class(make_request_builder_class());
2027
module.add_class(make_response_class());
2128

2229
module
2330
}
31+
32+
fn replace_and_set<T>(t: &mut T, init: T, f: impl FnOnce(T) -> T) {
33+
let x = f(replace(t, init));
34+
let _ = replace(t, x);
35+
}
36+
37+
fn replace_and_get<T, R>(t: &mut T, init: T, f: impl FnOnce(T) -> R) -> R {
38+
f(replace(t, init))
39+
}

0 commit comments

Comments
 (0)