在Linux服务器环境中部署SSH2扩展以编译PHP文件中的SSH脚本命令,具体配置方法如下,如果是在正式环境中直接运行,建议做好备份。
1.安装支持的库文件
命令:yum install php-devel php-pear libssh2 libssh2-devel
2.建立ssh2扩展
命令:pecl install -f ssh2
之后会显示安装的日志,需要选择时直接按回车键就好
3.安装成功后,需要修改ssh2.ini
[root@wcc etc]# touch /etc/php.d/ssh2.ini
[root@wcc etc]# echo extension=ssh2.so > /etc/php.d/ssh2.ini
可以用vim打开/etc/php.d/ssh2.ini 确认是否已经包含extension=ssh2.so
4.验证是否安装成功
php -m | grep ssh2
显示ssh2则表示安装成功
自己的Web服务器安装了Centos 7操作系统,为了方便管理安装了宝塔面板,最近要用php的rar扩展,发现宝塔内置的没有这个扩展,那就需要自己动手安装了。
先到rar扩展的主页https://pecl.php.net/package/rar因为服务器上运行的是php 7.2支持最新版本rar扩展,那就下载最新版4.0.0。
wget https://pecl.php.net/get/rar-4.0.0.tgz
然后解压
gunzip rar-4.0.0.tgztar -xvf rar-4.0.0.tar
查看宝塔面板php7.2安装路径下的是否存在/www/server/php/72/bin/phpize,如果存在,进入刚才解压的目录执行这个phpize
cd rar-4.0.0/www/server/php/72/bin/phpize
在解压目录里执行phpize主要是为了侦测当前php运行环境,给rar源码生成configure配置的相关文件,以便后期编译需要。
运行configure ,注意后面的--with-php-config参数一定不能缺,这个的作用是关联上当前需要关联的php版本,尤其是在安装多个不同版本php时。
./configure --with-php-config=/www/server/php/72/bin/php-config
编译源码
make && make install
编译完成在最后会提示生成的rar.so所在的路径。
Build complete.Don't forget to run 'make test'.Installing shared extensions: /www/server/php/72/lib/php/extensions/no-debug-non-zts-20170718/
在php7.2的配置文件php.ini最后一行添加
[rar]
extension="/www/server/php/72/lib/php/extensions/no-debug-non-zts-20170718/rar.so"
重启一下相应版本的php7.2 fpm服务,或者干脆重启一下服务器。
检查一下php7.2是否加载rar扩展成功
/www/server/php/72/bin/php -m
看到执行命令后返回的文字里面有rar,成功!
CSpeed 框架,全堆栈的轻量级C语言PHP扩展框架,以高性能为目标、极速为目标。
本篇文章主要给大家介绍Windows下PHP安装Redis扩展的具体步骤方法。
下面我们就结合详细的图文,给大家介绍Windows下PHP安装Redis扩展的方法:
首先我们打开这个上面给出的下载链接地址,界面如下:
这里我们随便选择点击一个目录,显示不同版本的Redis扩展如下:
注意,下载安装Redis扩展包必须要与我们使用的PHP版本相对应。比如这里我的PHP版本为php-7.2.9-NTS且为vc15、x64位,所以我需要下载的Redis扩展包版本就是php_redis-4.0.0rc1-7.2-nts-vc15-x64。
那么点击下载好Redis扩展包后,进行解压,并将php_redis.dll这个文件,直接复制到我们php对应环境下的ext文件夹中,操作如下图:
然后我们再打开php-ini文件,通过搜索extension,找到下图中位置,添加这段代码:
extension=php_redis.dll
具体操作如下图:
然后保存,重启一下我们的PHP环境。
最后我们可以通过PHPinfo查看,Redis是否安装成功。
那么如图所示,此时Redis扩展已经安装成功了。
redis扩展安装问题
本篇文章就是关于PHP安装Redis扩展的具体步骤方法介绍,通俗易懂,具有一定参考价值,希望对需要的朋友有所帮助!
(1)下载swoole安装包到本地(下载到root家目录下)(2)解压并进入该目录tar zxf v1.10.5.tar.gzcd swoole-src-1.10.5/(3)使用phpize来生成php编译配置(phpize路径在php安装目录的bin目录下,视实际安装路径而定)/www/wdlinux/nginx_php/bin/phpize出现类似以下内容就说明成功:Configuring for:PHP Api Version: 20121113Zend Module Api No: 20121212Zend Extension Api No: 220121212注意:这里有使用yum安装的php环境,直接运行phpize,可能会出现如下错误:Can't find PHP headers in /usr/include/phpThe php-devel package is required for use of this command.这是因为现在安装的php库不匹配现在的php版本,根据php -v得知当前版本,并依据版本yum安装php-devel,例如:yum install -y php55w-devel(4)使用./configure来做编译配置检测,没有出现error就说明成功./configure --with-php-config=/www/wdlinux/nginx_php/bin/php-config --enable-openssl注意:php-config的路径视实际情况而定。如果yum安装的php环境,只需要直接运行./configure --enable-openssl(5)使用make进行编译 make(6)使用make install进行安装make install没有出现error说明没有问题,make install成功后会生成一个Installing shared extensions及路径。例如:Installing shared extensions: /usr/lib64/php/modules/说明在这个路径下生成了swoole.so把 extension = /usr/lib64/php/modules/swoole.so复制到php.ini文件中(php.ini文件可以在phpinfo中或者使用php -i |grep php.ini命令查看)(7)重启php服务,使用phpinfo或者php -m查看是否安装成功
作为世界上最好的语言,php有着非常灵活的语法,它的灵活甚至有时候让你摸不到头脑,函数的命名不规范遭到了很多人的批评,然而这些并不能阻止它的发展。
没有一种语言从设计之初就可以做所有事情,因此我们需要不断去发展它,扩展它,而php的扩展就是它这些年不断前行的动力,yar,swoole的出现,让php又焕发出青春活力。
php的扩展开发主要有三种方式。
PHP-CPP
php-cpp是一套c++类库,方便我们用来开发php的扩展程序。它具有速度极快的特点。不需要知道zendengine的知识就可以开发扩展,并且可以访问php中的各种语法结构和变量。
因为php的很多函数都是来自于c语言命名,因此当你使用c++开发php的扩展的时候,你会感到很亲切,感到很熟悉,感到上手很容易。
Zephir
它提供了类型系统,以友好的语言将静态和动态类型结合在了一起,并使用行业标准的编译器将其编译为机器代码,具有高可维护的特点。
zephir是安全的,它禁止我们使用指针访问内存,并提供了垃圾回收器,进行垃圾回收,避免了内存泄漏。
它是跨平台的,可以支持多个系统平台。
ext_skel
这是php源码自带的扩展生成工具,使用它,可以快速搭建一个扩展框架。
使用此方法需要你有c语言的基本知识,会编译c代码。
使用phpize生成configure文件,进行编译安装。
总结
随着FFI的出现,php可以更加优雅的使用嵌套其它语言,也许以后,将会出现更多使用php原生语言开发出来的扩展。
1、打开php官方扩展地址:https://pecl.php.net/
2、搜索自己想要安装的扩展
3、搜索到结果后,点击 redis 进入到详情页面
4、下载相应版本的扩展包,
使用命令解压
tar -zxvf ./redis-5.1.1.tgz -C /usr/local/src/
5、进入到解压的源码文件夹
cd /usr/local/src/redis-5.1.1/
6、执行 phpize 命令
/usr/local/php/bin/phpize
如果不知道 phpize 命令的路径,可以执行以下命令搜索下
whereis phpize
7、最后执行以下命令
./configure --with-php-config=/usr/local/php/bin/php-config make && make install
/usr/local/php 为 PHP 的安装目录
编译安装完成之后,出现下面类似的安装路径:
/usr/local/php/lib/php/extensions/debug-non-zts-20131226/
8、修改 php.ini 文件,增加以下代码
extension=/usr/local/php/lib/php/extensions/debug-non-zts-20131226/redis.so
最后重启php即可。
所有动态添加的php扩展,都是这个步骤,先进入源码文件夹,然后执行:
> /usr/local/php/bin/phpize
> ./configure --with-php-config=/usr/local/php/bin/php-config
> make && make install
> vim php.ini
extension= ... ...
最后重新加载PHP配置文件或重启
本文实例讲述了PHP扩展Swoole实现实时异步任务队列。分享给大家供大家参考,具体如下:
假如要发100封邮件,for循环100遍,用户直接揭竿而起,什么破网站!
但实际上,我们很可能有超过1万的邮件。怎么处理这个延迟的问题?
答案就是用异步。把“发邮件”这个操作封装,然后后台异步地执行1万遍。这样的话,用户提交网页后,他所等待的时间只是“把发邮件任务请求推送进队列里”的时间。而我们的后台服务将在用户看不见的地方跑。
在实现“异步队列”这点上,有人采用MySQL表或者redis来存放待发送的邮件,然后,每分钟定时读取待发送列表,然后处理。这便是定时异步任务队列。但当前提交的任务要一分钟后才能执行,在某些实时性要求应用场景里还是不快。有些场景要求,只有一提交任务,便马上执行,但用户不需要等待返回结果。
本文将探讨用php扩展swoole实现实时异步任务队列的方案。
服务端
在打算放置脚本的目录(你也可以自行新建)新建Server.php,代码如下
启动服务后,让我们看看如何调用服务。新建测试文件Client_test.php客户端
保存好代码,在命令行或者浏览器中执行Client_test.php,便实现了异步任务队列。你所填写的URL,将会在每次异步任务被提交后,以HTTP GET的方式异步执行。在上面代码中,url即为任务所在地址,param为所需传递参数。
一、主要内容:
1php扩展的概念和底层实现
2编写一个php扩展的步骤
3php底层,Zend 引擎API的介绍 ,HashTable 原理
二、相关概念
1.【php扩展】
php的插件,也就是php的扩展,因为php的底层就是C语言。
php –m 或者 php –i 都可以查看扩展。
其实,xhproh, curl都是php的扩展。
php的扩展图phpinfo()2.【为什么要有扩展?】
1)【效率高】复杂的图像算法,需要写成PHP扩展
2)【操作底层,未支持的技术】1)PHP需要支持一项她还未支持的技术。这通常包括包裹一些现成的C函数库,以便提供PHP接口。 2)有些系统调用不能用PHP直接访问,需要编写成扩展,比如使用Linux下的fork()函数创建一个进程。
3)【商业化,保护源码】想商业话一个应用,但是又不想暴露源代码,就可以编写成扩展。
3.【PHP核心组成?】
Zend引擎:1)核心、基础设施 2)PHP语法实现 3)脚本编译执行 4)扩展机制 5)内存管理
SAPI:服务器抽象层, 上层调用它
php核心构成图二、走向开发(编写扩展)
总体步骤①下载PHP源码
要开发PHP扩展,第一步要下载PHP源代码,因为里面有开发扩展需要的工具。
下载地址: wget http://cn2.php.net/get/php-5.5.38.tar.gz/from/this/mirror
②生成扩展组件框架
ext_skel 命令
./ext_skel --extname=myext 会生成扩展基础文件夹及文件 php-5.5.38/ext/myext/
③修改配置文件
修改config.m4文件,去掉配置前的dnl
config.m4: 这是Unix环境下的Build System配置文件,后面将会通过它生成配置和安装。
修改配置config.m4④编写核心函数(可用默认的)
步骤如下(myext.c):
1、使用宏PHP_FE将函数加入到myext_functions中
2、使用宏PHP_FUNCTION定义函数体
扩展c文件myext.c⑤编译扩展+重启
1、调用phpize程序生成编译配置文件 cd myext && phpize
2、编译扩展库 ./configure --with-php-config=/Data/apps/php/bin/php-config
3、make
4、make test
5、sudo make install 生成myext.so 文件
6、 sudo vim /Data/apps/php/lib/php.ini 加上 extension=myext.so
7、 sudo /Data/apps/php/sbin/php-fpm reload
修改php配置文件,添加扩展三、了解原理
php的生命周期php的变量在内核--写时复制php的变量在内核--底层代码对比php的变量在内核--结构体表示
HashTable分析--结构体HashTable分析--绘图表示Zend引擎API
四、写在最后
php的核心是C语言,核心引擎是Zend,了解HashTable就能了解php的精髓。php的扩展开发说到底是C语言的编写,更见底层功力,希望大家都能成为技术大牛。
最后,预祝大家2018年万事如意,心想事成!
本文转自鸟哥博客:https://www.laruence.com/2020/03/11/5475.html
随着PHP7.4而来的有一个我认为非常有用的一个扩展,PHP FFI(Foreign Function interface), 引用一段PHP FFI RFC中的一段描述:
For PHP, FFI opens a way to write PHP extensions and bindings to C libraries in pure PHP.
是的,FFI提供了高级语言直接的互相调用,而对于PHP来说,FFI让我们可以方便的调用C语言写的各种库。
其实现有大量的PHP扩展是对一些已有的C库的包装,比如常用的mysqli, curl, gettext等,PECL中也有大量的类似扩展。
传统的方式,当我们需要用一些已有的C语言的库的能力的时候,我们需要用C语言写wrapper,把他们包装成扩展,这个过程中就需要大家去学习PHP的扩展怎么写,当然现在也有一些方便的方式,比如Zephir. 但总还是有一些学习成本的,而有了FFI以后,我们就可以直接在PHP脚本中调用C语言写的库中的函数了。
而C语言几十年的历史中,积累了大量的优秀的库,FFI直接让我们可以方便的享受这个庞大的资源了。
言归正传,今天我用一个例子来介绍,我们如何使用PHP来调用libcurl,来抓取一个网页的内容,为什么要用libcurl呢?PHP不是已经有了curl扩展了么?嗯,首先因为libcurl的api我比较熟,其次呢,正是因为有了,才好对比,传统扩展方式和FFI方式直接的易用性不是?
首先,比如我们就拿当前你看的这篇文章为例,我现在需要写一段代码来抓取它的内容,如果用传统的PHP的curl扩展,我们大概会这么写:
<?php$url="https://www.laruence.com/2020/03/11/5475.html";$ch=curl_init();curl_setopt($ch, CURLOPT_URL,$url);curl_setopt($ch, CURLOPT_SSL_VERIFYPEER,0);curl_exec($ch);curl_close($ch);(因为我的网站是https的,所以会多一个设置SSL_VERIFYPEER的操作)那如果是用FFI呢?
首先我们下载PHP-FFI, 编译安装,PHP-FFI需要PHP-7.4以及libffi-3以上。
然后,我们需要告诉PHP FFI我们要调用的函数原型是咋样的,这个我们可以使用FFI::cdef, 它的原型是:
FFI::cdef([string $cdef=""[, string $lib= null]]): FFI具体到这个例子,我们写一个curl.php, 包含所有要申明的东西,代码如下:
$libcurl= FFI::cdef(<<<CTYPEvoid *curl_easy_init();int curl_easy_setopt(void *curl, int option,...);int curl_easy_perform(void *curl);void curl_easy_cleanup(void *handle);CTYPE,"libcurl.so");在string $cdef中,我们可以写C语言函数式申明,FFI会parse它,了解到我们要在string $lib这个库中调用的函数的签名是啥样的,在这个例子中,我们用到三个libcurl的函数,它们的申明我们都可以在libcurl的文档里找到,比如对于curl_easy_init.
这里有个地方是,文档中写的是返回值是CURL *,但事实上因为我们的例子中不会引用它,只是传递,那就避免麻烦就用void *代替。
然而还有个麻烦的事情是,PHP预定义好了CURLOPT_等option的值,但现在我们需要自己定义,简单的办法就是查看curl的头文件,找到对应的值,然后我们把值给加进去:
<?phpconst CURLOPT_URL =10002;const CURLOPT_SSL_VERIFYPEER =64;$libcurl= FFI::cdef(<<<CTYPEvoid *curl_easy_init();int curl_easy_setopt(void *curl, int option,...);int curl_easy_perform(void *curl);void curl_easy_cleanup(void *handle);CTYPE,"libcurl.so");好了,定义部分就算完成了,现在我们完成实际逻辑部分,整个下来的代码会是:
<?phprequire"curl.php";$url="https://www.laruence.com/2020/03/11/5475.html";$ch=$libcurl->curl_easy_init();$libcurl->curl_easy_setopt($ch, CURLOPT_URL,$url);$libcurl->curl_easy_setopt($ch, CURLOPT_SSL_VERIFYPEER,0);$libcurl->curl_easy_perform($ch);$libcurl->curl_easy_cleanup($ch);怎么样,相比使用curl扩展的方式, 是不是一样简练呢?
接下来,我们稍微弄的复杂一点,也即使,如果我们不想要结果直接输出,而是返回成一个字符串呢, 对于PHP的curl扩展来说,我们只需要调用curl_setop 把CURLOPT_RETURNTRANSFER为1,但在libcurl中其实并没有直接返回字符串的能力,而是提供了一个WRITEFUNCTION的回掉函函数,在有数据返回的时候,libcurl会调用这个函数.
目前我们并不能直接把一个PHP函数作为回调函数通过FFI传递给libcurl, 那我们会有俩种方式来做:
1. 采用WRITEDATA, 默认的libcurl会调用fwrite作为回调函数,而我们可以通过WRITEDATA给libcurl一个fd,让它不要写入stdout,而是写入到这个fd2. 我们自己编写一个C到简单函数,通过FFI引入进来,传递给libcurl.
我们先用第一种方式,首先我们需要使用fopen,这次我们通过定义个C的头文件来申明原型(file.h):
void *fopen(char *filename, char *mode);void fclose(void * fp);像file.h一样,我们把所有的libcurl的函数申明也放到curl.h中去
#define FFI_LIB "libcurl.so"void*curl_easy_init();intcurl_easy_setopt(void*curl,int option,...);intcurl_easy_perform(void*curl);voidcurl_easy_cleanup(CURL*handle);注意, 我们通过定义了一个FFI_LIB的宏,来告诉FFI这些函数来自libcurl.so, 当我们用FFI::load加载这个h文件的时候,PHP FFI就会自动载入libcurl.so, 好,现在整个代码会是:
<?phpconst CURLOPT_URL =10002;const CURLOPT_SSL_VERIFYPEER =64;const CURLOPT_WRITEDATA =10001;$libc= FFI::load("file.h");$libcurl= FFI::load("curl.h");$url="https://www.laruence.com/2020/03/11/5475.html";$tmpfile="/tmp/tmpfile.out";$ch=$libcurl->curl_easy_init();$fp=$libc->fopen($tmpfile,"a");$libcurl->curl_easy_setopt($ch, CURLOPT_URL,$url);$libcurl->curl_easy_setopt($ch, CURLOPT_SSL_VERIFYPEER,0);$libcurl->curl_easy_setopt($ch, CURLOPT_WRITEDATA,$fp);$libcurl->curl_easy_perform($ch);$libcurl->curl_easy_cleanup($ch);$libc->fclose($fp);$ret=file_get_contents($tmpfile);@unlink($tmpfile);但这种方式呢就是需要一个临时的中转文件,还是不够优雅, 现在我们用第二种方式,要用第二种方式,我们需要自己用C写一个回掉函数传递给libcurl:
#include<stdlib.h>#include<string.h>#include"write.h"size_town_writefunc(void*ptr,size_tsize,size_tnmember,void*data){own_write_data*d =(own_write_data*)data;size_ttotal = size * nmember;if(d->buf == NULL){ d->buf =malloc(total);if(d->buf == NULL){return0;} d->size = total;memcpy(d->buf, ptr, total);}else{ d->buf =realloc(d->buf, d->size + total);if(d->buf == NULL){return0;}memcpy(d->buf + d->size, ptr, total); d->size += total;}return total;}void*init(){return&own_writefunc;}注意此处的init函数,因为在PHP FFI中,就目前的版本(2020-03-11)我们没有办法直接获得一个函数指针,所以我们定义了这个函数,返回own_writefunc的地址。
最后我们定义上面用到的头文件write.h:
#define FFI_LIB "write.so"typedefstruct_writedata{void*buf;size_tsize;} own_write_data;void*init();注意到我们在头文件中也定义了FFI_LIB, 这样这个头文件就可以同时被write.c和接下来我们的PHP FFI共同使用了。
然后我们编译write函数为一个动态库:
gcc -O2 -fPIC -shared -g write.c -o write.so好了, 现在整个的代码会变成:
<?phpconst CURLOPT_URL =10002;const CURLOPT_SSL_VERIFYPEER =64;const CURLOPT_WRITEDATA =10001;const CURLOPT_WRITEFUNCTION =20011;$libcurl= FFI::load("curl.h");$write= FFI::load("write.h");$url="https://www.laruence.com/2020/03/11/5475.html";$data=$write->new("own_write_data");$ch=$libcurl->curl_easy_init();$libcurl->curl_easy_setopt($ch, CURLOPT_URL,$url);$libcurl->curl_easy_setopt($ch, CURLOPT_SSL_VERIFYPEER,0);$libcurl->curl_easy_setopt($ch, CURLOPT_WRITEDATA, FFI::addr($data));$libcurl->curl_easy_setopt($ch, CURLOPT_WRITEFUNCTION,$write->init());$libcurl->curl_easy_perform($ch);$libcurl->curl_easy_cleanup($ch);ret = FFI::string($data->buf,$data->size);好了,跑一下吧?
然而毕竟直接在PHP中引用外部的so,还是会有很大的安全问题的,另外你也具备了1000中方法让PHP crash,安全起见我们可以采用preload的方式,这种模式下,我们不能在脚本中直接调用FFI::cdef,FF::load, 只能在通过opcache.preload:
ffi.enable=preloadopcache.preload=ffi_preload.incffi_preload.inc:
<?phpFFI::load("curl.h");FFI::load("write.h");但我们引用载入的FFI呢?为此我们需要修改一下这俩个.h头文件,加入FFI_SCOPE, 比如curl.h:
#define FFI_LIB "libcurl.so"#define FFI_SCOPE "libcurl"void*curl_easy_init();intcurl_easy_setopt(void*curl,int option,...);intcurl_easy_perform(void*curl);voidcurl_easy_cleanup(void*handle);对应的我们给write.h也加入FFI_SCOPE为"write", 然后我们的脚本现在看起来应该是这样:
<?phpconst CURLOPT_URL =10002;const CURLOPT_SSL_VERIFYPEER =64;const CURLOPT_WRITEDATA =10001;const CURLOPT_WRITEFUNCTION =20011;$libcurl= FFI::scope("libcurl");$write= FFI::scope("write");$url="https://www.laruence.com/2020/03/11/5475.html";$data=$write->new("own_write_data");$ch=$libcurl->curl_easy_init();$libcurl->curl_easy_setopt($ch, CURLOPT_URL,$url);$libcurl->curl_easy_setopt($ch, CURLOPT_SSL_VERIFYPEER,0);$libcurl->curl_easy_setopt($ch, CURLOPT_WRITEDATA, FFI::addr($data));$libcurl->curl_easy_setopt($ch, CURLOPT_WRITEFUNCTION,$write->init());$libcurl->curl_easy_perform($ch);$libcurl->curl_easy_cleanup($ch);ret = FFI::string($data->buf,$data->size);也就是,我们现在使用FFI::scope来代替FFI::load,引用对应的函数。
好了,经过这个例子,大家应该对FFI有了一个比较深入的理解了,有兴趣,就去找一个C库,试试吧?
本文的例子,你可以在我的github上下载到:FFI example
最后还是多说一句,例子只是为了演示功能,所以去掉了很多错误分支的判断捕获,大家自己写的时候还是要加入。使用FFI的话,确实让你会有1000种方式让PHP segfault crash,所以be careful