第一节:环境搭建
编译安装的关键点:配置编译安装环境,build-essential环境。
1.1 准备编译环境
针对于ubuntu16.04 下面建设编译安装环境:
apt-get install build-essential
1.2 编译
cd ~/php-src
./buildconf
./configure –help #查看可用参数
./configure –disable-all #编译最精简的php文件
make && make install 即可进行安装,
然后通过php -v 查看相应信息。
第二节:源码结构、阅读代码方法
2.1 目录结构
build 编译相关文件
ext 扩展相关文件
main php核心文件
Zend 引擎实现目录
pear PHP扩展与应用仓库
sapi 服务器抽象层代码
TSRM 线程安全
tests 测试脚本集合
win32 window 平台的相关实现
2.1 查看源码
任何编辑器均可
第三节:常用代码
3.1 “##”和”#”
3.1.1 双井号(##)【就是连接两个一起】
在C语言的宏中,”##”被称为连接符(concatenator),它是一种预处理运算符,用来把两个语言符号(Token)组合成单个语言符号。这里的语言符号不一定是宏的变量。并且双井号不能作为第一个或最后一个元素存在。如下所示源码:
#define PHP_FUNCTION ZEND_FUNCTION
#define ZEND_FUNCTION(name) ZEND_NAMED_FUNCTION(ZEND_FN(name))
#define ZEND_FN(name) zif_##name
#define ZEND_NAMED_FUNCTION(name) void name(INTERNAL_FUNCTION_PARAMETERS)
#define INTERNAL_FUNCTION_PARAMETERS int ht, zval *return_value, zval **return_value_ptr, zval *this_ptr, int return_value_used TSRMLS_DC
PHP_FUNCTION(count);
// 预处理器处理以后,PHP_FUNCTION(count);就展开为如下代码
void zif_count(int ht, zval *return_value, zval **return_value_ptr, zval *this_ptr, int return_value_used TSRMLS_DC)
宏ZEND_FN(name)中有一个“##”,它的作用一如之前所说,是一个连接符,将zif和宏的变量name的值连接起来。以这种连接方式为基础,多次使用这种宏形式,可以将它作为一个代码生成器,这样可以在一定程度上减少代码密度,我们可以将它理解为一种代码重用的手段,间接地减小不小心所造成的错误。
3.1.2 单井号(#)【表示后面的是字符串】
“#”是一种预处理运算符,它的功能是将其后面的宏参数进行字符串化操作,简单说就是在对它所引用的宏变量通过替换后在其左右各加上一个双引号,用比较官方的话说就是将语言符号(Token)转化为字符串。例如:
#define STR(x) #x
int main(int argc, char** argv)
{
printf(“%s\n”,STR(It is a long string));// 输出 it is a long string
return 0;
}
如前文所说, it is long string 是宏 STR 的参数,在展开后被包裹成一个字符串了。所以 printf 函数能直接输出这个字符串,当然这个使用场景不是很合适,因为这种用法并没有实际的意义,实际中在宏中可能包裹其它的逻辑,比如对字符串进行封装等等。
3.2 关于宏定义中的do-while循环【多行宏使用方式】
通用php内宏调用的用法,因为while(0),也就是仅仅执行一遍。这样方便编译,考虑到平台移植性和不同的系统配置。
#define ALLOC_ZVAL(z)
do{
(z) = (zval*)emalloc(sizeof(zval_gc_info));
GC_ZVAL_INIT(z);
}while(0)
因为通常的预处理方式,会导致不同的语法结构出错。例如下面情况:
#define TEST(a,b) a++;b++;
if(expr)
TEST(a,b);
else
do_else();
经过预处理后,会变成:
if(expr)
a++;b++;
else
do_else();
这样就会导致逻辑错误,b++就没有正常执行了。如果要正常的话,需要我们添加{}才能正常执行。
还有另外的一个问题就是:
为了更好的兼容平台,需要定义部分“空操作”
方式1:
#ifdef SERIALIZE_HEADERS
#define VEC_FREE() smart_str_free(&vec_str)
#else
#define VEC_FREE() do{}while(0)
#endif
方式2:
#ifdef DEBUG
# define LOG_MSG printf
#else
# define LOG_MSG()
#endif
等待更新
///
3.3 #line预处理
#line 838 “Zend/zend_language_scanner.c”
#line 预处理用于改变当前的行号(LINE)和文件名(FILE)。如上所示代码,将当前行号改变为838,文件名Zend/zend_language_scanner.c 它的作用体现在编译器的编写中,我们知道编译器对C源码编译器过程中会产生一些中间文件、通过这条指令,可以保证文件名是固定的,不会被这些中间文件代替,有利于进行调试分析。
3.4 php中的全局变量宏
在PHP代码中经常能看到一些类似PG()、EG()之类的函数,他们都是PHP中定义的宏,这系列宏主要的作用是解决线程安全所写的全局变量包裹宏,如果$PHP_SRC/main/php_globals.h文件中就包含了很多这类的宏。例如PG这个PHP的核心全局变量的宏。如下所示代码为其定义。
#ifdef ZTS // 编译时开启线程安全则使用线程安全库
#define PG(v) TSRMG(core_globals_id,php_core_gloabls *,v)
extern PHPAPI int core_globals_id;
#else
#define PG(V) (core_global.v)// 否则这其实就是一个普通的全局变量
extern ZEND_API struct _php_core_globals core_globals;
#endif
下面是php运行时全局变量的情况:
struct _php_core_globals{
zend_bool magic_quotes_gpc;//是否对输入的GET/POST/Cookie 数据使用自动字符串
zend_bool magic_quotes_runtimes;//是否对运行时从外部资源产生的数据使用自动字符串转义
zend_bool magic_quotes_sybase;//是否采用sybase 形式的自动字符串转义
zend_bool safe_mode;//是否启用安全模式
zend_bool allow_call_time_pass_reference;// 是否强迫在函数调用时按引用传递参数
zend_bool implicit_flush;//是否要求PHP输出层在每个输出块之后自动刷新数据
long output_buffering;//输出缓冲区大小(字节)
char *safe_mode_include_dir;//在安全模式下,该组目录和其子目录下的文件包含时,将跳过UID/GID检查。
zend_bool safe_mode_gid;//在安全模式下,默认在访问文件时会做UID比较检查
zend_bool sql_safe_mode;
zend_bool enable_dl;//是否允许使用dl()函数,dl()函数仅在将PHP作为apache模块安装式才有效。
char *output_handler;//将所有脚本的输出重定向到一个输出处理函数。
char *unserialize_callback_func;//如果解序列化处理器需要实例化一个未定义的类,这里指定的回调函数将以该未定义类的名字作为被unserialize()调用。
long serialize_precision;//将浮点型和双精度型数据序列化存储时的精度(有效位数)
char *safe_mode_exec_dir;//在安全模式下,只有该目录下的可执行程序才允许被执行系统程序的函数执行
long memory_limit;//一个脚本所能申请到的最大内存字节数(可以使用K和M作为单位)
long max_input_time;// 每个脚本解析输入数据(POST,GET,upload)的最大允许时间(秒)
zend_bool track_errors;//是否在变量php_errormsg中保存最近一个错误或警告消息
zend_bool display_errors;//是否将错误信息作为输出的一部分显示
zend_bool display_startup_errors;//是否显示PHP启动时的错误
zend_bool log_errors;//是否在日志文件里记录错误,具体在那里记录取决于error_log指令。
long long_errors_max_len;//设置错误日志中附加的与错误信息相关的错误源的最大长度
zend_bool ignore_repeated_errors;//记录错误日志时是否忽略重复的错误信息
zend_bool ignore_repeated_source;//是否在忽略重复的错误信息时忽略重复的错误源
zend_bool report_memleaks;//是否报告内存泄漏
char *error_log;//将错误日志记录到哪个文件中
char *doc_root;//PHP的“根目录”
char *user_dir;//告诉php在使用/~username 打开脚本时到哪个目录下去找
char *include_path;// 指定一组目录用于require(),include(),fopen_with_path()函数寻找文件。
char *open_basedir;//将PHP允许操作的所有文件(包括文件自身)都限制在此组目录列表下。
char *extension_dir;//存放扩展库(模块)的目录,也就是PHP用来寻找动态扩展模块的目录
char *upload_tmp_dir;//文件上传时存放文件的临时目录
long upload_max_filesize;//允许上传的文件的最大尺寸
char *error_append_string;//用于错误信息后输出的字符串
char *error_prepend_string;//用于错误信息前输出的字符串
char *auto_prepend_file;//指定在主文件之前自动解析的文件名
char *auto_appedn_file;//指定在主文件之后自动解析的文件名
arg_separators arg_separator;//PHP所产生的URL中用来分隔参数的分隔符。
char *variables_order;//PHP注册Enviroment,GET,POST,Cookie,Server变量的顺序
HashTable rfc1867_protected_variables;//RFC1867保护的变量名,在main/rfc1867.c文件中有用到此变量
short connection_status;//连接状态,有三个状态,正常,中断,超时
short ignore_user_abort;//是否即使在用户终止请求后也坚持完成整个请求
unsigned char header_is_being_sent;//是否头信息正在发送
zend_list tick_functions;//仅在main目录下的php_ticks.c文件中有用到,此处定义的函数在register_tick_function等函数中有用到
zval *http_globals[6];//存放GET,POST,SERVER等信息
zend_bool expose_php;//是否展示php的信息
zend_bool register_globals;//是否将E,G,P,C,S变量注册为全局变量。
zend_bool register_long_arrays;//是否启用旧式的长式数组(HTTP_*_VARS)
zend_bool register_argc_argv;//是否声明argv和
argc全局变量(包含用GET方法的信息)zendboolautoglobalsjit;//是否仅在使用到
_SERVER和
ENV变量时才创建(而不是在脚本一启动时自动创建)zendbooly2kcompliance;//是否强制打开2000年适应(可能在非y2k适应的浏览器中导致问题)char∗docrefroot;//如果打开了htmlerrors指令,PHP将会在出错信息上显示超链接char∗docrefext;//指定文件的扩展名必须含有点符号zendboolhtmlerrors;//是否在出错信息中使用HTML标记。zendboolxmlrpcerrors;longxmlrpcerrornumber;zendboolacrivatedautoglobals[8];zendboolmodulesactivated;//是否已经激活模块zendboolfileuploads;//是否允许HTTP文件上传zendboolduringrequeststartup;//是否在请求初始化过程中zendboolalwasyspopulaterawpostdata;//是否总是生成
HTTP_RAW_POST_DATA变量(原始POST数据)
zend_bool report_zend_debug;//是否打开zend debug,仅在main/main.c文件中有使用
int last_error_tyep;//最后的错误类型
char *last_error_message;//最后的错误信息
char *last_error_file;//最后的错误文件
char last_error_lineno;//最后的错误行
char *disable_functions;//该指令接受一个用逗号分隔
char *disable_classes;//该指令接受一个用逗号分隔的类名列表,以禁用特定的类
zend_bool allow_url_include;//是否允许include/require 远程文件
zend_bool exit_on_timeout;//超时则退出
#ifdef PHP_WIN32
zend_bool com_initialized;
#endif
long max_input_nesting_level;//最大的嵌套层数
zend_bool in_user_include;//是否在用户包含空间
char *user_ini_filename;//用户的ini文件名
long user_ini_cache_ttl;//ini缓存过期限制
char *request_order;//优先级比variables_order高,在request变量生成时用到,个人觉得是历史遗留问题
zend_bool mail_x_header;//仅在 ext/standard/mail.c文件中使用。
char *mail_log;
zend_bool in_error_log;
}
上述代码,绝大部分可以通过php.ini和 ini_set及ini_get进行设置处理。