This article focuses on the use of rust Extensions in PHP programs, and Rust is a recent emerging compilation language with outstanding performance. We hope to help you.
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). _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 __hello void 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. 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.phpfunctions available in the test Extension:confirm_hello_from_rust_compiledhello 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 = un Safe {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 __hello Const 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.php Real 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 { Retu RN 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.php Real 0m0.586suser 0m0.342ssys 0m0.221s
You can see it three times times faster than the former! Perfect Rust micro-benchmark!
Related recommendations:
Error resolution for dynamically adding PHP extensions using Phpize in MacOS
Linux under PHP extension CURL compilation installation
PHP Extension Library