Skip to content

Add crate phper-doc for documentation purpose. #72

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Dec 3, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ members = [
"phper-macros",
"phper-sys",
"phper-test",
"phper-doc",

# internal
"examples/complex",
Expand Down
84 changes: 6 additions & 78 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,16 @@

The framework that allows us to write PHP extensions using pure and safe Rust whenever possible.

## Document & Tutorial

- Document: <https://docs.rs/phper>
- Tutorial: <https://docs.rs/phper-doc/>

## Requirement

### Necessary

- **rust** 1.56 or later
- **rust** 1.65 or later
- **libclang** 9.0 or later
- **php** 7.0 or later

Expand Down Expand Up @@ -43,83 +48,6 @@ The framework that allows us to write PHP extensions using pure and safe Rust wh
- [x] disable
- [ ] enable

## Usage

1. Make sure `libclang` and `php` is installed.

```bash
# If you are using debian like linux system:
sudo apt install llvm-10-dev libclang-10-dev php-cli
```

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

```bash
cargo new myapp
```

1. Add the dependencies and metadata to you Cargo project.

```toml
[lib]
crate-type = ["cdylib"]

[dependencies]
phper = "<LATEST VERSION>"
```

1. Create the `build.rs` ( Adapting MacOS ).

```rust,no_run
fn main() {
#[cfg(target_os = "macos")]
{
println!("cargo:rustc-link-arg=-undefined");
println!("cargo:rustc-link-arg=dynamic_lookup");
}
}
```

1. Write you owned extension logic in `lib.rs`.

```rust
use phper::{php_get_module, modules::Module};

#[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"),
);

// ...

module
}
```

1. Build and install, if your php isn't installed globally, you should specify the path of `php-config`.

```bash
# Optional, specify if php isn't installed globally.
# export PHP_CONFIG=<Your path of php-config>

# Build libmyapp.so.
cargo build --release

# Install to php extension path.
cp target/release/libmyapp.so `${PHP_CONFIG:=php-config} --extension-dir`
```

1. Edit your `php.ini`, add the below line.

```ini
extension = myapp
```

1. Enjoy.

## Examples

See [examples](https://github.com/phper-framework/phper/tree/master/examples).
Expand Down
3 changes: 0 additions & 3 deletions examples/http-client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,6 @@ license = { workspace = true }
crate-type = ["lib", "cdylib"]

[dependencies]
anyhow = "1.0.66"
bytes = "1.3.0"
indexmap = "1.9.2"
phper = { version = "0.6.0", path = "../../phper" }
reqwest = { version = "0.11.13", features = ["blocking", "cookies"] }
thiserror = "1.0.37"
Expand Down
34 changes: 18 additions & 16 deletions examples/http-client/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,57 +8,59 @@
// 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_NAME,
utils::{replace_and_get, replace_and_set},
};
use crate::{errors::HttpClientError, request::REQUEST_BUILDER_CLASS_NAME};
use phper::{
alloc::ToRefOwned,
classes::{ClassEntry, StatefulClass, Visibility},
functions::Argument,
};
use reqwest::blocking::{Client, ClientBuilder};
use std::time::Duration;
use std::{mem::take, time::Duration};

const HTTP_CLIENT_BUILDER_CLASS_NAME: &str = "HttpClient\\HttpClientBuilder";
const HTTP_CLIENT_CLASS_NAME: &str = "HttpClient\\HttpClient";

pub fn make_client_builder_class() -> StatefulClass<ClientBuilder> {
// `new_with_default_state` means initialize the state of `ClientBuilder` as
// `Default::default`.
let mut class = StatefulClass::new_with_default_state(HTTP_CLIENT_BUILDER_CLASS_NAME);

// Inner call the `ClientBuilder::timeout`.
class.add_method(
"timeout",
Visibility::Public,
|this, arguments| {
let ms = arguments[0].expect_long()?;
let state: &mut ClientBuilder = this.as_mut_state();
replace_and_set(state, |builder| {
builder.timeout(Duration::from_millis(ms as u64))
});
let state = this.as_mut_state();
let builder: ClientBuilder = take(state);
*state = builder.timeout(Duration::from_millis(ms as u64));
Ok::<_, HttpClientError>(this.to_ref_owned())
},
vec![Argument::by_val("ms")],
);

// Inner call the `ClientBuilder::cookie_store`.
class.add_method(
"cookie_store",
Visibility::Public,
|this, arguments| {
let enable = arguments[0].expect_bool()?;
let state: &mut ClientBuilder = this.as_mut_state();
replace_and_set(state, |builder| builder.cookie_store(enable));
let state = this.as_mut_state();
let builder: ClientBuilder = take(state);
*state = builder.cookie_store(enable);
Ok::<_, HttpClientError>(this.to_ref_owned())
},
vec![Argument::by_val("enable")],
);

// Inner call the `ClientBuilder::build`, and wrap the result `Client` in
// Object.
class.add_method(
"build",
Visibility::Public,
|this, _arguments| {
let state = this.as_mut_state();
let client = replace_and_get(state, ClientBuilder::build)?;
let state = take(this.as_mut_state());
let client = ClientBuilder::build(state)?;
let mut object = ClassEntry::from_globals(HTTP_CLIENT_CLASS_NAME)?.init_object()?;
unsafe {
*object.as_mut_state() = Some(client);
Expand All @@ -80,7 +82,7 @@ pub fn make_client_class() -> StatefulClass<Option<Client>> {
"get",
Visibility::Public,
|this, arguments| {
let url = arguments[0].as_z_str().unwrap().to_str().unwrap();
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 = ClassEntry::from_globals(REQUEST_BUILDER_CLASS_NAME)?.init_object()?;
Expand All @@ -96,7 +98,7 @@ pub fn make_client_class() -> StatefulClass<Option<Client>> {
"post",
Visibility::Public,
|this, arguments| {
let url = arguments[0].as_z_str().unwrap().to_str().unwrap();
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 = ClassEntry::from_globals(REQUEST_BUILDER_CLASS_NAME)?.init_object()?;
Expand Down
5 changes: 5 additions & 0 deletions examples/http-client/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,15 @@

use phper::classes::{ClassEntry, StatefulClass};

/// The exception class name of extension.
const EXCEPTION_CLASS_NAME: &str = "HttpClient\\HttpClientException";

/// The struct implemented `phper::Throwable` will throw php Exception
/// when return as `Err(e)` in extension functions.
#[derive(Debug, thiserror::Error, phper::Throwable)]
#[throwable_class(EXCEPTION_CLASS_NAME)]
pub enum HttpClientError {
/// Generally, implement `From` for `phper::Error`.
#[error(transparent)]
#[throwable(transparent)]
Phper(#[from] phper::Error),
Expand All @@ -31,6 +35,7 @@ pub enum HttpClientError {

pub fn make_exception_class() -> StatefulClass<()> {
let mut exception_class = StatefulClass::new(EXCEPTION_CLASS_NAME);
// The `extends` is same as the PHP class `extends`.
exception_class.extends("Exception");
exception_class
}
1 change: 0 additions & 1 deletion examples/http-client/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ pub mod client;
pub mod errors;
pub mod request;
pub mod response;
pub mod utils;

#[php_get_module]
pub fn get_module() -> Module {
Expand Down
7 changes: 4 additions & 3 deletions examples/http-client/src/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@
// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
// See the Mulan PSL v2 for more details.

use crate::{errors::HttpClientError, response::RESPONSE_CLASS_NAME, utils::replace_and_get};
use crate::{errors::HttpClientError, response::RESPONSE_CLASS_NAME};
use phper::classes::{ClassEntry, StatefulClass, Visibility};
use reqwest::blocking::RequestBuilder;
use std::mem::take;

pub const REQUEST_BUILDER_CLASS_NAME: &str = "HttpClient\\RequestBuilder";

Expand All @@ -24,8 +25,8 @@ pub fn make_request_builder_class() -> StatefulClass<Option<RequestBuilder>> {
"send",
Visibility::Public,
|this, _arguments| {
let state = this.as_mut_state();
let response = replace_and_get(state, |builder| builder.unwrap().send())?;
let state = take(this.as_mut_state());
let response = state.unwrap().send()?;
let mut object = ClassEntry::from_globals(RESPONSE_CLASS_NAME)?.new_object([])?;
unsafe {
*object.as_mut_state() = Some(response);
Expand Down
13 changes: 6 additions & 7 deletions examples/http-client/src/response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@
// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
// See the Mulan PSL v2 for more details.

use crate::{errors::HttpClientError, utils::replace_and_get};
use crate::errors::HttpClientError;
use phper::{
arrays::{InsertKey, ZArray},
classes::{StatefulClass, Visibility},
values::ZVal,
};
use reqwest::blocking::Response;
use std::mem::take;

pub const RESPONSE_CLASS_NAME: &str = "HttpClient\\Response";

Expand All @@ -25,12 +26,10 @@ pub fn make_response_class() -> StatefulClass<Option<Response>> {
"body",
Visibility::Public,
|this, _arguments| {
let response = this.as_mut_state();
let body = replace_and_get(response, |response| {
response
.ok_or(HttpClientError::ResponseHadRead)
.and_then(|response| response.bytes().map_err(Into::into))
})?;
let response = take(this.as_mut_state());
let body = response
.ok_or(HttpClientError::ResponseHadRead)
.and_then(|response| response.bytes().map_err(Into::into))?;
Ok::<_, HttpClientError>(body.to_vec())
},
vec![],
Expand Down
20 changes: 0 additions & 20 deletions examples/http-client/src/utils.rs

This file was deleted.

4 changes: 2 additions & 2 deletions phper-alloc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@

[package]
name = "phper-alloc"
description = "Alloc related items for phper crate."
keywords = ["php", "alloc"]
version = { workspace = true }
authors = { workspace = true }
edition = { workspace = true }
rust-version = { workspace = true }
description = "Alloc related items for phper crate."
repository = { workspace = true }
license = { workspace = true }
keywords = ["php", "alloc"]

[dependencies]
phper-sys = { version = "0.6.0", path = "../phper-sys" }
Expand Down
4 changes: 2 additions & 2 deletions phper-build/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@

[package]
name = "phper-build"
description = "Generates stubs for project using phper."
keywords = ["php", "binding"]
version = { workspace = true }
authors = { workspace = true }
edition = { workspace = true }
rust-version = { workspace = true }
description = "Generates stubs for project using phper."
repository = { workspace = true }
license = { workspace = true }
keywords = ["php", "binding"]

[dependencies]
phper-sys = { version = "0.6.0", path = "../phper-sys" }
25 changes: 25 additions & 0 deletions phper-doc/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Copyright (c) 2022 PHPER Framework Team
# PHPER is licensed under Mulan PSL v2.
# You can use this software according to the terms and conditions of the Mulan
# PSL v2. You may obtain a copy of Mulan PSL v2 at:
# http://license.coscl.org.cn/MulanPSL2
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY
# KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
# NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
# See the Mulan PSL v2 for more details.

[package]
name = "phper-doc"
description = "The documentation of phper."
keywords = ["php", "documentation"]
version = { workspace = true }
authors = { workspace = true }
edition = { workspace = true }
rust-version = { workspace = true }
repository = { workspace = true }
license = { workspace = true }

[dev-dependencies]
phper = { version = "0.6.0", path = "../phper" }
thiserror = "1.0.37"
reqwest = { version = "0.11.13", features = ["blocking", "cookies"] }
1 change: 1 addition & 0 deletions phper-doc/LICENSE
9 changes: 9 additions & 0 deletions phper-doc/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# PHPER documentation

This is the documentation of [phper](https://crates.io/crates/phper).

**There is nothing here other than documentation, so you don't have to import this crate as a dependency.**

## License

[MulanPSL-2.0](https://github.com/phper-framework/phper/blob/master/LICENSE).
Loading