学习PHP-src之前,我准备了一份源文件:

GitHub下载->https://github.com/helingfeng/php-src

简单分析一下源码的目录结构:

1. root根目录下,包含项目的说明文件以及设计方案,大部分文件是必读的。

2. build顾名思义,放置一些和源码编译相关的文件,比如编译前脚本配置、环境监测等。

3. ext官方扩展,包含了绝大数PHP函数的定义和实现,包括date、pdo、ftp、curl等。

4. main 放置PHP核心文件,主要实现PHP的基础设施,这里和Zend engine不一样,Zend engine主要完成最核心的语言运行环境。

5. Zend 放置Zend engine实现文件,包含脚本语法解析,扩展机制的实现等。

6. pear PHP的扩展与应用仓库

7. sapi 包含多种服务器的抽象层代码,例如apache的mod_php、cgi、fcgi以及fpm等接口。

8. TSRM(Thread Safe Resource Manager) php的线程安全是构建在TSRM库之上的。

9. tests php的测试脚本集合,包含各个模块功能的测试文件。

10. win32 包含windows平台下的相关实现,比如socket的实现在windows与*Nix平台就不太相同,同时包含了在windows下编译php的相关脚本。

讲完php目录结构,先来一张php架构图:

从架构图中,很清楚看出SAPI(Server Application Programming Interface)应用编程接口,是非常重要的东东。

今天,拿最简单的CGI接口来学习SAPI。

先百度百科一下什么是CGI:Common Gateway Interface 是WWW技术中最重要的技术之一,有着不可替代的重要地位。CGI是外部应用程序(CGI程序)与Web服务器之间的接口标准,是在CGI程序和Web服务器之间传递信息的规程。CGI规范允许Web服务器执行外部程序,并将它们的输出发送给Web浏览器,CGI将Web的一组简单的静态超媒体文档变成一个完整的新的交互式媒体。

脚本执行的开始都是以SAPI接口实现开始的。只是不同的SAPI接口实现会完成他们特定的工作, 例如Apache的mod_php SAPI实现需要初始化从Apache获取的一些信息,在输出内容是将内容返回给Apache, 其他的SAPI实现也类似。

以CGI为例了解一下源码结构:

cgi_main.c

定义SAPI:

 /* {{{ sapi_module_struct cgi_sapi_module
*/
static sapi_module_struct cgi_sapi_module = {
"cgi-fcgi", /* name */
"CGI/FastCGI", /* pretty name */ php_cgi_startup, /* startup */
php_module_shutdown_wrapper, /* shutdown */ sapi_cgi_activate, /* activate */
sapi_cgi_deactivate, /* deactivate */ sapi_cgi_ub_write, /* unbuffered write */
sapi_cgi_flush, /* flush */
NULL, /* get uid */
sapi_cgi_getenv, /* getenv */ php_error, /* error handler */ NULL, /* header handler */
sapi_cgi_send_headers, /* send headers handler */
NULL, /* send header handler */ sapi_cgi_read_post, /* read POST data */
sapi_cgi_read_cookies, /* read Cookies */ sapi_cgi_register_variables, /* register server variables */
sapi_cgi_log_message, /* Log message */
NULL, /* Get request time */
NULL, /* Child terminate */ STANDARD_SAPI_MODULE_PROPERTIES
};

定义了一些常量字符串,以及定义初始化函数、销毁函数、以及一些函数指针,告诉Zend如何获取与输出数据。

例如:php_cgi_startup 调用php初始化。

 static int php_cgi_startup(sapi_module_struct *sapi_module)
{
if (php_module_startup(sapi_module, &cgi_module_entry, 1) == FAILURE) {
return FAILURE;
}
return SUCCESS;
}

例如:php_module_shutdown_wrapper php关闭函数。

 int php_module_shutdown_wrapper(sapi_module_struct *sapi_globals)
{
php_module_shutdown();
return SUCCESS;
}

