# whyNot πβ‘οΈπ¦
[](https://crates.io/crates/whynot)
[](https://docs.rs/whynot)
**Rust β PHP bridge** that lets you call PHP functions directly from Rust, capture return values, printed output, and exceptions β with async support via threads or Tokio.
---
## β¨ Features
- Call any PHP function from Rust
- Capture both return values and printed output
- Structured exception handling (`PhpException`)
- Objects returned with class + fields
- Persistent runtime (globals/includes survive across calls)
- `include` and `eval` support
- Async via threads (`call_async_with_cfg`)
- Async/await via Tokio (`--features async_tokio`)
---
## π Getting Started
### Install
To install you can run:
```bash
cargo add whynot
```
Or add to your `Cargo.toml`:
```toml
[dependencies]
whynot = "0.1.0"
```
### Requirements
- PHP CLI (`php`) available in PATH
- `php/runner.php` and `php/bootstrap.php` included in your project
---
## π Usage
### Basic call
```rust
use whynot::{new_runtime, PhpRuntime, RuntimeConfig, RuntimeKind};
fn main() {
let cfg = RuntimeConfig::default();
let mut rt = new_runtime(RuntimeKind::Process(cfg)).unwrap();
let result = rt.call("add", &[7.into(), 5.into()]).unwrap();
println!("add.result = {:?}", result.result);
println!("add.output = {:?}", result.output);
}
```
### Exceptions
```rust
let boom = rt.call("risky", &[]).unwrap();
if let Some(ex) = boom.exception {
println!("Exception: {} ({})", ex.message, ex.class);
println!("Trace: {}", ex.trace);
}
```
### Async (threads)
```rust
use whynot::process::ProcRuntime;
let h1 = ProcRuntime::call_async_with_cfg(cfg.clone(), "greet".to_string(), vec!["Milton".into()]);
let h2 = ProcRuntime::call_async_with_cfg(cfg.clone(), "add".to_string(), vec![7.into(), 9.into()]);
println!("greet = {:?}", h1.join().unwrap().unwrap().result);
println!("add = {:?}", h2.join().unwrap().unwrap().result);
```
### Async/await (Tokio)
Enable the feature:
```bash
cargo run --example tokio_async --features async_tokio
```
Example:
```rust
#[tokio::main]
async fn main() {
let cfg = RuntimeConfig::default();
let greet = whynot::async_tokio::call_async(cfg.clone(), "greet".to_string(), vec!["Milton".into()])
.await.unwrap();
println!("greet.result = {:?}", greet.result);
}
```
---
## π Project Layout
```
whynot/
Cargo.toml
src/
lib.rs
value.rs
macros.rs
process.rs
embedded.rs
async_tokio.rs
php/
runner.php
bootstrap.php
examples/
call_any.rs
async_calls.rs
tokio_async.rs
```
---
## π Roadmap
- [x] Function calls, output capture, exceptions
- [x] Objects serialization
- [x] Includes, eval
- [x] Threaded async
- [x] Tokio async/await
- [ ] Embedded Zend runtime (inβprocess)
- [ ] Resource/extension support
- [ ] Automatic Rust β PHP struct mapping
- [x] Publish crate to crates.io
---
## π€ Contributing
Pull requests welcome. Please open issues for bugs, feature requests, or questions.