whynot 0.1.0

Rust ↔ PHP bridge: call PHP functions from Rust, capture return values, output, and exceptions.
Documentation
# whyNot πŸ˜βž‘οΈπŸ¦€
[![Crates.io](https://img.shields.io/crates/v/whynot.svg)](https://crates.io/crates/whynot)
[![Docs.rs](https://docs.rs/whynot/badge.svg)](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.