[源码精读系列]初具形态PHP3源码分析

1.前言

上一篇系列文章,我们介绍了PHP2的Bison解释器的实现细节,如果您没有阅读过,这里可以回顾下内容。PHP3的改动还是挺多的,唯一不变的就是核心的解释器还是基于bison。通过源码的阅读,令我印象深刻的改动是:1.引入Server API 层 2.引入模块化的扩展机制 3.增加面向对象的语法特性 4.增加对windows95的支持,更多的内容,大家可以阅读源码包里面的change log相关部分。我们今天的源码分析思路还是和上一篇一样,不过,不会重复的去分析上一篇文章已经分析过的部分内容。在分析源码前,还是想介绍下, 令我影响深刻的几个部分的内容。

2.四大新特性(笔者选择性的整理)

2.1 Server API Layer

PHP3在PHP2的基础上,把和外部Web服务器所依赖的接口解耦成了Server API,分别为:CGI、SAPI、NSAPI,如下图所示:

每一种API都对应一种运行环境和一个入口函数,比如sapi:

main.c

2.2 增加对windows95的支持

我们都知道,PHP是跨平台的脚本语言。那么,它的跨平台是怎么实现的呢?

答案是:它的解释器通过预编译宏技巧,使脚本在不同平台调用不同平台的API去处理。这种技巧很实用,像如今很火的前端跨平台框架uni-app,其开发IDE,HBuilder,就是用C++写的,它里面也利用了预编译技巧,针对不同平台去适配不同的API,来解决跨平台的问题。我们来看看跨平台部分的编码:

另外,windows平台用的是IIS Web服务器,所以,PHP3里面有加入SAPI的接口。windows平台涉及到多线程的问题,所以,PHP3里面也引入了线程安全的版本模式。这里,我们不去研究它,因为PHP的多线程处理问题,到目前PHP7为止,发展得也是不温不火。

2.3 模块化的插件机制

PHP3的模块化的插件机制,让PHP的可扩展性在底层方面拥有很大的优势。几十年的发展,让现在的PHP拥有很多方便的模块库。比如:PHP3中加入了socket库,在windows平台上,通过引入socket相关的DLL加载到程序内部,而Linux平台上就直接引入。下面是模块分类:

internal_functions.c

而内部基础函数,如:Basic functions的回调模块为:

function_entry basic_functions[] = {
	{"intval",		int_value,					NULL},
	{"doubleval",	double_value,				NULL},
	{"strval",		string_value,				NULL},
	{"short_tags",	php3_toggle_short_open_tag, NULL},
	{"sleep",		php3_sleep,					NULL},
	{"usleep",		php3_usleep,				NULL},
	{"ksort",		php3_key_sort,				first_arg_force_ref},
	{"asort",		php3_asort,					first_arg_force_ref},
	{"arsort",		php3_arsort,				first_arg_force_ref},
	{"sort",		php3_sort,					first_arg_force_ref},
	{"rsort",		php3_rsort,					first_arg_force_ref},
	{"sizeof",		php3_count,					first_arg_allow_ref},
	{"count",		php3_count,					first_arg_allow_ref},
	{"time",		php3_time,					NULL},
	{"mktime",		php3_mktime,				NULL},
	{"date",		php3_date,					NULL},
	{"gmdate",		php3_gmdate,				NULL},
	{"getdate",		php3_getdate,				NULL},
	{"checkdate",	php3_checkdate,				NULL},
	{"chr",			php3_chr,					NULL},
	{"ord",			php3_ord,					NULL},
	{"flush",		php3_flush,					NULL},
	{"end",			array_end,					first_arg_force_ref},
	{"prev",		array_prev,					first_arg_force_ref},
	{"next",		array_next,					first_arg_force_ref},
	{"reset",		array_reset,				first_arg_force_ref},
	{"current",		array_current,				first_arg_force_ref},
	{"key",			array_current_key,			first_arg_force_ref},
	{"gettype",		php3_gettype,				NULL},
	{"settype",		php3_settype,				first_arg_force_ref},
	{"min",			php3_min,					NULL},
	{"max",			php3_max,					NULL},

	{"addslashes",	php3_addslashes,			NULL},
	{"chop",		php3_chop,					NULL},
	{"pos",			array_current,				NULL},

	{"fsockopen",			php3_fsockopen,		NULL},
	{"getimagesize",		php3_getimagesize,	NULL},
	{"htmlspecialchars",	php3_htmlspecialchars,	NULL},
	{"htmlentities",		php3_htmlentities,	NULL},
	{"md5",					php3_md5,			NULL},

	{"parse_url",	php3_parse_url,				NULL},

	{"parse_str",	php3_parsestr,				NULL},
	{"phpinfo",		php3_info,					NULL},
	{"phpversion",	php3_version,				NULL},
	{"strlen",		php3_strlen,				NULL},
	{"strtok",		php3_strtok,				NULL},
	{"strtoupper",	php3_strtoupper,			NULL},
	{"strtolower",	php3_strtolower,			NULL},
	{"strchr",		php3_strstr,				NULL},
	{"strrev",		php3_strrev,				NULL},
	{"hebrev",		php3_hebrev,				NULL},
	{"hebrevc",		php3_hebrev_with_conversion,NULL},
	{"nl2br",		php3_newline_to_br,			NULL},
	{"basename",	php3_basename,				NULL},
	{"dirname", 	php3_dirname,				NULL},
	{"stripslashes",	php3_stripslashes,		NULL},
	{"strstr",		php3_strstr,				NULL},
	{"strrchr",		php3_strrchr,				NULL},
	{"substr",		php3_substr,				NULL},
	{"quotemeta",	php3_quotemeta,				NULL},
	{"urlencode",	php3_urlencode,				NULL},
	{"urldecode",	php3_urldecode,				NULL},
	{"rawurlencode",	php3_rawurlencode,		NULL},
	{"rawurldecode",	php3_rawurldecode,		NULL},
	{"ucfirst",		php3_ucfirst,				NULL},
	{"strtr",		php3_strtr,					NULL},
	{"sprintf",		php3_user_sprintf,			NULL},
	{"printf",		php3_user_printf,			NULL},
	{"setlocale",	php3_setlocale,				NULL},

	{"exec",			php3_exec,				second_and_third_args_force_ref},
	{"system",			php3_system,			second_arg_force_ref},
	{"escapeshellcmd",	php3_escapeshellcmd,	NULL},
	{"passthru",		php3_passthru,			second_arg_force_ref},

	{"soundex",		soundex,					NULL},

	{"rand",		php3_rand,					NULL},
	{"srand",		php3_srand,					NULL},
	{"getrandmax",	php3_getrandmax,			NULL},
	{"gethostbyaddr",	php3_gethostbyaddr,		NULL},
	{"gethostbyname",	php3_gethostbyname,		NULL},
	{"explode",		php3_explode,				NULL},
	{"implode",		php3_implode,				NULL},
	{"getenv",		php3_getenv,				NULL},
	{"error_reporting",	php3_error_reporting,	NULL},
	{"clearstatcache",	php3_clearstatcache,	NULL},

	{"unlink",		php3_unlink,				NULL},

	{"getmyuid",	php3_getmyuid,				NULL},
	{"getmypid",	php3_getmypid,				NULL},
	{"getmyinode",	php3_getmyinode,			NULL},
	{"getlastmod",	php3_getlastmod,			NULL},

	{"base64_decode",	php3_base64_decode,		NULL},
	{"base64_encode",	php3_base64_encode,		NULL},

	{"abs",			php3_abs,					NULL},
	{"ceil",		php3_ceil,					NULL},
	{"floor",		php3_floor,					NULL},
	{"round",		php3_round,					NULL},
	{"sin",			php3_sin,					NULL},
	{"cos",			php3_cos,					NULL},
	{"tan",			php3_tan,					NULL},
	{"asin",		php3_asin,					NULL},
	{"acos",		php3_acos,					NULL},
	{"atan",		php3_atan,					NULL},
	{"pi",			php3_pi,					NULL},
	{"pow",			php3_pow,					NULL},
	{"exp",			php3_exp,					NULL},
	{"log",			php3_log,					NULL},
	{"log10",		php3_log10,					NULL},
	{"sqrt",		php3_sqrt,					NULL},
	{"bindec",		php3_bindec,				NULL},
	{"hexdec",		php3_hexdec,				NULL},
	{"octdec",		php3_octdec,				NULL},
	{"decbin",		php3_decbin,				NULL},
	{"decoct",		php3_decoct,				NULL},
	{"dechex",		php3_dechex,				NULL},

	{"putenv",		php3_putenv,				NULL},
	{"microtime",	php3_microtime,				NULL},
	{"uniqid",		php3_uniqid,				NULL},
	{"linkinfo",	php3_linkinfo,				NULL},
	{"readlink",	php3_readlink,				NULL},
	{"symlink",		php3_symlink,				NULL},
	{"link",		php3_link,					NULL},
	{"get_current_user",	php3_get_current_user,	NULL},
	{"set_time_limit",	php3_set_time_limit,	NULL},
	
	{"get_cfg_var",	php3_get_cfg_var,			NULL},
	{"magic_quotes_runtime",	php3_set_magic_quotes_runtime,	NULL},
	
	{"is_long",		php3_is_long,				first_arg_force_ref},
	{"is_integer",	php3_is_long,				first_arg_force_ref},
	{"is_double",	php3_is_double,				first_arg_force_ref},
	{"is_real",		php3_is_double,				first_arg_force_ref},
	{"is_string",	php3_is_string,				first_arg_force_ref},
	{"is_array",	php3_is_array,				first_arg_force_ref},
	{"is_object",	php3_is_object,				first_arg_force_ref},

	{"leak",		php3_leak,					NULL},	
	{"error_log",		php3_error_log,					NULL},	
	{NULL, NULL, NULL}
};

