php扩展编写的文章很多,但是进入php7.4以后,以前的ext_skel变成了ext_skel.php,而且语法也不一样了,具体的方法也大有不同,不要急,我们这里来讲一个简单扩展的写法,你发发现其实一切并没有变得更复杂,而是变得更简单了
一、编译安装php7.4
ext_skel.php必须要依靠php才能运行,那就必须先编译安装php,相关的安装我就不详细讲了,可以参考我的另一篇文章
https://blog.csdn.net/weixin_48803304/article/details/108600932
二、生成扩展框架
进入php的编译目录
cd php-7.4.10/ext
ls
可以看到ext_skel.php文件
bcmath com_dotnet date enchant ffi ftp gmp imap ldap mysqli odbc pcntl pdo_dblib pdo_oci pdo_sqlite posix reflection simplexml soap spl sysvmsg tidy xmlreader xsl zlib
bz2 ctype dba exif fileinfo gd hash intl libxml mysqlnd opcache pcre pdo_firebird pdo_odbc pgsql pspell session skeleton sockets sqlite3 sysvsem tokenizer xmlrpc zend_test
calendar curl dom ext_skel.php filter gettext iconv json mbstring oci8 openssl pdo pdo_mysql pdo_pgsql phar readline shmop snmp sodium standard sysvshm xml xmlwriter zip
注意,以前的命令为./ext_skel --extname=扩展名,已经不能用了,要用–ext
我编写的是一个支付加密扩展,命名为lzpay
./ext_skel.php --ext lzpay
成功后可以看到相应的提示
Copying config scripts... done
Copying sources... done
Copying tests... done
Success. The extension is now ready to be compiled. To do so, use the
following steps:
cd /path/to/php-src/lzpay
phpize
./configure
make
Don't forget to run tests once the compilation is done:
make test
Thank you for using PHP!
三、框架扩展说明
1、进入框架目录
cd lzpay
可以看到目录列表
-rw-r--r-- 1 root root 3231 Sep 17 10:44 config.m4
-rw-r--r-- 1 root root 204 Sep 17 10:44 config.w32
-rw-r--r-- 1 root root 2239 Sep 17 10:51 lzpay.c
-rw-r--r-- 1 root root 309 Sep 17 10:44 php_lzpay.h
drwxr-xr-x 2 root root 4096 Sep 17 10:44 tests
2、文件简要
config.m4配置文件
开发PHP扩展,在写C代码之前,要先配置一下这里。我们打开可以看到详细的注释说明,dnl是注释语法。
如果你的扩展用到了外部依赖,就配置–with-lzpay选项,否则配置–enable-lzpay选项
PHP_ARG_ENABLE(lzpay, whether to enable lzpay support,
Make sure that the comment is aligned:
[ –enable-lzpay Enable lzpay support])
PHP_ARG_WITH和PHP_ARG_ENABLE这两个宏用来配置configure选项,一个配置需要外部依赖的,另一个配置不需要外部依赖的
配置好的内容,在后面执行configure –help时可以看到。
php_lzpay.h头文件
类似于C语音的头文件,包含了一些自定义的结构和函数声明,在这个demo中暂时不需要改动
lzpay.c代码文件
真正的逻辑代码都在这个文件中,后面会详细介绍。
四、编写代码
1、了解扩展入口
打开lzpay.c文件。
整个扩展的入口是zend_module_entry这个结构,具体的定义可以在Zend目录下的zend_modules.h文件中看到,一共有十几个属性,快速跳过,我们暂时只需要”lzpay”
/* {
{
{
lzpay_module_entry
*/
zend_module_entry lzpay_module_entry = {
STANDARD_MODULE_HEADER,
"lzpay", /* Extension name */
lzpay_functions, /* zend_function_entry */
NULL, /* PHP_MINIT - Module initialization */
NULL, /* PHP_MSHUTDOWN - Module shutdown */
PHP_RINIT(lzpay), /* PHP_RINIT - Request initialization */
NULL, /* PHP_RSHUTDOWN - Request shutdown */
PHP_MINFO(lzpay), /* PHP_MINFO - Module info */
PHP_LZPAY_VERSION, /* Version */
STANDARD_MODULE_PROPERTIES
};
/* }}} */
我们来看一下这里,这里有扩展的相关属性和说明
STANDARD_MODULE_HEADER帮我们实现了前面6个属性
lzpay是扩展的名字,
lzpay_functions是扩展包含的全部方法的集合,后面5个宏分别代表5个扩展特定方法
PHP_HELLO_VERSION是扩展的版本号,定义在头文件中,如果需要修改的话直接打开php_lzpay.h找到define PHP_LZPAY_VERSION进行修改
STANDARD_MODULE_PROPERTIES帮我们实现了剩下的属性
暂时都不需要修改,知道这是一个入口就行。顺着这个入口,我们继续看怎么给扩展添加方法,在lzpay_functions[]方法数组中已经有了2个示例方法lzpay_test1和lzpay_test2,我们参考它写我们的方法,首先我们写一个测试方法,测试通过再写加密和解密方法
2、对现有扩展进行编译
我们查看代码,可以发现lzpay_test1方法运行的话会提示The extension %s is loaded and working!
而lzpay_test1方法可以对你输入的内容进行Hello的问侯,我们进接进行测试,这样可以有利于我们对编写PHP扩展的理解
我们将现有扩展进行编译
使用命令phpize
Configuring for:
PHP Api Version: 20190902
Zend Module Api No: 20190902
Zend Extension Api No: 320190902
我们看一下生成的文件
[root@zl-gz-099 lzpay]# ll
total 592
drwxr-xr-x 2 root root 4096 Sep 17 11:34 autom4te.cache
drwxr-xr-x 2 root root 4096 Sep 17 11:34 build
-rw-r--r-- 1 root root 1545 Sep 17 11:34 config.h.in
-rw-r--r-- 1 root root 3231 Sep 17 10:44 config.m4
-rwxr-xr-x 1 root root 436073 Sep 17 11:34 configure
-rw-r--r-- 1 root root 5082 Sep 17 11:34 configure.ac
-rw-r--r-- 1 root root 204 Sep 17 10:44 config.w32
-rw-r--r-- 1 root root 2239 Sep 17 10:51 lzpay.c
-rw-r--r-- 1 root root 307 Sep 17 11:27 php_lzpay.h
-rw-r--r-- 1 root root 126747 Sep 17 11:34 run-tests.php
drwxr-xr-x 2 root root 4096 Sep 17 10:44 tests
开始编译安装
./configure --with-php-config=/usr/bin/php-config
make && make install
Build complete.
Don't forget to run 'make test'.
Installing shared extensions: /usr/lib64/php/extensions/no-debug-non-zts-20190902/
配置php.ini在最后增加扩展
vi /etc/php/php.ini #编辑配置文件,在最后一行添加以下内容
添加
extension=lzpay.so
:wq! #保存退出
重启PHP则配置生效
我们来看一下,很明显成功了
我们还需要进行运行测试
3、测试扩展
我们来看一下代码
/* {
{
{
void lzpay_test1()
*/
PHP_FUNCTION(lzpay_test1)
{
ZEND_PARSE_PARAMETERS_NONE();
php_printf("The extension %s is loaded and working!\r\n", "lzpay");
}
/* }}} */
这里的方法是lzpay_test1,我们直接在命令行中测试
php -r 'echo lzpay_test1();'
看下输出内容
[root@zl-gz-099 lzpay]# php -r 'echo lzpay_test1();'
The extension lzpay is loaded and working!
很明显我们运行正常,我们再来测试另一个
php -r 'echo lzpay_test2();'
Hello World
带参数试一下
php -r 'echo lzpay_test2("贵客");'
Hello Hello 贵客
向你问好了哟,一切OK,但我们现在要写一个带参数的
4、扩展编写
这里我们来增加一点难度,网上多数是hello world之类的,这里就有个问题,如果我们只是使用到字符处理就多半不会不自定义扩展了,我们来写一个加密处理,也就是把如何加密的这部分隐藏起来,就算有人拿到了你的PHP代码,至少也不知道你这部分逻辑是怎么处理的。
首先我们会使用到md5的类,这里有两个方法,一个是调用系统的共用md5类,这个就有难度了,要求你的C语言能力比较强,因为在调用call_user_function_ex方法时,只能使用zval类型,针对单一的字符处理就已经够要命了,所以,我自己写了一个md5的类,闲话少说,上代码:
md5.h
#ifndef MD5_H
#define MD5_H
typedef struct
{
unsigned int count[2];
unsigned int state[4];
unsigned char buffer[64];
}MD5_CTX;
#define F(x,y,z) ((x & y) | (~x & z))
#define G(x,y,z) ((x & z) | (y & ~z))
#define H(x,y,z) (x^y^z)
#define I(x,y,z) (y ^ (x | ~z))
#define ROTATE_LEFT(x,n) ((x << n) | (x >> (32-n)))
#define FF(a,b,c,d,x,s,ac) \
{ \
a += F(b,c,d) + x + ac; \
a = ROTATE_LEFT(a,s); \
a += b; \
}
#define GG(a,b,c,d,x,s,ac) \
{ \
a += G(b,c,d) + x + ac; \
a = ROTATE_LEFT(a,s); \
a += b; \
}
#define HH(a,b,c,d,x,s,ac) \
{ \
a += H(b,c,d) + x + ac; \
a = ROTATE_LEFT(a,s); \
a += b; \
}
#define II(a,b,c,d,x,s,ac) \
{ \
a += I(b,c,d) + x + ac; \
a = ROTATE_LEFT(a,s); \
a += b; \
}
void MD5Init(MD5_CTX *context);
void MD5Update(MD5_CTX *context,unsigned char *input,unsigned int inputlen);
void MD5Final(unsigned char digest[16],MD5_CTX *context);
void MD5Transform(unsigned int state[4],unsigned char block[64]);
void MD5Encode(unsigned char *output,unsigned int *input,unsigned int len);
void MD5Decode(unsigned int *output,unsigned char *input,unsigned int len);
#endif
md5.c
#include <memory.h>
#include "md5.h"
unsigned char PADDING[]={
0x80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
void MD5Init(MD5_CTX *context)