您的位置:首页 >> LNMPA >> PHP安装PNI开启C扩展能力
PHP安装PNI开启C扩展能力
[ 孤狼 | 2017-07-27 15:47:41 | LNMPA ]

由于一个搞嵌入式又会php又会C还主要用C++的朋友的需要,目前PHP的功能满足不了他的XX.于是一起探讨能不能自己给PHP加一些功能,找遍了整个网上都是如何使用C直接写个模块然后用php编译安装,这么做的确可以,但是很麻烦,而且容易出问题.于是抽时间爬了一遍Github终于找到了一个比较好的东西PNI.


什么是PNI(PHP Native Interface )?


它是PHP 的一个C扩展,通过它,可以让PHP调用其他语言写的程序,比如C/C++、汇编等等

需要PHP来调用,但PHP有限使用的领域里,PNI可以发挥用处,比如图像处理、统计学习、神经网络、实时性要求高的程序等等


为啥要用PNI?


PHP是最好的语言[引战符].但是PHP不是最完美的语言,总有一些情况下,不得不使用其他语言来协助完成。在这些特殊的场景下,使用PNI就可以将PHP与其他语言连接起来,例如:

  • 实时性要求特别高的程序,特别底层的程序

  • 用其他语言写的程序,历史遗留下来的程序,如果用PHP重新实现成本太高的程序或逻辑

  • 基于平台特性的代码,不能用PHP实现的程序

  • 调用系统的动态链接库

等等这些情况都需要使用PNI来帮助.


好了说了那么多,来看看怎么使用吧.

由于PNI的作者zuocheng-liu这位大佬可能是太忙,所以官方给出的教程让我一次入坑,爬了将近一天才爬出来.所以这里写的一些操作可能和官方不太一样.


------------------安装------------------


在安装PNI之前,请先安装好PHP,并确定PHP.ini文件的位置,和php-config文件的位置,不同的脚本包和安装方式,可能位置都不一样,本教程以lnmp一键安装包为例.服务器不限制apache/nginx都可以因为和服务没关系.

然后我们打开linux的命令行,或远程连接上,进入命令界面.


0:安装git,由于后面我们要用到git所以先安装好git

yum -y install git


1:下载

git clone https://github.com/zuocheng-liu/pni.git

1.jpg


2.进入pni目录(这里容易坑)

cd ./pni/


3.选择php分支,根据你的php版本来选择,不过这里注意,php最低5.3,否则不兼容.

git checkout for_php_5

git checkout for_php_7

2.jpg


4.调用一次phpize(注意不要换目录)

phpize

3.jpg


5.执行configure,这里要确定你的php-config文件在什么位置.

./configure --with-php-config=/usr/local/php/bin/php-config

4.jpg


6.安装

make&&make install

5.jpg


7.添加extension=pni.so; 到php.ini文件

vi /usr/local/php/etc/php.ini

在最后加上extension=pni.so; 然后保存

6.jpg


8.根据你的服务器类型重启php

service php-fpm restart	// cgi模式
apachectl restart	// sapi模式

7.jpg



9.现在PNI已经安装好了,接着来准备测试,准备好.c文件(libtest.c)

#include"stdio.h"
#include"math.h"
int add(int a, int b)
{
    return a+b;
}


10.把c文件编译成so文件

命令:gcc -fPIC -shared -o 输出.so 你的.c

gcc -fPIC -shared -o libtest.so libtest.c

[大坑请注意:so文件必须以lib开头,否则无法关联]

8.jpg


11.创建拓展文件夹,我这里就放在php的安装目录下了,方便区别.

mkdir /usr/local/php/ex/


12.把第10步生成的.so文件拷贝到扩展文件夹,注意lib开头命名

cp ./libtest.so /usr/local/php/ex/libtest.so


13.修改系统的动态调用

vi /etc/ld.so.conf

在最后加上我们刚才创建的拓展文件夹/usr/local/php/ex/

9.jpg


14.重启动态调用

/sbin/ldconfig -v

10.jpg

这里注意一定要看到你的so文件被调用,如果不是lib开头,系统不会调用哦.


15.准备PHP测试文件(index.php)

<?php
echo 'PHP动态调用C语言程序.<br>';
echo '本程序共会执行2次调用,用以测试是否运行成功.第一次调用C原生库,第二次调用自定义库.<br><br>';
echo '开始调用C原生pow方法.<br>';
try {
    $pow = new PNIFunction(PNIDataType::DOUBLE, 'pow', 'libm.so.6');
    $a = new PNIDouble(2);
    $b = new PNIDouble(2);
    $res = $pow($a, $b);
    var_dump($res);
} catch (PNIException $e) {
}
echo '<br>调用完毕.<br><br>';
echo '开始调用自定义add方法.<br>';
try {
    $add = new PNIFunction(PNIDataType::INTEGER,'add', 'libtest.so');
    $a = new PNIInteger(15);
    $b = new PNIInteger(235);
    $res = $add($a, $b);
    var_dump($res);
} catch (PNIException $e) {
}
echo '<br>调用完毕.<br><br>';
echo '注意,返回的都是对象格式,需要使用对象调用方式获取数据,例如:<br>';
echo '你就是个'.$res->getValue('protected').'<br>';
echo '<br>end';


16.访问这个php文件.

11.jpg

可以看到,我们的so文件被调用了,而且得到了正确的结果.



------------------扩展知识------------------


PNI数据类型:

PNIDataType

PNIInteger

PNILong

PNIDouble

PNIFLOAT

PNIChar

PNIString

PNIPointer


PNI数据类型常量:

PNIDataType::VOID

PNIDataType::CHAR

PNIDataType::INTEGER

PNIDataType::LONG

PNIDataType::FLOAT

PNIDataType::DOUBLE

PNIDataType::POINTER


由于PHP只有64整形,所以PNILong 和 PNIInteger 实际上是等效的。

如果通过PNI调用的函数参数类型是32位、16位数据怎么办?需要开发人员保证PNILong和PNIInteger存放的值不能超出大小。

PNIDouble 和 PNIFloat 也是等效的,因为PHP只有64位浮点。如果调用的C函数参数列表里有32位浮点呢? 不用担心,即使是32位的浮点,在x86_64架构的CPU里,也是赋给了64位的浮点运算器。



注意事项


如果PHP是多线程运行,需要注意PNI调用的动态链接库是否是线程安全的

对于在动态链接库中申请的资源,要及时释放

目前PNI还不支持对复杂数据类型的操作,比如struct,C++的类等


传送门PNI作者的Github:

https://github.com/zuocheng-liu/pni/blob/master/README-zh.md


测试脚本及C脚本下载[<-点我下载]  密码: jpuz


转载请注明出处:http://gl.paea.cn/lnmpa/content/2017/07/27/102.html