PHP扩展开发-执行流程与扩展结构

简介:

在开发扩展之前,最好了解下PHP内核的执行流程,PHP大概包括三个方面:

SAPI
Zend VM
扩展

  • Zend VM是PHP的虚拟机,与JVM类似,都是各自语言的编译/执行的核心。它们都会把各自的代码先编译为一种中间代码,PHP的通常叫opcode,Java通常叫bytecode,不同的是PHP的opcode直接被Zend VM的执行单元调用对应的C函数执行(PHP7加入了AST,会先生成AST,再生成opcode),不会显示保留下来(可以cache保留),而Java通常是生成class文件保留下来。而这一点可能也是PHP interpreter的名称的由来吧。其实相对严格的C/C++等编译型语言,PHP和Java更多的是结合了编译型和解释性的风格。
  • SAPI可以看作是Zend VM向外界提供编译/执行PHP代码服务的方式和规范。无论是作为cli/cgi/fastcgi/apache_mod与其他程序交互,还是嵌入其他语言中如C/C++等,都可以通过SAPI的规范实现。它的一个重要数据结构就是sapi_module_struct(main/SAPI.h line 217)
  • 扩展部分可以看作是搭建在Zend VM和SAPI之上的库,为PHP开发人员提供性能和易用性上的保证。Java的各种包/Python的各种模块功能类似,不同的是PHP中为了性能是用C扩展来实现的,类似的在Java中可以通过JNI来实现,Python中如_socket和_select(多路复用)都不是原生Python实现。

生命周期

关于各种SAPI或者PHP本身的生命周期,可能会和其他组件如apache耦合,后续再细谈。关于PHP扩展的生命周期,这里借用一张图。流程应该是很容易明白的,关于这个过程,网上也有很多资料,不再赘述。我们开发扩展需要注意的几个地方也可以对应到图中的某些节点:

全局变量的定义,通常是zend_modulename_globals
模块的初始化,包括资源/类/常量/ini配置等模块级的初始化
请求的初始化,包括与单次请求相关的一些初始化
请求的结束,清理单次请求相关的数据/内存
模块的卸载,清理模块相关的数据/内存

基本上我们要做的就是按照上面的流程,实现相关的内置函数,定义自己的资源/全局变量/类/函数等。值得注意的地方是在在嵌入其他语言如Python或者被嵌入其他组件如apache时,要小心多进程多线程相关的问题。


PHP扩展结构

使用php-src/ext/ext_skel可以生成PHP扩展的框架

./ext_skel --extname=myext
[tan@tan ~/software/needbak/php-5.5.20/ext 12:24]$==> ls myext/
config.m4  config.w32  CREDITS  EXPERIMENTAL  myext.c  myext.php  php_myext.h  tests

比较重要的文件是config.m4(当然还有源码),config.m4文件可以使用phpize命令生成configure文件,其中说明了我们是否开启模块,以及外部依赖的库。

//config.m4
//如果你的扩展依赖其他外部库
dnl PHP_ARG_WITH(myext, for myext support,
dnl Make sure that the comment is aligned:
dnl [  --with-myext             Include myext support])

//扩展不依赖外部库
dnl PHP_ARG_ENABLE(myext, whether to enable myext support,
dnl Make sure that the comment is aligned:
dnl [  --enable-myext           Enable myext support])

//寻找并包含头文件
if test "$PHP_MYEXT" != "no"; then
  dnl Write more examples of tests here...

  dnl # --with-myext -> check with-path
  dnl SEARCH_PATH="/usr/local /usr"     # you might want to change this
  dnl SEARCH_FOR="/include/myext.h"  # you most likely want to change this
  dnl if test -r $PHP_MYEXT/$SEARCH_FOR; then # path given as parameter
  dnl   MYEXT_DIR=$PHP_MYEXT
  dnl else # search default path list
  dnl   AC_MSG_CHECKING([for myext files in default path])
  dnl   for i in $SEARCH_PATH ; do
  dnl     if test -r $i/$SEARCH_FOR; then
  dnl       MYEXT_DIR=$i
  dnl       AC_MSG_RESULT(found in $i)
  dnl     fi
  dnl   done
  dnl fi
  dnl
  dnl if test -z "$MYEXT_DIR"; then
  dnl   AC_MSG_RESULT([not found])
  dnl   AC_MSG_ERROR([Please reinstall the myext distribution])
  dnl fi

  dnl # --with-myext -> add include path
  dnl PHP_ADD_INCLUDE($MYEXT_DIR/include)

  //加载的lib位置
  dnl # --with-myext -> check for lib and symbol presence
  dnl LIBNAME=myext # you may want to change this
  dnl LIBSYMBOL=myext # you most likely want to change this 

  dnl PHP_CHECK_LIBRARY($LIBNAME,$LIBSYMBOL,
  dnl [
  dnl   PHP_ADD_LIBRARY_WITH_PATH($LIBNAME, $MYEXT_DIR/$PHP_LIBDIR, MYEXT_SHARED_LIBADD)
  dnl   AC_DEFINE(HAVE_MYEXTLIB,1,[ ])
  dnl ],[
  dnl   AC_MSG_ERROR([wrong myext lib version or lib not found])
  dnl ],[
  dnl   -L$MYEXT_DIR/$PHP_LIBDIR -lm
  dnl ])
  dnl
  dnl PHP_SUBST(MYEXT_SHARED_LIBADD)

  PHP_NEW_EXTENSION(myext, myext.c, $ext_shared)
