商城首页欢迎来到中国正版软件门户

您的位置: 首页 > 文章列表 > 编程开发 > PHP内核探索:嵌入式PHP

PHP内核探索:嵌入式PHP

  发布于2026-07-20 阅读(0)

扫一扫,手机访问

从PHP源码的目录结构以及它的生命周期来看,嵌入式PHP其实和CLI模式很像,本质上都是SAPI接口的另一种实现方式。正常情况下,一个嵌入式请求的生命周期也遵循着经典的流程:模块初始化、请求初始化、处理请求、关闭请求,最后关闭模块。当然,这只能说是一个理想化的模型。毕竟,具体的应用场景总有自己特殊的需求,只是在处理PHP脚本这个核心环节上,大家基本保持一致。

很多人对嵌入式PHP可能比较陌生,甚至觉得根本用不上,网上相关的资料也确实不多。举个例子,很多游戏会用Lua作为粘合语言或扩展脚本,浏览器里的Ja vaScript也是嵌入运行的。但说实话,目前很少有应用会把PHP当作嵌入语言来用,PHP的强项仍然牢牢扎根在Web开发领域。

那么,PHP到底为嵌入式提供了哪些支持,又有哪些接口或功能呢?我们不妨先来看一段核心的示例代码:

#include 

#ifdef ZTS
void ***tsrm_ls;
#endif

/* Extension bits */
zend_module_entry php_mymod_module_entry = {
    STANDARD_MODULE_HEADER,
    "mymod", /* extension name */
    NULL, /* function entries */
    NULL, /* MINIT */
    NULL, /* MSHUTDOWN */
    NULL, /* RINIT */
    NULL, /* RSHUTDOWN */
    NULL, /* MINFO */
    "1.0", /* version */
    STANDARD_MODULE_PROPERTIES
};

/* Embedded bits */
static void startup_php(void)
{
    int argc = 1;
    char *argv[2] = { "embed5", NULL };
    php_embed_init(argc, argv PTSRMLS_CC);
    zend_startup_module(&php_mymod_module_entry);
}

static void execute_php(char *filename)
{
    zend_first_try {
        char *include_script;
        spprintf(&include_script, 0, "include '%s'", filename);
        zend_eval_string(include_script, NULL, filename TSRMLS_CC);
        efree(include_script);
    } zend_end_try();
}

int main(int argc, char *argv[])
{
    if (argc <= 1) {
        printf("Usage: embed4 scriptfile");
        return -1;
    }
    startup_php();
    execute_php(argv[1]);
    php_embed_shutdown(TSRMLS_CC);
    return 0;
}

这段代码出自《Extending and Embedding PHP》的第20章(原书有个小符号错误,有兴趣的可以自己去翻翻)。它本质上就是一个“嵌入式PHP运行器”,可以让我们在上面跑PHP代码。代码逻辑很清晰:包含了嵌入式支持的声明,启动嵌入式环境,执行PHP脚本,最后关闭环境。接下来,我们就顺着代码来拆解,看看PHP在底层做了哪些工作。

先看第一行:

#include 

在源码的 sapi/embed 目录下,就是PHP为嵌入式提供的抽象层。我们需要的核心函数和宏定义,比如 php_embed_initphp_embed_shutdown,都在这里。

接着看第2到4行:

#ifdef ZTS
void ***tsrm_ls;
#endif

这里的 ZTS 是 Zend Thread Safety 的缩写,背后关联的是TSRM(线程安全资源管理)机制。这部分内容后面会专门展开,这里先不深入。

再来看第6到17行,这是PHP内部的模块结构声明:

zend_module_entry php_mymod_module_entry = {
    STANDARD_MODULE_HEADER,
    "mymod", /* extension name */
    NULL, /* function entries */
    NULL, /* MINIT */
    NULL, /* MSHUTDOWN */
    NULL, /* RINIT */
    NULL, /* RSHUTDOWN */
    NULL, /* MINFO */
    "1.0", /* version */
    STANDARD_MODULE_PROPERTIES
};