例如:sapi_cgi_activate 处理request进行初始化。

 static int sapi_cgi_activate(void)
{
char *path, *doc_root, *server_name;
size_t path_len, doc_root_len, server_name_len; /* PATH_TRANSLATED should be defined at this stage but better safe than sorry :) */
if (!SG(request_info).path_translated) {
return FAILURE;
} if (php_ini_has_per_host_config()) {
/* Activate per-host-system-configuration defined in php.ini and stored into configuration_hash during startup */
if (fcgi_is_fastcgi()) {
fcgi_request *request = (fcgi_request*) SG(server_context); server_name = FCGI_GETENV(request, "SERVER_NAME");
} else {
server_name = getenv("SERVER_NAME");
}
/* SERVER_NAME should also be defined at this stage..but better check it anyway */
if (server_name) {
server_name_len = strlen(server_name);
server_name = estrndup(server_name, server_name_len);
zend_str_tolower(server_name, server_name_len);
php_ini_activate_per_host_config(server_name, server_name_len);
efree(server_name);
}
} if (php_ini_has_per_dir_config() ||
(PG(user_ini_filename) && *PG(user_ini_filename))
) {
/* Prepare search path */
path_len = strlen(SG(request_info).path_translated); /* Make sure we have trailing slash! */
if (!IS_SLASH(SG(request_info).path_translated[path_len])) {
path = emalloc(path_len + 2);
memcpy(path, SG(request_info).path_translated, path_len + 1);
path_len = zend_dirname(path, path_len);
path[path_len++] = DEFAULT_SLASH;
} else {
path = estrndup(SG(request_info).path_translated, path_len);
path_len = zend_dirname(path, path_len);
}
path[path_len] = 0; /* Activate per-dir-system-configuration defined in php.ini and stored into configuration_hash during startup */
php_ini_activate_per_dir_config(path, path_len); /* Note: for global settings sake we check from root to path */ /* Load and activate user ini files in path starting from DOCUMENT_ROOT */
if (PG(user_ini_filename) && *PG(user_ini_filename)) {
if (fcgi_is_fastcgi()) {
fcgi_request *request = (fcgi_request*) SG(server_context); doc_root = FCGI_GETENV(request, "DOCUMENT_ROOT");
} else {
doc_root = getenv("DOCUMENT_ROOT");
}
/* DOCUMENT_ROOT should also be defined at this stage..but better check it anyway */
if (doc_root) {
doc_root_len = strlen(doc_root);
if (doc_root_len > 0 && IS_SLASH(doc_root[doc_root_len - 1])) {
--doc_root_len;
}
#ifdef PHP_WIN32
/* paths on windows should be case-insensitive */
doc_root = estrndup(doc_root, doc_root_len);
zend_str_tolower(doc_root, doc_root_len);
#endif
php_cgi_ini_activate_user_config(path, path_len, doc_root, doc_root_len, (doc_root_len > 0 && (doc_root_len - 1))); #ifdef PHP_WIN32
efree(doc_root);
#endif
}
} efree(path);
} return SUCCESS;
}

例如:sapi_cgi_deactivate 处理完request关闭函数,与activate对应。

 static int sapi_cgi_deactivate(void)
{
/* flush only when SAPI was started. The reasons are:
1. SAPI Deactivate is called from two places: module init and request shutdown
2. When the first call occurs and the request is not set up, flush fails on FastCGI.
*/
if (SG(sapi_started)) {
if (fcgi_is_fastcgi()) {
if (
!parent &&
!fcgi_finish_request((fcgi_request*)SG(server_context), 0)) {
php_handle_aborted_connection();
}
} else {
sapi_cgi_flush(SG(server_context));
}
}
return SUCCESS;
}

