Coder Social home page Coder Social logo

ext-php-rs's Introduction

ext-php-rs

Crates.io docs.rs Guide Workflow Status CI Workflow Status Discord

Bindings and abstractions for the Zend API to build PHP extensions natively in Rust.

Example

Export a simple function function hello_world(string $name): string to PHP:

#![cfg_attr(windows, feature(abi_vectorcall))]

use ext_php_rs::prelude::*;

/// Gives you a nice greeting!
/// 
/// @param string $name Your name.
/// 
/// @return string Nice greeting!
#[php_function]
pub fn hello_world(name: String) -> String {
    format!("Hello, {}!", name)
}

// Required to register the extension with PHP.
#[php_module]
pub fn module(module: ModuleBuilder) -> ModuleBuilder {
    module
}

Use cargo-php to build IDE stubs and install the extension:

$ cargo install cargo-php
  Installing cargo-php v0.1.0
$ cargo php stubs --stdout
  Compiling example-ext v0.1.0
  Finished dev [unoptimized + debuginfo] target(s) in 3.57s
<?php

// Stubs for example-ext

/**
 * Gives you a nice greeting!
 *
 * @param string $name Your name.
 *
 * @return string Nice greeting!
 */
function hello_world(string $name): string {}
$ cargo php install --release
  Compiling example-ext v0.1.0
  Finished release [optimized] target(s) in 1.68s
Are you sure you want to install the extension `example-ext`? yes
$ php -m
[PHP Modules]
// ...
example-ext
// ...

Calling the function from PHP:

var_dump(hello_world("David")); // string(13) "Hello, David!"

For more examples read the library guide.

Features

  • Easy to use: The built-in macros can abstract away the need to interact with the Zend API, such as Rust-type function parameter abstracting away interacting with Zend values.
  • Lightweight: You don't have to use the built-in helper macros. It's possible to write your own glue code around your own functions.
  • Extensible: Implement IntoZval and FromZval for your own custom types, allowing the type to be used as function parameters and return types.

Goals

Our main goal is to make extension development easier.

  • Writing extensions in C can be tedious, and with the Zend APIs limited documentation can be intimidating.
  • Rust's modern language features and feature-full standard library are big improvements on C.
  • Abstracting away the raw Zend APIs allows extensions to be developed faster and with more confidence.
  • Abstractions also allow us to support future (and potentially past) versions of PHP without significant changes to extension code.

Documentation

The library guide can be read here.

The project is documented in-line, so viewing the cargo documentation is the best resource at the moment. This can be viewed at docs.rs.

Requirements

  • Linux, macOS or Windows-based operating system.
  • PHP 8.0 or later.
    • No support is planned for earlier versions of PHP.
  • Rust.
    • Currently, we maintain no guarantee of a MSRV, however lib.rs suggests Rust 1.57 at the time of writing.
  • Clang 5.0 or later.

Windows Requirements

  • Extensions can only be compiled for PHP installations sourced from https://windows.php.net. Support is planned for other installations eventually.
  • Rust nightly is required for Windows. This is due to the vectorcall calling convention being used by some PHP functions on Windows, which is only available as a nightly unstable feature in Rust.
  • It is suggested to use the rust-lld linker to link your extension. The MSVC linker (link.exe) is supported however you may run into issues if the linker version is not supported by your PHP installation. You can use the rust-lld linker by creating a .cargo\config.toml file with the following content:
    # Replace target triple if you have a different architecture than x86_64
    [target.x86_64-pc-windows-msvc]
    linker = "rust-lld"
  • The cc crate requires cl.exe to be present on your system. This is usually bundled with Microsoft Visual Studio.
  • cargo-php's stub generation feature does not work on Windows. Rewriting this functionality to be cross-platform is on the roadmap.

Cargo Features

All features are disabled by default.

  • closure - Enables the ability to return Rust closures to PHP. Creates a new class type, RustClosure.
  • anyhow - Implements Into<PhpException> for anyhow::Error, allowing you to return anyhow results from PHP functions. Supports anyhow v1.x.

Usage

Check out one of the example projects:

Contributions

Contributions are very much welcome. I am a novice Rust developer and any suggestions are wanted and welcome. Feel free to file issues and PRs through Github.

Contributions welcome include:

  • Documentation expansion (examples in particular!)
  • Safety reviews (especially if you have experience with Rust and the Zend API).
  • Bug fixes and features.
  • Feature requests.

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

Resources

License

Licensed under either of

at your option.

ext-php-rs's People

Contributors

azjezz avatar christian-rades avatar danog avatar davidcole1340 avatar denzyldick avatar glyphpoch avatar joehoyle avatar joelwurtz avatar joycebabu avatar ju1ius avatar nikeee avatar pineappleionic avatar ptondereau avatar rmccue avatar roborourke avatar s00d avatar striezel avatar tobiasbengtsson avatar torstendittmann avatar vkill avatar vodik avatar willbrowningme avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ext-php-rs's Issues

Move zval value coercing into `FromZval`

At the moment, calling zval.string() will attempt to convert a f64 into a string, so zval.string() != zval.str() all the time. This should be removed, and a new function added to FromZval, coerce_from_zval():

pub trait FromZval<'a> {
    fn from_zval(zval: &'a Zval) -> Option<Self>;
    // by default, most types won't need coercing
    fn coerce_from_zval(zval: &'a Zval) -> Option<Self> {
        Self::from_zval(zval)
    }
}

`ArgParser` lifetimes are wrong

Currently, the ArgParser and Arg types are defined as so:

pub struct ArgParser<'a, 'arg, 'zval> {
    args: Vec<&'arg mut Arg<'zval>>,
    min_num_args: Option<u32>,
    execute_data: &'a ExecutionData,
}

pub struct Arg<'a> {
    name: String,
    _type: DataType,
    as_ref: bool,
    allow_null: bool,
    variadic: bool,
    default_value: Option<String>,
    zval: Option<&'a Zval>,
}

'zval should be replaced with 'a, as the zvals inside Arg are supplied from &'a ExecutionData. The compiler doesn't know this because pointers are converted into references.

How to pass array of callback as argument?

How to pass array of callback as argument?

Desired usage

$callbackSharedData = [];
$callbacks = [];
$callbacks[] = function() {
    echo "sleeping callback";
    sleep(1);
};

$callbacks[] = function() use ($callbackSharedData) {
    $callbackSharedData[] = 'item';
};
example_ext_pass_array_of_callbacks($callbacks);

Rust code

#[php_function]
pub fn example_ext_pass_array_of_callables(data: HashMap<String, ZendCallable>) {
    for (key, call) in data.iter() {
        call.try_call(vec![]).expect("Failed to call function");
    }
}

Fails with:

   Compiling ext-php-rs-example v0.1.0 (/app)
error: implementation of `FromZval` is not general enough
  --> src/lib.rs:24:1
   |
24 | #[php_function]
   | ^^^^^^^^^^^^^^^ implementation of `FromZval` is not general enough
   |
   = note: `FromZval<'0>` would have to be implemented for the type `ext_php_rs::types::ZendCallable<'_>`, for any lifetime `'0`...
   = note: ...but `FromZval<'1>` is actually implemented for the type `ext_php_rs::types::ZendCallable<'1>`, for some specific lifetime `'1`
   = note: this error originates in the attribute macro `php_function` (in Nightly builds, run with -Z macro-backtrace for more info)

error: implementation of `FromZval` is not general enough
  --> src/lib.rs:58:1
   |
58 | #[php_module]
   | ^^^^^^^^^^^^^ implementation of `FromZval` is not general enough
   |
   = note: `FromZval<'0>` would have to be implemented for the type `ext_php_rs::types::ZendCallable<'_>`, for any lifetime `'0`...
   = note: ...but `FromZval<'1>` is actually implemented for the type `ext_php_rs::types::ZendCallable<'1>`, for some specific lifetime `'1`
   = note: this error originates in the attribute macro `php_module` (in Nightly builds, run with -Z macro-backtrace for more info)

error: could not compile `ext-php-rs-example` due to 2 previous errors

So I have tried lifetimes like this:

#[php_function]
pub fn example_ext_pass_array_of_callables<'a>(data: HashMap<String, ZendCallable<'a>>) {
    for (key, call) in data.iter() {
        call.try_call(vec![]).expect("Failed to call function");
    }
}

