whynot

Crates.iowhynot
lib.rswhynot
version0.1.1
created_at2025-12-17 18:48:55.871009+00
updated_at2025-12-17 23:56:07.450103+00
descriptionRust ↔ PHP bridge: call PHP functions from Rust, capture return values, output, and exceptions.
homepagehttps://github.com/nia-cloud-official/whynot
repositoryhttps://github.com/nia-cloud-official/whynot
max_upload_size
id1990953
size35,066
Milton Vafana (nia-cloud-official)

documentation

https://docs.rs/whynot

README

whyNot πŸ˜βž‘οΈπŸ¦€

Crates.io Docs.rs

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:

cargo add whynot

Or add to your Cargo.toml:

[dependencies]
whynot = "0.1.1"

Requirements

  • PHP CLI (php) available in PATH
  • PHP scaffolding (php/runner.php and php/bootstrap.php) initialized in your project

Initialize PHP scaffolding

After installing the crate, install the CLI binary:

cargo install whynot

Then run:

whynot-init

This will create a php/ folder in your project root with:

  • runner.php β€” the bridge script used internally by whyNot
  • bootstrap.php β€” where you define your own PHP functions/classes

Edit bootstrap.php to add your PHP logic. The Rust side will call into these functions.

πŸ“– Usage

Basic call

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

let boom = rt.call("risky", &[]).unwrap();
if let Some(ex) = boom.exception {
    println!("Exception: {} ({})", ex.message, ex.class);
    println!("Trace: {}", ex.trace);
}

Async (threads)

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:

cargo run --example tokio_async --features async_tokio

Example:

#[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

  • Function calls, output capture, exceptions
  • Objects serialization
  • Includes, eval
  • Threaded async
  • Tokio async/await
  • Embedded Zend runtime (in‑process)
  • Resource/extension support
  • Automatic Rust ↔ PHP struct mapping
  • Publish crate to crates.io

🀝 Contributing

Pull requests welcome. Please open issues for bugs, feature requests, or questions.

Commit count: 0

cargo fmt