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:

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平台上就直接引入。下面是模块分类:

而内部基础函数,如: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的,下面我们来看看语法分析的格式文件描述:

面向对象的语法比较简单,支持class的定义和extend的继承,然后,通过格式定义文件迭代的去定义面向对象的正确语法格式内容。
3.源码分析
在分析前,我们还是基于一个问题,来追朔一下问题产生的原因:
我们知道,PHP的函数或者是类方法名,是大小写不敏感的。这是一个比较诡异的问题,PHP发展至今还是依然存在。下面,让我们来看一下,是哪个环节导致了这个原因。
3.1 lex的开始条件
这里需要先补充一个lex的开始条件知识。假如有一条规则是:<condition>{id} do(),那么只有当满足condition时,才会执行后面的判断。而这个condition的声明,可以通过%s 或者 %x 来声明。%s 相当于条件||判断,多个条件只要满足其中一个,则条件成立。而%x相当于&&判断,多个条件,只有全部满足,则条件成里。而condtion条件的激活,需要通过BEGIN(condition)来完成。
3.2 解析源码分析
我们直奔主题,来看一下函数调用部分的内容:









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


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

我们看到,函数调用的阶段,它也会把函数名转化成小写的操作,通过对函数声明周期的源码追溯,我们发现了PHP函数名大小写不敏感的原因所在。我们通过一个简单问题的分析,主要还是围绕Bison解释器的执行过程,慢慢得了解了PHP3的解释器执行相对比PHP2的优化内容。当然,本文并没有太多篇幅去描述这些东西,我只是想把一个分析问题的过程分享给大家,如果大家对PHP3的哪个方面感兴趣,可以按照Bison解释器的执行流程,去追踪分析源码学习。
4.参考
如无特殊说明,文章均为本站原创,转载请注明出处。如发现有什么不对的地方,希望得到您的指点。php-3.0b6
https://museum.php.net/