从PHP源码目录结构的介绍以及PHP生命周期可知:嵌入式PHP类似CLI,也是SAPI接口的另一种实现。 一般情况下,它的一个请求的生命周期也会和其它的SAPI一样:模块初始化=>请求初始化=>处理请求=>关闭请求=>关闭模 块。 当然,这只是理想情况。因为特定的应用由自己特殊的需求,只是在处理PHP脚本这个环节基本一致。

对于嵌入式PHP或许我们了解比较少,或者说根本用不到,甚至在网上相关的资料也不多, 例如很多游戏中使用Lua语言作为粘合语言,或者作为扩展游戏的脚本语言,类似的, 浏览器中的Javascript语言就是嵌入在浏览器中的。只是目前很少有应用将PHP作为嵌入语言来使用, PHP的强项目前还是在Web开发方面。

PHP对于嵌入式PHP的支持以及PHP为嵌入式提供了哪些接口或功能呢?首先我们看下所要用到的示例源码:

01 #include <sapi/embed/php_embed.h>
02 #ifdef ZTS
03     void ***tsrm_ls;
04 #endif
05 /* Extension bits */
06 zend_module_entry php_mymod_module_entry = {
07     STANDARD_MODULE_HEADER,
08     "mymod", /* extension name */
09     NULL, /* function entries */
10     NULL, /* MINIT */
11     NULL, /* MSHUTDOWN */
12     NULL, /* RINIT */
13     NULL, /* RSHUTDOWN */
14     NULL, /* MINFO */
15     "1.0", /* version */
16     STANDARD_MODULE_PROPERTIES
17 };
18 /* Embedded bits */
19 static void startup_php(void)
20 {
21     int argc = 1;
22     char *argv[2] = { "embed5", NULL };
23     php_embed_init(argc, argv PTSRMLS_CC);
24     zend_startup_module(&php_mymod_module_entry);
25 }
26 static void execute_php(char *filename)
27 {
28     zend_first_try {
29         char *include_script;
30         spprintf(&include_script, 0, "include '%s'", filename);
31         zend_eval_string(include_script, NULL, filename TSRMLS_CC);
32         efree(include_script);
33     } zend_end_try();
34 }
35 int main(int argc, char *argv[])
36 {
37     if (argc <= 1) {
38         printf("Usage: embed4 scriptfile";);
39         return -1;
40     }
41     startup_php();
42     execute_php(argv[1]);
43     php_embed_shutdown(TSRMLS_CC);
44     return 0;
45 }

以上的代码可以在《Extending and Embedding PHP》在第20章找到(原始代码有一个符号错误,有兴趣的童鞋可以去围观下)。 上面的代码是一个嵌入式PHP运行器(我们权当其为运行器吧),在这个运行器上我们可以运行PHP代码。 这段代码包括了对于PHP嵌入式支持的声明,启动嵌入式PHP运行环境,运行PHP代码,关闭嵌入式PHP运行环境。 下面我们就这段代码分析PHP对于嵌入式的支持做了哪些工作。 首先看下第一行:

1 #include <sapi/embed/php_embed.h>

在sapi目录下的embed目录是PHP对于嵌入式的抽象层所在。在这里有我们所要用到的函数或宏定义。 如示例中所使用的php_embed_init,php_embed_shutdown等函数。

第2到4行:

1 #ifdef ZTS
2     void ***tsrm_ls;
3 #endif

ZTS是Zend Thread Safety的简写,与这个相关的有一个TSRM(线程安全资源管理)的东东,这个后面的章节会有详细介绍,这里就不再作阐述。

第6到17行:

01 zend_module_entry php_mymod_module_entry = {
02     STANDARD_MODULE_HEADER,
03     "mymod", /* extension name */
04     NULL, /* function entries */
05     NULL, /* MINIT */
06     NULL, /* MSHUTDOWN */
07     NULL, /* RINIT */
08     NULL, /* RSHUTDOWN */
09     NULL, /* MINFO */
10     "1.0", /* version */
11     STANDARD_MODULE_PROPERTIES
12 };

以上PHP内部的模块结构声明,此处对于模块初始化,请求初始化等函数指针均为NULL, 也就是模块在初始化及请求开始结束等事件发生的时候不执行任何操作。 不过这些操作在sapi/embed/php_embed.c文件中的php_embed_shutdown等函数中有体现。 关于模块结构的定义在zend/zend_modules.h中。

startup_php函数:

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

这个函数调用了两个函数php_embed_init和zend_startup_module完成初始化工作。 php_embed_init函数定义在sapi/embed/php_embed.c文件中。它完成了PHP对于嵌入式的初始化支持。 zend_startup_module函数是PHP的内部API函数,它的作用是注册定义的模块,这里是注册mymod模块。 这个注册过程仅仅是将所定义的zend_module_entry结构添加到注册模块列表中。

