美文网首页PHP经验分享
PHP拓展开发 - 示例

PHP拓展开发 - 示例

作者: 半亩房顶 | 来源:发表于2019-05-03 23:36 被阅读37次

前言

个人兴趣,结合一个示例学习一下PHP拓展的开发

实例目标

实现一个简单的函数,将字符串大小写转换

<?php
    function my_toupper($str) {
        return strtoupper($str);
    }
    
    echo my_toupper('demo');
?>

实现例子

生成扩展框架

PHP提供了一个扩展框架生成器:ext_skel,这个工具在php源码的ext目录(我的是/usr/local/src/php-5.6.17/ext/)下。首先我们在php源码的ext目录下创建一个orlion.skel文件,文件内容为:
string my_toupper(string str)
这个文件就是要告诉ext_skel我们的扩展里有my_toupper这个函数,接下来执行:
./ext_skel --extname=orlion --proto=orlion.skel

运行结果
  • 有个报错


处理方案

done

修改代码

(1) 修改orlion目录下的config.m4,将这个文件第10、11、12行的dnl去掉
(2) 接下来就是要实现我们的功能了,打开orlion.c,然后找到函数PHP_FUNCTION(my_toupper),修改为如下:

PHP_FUNCTION(my_toupper)
{
    char *str = NULL;
    int argc = ZEND_NUM_ARGS();
    int str_len;

    if (zend_parse_parameters(argc TSRMLS_CC, "s", &str, &str_len) == FAILURE) 
        return;

    int i;
    for (i = 0; i < str_len; i++) {
        if(str[i] >= 'a' && str[i] < 'z') {
            str[i] = str[i] - 32;
        } else {
            str[i] = str[i] + 32;
        }
    }
    str[i] = '\0';

    RETURN_STRINGL(str, str_len, 0);
    // php_error(E_WARNING, "my_toupper: not yet implemented");
}

然后编译安装扩展

$ /usr/local/php/bin/phpize
$ ./configure --with-php-config=/usr/local/php/bin/php-config
$ make && make install

然后修改配置php.ini在文件最后边加上"extension=orlion.so"然后重启php-fpm。

测试

<?php
    var_dump(my_toupper('abcDEF'));
?>
测试结果

真正的学习才刚刚开始

你为什么需要一个拓展

  • 因为PHP语言本身抽象程度有限,有一些库或者操作系统级别的调用,不能用PHP直接调用。
  • 你想给PHP添加一些与众不同的行为。
  • 你已经写了一些PHP代码,但是当运行的时候你知道它可以更快,更小,消耗的内存更少。
  • 你有一部分程序想出售,你可以把它写成扩展,这样程序是可以执行的,但是别人却无法看到源码。

什么是扩展

PHP的核心是由两个独立的部分组成的。

  • 在最底层是Zend Engine (ZE)
    ZE 负责把人类可以理解的脚本解析成机器可以理解的符号(token),然后在一个进程空间内执行这些符号。ZE还负责内存管理,变量作用域,以及函数调用的调度。
  • 另一部分是PHP
    PHP负责与SAPI层(Server Application Programming Interface,经常被用来与Apache, IIS, CLI, CGI等host环境进行关联)的交互以及绑定。它也为safe_modeopen_basedir检查提供了一个统一的控制层,就像streams层把文件和网络I/O用户空间函数(例如fopen(),fread()和fwrite())关联起来一样。

生命周期

当一个给定的SAPI启动后,以/usr/local/apache/bin/apachectl start的响应为例

  • PHP以初始化它的核心子系统作为开始。随着SAPI启动程序的结束,PHP开始加载每个扩展的代码,然后调用它们的模块初始化(MINIT)程序。这就给每个扩展机会用来初始化内部变量申请资源注册资源处理器,并且用ZE注册自己的函数,这样如果一个脚本调用这些函数中的一个,ZE就知道执行哪些代码。

  • 接下来,PHP会等待SAPI层的页面处理请求。在CGI或者CLI SAPI情况下,这个请求会立即发生并且只执行一次。在Apache, IIS, 或者其他成熟的web服务器SAPI中,请求处理会在远程用户发起请求的时候发生,并且会重复执行很多次,也可能是并发的。不管请求是怎么进来的,PHP以让ZE来建立脚本可以运行的环境作为开始,然后调用每个扩展的请求初始化(RINIT)函数。RINIT给了扩展一个机会,让其可以建立指定的环境变量,分配请求指定的资源,或者执行其他任务例如审计。关于RINIT函数调用最典型的例子是在session扩展中,如果session.auto_start选项是开启的,RINIT会自动触发用户空间的session_start()函数并且预先填充$_SESSION变量。

  • 当请求一旦被初始化,ZE便把PHP脚本翻译成符号(token),最终翻译成可以进行单步调试和执行的opcode。如果这些opcode中的一个需要调用一个扩展函数,ZE将会给那个函数绑定参数,并且临时放弃控制权直到函数执行完成

  • 当一个脚本完成了执行之后,PHP将会调用每个扩展的请求结束(RSHUTDOWN)函数来执行最后的清理工作(比如保存session变量到磁盘上)。接下来,ZE执行一个清理过程(熟知的垃圾回收)实际上是对上次请求过程中使用的变量调用unset()函数

  • 一旦完成,PHP等待SAPI发起另一个文档请求或者一个关闭信号。在CGI和CLI SAPI的情况下,没有所谓的“下一个请求”,所以SAPI会立刻执行关闭流程。在关闭过程中,PHP又让每个扩展调用自己的模块关闭(MSHUTDOWN)函数,最后关闭自己的核心子系统

