澳门新萄京8522php怎么运作c语言程序?利用php调用C语言 扩大PHP的效能

更新: 初藳刚发表还未多少个钟头小编意识到本人的 PHP
基准测验是错的。为正义起见小编已经更新了 PHP 和 Rust 的版本。你能够在
GitHub 饭店里看看変更(链接在底层)。

 C或PHP中的Rust

第一步. 生成供给调用的so文件

二零一八年十一月,小编和 Etsy
的同事有过叁个有关什么为像PHP样的解释性语言写扩充的座谈,Ruby或Python前段时间的景色相应会比PHP轻易。我们聊起了写七个成功开创扩充的阻碍是它们平常供给用C来写,不过倘令你相当长于C那门语言的话很难有不行信心。

本人的主导出发点正是写一些得以编写翻译的Rust代码到五个Curry面,并写为它有个别C的头文件,在C中为被调用的PHP做二个开展。就算并不是异常粗略,可是很有意思。
Rust FFI(foreign function interface)

1.  首先做一个差不离的so文件:

从那时候起小编便萌生了用Rust写八个的主张,过去的几天平素在尝试。几目前早晨作者毕竟让它运维了。

小编所做的首先件专业正是摆弄Rust与C连接的Rust的外表函数接口。小编曾用简易的章程(hello_from_rust)写过三个灵活的库,伴有纯粹的扬言(a
pointer to a C char, otherwise known as a
string),如下是输入后输出的“Hello from Rust”。
 