和PHP2的命令哈希表相对比,PHP3在数据结构和设计理念上的设计花了一些功夫。另外,值得一提的是,PHP3中把PHP2里面需要在编译阶段配置的选项,通过读写配置文件的方式,动态的注入到了脚本的执行阶段。Linux平台上,模块的加载,通过 php3_config_ini_startup 来初始化模块的配置信息,然后调用module_startup_modules 来加载模块内容。

2.4 面向对象的语法特性

PHP3开始,增加了令人兴奋的面向对象语法特性。其核心实现,还是基于bison的,下面我们来看看语法分析的格式文件描述:

language-parser.y

面向对象的语法比较简单,支持class的定义和extend的继承,然后,通过格式定义文件迭代的去定义面向对象的正确语法格式内容。

3.源码分析

在分析前,我们还是基于一个问题,来追朔一下问题产生的原因:

我们知道,PHP的函数或者是类方法名,是大小写不敏感的。这是一个比较诡异的问题,PHP发展至今还是依然存在。下面,让我们来看一下,是哪个环节导致了这个原因。

3.1 lex的开始条件

这里需要先补充一个lex的开始条件知识。假如有一条规则是:<condition>{id} do(),那么只有当满足condition时,才会执行后面的判断。而这个condition的声明,可以通过%s 或者 %x 来声明。%s 相当于条件||判断,多个条件只要满足其中一个,则条件成立。而%x相当于&&判断,多个条件,只有全部满足,则条件成里。而condtion条件的激活,需要通过BEGIN(condition)来完成。

3.2 解析源码分析

我们直奔主题,来看一下函数调用部分的内容:

函数声明关键字的定义
language-parser.y 函数声明
language-parser.y 函数调用部分的声明
language-parser.y 执行函数调用结束 functioncall_pre_variable_passing函数调用预处理
language-scanner.lex IN_PHP条件的声明
language-scanner.lex BEGIN(IN_PHP)的触发条件,基于<INITIAL>条件
重置扫面器,激活INITIAL条件
php3_parse解析开始时,重置扫描器
main.c main函数如口,执行PHP3的解释器

上面,我们通过截图的方式,快速的整理出了函数的声明和调用的基本流程。下面,让我们来看看函数的调用细节。

声明部分:

control_structures_inline.c
operators.c

我们发现,在函数声明阶段,把函数存储到函数哈希表时,会把函数名转化成小写。所以导致了,函数名声明阶段,大小写就不敏感的问题。再来看看函数的调用阶段的处理

operators.c

我们看到,函数调用的阶段,它也会把函数名转化成小写的操作,通过对函数声明周期的源码追溯,我们发现了PHP函数名大小写不敏感的原因所在。我们通过一个简单问题的分析,主要还是围绕Bison解释器的执行过程,慢慢得了解了PHP3的解释器执行相对比PHP2的优化内容。当然,本文并没有太多篇幅去描述这些东西,我只是想把一个分析问题的过程分享给大家,如果大家对PHP3的哪个方面感兴趣,可以按照Bison解释器的执行流程,去追踪分析源码学习。

4.参考

php-3.0b6

https://museum.php.net/
如无特殊说明,文章均为本站原创,转载请注明出处。如发现有什么不对的地方,希望得到您的指点。

发表评论

电子邮件地址不会被公开。 必填项已用*标注