例如:sapi_cgibin_ub_write 告诉Zend如何输出数据。

 static size_t sapi_cgibin_ub_write(const char *str, size_t str_length) /* {{{ */
{
const char *ptr = str;
uint remaining = str_length;
size_t ret; while (remaining > 0) {
ret = sapi_cgibin_single_write(ptr, remaining);
if (!ret) {
php_handle_aborted_connection();
return str_length - remaining;
}
ptr += ret;
remaining -= ret;
} return str_length;
}

例如:sapi_cgi_flush Zend刷新缓存。

 static void sapi_cgi_flush(void *server_context)
{
if (fflush(stdout) == EOF) {
php_handle_aborted_connection();
}
}

例如:sapi_cgi_getenv 获取环境信息。

 static char *sapi_cgi_getenv(char *name, size_t name_len)
{
return getenv(name);
}

例如:php_error 异常输出。

CGI只是简单的调用了PHP提供的错误处理函数

例如:sapi_cgi_read_post 读取post data。

 static size_t sapi_cgi_read_post(char *buffer, size_t count_bytes)
{
size_t read_bytes = 0;
int tmp_read_bytes;
size_t remaining_bytes; assert(SG(request_info).content_length >= SG(read_post_bytes)); remaining_bytes = (size_t)(SG(request_info).content_length - SG(read_post_bytes)); count_bytes = MIN(count_bytes, remaining_bytes);
while (read_bytes < count_bytes) {
#ifdef PHP_WIN32
size_t diff = count_bytes - read_bytes;
unsigned int to_read = (diff > UINT_MAX) ? UINT_MAX : (unsigned int)diff; tmp_read_bytes = read(STDIN_FILENO, buffer + read_bytes, to_read);
#else
tmp_read_bytes = read(STDIN_FILENO, buffer + read_bytes, count_bytes - read_bytes);
#endif
if (tmp_read_bytes <= 0) {
break;
}
read_bytes += tmp_read_bytes;
}
return read_bytes;
}

......

在编译php时,如果你需要fastcgi支持:

You must add '--enable-fastcgi' to the configure command on Linux or
OSX based systems to get fastcgi support in the php-cgi binary. You
also must not use '--enable-discard-path'.

如果在Apache中使用:

Using FastCGI PHP with Apache
=============================

#LoadModule php7_module /usr/lib/apache/2.0/libphp7.so

ScriptAlias /fcgi-bin/ /space/fcgi-bin/
<Location /fcgi-bin/>
Options ExecCGI
SetHandler fastcgi-script
</Location>

......

SAPI 服务器端抽象层代码实现。

看完哪些C语言代码我是头晕的,每天进步一点点!

PHP内核学习(一)SAPI的更多相关文章

  1. Linux网络编程&内核学习

    c语言: 基础篇 1.<写给大家看的C语言书(第2版)> 原书名: Absolute Beginner's Guide to C (2nd Edition) 原出版社: Sams 作者: ...

  2. EPROCESS 进程/线程优先级 句柄表 GDT LDT 页表 《寒江独钓》内核学习笔记(2)

    在学习笔记(1)中,我们学习了IRP的数据结构的相关知识,接下来我们继续来学习内核中很重要的另一批数据结构: EPROCESS/KPROCESS/PEB.把它们放到一起是因为这三个数据结构及其外延和w ...

  3. Linux 内核学习的经典书籍及途径

    from:http://www.zhihu.com/question/19606660 知乎 Linux 内核学习的经典书籍及途径?修改 修改 写补充说明 举报   添加评论 分享 • 邀请回答   ...

  4. 关于Linux内核学习的误区以及相关书籍介绍

    http://www.hzlitai.com.cn/article/ARM9-article/system/1605.html 写给Linux内核新手-关于Linux内核学习的误区 先说句正经的:其实 ...

  5. linux内核学习之二:编译内核

    在linux内核学习系列的第一课中讲述了搭建学习环境的过程(http://www.cnblogs.com/xiongyuanxiong/p/3523306.html),环境搭好后,马上就进入到下一环节 ...

  6. linux内核学习之一:环境搭建--安装Debian7.3

    本系列文章假设读者已对linux有一定的了解,其实学习linux内核不需要有很深的关于linux的知识,只需要了解以下内容:linux基础知识及基本shell命令:现代操作系统的基本概念:C语言和gc ...

  7. Linux内核学习笔记-2.进程管理

    原创文章,转载请注明:Linux内核学习笔记-2.进程管理) By Lucio.Yang 部分内容来自:Linux Kernel Development(Third Edition),Robert L ...

  8. Linux内核学习笔记-1.简介和入门

    原创文章,转载请注明:Linux内核学习笔记-1.简介和入门 By Lucio.Yang 部分内容来自:Linux Kernel Development(Third Edition),Robert L ...

  9. Linux内核学习趣谈

    本文原创是freas_1990,转载请标明出处:http://blog.csdn.net/freas_1990/article/details/9304991 从大二开始学习Linux内核,到现在已经 ...

  10. Linux 内核学习经验总结

    Linux 内核学习经验总结 学习内核,每个人都有自己的学习方法,仁者见仁智者见智.以下是我在学习过程中总结出来的东西,对自身来说,我认为比较有效率,拿出来跟大家交流一下. 内核学习,一偏之见:疏漏难 ...