/** * hello.c

C或PHP中的Rust

本人的中坚出发点正是写一些能够编写翻译的Rust代码到多个Curry面,并写为它某些C的头文件,在C中为被调用的PHP做一个进展。即便并非超轻巧,不过很有趣。

// 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);
}

* To compile, use following commands:

Rust FFI(foreign function interface)

本人所做的第一件事情便是摆弄Rust与C连接的Rust的表面函数接口。小编曾用简短的方式(hello_from_rust)写过叁个灵活的库,伴有纯粹的宣示(a
pointer to a C char, otherwise known as a
string),如下是输入后输出的“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);
}

自己从C(或任何!)中调用的Rust库拆分它。那有多少个接下去会什么的很好的演讲。

编写翻译它会获得.a的叁个文书,libhello_from_rust.a。那是二个静态的库,包涵它本身全部的依靠关系,并且我们在编译八个C程序的时候链接它,那让我们能做持续的事体。注意:在大家编译后会得到如下输出:

note: link against the following native artifacts when linking against this static library
note: the order and any duplication can be significant on some platforms, and so may need to be preserved
note: library: Systemnote: library: pthread
note: library: c
note: library: m

那便是Rust编写翻译器在我们不使用这些依附的时候所告诉大家要求链接什么。

自家从C(或任何!)中调用的Rust库拆分它。那有二个接下去会怎么的很好的表达。

 *   gcc -O -c -fPIC -o hello.o hello.c

从C中调用Rust

既是我们有了二个库,不能不做两件事来确定保证它从C中可调用。首先,我们须要为它成立三个C的头文件,hello_from_rust.h。然后在大家编译的时候链接到它。

下边是头文件:

// hello_from_rust.h
#ifndef __HELLO
#define __HELLO

void hello_from_rust(const char *name);

#endif

这是八个一定根底的头文件,仅仅为了四个轻巧的函数提供具名/定义。接着大家要求写一个C程序并运用它。

// hello.c
#include <stdio.h>
#include <stdlib.h>
#include "hello_from_rust.h"

int main(int argc, char *argv[]) {
    hello_from_rust("Jared!");
}

作者们透过运营一下代码来编写翻译它:

gcc -Wall -o hello_c hello.c -L /Users/jmcfarland/code/rust/php-hello-rust -lhello_from_rust -lSystem -lpthread -lc -lm

在目的在于最终的-lSystem -lpthread -lc -lm告诉gcc不要链接那多少个“本地的古物”,为了当编写翻译大家的Rust库时Rust编译器能够提供出来。

经运转下边包车型大巴代码大家能够获得贰个二进制的文书:

$ ./hello_c
Hello from Rust, Jared!

精良!大家刚刚从C中调用了Rust库。未来我们须求领会Rust库是什么步向三个PHP扩张的。

从 php 中调用 c

该部分花了自家有的时刻来弄了然,在这里个世界上,该文书档案在 php
扩展中并不是最棒的。最好的一部分是出自绑定一个剧本 ext_skel 的 php
源(大大多象征“扩大骨架”)即生成大好多您须要的旗帜代码。为了让代码运营,小编十分全力地上学
php
文书档案,“扩充骨骼”。

你能够由此下载来起始,和未配额的 php 源,把代码写进 php 目录并且运营:

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

那将扭转需求成立 php
扩张的中坚骨架。现在,移动你各处想一些地涵养你的恢弘的文本夹。并且一举手一投足你的

.rust 源

.rust库

.c header

进去同一个目录。因而,未来您应有看看像这么的四个目录:

.
├── 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

贰个目录,10个公文

您能够在 php docs
在上边看见关于那些文件很好的描述。创建一个扩展的文件。我们将通过编制config.m4 来起头吧。

不解释,上边正是自己的硕果:

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

正如自己所领会的那么,那些是着力的宏命令。可是关于那些宏命令的文档是很差的(比方:google”PHP_ADD_LIBRARY_WITH_PATH”并不曾出现PHP团队所写的结果)。小编临时候那些PHP_ADD_LIBRARY_PATH宏命令在几个人所商议的在三个PHP拓宽里链接一个静态库的先前的线程里。在口不择言中任何的引荐使用的宏命令是在自家运维ext_skel后发出的。

既然如此大家开展了配备安装,大家供给从PHP脚本中实际地调用库。为此我们得改善自动生成的文书,hello_from_rust.c。首先大家增多hello_from_rust.h头文件到含有命令中。然后大家要改进confirm_hello_from_rust_compiled的定义方法。

#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);
}

留心:小编加多了hello_from_rust(“Jared (fromPHP!!)!”);。

到现在,大家得以试着创造我们的扩大:

$ phpize
$ ./configure
$ sudo make install

正是它,生成我们的元配置,运营生成的安排命令,然后安装该增添。安装时,笔者不得不亲自使用sudo,因为笔者的客户并不抱有安装目录的
php 增添。

前些天,我们得以运作它啦!

$ 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 into PHP.
Segmentation fault: 11

还不易,php 已跻身咱们的 c 扩张,看见大家的使用措施列表何况调用。接着,c
增加已步入大家的 rust
库,开首打字与印刷大家的字符串。那很有意思!然而……那段错误的后果发生了什么?

 

正如笔者所提到的,这里是使用了 Rust 相关的 println!
宏,可是自身尚未对它做越来越调解。借使大家从大家的 Rust
库中删除并回到三个 char* 替代,段错误就能破灭。

这里是 Rust 的代码:

#![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()
}

并变更 C 头文件:

#ifndef __HELLO
#define __HELLO

const char * hello_from_rust(const char *name);

#endif

还要改造 C 扩张文件:

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);
}

编写翻译它会获得.a的二个文书,libhello_from_rust.a。那是二个静态的库,包括它自个儿独具的依赖关系,并且咱们在编写翻译多少个C程序的时候链接它,那让我们能做持续的作业。注意:在我们编写翻译后会获得如下输出:
 

  *   gcc -shared -o libhello.so hello.o

失效的微基准

那正是说为何您还要那样做?笔者还真的未有在具体世界里使用过那几个。但是自己确实以为斐波那契系列算法正是三个好的例证来证实二个PHP拓宽怎么样很基本。经常是行动坚决果断(在Ruby中):

def fib(at) do
    if (at == 1 || at == 0)
        return at
    else
        return fib(at - 1) + fib(at - 2)
    end
end

再者能够由此不接纳递回来修改那倒霉的质量:

def fib(at) do
    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 total
end

那便是说大家围绕它来写多少个例子,四个在PHP中,贰个在Rust中。看看哪些越来越快。上边是PHP版:

def fib(at) do
    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 total
end

那是它的运维结果:

$ time php php_fib.php

real    0m2.046s
user    0m1.823s
sys 0m0.207s

今昔大家来做Rust版。下边是库能源:

#![crate_type = "staticlib"]

fn fib(at: usize) -> usize {
    if at == 0 {
        return 0;
    } else if at == 1 {
        return 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)
}

专心,作者编写翻译的库rustc – O
rust_lib.rs使编写翻译器优化(因为咱们是这里的标准)。这里是C扩大源(相关摘录):

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));
}

运行PHP脚本:

<?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);
}
?>

那正是它的运转结果:

$ time php rust_fib.php

real    0m0.586s
user    0m0.342s
sys 0m0.221s

你可以知道它比前者快了三倍!完美的Rust微基准!

note: link against the following native artifacts when linking against this static library
note: the order and any duplication can be significant on some platforms, and so may need to be preserved
note: library: Systemnote: library: pthread
note: library: c
note: library: m

*/