execute_php函数:

1 static void execute_php(char *filename)
2 {
3     zend_first_try {
4         char *include_script;
5         spprintf(&include_script, 0, "include '%s'", filename);
6         zend_eval_string(include_script, NULL, filename TSRMLS_CC);
7         efree(include_script);
8     } zend_end_try();
9 }

从函数的名称来看,这个函数的功能是执行PHP代码的。 它通过调用sprrintf函数构造一个include语句,然后再调用zend_eval_string函数执行这个include语句。 zend_eval_string最终是调用zend_eval_stringl函数,这个函数是流程是一个编译PHP代码, 生成zend_op_array类型数据,并执行opcode的过程。 这段程序相当于下面的这段php程序,这段程序可以用php命令来执行,虽然下面这段程序没有实际意义, 而通过嵌入式PHP中,你可以在一个用C实现的系统中嵌入PHP,然后用PHP来实现功能。

1 <?php
2 if($argc < 2) die("Usage: embed4 scriptfile");
3   
4 include $argv[1];
5 ?>

main函数:

01 int main(int argc, char *argv[])
02 {
03     if (argc <= 1) {
04         printf("Usage: embed4 scriptfile";);
05         return -1;
06     }
07     startup_php();
08     execute_php(argv[1]);
09     php_embed_shutdown(TSRMLS_CC);
10     return 0;
11 }

这个函数是主函数,执行初始化操作,根据输入的参数执行PHP的include语句,最后执行关闭操作,返回。 其中php_embed_shutdown函数定义在sapi/embed/php_embed.c文件中。它完成了PHP对于嵌入式的关闭操作支持。 包括请求关闭操作,模块关闭操作等。

以上是使用PHP的嵌入式方式开发的一个简单的PHP代码运行器,它的这些调用的方式都基于PHP本身的一些实现, 而针对嵌入式的SAPI定义是非常简单的,没有Apache和CGI模式的复杂,或者说是相当简陋,这也是由其所在环境决定。 在嵌入式的环境下,很多的网络协议所需要的方法都不再需要。如下所示,为嵌入式的模块定义。

01 sapi_module_struct php_embed_module = {
02     "embed",                       /* name */
03     "PHP Embedded Library",        /* pretty name */
04   
05     php_embed_startup,              /* startup */
06     php_module_shutdown_wrapper,   /* shutdown */
07   
08     NULL,                          /* activate */
09     php_embed_deactivate,           /* deactivate */
10   
11     php_embed_ub_write,             /* unbuffered write */
12     php_embed_flush,                /* flush */
13     NULL,                          /* get uid */
14     NULL,                          /* getenv */
15   
16     php_error,                     /* error handler */
17   
18     NULL,                          /* header handler */
19     NULL,                          /* send headers handler */
20     php_embed_send_header,          /* send header handler */
21   
22     NULL,                          /* read POST data */
23     php_embed_read_cookies,         /* read Cookies */
24   
25     php_embed_register_variables,   /* register server variables */
26     php_embed_log_message,          /* Log message */
27     NULL,                           /* Get request time */
28     NULL,                           /* Child terminate */
29   
30     STANDARD_SAPI_MODULE_PROPERTIES
31 };
32 /* }}} */

在这个定义中我们看到了若干的NULl定义,在前面一小节中说到SAPI时,我们是以cookie的读取为例, 在这里也有读取cookie的实现——php_embed_read_cookies函数,但是这个函数的实现是一个空指针NULL。