Fails with:

   Compiling ext-php-rs-example v0.1.0 (/app)
error[E0261]: use of undeclared lifetime name `'a`
  --> src/lib.rs:24:1
   |
24 | #[php_function]
   | ^^^^^^^^^^^^^^^- lifetime `'a` is missing in item created through this procedural macro
   | |
   | undeclared lifetime
   |
   = note: this error originates in the attribute macro `php_function` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0261]: use of undeclared lifetime name `'a`
  --> src/lib.rs:58:1
   |
58 | #[php_module]
   | ^^^^^^^^^^^^^- lifetime `'a` is missing in item created through this procedural macro
   | |
   | undeclared lifetime
   |
   = note: this error originates in the attribute macro `php_module` (in Nightly builds, run with -Z macro-backtrace for more info)

For more information about this error, try `rustc --explain E0261`.
error: could not compile `ext-php-rs-example` due to 2 previous errors

Any ideas?
Is this even possible?

Thanks 🙏

👋🏻 Not an issue; I just wrote an extension!

This is a great lib! Tripped through some details that was probably mostly related to my level of Rust ability honestly.

Anyways, I figured I'd share in case the example can be useful for folks: https://github.com/jphenow/tomlrs-php

It's incomplete because I didn't finish packaging it up for composer or anything but still might give folks some ideas as they get started.

Feel free to just close if this isn't useful at all - mostly wanted to say thanks for this library!

Add `FromZval` derive macro

Ability to derive FromZval (and potentially IntoZval) using a derive macro. Could be used in two situations:

  • Enums. Data type would be mixed, and would act as a mixed type parameter. Example:
#[derive(FromZval)]
pub enum IntOrBool {
    Integer(u32),
    Boolean(bool),
}

// translates to

impl FromZval<'_> for IntOrBool {
    const TYPE: DataType = DataType::Mixed;
    fn from_zval(zval: &'_ Zval) -> Option<Self> {
        if let Some(integer) = u32::from_zval(zval) {
            Some(integer)
        } else if let Some(boolean) = bool::from_zval(zval) {
            Some(boolean)
        } else {
            None
        }
    }
}
  • Structs. Data type would be object, and the struct fields would be read from the objects properties. Primarily for passing stdClass into Rust. Example:
#[derive(FromZval)]
pub struct ExampleStdClass {
    a: i32,
    b: String,
    c: bool,
}

// translates to

impl FromZval<'_> for ExampleStdClass {
    const TYPE: DataType = DataType::Object(None);

    fn from_zval(zval: &'_ Zval) -> Option<Self> {
        let obj = zval.object()?;

        Some(Self {
            a: obj.get_property("a").ok()?,
            b: obj.get_property("b").ok()?,
            c: obj.get_property("c").ok()?,
        })
    }
}

Refactor `DataType` and `ZvalTypeFlags`

ZvalTypeFlags are not actually bitflags as the data types are exclusive and cannot be logically OR'd together. Could introduce a structure like so:

pub struct Type {
    data_type: DataType,
    is_refcounted: bool,
    is_collectable: bool,
    is_immutable: bool,
    is_persistent: bool,
}

impl From<u32> for Type { ... }

Replace ownership wrapper types with `ZBox<T>`

Types such as ZendStr have an owned variant ZendString. This can be generically replaced with a type ZBox<T>, where T implements a new trait ZBoxable, which is implemented on Zend heap-allocated types, such as ZendStr, ZendObject and HashTable:

pub struct ZBox<T: ZBoxable>(NonNull<T>);

pub unsafe trait ZBoxable {
    fn free(&mut self) {
        // by default, just free the memory using the ZMM
        // downstream types like strings and objects can override with their respective release functions
        unsafe { efree(self) };
    }
}

ZBox will dereference to T, and downstream modules such as string can implement on ZBox<ZendStr>, such as impl TryFrom<String> for ZBox<ZendStr>.

Remove `get/set_property` from `RegisteredClass`

Similar to #86, these functions rely on the user only calling them on types that have been allocated on the heap and immediately proceed a ZendObject. This should be removed in favour of users using the ZendClassObject<Self> APIs.

Add support for autogenerating stub files

It be super cool if we could extend the derive macros to be able to also store metadata information about the compiled module and automatically generate stub files. For example:

#[php_class(name = "Vodik\\Example")]
#[derive(Debug, Clone)]
struct Example(...);

#[php_impl]
impl Example {
    pub fn __construct(arg: u32) -> Self {
        // ...
    }

    /// Example documentation
    pub fn get_value(&self) -> u32 {
        // ...
    }

could result in automatically emitting a PHP file like this:

namespace Vodik\Example
{
    class Example
    {
        public function __construct(int $arg) {}

        /**
         * Example documentation
         */
        public function getValue(): int {}
    }
}

Auto-generate stubs for IDE auto-completion ?

Don't know how hard it would be to implement, but it would be nice if this project could auto-generate stubs for the extension on build to be used for auto-completion in an IDE.

Question: Unable to compile - "unknown type name ‘bool’; did you mean ‘_Bool’?"

Hello,

any idea what dependency am I missing?
I am unable to compile hello world example because of following errors:

   Compiling ext-php-rs v0.7.3
The following warnings were emitted during compilation:

warning: In file included from src/wrapper.c:1:0:
warning: src/wrapper.h:7:71: error: unknown type name ‘bool’; did you mean ‘_Bool’?
warning:  zend_string *ext_php_rs_zend_string_init(const char *str, size_t len, bool persistent);
warning:                                                                        ^~~~
warning:                                                                        _Bool
warning: src/wrapper.c:3:71: error: unknown type name ‘bool’; did you mean ‘_Bool’?
warning:  zend_string *ext_php_rs_zend_string_init(const char *str, size_t len, bool persistent)
warning:                                                                        ^~~~
warning:                                                                        _Bool

error: failed to run custom build command for `ext-php-rs v0.7.3`

Caused by:
  process didn't exit successfully: `/home/mrceperka/projects/mrceperka/ext-php-rs-example/target/debug/build/ext-php-rs-7bf2e69a5b04eccc/build-script-build` (exit status: 1)
  --- stdout
  cargo:rerun-if-changed=src/wrapper.h
  cargo:rerun-if-changed=src/wrapper.c
  cargo:rerun-if-changed=allowed_bindings.rs
  TARGET = Some("x86_64-unknown-linux-gnu")
  OPT_LEVEL = Some("0")
  HOST = Some("x86_64-unknown-linux-gnu")
  CC_x86_64-unknown-linux-gnu = None
  CC_x86_64_unknown_linux_gnu = None
  HOST_CC = None
  CC = None
  CFLAGS_x86_64-unknown-linux-gnu = None
  CFLAGS_x86_64_unknown_linux_gnu = None
  HOST_CFLAGS = None
  CFLAGS = None
  CRATE_CC_NO_DEFAULTS = None
  DEBUG = Some("true")
  CARGO_CFG_TARGET_FEATURE = Some("fxsr,sse,sse2")
  running: "cc" "-O0" "-ffunction-sections" "-fdata-sections" "-fPIC" "-g" "-fno-omit-frame-pointer" "-m64" "-I" "/usr/include/php/20180731" "-I" "/usr/include/php/20180731/main" "-I" "/usr/include/php/20180731/TSRM" "-I" "/usr/include/php/20180731/Zend" "-I" "/usr/include/php/20180731/ext" "-I" "/usr/include/php/20180731/ext/date/lib\n" "-Wall" "-Wextra" "-o" "/home/mrceperka/projects/mrceperka/ext-php-rs-example/target/debug/build/ext-php-rs-adeee84e1b8aaa1e/out/src/wrapper.o" "-c" "src/wrapper.c"
  cargo:warning=In file included from src/wrapper.c:1:0:
  cargo:warning=src/wrapper.h:7:71: error: unknown type name ‘bool’; did you mean ‘_Bool’?
  cargo:warning= zend_string *ext_php_rs_zend_string_init(const char *str, size_t len, bool persistent);
  cargo:warning=                                                                       ^~~~
  cargo:warning=                                                                       _Bool
  cargo:warning=src/wrapper.c:3:71: error: unknown type name ‘bool’; did you mean ‘_Bool’?
  cargo:warning= zend_string *ext_php_rs_zend_string_init(const char *str, size_t len, bool persistent)
  cargo:warning=                                                                       ^~~~
  cargo:warning=                                                                       _Bool
  exit status: 1