总结

此间差不离从未搜查缴获什么结论。笔者不鲜明在Rust上写三个PHP的强大是二个好的主张,可是花费一些时日去商讨Rust,PHP和C,那是三个很好的方式。

假诺您指望查看全部代码或许查看改良记录,可以访谈GitHub
Repo。

那正是Rust编写翻译器在大家不使用那个依靠的时候所告诉大家须求链接什么。

int hello_add(int a, int b)

从C中调用Rust

{

既然如此我们有了叁个库,一定要做两件事来保障它从C中可调用。首先,大家要求为它创制八个C的头文件,hello_from_rust.h。然后在大家编写翻译的时候链接到它。

    return a + b;

上面是头文件:
 

}

// hello_from_rust.h
#ifndef __HELLO
#define __HELLO

void hello_from_rust(const char *name);

#endif

接下来将它编写翻译成.so文件并内置系统中:

这是叁个一定根底的头文件,仅仅为了叁个精练的函数提供具名/定义。接着大家要求写八个C程序并利用它。
 

$ gcc -O -c -fPIC -o hello.o hello.c                      //
-fPIC:是指生成的动态库与岗位非亲非故

// hello.c
#include 
#include 
#include "hello_from_rust.h"

int main(int argc, char *argv[]) {
 hello_from_rust("Jared!");
}

$ gcc -shared -o libhello.so hello.o                     //
-shared:是指明生成动态链接库

我们通过运转一下代码来编写翻译它:
 

$ su        // 切换来最棒顾客,此时,需求输入密码。

gcc -Wall -o hello_c hello.c -L /Users/jmcfarland/code/rust/php-hello-rust -lhello_from_rust -lSystem -lpthread -lc -lm

# cp libhello.so /usr/local/lib      // 把转换的链接库放到内定之处

只顾在终极的-lSystem -lpthread -lc
-lm告诉gcc不要链接那一个“本地的古文物”,为了当编译大家的Rust库时Rust编译器能够提供出来。

# echo /usr/local/lib > /etc/ld.so.conf.d/local.conf       // 
把库地址写入到布署文件中

经运维下边包车型客车代码大家得以得到二个二进制的文本:
 

# /sbin/ldconfig                // 用此命令,使刚刚写的安排文件生效

$ ./hello_c
Hello from Rust, Jared!
  1. 写段小程序来证实其正确:

了不起!大家刚刚从C中调用了Rust库。未来大家供给理解Rust库是什么样步入四个PHP扩张的。

/**

从 php 中调用 c

 * hellotest.c

该部分花了自个儿有的光阴来弄精通,在这里个世界上,该文书档案在 php
扩大中实际不是最棒的。最棒的一部分是来自绑定贰个剧本 ext_skel 的 php
源(大比相当多表示“增添骨架”)即生成大非常多您需求的金科玉律代码。 
你能够经过下载来最早,和未分配的定额的 php 源,把代码写进 php 目录况且运转:

 * To compile, use following commands:

 $ cd ext/
$ ./ext_skel --extname=hello_from_rust

*   gcc -o hellotest -lhello hellotest.c

  这将扭转供给创制 php
扩充的主干骨架。以往,移动你处处想有个别地维持您的恢弘的文书夹。並且一抬手一动脚你的

*/

  •     .rust 源
  •     .rust库
  •     .c header

#include

跻身同一个索引。由此,今后您应该看看像那样的一个索引:

int main()

 .
├── 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

{

八个索引,13个文件

    int a = 3, b = 4;

你能够在 php docs
在上头看见有关这个文件很好的汇报。创建三个增加的公文。大家将因而编制config.m4 来开端吧。

    printf(“%d + %d = %d”, a, b, hello_add(a,b));

不表达,下边就是作者的硕果:
 

    return 0;

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

}

正如作者所知晓的那么,这一个是主题的宏命令。不过关于这个宏命令的文书档案是一定倒霉的(例如:google”PHP_ADD_LIBRARY_WITH_PATH”并从未现身PHP团队所写的结果)。作者不经常那一个PHP_ADD_LIBRARY_PATH宏命令在有一点点人所斟酌的在二个PHP扩充里链接三个静态库的先前的线程里。在言三语四中任何的推荐使用的宏命令是在自身运转ext_skel后爆发的。

编写翻译并实行:

既然如此大家进行了布署安装,我们须要从PHP脚本中实际地调用库。为此大家得修正自动生成的公文,hello_from_rust.c。首先咱们增添hello_from_rust.h头文件到含有命令中。然后大家要修改confirm_hello_from_rust_compiled的定义方法。
 

$ gcc -o hellotest -lhello hellotest.c                //
编写翻译测验文件,生成测量试验程序

#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);
}

$ ./hellotest           // 运转测量检验程序

小心:作者增多了hello_from_rust(“Jared (fromPHP!!)!”);。

第二步. 制作PHP模块(外界模块)

以往,大家得以试着创立大家的扩张:

请保管您已设置 PHP及APACHE服务器。

$ phpize
$ ./configure
$ sudo make install

$ cd php-5.2.3/ext

就是它,生成大家的元配置,运营生成的布置命令,然后安装该扩展。安装时,我必须要亲自使用sudo,因为笔者的客商并不辜负有安装目录的
php 扩张。

  1. 下一场通过下边包车型地铁授命用ext_skel脚本创立一个名叫 hello 的模块:

当今,大家得以运转它啦!

$ ./ext_skel –extname=hello

$ 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 into PHP.
Segmentation fault: 11

2.
实行该命令之后它会唤起您应当用哪些命令来编写翻译模块,缺憾这是将模块集成到php内部的编写翻译方法。

强制能够,php 已步入大家的 c 扩充,见到我们的应用措施列表而且调用。接着,c
扩展已跻身大家的 rust
库,最初打字与印刷大家的字符串。那很有趣!可是……这段错误的结果产生了怎样?

假定要编写翻译成可动态加载的 php_hello.so,方法要更为简易。

正如本身所涉及的,这里是选择了 Rust 相关的 println!
宏,不过本身从不对它做更加的的调节和测验。如若大家从我们的 Rust
库中剔除并重返贰个 char* 代替,段错误就能够未有。

$ cd hello

这里是 Rust 的代码:
 复制代码 代码如下:
#![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_澳门新萄京8522,name);
 
    CString::new(c_name).unwrap().as_ptr()
}

 首先编辑 config.m4 文件,去掉第16行和第18行的疏解(注释符号为 dnl 。)

并变更 C 头文件:
 

16:  PHP_ARG_ENABLE(hello, whether to enable hello support,

#ifndef __HELLO
#define __HELLO

const char * hello_from_rust(const char *name);

#endif

17:  dnl Make sure that the comment is aligned:

还要更改 C 扩大文件:
 

18:  [  –enable-hello           Enable hello support])

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);
}
  1. 下一场施行 phpize 程序,生成configure脚本:

失效的微基准

$ phpize

那正是说为啥您还要如此做?小编还真的未有在切切实实世界里接纳过这几个。不过自身实在认为斐波那契连串算法就是叁个好的例子来证实二个PHP扩充怎样很基本。经常是干脆俐落(在Ruby中):
 

 该程序在ubuntu的php5-dev包中

def fib(at) do
 if (at == 1 || at == 0)
  return at
 else
  return fib(at - 1) + fib(at - 2)
 end
end
  1. 打开 php_hello.h,在 PHP_FUNCTION(confirm_hello_compiled卡塔尔国;
    之下参与函数证明:

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图