11.PHP内核探索:嵌入式PHP PHP内核探索:嵌入式PHP的更多相关文章

  1. 嵌入式系统Linux内核开发工程师必须掌握的三十道题(转)

    嵌入式系统Linux内核开发工程师必须掌握的三十道题 如果你能正确回答以下问题并理解相关知识点原理,那么你就可以算得上是基本合格的Linux内核开发工程师,试试看! 1) Linux中主要有哪几种内核 ...

  2. 嵌入式Linux编译内核步骤 / 重点解决机器码问题 / 三星2451

    嵌入式系统更新内核 1. 前言 手里有一块Friendly ARM的MINI2451的板子,这周试着编译内核,然后更新一下这个板子的Linux内核,想要更新Linux Kernel 4.1版本,但是种 ...

  3. 【内核】linux2.6版本内核编译配置选项(一)

    Linux 2.6.19.x 内核编译配置选项简介 作者:金步国 版权声明 本文作者是一位自由软件爱好者,所以本文虽然不是软件,但是本着 GPL 的精神发布.任何人都可以自由使用.转载.复制和再分发, ...

  4. Linux内核Makefile文件(翻译自内核手册)

    --译自Linux3.9.5 Kernel Makefiles(内核目录documention/kbuild/makefiles.txt) kbuild(kernel build) 内核编译器 Thi ...

  5. Linux内核分析——第二章 从内核出发

    第二章 从内核出发 一.获取内核源码 1.Git是分布式的:下载和管理Linux内核源代码: 2.获取最新提交到版本树的一个副本 $ git clone git://git.kernel.org/pu ...

  6. 【内核】linux2.6版本内核编译配置选项(二)

    目录 Linux2.6版本内核编译配置选项(一):http://infohacker.blog.51cto.com/6751239/1203633 Linux2.6版本内核编译配置选项(二):http ...

  7. 《Linux内核精髓:精通Linux内核必会的75个绝技》一HACK #15 ramzswap

    HACK #15 ramzswap 本节介绍将一部分内存作为交换设备使用的ramzswap.ramzswap是将一部分内存空间作为交换设备使用的基于RAM的块设备.对要换出(swapout)的页面进行 ...

  8. 《Linux内核精髓:精通Linux内核必会的75个绝技》一HACK #1 如何获取Linux内核

    HACK #1 如何获取Linux内核 本节介绍获取Linux内核源代码的各种方法.“获取内核”这个说法看似简单,其实Linux内核有很多种衍生版本.要找出自己想要的源代码到底是哪一个,必须首先理解各 ...

  9. 戴文的Linux内核专题:05配置内核(1)

    转自Linux中国 现在我们已经了解了内核,现在我们可以进入主要工作:配置并编译内核代码.配置内核代码并不会花费太长时间.配置工具会询问许多问题并且允许开发者配置内核的每个方面.如果你有不确定的问题或 ...

  10. 向linux内核加入系统调用新老内核比較

    2.6内核 1>改动linux-source-2.6.31/kernel/sys.c文件,在文件末尾加入系统响应函数.函数实现例如以下: asmlinkage int sys_mycall(in ...

随机推荐

  1. Struts2标签实现for循环

    感悟:但是不建议使用这种方法,按照MVC框架的思想 ,应该把业务更多放在后台.前台尽量只进行数据展示. 转自:http://blog.csdn.net/guandajian/article/detai ...

  2. SQL2008的数据更新跟踪测试 (监控数据表变化,可用于同步)

    POC过程如下: 这里我们建立一个测试环境,模拟数据在 Insert , Update 和 Delete 情况下的跟踪效果.1 .测试脚本的准备,下面脚本建立一个新的数据库环境,并作相应的跟踪配置后向 ...

  3. 微信绑定后台是验证token失败

    /AX/dapeng/VfanCms/Lib/ORG/ 在ORG文件夹中,找到Wechat.class.php文件,去掉解释,验证完后改回来!应该是为了防止后台被别人绑定了去.

  4. SparkStreaming+Flume出现ERROR ReceiverTracker: Deregistered receiver for stream 0: Error starting receiver 0 - org.jboss.netty.channel.ChannelException

    文章发自http://www.cnblogs.com/hark0623/p/4204104.html ,转载请注明 我发现太多太多的坑要趟了… 向yarn提交sparkstreaming了,提交脚本如 ...

  5. Windows内核下操作字符串!

    * Windows内核下操作字符串! */ #include <ntddk.h> #include <ntstrsafe.h> #define BUFFER_SIZE 1024 ...

  6. ZOJ 3157 Weapon

    题目传送门 题意:就是CF round# 329 B 的升级版,要求出相交点的个数 分析:逆序数用树状数组维护,求出非逆序数,然后所有情况(n * (n - 1)) / 2减之就是逆序数个数. #in ...

  7. LCIS POJ 2172 Greatest Common Increasing Subsequence

    题目传送门 题意:LCIS(Longest Common Increasing Subsequence) 最长公共上升子序列 分析:a[i] != b[j]: dp[i][j] = dp[i-1][j ...

  8. JavaBean中的get/set 的命名规范

      最近的struts项目中遇到了这样的问题: 我的action中全局变量明明有getset方法,而且是自动生成的,但是在使用的时候,总是说找不到这个属性的getset方法,取不到从jsp传来 的值, ...

  9. 【BZOJ】1051: [HAOI2006]受欢迎的牛(tarjan)

    http://www.lydsy.com/JudgeOnline/problem.php?id=1051 这题还好-1A了..但是前提还是看了题解的 囧.....一开始认为是并查集,oh,不行,,无法 ...

  10. lucene 使用注意

    1.建立索引时,忘记writer.close(); 结果: 正常结果: