Creating a php extension with rust

Source: Internet
Author: User
Tags benchmark configuration settings ffi
Last October, my colleagues and I had a discussion about how to write a Etsy for PHP-like explanatory languages, and Ruby or Python should now be easier than PHP. We talked about writing a successful creation of the extension of the barrier is that they usually need to write in C, but if you are not good at C this language, it is difficult to have that confidence.

Since then I have had the idea of writing with Rust, which has been trying for the past few days. I finally let it run this morning.

C or Rust in PHP

My basic starting point is to write some of the rust code can be compiled into a library, and write it some C header file, in C for the called PHP to do an extension. It's not very simple, but it's fun.

Rust FFI (Foreign function interface)

The first thing I did was to fiddle with the external function interfaces of rust and the C-linked rust. I used a simple method (Hello_from_rust) to write a flexible library with a single declaration (a pointer to a C char, otherwise known as a string), followed by the input output "Hello from rust".

hello_from_rust.rs#! [Crate_type = "Staticlib"]#! [Feature (LIBC)]extern crate libc;use std::ffi::cstr;#[no_mangle]pub extern "C" FN hello_from_rust (name: *const libc::c_ char) {Let    buf_name = unsafe {cstr::from_ptr (name). To_bytes ()};    Let Str_name = String::from_utf8 (Buf_name.to_vec ()). Unwrap ();    Let c_name   = format! (" Hello from Rust, {} ", str_name);    println! ("{}", c_name);}

I'm from C (or other!) ) to split it in the Rust library called. Here's a good explanation for what happens next.

Compiling it will get a file of. A, libhello_from_rust.a. This is a static library that contains all of its own dependencies, and we link it when compiling a C program, which allows us to do the following things. Note: After we compile we will get the following output:

Note:link against the following native artifacts when linking against this static librarynote:the order and any Duplicat Ion can is significant on some platforms, and so could need to be preservednote:library:Systemnote:library:pthreadnote: Library:cnote:library:m

This is where the rust compiler tells us what to link when we don't use this dependency.

Calling Rust from C

Now that we have a library, we have to do two things to make sure it is callable from C. First, we need to create a C header file for it, Hello_from_rust.h. Then link to it when we compile.

Here is the header file:

Hello_from_rust.h#ifndef __hello#define __hellovoid hello_from_rust (const char *name); #endif

This is a fairly basic header file that provides signature/definition only for a simple function. Then we need to write a C program and use it.

Hello.c#include <stdio.h> #include <stdlib.h> #include "hello_from_rust.h" int main (int argc, char *argv[] {    hello_from_rust ("jared!");}

We compile it by running the code:

Gcc-wall-o Hello_c hello.c-l/USERS/JMCFARLAND/CODE/RUST/PHP-HELLO-RUST-LHELLO_FROM_RUST-LSYSTEM-LPTHREAD-LC-LM

Note at the end of the-LSYSTEM-LPTHREAD-LC-LM tells GCC not to link those "local antiques", in order to compile our rust library when the rust compiler can provide it.

By running the following code we can get a binary file:

$./hello_chello from Rust, jared!

Pretty! We just called the Rust Library from C. Now we need to understand how the rust library goes into a php extension.

Calling C from PHP

It took me some time to figure out that in this world, this document is not the best in PHP extensions. The best part is from a PHP source that binds a script Ext_skel (most of which represents the "extended skeleton") that generates most of the boilerplate code you need. To get the code running, I worked very the PHP documentation, Extending the skeleton.

You can start with the download, and the non-quota PHP source, write the code into the PHP directory and run:

$ CD ext/
$./ext_skel–extname=hello_from_rust

This will generate the basic skeleton needed to create the PHP extension. Now, move your folders everywhere you want to keep your extensions in place. and move your

. Rust Source

. Rust Library

. C Header

into the same directory. So now you should look at a directory like this:

.
├──credits
├──experimental
├──config.m4
├──config.w32
├──hello_from_rust.c
├──hello_from_rust.h
├──hello_from_rust.php
├──hello_from_rust.rs
├──libhello_from_rust.a
├──php_hello_from_rust.h
└──tests
└──001.phpt

One directory, 11 files

You can see a good description of these files in PHP docs above. Create an extended file. We're going to start by editing config.m4.

Not explained, here is my result:

Php_arg_with (Hello_from_rust, for Hello_from_rust support,[  --with-hello_from_rust             Include hello_from_rust Support]) if test "$PHP _hello_from_rust"! = "no"; Then  php_subst (hello_from_rust_shared_libadd)  Php_add_library_with_path (Hello_from_rust,., HELLO_FROM_ Rust_shared_libadd)  php_new_extension (Hello_from_rust, hello_from_rust.c, $ext _shared) fi

As I understand it, these are basic macro commands. But the documentation for these macro commands is pretty bad (for example: Google "Php_add_library_with_path" doesn't show the results that the PHP team wrote). I stumbled upon this Php_add_library_path macro command in a previous thread that some people are talking about linking a static library in a PHP extension. Other recommended macro commands in the comments are generated after I run the Ext_skel.

Now that we have the configuration settings, we need to actually call the library from the PHP script. For this we have to modify the automatically generated files, hello_from_rust.c. First we add the Hello_from_rust.h header file to the containing command. Then we need to modify the Confirm_hello_from_rust_compiled definition method.

#include "hello_from_rust.h"//a bunch of comments and code removed ... Php_function (confirm_hello_from_rust_compiled) {    char *arg = NULL;    int Arg_len, Len;    char *STRG;    if (Zend_parse_parameters (Zend_num_args () tsrmls_cc, "s", &arg, &arg_len) = = FAILURE) {        return;    }    Hello_from_rust ("Jared (from php!!)!");    Len = spprintf (&STRG, 0, "congratulations! You have successfully modified EXT/%.78S/CONFIG.M4. Module%.78s is now compiled into PHP. "," Hello_from_rust ", Arg);    Return_stringl (STRG, Len, 0);}

Note: I added Hello_from_rust ("Jared (fromphp!!)!");.

Now, we can try to build our extensions:

$ phpize
$./configure
$ sudo make install

That's it, build our meta configuration, run the generated configuration command, and then install the extension. When installing, I have to use sudo myself because my users do not own the PHP extension of the installation directory.

Now, we can run it!

$ PHP hello_from_rust.php
Functions available in the test extension:
Confirm_hello_from_rust_compiled

Hello from Rust, Jared (from php!!)!
congratulations! You have successfully modified EXT/HELLO_FROM_RUST/CONFIG.M4. Module Hello_from_rust is now compiled to PHP.
Segmentation fault:11

Also good, PHP has entered our C extension, see our list of application methods and call. Then, the C extension has entered our rust library and begins printing our string. That's funny! But...... What happened to the wrong ending?

As I mentioned, this is the use of Rust-related println! Macro, but I didn't do any further debugging on it. If we remove from our Rust library and return a char* substitution, the segment error disappears.

Here's the Rust code:

#! [Crate_type = "Staticlib"]#! [Feature (LIBC)]extern crate libc;use std::ffi::{cstr, cstring};#[no_mangle]pub extern "C" FN hello_from_rust (name: * Const Libc::c_char), *const Libc::c_char {let    buf_name = unsafe {cstr::from_ptr (name). To_bytes ()};    Let Str_name = String::from_utf8 (Buf_name.to_vec ()). Unwrap ();    Let c_name   = format! (" Hello from Rust, {} ", str_name);    Cstring::new (C_name). Unwrap (). As_ptr ()}

and change the C header file:

#ifndef __hello#define __helloconst char * hello_from_rust (const char *name); #endif

Also change the C extension file:

Php_function (confirm_hello_from_rust_compiled) {char *arg = NULL;    int Arg_len, Len;    char *STRG;    if (Zend_parse_parameters (Zend_num_args () tsrmls_cc, "s", &arg, &arg_len) = = FAILURE) {        return;    }    char *str;    str = Hello_from_rust ("Jared (from php!!)!");    printf ("%s/n", str);    Len = spprintf (&STRG, 0, "congratulations! You have successfully modified EXT/%.78S/CONFIG.M4. Module%.78s is now compiled into PHP. "," Hello_from_rust ", Arg);    Return_stringl (STRG, Len, 0);}

Useless micro-benchmark

So why are you doing this? I really didn't use this in the real world. But I really think that the Fibonacci sequence algorithm is a good example of how basic a php extension is. is usually straightforward (in Ruby):

def fib (at) does    if (at = = 1 | | at = = 0)        return at    else        return fib (at-1) + fib (at-2)    endend

And you can improve this poor performance by not using recursive returns:

def fib (at)    does if (at = = 1 |-at = = 0)        return at    elsif (val = @cache [at]). Present?        Return Val      end    Total  = 1    parent = 1    gp     = 1    (1..at).        each do |i| Total  = parent + GP        gp     = parent        parent = Total    end    return totalend

So we're going to write two examples around it, one in PHP and one in rust. See which one is faster. Here is the PHP version:

def fib (at)    does if (at = = 1 |-at = = 0)        return at    elsif (val = @cache [at]). Present?        Return Val      end    Total  = 1    parent = 1    gp     = 1    (1..at).        each do |i| Total  = parent + GP        gp     = parent        parent = Total    end    return totalend

This is the result of its operation:

$ time php php_fib.phpreal    0m2.046suser    0m1.823ssys 0m0.207s

Now let's do the rust version. Here is the Library resource:

#! [Crate_type = "Staticlib"]fn fib (at:usize), usize {    if at = = 0 {        return 0;    } else if at = = 1 {        retur n 1;    }    Let Mut total  = 1;    Let mut parent = 1;    Let mut gp     = 0;    For _ in 1.. At {        Total  = parent + GP;        GP     = parent;        parent = total;    }    return total;} #[no_mangle]pub extern "C" FN RUST_FIB (at:usize), usize {    fib (at)}

Note that I compiled the library rustc–o rust_lib.rs to make the compiler optimized (because we are the standard here). Here is the C extension source (related excerpt):

Php_function (confirm_rust_fib_compiled) {    long number;    if (Zend_parse_parameters (Zend_num_args () tsrmls_cc, "L", &number) = = FAILURE) {        return;    }    Return_long (RUST_FIB (number));}

To run the PHP script:

<?PHP$BR = (Php_sapi_name () = = "CLI")? "": "<br>", if (!extension_loaded (' Rust_fib ')) {    dl (' Rust_fib. '). Php_shlib_suffix);} for ($i = 0; $i < 100000; $i + +) {    confirm_rust_fib_compiled (92);}? >

This is the result of its operation:

$ time php rust_fib.phpreal    0m0.586suser    0m0.342ssys 0m0.221s

You can see it three times times faster than the former! Perfect Rust micro-benchmark!

Summarize

There is little conclusion to be drawn here. I'm not sure it's a good idea to write a php extension on rust, but it's a good way to spend some time studying rust,php and C.

  • Contact Us

    The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

    If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

    A Free Trial That Lets You Build Big!

    Start building with 50+ products and up to 12 months usage for Elastic Compute Service

    • Sales Support

      1 on 1 presale consultation

    • After-Sales Support

      24/7 Technical Support 6 Free Tickets per Quarter Faster Response

    • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.