这个过程第一次听令人有些费解,但是一旦你深入到一个扩展的开发过程中,它就会逐渐的清晰起来。


多线程
多进程

内存分配

为了避免写的很糟糕的扩展泄露内存,ZE以自己内部的方式来进行内存管理,通过用一个附加的标志来指明持久化。一个持久化分配的内存比单个页面请求存在的时间要长。一个非持久化分配的内存,相比之下,在请求结束的时候就会被释放,不管free函数是否被调用。例如用户空间变量,都是非持久化分配的内存,因为在请求结束之后这些变量都没有用了。

一个扩展理论上可以依靠ZE在每个页面请求结束后自动释放非持久化的内存,但这是不被推荐的。在请求结束的时候,分配的内存不会被立即被回收,并且会持续一段时间,所以和那块内存关联的资源将不会被恰当的关闭,这是一个很糟的做法,因为如果不能适当的清理的话,这会产生混乱。所以养成一个清理内存的习惯是有必要的

建立一个开发环境

现在你已经掌握了一些关于PHP和ZE的工作原理,我估计你希望要深入进去,并且开始写些什么。无论如何在你能做之前,你需要收集一些必要的开发工具,并且建立一个满足自己目标的环境。

第一你需要PHP本身,以及构建PHP所需要的开发工具集合。如果你对于从源码编译PHP不熟悉,我建议你看看http://www.php.net/install.unix。(开发windows下的PHP扩展在以后的文章会介绍)。使用适合自己发行版的PHP二进制包是很诱人的,但是这些版本总是会忽略两个重要的./configure选项,这两个选项在开发过程中非常方便。

  • 第一个是--enable-debug。这个选项将会用附加符号信息来编译PHP所以,如果一个段错误发生,那么你将可以从PHP收集到一个核心dump信息,然后使用gdb来跟踪这个段错误是在哪里发生的,为什么会发生。
  • 另一个选项依赖于你将要进行扩展开发的PHP版本。在PHP4.3这个选项叫--enable-experimental-zts,在PHP5和以后的版本中叫--enable-maintainer-zts。这个选项将会让PHP思考在多线程环境中的行为,并且可以让你捕获常见的程序错误,这些错误在非线程环境中不会引起问题,但在多线程环境中却使你的扩展变得不可用。一旦你已经使用这些额外的选项编译好了PHP,并且已经安装在了你的开发服务器(或者工作站)上,那么你可以开始建立你的第一个扩展了。

代码文件详解 - 链接

扩展函数的传参 - 链接

扩展参数返回 - 链接

扩展变量详解 - 链接

在扩展里调第三方函数(标准库/原生/个人) - 链接

至此,本文结束,之后如果有开发拓展的实际操作项目,会继续学习并总结,分享出来

参考资料

PHP扩展开发入门
菜鸟学php扩展 - 咖啡色的羊驼
PHP源码
zend_API.h
PHP和Zend介绍
Zend API:深入 PHP 内核
关于做PHP扩展开发的一些资源
揭秘TSRM

以上

欢迎大家关注我的公众号


半亩房顶

相关文章

  • PHP拓展开发 - 示例

    前言 个人兴趣,结合一个示例学习一下PHP拓展的开发 实例目标 实现一个简单的函数,将字符串大小写转换 实现例子 ...

  • 阿里云移动推送

    IOS推送代码示例 android代码示例 备注 参考资源 官方开发文档地址--PHP官方github地址

  • php-rabbitMQ拓展安装

    一、PHP-rabbitMQ拓展安装 1.php-rabbitMQ拓展下载php-rabbitMQ拓展源码通过ph...

  • Centos7 yum 安装 php7.1

    配置yum源 安装 查看php版本 查看php拓展 启动php-fpm pecl安装php拓展 1.安装pecl ...

  • [原]使用PHP安全检测拓展Taint检测你的PHP代码 (附源

    一.拓展简介 Taint是鸟哥写的一个PHP拓展 支持PHP5.2~PHP7.2。拓展启用后能监控某些关键函数是否...

  • 2020-02-14 从php回头学C

    一直用php编程,回头学习C和C++。php核心也是基于C开发的,也能使用C来写php的拓展,记录下从脚本语言到底...

  • PHP拓展

    PHP拓展 影响PHP行为的拓展 音频格式操作 身份认证服务 针对命令行的拓展 压缩与归档拓展 信用卡处理 加密拓...

  • Cookie & Session

    阅读原文 cookie.php 为cookie示例session.php 为session示例demo验证cook...

  • php安装拓展

    php拓展文件类型 windows .dll文件 linux .so文件 查看已经安装的php拓展 phpinfo...

  • 工具类:延迟执行队列

    代码: 使用示例 使用拓展

网友评论

    本文标题:PHP拓展开发 - 示例

    本文链接:https://www.haomeiwen.com/subject/nxohnqtx.html