发布于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_init、php_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 的函数,但它的实现,其实就是一个空指针。
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
正版软件
正版软件
正版软件
正版软件
正版软件
1
2
3
7
8