随机推荐

  1. FileMode枚举

    FileMode枚举是一个简单枚举,用于指定操作系统打开文件的方式. 枚举成员 成员值 描述 CreateNew 1 指定操作系统应创建新文件,如果文件存在则引发异常. Create 2 指定操作系统 ...

  2. Shell变量替换,命令替换,转义字符

    如果表达式中包含特殊字符,Shell 将会进行替换.例如,在双引号中使用变量就是一种替换,转义字符也是一种替换. 举个例子: #!/bin/bash a=10 echo -e "Value ...

  3. C语言的本质(37)——makefile之隐含规则和模式规则

    Makefile有很多灵活的写法,可以写得更简洁,同时减少出错的可能.本节我们来看看这样一个例子还有哪些改进的余地. 一个目标依赖的所有条件不一定非得写在一条规则中,也可以拆开写,例如: main.o ...

  4. 剑指offer-面试题18.树的子结构

    题目:输入两棵二叉树A和B,判断B是不是A的子结构. 二叉树节点定义如下: struct BinaryTreeNode { int m_nValue; BinaryTreeNode* m_pLeft; ...

  5. hdu 2896 病毒侵袭_ac自动机

    题意:略 思路:套用ac自动机模板 #include <iostream> #include<cstdio> #include<cstring> using nam ...

  6. HTML5 Canvas Arc Tutorial

    HTML5 Canvas Arc Tutorial HTML5 Canvas Arc Tutorial  

  7. 【Python】Coding the Matrix:Week 5: Dimension Homework 5

    这一周的作业,刚压线写完.Problem3 没有写,不想证明了.从Problem 9 开始一直到最后难度都挺大的,我是在论坛上看过了别人的讨论才写出来的,挣扎了很久. Problem 9在给定的基上分 ...

  8. Eclipse 快捷键总结

    导包:alt+/ctrl+shift+o (alt+/) 全局文件搜索:ctrl+shift+r 全局关键词搜索:ctrl+h 查看解决方案:ctrl+1 自动补全:abcdefghjklmnopqr ...

  9. java面试题集3

    一.选择题 1.下面程序的运行结果是 int i=0; while(true){ if(i++>10)  break; System.out.println(i); }1-11 2.下面程序的运 ...

  10. 被Oracle全局暂时表坑了

    今天凌晨4点多钟,在客户现场的负责人打电话给我,说非常奇怪,下载功能时快时慢.此下载功能非常复杂,之前一直是我优化,在半梦半醒中打开电脑,通过远程看着现场同事在PL/SQL developer中操作. ...