fi
//php_myext.h
#ifndef PHP_MYEXT_H
#define PHP_MYEXT_H

extern zend_module_entry myext_module_entry;
#define phpext_myext_ptr &myext_module_entry

//导出符号,在链接的时候有用
#ifdef PHP_WIN32
#   define PHP_MYEXT_API __declspec(dllexport)
#elif defined(__GNUC__) && __GNUC__ >= 4
#   define PHP_MYEXT_API __attribute__ ((visibility("default")))
#else
#   define PHP_MYEXT_API
#endif

#ifdef ZTS
#include "TSRM.h"
#endif

//几个核心函数的声明
PHP_MINIT_FUNCTION(myext);
PHP_MSHUTDOWN_FUNCTION(myext);
PHP_RINIT_FUNCTION(myext);
PHP_RSHUTDOWN_FUNCTION(myext);
PHP_MINFO_FUNCTION(myext);

//自动生成的测试函数声明,我们自己定义的模块函数需要在此声明
PHP_FUNCTION(confirm_myext_compiled);   

//全局变量在这定义,展开后是zend_myext_globals结构体
ZEND_BEGIN_MODULE_GLOBALS(myext)
    long  global_value;
    char *global_string;
ZEND_END_MODULE_GLOBALS(myext)

//线程安全与非线程安全下获取全局变量的方式
#ifdef ZTS
#define MYEXT_G(v) TSRMG(myext_globals_id, zend_myext_globals *, v)
#else
#define MYEXT_G(v) (myext_globals.v)
#endif

#endif  /* PHP_MYEXT_H */
//myext.c
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "php.h"
#include "php_ini.h"
#include "ext/standard/info.h"
#include "php_myext.h"

//全局变量声明
ZEND_DECLARE_MODULE_GLOBALS(myext)


/* True global resources - no need for thread safety here */
static int le_myext;

//模块函数的导出
const zend_function_entry myext_functions[] = {
    PHP_FE(confirm_myext_compiled,  NULL)       /* For testing, remove later. */
    PHP_FE_END  /* Must be the last line in myext_functions[] */
};


//模块结构
zend_module_entry myext_module_entry = {
#if ZEND_MODULE_API_NO >= 20010901
    STANDARD_MODULE_HEADER,
#endif
    "myext",
    myext_functions,
    PHP_MINIT(myext),
    PHP_MSHUTDOWN(myext),
    PHP_RINIT(myext),       /* Replace with NULL if there's nothing to do at request start */
    PHP_RSHUTDOWN(myext),   /* Replace with NULL if there's nothing to do at request end */
    PHP_MINFO(myext),
#if ZEND_MODULE_API_NO >= 20010901
    PHP_MYEXT_VERSION,
#endif
    STANDARD_MODULE_PROPERTIES
};


#ifdef COMPILE_DL_MYEXT
ZEND_GET_MODULE(myext)
#endif

//ini配置文件的设置
PHP_INI_BEGIN()
    STD_PHP_INI_ENTRY("myext.global_value",      "42", PHP_INI_ALL, OnUpdateLong, global_value, zend_myext_globals, myext_globals)
    STD_PHP_INI_ENTRY("myext.global_string", "foobar", PHP_INI_ALL, OnUpdateString, global_string, zend_myext_globals, myext_globals)
PHP_INI_END()


//初始化全局变量
static void php_myext_init_globals(zend_myext_globals *myext_globals)
{
    myext_globals->global_value = 0;
    myext_globals->global_string = NULL;
}


//模块加载时的函数
PHP_MINIT_FUNCTION(myext)
{
    /* If you have INI entries, uncomment these lines 
    REGISTER_INI_ENTRIES();
    */
    return SUCCESS;
}


//模块卸载时函数
PHP_MSHUTDOWN_FUNCTION(myext)
{
    /* uncomment this line if you have INI entries
    UNREGISTER_INI_ENTRIES();
    */
    return SUCCESS;
}


//请求初始化函数
PHP_RINIT_FUNCTION(myext)
{
    return SUCCESS;
}


//请求关闭函数
PHP_RSHUTDOWN_FUNCTION(myext)
{
    return SUCCESS;
}


//模块信息,phpinfo
PHP_MINFO_FUNCTION(myext)
{
    php_info_print_table_start();
    php_info_print_table_header(2, "myext support", "enabled");
    php_info_print_table_end();

    /* Remove comments if you have entries in php.ini
    DISPLAY_INI_ENTRIES();
    */
}



