mirror of
https://github.com/danog/ext-php-rs.git
synced 2024-12-12 09:09:46 +01:00
Merge branch 'master' into throw-exception-object
This commit is contained in:
commit
33c6732a6e
2
.github/actions/zts/Dockerfile
vendored
2
.github/actions/zts/Dockerfile
vendored
@ -1,4 +1,4 @@
|
||||
FROM php:zts
|
||||
FROM php:8.1-zts
|
||||
|
||||
WORKDIR /tmp
|
||||
|
||||
|
71
.github/workflows/build.yml
vendored
71
.github/workflows/build.yml
vendored
@ -1,5 +1,8 @@
|
||||
name: Build and Lint
|
||||
on:
|
||||
schedule:
|
||||
# runs every monday at midnight
|
||||
- cron: "0 0 * * 1"
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
@ -12,8 +15,9 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
php: ['8.0', '8.1']
|
||||
php: ["8.0", "8.1"]
|
||||
rust: [stable, nightly]
|
||||
clang: ["14"]
|
||||
phpts: [ts, nts]
|
||||
exclude:
|
||||
# ext-php-rs requires nightly Rust when on Windows.
|
||||
@ -24,6 +28,8 @@ jobs:
|
||||
phpts: ts
|
||||
- os: ubuntu-latest
|
||||
phpts: ts
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
@ -34,52 +40,67 @@ jobs:
|
||||
env:
|
||||
phpts: ${{ matrix.phpts }}
|
||||
- name: Setup Rust
|
||||
uses: actions-rs/toolchain@v1
|
||||
uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
toolchain: ${{ matrix.rust }}
|
||||
override: true
|
||||
components: rustfmt, clippy
|
||||
- name: Setup LLVM & Clang
|
||||
- run: rustup show
|
||||
- name: Cache cargo dependencies
|
||||
uses: Swatinem/rust-cache@v2
|
||||
# Uncomment the following if statement if caching nightly deps
|
||||
# ends up causing too much cache invalidation.
|
||||
# if: matrix.rust == 'stable'
|
||||
with:
|
||||
# increment this manually to force cache eviction
|
||||
prefix-key: "v0-rust"
|
||||
# LLVM & Clang
|
||||
- name: Cache LLVM and Clang
|
||||
id: cache-llvm
|
||||
uses: actions/cache@v3
|
||||
if: "!contains(matrix.os, 'windows')"
|
||||
with:
|
||||
path: ${{ runner.temp }}/llvm-${{ matrix.clang }}
|
||||
key: ${{ matrix.os }}-llvm-${{ matrix.clang }}
|
||||
- name: Setup LLVM & Clang
|
||||
id: clang
|
||||
uses: KyleMayes/install-llvm-action@v1
|
||||
if: "!contains(matrix.os, 'windows')"
|
||||
with:
|
||||
version: '13.0'
|
||||
directory: ${{ runner.temp }}/llvm
|
||||
version: ${{ matrix.clang }}
|
||||
directory: ${{ runner.temp }}/llvm-${{ matrix.clang }}
|
||||
cached: ${{ steps.cache-llvm.outputs.cache-hit }}
|
||||
- name: Configure Clang
|
||||
if: "!contains(matrix.os, 'windows')"
|
||||
run: |
|
||||
echo "LIBCLANG_PATH=${{ runner.temp }}/llvm/lib" >> $GITHUB_ENV
|
||||
echo "LIBCLANG_PATH=${{ runner.temp }}/llvm-${{ matrix.clang }}/lib" >> $GITHUB_ENV
|
||||
echo "LLVM_VERSION=${{ steps.clang.outputs.version }}" >> $GITHUB_ENV
|
||||
echo "LLVM_CONFIG_PATH=${{ runner.temp }}/llvm-${{ matrix.clang }}/bin/llvm-config" >> $GITHUB_ENV
|
||||
- name: Configure Clang (macOS only)
|
||||
if: "contains(matrix.os, 'macos')"
|
||||
run: echo "SDKROOT=$(xcrun --show-sdk-path)" >> $GITHUB_ENV
|
||||
# Build
|
||||
- name: Build
|
||||
env:
|
||||
EXT_PHP_RS_TEST:
|
||||
EXT_PHP_RS_TEST: ""
|
||||
run: cargo build --release --all-features --all
|
||||
# Test & lint
|
||||
- name: Test inline examples
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
args: --release --all --all-features
|
||||
run: cargo test --release --all --all-features
|
||||
- name: Run rustfmt
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: fmt
|
||||
args: --all -- --check
|
||||
if: matrix.rust == 'stable' && matrix.os == 'ubuntu-latest' && matrix.php == '8.1'
|
||||
run: cargo fmt --all -- --check
|
||||
- name: Run clippy
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: clippy
|
||||
args: --all -- -D warnings
|
||||
if: matrix.rust == 'stable'
|
||||
if: matrix.rust == 'stable' && matrix.os == 'ubuntu-latest' && matrix.php == '8.1'
|
||||
run: cargo clippy --all -- -D warnings
|
||||
# Docs
|
||||
- name: Run rustdoc
|
||||
if: matrix.rust == 'stable' && matrix.os == 'ubuntu-latest' && matrix.php == '8.1'
|
||||
run: cargo rustdoc -- -D warnings
|
||||
- name: Build with docs stub
|
||||
if: "contains(matrix.os, 'ubuntu') && matrix.php == '8.1'"
|
||||
if: matrix.rust == 'stable' && matrix.os == 'ubuntu-latest' && matrix.php == '8.1'
|
||||
env:
|
||||
DOCS_RS:
|
||||
run:
|
||||
cargo clean && cargo build
|
||||
DOCS_RS: ""
|
||||
run: cargo clean && cargo build
|
||||
build-zts:
|
||||
name: Build with ZTS
|
||||
runs-on: ubuntu-latest
|
||||
|
29
.github/workflows/docs.yml
vendored
29
.github/workflows/docs.yml
vendored
@ -1,6 +1,9 @@
|
||||
name: Deploy documentation
|
||||
on:
|
||||
workflow_dispatch:
|
||||
# runs every monday at midnight
|
||||
schedule:
|
||||
- cron: "0 0 * * 1"
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
@ -8,28 +11,38 @@ on:
|
||||
jobs:
|
||||
docs:
|
||||
name: Build and Deploy
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: ["ubuntu-latest"]
|
||||
php: ["8.0"]
|
||||
clang: ["14"]
|
||||
mdbook: ["latest"]
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: 8.0
|
||||
php-version: ${{ matrix.php }}
|
||||
- name: Setup Rust
|
||||
uses: actions-rs/toolchain@v1
|
||||
uses: dtolnay/rust-toolchain@nightly
|
||||
- name: Cache LLVM and Clang
|
||||
id: cache-llvm
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
toolchain: nightly
|
||||
override: true
|
||||
path: ${{ runner.temp }}/llvm-${{ matrix.clang }}
|
||||
key: ${{ matrix.os }}-llvm-${{ matrix.clang }}
|
||||
- name: Setup LLVM & Clang
|
||||
uses: KyleMayes/install-llvm-action@v1
|
||||
with:
|
||||
version: 11.0
|
||||
directory: ${{ runner.temp }}/llvm-11.0
|
||||
version: ${{ matrix.clang }}
|
||||
directory: ${{ runner.temp }}/llvm-${{ matrix.clang }}
|
||||
cached: ${{ steps.cache-llvm.outputs.cache-hit }}
|
||||
- name: Install mdbook
|
||||
uses: peaceiris/actions-mdbook@v1
|
||||
with:
|
||||
mdbook-version: latest
|
||||
mdbook-version: ${{ matrix.mdbook }}
|
||||
- name: Build guide
|
||||
run: mdbook build guide
|
||||
- name: Publish docs
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -2,4 +2,5 @@
|
||||
Cargo.lock
|
||||
/.vscode
|
||||
/.idea
|
||||
/tmp
|
||||
expand.rs
|
120
CHANGELOG.md
120
CHANGELOG.md
@ -1,5 +1,125 @@
|
||||
# Changelog
|
||||
|
||||
## Version 0.9.0
|
||||
|
||||
- ci+docs: honour PHP_CONFIG & rebuild automatically when env vars change by @julius [#210]
|
||||
- chore: Update generated FFI bindings with bindgen 0.63 by @ptondereau [#211]
|
||||
|
||||
**BC changes**
|
||||
- feat: allows ZendStr to contain null bytes by @julius [#202]
|
||||
|
||||
**Migration**
|
||||
See: [#202]
|
||||
|
||||
[#202]: https://github.com/davidcole1340/ext-php-rs/pull/202
|
||||
[#210]: https://github.com/davidcole1340/ext-php-rs/pull/210
|
||||
[#211]: https://github.com/davidcole1340/ext-php-rs/pull/211
|
||||
|
||||
|
||||
## Version 0.8.3
|
||||
|
||||
- build: Check docs warnings in CI by @davidcole1340 in [#180]
|
||||
- fix: Fixes inifinte loop in ClassEntry::instance_of() by @ju1ius in [#188]
|
||||
- fix: Fix binary slice lifetimes by @davidcole1340 in [#181]
|
||||
- build: Fixes CI workflow configuration by @ju1ius in [#195]
|
||||
- feat: Add get_id() and hash() methods on ZendObject by @ju1ius in [#196]
|
||||
- docs: Describes restrictions on generic parameters for `php_class` by @ju1ius in [#194]
|
||||
- feat: Add instance_of() and get_class_entry() methods on ZendObject by @ju1ius in [#197]
|
||||
|
||||
[#180]: https://github.com/davidcole1340/ext-php-rs/pull/180
|
||||
[#188]: https://github.com/davidcole1340/ext-php-rs/pull/188
|
||||
[#181]: https://github.com/davidcole1340/ext-php-rs/pull/181
|
||||
[#195]: https://github.com/davidcole1340/ext-php-rs/pull/195
|
||||
[#196]: https://github.com/davidcole1340/ext-php-rs/pull/196
|
||||
[#194]: https://github.com/davidcole1340/ext-php-rs/pull/194
|
||||
[#197]: https://github.com/davidcole1340/ext-php-rs/pull/197
|
||||
|
||||
## Version 0.8.2
|
||||
|
||||
- Update changelog for latest versions by @striezel in [#161]
|
||||
- fix building docs on docs.rs by @davidcole1340 in [#165]
|
||||
- Add some standard zend interfaces by @nikeee in [#164]
|
||||
- Correct parameter name. by @denzyldick in [#168]
|
||||
- fix describe when using `#[implements]` by @davidcole1340 in [#169]
|
||||
- Add example that shows how to implement an interface by @nikeee in [#167]
|
||||
- add `before` flag to `#[php_startup]` by @davidcole1340 in [#170]
|
||||
- add ability to define abstract methods by @davidcole1340 in [#171]
|
||||
- chore(cli): Bump Clap for CLI tool by @ptondereau in [#177]
|
||||
- fix type links in docs.rs by @davidcole1340 in [#179]
|
||||
|
||||
[#161]: https://github.com/davidcole1340/ext-php-rs/pull/161
|
||||
[#165]: https://github.com/davidcole1340/ext-php-rs/pull/165
|
||||
[#164]: https://github.com/davidcole1340/ext-php-rs/pull/164
|
||||
[#168]: https://github.com/davidcole1340/ext-php-rs/pull/168
|
||||
[#169]: https://github.com/davidcole1340/ext-php-rs/pull/169
|
||||
[#167]: https://github.com/davidcole1340/ext-php-rs/pull/167
|
||||
[#170]: https://github.com/davidcole1340/ext-php-rs/pull/170
|
||||
[#171]: https://github.com/davidcole1340/ext-php-rs/pull/171
|
||||
[#177]: https://github.com/davidcole1340/ext-php-rs/pull/177
|
||||
[#179]: https://github.com/davidcole1340/ext-php-rs/pull/179
|
||||
|
||||
## Version 0.8.1
|
||||
|
||||
- 404 /guide doesn't exists. by @denzyldick in [#149]
|
||||
- Fixed some typos by @denzyldick in [#148]
|
||||
- Fix a few typos by @striezel in [#150]
|
||||
- fix causes of some clippy warnings by @striezel in [#152]
|
||||
- fix more causes of clippy warnings by @striezel in [#157]
|
||||
- attempt to fix errors related to clap by @striezel in [#158]
|
||||
- ci: run clippy only on stable Rust channel by @striezel in [#159]
|
||||
- update actions/checkout in GitHub Actions workflows to v3 by @striezel in
|
||||
[#151]
|
||||
- Add ability to set function name on php_function macro by @joehoyle in [#153]
|
||||
- Specify classes as fully-qualified names in stubs by @joehoyle in [#156]
|
||||
- Support marking classes as interfaces by @joehoyle in [#155]
|
||||
- Support marking methods as abstract by @joehoyle in [#154]
|
||||
- Add php-scrypt as a example project by @PineappleIOnic in [#146]
|
||||
- Fix ini file duplication and truncation when using cargo-php command by
|
||||
@roborourke in [#136]
|
||||
- Allow passing --yes parameter to bypass prompts by @roborourke in [#135]
|
||||
|
||||
[#135]: https://github.com/davidcole1340/ext-php-rs/pull/135
|
||||
[#136]: https://github.com/davidcole1340/ext-php-rs/pull/136
|
||||
[#146]: https://github.com/davidcole1340/ext-php-rs/pull/146
|
||||
[#148]: https://github.com/davidcole1340/ext-php-rs/pull/148
|
||||
[#149]: https://github.com/davidcole1340/ext-php-rs/pull/149
|
||||
[#150]: https://github.com/davidcole1340/ext-php-rs/pull/150
|
||||
[#151]: https://github.com/davidcole1340/ext-php-rs/pull/151
|
||||
[#152]: https://github.com/davidcole1340/ext-php-rs/pull/152
|
||||
[#153]: https://github.com/davidcole1340/ext-php-rs/pull/153
|
||||
[#154]: https://github.com/davidcole1340/ext-php-rs/pull/154
|
||||
[#155]: https://github.com/davidcole1340/ext-php-rs/pull/155
|
||||
[#156]: https://github.com/davidcole1340/ext-php-rs/pull/156
|
||||
[#157]: https://github.com/davidcole1340/ext-php-rs/pull/157
|
||||
[#158]: https://github.com/davidcole1340/ext-php-rs/pull/158
|
||||
[#159]: https://github.com/davidcole1340/ext-php-rs/pull/159
|
||||
|
||||
## Version 0.8.0
|
||||
|
||||
- Windows support by @davidcole1340 in [#128]
|
||||
- Support for binary slice to avoid extra allocation by @TobiasBengtsson in
|
||||
[#139]
|
||||
- Bump dependencies by @ptondereau in [#144]
|
||||
|
||||
[#128]: https://github.com/davidcole1340/ext-php-rs/pull/128
|
||||
[#139]: https://github.com/davidcole1340/ext-php-rs/pull/139
|
||||
[#144]: https://github.com/davidcole1340/ext-php-rs/pull/144
|
||||
|
||||
## Version 0.7.4
|
||||
|
||||
- Fix is_true() / is_false() in Zval by @joehoyle in [#116]
|
||||
- readme: fix link to guide by @TorstenDittmann in [#120]
|
||||
- Fix request_(startup|shutdown)_function in ModuleBuilder by @glyphpoch in
|
||||
[#119]
|
||||
- Fix CI on macOS by @davidcole1340 in [#126]
|
||||
- Add ability to pass modifier function for classes by @davidcole1340 in [#127]
|
||||
|
||||
[#116]: https://github.com/davidcole1340/ext-php-rs/pull/116
|
||||
[#119]: https://github.com/davidcole1340/ext-php-rs/pull/119
|
||||
[#120]: https://github.com/davidcole1340/ext-php-rs/pull/120
|
||||
[#126]: https://github.com/davidcole1340/ext-php-rs/pull/126
|
||||
[#127]: https://github.com/davidcole1340/ext-php-rs/pull/127
|
||||
|
||||
## Version 0.7.3
|
||||
|
||||
- Upgrade `clap` to `3.0.0-rc3`. [#113]
|
||||
|
@ -5,7 +5,7 @@ repository = "https://github.com/davidcole1340/ext-php-rs"
|
||||
homepage = "https://github.com/davidcole1340/ext-php-rs"
|
||||
license = "MIT OR Apache-2.0"
|
||||
keywords = ["php", "ffi", "zend"]
|
||||
version = "0.8.0"
|
||||
version = "0.9.0"
|
||||
authors = ["David Cole <david.cole1340@gmail.com>"]
|
||||
edition = "2018"
|
||||
categories = ["api-bindings"]
|
||||
@ -17,15 +17,14 @@ parking_lot = "0.12.1"
|
||||
cfg-if = "1.0"
|
||||
once_cell = "1.8.0"
|
||||
anyhow = { version = "1", optional = true }
|
||||
ext-php-rs-derive = { version = "=0.8.0", path = "./crates/macros" }
|
||||
ext-php-rs-derive = { version = "=0.9.0", path = "./crates/macros" }
|
||||
|
||||
[dev-dependencies]
|
||||
skeptic = "0.13"
|
||||
|
||||
[build-dependencies]
|
||||
anyhow = "1"
|
||||
# bindgen = { version = "0.59" }
|
||||
bindgen = "0.60"
|
||||
bindgen = "0.63"
|
||||
cc = "1.0"
|
||||
skeptic = "0.13"
|
||||
|
||||
|
@ -163,6 +163,8 @@ Check out one of the example projects:
|
||||
- [opus-php](https://github.com/davidcole1340/opus-php) - Audio encoder for the
|
||||
Opus codec in PHP.
|
||||
- [tomlrs-php](https://github.com/jphenow/tomlrs-php) - TOML data format parser.
|
||||
- [php-scrypt](https://github.com/appwrite/php-scrypt) - PHP wrapper for the
|
||||
scrypt password hashing algorithm.
|
||||
|
||||
## Contributions
|
||||
|
||||
|
@ -4,6 +4,20 @@
|
||||
// exist in the bindings file. Which ever script include!s the bindings must
|
||||
// define the `bind` macro. This allows us to have the list in string format
|
||||
// inside the build script and in macro format inside the CLI crate.
|
||||
//
|
||||
// NOTE TO EDITORS:
|
||||
// When updating this file, you must re-generate the `docsrs_bindings.rs`
|
||||
// file used by docs.rs to build documentation. To perform this:
|
||||
//
|
||||
// $ cargo clean
|
||||
// $ cargo build
|
||||
// $ cp target/debug/build/ext-php-rs-e2cb315d27898d01/out/bindings.rs
|
||||
// docsrs_bindings.rs
|
||||
// $ git add . && git commit -m "update docs.rs bindings"
|
||||
//
|
||||
// The hash after `ext-php-rs-` in the bindings path may change. There should
|
||||
// be two folders beginning with `ext-php-rs-` in `target/debug/build`, so
|
||||
// check both for the presense of the bindings file.
|
||||
|
||||
bind! {
|
||||
HashTable,
|
||||
@ -29,6 +43,8 @@ bind! {
|
||||
// ext_php_rs_zend_object_release,
|
||||
// ext_php_rs_zend_string_init,
|
||||
// ext_php_rs_zend_string_release,
|
||||
// ext_php_rs_is_kown_valid_utf8,
|
||||
// ext_php_rs_set_kown_valid_utf8,
|
||||
object_properties_init,
|
||||
php_info_print_table_end,
|
||||
php_info_print_table_header,
|
||||
@ -49,6 +65,13 @@ bind! {
|
||||
zend_ce_type_error,
|
||||
zend_ce_unhandled_match_error,
|
||||
zend_ce_value_error,
|
||||
zend_ce_traversable,
|
||||
zend_ce_aggregate,
|
||||
zend_ce_iterator,
|
||||
zend_ce_arrayaccess,
|
||||
zend_ce_serializable,
|
||||
zend_ce_countable,
|
||||
zend_ce_stringable,
|
||||
zend_class_entry,
|
||||
zend_declare_class_constant,
|
||||
zend_declare_property,
|
||||
|
43
build.rs
43
build.rs
@ -43,7 +43,7 @@ pub trait PHPProvider<'a>: Sized {
|
||||
}
|
||||
|
||||
/// Finds the location of an executable `name`.
|
||||
fn find_executable(name: &str) -> Option<PathBuf> {
|
||||
pub fn find_executable(name: &str) -> Option<PathBuf> {
|
||||
const WHICH: &str = if cfg!(windows) { "where" } else { "which" };
|
||||
let cmd = Command::new(WHICH).arg(name).output().ok()?;
|
||||
if cmd.status.success() {
|
||||
@ -54,15 +54,25 @@ fn find_executable(name: &str) -> Option<PathBuf> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns an environment variable's value as a PathBuf
|
||||
pub fn path_from_env(key: &str) -> Option<PathBuf> {
|
||||
std::env::var_os(key).map(PathBuf::from)
|
||||
}
|
||||
|
||||
/// Finds the location of the PHP executable.
|
||||
fn find_php() -> Result<PathBuf> {
|
||||
// If PHP path is given via env, it takes priority.
|
||||
let env = std::env::var("PHP");
|
||||
if let Ok(env) = env {
|
||||
return Ok(env.into());
|
||||
// If path is given via env, it takes priority.
|
||||
if let Some(path) = path_from_env("PHP") {
|
||||
if !path.try_exists()? {
|
||||
// If path was explicitly given and it can't be found, this is a hard error
|
||||
bail!("php executable not found at {:?}", path);
|
||||
}
|
||||
return Ok(path);
|
||||
}
|
||||
|
||||
find_executable("php").context("Could not find PHP path. Please ensure `php` is in your PATH or the `PHP` environment variable is set.")
|
||||
find_executable("php").with_context(|| {
|
||||
"Could not find PHP executable. \
|
||||
Please ensure `php` is in your PATH or the `PHP` environment variable is set."
|
||||
})
|
||||
}
|
||||
|
||||
pub struct PHPInfo(String);
|
||||
@ -206,6 +216,8 @@ fn check_php_version(info: &PHPInfo) -> Result<()> {
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let out_dir = env::var_os("OUT_DIR").context("Failed to get OUT_DIR")?;
|
||||
let out_path = PathBuf::from(out_dir).join("bindings.rs");
|
||||
let manifest: PathBuf = std::env::var("CARGO_MANIFEST_DIR").unwrap().into();
|
||||
for path in [
|
||||
manifest.join("src").join("wrapper.h"),
|
||||
@ -216,6 +228,21 @@ fn main() -> Result<()> {
|
||||
] {
|
||||
println!("cargo:rerun-if-changed={}", path.to_string_lossy());
|
||||
}
|
||||
for env_var in ["PHP", "PHP_CONFIG", "PATH"] {
|
||||
println!("cargo:rerun-if-env-changed={}", env_var);
|
||||
}
|
||||
|
||||
println!("cargo:rerun-if-changed=build.rs");
|
||||
|
||||
// docs.rs runners only have PHP 7.4 - use pre-generated bindings
|
||||
if env::var("DOCS_RS").is_ok() {
|
||||
println!("cargo:warning=docs.rs detected - using stub bindings");
|
||||
println!("cargo:rustc-cfg=php_debug");
|
||||
println!("cargo:rustc-cfg=php81");
|
||||
std::fs::copy("docsrs_bindings.rs", out_path)
|
||||
.expect("failed to copy docs.rs stub bindings to out directory");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let php = find_php()?;
|
||||
let info = PHPInfo::get(&php)?;
|
||||
@ -228,8 +255,6 @@ fn main() -> Result<()> {
|
||||
build_wrapper(&defines, &includes)?;
|
||||
let bindings = generate_bindings(&defines, &includes)?;
|
||||
|
||||
let out_dir = env::var_os("OUT_DIR").context("Failed to get OUT_DIR")?;
|
||||
let out_path = PathBuf::from(out_dir).join("bindings.rs");
|
||||
let out_file =
|
||||
File::create(&out_path).context("Failed to open output bindings file for writing")?;
|
||||
let mut out_writer = BufWriter::new(out_file);
|
||||
|
@ -5,7 +5,7 @@ repository = "https://github.com/davidcole1340/ext-php-rs"
|
||||
homepage = "https://github.com/davidcole1340/ext-php-rs"
|
||||
license = "MIT OR Apache-2.0"
|
||||
keywords = ["php", "ffi", "zend"]
|
||||
version = "0.1.5"
|
||||
version = "0.1.7"
|
||||
authors = ["David Cole <david.cole1340@gmail.com>"]
|
||||
edition = "2018"
|
||||
categories = ["api-bindings", "command-line-interface"]
|
||||
@ -13,9 +13,9 @@ categories = ["api-bindings", "command-line-interface"]
|
||||
[dependencies]
|
||||
ext-php-rs = { version = ">=0.7.1", path = "../../" }
|
||||
|
||||
clap = { version = ">=3.2.5", features = ["derive"] }
|
||||
clap = { version = "4.0", features = ["derive"] }
|
||||
anyhow = "1"
|
||||
dialoguer = "0.10"
|
||||
libloading = "0.7"
|
||||
cargo_metadata = "0.14"
|
||||
cargo_metadata = "0.15"
|
||||
semver = "1.0"
|
||||
|
@ -71,6 +71,9 @@ OPTIONS:
|
||||
--release
|
||||
Whether to install the release version of the extension
|
||||
|
||||
--yes
|
||||
Bypasses the confirmation prompt
|
||||
|
||||
$ cargo php remove --help
|
||||
cargo-php-remove
|
||||
|
||||
@ -97,6 +100,9 @@ OPTIONS:
|
||||
Path to the Cargo manifest of the extension. Defaults to the manifest in the directory
|
||||
the command is called
|
||||
|
||||
--yes
|
||||
Bypasses the confirmation prompt
|
||||
|
||||
$ cargo php stubs --help
|
||||
cargo-php-stubs
|
||||
|
||||
|
@ -10,7 +10,7 @@ use dialoguer::{Confirm, Select};
|
||||
|
||||
use std::{
|
||||
fs::OpenOptions,
|
||||
io::{BufRead, BufReader, Write},
|
||||
io::{BufRead, BufReader, Seek, SeekFrom, Write},
|
||||
path::PathBuf,
|
||||
process::{Command, Stdio},
|
||||
};
|
||||
@ -105,6 +105,9 @@ struct Install {
|
||||
/// the directory the command is called.
|
||||
#[arg(long)]
|
||||
manifest: Option<PathBuf>,
|
||||
/// Whether to bypass the install prompt.
|
||||
#[clap(long)]
|
||||
yes: bool,
|
||||
}
|
||||
|
||||
#[derive(Parser)]
|
||||
@ -121,6 +124,9 @@ struct Remove {
|
||||
/// the directory the command is called.
|
||||
#[arg(long)]
|
||||
manifest: Option<PathBuf>,
|
||||
/// Whether to bypass the remove prompt.
|
||||
#[clap(long)]
|
||||
yes: bool,
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
@ -172,12 +178,13 @@ impl Install {
|
||||
php_ini = Some(ini_path);
|
||||
}
|
||||
|
||||
if !Confirm::new()
|
||||
.with_prompt(format!(
|
||||
"Are you sure you want to install the extension `{}`?",
|
||||
artifact.name
|
||||
))
|
||||
.interact()?
|
||||
if !self.yes
|
||||
&& !Confirm::new()
|
||||
.with_prompt(format!(
|
||||
"Are you sure you want to install the extension `{}`?",
|
||||
artifact.name
|
||||
))
|
||||
.interact()?
|
||||
{
|
||||
bail!("Installation cancelled.");
|
||||
}
|
||||
@ -207,6 +214,8 @@ impl Install {
|
||||
let line = line.with_context(|| "Failed to read line from `php.ini`")?;
|
||||
if !line.contains(&ext_line) {
|
||||
new_lines.push(line);
|
||||
} else {
|
||||
bail!("Extension already enabled.");
|
||||
}
|
||||
}
|
||||
|
||||
@ -216,6 +225,8 @@ impl Install {
|
||||
}
|
||||
|
||||
new_lines.push(ext_line);
|
||||
file.seek(SeekFrom::Start(0))?;
|
||||
file.set_len(0)?;
|
||||
file.write(new_lines.join("\n").as_bytes())
|
||||
.with_context(|| "Failed to update `php.ini`")?;
|
||||
}
|
||||
@ -301,12 +312,13 @@ impl Remove {
|
||||
bail!("Unable to find extension installed.");
|
||||
}
|
||||
|
||||
if !Confirm::new()
|
||||
.with_prompt(format!(
|
||||
"Are you sure you want to remove the extension `{}`?",
|
||||
artifact.name
|
||||
))
|
||||
.interact()?
|
||||
if !self.yes
|
||||
&& !Confirm::new()
|
||||
.with_prompt(format!(
|
||||
"Are you sure you want to remove the extension `{}`?",
|
||||
artifact.name
|
||||
))
|
||||
.interact()?
|
||||
{
|
||||
bail!("Installation cancelled.");
|
||||
}
|
||||
@ -318,7 +330,6 @@ impl Remove {
|
||||
.read(true)
|
||||
.write(true)
|
||||
.create(true)
|
||||
.truncate(true)
|
||||
.open(php_ini)
|
||||
.with_context(|| "Failed to open `php.ini`")?;
|
||||
|
||||
@ -330,6 +341,8 @@ impl Remove {
|
||||
}
|
||||
}
|
||||
|
||||
file.seek(SeekFrom::Start(0))?;
|
||||
file.set_len(0)?;
|
||||
file.write(new_lines.join("\n").as_bytes())
|
||||
.with_context(|| "Failed to update `php.ini`")?;
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ description = "Derive macros for ext-php-rs."
|
||||
repository = "https://github.com/davidcole1340/ext-php-rs"
|
||||
homepage = "https://github.com/davidcole1340/ext-php-rs"
|
||||
license = "MIT OR Apache-2.0"
|
||||
version = "0.8.0"
|
||||
version = "0.9.0"
|
||||
authors = ["David Cole <david.cole1340@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
|
@ -22,6 +22,7 @@ pub struct Class {
|
||||
/// A function name called when creating the class entry. Given an instance
|
||||
/// of `ClassBuilder` and must return it.
|
||||
pub modifier: Option<String>,
|
||||
pub flags: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -37,6 +38,7 @@ pub enum ParsedAttribute {
|
||||
pub struct AttrArgs {
|
||||
name: Option<String>,
|
||||
modifier: Option<String>,
|
||||
flags: Option<Expr>,
|
||||
}
|
||||
|
||||
pub fn parser(args: AttributeArgs, mut input: ItemStruct) -> Result<TokenStream> {
|
||||
@ -117,6 +119,7 @@ pub fn parser(args: AttributeArgs, mut input: ItemStruct) -> Result<TokenStream>
|
||||
let ItemStruct { ident, .. } = &input;
|
||||
let class_name = args.name.unwrap_or_else(|| ident.to_string());
|
||||
let struct_path = ident.to_string();
|
||||
let flags = args.flags.map(|flags| flags.to_token_stream().to_string());
|
||||
let class = Class {
|
||||
class_name,
|
||||
struct_path,
|
||||
@ -125,6 +128,7 @@ pub fn parser(args: AttributeArgs, mut input: ItemStruct) -> Result<TokenStream>
|
||||
docs: comments,
|
||||
properties,
|
||||
modifier: args.modifier,
|
||||
flags,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
|
@ -17,6 +17,7 @@ pub struct AttrArgs {
|
||||
optional: Option<String>,
|
||||
ignore_module: bool,
|
||||
defaults: HashMap<String, Lit>,
|
||||
name: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@ -93,7 +94,7 @@ pub fn parser(args: AttributeArgs, input: ItemFn) -> Result<(TokenStream, Functi
|
||||
}
|
||||
|
||||
let function = Function {
|
||||
name: ident.to_string(),
|
||||
name: attr_args.name.unwrap_or_else(|| ident.to_string()),
|
||||
docs: get_docs(&input.attrs),
|
||||
ident: internal_ident.to_string(),
|
||||
args,
|
||||
|
@ -85,6 +85,7 @@ pub enum ParsedAttribute {
|
||||
},
|
||||
Constructor,
|
||||
This,
|
||||
Abstract,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, FromMeta)]
|
||||
@ -212,6 +213,7 @@ pub fn parse_attribute(attr: &Attribute) -> Result<Option<ParsedAttribute>> {
|
||||
"public" => ParsedAttribute::Visibility(Visibility::Public),
|
||||
"protected" => ParsedAttribute::Visibility(Visibility::Protected),
|
||||
"private" => ParsedAttribute::Visibility(Visibility::Private),
|
||||
"abstract_method" => ParsedAttribute::Abstract,
|
||||
"rename" => {
|
||||
let ident = if let Meta::List(list) = meta {
|
||||
if let Some(NestedMeta::Lit(lit)) = list.nested.first() {
|
||||
|
@ -87,10 +87,11 @@ pub fn php_module(_: TokenStream, input: TokenStream) -> TokenStream {
|
||||
}
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn php_startup(_: TokenStream, input: TokenStream) -> TokenStream {
|
||||
pub fn php_startup(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||
let args = parse_macro_input!(args as AttributeArgs);
|
||||
let input = parse_macro_input!(input as ItemFn);
|
||||
|
||||
match startup_function::parser(input) {
|
||||
match startup_function::parser(Some(args), input) {
|
||||
Ok(parsed) => parsed,
|
||||
Err(e) => syn::Error::new(Span::call_site(), e).to_compile_error(),
|
||||
}
|
||||
|
@ -38,6 +38,7 @@ pub struct Method {
|
||||
pub optional: Option<String>,
|
||||
pub output: Option<(String, bool)>,
|
||||
pub _static: bool,
|
||||
pub _abstract: bool,
|
||||
pub visibility: Visibility,
|
||||
}
|
||||
|
||||
@ -81,6 +82,7 @@ pub fn parser(
|
||||
let mut visibility = Visibility::Public;
|
||||
let mut as_prop = None;
|
||||
let mut identifier = None;
|
||||
let mut is_abstract = false;
|
||||
let mut is_constructor = false;
|
||||
let docs = get_docs(&input.attrs);
|
||||
|
||||
@ -90,6 +92,7 @@ pub fn parser(
|
||||
ParsedAttribute::Default(list) => defaults = list,
|
||||
ParsedAttribute::Optional(name) => optional = Some(name),
|
||||
ParsedAttribute::Visibility(vis) => visibility = vis,
|
||||
ParsedAttribute::Abstract => is_abstract = true,
|
||||
ParsedAttribute::Rename(ident) => identifier = Some(ident),
|
||||
ParsedAttribute::Property { prop_name, ty } => {
|
||||
if as_prop.is_some() {
|
||||
@ -211,6 +214,7 @@ pub fn parser(
|
||||
optional,
|
||||
output: get_return_type(struct_ty, &input.sig.output)?,
|
||||
_static: matches!(method_type, MethodType::Static),
|
||||
_abstract: is_abstract,
|
||||
visibility,
|
||||
};
|
||||
|
||||
@ -447,6 +451,10 @@ impl Method {
|
||||
flags.push(quote! { Static });
|
||||
}
|
||||
|
||||
if self._abstract {
|
||||
flags.push(quote! { Abstract });
|
||||
}
|
||||
|
||||
flags
|
||||
.iter()
|
||||
.map(|flag| quote! { ::ext_php_rs::flags::MethodFlags::#flag })
|
||||
|
@ -34,7 +34,7 @@ pub fn parser(input: ItemFn) -> Result<TokenStream> {
|
||||
fn php_module_startup() {}
|
||||
})
|
||||
.map_err(|_| anyhow!("Unable to generate PHP module startup function."))?;
|
||||
let startup = startup_function::parser(parsed)?;
|
||||
let startup = startup_function::parser(None, parsed)?;
|
||||
|
||||
state = STATE.lock();
|
||||
Some(startup)
|
||||
@ -227,10 +227,7 @@ impl Describe for Class {
|
||||
} else {
|
||||
quote! { None }
|
||||
};
|
||||
let interfaces = self
|
||||
.interfaces
|
||||
.iter()
|
||||
.map(|iface| quote! { #iface.into(), });
|
||||
let interfaces = self.interfaces.iter().map(|iface| quote! { #iface.into() });
|
||||
let properties = self.properties.iter().map(|d| d.describe());
|
||||
let mut methods: Vec<_> = self.methods.iter().map(Describe::describe).collect();
|
||||
let docs = self.docs.iter().map(|c| {
|
||||
|
@ -1,13 +1,27 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
use darling::FromMeta;
|
||||
use proc_macro2::{Ident, Span, TokenStream};
|
||||
use quote::quote;
|
||||
use syn::{Expr, ItemFn, Signature};
|
||||
use syn::{AttributeArgs, Expr, ItemFn, Signature};
|
||||
|
||||
use crate::{class::Class, constant::Constant, STATE};
|
||||
|
||||
pub fn parser(input: ItemFn) -> Result<TokenStream> {
|
||||
#[derive(Default, Debug, FromMeta)]
|
||||
#[darling(default)]
|
||||
struct StartupArgs {
|
||||
before: bool,
|
||||
}
|
||||
|
||||
pub fn parser(args: Option<AttributeArgs>, input: ItemFn) -> Result<TokenStream> {
|
||||
let args = if let Some(args) = args {
|
||||
StartupArgs::from_list(&args)
|
||||
.map_err(|e| anyhow!("Unable to parse attribute arguments: {:?}", e))?
|
||||
} else {
|
||||
StartupArgs::default()
|
||||
};
|
||||
|
||||
let ItemFn { sig, block, .. } = input;
|
||||
let Signature { ident, .. } = sig;
|
||||
let stmts = &block.stmts;
|
||||
@ -17,6 +31,11 @@ pub fn parser(input: ItemFn) -> Result<TokenStream> {
|
||||
|
||||
let classes = build_classes(&state.classes)?;
|
||||
let constants = build_constants(&state.constants);
|
||||
let (before, after) = if args.before {
|
||||
(Some(quote! { internal(); }), None)
|
||||
} else {
|
||||
(None, Some(quote! { internal(); }))
|
||||
};
|
||||
|
||||
let func = quote! {
|
||||
#[doc(hidden)]
|
||||
@ -30,11 +49,10 @@ pub fn parser(input: ItemFn) -> Result<TokenStream> {
|
||||
|
||||
::ext_php_rs::internal::ext_php_rs_startup();
|
||||
|
||||
#before
|
||||
#(#classes)*
|
||||
#(#constants)*
|
||||
|
||||
// TODO return result?
|
||||
internal();
|
||||
#after
|
||||
|
||||
0
|
||||
}
|
||||
@ -121,6 +139,31 @@ fn build_classes(classes: &HashMap<String, Class>) -> Result<Vec<TokenStream>> {
|
||||
}
|
||||
});
|
||||
|
||||
let flags = {
|
||||
if let Some(flags) = &class.flags {
|
||||
let mut name = "::ext_php_rs::flags::ClassFlags::".to_owned();
|
||||
name.push_str(flags);
|
||||
let expr: Expr = syn::parse_str(&name).map_err(|_| {
|
||||
anyhow!("Invalid expression given for `{}` flags", class_name)
|
||||
})?;
|
||||
Some(quote! { .flags(#expr) })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
let object_override = {
|
||||
if let Some(flags) = &class.flags {
|
||||
if flags == "Interface" {
|
||||
None
|
||||
} else {
|
||||
Some(quote! { .object_override::<#ident>() })
|
||||
}
|
||||
} else {
|
||||
Some(quote! { .object_override::<#ident>() })
|
||||
}
|
||||
};
|
||||
|
||||
Ok(quote! {{
|
||||
let builder = ::ext_php_rs::builders::ClassBuilder::new(#class_name)
|
||||
#(#methods)*
|
||||
@ -128,7 +171,9 @@ fn build_classes(classes: &HashMap<String, Class>) -> Result<Vec<TokenStream>> {
|
||||
#(#interfaces)*
|
||||
// #(#properties)*
|
||||
#parent
|
||||
.object_override::<#ident>();
|
||||
#flags
|
||||
#object_override
|
||||
;
|
||||
#class_modifier
|
||||
let class = builder.build()
|
||||
.expect(concat!("Unable to build class `", #class_name, "`"));
|
||||
|
@ -1,7 +1,6 @@
|
||||
/* automatically generated by rust-bindgen 0.59.1 */
|
||||
/* automatically generated by rust-bindgen 0.63.0 */
|
||||
|
||||
pub const ZEND_DEBUG: u32 = 1;
|
||||
pub const ZEND_MM_ALIGNMENT: u32 = 8;
|
||||
pub const _ZEND_TYPE_NAME_BIT: u32 = 16777216;
|
||||
pub const _ZEND_TYPE_NULLABLE_BIT: u32 = 2;
|
||||
pub const HT_MIN_SIZE: u32 = 8;
|
||||
@ -32,7 +31,6 @@ pub const IS_OBJECT_EX: u32 = 776;
|
||||
pub const IS_RESOURCE_EX: u32 = 265;
|
||||
pub const IS_REFERENCE_EX: u32 = 266;
|
||||
pub const IS_CONSTANT_AST_EX: u32 = 267;
|
||||
pub const ZEND_MM_ALIGNMENT_MASK: i32 = -8;
|
||||
pub const ZEND_PROPERTY_ISSET: u32 = 0;
|
||||
pub const ZEND_PROPERTY_EXISTS: u32 = 2;
|
||||
pub const ZEND_ACC_PUBLIC: u32 = 1;
|
||||
@ -90,8 +88,11 @@ pub const CONST_CS: u32 = 0;
|
||||
pub const CONST_PERSISTENT: u32 = 1;
|
||||
pub const CONST_NO_FILE_CACHE: u32 = 2;
|
||||
pub const CONST_DEPRECATED: u32 = 4;
|
||||
pub type __darwin_size_t = ::std::os::raw::c_ulong;
|
||||
pub type size_t = __darwin_size_t;
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct __sigset_t {
|
||||
pub __val: [::std::os::raw::c_ulong; 16usize],
|
||||
}
|
||||
pub type zend_long = i64;
|
||||
pub type zend_ulong = u64;
|
||||
pub type zend_uchar = ::std::os::raw::c_uchar;
|
||||
@ -202,7 +203,7 @@ pub struct _zend_refcounted {
|
||||
pub struct _zend_string {
|
||||
pub gc: zend_refcounted_h,
|
||||
pub h: zend_ulong,
|
||||
pub len: size_t,
|
||||
pub len: usize,
|
||||
pub val: [::std::os::raw::c_char; 1usize],
|
||||
}
|
||||
#[repr(C)]
|
||||
@ -284,7 +285,7 @@ pub struct _zend_ast_ref {
|
||||
}
|
||||
extern "C" {
|
||||
pub fn _emalloc(
|
||||
size: size_t,
|
||||
size: usize,
|
||||
__zend_filename: *const ::std::os::raw::c_char,
|
||||
__zend_lineno: u32,
|
||||
__zend_orig_filename: *const ::std::os::raw::c_char,
|
||||
@ -301,12 +302,12 @@ extern "C" {
|
||||
);
|
||||
}
|
||||
extern "C" {
|
||||
pub fn __zend_malloc(len: size_t) -> *mut ::std::os::raw::c_void;
|
||||
pub fn __zend_malloc(len: usize) -> *mut ::std::os::raw::c_void;
|
||||
}
|
||||
pub type zend_string_init_interned_func_t = ::std::option::Option<
|
||||
unsafe extern "C" fn(
|
||||
str_: *const ::std::os::raw::c_char,
|
||||
size: size_t,
|
||||
size: usize,
|
||||
permanent: bool,
|
||||
) -> *mut zend_string,
|
||||
>;
|
||||
@ -320,7 +321,7 @@ extern "C" {
|
||||
pub fn zend_hash_str_update(
|
||||
ht: *mut HashTable,
|
||||
key: *const ::std::os::raw::c_char,
|
||||
len: size_t,
|
||||
len: usize,
|
||||
pData: *mut zval,
|
||||
) -> *mut zval;
|
||||
}
|
||||
@ -335,7 +336,7 @@ extern "C" {
|
||||
pub fn zend_hash_str_del(
|
||||
ht: *mut HashTable,
|
||||
key: *const ::std::os::raw::c_char,
|
||||
len: size_t,
|
||||
len: usize,
|
||||
) -> zend_result;
|
||||
}
|
||||
extern "C" {
|
||||
@ -345,7 +346,7 @@ extern "C" {
|
||||
pub fn zend_hash_str_find(
|
||||
ht: *const HashTable,
|
||||
key: *const ::std::os::raw::c_char,
|
||||
len: size_t,
|
||||
len: usize,
|
||||
) -> *mut zval;
|
||||
}
|
||||
extern "C" {
|
||||
@ -548,7 +549,7 @@ pub struct _zend_class_entry {
|
||||
unsafe extern "C" fn(
|
||||
object: *mut zval,
|
||||
buffer: *mut *mut ::std::os::raw::c_uchar,
|
||||
buf_len: *mut size_t,
|
||||
buf_len: *mut usize,
|
||||
data: *mut zend_serialize_data,
|
||||
) -> ::std::os::raw::c_int,
|
||||
>,
|
||||
@ -557,7 +558,7 @@ pub struct _zend_class_entry {
|
||||
object: *mut zval,
|
||||
ce: *mut zend_class_entry,
|
||||
buf: *const ::std::os::raw::c_uchar,
|
||||
buf_len: size_t,
|
||||
buf_len: usize,
|
||||
data: *mut zend_unserialize_data,
|
||||
) -> ::std::os::raw::c_int,
|
||||
>,
|
||||
@ -982,7 +983,15 @@ pub struct _zend_execute_data {
|
||||
pub run_time_cache: *mut *mut ::std::os::raw::c_void,
|
||||
pub extra_named_params: *mut zend_array,
|
||||
}
|
||||
pub type sigjmp_buf = [::std::os::raw::c_int; 49usize];
|
||||
pub type __jmp_buf = [::std::os::raw::c_long; 8usize];
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct __jmp_buf_tag {
|
||||
pub __jmpbuf: __jmp_buf,
|
||||
pub __mask_was_saved: ::std::os::raw::c_int,
|
||||
pub __saved_mask: __sigset_t,
|
||||
}
|
||||
pub type jmp_buf = [__jmp_buf_tag; 1usize];
|
||||
pub type zend_executor_globals = _zend_executor_globals;
|
||||
extern "C" {
|
||||
pub static mut executor_globals: zend_executor_globals;
|
||||
@ -1043,7 +1052,7 @@ pub struct _zend_executor_globals {
|
||||
pub symtable_cache_ptr: *mut *mut zend_array,
|
||||
pub symbol_table: zend_array,
|
||||
pub included_files: HashTable,
|
||||
pub bailout: *mut sigjmp_buf,
|
||||
pub bailout: *mut jmp_buf,
|
||||
pub error_reporting: ::std::os::raw::c_int,
|
||||
pub exit_status: ::std::os::raw::c_int,
|
||||
pub function_table: *mut HashTable,
|
||||
@ -1052,7 +1061,7 @@ pub struct _zend_executor_globals {
|
||||
pub vm_stack_top: *mut zval,
|
||||
pub vm_stack_end: *mut zval,
|
||||
pub vm_stack: zend_vm_stack,
|
||||
pub vm_stack_page_size: size_t,
|
||||
pub vm_stack_page_size: usize,
|
||||
pub current_execute_data: *mut _zend_execute_data,
|
||||
pub fake_scope: *mut zend_class_entry,
|
||||
pub jit_trace_num: u32,
|
||||
@ -1149,7 +1158,7 @@ pub struct _zend_module_entry {
|
||||
>,
|
||||
pub info_func: ::std::option::Option<unsafe extern "C" fn(zend_module: *mut zend_module_entry)>,
|
||||
pub version: *const ::std::os::raw::c_char,
|
||||
pub globals_size: size_t,
|
||||
pub globals_size: usize,
|
||||
pub globals_ptr: *mut ::std::os::raw::c_void,
|
||||
pub globals_ctor:
|
||||
::std::option::Option<unsafe extern "C" fn(global: *mut ::std::os::raw::c_void)>,
|
||||
@ -1185,7 +1194,7 @@ pub struct _zend_vm_stack {
|
||||
pub prev: zend_vm_stack,
|
||||
}
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct _zend_function_entry {
|
||||
pub fname: *const ::std::os::raw::c_char,
|
||||
pub handler: zif_handler,
|
||||
@ -1211,7 +1220,7 @@ extern "C" {
|
||||
pub fn zend_declare_property(
|
||||
ce: *mut zend_class_entry,
|
||||
name: *const ::std::os::raw::c_char,
|
||||
name_length: size_t,
|
||||
name_length: usize,
|
||||
property: *mut zval,
|
||||
access_type: ::std::os::raw::c_int,
|
||||
);
|
||||
@ -1220,7 +1229,7 @@ extern "C" {
|
||||
pub fn zend_declare_class_constant(
|
||||
ce: *mut zend_class_entry,
|
||||
name: *const ::std::os::raw::c_char,
|
||||
name_length: size_t,
|
||||
name_length: usize,
|
||||
value: *mut zval,
|
||||
);
|
||||
}
|
||||
@ -1286,7 +1295,7 @@ extern "C" {
|
||||
pub fn zend_wrong_parameters_count_error(min_num_args: u32, max_num_args: u32);
|
||||
}
|
||||
extern "C" {
|
||||
pub fn php_printf(format: *const ::std::os::raw::c_char, ...) -> size_t;
|
||||
pub fn php_printf(format: *const ::std::os::raw::c_char, ...) -> usize;
|
||||
}
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
@ -1318,7 +1327,7 @@ pub struct _zend_ini_entry {
|
||||
extern "C" {
|
||||
pub fn zend_register_bool_constant(
|
||||
name: *const ::std::os::raw::c_char,
|
||||
name_len: size_t,
|
||||
name_len: usize,
|
||||
bval: bool,
|
||||
flags: ::std::os::raw::c_int,
|
||||
module_number: ::std::os::raw::c_int,
|
||||
@ -1327,7 +1336,7 @@ extern "C" {
|
||||
extern "C" {
|
||||
pub fn zend_register_long_constant(
|
||||
name: *const ::std::os::raw::c_char,
|
||||
name_len: size_t,
|
||||
name_len: usize,
|
||||
lval: zend_long,
|
||||
flags: ::std::os::raw::c_int,
|
||||
module_number: ::std::os::raw::c_int,
|
||||
@ -1336,7 +1345,7 @@ extern "C" {
|
||||
extern "C" {
|
||||
pub fn zend_register_double_constant(
|
||||
name: *const ::std::os::raw::c_char,
|
||||
name_len: size_t,
|
||||
name_len: usize,
|
||||
dval: f64,
|
||||
flags: ::std::os::raw::c_int,
|
||||
module_number: ::std::os::raw::c_int,
|
||||
@ -1345,7 +1354,7 @@ extern "C" {
|
||||
extern "C" {
|
||||
pub fn zend_register_string_constant(
|
||||
name: *const ::std::os::raw::c_char,
|
||||
name_len: size_t,
|
||||
name_len: usize,
|
||||
strval: *const ::std::os::raw::c_char,
|
||||
flags: ::std::os::raw::c_int,
|
||||
module_number: ::std::os::raw::c_int,
|
||||
@ -1408,27 +1417,23 @@ extern "C" {
|
||||
pub fn zend_do_implement_interface(ce: *mut zend_class_entry, iface: *mut zend_class_entry);
|
||||
}
|
||||
extern "C" {
|
||||
pub fn ext_php_rs_zend_string_init(
|
||||
str_: *const ::std::os::raw::c_char,
|
||||
len: size_t,
|
||||
persistent: bool,
|
||||
) -> *mut zend_string;
|
||||
pub static mut zend_ce_traversable: *mut zend_class_entry;
|
||||
}
|
||||
extern "C" {
|
||||
pub fn ext_php_rs_zend_string_release(zs: *mut zend_string);
|
||||
pub static mut zend_ce_aggregate: *mut zend_class_entry;
|
||||
}
|
||||
extern "C" {
|
||||
pub fn ext_php_rs_php_build_id() -> *const ::std::os::raw::c_char;
|
||||
pub static mut zend_ce_iterator: *mut zend_class_entry;
|
||||
}
|
||||
extern "C" {
|
||||
pub fn ext_php_rs_zend_object_alloc(
|
||||
obj_size: size_t,
|
||||
ce: *mut zend_class_entry,
|
||||
) -> *mut ::std::os::raw::c_void;
|
||||
pub static mut zend_ce_arrayaccess: *mut zend_class_entry;
|
||||
}
|
||||
extern "C" {
|
||||
pub fn ext_php_rs_zend_object_release(obj: *mut zend_object);
|
||||
pub static mut zend_ce_serializable: *mut zend_class_entry;
|
||||
}
|
||||
extern "C" {
|
||||
pub fn ext_php_rs_executor_globals() -> *mut zend_executor_globals;
|
||||
pub static mut zend_ce_countable: *mut zend_class_entry;
|
||||
}
|
||||
extern "C" {
|
||||
pub static mut zend_ce_stringable: *mut zend_class_entry;
|
||||
}
|
||||
|
@ -1,9 +1,15 @@
|
||||
# Summary
|
||||
|
||||
- [Introduction](./introduction.md)
|
||||
- [`cargo php`](./cargo-php.md)
|
||||
- [Examples](./examples/index.md)
|
||||
- [Hello World](./examples/hello_world.md)
|
||||
[Introduction](./introduction.md)
|
||||
|
||||
# Getting Started
|
||||
|
||||
- [Installation](./getting-started/installation.md)
|
||||
- [Hello World](./getting-started/hello_world.md)
|
||||
- [`cargo php`](./getting-started/cargo-php.md)
|
||||
|
||||
# Reference Guide
|
||||
|
||||
- [Types](./types/index.md)
|
||||
- [Primitive Numbers](./types/numbers.md)
|
||||
- [`String`](./types/string.md)
|
||||
|
@ -1,103 +0,0 @@
|
||||
# Hello World
|
||||
|
||||
Let's create a basic PHP extension. We will start by creating a new Rust library
|
||||
crate:
|
||||
|
||||
```sh
|
||||
$ cargo new hello_world --lib
|
||||
$ cd hello_world
|
||||
```
|
||||
|
||||
### `Cargo.toml`
|
||||
|
||||
Let's set up our crate by adding `ext-php-rs` as a dependency and setting the
|
||||
crate type to `cdylib`. Update the `Cargo.toml` to look something like so:
|
||||
|
||||
```toml
|
||||
[package]
|
||||
name = "hello_world"
|
||||
version = "0.1.0"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
ext-php-rs = "*"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
```
|
||||
|
||||
### `.cargo/config.toml`
|
||||
|
||||
When compiling for Linux and macOS, we do not link directly to PHP, rather PHP
|
||||
will dynamically load the library. We need to tell the linker it's ok to have
|
||||
undefined symbols (as they will be resolved when loaded by PHP).
|
||||
|
||||
On Windows, we also need to switch to using the `rust-lld` linker.
|
||||
|
||||
> Microsoft Visual C++'s `link.exe` is supported, however you may run into
|
||||
> issues if your linker is not compatible with the linker used to compile PHP.
|
||||
|
||||
We do this by creating a Cargo config file in `.cargo/config.toml` with the
|
||||
following contents:
|
||||
|
||||
```toml
|
||||
{{#include ../../../.cargo/config.toml}}
|
||||
```
|
||||
|
||||
### `src/lib.rs`
|
||||
|
||||
Let's actually write the extension code now. We start by importing the
|
||||
`ext-php-rs` prelude, which contains most of the imports required to make a
|
||||
basic extension. We will then write our basic `hello_world` function, which will
|
||||
take a string argument for the callers name, and we will return another string.
|
||||
Finally, we write a `get_module` function which is used by PHP to find out about
|
||||
your module. The `#[php_module]` attribute automatically registers your new
|
||||
function so we don't need to do anything except return the `ModuleBuilder` that
|
||||
we were given.
|
||||
|
||||
We also need to enable the `abi_vectorcall` feature when compiling for Windows.
|
||||
This is a nightly-only feature so it is recommended to use the `#[cfg_attr]`
|
||||
macro to not enable the feature on other operating systems.
|
||||
|
||||
```rust,ignore
|
||||
#![cfg_attr(windows, feature(abi_vectorcall))]
|
||||
use ext_php_rs::prelude::*;
|
||||
|
||||
#[php_function]
|
||||
pub fn hello_world(name: &str) -> String {
|
||||
format!("Hello, {}!", name)
|
||||
}
|
||||
|
||||
#[php_module]
|
||||
pub fn get_module(module: ModuleBuilder) -> ModuleBuilder {
|
||||
module
|
||||
}
|
||||
```
|
||||
|
||||
### `test.php`
|
||||
|
||||
Let's make a test script.
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
var_dump(hello_world("David"));
|
||||
```
|
||||
|
||||
Now let's build our extension and run our test script. This is done through
|
||||
`cargo` like any other Rust crate. It is required that the `php-config`
|
||||
executable is able to be found by the `ext-php-rs` build script.
|
||||
|
||||
The extension is stored inside `target/debug` (if you did a debug build,
|
||||
`target/release` for release builds). The file name will be based on your crate
|
||||
name, so for us it will be `libhello_world`. The extension is based on your OS -
|
||||
on Linux it will be `libhello_world.so`, on macOS it will be
|
||||
`libhello_world.dylib` and on Windows it will be `hello_world.dll` (no `lib`
|
||||
prefix).
|
||||
|
||||
```sh
|
||||
$ cargo build
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 0.01s
|
||||
$ php -dextension=./target/debug/libhello_world.dylib test.php
|
||||
string(13) "Hello, David!"
|
||||
```
|
@ -1,3 +0,0 @@
|
||||
# Examples
|
||||
|
||||
- [Hello World](./hello_world.md)
|
@ -164,6 +164,9 @@ OPTIONS:
|
||||
|
||||
--release
|
||||
Whether to install the release version of the extension
|
||||
|
||||
--yes
|
||||
Bypasses the confirmation prompt
|
||||
```
|
||||
|
||||
## Extension Removal
|
||||
@ -203,6 +206,9 @@ OPTIONS:
|
||||
--manifest <MANIFEST>
|
||||
Path to the Cargo manifest of the extension. Defaults to the manifest in the directory
|
||||
the command is called
|
||||
|
||||
--yes
|
||||
Bypasses the confirmation prompt
|
||||
```
|
||||
|
||||
[`cargo-php`]: https://crates.io/crates/cargo-php
|
162
guide/src/getting-started/hello_world.md
Normal file
162
guide/src/getting-started/hello_world.md
Normal file
@ -0,0 +1,162 @@
|
||||
# Hello World
|
||||
|
||||
## Project Setup
|
||||
|
||||
We will start by creating a new Rust library crate:
|
||||
|
||||
```sh
|
||||
$ cargo new hello_world --lib
|
||||
$ cd hello_world
|
||||
```
|
||||
|
||||
### `Cargo.toml`
|
||||
|
||||
Let's set up our crate by adding `ext-php-rs` as a dependency and setting the
|
||||
crate type to `cdylib`. Update the `Cargo.toml` to look something like so:
|
||||
|
||||
```toml
|
||||
[package]
|
||||
name = "hello_world"
|
||||
version = "0.1.0"
|
||||
edition = "2018"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
ext-php-rs = "*"
|
||||
|
||||
[profile.release]
|
||||
strip = "debuginfo"
|
||||
```
|
||||
|
||||
### `.cargo/config.toml`
|
||||
|
||||
When compiling for Linux and macOS, we do not link directly to PHP, rather PHP
|
||||
will dynamically load the library. We need to tell the linker it's ok to have
|
||||
undefined symbols (as they will be resolved when loaded by PHP).
|
||||
|
||||
On Windows, we also need to switch to using the `rust-lld` linker.
|
||||
|
||||
> Microsoft Visual C++'s `link.exe` is supported, however you may run into
|
||||
> issues if your linker is not compatible with the linker used to compile PHP.
|
||||
|
||||
We do this by creating a Cargo config file in `.cargo/config.toml` with the
|
||||
following contents:
|
||||
|
||||
```toml
|
||||
{{#include ../../../.cargo/config.toml}}
|
||||
```
|
||||
|
||||
## Writing our extension
|
||||
|
||||
### `src/lib.rs`
|
||||
|
||||
Let's actually write the extension code now. We start by importing the
|
||||
`ext-php-rs` prelude, which contains most of the imports required to make a
|
||||
basic extension. We will then write our basic `hello_world` function, which will
|
||||
take a string argument for the callers name, and we will return another string.
|
||||
Finally, we write a `get_module` function which is used by PHP to find out about
|
||||
your module. The `#[php_module]` attribute automatically registers your new
|
||||
function so we don't need to do anything except return the `ModuleBuilder` that
|
||||
we were given.
|
||||
|
||||
We also need to enable the `abi_vectorcall` feature when compiling for Windows.
|
||||
This is a nightly-only feature so it is recommended to use the `#[cfg_attr]`
|
||||
macro to not enable the feature on other operating systems.
|
||||
|
||||
```rust,ignore
|
||||
#![cfg_attr(windows, feature(abi_vectorcall))]
|
||||
use ext_php_rs::prelude::*;
|
||||
|
||||
#[php_function]
|
||||
pub fn hello_world(name: &str) -> String {
|
||||
format!("Hello, {}!", name)
|
||||
}
|
||||
|
||||
#[php_module]
|
||||
pub fn get_module(module: ModuleBuilder) -> ModuleBuilder {
|
||||
module
|
||||
}
|
||||
```
|
||||
|
||||
## Building the extension
|
||||
|
||||
Now let's build our extension.
|
||||
This is done through `cargo` like any other Rust crate.
|
||||
|
||||
If you installed php using a package manager in the previous chapter
|
||||
(or if the `php` and `php-config` binaries are already in your `$PATH`),
|
||||
then you can just run
|
||||
|
||||
```sh
|
||||
cargo build
|
||||
```
|
||||
|
||||
If you have multiple PHP versions in your PATH, or your installation
|
||||
resides in a custom location, you can use the following environment variables:
|
||||
|
||||
```sh
|
||||
# explicitly specifies the path to the PHP executable:
|
||||
export PHP=/path/to/php
|
||||
# explicitly specifies the path to the php-config executable:
|
||||
export PHP_CONFIG=/path/to/php-config
|
||||
```
|
||||
|
||||
As an alternative, if you compiled PHP from source and installed it under
|
||||
it's own prefix (`configure --prefix=/my/prefix`), you can just put
|
||||
this prefix in front of your PATH:
|
||||
|
||||
```sh
|
||||
export PATH="/my/prefix:${PATH}"
|
||||
```
|
||||
|
||||
Once you've setup these variables, you can just run
|
||||
|
||||
```sh
|
||||
cargo build
|
||||
```
|
||||
|
||||
Cargo will track changes to these environment variables and rebuild the library accordingly.
|
||||
|
||||
## Testing our extension
|
||||
|
||||
The extension we just built is stored inside the cargo target directory:
|
||||
`target/debug` if you did a debug build, `target/release` for release builds.
|
||||
|
||||
The extension file name is OS-dependent. The naming works as follows:
|
||||
|
||||
- let `S` be the empty string
|
||||
- append to `S` the value of [std::env::consts::DLL_PREFIX](https://doc.rust-lang.org/std/env/consts/constant.DLL_PREFIX.html)
|
||||
(empty on windows, `lib` on unixes)
|
||||
- append to `S` the lower-snake-case version of your crate name
|
||||
- append to `S` the value of [std::env::consts::DLL_SUFFIX](https://doc.rust-lang.org/std/env/consts/constant.DLL_SUFFIX.html)
|
||||
(`.dll` on windows, `.dylib` on macOS, `.so` on other unixes).
|
||||
- set the filename to the value of `S`
|
||||
|
||||
Which in our case would give us:
|
||||
|
||||
- linux: `libhello_world.so`
|
||||
- macOS: `libhello_world.dylib`
|
||||
- windows: `hello_world.dll`
|
||||
|
||||
Now we need a way to tell the PHP CLI binary to load our extension.
|
||||
There are [several ways to do that](https://www.phpinternalsbook.com/php7/build_system/building_extensions.html#loading-shared-extensions).
|
||||
For now we'll simply pass the `-d extension=/path/to/extension` option to the PHP CLI binary.
|
||||
|
||||
Let's make a test script:
|
||||
|
||||
### `test.php`
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
var_dump(hello_world("David"));
|
||||
```
|
||||
|
||||
And run it:
|
||||
|
||||
```sh
|
||||
$ php -d extension=./target/debug/libhello_world.so test.php
|
||||
string(13) "Hello, David!"
|
||||
```
|
68
guide/src/getting-started/installation.md
Normal file
68
guide/src/getting-started/installation.md
Normal file
@ -0,0 +1,68 @@
|
||||
# Installation
|
||||
|
||||
To get started using `ext-php-rs` you will need both a Rust toolchain
|
||||
and a PHP development environment. We'll cover each of these below.
|
||||
|
||||
## Rust toolchain
|
||||
|
||||
First, make sure you have rust installed on your system.
|
||||
If you haven't already done so you can do so by following the instructions [here](https://www.rust-lang.org/tools/install).
|
||||
`ext-php-rs` runs on both the stable and nightly versions so you can choose whichever one fits you best.
|
||||
|
||||
## PHP development environment
|
||||
|
||||
In order to develop PHP extensions, you'll need the following installed on your system:
|
||||
|
||||
1. The PHP CLI executable itself
|
||||
2. The PHP development headers
|
||||
3. The `php-config` binary
|
||||
|
||||
While the easiest way to get started is to use the packages provided by your distribution,
|
||||
we recommend building PHP from source.
|
||||
|
||||
**NB:** To use `ext-php-rs` you'll need at least PHP 8.0.
|
||||
|
||||
### Using a package manager
|
||||
|
||||
```sh
|
||||
# Debian and derivatives
|
||||
apt install php-dev
|
||||
# Arch Linux
|
||||
pacman -S php
|
||||
# Fedora
|
||||
dnf install php-devel
|
||||
# Homebrew
|
||||
brew install php
|
||||
```
|
||||
|
||||
### Compiling PHP from source
|
||||
|
||||
Please refer to this [PHP internals book chapter](https://www.phpinternalsbook.com/php7/build_system/building_php.html)
|
||||
for an in-depth guide on how to build PHP from source.
|
||||
|
||||
**TL;DR;** use the following commands to build a minimal development version
|
||||
with debug symbols enabled.
|
||||
|
||||
```sh
|
||||
# clone the php-src repository
|
||||
git clone https://github.com/php/php-src.git
|
||||
cd php-src
|
||||
# by default you will be on the master branch, which is the current
|
||||
# development version. You can check out a stable branch instead:
|
||||
git checkout PHP-8.1
|
||||
./buildconf
|
||||
PREFIX="${HOME}/build/php"
|
||||
./configure --prefix="${PREFIX}" \
|
||||
--enable-debug \
|
||||
--disable-all --disable-cgi
|
||||
make -j "$(nproc)"
|
||||
make install
|
||||
```
|
||||
|
||||
The PHP CLI binary should now be located at `${PREFIX}/bin/php`
|
||||
and the `php-config` binary at `${PREFIX}/bin/php-config`.
|
||||
|
||||
## Next steps
|
||||
|
||||
Now that we have our development environment in place,
|
||||
let's go [build an extension](./hello_world.md) !
|
@ -32,6 +32,34 @@ You can rename the property with options:
|
||||
- `rename` - Allows you to rename the property, e.g.
|
||||
`#[prop(rename = "new_name")]`
|
||||
|
||||
## Restrictions
|
||||
|
||||
### No lifetime parameters
|
||||
|
||||
Rust lifetimes are used by the Rust compiler to reason about a program's memory safety.
|
||||
They are a compile-time only concept;
|
||||
there is no way to access Rust lifetimes at runtime from a dynamic language like PHP.
|
||||
|
||||
As soon as Rust data is exposed to PHP,
|
||||
there is no guarantee which the Rust compiler can make on how long the data will live.
|
||||
PHP is a reference-counted language and those references can be held
|
||||
for an arbitrarily long time, which is untraceable by the Rust compiler.
|
||||
The only possible way to express this correctly is to require that any `#[php_class]`
|
||||
does not borrow data for any lifetime shorter than the `'static` lifetime,
|
||||
i.e. the `#[php_class]` cannot have any lifetime parameters.
|
||||
|
||||
When you need to share ownership of data between PHP and Rust,
|
||||
instead of using borrowed references with lifetimes, consider using
|
||||
reference-counted smart pointers such as [Arc](https://doc.rust-lang.org/std/sync/struct.Arc.html).
|
||||
|
||||
### No generic parameters
|
||||
|
||||
A Rust struct `Foo<T>` with a generic parameter `T` generates new compiled implementations
|
||||
each time it is used with a different concrete type for `T`.
|
||||
These new implementations are generated by the compiler at each usage site.
|
||||
This is incompatible with wrapping `Foo` in PHP,
|
||||
where there needs to be a single compiled implementation of `Foo` which is integrated with the PHP interpreter.
|
||||
|
||||
## Example
|
||||
|
||||
This example creates a PHP class `Human`, adding a PHP property `address`.
|
||||
@ -79,3 +107,56 @@ pub fn throw_exception() -> PhpResult<i32> {
|
||||
# }
|
||||
# fn main() {}
|
||||
```
|
||||
|
||||
## Implementing an Interface
|
||||
|
||||
To implement an interface, use `#[implements(ce)]` where `ce` is an expression returning a `ClassEntry`.
|
||||
The following example implements [`ArrayAccess`](https://www.php.net/manual/en/class.arrayaccess.php):
|
||||
```rust,no_run
|
||||
# #![cfg_attr(windows, feature(abi_vectorcall))]
|
||||
# extern crate ext_php_rs;
|
||||
use ext_php_rs::prelude::*;
|
||||
use ext_php_rs::{exception::PhpResult, types::Zval, zend::ce};
|
||||
|
||||
#[php_class]
|
||||
#[implements(ce::arrayaccess())]
|
||||
#[derive(Default)]
|
||||
pub struct EvenNumbersArray;
|
||||
|
||||
/// Returns `true` if the array offset is an even number.
|
||||
/// Usage:
|
||||
/// ```php
|
||||
/// $arr = new EvenNumbersArray();
|
||||
/// var_dump($arr[0]); // true
|
||||
/// var_dump($arr[1]); // false
|
||||
/// var_dump($arr[2]); // true
|
||||
/// var_dump($arr[3]); // false
|
||||
/// var_dump($arr[4]); // true
|
||||
/// var_dump($arr[5] = true); // Fatal error: Uncaught Exception: Setting values is not supported
|
||||
/// ```
|
||||
#[php_impl]
|
||||
impl EvenNumbersArray {
|
||||
pub fn __construct() -> EvenNumbersArray {
|
||||
EvenNumbersArray {}
|
||||
}
|
||||
// We need to use `Zval` because ArrayAccess needs $offset to be a `mixed`
|
||||
pub fn offset_exists(&self, offset: &'_ Zval) -> bool {
|
||||
offset.is_long()
|
||||
}
|
||||
pub fn offset_get(&self, offset: &'_ Zval) -> PhpResult<bool> {
|
||||
let integer_offset = offset.long().ok_or("Expected integer offset")?;
|
||||
Ok(integer_offset % 2 == 0)
|
||||
}
|
||||
pub fn offset_set(&mut self, _offset: &'_ Zval, _value: &'_ Zval) -> PhpResult {
|
||||
Err("Setting values is not supported".into())
|
||||
}
|
||||
pub fn offset_unset(&mut self, _offset: &'_ Zval) -> PhpResult {
|
||||
Err("Setting values is not supported".into())
|
||||
}
|
||||
}
|
||||
# #[php_module]
|
||||
# pub fn get_module(module: ModuleBuilder) -> ModuleBuilder {
|
||||
# module
|
||||
# }
|
||||
# fn main() {}
|
||||
```
|
||||
|
@ -118,7 +118,7 @@ impl<'a> Arg<'a> {
|
||||
/// return value of the function, or an error.
|
||||
///
|
||||
/// You should not call this function directly, rather through the
|
||||
/// [`call_user_func`] macro.
|
||||
/// [`call_user_func`](crate::call_user_func) macro.
|
||||
///
|
||||
/// # Parameters
|
||||
///
|
||||
|
@ -6,16 +6,11 @@
|
||||
|
||||
use crate::ffi::zend_string;
|
||||
|
||||
use std::{convert::TryFrom, ops::Deref, slice::from_raw_parts};
|
||||
use std::{ops::Deref, slice::from_raw_parts};
|
||||
|
||||
use crate::{
|
||||
convert::FromZval,
|
||||
error::{Error, Result},
|
||||
flags::DataType,
|
||||
types::Zval,
|
||||
};
|
||||
use crate::{convert::FromZval, flags::DataType, types::Zval};
|
||||
|
||||
/// Acts as a wrapper around [`&[T]`] where `T` implements [`PackSlice`].
|
||||
/// Acts as a wrapper around `&[T]` where `T` implements [`PackSlice`].
|
||||
/// Primarily used for passing read-only binary data into Rust functions.
|
||||
#[derive(Debug)]
|
||||
pub struct BinarySlice<'a, T>(&'a [T])
|
||||
@ -47,28 +42,17 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> FromZval<'_> for BinarySlice<'_, T>
|
||||
impl<'a, T> FromZval<'a> for BinarySlice<'a, T>
|
||||
where
|
||||
T: PackSlice,
|
||||
{
|
||||
const TYPE: DataType = DataType::String;
|
||||
|
||||
fn from_zval(zval: &Zval) -> Option<Self> {
|
||||
fn from_zval(zval: &'a Zval) -> Option<Self> {
|
||||
zval.binary_slice().map(BinarySlice)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> TryFrom<Zval> for BinarySlice<'_, T>
|
||||
where
|
||||
T: PackSlice,
|
||||
{
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: Zval) -> Result<Self> {
|
||||
Self::from_zval(&value).ok_or_else(|| Error::ZvalConversion(value.get_type()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> From<BinarySlice<'a, T>> for &'a [T]
|
||||
where
|
||||
T: PackSlice,
|
||||
@ -117,7 +101,7 @@ pub unsafe trait PackSlice: Clone {
|
||||
/// * `s` - The Zend string containing the binary data.
|
||||
///
|
||||
/// [`pack`]: https://www.php.net/manual/en/function.pack.php
|
||||
fn unpack_into<'a>(s: &zend_string) -> &'a [Self];
|
||||
fn unpack_into(s: &zend_string) -> &[Self];
|
||||
}
|
||||
|
||||
/// Implements the [`PackSlice`] trait for a given type.
|
||||
@ -128,7 +112,7 @@ macro_rules! pack_slice_impl {
|
||||
|
||||
($t: ty, $d: expr) => {
|
||||
unsafe impl PackSlice for $t {
|
||||
fn unpack_into<'a>(s: &zend_string) -> &'a [Self] {
|
||||
fn unpack_into(s: &zend_string) -> &[Self] {
|
||||
let bytes = ($d / 8) as usize;
|
||||
let len = (s.len as usize) / bytes;
|
||||
let ptr = s.val.as_ptr() as *const $t;
|
||||
|
@ -85,7 +85,7 @@ impl ClassBuilder {
|
||||
/// * `func` - The function entry to add to the class.
|
||||
/// * `flags` - Flags relating to the function. See [`MethodFlags`].
|
||||
pub fn method(mut self, mut func: FunctionEntry, flags: MethodFlags) -> Self {
|
||||
func.flags = flags.bits();
|
||||
func.flags |= flags.bits();
|
||||
self.methods.push(func);
|
||||
self
|
||||
}
|
||||
@ -226,7 +226,7 @@ impl ClassBuilder {
|
||||
///
|
||||
/// Returns an [`Error`] variant if the class could not be registered.
|
||||
pub fn build(mut self) -> Result<&'static mut ClassEntry> {
|
||||
self.ce.name = ZendStr::new_interned(&self.name, true)?.into_raw();
|
||||
self.ce.name = ZendStr::new_interned(&self.name, true).into_raw();
|
||||
|
||||
self.methods.push(FunctionEntry::end());
|
||||
let func = Box::into_raw(self.methods.into_boxed_slice()) as *const FunctionEntry;
|
||||
@ -284,7 +284,7 @@ impl ClassBuilder {
|
||||
zend_declare_class_constant(
|
||||
class,
|
||||
CString::new(name.as_str())?.as_ptr(),
|
||||
name.len() as u64,
|
||||
name.len(),
|
||||
value,
|
||||
)
|
||||
};
|
||||
|
@ -1,7 +1,7 @@
|
||||
use crate::{
|
||||
args::{Arg, ArgInfo},
|
||||
error::{Error, Result},
|
||||
flags::DataType,
|
||||
flags::{DataType, MethodFlags},
|
||||
types::Zval,
|
||||
zend::{ExecuteData, FunctionEntry, ZendType},
|
||||
};
|
||||
@ -64,6 +64,30 @@ impl<'a> FunctionBuilder<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new function builder for an abstract function that can be used
|
||||
/// on an abstract class or an interface.
|
||||
///
|
||||
/// # Parameters
|
||||
///
|
||||
/// * `name` - The name of the function.
|
||||
pub fn new_abstract<T: Into<String>>(name: T) -> Self {
|
||||
Self {
|
||||
name: name.into(),
|
||||
function: FunctionEntry {
|
||||
fname: ptr::null(),
|
||||
handler: None,
|
||||
arg_info: ptr::null(),
|
||||
num_args: 0,
|
||||
flags: MethodFlags::Abstract.bits(),
|
||||
},
|
||||
args: vec![],
|
||||
n_req: None,
|
||||
retval: None,
|
||||
ret_as_ref: false,
|
||||
ret_as_null: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a constructor builder, used to build the constructor
|
||||
/// for classes.
|
||||
///
|
||||
|
@ -153,6 +153,7 @@ impl ToStub for Parameter {
|
||||
|
||||
impl ToStub for DataType {
|
||||
fn fmt_stub(&self, buf: &mut String) -> FmtResult {
|
||||
let mut fqdn = "\\".to_owned();
|
||||
write!(
|
||||
buf,
|
||||
"{}",
|
||||
@ -162,7 +163,10 @@ impl ToStub for DataType {
|
||||
DataType::Double => "float",
|
||||
DataType::String => "string",
|
||||
DataType::Array => "array",
|
||||
DataType::Object(Some(ty)) => ty,
|
||||
DataType::Object(Some(ty)) => {
|
||||
fqdn.push_str(ty);
|
||||
fqdn.as_str()
|
||||
}
|
||||
DataType::Object(None) => "object",
|
||||
DataType::Resource => "resource",
|
||||
DataType::Reference => "reference",
|
||||
|
@ -48,6 +48,8 @@ pub enum Error {
|
||||
/// The string could not be converted into a C-string due to the presence of
|
||||
/// a NUL character.
|
||||
InvalidCString,
|
||||
/// The string could not be converted into a valid Utf8 string
|
||||
InvalidUtf8,
|
||||
/// Could not call the given function.
|
||||
Callable,
|
||||
/// An invalid exception type was thrown.
|
||||
@ -82,6 +84,7 @@ impl Display for Error {
|
||||
f,
|
||||
"String given contains NUL-bytes which cannot be present in a C string."
|
||||
),
|
||||
Error::InvalidUtf8 => write!(f, "Invalid Utf8 byte sequence."),
|
||||
Error::Callable => write!(f, "Could not call given function."),
|
||||
Error::InvalidException(flags) => {
|
||||
write!(f, "Invalid exception type was thrown: {:?}", flags)
|
||||
|
@ -19,6 +19,9 @@ extern "C" {
|
||||
persistent: bool,
|
||||
) -> *mut zend_string;
|
||||
pub fn ext_php_rs_zend_string_release(zs: *mut zend_string);
|
||||
pub fn ext_php_rs_is_known_valid_utf8(zs: *const zend_string) -> bool;
|
||||
pub fn ext_php_rs_set_known_valid_utf8(zs: *mut zend_string);
|
||||
|
||||
pub fn ext_php_rs_php_build_id() -> *const c_char;
|
||||
pub fn ext_php_rs_zend_object_alloc(obj_size: usize, ce: *mut zend_class_entry) -> *mut c_void;
|
||||
pub fn ext_php_rs_zend_object_release(obj: *mut zend_object);
|
||||
|
35
src/lib.rs
35
src/lib.rs
@ -139,8 +139,8 @@ pub use ext_php_rs_derive::php_const;
|
||||
/// ```
|
||||
///
|
||||
/// [`strpos`]: https://www.php.net/manual/en/function.strpos.php
|
||||
/// [`IntoZval`]: ext_php_rs::php::types::zval::IntoZval
|
||||
/// [`Zval`]: ext_php_rs::php::types::zval::Zval
|
||||
/// [`IntoZval`]: crate::convert::IntoZval
|
||||
/// [`Zval`]: crate::types::Zval
|
||||
pub use ext_php_rs_derive::php_extern;
|
||||
|
||||
/// Attribute used to annotate a function as a PHP function.
|
||||
@ -244,7 +244,7 @@ pub use ext_php_rs_derive::php_extern;
|
||||
/// ```
|
||||
///
|
||||
/// Parameters can also be deemed optional by passing the parameter name in the
|
||||
/// attribute options. This function takes one required parameter (`hello`) and
|
||||
/// attribute options. This function takes one required parameter (`name`) and
|
||||
/// two optional parameters (`description` and `age`).
|
||||
///
|
||||
/// ```
|
||||
@ -289,12 +289,12 @@ pub use ext_php_rs_derive::php_extern;
|
||||
///
|
||||
/// [`Result<T, E>`]: std::result::Result
|
||||
/// [`FunctionBuilder`]: crate::php::function::FunctionBuilder
|
||||
/// [`FromZval`]: crate::php::types::zval::FromZval
|
||||
/// [`IntoZval`]: crate::php::types::zval::IntoZval
|
||||
/// [`Zval`]: crate::php::types::zval::Zval
|
||||
/// [`Binary<T>`]: crate::php::types::binary::Binary
|
||||
/// [`ZendCallable`]: crate::php::types::callable::ZendCallable
|
||||
/// [`PhpException`]: crate::php::exceptions::PhpException
|
||||
/// [`FromZval`]: crate::convert::FromZval
|
||||
/// [`IntoZval`]: crate::convert::IntoZval
|
||||
/// [`Zval`]: crate::types::Zval.
|
||||
/// [`Binary<T>`]: crate::binary::Binary
|
||||
/// [`ZendCallable`]: crate::types::ZendCallable
|
||||
/// [`PhpException`]: crate::exception::PhpException
|
||||
pub use ext_php_rs_derive::php_function;
|
||||
|
||||
/// Annotates a structs `impl` block, declaring that all methods and constants
|
||||
@ -518,6 +518,11 @@ pub use ext_php_rs_derive::php_class;
|
||||
/// this macro if you have registered any classes or constants when using the
|
||||
/// [`macro@php_module`] macro.
|
||||
///
|
||||
/// The attribute accepts one optional flag -- `#[php_startup(before)]` --
|
||||
/// which forces the annotated function to be called _before_ the other classes
|
||||
/// and constants are registered. By default the annotated function is called
|
||||
/// after these classes and constants are registered.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
@ -665,12 +670,12 @@ pub use ext_php_rs_derive::php_startup;
|
||||
/// var_dump(give_union()); // int(5)
|
||||
/// ```
|
||||
///
|
||||
/// [`FromZval`]: crate::php::types::zval::FromZval
|
||||
/// [`IntoZval`]: crate::php::types::zval::IntoZval
|
||||
/// [`FromZendObject`]: crate::php::types::object::FromZendObject
|
||||
/// [`IntoZendObject`]: crate::php::types::object::IntoZendObject
|
||||
/// [`Zval`]: crate::php::types::zval::Zval
|
||||
/// [`Zval::string`]: crate::php::types::zval::Zval::string
|
||||
/// [`FromZval`]: crate::convert::FromZval
|
||||
/// [`IntoZval`]: crate::convert::IntoZval
|
||||
/// [`FromZendObject`]: crate::convert::FromZendObject
|
||||
/// [`IntoZendObject`]: crate::convert::IntoZendObject
|
||||
/// [`Zval`]: crate::types::Zval.
|
||||
/// [`Zval::string`]: crate::types::Zval.::string
|
||||
pub use ext_php_rs_derive::ZvalConvert;
|
||||
|
||||
/// Defines an `extern` function with the Zend fastcall convention based on
|
||||
|
@ -312,14 +312,7 @@ impl ZendHashTable {
|
||||
V: IntoZval,
|
||||
{
|
||||
let mut val = val.into_zval(false)?;
|
||||
unsafe {
|
||||
zend_hash_str_update(
|
||||
self,
|
||||
CString::new(key)?.as_ptr(),
|
||||
key.len() as u64,
|
||||
&mut val,
|
||||
)
|
||||
};
|
||||
unsafe { zend_hash_str_update(self, CString::new(key)?.as_ptr(), key.len(), &mut val) };
|
||||
val.release();
|
||||
Ok(())
|
||||
}
|
||||
|
@ -80,6 +80,17 @@ impl ZendObject {
|
||||
unsafe { ZBox::from_raw(this.get_mut_zend_obj()) }
|
||||
}
|
||||
|
||||
/// Returns the [`ClassEntry`] associated with this object.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the class entry is invalid.
|
||||
pub fn get_class_entry(&self) -> &'static ClassEntry {
|
||||
// SAFETY: it is OK to panic here since PHP would segfault anyway
|
||||
// when encountering an object with no class entry.
|
||||
unsafe { self.ce.as_ref() }.expect("Could not retrieve class entry.")
|
||||
}
|
||||
|
||||
/// Attempts to retrieve the class name of the object.
|
||||
pub fn get_class_name(&self) -> Result<String> {
|
||||
unsafe {
|
||||
@ -91,8 +102,21 @@ impl ZendObject {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns whether this object is an instance of the given [`ClassEntry`].
|
||||
///
|
||||
/// This method checks the class and interface inheritance chain.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the class entry is invalid.
|
||||
pub fn instance_of(&self, ce: &ClassEntry) -> bool {
|
||||
self.get_class_entry().instance_of(ce)
|
||||
}
|
||||
|
||||
/// Checks if the given object is an instance of a registered class with
|
||||
/// Rust type `T`.
|
||||
///
|
||||
/// This method doesn't check the class and interface inheritance chain.
|
||||
pub fn is_instance<T: RegisteredClass>(&self) -> bool {
|
||||
(self.ce as *const ClassEntry).eq(&(T::get_metadata().ce() as *const _))
|
||||
}
|
||||
@ -113,7 +137,7 @@ impl ZendObject {
|
||||
return Err(Error::InvalidProperty);
|
||||
}
|
||||
|
||||
let mut name = ZendStr::new(name, false)?;
|
||||
let mut name = ZendStr::new(name, false);
|
||||
let mut rv = Zval::new();
|
||||
|
||||
let zv = unsafe {
|
||||
@ -138,7 +162,7 @@ impl ZendObject {
|
||||
/// * `name` - The name of the property.
|
||||
/// * `value` - The value to set the property to.
|
||||
pub fn set_property(&mut self, name: &str, value: impl IntoZval) -> Result<()> {
|
||||
let mut name = ZendStr::new(name, false)?;
|
||||
let mut name = ZendStr::new(name, false);
|
||||
let mut value = value.into_zval(false)?;
|
||||
|
||||
unsafe {
|
||||
@ -163,7 +187,7 @@ impl ZendObject {
|
||||
/// * `name` - The name of the property.
|
||||
/// * `query` - The 'query' to classify if a property exists.
|
||||
pub fn has_property(&self, name: &str, query: PropertyQuery) -> Result<bool> {
|
||||
let mut name = ZendStr::new(name, false)?;
|
||||
let mut name = ZendStr::new(name, false);
|
||||
|
||||
Ok(unsafe {
|
||||
self.handlers()?.has_property.ok_or(Error::InvalidScope)?(
|
||||
@ -196,6 +220,29 @@ impl ZendObject {
|
||||
T::from_zend_object(self)
|
||||
}
|
||||
|
||||
/// Returns an unique identifier for the object.
|
||||
///
|
||||
/// The id is guaranteed to be unique for the lifetime of the object.
|
||||
/// Once the object is destroyed, it may be reused for other objects.
|
||||
/// This is equivalent to calling the [`spl_object_id`] PHP function.
|
||||
///
|
||||
/// [`spl_object_id`]: https://www.php.net/manual/function.spl-object-id
|
||||
#[inline]
|
||||
pub fn get_id(&self) -> u32 {
|
||||
self.handle
|
||||
}
|
||||
|
||||
/// Computes an unique hash for the object.
|
||||
///
|
||||
/// The hash is guaranteed to be unique for the lifetime of the object.
|
||||
/// Once the object is destroyed, it may be reused for other objects.
|
||||
/// This is equivalent to calling the [`spl_object_hash`] PHP function.
|
||||
///
|
||||
/// [`spl_object_hash`]: https://www.php.net/manual/function.spl-object-hash.php
|
||||
pub fn hash(&self) -> String {
|
||||
format!("{:016x}0000000000000000", self.handle)
|
||||
}
|
||||
|
||||
/// Attempts to retrieve a reference to the object handlers.
|
||||
#[inline]
|
||||
unsafe fn handlers(&self) -> Result<&ZendObjectHandlers> {
|
||||
|
@ -16,6 +16,7 @@ use crate::{
|
||||
convert::{FromZval, IntoZval},
|
||||
error::{Error, Result},
|
||||
ffi::{
|
||||
ext_php_rs_is_known_valid_utf8, ext_php_rs_set_known_valid_utf8,
|
||||
ext_php_rs_zend_string_init, ext_php_rs_zend_string_release, zend_string,
|
||||
zend_string_init_interned,
|
||||
},
|
||||
@ -30,7 +31,7 @@ use crate::{
|
||||
/// cannot represent unsized types, an array of size 1 is used at the end of the
|
||||
/// type to represent the contents of the string, therefore this type is
|
||||
/// actually unsized. All constructors return [`ZBox<ZendStr>`], the owned
|
||||
/// varaint.
|
||||
/// variant.
|
||||
///
|
||||
/// Once the `ptr_metadata` feature lands in stable rust, this type can
|
||||
/// potentially be changed to a DST using slices and metadata. See the tracking issue here: <https://github.com/rust-lang/rust/issues/81513>
|
||||
@ -46,7 +47,7 @@ static INTERNED_LOCK: Mutex<()> = const_mutex(());
|
||||
// on the alias `ZendStr` :( <https://github.com/rust-lang/rust-clippy/issues/7702>
|
||||
#[allow(clippy::len_without_is_empty)]
|
||||
impl ZendStr {
|
||||
/// Creates a new Zend string from a [`str`].
|
||||
/// Creates a new Zend string from a slice of bytes.
|
||||
///
|
||||
/// # Parameters
|
||||
///
|
||||
@ -54,12 +55,6 @@ impl ZendStr {
|
||||
/// * `persistent` - Whether the string should persist through the request
|
||||
/// boundary.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// Returns a result containing the Zend string if successful. Returns an
|
||||
/// error if the given string contains NUL bytes, which cannot be
|
||||
/// contained inside a C string.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the function was unable to allocate memory for the Zend
|
||||
@ -78,10 +73,19 @@ impl ZendStr {
|
||||
/// ```no_run
|
||||
/// use ext_php_rs::types::ZendStr;
|
||||
///
|
||||
/// let s = ZendStr::new("Hello, world!", false).unwrap();
|
||||
/// let s = ZendStr::new("Hello, world!", false);
|
||||
/// let php = ZendStr::new([80, 72, 80], false);
|
||||
/// ```
|
||||
pub fn new(str: &str, persistent: bool) -> Result<ZBox<Self>> {
|
||||
Ok(Self::from_c_str(&CString::new(str)?, persistent))
|
||||
pub fn new(str: impl AsRef<[u8]>, persistent: bool) -> ZBox<Self> {
|
||||
let s = str.as_ref();
|
||||
// TODO: we should handle the special cases when length is either 0 or 1
|
||||
// see `zend_string_init_fast()` in `zend_string.h`
|
||||
unsafe {
|
||||
let ptr = ext_php_rs_zend_string_init(s.as_ptr().cast(), s.len(), persistent)
|
||||
.as_mut()
|
||||
.expect("Failed to allocate memory for new Zend string");
|
||||
ZBox::from_raw(ptr)
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new Zend string from a [`CStr`].
|
||||
@ -126,7 +130,7 @@ impl ZendStr {
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new interned Zend string from a [`str`].
|
||||
/// Creates a new interned Zend string from a slice of bytes.
|
||||
///
|
||||
/// An interned string is only ever stored once and is immutable. PHP stores
|
||||
/// the string in an internal hashtable which stores the interned
|
||||
@ -145,16 +149,12 @@ impl ZendStr {
|
||||
/// * `persistent` - Whether the string should persist through the request
|
||||
/// boundary.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// Returns a result containing the Zend string if successful. Returns an
|
||||
/// error if the given string contains NUL bytes, which cannot be
|
||||
/// contained inside a C string.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the function was unable to allocate memory for the Zend
|
||||
/// string.
|
||||
/// Panics under the following circumstances:
|
||||
///
|
||||
/// * The function used to create interned strings has not been set.
|
||||
/// * The function could not allocate enough memory for the Zend string.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
@ -171,8 +171,16 @@ impl ZendStr {
|
||||
///
|
||||
/// let s = ZendStr::new_interned("PHP", true);
|
||||
/// ```
|
||||
pub fn new_interned(str: &str, persistent: bool) -> Result<ZBox<Self>> {
|
||||
Ok(Self::interned_from_c_str(&CString::new(str)?, persistent))
|
||||
pub fn new_interned(str: impl AsRef<[u8]>, persistent: bool) -> ZBox<Self> {
|
||||
let _lock = INTERNED_LOCK.lock();
|
||||
let s = str.as_ref();
|
||||
unsafe {
|
||||
let init = zend_string_init_interned.expect("`zend_string_init_interned` not ready");
|
||||
let ptr = init(s.as_ptr().cast(), s.len() as _, persistent)
|
||||
.as_mut()
|
||||
.expect("Failed to allocate memory for new Zend string");
|
||||
ZBox::from_raw(ptr)
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new interned Zend string from a [`CStr`].
|
||||
@ -222,11 +230,8 @@ impl ZendStr {
|
||||
let _lock = INTERNED_LOCK.lock();
|
||||
|
||||
unsafe {
|
||||
let ptr = zend_string_init_interned.expect("`zend_string_init_interned` not ready")(
|
||||
str.as_ptr(),
|
||||
str.to_bytes().len() as _,
|
||||
persistent,
|
||||
);
|
||||
let init = zend_string_init_interned.expect("`zend_string_init_interned` not ready");
|
||||
let ptr = init(str.as_ptr(), str.to_bytes().len() as _, persistent);
|
||||
|
||||
ZBox::from_raw(
|
||||
ptr.as_mut()
|
||||
@ -242,7 +247,7 @@ impl ZendStr {
|
||||
/// ```no_run
|
||||
/// use ext_php_rs::types::ZendStr;
|
||||
///
|
||||
/// let s = ZendStr::new("hello, world!", false).unwrap();
|
||||
/// let s = ZendStr::new("hello, world!", false);
|
||||
/// assert_eq!(s.len(), 13);
|
||||
/// ```
|
||||
pub fn len(&self) -> usize {
|
||||
@ -256,39 +261,61 @@ impl ZendStr {
|
||||
/// ```no_run
|
||||
/// use ext_php_rs::types::ZendStr;
|
||||
///
|
||||
/// let s = ZendStr::new("hello, world!", false).unwrap();
|
||||
/// let s = ZendStr::new("hello, world!", false);
|
||||
/// assert_eq!(s.is_empty(), false);
|
||||
/// ```
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
|
||||
/// Returns a reference to the underlying [`CStr`] inside the Zend string.
|
||||
pub fn as_c_str(&self) -> &CStr {
|
||||
// SAFETY: Zend strings store their readable length in a fat pointer.
|
||||
unsafe {
|
||||
let slice = slice::from_raw_parts(self.val.as_ptr() as *const u8, self.len() + 1);
|
||||
CStr::from_bytes_with_nul_unchecked(slice)
|
||||
}
|
||||
/// Attempts to return a reference to the underlying bytes inside the Zend
|
||||
/// string as a [`CStr`].
|
||||
///
|
||||
/// Returns an [Error::InvalidCString] variant if the string contains null
|
||||
/// bytes.
|
||||
pub fn as_c_str(&self) -> Result<&CStr> {
|
||||
let bytes_with_null =
|
||||
unsafe { slice::from_raw_parts(self.val.as_ptr().cast(), self.len() + 1) };
|
||||
CStr::from_bytes_with_nul(bytes_with_null).map_err(|_| Error::InvalidCString)
|
||||
}
|
||||
|
||||
/// Attempts to return a reference to the underlying [`str`] inside the Zend
|
||||
/// Attempts to return a reference to the underlying bytes inside the Zend
|
||||
/// string.
|
||||
///
|
||||
/// Returns the [`None`] variant if the [`CStr`] contains non-UTF-8
|
||||
/// characters.
|
||||
/// Returns an [Error::InvalidUtf8] variant if the [`str`] contains
|
||||
/// non-UTF-8 characters.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// use ext_php_rs::types::ZendStr;
|
||||
///
|
||||
/// let s = ZendStr::new("hello, world!", false).unwrap();
|
||||
/// let as_str = s.as_str();
|
||||
/// assert_eq!(as_str, Some("hello, world!"));
|
||||
/// let s = ZendStr::new("hello, world!", false);
|
||||
/// assert!(s.as_str().is_ok());
|
||||
/// ```
|
||||
pub fn as_str(&self) -> Option<&str> {
|
||||
self.as_c_str().to_str().ok()
|
||||
pub fn as_str(&self) -> Result<&str> {
|
||||
if unsafe { ext_php_rs_is_known_valid_utf8(self.as_ptr()) } {
|
||||
let str = unsafe { std::str::from_utf8_unchecked(self.as_bytes()) };
|
||||
return Ok(str);
|
||||
}
|
||||
let str = std::str::from_utf8(self.as_bytes()).map_err(|_| Error::InvalidUtf8)?;
|
||||
unsafe { ext_php_rs_set_known_valid_utf8(self.as_ptr() as *mut _) };
|
||||
Ok(str)
|
||||
}
|
||||
|
||||
/// Returns a reference to the underlying bytes inside the Zend string.
|
||||
pub fn as_bytes(&self) -> &[u8] {
|
||||
unsafe { slice::from_raw_parts(self.val.as_ptr().cast(), self.len()) }
|
||||
}
|
||||
|
||||
/// Returns a raw pointer to this object
|
||||
pub fn as_ptr(&self) -> *const ZendStr {
|
||||
self as *const _
|
||||
}
|
||||
|
||||
/// Returns a mutable pointer to this object
|
||||
pub fn as_mut_ptr(&mut self) -> *mut ZendStr {
|
||||
self as *mut _
|
||||
}
|
||||
}
|
||||
|
||||
@ -300,7 +327,22 @@ unsafe impl ZBoxable for ZendStr {
|
||||
|
||||
impl Debug for ZendStr {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.as_c_str().fmt(f)
|
||||
self.as_str().fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for ZendStr {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
self.as_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> PartialEq<T> for ZendStr
|
||||
where
|
||||
T: AsRef<[u8]>,
|
||||
{
|
||||
fn eq(&self, other: &T) -> bool {
|
||||
self.as_ref() == other.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
@ -308,19 +350,14 @@ impl ToOwned for ZendStr {
|
||||
type Owned = ZBox<ZendStr>;
|
||||
|
||||
fn to_owned(&self) -> Self::Owned {
|
||||
Self::from_c_str(self.as_c_str(), false)
|
||||
Self::new(self.as_bytes(), false)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for ZendStr {
|
||||
#[inline]
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.as_c_str().eq(other.as_c_str())
|
||||
}
|
||||
}
|
||||
impl<'a> TryFrom<&'a ZendStr> for &'a CStr {
|
||||
type Error = Error;
|
||||
|
||||
impl<'a> From<&'a ZendStr> for &'a CStr {
|
||||
fn from(value: &'a ZendStr) -> Self {
|
||||
fn try_from(value: &'a ZendStr) -> Result<Self> {
|
||||
value.as_c_str()
|
||||
}
|
||||
}
|
||||
@ -329,7 +366,7 @@ impl<'a> TryFrom<&'a ZendStr> for &'a str {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: &'a ZendStr) -> Result<Self> {
|
||||
value.as_str().ok_or(Error::InvalidCString)
|
||||
value.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
@ -337,10 +374,7 @@ impl TryFrom<&ZendStr> for String {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: &ZendStr) -> Result<Self> {
|
||||
value
|
||||
.as_str()
|
||||
.map(|s| s.to_string())
|
||||
.ok_or(Error::InvalidCString)
|
||||
value.as_str().map(ToString::to_string)
|
||||
}
|
||||
}
|
||||
|
||||
@ -362,18 +396,14 @@ impl From<CString> for ZBox<ZendStr> {
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&str> for ZBox<ZendStr> {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: &str) -> Result<Self> {
|
||||
ZendStr::new(value, false)
|
||||
impl From<&str> for ZBox<ZendStr> {
|
||||
fn from(value: &str) -> Self {
|
||||
ZendStr::new(value.as_bytes(), false)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<String> for ZBox<ZendStr> {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: String) -> Result<Self> {
|
||||
impl From<String> for ZBox<ZendStr> {
|
||||
fn from(value: String) -> Self {
|
||||
ZendStr::new(value.as_str(), false)
|
||||
}
|
||||
}
|
||||
|
@ -113,12 +113,12 @@ impl Zval {
|
||||
/// convert other types into a [`String`], as it could not pass back a
|
||||
/// [`&str`] in those cases.
|
||||
pub fn str(&self) -> Option<&str> {
|
||||
self.zend_str().and_then(|zs| zs.as_str())
|
||||
self.zend_str().and_then(|zs| zs.as_str().ok())
|
||||
}
|
||||
|
||||
/// Returns the value of the zval if it is a string and can be unpacked into
|
||||
/// a vector of a given type. Similar to the [`unpack`](https://www.php.net/manual/en/function.unpack.php)
|
||||
/// in PHP, except you can only unpack one type.
|
||||
/// a vector of a given type. Similar to the [`unpack`] function in PHP,
|
||||
/// except you can only unpack one type.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
@ -129,22 +129,31 @@ impl Zval {
|
||||
/// documentation for more details.
|
||||
///
|
||||
/// [`pack`]: https://www.php.net/manual/en/function.pack.php
|
||||
/// [`unpack`]: https://www.php.net/manual/en/function.unpack.php
|
||||
pub fn binary<T: Pack>(&self) -> Option<Vec<T>> {
|
||||
if self.is_string() {
|
||||
// SAFETY: Type is string therefore we are able to take a reference.
|
||||
Some(T::unpack_into(unsafe { self.value.str_.as_ref() }?))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
self.zend_str().map(T::unpack_into)
|
||||
}
|
||||
|
||||
pub fn binary_slice<'a, T: PackSlice>(&self) -> Option<&'a [T]> {
|
||||
if self.is_string() {
|
||||
// SAFETY: Type is string therefore we are able to take a reference.
|
||||
Some(T::unpack_into(unsafe { self.value.str_.as_ref() }?))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
/// Returns the value of the zval if it is a string and can be unpacked into
|
||||
/// a slice of a given type. Similar to the [`unpack`] function in PHP,
|
||||
/// except you can only unpack one type.
|
||||
///
|
||||
/// This function is similar to [`Zval::binary`] except that a slice is
|
||||
/// returned instead of a vector, meaning the contents of the string is
|
||||
/// not copied.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// There is no way to tell if the data stored in the string is actually of
|
||||
/// the given type. The results of this function can also differ from
|
||||
/// platform-to-platform due to the different representation of some
|
||||
/// types on different platforms. Consult the [`pack`] function
|
||||
/// documentation for more details.
|
||||
///
|
||||
/// [`pack`]: https://www.php.net/manual/en/function.pack.php
|
||||
/// [`unpack`]: https://www.php.net/manual/en/function.unpack.php
|
||||
pub fn binary_slice<T: PackSlice>(&self) -> Option<&[T]> {
|
||||
self.zend_str().map(T::unpack_into)
|
||||
}
|
||||
|
||||
/// Returns the value of the zval if it is a resource.
|
||||
@ -331,7 +340,7 @@ impl Zval {
|
||||
/// * `val` - The value to set the zval as.
|
||||
/// * `persistent` - Whether the string should persist between requests.
|
||||
pub fn set_string(&mut self, val: &str, persistent: bool) -> Result<()> {
|
||||
self.set_zend_string(ZendStr::new(val, persistent)?);
|
||||
self.set_zend_string(ZendStr::new(val, persistent));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -365,7 +374,7 @@ impl Zval {
|
||||
/// * `val` - The value to set the zval as.
|
||||
/// * `persistent` - Whether the string should persist between requests.
|
||||
pub fn set_interned_string(&mut self, val: &str, persistent: bool) -> Result<()> {
|
||||
self.set_zend_string(ZendStr::new_interned(val, persistent)?);
|
||||
self.set_zend_string(ZendStr::new_interned(val, persistent));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
#include "wrapper.h"
|
||||
|
||||
zend_string *ext_php_rs_zend_string_init(const char *str, size_t len,
|
||||
bool persistent) {
|
||||
zend_string *ext_php_rs_zend_string_init(const char *str, size_t len, bool persistent) {
|
||||
return zend_string_init(str, len, persistent);
|
||||
}
|
||||
|
||||
@ -9,6 +8,16 @@ void ext_php_rs_zend_string_release(zend_string *zs) {
|
||||
zend_string_release(zs);
|
||||
}
|
||||
|
||||
bool ext_php_rs_is_known_valid_utf8(const zend_string *zs) {
|
||||
return GC_FLAGS(zs) & IS_STR_VALID_UTF8;
|
||||
}
|
||||
|
||||
void ext_php_rs_set_known_valid_utf8(zend_string *zs) {
|
||||
if (!ZSTR_IS_INTERNED(zs)) {
|
||||
GC_ADD_FLAGS(zs, IS_STR_VALID_UTF8);
|
||||
}
|
||||
}
|
||||
|
||||
const char *ext_php_rs_php_build_id() { return ZEND_MODULE_BUILD_ID; }
|
||||
|
||||
void *ext_php_rs_zend_object_alloc(size_t obj_size, zend_class_entry *ce) {
|
||||
|
@ -21,9 +21,11 @@
|
||||
#include "zend_inheritance.h"
|
||||
#include "zend_interfaces.h"
|
||||
|
||||
zend_string *ext_php_rs_zend_string_init(const char *str, size_t len,
|
||||
bool persistent);
|
||||
zend_string *ext_php_rs_zend_string_init(const char *str, size_t len, bool persistent);
|
||||
void ext_php_rs_zend_string_release(zend_string *zs);
|
||||
bool ext_php_rs_is_known_valid_utf8(const zend_string *zs);
|
||||
void ext_php_rs_set_known_valid_utf8(zend_string *zs);
|
||||
|
||||
const char *ext_php_rs_php_build_id();
|
||||
void *ext_php_rs_zend_object_alloc(size_t obj_size, zend_class_entry *ce);
|
||||
void ext_php_rs_zend_object_release(zend_object *obj);
|
||||
|
@ -3,70 +3,107 @@
|
||||
#![allow(clippy::unwrap_used)]
|
||||
|
||||
use crate::ffi::{
|
||||
zend_ce_argument_count_error, zend_ce_arithmetic_error, zend_ce_compile_error,
|
||||
zend_ce_division_by_zero_error, zend_ce_error_exception, zend_ce_exception,
|
||||
zend_ce_parse_error, zend_ce_throwable, zend_ce_type_error, zend_ce_unhandled_match_error,
|
||||
zend_ce_value_error, zend_standard_class_def,
|
||||
zend_ce_aggregate, zend_ce_argument_count_error, zend_ce_arithmetic_error, zend_ce_arrayaccess,
|
||||
zend_ce_compile_error, zend_ce_countable, zend_ce_division_by_zero_error,
|
||||
zend_ce_error_exception, zend_ce_exception, zend_ce_iterator, zend_ce_parse_error,
|
||||
zend_ce_serializable, zend_ce_stringable, zend_ce_throwable, zend_ce_traversable,
|
||||
zend_ce_type_error, zend_ce_unhandled_match_error, zend_ce_value_error,
|
||||
zend_standard_class_def,
|
||||
};
|
||||
|
||||
use super::ClassEntry;
|
||||
|
||||
/// Returns the base `stdClass` class.
|
||||
/// Returns the base [`stdClass`](https://www.php.net/manual/en/class.stdclass.php) class.
|
||||
pub fn stdclass() -> &'static ClassEntry {
|
||||
unsafe { zend_standard_class_def.as_ref() }.unwrap()
|
||||
}
|
||||
|
||||
/// Returns the base `Throwable` class.
|
||||
/// Returns the base [`Throwable`](https://www.php.net/manual/en/class.throwable.php) class.
|
||||
pub fn throwable() -> &'static ClassEntry {
|
||||
unsafe { zend_ce_throwable.as_ref() }.unwrap()
|
||||
}
|
||||
|
||||
/// Returns the base `Exception` class.
|
||||
/// Returns the base [`Exception`](https://www.php.net/manual/en/class.exception.php) class.
|
||||
pub fn exception() -> &'static ClassEntry {
|
||||
unsafe { zend_ce_exception.as_ref() }.unwrap()
|
||||
}
|
||||
|
||||
/// Returns the base `ErrorException` class.
|
||||
/// Returns the base [`ErrorException`](https://www.php.net/manual/en/class.errorexception.php) class.
|
||||
pub fn error_exception() -> &'static ClassEntry {
|
||||
unsafe { zend_ce_error_exception.as_ref() }.unwrap()
|
||||
}
|
||||
|
||||
/// Returns the base `CompileError` class.
|
||||
/// Returns the base [`CompileError`](https://www.php.net/manual/en/class.compileerror.php) class.
|
||||
pub fn compile_error() -> &'static ClassEntry {
|
||||
unsafe { zend_ce_compile_error.as_ref() }.unwrap()
|
||||
}
|
||||
|
||||
/// Returns the base `ParseError` class.
|
||||
/// Returns the base [`ParseError`](https://www.php.net/manual/en/class.parseerror.php) class.
|
||||
pub fn parse_error() -> &'static ClassEntry {
|
||||
unsafe { zend_ce_parse_error.as_ref() }.unwrap()
|
||||
}
|
||||
|
||||
/// Returns the base `TypeError` class.
|
||||
/// Returns the base [`TypeError`](https://www.php.net/manual/en/class.typeerror.php) class.
|
||||
pub fn type_error() -> &'static ClassEntry {
|
||||
unsafe { zend_ce_type_error.as_ref() }.unwrap()
|
||||
}
|
||||
|
||||
/// Returns the base `ArgumentCountError` class.
|
||||
/// Returns the base [`ArgumentCountError`](https://www.php.net/manual/en/class.argumentcounterror.php) class.
|
||||
pub fn argument_count_error() -> &'static ClassEntry {
|
||||
unsafe { zend_ce_argument_count_error.as_ref() }.unwrap()
|
||||
}
|
||||
|
||||
/// Returns the base `ValueError` class.
|
||||
/// Returns the base [`ValueError`](https://www.php.net/manual/en/class.valueerror.php) class.
|
||||
pub fn value_error() -> &'static ClassEntry {
|
||||
unsafe { zend_ce_value_error.as_ref() }.unwrap()
|
||||
}
|
||||
|
||||
/// Returns the base `ArithmeticError` class.
|
||||
/// Returns the base [`ArithmeticError`](https://www.php.net/manual/en/class.arithmeticerror.php) class.
|
||||
pub fn arithmetic_error() -> &'static ClassEntry {
|
||||
unsafe { zend_ce_arithmetic_error.as_ref() }.unwrap()
|
||||
}
|
||||
|
||||
/// Returns the base `DivisionByZeroError` class.
|
||||
/// Returns the base [`DivisionByZeroError`](https://www.php.net/manual/en/class.divisionbyzeroerror.php) class.
|
||||
pub fn division_by_zero_error() -> &'static ClassEntry {
|
||||
unsafe { zend_ce_division_by_zero_error.as_ref() }.unwrap()
|
||||
}
|
||||
|
||||
/// Returns the base `UnhandledMatchError` class.
|
||||
/// Returns the base [`UnhandledMatchError`](https://www.php.net/manual/en/class.unhandledmatcherror.php) class.
|
||||
pub fn unhandled_match_error() -> &'static ClassEntry {
|
||||
unsafe { zend_ce_unhandled_match_error.as_ref() }.unwrap()
|
||||
}
|
||||
|
||||
/// Returns the [`Traversable`](https://www.php.net/manual/en/class.traversable.php) interface.
|
||||
pub fn traversable() -> &'static ClassEntry {
|
||||
unsafe { zend_ce_traversable.as_ref() }.unwrap()
|
||||
}
|
||||
|
||||
/// Returns the [`IteratorAggregate`](https://www.php.net/manual/en/class.iteratoraggregate.php) interface.
|
||||
pub fn aggregate() -> &'static ClassEntry {
|
||||
unsafe { zend_ce_aggregate.as_ref() }.unwrap()
|
||||
}
|
||||
|
||||
/// Returns the [`Iterator`](https://www.php.net/manual/en/class.iterator.php) interface.
|
||||
pub fn iterator() -> &'static ClassEntry {
|
||||
unsafe { zend_ce_iterator.as_ref() }.unwrap()
|
||||
}
|
||||
|
||||
/// Returns the [`ArrayAccess`](https://www.php.net/manual/en/class.arrayaccess.php) interface.
|
||||
pub fn arrayaccess() -> &'static ClassEntry {
|
||||
unsafe { zend_ce_arrayaccess.as_ref() }.unwrap()
|
||||
}
|
||||
|
||||
/// Returns the [`Serializable`](https://www.php.net/manual/en/class.serializable.php) interface.
|
||||
pub fn serializable() -> &'static ClassEntry {
|
||||
unsafe { zend_ce_serializable.as_ref() }.unwrap()
|
||||
}
|
||||
|
||||
/// Returns the [`Countable`](https://www.php.net/manual/en/class.countable.php) interface.
|
||||
pub fn countable() -> &'static ClassEntry {
|
||||
unsafe { zend_ce_countable.as_ref() }.unwrap()
|
||||
}
|
||||
|
||||
/// Returns the [`Stringable`](https://www.php.net/manual/en/class.stringable.php) interface.
|
||||
pub fn stringable() -> &'static ClassEntry {
|
||||
unsafe { zend_ce_stringable.as_ref() }.unwrap()
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ impl ClassEntry {
|
||||
/// could not be found or the class table has not been initialized.
|
||||
pub fn try_find(name: &str) -> Option<&'static Self> {
|
||||
ExecutorGlobals::get().class_table()?;
|
||||
let mut name = ZendStr::new(name, false).ok()?;
|
||||
let mut name = ZendStr::new(name, false);
|
||||
|
||||
unsafe {
|
||||
crate::ffi::zend_lookup_class_ex(name.deref_mut(), std::ptr::null_mut(), 0).as_ref()
|
||||
@ -37,37 +37,19 @@ impl ClassEntry {
|
||||
///
|
||||
/// # Parameters
|
||||
///
|
||||
/// * `ce` - The inherited class entry to check.
|
||||
pub fn instance_of(&self, ce: &ClassEntry) -> bool {
|
||||
if self == ce {
|
||||
/// * `other` - The inherited class entry to check.
|
||||
pub fn instance_of(&self, other: &ClassEntry) -> bool {
|
||||
if self == other {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ce.flags().contains(ClassFlags::Interface) {
|
||||
let interfaces = match self.interfaces() {
|
||||
Some(interfaces) => interfaces,
|
||||
None => return false,
|
||||
};
|
||||
|
||||
for i in interfaces {
|
||||
if ce == i {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
loop {
|
||||
let parent = match self.parent() {
|
||||
Some(parent) => parent,
|
||||
None => return false,
|
||||
};
|
||||
|
||||
if parent == ce {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if other.is_interface() {
|
||||
return self
|
||||
.interfaces()
|
||||
.map_or(false, |mut it| it.any(|ce| ce == other));
|
||||
}
|
||||
|
||||
false
|
||||
std::iter::successors(self.parent(), |p| p.parent()).any(|ce| ce == other)
|
||||
}
|
||||
|
||||
/// Returns an iterator of all the interfaces that the class implements.
|
||||
@ -95,7 +77,7 @@ impl ClassEntry {
|
||||
unsafe { self.__bindgen_anon_1.parent.as_ref() }
|
||||
} else {
|
||||
let name = unsafe { self.__bindgen_anon_1.parent_name.as_ref()? };
|
||||
Self::try_find(name.as_str()?)
|
||||
Self::try_find(name.as_str().ok()?)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -87,11 +87,7 @@ impl ZendObjectHandlers {
|
||||
.ok_or("Invalid property name pointer given")?;
|
||||
let self_ = &mut **obj;
|
||||
let props = T::get_metadata().get_properties();
|
||||
let prop = props.get(
|
||||
prop_name
|
||||
.as_str()
|
||||
.ok_or("Invalid property name was given")?,
|
||||
);
|
||||
let prop = props.get(prop_name.as_str()?);
|
||||
|
||||
// retval needs to be treated as initialized, so we set the type to null
|
||||
let rv_mut = rv.as_mut().ok_or("Invalid return zval given")?;
|
||||
@ -138,7 +134,7 @@ impl ZendObjectHandlers {
|
||||
.ok_or("Invalid property name pointer given")?;
|
||||
let self_ = &mut **obj;
|
||||
let props = T::get_metadata().get_properties();
|
||||
let prop = props.get(prop_name.as_str().ok_or("Invalid property name given")?);
|
||||
let prop = props.get(prop_name.as_str()?);
|
||||
let value_mut = value.as_mut().ok_or("Invalid return zval given")?;
|
||||
|
||||
Ok(match prop {
|
||||
@ -220,7 +216,7 @@ impl ZendObjectHandlers {
|
||||
.as_ref()
|
||||
.ok_or("Invalid property name pointer given")?;
|
||||
let props = T::get_metadata().get_properties();
|
||||
let prop = props.get(prop_name.as_str().ok_or("Invalid property name given")?);
|
||||
let prop = props.get(prop_name.as_str()?);
|
||||
let self_ = &mut **obj;
|
||||
|
||||
match has_set_exists {
|
||||
|
@ -2,14 +2,14 @@ use std::{path::PathBuf, process::Command};
|
||||
|
||||
use anyhow::{bail, Context, Result};
|
||||
|
||||
use crate::{PHPInfo, PHPProvider};
|
||||
use crate::{find_executable, path_from_env, PHPInfo, PHPProvider};
|
||||
|
||||
pub struct Provider {}
|
||||
|
||||
impl Provider {
|
||||
/// Runs `php-config` with one argument, returning the stdout.
|
||||
fn php_config(&self, arg: &str) -> Result<String> {
|
||||
let cmd = Command::new("php-config")
|
||||
let cmd = Command::new(self.find_bin()?)
|
||||
.arg(arg)
|
||||
.output()
|
||||
.context("Failed to run `php-config`")?;
|
||||
@ -20,6 +20,22 @@ impl Provider {
|
||||
}
|
||||
Ok(stdout.to_string())
|
||||
}
|
||||
|
||||
fn find_bin(&self) -> Result<PathBuf> {
|
||||
// If path is given via env, it takes priority.
|
||||
if let Some(path) = path_from_env("PHP_CONFIG") {
|
||||
if !path.try_exists()? {
|
||||
// If path was explicitly given and it can't be found, this is a hard error
|
||||
bail!("php-config executable not found at {:?}", path);
|
||||
}
|
||||
return Ok(path);
|
||||
}
|
||||
find_executable("php-config").with_context(|| {
|
||||
"Could not find `php-config` executable. \
|
||||
Please ensure `php-config` is in your PATH or the \
|
||||
`PHP_CONFIG` environment variable is set."
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> PHPProvider<'a> for Provider {
|
||||
|
Loading…
Reference in New Issue
Block a user