mirror of
https://github.com/danog/ext-php-rs.git
synced 2024-12-04 10:37:56 +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
|
WORKDIR /tmp
|
||||||
|
|
||||||
|
71
.github/workflows/build.yml
vendored
71
.github/workflows/build.yml
vendored
@ -1,5 +1,8 @@
|
|||||||
name: Build and Lint
|
name: Build and Lint
|
||||||
on:
|
on:
|
||||||
|
schedule:
|
||||||
|
# runs every monday at midnight
|
||||||
|
- cron: "0 0 * * 1"
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
@ -12,8 +15,9 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||||
php: ['8.0', '8.1']
|
php: ["8.0", "8.1"]
|
||||||
rust: [stable, nightly]
|
rust: [stable, nightly]
|
||||||
|
clang: ["14"]
|
||||||
phpts: [ts, nts]
|
phpts: [ts, nts]
|
||||||
exclude:
|
exclude:
|
||||||
# ext-php-rs requires nightly Rust when on Windows.
|
# ext-php-rs requires nightly Rust when on Windows.
|
||||||
@ -24,6 +28,8 @@ jobs:
|
|||||||
phpts: ts
|
phpts: ts
|
||||||
- os: ubuntu-latest
|
- os: ubuntu-latest
|
||||||
phpts: ts
|
phpts: ts
|
||||||
|
env:
|
||||||
|
CARGO_TERM_COLOR: always
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
@ -34,52 +40,67 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
phpts: ${{ matrix.phpts }}
|
phpts: ${{ matrix.phpts }}
|
||||||
- name: Setup Rust
|
- name: Setup Rust
|
||||||
uses: actions-rs/toolchain@v1
|
uses: dtolnay/rust-toolchain@master
|
||||||
with:
|
with:
|
||||||
toolchain: ${{ matrix.rust }}
|
toolchain: ${{ matrix.rust }}
|
||||||
override: true
|
|
||||||
components: rustfmt, clippy
|
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')"
|
if: "!contains(matrix.os, 'windows')"
|
||||||
|
with:
|
||||||
|
path: ${{ runner.temp }}/llvm-${{ matrix.clang }}
|
||||||
|
key: ${{ matrix.os }}-llvm-${{ matrix.clang }}
|
||||||
|
- name: Setup LLVM & Clang
|
||||||
id: clang
|
id: clang
|
||||||
uses: KyleMayes/install-llvm-action@v1
|
uses: KyleMayes/install-llvm-action@v1
|
||||||
|
if: "!contains(matrix.os, 'windows')"
|
||||||
with:
|
with:
|
||||||
version: '13.0'
|
version: ${{ matrix.clang }}
|
||||||
directory: ${{ runner.temp }}/llvm
|
directory: ${{ runner.temp }}/llvm-${{ matrix.clang }}
|
||||||
|
cached: ${{ steps.cache-llvm.outputs.cache-hit }}
|
||||||
- name: Configure Clang
|
- name: Configure Clang
|
||||||
if: "!contains(matrix.os, 'windows')"
|
if: "!contains(matrix.os, 'windows')"
|
||||||
run: |
|
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_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)
|
- name: Configure Clang (macOS only)
|
||||||
if: "contains(matrix.os, 'macos')"
|
if: "contains(matrix.os, 'macos')"
|
||||||
run: echo "SDKROOT=$(xcrun --show-sdk-path)" >> $GITHUB_ENV
|
run: echo "SDKROOT=$(xcrun --show-sdk-path)" >> $GITHUB_ENV
|
||||||
|
# Build
|
||||||
- name: Build
|
- name: Build
|
||||||
env:
|
env:
|
||||||
EXT_PHP_RS_TEST:
|
EXT_PHP_RS_TEST: ""
|
||||||
run: cargo build --release --all-features --all
|
run: cargo build --release --all-features --all
|
||||||
|
# Test & lint
|
||||||
- name: Test inline examples
|
- name: Test inline examples
|
||||||
uses: actions-rs/cargo@v1
|
run: cargo test --release --all --all-features
|
||||||
with:
|
|
||||||
command: test
|
|
||||||
args: --release --all --all-features
|
|
||||||
- name: Run rustfmt
|
- name: Run rustfmt
|
||||||
uses: actions-rs/cargo@v1
|
if: matrix.rust == 'stable' && matrix.os == 'ubuntu-latest' && matrix.php == '8.1'
|
||||||
with:
|
run: cargo fmt --all -- --check
|
||||||
command: fmt
|
|
||||||
args: --all -- --check
|
|
||||||
- name: Run clippy
|
- name: Run clippy
|
||||||
uses: actions-rs/cargo@v1
|
if: matrix.rust == 'stable' && matrix.os == 'ubuntu-latest' && matrix.php == '8.1'
|
||||||
with:
|
run: cargo clippy --all -- -D warnings
|
||||||
command: clippy
|
# Docs
|
||||||
args: --all -- -D warnings
|
- name: Run rustdoc
|
||||||
if: matrix.rust == 'stable'
|
if: matrix.rust == 'stable' && matrix.os == 'ubuntu-latest' && matrix.php == '8.1'
|
||||||
|
run: cargo rustdoc -- -D warnings
|
||||||
- name: Build with docs stub
|
- 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:
|
env:
|
||||||
DOCS_RS:
|
DOCS_RS: ""
|
||||||
run:
|
run: cargo clean && cargo build
|
||||||
cargo clean && cargo build
|
|
||||||
build-zts:
|
build-zts:
|
||||||
name: Build with ZTS
|
name: Build with ZTS
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
29
.github/workflows/docs.yml
vendored
29
.github/workflows/docs.yml
vendored
@ -1,6 +1,9 @@
|
|||||||
name: Deploy documentation
|
name: Deploy documentation
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
# runs every monday at midnight
|
||||||
|
schedule:
|
||||||
|
- cron: "0 0 * * 1"
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
@ -8,28 +11,38 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
docs:
|
docs:
|
||||||
name: Build and Deploy
|
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:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
- name: Setup PHP
|
- name: Setup PHP
|
||||||
uses: shivammathur/setup-php@v2
|
uses: shivammathur/setup-php@v2
|
||||||
with:
|
with:
|
||||||
php-version: 8.0
|
php-version: ${{ matrix.php }}
|
||||||
- name: Setup Rust
|
- 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:
|
with:
|
||||||
toolchain: nightly
|
path: ${{ runner.temp }}/llvm-${{ matrix.clang }}
|
||||||
override: true
|
key: ${{ matrix.os }}-llvm-${{ matrix.clang }}
|
||||||
- name: Setup LLVM & Clang
|
- name: Setup LLVM & Clang
|
||||||
uses: KyleMayes/install-llvm-action@v1
|
uses: KyleMayes/install-llvm-action@v1
|
||||||
with:
|
with:
|
||||||
version: 11.0
|
version: ${{ matrix.clang }}
|
||||||
directory: ${{ runner.temp }}/llvm-11.0
|
directory: ${{ runner.temp }}/llvm-${{ matrix.clang }}
|
||||||
|
cached: ${{ steps.cache-llvm.outputs.cache-hit }}
|
||||||
- name: Install mdbook
|
- name: Install mdbook
|
||||||
uses: peaceiris/actions-mdbook@v1
|
uses: peaceiris/actions-mdbook@v1
|
||||||
with:
|
with:
|
||||||
mdbook-version: latest
|
mdbook-version: ${{ matrix.mdbook }}
|
||||||
- name: Build guide
|
- name: Build guide
|
||||||
run: mdbook build guide
|
run: mdbook build guide
|
||||||
- name: Publish docs
|
- name: Publish docs
|
||||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -2,4 +2,5 @@
|
|||||||
Cargo.lock
|
Cargo.lock
|
||||||
/.vscode
|
/.vscode
|
||||||
/.idea
|
/.idea
|
||||||
expand.rs
|
/tmp
|
||||||
|
expand.rs
|
120
CHANGELOG.md
120
CHANGELOG.md
@ -1,5 +1,125 @@
|
|||||||
# Changelog
|
# 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
|
## Version 0.7.3
|
||||||
|
|
||||||
- Upgrade `clap` to `3.0.0-rc3`. [#113]
|
- 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"
|
homepage = "https://github.com/davidcole1340/ext-php-rs"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
keywords = ["php", "ffi", "zend"]
|
keywords = ["php", "ffi", "zend"]
|
||||||
version = "0.8.0"
|
version = "0.9.0"
|
||||||
authors = ["David Cole <david.cole1340@gmail.com>"]
|
authors = ["David Cole <david.cole1340@gmail.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
categories = ["api-bindings"]
|
categories = ["api-bindings"]
|
||||||
@ -17,15 +17,14 @@ parking_lot = "0.12.1"
|
|||||||
cfg-if = "1.0"
|
cfg-if = "1.0"
|
||||||
once_cell = "1.8.0"
|
once_cell = "1.8.0"
|
||||||
anyhow = { version = "1", optional = true }
|
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]
|
[dev-dependencies]
|
||||||
skeptic = "0.13"
|
skeptic = "0.13"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
# bindgen = { version = "0.59" }
|
bindgen = "0.63"
|
||||||
bindgen = "0.60"
|
|
||||||
cc = "1.0"
|
cc = "1.0"
|
||||||
skeptic = "0.13"
|
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-php](https://github.com/davidcole1340/opus-php) - Audio encoder for the
|
||||||
Opus codec in PHP.
|
Opus codec in PHP.
|
||||||
- [tomlrs-php](https://github.com/jphenow/tomlrs-php) - TOML data format parser.
|
- [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
|
## Contributions
|
||||||
|
|
||||||
|
@ -4,6 +4,20 @@
|
|||||||
// exist in the bindings file. Which ever script include!s the bindings must
|
// 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
|
// 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.
|
// 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! {
|
bind! {
|
||||||
HashTable,
|
HashTable,
|
||||||
@ -29,6 +43,8 @@ bind! {
|
|||||||
// ext_php_rs_zend_object_release,
|
// ext_php_rs_zend_object_release,
|
||||||
// ext_php_rs_zend_string_init,
|
// ext_php_rs_zend_string_init,
|
||||||
// ext_php_rs_zend_string_release,
|
// ext_php_rs_zend_string_release,
|
||||||
|
// ext_php_rs_is_kown_valid_utf8,
|
||||||
|
// ext_php_rs_set_kown_valid_utf8,
|
||||||
object_properties_init,
|
object_properties_init,
|
||||||
php_info_print_table_end,
|
php_info_print_table_end,
|
||||||
php_info_print_table_header,
|
php_info_print_table_header,
|
||||||
@ -49,6 +65,13 @@ bind! {
|
|||||||
zend_ce_type_error,
|
zend_ce_type_error,
|
||||||
zend_ce_unhandled_match_error,
|
zend_ce_unhandled_match_error,
|
||||||
zend_ce_value_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_class_entry,
|
||||||
zend_declare_class_constant,
|
zend_declare_class_constant,
|
||||||
zend_declare_property,
|
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`.
|
/// 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" };
|
const WHICH: &str = if cfg!(windows) { "where" } else { "which" };
|
||||||
let cmd = Command::new(WHICH).arg(name).output().ok()?;
|
let cmd = Command::new(WHICH).arg(name).output().ok()?;
|
||||||
if cmd.status.success() {
|
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.
|
/// Finds the location of the PHP executable.
|
||||||
fn find_php() -> Result<PathBuf> {
|
fn find_php() -> Result<PathBuf> {
|
||||||
// If PHP path is given via env, it takes priority.
|
// If path is given via env, it takes priority.
|
||||||
let env = std::env::var("PHP");
|
if let Some(path) = path_from_env("PHP") {
|
||||||
if let Ok(env) = env {
|
if !path.try_exists()? {
|
||||||
return Ok(env.into());
|
// 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").with_context(|| {
|
||||||
find_executable("php").context("Could not find PHP path. Please ensure `php` is in your PATH or the `PHP` environment variable is set.")
|
"Could not find PHP executable. \
|
||||||
|
Please ensure `php` is in your PATH or the `PHP` environment variable is set."
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct PHPInfo(String);
|
pub struct PHPInfo(String);
|
||||||
@ -206,6 +216,8 @@ fn check_php_version(info: &PHPInfo) -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> 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();
|
let manifest: PathBuf = std::env::var("CARGO_MANIFEST_DIR").unwrap().into();
|
||||||
for path in [
|
for path in [
|
||||||
manifest.join("src").join("wrapper.h"),
|
manifest.join("src").join("wrapper.h"),
|
||||||
@ -216,6 +228,21 @@ fn main() -> Result<()> {
|
|||||||
] {
|
] {
|
||||||
println!("cargo:rerun-if-changed={}", path.to_string_lossy());
|
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 php = find_php()?;
|
||||||
let info = PHPInfo::get(&php)?;
|
let info = PHPInfo::get(&php)?;
|
||||||
@ -228,8 +255,6 @@ fn main() -> Result<()> {
|
|||||||
build_wrapper(&defines, &includes)?;
|
build_wrapper(&defines, &includes)?;
|
||||||
let bindings = generate_bindings(&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 =
|
let out_file =
|
||||||
File::create(&out_path).context("Failed to open output bindings file for writing")?;
|
File::create(&out_path).context("Failed to open output bindings file for writing")?;
|
||||||
let mut out_writer = BufWriter::new(out_file);
|
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"
|
homepage = "https://github.com/davidcole1340/ext-php-rs"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
keywords = ["php", "ffi", "zend"]
|
keywords = ["php", "ffi", "zend"]
|
||||||
version = "0.1.5"
|
version = "0.1.7"
|
||||||
authors = ["David Cole <david.cole1340@gmail.com>"]
|
authors = ["David Cole <david.cole1340@gmail.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
categories = ["api-bindings", "command-line-interface"]
|
categories = ["api-bindings", "command-line-interface"]
|
||||||
@ -13,9 +13,9 @@ categories = ["api-bindings", "command-line-interface"]
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
ext-php-rs = { version = ">=0.7.1", path = "../../" }
|
ext-php-rs = { version = ">=0.7.1", path = "../../" }
|
||||||
|
|
||||||
clap = { version = ">=3.2.5", features = ["derive"] }
|
clap = { version = "4.0", features = ["derive"] }
|
||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
dialoguer = "0.10"
|
dialoguer = "0.10"
|
||||||
libloading = "0.7"
|
libloading = "0.7"
|
||||||
cargo_metadata = "0.14"
|
cargo_metadata = "0.15"
|
||||||
semver = "1.0"
|
semver = "1.0"
|
||||||
|
@ -40,7 +40,7 @@ SUBCOMMANDS:
|
|||||||
Generates stub PHP files for the extension
|
Generates stub PHP files for the extension
|
||||||
|
|
||||||
$ cargo php install --help
|
$ cargo php install --help
|
||||||
cargo-php-install
|
cargo-php-install
|
||||||
|
|
||||||
Installs the extension in the current PHP installation.
|
Installs the extension in the current PHP installation.
|
||||||
|
|
||||||
@ -71,8 +71,11 @@ OPTIONS:
|
|||||||
--release
|
--release
|
||||||
Whether to install the release version of the extension
|
Whether to install the release version of the extension
|
||||||
|
|
||||||
|
--yes
|
||||||
|
Bypasses the confirmation prompt
|
||||||
|
|
||||||
$ cargo php remove --help
|
$ cargo php remove --help
|
||||||
cargo-php-remove
|
cargo-php-remove
|
||||||
|
|
||||||
Removes the extension in the current PHP installation.
|
Removes the extension in the current PHP installation.
|
||||||
|
|
||||||
@ -97,8 +100,11 @@ OPTIONS:
|
|||||||
Path to the Cargo manifest of the extension. Defaults to the manifest in the directory
|
Path to the Cargo manifest of the extension. Defaults to the manifest in the directory
|
||||||
the command is called
|
the command is called
|
||||||
|
|
||||||
|
--yes
|
||||||
|
Bypasses the confirmation prompt
|
||||||
|
|
||||||
$ cargo php stubs --help
|
$ cargo php stubs --help
|
||||||
cargo-php-stubs
|
cargo-php-stubs
|
||||||
|
|
||||||
Generates stub PHP files for the extension.
|
Generates stub PHP files for the extension.
|
||||||
|
|
||||||
@ -120,7 +126,7 @@ OPTIONS:
|
|||||||
--manifest <MANIFEST>
|
--manifest <MANIFEST>
|
||||||
Path to the Cargo manifest of the extension. Defaults to the manifest in the directory
|
Path to the Cargo manifest of the extension. Defaults to the manifest in the directory
|
||||||
the command is called.
|
the command is called.
|
||||||
|
|
||||||
This cannot be provided alongside the `ext` option, as that option provides a direct
|
This cannot be provided alongside the `ext` option, as that option provides a direct
|
||||||
path to the extension shared library.
|
path to the extension shared library.
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ use dialoguer::{Confirm, Select};
|
|||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
fs::OpenOptions,
|
fs::OpenOptions,
|
||||||
io::{BufRead, BufReader, Write},
|
io::{BufRead, BufReader, Seek, SeekFrom, Write},
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
process::{Command, Stdio},
|
process::{Command, Stdio},
|
||||||
};
|
};
|
||||||
@ -105,6 +105,9 @@ struct Install {
|
|||||||
/// the directory the command is called.
|
/// the directory the command is called.
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
manifest: Option<PathBuf>,
|
manifest: Option<PathBuf>,
|
||||||
|
/// Whether to bypass the install prompt.
|
||||||
|
#[clap(long)]
|
||||||
|
yes: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
@ -121,6 +124,9 @@ struct Remove {
|
|||||||
/// the directory the command is called.
|
/// the directory the command is called.
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
manifest: Option<PathBuf>,
|
manifest: Option<PathBuf>,
|
||||||
|
/// Whether to bypass the remove prompt.
|
||||||
|
#[clap(long)]
|
||||||
|
yes: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
@ -172,12 +178,13 @@ impl Install {
|
|||||||
php_ini = Some(ini_path);
|
php_ini = Some(ini_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
if !Confirm::new()
|
if !self.yes
|
||||||
.with_prompt(format!(
|
&& !Confirm::new()
|
||||||
"Are you sure you want to install the extension `{}`?",
|
.with_prompt(format!(
|
||||||
artifact.name
|
"Are you sure you want to install the extension `{}`?",
|
||||||
))
|
artifact.name
|
||||||
.interact()?
|
))
|
||||||
|
.interact()?
|
||||||
{
|
{
|
||||||
bail!("Installation cancelled.");
|
bail!("Installation cancelled.");
|
||||||
}
|
}
|
||||||
@ -207,6 +214,8 @@ impl Install {
|
|||||||
let line = line.with_context(|| "Failed to read line from `php.ini`")?;
|
let line = line.with_context(|| "Failed to read line from `php.ini`")?;
|
||||||
if !line.contains(&ext_line) {
|
if !line.contains(&ext_line) {
|
||||||
new_lines.push(line);
|
new_lines.push(line);
|
||||||
|
} else {
|
||||||
|
bail!("Extension already enabled.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -216,6 +225,8 @@ impl Install {
|
|||||||
}
|
}
|
||||||
|
|
||||||
new_lines.push(ext_line);
|
new_lines.push(ext_line);
|
||||||
|
file.seek(SeekFrom::Start(0))?;
|
||||||
|
file.set_len(0)?;
|
||||||
file.write(new_lines.join("\n").as_bytes())
|
file.write(new_lines.join("\n").as_bytes())
|
||||||
.with_context(|| "Failed to update `php.ini`")?;
|
.with_context(|| "Failed to update `php.ini`")?;
|
||||||
}
|
}
|
||||||
@ -301,12 +312,13 @@ impl Remove {
|
|||||||
bail!("Unable to find extension installed.");
|
bail!("Unable to find extension installed.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if !Confirm::new()
|
if !self.yes
|
||||||
.with_prompt(format!(
|
&& !Confirm::new()
|
||||||
"Are you sure you want to remove the extension `{}`?",
|
.with_prompt(format!(
|
||||||
artifact.name
|
"Are you sure you want to remove the extension `{}`?",
|
||||||
))
|
artifact.name
|
||||||
.interact()?
|
))
|
||||||
|
.interact()?
|
||||||
{
|
{
|
||||||
bail!("Installation cancelled.");
|
bail!("Installation cancelled.");
|
||||||
}
|
}
|
||||||
@ -318,7 +330,6 @@ impl Remove {
|
|||||||
.read(true)
|
.read(true)
|
||||||
.write(true)
|
.write(true)
|
||||||
.create(true)
|
.create(true)
|
||||||
.truncate(true)
|
|
||||||
.open(php_ini)
|
.open(php_ini)
|
||||||
.with_context(|| "Failed to 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())
|
file.write(new_lines.join("\n").as_bytes())
|
||||||
.with_context(|| "Failed to update `php.ini`")?;
|
.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"
|
repository = "https://github.com/davidcole1340/ext-php-rs"
|
||||||
homepage = "https://github.com/davidcole1340/ext-php-rs"
|
homepage = "https://github.com/davidcole1340/ext-php-rs"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
version = "0.8.0"
|
version = "0.9.0"
|
||||||
authors = ["David Cole <david.cole1340@gmail.com>"]
|
authors = ["David Cole <david.cole1340@gmail.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
|
@ -22,6 +22,7 @@ pub struct Class {
|
|||||||
/// A function name called when creating the class entry. Given an instance
|
/// A function name called when creating the class entry. Given an instance
|
||||||
/// of `ClassBuilder` and must return it.
|
/// of `ClassBuilder` and must return it.
|
||||||
pub modifier: Option<String>,
|
pub modifier: Option<String>,
|
||||||
|
pub flags: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -37,6 +38,7 @@ pub enum ParsedAttribute {
|
|||||||
pub struct AttrArgs {
|
pub struct AttrArgs {
|
||||||
name: Option<String>,
|
name: Option<String>,
|
||||||
modifier: Option<String>,
|
modifier: Option<String>,
|
||||||
|
flags: Option<Expr>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parser(args: AttributeArgs, mut input: ItemStruct) -> Result<TokenStream> {
|
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 ItemStruct { ident, .. } = &input;
|
||||||
let class_name = args.name.unwrap_or_else(|| ident.to_string());
|
let class_name = args.name.unwrap_or_else(|| ident.to_string());
|
||||||
let struct_path = ident.to_string();
|
let struct_path = ident.to_string();
|
||||||
|
let flags = args.flags.map(|flags| flags.to_token_stream().to_string());
|
||||||
let class = Class {
|
let class = Class {
|
||||||
class_name,
|
class_name,
|
||||||
struct_path,
|
struct_path,
|
||||||
@ -125,6 +128,7 @@ pub fn parser(args: AttributeArgs, mut input: ItemStruct) -> Result<TokenStream>
|
|||||||
docs: comments,
|
docs: comments,
|
||||||
properties,
|
properties,
|
||||||
modifier: args.modifier,
|
modifier: args.modifier,
|
||||||
|
flags,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@ pub struct AttrArgs {
|
|||||||
optional: Option<String>,
|
optional: Option<String>,
|
||||||
ignore_module: bool,
|
ignore_module: bool,
|
||||||
defaults: HashMap<String, Lit>,
|
defaults: HashMap<String, Lit>,
|
||||||
|
name: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@ -93,7 +94,7 @@ pub fn parser(args: AttributeArgs, input: ItemFn) -> Result<(TokenStream, Functi
|
|||||||
}
|
}
|
||||||
|
|
||||||
let function = Function {
|
let function = Function {
|
||||||
name: ident.to_string(),
|
name: attr_args.name.unwrap_or_else(|| ident.to_string()),
|
||||||
docs: get_docs(&input.attrs),
|
docs: get_docs(&input.attrs),
|
||||||
ident: internal_ident.to_string(),
|
ident: internal_ident.to_string(),
|
||||||
args,
|
args,
|
||||||
|
@ -85,6 +85,7 @@ pub enum ParsedAttribute {
|
|||||||
},
|
},
|
||||||
Constructor,
|
Constructor,
|
||||||
This,
|
This,
|
||||||
|
Abstract,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug, FromMeta)]
|
#[derive(Default, Debug, FromMeta)]
|
||||||
@ -212,6 +213,7 @@ pub fn parse_attribute(attr: &Attribute) -> Result<Option<ParsedAttribute>> {
|
|||||||
"public" => ParsedAttribute::Visibility(Visibility::Public),
|
"public" => ParsedAttribute::Visibility(Visibility::Public),
|
||||||
"protected" => ParsedAttribute::Visibility(Visibility::Protected),
|
"protected" => ParsedAttribute::Visibility(Visibility::Protected),
|
||||||
"private" => ParsedAttribute::Visibility(Visibility::Private),
|
"private" => ParsedAttribute::Visibility(Visibility::Private),
|
||||||
|
"abstract_method" => ParsedAttribute::Abstract,
|
||||||
"rename" => {
|
"rename" => {
|
||||||
let ident = if let Meta::List(list) = meta {
|
let ident = if let Meta::List(list) = meta {
|
||||||
if let Some(NestedMeta::Lit(lit)) = list.nested.first() {
|
if let Some(NestedMeta::Lit(lit)) = list.nested.first() {
|
||||||
|
@ -87,10 +87,11 @@ pub fn php_module(_: TokenStream, input: TokenStream) -> TokenStream {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[proc_macro_attribute]
|
#[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);
|
let input = parse_macro_input!(input as ItemFn);
|
||||||
|
|
||||||
match startup_function::parser(input) {
|
match startup_function::parser(Some(args), input) {
|
||||||
Ok(parsed) => parsed,
|
Ok(parsed) => parsed,
|
||||||
Err(e) => syn::Error::new(Span::call_site(), e).to_compile_error(),
|
Err(e) => syn::Error::new(Span::call_site(), e).to_compile_error(),
|
||||||
}
|
}
|
||||||
|
@ -38,6 +38,7 @@ pub struct Method {
|
|||||||
pub optional: Option<String>,
|
pub optional: Option<String>,
|
||||||
pub output: Option<(String, bool)>,
|
pub output: Option<(String, bool)>,
|
||||||
pub _static: bool,
|
pub _static: bool,
|
||||||
|
pub _abstract: bool,
|
||||||
pub visibility: Visibility,
|
pub visibility: Visibility,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,6 +82,7 @@ pub fn parser(
|
|||||||
let mut visibility = Visibility::Public;
|
let mut visibility = Visibility::Public;
|
||||||
let mut as_prop = None;
|
let mut as_prop = None;
|
||||||
let mut identifier = None;
|
let mut identifier = None;
|
||||||
|
let mut is_abstract = false;
|
||||||
let mut is_constructor = false;
|
let mut is_constructor = false;
|
||||||
let docs = get_docs(&input.attrs);
|
let docs = get_docs(&input.attrs);
|
||||||
|
|
||||||
@ -90,6 +92,7 @@ pub fn parser(
|
|||||||
ParsedAttribute::Default(list) => defaults = list,
|
ParsedAttribute::Default(list) => defaults = list,
|
||||||
ParsedAttribute::Optional(name) => optional = Some(name),
|
ParsedAttribute::Optional(name) => optional = Some(name),
|
||||||
ParsedAttribute::Visibility(vis) => visibility = vis,
|
ParsedAttribute::Visibility(vis) => visibility = vis,
|
||||||
|
ParsedAttribute::Abstract => is_abstract = true,
|
||||||
ParsedAttribute::Rename(ident) => identifier = Some(ident),
|
ParsedAttribute::Rename(ident) => identifier = Some(ident),
|
||||||
ParsedAttribute::Property { prop_name, ty } => {
|
ParsedAttribute::Property { prop_name, ty } => {
|
||||||
if as_prop.is_some() {
|
if as_prop.is_some() {
|
||||||
@ -211,6 +214,7 @@ pub fn parser(
|
|||||||
optional,
|
optional,
|
||||||
output: get_return_type(struct_ty, &input.sig.output)?,
|
output: get_return_type(struct_ty, &input.sig.output)?,
|
||||||
_static: matches!(method_type, MethodType::Static),
|
_static: matches!(method_type, MethodType::Static),
|
||||||
|
_abstract: is_abstract,
|
||||||
visibility,
|
visibility,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -447,6 +451,10 @@ impl Method {
|
|||||||
flags.push(quote! { Static });
|
flags.push(quote! { Static });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if self._abstract {
|
||||||
|
flags.push(quote! { Abstract });
|
||||||
|
}
|
||||||
|
|
||||||
flags
|
flags
|
||||||
.iter()
|
.iter()
|
||||||
.map(|flag| quote! { ::ext_php_rs::flags::MethodFlags::#flag })
|
.map(|flag| quote! { ::ext_php_rs::flags::MethodFlags::#flag })
|
||||||
|
@ -34,7 +34,7 @@ pub fn parser(input: ItemFn) -> Result<TokenStream> {
|
|||||||
fn php_module_startup() {}
|
fn php_module_startup() {}
|
||||||
})
|
})
|
||||||
.map_err(|_| anyhow!("Unable to generate PHP module startup function."))?;
|
.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();
|
state = STATE.lock();
|
||||||
Some(startup)
|
Some(startup)
|
||||||
@ -227,10 +227,7 @@ impl Describe for Class {
|
|||||||
} else {
|
} else {
|
||||||
quote! { None }
|
quote! { None }
|
||||||
};
|
};
|
||||||
let interfaces = self
|
let interfaces = self.interfaces.iter().map(|iface| quote! { #iface.into() });
|
||||||
.interfaces
|
|
||||||
.iter()
|
|
||||||
.map(|iface| quote! { #iface.into(), });
|
|
||||||
let properties = self.properties.iter().map(|d| d.describe());
|
let properties = self.properties.iter().map(|d| d.describe());
|
||||||
let mut methods: Vec<_> = self.methods.iter().map(Describe::describe).collect();
|
let mut methods: Vec<_> = self.methods.iter().map(Describe::describe).collect();
|
||||||
let docs = self.docs.iter().map(|c| {
|
let docs = self.docs.iter().map(|c| {
|
||||||
|
@ -1,13 +1,27 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
|
use darling::FromMeta;
|
||||||
use proc_macro2::{Ident, Span, TokenStream};
|
use proc_macro2::{Ident, Span, TokenStream};
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
use syn::{Expr, ItemFn, Signature};
|
use syn::{AttributeArgs, Expr, ItemFn, Signature};
|
||||||
|
|
||||||
use crate::{class::Class, constant::Constant, STATE};
|
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 ItemFn { sig, block, .. } = input;
|
||||||
let Signature { ident, .. } = sig;
|
let Signature { ident, .. } = sig;
|
||||||
let stmts = &block.stmts;
|
let stmts = &block.stmts;
|
||||||
@ -17,6 +31,11 @@ pub fn parser(input: ItemFn) -> Result<TokenStream> {
|
|||||||
|
|
||||||
let classes = build_classes(&state.classes)?;
|
let classes = build_classes(&state.classes)?;
|
||||||
let constants = build_constants(&state.constants);
|
let constants = build_constants(&state.constants);
|
||||||
|
let (before, after) = if args.before {
|
||||||
|
(Some(quote! { internal(); }), None)
|
||||||
|
} else {
|
||||||
|
(None, Some(quote! { internal(); }))
|
||||||
|
};
|
||||||
|
|
||||||
let func = quote! {
|
let func = quote! {
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
@ -30,11 +49,10 @@ pub fn parser(input: ItemFn) -> Result<TokenStream> {
|
|||||||
|
|
||||||
::ext_php_rs::internal::ext_php_rs_startup();
|
::ext_php_rs::internal::ext_php_rs_startup();
|
||||||
|
|
||||||
|
#before
|
||||||
#(#classes)*
|
#(#classes)*
|
||||||
#(#constants)*
|
#(#constants)*
|
||||||
|
#after
|
||||||
// TODO return result?
|
|
||||||
internal();
|
|
||||||
|
|
||||||
0
|
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! {{
|
Ok(quote! {{
|
||||||
let builder = ::ext_php_rs::builders::ClassBuilder::new(#class_name)
|
let builder = ::ext_php_rs::builders::ClassBuilder::new(#class_name)
|
||||||
#(#methods)*
|
#(#methods)*
|
||||||
@ -128,7 +171,9 @@ fn build_classes(classes: &HashMap<String, Class>) -> Result<Vec<TokenStream>> {
|
|||||||
#(#interfaces)*
|
#(#interfaces)*
|
||||||
// #(#properties)*
|
// #(#properties)*
|
||||||
#parent
|
#parent
|
||||||
.object_override::<#ident>();
|
#flags
|
||||||
|
#object_override
|
||||||
|
;
|
||||||
#class_modifier
|
#class_modifier
|
||||||
let class = builder.build()
|
let class = builder.build()
|
||||||
.expect(concat!("Unable to build class `", #class_name, "`"));
|
.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_DEBUG: u32 = 1;
|
||||||
pub const ZEND_MM_ALIGNMENT: u32 = 8;
|
|
||||||
pub const _ZEND_TYPE_NAME_BIT: u32 = 16777216;
|
pub const _ZEND_TYPE_NAME_BIT: u32 = 16777216;
|
||||||
pub const _ZEND_TYPE_NULLABLE_BIT: u32 = 2;
|
pub const _ZEND_TYPE_NULLABLE_BIT: u32 = 2;
|
||||||
pub const HT_MIN_SIZE: u32 = 8;
|
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_RESOURCE_EX: u32 = 265;
|
||||||
pub const IS_REFERENCE_EX: u32 = 266;
|
pub const IS_REFERENCE_EX: u32 = 266;
|
||||||
pub const IS_CONSTANT_AST_EX: u32 = 267;
|
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_ISSET: u32 = 0;
|
||||||
pub const ZEND_PROPERTY_EXISTS: u32 = 2;
|
pub const ZEND_PROPERTY_EXISTS: u32 = 2;
|
||||||
pub const ZEND_ACC_PUBLIC: u32 = 1;
|
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_PERSISTENT: u32 = 1;
|
||||||
pub const CONST_NO_FILE_CACHE: u32 = 2;
|
pub const CONST_NO_FILE_CACHE: u32 = 2;
|
||||||
pub const CONST_DEPRECATED: u32 = 4;
|
pub const CONST_DEPRECATED: u32 = 4;
|
||||||
pub type __darwin_size_t = ::std::os::raw::c_ulong;
|
#[repr(C)]
|
||||||
pub type size_t = __darwin_size_t;
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct __sigset_t {
|
||||||
|
pub __val: [::std::os::raw::c_ulong; 16usize],
|
||||||
|
}
|
||||||
pub type zend_long = i64;
|
pub type zend_long = i64;
|
||||||
pub type zend_ulong = u64;
|
pub type zend_ulong = u64;
|
||||||
pub type zend_uchar = ::std::os::raw::c_uchar;
|
pub type zend_uchar = ::std::os::raw::c_uchar;
|
||||||
@ -202,7 +203,7 @@ pub struct _zend_refcounted {
|
|||||||
pub struct _zend_string {
|
pub struct _zend_string {
|
||||||
pub gc: zend_refcounted_h,
|
pub gc: zend_refcounted_h,
|
||||||
pub h: zend_ulong,
|
pub h: zend_ulong,
|
||||||
pub len: size_t,
|
pub len: usize,
|
||||||
pub val: [::std::os::raw::c_char; 1usize],
|
pub val: [::std::os::raw::c_char; 1usize],
|
||||||
}
|
}
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
@ -284,7 +285,7 @@ pub struct _zend_ast_ref {
|
|||||||
}
|
}
|
||||||
extern "C" {
|
extern "C" {
|
||||||
pub fn _emalloc(
|
pub fn _emalloc(
|
||||||
size: size_t,
|
size: usize,
|
||||||
__zend_filename: *const ::std::os::raw::c_char,
|
__zend_filename: *const ::std::os::raw::c_char,
|
||||||
__zend_lineno: u32,
|
__zend_lineno: u32,
|
||||||
__zend_orig_filename: *const ::std::os::raw::c_char,
|
__zend_orig_filename: *const ::std::os::raw::c_char,
|
||||||
@ -301,12 +302,12 @@ extern "C" {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
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<
|
pub type zend_string_init_interned_func_t = ::std::option::Option<
|
||||||
unsafe extern "C" fn(
|
unsafe extern "C" fn(
|
||||||
str_: *const ::std::os::raw::c_char,
|
str_: *const ::std::os::raw::c_char,
|
||||||
size: size_t,
|
size: usize,
|
||||||
permanent: bool,
|
permanent: bool,
|
||||||
) -> *mut zend_string,
|
) -> *mut zend_string,
|
||||||
>;
|
>;
|
||||||
@ -320,7 +321,7 @@ extern "C" {
|
|||||||
pub fn zend_hash_str_update(
|
pub fn zend_hash_str_update(
|
||||||
ht: *mut HashTable,
|
ht: *mut HashTable,
|
||||||
key: *const ::std::os::raw::c_char,
|
key: *const ::std::os::raw::c_char,
|
||||||
len: size_t,
|
len: usize,
|
||||||
pData: *mut zval,
|
pData: *mut zval,
|
||||||
) -> *mut zval;
|
) -> *mut zval;
|
||||||
}
|
}
|
||||||
@ -335,7 +336,7 @@ extern "C" {
|
|||||||
pub fn zend_hash_str_del(
|
pub fn zend_hash_str_del(
|
||||||
ht: *mut HashTable,
|
ht: *mut HashTable,
|
||||||
key: *const ::std::os::raw::c_char,
|
key: *const ::std::os::raw::c_char,
|
||||||
len: size_t,
|
len: usize,
|
||||||
) -> zend_result;
|
) -> zend_result;
|
||||||
}
|
}
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@ -345,7 +346,7 @@ extern "C" {
|
|||||||
pub fn zend_hash_str_find(
|
pub fn zend_hash_str_find(
|
||||||
ht: *const HashTable,
|
ht: *const HashTable,
|
||||||
key: *const ::std::os::raw::c_char,
|
key: *const ::std::os::raw::c_char,
|
||||||
len: size_t,
|
len: usize,
|
||||||
) -> *mut zval;
|
) -> *mut zval;
|
||||||
}
|
}
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@ -548,7 +549,7 @@ pub struct _zend_class_entry {
|
|||||||
unsafe extern "C" fn(
|
unsafe extern "C" fn(
|
||||||
object: *mut zval,
|
object: *mut zval,
|
||||||
buffer: *mut *mut ::std::os::raw::c_uchar,
|
buffer: *mut *mut ::std::os::raw::c_uchar,
|
||||||
buf_len: *mut size_t,
|
buf_len: *mut usize,
|
||||||
data: *mut zend_serialize_data,
|
data: *mut zend_serialize_data,
|
||||||
) -> ::std::os::raw::c_int,
|
) -> ::std::os::raw::c_int,
|
||||||
>,
|
>,
|
||||||
@ -557,7 +558,7 @@ pub struct _zend_class_entry {
|
|||||||
object: *mut zval,
|
object: *mut zval,
|
||||||
ce: *mut zend_class_entry,
|
ce: *mut zend_class_entry,
|
||||||
buf: *const ::std::os::raw::c_uchar,
|
buf: *const ::std::os::raw::c_uchar,
|
||||||
buf_len: size_t,
|
buf_len: usize,
|
||||||
data: *mut zend_unserialize_data,
|
data: *mut zend_unserialize_data,
|
||||||
) -> ::std::os::raw::c_int,
|
) -> ::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 run_time_cache: *mut *mut ::std::os::raw::c_void,
|
||||||
pub extra_named_params: *mut zend_array,
|
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;
|
pub type zend_executor_globals = _zend_executor_globals;
|
||||||
extern "C" {
|
extern "C" {
|
||||||
pub static mut executor_globals: zend_executor_globals;
|
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 symtable_cache_ptr: *mut *mut zend_array,
|
||||||
pub symbol_table: zend_array,
|
pub symbol_table: zend_array,
|
||||||
pub included_files: HashTable,
|
pub included_files: HashTable,
|
||||||
pub bailout: *mut sigjmp_buf,
|
pub bailout: *mut jmp_buf,
|
||||||
pub error_reporting: ::std::os::raw::c_int,
|
pub error_reporting: ::std::os::raw::c_int,
|
||||||
pub exit_status: ::std::os::raw::c_int,
|
pub exit_status: ::std::os::raw::c_int,
|
||||||
pub function_table: *mut HashTable,
|
pub function_table: *mut HashTable,
|
||||||
@ -1052,7 +1061,7 @@ pub struct _zend_executor_globals {
|
|||||||
pub vm_stack_top: *mut zval,
|
pub vm_stack_top: *mut zval,
|
||||||
pub vm_stack_end: *mut zval,
|
pub vm_stack_end: *mut zval,
|
||||||
pub vm_stack: zend_vm_stack,
|
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 current_execute_data: *mut _zend_execute_data,
|
||||||
pub fake_scope: *mut zend_class_entry,
|
pub fake_scope: *mut zend_class_entry,
|
||||||
pub jit_trace_num: u32,
|
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 info_func: ::std::option::Option<unsafe extern "C" fn(zend_module: *mut zend_module_entry)>,
|
||||||
pub version: *const ::std::os::raw::c_char,
|
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_ptr: *mut ::std::os::raw::c_void,
|
||||||
pub globals_ctor:
|
pub globals_ctor:
|
||||||
::std::option::Option<unsafe extern "C" fn(global: *mut ::std::os::raw::c_void)>,
|
::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,
|
pub prev: zend_vm_stack,
|
||||||
}
|
}
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
pub struct _zend_function_entry {
|
pub struct _zend_function_entry {
|
||||||
pub fname: *const ::std::os::raw::c_char,
|
pub fname: *const ::std::os::raw::c_char,
|
||||||
pub handler: zif_handler,
|
pub handler: zif_handler,
|
||||||
@ -1211,7 +1220,7 @@ extern "C" {
|
|||||||
pub fn zend_declare_property(
|
pub fn zend_declare_property(
|
||||||
ce: *mut zend_class_entry,
|
ce: *mut zend_class_entry,
|
||||||
name: *const ::std::os::raw::c_char,
|
name: *const ::std::os::raw::c_char,
|
||||||
name_length: size_t,
|
name_length: usize,
|
||||||
property: *mut zval,
|
property: *mut zval,
|
||||||
access_type: ::std::os::raw::c_int,
|
access_type: ::std::os::raw::c_int,
|
||||||
);
|
);
|
||||||
@ -1220,7 +1229,7 @@ extern "C" {
|
|||||||
pub fn zend_declare_class_constant(
|
pub fn zend_declare_class_constant(
|
||||||
ce: *mut zend_class_entry,
|
ce: *mut zend_class_entry,
|
||||||
name: *const ::std::os::raw::c_char,
|
name: *const ::std::os::raw::c_char,
|
||||||
name_length: size_t,
|
name_length: usize,
|
||||||
value: *mut zval,
|
value: *mut zval,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -1286,7 +1295,7 @@ extern "C" {
|
|||||||
pub fn zend_wrong_parameters_count_error(min_num_args: u32, max_num_args: u32);
|
pub fn zend_wrong_parameters_count_error(min_num_args: u32, max_num_args: u32);
|
||||||
}
|
}
|
||||||
extern "C" {
|
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)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
@ -1318,7 +1327,7 @@ pub struct _zend_ini_entry {
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
pub fn zend_register_bool_constant(
|
pub fn zend_register_bool_constant(
|
||||||
name: *const ::std::os::raw::c_char,
|
name: *const ::std::os::raw::c_char,
|
||||||
name_len: size_t,
|
name_len: usize,
|
||||||
bval: bool,
|
bval: bool,
|
||||||
flags: ::std::os::raw::c_int,
|
flags: ::std::os::raw::c_int,
|
||||||
module_number: ::std::os::raw::c_int,
|
module_number: ::std::os::raw::c_int,
|
||||||
@ -1327,7 +1336,7 @@ extern "C" {
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
pub fn zend_register_long_constant(
|
pub fn zend_register_long_constant(
|
||||||
name: *const ::std::os::raw::c_char,
|
name: *const ::std::os::raw::c_char,
|
||||||
name_len: size_t,
|
name_len: usize,
|
||||||
lval: zend_long,
|
lval: zend_long,
|
||||||
flags: ::std::os::raw::c_int,
|
flags: ::std::os::raw::c_int,
|
||||||
module_number: ::std::os::raw::c_int,
|
module_number: ::std::os::raw::c_int,
|
||||||
@ -1336,7 +1345,7 @@ extern "C" {
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
pub fn zend_register_double_constant(
|
pub fn zend_register_double_constant(
|
||||||
name: *const ::std::os::raw::c_char,
|
name: *const ::std::os::raw::c_char,
|
||||||
name_len: size_t,
|
name_len: usize,
|
||||||
dval: f64,
|
dval: f64,
|
||||||
flags: ::std::os::raw::c_int,
|
flags: ::std::os::raw::c_int,
|
||||||
module_number: ::std::os::raw::c_int,
|
module_number: ::std::os::raw::c_int,
|
||||||
@ -1345,7 +1354,7 @@ extern "C" {
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
pub fn zend_register_string_constant(
|
pub fn zend_register_string_constant(
|
||||||
name: *const ::std::os::raw::c_char,
|
name: *const ::std::os::raw::c_char,
|
||||||
name_len: size_t,
|
name_len: usize,
|
||||||
strval: *const ::std::os::raw::c_char,
|
strval: *const ::std::os::raw::c_char,
|
||||||
flags: ::std::os::raw::c_int,
|
flags: ::std::os::raw::c_int,
|
||||||
module_number: ::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);
|
pub fn zend_do_implement_interface(ce: *mut zend_class_entry, iface: *mut zend_class_entry);
|
||||||
}
|
}
|
||||||
extern "C" {
|
extern "C" {
|
||||||
pub fn ext_php_rs_zend_string_init(
|
pub static mut zend_ce_traversable: *mut zend_class_entry;
|
||||||
str_: *const ::std::os::raw::c_char,
|
|
||||||
len: size_t,
|
|
||||||
persistent: bool,
|
|
||||||
) -> *mut zend_string;
|
|
||||||
}
|
}
|
||||||
extern "C" {
|
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" {
|
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" {
|
extern "C" {
|
||||||
pub fn ext_php_rs_zend_object_alloc(
|
pub static mut zend_ce_arrayaccess: *mut zend_class_entry;
|
||||||
obj_size: size_t,
|
|
||||||
ce: *mut zend_class_entry,
|
|
||||||
) -> *mut ::std::os::raw::c_void;
|
|
||||||
}
|
}
|
||||||
extern "C" {
|
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" {
|
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
|
# Summary
|
||||||
|
|
||||||
- [Introduction](./introduction.md)
|
[Introduction](./introduction.md)
|
||||||
- [`cargo php`](./cargo-php.md)
|
|
||||||
- [Examples](./examples/index.md)
|
# Getting Started
|
||||||
- [Hello World](./examples/hello_world.md)
|
|
||||||
|
- [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)
|
- [Types](./types/index.md)
|
||||||
- [Primitive Numbers](./types/numbers.md)
|
- [Primitive Numbers](./types/numbers.md)
|
||||||
- [`String`](./types/string.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)
|
|
@ -84,7 +84,7 @@ personally recommend for use in Visual Studio Code).
|
|||||||
|
|
||||||
```text
|
```text
|
||||||
$ cargo php stubs --help
|
$ cargo php stubs --help
|
||||||
cargo-php-stubs
|
cargo-php-stubs
|
||||||
|
|
||||||
Generates stub PHP files for the extension.
|
Generates stub PHP files for the extension.
|
||||||
|
|
||||||
@ -106,7 +106,7 @@ OPTIONS:
|
|||||||
--manifest <MANIFEST>
|
--manifest <MANIFEST>
|
||||||
Path to the Cargo manifest of the extension. Defaults to the manifest in the directory
|
Path to the Cargo manifest of the extension. Defaults to the manifest in the directory
|
||||||
the command is called.
|
the command is called.
|
||||||
|
|
||||||
This cannot be provided alongside the `ext` option, as that option provides a direct
|
This cannot be provided alongside the `ext` option, as that option provides a direct
|
||||||
path to the extension shared library.
|
path to the extension shared library.
|
||||||
|
|
||||||
@ -130,7 +130,7 @@ so you are able to restore if you run into any issues.
|
|||||||
|
|
||||||
```text
|
```text
|
||||||
$ cargo php install --help
|
$ cargo php install --help
|
||||||
cargo-php-install
|
cargo-php-install
|
||||||
|
|
||||||
Installs the extension in the current PHP installation.
|
Installs the extension in the current PHP installation.
|
||||||
|
|
||||||
@ -164,6 +164,9 @@ OPTIONS:
|
|||||||
|
|
||||||
--release
|
--release
|
||||||
Whether to install the release version of the extension
|
Whether to install the release version of the extension
|
||||||
|
|
||||||
|
--yes
|
||||||
|
Bypasses the confirmation prompt
|
||||||
```
|
```
|
||||||
|
|
||||||
## Extension Removal
|
## Extension Removal
|
||||||
@ -175,7 +178,7 @@ from your `php.ini` if present.
|
|||||||
|
|
||||||
```text
|
```text
|
||||||
$ cargo php remove --help
|
$ cargo php remove --help
|
||||||
cargo-php-remove
|
cargo-php-remove
|
||||||
|
|
||||||
Removes the extension in the current PHP installation.
|
Removes the extension in the current PHP installation.
|
||||||
|
|
||||||
@ -203,6 +206,9 @@ OPTIONS:
|
|||||||
--manifest <MANIFEST>
|
--manifest <MANIFEST>
|
||||||
Path to the Cargo manifest of the extension. Defaults to the manifest in the directory
|
Path to the Cargo manifest of the extension. Defaults to the manifest in the directory
|
||||||
the command is called
|
the command is called
|
||||||
|
|
||||||
|
--yes
|
||||||
|
Bypasses the confirmation prompt
|
||||||
```
|
```
|
||||||
|
|
||||||
[`cargo-php`]: https://crates.io/crates/cargo-php
|
[`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.
|
- `rename` - Allows you to rename the property, e.g.
|
||||||
`#[prop(rename = "new_name")]`
|
`#[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
|
## Example
|
||||||
|
|
||||||
This example creates a PHP class `Human`, adding a PHP property `address`.
|
This example creates a PHP class `Human`, adding a PHP property `address`.
|
||||||
@ -79,3 +107,56 @@ pub fn throw_exception() -> PhpResult<i32> {
|
|||||||
# }
|
# }
|
||||||
# fn main() {}
|
# 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.
|
/// return value of the function, or an error.
|
||||||
///
|
///
|
||||||
/// You should not call this function directly, rather through the
|
/// You should not call this function directly, rather through the
|
||||||
/// [`call_user_func`] macro.
|
/// [`call_user_func`](crate::call_user_func) macro.
|
||||||
///
|
///
|
||||||
/// # Parameters
|
/// # Parameters
|
||||||
///
|
///
|
||||||
|
@ -6,16 +6,11 @@
|
|||||||
|
|
||||||
use crate::ffi::zend_string;
|
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::{
|
use crate::{convert::FromZval, flags::DataType, types::Zval};
|
||||||
convert::FromZval,
|
|
||||||
error::{Error, Result},
|
|
||||||
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.
|
/// Primarily used for passing read-only binary data into Rust functions.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct BinarySlice<'a, T>(&'a [T])
|
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
|
where
|
||||||
T: PackSlice,
|
T: PackSlice,
|
||||||
{
|
{
|
||||||
const TYPE: DataType = DataType::String;
|
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)
|
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]
|
impl<'a, T> From<BinarySlice<'a, T>> for &'a [T]
|
||||||
where
|
where
|
||||||
T: PackSlice,
|
T: PackSlice,
|
||||||
@ -117,7 +101,7 @@ pub unsafe trait PackSlice: Clone {
|
|||||||
/// * `s` - The Zend string containing the binary data.
|
/// * `s` - The Zend string containing the binary data.
|
||||||
///
|
///
|
||||||
/// [`pack`]: https://www.php.net/manual/en/function.pack.php
|
/// [`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.
|
/// Implements the [`PackSlice`] trait for a given type.
|
||||||
@ -128,7 +112,7 @@ macro_rules! pack_slice_impl {
|
|||||||
|
|
||||||
($t: ty, $d: expr) => {
|
($t: ty, $d: expr) => {
|
||||||
unsafe impl PackSlice for $t {
|
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 bytes = ($d / 8) as usize;
|
||||||
let len = (s.len as usize) / bytes;
|
let len = (s.len as usize) / bytes;
|
||||||
let ptr = s.val.as_ptr() as *const $t;
|
let ptr = s.val.as_ptr() as *const $t;
|
||||||
|
@ -85,7 +85,7 @@ impl ClassBuilder {
|
|||||||
/// * `func` - The function entry to add to the class.
|
/// * `func` - The function entry to add to the class.
|
||||||
/// * `flags` - Flags relating to the function. See [`MethodFlags`].
|
/// * `flags` - Flags relating to the function. See [`MethodFlags`].
|
||||||
pub fn method(mut self, mut func: FunctionEntry, flags: MethodFlags) -> Self {
|
pub fn method(mut self, mut func: FunctionEntry, flags: MethodFlags) -> Self {
|
||||||
func.flags = flags.bits();
|
func.flags |= flags.bits();
|
||||||
self.methods.push(func);
|
self.methods.push(func);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@ -226,7 +226,7 @@ impl ClassBuilder {
|
|||||||
///
|
///
|
||||||
/// Returns an [`Error`] variant if the class could not be registered.
|
/// Returns an [`Error`] variant if the class could not be registered.
|
||||||
pub fn build(mut self) -> Result<&'static mut ClassEntry> {
|
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());
|
self.methods.push(FunctionEntry::end());
|
||||||
let func = Box::into_raw(self.methods.into_boxed_slice()) as *const FunctionEntry;
|
let func = Box::into_raw(self.methods.into_boxed_slice()) as *const FunctionEntry;
|
||||||
@ -284,7 +284,7 @@ impl ClassBuilder {
|
|||||||
zend_declare_class_constant(
|
zend_declare_class_constant(
|
||||||
class,
|
class,
|
||||||
CString::new(name.as_str())?.as_ptr(),
|
CString::new(name.as_str())?.as_ptr(),
|
||||||
name.len() as u64,
|
name.len(),
|
||||||
value,
|
value,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
args::{Arg, ArgInfo},
|
args::{Arg, ArgInfo},
|
||||||
error::{Error, Result},
|
error::{Error, Result},
|
||||||
flags::DataType,
|
flags::{DataType, MethodFlags},
|
||||||
types::Zval,
|
types::Zval,
|
||||||
zend::{ExecuteData, FunctionEntry, ZendType},
|
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
|
/// Creates a constructor builder, used to build the constructor
|
||||||
/// for classes.
|
/// for classes.
|
||||||
///
|
///
|
||||||
|
@ -153,6 +153,7 @@ impl ToStub for Parameter {
|
|||||||
|
|
||||||
impl ToStub for DataType {
|
impl ToStub for DataType {
|
||||||
fn fmt_stub(&self, buf: &mut String) -> FmtResult {
|
fn fmt_stub(&self, buf: &mut String) -> FmtResult {
|
||||||
|
let mut fqdn = "\\".to_owned();
|
||||||
write!(
|
write!(
|
||||||
buf,
|
buf,
|
||||||
"{}",
|
"{}",
|
||||||
@ -162,7 +163,10 @@ impl ToStub for DataType {
|
|||||||
DataType::Double => "float",
|
DataType::Double => "float",
|
||||||
DataType::String => "string",
|
DataType::String => "string",
|
||||||
DataType::Array => "array",
|
DataType::Array => "array",
|
||||||
DataType::Object(Some(ty)) => ty,
|
DataType::Object(Some(ty)) => {
|
||||||
|
fqdn.push_str(ty);
|
||||||
|
fqdn.as_str()
|
||||||
|
}
|
||||||
DataType::Object(None) => "object",
|
DataType::Object(None) => "object",
|
||||||
DataType::Resource => "resource",
|
DataType::Resource => "resource",
|
||||||
DataType::Reference => "reference",
|
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
|
/// The string could not be converted into a C-string due to the presence of
|
||||||
/// a NUL character.
|
/// a NUL character.
|
||||||
InvalidCString,
|
InvalidCString,
|
||||||
|
/// The string could not be converted into a valid Utf8 string
|
||||||
|
InvalidUtf8,
|
||||||
/// Could not call the given function.
|
/// Could not call the given function.
|
||||||
Callable,
|
Callable,
|
||||||
/// An invalid exception type was thrown.
|
/// An invalid exception type was thrown.
|
||||||
@ -82,6 +84,7 @@ impl Display for Error {
|
|||||||
f,
|
f,
|
||||||
"String given contains NUL-bytes which cannot be present in a C string."
|
"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::Callable => write!(f, "Could not call given function."),
|
||||||
Error::InvalidException(flags) => {
|
Error::InvalidException(flags) => {
|
||||||
write!(f, "Invalid exception type was thrown: {:?}", flags)
|
write!(f, "Invalid exception type was thrown: {:?}", flags)
|
||||||
|
@ -19,6 +19,9 @@ extern "C" {
|
|||||||
persistent: bool,
|
persistent: bool,
|
||||||
) -> *mut zend_string;
|
) -> *mut zend_string;
|
||||||
pub fn ext_php_rs_zend_string_release(zs: *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_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_alloc(obj_size: usize, ce: *mut zend_class_entry) -> *mut c_void;
|
||||||
pub fn ext_php_rs_zend_object_release(obj: *mut zend_object);
|
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
|
/// [`strpos`]: https://www.php.net/manual/en/function.strpos.php
|
||||||
/// [`IntoZval`]: ext_php_rs::php::types::zval::IntoZval
|
/// [`IntoZval`]: crate::convert::IntoZval
|
||||||
/// [`Zval`]: ext_php_rs::php::types::zval::Zval
|
/// [`Zval`]: crate::types::Zval
|
||||||
pub use ext_php_rs_derive::php_extern;
|
pub use ext_php_rs_derive::php_extern;
|
||||||
|
|
||||||
/// Attribute used to annotate a function as a PHP function.
|
/// 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
|
/// 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`).
|
/// two optional parameters (`description` and `age`).
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
@ -289,12 +289,12 @@ pub use ext_php_rs_derive::php_extern;
|
|||||||
///
|
///
|
||||||
/// [`Result<T, E>`]: std::result::Result
|
/// [`Result<T, E>`]: std::result::Result
|
||||||
/// [`FunctionBuilder`]: crate::php::function::FunctionBuilder
|
/// [`FunctionBuilder`]: crate::php::function::FunctionBuilder
|
||||||
/// [`FromZval`]: crate::php::types::zval::FromZval
|
/// [`FromZval`]: crate::convert::FromZval
|
||||||
/// [`IntoZval`]: crate::php::types::zval::IntoZval
|
/// [`IntoZval`]: crate::convert::IntoZval
|
||||||
/// [`Zval`]: crate::php::types::zval::Zval
|
/// [`Zval`]: crate::types::Zval.
|
||||||
/// [`Binary<T>`]: crate::php::types::binary::Binary
|
/// [`Binary<T>`]: crate::binary::Binary
|
||||||
/// [`ZendCallable`]: crate::php::types::callable::ZendCallable
|
/// [`ZendCallable`]: crate::types::ZendCallable
|
||||||
/// [`PhpException`]: crate::php::exceptions::PhpException
|
/// [`PhpException`]: crate::exception::PhpException
|
||||||
pub use ext_php_rs_derive::php_function;
|
pub use ext_php_rs_derive::php_function;
|
||||||
|
|
||||||
/// Annotates a structs `impl` block, declaring that all methods and constants
|
/// 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
|
/// this macro if you have registered any classes or constants when using the
|
||||||
/// [`macro@php_module`] macro.
|
/// [`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
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
@ -665,12 +670,12 @@ pub use ext_php_rs_derive::php_startup;
|
|||||||
/// var_dump(give_union()); // int(5)
|
/// var_dump(give_union()); // int(5)
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// [`FromZval`]: crate::php::types::zval::FromZval
|
/// [`FromZval`]: crate::convert::FromZval
|
||||||
/// [`IntoZval`]: crate::php::types::zval::IntoZval
|
/// [`IntoZval`]: crate::convert::IntoZval
|
||||||
/// [`FromZendObject`]: crate::php::types::object::FromZendObject
|
/// [`FromZendObject`]: crate::convert::FromZendObject
|
||||||
/// [`IntoZendObject`]: crate::php::types::object::IntoZendObject
|
/// [`IntoZendObject`]: crate::convert::IntoZendObject
|
||||||
/// [`Zval`]: crate::php::types::zval::Zval
|
/// [`Zval`]: crate::types::Zval.
|
||||||
/// [`Zval::string`]: crate::php::types::zval::Zval::string
|
/// [`Zval::string`]: crate::types::Zval.::string
|
||||||
pub use ext_php_rs_derive::ZvalConvert;
|
pub use ext_php_rs_derive::ZvalConvert;
|
||||||
|
|
||||||
/// Defines an `extern` function with the Zend fastcall convention based on
|
/// Defines an `extern` function with the Zend fastcall convention based on
|
||||||
|
@ -312,14 +312,7 @@ impl ZendHashTable {
|
|||||||
V: IntoZval,
|
V: IntoZval,
|
||||||
{
|
{
|
||||||
let mut val = val.into_zval(false)?;
|
let mut val = val.into_zval(false)?;
|
||||||
unsafe {
|
unsafe { zend_hash_str_update(self, CString::new(key)?.as_ptr(), key.len(), &mut val) };
|
||||||
zend_hash_str_update(
|
|
||||||
self,
|
|
||||||
CString::new(key)?.as_ptr(),
|
|
||||||
key.len() as u64,
|
|
||||||
&mut val,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
val.release();
|
val.release();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -80,6 +80,17 @@ impl ZendObject {
|
|||||||
unsafe { ZBox::from_raw(this.get_mut_zend_obj()) }
|
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.
|
/// Attempts to retrieve the class name of the object.
|
||||||
pub fn get_class_name(&self) -> Result<String> {
|
pub fn get_class_name(&self) -> Result<String> {
|
||||||
unsafe {
|
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
|
/// Checks if the given object is an instance of a registered class with
|
||||||
/// Rust type `T`.
|
/// Rust type `T`.
|
||||||
|
///
|
||||||
|
/// This method doesn't check the class and interface inheritance chain.
|
||||||
pub fn is_instance<T: RegisteredClass>(&self) -> bool {
|
pub fn is_instance<T: RegisteredClass>(&self) -> bool {
|
||||||
(self.ce as *const ClassEntry).eq(&(T::get_metadata().ce() as *const _))
|
(self.ce as *const ClassEntry).eq(&(T::get_metadata().ce() as *const _))
|
||||||
}
|
}
|
||||||
@ -113,7 +137,7 @@ impl ZendObject {
|
|||||||
return Err(Error::InvalidProperty);
|
return Err(Error::InvalidProperty);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut name = ZendStr::new(name, false)?;
|
let mut name = ZendStr::new(name, false);
|
||||||
let mut rv = Zval::new();
|
let mut rv = Zval::new();
|
||||||
|
|
||||||
let zv = unsafe {
|
let zv = unsafe {
|
||||||
@ -138,7 +162,7 @@ impl ZendObject {
|
|||||||
/// * `name` - The name of the property.
|
/// * `name` - The name of the property.
|
||||||
/// * `value` - The value to set the property to.
|
/// * `value` - The value to set the property to.
|
||||||
pub fn set_property(&mut self, name: &str, value: impl IntoZval) -> Result<()> {
|
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)?;
|
let mut value = value.into_zval(false)?;
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
@ -163,7 +187,7 @@ impl ZendObject {
|
|||||||
/// * `name` - The name of the property.
|
/// * `name` - The name of the property.
|
||||||
/// * `query` - The 'query' to classify if a property exists.
|
/// * `query` - The 'query' to classify if a property exists.
|
||||||
pub fn has_property(&self, name: &str, query: PropertyQuery) -> Result<bool> {
|
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 {
|
Ok(unsafe {
|
||||||
self.handlers()?.has_property.ok_or(Error::InvalidScope)?(
|
self.handlers()?.has_property.ok_or(Error::InvalidScope)?(
|
||||||
@ -196,6 +220,29 @@ impl ZendObject {
|
|||||||
T::from_zend_object(self)
|
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.
|
/// Attempts to retrieve a reference to the object handlers.
|
||||||
#[inline]
|
#[inline]
|
||||||
unsafe fn handlers(&self) -> Result<&ZendObjectHandlers> {
|
unsafe fn handlers(&self) -> Result<&ZendObjectHandlers> {
|
||||||
|
@ -16,6 +16,7 @@ use crate::{
|
|||||||
convert::{FromZval, IntoZval},
|
convert::{FromZval, IntoZval},
|
||||||
error::{Error, Result},
|
error::{Error, Result},
|
||||||
ffi::{
|
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,
|
ext_php_rs_zend_string_init, ext_php_rs_zend_string_release, zend_string,
|
||||||
zend_string_init_interned,
|
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
|
/// 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
|
/// type to represent the contents of the string, therefore this type is
|
||||||
/// actually unsized. All constructors return [`ZBox<ZendStr>`], the owned
|
/// actually unsized. All constructors return [`ZBox<ZendStr>`], the owned
|
||||||
/// varaint.
|
/// variant.
|
||||||
///
|
///
|
||||||
/// Once the `ptr_metadata` feature lands in stable rust, this type can
|
/// 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>
|
/// 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>
|
// on the alias `ZendStr` :( <https://github.com/rust-lang/rust-clippy/issues/7702>
|
||||||
#[allow(clippy::len_without_is_empty)]
|
#[allow(clippy::len_without_is_empty)]
|
||||||
impl ZendStr {
|
impl ZendStr {
|
||||||
/// Creates a new Zend string from a [`str`].
|
/// Creates a new Zend string from a slice of bytes.
|
||||||
///
|
///
|
||||||
/// # Parameters
|
/// # Parameters
|
||||||
///
|
///
|
||||||
@ -54,12 +55,6 @@ impl ZendStr {
|
|||||||
/// * `persistent` - Whether the string should persist through the request
|
/// * `persistent` - Whether the string should persist through the request
|
||||||
/// boundary.
|
/// 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
|
||||||
///
|
///
|
||||||
/// Panics if the function was unable to allocate memory for the Zend
|
/// Panics if the function was unable to allocate memory for the Zend
|
||||||
@ -78,10 +73,19 @@ impl ZendStr {
|
|||||||
/// ```no_run
|
/// ```no_run
|
||||||
/// use ext_php_rs::types::ZendStr;
|
/// 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>> {
|
pub fn new(str: impl AsRef<[u8]>, persistent: bool) -> ZBox<Self> {
|
||||||
Ok(Self::from_c_str(&CString::new(str)?, persistent))
|
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`].
|
/// 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
|
/// An interned string is only ever stored once and is immutable. PHP stores
|
||||||
/// the string in an internal hashtable which stores the interned
|
/// 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
|
/// * `persistent` - Whether the string should persist through the request
|
||||||
/// boundary.
|
/// 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
|
||||||
///
|
///
|
||||||
/// Panics if the function was unable to allocate memory for the Zend
|
/// Panics under the following circumstances:
|
||||||
/// string.
|
///
|
||||||
|
/// * The function used to create interned strings has not been set.
|
||||||
|
/// * The function could not allocate enough memory for the Zend string.
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
@ -171,8 +171,16 @@ impl ZendStr {
|
|||||||
///
|
///
|
||||||
/// let s = ZendStr::new_interned("PHP", true);
|
/// let s = ZendStr::new_interned("PHP", true);
|
||||||
/// ```
|
/// ```
|
||||||
pub fn new_interned(str: &str, persistent: bool) -> Result<ZBox<Self>> {
|
pub fn new_interned(str: impl AsRef<[u8]>, persistent: bool) -> ZBox<Self> {
|
||||||
Ok(Self::interned_from_c_str(&CString::new(str)?, persistent))
|
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`].
|
/// Creates a new interned Zend string from a [`CStr`].
|
||||||
@ -222,11 +230,8 @@ impl ZendStr {
|
|||||||
let _lock = INTERNED_LOCK.lock();
|
let _lock = INTERNED_LOCK.lock();
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let ptr = zend_string_init_interned.expect("`zend_string_init_interned` not ready")(
|
let init = zend_string_init_interned.expect("`zend_string_init_interned` not ready");
|
||||||
str.as_ptr(),
|
let ptr = init(str.as_ptr(), str.to_bytes().len() as _, persistent);
|
||||||
str.to_bytes().len() as _,
|
|
||||||
persistent,
|
|
||||||
);
|
|
||||||
|
|
||||||
ZBox::from_raw(
|
ZBox::from_raw(
|
||||||
ptr.as_mut()
|
ptr.as_mut()
|
||||||
@ -242,7 +247,7 @@ impl ZendStr {
|
|||||||
/// ```no_run
|
/// ```no_run
|
||||||
/// use ext_php_rs::types::ZendStr;
|
/// 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);
|
/// assert_eq!(s.len(), 13);
|
||||||
/// ```
|
/// ```
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
@ -256,39 +261,61 @@ impl ZendStr {
|
|||||||
/// ```no_run
|
/// ```no_run
|
||||||
/// use ext_php_rs::types::ZendStr;
|
/// 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);
|
/// assert_eq!(s.is_empty(), false);
|
||||||
/// ```
|
/// ```
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.len() == 0
|
self.len() == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a reference to the underlying [`CStr`] inside the Zend string.
|
/// Attempts to return a reference to the underlying bytes inside the Zend
|
||||||
pub fn as_c_str(&self) -> &CStr {
|
/// string as a [`CStr`].
|
||||||
// SAFETY: Zend strings store their readable length in a fat pointer.
|
///
|
||||||
unsafe {
|
/// Returns an [Error::InvalidCString] variant if the string contains null
|
||||||
let slice = slice::from_raw_parts(self.val.as_ptr() as *const u8, self.len() + 1);
|
/// bytes.
|
||||||
CStr::from_bytes_with_nul_unchecked(slice)
|
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.
|
/// string.
|
||||||
///
|
///
|
||||||
/// Returns the [`None`] variant if the [`CStr`] contains non-UTF-8
|
/// Returns an [Error::InvalidUtf8] variant if the [`str`] contains
|
||||||
/// characters.
|
/// non-UTF-8 characters.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```no_run
|
/// ```no_run
|
||||||
/// use ext_php_rs::types::ZendStr;
|
/// use ext_php_rs::types::ZendStr;
|
||||||
///
|
///
|
||||||
/// let s = ZendStr::new("hello, world!", false).unwrap();
|
/// let s = ZendStr::new("hello, world!", false);
|
||||||
/// let as_str = s.as_str();
|
/// assert!(s.as_str().is_ok());
|
||||||
/// assert_eq!(as_str, Some("hello, world!"));
|
|
||||||
/// ```
|
/// ```
|
||||||
pub fn as_str(&self) -> Option<&str> {
|
pub fn as_str(&self) -> Result<&str> {
|
||||||
self.as_c_str().to_str().ok()
|
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 {
|
impl Debug for ZendStr {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
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>;
|
type Owned = ZBox<ZendStr>;
|
||||||
|
|
||||||
fn to_owned(&self) -> Self::Owned {
|
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 {
|
impl<'a> TryFrom<&'a ZendStr> for &'a CStr {
|
||||||
#[inline]
|
type Error = Error;
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
self.as_c_str().eq(other.as_c_str())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> From<&'a ZendStr> for &'a CStr {
|
fn try_from(value: &'a ZendStr) -> Result<Self> {
|
||||||
fn from(value: &'a ZendStr) -> Self {
|
|
||||||
value.as_c_str()
|
value.as_c_str()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -329,7 +366,7 @@ impl<'a> TryFrom<&'a ZendStr> for &'a str {
|
|||||||
type Error = Error;
|
type Error = Error;
|
||||||
|
|
||||||
fn try_from(value: &'a ZendStr) -> Result<Self> {
|
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;
|
type Error = Error;
|
||||||
|
|
||||||
fn try_from(value: &ZendStr) -> Result<Self> {
|
fn try_from(value: &ZendStr) -> Result<Self> {
|
||||||
value
|
value.as_str().map(ToString::to_string)
|
||||||
.as_str()
|
|
||||||
.map(|s| s.to_string())
|
|
||||||
.ok_or(Error::InvalidCString)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -362,18 +396,14 @@ impl From<CString> for ZBox<ZendStr> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<&str> for ZBox<ZendStr> {
|
impl From<&str> for ZBox<ZendStr> {
|
||||||
type Error = Error;
|
fn from(value: &str) -> Self {
|
||||||
|
ZendStr::new(value.as_bytes(), false)
|
||||||
fn try_from(value: &str) -> Result<Self> {
|
|
||||||
ZendStr::new(value, false)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<String> for ZBox<ZendStr> {
|
impl From<String> for ZBox<ZendStr> {
|
||||||
type Error = Error;
|
fn from(value: String) -> Self {
|
||||||
|
|
||||||
fn try_from(value: String) -> Result<Self> {
|
|
||||||
ZendStr::new(value.as_str(), false)
|
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
|
/// convert other types into a [`String`], as it could not pass back a
|
||||||
/// [`&str`] in those cases.
|
/// [`&str`] in those cases.
|
||||||
pub fn str(&self) -> Option<&str> {
|
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
|
/// 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)
|
/// a vector of a given type. Similar to the [`unpack`] function in PHP,
|
||||||
/// in PHP, except you can only unpack one type.
|
/// except you can only unpack one type.
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
@ -129,22 +129,31 @@ impl Zval {
|
|||||||
/// documentation for more details.
|
/// documentation for more details.
|
||||||
///
|
///
|
||||||
/// [`pack`]: https://www.php.net/manual/en/function.pack.php
|
/// [`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>> {
|
pub fn binary<T: Pack>(&self) -> Option<Vec<T>> {
|
||||||
if self.is_string() {
|
self.zend_str().map(T::unpack_into)
|
||||||
// SAFETY: Type is string therefore we are able to take a reference.
|
|
||||||
Some(T::unpack_into(unsafe { self.value.str_.as_ref() }?))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn binary_slice<'a, T: PackSlice>(&self) -> Option<&'a [T]> {
|
/// Returns the value of the zval if it is a string and can be unpacked into
|
||||||
if self.is_string() {
|
/// a slice of a given type. Similar to the [`unpack`] function in PHP,
|
||||||
// SAFETY: Type is string therefore we are able to take a reference.
|
/// except you can only unpack one type.
|
||||||
Some(T::unpack_into(unsafe { self.value.str_.as_ref() }?))
|
///
|
||||||
} else {
|
/// This function is similar to [`Zval::binary`] except that a slice is
|
||||||
None
|
/// 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.
|
/// 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.
|
/// * `val` - The value to set the zval as.
|
||||||
/// * `persistent` - Whether the string should persist between requests.
|
/// * `persistent` - Whether the string should persist between requests.
|
||||||
pub fn set_string(&mut self, val: &str, persistent: bool) -> Result<()> {
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -365,7 +374,7 @@ impl Zval {
|
|||||||
/// * `val` - The value to set the zval as.
|
/// * `val` - The value to set the zval as.
|
||||||
/// * `persistent` - Whether the string should persist between requests.
|
/// * `persistent` - Whether the string should persist between requests.
|
||||||
pub fn set_interned_string(&mut self, val: &str, persistent: bool) -> Result<()> {
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
#include "wrapper.h"
|
#include "wrapper.h"
|
||||||
|
|
||||||
zend_string *ext_php_rs_zend_string_init(const char *str, size_t len,
|
zend_string *ext_php_rs_zend_string_init(const char *str, size_t len, bool persistent) {
|
||||||
bool persistent) {
|
|
||||||
return zend_string_init(str, len, 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);
|
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; }
|
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) {
|
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_inheritance.h"
|
||||||
#include "zend_interfaces.h"
|
#include "zend_interfaces.h"
|
||||||
|
|
||||||
zend_string *ext_php_rs_zend_string_init(const char *str, size_t len,
|
zend_string *ext_php_rs_zend_string_init(const char *str, size_t len, bool persistent);
|
||||||
bool persistent);
|
|
||||||
void ext_php_rs_zend_string_release(zend_string *zs);
|
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();
|
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_alloc(size_t obj_size, zend_class_entry *ce);
|
||||||
void ext_php_rs_zend_object_release(zend_object *obj);
|
void ext_php_rs_zend_object_release(zend_object *obj);
|
||||||
|
@ -3,70 +3,107 @@
|
|||||||
#![allow(clippy::unwrap_used)]
|
#![allow(clippy::unwrap_used)]
|
||||||
|
|
||||||
use crate::ffi::{
|
use crate::ffi::{
|
||||||
zend_ce_argument_count_error, zend_ce_arithmetic_error, zend_ce_compile_error,
|
zend_ce_aggregate, zend_ce_argument_count_error, zend_ce_arithmetic_error, zend_ce_arrayaccess,
|
||||||
zend_ce_division_by_zero_error, zend_ce_error_exception, zend_ce_exception,
|
zend_ce_compile_error, zend_ce_countable, zend_ce_division_by_zero_error,
|
||||||
zend_ce_parse_error, zend_ce_throwable, zend_ce_type_error, zend_ce_unhandled_match_error,
|
zend_ce_error_exception, zend_ce_exception, zend_ce_iterator, zend_ce_parse_error,
|
||||||
zend_ce_value_error, zend_standard_class_def,
|
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;
|
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 {
|
pub fn stdclass() -> &'static ClassEntry {
|
||||||
unsafe { zend_standard_class_def.as_ref() }.unwrap()
|
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 {
|
pub fn throwable() -> &'static ClassEntry {
|
||||||
unsafe { zend_ce_throwable.as_ref() }.unwrap()
|
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 {
|
pub fn exception() -> &'static ClassEntry {
|
||||||
unsafe { zend_ce_exception.as_ref() }.unwrap()
|
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 {
|
pub fn error_exception() -> &'static ClassEntry {
|
||||||
unsafe { zend_ce_error_exception.as_ref() }.unwrap()
|
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 {
|
pub fn compile_error() -> &'static ClassEntry {
|
||||||
unsafe { zend_ce_compile_error.as_ref() }.unwrap()
|
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 {
|
pub fn parse_error() -> &'static ClassEntry {
|
||||||
unsafe { zend_ce_parse_error.as_ref() }.unwrap()
|
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 {
|
pub fn type_error() -> &'static ClassEntry {
|
||||||
unsafe { zend_ce_type_error.as_ref() }.unwrap()
|
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 {
|
pub fn argument_count_error() -> &'static ClassEntry {
|
||||||
unsafe { zend_ce_argument_count_error.as_ref() }.unwrap()
|
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 {
|
pub fn value_error() -> &'static ClassEntry {
|
||||||
unsafe { zend_ce_value_error.as_ref() }.unwrap()
|
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 {
|
pub fn arithmetic_error() -> &'static ClassEntry {
|
||||||
unsafe { zend_ce_arithmetic_error.as_ref() }.unwrap()
|
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 {
|
pub fn division_by_zero_error() -> &'static ClassEntry {
|
||||||
unsafe { zend_ce_division_by_zero_error.as_ref() }.unwrap()
|
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 {
|
pub fn unhandled_match_error() -> &'static ClassEntry {
|
||||||
unsafe { zend_ce_unhandled_match_error.as_ref() }.unwrap()
|
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.
|
/// could not be found or the class table has not been initialized.
|
||||||
pub fn try_find(name: &str) -> Option<&'static Self> {
|
pub fn try_find(name: &str) -> Option<&'static Self> {
|
||||||
ExecutorGlobals::get().class_table()?;
|
ExecutorGlobals::get().class_table()?;
|
||||||
let mut name = ZendStr::new(name, false).ok()?;
|
let mut name = ZendStr::new(name, false);
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
crate::ffi::zend_lookup_class_ex(name.deref_mut(), std::ptr::null_mut(), 0).as_ref()
|
crate::ffi::zend_lookup_class_ex(name.deref_mut(), std::ptr::null_mut(), 0).as_ref()
|
||||||
@ -37,37 +37,19 @@ impl ClassEntry {
|
|||||||
///
|
///
|
||||||
/// # Parameters
|
/// # Parameters
|
||||||
///
|
///
|
||||||
/// * `ce` - The inherited class entry to check.
|
/// * `other` - The inherited class entry to check.
|
||||||
pub fn instance_of(&self, ce: &ClassEntry) -> bool {
|
pub fn instance_of(&self, other: &ClassEntry) -> bool {
|
||||||
if self == ce {
|
if self == other {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ce.flags().contains(ClassFlags::Interface) {
|
if other.is_interface() {
|
||||||
let interfaces = match self.interfaces() {
|
return self
|
||||||
Some(interfaces) => interfaces,
|
.interfaces()
|
||||||
None => return false,
|
.map_or(false, |mut it| it.any(|ce| ce == other));
|
||||||
};
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
false
|
std::iter::successors(self.parent(), |p| p.parent()).any(|ce| ce == other)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns an iterator of all the interfaces that the class implements.
|
/// 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() }
|
unsafe { self.__bindgen_anon_1.parent.as_ref() }
|
||||||
} else {
|
} else {
|
||||||
let name = unsafe { self.__bindgen_anon_1.parent_name.as_ref()? };
|
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")?;
|
.ok_or("Invalid property name pointer given")?;
|
||||||
let self_ = &mut **obj;
|
let self_ = &mut **obj;
|
||||||
let props = T::get_metadata().get_properties();
|
let props = T::get_metadata().get_properties();
|
||||||
let prop = props.get(
|
let prop = props.get(prop_name.as_str()?);
|
||||||
prop_name
|
|
||||||
.as_str()
|
|
||||||
.ok_or("Invalid property name was given")?,
|
|
||||||
);
|
|
||||||
|
|
||||||
// retval needs to be treated as initialized, so we set the type to null
|
// 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")?;
|
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")?;
|
.ok_or("Invalid property name pointer given")?;
|
||||||
let self_ = &mut **obj;
|
let self_ = &mut **obj;
|
||||||
let props = T::get_metadata().get_properties();
|
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")?;
|
let value_mut = value.as_mut().ok_or("Invalid return zval given")?;
|
||||||
|
|
||||||
Ok(match prop {
|
Ok(match prop {
|
||||||
@ -220,7 +216,7 @@ impl ZendObjectHandlers {
|
|||||||
.as_ref()
|
.as_ref()
|
||||||
.ok_or("Invalid property name pointer given")?;
|
.ok_or("Invalid property name pointer given")?;
|
||||||
let props = T::get_metadata().get_properties();
|
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;
|
let self_ = &mut **obj;
|
||||||
|
|
||||||
match has_set_exists {
|
match has_set_exists {
|
||||||
|
@ -2,14 +2,14 @@ use std::{path::PathBuf, process::Command};
|
|||||||
|
|
||||||
use anyhow::{bail, Context, Result};
|
use anyhow::{bail, Context, Result};
|
||||||
|
|
||||||
use crate::{PHPInfo, PHPProvider};
|
use crate::{find_executable, path_from_env, PHPInfo, PHPProvider};
|
||||||
|
|
||||||
pub struct Provider {}
|
pub struct Provider {}
|
||||||
|
|
||||||
impl Provider {
|
impl Provider {
|
||||||
/// Runs `php-config` with one argument, returning the stdout.
|
/// Runs `php-config` with one argument, returning the stdout.
|
||||||
fn php_config(&self, arg: &str) -> Result<String> {
|
fn php_config(&self, arg: &str) -> Result<String> {
|
||||||
let cmd = Command::new("php-config")
|
let cmd = Command::new(self.find_bin()?)
|
||||||
.arg(arg)
|
.arg(arg)
|
||||||
.output()
|
.output()
|
||||||
.context("Failed to run `php-config`")?;
|
.context("Failed to run `php-config`")?;
|
||||||
@ -20,6 +20,22 @@ impl Provider {
|
|||||||
}
|
}
|
||||||
Ok(stdout.to_string())
|
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 {
|
impl<'a> PHPProvider<'a> for Provider {
|
||||||
|
Loading…
Reference in New Issue
Block a user