  --- stderr


  error occurred: Command "cc" "-O0" "-ffunction-sections" "-fdata-sections" "-fPIC" "-g" "-fno-omit-frame-pointer" "-m64" "-I" "/usr/include/php/20180731" "-I" "/usr/include/php/20180731/main" "-I" "/usr/include/php/20180731/TSRM" "-I" "/usr/include/php/20180731/Zend" "-I" "/usr/include/php/20180731/ext" "-I" "/usr/include/php/20180731/ext/date/lib\n" "-Wall" "-Wextra" "-o" "/home/mrceperka/projects/mrceperka/ext-php-rs-example/target/debug/build/ext-php-rs-adeee84e1b8aaa1e/out/src/wrapper.o" "-c" "src/wrapper.c" with args "cc" did not execute successfully (status code exit status: 1).

Platform:

  • Ubuntu 18.04
  • cc (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0
  • rustc 1.56.1

Thanks 🙏

Extract `&mut T` from `Zval` and `ZendObject`

At the moment you can extract &T from Zval and ZendObject when T: RegisteredClass, however you can't do the same for &mut T.

This will require two new traits: FromZvalMut and FromZendObjectMut. FromZvalMut can be generically implemented for any type that already implements FromZval, and the same goes for FromZendObjectMut.

This will also require the Zval stored in Arg to be mutable.

Methods take `&self` but return `&mut T`

Some methods take an immutable reference to self and then return a mutable reference to a self derivative. This should be changed to take a mutable reference to self, while adding the corresponding immutable reference function.

This is currently blocked on the argument parser. Consider this example:

extern "C" fn example(ex: &mut ExecutionData, ret: &mut Zval) {
    let mut a = Arg::new("a", DataType::Long);
    let mut b = Arg::new("b", DataType::String);

    let parser = ArgParser::new(ex).arg(&mut a).arg(&mut b).parse();
                                ^^ immutable borrow happens here

    if parser.is_err() {
        return;
    }

    let this: &mut ZendClassObject<Example> = ex.get_object();
                                              ^^ mutable borrow happens here
}

ArgParser takes an &ExecutionData, and when parsed stores a reference to each arguments zval inside each argument, which has the same lifetime as ex. When we call ex.get_object(), we need to borrow mutably to mutate the underlying object, but &ExecutionData is still borrowed through the two arguments a and b.

Functions which exhibit this behaviour:

  • ExecutionData::get_object()
  • ExecutionData::get_self()

How to create class constants?

When using the #[php_class] macro on a struct, I'm trying to also set a PHP class constant, but I don't see any support. Am I missing something here on how to register class constants (either via macros or procedurally)

Documentation isn't generated on docs.rs

Seems like our documentation isn't being published on docs.rs because PHP is required to compile the crate :'( - could potentially look at moving the bindings into a seperate ext-php-rs-sys crate so the main crate's docs can compile.

Windows support

Windows is not currently supported, at the moment the only blocking factor that I know of is that the php-config executable is not available on Windows, so we would need to find a way to retrieve the include path for bindgen.

Tidy-up

  • General tidy-up of all documentation.
  • where T: AsRef<str> simply replace T with impl AsRef<str> for strings, keep as before for normal complex cases.

Remove `ClassRef`

The ClassRef type is used to return a reference to self in class methods. It is inherently unsafe because it relies on the user returning self where self has been allocated on the heap, and it must be followed by a ZendObject.

This should be removed in favour of functions taking a ZendClassObject<Self> when they want to return self.

Classes with object overrides can be serialized

Classes that have an associated object registered can currently be serialized and unserialized. Since PHP can't serialize the associated struct this should be disabled. Currently, PHP creates a new object without calling the constructor, attempts to access the properties which fails due to an uninitialized object.

Adding properties causes segfault

Attempting to access properties which have been added to classes causes a segfault:

php: /home/david/.phpbrew/build/8.0.3-debug/Zend/zend_operators.c:923: __zval_get_string_func: Assertion `0' failed.
fish: “php -dextension=(pwd)/target/de…” terminated by signal SIGABRT (Abort)

Panic when using PhpException::from_class

When running the example from https://github.com/davidcole1340/ext-php-rs/blob/466c1658e3c91db3398c585a3b859b18e5b8e070/guide/src/macros/classes.md#example in returning an exception using PhpException::from_class::<RedisException>, I'm seeing a panic:

thread '<unnamed>' panicked at 'Attempted to access uninitalized class object', /Users/joe/.cargo/git/checkouts/ext-php-rs-0adfd3c26f55092c/f9528f0/src/types/class_object.rs:245:14
stack backtrace:
   0: rust_begin_unwind
             at /rustc/59eed8a2aac0230a8b53e89d4e99d55912ba6b35/library/std/src/panicking.rs:517:5
   1: core::panicking::panic_fmt
             at /rustc/59eed8a2aac0230a8b53e89d4e99d55912ba6b35/library/core/src/panicking.rs:101:14
   2: core::option::expect_failed
             at /rustc/59eed8a2aac0230a8b53e89d4e99d55912ba6b35/library/core/src/option.rs:1615:5
   3: core::option::Option<T>::expect
             at /rustc/59eed8a2aac0230a8b53e89d4e99d55912ba6b35/library/core/src/option.rs:698:21
   4: <ext_php_rs::types::class_object::ZendClassObject<T> as core::ops::deref::DerefMut>::deref_mut
             at /Users/joe/.cargo/git/checkouts/ext-php-rs-0adfd3c26f55092c/f9528f0/src/types/class_object.rs:243:9
   5: ext_php_rs::zend::handlers::<impl ext_php_rs::ffi::_zend_object_handlers>::write_property::internal
             at /Users/joe/.cargo/git/checkouts/ext-php-rs-0adfd3c26f55092c/f9528f0/src/zend/handlers.rs:139:30
   6: ext_php_rs::zend::handlers::<impl ext_php_rs::ffi::_zend_object_handlers>::write_property
             at /Users/joe/.cargo/git/checkouts/ext-php-rs-0adfd3c26f55092c/f9528f0/src/zend/handlers.rs:153:15
   7: _zend_update_property_ex
   8: _zend_throw_exception_zstr
   9: _zend_throw_exception
  10: _zend_throw_exception_ex
  11: ext_php_rs::exception::throw_with_code
             at /Users/joe/.cargo/git/checkouts/ext-php-rs-0adfd3c26f55092c/f9528f0/src/exception.rs:138:9
  12: ext_php_rs::exception::PhpException::throw
             at /Users/joe/.cargo/git/checkouts/ext-php-rs-0adfd3c26f55092c/f9528f0/src/exception.rs:65:9
  13: <core::result::Result<T,E> as ext_php_rs::convert::IntoZval>::set_zval
             at /Users/joe/.cargo/git/checkouts/ext-php-rs-0adfd3c26f55092c/f9528f0/src/convert.rs:186:17
  14: php_v8::V8Js::_internal_php_execute_string
             at ./src/lib.rs:141:1
  15: _ZEND_DO_FCALL_SPEC_RETVAL_USED_HANDLER
  16: _execute_ex
  17: _zend_execute
  18: _zend_eval_stringl
  19: _zend_eval_stringl_ex
  20: _do_cli
  21: _main
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
fatal runtime error: failed to initiate panic, error 5
zsh: abort      RUST_BACKTRACE=1 php -r 

Recommended way for passing binary data

Hi!
First of all, thanks for developing this crate, it works wonderfully :)

However, there is a scenario that I'd like to use this crate that I don't fully understand how to do it. I'd like to pass a binary data string to Rust code, and despite having a look at #31 I don't fully understand how to use this.

I have the following code

fn do_something(input: &[u8]) -> u64 {
  // Do stuff
}

/// Computes some things
///
/// @param $input string Your input.
///
/// @return int Your result
#[php_function]
pub fn computation_rs(input: &str) -> u64 {
    do_something(input.as_bytes())
}

// Required to register the extension with PHP.
#[php_module]
pub fn module(module: ModuleBuilder) -> ModuleBuilder {
    module
}

And my PHP code looks like

$data = random_bytes(32);
$result = computation_rs($data);
echo $result;

If I replace $data with a valid string, this works, but I haven't managed to pass an array of bytes. I've tried setting the input type in rust to Vec<u8> but it didn't work either 🤔

Do you have any pointers?

Thanks!

Replace class object `MaybeUninit` with `Option`

Previously MaybeUninit was used to store a struct inside a Zend object. This can be replaced with an Option<T>, using std::ptr::write() to write the option into memory without dropping the uninitalized object.

Reorganise modules

Module paths are currently long and convoluted. Can easily be shortened (drop ::php, re-export types from ::types instead of exporting modules).

Memory leak when returning an array

retval.set_array(vec![1, 2, 3, 4]);
var_dump(function());
array(4) {
  [0]=>
  int(1)
  [1]=>
  int(2)
  [2]=>
  int(3)
  [3]=>
  int(4)
}
[Mon Apr 19 11:37:15 2021]  Script:  '/home/david/Projects/opusphp/opus/test.php'
/home/david/.phpbrew/build/8.0.3-debug/Zend/zend_hash.c(278) :  Freeing 0x00007f15d60588a0 (56 bytes), script=/home/david/Projects/opusphp/opus/test.php
[Mon Apr 19 11:37:15 2021]  Script:  '/home/david/Projects/opusphp/opus/test.php'
/home/david/.phpbrew/build/8.0.3-debug/Zend/zend_hash.c(153) :  Freeing 0x00007f15d605c3c0 (264 bytes), script=/home/david/Projects/opusphp/opus/test.php
=== Total 2 memory leaks detected ===

Rust analyzer macro expansion errors

With this change in rust-analyzer, expansion of ext-php-rs macros errors out as they are no longer expanded in order. This is because Rust provides no guarantees of macro expansion, and the library abuses the fact that rustc expands them (at the moment) in order by maintaining a global proc-macro state.

Currently the solution to this is to disable macro expansion in rust-analyzer:

"rust-analyzer.experimental.procAttrMacros": false

It could be best to move to remove this global state. The global state is used to maintain a register of all the entities to be registered in the PHP module startup and get module functions.

Modifying array parameter in function causes assertion fail

Assertion failed: ((zend_gc_refcount(&(ht)->gc) == 1) || ((ht)->u.flags & (1<<6))), function _zend_hash_str_add_or_update_i, file /Users/davidcole/.php/build/php-8.0.9/Zend/zend_hash.c, line 819.
fish: Job 1, 'php -dextension=(pwd)/../../t...' terminated by signal SIGABRT (Abort)

Zvals are not `Send` nor `Sync`

Zvals currently implement both Send and Sync, however, if the zval contains a reference counted type (strings, arrays, objects), when dropped it will attempt to decrement the reference counter, which is not atomic and therefore not defined behaviour.

Add a way to pass binary data

Hi!

I'd like to pass binary data between PHP and the Rust crate. As far as I understand both on PHP side this is just a string while on rust side they'd be slices of u8 (or vectors?).

Is something like that possible or planned?

Thanks again for this great library!

Error occurs after run test php script with extension

I did all steps from README example section, but when I run php -dextension=./target/release/libphp_lib.dylib test.php
I get the error:

% php -dextension=./target/release/libphp_lib.dylib test.php
PHP Warning: PHP Startup: Invalid library (maybe not a PHP library) './target/release/libphp_lib.dylib' in Unknown on line 0

Warning: PHP Startup: Invalid library (maybe not a PHP library) './target/release/libphp_lib.dylib' in Unknown on line 0
PHP Fatal error: Uncaught Error: Call to undefined function hello_world() in /Users/.../Documents/Code/Rust/php_lib/test.php:2
Stack trace:
#0 {main}
thrown in /Users/.../Documents/Code/Rust/php_lib/test.php on line 2

Fatal error: Uncaught Error: Call to undefined function hello_world() in /Users/.../Documents/Code/Rust/php_lib/test.php:2
Stack trace:
#0 {main}
thrown in /Users/.../Documents/Code/Rust/php_lib/test.php on line 2

Cargo.toml
[package]
name = "php_lib"
version = "0.1.0"
edition = "2021"

[dependencies]
ext-php-rs="0.7.3"

[lib]
crate-type = ["cdylib"]

MacOS 12.1
PHP 8.0
Rust 1.56.0

cargo build and cargo build --release works fine.

Any ideas?

Thanks in advance.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.