可以看到,这个模块在初始化、请求开始、结束等事件发生时,函数指针都设为了NULL,意味着这些环节它什么都不做。不过,真正的初始化与关闭操作,其实藏在了 sapi/embed/php_embed.c 文件的 php_embed_shutdown 等函数里。关于模块结构的具体定义,可以去 zend/zend_modules.h 中查看。

然后是 startup_php 函数:

static void startup_php(void)
{
    int argc = 1;
    char *argv[2] = { "embed5", NULL };
    php_embed_init(argc, argv PTSRMLS_CC);
    zend_startup_module(&php_mymod_module_entry);
}

这个函数干了两件事:调用 php_embed_init(在 sapi/embed/php_embed.c 中定义)来完成嵌入式环境的初始化;然后调用 zend_startup_module,这是PHP的内部API,用来注册我们定义的模块——这里就是注册 mymod 模块。注册的过程其实很简单,就是把 zend_module_entry 结构体添加到模块注册列表里。

接着是 execute_php 函数,从名字就能看出它的职责:

static void execute_php(char *filename)
{
    zend_first_try {
        char *include_script;
        spprintf(&include_script, 0, "include '%s'", filename);
        zend_eval_string(include_script, NULL, filename TSRMLS_CC);
        efree(include_script);
    } zend_end_try();
}

它通过 spprintf 构造了一条 include 语句,然后调用 zend_eval_string 去执行。而 zend_eval_string 最终会调用 zend_eval_stringl,流程就是编译PHP代码、生成 zend_op_array 类型的数据,最后执行opcode。说白了,这段C代码相当于下面这段PHP程序:

虽然这段PHP程序单独用命令行也能跑,没什么实际意义,但嵌入式的意义在于:你可以在一个用C实现的系统里,把PHP嵌进去,然后用PHP来实现业务逻辑,这才是它的价值所在。

最后是 main 函数:

int main(int argc, char *argv[])
{
    if (argc <= 1) {
        printf("Usage: embed4 scriptfile");
        return -1;
    }
    startup_php();
    execute_php(argv[1]);
    php_embed_shutdown(TSRMLS_CC);
    return 0;
}

主函数的逻辑很直白:先做初始化,然后根据传入的参数执行 include,最后调用 php_embed_shutdown(也在 sapi/embed/php_embed.c 中定义)完成关闭操作,包括请求关闭、模块关闭等。

以上就是用PHP嵌入式方式开发的一个简易PHP代码运行器。可以看到,这些调用都基于PHP自身的实现,但针对嵌入式这个SAPI的定义,其实非常简单——甚至可以说是“简陋”。这完全可以理解,毕竟在嵌入式环境下,很多网络协议所需的方法都用不上了。下面就是嵌入式的模块定义:

sapi_module_struct php_embed_module = {
    "embed", /* name */
    "PHP Embedded Library", /* pretty name */
    php_embed_startup, /* startup */
    php_module_shutdown_wrapper, /* shutdown */
    NULL, /* activate */
    php_embed_deactivate, /* deactivate */
    php_embed_ub_write, /* unbuffered write */
    php_embed_flush, /* flush */
    NULL, /* get uid */
    NULL, /* getenv */
    php_error, /* error handler */
    NULL, /* header handler */
    NULL, /* send headers handler */
    php_embed_send_header, /* send header handler */
    NULL, /* read POST data */
    php_embed_read_cookies, /* read Cookies */
    php_embed_register_variables, /* register server variables */
    php_embed_log_message, /* Log message */
    NULL, /* Get request time */
    NULL, /* Child terminate */
    STANDARD_SAPI_MODULE_PROPERTIES
};

在这个定义里,能看到很多 NULL。之前我们提到过,在SAPI那一节,我们以Cookie读取为例,这里也有一个名为 php_embed_read_cookies 的函数,但它的实现,其实就是一个空指针。

本文转载于:https://blog.csdn.net/zndy10/article/details/74942513 如有侵犯,请联系zhengruancom@outlook.com删除。
免责声明:正软商城发布此文仅为传递信息,不代表正软商城认同其观点或证实其描述。

热门关注