//测试函数
PHP_FUNCTION(confirm_myext_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;
    }

    len = spprintf(&strg, 0, "Congratulations! You have successfully modified ext/%.78s/config.m4. Module %.78s is now compiled into PHP.", "myext", arg);
    RETURN_STRINGL(strg, len, 0);
}
相关文章
ly~
|
2月前
|
存储 供应链 小程序
除了微信小程序,PHP 还可以用于开发哪些类型的小程序?
除了微信小程序,PHP 还可用于开发多种类型的小程序,包括支付宝小程序、百度智能小程序、抖音小程序、企业内部小程序及行业特定小程序。在电商、生活服务、资讯、工具、娱乐、营销等领域,PHP 能有效管理商品信息、订单处理、支付接口、内容抓取、复杂计算、游戏数据、活动规则等多种业务。同时,在企业内部,PHP 可提升工作效率,实现审批流程、文件共享、生产计划等功能;在医疗和教育等行业,PHP 能管理患者信息、在线问诊、课程资源、成绩查询等重要数据。
ly~
79 6
|
2月前
|
设计模式 算法 数据库连接
PHP中的设计模式:提高代码的可维护性与扩展性本文旨在探讨PHP中常见的设计模式及其应用,帮助开发者编写出更加灵活、可维护和易于扩展的代码。通过深入浅出的解释和实例演示,我们将了解如何使用设计模式解决实际开发中的问题,并提升代码质量。
在软件开发过程中,设计模式是一套经过验证的解决方案模板,用于处理常见的软件设计问题。PHP作为流行的服务器端脚本语言,也有其特定的设计模式应用。本文将重点介绍几种PHP中常用的设计模式,包括单例模式、工厂模式和策略模式,并通过实际代码示例展示它们的具体用法。同时,我们还将讨论如何在实际项目中合理选择和应用这些设计模式,以提升代码的可维护性和扩展性。
61 4
|
11天前
|
自然语言处理 前端开发 JavaScript
php结构
php结构
19 1
|
14天前
|
XML JSON API
【PHP开发专栏】PHP RESTful API设计与开发
随着互联网技术的发展,前后端分离成为Web开发的主流模式。本文介绍RESTful API的基本概念、设计原则及在PHP中的实现方法。RESTful API是一种轻量级、无状态的接口设计风格,通过HTTP方法(GET、POST、PUT、DELETE)操作资源,使用JSON或XML格式传输数据。在PHP中,通过定义路由、创建控制器、处理HTTP请求和响应等步骤实现RESTful API,并强调了安全性的重要性。
22 2
|
18天前
|
XML 安全 PHP
PHP与SOAP Web服务开发:基础与进阶教程
本文介绍了PHP与SOAP Web服务的基础和进阶知识,涵盖SOAP的基本概念、PHP中的SoapServer和SoapClient类的使用方法,以及服务端和客户端的开发示例。此外,还探讨了安全性、性能优化等高级主题,帮助开发者掌握更高效的Web服务开发技巧。
|
28天前
|
NoSQL 安全 Linux
MongoDB PHP 扩展
10月更文挑战第19天
12 0
MongoDB PHP 扩展
|
1月前
|
SQL 关系型数据库 MySQL
PHP与MySQL协同工作的艺术:开发高效动态网站
在这个后端技术迅速迭代的时代,PHP和MySQL的组合仍然是创建动态网站和应用的主流选择之一。本文将带领读者深入理解PHP后端逻辑与MySQL数据库之间的协同工作方式,包括数据的检索、插入、更新和删除操作。文章将通过一系列实用的示例和最佳实践,揭示如何充分利用这两种技术的优势,构建高效、安全且易于维护的动态网站。
|
1月前
|
Java PHP
PHP作为广受青睐的服务器端脚本语言,在Web开发中占据重要地位。理解其垃圾回收机制有助于开发高效稳定的PHP应用。
【10月更文挑战第1天】PHP作为广受青睐的服务器端脚本语言,在Web开发中占据重要地位。其垃圾回收机制包括引用计数与循环垃圾回收,对提升应用性能和稳定性至关重要。本文通过具体案例分析,详细探讨PHP垃圾回收机制的工作原理,特别是如何解决循环引用问题。在PHP 8中,垃圾回收机制得到进一步优化,提高了效率和准确性。理解这些机制有助于开发高效稳定的PHP应用。
45 3
|
2月前
|
设计模式 算法 数据库连接
PHP中的设计模式:如何优化你的代码结构
在本文中,我们将深入探讨PHP中的设计模式。设计模式是解决常见软件设计问题的最佳实践。它们不是具体的代码,而是一种编程经验的总结。掌握设计模式可以帮助你写出更高效、灵活和可维护的代码。本文将介绍几种常见的设计模式,并通过示例展示如何在PHP项目中应用这些模式。无论你是PHP初学者还是有经验的开发者,都能从本文中获得启发和实用的技巧。
|
1月前
|
前端开发 JavaScript 小程序
前端uni开发后端用PHP的圈子系统该 如何做源码?
圈子系统系统基于TP6+Uni-app框架开发;客户移动端采用uni-app开发,管理后台TH6开发。系统支持微信公众号端、微信小程序端、H5端、PC端多端账号同步,可快速打包生成APP
下一篇
无影云桌面